config.c 135 KB

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