Fixed issue #3808: LFS stopped working (maybe related to major upgrade of git-lfs...
[TortoiseGit.git] / src / TortoisePlink / CMDLINE.C
blob9a2acd358af5ba06a4edceebce2fb520cf6ecaae
1 /*\r
2  * cmdline.c - command-line parsing shared between many of the\r
3  * PuTTY applications\r
4  */\r
5 \r
6 #include <stdio.h>\r
7 #include <assert.h>\r
8 #include <stdlib.h>\r
9 #include "putty.h"\r
11 /*\r
12  * Some command-line parameters need to be saved up until after\r
13  * we've loaded the saved session which will form the basis of our\r
14  * eventual running configuration. For this we use the macro\r
15  * SAVEABLE, which notices if the `need_save' parameter is set and\r
16  * saves the parameter and value on a list.\r
17  *\r
18  * We also assign priorities to saved parameters, just to slightly\r
19  * ameliorate silly ordering problems. For example, if you specify\r
20  * a saved session to load, it will be loaded _before_ all your\r
21  * local modifications such as -L are evaluated; and if you specify\r
22  * a protocol and a port, the protocol is set up first so that the\r
23  * port can override its choice of port number.\r
24  *\r
25  * (In fact -load is not saved at all, since in at least Plink the\r
26  * processing of further command-line options depends on whether or\r
27  * not the loaded session contained a hostname. So it must be\r
28  * executed immediately.)\r
29  */\r
31 #define NPRIORITIES 2\r
33 struct cmdline_saved_param {\r
34     char *p, *value;\r
35 };\r
36 struct cmdline_saved_param_set {\r
37     struct cmdline_saved_param *params;\r
38     size_t nsaved, savesize;\r
39 };\r
41 /*\r
42  * C guarantees this structure will be initialised to all zero at\r
43  * program start, which is exactly what we want.\r
44  */\r
45 static struct cmdline_saved_param_set saves[NPRIORITIES];\r
47 static void cmdline_save_param(const char *p, const char *value, int pri)\r
48 {\r
49     sgrowarray(saves[pri].params, saves[pri].savesize, saves[pri].nsaved);\r
50     saves[pri].params[saves[pri].nsaved].p = dupstr(p);\r
51     saves[pri].params[saves[pri].nsaved].value = dupstr(value);\r
52     saves[pri].nsaved++;\r
53 }\r
55 static char *cmdline_password = NULL;\r
57 void cmdline_cleanup(void)\r
58 {\r
59     int pri;\r
61     if (cmdline_password) {\r
62         smemclr(cmdline_password, strlen(cmdline_password));\r
63         sfree(cmdline_password);\r
64         cmdline_password = NULL;\r
65     }\r
67     for (pri = 0; pri < NPRIORITIES; pri++) {\r
68         sfree(saves[pri].params);\r
69         saves[pri].params = NULL;\r
70         saves[pri].savesize = 0;\r
71         saves[pri].nsaved = 0;\r
72     }\r
73 }\r
75 #define SAVEABLE(pri) do { \\r
76     if (need_save) { cmdline_save_param(p, value, pri); return ret; } \\r
77 } while (0)\r
79 /*\r
80  * Similar interface to seat_get_userpass_input(), except that here a\r
81  * -1 return means that we aren't capable of processing the prompt and\r
82  * someone else should do it.\r
83  */\r
84 int cmdline_get_passwd_input(prompts_t *p)\r
85 {\r
86     static bool tried_once = false;\r
88     /*\r
89      * We only handle prompts which don't echo (which we assume to be\r
90      * passwords), and (currently) we only cope with a password prompt\r
91      * that comes in a prompt-set on its own.\r
92      */\r
93     if (!cmdline_password || p->n_prompts != 1 || p->prompts[0]->echo) {\r
94         return -1;\r
95     }\r
97     /*\r
98      * If we've tried once, return utter failure (no more passwords left\r
99      * to try).\r
100      */\r
101     if (tried_once)\r
102         return 0;\r
104     prompt_set_result(p->prompts[0], cmdline_password);\r
105     smemclr(cmdline_password, strlen(cmdline_password));\r
106     sfree(cmdline_password);\r
107     cmdline_password = NULL;\r
108     tried_once = true;\r
109     return 1;\r
112 static bool cmdline_check_unavailable(int flag, const char *p)\r
114     if (cmdline_tooltype & flag) {\r
115         cmdline_error("option \"%s\" not available in this tool", p);\r
116         return true;\r
117     }\r
118     return false;\r
121 #define UNAVAILABLE_IN(flag) do { \\r
122     if (cmdline_check_unavailable(flag, p)) return ret; \\r
123 } while (0)\r
125 /*\r
126  * Process a standard command-line parameter. `p' is the parameter\r
127  * in question; `value' is the subsequent element of argv, which\r
128  * may or may not be required as an operand to the parameter.\r
129  * If `need_save' is 1, arguments which need to be saved as\r
130  * described at this top of this file are, for later execution;\r
131  * if 0, they are processed normally. (-1 is a special value used\r
132  * by pterm to count arguments for a preliminary pass through the\r
133  * argument list; it causes immediate return with an appropriate\r
134  * value with no action taken.)\r
135  * Return value is 2 if both arguments were used; 1 if only p was\r
136  * used; 0 if the parameter wasn't one we recognised; -2 if it\r
137  * should have been 2 but value was NULL.\r
138  */\r
140 #define RETURN(x) do { \\r
141     if ((x) == 2 && !value) return -2; \\r
142     ret = x; \\r
143     if (need_save < 0) return x; \\r
144 } while (0)\r
146 static bool seen_hostname_argument = false;\r
147 static bool seen_port_argument = false;\r
148 static bool seen_verbose_option = false;\r
149 static bool loaded_session = false;\r
150 bool cmdline_verbose(void) { return seen_verbose_option; }\r
151 bool cmdline_seat_verbose(Seat *seat) { return cmdline_verbose(); }\r
152 bool cmdline_lp_verbose(LogPolicy *lp) { return cmdline_verbose(); }\r
153 bool cmdline_loaded_session(void) { return loaded_session; }\r
155 static void set_protocol(Conf *conf, int protocol)\r
157     settings_set_default_protocol(protocol);\r
158     conf_set_int(conf, CONF_protocol, protocol);\r
161 static void set_port(Conf *conf, int port)\r
163     settings_set_default_port(port);\r
164     conf_set_int(conf, CONF_port, port);\r
167 int cmdline_process_param(const char *p, char *value,\r
168                           int need_save, Conf *conf, bool ignoreFurtherParameters)\r
170     int ret = 0;\r
172     if (p[0] != '-' || ignoreFurtherParameters) {\r
173         if (need_save < 0)\r
174             return 0;\r
176         /*\r
177          * Common handling for the tools whose initial command-line\r
178          * arguments specify a hostname to connect to, i.e. PuTTY and\r
179          * Plink. Doesn't count the file transfer tools, because their\r
180          * hostname specification appears as part of a more\r
181          * complicated scheme.\r
182          */\r
184         if ((cmdline_tooltype & TOOLTYPE_HOST_ARG) &&\r
185             !seen_hostname_argument &&\r
186             (!(cmdline_tooltype & TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD) ||\r
187              !loaded_session || !conf_launchable(conf))) {\r
188             /*\r
189              * Treat this argument as a host name, if we have not yet\r
190              * seen a host name argument or -load.\r
191              *\r
192              * Exception, in some tools (Plink): if we have seen -load\r
193              * but it didn't create a launchable session, then we\r
194              * still accept a hostname argument following that -load.\r
195              * This allows you to make saved sessions that configure\r
196              * lots of other stuff (colour schemes, terminal settings\r
197              * etc) and then say 'putty -load sessionname hostname'.\r
198              *\r
199              * Also, we carefully _don't_ test conf for launchability\r
200              * if we haven't been explicitly told to load a session\r
201              * (otherwise saving a host name into Default Settings\r
202              * would cause 'putty' on its own to immediately launch\r
203              * the default session and never be able to do anything\r
204              * else).\r
205              */\r
206             if (!strncmp(p, "telnet:", 7)) {\r
207                 /*\r
208                  * If the argument starts with "telnet:", set the\r
209                  * protocol to Telnet and process the string as a\r
210                  * Telnet URL.\r
211                  */\r
213                 /*\r
214                  * Skip the "telnet:" or "telnet://" prefix.\r
215                  */\r
216                 p += 7;\r
217                 if (p[0] == '/' && p[1] == '/')\r
218                     p += 2;\r
219                 conf_set_int(conf, CONF_protocol, PROT_TELNET);\r
221                 /*\r
222                  * The next thing we expect is a host name.\r
223                  */\r
224                 {\r
225                     const char *host = p;\r
226                     char *buf;\r
228                     p += host_strcspn(p, ":/");\r
229                     buf = dupprintf("%.*s", (int)(p - host), host);\r
230                     conf_set_str(conf, CONF_host, buf);\r
231                     sfree(buf);\r
232                     seen_hostname_argument = true;\r
233                 }\r
235                 /*\r
236                  * If the host name is followed by a colon, then\r
237                  * expect a port number after it.\r
238                  */\r
239                 if (*p == ':') {\r
240                     p++;\r
242                     conf_set_int(conf, CONF_port, atoi(p));\r
243                     /*\r
244                      * Set the flag that will stop us from treating\r
245                      * the next argument as a separate port; this one\r
246                      * counts as explicitly provided.\r
247                      */\r
248                     seen_port_argument = true;\r
249                 } else {\r
250                     conf_set_int(conf, CONF_port, -1);\r
251                 }\r
252             } else {\r
253                 char *user = NULL, *hostname = NULL;\r
254                 const char *hostname_after_user;\r
255                 int port_override = -1;\r
256                 size_t len;\r
258                 /*\r
259                  * Otherwise, treat it as a bare host name.\r
260                  */\r
262                 if (cmdline_tooltype & TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX) {\r
263                     /*\r
264                      * Here Plink checks for a comma-separated\r
265                      * protocol prefix, e.g. 'ssh,hostname' or\r
266                      * 'ssh,user@hostname'.\r
267                      *\r
268                      * I'm not entirely sure why; this behaviour dates\r
269                      * from 2000 and isn't explained. But I _think_ it\r
270                      * has to do with CVS transport or similar use\r
271                      * cases, in which the end user invokes the SSH\r
272                      * client indirectly, via some means that only\r
273                      * lets them pass a single string argument, and it\r
274                      * was occasionally useful to shoehorn the choice\r
275                      * of protocol into that argument.\r
276                      */\r
277                     const char *comma = strchr(p, ',');\r
278                     if (comma) {\r
279                         char *prefix = dupprintf("%.*s", (int)(comma - p), p);\r
280                         const struct BackendVtable *vt =\r
281                             backend_vt_from_name(prefix);\r
283                         if (vt) {\r
284                             set_protocol(conf, vt->protocol);\r
285                             port_override = vt->default_port;\r
286                         } else {\r
287                             cmdline_error("unrecognised protocol prefix '%s'",\r
288                                           prefix);\r
289                         }\r
291                         sfree(prefix);\r
292                         p = comma + 1;\r
293                     }\r
294                 }\r
296                 hostname_after_user = p;\r
297                 if (cmdline_tooltype & TOOLTYPE_HOST_ARG_CAN_BE_SESSION) {\r
298                     /*\r
299                      * If the hostname argument can also be a saved\r
300                      * session (see below), then here we also check\r
301                      * for a user@ prefix, which will override the\r
302                      * username from the saved session.\r
303                      *\r
304                      * (If the hostname argument _isn't_ a saved\r
305                      * session, we don't do this.)\r
306                      */\r
307                     const char *at = strrchr(p, '@');\r
308                     if (at) {\r
309                         user = dupprintf("%.*s", (int)(at - p), p);\r
310                         hostname_after_user = at + 1;\r
311                     }\r
312                 }\r
314                 /*\r
315                  * Write the whole hostname argument (minus only that\r
316                  * optional protocol prefix) into the existing Conf,\r
317                  * for tools that don't treat it as a saved session\r
318                  * and as a fallback for those that do.\r
319                  */\r
320                 hostname = dupstr(p + strspn(p, " \t"));\r
321                 len = strlen(hostname);\r
322                 while (len > 0 && (hostname[len-1] == ' ' ||\r
323                                    hostname[len-1] == '\t'))\r
324                     hostname[--len] = '\0';\r
325                 seen_hostname_argument = true;\r
326                 conf_set_str(conf, CONF_host, hostname);\r
328                 if ((cmdline_tooltype & TOOLTYPE_HOST_ARG_CAN_BE_SESSION) &&\r
329                     !loaded_session) {\r
330                     /*\r
331                      * For some tools, we equivocate between a\r
332                      * hostname argument and an argument naming a\r
333                      * saved session. Here we attempt to load a\r
334                      * session with the specified name, and if that\r
335                      * session exists and is launchable, we overwrite\r
336                      * the entire Conf with it.\r
337                      *\r
338                      * We skip this check if a -load option has\r
339                      * already happened, so that\r
340                      *\r
341                      *   plink -load non-launchable-session hostname\r
342                      *\r
343                      * will treat 'hostname' as a hostname _even_ if a\r
344                      * saved session called 'hostname' exists. (This\r
345                      * doesn't lose any functionality someone could\r
346                      * have needed, because if 'hostname' did cause a\r
347                      * session to be loaded, then it would overwrite\r
348                      * everything from the previously loaded session.\r
349                      * So if that was the behaviour someone wanted,\r
350                      * then they could get it by leaving off the\r
351                      * -load completely.)\r
352                      */\r
353                     Conf *conf2 = conf_new();\r
354                     if (do_defaults(hostname_after_user, conf2) &&\r
355                         conf_launchable(conf2)) {\r
356                         conf_copy_into(conf, conf2);\r
357                         loaded_session = true;\r
358                         /* And override the username if one was given. */\r
359                         if (user)\r
360                             conf_set_str(conf, CONF_username, user);\r
361                     }\r
362                     conf_free(conf2);\r
363                 }\r
365                 sfree(hostname);\r
366                 sfree(user);\r
368                 if (port_override >= 0)\r
369                     conf_set_int(conf, CONF_port, port_override);\r
370             }\r
372             return 1;\r
373         } else if ((cmdline_tooltype & TOOLTYPE_PORT_ARG) &&\r
374                    !seen_port_argument) {\r
375             /*\r
376              * If we've already got a host name from the command line\r
377              * (either as a hostname argument or a qualifying -load),\r
378              * but not a port number, then treat the next argument as\r
379              * a port number.\r
380              *\r
381              * We handle this by calling ourself recursively to\r
382              * pretend we received a -P argument, so that it will be\r
383              * deferred until it's a good moment to run it.\r
384              */\r
385             char *dup = dupstr(p);     /* 'value' is not a const char * */\r
386             int retd = cmdline_process_param("-P", dup, 1, conf, false);\r
387             sfree(dup);\r
388             assert(retd == 2);\r
389             seen_port_argument = true;\r
390             return 1;\r
391         } else {\r
392             /*\r
393              * Refuse to recognise this argument, and give it back to\r
394              * the tool's own command-line processing.\r
395              */\r
396             return 0;\r
397         }\r
398     }\r
400     if (ignoreFurtherParameters)\r
401         return ret;\r
403     if (!strcmp(p, "-load")) {\r
404         RETURN(2);\r
405         /* This parameter must be processed immediately rather than being\r
406          * saved. */\r
407         do_defaults(value, conf);\r
408         loaded_session = true;\r
409         return 2;\r
410     }\r
411     for (size_t i = 0; backends[i]; i++) {\r
412         if (p[0] == '-' && !strcmp(p+1, backends[i]->id)) {\r
413             RETURN(1);\r
414             UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
415             SAVEABLE(0);\r
416             set_protocol(conf, backends[i]->protocol);\r
417             if (backends[i]->default_port)\r
418                 set_port(conf, backends[i]->default_port);\r
419             if (backends[i]->protocol == PROT_SERIAL) {\r
420                 /* Special handling: the 'where to connect to' argument will\r
421                  * have been placed into CONF_host, but for this protocol, it\r
422                  * needs to be in CONF_serline */\r
423                 conf_set_str(conf, CONF_serline,\r
424                              conf_get_str(conf, CONF_host));\r
425             }\r
426             return 1;\r
427         }\r
428     }\r
429     if (!strcmp(p, "-v")) {\r
430         RETURN(1);\r
431         UNAVAILABLE_IN(TOOLTYPE_NO_VERBOSE_OPTION);\r
432         seen_verbose_option = true;\r
433     }\r
434     if (!strcmp(p, "-l")) {\r
435         RETURN(2);\r
436         UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
437         SAVEABLE(0);\r
438         conf_set_str(conf, CONF_username, value);\r
439     }\r
440     if (!strcmp(p, "-loghost")) {\r
441         RETURN(2);\r
442         UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
443         SAVEABLE(0);\r
444         conf_set_str(conf, CONF_loghost, value);\r
445     }\r
446     if (!strcmp(p, "-hostkey")) {\r
447         char *dup;\r
448         RETURN(2);\r
449         UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
450         SAVEABLE(0);\r
451         dup = dupstr(value);\r
452         if (!validate_manual_hostkey(dup)) {\r
453             cmdline_error("'%s' is not a valid format for a manual host "\r
454                           "key specification", value);\r
455             sfree(dup);\r
456             return ret;\r
457         }\r
458         conf_set_str_str(conf, CONF_ssh_manual_hostkeys, dup, "");\r
459         sfree(dup);\r
460     }\r
461     if ((!strcmp(p, "-L") || !strcmp(p, "-R") || !strcmp(p, "-D"))) {\r
462         char type, *q, *qq, *key, *val;\r
463         RETURN(2);\r
464         UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
465         SAVEABLE(0);\r
466         if (strcmp(p, "-D")) {\r
467             /*\r
468              * For -L or -R forwarding types:\r
469              *\r
470              * We expect _at least_ two colons in this string. The\r
471              * possible formats are `sourceport:desthost:destport',\r
472              * or `sourceip:sourceport:desthost:destport' if you're\r
473              * specifying a particular loopback address. We need to\r
474              * replace the one between source and dest with a \t;\r
475              * this means we must find the second-to-last colon in\r
476              * the string.\r
477              *\r
478              * (This looks like a foolish way of doing it given the\r
479              * existence of strrchr, but it's more efficient than\r
480              * two strrchrs - not to mention that the second strrchr\r
481              * would require us to modify the input string!)\r
482              */\r
484             type = p[1];               /* 'L' or 'R' */\r
486             q = qq = host_strchr(value, ':');\r
487             while (qq) {\r
488                 char *qqq = host_strchr(qq+1, ':');\r
489                 if (qqq)\r
490                     q = qq;\r
491                 qq = qqq;\r
492             }\r
494             if (!q) {\r
495                 cmdline_error("-%c expects at least two colons in its"\r
496                               " argument", type);\r
497                 return ret;\r
498             }\r
500             key = dupprintf("%c%.*s", type, (int)(q - value), value);\r
501             val = dupstr(q+1);\r
502         } else {\r
503             /*\r
504              * Dynamic port forwardings are entered under the same key\r
505              * as if they were local (because they occupy the same\r
506              * port space - a local and a dynamic forwarding on the\r
507              * same local port are mutually exclusive), with the\r
508              * special value "D" (which can be distinguished from\r
509              * anything in the ordinary -L case by containing no\r
510              * colon).\r
511              */\r
512             key = dupprintf("L%s", value);\r
513             val = dupstr("D");\r
514         }\r
515         conf_set_str_str(conf, CONF_portfwd, key, val);\r
516         sfree(key);\r
517         sfree(val);\r
518     }\r
519     if ((!strcmp(p, "-nc"))) {\r
520         char *host, *portp;\r
522         RETURN(2);\r
523         UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
524         SAVEABLE(0);\r
526         portp = host_strchr(value, ':');\r
527         if (!portp) {\r
528             cmdline_error("-nc expects argument of form 'host:port'");\r
529             return ret;\r
530         }\r
532         host = dupprintf("%.*s", (int)(portp - value), value);\r
533         conf_set_str(conf, CONF_ssh_nc_host, host);\r
534         conf_set_int(conf, CONF_ssh_nc_port, atoi(portp + 1));\r
535         sfree(host);\r
536     }\r
537     if (!strcmp(p, "-m")) {\r
538         const char *filename;\r
539         FILE *fp;\r
541         RETURN(2);\r
542         UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
543         SAVEABLE(0);\r
545         filename = value;\r
547         fp = fopen(filename, "r");\r
548         if (!fp) {\r
549             cmdline_error("unable to open command file \"%s\"", filename);\r
550             return ret;\r
551         }\r
552         strbuf *command = strbuf_new();\r
553         char readbuf[4096];\r
554         while (1) {\r
555             size_t nread = fread(readbuf, 1, sizeof(readbuf), fp);\r
556             if (nread == 0)\r
557                 break;\r
558             put_data(command, readbuf, nread);\r
559         }\r
560         fclose(fp);\r
561         conf_set_str(conf, CONF_remote_cmd, command->s);\r
562         conf_set_str(conf, CONF_remote_cmd2, "");\r
563         conf_set_bool(conf, CONF_nopty, true);   /* command => no terminal */\r
564         strbuf_free(command);\r
565     }\r
566     if (!strcmp(p, "-P") || !strcmp(p, "-p")) {\r
567         RETURN(2);\r
568         UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
569         SAVEABLE(1);            /* lower priority than -ssh, -telnet, etc */\r
570         conf_set_int(conf, CONF_port, atoi(value));\r
571     }\r
572     if (!strcmp(p, "-o")) {\r
573         RETURN(2);\r
574         UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
575         SAVEABLE(0);\r
576         if (stricmp(value, "SendEnv=GIT_PROTOCOL")) {\r
577             cmdline_error("Unrecognised suboption \"-o %s\"", value);\r
578             return -1;\r
579         }\r
580         const char *env = getenv("GIT_PROTOCOL");\r
581         if (env)\r
582             conf_set_str_str(conf, CONF_environmt, "GIT_PROTOCOL", env);\r
583     }\r
584     if (!strcmp(p, "-pw")) {\r
585         RETURN(2);\r
586         UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
587         SAVEABLE(1);\r
588         /* We delay evaluating this until after the protocol is decided,\r
589          * so that we can warn if it's of no use with the selected protocol */\r
590         if (conf_get_int(conf, CONF_protocol) != PROT_SSH)\r
591             cmdline_error("the -pw option can only be used with the "\r
592                           "SSH protocol");\r
593         else {\r
594             cmdline_password = dupstr(value);\r
595             /* Assuming that `value' is directly from argv, make a good faith\r
596              * attempt to trample it, to stop it showing up in `ps' output\r
597              * on Unix-like systems. Not guaranteed, of course. */\r
598             smemclr(value, strlen(value));\r
599         }\r
600     }\r
602     if (!strcmp(p, "-agent") || !strcmp(p, "-pagent") ||\r
603         !strcmp(p, "-pageant")) {\r
604         RETURN(1);\r
605         UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
606         SAVEABLE(0);\r
607         conf_set_bool(conf, CONF_tryagent, true);\r
608     }\r
609     if (!strcmp(p, "-noagent") || !strcmp(p, "-nopagent") ||\r
610         !strcmp(p, "-nopageant")) {\r
611         RETURN(1);\r
612         UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
613         SAVEABLE(0);\r
614         conf_set_bool(conf, CONF_tryagent, false);\r
615     }\r
617     if (!strcmp(p, "-no-trivial-auth")) {\r
618         RETURN(1);\r
619         UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
620         SAVEABLE(0);\r
621         conf_set_bool(conf, CONF_ssh_no_trivial_userauth, true);\r
622     }\r
624     if (!strcmp(p, "-share")) {\r
625         RETURN(1);\r
626         UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
627         SAVEABLE(0);\r
628         conf_set_bool(conf, CONF_ssh_connection_sharing, true);\r
629     }\r
630     if (!strcmp(p, "-noshare")) {\r
631         RETURN(1);\r
632         UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
633         SAVEABLE(0);\r
634         conf_set_bool(conf, CONF_ssh_connection_sharing, false);\r
635     }\r
636     if (!strcmp(p, "-A")) {\r
637         RETURN(1);\r
638         UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
639         SAVEABLE(0);\r
640         conf_set_bool(conf, CONF_agentfwd, true);\r
641     }\r
642     if (!strcmp(p, "-a")) {\r
643         RETURN(1);\r
644         UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
645         SAVEABLE(0);\r
646         conf_set_bool(conf, CONF_agentfwd, false);\r
647     }\r
649     if (!strcmp(p, "-X")) {\r
650         RETURN(1);\r
651         UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
652         SAVEABLE(0);\r
653         conf_set_bool(conf, CONF_x11_forward, true);\r
654     }\r
655     if (!strcmp(p, "-x")) {\r
656         RETURN(1);\r
657         UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
658         SAVEABLE(0);\r
659         conf_set_bool(conf, CONF_x11_forward, false);\r
660     }\r
662     if (!strcmp(p, "-t")) {\r
663         RETURN(1);\r
664         UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
665         SAVEABLE(1);    /* lower priority than -m */\r
666         conf_set_bool(conf, CONF_nopty, false);\r
667     }\r
668     if (!strcmp(p, "-T")) {\r
669         RETURN(1);\r
670         UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
671         SAVEABLE(1);\r
672         conf_set_bool(conf, CONF_nopty, true);\r
673     }\r
675     if (!strcmp(p, "-N")) {\r
676         RETURN(1);\r
677         UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
678         SAVEABLE(0);\r
679         conf_set_bool(conf, CONF_ssh_no_shell, true);\r
680     }\r
682     if (!strcmp(p, "-C")) {\r
683         RETURN(1);\r
684         UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
685         SAVEABLE(0);\r
686         conf_set_bool(conf, CONF_compression, true);\r
687     }\r
689     if (!strcmp(p, "-1")) {\r
690         RETURN(1);\r
691         UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
692         SAVEABLE(0);\r
693         conf_set_int(conf, CONF_sshprot, 0);   /* ssh protocol 1 only */\r
694     }\r
695     if (!strcmp(p, "-2")) {\r
696         RETURN(1);\r
697         UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
698         SAVEABLE(0);\r
699         conf_set_int(conf, CONF_sshprot, 3);   /* ssh protocol 2 only */\r
700     }\r
702     if (!strcmp(p, "-i")) {\r
703         Filename *fn;\r
704         RETURN(2);\r
705         UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
706         SAVEABLE(0);\r
707         fn = filename_from_str(value);\r
708         conf_set_filename(conf, CONF_keyfile, fn);\r
709         filename_free(fn);\r
710     }\r
712     if (!strcmp(p, "-4") || !strcmp(p, "-ipv4")) {\r
713         RETURN(1);\r
714         SAVEABLE(1);\r
715         conf_set_int(conf, CONF_addressfamily, ADDRTYPE_IPV4);\r
716     }\r
717     if (!strcmp(p, "-6") || !strcmp(p, "-ipv6")) {\r
718         RETURN(1);\r
719         SAVEABLE(1);\r
720         conf_set_int(conf, CONF_addressfamily, ADDRTYPE_IPV6);\r
721     }\r
722     if (!strcmp(p, "-sercfg")) {\r
723         char* nextitem;\r
724         RETURN(2);\r
725         UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);\r
726         SAVEABLE(1);\r
727         if (conf_get_int(conf, CONF_protocol) != PROT_SERIAL)\r
728             cmdline_error("the -sercfg option can only be used with the "\r
729                           "serial protocol");\r
730         /* Value[0] contains one or more , separated values, like 19200,8,n,1,X */\r
731         nextitem = value;\r
732         while (nextitem[0] != '\0') {\r
733             int length, skip;\r
734             char *end = strchr(nextitem, ',');\r
735             if (!end) {\r
736                 length = strlen(nextitem);\r
737                 skip = 0;\r
738             } else {\r
739                 length = end - nextitem;\r
740                 nextitem[length] = '\0';\r
741                 skip = 1;\r
742             }\r
743             if (length == 1) {\r
744                 switch (*nextitem) {\r
745                   case '1':\r
746                   case '2':\r
747                     conf_set_int(conf, CONF_serstopbits, 2 * (*nextitem-'0'));\r
748                     break;\r
750                   case '5':\r
751                   case '6':\r
752                   case '7':\r
753                   case '8':\r
754                   case '9':\r
755                     conf_set_int(conf, CONF_serdatabits, *nextitem-'0');\r
756                     break;\r
758                   case 'n':\r
759                     conf_set_int(conf, CONF_serparity, SER_PAR_NONE);\r
760                     break;\r
761                   case 'o':\r
762                     conf_set_int(conf, CONF_serparity, SER_PAR_ODD);\r
763                     break;\r
764                   case 'e':\r
765                     conf_set_int(conf, CONF_serparity, SER_PAR_EVEN);\r
766                     break;\r
767                   case 'm':\r
768                     conf_set_int(conf, CONF_serparity, SER_PAR_MARK);\r
769                     break;\r
770                   case 's':\r
771                     conf_set_int(conf, CONF_serparity, SER_PAR_SPACE);\r
772                     break;\r
774                   case 'N':\r
775                     conf_set_int(conf, CONF_serflow, SER_FLOW_NONE);\r
776                     break;\r
777                   case 'X':\r
778                     conf_set_int(conf, CONF_serflow, SER_FLOW_XONXOFF);\r
779                     break;\r
780                   case 'R':\r
781                     conf_set_int(conf, CONF_serflow, SER_FLOW_RTSCTS);\r
782                     break;\r
783                   case 'D':\r
784                     conf_set_int(conf, CONF_serflow, SER_FLOW_DSRDTR);\r
785                     break;\r
787                   default:\r
788                     cmdline_error("Unrecognised suboption \"-sercfg %c\"",\r
789                                   *nextitem);\r
790                 }\r
791             } else if (length == 3 && !strncmp(nextitem,"1.5",3)) {\r
792                 /* Messy special case */\r
793                 conf_set_int(conf, CONF_serstopbits, 3);\r
794             } else {\r
795                 int serspeed = atoi(nextitem);\r
796                 if (serspeed != 0) {\r
797                     conf_set_int(conf, CONF_serspeed, serspeed);\r
798                 } else {\r
799                     cmdline_error("Unrecognised suboption \"-sercfg %s\"",\r
800                                   nextitem);\r
801                 }\r
802             }\r
803             nextitem += length + skip;\r
804         }\r
805     }\r
807     if (!strcmp(p, "-sessionlog")) {\r
808         Filename *fn;\r
809         RETURN(2);\r
810         UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER);\r
811         /* but available even in TOOLTYPE_NONNETWORK, cf pterm "-log" */\r
812         SAVEABLE(0);\r
813         fn = filename_from_str(value);\r
814         conf_set_filename(conf, CONF_logfilename, fn);\r
815         conf_set_int(conf, CONF_logtype, LGTYP_DEBUG);\r
816         filename_free(fn);\r
817     }\r
819     if (!strcmp(p, "-sshlog") ||\r
820         !strcmp(p, "-sshrawlog")) {\r
821         Filename *fn;\r
822         RETURN(2);\r
823         UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
824         SAVEABLE(0);\r
825         fn = filename_from_str(value);\r
826         conf_set_filename(conf, CONF_logfilename, fn);\r
827         conf_set_int(conf, CONF_logtype,\r
828                      !strcmp(p, "-sshlog") ? LGTYP_PACKETS :\r
829                      /* !strcmp(p, "-sshrawlog") ? */ LGTYP_SSHRAW);\r
830         filename_free(fn);\r
831     }\r
833     if (!strcmp(p, "-logoverwrite")) {\r
834         RETURN(1);\r
835         UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
836         SAVEABLE(0);\r
837         conf_set_int(conf, CONF_logxfovr, LGXF_OVR);\r
838     }\r
840     if (!strcmp(p, "-logappend")) {\r
841         RETURN(1);\r
842         UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
843         SAVEABLE(0);\r
844         conf_set_int(conf, CONF_logxfovr, LGXF_APN);\r
845     }\r
847     if (!strcmp(p, "-proxycmd")) {\r
848         RETURN(2);\r
849         UNAVAILABLE_IN(TOOLTYPE_NONNETWORK);\r
850         SAVEABLE(0);\r
851         conf_set_int(conf, CONF_proxy_type, PROXY_CMD);\r
852         conf_set_str(conf, CONF_proxy_telnet_command, value);\r
853     }\r
855 #ifdef _WINDOWS\r
856     /*\r
857      * Cross-tool options only available on Windows.\r
858      */\r
859     if (!strcmp(p, "-restrict-acl") || !strcmp(p, "-restrict_acl") ||\r
860         !strcmp(p, "-restrictacl")) {\r
861         RETURN(1);\r
862         restrict_process_acl();\r
863     }\r
864 #endif\r
866     return ret;                        /* unrecognised */\r
869 void cmdline_run_saved(Conf *conf)\r
871     for (size_t pri = 0; pri < NPRIORITIES; pri++) {\r
872         for (size_t i = 0; i < saves[pri].nsaved; i++) {\r
873             cmdline_process_param(saves[pri].params[i].p,\r
874                                   saves[pri].params[i].value, 0, conf, false);\r
875             sfree(saves[pri].params[i].p);\r
876             sfree(saves[pri].params[i].value);\r
877         }\r
878         saves[pri].nsaved = 0;\r
879     }\r
882 bool cmdline_host_ok(Conf *conf)\r
884     /*\r
885      * Return true if the command-line arguments we've processed in\r
886      * TOOLTYPE_HOST_ARG mode are sufficient to justify launching a\r
887      * session.\r
888      */\r
889     assert(cmdline_tooltype & TOOLTYPE_HOST_ARG);\r
891     /*\r
892      * Of course, if we _can't_ launch a session, the answer is\r
893      * clearly no.\r
894      */\r
895     if (!conf_launchable(conf))\r
896         return false;\r
898     /*\r
899      * But also, if we haven't seen either a -load option or a\r
900      * hostname argument, i.e. the only saved settings we've loaded\r
901      * are Default Settings plus any non-hostname-based stuff from the\r
902      * command line, then the answer is still no, _even_ if this Conf\r
903      * is launchable. Otherwise, if you saved your favourite hostname\r
904      * into Default Settings, then just running 'putty' without\r
905      * arguments would connect to it without ever offering you the\r
906      * option to connect to something else or change the setting.\r
907      */\r
908     if (!seen_hostname_argument && !loaded_session)\r
909         return false;\r
911     return true;\r