config.c 135 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348
  1. /*
  2. * config.c - the platform-independent parts of the PuTTY
  3. * configuration box.
  4. */
  5. #include <assert.h>
  6. #include <stdlib.h>
  7. #include "putty.h"
  8. #include "dialog.h"
  9. #include "storage.h"
  10. #include "tree234.h"
  11. #define PRINTER_DISABLED_STRING "None (printing disabled)"
  12. #define HOST_BOX_TITLE "Host Name (or IP address)"
  13. #define PORT_BOX_TITLE "Port"
  14. void conf_radiobutton_handler(dlgcontrol *ctrl, dlgparam *dlg,
  15. void *data, int event)
  16. {
  17. int button;
  18. Conf *conf = (Conf *)data;
  19. /*
  20. * For a standard radio button set, the context parameter gives
  21. * the primary key (CONF_foo), and the extra data per button
  22. * gives the value the target field should take if that button
  23. * is the one selected.
  24. */
  25. if (event == EVENT_REFRESH) {
  26. int val = conf_get_int(conf, ctrl->context.i);
  27. for (button = 0; button < ctrl->radio.nbuttons; button++)
  28. if (val == ctrl->radio.buttondata[button].i)
  29. break;
  30. /* We expected that `break' to happen, in all circumstances. */
  31. assert(button < ctrl->radio.nbuttons);
  32. dlg_radiobutton_set(ctrl, dlg, button);
  33. } else if (event == EVENT_VALCHANGE) {
  34. button = dlg_radiobutton_get(ctrl, dlg);
  35. assert(button >= 0 && button < ctrl->radio.nbuttons);
  36. conf_set_int(conf, ctrl->context.i,
  37. ctrl->radio.buttondata[button].i);
  38. }
  39. }
  40. void conf_radiobutton_bool_handler(dlgcontrol *ctrl, dlgparam *dlg,
  41. void *data, int event)
  42. {
  43. int button;
  44. Conf *conf = (Conf *)data;
  45. /*
  46. * Same as conf_radiobutton_handler, but using conf_set_bool in
  47. * place of conf_set_int, because it's dealing with a bool-typed
  48. * config option.
  49. */
  50. if (event == EVENT_REFRESH) {
  51. int val = conf_get_bool(conf, ctrl->context.i);
  52. for (button = 0; button < ctrl->radio.nbuttons; button++)
  53. if (val == ctrl->radio.buttondata[button].i)
  54. break;
  55. /* We expected that `break' to happen, in all circumstances. */
  56. assert(button < ctrl->radio.nbuttons);
  57. dlg_radiobutton_set(ctrl, dlg, button);
  58. } else if (event == EVENT_VALCHANGE) {
  59. button = dlg_radiobutton_get(ctrl, dlg);
  60. assert(button >= 0 && button < ctrl->radio.nbuttons);
  61. conf_set_bool(conf, ctrl->context.i,
  62. ctrl->radio.buttondata[button].i);
  63. }
  64. }
  65. #define CHECKBOX_INVERT (1<<30)
  66. void conf_checkbox_handler(dlgcontrol *ctrl, dlgparam *dlg,
  67. void *data, int event)
  68. {
  69. int key;
  70. bool invert;
  71. Conf *conf = (Conf *)data;
  72. /*
  73. * For a standard checkbox, the context parameter gives the
  74. * primary key (CONF_foo), optionally ORed with CHECKBOX_INVERT.
  75. */
  76. key = ctrl->context.i;
  77. if (key & CHECKBOX_INVERT) {
  78. key &= ~CHECKBOX_INVERT;
  79. invert = true;
  80. } else
  81. invert = false;
  82. /*
  83. * C lacks a logical XOR, so the following code uses the idiom
  84. * (!a ^ !b) to obtain the logical XOR of a and b. (That is, 1
  85. * iff exactly one of a and b is nonzero, otherwise 0.)
  86. */
  87. if (event == EVENT_REFRESH) {
  88. bool val = conf_get_bool(conf, key);
  89. dlg_checkbox_set(ctrl, dlg, (!val ^ !invert));
  90. } else if (event == EVENT_VALCHANGE) {
  91. conf_set_bool(conf, key, !dlg_checkbox_get(ctrl,dlg) ^ !invert);
  92. }
  93. }
  94. const struct conf_editbox_handler_type conf_editbox_str = {.type = EDIT_STR};
  95. const struct conf_editbox_handler_type conf_editbox_int = {.type = EDIT_INT};
  96. void conf_editbox_handler(dlgcontrol *ctrl, dlgparam *dlg,
  97. void *data, int event)
  98. {
  99. /*
  100. * The standard edit-box handler expects the main `context' field
  101. * to contain the primary key. The secondary `context2' field is a
  102. * pointer to the struct conf_editbox_handler_type defined in
  103. * putty.h.
  104. */
  105. int key = ctrl->context.i;
  106. const struct conf_editbox_handler_type *type = ctrl->context2.cp;
  107. Conf *conf = (Conf *)data;
  108. if (type->type == EDIT_STR) {
  109. if (event == EVENT_REFRESH) {
  110. bool utf8;
  111. char *field = conf_get_str_ambi(conf, key, &utf8);
  112. if (utf8)
  113. dlg_editbox_set_utf8(ctrl, dlg, field);
  114. else
  115. dlg_editbox_set(ctrl, dlg, field);
  116. } else if (event == EVENT_VALCHANGE) {
  117. char *field = dlg_editbox_get_utf8(ctrl, dlg);
  118. if (!conf_try_set_utf8(conf, key, field)) {
  119. sfree(field);
  120. field = dlg_editbox_get(ctrl, dlg);
  121. conf_set_str(conf, key, field);
  122. }
  123. sfree(field);
  124. }
  125. } else {
  126. if (event == EVENT_REFRESH) {
  127. char str[80];
  128. int value = conf_get_int(conf, key);
  129. if (type->type == EDIT_INT)
  130. sprintf(str, "%d", value);
  131. else
  132. sprintf(str, "%g", (double)value / type->denominator);
  133. dlg_editbox_set(ctrl, dlg, str);
  134. } else if (event == EVENT_VALCHANGE) {
  135. char *str = dlg_editbox_get(ctrl, dlg);
  136. if (type->type == EDIT_INT)
  137. conf_set_int(conf, key, atoi(str));
  138. else
  139. conf_set_int(conf, key, (int)(type->denominator * atof(str)));
  140. sfree(str);
  141. }
  142. }
  143. }
  144. void conf_filesel_handler(dlgcontrol *ctrl, dlgparam *dlg,
  145. void *data, int event)
  146. {
  147. int key = ctrl->context.i;
  148. Conf *conf = (Conf *)data;
  149. if (event == EVENT_REFRESH) {
  150. dlg_filesel_set(
  151. ctrl, dlg, conf_get_filename(conf, key));
  152. } else if (event == EVENT_VALCHANGE) {
  153. Filename *filename = dlg_filesel_get(ctrl, dlg);
  154. conf_set_filename(conf, key, filename);
  155. filename_free(filename);
  156. }
  157. }
  158. void conf_fontsel_handler(dlgcontrol *ctrl, dlgparam *dlg,
  159. void *data, int event)
  160. {
  161. int key = ctrl->context.i;
  162. Conf *conf = (Conf *)data;
  163. if (event == EVENT_REFRESH) {
  164. dlg_fontsel_set(
  165. ctrl, dlg, conf_get_fontspec(conf, key));
  166. } else if (event == EVENT_VALCHANGE) {
  167. FontSpec *fontspec = dlg_fontsel_get(ctrl, dlg);
  168. conf_set_fontspec(conf, key, fontspec);
  169. fontspec_free(fontspec);
  170. }
  171. }
  172. static void config_host_handler(dlgcontrol *ctrl, dlgparam *dlg,
  173. void *data, int event)
  174. {
  175. Conf *conf = (Conf *)data;
  176. /*
  177. * This function works just like the standard edit box handler,
  178. * only it has to choose the control's label and text from two
  179. * different places depending on the protocol.
  180. */
  181. if (event == EVENT_REFRESH) {
  182. if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) {
  183. /*
  184. * This label text is carefully chosen to contain an n,
  185. * since that's the shortcut for the host name control.
  186. */
  187. dlg_label_change(ctrl, dlg, "Serial line");
  188. dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_serline));
  189. } else {
  190. dlg_label_change(ctrl, dlg, HOST_BOX_TITLE);
  191. dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_host));
  192. }
  193. } else if (event == EVENT_VALCHANGE) {
  194. char *s = dlg_editbox_get(ctrl, dlg);
  195. if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
  196. conf_set_str(conf, CONF_serline, s);
  197. else
  198. conf_set_str(conf, CONF_host, s);
  199. sfree(s);
  200. }
  201. }
  202. static void config_port_handler(dlgcontrol *ctrl, dlgparam *dlg,
  203. void *data, int event)
  204. {
  205. Conf *conf = (Conf *)data;
  206. char buf[80];
  207. /*
  208. * This function works similarly to the standard edit box handler,
  209. * only it has to choose the control's label and text from two
  210. * different places depending on the protocol.
  211. */
  212. if (event == EVENT_REFRESH) {
  213. if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) {
  214. /*
  215. * This label text is carefully chosen to contain a p,
  216. * since that's the shortcut for the port control.
  217. */
  218. dlg_label_change(ctrl, dlg, "Speed");
  219. sprintf(buf, "%d", conf_get_int(conf, CONF_serspeed));
  220. } else {
  221. dlg_label_change(ctrl, dlg, PORT_BOX_TITLE);
  222. if (conf_get_int(conf, CONF_port) != 0)
  223. sprintf(buf, "%d", conf_get_int(conf, CONF_port));
  224. else
  225. /* Display an (invalid) port of 0 as blank */
  226. buf[0] = '\0';
  227. }
  228. dlg_editbox_set(ctrl, dlg, buf);
  229. } else if (event == EVENT_VALCHANGE) {
  230. char *s = dlg_editbox_get(ctrl, dlg);
  231. int i = atoi(s);
  232. sfree(s);
  233. if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
  234. conf_set_int(conf, CONF_serspeed, i);
  235. else
  236. conf_set_int(conf, CONF_port, i);
  237. }
  238. }
  239. struct hostport {
  240. dlgcontrol *host, *port, *protradio, *protlist;
  241. bool mid_refresh;
  242. };
  243. /*
  244. * Shared handler for protocol radio-button and drop-list controls.
  245. * Handles the interaction of those two controls, and also changes
  246. * the setting of the port box to match the protocol if necessary,
  247. * and refreshes both host and port boxes when switching to/from the
  248. * serial backend.
  249. */
  250. static void config_protocols_handler(dlgcontrol *ctrl, dlgparam *dlg,
  251. void *data, int event)
  252. {
  253. Conf *conf = (Conf *)data;
  254. int curproto = conf_get_int(conf, CONF_protocol);
  255. struct hostport *hp = (struct hostport *)ctrl->context.p;
  256. if (event == EVENT_REFRESH) {
  257. /*
  258. * Refresh the states of the controls from Conf.
  259. *
  260. * When refreshing these controls, we have to watch out for
  261. * re-entrancy: because there are two controls involved, the
  262. * refresh is not atomic, so the VALCHANGE and/or SELCHANGE
  263. * callbacks resulting from our updates here might cause other
  264. * settings here to change unwantedly. (E.g. setting the list
  265. * selection shouldn't trigger the SELCHANGE side effect of
  266. * selecting the Other radio button; setting the radio button
  267. * to Other here shouldn't have the side effect of selecting
  268. * whatever protocol is _currently_ selected in the list box,
  269. * if we haven't selected the right one yet.)
  270. */
  271. hp->mid_refresh = true;
  272. if (ctrl == hp->protradio) {
  273. /* Available buttons were set up when control was created.
  274. * Just select one of them, possibly. */
  275. for (int button = 0; button < ctrl->radio.nbuttons; button++)
  276. /* The final button is "Other:". If we reach that one, the
  277. * current protocol must be in the drop list, so we should
  278. * select the "Other:" button. */
  279. if (curproto == ctrl->radio.buttondata[button].i ||
  280. button == ctrl->radio.nbuttons-1) {
  281. dlg_radiobutton_set(ctrl, dlg, button);
  282. break;
  283. }
  284. } else if (ctrl == hp->protlist) {
  285. int curentry = -1;
  286. dlg_update_start(ctrl, dlg);
  287. dlg_listbox_clear(ctrl, dlg);
  288. assert(n_ui_backends > 0 && n_ui_backends < PROTOCOL_LIMIT);
  289. for (size_t i = n_ui_backends;
  290. i < PROTOCOL_LIMIT && backends[i]; i++) {
  291. dlg_listbox_addwithid(ctrl, dlg,
  292. backends[i]->displayname_tc,
  293. backends[i]->protocol);
  294. if (backends[i]->protocol == curproto)
  295. curentry = i - n_ui_backends;
  296. }
  297. if (curentry > 0) {
  298. /*
  299. * The currently configured protocol is one of the
  300. * list-box ones, so select it in protlist.
  301. *
  302. * (The corresponding refresh event for protradio
  303. * should have selected the "Other:" radio button, to
  304. * keep things consistent.)
  305. */
  306. dlg_listbox_select(ctrl, dlg, curentry);
  307. } else {
  308. /*
  309. * If the currently configured protocol is one of the
  310. * radio buttons, we must still ensure *something* is
  311. * selected in the list box. The sensible default is
  312. * the first list element, which be_*.c ought to have
  313. * arranged to be the 'runner-up' in protocol
  314. * popularity out of the ones relegated to the list
  315. * box.
  316. *
  317. * We don't make much effort to retain the state of
  318. * the list box when it doesn't correspond to an
  319. * actual protocol. So it's easy for this case to be
  320. * reached as a side effect of other actions, e.g.
  321. * loading a saved session that has a radio-button
  322. * protocol configured.
  323. */
  324. dlg_listbox_select(ctrl, dlg, 0);
  325. }
  326. dlg_update_done(ctrl, dlg);
  327. }
  328. hp->mid_refresh = false;
  329. } else if (!hp->mid_refresh) {
  330. /*
  331. * Potentially update Conf from the states of the controls.
  332. */
  333. int newproto = curproto;
  334. if (event == EVENT_VALCHANGE && ctrl == hp->protradio) {
  335. int button = dlg_radiobutton_get(ctrl, dlg);
  336. assert(button >= 0 && button < ctrl->radio.nbuttons);
  337. if (ctrl->radio.buttondata[button].i == -1) {
  338. /*
  339. * The 'Other' radio button was selected, which means we
  340. * have to set CONF_protocol based on the currently
  341. * selected list box entry.
  342. *
  343. * (We conditionalise this on there _being_ a selected
  344. * list box entry. I hope the case where nothing is
  345. * selected can't actually come up except during
  346. * initialisation, and I also hope that hp->mid_session
  347. * will prevent that case from getting here. But as a
  348. * last-ditch fallback, this if statement should at least
  349. * guarantee that we don't pass a nonsense value to
  350. * dlg_listbox_getid.)
  351. */
  352. int i = dlg_listbox_index(hp->protlist, dlg);
  353. if (i >= 0)
  354. newproto = dlg_listbox_getid(hp->protlist, dlg, i);
  355. } else {
  356. newproto = ctrl->radio.buttondata[button].i;
  357. }
  358. } else if (event == EVENT_SELCHANGE && ctrl == hp->protlist) {
  359. int i = dlg_listbox_index(ctrl, dlg);
  360. if (i >= 0) {
  361. newproto = dlg_listbox_getid(ctrl, dlg, i);
  362. /* Select the "Other" radio button, too */
  363. dlg_radiobutton_set(hp->protradio, dlg,
  364. hp->protradio->radio.nbuttons-1);
  365. }
  366. }
  367. if (newproto != curproto) {
  368. conf_set_int(conf, CONF_protocol, newproto);
  369. const struct BackendVtable *cvt = backend_vt_from_proto(curproto);
  370. const struct BackendVtable *nvt = backend_vt_from_proto(newproto);
  371. assert(cvt);
  372. assert(nvt);
  373. /*
  374. * Iff the user hasn't changed the port from the old
  375. * protocol's default, update it with the new protocol's
  376. * default.
  377. *
  378. * (This includes a "default" of 0, implying that there is
  379. * no sensible default for that protocol; in this case
  380. * it's displayed as a blank.)
  381. *
  382. * This helps with the common case of tabbing through the
  383. * controls in order and setting a non-default port before
  384. * getting to the protocol; we want that non-default port
  385. * to be preserved.
  386. */
  387. int port = conf_get_int(conf, CONF_port);
  388. if (port == cvt->default_port)
  389. conf_set_int(conf, CONF_port, nvt->default_port);
  390. dlg_refresh(hp->host, dlg);
  391. dlg_refresh(hp->port, dlg);
  392. }
  393. }
  394. }
  395. static void loggingbuttons_handler(dlgcontrol *ctrl, dlgparam *dlg,
  396. void *data, int event)
  397. {
  398. int button;
  399. Conf *conf = (Conf *)data;
  400. /* This function works just like the standard radio-button handler,
  401. * but it has to fall back to "no logging" in situations where the
  402. * configured logging type isn't applicable.
  403. */
  404. if (event == EVENT_REFRESH) {
  405. int logtype = conf_get_int(conf, CONF_logtype);
  406. for (button = 0; button < ctrl->radio.nbuttons; button++)
  407. if (logtype == ctrl->radio.buttondata[button].i)
  408. break;
  409. /* We fell off the end, so we lack the configured logging type */
  410. if (button == ctrl->radio.nbuttons) {
  411. button = 0;
  412. conf_set_int(conf, CONF_logtype, LGTYP_NONE);
  413. }
  414. dlg_radiobutton_set(ctrl, dlg, button);
  415. } else if (event == EVENT_VALCHANGE) {
  416. button = dlg_radiobutton_get(ctrl, dlg);
  417. assert(button >= 0 && button < ctrl->radio.nbuttons);
  418. conf_set_int(conf, CONF_logtype, ctrl->radio.buttondata[button].i);
  419. }
  420. }
  421. static void numeric_keypad_handler(dlgcontrol *ctrl, dlgparam *dlg,
  422. void *data, int event)
  423. {
  424. int button;
  425. Conf *conf = (Conf *)data;
  426. /*
  427. * This function works much like the standard radio button
  428. * handler, but it has to handle two fields in Conf.
  429. */
  430. if (event == EVENT_REFRESH) {
  431. if (conf_get_bool(conf, CONF_nethack_keypad))
  432. button = 2;
  433. else if (conf_get_bool(conf, CONF_app_keypad))
  434. button = 1;
  435. else
  436. button = 0;
  437. assert(button < ctrl->radio.nbuttons);
  438. dlg_radiobutton_set(ctrl, dlg, button);
  439. } else if (event == EVENT_VALCHANGE) {
  440. button = dlg_radiobutton_get(ctrl, dlg);
  441. assert(button >= 0 && button < ctrl->radio.nbuttons);
  442. if (button == 2) {
  443. conf_set_bool(conf, CONF_app_keypad, false);
  444. conf_set_bool(conf, CONF_nethack_keypad, true);
  445. } else {
  446. conf_set_bool(conf, CONF_app_keypad, (button != 0));
  447. conf_set_bool(conf, CONF_nethack_keypad, false);
  448. }
  449. }
  450. }
  451. static void cipherlist_handler(dlgcontrol *ctrl, dlgparam *dlg,
  452. void *data, int event)
  453. {
  454. Conf *conf = (Conf *)data;
  455. if (event == EVENT_REFRESH) {
  456. int i;
  457. static const struct { const char *s; int c; } ciphers[] = {
  458. { "ChaCha20 (SSH-2 only)", CIPHER_CHACHA20 },
  459. { "AES-GCM (SSH-2 only)", CIPHER_AESGCM },
  460. { "3DES", CIPHER_3DES },
  461. { "Blowfish", CIPHER_BLOWFISH },
  462. { "DES", CIPHER_DES },
  463. { "AES (SSH-2 only)", CIPHER_AES },
  464. { "Arcfour (SSH-2 only)", CIPHER_ARCFOUR },
  465. { "-- warn below here --", CIPHER_WARN }
  466. };
  467. /* Set up the "selected ciphers" box. */
  468. /* (cipherlist assumed to contain all ciphers) */
  469. dlg_update_start(ctrl, dlg);
  470. dlg_listbox_clear(ctrl, dlg);
  471. for (i = 0; i < CIPHER_MAX; i++) {
  472. int c = conf_get_int_int(conf, CONF_ssh_cipherlist, i);
  473. int j;
  474. const char *cstr = NULL;
  475. for (j = 0; j < (sizeof ciphers) / (sizeof ciphers[0]); j++) {
  476. if (ciphers[j].c == c) {
  477. cstr = ciphers[j].s;
  478. break;
  479. }
  480. }
  481. dlg_listbox_addwithid(ctrl, dlg, cstr, c);
  482. }
  483. dlg_update_done(ctrl, dlg);
  484. } else if (event == EVENT_VALCHANGE) {
  485. int i;
  486. /* Update array to match the list box. */
  487. for (i=0; i < CIPHER_MAX; i++)
  488. conf_set_int_int(conf, CONF_ssh_cipherlist, i,
  489. dlg_listbox_getid(ctrl, dlg, i));
  490. }
  491. }
  492. #ifndef NO_GSSAPI
  493. static void gsslist_handler(dlgcontrol *ctrl, dlgparam *dlg,
  494. void *data, int event)
  495. {
  496. Conf *conf = (Conf *)data;
  497. if (event == EVENT_REFRESH) {
  498. int i;
  499. dlg_update_start(ctrl, dlg);
  500. dlg_listbox_clear(ctrl, dlg);
  501. for (i = 0; i < ngsslibs; i++) {
  502. int id = conf_get_int_int(conf, CONF_ssh_gsslist, i);
  503. assert(id >= 0 && id < ngsslibs);
  504. dlg_listbox_addwithid(ctrl, dlg, gsslibnames[id], id);
  505. }
  506. dlg_update_done(ctrl, dlg);
  507. } else if (event == EVENT_VALCHANGE) {
  508. int i;
  509. /* Update array to match the list box. */
  510. for (i=0; i < ngsslibs; i++)
  511. conf_set_int_int(conf, CONF_ssh_gsslist, i,
  512. dlg_listbox_getid(ctrl, dlg, i));
  513. }
  514. }
  515. #endif
  516. static void kexlist_handler(dlgcontrol *ctrl, dlgparam *dlg,
  517. void *data, int event)
  518. {
  519. Conf *conf = (Conf *)data;
  520. if (event == EVENT_REFRESH) {
  521. int i;
  522. static const struct { const char *s; int k; } kexes[] = {
  523. { "Diffie-Hellman group 1 (1024-bit)", KEX_DHGROUP1 },
  524. { "Diffie-Hellman group 14 (2048-bit)", KEX_DHGROUP14 },
  525. { "Diffie-Hellman group 15 (3072-bit)", KEX_DHGROUP15 },
  526. { "Diffie-Hellman group 16 (4096-bit)", KEX_DHGROUP16 },
  527. { "Diffie-Hellman group 17 (6144-bit)", KEX_DHGROUP17 },
  528. { "Diffie-Hellman group 18 (8192-bit)", KEX_DHGROUP18 },
  529. { "Diffie-Hellman group exchange", KEX_DHGEX },
  530. { "RSA-based key exchange", KEX_RSA },
  531. { "ECDH key exchange", KEX_ECDH },
  532. { "NTRU Prime / Curve25519 hybrid kex", KEX_NTRU_HYBRID },
  533. { "-- warn below here --", KEX_WARN }
  534. };
  535. /* Set up the "kex preference" box. */
  536. /* (kexlist assumed to contain all algorithms) */
  537. dlg_update_start(ctrl, dlg);
  538. dlg_listbox_clear(ctrl, dlg);
  539. for (i = 0; i < KEX_MAX; i++) {
  540. int k = conf_get_int_int(conf, CONF_ssh_kexlist, i);
  541. int j;
  542. const char *kstr = NULL;
  543. for (j = 0; j < (sizeof kexes) / (sizeof kexes[0]); j++) {
  544. if (kexes[j].k == k) {
  545. kstr = kexes[j].s;
  546. break;
  547. }
  548. }
  549. dlg_listbox_addwithid(ctrl, dlg, kstr, k);
  550. }
  551. dlg_update_done(ctrl, dlg);
  552. } else if (event == EVENT_VALCHANGE) {
  553. int i;
  554. /* Update array to match the list box. */
  555. for (i=0; i < KEX_MAX; i++)
  556. conf_set_int_int(conf, CONF_ssh_kexlist, i,
  557. dlg_listbox_getid(ctrl, dlg, i));
  558. }
  559. }
  560. static void hklist_handler(dlgcontrol *ctrl, dlgparam *dlg,
  561. void *data, int event)
  562. {
  563. Conf *conf = (Conf *)data;
  564. if (event == EVENT_REFRESH) {
  565. int i;
  566. static const struct { const char *s; int k; } hks[] = {
  567. { "Ed25519", HK_ED25519 },
  568. { "Ed448", HK_ED448 },
  569. { "ECDSA", HK_ECDSA },
  570. { "DSA", HK_DSA },
  571. { "RSA", HK_RSA },
  572. { "-- warn below here --", HK_WARN }
  573. };
  574. /* Set up the "host key preference" box. */
  575. /* (hklist assumed to contain all algorithms) */
  576. dlg_update_start(ctrl, dlg);
  577. dlg_listbox_clear(ctrl, dlg);
  578. for (i = 0; i < HK_MAX; i++) {
  579. int k = conf_get_int_int(conf, CONF_ssh_hklist, i);
  580. int j;
  581. const char *kstr = NULL;
  582. for (j = 0; j < lenof(hks); j++) {
  583. if (hks[j].k == k) {
  584. kstr = hks[j].s;
  585. break;
  586. }
  587. }
  588. dlg_listbox_addwithid(ctrl, dlg, kstr, k);
  589. }
  590. dlg_update_done(ctrl, dlg);
  591. } else if (event == EVENT_VALCHANGE) {
  592. int i;
  593. /* Update array to match the list box. */
  594. for (i=0; i < HK_MAX; i++)
  595. conf_set_int_int(conf, CONF_ssh_hklist, i,
  596. dlg_listbox_getid(ctrl, dlg, i));
  597. }
  598. }
  599. static void printerbox_handler(dlgcontrol *ctrl, dlgparam *dlg,
  600. void *data, int event)
  601. {
  602. Conf *conf = (Conf *)data;
  603. if (event == EVENT_REFRESH) {
  604. int nprinters, i;
  605. printer_enum *pe;
  606. const char *printer;
  607. dlg_update_start(ctrl, dlg);
  608. /*
  609. * Some backends may wish to disable the drop-down list on
  610. * this edit box. Be prepared for this.
  611. */
  612. if (ctrl->editbox.has_list) {
  613. dlg_listbox_clear(ctrl, dlg);
  614. dlg_listbox_add(ctrl, dlg, PRINTER_DISABLED_STRING);
  615. pe = printer_start_enum(&nprinters);
  616. for (i = 0; i < nprinters; i++)
  617. dlg_listbox_add(ctrl, dlg, printer_get_name(pe, i));
  618. printer_finish_enum(pe);
  619. }
  620. printer = conf_get_str(conf, CONF_printer);
  621. if (!printer)
  622. printer = PRINTER_DISABLED_STRING;
  623. dlg_editbox_set(ctrl, dlg, printer);
  624. dlg_update_done(ctrl, dlg);
  625. } else if (event == EVENT_VALCHANGE) {
  626. char *printer = dlg_editbox_get(ctrl, dlg);
  627. if (!strcmp(printer, PRINTER_DISABLED_STRING))
  628. printer[0] = '\0';
  629. conf_set_str(conf, CONF_printer, printer);
  630. sfree(printer);
  631. }
  632. }
  633. static void codepage_handler(dlgcontrol *ctrl, dlgparam *dlg,
  634. void *data, int event)
  635. {
  636. Conf *conf = (Conf *)data;
  637. if (event == EVENT_REFRESH) {
  638. int i;
  639. const char *cp, *thiscp;
  640. dlg_update_start(ctrl, dlg);
  641. thiscp = cp_name(decode_codepage(conf_get_str(conf,
  642. CONF_line_codepage)));
  643. dlg_listbox_clear(ctrl, dlg);
  644. for (i = 0; (cp = cp_enumerate(i)) != NULL; i++)
  645. dlg_listbox_add(ctrl, dlg, cp);
  646. dlg_editbox_set(ctrl, dlg, thiscp);
  647. conf_set_str(conf, CONF_line_codepage, thiscp);
  648. dlg_update_done(ctrl, dlg);
  649. } else if (event == EVENT_VALCHANGE) {
  650. char *codepage = dlg_editbox_get(ctrl, dlg);
  651. conf_set_str(conf, CONF_line_codepage,
  652. cp_name(decode_codepage(codepage)));
  653. sfree(codepage);
  654. }
  655. }
  656. static void sshbug_handler(dlgcontrol *ctrl, dlgparam *dlg,
  657. void *data, int event)
  658. {
  659. Conf *conf = (Conf *)data;
  660. if (event == EVENT_REFRESH) {
  661. /*
  662. * We must fetch the previously configured value from the Conf
  663. * before we start modifying the drop-down list, otherwise the
  664. * spurious SELCHANGE we trigger in the process will overwrite
  665. * the value we wanted to keep.
  666. */
  667. int oldconf = conf_get_int(conf, ctrl->context.i);
  668. dlg_update_start(ctrl, dlg);
  669. dlg_listbox_clear(ctrl, dlg);
  670. dlg_listbox_addwithid(ctrl, dlg, "Auto", AUTO);
  671. dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF);
  672. dlg_listbox_addwithid(ctrl, dlg, "On", FORCE_ON);
  673. switch (oldconf) {
  674. case AUTO: dlg_listbox_select(ctrl, dlg, 0); break;
  675. case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 1); break;
  676. case FORCE_ON: dlg_listbox_select(ctrl, dlg, 2); break;
  677. }
  678. dlg_update_done(ctrl, dlg);
  679. } else if (event == EVENT_SELCHANGE) {
  680. int i = dlg_listbox_index(ctrl, dlg);
  681. if (i < 0)
  682. i = AUTO;
  683. else
  684. i = dlg_listbox_getid(ctrl, dlg, i);
  685. conf_set_int(conf, ctrl->context.i, i);
  686. }
  687. }
  688. static void sshbug_handler_manual_only(dlgcontrol *ctrl, dlgparam *dlg,
  689. void *data, int event)
  690. {
  691. /*
  692. * This is just like sshbug_handler, except that there's no 'Auto'
  693. * option. Used for bug workaround flags that can't be
  694. * autodetected, and have to be manually enabled if they're to be
  695. * used at all.
  696. */
  697. Conf *conf = (Conf *)data;
  698. if (event == EVENT_REFRESH) {
  699. int oldconf = conf_get_int(conf, ctrl->context.i);
  700. dlg_update_start(ctrl, dlg);
  701. dlg_listbox_clear(ctrl, dlg);
  702. dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF);
  703. dlg_listbox_addwithid(ctrl, dlg, "On", FORCE_ON);
  704. switch (oldconf) {
  705. case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 0); break;
  706. case FORCE_ON: dlg_listbox_select(ctrl, dlg, 1); break;
  707. }
  708. dlg_update_done(ctrl, dlg);
  709. } else if (event == EVENT_SELCHANGE) {
  710. int i = dlg_listbox_index(ctrl, dlg);
  711. if (i < 0)
  712. i = FORCE_OFF;
  713. else
  714. i = dlg_listbox_getid(ctrl, dlg, i);
  715. conf_set_int(conf, ctrl->context.i, i);
  716. }
  717. }
  718. struct sessionsaver_data {
  719. dlgcontrol *editbox, *listbox, *loadbutton, *savebutton, *delbutton;
  720. dlgcontrol *okbutton, *cancelbutton;
  721. struct sesslist sesslist;
  722. bool midsession;
  723. char *savedsession; /* the current contents of ssd->editbox */
  724. };
  725. static void sessionsaver_data_free(void *ssdv)
  726. {
  727. struct sessionsaver_data *ssd = (struct sessionsaver_data *)ssdv;
  728. get_sesslist(&ssd->sesslist, false);
  729. sfree(ssd->savedsession);
  730. sfree(ssd);
  731. }
  732. /*
  733. * Helper function to load the session selected in the list box, if
  734. * any, as this is done in more than one place below. Returns 0 for
  735. * failure.
  736. */
  737. static bool load_selected_session(
  738. struct sessionsaver_data *ssd,
  739. dlgparam *dlg, Conf *conf, bool *maybe_launch)
  740. {
  741. int i = dlg_listbox_index(ssd->listbox, dlg);
  742. bool isdef;
  743. if (i < 0) {
  744. dlg_beep(dlg);
  745. return false;
  746. }
  747. isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
  748. load_settings(ssd->sesslist.sessions[i], conf);
  749. sfree(ssd->savedsession);
  750. ssd->savedsession = dupstr(isdef ? "" : ssd->sesslist.sessions[i]);
  751. if (maybe_launch)
  752. *maybe_launch = !isdef;
  753. dlg_refresh(NULL, dlg);
  754. /* Restore the selection, which might have been clobbered by
  755. * changing the value of the edit box. */
  756. dlg_listbox_select(ssd->listbox, dlg, i);
  757. return true;
  758. }
  759. static void sessionsaver_handler(dlgcontrol *ctrl, dlgparam *dlg,
  760. void *data, int event)
  761. {
  762. Conf *conf = (Conf *)data;
  763. struct sessionsaver_data *ssd =
  764. (struct sessionsaver_data *)ctrl->context.p;
  765. if (event == EVENT_REFRESH) {
  766. if (ctrl == ssd->editbox) {
  767. dlg_editbox_set(ctrl, dlg, ssd->savedsession);
  768. } else if (ctrl == ssd->listbox) {
  769. int i;
  770. dlg_update_start(ctrl, dlg);
  771. dlg_listbox_clear(ctrl, dlg);
  772. for (i = 0; i < ssd->sesslist.nsessions; i++)
  773. dlg_listbox_add(ctrl, dlg, ssd->sesslist.sessions[i]);
  774. dlg_update_done(ctrl, dlg);
  775. }
  776. } else if (event == EVENT_VALCHANGE) {
  777. int top, bottom, halfway, i;
  778. if (ctrl == ssd->editbox) {
  779. sfree(ssd->savedsession);
  780. ssd->savedsession = dlg_editbox_get(ctrl, dlg);
  781. top = ssd->sesslist.nsessions;
  782. bottom = -1;
  783. while (top-bottom > 1) {
  784. halfway = (top+bottom)/2;
  785. i = strcmp(ssd->savedsession, ssd->sesslist.sessions[halfway]);
  786. if (i <= 0 ) {
  787. top = halfway;
  788. } else {
  789. bottom = halfway;
  790. }
  791. }
  792. if (top == ssd->sesslist.nsessions) {
  793. top -= 1;
  794. }
  795. dlg_listbox_select(ssd->listbox, dlg, top);
  796. }
  797. } else if (event == EVENT_ACTION) {
  798. bool mbl = false;
  799. if (!ssd->midsession &&
  800. (ctrl == ssd->listbox ||
  801. (ssd->loadbutton && ctrl == ssd->loadbutton))) {
  802. /*
  803. * The user has double-clicked a session, or hit Load.
  804. * We must load the selected session, and then
  805. * terminate the configuration dialog _if_ there was a
  806. * double-click on the list box _and_ that session
  807. * contains a hostname.
  808. */
  809. if (load_selected_session(ssd, dlg, conf, &mbl) &&
  810. (mbl && ctrl == ssd->listbox && conf_launchable(conf))) {
  811. dlg_end(dlg, 1); /* it's all over, and succeeded */
  812. }
  813. } else if (ctrl == ssd->savebutton) {
  814. bool isdef = !strcmp(ssd->savedsession, "Default Settings");
  815. if (!ssd->savedsession[0]) {
  816. int i = dlg_listbox_index(ssd->listbox, dlg);
  817. if (i < 0) {
  818. dlg_beep(dlg);
  819. return;
  820. }
  821. isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
  822. sfree(ssd->savedsession);
  823. ssd->savedsession = dupstr(isdef ? "" :
  824. ssd->sesslist.sessions[i]);
  825. }
  826. {
  827. char *errmsg = save_settings(ssd->savedsession, conf);
  828. if (errmsg) {
  829. dlg_error_msg(dlg, errmsg);
  830. sfree(errmsg);
  831. }
  832. }
  833. get_sesslist(&ssd->sesslist, false);
  834. get_sesslist(&ssd->sesslist, true);
  835. dlg_refresh(ssd->editbox, dlg);
  836. dlg_refresh(ssd->listbox, dlg);
  837. } else if (!ssd->midsession &&
  838. ssd->delbutton && ctrl == ssd->delbutton) {
  839. int i = dlg_listbox_index(ssd->listbox, dlg);
  840. if (i <= 0) {
  841. dlg_beep(dlg);
  842. } else {
  843. del_settings(ssd->sesslist.sessions[i]);
  844. get_sesslist(&ssd->sesslist, false);
  845. get_sesslist(&ssd->sesslist, true);
  846. dlg_refresh(ssd->listbox, dlg);
  847. }
  848. } else if (ctrl == ssd->okbutton) {
  849. if (ssd->midsession) {
  850. /* In a mid-session Change Settings, Apply is always OK. */
  851. dlg_end(dlg, 1);
  852. return;
  853. }
  854. /*
  855. * Annoying special case. If the `Open' button is
  856. * pressed while no host name is currently set, _and_
  857. * the session list previously had the focus, _and_
  858. * there was a session selected in that which had a
  859. * valid host name in it, then load it and go.
  860. */
  861. if (dlg_last_focused(ctrl, dlg) == ssd->listbox &&
  862. !conf_launchable(conf) && dlg_is_visible(ssd->listbox, dlg)) {
  863. Conf *conf2 = conf_new();
  864. bool mbl = false;
  865. if (!load_selected_session(ssd, dlg, conf2, &mbl)) {
  866. dlg_beep(dlg);
  867. conf_free(conf2);
  868. return;
  869. }
  870. /* If at this point we have a valid session, go! */
  871. if (mbl && conf_launchable(conf2)) {
  872. conf_copy_into(conf, conf2);
  873. dlg_end(dlg, 1);
  874. } else
  875. dlg_beep(dlg);
  876. conf_free(conf2);
  877. return;
  878. }
  879. /*
  880. * Otherwise, do the normal thing: if we have a valid
  881. * session, get going.
  882. */
  883. if (conf_launchable(conf)) {
  884. dlg_end(dlg, 1);
  885. } else
  886. dlg_beep(dlg);
  887. } else if (ctrl == ssd->cancelbutton) {
  888. dlg_end(dlg, 0);
  889. }
  890. }
  891. }
  892. struct charclass_data {
  893. dlgcontrol *listbox, *editbox, *button;
  894. };
  895. static void charclass_handler(dlgcontrol *ctrl, dlgparam *dlg,
  896. void *data, int event)
  897. {
  898. Conf *conf = (Conf *)data;
  899. struct charclass_data *ccd =
  900. (struct charclass_data *)ctrl->context.p;
  901. if (event == EVENT_REFRESH) {
  902. if (ctrl == ccd->listbox) {
  903. int i;
  904. dlg_update_start(ctrl, dlg);
  905. dlg_listbox_clear(ctrl, dlg);
  906. for (i = 0; i < 128; i++) {
  907. char str[100];
  908. sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
  909. (i >= 0x21 && i != 0x7F) ? i : ' ',
  910. conf_get_int_int(conf, CONF_wordness, i));
  911. dlg_listbox_add(ctrl, dlg, str);
  912. }
  913. dlg_update_done(ctrl, dlg);
  914. }
  915. } else if (event == EVENT_ACTION) {
  916. if (ctrl == ccd->button) {
  917. char *str;
  918. int i, n;
  919. str = dlg_editbox_get(ccd->editbox, dlg);
  920. n = atoi(str);
  921. sfree(str);
  922. for (i = 0; i < 128; i++) {
  923. if (dlg_listbox_issel(ccd->listbox, dlg, i))
  924. conf_set_int_int(conf, CONF_wordness, i, n);
  925. }
  926. dlg_refresh(ccd->listbox, dlg);
  927. }
  928. }
  929. }
  930. struct colour_data {
  931. dlgcontrol *listbox, *redit, *gedit, *bedit, *button;
  932. };
  933. /* Array of the user-visible colour names defined in the list macro in
  934. * putty.h */
  935. static const char *const colours[] = {
  936. #define CONF_COLOUR_NAME_DECL(id,name) name,
  937. CONF_COLOUR_LIST(CONF_COLOUR_NAME_DECL)
  938. #undef CONF_COLOUR_NAME_DECL
  939. };
  940. static void colour_handler(dlgcontrol *ctrl, dlgparam *dlg,
  941. void *data, int event)
  942. {
  943. Conf *conf = (Conf *)data;
  944. struct colour_data *cd =
  945. (struct colour_data *)ctrl->context.p;
  946. bool update = false, clear = false;
  947. int r, g, b;
  948. if (event == EVENT_REFRESH) {
  949. if (ctrl == cd->listbox) {
  950. int i;
  951. dlg_update_start(ctrl, dlg);
  952. dlg_listbox_clear(ctrl, dlg);
  953. for (i = 0; i < lenof(colours); i++)
  954. dlg_listbox_add(ctrl, dlg, colours[i]);
  955. dlg_update_done(ctrl, dlg);
  956. clear = true;
  957. update = true;
  958. }
  959. } else if (event == EVENT_SELCHANGE) {
  960. if (ctrl == cd->listbox) {
  961. /* The user has selected a colour. Update the RGB text. */
  962. int i = dlg_listbox_index(ctrl, dlg);
  963. if (i < 0) {
  964. clear = true;
  965. } else {
  966. clear = false;
  967. r = conf_get_int_int(conf, CONF_colours, i*3+0);
  968. g = conf_get_int_int(conf, CONF_colours, i*3+1);
  969. b = conf_get_int_int(conf, CONF_colours, i*3+2);
  970. }
  971. update = true;
  972. }
  973. } else if (event == EVENT_VALCHANGE) {
  974. if (ctrl == cd->redit || ctrl == cd->gedit || ctrl == cd->bedit) {
  975. /* The user has changed the colour using the edit boxes. */
  976. char *str;
  977. int i, cval;
  978. str = dlg_editbox_get(ctrl, dlg);
  979. cval = atoi(str);
  980. sfree(str);
  981. if (cval > 255) cval = 255;
  982. if (cval < 0) cval = 0;
  983. i = dlg_listbox_index(cd->listbox, dlg);
  984. if (i >= 0) {
  985. if (ctrl == cd->redit)
  986. conf_set_int_int(conf, CONF_colours, i*3+0, cval);
  987. else if (ctrl == cd->gedit)
  988. conf_set_int_int(conf, CONF_colours, i*3+1, cval);
  989. else if (ctrl == cd->bedit)
  990. conf_set_int_int(conf, CONF_colours, i*3+2, cval);
  991. }
  992. }
  993. } else if (event == EVENT_ACTION) {
  994. if (ctrl == cd->button) {
  995. int i = dlg_listbox_index(cd->listbox, dlg);
  996. if (i < 0) {
  997. dlg_beep(dlg);
  998. return;
  999. }
  1000. /*
  1001. * Start a colour selector, which will send us an
  1002. * EVENT_CALLBACK when it's finished and allow us to
  1003. * pick up the results.
  1004. */
  1005. dlg_coloursel_start(ctrl, dlg,
  1006. conf_get_int_int(conf, CONF_colours, i*3+0),
  1007. conf_get_int_int(conf, CONF_colours, i*3+1),
  1008. conf_get_int_int(conf, CONF_colours, i*3+2));
  1009. }
  1010. } else if (event == EVENT_CALLBACK) {
  1011. if (ctrl == cd->button) {
  1012. int i = dlg_listbox_index(cd->listbox, dlg);
  1013. /*
  1014. * Collect the results of the colour selector. Will
  1015. * return nonzero on success, or zero if the colour
  1016. * selector did nothing (user hit Cancel, for example).
  1017. */
  1018. if (dlg_coloursel_results(ctrl, dlg, &r, &g, &b)) {
  1019. conf_set_int_int(conf, CONF_colours, i*3+0, r);
  1020. conf_set_int_int(conf, CONF_colours, i*3+1, g);
  1021. conf_set_int_int(conf, CONF_colours, i*3+2, b);
  1022. clear = false;
  1023. update = true;
  1024. }
  1025. }
  1026. }
  1027. if (update) {
  1028. if (clear) {
  1029. dlg_editbox_set(cd->redit, dlg, "");
  1030. dlg_editbox_set(cd->gedit, dlg, "");
  1031. dlg_editbox_set(cd->bedit, dlg, "");
  1032. } else {
  1033. char buf[40];
  1034. sprintf(buf, "%d", r); dlg_editbox_set(cd->redit, dlg, buf);
  1035. sprintf(buf, "%d", g); dlg_editbox_set(cd->gedit, dlg, buf);
  1036. sprintf(buf, "%d", b); dlg_editbox_set(cd->bedit, dlg, buf);
  1037. }
  1038. }
  1039. }
  1040. struct ttymodes_data {
  1041. dlgcontrol *valradio, *valbox, *setbutton, *listbox;
  1042. };
  1043. static void ttymodes_handler(dlgcontrol *ctrl, dlgparam *dlg,
  1044. void *data, int event)
  1045. {
  1046. Conf *conf = (Conf *)data;
  1047. struct ttymodes_data *td =
  1048. (struct ttymodes_data *)ctrl->context.p;
  1049. if (event == EVENT_REFRESH) {
  1050. if (ctrl == td->listbox) {
  1051. char *key, *val;
  1052. dlg_update_start(ctrl, dlg);
  1053. dlg_listbox_clear(ctrl, dlg);
  1054. for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key);
  1055. val != NULL;
  1056. val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) {
  1057. char *disp = dupprintf("%s\t%s", key,
  1058. (val[0] == 'A') ? "(auto)" :
  1059. ((val[0] == 'N') ? "(don't send)"
  1060. : val+1));
  1061. dlg_listbox_add(ctrl, dlg, disp);
  1062. sfree(disp);
  1063. }
  1064. dlg_update_done(ctrl, dlg);
  1065. } else if (ctrl == td->valradio) {
  1066. dlg_radiobutton_set(ctrl, dlg, 0);
  1067. }
  1068. } else if (event == EVENT_SELCHANGE) {
  1069. if (ctrl == td->listbox) {
  1070. int ind = dlg_listbox_index(td->listbox, dlg);
  1071. char *val;
  1072. if (ind < 0) {
  1073. return; /* no item selected */
  1074. }
  1075. val = conf_get_str_str(conf, CONF_ttymodes,
  1076. conf_get_str_nthstrkey(conf, CONF_ttymodes,
  1077. ind));
  1078. assert(val != NULL);
  1079. /* Do this first to defuse side-effects on radio buttons: */
  1080. dlg_editbox_set(td->valbox, dlg, val+1);
  1081. dlg_radiobutton_set(td->valradio, dlg,
  1082. val[0] == 'A' ? 0 : (val[0] == 'N' ? 1 : 2));
  1083. }
  1084. } else if (event == EVENT_VALCHANGE) {
  1085. if (ctrl == td->valbox) {
  1086. /* If they're editing the text box, we assume they want its
  1087. * value to be used. */
  1088. dlg_radiobutton_set(td->valradio, dlg, 2);
  1089. }
  1090. } else if (event == EVENT_ACTION) {
  1091. if (ctrl == td->setbutton) {
  1092. int ind = dlg_listbox_index(td->listbox, dlg);
  1093. const char *key;
  1094. char *str, *val;
  1095. char type;
  1096. {
  1097. const char types[] = {'A', 'N', 'V'};
  1098. int button = dlg_radiobutton_get(td->valradio, dlg);
  1099. assert(button >= 0 && button < lenof(types));
  1100. type = types[button];
  1101. }
  1102. /* Construct new entry */
  1103. if (ind >= 0) {
  1104. key = conf_get_str_nthstrkey(conf, CONF_ttymodes, ind);
  1105. str = (type == 'V' ? dlg_editbox_get(td->valbox, dlg)
  1106. : dupstr(""));
  1107. val = dupprintf("%c%s", type, str);
  1108. sfree(str);
  1109. conf_set_str_str(conf, CONF_ttymodes, key, val);
  1110. sfree(val);
  1111. dlg_refresh(td->listbox, dlg);
  1112. dlg_listbox_select(td->listbox, dlg, ind);
  1113. } else {
  1114. /* Not a multisel listbox, so this means nothing selected */
  1115. dlg_beep(dlg);
  1116. }
  1117. }
  1118. }
  1119. }
  1120. struct environ_data {
  1121. dlgcontrol *varbox, *valbox, *addbutton, *rembutton, *listbox;
  1122. };
  1123. static void environ_handler(dlgcontrol *ctrl, dlgparam *dlg,
  1124. void *data, int event)
  1125. {
  1126. Conf *conf = (Conf *)data;
  1127. struct environ_data *ed =
  1128. (struct environ_data *)ctrl->context.p;
  1129. if (event == EVENT_REFRESH) {
  1130. if (ctrl == ed->listbox) {
  1131. char *key, *val;
  1132. dlg_update_start(ctrl, dlg);
  1133. dlg_listbox_clear(ctrl, dlg);
  1134. for (val = conf_get_str_strs(conf, CONF_environmt, NULL, &key);
  1135. val != NULL;
  1136. val = conf_get_str_strs(conf, CONF_environmt, key, &key)) {
  1137. char *p = dupprintf("%s\t%s", key, val);
  1138. dlg_listbox_add(ctrl, dlg, p);
  1139. sfree(p);
  1140. }
  1141. dlg_update_done(ctrl, dlg);
  1142. }
  1143. } else if (event == EVENT_ACTION) {
  1144. if (ctrl == ed->addbutton) {
  1145. char *key, *val, *str;
  1146. key = dlg_editbox_get(ed->varbox, dlg);
  1147. if (!*key) {
  1148. sfree(key);
  1149. dlg_beep(dlg);
  1150. return;
  1151. }
  1152. val = dlg_editbox_get(ed->valbox, dlg);
  1153. if (!*val) {
  1154. sfree(key);
  1155. sfree(val);
  1156. dlg_beep(dlg);
  1157. return;
  1158. }
  1159. conf_set_str_str(conf, CONF_environmt, key, val);
  1160. str = dupcat(key, "\t", val);
  1161. dlg_editbox_set(ed->varbox, dlg, "");
  1162. dlg_editbox_set(ed->valbox, dlg, "");
  1163. sfree(str);
  1164. sfree(key);
  1165. sfree(val);
  1166. dlg_refresh(ed->listbox, dlg);
  1167. } else if (ctrl == ed->rembutton) {
  1168. int i = dlg_listbox_index(ed->listbox, dlg);
  1169. if (i < 0) {
  1170. dlg_beep(dlg);
  1171. } else {
  1172. char *key, *val;
  1173. key = conf_get_str_nthstrkey(conf, CONF_environmt, i);
  1174. if (key) {
  1175. /* Populate controls with the entry we're about to delete
  1176. * for ease of editing */
  1177. val = conf_get_str_str(conf, CONF_environmt, key);
  1178. dlg_editbox_set(ed->varbox, dlg, key);
  1179. dlg_editbox_set(ed->valbox, dlg, val);
  1180. /* And delete it */
  1181. conf_del_str_str(conf, CONF_environmt, key);
  1182. }
  1183. }
  1184. dlg_refresh(ed->listbox, dlg);
  1185. }
  1186. }
  1187. }
  1188. struct portfwd_data {
  1189. dlgcontrol *addbutton, *rembutton, *listbox;
  1190. dlgcontrol *sourcebox, *destbox, *direction;
  1191. #ifndef NO_IPV6
  1192. dlgcontrol *addressfamily;
  1193. #endif
  1194. };
  1195. static void portfwd_handler(dlgcontrol *ctrl, dlgparam *dlg,
  1196. void *data, int event)
  1197. {
  1198. Conf *conf = (Conf *)data;
  1199. struct portfwd_data *pfd =
  1200. (struct portfwd_data *)ctrl->context.p;
  1201. if (event == EVENT_REFRESH) {
  1202. if (ctrl == pfd->listbox) {
  1203. char *key, *val;
  1204. dlg_update_start(ctrl, dlg);
  1205. dlg_listbox_clear(ctrl, dlg);
  1206. for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key);
  1207. val != NULL;
  1208. val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) {
  1209. char *p;
  1210. if (!strcmp(val, "D")) {
  1211. char *L;
  1212. /*
  1213. * A dynamic forwarding is stored as L12345=D or
  1214. * 6L12345=D (since it's mutually exclusive with
  1215. * L12345=anything else), but displayed as D12345
  1216. * to match the fiction that 'Local', 'Remote' and
  1217. * 'Dynamic' are three distinct modes and also to
  1218. * align with OpenSSH's command line option syntax
  1219. * that people will already be used to. So, for
  1220. * display purposes, find the L in the key string
  1221. * and turn it into a D.
  1222. */
  1223. p = dupprintf("%s\t", key);
  1224. L = strchr(p, 'L');
  1225. if (L) *L = 'D';
  1226. } else
  1227. p = dupprintf("%s\t%s", key, val);
  1228. dlg_listbox_add(ctrl, dlg, p);
  1229. sfree(p);
  1230. }
  1231. dlg_update_done(ctrl, dlg);
  1232. } else if (ctrl == pfd->direction) {
  1233. /*
  1234. * Default is Local.
  1235. */
  1236. dlg_radiobutton_set(ctrl, dlg, 0);
  1237. #ifndef NO_IPV6
  1238. } else if (ctrl == pfd->addressfamily) {
  1239. dlg_radiobutton_set(ctrl, dlg, 0);
  1240. #endif
  1241. }
  1242. } else if (event == EVENT_ACTION) {
  1243. if (ctrl == pfd->addbutton) {
  1244. const char *family, *type;
  1245. char *src, *key, *val;
  1246. int whichbutton;
  1247. #ifndef NO_IPV6
  1248. whichbutton = dlg_radiobutton_get(pfd->addressfamily, dlg);
  1249. if (whichbutton == 1)
  1250. family = "4";
  1251. else if (whichbutton == 2)
  1252. family = "6";
  1253. else
  1254. #endif
  1255. family = "";
  1256. whichbutton = dlg_radiobutton_get(pfd->direction, dlg);
  1257. if (whichbutton == 0)
  1258. type = "L";
  1259. else if (whichbutton == 1)
  1260. type = "R";
  1261. else
  1262. type = "D";
  1263. src = dlg_editbox_get(pfd->sourcebox, dlg);
  1264. if (!*src) {
  1265. dlg_error_msg(dlg, "You need to specify a source port number");
  1266. sfree(src);
  1267. return;
  1268. }
  1269. if (*type != 'D') {
  1270. val = dlg_editbox_get(pfd->destbox, dlg);
  1271. if (!*val || !host_strchr(val, ':')) {
  1272. dlg_error_msg(dlg,
  1273. "You need to specify a destination address\n"
  1274. "in the form \"host.name:port\"");
  1275. sfree(src);
  1276. sfree(val);
  1277. return;
  1278. }
  1279. } else {
  1280. type = "L";
  1281. val = dupstr("D"); /* special case */
  1282. }
  1283. key = dupcat(family, type, src);
  1284. sfree(src);
  1285. if (conf_get_str_str_opt(conf, CONF_portfwd, key)) {
  1286. dlg_error_msg(dlg, "Specified forwarding already exists");
  1287. } else {
  1288. conf_set_str_str(conf, CONF_portfwd, key, val);
  1289. }
  1290. sfree(key);
  1291. sfree(val);
  1292. dlg_refresh(pfd->listbox, dlg);
  1293. } else if (ctrl == pfd->rembutton) {
  1294. int i = dlg_listbox_index(pfd->listbox, dlg);
  1295. if (i < 0) {
  1296. dlg_beep(dlg);
  1297. } else {
  1298. char *key, *p;
  1299. const char *val;
  1300. key = conf_get_str_nthstrkey(conf, CONF_portfwd, i);
  1301. if (key) {
  1302. static const char *const afs = "A46";
  1303. static const char *const dirs = "LRD";
  1304. const char *afp;
  1305. int dir;
  1306. #ifndef NO_IPV6
  1307. int idx;
  1308. #endif
  1309. /* Populate controls with the entry we're about to delete
  1310. * for ease of editing */
  1311. p = key;
  1312. afp = strchr(afs, *p);
  1313. #ifndef NO_IPV6
  1314. idx = afp ? afp-afs : 0;
  1315. #endif
  1316. if (afp)
  1317. p++;
  1318. #ifndef NO_IPV6
  1319. dlg_radiobutton_set(pfd->addressfamily, dlg, idx);
  1320. #endif
  1321. dir = *p;
  1322. val = conf_get_str_str(conf, CONF_portfwd, key);
  1323. if (!strcmp(val, "D")) {
  1324. dir = 'D';
  1325. val = "";
  1326. }
  1327. dlg_radiobutton_set(pfd->direction, dlg,
  1328. strchr(dirs, dir) - dirs);
  1329. p++;
  1330. dlg_editbox_set(pfd->sourcebox, dlg, p);
  1331. dlg_editbox_set(pfd->destbox, dlg, val);
  1332. /* And delete it */
  1333. conf_del_str_str(conf, CONF_portfwd, key);
  1334. }
  1335. }
  1336. dlg_refresh(pfd->listbox, dlg);
  1337. }
  1338. }
  1339. }
  1340. struct manual_hostkey_data {
  1341. dlgcontrol *addbutton, *rembutton, *listbox, *keybox;
  1342. };
  1343. static void manual_hostkey_handler(dlgcontrol *ctrl, dlgparam *dlg,
  1344. void *data, int event)
  1345. {
  1346. Conf *conf = (Conf *)data;
  1347. struct manual_hostkey_data *mh =
  1348. (struct manual_hostkey_data *)ctrl->context.p;
  1349. if (event == EVENT_REFRESH) {
  1350. if (ctrl == mh->listbox) {
  1351. char *key, *val;
  1352. dlg_update_start(ctrl, dlg);
  1353. dlg_listbox_clear(ctrl, dlg);
  1354. for (val = conf_get_str_strs(conf, CONF_ssh_manual_hostkeys,
  1355. NULL, &key);
  1356. val != NULL;
  1357. val = conf_get_str_strs(conf, CONF_ssh_manual_hostkeys,
  1358. key, &key)) {
  1359. dlg_listbox_add(ctrl, dlg, key);
  1360. }
  1361. dlg_update_done(ctrl, dlg);
  1362. }
  1363. } else if (event == EVENT_ACTION) {
  1364. if (ctrl == mh->addbutton) {
  1365. char *key;
  1366. key = dlg_editbox_get(mh->keybox, dlg);
  1367. if (!*key) {
  1368. dlg_error_msg(dlg, "You need to specify a host key or "
  1369. "fingerprint");
  1370. sfree(key);
  1371. return;
  1372. }
  1373. if (!validate_manual_hostkey(key)) {
  1374. dlg_error_msg(dlg, "Host key is not in a valid format");
  1375. } else if (conf_get_str_str_opt(conf, CONF_ssh_manual_hostkeys,
  1376. key)) {
  1377. dlg_error_msg(dlg, "Specified host key is already listed");
  1378. } else {
  1379. conf_set_str_str(conf, CONF_ssh_manual_hostkeys, key, "");
  1380. }
  1381. sfree(key);
  1382. dlg_refresh(mh->listbox, dlg);
  1383. } else if (ctrl == mh->rembutton) {
  1384. int i = dlg_listbox_index(mh->listbox, dlg);
  1385. if (i < 0) {
  1386. dlg_beep(dlg);
  1387. } else {
  1388. char *key;
  1389. key = conf_get_str_nthstrkey(conf, CONF_ssh_manual_hostkeys, i);
  1390. if (key) {
  1391. dlg_editbox_set(mh->keybox, dlg, key);
  1392. /* And delete it */
  1393. conf_del_str_str(conf, CONF_ssh_manual_hostkeys, key);
  1394. }
  1395. }
  1396. dlg_refresh(mh->listbox, dlg);
  1397. }
  1398. }
  1399. }
  1400. static void clipboard_selector_handler(dlgcontrol *ctrl, dlgparam *dlg,
  1401. void *data, int event)
  1402. {
  1403. Conf *conf = (Conf *)data;
  1404. int setting = ctrl->context.i;
  1405. #ifdef NAMED_CLIPBOARDS
  1406. int strsetting = ctrl->context2.i;
  1407. #endif
  1408. static const struct {
  1409. const char *name;
  1410. int id;
  1411. } options[] = {
  1412. {"No action", CLIPUI_NONE},
  1413. {CLIPNAME_IMPLICIT, CLIPUI_IMPLICIT},
  1414. {CLIPNAME_EXPLICIT, CLIPUI_EXPLICIT},
  1415. };
  1416. if (event == EVENT_REFRESH) {
  1417. int i, val = conf_get_int(conf, setting);
  1418. dlg_update_start(ctrl, dlg);
  1419. dlg_listbox_clear(ctrl, dlg);
  1420. #ifdef NAMED_CLIPBOARDS
  1421. for (i = 0; i < lenof(options); i++)
  1422. dlg_listbox_add(ctrl, dlg, options[i].name);
  1423. if (val == CLIPUI_CUSTOM) {
  1424. const char *sval = conf_get_str(conf, strsetting);
  1425. for (i = 0; i < lenof(options); i++)
  1426. if (!strcmp(sval, options[i].name))
  1427. break; /* needs escaping */
  1428. if (i < lenof(options) || sval[0] == '=') {
  1429. char *escaped = dupcat("=", sval);
  1430. dlg_editbox_set(ctrl, dlg, escaped);
  1431. sfree(escaped);
  1432. } else {
  1433. dlg_editbox_set(ctrl, dlg, sval);
  1434. }
  1435. } else {
  1436. dlg_editbox_set(ctrl, dlg, options[0].name); /* fallback */
  1437. for (i = 0; i < lenof(options); i++)
  1438. if (val == options[i].id)
  1439. dlg_editbox_set(ctrl, dlg, options[i].name);
  1440. }
  1441. #else
  1442. for (i = 0; i < lenof(options); i++)
  1443. dlg_listbox_addwithid(ctrl, dlg, options[i].name, options[i].id);
  1444. dlg_listbox_select(ctrl, dlg, 0); /* fallback */
  1445. for (i = 0; i < lenof(options); i++)
  1446. if (val == options[i].id)
  1447. dlg_listbox_select(ctrl, dlg, i);
  1448. #endif
  1449. dlg_update_done(ctrl, dlg);
  1450. } else if (event == EVENT_SELCHANGE
  1451. #ifdef NAMED_CLIPBOARDS
  1452. || event == EVENT_VALCHANGE
  1453. #endif
  1454. ) {
  1455. #ifdef NAMED_CLIPBOARDS
  1456. char *sval = dlg_editbox_get(ctrl, dlg);
  1457. int i;
  1458. for (i = 0; i < lenof(options); i++)
  1459. if (!strcmp(sval, options[i].name)) {
  1460. conf_set_int(conf, setting, options[i].id);
  1461. conf_set_str(conf, strsetting, "");
  1462. break;
  1463. }
  1464. if (i == lenof(options)) {
  1465. conf_set_int(conf, setting, CLIPUI_CUSTOM);
  1466. if (sval[0] == '=')
  1467. sval++;
  1468. conf_set_str(conf, strsetting, sval);
  1469. }
  1470. sfree(sval);
  1471. #else
  1472. int index = dlg_listbox_index(ctrl, dlg);
  1473. if (index >= 0) {
  1474. int val = dlg_listbox_getid(ctrl, dlg, index);
  1475. conf_set_int(conf, setting, val);
  1476. }
  1477. #endif
  1478. }
  1479. }
  1480. static void clipboard_control(struct controlset *s, const char *label,
  1481. char shortcut, int percentage, HelpCtx helpctx,
  1482. int setting, int strsetting)
  1483. {
  1484. #ifdef NAMED_CLIPBOARDS
  1485. ctrl_combobox(s, label, shortcut, percentage, helpctx,
  1486. clipboard_selector_handler, I(setting), I(strsetting));
  1487. #else
  1488. /* strsetting isn't needed in this case */
  1489. ctrl_droplist(s, label, shortcut, percentage, helpctx,
  1490. clipboard_selector_handler, I(setting));
  1491. #endif
  1492. }
  1493. static void serial_parity_handler(dlgcontrol *ctrl, dlgparam *dlg,
  1494. void *data, int event)
  1495. {
  1496. static const struct {
  1497. const char *name;
  1498. int val;
  1499. } parities[] = {
  1500. {"None", SER_PAR_NONE},
  1501. {"Odd", SER_PAR_ODD},
  1502. {"Even", SER_PAR_EVEN},
  1503. {"Mark", SER_PAR_MARK},
  1504. {"Space", SER_PAR_SPACE},
  1505. };
  1506. int mask = ctrl->context.i;
  1507. int i, j;
  1508. Conf *conf = (Conf *)data;
  1509. if (event == EVENT_REFRESH) {
  1510. /* Fetching this once at the start of the function ensures we
  1511. * remember what the right value is supposed to be when
  1512. * operations below cause reentrant calls to this function. */
  1513. int oldparity = conf_get_int(conf, CONF_serparity);
  1514. dlg_update_start(ctrl, dlg);
  1515. dlg_listbox_clear(ctrl, dlg);
  1516. for (i = 0; i < lenof(parities); i++) {
  1517. if (mask & (1 << parities[i].val))
  1518. dlg_listbox_addwithid(ctrl, dlg, parities[i].name,
  1519. parities[i].val);
  1520. }
  1521. for (i = j = 0; i < lenof(parities); i++) {
  1522. if (mask & (1 << parities[i].val)) {
  1523. if (oldparity == parities[i].val) {
  1524. dlg_listbox_select(ctrl, dlg, j);
  1525. break;
  1526. }
  1527. j++;
  1528. }
  1529. }
  1530. if (i == lenof(parities)) { /* an unsupported setting was chosen */
  1531. dlg_listbox_select(ctrl, dlg, 0);
  1532. oldparity = SER_PAR_NONE;
  1533. }
  1534. dlg_update_done(ctrl, dlg);
  1535. conf_set_int(conf, CONF_serparity, oldparity); /* restore */
  1536. } else if (event == EVENT_SELCHANGE) {
  1537. int i = dlg_listbox_index(ctrl, dlg);
  1538. if (i < 0)
  1539. i = SER_PAR_NONE;
  1540. else
  1541. i = dlg_listbox_getid(ctrl, dlg, i);
  1542. conf_set_int(conf, CONF_serparity, i);
  1543. }
  1544. }
  1545. static void serial_flow_handler(dlgcontrol *ctrl, dlgparam *dlg,
  1546. void *data, int event)
  1547. {
  1548. static const struct {
  1549. const char *name;
  1550. int val;
  1551. } flows[] = {
  1552. {"None", SER_FLOW_NONE},
  1553. {"XON/XOFF", SER_FLOW_XONXOFF},
  1554. {"RTS/CTS", SER_FLOW_RTSCTS},
  1555. {"DSR/DTR", SER_FLOW_DSRDTR},
  1556. };
  1557. int mask = ctrl->context.i;
  1558. int i, j;
  1559. Conf *conf = (Conf *)data;
  1560. if (event == EVENT_REFRESH) {
  1561. /* Fetching this once at the start of the function ensures we
  1562. * remember what the right value is supposed to be when
  1563. * operations below cause reentrant calls to this function. */
  1564. int oldflow = conf_get_int(conf, CONF_serflow);
  1565. dlg_update_start(ctrl, dlg);
  1566. dlg_listbox_clear(ctrl, dlg);
  1567. for (i = 0; i < lenof(flows); i++) {
  1568. if (mask & (1 << flows[i].val))
  1569. dlg_listbox_addwithid(ctrl, dlg, flows[i].name, flows[i].val);
  1570. }
  1571. for (i = j = 0; i < lenof(flows); i++) {
  1572. if (mask & (1 << flows[i].val)) {
  1573. if (oldflow == flows[i].val) {
  1574. dlg_listbox_select(ctrl, dlg, j);
  1575. break;
  1576. }
  1577. j++;
  1578. }
  1579. }
  1580. if (i == lenof(flows)) { /* an unsupported setting was chosen */
  1581. dlg_listbox_select(ctrl, dlg, 0);
  1582. oldflow = SER_FLOW_NONE;
  1583. }
  1584. dlg_update_done(ctrl, dlg);
  1585. conf_set_int(conf, CONF_serflow, oldflow);/* restore */
  1586. } else if (event == EVENT_SELCHANGE) {
  1587. int i = dlg_listbox_index(ctrl, dlg);
  1588. if (i < 0)
  1589. i = SER_FLOW_NONE;
  1590. else
  1591. i = dlg_listbox_getid(ctrl, dlg, i);
  1592. conf_set_int(conf, CONF_serflow, i);
  1593. }
  1594. }
  1595. void proxy_type_handler(dlgcontrol *ctrl, dlgparam *dlg,
  1596. void *data, int event)
  1597. {
  1598. Conf *conf = (Conf *)data;
  1599. if (event == EVENT_REFRESH) {
  1600. /*
  1601. * We must fetch the previously configured value from the Conf
  1602. * before we start modifying the drop-down list, otherwise the
  1603. * spurious SELCHANGE we trigger in the process will overwrite
  1604. * the value we wanted to keep.
  1605. */
  1606. int proxy_type = conf_get_int(conf, CONF_proxy_type);
  1607. dlg_update_start(ctrl, dlg);
  1608. dlg_listbox_clear(ctrl, dlg);
  1609. int index_to_select = 0, current_index = 0;
  1610. #define ADD(id, title) do { \
  1611. dlg_listbox_addwithid(ctrl, dlg, title, id); \
  1612. if (id == proxy_type) \
  1613. index_to_select = current_index; \
  1614. current_index++; \
  1615. } while (0)
  1616. ADD(PROXY_NONE, "None");
  1617. ADD(PROXY_SOCKS5, "SOCKS 5");
  1618. ADD(PROXY_SOCKS4, "SOCKS 4");
  1619. ADD(PROXY_HTTP, "HTTP CONNECT");
  1620. if (ssh_proxy_supported) {
  1621. ADD(PROXY_SSH_TCPIP, "SSH to proxy and use port forwarding");
  1622. ADD(PROXY_SSH_EXEC, "SSH to proxy and execute a command");
  1623. ADD(PROXY_SSH_SUBSYSTEM, "SSH to proxy and invoke a subsystem");
  1624. }
  1625. if (ctrl->context.i & PROXY_UI_FLAG_LOCAL) {
  1626. ADD(PROXY_CMD, "Local (run a subprogram to connect)");
  1627. }
  1628. ADD(PROXY_TELNET, "'Telnet' (send an ad-hoc command)");
  1629. #undef ADD
  1630. dlg_listbox_select(ctrl, dlg, index_to_select);
  1631. dlg_update_done(ctrl, dlg);
  1632. } else if (event == EVENT_SELCHANGE) {
  1633. int i = dlg_listbox_index(ctrl, dlg);
  1634. if (i < 0)
  1635. i = AUTO;
  1636. else
  1637. i = dlg_listbox_getid(ctrl, dlg, i);
  1638. conf_set_int(conf, CONF_proxy_type, i);
  1639. }
  1640. }
  1641. static void host_ca_button_handler(dlgcontrol *ctrl, dlgparam *dp,
  1642. void *data, int event)
  1643. {
  1644. if (event == EVENT_ACTION)
  1645. show_ca_config_box(dp);
  1646. }
  1647. void setup_config_box(struct controlbox *b, bool midsession,
  1648. int protocol, int protcfginfo)
  1649. {
  1650. const struct BackendVtable *backvt;
  1651. struct controlset *s;
  1652. struct sessionsaver_data *ssd;
  1653. struct charclass_data *ccd;
  1654. struct colour_data *cd;
  1655. struct ttymodes_data *td;
  1656. struct environ_data *ed;
  1657. struct portfwd_data *pfd;
  1658. struct manual_hostkey_data *mh;
  1659. dlgcontrol *c;
  1660. bool resize_forbidden = false;
  1661. char *str;
  1662. ssd = (struct sessionsaver_data *)
  1663. ctrl_alloc_with_free(b, sizeof(struct sessionsaver_data),
  1664. sessionsaver_data_free);
  1665. memset(ssd, 0, sizeof(*ssd));
  1666. ssd->savedsession = dupstr("");
  1667. ssd->midsession = midsession;
  1668. /*
  1669. * The standard panel that appears at the bottom of all panels:
  1670. * Open, Cancel, Apply etc.
  1671. */
  1672. s = ctrl_getset(b, "", "", "");
  1673. ctrl_columns(s, 5, 20, 20, 20, 20, 20);
  1674. ssd->okbutton = ctrl_pushbutton(s,
  1675. (midsession ? "Apply" : "Open"),
  1676. (char)(midsession ? 'a' : 'o'),
  1677. HELPCTX(no_help),
  1678. sessionsaver_handler, P(ssd));
  1679. ssd->okbutton->button.isdefault = true;
  1680. ssd->okbutton->column = 3;
  1681. ssd->cancelbutton = ctrl_pushbutton(s, "Cancel", 'c', HELPCTX(no_help),
  1682. sessionsaver_handler, P(ssd));
  1683. ssd->cancelbutton->button.iscancel = true;
  1684. ssd->cancelbutton->column = 4;
  1685. /* We carefully don't close the 5-column part, so that platform-
  1686. * specific add-ons can put extra buttons alongside Open and Cancel. */
  1687. /*
  1688. * The Session panel.
  1689. */
  1690. str = dupprintf("Basic options for your %s session", appname);
  1691. ctrl_settitle(b, "Session", str);
  1692. sfree(str);
  1693. if (!midsession) {
  1694. struct hostport *hp = (struct hostport *)
  1695. ctrl_alloc(b, sizeof(struct hostport));
  1696. memset(hp, 0, sizeof(*hp));
  1697. s = ctrl_getset(b, "Session", "hostport",
  1698. "Specify the destination you want to connect to");
  1699. ctrl_columns(s, 2, 75, 25);
  1700. c = ctrl_editbox(s, HOST_BOX_TITLE, 'n', 100,
  1701. HELPCTX(session_hostname),
  1702. config_host_handler, I(0), I(0));
  1703. c->column = 0;
  1704. hp->host = c;
  1705. c = ctrl_editbox(s, PORT_BOX_TITLE, 'p', 100,
  1706. HELPCTX(session_hostname),
  1707. config_port_handler, I(0), I(0));
  1708. c->column = 1;
  1709. hp->port = c;
  1710. ctrl_columns(s, 1, 100);
  1711. c = ctrl_text(s, "Connection type:", HELPCTX(session_hostname));
  1712. ctrl_columns(s, 2, 62, 38);
  1713. c = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
  1714. HELPCTX(session_hostname),
  1715. config_protocols_handler, P(hp));
  1716. c->column = 0;
  1717. hp->protradio = c;
  1718. c->radio.buttons = sresize(c->radio.buttons, PROTOCOL_LIMIT, char *);
  1719. c->radio.shortcuts = sresize(c->radio.shortcuts, PROTOCOL_LIMIT, char);
  1720. c->radio.buttondata = sresize(c->radio.buttondata, PROTOCOL_LIMIT,
  1721. intorptr);
  1722. assert(c->radio.nbuttons == 0);
  1723. /* UI design assumes there exists at least one 'real' radio button */
  1724. assert(n_ui_backends > 0 && n_ui_backends < PROTOCOL_LIMIT);
  1725. for (size_t i = 0; i < n_ui_backends; i++) {
  1726. assert(backends[i]);
  1727. c->radio.buttons[c->radio.nbuttons] =
  1728. dupstr(backends[i]->displayname_tc);
  1729. c->radio.shortcuts[c->radio.nbuttons] =
  1730. (backends[i]->protocol == PROT_SSH ? 's' :
  1731. backends[i]->protocol == PROT_SERIAL ? 'r' :
  1732. backends[i]->protocol == PROT_RAW ? 'w' : /* FIXME unused */
  1733. NO_SHORTCUT);
  1734. c->radio.buttondata[c->radio.nbuttons] =
  1735. I(backends[i]->protocol);
  1736. c->radio.nbuttons++;
  1737. }
  1738. /* UI design assumes there exists at least one droplist entry */
  1739. assert(backends[c->radio.nbuttons]);
  1740. c->radio.buttons[c->radio.nbuttons] = dupstr("Other:");
  1741. c->radio.shortcuts[c->radio.nbuttons] = 't';
  1742. c->radio.buttondata[c->radio.nbuttons] = I(-1);
  1743. c->radio.nbuttons++;
  1744. c = ctrl_droplist(s, NULL, NO_SHORTCUT, 100,
  1745. HELPCTX(session_hostname),
  1746. config_protocols_handler, P(hp));
  1747. hp->protlist = c;
  1748. /* droplist is populated in config_protocols_handler */
  1749. c->column = 1;
  1750. /* Vertically centre the two protocol controls w.r.t. each other */
  1751. hp->protlist->align_next_to = hp->protradio;
  1752. ctrl_columns(s, 1, 100);
  1753. }
  1754. /*
  1755. * The Load/Save panel is available even in mid-session.
  1756. */
  1757. s = ctrl_getset(b, "Session", "savedsessions",
  1758. midsession ? "Save the current session settings" :
  1759. "Load, save or delete a stored session");
  1760. ctrl_columns(s, 2, 75, 25);
  1761. get_sesslist(&ssd->sesslist, true);
  1762. ssd->editbox = ctrl_editbox(s, "Saved Sessions", 'e', 100,
  1763. HELPCTX(session_saved),
  1764. sessionsaver_handler, P(ssd), P(NULL));
  1765. ssd->editbox->column = 0;
  1766. /* Reset columns so that the buttons are alongside the list, rather
  1767. * than alongside that edit box. */
  1768. ctrl_columns(s, 1, 100);
  1769. ctrl_columns(s, 2, 75, 25);
  1770. ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
  1771. HELPCTX(session_saved),
  1772. sessionsaver_handler, P(ssd));
  1773. ssd->listbox->column = 0;
  1774. ssd->listbox->listbox.height = 7;
  1775. if (!midsession) {
  1776. ssd->loadbutton = ctrl_pushbutton(s, "Load", 'l',
  1777. HELPCTX(session_saved),
  1778. sessionsaver_handler, P(ssd));
  1779. ssd->loadbutton->column = 1;
  1780. } else {
  1781. /* We can't offer the Load button mid-session, as it would allow the
  1782. * user to load and subsequently save settings they can't see. (And
  1783. * also change otherwise immutable settings underfoot; that probably
  1784. * shouldn't be a problem, but.) */
  1785. ssd->loadbutton = NULL;
  1786. }
  1787. /* "Save" button is permitted mid-session. */
  1788. ssd->savebutton = ctrl_pushbutton(s, "Save", 'v',
  1789. HELPCTX(session_saved),
  1790. sessionsaver_handler, P(ssd));
  1791. ssd->savebutton->column = 1;
  1792. if (!midsession) {
  1793. ssd->delbutton = ctrl_pushbutton(s, "Delete", 'd',
  1794. HELPCTX(session_saved),
  1795. sessionsaver_handler, P(ssd));
  1796. ssd->delbutton->column = 1;
  1797. } else {
  1798. /* Disable the Delete button mid-session too, for UI consistency. */
  1799. ssd->delbutton = NULL;
  1800. }
  1801. ctrl_columns(s, 1, 100);
  1802. s = ctrl_getset(b, "Session", "otheropts", NULL);
  1803. ctrl_radiobuttons(s, "Close window on exit:", 'x', 4,
  1804. HELPCTX(session_coe),
  1805. conf_radiobutton_handler,
  1806. I(CONF_close_on_exit),
  1807. "Always", I(FORCE_ON),
  1808. "Never", I(FORCE_OFF),
  1809. "Only on clean exit", I(AUTO));
  1810. /*
  1811. * The Session/Logging panel.
  1812. */
  1813. ctrl_settitle(b, "Session/Logging", "Options controlling session logging");
  1814. s = ctrl_getset(b, "Session/Logging", "main", NULL);
  1815. /*
  1816. * The logging buttons change depending on whether SSH packet
  1817. * logging can sensibly be available.
  1818. */
  1819. {
  1820. const char *sshlogname, *sshrawlogname;
  1821. if ((midsession && protocol == PROT_SSH) ||
  1822. (!midsession && backend_vt_from_proto(PROT_SSH))) {
  1823. sshlogname = "SSH packets";
  1824. sshrawlogname = "SSH packets and raw data";
  1825. } else {
  1826. sshlogname = NULL; /* this will disable both buttons */
  1827. sshrawlogname = NULL; /* this will just placate optimisers */
  1828. }
  1829. ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2,
  1830. HELPCTX(logging_main),
  1831. loggingbuttons_handler,
  1832. I(CONF_logtype),
  1833. "None", 't', I(LGTYP_NONE),
  1834. "Printable output", 'p', I(LGTYP_ASCII),
  1835. "All session output", 'l', I(LGTYP_DEBUG),
  1836. sshlogname, 's', I(LGTYP_PACKETS),
  1837. sshrawlogname, 'r', I(LGTYP_SSHRAW));
  1838. }
  1839. ctrl_filesel(s, "Log file name:", 'f',
  1840. NULL, true, "Select session log file name",
  1841. HELPCTX(logging_filename),
  1842. conf_filesel_handler, I(CONF_logfilename));
  1843. ctrl_text(s, "(Log file name can contain &Y, &M, &D for date,"
  1844. " &T for time, &H for host name, and &P for port number)",
  1845. HELPCTX(logging_filename));
  1846. ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1,
  1847. HELPCTX(logging_exists),
  1848. conf_radiobutton_handler, I(CONF_logxfovr),
  1849. "Always overwrite it", I(LGXF_OVR),
  1850. "Always append to the end of it", I(LGXF_APN),
  1851. "Ask the user every time", I(LGXF_ASK));
  1852. ctrl_checkbox(s, "Flush log file frequently", 'u',
  1853. HELPCTX(logging_flush),
  1854. conf_checkbox_handler, I(CONF_logflush));
  1855. ctrl_checkbox(s, "Include header", 'i',
  1856. HELPCTX(logging_header),
  1857. conf_checkbox_handler, I(CONF_logheader));
  1858. if ((midsession && protocol == PROT_SSH) ||
  1859. (!midsession && backend_vt_from_proto(PROT_SSH))) {
  1860. s = ctrl_getset(b, "Session/Logging", "ssh",
  1861. "Options specific to SSH packet logging");
  1862. ctrl_checkbox(s, "Omit known password fields", 'k',
  1863. HELPCTX(logging_ssh_omit_password),
  1864. conf_checkbox_handler, I(CONF_logomitpass));
  1865. ctrl_checkbox(s, "Omit session data", 'd',
  1866. HELPCTX(logging_ssh_omit_data),
  1867. conf_checkbox_handler, I(CONF_logomitdata));
  1868. }
  1869. /*
  1870. * The Terminal panel.
  1871. */
  1872. ctrl_settitle(b, "Terminal", "Options controlling the terminal emulation");
  1873. s = ctrl_getset(b, "Terminal", "general", "Set various terminal options");
  1874. ctrl_checkbox(s, "Auto wrap mode initially on", 'w',
  1875. HELPCTX(terminal_autowrap),
  1876. conf_checkbox_handler, I(CONF_wrap_mode));
  1877. ctrl_checkbox(s, "DEC Origin Mode initially on", 'd',
  1878. HELPCTX(terminal_decom),
  1879. conf_checkbox_handler, I(CONF_dec_om));
  1880. ctrl_checkbox(s, "Implicit CR in every LF", 'r',
  1881. HELPCTX(terminal_lfhascr),
  1882. conf_checkbox_handler, I(CONF_lfhascr));
  1883. ctrl_checkbox(s, "Implicit LF in every CR", 'f',
  1884. HELPCTX(terminal_crhaslf),
  1885. conf_checkbox_handler, I(CONF_crhaslf));
  1886. ctrl_checkbox(s, "Use background colour to erase screen", 'e',
  1887. HELPCTX(terminal_bce),
  1888. conf_checkbox_handler, I(CONF_bce));
  1889. ctrl_checkbox(s, "Enable blinking text", 'n',
  1890. HELPCTX(terminal_blink),
  1891. conf_checkbox_handler, I(CONF_blinktext));
  1892. ctrl_editbox(s, "Answerback to ^E:", 's', 100,
  1893. HELPCTX(terminal_answerback),
  1894. conf_editbox_handler, I(CONF_answerback), ED_STR);
  1895. s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options");
  1896. ctrl_radiobuttons(s, "Local echo:", 'l', 3,
  1897. HELPCTX(terminal_localecho),
  1898. conf_radiobutton_handler,I(CONF_localecho),
  1899. "Auto", I(AUTO),
  1900. "Force on", I(FORCE_ON),
  1901. "Force off", I(FORCE_OFF));
  1902. ctrl_radiobuttons(s, "Local line editing:", 't', 3,
  1903. HELPCTX(terminal_localedit),
  1904. conf_radiobutton_handler,I(CONF_localedit),
  1905. "Auto", I(AUTO),
  1906. "Force on", I(FORCE_ON),
  1907. "Force off", I(FORCE_OFF));
  1908. s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing");
  1909. ctrl_combobox(s, "Printer to send ANSI printer output to:", 'p', 100,
  1910. HELPCTX(terminal_printing),
  1911. printerbox_handler, P(NULL), P(NULL));
  1912. /*
  1913. * The Terminal/Keyboard panel.
  1914. */
  1915. ctrl_settitle(b, "Terminal/Keyboard",
  1916. "Options controlling the effects of keys");
  1917. s = ctrl_getset(b, "Terminal/Keyboard", "mappings",
  1918. "Change the sequences sent by:");
  1919. ctrl_radiobuttons(s, "The Backspace key", 'b', 2,
  1920. HELPCTX(keyboard_backspace),
  1921. conf_radiobutton_bool_handler,
  1922. I(CONF_bksp_is_delete),
  1923. "Control-H", I(0), "Control-? (127)", I(1));
  1924. ctrl_radiobuttons(s, "The Home and End keys", 'e', 2,
  1925. HELPCTX(keyboard_homeend),
  1926. conf_radiobutton_bool_handler,
  1927. I(CONF_rxvt_homeend),
  1928. "Standard", I(false), "rxvt", I(true));
  1929. ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 4,
  1930. HELPCTX(keyboard_funkeys),
  1931. conf_radiobutton_handler,
  1932. I(CONF_funky_type),
  1933. "ESC[n~", I(FUNKY_TILDE),
  1934. "Linux", I(FUNKY_LINUX),
  1935. "Xterm R6", I(FUNKY_XTERM),
  1936. "VT400", I(FUNKY_VT400),
  1937. "VT100+", I(FUNKY_VT100P),
  1938. "SCO", I(FUNKY_SCO),
  1939. "Xterm 216+", I(FUNKY_XTERM_216));
  1940. ctrl_radiobuttons(s, "Shift/Ctrl/Alt with the arrow keys", 'w', 2,
  1941. HELPCTX(keyboard_sharrow),
  1942. conf_radiobutton_handler,
  1943. I(CONF_sharrow_type),
  1944. "Ctrl toggles app mode", I(SHARROW_APPLICATION),
  1945. "xterm-style bitmap", I(SHARROW_BITMAP));
  1946. s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad",
  1947. "Application keypad settings:");
  1948. ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3,
  1949. HELPCTX(keyboard_appcursor),
  1950. conf_radiobutton_bool_handler,
  1951. I(CONF_app_cursor),
  1952. "Normal", I(0), "Application", I(1));
  1953. ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3,
  1954. HELPCTX(keyboard_appkeypad),
  1955. numeric_keypad_handler, P(NULL),
  1956. "Normal", I(0), "Application", I(1), "NetHack", I(2));
  1957. /*
  1958. * The Terminal/Bell panel.
  1959. */
  1960. ctrl_settitle(b, "Terminal/Bell",
  1961. "Options controlling the terminal bell");
  1962. s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");
  1963. ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1,
  1964. HELPCTX(bell_style),
  1965. conf_radiobutton_handler, I(CONF_beep),
  1966. "None (bell disabled)", I(BELL_DISABLED),
  1967. "Make default system alert sound", I(BELL_DEFAULT),
  1968. "Visual bell (flash window)", I(BELL_VISUAL));
  1969. s = ctrl_getset(b, "Terminal/Bell", "overload",
  1970. "Control the bell overload behaviour");
  1971. ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd',
  1972. HELPCTX(bell_overload),
  1973. conf_checkbox_handler, I(CONF_bellovl));
  1974. ctrl_editbox(s, "Over-use means this many bells...", 'm', 20,
  1975. HELPCTX(bell_overload),
  1976. conf_editbox_handler, I(CONF_bellovl_n), ED_INT);
  1977. static const struct conf_editbox_handler_type conf_editbox_tickspersec = {
  1978. .type = EDIT_FIXEDPOINT, .denominator = TICKSPERSEC};
  1979. ctrl_editbox(s, "... in this many seconds", 't', 20,
  1980. HELPCTX(bell_overload),
  1981. conf_editbox_handler, I(CONF_bellovl_t),
  1982. CP(&conf_editbox_tickspersec));
  1983. ctrl_text(s, "The bell is re-enabled after a few seconds of silence.",
  1984. HELPCTX(bell_overload));
  1985. ctrl_editbox(s, "Seconds of silence required", 's', 20,
  1986. HELPCTX(bell_overload),
  1987. conf_editbox_handler, I(CONF_bellovl_s),
  1988. CP(&conf_editbox_tickspersec));
  1989. /*
  1990. * The Terminal/Features panel.
  1991. */
  1992. ctrl_settitle(b, "Terminal/Features",
  1993. "Enabling and disabling advanced terminal features");
  1994. s = ctrl_getset(b, "Terminal/Features", "main", NULL);
  1995. ctrl_checkbox(s, "Disable application cursor keys mode", 'u',
  1996. HELPCTX(features_application),
  1997. conf_checkbox_handler, I(CONF_no_applic_c));
  1998. ctrl_checkbox(s, "Disable application keypad mode", 'k',
  1999. HELPCTX(features_application),
  2000. conf_checkbox_handler, I(CONF_no_applic_k));
  2001. ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x',
  2002. HELPCTX(features_mouse),
  2003. conf_checkbox_handler, I(CONF_no_mouse_rep));
  2004. ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's',
  2005. HELPCTX(features_resize),
  2006. conf_checkbox_handler,
  2007. I(CONF_no_remote_resize));
  2008. ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w',
  2009. HELPCTX(features_altscreen),
  2010. conf_checkbox_handler, I(CONF_no_alt_screen));
  2011. ctrl_checkbox(s, "Disable remote-controlled window title changing", 't',
  2012. HELPCTX(features_retitle),
  2013. conf_checkbox_handler,
  2014. I(CONF_no_remote_wintitle));
  2015. ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3,
  2016. HELPCTX(features_qtitle),
  2017. conf_radiobutton_handler,
  2018. I(CONF_remote_qtitle_action),
  2019. "None", I(TITLE_NONE),
  2020. "Empty string", I(TITLE_EMPTY),
  2021. "Window title", I(TITLE_REAL));
  2022. ctrl_checkbox(s, "Disable remote-controlled clearing of scrollback", 'e',
  2023. HELPCTX(features_clearscroll),
  2024. conf_checkbox_handler,
  2025. I(CONF_no_remote_clearscroll));
  2026. ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
  2027. HELPCTX(features_dbackspace),
  2028. conf_checkbox_handler, I(CONF_no_dbackspace));
  2029. ctrl_checkbox(s, "Disable remote-controlled character set configuration",
  2030. 'r', HELPCTX(features_charset), conf_checkbox_handler,
  2031. I(CONF_no_remote_charset));
  2032. ctrl_checkbox(s, "Disable Arabic text shaping",
  2033. 'l', HELPCTX(features_arabicshaping), conf_checkbox_handler,
  2034. I(CONF_no_arabicshaping));
  2035. ctrl_checkbox(s, "Disable bidirectional text display",
  2036. 'd', HELPCTX(features_bidi), conf_checkbox_handler,
  2037. I(CONF_no_bidi));
  2038. ctrl_checkbox(s, "Disable bracketed paste mode",
  2039. 'p', HELPCTX(features_bracketed_paste), conf_checkbox_handler,
  2040. I(CONF_no_bracketed_paste));
  2041. /*
  2042. * The Window panel.
  2043. */
  2044. str = dupprintf("Options controlling %s's window", appname);
  2045. ctrl_settitle(b, "Window", str);
  2046. sfree(str);
  2047. backvt = backend_vt_from_proto(protocol);
  2048. if (backvt)
  2049. resize_forbidden = (backvt->flags & BACKEND_RESIZE_FORBIDDEN);
  2050. if (!resize_forbidden || !midsession) {
  2051. s = ctrl_getset(b, "Window", "size", "Set the size of the window");
  2052. ctrl_columns(s, 2, 50, 50);
  2053. c = ctrl_editbox(s, "Columns", 'm', 100,
  2054. HELPCTX(window_size),
  2055. conf_editbox_handler, I(CONF_width), ED_INT);
  2056. c->column = 0;
  2057. c = ctrl_editbox(s, "Rows", 'r', 100,
  2058. HELPCTX(window_size),
  2059. conf_editbox_handler, I(CONF_height),ED_INT);
  2060. c->column = 1;
  2061. ctrl_columns(s, 1, 100);
  2062. }
  2063. s = ctrl_getset(b, "Window", "scrollback",
  2064. "Control the scrollback in the window");
  2065. ctrl_editbox(s, "Lines of scrollback", 's', 50,
  2066. HELPCTX(window_scrollback),
  2067. conf_editbox_handler, I(CONF_savelines), ED_INT);
  2068. ctrl_checkbox(s, "Display scrollbar", 'd',
  2069. HELPCTX(window_scrollback),
  2070. conf_checkbox_handler, I(CONF_scrollbar));
  2071. ctrl_checkbox(s, "Reset scrollback on keypress", 'k',
  2072. HELPCTX(window_scrollback),
  2073. conf_checkbox_handler, I(CONF_scroll_on_key));
  2074. ctrl_checkbox(s, "Reset scrollback on display activity", 'p',
  2075. HELPCTX(window_scrollback),
  2076. conf_checkbox_handler, I(CONF_scroll_on_disp));
  2077. ctrl_checkbox(s, "Push erased text into scrollback", 'e',
  2078. HELPCTX(window_erased),
  2079. conf_checkbox_handler,
  2080. I(CONF_erase_to_scrollback));
  2081. /*
  2082. * The Window/Appearance panel.
  2083. */
  2084. str = dupprintf("Configure the appearance of %s's window", appname);
  2085. ctrl_settitle(b, "Window/Appearance", str);
  2086. sfree(str);
  2087. s = ctrl_getset(b, "Window/Appearance", "cursor",
  2088. "Adjust the use of the cursor");
  2089. ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,
  2090. HELPCTX(appearance_cursor),
  2091. conf_radiobutton_handler,
  2092. I(CONF_cursor_type),
  2093. "Block", 'l', I(CURSOR_BLOCK),
  2094. "Underline", 'u', I(CURSOR_UNDERLINE),
  2095. "Vertical line", 'v', I(CURSOR_VERTICAL_LINE));
  2096. ctrl_checkbox(s, "Cursor blinks", 'b',
  2097. HELPCTX(appearance_cursor),
  2098. conf_checkbox_handler, I(CONF_blink_cur));
  2099. s = ctrl_getset(b, "Window/Appearance", "font",
  2100. "Font settings");
  2101. ctrl_fontsel(s, "Font used in the terminal window", 'n',
  2102. HELPCTX(appearance_font),
  2103. conf_fontsel_handler, I(CONF_font));
  2104. s = ctrl_getset(b, "Window/Appearance", "mouse",
  2105. "Adjust the use of the mouse pointer");
  2106. ctrl_checkbox(s, "Hide mouse pointer when typing in window", 'p',
  2107. HELPCTX(appearance_hidemouse),
  2108. conf_checkbox_handler, I(CONF_hide_mouseptr));
  2109. s = ctrl_getset(b, "Window/Appearance", "border",
  2110. "Adjust the window border");
  2111. ctrl_editbox(s, "Gap between text and window edge:", 'e', 20,
  2112. HELPCTX(appearance_border),
  2113. conf_editbox_handler,
  2114. I(CONF_window_border), ED_INT);
  2115. /*
  2116. * The Window/Behaviour panel.
  2117. */
  2118. str = dupprintf("Configure the behaviour of %s's window", appname);
  2119. ctrl_settitle(b, "Window/Behaviour", str);
  2120. sfree(str);
  2121. s = ctrl_getset(b, "Window/Behaviour", "title",
  2122. "Adjust the behaviour of the window title");
  2123. ctrl_editbox(s, "Window title:", 't', 100,
  2124. HELPCTX(appearance_title),
  2125. conf_editbox_handler, I(CONF_wintitle), ED_STR);
  2126. ctrl_checkbox(s, "Separate window and icon titles", 'i',
  2127. HELPCTX(appearance_title),
  2128. conf_checkbox_handler,
  2129. I(CHECKBOX_INVERT | CONF_win_name_always));
  2130. s = ctrl_getset(b, "Window/Behaviour", "main", NULL);
  2131. ctrl_checkbox(s, "Warn before closing window", 'w',
  2132. HELPCTX(behaviour_closewarn),
  2133. conf_checkbox_handler, I(CONF_warn_on_close));
  2134. /*
  2135. * The Window/Translation panel.
  2136. */
  2137. ctrl_settitle(b, "Window/Translation",
  2138. "Options controlling character set translation");
  2139. s = ctrl_getset(b, "Window/Translation", "trans",
  2140. "Character set translation");
  2141. ctrl_combobox(s, "Remote character set:",
  2142. 'r', 100, HELPCTX(translation_codepage),
  2143. codepage_handler, P(NULL), P(NULL));
  2144. s = ctrl_getset(b, "Window/Translation", "tweaks", NULL);
  2145. ctrl_checkbox(s, "Treat CJK ambiguous characters as wide", 'w',
  2146. HELPCTX(translation_cjk_ambig_wide),
  2147. conf_checkbox_handler, I(CONF_cjk_ambig_wide));
  2148. str = dupprintf("Adjust how %s handles line drawing characters", appname);
  2149. s = ctrl_getset(b, "Window/Translation", "linedraw", str);
  2150. sfree(str);
  2151. ctrl_radiobuttons(
  2152. s, "Handling of line drawing characters:", NO_SHORTCUT,1,
  2153. HELPCTX(translation_linedraw),
  2154. conf_radiobutton_handler, I(CONF_vtmode),
  2155. "Use Unicode line drawing code points",'u',I(VT_UNICODE),
  2156. "Poor man's line drawing (+, - and |)",'p',I(VT_POORMAN));
  2157. ctrl_checkbox(s, "Copy and paste line drawing characters as lqqqk",'d',
  2158. HELPCTX(selection_linedraw),
  2159. conf_checkbox_handler, I(CONF_rawcnp));
  2160. ctrl_checkbox(s, "Enable VT100 line drawing even in UTF-8 mode",'8',
  2161. HELPCTX(translation_utf8linedraw),
  2162. conf_checkbox_handler, I(CONF_utf8linedraw));
  2163. /*
  2164. * The Window/Selection panel.
  2165. */
  2166. ctrl_settitle(b, "Window/Selection", "Options controlling copy and paste");
  2167. s = ctrl_getset(b, "Window/Selection", "mouse",
  2168. "Control use of mouse");
  2169. ctrl_checkbox(s, "Shift overrides application's use of mouse", 'p',
  2170. HELPCTX(selection_shiftdrag),
  2171. conf_checkbox_handler, I(CONF_mouse_override));
  2172. ctrl_radiobuttons(s,
  2173. "Default selection mode (Alt+drag does the other one):",
  2174. NO_SHORTCUT, 2,
  2175. HELPCTX(selection_rect),
  2176. conf_radiobutton_bool_handler,
  2177. I(CONF_rect_select),
  2178. "Normal", 'n', I(false),
  2179. "Rectangular block", 'r', I(true));
  2180. s = ctrl_getset(b, "Window/Selection", "clipboards",
  2181. "Assign copy/paste actions to clipboards");
  2182. ctrl_checkbox(s, "Auto-copy selected text to "
  2183. CLIPNAME_EXPLICIT_OBJECT,
  2184. NO_SHORTCUT, HELPCTX(selection_autocopy),
  2185. conf_checkbox_handler, I(CONF_mouseautocopy));
  2186. clipboard_control(s, "Mouse paste action:", NO_SHORTCUT, 60,
  2187. HELPCTX(selection_clipactions),
  2188. CONF_mousepaste, CONF_mousepaste_custom);
  2189. clipboard_control(s, "{Ctrl,Shift} + Ins:", NO_SHORTCUT, 60,
  2190. HELPCTX(selection_clipactions),
  2191. CONF_ctrlshiftins, CONF_ctrlshiftins_custom);
  2192. clipboard_control(s, "Ctrl + Shift + {C,V}:", NO_SHORTCUT, 60,
  2193. HELPCTX(selection_clipactions),
  2194. CONF_ctrlshiftcv, CONF_ctrlshiftcv_custom);
  2195. s = ctrl_getset(b, "Window/Selection", "paste",
  2196. "Control pasting of text from clipboard to terminal");
  2197. ctrl_checkbox(s, "Permit control characters in pasted text",
  2198. NO_SHORTCUT, HELPCTX(selection_pastectrl),
  2199. conf_checkbox_handler, I(CONF_paste_controls));
  2200. /*
  2201. * The Window/Selection/Copy panel.
  2202. */
  2203. ctrl_settitle(b, "Window/Selection/Copy",
  2204. "Options controlling copying from terminal to clipboard");
  2205. s = ctrl_getset(b, "Window/Selection/Copy", "charclass",
  2206. "Classes of character that group together");
  2207. ccd = (struct charclass_data *)
  2208. ctrl_alloc(b, sizeof(struct charclass_data));
  2209. ccd->listbox = ctrl_listbox(s, "Character classes:", 'e',
  2210. HELPCTX(copy_charclasses),
  2211. charclass_handler, P(ccd));
  2212. ccd->listbox->listbox.multisel = 1;
  2213. ccd->listbox->listbox.ncols = 4;
  2214. ccd->listbox->listbox.percentages = snewn(4, int);
  2215. ccd->listbox->listbox.percentages[0] = 15;
  2216. ccd->listbox->listbox.percentages[1] = 25;
  2217. ccd->listbox->listbox.percentages[2] = 20;
  2218. ccd->listbox->listbox.percentages[3] = 40;
  2219. ctrl_columns(s, 2, 67, 33);
  2220. ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50,
  2221. HELPCTX(copy_charclasses),
  2222. charclass_handler, P(ccd), P(NULL));
  2223. ccd->editbox->column = 0;
  2224. ccd->button = ctrl_pushbutton(s, "Set", 's',
  2225. HELPCTX(copy_charclasses),
  2226. charclass_handler, P(ccd));
  2227. ccd->button->column = 1;
  2228. ctrl_columns(s, 1, 100);
  2229. /*
  2230. * The Window/Colours panel.
  2231. */
  2232. ctrl_settitle(b, "Window/Colours", "Options controlling use of colours");
  2233. s = ctrl_getset(b, "Window/Colours", "general",
  2234. "General options for colour usage");
  2235. ctrl_checkbox(s, "Allow terminal to specify ANSI colours", 'i',
  2236. HELPCTX(colours_ansi),
  2237. conf_checkbox_handler, I(CONF_ansi_colour));
  2238. ctrl_checkbox(s, "Allow terminal to use xterm 256-colour mode", '2',
  2239. HELPCTX(colours_xterm256), conf_checkbox_handler,
  2240. I(CONF_xterm_256_colour));
  2241. ctrl_checkbox(s, "Allow terminal to use 24-bit colours", '4',
  2242. HELPCTX(colours_truecolour), conf_checkbox_handler,
  2243. I(CONF_true_colour));
  2244. ctrl_radiobuttons(s, "Indicate bolded text by changing:", 'b', 3,
  2245. HELPCTX(colours_bold),
  2246. conf_radiobutton_handler, I(CONF_bold_style),
  2247. "The font", I(BOLD_STYLE_FONT),
  2248. "The colour", I(BOLD_STYLE_COLOUR),
  2249. "Both", I(BOLD_STYLE_FONT | BOLD_STYLE_COLOUR));
  2250. str = dupprintf("Adjust the precise colours %s displays", appname);
  2251. s = ctrl_getset(b, "Window/Colours", "adjust", str);
  2252. sfree(str);
  2253. ctrl_text(s, "Select a colour from the list, and then click the"
  2254. " Modify button to change its appearance.",
  2255. HELPCTX(colours_config));
  2256. ctrl_columns(s, 2, 67, 33);
  2257. cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data));
  2258. cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u',
  2259. HELPCTX(colours_config), colour_handler, P(cd));
  2260. cd->listbox->column = 0;
  2261. cd->listbox->listbox.height = 7;
  2262. c = ctrl_text(s, "RGB value:", HELPCTX(colours_config));
  2263. c->column = 1;
  2264. cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config),
  2265. colour_handler, P(cd), P(NULL));
  2266. cd->redit->column = 1;
  2267. cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config),
  2268. colour_handler, P(cd), P(NULL));
  2269. cd->gedit->column = 1;
  2270. cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config),
  2271. colour_handler, P(cd), P(NULL));
  2272. cd->bedit->column = 1;
  2273. cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config),
  2274. colour_handler, P(cd));
  2275. cd->button->column = 1;
  2276. ctrl_columns(s, 1, 100);
  2277. /*
  2278. * The Connection panel. This doesn't show up if we're in a
  2279. * non-network utility such as pterm. We tell this by being
  2280. * passed a protocol < 0.
  2281. */
  2282. if (protocol >= 0) {
  2283. ctrl_settitle(b, "Connection", "Options controlling the connection");
  2284. s = ctrl_getset(b, "Connection", "keepalive",
  2285. "Sending of null packets to keep session active");
  2286. ctrl_editbox(s, "Seconds between keepalives (0 to turn off)", 'k', 20,
  2287. HELPCTX(connection_keepalive),
  2288. conf_editbox_handler, I(CONF_ping_interval), ED_INT);
  2289. if (!midsession) {
  2290. s = ctrl_getset(b, "Connection", "tcp",
  2291. "Low-level TCP connection options");
  2292. ctrl_checkbox(s, "Disable Nagle's algorithm (TCP_NODELAY option)",
  2293. 'n', HELPCTX(connection_nodelay),
  2294. conf_checkbox_handler,
  2295. I(CONF_tcp_nodelay));
  2296. ctrl_checkbox(s, "Enable TCP keepalives (SO_KEEPALIVE option)",
  2297. 'p', HELPCTX(connection_tcpkeepalive),
  2298. conf_checkbox_handler,
  2299. I(CONF_tcp_keepalives));
  2300. #ifndef NO_IPV6
  2301. s = ctrl_getset(b, "Connection", "ipversion",
  2302. "Internet protocol version");
  2303. ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
  2304. HELPCTX(connection_ipversion),
  2305. conf_radiobutton_handler,
  2306. I(CONF_addressfamily),
  2307. "Auto", 'u', I(ADDRTYPE_UNSPEC),
  2308. "IPv4", '4', I(ADDRTYPE_IPV4),
  2309. "IPv6", '6', I(ADDRTYPE_IPV6));
  2310. #endif
  2311. {
  2312. const char *label = backend_vt_from_proto(PROT_SSH) ?
  2313. "Logical name of remote host (e.g. for SSH key lookup):" :
  2314. "Logical name of remote host:";
  2315. s = ctrl_getset(b, "Connection", "identity",
  2316. "Logical name of remote host");
  2317. ctrl_editbox(s, label, 'm', 100,
  2318. HELPCTX(connection_loghost),
  2319. conf_editbox_handler, I(CONF_loghost), ED_STR);
  2320. }
  2321. }
  2322. /*
  2323. * A sub-panel Connection/Data, containing options that
  2324. * decide on data to send to the server.
  2325. */
  2326. if (!midsession) {
  2327. ctrl_settitle(b, "Connection/Data", "Data to send to the server");
  2328. s = ctrl_getset(b, "Connection/Data", "login",
  2329. "Login details");
  2330. ctrl_editbox(s, "Auto-login username", 'u', 50,
  2331. HELPCTX(connection_username),
  2332. conf_editbox_handler, I(CONF_username), ED_STR);
  2333. {
  2334. /* We assume the local username is sufficiently stable
  2335. * to include on the dialog box. */
  2336. char *user = get_username();
  2337. char *userlabel = dupprintf("Use system username (%s)",
  2338. user ? user : "");
  2339. sfree(user);
  2340. ctrl_radiobuttons(s, "When username is not specified:", 'n', 4,
  2341. HELPCTX(connection_username_from_env),
  2342. conf_radiobutton_bool_handler,
  2343. I(CONF_username_from_env),
  2344. "Prompt", I(false),
  2345. userlabel, I(true));
  2346. sfree(userlabel);
  2347. }
  2348. s = ctrl_getset(b, "Connection/Data", "term",
  2349. "Terminal details");
  2350. ctrl_editbox(s, "Terminal-type string", 't', 50,
  2351. HELPCTX(connection_termtype),
  2352. conf_editbox_handler, I(CONF_termtype), ED_STR);
  2353. ctrl_editbox(s, "Terminal speeds", 's', 50,
  2354. HELPCTX(connection_termspeed),
  2355. conf_editbox_handler, I(CONF_termspeed), ED_STR);
  2356. s = ctrl_getset(b, "Connection/Data", "env",
  2357. "Environment variables");
  2358. ctrl_columns(s, 2, 80, 20);
  2359. ed = (struct environ_data *)
  2360. ctrl_alloc(b, sizeof(struct environ_data));
  2361. ed->varbox = ctrl_editbox(s, "Variable", 'v', 60,
  2362. HELPCTX(telnet_environ),
  2363. environ_handler, P(ed), P(NULL));
  2364. ed->varbox->column = 0;
  2365. ed->valbox = ctrl_editbox(s, "Value", 'l', 60,
  2366. HELPCTX(telnet_environ),
  2367. environ_handler, P(ed), P(NULL));
  2368. ed->valbox->column = 0;
  2369. ed->addbutton = ctrl_pushbutton(s, "Add", 'd',
  2370. HELPCTX(telnet_environ),
  2371. environ_handler, P(ed));
  2372. ed->addbutton->column = 1;
  2373. ed->rembutton = ctrl_pushbutton(s, "Remove", 'r',
  2374. HELPCTX(telnet_environ),
  2375. environ_handler, P(ed));
  2376. ed->rembutton->column = 1;
  2377. ctrl_columns(s, 1, 100);
  2378. ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
  2379. HELPCTX(telnet_environ),
  2380. environ_handler, P(ed));
  2381. ed->listbox->listbox.height = 3;
  2382. ed->listbox->listbox.ncols = 2;
  2383. ed->listbox->listbox.percentages = snewn(2, int);
  2384. ed->listbox->listbox.percentages[0] = 30;
  2385. ed->listbox->listbox.percentages[1] = 70;
  2386. }
  2387. }
  2388. if (!midsession) {
  2389. /*
  2390. * The Connection/Proxy panel.
  2391. */
  2392. ctrl_settitle(b, "Connection/Proxy",
  2393. "Options controlling proxy usage");
  2394. s = ctrl_getset(b, "Connection/Proxy", "basics", NULL);
  2395. c = ctrl_droplist(s, "Proxy type:", 't', 70,
  2396. HELPCTX(proxy_type), proxy_type_handler, I(0));
  2397. ctrl_columns(s, 2, 80, 20);
  2398. c = ctrl_editbox(s, "Proxy hostname", 'y', 100,
  2399. HELPCTX(proxy_main),
  2400. conf_editbox_handler,
  2401. I(CONF_proxy_host), ED_STR);
  2402. c->column = 0;
  2403. c = ctrl_editbox(s, "Port", 'p', 100,
  2404. HELPCTX(proxy_main),
  2405. conf_editbox_handler,
  2406. I(CONF_proxy_port),
  2407. ED_INT);
  2408. c->column = 1;
  2409. ctrl_columns(s, 1, 100);
  2410. ctrl_editbox(s, "Exclude Hosts/IPs", 'e', 100,
  2411. HELPCTX(proxy_exclude),
  2412. conf_editbox_handler,
  2413. I(CONF_proxy_exclude_list), ED_STR);
  2414. ctrl_checkbox(s, "Consider proxying local host connections", 'x',
  2415. HELPCTX(proxy_exclude),
  2416. conf_checkbox_handler,
  2417. I(CONF_even_proxy_localhost));
  2418. ctrl_radiobuttons(s, "Do DNS name lookup at proxy end:", 'd', 3,
  2419. HELPCTX(proxy_dns),
  2420. conf_radiobutton_handler,
  2421. I(CONF_proxy_dns),
  2422. "No", I(FORCE_OFF),
  2423. "Auto", I(AUTO),
  2424. "Yes", I(FORCE_ON));
  2425. ctrl_editbox(s, "Username", 'u', 60,
  2426. HELPCTX(proxy_auth),
  2427. conf_editbox_handler,
  2428. I(CONF_proxy_username), ED_STR);
  2429. c = ctrl_editbox(s, "Password", 'w', 60,
  2430. HELPCTX(proxy_auth),
  2431. conf_editbox_handler,
  2432. I(CONF_proxy_password), ED_STR);
  2433. c->editbox.password = true;
  2434. ctrl_editbox(s, "Command to send to proxy (for some types)", 'm', 100,
  2435. HELPCTX(proxy_command),
  2436. conf_editbox_handler,
  2437. I(CONF_proxy_telnet_command), ED_STR);
  2438. ctrl_radiobuttons(s, "Print proxy diagnostics "
  2439. "in the terminal window", 'r', 5,
  2440. HELPCTX(proxy_logging),
  2441. conf_radiobutton_handler,
  2442. I(CONF_proxy_log_to_term),
  2443. "No", I(FORCE_OFF),
  2444. "Yes", I(FORCE_ON),
  2445. "Only until session starts", I(AUTO));
  2446. }
  2447. /*
  2448. * Each per-protocol configuration GUI panel is conditionally
  2449. * displayed. We don't display it if this binary doesn't contain a
  2450. * backend for its protocol at all; we don't display it if we're
  2451. * already in mid-session with a different protocol selected; and
  2452. * even if we _do_ have this protocol selected, we don't display
  2453. * the panel if the protocol doesn't permit any mid-session
  2454. * reconfiguration anyway.
  2455. */
  2456. #define DISPLAY_RECONFIGURABLE_PROTOCOL(which_proto) \
  2457. (backend_vt_from_proto(which_proto) && \
  2458. (!midsession || protocol == (which_proto)))
  2459. #define DISPLAY_NON_RECONFIGURABLE_PROTOCOL(which_proto) \
  2460. (backend_vt_from_proto(which_proto) && !midsession)
  2461. if (DISPLAY_RECONFIGURABLE_PROTOCOL(PROT_SSH) ||
  2462. DISPLAY_RECONFIGURABLE_PROTOCOL(PROT_SSHCONN)) {
  2463. /*
  2464. * The Connection/SSH panel.
  2465. */
  2466. ctrl_settitle(b, "Connection/SSH",
  2467. "Options controlling SSH connections");
  2468. /* SSH-1 or connection-sharing downstream */
  2469. if (midsession && (protcfginfo == 1 || protcfginfo == -1)) {
  2470. s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL);
  2471. ctrl_text(s, "Nothing on this panel may be reconfigured in mid-"
  2472. "session; it is only here so that sub-panels of it can "
  2473. "exist without looking strange.", HELPCTX(no_help));
  2474. }
  2475. if (!midsession) {
  2476. s = ctrl_getset(b, "Connection/SSH", "data",
  2477. "Data to send to the server");
  2478. ctrl_editbox(s, "Remote command:", 'r', 100,
  2479. HELPCTX(ssh_command),
  2480. conf_editbox_handler, I(CONF_remote_cmd), ED_STR);
  2481. s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
  2482. ctrl_checkbox(s, "Don't start a shell or command at all", 'n',
  2483. HELPCTX(ssh_noshell),
  2484. conf_checkbox_handler,
  2485. I(CONF_ssh_no_shell));
  2486. }
  2487. if (!midsession || !(protcfginfo == 1 || protcfginfo == -1)) {
  2488. s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
  2489. ctrl_checkbox(s, "Enable compression", 'e',
  2490. HELPCTX(ssh_compress),
  2491. conf_checkbox_handler,
  2492. I(CONF_compression));
  2493. }
  2494. if (!midsession) {
  2495. s = ctrl_getset(b, "Connection/SSH", "sharing", "Sharing an SSH connection between PuTTY tools");
  2496. ctrl_checkbox(s, "Share SSH connections if possible", 's',
  2497. HELPCTX(ssh_share),
  2498. conf_checkbox_handler,
  2499. I(CONF_ssh_connection_sharing));
  2500. ctrl_text(s, "Permitted roles in a shared connection:",
  2501. HELPCTX(ssh_share));
  2502. ctrl_checkbox(s, "Upstream (connecting to the real server)", 'u',
  2503. HELPCTX(ssh_share),
  2504. conf_checkbox_handler,
  2505. I(CONF_ssh_connection_sharing_upstream));
  2506. ctrl_checkbox(s, "Downstream (connecting to the upstream PuTTY)", 'd',
  2507. HELPCTX(ssh_share),
  2508. conf_checkbox_handler,
  2509. I(CONF_ssh_connection_sharing_downstream));
  2510. }
  2511. if (!midsession) {
  2512. s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
  2513. ctrl_radiobuttons(s, "SSH protocol version:", NO_SHORTCUT, 2,
  2514. HELPCTX(ssh_protocol),
  2515. conf_radiobutton_handler,
  2516. I(CONF_sshprot),
  2517. "2", '2', I(3),
  2518. "1 (INSECURE)", '1', I(0));
  2519. }
  2520. /*
  2521. * The Connection/SSH/Kex panel. (Owing to repeat key
  2522. * exchange, much of this is meaningful in mid-session _if_
  2523. * we're using SSH-2 and are not a connection-sharing
  2524. * downstream, or haven't decided yet.)
  2525. */
  2526. if (protcfginfo != 1 && protcfginfo != -1) {
  2527. ctrl_settitle(b, "Connection/SSH/Kex",
  2528. "Options controlling SSH key exchange");
  2529. s = ctrl_getset(b, "Connection/SSH/Kex", "main",
  2530. "Key exchange algorithm options");
  2531. c = ctrl_draglist(s, "Algorithm selection policy:", 's',
  2532. HELPCTX(ssh_kexlist),
  2533. kexlist_handler, P(NULL));
  2534. c->listbox.height = KEX_MAX;
  2535. #ifndef NO_GSSAPI
  2536. ctrl_checkbox(s, "Attempt GSSAPI key exchange",
  2537. 'k', HELPCTX(ssh_gssapi),
  2538. conf_checkbox_handler,
  2539. I(CONF_try_gssapi_kex));
  2540. #endif
  2541. s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",
  2542. "Options controlling key re-exchange");
  2543. ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,
  2544. HELPCTX(ssh_kex_repeat),
  2545. conf_editbox_handler,
  2546. I(CONF_ssh_rekey_time),
  2547. ED_INT);
  2548. #ifndef NO_GSSAPI
  2549. ctrl_editbox(s, "Minutes between GSS checks (0 for never)", NO_SHORTCUT, 20,
  2550. HELPCTX(ssh_kex_repeat),
  2551. conf_editbox_handler,
  2552. I(CONF_gssapirekey),
  2553. ED_INT);
  2554. #endif
  2555. ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'x', 20,
  2556. HELPCTX(ssh_kex_repeat),
  2557. conf_editbox_handler,
  2558. I(CONF_ssh_rekey_data),
  2559. ED_STR);
  2560. ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",
  2561. HELPCTX(ssh_kex_repeat));
  2562. }
  2563. /*
  2564. * The 'Connection/SSH/Host keys' panel.
  2565. */
  2566. if (protcfginfo != 1 && protcfginfo != -1) {
  2567. ctrl_settitle(b, "Connection/SSH/Host keys",
  2568. "Options controlling SSH host keys");
  2569. s = ctrl_getset(b, "Connection/SSH/Host keys", "main",
  2570. "Host key algorithm preference");
  2571. c = ctrl_draglist(s, "Algorithm selection policy:", 's',
  2572. HELPCTX(ssh_hklist),
  2573. hklist_handler, P(NULL));
  2574. c->listbox.height = 5;
  2575. ctrl_checkbox(s, "Prefer algorithms for which a host key is known",
  2576. 'p', HELPCTX(ssh_hk_known), conf_checkbox_handler,
  2577. I(CONF_ssh_prefer_known_hostkeys));
  2578. }
  2579. /*
  2580. * Manual host key configuration is irrelevant mid-session,
  2581. * as we enforce that the host key for rekeys is the
  2582. * same as that used at the start of the session.
  2583. */
  2584. if (!midsession) {
  2585. s = ctrl_getset(b, "Connection/SSH/Host keys", "hostkeys",
  2586. "Manually configure host keys for this connection");
  2587. ctrl_columns(s, 2, 75, 25);
  2588. c = ctrl_text(s, "Host keys or fingerprints to accept:",
  2589. HELPCTX(ssh_kex_manual_hostkeys));
  2590. c->column = 0;
  2591. /* You want to select from the list, _then_ hit Remove. So
  2592. * tab order should be that way round. */
  2593. mh = (struct manual_hostkey_data *)
  2594. ctrl_alloc(b,sizeof(struct manual_hostkey_data));
  2595. mh->rembutton = ctrl_pushbutton(s, "Remove", 'r',
  2596. HELPCTX(ssh_kex_manual_hostkeys),
  2597. manual_hostkey_handler, P(mh));
  2598. mh->rembutton->column = 1;
  2599. mh->rembutton->delay_taborder = true;
  2600. mh->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
  2601. HELPCTX(ssh_kex_manual_hostkeys),
  2602. manual_hostkey_handler, P(mh));
  2603. /* This list box can't be very tall, because there's not
  2604. * much room in the pane on Windows at least. This makes
  2605. * it become really unhelpful if a horizontal scrollbar
  2606. * appears, so we suppress that. */
  2607. mh->listbox->listbox.height = 2;
  2608. mh->listbox->listbox.hscroll = false;
  2609. ctrl_tabdelay(s, mh->rembutton);
  2610. mh->keybox = ctrl_editbox(s, "Key", 'k', 80,
  2611. HELPCTX(ssh_kex_manual_hostkeys),
  2612. manual_hostkey_handler, P(mh), P(NULL));
  2613. mh->keybox->column = 0;
  2614. mh->addbutton = ctrl_pushbutton(s, "Add key", 'y',
  2615. HELPCTX(ssh_kex_manual_hostkeys),
  2616. manual_hostkey_handler, P(mh));
  2617. mh->addbutton->column = 1;
  2618. ctrl_columns(s, 1, 100);
  2619. }
  2620. /*
  2621. * But there's no reason not to forbid access to the host CA
  2622. * configuration box, which is common across sessions in any
  2623. * case.
  2624. */
  2625. s = ctrl_getset(b, "Connection/SSH/Host keys", "ca",
  2626. "Configure trusted certification authorities");
  2627. c = ctrl_pushbutton(s, "Configure host CAs", NO_SHORTCUT,
  2628. HELPCTX(ssh_kex_cert),
  2629. host_ca_button_handler, I(0));
  2630. if (!midsession || !(protcfginfo == 1 || protcfginfo == -1)) {
  2631. /*
  2632. * The Connection/SSH/Cipher panel.
  2633. */
  2634. ctrl_settitle(b, "Connection/SSH/Cipher",
  2635. "Options controlling SSH encryption");
  2636. s = ctrl_getset(b, "Connection/SSH/Cipher",
  2637. "encryption", "Encryption options");
  2638. c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
  2639. HELPCTX(ssh_ciphers),
  2640. cipherlist_handler, P(NULL));
  2641. c->listbox.height = 6;
  2642. ctrl_checkbox(s, "Enable legacy use of single-DES in SSH-2", 'i',
  2643. HELPCTX(ssh_ciphers),
  2644. conf_checkbox_handler,
  2645. I(CONF_ssh2_des_cbc));
  2646. }
  2647. if (!midsession) {
  2648. /*
  2649. * The Connection/SSH/Auth panel.
  2650. */
  2651. ctrl_settitle(b, "Connection/SSH/Auth",
  2652. "Options controlling SSH authentication");
  2653. s = ctrl_getset(b, "Connection/SSH/Auth", "main", NULL);
  2654. ctrl_checkbox(s, "Display pre-authentication banner (SSH-2 only)",
  2655. 'd', HELPCTX(ssh_auth_banner),
  2656. conf_checkbox_handler,
  2657. I(CONF_ssh_show_banner));
  2658. ctrl_checkbox(s, "Bypass authentication entirely (SSH-2 only)", 'b',
  2659. HELPCTX(ssh_auth_bypass),
  2660. conf_checkbox_handler,
  2661. I(CONF_ssh_no_userauth));
  2662. ctrl_checkbox(s, "Disconnect if authentication succeeds trivially",
  2663. 'n', HELPCTX(ssh_no_trivial_userauth),
  2664. conf_checkbox_handler,
  2665. I(CONF_ssh_no_trivial_userauth));
  2666. s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
  2667. "Authentication methods");
  2668. ctrl_checkbox(s, "Attempt authentication using Pageant", 'p',
  2669. HELPCTX(ssh_auth_pageant),
  2670. conf_checkbox_handler,
  2671. I(CONF_tryagent));
  2672. ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH-1)", 'm',
  2673. HELPCTX(ssh_auth_tis),
  2674. conf_checkbox_handler,
  2675. I(CONF_try_tis_auth));
  2676. ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH-2)",
  2677. 'i', HELPCTX(ssh_auth_ki),
  2678. conf_checkbox_handler,
  2679. I(CONF_try_ki_auth));
  2680. s = ctrl_getset(b, "Connection/SSH/Auth", "aux",
  2681. "Other authentication-related options");
  2682. ctrl_checkbox(s, "Allow agent forwarding", 'f',
  2683. HELPCTX(ssh_auth_agentfwd),
  2684. conf_checkbox_handler, I(CONF_agentfwd));
  2685. ctrl_checkbox(s, "Allow attempted changes of username in SSH-2", NO_SHORTCUT,
  2686. HELPCTX(ssh_auth_changeuser),
  2687. conf_checkbox_handler,
  2688. I(CONF_change_username));
  2689. ctrl_settitle(b, "Connection/SSH/Auth/Credentials",
  2690. "Credentials to authenticate with");
  2691. s = ctrl_getset(b, "Connection/SSH/Auth/Credentials", "publickey",
  2692. "Public-key authentication");
  2693. ctrl_filesel(s, "Private key file for authentication:", 'k',
  2694. FILTER_KEY_FILES, false, "Select private key file",
  2695. HELPCTX(ssh_auth_privkey),
  2696. conf_filesel_handler, I(CONF_keyfile));
  2697. ctrl_filesel(s, "Certificate to use with the private key "
  2698. "(optional):", 'e',
  2699. NULL, false, "Select certificate file",
  2700. HELPCTX(ssh_auth_cert),
  2701. conf_filesel_handler, I(CONF_detached_cert));
  2702. s = ctrl_getset(b, "Connection/SSH/Auth/Credentials", "plugin",
  2703. "Plugin to provide authentication responses");
  2704. ctrl_editbox(s, "Plugin command to run", NO_SHORTCUT, 100,
  2705. HELPCTX(ssh_auth_plugin),
  2706. conf_editbox_handler, I(CONF_auth_plugin), ED_STR);
  2707. #ifndef NO_GSSAPI
  2708. /*
  2709. * Connection/SSH/Auth/GSSAPI, which sadly won't fit on
  2710. * the main Auth panel.
  2711. */
  2712. ctrl_settitle(b, "Connection/SSH/Auth/GSSAPI",
  2713. "Options controlling GSSAPI authentication");
  2714. s = ctrl_getset(b, "Connection/SSH/Auth/GSSAPI", "gssapi", NULL);
  2715. ctrl_checkbox(s, "Attempt GSSAPI authentication (SSH-2 only)",
  2716. 't', HELPCTX(ssh_gssapi),
  2717. conf_checkbox_handler,
  2718. I(CONF_try_gssapi_auth));
  2719. ctrl_checkbox(s, "Attempt GSSAPI key exchange (SSH-2 only)",
  2720. 'k', HELPCTX(ssh_gssapi),
  2721. conf_checkbox_handler,
  2722. I(CONF_try_gssapi_kex));
  2723. ctrl_checkbox(s, "Allow GSSAPI credential delegation", 'l',
  2724. HELPCTX(ssh_gssapi_delegation),
  2725. conf_checkbox_handler,
  2726. I(CONF_gssapifwd));
  2727. /*
  2728. * GSSAPI library selection.
  2729. */
  2730. if (ngsslibs > 1) {
  2731. c = ctrl_draglist(s, "Preference order for GSSAPI libraries:",
  2732. 'p', HELPCTX(ssh_gssapi_libraries),
  2733. gsslist_handler, P(NULL));
  2734. c->listbox.height = ngsslibs;
  2735. /*
  2736. * I currently assume that if more than one GSS
  2737. * library option is available, then one of them is
  2738. * 'user-supplied' and so we should present the
  2739. * following file selector. This is at least half-
  2740. * reasonable, because if we're using statically
  2741. * linked GSSAPI then there will only be one option
  2742. * and no way to load from a user-supplied library,
  2743. * whereas if we're using dynamic libraries then
  2744. * there will almost certainly be some default
  2745. * option in addition to a user-supplied path. If
  2746. * anyone ever ports PuTTY to a system on which
  2747. * dynamic-library GSSAPI is available but there is
  2748. * absolutely no consensus on where to keep the
  2749. * libraries, there'll need to be a flag alongside
  2750. * ngsslibs to control whether the file selector is
  2751. * displayed.
  2752. */
  2753. ctrl_filesel(s, "User-supplied GSSAPI library path:", 's',
  2754. FILTER_DYNLIB_FILES, false, "Select library file",
  2755. HELPCTX(ssh_gssapi_libraries),
  2756. conf_filesel_handler,
  2757. I(CONF_ssh_gss_custom));
  2758. }
  2759. #endif
  2760. }
  2761. if (!midsession) {
  2762. /*
  2763. * The Connection/SSH/TTY panel.
  2764. */
  2765. ctrl_settitle(b, "Connection/SSH/TTY", "Remote terminal settings");
  2766. s = ctrl_getset(b, "Connection/SSH/TTY", "sshtty", NULL);
  2767. ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
  2768. HELPCTX(ssh_nopty),
  2769. conf_checkbox_handler,
  2770. I(CONF_nopty));
  2771. s = ctrl_getset(b, "Connection/SSH/TTY", "ttymodes",
  2772. "Terminal modes");
  2773. td = (struct ttymodes_data *)
  2774. ctrl_alloc(b, sizeof(struct ttymodes_data));
  2775. ctrl_text(s, "Terminal modes to send:", HELPCTX(ssh_ttymodes));
  2776. td->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
  2777. HELPCTX(ssh_ttymodes),
  2778. ttymodes_handler, P(td));
  2779. td->listbox->listbox.height = 8;
  2780. td->listbox->listbox.ncols = 2;
  2781. td->listbox->listbox.percentages = snewn(2, int);
  2782. td->listbox->listbox.percentages[0] = 40;
  2783. td->listbox->listbox.percentages[1] = 60;
  2784. ctrl_columns(s, 2, 75, 25);
  2785. c = ctrl_text(s, "For selected mode, send:", HELPCTX(ssh_ttymodes));
  2786. c->column = 0;
  2787. td->setbutton = ctrl_pushbutton(s, "Set", 's',
  2788. HELPCTX(ssh_ttymodes),
  2789. ttymodes_handler, P(td));
  2790. td->setbutton->column = 1;
  2791. td->setbutton->delay_taborder = true;
  2792. ctrl_columns(s, 1, 100); /* column break */
  2793. /* Bit of a hack to get the value radio buttons and
  2794. * edit-box on the same row. */
  2795. ctrl_columns(s, 2, 75, 25);
  2796. td->valradio = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
  2797. HELPCTX(ssh_ttymodes),
  2798. ttymodes_handler, P(td),
  2799. "Auto", NO_SHORTCUT, P(NULL),
  2800. "Nothing", NO_SHORTCUT, P(NULL),
  2801. "This:", NO_SHORTCUT, P(NULL));
  2802. td->valradio->column = 0;
  2803. td->valbox = ctrl_editbox(s, NULL, NO_SHORTCUT, 100,
  2804. HELPCTX(ssh_ttymodes),
  2805. ttymodes_handler, P(td), P(NULL));
  2806. td->valbox->column = 1;
  2807. td->valbox->align_next_to = td->valradio;
  2808. ctrl_tabdelay(s, td->setbutton);
  2809. }
  2810. if (!midsession) {
  2811. /*
  2812. * The Connection/SSH/X11 panel.
  2813. */
  2814. ctrl_settitle(b, "Connection/SSH/X11",
  2815. "Options controlling SSH X11 forwarding");
  2816. s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding");
  2817. ctrl_checkbox(s, "Enable X11 forwarding", 'e',
  2818. HELPCTX(ssh_tunnels_x11),
  2819. conf_checkbox_handler,I(CONF_x11_forward));
  2820. ctrl_editbox(s, "X display location", 'x', 50,
  2821. HELPCTX(ssh_tunnels_x11),
  2822. conf_editbox_handler, I(CONF_x11_display), ED_STR);
  2823. ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
  2824. HELPCTX(ssh_tunnels_x11auth),
  2825. conf_radiobutton_handler,
  2826. I(CONF_x11_auth),
  2827. "MIT-Magic-Cookie-1", I(X11_MIT),
  2828. "XDM-Authorization-1", I(X11_XDM));
  2829. }
  2830. /*
  2831. * The Tunnels panel _is_ still available in mid-session.
  2832. */
  2833. ctrl_settitle(b, "Connection/SSH/Tunnels",
  2834. "Options controlling SSH port forwarding");
  2835. s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
  2836. "Port forwarding");
  2837. ctrl_checkbox(s, "Local ports accept connections from other hosts",'t',
  2838. HELPCTX(ssh_tunnels_portfwd_localhost),
  2839. conf_checkbox_handler,
  2840. I(CONF_lport_acceptall));
  2841. ctrl_checkbox(s, "Remote ports do the same (SSH-2 only)", 'p',
  2842. HELPCTX(ssh_tunnels_portfwd_localhost),
  2843. conf_checkbox_handler,
  2844. I(CONF_rport_acceptall));
  2845. ctrl_columns(s, 3, 55, 20, 25);
  2846. c = ctrl_text(s, "Forwarded ports:", HELPCTX(ssh_tunnels_portfwd));
  2847. c->column = COLUMN_FIELD(0,2);
  2848. /* You want to select from the list, _then_ hit Remove. So tab order
  2849. * should be that way round. */
  2850. pfd = (struct portfwd_data *)ctrl_alloc(b,sizeof(struct portfwd_data));
  2851. pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r',
  2852. HELPCTX(ssh_tunnels_portfwd),
  2853. portfwd_handler, P(pfd));
  2854. pfd->rembutton->column = 2;
  2855. pfd->rembutton->delay_taborder = true;
  2856. pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
  2857. HELPCTX(ssh_tunnels_portfwd),
  2858. portfwd_handler, P(pfd));
  2859. pfd->listbox->listbox.height = 3;
  2860. pfd->listbox->listbox.ncols = 2;
  2861. pfd->listbox->listbox.percentages = snewn(2, int);
  2862. pfd->listbox->listbox.percentages[0] = 20;
  2863. pfd->listbox->listbox.percentages[1] = 80;
  2864. ctrl_tabdelay(s, pfd->rembutton);
  2865. ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd));
  2866. /* You want to enter source, destination and type, _then_ hit Add.
  2867. * Again, we adjust the tab order to reflect this. */
  2868. pfd->addbutton = ctrl_pushbutton(s, "Add", 'd',
  2869. HELPCTX(ssh_tunnels_portfwd),
  2870. portfwd_handler, P(pfd));
  2871. pfd->addbutton->column = 2;
  2872. pfd->addbutton->delay_taborder = true;
  2873. pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40,
  2874. HELPCTX(ssh_tunnels_portfwd),
  2875. portfwd_handler, P(pfd), P(NULL));
  2876. pfd->sourcebox->column = 0;
  2877. pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67,
  2878. HELPCTX(ssh_tunnels_portfwd),
  2879. portfwd_handler, P(pfd), P(NULL));
  2880. pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
  2881. HELPCTX(ssh_tunnels_portfwd),
  2882. portfwd_handler, P(pfd),
  2883. "Local", 'l', P(NULL),
  2884. "Remote", 'm', P(NULL),
  2885. "Dynamic", 'y', P(NULL));
  2886. #ifndef NO_IPV6
  2887. pfd->addressfamily =
  2888. ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
  2889. HELPCTX(ssh_tunnels_portfwd_ipversion),
  2890. portfwd_handler, P(pfd),
  2891. "Auto", 'u', I(ADDRTYPE_UNSPEC),
  2892. "IPv4", '4', I(ADDRTYPE_IPV4),
  2893. "IPv6", '6', I(ADDRTYPE_IPV6));
  2894. #endif
  2895. ctrl_tabdelay(s, pfd->addbutton);
  2896. ctrl_columns(s, 1, 100);
  2897. if (!midsession) {
  2898. /*
  2899. * The Connection/SSH/Bugs panels.
  2900. */
  2901. ctrl_settitle(b, "Connection/SSH/Bugs",
  2902. "Workarounds for SSH server bugs");
  2903. s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
  2904. "Detection of known bugs in SSH servers");
  2905. ctrl_droplist(s, "Chokes on SSH-2 ignore messages", '2', 20,
  2906. HELPCTX(ssh_bugs_ignore2),
  2907. sshbug_handler, I(CONF_sshbug_ignore2));
  2908. ctrl_droplist(s, "Handles SSH-2 key re-exchange badly", 'k', 20,
  2909. HELPCTX(ssh_bugs_rekey2),
  2910. sshbug_handler, I(CONF_sshbug_rekey2));
  2911. ctrl_droplist(s, "Chokes on PuTTY's SSH-2 'winadj' requests", 'j',
  2912. 20, HELPCTX(ssh_bugs_winadj),
  2913. sshbug_handler, I(CONF_sshbug_winadj));
  2914. ctrl_droplist(s, "Replies to requests on closed channels", 'q', 20,
  2915. HELPCTX(ssh_bugs_chanreq),
  2916. sshbug_handler, I(CONF_sshbug_chanreq));
  2917. ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20,
  2918. HELPCTX(ssh_bugs_maxpkt2),
  2919. sshbug_handler, I(CONF_sshbug_maxpkt2));
  2920. s = ctrl_getset(b, "Connection/SSH/Bugs", "manual",
  2921. "Manually enabled workarounds");
  2922. ctrl_droplist(s, "Discards data sent before its greeting", 'd', 20,
  2923. HELPCTX(ssh_bugs_dropstart),
  2924. sshbug_handler_manual_only,
  2925. I(CONF_sshbug_dropstart));
  2926. ctrl_droplist(s, "Chokes on PuTTY's full KEXINIT", 'p', 20,
  2927. HELPCTX(ssh_bugs_filter_kexinit),
  2928. sshbug_handler_manual_only,
  2929. I(CONF_sshbug_filter_kexinit));
  2930. ctrl_settitle(b, "Connection/SSH/More bugs",
  2931. "Further workarounds for SSH server bugs");
  2932. s = ctrl_getset(b, "Connection/SSH/More bugs", "main",
  2933. "Detection of known bugs in SSH servers");
  2934. ctrl_droplist(s, "Old RSA/SHA2 cert algorithm naming", 'l', 20,
  2935. HELPCTX(ssh_bugs_rsa_sha2_cert_userauth),
  2936. sshbug_handler,
  2937. I(CONF_sshbug_rsa_sha2_cert_userauth));
  2938. ctrl_droplist(s, "Requires padding on SSH-2 RSA signatures", 'p', 20,
  2939. HELPCTX(ssh_bugs_rsapad2),
  2940. sshbug_handler, I(CONF_sshbug_rsapad2));
  2941. ctrl_droplist(s, "Only supports pre-RFC4419 SSH-2 DH GEX", 'd', 20,
  2942. HELPCTX(ssh_bugs_oldgex2),
  2943. sshbug_handler, I(CONF_sshbug_oldgex2));
  2944. ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20,
  2945. HELPCTX(ssh_bugs_hmac2),
  2946. sshbug_handler, I(CONF_sshbug_hmac2));
  2947. ctrl_droplist(s, "Misuses the session ID in SSH-2 PK auth", 'n', 20,
  2948. HELPCTX(ssh_bugs_pksessid2),
  2949. sshbug_handler, I(CONF_sshbug_pksessid2));
  2950. ctrl_droplist(s, "Miscomputes SSH-2 encryption keys", 'e', 20,
  2951. HELPCTX(ssh_bugs_derivekey2),
  2952. sshbug_handler, I(CONF_sshbug_derivekey2));
  2953. ctrl_droplist(s, "Chokes on SSH-1 ignore messages", 'i', 20,
  2954. HELPCTX(ssh_bugs_ignore1),
  2955. sshbug_handler, I(CONF_sshbug_ignore1));
  2956. ctrl_droplist(s, "Refuses all SSH-1 password camouflage", 's', 20,
  2957. HELPCTX(ssh_bugs_plainpw1),
  2958. sshbug_handler, I(CONF_sshbug_plainpw1));
  2959. ctrl_droplist(s, "Chokes on SSH-1 RSA authentication", 'r', 20,
  2960. HELPCTX(ssh_bugs_rsa1),
  2961. sshbug_handler, I(CONF_sshbug_rsa1));
  2962. }
  2963. }
  2964. if (DISPLAY_RECONFIGURABLE_PROTOCOL(PROT_SERIAL)) {
  2965. const BackendVtable *ser_vt = backend_vt_from_proto(PROT_SERIAL);
  2966. /*
  2967. * The Connection/Serial panel.
  2968. */
  2969. ctrl_settitle(b, "Connection/Serial",
  2970. "Options controlling local serial lines");
  2971. if (!midsession) {
  2972. /*
  2973. * We don't permit switching to a different serial port in
  2974. * midflight, although we do allow all other
  2975. * reconfiguration.
  2976. */
  2977. s = ctrl_getset(b, "Connection/Serial", "serline",
  2978. "Select a serial line");
  2979. ctrl_editbox(s, "Serial line to connect to", 'l', 40,
  2980. HELPCTX(serial_line),
  2981. conf_editbox_handler, I(CONF_serline), ED_STR);
  2982. }
  2983. s = ctrl_getset(b, "Connection/Serial", "sercfg", "Configure the serial line");
  2984. ctrl_editbox(s, "Speed (baud)", 's', 40,
  2985. HELPCTX(serial_speed),
  2986. conf_editbox_handler, I(CONF_serspeed), ED_INT);
  2987. ctrl_editbox(s, "Data bits", 'b', 40,
  2988. HELPCTX(serial_databits),
  2989. conf_editbox_handler, I(CONF_serdatabits), ED_INT);
  2990. /*
  2991. * Stop bits come in units of one half.
  2992. */
  2993. static const struct conf_editbox_handler_type conf_editbox_stopbits = {
  2994. .type = EDIT_FIXEDPOINT, .denominator = 2};
  2995. ctrl_editbox(s, "Stop bits", 't', 40,
  2996. HELPCTX(serial_stopbits),
  2997. conf_editbox_handler, I(CONF_serstopbits),
  2998. CP(&conf_editbox_stopbits));
  2999. ctrl_droplist(s, "Parity", 'p', 40,
  3000. HELPCTX(serial_parity), serial_parity_handler,
  3001. I(ser_vt->serial_parity_mask));
  3002. ctrl_droplist(s, "Flow control", 'f', 40,
  3003. HELPCTX(serial_flow), serial_flow_handler,
  3004. I(ser_vt->serial_flow_mask));
  3005. }
  3006. if (DISPLAY_RECONFIGURABLE_PROTOCOL(PROT_TELNET)) {
  3007. /*
  3008. * The Connection/Telnet panel.
  3009. */
  3010. ctrl_settitle(b, "Connection/Telnet",
  3011. "Options controlling Telnet connections");
  3012. s = ctrl_getset(b, "Connection/Telnet", "protocol",
  3013. "Telnet protocol adjustments");
  3014. if (!midsession) {
  3015. ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:",
  3016. NO_SHORTCUT, 2,
  3017. HELPCTX(telnet_oldenviron),
  3018. conf_radiobutton_bool_handler,
  3019. I(CONF_rfc_environ),
  3020. "BSD (commonplace)", 'b', I(false),
  3021. "RFC 1408 (unusual)", 'f', I(true));
  3022. ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2,
  3023. HELPCTX(telnet_passive),
  3024. conf_radiobutton_bool_handler,
  3025. I(CONF_passive_telnet),
  3026. "Passive", I(true), "Active", I(false));
  3027. }
  3028. ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k',
  3029. HELPCTX(telnet_specialkeys),
  3030. conf_checkbox_handler,
  3031. I(CONF_telnet_keyboard));
  3032. ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M",
  3033. 'm', HELPCTX(telnet_newline),
  3034. conf_checkbox_handler,
  3035. I(CONF_telnet_newline));
  3036. }
  3037. if (DISPLAY_NON_RECONFIGURABLE_PROTOCOL(PROT_RLOGIN)) {
  3038. /*
  3039. * The Connection/Rlogin panel.
  3040. */
  3041. ctrl_settitle(b, "Connection/Rlogin",
  3042. "Options controlling Rlogin connections");
  3043. s = ctrl_getset(b, "Connection/Rlogin", "data",
  3044. "Data to send to the server");
  3045. ctrl_editbox(s, "Local username:", 'l', 50,
  3046. HELPCTX(rlogin_localuser),
  3047. conf_editbox_handler, I(CONF_localusername), ED_STR);
  3048. }
  3049. if (DISPLAY_NON_RECONFIGURABLE_PROTOCOL(PROT_SUPDUP)) {
  3050. /*
  3051. * The Connection/SUPDUP panel.
  3052. */
  3053. ctrl_settitle(b, "Connection/SUPDUP",
  3054. "Options controlling SUPDUP connections");
  3055. s = ctrl_getset(b, "Connection/SUPDUP", "main", NULL);
  3056. ctrl_editbox(s, "Location string", 'l', 70,
  3057. HELPCTX(supdup_location),
  3058. conf_editbox_handler, I(CONF_supdup_location),
  3059. ED_STR);
  3060. ctrl_radiobuttons(s, "Extended ASCII Character set:", 'e', 4,
  3061. HELPCTX(supdup_ascii),
  3062. conf_radiobutton_handler,
  3063. I(CONF_supdup_ascii_set),
  3064. "None", I(SUPDUP_CHARSET_ASCII),
  3065. "ITS", I(SUPDUP_CHARSET_ITS),
  3066. "WAITS", I(SUPDUP_CHARSET_WAITS));
  3067. ctrl_checkbox(s, "**MORE** processing", 'm',
  3068. HELPCTX(supdup_more),
  3069. conf_checkbox_handler,
  3070. I(CONF_supdup_more));
  3071. ctrl_checkbox(s, "Terminal scrolling", 's',
  3072. HELPCTX(supdup_scroll),
  3073. conf_checkbox_handler,
  3074. I(CONF_supdup_scroll));
  3075. }
  3076. }