psftp.c 85 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910
  1. /*
  2. * psftp.c: (platform-independent) front end for PSFTP.
  3. */
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <stdarg.h>
  7. #include <assert.h>
  8. #include <limits.h>
  9. #include "putty.h"
  10. #include "psftp.h"
  11. #include "storage.h"
  12. #include "ssh.h"
  13. #include "sftp.h"
  14. const char *const appname = "PSFTP";
  15. /*
  16. * Since SFTP is a request-response oriented protocol, it requires
  17. * no buffer management: when we send data, we stop and wait for an
  18. * acknowledgement _anyway_, and so we can't possibly overfill our
  19. * send buffer.
  20. */
  21. static int psftp_connect(char *userhost, char *user, int portnumber);
  22. static int do_sftp_init(void);
  23. static void do_sftp_cleanup(void);
  24. /* ----------------------------------------------------------------------
  25. * sftp client state.
  26. */
  27. static char *pwd, *homedir;
  28. static LogContext *psftp_logctx = NULL;
  29. static Backend *backend;
  30. static Conf *conf;
  31. static bool sent_eof = false;
  32. /* ------------------------------------------------------------
  33. * Seat vtable.
  34. */
  35. static size_t psftp_output(Seat *, bool is_stderr, const void *, size_t);
  36. static bool psftp_eof(Seat *);
  37. static const SeatVtable psftp_seat_vt = {
  38. .output = psftp_output,
  39. .eof = psftp_eof,
  40. .get_userpass_input = filexfer_get_userpass_input,
  41. .notify_remote_exit = nullseat_notify_remote_exit,
  42. .connection_fatal = console_connection_fatal,
  43. .update_specials_menu = nullseat_update_specials_menu,
  44. .get_ttymode = nullseat_get_ttymode,
  45. .set_busy_status = nullseat_set_busy_status,
  46. .verify_ssh_host_key = console_verify_ssh_host_key,
  47. .confirm_weak_crypto_primitive = console_confirm_weak_crypto_primitive,
  48. .confirm_weak_cached_hostkey = console_confirm_weak_cached_hostkey,
  49. .is_utf8 = nullseat_is_never_utf8,
  50. .echoedit_update = nullseat_echoedit_update,
  51. .get_x_display = nullseat_get_x_display,
  52. .get_windowid = nullseat_get_windowid,
  53. .get_window_pixel_size = nullseat_get_window_pixel_size,
  54. .stripctrl_new = console_stripctrl_new,
  55. .set_trust_status = nullseat_set_trust_status_vacuously,
  56. .verbose = cmdline_seat_verbose,
  57. .interactive = nullseat_interactive_yes,
  58. .get_cursor_position = nullseat_get_cursor_position,
  59. };
  60. static Seat psftp_seat[1] = {{ &psftp_seat_vt }};
  61. /* ----------------------------------------------------------------------
  62. * A nasty loop macro that lets me get an escape-sequence sanitised
  63. * version of a string for display, and free it automatically
  64. * afterwards.
  65. */
  66. static StripCtrlChars *string_scc;
  67. #define with_stripctrl(varname, input) \
  68. for (char *varname = stripctrl_string(string_scc, input); varname; \
  69. sfree(varname), varname = NULL)
  70. /* ----------------------------------------------------------------------
  71. * Manage sending requests and waiting for replies.
  72. */
  73. struct sftp_packet *sftp_wait_for_reply(struct sftp_request *req)
  74. {
  75. struct sftp_packet *pktin;
  76. struct sftp_request *rreq;
  77. sftp_register(req);
  78. pktin = sftp_recv();
  79. if (pktin == NULL) {
  80. seat_connection_fatal(
  81. psftp_seat, "did not receive SFTP response packet from server");
  82. }
  83. rreq = sftp_find_request(pktin);
  84. if (rreq != req) {
  85. seat_connection_fatal(
  86. psftp_seat,
  87. "unable to understand SFTP response packet from server: %s",
  88. fxp_error());
  89. }
  90. return pktin;
  91. }
  92. /* ----------------------------------------------------------------------
  93. * Higher-level helper functions used in commands.
  94. */
  95. /*
  96. * Attempt to canonify a pathname starting from the pwd. If
  97. * canonification fails, at least fall back to returning a _valid_
  98. * pathname (though it may be ugly, eg /home/simon/../foobar).
  99. */
  100. char *canonify(const char *name)
  101. {
  102. char *fullname, *canonname;
  103. struct sftp_packet *pktin;
  104. struct sftp_request *req;
  105. if (name[0] == '/') {
  106. fullname = dupstr(name);
  107. } else {
  108. const char *slash;
  109. if (pwd[strlen(pwd) - 1] == '/')
  110. slash = "";
  111. else
  112. slash = "/";
  113. fullname = dupcat(pwd, slash, name);
  114. }
  115. req = fxp_realpath_send(fullname);
  116. pktin = sftp_wait_for_reply(req);
  117. canonname = fxp_realpath_recv(pktin, req);
  118. if (canonname) {
  119. sfree(fullname);
  120. return canonname;
  121. } else {
  122. /*
  123. * Attempt number 2. Some FXP_REALPATH implementations
  124. * (glibc-based ones, in particular) require the _whole_
  125. * path to point to something that exists, whereas others
  126. * (BSD-based) only require all but the last component to
  127. * exist. So if the first call failed, we should strip off
  128. * everything from the last slash onwards and try again,
  129. * then put the final component back on.
  130. *
  131. * Special cases:
  132. *
  133. * - if the last component is "/." or "/..", then we don't
  134. * bother trying this because there's no way it can work.
  135. *
  136. * - if the thing actually ends with a "/", we remove it
  137. * before we start. Except if the string is "/" itself
  138. * (although I can't see why we'd have got here if so,
  139. * because surely "/" would have worked the first
  140. * time?), in which case we don't bother.
  141. *
  142. * - if there's no slash in the string at all, give up in
  143. * confusion (we expect at least one because of the way
  144. * we constructed the string).
  145. */
  146. int i;
  147. char *returnname;
  148. i = strlen(fullname);
  149. if (i > 2 && fullname[i - 1] == '/')
  150. fullname[--i] = '\0'; /* strip trailing / unless at pos 0 */
  151. while (i > 0 && fullname[--i] != '/');
  152. /*
  153. * Give up on special cases.
  154. */
  155. if (fullname[i] != '/' || /* no slash at all */
  156. !strcmp(fullname + i, "/.") || /* ends in /. */
  157. !strcmp(fullname + i, "/..") || /* ends in /.. */
  158. !strcmp(fullname, "/")) {
  159. return fullname;
  160. }
  161. /*
  162. * Now i points at the slash. Deal with the final special
  163. * case i==0 (ie the whole path was "/nonexistentfile").
  164. */
  165. fullname[i] = '\0'; /* separate the string */
  166. if (i == 0) {
  167. req = fxp_realpath_send("/");
  168. } else {
  169. req = fxp_realpath_send(fullname);
  170. }
  171. pktin = sftp_wait_for_reply(req);
  172. canonname = fxp_realpath_recv(pktin, req);
  173. if (!canonname) {
  174. /* Even that failed. Restore our best guess at the
  175. * constructed filename and give up */
  176. fullname[i] = '/'; /* restore slash and last component */
  177. return fullname;
  178. }
  179. /*
  180. * We have a canonical name for all but the last path
  181. * component. Concatenate the last component and return.
  182. */
  183. returnname = dupcat(canonname,
  184. (strendswith(canonname, "/") ? "" : "/"),
  185. fullname + i + 1);
  186. sfree(fullname);
  187. sfree(canonname);
  188. return returnname;
  189. }
  190. }
  191. static int bare_name_compare(const void *av, const void *bv)
  192. {
  193. const char **a = (const char **) av;
  194. const char **b = (const char **) bv;
  195. return strcmp(*a, *b);
  196. }
  197. static void not_connected(void)
  198. {
  199. printf("psftp: not connected to a host; use \"open host.name\"\n");
  200. }
  201. /* ----------------------------------------------------------------------
  202. * The meat of the `get' and `put' commands.
  203. */
  204. bool sftp_get_file(char *fname, char *outfname, bool recurse, bool restart)
  205. {
  206. struct fxp_handle *fh;
  207. struct sftp_packet *pktin;
  208. struct sftp_request *req;
  209. struct fxp_xfer *xfer;
  210. uint64_t offset;
  211. WFile *file;
  212. bool toret, shown_err = false;
  213. struct fxp_attrs attrs;
  214. /*
  215. * In recursive mode, see if we're dealing with a directory.
  216. * (If we're not in recursive mode, we need not even check: the
  217. * subsequent FXP_OPEN will return a usable error message.)
  218. */
  219. if (recurse) {
  220. bool result;
  221. req = fxp_stat_send(fname);
  222. pktin = sftp_wait_for_reply(req);
  223. result = fxp_stat_recv(pktin, req, &attrs);
  224. if (result &&
  225. (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
  226. (attrs.permissions & 0040000)) {
  227. struct fxp_handle *dirhandle;
  228. size_t nnames, namesize;
  229. struct fxp_name **ournames;
  230. struct fxp_names *names;
  231. int i;
  232. /*
  233. * First, attempt to create the destination directory,
  234. * unless it already exists.
  235. */
  236. if (file_type(outfname) != FILE_TYPE_DIRECTORY &&
  237. !create_directory(outfname)) {
  238. with_stripctrl(san, outfname)
  239. printf("%s: Cannot create directory\n", san);
  240. return false;
  241. }
  242. /*
  243. * Now get the list of filenames in the remote
  244. * directory.
  245. */
  246. req = fxp_opendir_send(fname);
  247. pktin = sftp_wait_for_reply(req);
  248. dirhandle = fxp_opendir_recv(pktin, req);
  249. if (!dirhandle) {
  250. with_stripctrl(san, fname)
  251. printf("%s: unable to open directory: %s\n",
  252. san, fxp_error());
  253. return false;
  254. }
  255. nnames = namesize = 0;
  256. ournames = NULL;
  257. while (1) {
  258. int i;
  259. req = fxp_readdir_send(dirhandle);
  260. pktin = sftp_wait_for_reply(req);
  261. names = fxp_readdir_recv(pktin, req);
  262. if (names == NULL) {
  263. if (fxp_error_type() == SSH_FX_EOF)
  264. break;
  265. with_stripctrl(san, fname)
  266. printf("%s: reading directory: %s\n",
  267. san, fxp_error());
  268. req = fxp_close_send(dirhandle);
  269. pktin = sftp_wait_for_reply(req);
  270. fxp_close_recv(pktin, req);
  271. sfree(ournames);
  272. return false;
  273. }
  274. if (names->nnames == 0) {
  275. fxp_free_names(names);
  276. break;
  277. }
  278. sgrowarrayn(ournames, namesize, nnames, names->nnames);
  279. for (i = 0; i < names->nnames; i++)
  280. if (strcmp(names->names[i].filename, ".") &&
  281. strcmp(names->names[i].filename, "..")) {
  282. if (!vet_filename(names->names[i].filename)) {
  283. with_stripctrl(san, names->names[i].filename)
  284. printf("ignoring potentially dangerous server-"
  285. "supplied filename '%s'\n", san);
  286. } else {
  287. ournames[nnames++] =
  288. fxp_dup_name(&names->names[i]);
  289. }
  290. }
  291. fxp_free_names(names);
  292. }
  293. req = fxp_close_send(dirhandle);
  294. pktin = sftp_wait_for_reply(req);
  295. fxp_close_recv(pktin, req);
  296. /*
  297. * Sort the names into a clear order. This ought to
  298. * make things more predictable when we're doing a
  299. * reget of the same directory, just in case two
  300. * readdirs on the same remote directory return a
  301. * different order.
  302. */
  303. if (nnames > 0)
  304. qsort(ournames, nnames, sizeof(*ournames), sftp_name_compare);
  305. /*
  306. * If we're in restart mode, find the last filename on
  307. * this list that already exists. We may have to do a
  308. * reget on _that_ file, but shouldn't have to do
  309. * anything on the previous files.
  310. *
  311. * If none of them exists, of course, we start at 0.
  312. */
  313. i = 0;
  314. if (restart) {
  315. while (i < nnames) {
  316. char *nextoutfname;
  317. bool nonexistent;
  318. nextoutfname = dir_file_cat(outfname,
  319. ournames[i]->filename);
  320. nonexistent = (file_type(nextoutfname) ==
  321. FILE_TYPE_NONEXISTENT);
  322. sfree(nextoutfname);
  323. if (nonexistent)
  324. break;
  325. i++;
  326. }
  327. if (i > 0)
  328. i--;
  329. }
  330. /*
  331. * Now we're ready to recurse. Starting at ournames[i]
  332. * and continuing on to the end of the list, we
  333. * construct a new source and target file name, and
  334. * call sftp_get_file again.
  335. */
  336. for (; i < nnames; i++) {
  337. char *nextfname, *nextoutfname;
  338. bool retd;
  339. nextfname = dupcat(fname, "/", ournames[i]->filename);
  340. nextoutfname = dir_file_cat(outfname, ournames[i]->filename);
  341. retd = sftp_get_file(
  342. nextfname, nextoutfname, recurse, restart);
  343. restart = false; /* after first partial file, do full */
  344. sfree(nextoutfname);
  345. sfree(nextfname);
  346. if (!retd) {
  347. for (i = 0; i < nnames; i++) {
  348. fxp_free_name(ournames[i]);
  349. }
  350. sfree(ournames);
  351. return false;
  352. }
  353. }
  354. /*
  355. * Done this recursion level. Free everything.
  356. */
  357. for (i = 0; i < nnames; i++) {
  358. fxp_free_name(ournames[i]);
  359. }
  360. sfree(ournames);
  361. return true;
  362. }
  363. }
  364. req = fxp_stat_send(fname);
  365. pktin = sftp_wait_for_reply(req);
  366. if (!fxp_stat_recv(pktin, req, &attrs))
  367. attrs.flags = 0;
  368. req = fxp_open_send(fname, SSH_FXF_READ, NULL);
  369. pktin = sftp_wait_for_reply(req);
  370. fh = fxp_open_recv(pktin, req);
  371. if (!fh) {
  372. with_stripctrl(san, fname)
  373. printf("%s: open for read: %s\n", san, fxp_error());
  374. return false;
  375. }
  376. if (restart) {
  377. file = open_existing_wfile(outfname, NULL);
  378. } else {
  379. file = open_new_file(outfname, GET_PERMISSIONS(attrs, -1));
  380. }
  381. if (!file) {
  382. with_stripctrl(san, outfname)
  383. printf("local: unable to open %s\n", san);
  384. req = fxp_close_send(fh);
  385. pktin = sftp_wait_for_reply(req);
  386. fxp_close_recv(pktin, req);
  387. return false;
  388. }
  389. if (restart) {
  390. if (seek_file(file, 0, FROM_END) == -1) {
  391. close_wfile(file);
  392. with_stripctrl(san, outfname)
  393. printf("reget: cannot restart %s - file too large\n", san);
  394. req = fxp_close_send(fh);
  395. pktin = sftp_wait_for_reply(req);
  396. fxp_close_recv(pktin, req);
  397. return false;
  398. }
  399. offset = get_file_posn(file);
  400. printf("reget: restarting at file position %"PRIu64"\n", offset);
  401. } else {
  402. offset = 0;
  403. }
  404. with_stripctrl(san, fname) {
  405. with_stripctrl(sano, outfname)
  406. printf("remote:%s => local:%s\n", san, sano);
  407. }
  408. /*
  409. * FIXME: we can use FXP_FSTAT here to get the file size, and
  410. * thus put up a progress bar.
  411. */
  412. toret = true;
  413. xfer = xfer_download_init(fh, offset);
  414. while (!xfer_done(xfer)) {
  415. void *vbuf;
  416. int retd, len;
  417. int wpos, wlen;
  418. xfer_download_queue(xfer);
  419. pktin = sftp_recv();
  420. retd = xfer_download_gotpkt(xfer, pktin);
  421. if (retd <= 0) {
  422. if (!shown_err) {
  423. printf("error while reading: %s\n", fxp_error());
  424. shown_err = true;
  425. }
  426. if (retd == INT_MIN) /* pktin not even freed */
  427. sfree(pktin);
  428. toret = false;
  429. }
  430. while (xfer_download_data(xfer, &vbuf, &len)) {
  431. unsigned char *buf = (unsigned char *)vbuf;
  432. wpos = 0;
  433. while (wpos < len) {
  434. wlen = write_to_file(file, buf + wpos, len - wpos);
  435. if (wlen <= 0) {
  436. printf("error while writing local file\n");
  437. toret = false;
  438. xfer_set_error(xfer);
  439. break;
  440. }
  441. wpos += wlen;
  442. }
  443. if (wpos < len) { /* we had an error */
  444. toret = false;
  445. xfer_set_error(xfer);
  446. }
  447. sfree(vbuf);
  448. }
  449. }
  450. xfer_cleanup(xfer);
  451. close_wfile(file);
  452. req = fxp_close_send(fh);
  453. pktin = sftp_wait_for_reply(req);
  454. fxp_close_recv(pktin, req);
  455. return toret;
  456. }
  457. bool sftp_put_file(char *fname, char *outfname, bool recurse, bool restart)
  458. {
  459. struct fxp_handle *fh;
  460. struct fxp_xfer *xfer;
  461. struct sftp_packet *pktin;
  462. struct sftp_request *req;
  463. uint64_t offset;
  464. RFile *file;
  465. bool err = false, eof;
  466. struct fxp_attrs attrs;
  467. long permissions;
  468. /*
  469. * In recursive mode, see if we're dealing with a directory.
  470. * (If we're not in recursive mode, we need not even check: the
  471. * subsequent fopen will return an error message.)
  472. */
  473. if (recurse && file_type(fname) == FILE_TYPE_DIRECTORY) {
  474. bool result;
  475. size_t nnames, namesize;
  476. char *name, **ournames;
  477. const char *opendir_err;
  478. DirHandle *dh;
  479. size_t i;
  480. /*
  481. * First, attempt to create the destination directory,
  482. * unless it already exists.
  483. */
  484. req = fxp_stat_send(outfname);
  485. pktin = sftp_wait_for_reply(req);
  486. result = fxp_stat_recv(pktin, req, &attrs);
  487. if (!result ||
  488. !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) ||
  489. !(attrs.permissions & 0040000)) {
  490. req = fxp_mkdir_send(outfname, NULL);
  491. pktin = sftp_wait_for_reply(req);
  492. result = fxp_mkdir_recv(pktin, req);
  493. if (!result) {
  494. printf("%s: create directory: %s\n",
  495. outfname, fxp_error());
  496. return false;
  497. }
  498. }
  499. /*
  500. * Now get the list of filenames in the local directory.
  501. */
  502. nnames = namesize = 0;
  503. ournames = NULL;
  504. dh = open_directory(fname, &opendir_err);
  505. if (!dh) {
  506. printf("%s: unable to open directory: %s\n", fname, opendir_err);
  507. return false;
  508. }
  509. while ((name = read_filename(dh)) != NULL) {
  510. sgrowarray(ournames, namesize, nnames);
  511. ournames[nnames++] = name;
  512. }
  513. close_directory(dh);
  514. /*
  515. * Sort the names into a clear order. This ought to make
  516. * things more predictable when we're doing a reput of the
  517. * same directory, just in case two readdirs on the same
  518. * local directory return a different order.
  519. */
  520. if (nnames > 0)
  521. qsort(ournames, nnames, sizeof(*ournames), bare_name_compare);
  522. /*
  523. * If we're in restart mode, find the last filename on this
  524. * list that already exists. We may have to do a reput on
  525. * _that_ file, but shouldn't have to do anything on the
  526. * previous files.
  527. *
  528. * If none of them exists, of course, we start at 0.
  529. */
  530. i = 0;
  531. if (restart) {
  532. while (i < nnames) {
  533. char *nextoutfname;
  534. nextoutfname = dupcat(outfname, "/", ournames[i]);
  535. req = fxp_stat_send(nextoutfname);
  536. pktin = sftp_wait_for_reply(req);
  537. result = fxp_stat_recv(pktin, req, &attrs);
  538. sfree(nextoutfname);
  539. if (!result)
  540. break;
  541. i++;
  542. }
  543. if (i > 0)
  544. i--;
  545. }
  546. /*
  547. * Now we're ready to recurse. Starting at ournames[i]
  548. * and continuing on to the end of the list, we
  549. * construct a new source and target file name, and
  550. * call sftp_put_file again.
  551. */
  552. for (; i < nnames; i++) {
  553. char *nextfname, *nextoutfname;
  554. bool retd;
  555. nextfname = dir_file_cat(fname, ournames[i]);
  556. nextoutfname = dupcat(outfname, "/", ournames[i]);
  557. retd = sftp_put_file(nextfname, nextoutfname, recurse, restart);
  558. restart = false; /* after first partial file, do full */
  559. sfree(nextoutfname);
  560. sfree(nextfname);
  561. if (!retd) {
  562. for (size_t i = 0; i < nnames; i++) {
  563. sfree(ournames[i]);
  564. }
  565. sfree(ournames);
  566. return false;
  567. }
  568. }
  569. /*
  570. * Done this recursion level. Free everything.
  571. */
  572. for (size_t i = 0; i < nnames; i++) {
  573. sfree(ournames[i]);
  574. }
  575. sfree(ournames);
  576. return true;
  577. }
  578. file = open_existing_file(fname, NULL, NULL, NULL, &permissions);
  579. if (!file) {
  580. printf("local: unable to open %s\n", fname);
  581. return false;
  582. }
  583. attrs.flags = 0;
  584. PUT_PERMISSIONS(attrs, permissions);
  585. if (restart) {
  586. req = fxp_open_send(outfname, SSH_FXF_WRITE, &attrs);
  587. } else {
  588. req = fxp_open_send(outfname,
  589. SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC,
  590. &attrs);
  591. }
  592. pktin = sftp_wait_for_reply(req);
  593. fh = fxp_open_recv(pktin, req);
  594. if (!fh) {
  595. close_rfile(file);
  596. printf("%s: open for write: %s\n", outfname, fxp_error());
  597. return false;
  598. }
  599. if (restart) {
  600. struct fxp_attrs attrs;
  601. bool retd;
  602. req = fxp_fstat_send(fh);
  603. pktin = sftp_wait_for_reply(req);
  604. retd = fxp_fstat_recv(pktin, req, &attrs);
  605. if (!retd) {
  606. printf("read size of %s: %s\n", outfname, fxp_error());
  607. err = true;
  608. goto cleanup;
  609. }
  610. if (!(attrs.flags & SSH_FILEXFER_ATTR_SIZE)) {
  611. printf("read size of %s: size was not given\n", outfname);
  612. err = true;
  613. goto cleanup;
  614. }
  615. offset = attrs.size;
  616. printf("reput: restarting at file position %"PRIu64"\n", offset);
  617. if (seek_file((WFile *)file, offset, FROM_START) != 0)
  618. seek_file((WFile *)file, 0, FROM_END); /* *shrug* */
  619. } else {
  620. offset = 0;
  621. }
  622. printf("local:%s => remote:%s\n", fname, outfname);
  623. /*
  624. * FIXME: we can use FXP_FSTAT here to get the file size, and
  625. * thus put up a progress bar.
  626. */
  627. xfer = xfer_upload_init(fh, offset);
  628. eof = false;
  629. while ((!err && !eof) || !xfer_done(xfer)) {
  630. char buffer[4096];
  631. int len, ret;
  632. while (xfer_upload_ready(xfer) && !err && !eof) {
  633. len = read_from_file(file, buffer, sizeof(buffer));
  634. if (len == -1) {
  635. printf("error while reading local file\n");
  636. err = true;
  637. } else if (len == 0) {
  638. eof = true;
  639. } else {
  640. xfer_upload_data(xfer, buffer, len);
  641. }
  642. }
  643. if (toplevel_callback_pending() && !err && !eof) {
  644. /* If we have pending callbacks, they might make
  645. * xfer_upload_ready start to return true. So we should
  646. * run them and then re-check xfer_upload_ready, before
  647. * we go as far as waiting for an entire packet to
  648. * arrive. */
  649. run_toplevel_callbacks();
  650. continue;
  651. }
  652. if (!xfer_done(xfer)) {
  653. pktin = sftp_recv();
  654. ret = xfer_upload_gotpkt(xfer, pktin);
  655. if (ret <= 0) {
  656. if (ret == INT_MIN) /* pktin not even freed */
  657. sfree(pktin);
  658. if (!err) {
  659. printf("error while writing: %s\n", fxp_error());
  660. err = true;
  661. }
  662. }
  663. }
  664. }
  665. xfer_cleanup(xfer);
  666. cleanup:
  667. req = fxp_close_send(fh);
  668. pktin = sftp_wait_for_reply(req);
  669. if (!fxp_close_recv(pktin, req)) {
  670. if (!err) {
  671. printf("error while closing: %s", fxp_error());
  672. err = true;
  673. }
  674. }
  675. close_rfile(file);
  676. return !err;
  677. }
  678. /* ----------------------------------------------------------------------
  679. * A remote wildcard matcher, providing a similar interface to the
  680. * local one in psftp.h.
  681. */
  682. typedef struct SftpWildcardMatcher {
  683. struct fxp_handle *dirh;
  684. struct fxp_names *names;
  685. int namepos;
  686. char *wildcard, *prefix;
  687. } SftpWildcardMatcher;
  688. SftpWildcardMatcher *sftp_begin_wildcard_matching(char *name)
  689. {
  690. struct sftp_packet *pktin;
  691. struct sftp_request *req;
  692. char *wildcard;
  693. char *unwcdir, *tmpdir, *cdir;
  694. int len;
  695. bool check;
  696. SftpWildcardMatcher *swcm;
  697. struct fxp_handle *dirh;
  698. /*
  699. * We don't handle multi-level wildcards; so we expect to find
  700. * a fully specified directory part, followed by a wildcard
  701. * after that.
  702. */
  703. wildcard = stripslashes(name, false);
  704. unwcdir = dupstr(name);
  705. len = wildcard - name;
  706. unwcdir[len] = '\0';
  707. if (len > 0 && unwcdir[len-1] == '/')
  708. unwcdir[len-1] = '\0';
  709. tmpdir = snewn(1 + len, char);
  710. check = wc_unescape(tmpdir, unwcdir);
  711. sfree(tmpdir);
  712. if (!check) {
  713. printf("Multiple-level wildcards are not supported\n");
  714. sfree(unwcdir);
  715. return NULL;
  716. }
  717. cdir = canonify(unwcdir);
  718. req = fxp_opendir_send(cdir);
  719. pktin = sftp_wait_for_reply(req);
  720. dirh = fxp_opendir_recv(pktin, req);
  721. if (dirh) {
  722. swcm = snew(SftpWildcardMatcher);
  723. swcm->dirh = dirh;
  724. swcm->names = NULL;
  725. swcm->wildcard = dupstr(wildcard);
  726. swcm->prefix = unwcdir;
  727. } else {
  728. printf("Unable to open %s: %s\n", cdir, fxp_error());
  729. swcm = NULL;
  730. sfree(unwcdir);
  731. }
  732. sfree(cdir);
  733. return swcm;
  734. }
  735. char *sftp_wildcard_get_filename(SftpWildcardMatcher *swcm)
  736. {
  737. struct fxp_name *name;
  738. struct sftp_packet *pktin;
  739. struct sftp_request *req;
  740. while (1) {
  741. if (swcm->names && swcm->namepos >= swcm->names->nnames) {
  742. fxp_free_names(swcm->names);
  743. swcm->names = NULL;
  744. }
  745. if (!swcm->names) {
  746. req = fxp_readdir_send(swcm->dirh);
  747. pktin = sftp_wait_for_reply(req);
  748. swcm->names = fxp_readdir_recv(pktin, req);
  749. if (!swcm->names) {
  750. if (fxp_error_type() != SSH_FX_EOF) {
  751. with_stripctrl(san, swcm->prefix)
  752. printf("%s: reading directory: %s\n",
  753. san, fxp_error());
  754. }
  755. return NULL;
  756. } else if (swcm->names->nnames == 0) {
  757. /*
  758. * Another failure mode which we treat as EOF is if
  759. * the server reports success from FXP_READDIR but
  760. * returns no actual names. This is unusual, since
  761. * from most servers you'd expect at least "." and
  762. * "..", but there's nothing forbidding a server from
  763. * omitting those if it wants to.
  764. */
  765. return NULL;
  766. }
  767. swcm->namepos = 0;
  768. }
  769. assert(swcm->names && swcm->namepos < swcm->names->nnames);
  770. name = &swcm->names->names[swcm->namepos++];
  771. if (!strcmp(name->filename, ".") || !strcmp(name->filename, ".."))
  772. continue; /* expected bad filenames */
  773. if (!vet_filename(name->filename)) {
  774. with_stripctrl(san, name->filename)
  775. printf("ignoring potentially dangerous server-"
  776. "supplied filename '%s'\n", san);
  777. continue; /* unexpected bad filename */
  778. }
  779. if (!wc_match(swcm->wildcard, name->filename))
  780. continue; /* doesn't match the wildcard */
  781. /*
  782. * We have a working filename. Return it.
  783. */
  784. return dupprintf("%s%s%s", swcm->prefix,
  785. (!swcm->prefix[0] ||
  786. swcm->prefix[strlen(swcm->prefix)-1]=='/' ?
  787. "" : "/"),
  788. name->filename);
  789. }
  790. }
  791. void sftp_finish_wildcard_matching(SftpWildcardMatcher *swcm)
  792. {
  793. struct sftp_packet *pktin;
  794. struct sftp_request *req;
  795. req = fxp_close_send(swcm->dirh);
  796. pktin = sftp_wait_for_reply(req);
  797. fxp_close_recv(pktin, req);
  798. if (swcm->names)
  799. fxp_free_names(swcm->names);
  800. sfree(swcm->prefix);
  801. sfree(swcm->wildcard);
  802. sfree(swcm);
  803. }
  804. /*
  805. * General function to match a potential wildcard in a filename
  806. * argument and iterate over every matching file. Used in several
  807. * PSFTP commands (rmdir, rm, chmod, mv).
  808. */
  809. bool wildcard_iterate(char *filename, bool (*func)(void *, char *), void *ctx)
  810. {
  811. char *unwcfname, *newname, *cname;
  812. bool is_wc, toret;
  813. unwcfname = snewn(strlen(filename)+1, char);
  814. is_wc = !wc_unescape(unwcfname, filename);
  815. if (is_wc) {
  816. SftpWildcardMatcher *swcm = sftp_begin_wildcard_matching(filename);
  817. bool matched = false;
  818. sfree(unwcfname);
  819. if (!swcm)
  820. return false;
  821. toret = true;
  822. while ( (newname = sftp_wildcard_get_filename(swcm)) != NULL ) {
  823. cname = canonify(newname);
  824. sfree(newname);
  825. matched = true;
  826. if (!func(ctx, cname))
  827. toret = false;
  828. sfree(cname);
  829. }
  830. if (!matched) {
  831. /* Politely warn the user that nothing matched. */
  832. printf("%s: nothing matched\n", filename);
  833. }
  834. sftp_finish_wildcard_matching(swcm);
  835. } else {
  836. cname = canonify(unwcfname);
  837. toret = func(ctx, cname);
  838. sfree(cname);
  839. sfree(unwcfname);
  840. }
  841. return toret;
  842. }
  843. /*
  844. * Handy helper function.
  845. */
  846. bool is_wildcard(char *name)
  847. {
  848. char *unwcfname = snewn(strlen(name)+1, char);
  849. bool is_wc = !wc_unescape(unwcfname, name);
  850. sfree(unwcfname);
  851. return is_wc;
  852. }
  853. /* ----------------------------------------------------------------------
  854. * Actual sftp commands.
  855. */
  856. struct sftp_command {
  857. char **words;
  858. size_t nwords, wordssize;
  859. int (*obey) (struct sftp_command *); /* returns <0 to quit */
  860. };
  861. int sftp_cmd_null(struct sftp_command *cmd)
  862. {
  863. return 1; /* success */
  864. }
  865. int sftp_cmd_unknown(struct sftp_command *cmd)
  866. {
  867. printf("psftp: unknown command \"%s\"\n", cmd->words[0]);
  868. return 0; /* failure */
  869. }
  870. int sftp_cmd_quit(struct sftp_command *cmd)
  871. {
  872. return -1;
  873. }
  874. int sftp_cmd_close(struct sftp_command *cmd)
  875. {
  876. if (!backend) {
  877. not_connected();
  878. return 0;
  879. }
  880. if (backend_connected(backend)) {
  881. char ch;
  882. backend_special(backend, SS_EOF, 0);
  883. sent_eof = true;
  884. sftp_recvdata(&ch, 1);
  885. }
  886. do_sftp_cleanup();
  887. return 0;
  888. }
  889. void list_directory_from_sftp_warn_unsorted(void)
  890. {
  891. printf("Directory is too large to sort; writing file names unsorted\n");
  892. }
  893. void list_directory_from_sftp_print(struct fxp_name *name)
  894. {
  895. with_stripctrl(san, name->longname)
  896. printf("%s\n", san);
  897. }
  898. /*
  899. * List a directory. If no arguments are given, list pwd; otherwise
  900. * list the directory given in words[1].
  901. */
  902. int sftp_cmd_ls(struct sftp_command *cmd)
  903. {
  904. struct fxp_handle *dirh;
  905. struct fxp_names *names;
  906. const char *dir;
  907. char *cdir, *unwcdir, *wildcard;
  908. struct sftp_packet *pktin;
  909. struct sftp_request *req;
  910. if (!backend) {
  911. not_connected();
  912. return 0;
  913. }
  914. if (cmd->nwords < 2)
  915. dir = ".";
  916. else
  917. dir = cmd->words[1];
  918. unwcdir = snewn(1 + strlen(dir), char);
  919. if (wc_unescape(unwcdir, dir)) {
  920. dir = unwcdir;
  921. wildcard = NULL;
  922. } else {
  923. char *tmpdir;
  924. int len;
  925. bool check;
  926. sfree(unwcdir);
  927. wildcard = stripslashes(dir, false);
  928. unwcdir = dupstr(dir);
  929. len = wildcard - dir;
  930. unwcdir[len] = '\0';
  931. if (len > 0 && unwcdir[len-1] == '/')
  932. unwcdir[len-1] = '\0';
  933. tmpdir = snewn(1 + len, char);
  934. check = wc_unescape(tmpdir, unwcdir);
  935. sfree(tmpdir);
  936. if (!check) {
  937. printf("Multiple-level wildcards are not supported\n");
  938. sfree(unwcdir);
  939. return 0;
  940. }
  941. dir = unwcdir;
  942. }
  943. cdir = canonify(dir);
  944. with_stripctrl(san, cdir)
  945. printf("Listing directory %s\n", san);
  946. req = fxp_opendir_send(cdir);
  947. pktin = sftp_wait_for_reply(req);
  948. dirh = fxp_opendir_recv(pktin, req);
  949. if (dirh == NULL) {
  950. printf("Unable to open %s: %s\n", dir, fxp_error());
  951. sfree(cdir);
  952. sfree(unwcdir);
  953. return 0;
  954. } else {
  955. struct list_directory_from_sftp_ctx *ctx =
  956. list_directory_from_sftp_new();
  957. while (1) {
  958. req = fxp_readdir_send(dirh);
  959. pktin = sftp_wait_for_reply(req);
  960. names = fxp_readdir_recv(pktin, req);
  961. if (names == NULL) {
  962. if (fxp_error_type() == SSH_FX_EOF)
  963. break;
  964. printf("Reading directory %s: %s\n", dir, fxp_error());
  965. break;
  966. }
  967. if (names->nnames == 0) {
  968. fxp_free_names(names);
  969. break;
  970. }
  971. for (size_t i = 0; i < names->nnames; i++)
  972. if (!wildcard || wc_match(wildcard, names->names[i].filename))
  973. list_directory_from_sftp_feed(ctx, &names->names[i]);
  974. fxp_free_names(names);
  975. }
  976. req = fxp_close_send(dirh);
  977. pktin = sftp_wait_for_reply(req);
  978. fxp_close_recv(pktin, req);
  979. list_directory_from_sftp_finish(ctx);
  980. list_directory_from_sftp_free(ctx);
  981. }
  982. sfree(cdir);
  983. sfree(unwcdir);
  984. return 1;
  985. }
  986. /*
  987. * Change directories. We do this by canonifying the new name, then
  988. * trying to OPENDIR it. Only if that succeeds do we set the new pwd.
  989. */
  990. int sftp_cmd_cd(struct sftp_command *cmd)
  991. {
  992. struct fxp_handle *dirh;
  993. struct sftp_packet *pktin;
  994. struct sftp_request *req;
  995. char *dir;
  996. if (!backend) {
  997. not_connected();
  998. return 0;
  999. }
  1000. if (cmd->nwords < 2)
  1001. dir = dupstr(homedir);
  1002. else {
  1003. dir = canonify(cmd->words[1]);
  1004. }
  1005. req = fxp_opendir_send(dir);
  1006. pktin = sftp_wait_for_reply(req);
  1007. dirh = fxp_opendir_recv(pktin, req);
  1008. if (!dirh) {
  1009. with_stripctrl(san, dir)
  1010. printf("Directory %s: %s\n", san, fxp_error());
  1011. sfree(dir);
  1012. return 0;
  1013. }
  1014. req = fxp_close_send(dirh);
  1015. pktin = sftp_wait_for_reply(req);
  1016. fxp_close_recv(pktin, req);
  1017. sfree(pwd);
  1018. pwd = dir;
  1019. with_stripctrl(san, pwd)
  1020. printf("Remote directory is now %s\n", san);
  1021. return 1;
  1022. }
  1023. /*
  1024. * Print current directory. Easy as pie.
  1025. */
  1026. int sftp_cmd_pwd(struct sftp_command *cmd)
  1027. {
  1028. if (!backend) {
  1029. not_connected();
  1030. return 0;
  1031. }
  1032. with_stripctrl(san, pwd)
  1033. printf("Remote directory is %s\n", san);
  1034. return 1;
  1035. }
  1036. /*
  1037. * Get a file and save it at the local end. We have three very
  1038. * similar commands here. The basic one is `get'; `reget' differs
  1039. * in that it checks for the existence of the destination file and
  1040. * starts from where a previous aborted transfer left off; `mget'
  1041. * differs in that it interprets all its arguments as files to
  1042. * transfer (never as a different local name for a remote file) and
  1043. * can handle wildcards.
  1044. */
  1045. int sftp_general_get(struct sftp_command *cmd, bool restart, bool multiple)
  1046. {
  1047. char *fname, *unwcfname, *origfname, *origwfname, *outfname;
  1048. int i, toret;
  1049. bool recurse = false;
  1050. if (!backend) {
  1051. not_connected();
  1052. return 0;
  1053. }
  1054. i = 1;
  1055. while (i < cmd->nwords && cmd->words[i][0] == '-') {
  1056. if (!strcmp(cmd->words[i], "--")) {
  1057. /* finish processing options */
  1058. i++;
  1059. break;
  1060. } else if (!strcmp(cmd->words[i], "-r")) {
  1061. recurse = true;
  1062. } else {
  1063. printf("%s: unrecognised option '%s'\n", cmd->words[0], cmd->words[i]);
  1064. return 0;
  1065. }
  1066. i++;
  1067. }
  1068. if (i >= cmd->nwords) {
  1069. printf("%s: expects a filename\n", cmd->words[0]);
  1070. return 0;
  1071. }
  1072. toret = 1;
  1073. do {
  1074. SftpWildcardMatcher *swcm;
  1075. origfname = cmd->words[i++];
  1076. unwcfname = snewn(strlen(origfname)+1, char);
  1077. if (multiple && !wc_unescape(unwcfname, origfname)) {
  1078. swcm = sftp_begin_wildcard_matching(origfname);
  1079. if (!swcm) {
  1080. sfree(unwcfname);
  1081. continue;
  1082. }
  1083. origwfname = sftp_wildcard_get_filename(swcm);
  1084. if (!origwfname) {
  1085. /* Politely warn the user that nothing matched. */
  1086. printf("%s: nothing matched\n", origfname);
  1087. sftp_finish_wildcard_matching(swcm);
  1088. sfree(unwcfname);
  1089. continue;
  1090. }
  1091. } else {
  1092. origwfname = origfname;
  1093. swcm = NULL;
  1094. }
  1095. while (origwfname) {
  1096. fname = canonify(origwfname);
  1097. if (!multiple && i < cmd->nwords)
  1098. outfname = cmd->words[i++];
  1099. else
  1100. outfname = stripslashes(origwfname, false);
  1101. toret = sftp_get_file(fname, outfname, recurse, restart);
  1102. sfree(fname);
  1103. if (swcm) {
  1104. sfree(origwfname);
  1105. origwfname = sftp_wildcard_get_filename(swcm);
  1106. } else {
  1107. origwfname = NULL;
  1108. }
  1109. }
  1110. sfree(unwcfname);
  1111. if (swcm)
  1112. sftp_finish_wildcard_matching(swcm);
  1113. if (!toret)
  1114. return toret;
  1115. } while (multiple && i < cmd->nwords);
  1116. return toret;
  1117. }
  1118. int sftp_cmd_get(struct sftp_command *cmd)
  1119. {
  1120. return sftp_general_get(cmd, false, false);
  1121. }
  1122. int sftp_cmd_mget(struct sftp_command *cmd)
  1123. {
  1124. return sftp_general_get(cmd, false, true);
  1125. }
  1126. int sftp_cmd_reget(struct sftp_command *cmd)
  1127. {
  1128. return sftp_general_get(cmd, true, false);
  1129. }
  1130. /*
  1131. * Send a file and store it at the remote end. We have three very
  1132. * similar commands here. The basic one is `put'; `reput' differs
  1133. * in that it checks for the existence of the destination file and
  1134. * starts from where a previous aborted transfer left off; `mput'
  1135. * differs in that it interprets all its arguments as files to
  1136. * transfer (never as a different remote name for a local file) and
  1137. * can handle wildcards.
  1138. */
  1139. int sftp_general_put(struct sftp_command *cmd, bool restart, bool multiple)
  1140. {
  1141. char *fname, *wfname, *origoutfname, *outfname;
  1142. int i;
  1143. int toret;
  1144. bool recurse = false;
  1145. if (!backend) {
  1146. not_connected();
  1147. return 0;
  1148. }
  1149. i = 1;
  1150. while (i < cmd->nwords && cmd->words[i][0] == '-') {
  1151. if (!strcmp(cmd->words[i], "--")) {
  1152. /* finish processing options */
  1153. i++;
  1154. break;
  1155. } else if (!strcmp(cmd->words[i], "-r")) {
  1156. recurse = true;
  1157. } else {
  1158. printf("%s: unrecognised option '%s'\n", cmd->words[0], cmd->words[i]);
  1159. return 0;
  1160. }
  1161. i++;
  1162. }
  1163. if (i >= cmd->nwords) {
  1164. printf("%s: expects a filename\n", cmd->words[0]);
  1165. return 0;
  1166. }
  1167. toret = 1;
  1168. do {
  1169. WildcardMatcher *wcm;
  1170. fname = cmd->words[i++];
  1171. if (multiple && test_wildcard(fname, false) == WCTYPE_WILDCARD) {
  1172. wcm = begin_wildcard_matching(fname);
  1173. wfname = wildcard_get_filename(wcm);
  1174. if (!wfname) {
  1175. /* Politely warn the user that nothing matched. */
  1176. printf("%s: nothing matched\n", fname);
  1177. finish_wildcard_matching(wcm);
  1178. continue;
  1179. }
  1180. } else {
  1181. wfname = fname;
  1182. wcm = NULL;
  1183. }
  1184. while (wfname) {
  1185. if (!multiple && i < cmd->nwords)
  1186. origoutfname = cmd->words[i++];
  1187. else
  1188. origoutfname = stripslashes(wfname, true);
  1189. outfname = canonify(origoutfname);
  1190. toret = sftp_put_file(wfname, outfname, recurse, restart);
  1191. sfree(outfname);
  1192. if (wcm) {
  1193. sfree(wfname);
  1194. wfname = wildcard_get_filename(wcm);
  1195. } else {
  1196. wfname = NULL;
  1197. }
  1198. }
  1199. if (wcm)
  1200. finish_wildcard_matching(wcm);
  1201. if (!toret)
  1202. return toret;
  1203. } while (multiple && i < cmd->nwords);
  1204. return toret;
  1205. }
  1206. int sftp_cmd_put(struct sftp_command *cmd)
  1207. {
  1208. return sftp_general_put(cmd, false, false);
  1209. }
  1210. int sftp_cmd_mput(struct sftp_command *cmd)
  1211. {
  1212. return sftp_general_put(cmd, false, true);
  1213. }
  1214. int sftp_cmd_reput(struct sftp_command *cmd)
  1215. {
  1216. return sftp_general_put(cmd, true, false);
  1217. }
  1218. int sftp_cmd_mkdir(struct sftp_command *cmd)
  1219. {
  1220. char *dir;
  1221. struct sftp_packet *pktin;
  1222. struct sftp_request *req;
  1223. bool result;
  1224. int i, ret;
  1225. if (!backend) {
  1226. not_connected();
  1227. return 0;
  1228. }
  1229. if (cmd->nwords < 2) {
  1230. printf("mkdir: expects a directory\n");
  1231. return 0;
  1232. }
  1233. ret = 1;
  1234. for (i = 1; i < cmd->nwords; i++) {
  1235. dir = canonify(cmd->words[i]);
  1236. req = fxp_mkdir_send(dir, NULL);
  1237. pktin = sftp_wait_for_reply(req);
  1238. result = fxp_mkdir_recv(pktin, req);
  1239. if (!result) {
  1240. with_stripctrl(san, dir)
  1241. printf("mkdir %s: %s\n", san, fxp_error());
  1242. ret = 0;
  1243. } else
  1244. with_stripctrl(san, dir)
  1245. printf("mkdir %s: OK\n", san);
  1246. sfree(dir);
  1247. }
  1248. return ret;
  1249. }
  1250. static bool sftp_action_rmdir(void *vctx, char *dir)
  1251. {
  1252. struct sftp_packet *pktin;
  1253. struct sftp_request *req;
  1254. bool result;
  1255. req = fxp_rmdir_send(dir);
  1256. pktin = sftp_wait_for_reply(req);
  1257. result = fxp_rmdir_recv(pktin, req);
  1258. if (!result) {
  1259. printf("rmdir %s: %s\n", dir, fxp_error());
  1260. return false;
  1261. }
  1262. printf("rmdir %s: OK\n", dir);
  1263. return true;
  1264. }
  1265. int sftp_cmd_rmdir(struct sftp_command *cmd)
  1266. {
  1267. int i, ret;
  1268. if (!backend) {
  1269. not_connected();
  1270. return 0;
  1271. }
  1272. if (cmd->nwords < 2) {
  1273. printf("rmdir: expects a directory\n");
  1274. return 0;
  1275. }
  1276. ret = 1;
  1277. for (i = 1; i < cmd->nwords; i++)
  1278. ret &= wildcard_iterate(cmd->words[i], sftp_action_rmdir, NULL);
  1279. return ret;
  1280. }
  1281. static bool sftp_action_rm(void *vctx, char *fname)
  1282. {
  1283. struct sftp_packet *pktin;
  1284. struct sftp_request *req;
  1285. bool result;
  1286. req = fxp_remove_send(fname);
  1287. pktin = sftp_wait_for_reply(req);
  1288. result = fxp_remove_recv(pktin, req);
  1289. if (!result) {
  1290. printf("rm %s: %s\n", fname, fxp_error());
  1291. return false;
  1292. }
  1293. printf("rm %s: OK\n", fname);
  1294. return true;
  1295. }
  1296. int sftp_cmd_rm(struct sftp_command *cmd)
  1297. {
  1298. int i, ret;
  1299. if (!backend) {
  1300. not_connected();
  1301. return 0;
  1302. }
  1303. if (cmd->nwords < 2) {
  1304. printf("rm: expects a filename\n");
  1305. return 0;
  1306. }
  1307. ret = 1;
  1308. for (i = 1; i < cmd->nwords; i++)
  1309. ret &= wildcard_iterate(cmd->words[i], sftp_action_rm, NULL);
  1310. return ret;
  1311. }
  1312. static bool check_is_dir(char *dstfname)
  1313. {
  1314. struct sftp_packet *pktin;
  1315. struct sftp_request *req;
  1316. struct fxp_attrs attrs;
  1317. bool result;
  1318. req = fxp_stat_send(dstfname);
  1319. pktin = sftp_wait_for_reply(req);
  1320. result = fxp_stat_recv(pktin, req, &attrs);
  1321. if (result &&
  1322. (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
  1323. (attrs.permissions & 0040000))
  1324. return true;
  1325. else
  1326. return false;
  1327. }
  1328. struct sftp_context_mv {
  1329. char *dstfname;
  1330. bool dest_is_dir;
  1331. };
  1332. static bool sftp_action_mv(void *vctx, char *srcfname)
  1333. {
  1334. struct sftp_context_mv *ctx = (struct sftp_context_mv *)vctx;
  1335. struct sftp_packet *pktin;
  1336. struct sftp_request *req;
  1337. const char *error;
  1338. char *finalfname, *newcanon = NULL;
  1339. bool toret, result;
  1340. if (ctx->dest_is_dir) {
  1341. char *p;
  1342. char *newname;
  1343. p = srcfname + strlen(srcfname);
  1344. while (p > srcfname && p[-1] != '/') p--;
  1345. newname = dupcat(ctx->dstfname, "/", p);
  1346. newcanon = canonify(newname);
  1347. sfree(newname);
  1348. finalfname = newcanon;
  1349. } else {
  1350. finalfname = ctx->dstfname;
  1351. }
  1352. req = fxp_rename_send(srcfname, finalfname);
  1353. pktin = sftp_wait_for_reply(req);
  1354. result = fxp_rename_recv(pktin, req);
  1355. error = result ? NULL : fxp_error();
  1356. if (error) {
  1357. with_stripctrl(san, finalfname)
  1358. printf("mv %s %s: %s\n", srcfname, san, error);
  1359. toret = false;
  1360. } else {
  1361. with_stripctrl(san, finalfname)
  1362. printf("%s -> %s\n", srcfname, san);
  1363. toret = true;
  1364. }
  1365. sfree(newcanon);
  1366. return toret;
  1367. }
  1368. int sftp_cmd_mv(struct sftp_command *cmd)
  1369. {
  1370. struct sftp_context_mv ctx[1];
  1371. int i, ret;
  1372. if (!backend) {
  1373. not_connected();
  1374. return 0;
  1375. }
  1376. if (cmd->nwords < 3) {
  1377. printf("mv: expects two filenames\n");
  1378. return 0;
  1379. }
  1380. ctx->dstfname = canonify(cmd->words[cmd->nwords-1]);
  1381. /*
  1382. * If there's more than one source argument, or one source
  1383. * argument which is a wildcard, we _require_ that the
  1384. * destination is a directory.
  1385. */
  1386. ctx->dest_is_dir = check_is_dir(ctx->dstfname);
  1387. if ((cmd->nwords > 3 || is_wildcard(cmd->words[1])) && !ctx->dest_is_dir) {
  1388. printf("mv: multiple or wildcard arguments require the destination"
  1389. " to be a directory\n");
  1390. sfree(ctx->dstfname);
  1391. return 0;
  1392. }
  1393. /*
  1394. * Now iterate over the source arguments.
  1395. */
  1396. ret = 1;
  1397. for (i = 1; i < cmd->nwords-1; i++)
  1398. ret &= wildcard_iterate(cmd->words[i], sftp_action_mv, ctx);
  1399. sfree(ctx->dstfname);
  1400. return ret;
  1401. }
  1402. struct sftp_context_chmod {
  1403. unsigned attrs_clr, attrs_xor;
  1404. };
  1405. static bool sftp_action_chmod(void *vctx, char *fname)
  1406. {
  1407. struct fxp_attrs attrs;
  1408. struct sftp_packet *pktin;
  1409. struct sftp_request *req;
  1410. bool result;
  1411. unsigned oldperms, newperms;
  1412. struct sftp_context_chmod *ctx = (struct sftp_context_chmod *)vctx;
  1413. req = fxp_stat_send(fname);
  1414. pktin = sftp_wait_for_reply(req);
  1415. result = fxp_stat_recv(pktin, req, &attrs);
  1416. if (!result || !(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) {
  1417. printf("get attrs for %s: %s\n", fname,
  1418. result ? "file permissions not provided" : fxp_error());
  1419. return false;
  1420. }
  1421. attrs.flags = SSH_FILEXFER_ATTR_PERMISSIONS; /* perms _only_ */
  1422. oldperms = attrs.permissions & 07777;
  1423. attrs.permissions &= ~ctx->attrs_clr;
  1424. attrs.permissions ^= ctx->attrs_xor;
  1425. newperms = attrs.permissions & 07777;
  1426. if (oldperms == newperms)
  1427. return true; /* no need to do anything! */
  1428. req = fxp_setstat_send(fname, attrs);
  1429. pktin = sftp_wait_for_reply(req);
  1430. result = fxp_setstat_recv(pktin, req);
  1431. if (!result) {
  1432. printf("set attrs for %s: %s\n", fname, fxp_error());
  1433. return false;
  1434. }
  1435. printf("%s: %04o -> %04o\n", fname, oldperms, newperms);
  1436. return true;
  1437. }
  1438. int sftp_cmd_chmod(struct sftp_command *cmd)
  1439. {
  1440. char *mode;
  1441. int i, ret;
  1442. struct sftp_context_chmod ctx[1];
  1443. if (!backend) {
  1444. not_connected();
  1445. return 0;
  1446. }
  1447. if (cmd->nwords < 3) {
  1448. printf("chmod: expects a mode specifier and a filename\n");
  1449. return 0;
  1450. }
  1451. /*
  1452. * Attempt to parse the mode specifier in cmd->words[1]. We
  1453. * don't support the full horror of Unix chmod; instead we
  1454. * support a much simpler syntax in which the user can either
  1455. * specify an octal number, or a comma-separated sequence of
  1456. * [ugoa]*[-+=][rwxst]+. (The initial [ugoa] sequence may
  1457. * _only_ be omitted if the only attribute mentioned is t,
  1458. * since all others require a user/group/other specification.
  1459. * Additionally, the s attribute may not be specified for any
  1460. * [ugoa] specifications other than exactly u or exactly g.
  1461. */
  1462. ctx->attrs_clr = ctx->attrs_xor = 0;
  1463. mode = cmd->words[1];
  1464. if (mode[0] >= '0' && mode[0] <= '9') {
  1465. if (mode[strspn(mode, "01234567")]) {
  1466. printf("chmod: numeric file modes should"
  1467. " contain digits 0-7 only\n");
  1468. return 0;
  1469. }
  1470. ctx->attrs_clr = 07777;
  1471. sscanf(mode, "%o", &ctx->attrs_xor);
  1472. ctx->attrs_xor &= ctx->attrs_clr;
  1473. } else {
  1474. while (*mode) {
  1475. char *modebegin = mode;
  1476. unsigned subset, perms;
  1477. int action;
  1478. subset = 0;
  1479. while (*mode && *mode != ',' &&
  1480. *mode != '+' && *mode != '-' && *mode != '=') {
  1481. switch (*mode) {
  1482. case 'u': subset |= 04700; break; /* setuid, user perms */
  1483. case 'g': subset |= 02070; break; /* setgid, group perms */
  1484. case 'o': subset |= 00007; break; /* just other perms */
  1485. case 'a': subset |= 06777; break; /* all of the above */
  1486. default:
  1487. printf("chmod: file mode '%.*s' contains unrecognised"
  1488. " user/group/other specifier '%c'\n",
  1489. (int)strcspn(modebegin, ","), modebegin, *mode);
  1490. return 0;
  1491. }
  1492. mode++;
  1493. }
  1494. if (!*mode || *mode == ',') {
  1495. printf("chmod: file mode '%.*s' is incomplete\n",
  1496. (int)strcspn(modebegin, ","), modebegin);
  1497. return 0;
  1498. }
  1499. action = *mode++;
  1500. if (!*mode || *mode == ',') {
  1501. printf("chmod: file mode '%.*s' is incomplete\n",
  1502. (int)strcspn(modebegin, ","), modebegin);
  1503. return 0;
  1504. }
  1505. perms = 0;
  1506. while (*mode && *mode != ',') {
  1507. switch (*mode) {
  1508. case 'r': perms |= 00444; break;
  1509. case 'w': perms |= 00222; break;
  1510. case 'x': perms |= 00111; break;
  1511. case 't': perms |= 01000; subset |= 01000; break;
  1512. case 's':
  1513. if ((subset & 06777) != 04700 &&
  1514. (subset & 06777) != 02070) {
  1515. printf("chmod: file mode '%.*s': set[ug]id bit should"
  1516. " be used with exactly one of u or g only\n",
  1517. (int)strcspn(modebegin, ","), modebegin);
  1518. return 0;
  1519. }
  1520. perms |= 06000;
  1521. break;
  1522. default:
  1523. printf("chmod: file mode '%.*s' contains unrecognised"
  1524. " permission specifier '%c'\n",
  1525. (int)strcspn(modebegin, ","), modebegin, *mode);
  1526. return 0;
  1527. }
  1528. mode++;
  1529. }
  1530. if (!(subset & 06777) && (perms &~ subset)) {
  1531. printf("chmod: file mode '%.*s' contains no user/group/other"
  1532. " specifier and permissions other than 't' \n",
  1533. (int)strcspn(modebegin, ","), modebegin);
  1534. return 0;
  1535. }
  1536. perms &= subset;
  1537. switch (action) {
  1538. case '+':
  1539. ctx->attrs_clr |= perms;
  1540. ctx->attrs_xor |= perms;
  1541. break;
  1542. case '-':
  1543. ctx->attrs_clr |= perms;
  1544. ctx->attrs_xor &= ~perms;
  1545. break;
  1546. case '=':
  1547. ctx->attrs_clr |= subset;
  1548. ctx->attrs_xor |= perms;
  1549. break;
  1550. }
  1551. if (*mode) mode++; /* eat comma */
  1552. }
  1553. }
  1554. ret = 1;
  1555. for (i = 2; i < cmd->nwords; i++)
  1556. ret &= wildcard_iterate(cmd->words[i], sftp_action_chmod, ctx);
  1557. return ret;
  1558. }
  1559. static int sftp_cmd_open(struct sftp_command *cmd)
  1560. {
  1561. int portnumber;
  1562. if (backend) {
  1563. printf("psftp: already connected\n");
  1564. return 0;
  1565. }
  1566. if (cmd->nwords < 2) {
  1567. printf("open: expects a host name\n");
  1568. return 0;
  1569. }
  1570. if (cmd->nwords > 2) {
  1571. portnumber = atoi(cmd->words[2]);
  1572. if (portnumber == 0) {
  1573. printf("open: invalid port number\n");
  1574. return 0;
  1575. }
  1576. } else
  1577. portnumber = 0;
  1578. if (psftp_connect(cmd->words[1], NULL, portnumber)) {
  1579. backend = NULL; /* connection is already closed */
  1580. return -1; /* this is fatal */
  1581. }
  1582. do_sftp_init();
  1583. return 1;
  1584. }
  1585. static int sftp_cmd_lcd(struct sftp_command *cmd)
  1586. {
  1587. char *currdir, *errmsg;
  1588. if (cmd->nwords < 2) {
  1589. printf("lcd: expects a local directory name\n");
  1590. return 0;
  1591. }
  1592. errmsg = psftp_lcd(cmd->words[1]);
  1593. if (errmsg) {
  1594. printf("lcd: unable to change directory: %s\n", errmsg);
  1595. sfree(errmsg);
  1596. return 0;
  1597. }
  1598. currdir = psftp_getcwd();
  1599. printf("New local directory is %s\n", currdir);
  1600. sfree(currdir);
  1601. return 1;
  1602. }
  1603. static int sftp_cmd_lpwd(struct sftp_command *cmd)
  1604. {
  1605. char *currdir;
  1606. currdir = psftp_getcwd();
  1607. printf("Current local directory is %s\n", currdir);
  1608. sfree(currdir);
  1609. return 1;
  1610. }
  1611. static int sftp_cmd_pling(struct sftp_command *cmd)
  1612. {
  1613. int exitcode;
  1614. exitcode = system(cmd->words[1]);
  1615. return (exitcode == 0);
  1616. }
  1617. static int sftp_cmd_help(struct sftp_command *cmd);
  1618. static struct sftp_cmd_lookup {
  1619. const char *name;
  1620. /*
  1621. * For help purposes, there are two kinds of command:
  1622. *
  1623. * - primary commands, in which `longhelp' is non-NULL. In
  1624. * this case `shorthelp' is descriptive text, and `longhelp'
  1625. * is longer descriptive text intended to be printed after
  1626. * the command name.
  1627. *
  1628. * - alias commands, in which `longhelp' is NULL. In this case
  1629. * `shorthelp' is the name of a primary command, which
  1630. * contains the help that should double up for this command.
  1631. */
  1632. bool listed; /* do we list this in primary help? */
  1633. const char *shorthelp;
  1634. const char *longhelp;
  1635. int (*obey) (struct sftp_command *);
  1636. } sftp_lookup[] = {
  1637. /*
  1638. * List of sftp commands. This is binary-searched so it MUST be
  1639. * in ASCII order.
  1640. */
  1641. {
  1642. "!", true, "run a local command",
  1643. "<command>\n"
  1644. /* FIXME: this example is crap for non-Windows. */
  1645. " Runs a local command. For example, \"!del myfile\".\n",
  1646. sftp_cmd_pling
  1647. },
  1648. {
  1649. "bye", true, "finish your SFTP session",
  1650. "\n"
  1651. " Terminates your SFTP session and quits the PSFTP program.\n",
  1652. sftp_cmd_quit
  1653. },
  1654. {
  1655. "cd", true, "change your remote working directory",
  1656. " [ <new working directory> ]\n"
  1657. " Change the remote working directory for your SFTP session.\n"
  1658. " If a new working directory is not supplied, you will be\n"
  1659. " returned to your home directory.\n",
  1660. sftp_cmd_cd
  1661. },
  1662. {
  1663. "chmod", true, "change file permissions and modes",
  1664. " <modes> <filename-or-wildcard> [ <filename-or-wildcard>... ]\n"
  1665. " Change the file permissions on one or more remote files or\n"
  1666. " directories.\n"
  1667. " <modes> can be any octal Unix permission specifier.\n"
  1668. " Alternatively, <modes> can include the following modifiers:\n"
  1669. " u+r make file readable by owning user\n"
  1670. " u+w make file writable by owning user\n"
  1671. " u+x make file executable by owning user\n"
  1672. " u-r make file not readable by owning user\n"
  1673. " [also u-w, u-x]\n"
  1674. " g+r make file readable by members of owning group\n"
  1675. " [also g+w, g+x, g-r, g-w, g-x]\n"
  1676. " o+r make file readable by all other users\n"
  1677. " [also o+w, o+x, o-r, o-w, o-x]\n"
  1678. " a+r make file readable by absolutely everybody\n"
  1679. " [also a+w, a+x, a-r, a-w, a-x]\n"
  1680. " u+s enable the Unix set-user-ID bit\n"
  1681. " u-s disable the Unix set-user-ID bit\n"
  1682. " g+s enable the Unix set-group-ID bit\n"
  1683. " g-s disable the Unix set-group-ID bit\n"
  1684. " +t enable the Unix \"sticky bit\"\n"
  1685. " You can give more than one modifier for the same user (\"g-rwx\"), and\n"
  1686. " more than one user for the same modifier (\"ug+w\"). You can\n"
  1687. " use commas to separate different modifiers (\"u+rwx,g+s\").\n",
  1688. sftp_cmd_chmod
  1689. },
  1690. {
  1691. "close", true, "finish your SFTP session but do not quit PSFTP",
  1692. "\n"
  1693. " Terminates your SFTP session, but does not quit the PSFTP\n"
  1694. " program. You can then use \"open\" to start another SFTP\n"
  1695. " session, to the same server or to a different one.\n",
  1696. sftp_cmd_close
  1697. },
  1698. {
  1699. "del", true, "delete files on the remote server",
  1700. " <filename-or-wildcard> [ <filename-or-wildcard>... ]\n"
  1701. " Delete a file or files from the server.\n",
  1702. sftp_cmd_rm
  1703. },
  1704. {
  1705. "delete", false, "del", NULL, sftp_cmd_rm
  1706. },
  1707. {
  1708. "dir", true, "list remote files",
  1709. " [ <directory-name> ]/[ <wildcard> ]\n"
  1710. " List the contents of a specified directory on the server.\n"
  1711. " If <directory-name> is not given, the current working directory\n"
  1712. " is assumed.\n"
  1713. " If <wildcard> is given, it is treated as a set of files to\n"
  1714. " list; otherwise, all files are listed.\n",
  1715. sftp_cmd_ls
  1716. },
  1717. {
  1718. "exit", true, "bye", NULL, sftp_cmd_quit
  1719. },
  1720. {
  1721. "get", true, "download a file from the server to your local machine",
  1722. " [ -r ] [ -- ] <filename> [ <local-filename> ]\n"
  1723. " Downloads a file on the server and stores it locally under\n"
  1724. " the same name, or under a different one if you supply the\n"
  1725. " argument <local-filename>.\n"
  1726. " If -r specified, recursively fetch a directory.\n",
  1727. sftp_cmd_get
  1728. },
  1729. {
  1730. "help", true, "give help",
  1731. " [ <command> [ <command> ... ] ]\n"
  1732. " Give general help if no commands are specified.\n"
  1733. " If one or more commands are specified, give specific help on\n"
  1734. " those particular commands.\n",
  1735. sftp_cmd_help
  1736. },
  1737. {
  1738. "lcd", true, "change local working directory",
  1739. " <local-directory-name>\n"
  1740. " Change the local working directory of the PSFTP program (the\n"
  1741. " default location where the \"get\" command will save files).\n",
  1742. sftp_cmd_lcd
  1743. },
  1744. {
  1745. "lpwd", true, "print local working directory",
  1746. "\n"
  1747. " Print the local working directory of the PSFTP program (the\n"
  1748. " default location where the \"get\" command will save files).\n",
  1749. sftp_cmd_lpwd
  1750. },
  1751. {
  1752. "ls", true, "dir", NULL,
  1753. sftp_cmd_ls
  1754. },
  1755. {
  1756. "mget", true, "download multiple files at once",
  1757. " [ -r ] [ -- ] <filename-or-wildcard> [ <filename-or-wildcard>... ]\n"
  1758. " Downloads many files from the server, storing each one under\n"
  1759. " the same name it has on the server side. You can use wildcards\n"
  1760. " such as \"*.c\" to specify lots of files at once.\n"
  1761. " If -r specified, recursively fetch files and directories.\n",
  1762. sftp_cmd_mget
  1763. },
  1764. {
  1765. "mkdir", true, "create directories on the remote server",
  1766. " <directory-name> [ <directory-name>... ]\n"
  1767. " Creates directories with the given names on the server.\n",
  1768. sftp_cmd_mkdir
  1769. },
  1770. {
  1771. "mput", true, "upload multiple files at once",
  1772. " [ -r ] [ -- ] <filename-or-wildcard> [ <filename-or-wildcard>... ]\n"
  1773. " Uploads many files to the server, storing each one under the\n"
  1774. " same name it has on the client side. You can use wildcards\n"
  1775. " such as \"*.c\" to specify lots of files at once.\n"
  1776. " If -r specified, recursively store files and directories.\n",
  1777. sftp_cmd_mput
  1778. },
  1779. {
  1780. "mv", true, "move or rename file(s) on the remote server",
  1781. " <source> [ <source>... ] <destination>\n"
  1782. " Moves or renames <source>(s) on the server to <destination>,\n"
  1783. " also on the server.\n"
  1784. " If <destination> specifies an existing directory, then <source>\n"
  1785. " may be a wildcard, and multiple <source>s may be given; all\n"
  1786. " source files are moved into <destination>.\n"
  1787. " Otherwise, <source> must specify a single file, which is moved\n"
  1788. " or renamed so that it is accessible under the name <destination>.\n",
  1789. sftp_cmd_mv
  1790. },
  1791. {
  1792. "open", true, "connect to a host",
  1793. " [<user>@]<hostname> [<port>]\n"
  1794. " Establishes an SFTP connection to a given host. Only usable\n"
  1795. " when you are not already connected to a server.\n",
  1796. sftp_cmd_open
  1797. },
  1798. {
  1799. "put", true, "upload a file from your local machine to the server",
  1800. " [ -r ] [ -- ] <filename> [ <remote-filename> ]\n"
  1801. " Uploads a file to the server and stores it there under\n"
  1802. " the same name, or under a different one if you supply the\n"
  1803. " argument <remote-filename>.\n"
  1804. " If -r specified, recursively store a directory.\n",
  1805. sftp_cmd_put
  1806. },
  1807. {
  1808. "pwd", true, "print your remote working directory",
  1809. "\n"
  1810. " Print the current remote working directory for your SFTP session.\n",
  1811. sftp_cmd_pwd
  1812. },
  1813. {
  1814. "quit", true, "bye", NULL,
  1815. sftp_cmd_quit
  1816. },
  1817. {
  1818. "reget", true, "continue downloading files",
  1819. " [ -r ] [ -- ] <filename> [ <local-filename> ]\n"
  1820. " Works exactly like the \"get\" command, but the local file\n"
  1821. " must already exist. The download will begin at the end of the\n"
  1822. " file. This is for resuming a download that was interrupted.\n"
  1823. " If -r specified, resume interrupted \"get -r\".\n",
  1824. sftp_cmd_reget
  1825. },
  1826. {
  1827. "ren", true, "mv", NULL,
  1828. sftp_cmd_mv
  1829. },
  1830. {
  1831. "rename", false, "mv", NULL,
  1832. sftp_cmd_mv
  1833. },
  1834. {
  1835. "reput", true, "continue uploading files",
  1836. " [ -r ] [ -- ] <filename> [ <remote-filename> ]\n"
  1837. " Works exactly like the \"put\" command, but the remote file\n"
  1838. " must already exist. The upload will begin at the end of the\n"
  1839. " file. This is for resuming an upload that was interrupted.\n"
  1840. " If -r specified, resume interrupted \"put -r\".\n",
  1841. sftp_cmd_reput
  1842. },
  1843. {
  1844. "rm", true, "del", NULL,
  1845. sftp_cmd_rm
  1846. },
  1847. {
  1848. "rmdir", true, "remove directories on the remote server",
  1849. " <directory-name> [ <directory-name>... ]\n"
  1850. " Removes the directory with the given name on the server.\n"
  1851. " The directory will not be removed unless it is empty.\n"
  1852. " Wildcards may be used to specify multiple directories.\n",
  1853. sftp_cmd_rmdir
  1854. }
  1855. };
  1856. const struct sftp_cmd_lookup *lookup_command(const char *name)
  1857. {
  1858. int i, j, k, cmp;
  1859. i = -1;
  1860. j = lenof(sftp_lookup);
  1861. while (j - i > 1) {
  1862. k = (j + i) / 2;
  1863. cmp = strcmp(name, sftp_lookup[k].name);
  1864. if (cmp < 0)
  1865. j = k;
  1866. else if (cmp > 0)
  1867. i = k;
  1868. else {
  1869. return &sftp_lookup[k];
  1870. }
  1871. }
  1872. return NULL;
  1873. }
  1874. static int sftp_cmd_help(struct sftp_command *cmd)
  1875. {
  1876. int i;
  1877. if (cmd->nwords == 1) {
  1878. /*
  1879. * Give short help on each command.
  1880. */
  1881. int maxlen;
  1882. maxlen = 0;
  1883. for (i = 0; i < lenof(sftp_lookup); i++) {
  1884. int len;
  1885. if (!sftp_lookup[i].listed)
  1886. continue;
  1887. len = strlen(sftp_lookup[i].name);
  1888. if (maxlen < len)
  1889. maxlen = len;
  1890. }
  1891. for (i = 0; i < lenof(sftp_lookup); i++) {
  1892. const struct sftp_cmd_lookup *lookup;
  1893. if (!sftp_lookup[i].listed)
  1894. continue;
  1895. lookup = &sftp_lookup[i];
  1896. printf("%-*s", maxlen+2, lookup->name);
  1897. if (lookup->longhelp == NULL)
  1898. lookup = lookup_command(lookup->shorthelp);
  1899. printf("%s\n", lookup->shorthelp);
  1900. }
  1901. } else {
  1902. /*
  1903. * Give long help on specific commands.
  1904. */
  1905. for (i = 1; i < cmd->nwords; i++) {
  1906. const struct sftp_cmd_lookup *lookup;
  1907. lookup = lookup_command(cmd->words[i]);
  1908. if (!lookup) {
  1909. printf("help: %s: command not found\n", cmd->words[i]);
  1910. } else {
  1911. printf("%s", lookup->name);
  1912. if (lookup->longhelp == NULL)
  1913. lookup = lookup_command(lookup->shorthelp);
  1914. printf("%s", lookup->longhelp);
  1915. }
  1916. }
  1917. }
  1918. return 1;
  1919. }
  1920. /* ----------------------------------------------------------------------
  1921. * Command line reading and parsing.
  1922. */
  1923. struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags)
  1924. {
  1925. char *line;
  1926. struct sftp_command *cmd;
  1927. char *p, *q, *r;
  1928. bool quoting;
  1929. cmd = snew(struct sftp_command);
  1930. cmd->words = NULL;
  1931. cmd->nwords = 0;
  1932. cmd->wordssize = 0;
  1933. line = NULL;
  1934. if (fp) {
  1935. if (modeflags & 1)
  1936. printf("psftp> ");
  1937. line = fgetline(fp);
  1938. } else {
  1939. line = ssh_sftp_get_cmdline("psftp> ", !backend);
  1940. }
  1941. if (!line || !*line) {
  1942. cmd->obey = sftp_cmd_quit;
  1943. if ((mode == 0) || (modeflags & 1))
  1944. printf("quit\n");
  1945. sfree(line);
  1946. return cmd; /* eof */
  1947. }
  1948. line[strcspn(line, "\r\n")] = '\0';
  1949. if (modeflags & 1) {
  1950. printf("%s\n", line);
  1951. }
  1952. p = line;
  1953. while (*p && (*p == ' ' || *p == '\t'))
  1954. p++;
  1955. if (*p == '!') {
  1956. /*
  1957. * Special case: the ! command. This is always parsed as
  1958. * exactly two words: one containing the !, and the second
  1959. * containing everything else on the line.
  1960. */
  1961. cmd->nwords = 2;
  1962. sgrowarrayn(cmd->words, cmd->wordssize, cmd->nwords, 0);
  1963. cmd->words[0] = dupstr("!");
  1964. cmd->words[1] = dupstr(p+1);
  1965. } else if (*p == '#') {
  1966. /*
  1967. * Special case: comment. Entire line is ignored.
  1968. */
  1969. cmd->nwords = cmd->wordssize = 0;
  1970. } else {
  1971. /*
  1972. * Parse the command line into words. The syntax is:
  1973. * - double quotes are removed, but cause spaces within to be
  1974. * treated as non-separating.
  1975. * - a double-doublequote pair is a literal double quote, inside
  1976. * _or_ outside quotes. Like this:
  1977. *
  1978. * firstword "second word" "this has ""quotes"" in" and""this""
  1979. *
  1980. * becomes
  1981. *
  1982. * >firstword<
  1983. * >second word<
  1984. * >this has "quotes" in<
  1985. * >and"this"<
  1986. */
  1987. while (1) {
  1988. /* skip whitespace */
  1989. while (*p && (*p == ' ' || *p == '\t'))
  1990. p++;
  1991. /* terminate loop */
  1992. if (!*p)
  1993. break;
  1994. /* mark start of word */
  1995. q = r = p; /* q sits at start, r writes word */
  1996. quoting = false;
  1997. while (*p) {
  1998. if (!quoting && (*p == ' ' || *p == '\t'))
  1999. break; /* reached end of word */
  2000. else if (*p == '"' && p[1] == '"')
  2001. p += 2, *r++ = '"'; /* a literal quote */
  2002. else if (*p == '"')
  2003. p++, quoting = !quoting;
  2004. else
  2005. *r++ = *p++;
  2006. }
  2007. if (*p)
  2008. p++; /* skip over the whitespace */
  2009. *r = '\0';
  2010. sgrowarray(cmd->words, cmd->wordssize, cmd->nwords);
  2011. cmd->words[cmd->nwords++] = dupstr(q);
  2012. }
  2013. }
  2014. sfree(line);
  2015. /*
  2016. * Now parse the first word and assign a function.
  2017. */
  2018. if (cmd->nwords == 0)
  2019. cmd->obey = sftp_cmd_null;
  2020. else {
  2021. const struct sftp_cmd_lookup *lookup;
  2022. lookup = lookup_command(cmd->words[0]);
  2023. if (!lookup)
  2024. cmd->obey = sftp_cmd_unknown;
  2025. else
  2026. cmd->obey = lookup->obey;
  2027. }
  2028. return cmd;
  2029. }
  2030. static void sftp_cmd_free(struct sftp_command *cmd)
  2031. {
  2032. if (cmd->words) {
  2033. for (size_t i = 0; i < cmd->nwords; i++)
  2034. sfree(cmd->words[i]);
  2035. sfree(cmd->words);
  2036. }
  2037. sfree(cmd);
  2038. }
  2039. static int do_sftp_init(void)
  2040. {
  2041. struct sftp_packet *pktin;
  2042. struct sftp_request *req;
  2043. /*
  2044. * Do protocol initialisation.
  2045. */
  2046. if (!fxp_init()) {
  2047. fprintf(stderr,
  2048. "Fatal: unable to initialise SFTP: %s\n", fxp_error());
  2049. return 1; /* failure */
  2050. }
  2051. /*
  2052. * Find out where our home directory is.
  2053. */
  2054. req = fxp_realpath_send(".");
  2055. pktin = sftp_wait_for_reply(req);
  2056. homedir = fxp_realpath_recv(pktin, req);
  2057. if (!homedir) {
  2058. fprintf(stderr,
  2059. "Warning: failed to resolve home directory: %s\n",
  2060. fxp_error());
  2061. homedir = dupstr(".");
  2062. } else {
  2063. with_stripctrl(san, homedir)
  2064. printf("Remote working directory is %s\n", san);
  2065. }
  2066. pwd = dupstr(homedir);
  2067. return 0;
  2068. }
  2069. static void do_sftp_cleanup(void)
  2070. {
  2071. char ch;
  2072. if (backend) {
  2073. backend_special(backend, SS_EOF, 0);
  2074. sent_eof = true;
  2075. sftp_recvdata(&ch, 1);
  2076. backend_free(backend);
  2077. sftp_cleanup_request();
  2078. backend = NULL;
  2079. }
  2080. if (pwd) {
  2081. sfree(pwd);
  2082. pwd = NULL;
  2083. }
  2084. if (homedir) {
  2085. sfree(homedir);
  2086. homedir = NULL;
  2087. }
  2088. }
  2089. int do_sftp(int mode, int modeflags, char *batchfile)
  2090. {
  2091. FILE *fp;
  2092. int ret;
  2093. /*
  2094. * Batch mode?
  2095. */
  2096. if (mode == 0) {
  2097. /* ------------------------------------------------------------------
  2098. * Now we're ready to do Real Stuff.
  2099. */
  2100. while (1) {
  2101. struct sftp_command *cmd;
  2102. cmd = sftp_getcmd(NULL, 0, 0);
  2103. if (!cmd)
  2104. break;
  2105. ret = cmd->obey(cmd);
  2106. sftp_cmd_free(cmd);
  2107. if (ret < 0)
  2108. break;
  2109. }
  2110. } else {
  2111. fp = fopen(batchfile, "r");
  2112. if (!fp) {
  2113. printf("Fatal: unable to open %s\n", batchfile);
  2114. return 1;
  2115. }
  2116. ret = 0;
  2117. while (1) {
  2118. struct sftp_command *cmd;
  2119. cmd = sftp_getcmd(fp, mode, modeflags);
  2120. if (!cmd)
  2121. break;
  2122. ret = cmd->obey(cmd);
  2123. sftp_cmd_free(cmd);
  2124. if (ret < 0)
  2125. break;
  2126. if (ret == 0) {
  2127. if (!(modeflags & 2))
  2128. break;
  2129. }
  2130. }
  2131. fclose(fp);
  2132. /*
  2133. * In batch mode, and if exit on command failure is enabled,
  2134. * any command failure causes the whole of PSFTP to fail.
  2135. */
  2136. if (ret == 0 && !(modeflags & 2)) return 2;
  2137. }
  2138. return 0;
  2139. }
  2140. /* ----------------------------------------------------------------------
  2141. * Dirty bits: integration with PuTTY.
  2142. */
  2143. static bool verbose = false;
  2144. void ldisc_echoedit_update(Ldisc *ldisc) { }
  2145. /*
  2146. * Receive a block of data from the SSH link. Block until all data
  2147. * is available.
  2148. *
  2149. * To do this, we repeatedly call the SSH protocol module, with our
  2150. * own psftp_output() function to catch the data that comes back. We
  2151. * do this until we have enough data.
  2152. */
  2153. static bufchain received_data;
  2154. static BinarySink *stderr_bs;
  2155. static size_t psftp_output(
  2156. Seat *seat, bool is_stderr, const void *data, size_t len)
  2157. {
  2158. /*
  2159. * stderr data is just spouted to local stderr (optionally via a
  2160. * sanitiser) and otherwise ignored.
  2161. */
  2162. if (is_stderr) {
  2163. put_data(stderr_bs, data, len);
  2164. return 0;
  2165. }
  2166. bufchain_add(&received_data, data, len);
  2167. return 0;
  2168. }
  2169. static bool psftp_eof(Seat *seat)
  2170. {
  2171. /*
  2172. * We expect to be the party deciding when to close the
  2173. * connection, so if we see EOF before we sent it ourselves, we
  2174. * should panic.
  2175. */
  2176. if (!sent_eof) {
  2177. seat_connection_fatal(
  2178. psftp_seat, "Received unexpected end-of-file from SFTP server");
  2179. }
  2180. return false;
  2181. }
  2182. bool sftp_recvdata(char *buf, size_t len)
  2183. {
  2184. while (len > 0) {
  2185. while (bufchain_size(&received_data) == 0) {
  2186. if (backend_exitcode(backend) >= 0 ||
  2187. ssh_sftp_loop_iteration() < 0)
  2188. return false; /* doom */
  2189. }
  2190. size_t got = bufchain_fetch_consume_up_to(&received_data, buf, len);
  2191. buf += got;
  2192. len -= got;
  2193. }
  2194. return true;
  2195. }
  2196. bool sftp_senddata(const char *buf, size_t len)
  2197. {
  2198. backend_send(backend, buf, len);
  2199. return true;
  2200. }
  2201. size_t sftp_sendbuffer(void)
  2202. {
  2203. return backend_sendbuffer(backend);
  2204. }
  2205. /*
  2206. * Short description of parameters.
  2207. */
  2208. static void usage(void)
  2209. {
  2210. printf("PuTTY Secure File Transfer (SFTP) client\n");
  2211. printf("%s\n", ver);
  2212. printf("Usage: psftp [options] [user@]host\n");
  2213. printf("Options:\n");
  2214. printf(" -V print version information and exit\n");
  2215. printf(" -pgpfp print PGP key fingerprints and exit\n");
  2216. printf(" -b file use specified batchfile\n");
  2217. printf(" -bc output batchfile commands\n");
  2218. printf(" -be don't stop batchfile processing if errors\n");
  2219. printf(" -v show verbose messages\n");
  2220. printf(" -load sessname Load settings from saved session\n");
  2221. printf(" -l user connect with specified username\n");
  2222. printf(" -P port connect to specified port\n");
  2223. printf(" -pw passw login with specified password\n");
  2224. printf(" -1 -2 force use of particular SSH protocol version\n");
  2225. printf(" -ssh -ssh-connection\n");
  2226. printf(" force use of particular SSH protocol variant\n");
  2227. printf(" -4 -6 force use of IPv4 or IPv6\n");
  2228. printf(" -C enable compression\n");
  2229. printf(" -i key private key file for user authentication\n");
  2230. printf(" -noagent disable use of Pageant\n");
  2231. printf(" -agent enable use of Pageant\n");
  2232. printf(" -no-trivial-auth\n");
  2233. printf(" disconnect if SSH authentication succeeds trivially\n");
  2234. printf(" -hostkey keyid\n");
  2235. printf(" manually specify a host key (may be repeated)\n");
  2236. printf(" -batch disable all interactive prompts\n");
  2237. printf(" -no-sanitise-stderr don't strip control chars from"
  2238. " standard error\n");
  2239. printf(" -proxycmd command\n");
  2240. printf(" use 'command' as local proxy\n");
  2241. printf(" -sshlog file\n");
  2242. printf(" -sshrawlog file\n");
  2243. printf(" log protocol details to a file\n");
  2244. printf(" -logoverwrite\n");
  2245. printf(" -logappend\n");
  2246. printf(" control what happens when a log file already exists\n");
  2247. cleanup_exit(1);
  2248. }
  2249. static void version(void)
  2250. {
  2251. char *buildinfo_text = buildinfo("\n");
  2252. printf("psftp: %s\n%s\n", ver, buildinfo_text);
  2253. sfree(buildinfo_text);
  2254. exit(0);
  2255. }
  2256. /*
  2257. * Connect to a host.
  2258. */
  2259. static int psftp_connect(char *userhost, char *user, int portnumber)
  2260. {
  2261. char *host, *realhost;
  2262. const char *err;
  2263. /* Separate host and username */
  2264. host = userhost;
  2265. host = strrchr(host, '@');
  2266. if (host == NULL) {
  2267. host = userhost;
  2268. } else {
  2269. *host++ = '\0';
  2270. if (user) {
  2271. printf("psftp: multiple usernames specified; using \"%s\"\n",
  2272. user);
  2273. } else
  2274. user = userhost;
  2275. }
  2276. /*
  2277. * If we haven't loaded session details already (e.g., from -load),
  2278. * try looking for a session called "host".
  2279. */
  2280. if (!cmdline_loaded_session()) {
  2281. /* Try to load settings for `host' into a temporary config */
  2282. Conf *conf2 = conf_new();
  2283. conf_set_str(conf2, CONF_host, "");
  2284. do_defaults(host, conf2);
  2285. if (conf_get_str(conf2, CONF_host)[0] != '\0') {
  2286. /* Settings present and include hostname */
  2287. /* Re-load data into the real config. */
  2288. do_defaults(host, conf);
  2289. } else {
  2290. /* Session doesn't exist or mention a hostname. */
  2291. /* Use `host' as a bare hostname. */
  2292. conf_set_str(conf, CONF_host, host);
  2293. }
  2294. conf_free(conf2);
  2295. } else {
  2296. /* Patch in hostname `host' to session details. */
  2297. conf_set_str(conf, CONF_host, host);
  2298. }
  2299. /*
  2300. * Force protocol to SSH if the user has somehow contrived to
  2301. * select one we don't support (e.g. by loading an inappropriate
  2302. * saved session). In that situation we assume the port number is
  2303. * useless too.)
  2304. */
  2305. if (!backend_vt_from_proto(conf_get_int(conf, CONF_protocol))) {
  2306. conf_set_int(conf, CONF_protocol, PROT_SSH);
  2307. conf_set_int(conf, CONF_port, 22);
  2308. }
  2309. /*
  2310. * If saved session / Default Settings says SSH-1 (`1 only' or `1'),
  2311. * then change it to SSH-2, on the grounds that that's more likely to
  2312. * work for SFTP. (Can be overridden with `-1' option.)
  2313. * But if it says `2 only' or `2', respect which.
  2314. */
  2315. if ((conf_get_int(conf, CONF_sshprot) & ~1) != 2) /* is it 2 or 3? */
  2316. conf_set_int(conf, CONF_sshprot, 2);
  2317. /*
  2318. * Enact command-line overrides.
  2319. */
  2320. cmdline_run_saved(conf);
  2321. /*
  2322. * Muck about with the hostname in various ways.
  2323. */
  2324. {
  2325. char *hostbuf = dupstr(conf_get_str(conf, CONF_host));
  2326. char *host = hostbuf;
  2327. char *p, *q;
  2328. /*
  2329. * Trim leading whitespace.
  2330. */
  2331. host += strspn(host, " \t");
  2332. /*
  2333. * See if host is of the form user@host, and separate out
  2334. * the username if so.
  2335. */
  2336. if (host[0] != '\0') {
  2337. char *atsign = strrchr(host, '@');
  2338. if (atsign) {
  2339. *atsign = '\0';
  2340. conf_set_str(conf, CONF_username, host);
  2341. host = atsign + 1;
  2342. }
  2343. }
  2344. /*
  2345. * Remove any remaining whitespace.
  2346. */
  2347. p = hostbuf;
  2348. q = host;
  2349. while (*q) {
  2350. if (*q != ' ' && *q != '\t')
  2351. *p++ = *q;
  2352. q++;
  2353. }
  2354. *p = '\0';
  2355. conf_set_str(conf, CONF_host, hostbuf);
  2356. sfree(hostbuf);
  2357. }
  2358. /* Set username */
  2359. if (user != NULL && user[0] != '\0') {
  2360. conf_set_str(conf, CONF_username, user);
  2361. }
  2362. if (portnumber)
  2363. conf_set_int(conf, CONF_port, portnumber);
  2364. /*
  2365. * Disable scary things which shouldn't be enabled for simple
  2366. * things like SCP and SFTP: agent forwarding, port forwarding,
  2367. * X forwarding.
  2368. */
  2369. conf_set_bool(conf, CONF_x11_forward, false);
  2370. conf_set_bool(conf, CONF_agentfwd, false);
  2371. conf_set_bool(conf, CONF_ssh_simple, true);
  2372. {
  2373. char *key;
  2374. while ((key = conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) != NULL)
  2375. conf_del_str_str(conf, CONF_portfwd, key);
  2376. }
  2377. /* Set up subsystem name. */
  2378. conf_set_str(conf, CONF_remote_cmd, "sftp");
  2379. conf_set_bool(conf, CONF_ssh_subsys, true);
  2380. conf_set_bool(conf, CONF_nopty, true);
  2381. /*
  2382. * Set up fallback option, for SSH-1 servers or servers with the
  2383. * sftp subsystem not enabled but the server binary installed
  2384. * in the usual place. We only support fallback on Unix
  2385. * systems, and we use a kludgy piece of shellery which should
  2386. * try to find sftp-server in various places (the obvious
  2387. * systemwide spots /usr/lib and /usr/local/lib, and then the
  2388. * user's PATH) and finally give up.
  2389. *
  2390. * test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server
  2391. * test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server
  2392. * exec sftp-server
  2393. *
  2394. * the idea being that this will attempt to use either of the
  2395. * obvious pathnames and then give up, and when it does give up
  2396. * it will print the preferred pathname in the error messages.
  2397. */
  2398. conf_set_str(conf, CONF_remote_cmd2,
  2399. "test -x /usr/lib/sftp-server &&"
  2400. " exec /usr/lib/sftp-server\n"
  2401. "test -x /usr/local/lib/sftp-server &&"
  2402. " exec /usr/local/lib/sftp-server\n"
  2403. "exec sftp-server");
  2404. conf_set_bool(conf, CONF_ssh_subsys2, false);
  2405. psftp_logctx = log_init(console_cli_logpolicy, conf);
  2406. platform_psftp_pre_conn_setup(console_cli_logpolicy);
  2407. err = backend_init(backend_vt_from_proto(
  2408. conf_get_int(conf, CONF_protocol)),
  2409. psftp_seat, &backend, psftp_logctx, conf,
  2410. conf_get_str(conf, CONF_host),
  2411. conf_get_int(conf, CONF_port),
  2412. &realhost, 0,
  2413. conf_get_bool(conf, CONF_tcp_keepalives));
  2414. if (err != NULL) {
  2415. fprintf(stderr, "ssh_init: %s\n", err);
  2416. return 1;
  2417. }
  2418. while (!backend_sendok(backend)) {
  2419. if (backend_exitcode(backend) >= 0)
  2420. return 1;
  2421. if (ssh_sftp_loop_iteration() < 0) {
  2422. fprintf(stderr, "ssh_init: error during SSH connection setup\n");
  2423. return 1;
  2424. }
  2425. }
  2426. if (verbose && realhost != NULL)
  2427. printf("Connected to %s\n", realhost);
  2428. if (realhost != NULL)
  2429. sfree(realhost);
  2430. return 0;
  2431. }
  2432. void cmdline_error(const char *p, ...)
  2433. {
  2434. va_list ap;
  2435. fprintf(stderr, "psftp: ");
  2436. va_start(ap, p);
  2437. vfprintf(stderr, p, ap);
  2438. va_end(ap);
  2439. fprintf(stderr, "\n try typing \"psftp -h\" for help\n");
  2440. exit(1);
  2441. }
  2442. const bool share_can_be_downstream = true;
  2443. const bool share_can_be_upstream = false;
  2444. static stdio_sink stderr_ss;
  2445. static StripCtrlChars *stderr_scc;
  2446. const unsigned cmdline_tooltype = TOOLTYPE_FILETRANSFER;
  2447. /*
  2448. * Main program. Parse arguments etc.
  2449. */
  2450. int psftp_main(int argc, char *argv[])
  2451. {
  2452. int i, ret;
  2453. int portnumber = 0;
  2454. char *userhost, *user;
  2455. int mode = 0;
  2456. int modeflags = 0;
  2457. bool sanitise_stderr = true;
  2458. char *batchfile = NULL;
  2459. sk_init();
  2460. userhost = user = NULL;
  2461. /* Load Default Settings before doing anything else. */
  2462. conf = conf_new();
  2463. do_defaults(NULL, conf);
  2464. for (i = 1; i < argc; i++) {
  2465. int ret;
  2466. if (argv[i][0] != '-') {
  2467. if (userhost)
  2468. usage();
  2469. else
  2470. userhost = dupstr(argv[i]);
  2471. continue;
  2472. }
  2473. ret = cmdline_process_param(argv[i], i+1<argc?argv[i+1]:NULL, 1, conf);
  2474. if (ret == -2) {
  2475. cmdline_error("option \"%s\" requires an argument", argv[i]);
  2476. } else if (ret == 2) {
  2477. i++; /* skip next argument */
  2478. } else if (ret == 1) {
  2479. /* We have our own verbosity in addition to `flags'. */
  2480. if (cmdline_verbose())
  2481. verbose = true;
  2482. } else if (strcmp(argv[i], "-h") == 0 ||
  2483. strcmp(argv[i], "-?") == 0 ||
  2484. strcmp(argv[i], "--help") == 0) {
  2485. usage();
  2486. } else if (strcmp(argv[i], "-pgpfp") == 0) {
  2487. pgp_fingerprints();
  2488. return 1;
  2489. } else if (strcmp(argv[i], "-V") == 0 ||
  2490. strcmp(argv[i], "--version") == 0) {
  2491. version();
  2492. } else if (strcmp(argv[i], "-batch") == 0) {
  2493. console_batch_mode = true;
  2494. } else if (strcmp(argv[i], "-b") == 0 && i + 1 < argc) {
  2495. mode = 1;
  2496. batchfile = argv[++i];
  2497. } else if (strcmp(argv[i], "-bc") == 0) {
  2498. modeflags = modeflags | 1;
  2499. } else if (strcmp(argv[i], "-be") == 0) {
  2500. modeflags = modeflags | 2;
  2501. } else if (strcmp(argv[i], "-sanitise-stderr") == 0) {
  2502. sanitise_stderr = true;
  2503. } else if (strcmp(argv[i], "-no-sanitise-stderr") == 0) {
  2504. sanitise_stderr = false;
  2505. } else if (strcmp(argv[i], "--") == 0) {
  2506. i++;
  2507. break;
  2508. } else {
  2509. cmdline_error("unknown option \"%s\"", argv[i]);
  2510. }
  2511. }
  2512. argc -= i;
  2513. argv += i;
  2514. backend = NULL;
  2515. stdio_sink_init(&stderr_ss, stderr);
  2516. stderr_bs = BinarySink_UPCAST(&stderr_ss);
  2517. if (sanitise_stderr) {
  2518. stderr_scc = stripctrl_new(stderr_bs, false, L'\0');
  2519. stderr_bs = BinarySink_UPCAST(stderr_scc);
  2520. }
  2521. string_scc = stripctrl_new(NULL, false, L'\0');
  2522. /*
  2523. * If the loaded session provides a hostname, and a hostname has not
  2524. * otherwise been specified, pop it in `userhost' so that
  2525. * `psftp -load sessname' is sufficient to start a session.
  2526. */
  2527. if (!userhost && conf_get_str(conf, CONF_host)[0] != '\0') {
  2528. userhost = dupstr(conf_get_str(conf, CONF_host));
  2529. }
  2530. /*
  2531. * If a user@host string has already been provided, connect to
  2532. * it now.
  2533. */
  2534. if (userhost) {
  2535. int ret;
  2536. ret = psftp_connect(userhost, user, portnumber);
  2537. sfree(userhost);
  2538. if (ret)
  2539. return 1;
  2540. if (do_sftp_init())
  2541. return 1;
  2542. } else {
  2543. printf("psftp: no hostname specified; use \"open host.name\""
  2544. " to connect\n");
  2545. }
  2546. ret = do_sftp(mode, modeflags, batchfile);
  2547. if (backend && backend_connected(backend)) {
  2548. char ch;
  2549. backend_special(backend, SS_EOF, 0);
  2550. sent_eof = true;
  2551. sftp_recvdata(&ch, 1);
  2552. }
  2553. do_sftp_cleanup();
  2554. random_save_seed();
  2555. cmdline_cleanup();
  2556. sk_cleanup();
  2557. stripctrl_free(string_scc);
  2558. stripctrl_free(stderr_scc);
  2559. if (psftp_logctx)
  2560. log_free(psftp_logctx);
  2561. return ret;
  2562. }