psftp.c 75 KB

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