nanoftp.c 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982
  1. /*
  2. * nanoftp.c: basic FTP client support
  3. *
  4. * Reference: RFC 959
  5. */
  6. #ifdef TESTING
  7. #define STANDALONE
  8. #define HAVE_UNISTD_H
  9. #define HAVE_SYS_SOCKET_H
  10. #define HAVE_NETINET_IN_H
  11. #define HAVE_NETDB_H
  12. #define HAVE_SYS_TIME_H
  13. #endif /* TESTING */
  14. #define IN_LIBXML
  15. #include "libxml.h"
  16. #ifdef LIBXML_FTP_ENABLED
  17. #include <string.h>
  18. #include <stdlib.h>
  19. #include <errno.h>
  20. #ifdef HAVE_UNISTD_H
  21. #include <unistd.h>
  22. #elif defined (_WIN32)
  23. #include <io.h>
  24. #endif
  25. #ifdef HAVE_SYS_SOCKET_H
  26. #include <sys/socket.h>
  27. #endif
  28. #ifdef HAVE_NETINET_IN_H
  29. #include <netinet/in.h>
  30. #endif
  31. #ifdef HAVE_ARPA_INET_H
  32. #include <arpa/inet.h>
  33. #endif
  34. #ifdef HAVE_NETDB_H
  35. #include <netdb.h>
  36. #endif
  37. #ifdef HAVE_FCNTL_H
  38. #include <fcntl.h>
  39. #endif
  40. #ifdef HAVE_SYS_TIME_H
  41. #include <sys/time.h>
  42. #endif
  43. #ifdef HAVE_SYS_SELECT_H
  44. #include <sys/select.h>
  45. #endif
  46. #ifdef HAVE_SYS_SOCKET_H
  47. #include <sys/socket.h>
  48. #endif
  49. #include <libxml/xmlmemory.h>
  50. #include <libxml/parser.h>
  51. #include <libxml/xmlerror.h>
  52. #include <libxml/uri.h>
  53. #include <libxml/nanoftp.h>
  54. #include "private/error.h"
  55. #include "private/io.h"
  56. #if defined(_WIN32)
  57. #include <wsockcompat.h>
  58. #endif
  59. /**
  60. * A couple portability macros
  61. */
  62. #ifndef _WINSOCKAPI_
  63. #define closesocket(s) close(s)
  64. #endif
  65. #ifndef XML_SOCKLEN_T
  66. #define XML_SOCKLEN_T unsigned int
  67. #endif
  68. #define GETHOSTBYNAME_ARG_CAST (char *)
  69. #define SEND_ARG2_CAST (char *)
  70. #define FTP_COMMAND_OK 200
  71. #define FTP_SYNTAX_ERROR 500
  72. #define FTP_GET_PASSWD 331
  73. #define FTP_BUF_SIZE 1024
  74. #define XML_NANO_MAX_URLBUF 4096
  75. typedef struct xmlNanoFTPCtxt {
  76. char *protocol; /* the protocol name */
  77. char *hostname; /* the host name */
  78. int port; /* the port */
  79. char *path; /* the path within the URL */
  80. char *user; /* user string */
  81. char *passwd; /* passwd string */
  82. #ifdef SUPPORT_IP6
  83. struct sockaddr_storage ftpAddr; /* this is large enough to hold IPv6 address*/
  84. #else
  85. struct sockaddr_in ftpAddr; /* the socket address struct */
  86. #endif
  87. int passive; /* currently we support only passive !!! */
  88. SOCKET controlFd; /* the file descriptor for the control socket */
  89. SOCKET dataFd; /* the file descriptor for the data socket */
  90. int state; /* WRITE / READ / CLOSED */
  91. int returnValue; /* the protocol return value */
  92. /* buffer for data received from the control connection */
  93. char controlBuf[FTP_BUF_SIZE + 1];
  94. int controlBufIndex;
  95. int controlBufUsed;
  96. int controlBufAnswer;
  97. } xmlNanoFTPCtxt, *xmlNanoFTPCtxtPtr;
  98. static int initialized = 0;
  99. static char *proxy = NULL; /* the proxy name if any */
  100. static int proxyPort = 0; /* the proxy port if any */
  101. static char *proxyUser = NULL; /* user for proxy authentication */
  102. static char *proxyPasswd = NULL;/* passwd for proxy authentication */
  103. static int proxyType = 0; /* uses TYPE or a@b ? */
  104. #ifdef SUPPORT_IP6
  105. static
  106. int have_ipv6(void) {
  107. int s;
  108. s = socket (AF_INET6, SOCK_STREAM, 0);
  109. if (s != -1) {
  110. close (s);
  111. return (1);
  112. }
  113. return (0);
  114. }
  115. #endif
  116. /**
  117. * xmlFTPErrMemory:
  118. * @extra: extra information
  119. *
  120. * Handle an out of memory condition
  121. */
  122. static void
  123. xmlFTPErrMemory(const char *extra)
  124. {
  125. __xmlSimpleError(XML_FROM_FTP, XML_ERR_NO_MEMORY, NULL, NULL, extra);
  126. }
  127. /**
  128. * xmlNanoFTPInit:
  129. *
  130. * Initialize the FTP protocol layer.
  131. * Currently it just checks for proxy information,
  132. * and get the hostname
  133. */
  134. void
  135. xmlNanoFTPInit(void) {
  136. const char *env;
  137. #ifdef _WINSOCKAPI_
  138. WSADATA wsaData;
  139. #endif
  140. if (initialized)
  141. return;
  142. #ifdef _WINSOCKAPI_
  143. if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
  144. return;
  145. #endif
  146. proxyPort = 21;
  147. env = getenv("no_proxy");
  148. if (env && ((env[0] == '*' ) && (env[1] == 0)))
  149. return;
  150. env = getenv("ftp_proxy");
  151. if (env != NULL) {
  152. xmlNanoFTPScanProxy(env);
  153. } else {
  154. env = getenv("FTP_PROXY");
  155. if (env != NULL) {
  156. xmlNanoFTPScanProxy(env);
  157. }
  158. }
  159. env = getenv("ftp_proxy_user");
  160. if (env != NULL) {
  161. proxyUser = xmlMemStrdup(env);
  162. }
  163. env = getenv("ftp_proxy_password");
  164. if (env != NULL) {
  165. proxyPasswd = xmlMemStrdup(env);
  166. }
  167. initialized = 1;
  168. }
  169. /**
  170. * xmlNanoFTPCleanup:
  171. *
  172. * Cleanup the FTP protocol layer. This cleanup proxy information.
  173. */
  174. void
  175. xmlNanoFTPCleanup(void) {
  176. if (proxy != NULL) {
  177. xmlFree(proxy);
  178. proxy = NULL;
  179. }
  180. if (proxyUser != NULL) {
  181. xmlFree(proxyUser);
  182. proxyUser = NULL;
  183. }
  184. if (proxyPasswd != NULL) {
  185. xmlFree(proxyPasswd);
  186. proxyPasswd = NULL;
  187. }
  188. #ifdef _WINSOCKAPI_
  189. if (initialized)
  190. WSACleanup();
  191. #endif
  192. initialized = 0;
  193. }
  194. /**
  195. * xmlNanoFTPProxy:
  196. * @host: the proxy host name
  197. * @port: the proxy port
  198. * @user: the proxy user name
  199. * @passwd: the proxy password
  200. * @type: the type of proxy 1 for using SITE, 2 for USER a@b
  201. *
  202. * Setup the FTP proxy information.
  203. * This can also be done by using ftp_proxy ftp_proxy_user and
  204. * ftp_proxy_password environment variables.
  205. */
  206. void
  207. xmlNanoFTPProxy(const char *host, int port, const char *user,
  208. const char *passwd, int type) {
  209. if (proxy != NULL) {
  210. xmlFree(proxy);
  211. proxy = NULL;
  212. }
  213. if (proxyUser != NULL) {
  214. xmlFree(proxyUser);
  215. proxyUser = NULL;
  216. }
  217. if (proxyPasswd != NULL) {
  218. xmlFree(proxyPasswd);
  219. proxyPasswd = NULL;
  220. }
  221. if (host)
  222. proxy = xmlMemStrdup(host);
  223. if (user)
  224. proxyUser = xmlMemStrdup(user);
  225. if (passwd)
  226. proxyPasswd = xmlMemStrdup(passwd);
  227. proxyPort = port;
  228. proxyType = type;
  229. }
  230. /**
  231. * xmlNanoFTPScanURL:
  232. * @ctx: an FTP context
  233. * @URL: The URL used to initialize the context
  234. *
  235. * (Re)Initialize an FTP context by parsing the URL and finding
  236. * the protocol host port and path it indicates.
  237. */
  238. static void
  239. xmlNanoFTPScanURL(void *ctx, const char *URL) {
  240. xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
  241. xmlURIPtr uri;
  242. /*
  243. * Clear any existing data from the context
  244. */
  245. if (ctxt->protocol != NULL) {
  246. xmlFree(ctxt->protocol);
  247. ctxt->protocol = NULL;
  248. }
  249. if (ctxt->hostname != NULL) {
  250. xmlFree(ctxt->hostname);
  251. ctxt->hostname = NULL;
  252. }
  253. if (ctxt->path != NULL) {
  254. xmlFree(ctxt->path);
  255. ctxt->path = NULL;
  256. }
  257. if (URL == NULL) return;
  258. uri = xmlParseURIRaw(URL, 1);
  259. if (uri == NULL)
  260. return;
  261. if ((uri->scheme == NULL) || (uri->server == NULL)) {
  262. xmlFreeURI(uri);
  263. return;
  264. }
  265. ctxt->protocol = xmlMemStrdup(uri->scheme);
  266. ctxt->hostname = xmlMemStrdup(uri->server);
  267. if (uri->path != NULL)
  268. ctxt->path = xmlMemStrdup(uri->path);
  269. else
  270. ctxt->path = xmlMemStrdup("/");
  271. if (uri->port != 0)
  272. ctxt->port = uri->port;
  273. if (uri->user != NULL) {
  274. char *cptr;
  275. if ((cptr=strchr(uri->user, ':')) == NULL)
  276. ctxt->user = xmlMemStrdup(uri->user);
  277. else {
  278. ctxt->user = (char *)xmlStrndup((xmlChar *)uri->user,
  279. (cptr - uri->user));
  280. ctxt->passwd = xmlMemStrdup(cptr+1);
  281. }
  282. }
  283. xmlFreeURI(uri);
  284. }
  285. /**
  286. * xmlNanoFTPUpdateURL:
  287. * @ctx: an FTP context
  288. * @URL: The URL used to update the context
  289. *
  290. * Update an FTP context by parsing the URL and finding
  291. * new path it indicates. If there is an error in the
  292. * protocol, hostname, port or other information, the
  293. * error is raised. It indicates a new connection has to
  294. * be established.
  295. *
  296. * Returns 0 if Ok, -1 in case of error (other host).
  297. */
  298. int
  299. xmlNanoFTPUpdateURL(void *ctx, const char *URL) {
  300. xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
  301. xmlURIPtr uri;
  302. if (URL == NULL)
  303. return(-1);
  304. if (ctxt == NULL)
  305. return(-1);
  306. if (ctxt->protocol == NULL)
  307. return(-1);
  308. if (ctxt->hostname == NULL)
  309. return(-1);
  310. uri = xmlParseURIRaw(URL, 1);
  311. if (uri == NULL)
  312. return(-1);
  313. if ((uri->scheme == NULL) || (uri->server == NULL)) {
  314. xmlFreeURI(uri);
  315. return(-1);
  316. }
  317. if ((strcmp(ctxt->protocol, uri->scheme)) ||
  318. (strcmp(ctxt->hostname, uri->server)) ||
  319. ((uri->port != 0) && (ctxt->port != uri->port))) {
  320. xmlFreeURI(uri);
  321. return(-1);
  322. }
  323. if (uri->port != 0)
  324. ctxt->port = uri->port;
  325. if (ctxt->path != NULL) {
  326. xmlFree(ctxt->path);
  327. ctxt->path = NULL;
  328. }
  329. if (uri->path == NULL)
  330. ctxt->path = xmlMemStrdup("/");
  331. else
  332. ctxt->path = xmlMemStrdup(uri->path);
  333. xmlFreeURI(uri);
  334. return(0);
  335. }
  336. /**
  337. * xmlNanoFTPScanProxy:
  338. * @URL: The proxy URL used to initialize the proxy context
  339. *
  340. * (Re)Initialize the FTP Proxy context by parsing the URL and finding
  341. * the protocol host port it indicates.
  342. * Should be like ftp://myproxy/ or ftp://myproxy:3128/
  343. * A NULL URL cleans up proxy information.
  344. */
  345. void
  346. xmlNanoFTPScanProxy(const char *URL) {
  347. xmlURIPtr uri;
  348. if (proxy != NULL) {
  349. xmlFree(proxy);
  350. proxy = NULL;
  351. }
  352. proxyPort = 0;
  353. if (URL == NULL) return;
  354. uri = xmlParseURIRaw(URL, 1);
  355. if ((uri == NULL) || (uri->scheme == NULL) ||
  356. (strcmp(uri->scheme, "ftp")) || (uri->server == NULL)) {
  357. __xmlIOErr(XML_FROM_FTP, XML_FTP_URL_SYNTAX, "Syntax Error\n");
  358. if (uri != NULL)
  359. xmlFreeURI(uri);
  360. return;
  361. }
  362. proxy = xmlMemStrdup(uri->server);
  363. if (uri->port != 0)
  364. proxyPort = uri->port;
  365. xmlFreeURI(uri);
  366. }
  367. /**
  368. * xmlNanoFTPNewCtxt:
  369. * @URL: The URL used to initialize the context
  370. *
  371. * Allocate and initialize a new FTP context.
  372. *
  373. * Returns an FTP context or NULL in case of error.
  374. */
  375. void*
  376. xmlNanoFTPNewCtxt(const char *URL) {
  377. xmlNanoFTPCtxtPtr ret;
  378. char *unescaped;
  379. ret = (xmlNanoFTPCtxtPtr) xmlMalloc(sizeof(xmlNanoFTPCtxt));
  380. if (ret == NULL) {
  381. xmlFTPErrMemory("allocating FTP context");
  382. return(NULL);
  383. }
  384. memset(ret, 0, sizeof(xmlNanoFTPCtxt));
  385. ret->port = 21;
  386. ret->passive = 1;
  387. ret->returnValue = 0;
  388. ret->controlBufIndex = 0;
  389. ret->controlBufUsed = 0;
  390. ret->controlFd = INVALID_SOCKET;
  391. unescaped = xmlURIUnescapeString(URL, 0, NULL);
  392. if (unescaped != NULL) {
  393. xmlNanoFTPScanURL(ret, unescaped);
  394. xmlFree(unescaped);
  395. } else if (URL != NULL)
  396. xmlNanoFTPScanURL(ret, URL);
  397. return(ret);
  398. }
  399. /**
  400. * xmlNanoFTPFreeCtxt:
  401. * @ctx: an FTP context
  402. *
  403. * Frees the context after closing the connection.
  404. */
  405. void
  406. xmlNanoFTPFreeCtxt(void * ctx) {
  407. xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
  408. if (ctxt == NULL) return;
  409. if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
  410. if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
  411. if (ctxt->path != NULL) xmlFree(ctxt->path);
  412. if (ctxt->user != NULL) xmlFree(ctxt->user);
  413. if (ctxt->passwd != NULL) xmlFree(ctxt->passwd);
  414. ctxt->passive = 1;
  415. if (ctxt->controlFd != INVALID_SOCKET) closesocket(ctxt->controlFd);
  416. ctxt->controlFd = INVALID_SOCKET;
  417. ctxt->controlBufIndex = -1;
  418. ctxt->controlBufUsed = -1;
  419. xmlFree(ctxt);
  420. }
  421. /**
  422. * xmlNanoFTPParseResponse:
  423. * @buf: the buffer containing the response
  424. * @len: the buffer length
  425. *
  426. * Parsing of the server answer, we just extract the code.
  427. *
  428. * returns 0 for errors
  429. * +XXX for last line of response
  430. * -XXX for response to be continued
  431. */
  432. static int
  433. xmlNanoFTPParseResponse(char *buf, int len) {
  434. int val = 0;
  435. if (len < 3) return(-1);
  436. if ((*buf >= '0') && (*buf <= '9'))
  437. val = val * 10 + (*buf - '0');
  438. else
  439. return(0);
  440. buf++;
  441. if ((*buf >= '0') && (*buf <= '9'))
  442. val = val * 10 + (*buf - '0');
  443. else
  444. return(0);
  445. buf++;
  446. if ((*buf >= '0') && (*buf <= '9'))
  447. val = val * 10 + (*buf - '0');
  448. else
  449. return(0);
  450. buf++;
  451. if (*buf == '-')
  452. return(-val);
  453. return(val);
  454. }
  455. /**
  456. * xmlNanoFTPGetMore:
  457. * @ctx: an FTP context
  458. *
  459. * Read more information from the FTP control connection
  460. * Returns the number of bytes read, < 0 indicates an error
  461. */
  462. static int
  463. xmlNanoFTPGetMore(void *ctx) {
  464. xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
  465. int len;
  466. int size;
  467. if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
  468. if ((ctxt->controlBufIndex < 0) || (ctxt->controlBufIndex > FTP_BUF_SIZE)) {
  469. return(-1);
  470. }
  471. if ((ctxt->controlBufUsed < 0) || (ctxt->controlBufUsed > FTP_BUF_SIZE)) {
  472. return(-1);
  473. }
  474. if (ctxt->controlBufIndex > ctxt->controlBufUsed) {
  475. return(-1);
  476. }
  477. /*
  478. * First pack the control buffer
  479. */
  480. if (ctxt->controlBufIndex > 0) {
  481. memmove(&ctxt->controlBuf[0], &ctxt->controlBuf[ctxt->controlBufIndex],
  482. ctxt->controlBufUsed - ctxt->controlBufIndex);
  483. ctxt->controlBufUsed -= ctxt->controlBufIndex;
  484. ctxt->controlBufIndex = 0;
  485. }
  486. size = FTP_BUF_SIZE - ctxt->controlBufUsed;
  487. if (size == 0) {
  488. return(0);
  489. }
  490. /*
  491. * Read the amount left on the control connection
  492. */
  493. if ((len = recv(ctxt->controlFd, &ctxt->controlBuf[ctxt->controlBufIndex],
  494. size, 0)) < 0) {
  495. __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
  496. closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
  497. ctxt->controlFd = INVALID_SOCKET;
  498. return(-1);
  499. }
  500. ctxt->controlBufUsed += len;
  501. ctxt->controlBuf[ctxt->controlBufUsed] = 0;
  502. return(len);
  503. }
  504. /**
  505. * xmlNanoFTPReadResponse:
  506. * @ctx: an FTP context
  507. *
  508. * Read the response from the FTP server after a command.
  509. * Returns the code number
  510. */
  511. static int
  512. xmlNanoFTPReadResponse(void *ctx) {
  513. xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
  514. char *ptr, *end;
  515. int len;
  516. int res = -1, cur = -1;
  517. if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
  518. get_more:
  519. /*
  520. * Assumes everything up to controlBuf[controlBufIndex] has been read
  521. * and analyzed.
  522. */
  523. len = xmlNanoFTPGetMore(ctx);
  524. if (len < 0) {
  525. return(-1);
  526. }
  527. if ((ctxt->controlBufUsed == 0) && (len == 0)) {
  528. return(-1);
  529. }
  530. ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
  531. end = &ctxt->controlBuf[ctxt->controlBufUsed];
  532. while (ptr < end) {
  533. cur = xmlNanoFTPParseResponse(ptr, end - ptr);
  534. if (cur > 0) {
  535. /*
  536. * Successfully scanned the control code, scratch
  537. * till the end of the line, but keep the index to be
  538. * able to analyze the result if needed.
  539. */
  540. res = cur;
  541. ptr += 3;
  542. ctxt->controlBufAnswer = ptr - ctxt->controlBuf;
  543. while ((ptr < end) && (*ptr != '\n')) ptr++;
  544. if (*ptr == '\n') ptr++;
  545. if (*ptr == '\r') ptr++;
  546. break;
  547. }
  548. while ((ptr < end) && (*ptr != '\n')) ptr++;
  549. if (ptr >= end) {
  550. ctxt->controlBufIndex = ctxt->controlBufUsed;
  551. goto get_more;
  552. }
  553. if (*ptr != '\r') ptr++;
  554. }
  555. if (res < 0) goto get_more;
  556. ctxt->controlBufIndex = ptr - ctxt->controlBuf;
  557. return(res / 100);
  558. }
  559. /**
  560. * xmlNanoFTPGetResponse:
  561. * @ctx: an FTP context
  562. *
  563. * Get the response from the FTP server after a command.
  564. * Returns the code number
  565. */
  566. int
  567. xmlNanoFTPGetResponse(void *ctx) {
  568. int res;
  569. res = xmlNanoFTPReadResponse(ctx);
  570. return(res);
  571. }
  572. /**
  573. * xmlNanoFTPCheckResponse:
  574. * @ctx: an FTP context
  575. *
  576. * Check if there is a response from the FTP server after a command.
  577. * Returns the code number, or 0
  578. */
  579. int
  580. xmlNanoFTPCheckResponse(void *ctx) {
  581. xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
  582. fd_set rfd;
  583. struct timeval tv;
  584. if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
  585. tv.tv_sec = 0;
  586. tv.tv_usec = 0;
  587. FD_ZERO(&rfd);
  588. FD_SET(ctxt->controlFd, &rfd);
  589. switch(select(ctxt->controlFd + 1, &rfd, NULL, NULL, &tv)) {
  590. case 0:
  591. return(0);
  592. case -1:
  593. __xmlIOErr(XML_FROM_FTP, 0, "select");
  594. return(-1);
  595. }
  596. return(xmlNanoFTPReadResponse(ctx));
  597. }
  598. /**
  599. * Send the user authentication
  600. */
  601. static int
  602. xmlNanoFTPSendUser(void *ctx) {
  603. xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
  604. char buf[200];
  605. int len;
  606. int res;
  607. if (ctxt->user == NULL)
  608. snprintf(buf, sizeof(buf), "USER anonymous\r\n");
  609. else
  610. snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user);
  611. buf[sizeof(buf) - 1] = 0;
  612. len = strlen(buf);
  613. res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
  614. if (res < 0) {
  615. __xmlIOErr(XML_FROM_FTP, 0, "send failed");
  616. return(res);
  617. }
  618. return(0);
  619. }
  620. /**
  621. * Send the password authentication
  622. */
  623. static int
  624. xmlNanoFTPSendPasswd(void *ctx) {
  625. xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
  626. char buf[200];
  627. int len;
  628. int res;
  629. if (ctxt->passwd == NULL)
  630. snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
  631. else
  632. snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
  633. buf[sizeof(buf) - 1] = 0;
  634. len = strlen(buf);
  635. res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
  636. if (res < 0) {
  637. __xmlIOErr(XML_FROM_FTP, 0, "send failed");
  638. return(res);
  639. }
  640. return(0);
  641. }
  642. /**
  643. * xmlNanoFTPQuit:
  644. * @ctx: an FTP context
  645. *
  646. * Send a QUIT command to the server
  647. *
  648. * Returns -1 in case of error, 0 otherwise
  649. */
  650. int
  651. xmlNanoFTPQuit(void *ctx) {
  652. xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
  653. char buf[200];
  654. int len, res;
  655. if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
  656. snprintf(buf, sizeof(buf), "QUIT\r\n");
  657. len = strlen(buf);
  658. res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
  659. if (res < 0) {
  660. __xmlIOErr(XML_FROM_FTP, 0, "send failed");
  661. return(res);
  662. }
  663. return(0);
  664. }
  665. /**
  666. * xmlNanoFTPConnect:
  667. * @ctx: an FTP context
  668. *
  669. * Tries to open a control connection
  670. *
  671. * Returns -1 in case of error, 0 otherwise
  672. */
  673. int
  674. xmlNanoFTPConnect(void *ctx) {
  675. xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
  676. struct hostent *hp;
  677. int port;
  678. int res;
  679. int addrlen = sizeof (struct sockaddr_in);
  680. if (ctxt == NULL)
  681. return(-1);
  682. if (ctxt->hostname == NULL)
  683. return(-1);
  684. /*
  685. * do the blocking DNS query.
  686. */
  687. if (proxy) {
  688. port = proxyPort;
  689. } else {
  690. port = ctxt->port;
  691. }
  692. if (port == 0)
  693. port = 21;
  694. memset (&ctxt->ftpAddr, 0, sizeof(ctxt->ftpAddr));
  695. #ifdef SUPPORT_IP6
  696. if (have_ipv6 ()) {
  697. struct addrinfo hints, *tmp, *result;
  698. result = NULL;
  699. memset (&hints, 0, sizeof(hints));
  700. hints.ai_socktype = SOCK_STREAM;
  701. if (proxy) {
  702. if (getaddrinfo (proxy, NULL, &hints, &result) != 0) {
  703. __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
  704. return (-1);
  705. }
  706. }
  707. else
  708. if (getaddrinfo (ctxt->hostname, NULL, &hints, &result) != 0) {
  709. __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
  710. return (-1);
  711. }
  712. for (tmp = result; tmp; tmp = tmp->ai_next)
  713. if (tmp->ai_family == AF_INET || tmp->ai_family == AF_INET6)
  714. break;
  715. if (!tmp) {
  716. if (result)
  717. freeaddrinfo (result);
  718. __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
  719. return (-1);
  720. }
  721. if ((size_t)tmp->ai_addrlen > sizeof(ctxt->ftpAddr)) {
  722. if (result)
  723. freeaddrinfo (result);
  724. __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname address mismatch");
  725. return (-1);
  726. }
  727. if (tmp->ai_family == AF_INET6) {
  728. memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
  729. ((struct sockaddr_in6 *) &ctxt->ftpAddr)->sin6_port = htons (port);
  730. ctxt->controlFd = socket (AF_INET6, SOCK_STREAM, 0);
  731. }
  732. else {
  733. memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
  734. ((struct sockaddr_in *) &ctxt->ftpAddr)->sin_port = htons (port);
  735. ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
  736. }
  737. addrlen = tmp->ai_addrlen;
  738. freeaddrinfo (result);
  739. }
  740. else
  741. #endif
  742. {
  743. if (proxy)
  744. hp = gethostbyname (GETHOSTBYNAME_ARG_CAST proxy);
  745. else
  746. hp = gethostbyname (GETHOSTBYNAME_ARG_CAST ctxt->hostname);
  747. if (hp == NULL) {
  748. __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname failed");
  749. return (-1);
  750. }
  751. if ((unsigned int) hp->h_length >
  752. sizeof(((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr)) {
  753. __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname address mismatch");
  754. return (-1);
  755. }
  756. /*
  757. * Prepare the socket
  758. */
  759. ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_family = AF_INET;
  760. memcpy (&((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr,
  761. hp->h_addr_list[0], hp->h_length);
  762. ((struct sockaddr_in *)&ctxt->ftpAddr)->sin_port =
  763. (unsigned short)htons ((unsigned short)port);
  764. ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
  765. addrlen = sizeof (struct sockaddr_in);
  766. }
  767. if (ctxt->controlFd == INVALID_SOCKET) {
  768. __xmlIOErr(XML_FROM_FTP, 0, "socket failed");
  769. return(-1);
  770. }
  771. /*
  772. * Do the connect.
  773. */
  774. if (connect(ctxt->controlFd, (struct sockaddr *) &ctxt->ftpAddr,
  775. addrlen) < 0) {
  776. __xmlIOErr(XML_FROM_FTP, 0, "Failed to create a connection");
  777. closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
  778. ctxt->controlFd = INVALID_SOCKET;
  779. return(-1);
  780. }
  781. /*
  782. * Wait for the HELLO from the server.
  783. */
  784. res = xmlNanoFTPGetResponse(ctxt);
  785. if (res != 2) {
  786. closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
  787. ctxt->controlFd = INVALID_SOCKET;
  788. return(-1);
  789. }
  790. /*
  791. * State diagram for the login operation on the FTP server
  792. *
  793. * Reference: RFC 959
  794. *
  795. * 1
  796. * +---+ USER +---+------------->+---+
  797. * | B |---------->| W | 2 ---->| E |
  798. * +---+ +---+------ | -->+---+
  799. * | | | | |
  800. * 3 | | 4,5 | | |
  801. * -------------- ----- | | |
  802. * | | | | |
  803. * | | | | |
  804. * | --------- |
  805. * | 1| | | |
  806. * V | | | |
  807. * +---+ PASS +---+ 2 | ------>+---+
  808. * | |---------->| W |------------->| S |
  809. * +---+ +---+ ---------->+---+
  810. * | | | | |
  811. * 3 | |4,5| | |
  812. * -------------- -------- |
  813. * | | | | |
  814. * | | | | |
  815. * | -----------
  816. * | 1,3| | | |
  817. * V | 2| | |
  818. * +---+ ACCT +---+-- | ----->+---+
  819. * | |---------->| W | 4,5 -------->| F |
  820. * +---+ +---+------------->+---+
  821. *
  822. * Of course in case of using a proxy this get really nasty and is not
  823. * standardized at all :-(
  824. */
  825. if (proxy) {
  826. int len;
  827. char buf[400];
  828. if (proxyUser != NULL) {
  829. /*
  830. * We need proxy auth
  831. */
  832. snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser);
  833. buf[sizeof(buf) - 1] = 0;
  834. len = strlen(buf);
  835. res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
  836. if (res < 0) {
  837. __xmlIOErr(XML_FROM_FTP, 0, "send failed");
  838. closesocket(ctxt->controlFd);
  839. ctxt->controlFd = INVALID_SOCKET;
  840. return(res);
  841. }
  842. res = xmlNanoFTPGetResponse(ctxt);
  843. switch (res) {
  844. case 2:
  845. if (proxyPasswd == NULL)
  846. break;
  847. /* Falls through. */
  848. case 3:
  849. if (proxyPasswd != NULL)
  850. snprintf(buf, sizeof(buf), "PASS %s\r\n", proxyPasswd);
  851. else
  852. snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
  853. buf[sizeof(buf) - 1] = 0;
  854. len = strlen(buf);
  855. res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
  856. if (res < 0) {
  857. __xmlIOErr(XML_FROM_FTP, 0, "send failed");
  858. closesocket(ctxt->controlFd);
  859. ctxt->controlFd = INVALID_SOCKET;
  860. return(res);
  861. }
  862. res = xmlNanoFTPGetResponse(ctxt);
  863. if (res > 3) {
  864. closesocket(ctxt->controlFd);
  865. ctxt->controlFd = INVALID_SOCKET;
  866. return(-1);
  867. }
  868. break;
  869. case 1:
  870. break;
  871. case 4:
  872. case 5:
  873. case -1:
  874. default:
  875. closesocket(ctxt->controlFd);
  876. ctxt->controlFd = INVALID_SOCKET;
  877. return(-1);
  878. }
  879. }
  880. /*
  881. * We assume we don't need more authentication to the proxy
  882. * and that it succeeded :-\
  883. */
  884. switch (proxyType) {
  885. case 0:
  886. /* we will try in sequence */
  887. case 1:
  888. /* Using SITE command */
  889. snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname);
  890. buf[sizeof(buf) - 1] = 0;
  891. len = strlen(buf);
  892. res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
  893. if (res < 0) {
  894. __xmlIOErr(XML_FROM_FTP, 0, "send failed");
  895. closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
  896. ctxt->controlFd = INVALID_SOCKET;
  897. return(res);
  898. }
  899. res = xmlNanoFTPGetResponse(ctxt);
  900. if (res == 2) {
  901. /* we assume it worked :-\ 1 is error for SITE command */
  902. proxyType = 1;
  903. break;
  904. }
  905. if (proxyType == 1) {
  906. closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
  907. ctxt->controlFd = INVALID_SOCKET;
  908. return(-1);
  909. }
  910. /* Falls through. */
  911. case 2:
  912. /* USER user@host command */
  913. if (ctxt->user == NULL)
  914. snprintf(buf, sizeof(buf), "USER anonymous@%s\r\n",
  915. ctxt->hostname);
  916. else
  917. snprintf(buf, sizeof(buf), "USER %s@%s\r\n",
  918. ctxt->user, ctxt->hostname);
  919. buf[sizeof(buf) - 1] = 0;
  920. len = strlen(buf);
  921. res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
  922. if (res < 0) {
  923. __xmlIOErr(XML_FROM_FTP, 0, "send failed");
  924. closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
  925. ctxt->controlFd = INVALID_SOCKET;
  926. return(res);
  927. }
  928. res = xmlNanoFTPGetResponse(ctxt);
  929. if ((res == 1) || (res == 2)) {
  930. /* we assume it worked :-\ */
  931. proxyType = 2;
  932. return(0);
  933. }
  934. if (ctxt->passwd == NULL)
  935. snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
  936. else
  937. snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
  938. buf[sizeof(buf) - 1] = 0;
  939. len = strlen(buf);
  940. res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
  941. if (res < 0) {
  942. __xmlIOErr(XML_FROM_FTP, 0, "send failed");
  943. closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
  944. ctxt->controlFd = INVALID_SOCKET;
  945. return(res);
  946. }
  947. res = xmlNanoFTPGetResponse(ctxt);
  948. if ((res == 1) || (res == 2)) {
  949. /* we assume it worked :-\ */
  950. proxyType = 2;
  951. return(0);
  952. }
  953. if (proxyType == 2) {
  954. closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
  955. ctxt->controlFd = INVALID_SOCKET;
  956. return(-1);
  957. }
  958. /* Falls through. */
  959. case 3:
  960. /*
  961. * If you need support for other Proxy authentication scheme
  962. * send the code or at least the sequence in use.
  963. */
  964. default:
  965. closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
  966. ctxt->controlFd = INVALID_SOCKET;
  967. return(-1);
  968. }
  969. }
  970. /*
  971. * Non-proxy handling.
  972. */
  973. res = xmlNanoFTPSendUser(ctxt);
  974. if (res < 0) {
  975. closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
  976. ctxt->controlFd = INVALID_SOCKET;
  977. return(-1);
  978. }
  979. res = xmlNanoFTPGetResponse(ctxt);
  980. switch (res) {
  981. case 2:
  982. return(0);
  983. case 3:
  984. break;
  985. case 1:
  986. case 4:
  987. case 5:
  988. case -1:
  989. default:
  990. closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
  991. ctxt->controlFd = INVALID_SOCKET;
  992. return(-1);
  993. }
  994. res = xmlNanoFTPSendPasswd(ctxt);
  995. if (res < 0) {
  996. closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
  997. ctxt->controlFd = INVALID_SOCKET;
  998. return(-1);
  999. }
  1000. res = xmlNanoFTPGetResponse(ctxt);
  1001. switch (res) {
  1002. case 2:
  1003. break;
  1004. case 3:
  1005. __xmlIOErr(XML_FROM_FTP, XML_FTP_ACCNT,
  1006. "FTP server asking for ACCNT on anonymous\n");
  1007. /* Falls through. */
  1008. case 1:
  1009. case 4:
  1010. case 5:
  1011. case -1:
  1012. default:
  1013. closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
  1014. ctxt->controlFd = INVALID_SOCKET;
  1015. return(-1);
  1016. }
  1017. return(0);
  1018. }
  1019. /**
  1020. * xmlNanoFTPConnectTo:
  1021. * @server: an FTP server name
  1022. * @port: the port (use 21 if 0)
  1023. *
  1024. * Tries to open a control connection to the given server/port
  1025. *
  1026. * Returns an fTP context or NULL if it failed
  1027. */
  1028. void*
  1029. xmlNanoFTPConnectTo(const char *server, int port) {
  1030. xmlNanoFTPCtxtPtr ctxt;
  1031. int res;
  1032. xmlNanoFTPInit();
  1033. if (server == NULL)
  1034. return(NULL);
  1035. if (port <= 0)
  1036. return(NULL);
  1037. ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(NULL);
  1038. if (ctxt == NULL)
  1039. return(NULL);
  1040. ctxt->hostname = xmlMemStrdup(server);
  1041. if (ctxt->hostname == NULL) {
  1042. xmlNanoFTPFreeCtxt(ctxt);
  1043. return(NULL);
  1044. }
  1045. ctxt->port = port;
  1046. res = xmlNanoFTPConnect(ctxt);
  1047. if (res < 0) {
  1048. xmlNanoFTPFreeCtxt(ctxt);
  1049. return(NULL);
  1050. }
  1051. return(ctxt);
  1052. }
  1053. /**
  1054. * xmlNanoFTPCwd:
  1055. * @ctx: an FTP context
  1056. * @directory: a directory on the server
  1057. *
  1058. * Tries to change the remote directory
  1059. *
  1060. * Returns -1 in case of error, 1 if CWD worked, 0 if it failed
  1061. */
  1062. int
  1063. xmlNanoFTPCwd(void *ctx, const char *directory) {
  1064. xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
  1065. char buf[400];
  1066. int len;
  1067. int res;
  1068. if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
  1069. if (directory == NULL) return 0;
  1070. /*
  1071. * Expected response code for CWD:
  1072. *
  1073. * CWD
  1074. * 250
  1075. * 500, 501, 502, 421, 530, 550
  1076. */
  1077. snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
  1078. buf[sizeof(buf) - 1] = 0;
  1079. len = strlen(buf);
  1080. res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
  1081. if (res < 0) {
  1082. __xmlIOErr(XML_FROM_FTP, 0, "send failed");
  1083. return(res);
  1084. }
  1085. res = xmlNanoFTPGetResponse(ctxt);
  1086. if (res == 4) {
  1087. return(-1);
  1088. }
  1089. if (res == 2) return(1);
  1090. if (res == 5) {
  1091. return(0);
  1092. }
  1093. return(0);
  1094. }
  1095. /**
  1096. * xmlNanoFTPDele:
  1097. * @ctx: an FTP context
  1098. * @file: a file or directory on the server
  1099. *
  1100. * Tries to delete an item (file or directory) from server
  1101. *
  1102. * Returns -1 in case of error, 1 if DELE worked, 0 if it failed
  1103. */
  1104. int
  1105. xmlNanoFTPDele(void *ctx, const char *file) {
  1106. xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
  1107. char buf[400];
  1108. int len;
  1109. int res;
  1110. if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET) ||
  1111. (file == NULL)) return(-1);
  1112. /*
  1113. * Expected response code for DELE:
  1114. *
  1115. * DELE
  1116. * 250
  1117. * 450, 550
  1118. * 500, 501, 502, 421, 530
  1119. */
  1120. snprintf(buf, sizeof(buf), "DELE %s\r\n", file);
  1121. buf[sizeof(buf) - 1] = 0;
  1122. len = strlen(buf);
  1123. res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
  1124. if (res < 0) {
  1125. __xmlIOErr(XML_FROM_FTP, 0, "send failed");
  1126. return(res);
  1127. }
  1128. res = xmlNanoFTPGetResponse(ctxt);
  1129. if (res == 4) {
  1130. return(-1);
  1131. }
  1132. if (res == 2) return(1);
  1133. if (res == 5) {
  1134. return(0);
  1135. }
  1136. return(0);
  1137. }
  1138. /**
  1139. * xmlNanoFTPGetConnection:
  1140. * @ctx: an FTP context
  1141. *
  1142. * Try to open a data connection to the server. Currently only
  1143. * passive mode is supported.
  1144. *
  1145. * Returns -1 in case of error, 0 otherwise
  1146. */
  1147. SOCKET
  1148. xmlNanoFTPGetConnection(void *ctx) {
  1149. xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
  1150. char buf[200], *cur;
  1151. int len, i;
  1152. int res;
  1153. unsigned char ad[6], *adp, *portp;
  1154. unsigned int temp[6];
  1155. #ifdef SUPPORT_IP6
  1156. struct sockaddr_storage dataAddr;
  1157. #else
  1158. struct sockaddr_in dataAddr;
  1159. #endif
  1160. XML_SOCKLEN_T dataAddrLen;
  1161. if (ctxt == NULL) return INVALID_SOCKET;
  1162. memset (&dataAddr, 0, sizeof(dataAddr));
  1163. #ifdef SUPPORT_IP6
  1164. if ((ctxt->ftpAddr).ss_family == AF_INET6) {
  1165. ctxt->dataFd = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP);
  1166. ((struct sockaddr_in6 *)&dataAddr)->sin6_family = AF_INET6;
  1167. dataAddrLen = sizeof(struct sockaddr_in6);
  1168. } else
  1169. #endif
  1170. {
  1171. ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
  1172. ((struct sockaddr_in *)&dataAddr)->sin_family = AF_INET;
  1173. dataAddrLen = sizeof (struct sockaddr_in);
  1174. }
  1175. if (ctxt->dataFd == INVALID_SOCKET) {
  1176. __xmlIOErr(XML_FROM_FTP, 0, "socket failed");
  1177. return INVALID_SOCKET;
  1178. }
  1179. if (ctxt->passive) {
  1180. #ifdef SUPPORT_IP6
  1181. if ((ctxt->ftpAddr).ss_family == AF_INET6)
  1182. snprintf (buf, sizeof(buf), "EPSV\r\n");
  1183. else
  1184. #endif
  1185. snprintf (buf, sizeof(buf), "PASV\r\n");
  1186. len = strlen (buf);
  1187. res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
  1188. if (res < 0) {
  1189. __xmlIOErr(XML_FROM_FTP, 0, "send failed");
  1190. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1191. return INVALID_SOCKET;
  1192. }
  1193. res = xmlNanoFTPReadResponse(ctx);
  1194. if (res != 2) {
  1195. if (res == 5) {
  1196. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1197. return INVALID_SOCKET;
  1198. } else {
  1199. /*
  1200. * retry with an active connection
  1201. */
  1202. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1203. ctxt->passive = 0;
  1204. }
  1205. }
  1206. cur = &ctxt->controlBuf[ctxt->controlBufAnswer];
  1207. while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
  1208. #ifdef SUPPORT_IP6
  1209. if ((ctxt->ftpAddr).ss_family == AF_INET6) {
  1210. if (sscanf (cur, "%u", &temp[0]) != 1) {
  1211. __xmlIOErr(XML_FROM_FTP, XML_FTP_EPSV_ANSWER,
  1212. "Invalid answer to EPSV\n");
  1213. if (ctxt->dataFd != INVALID_SOCKET) {
  1214. closesocket (ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1215. }
  1216. return INVALID_SOCKET;
  1217. }
  1218. memcpy (&((struct sockaddr_in6 *)&dataAddr)->sin6_addr, &((struct sockaddr_in6 *)&ctxt->ftpAddr)->sin6_addr, sizeof(struct in6_addr));
  1219. ((struct sockaddr_in6 *)&dataAddr)->sin6_port = htons (temp[0]);
  1220. }
  1221. else
  1222. #endif
  1223. {
  1224. if (sscanf (cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2],
  1225. &temp[3], &temp[4], &temp[5]) != 6) {
  1226. __xmlIOErr(XML_FROM_FTP, XML_FTP_PASV_ANSWER,
  1227. "Invalid answer to PASV\n");
  1228. if (ctxt->dataFd != INVALID_SOCKET) {
  1229. closesocket (ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1230. }
  1231. return INVALID_SOCKET;
  1232. }
  1233. for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
  1234. memcpy (&((struct sockaddr_in *)&dataAddr)->sin_addr, &ad[0], 4);
  1235. memcpy (&((struct sockaddr_in *)&dataAddr)->sin_port, &ad[4], 2);
  1236. }
  1237. if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
  1238. __xmlIOErr(XML_FROM_FTP, 0, "Failed to create a data connection");
  1239. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1240. return INVALID_SOCKET;
  1241. }
  1242. } else {
  1243. getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
  1244. #ifdef SUPPORT_IP6
  1245. if ((ctxt->ftpAddr).ss_family == AF_INET6)
  1246. ((struct sockaddr_in6 *)&dataAddr)->sin6_port = 0;
  1247. else
  1248. #endif
  1249. ((struct sockaddr_in *)&dataAddr)->sin_port = 0;
  1250. if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
  1251. __xmlIOErr(XML_FROM_FTP, 0, "bind failed");
  1252. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1253. return INVALID_SOCKET;
  1254. }
  1255. getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
  1256. if (listen(ctxt->dataFd, 1) < 0) {
  1257. __xmlIOErr(XML_FROM_FTP, 0, "listen failed");
  1258. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1259. return INVALID_SOCKET;
  1260. }
  1261. #ifdef SUPPORT_IP6
  1262. if ((ctxt->ftpAddr).ss_family == AF_INET6) {
  1263. char buf6[INET6_ADDRSTRLEN];
  1264. inet_ntop (AF_INET6, &((struct sockaddr_in6 *)&dataAddr)->sin6_addr,
  1265. buf6, INET6_ADDRSTRLEN);
  1266. adp = (unsigned char *) buf6;
  1267. portp = (unsigned char *) &((struct sockaddr_in6 *)&dataAddr)->sin6_port;
  1268. snprintf (buf, sizeof(buf), "EPRT |2|%s|%s|\r\n", adp, portp);
  1269. } else
  1270. #endif
  1271. {
  1272. adp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_addr;
  1273. portp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_port;
  1274. snprintf (buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
  1275. adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
  1276. portp[0] & 0xff, portp[1] & 0xff);
  1277. }
  1278. buf[sizeof(buf) - 1] = 0;
  1279. len = strlen(buf);
  1280. res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
  1281. if (res < 0) {
  1282. __xmlIOErr(XML_FROM_FTP, 0, "send failed");
  1283. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1284. return INVALID_SOCKET;
  1285. }
  1286. res = xmlNanoFTPGetResponse(ctxt);
  1287. if (res != 2) {
  1288. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1289. return INVALID_SOCKET;
  1290. }
  1291. }
  1292. return(ctxt->dataFd);
  1293. }
  1294. /**
  1295. * xmlNanoFTPCloseConnection:
  1296. * @ctx: an FTP context
  1297. *
  1298. * Close the data connection from the server
  1299. *
  1300. * Returns -1 in case of error, 0 otherwise
  1301. */
  1302. int
  1303. xmlNanoFTPCloseConnection(void *ctx) {
  1304. xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
  1305. int res;
  1306. fd_set rfd, efd;
  1307. struct timeval tv;
  1308. if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
  1309. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1310. tv.tv_sec = 15;
  1311. tv.tv_usec = 0;
  1312. FD_ZERO(&rfd);
  1313. FD_SET(ctxt->controlFd, &rfd);
  1314. FD_ZERO(&efd);
  1315. FD_SET(ctxt->controlFd, &efd);
  1316. res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv);
  1317. if (res < 0) {
  1318. closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
  1319. return(-1);
  1320. }
  1321. if (res == 0) {
  1322. closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
  1323. } else {
  1324. res = xmlNanoFTPGetResponse(ctxt);
  1325. if (res != 2) {
  1326. closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
  1327. return(-1);
  1328. }
  1329. }
  1330. return(0);
  1331. }
  1332. /**
  1333. * xmlNanoFTPParseList:
  1334. * @list: some data listing received from the server
  1335. * @callback: the user callback
  1336. * @userData: the user callback data
  1337. *
  1338. * Parse at most one entry from the listing.
  1339. *
  1340. * Returns -1 in case of error, the length of data parsed otherwise
  1341. */
  1342. static int
  1343. xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
  1344. const char *cur = list;
  1345. char filename[151];
  1346. char attrib[11];
  1347. char owner[11];
  1348. char group[11];
  1349. char month[4];
  1350. int year = 0;
  1351. int minute = 0;
  1352. int hour = 0;
  1353. int day = 0;
  1354. unsigned long size = 0;
  1355. int links = 0;
  1356. int i;
  1357. if (!strncmp(cur, "total", 5)) {
  1358. cur += 5;
  1359. while (*cur == ' ') cur++;
  1360. while ((*cur >= '0') && (*cur <= '9'))
  1361. links = (links * 10) + (*cur++ - '0');
  1362. while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
  1363. cur++;
  1364. return(cur - list);
  1365. } else if (*list == '+') {
  1366. return(0);
  1367. } else {
  1368. while ((*cur == ' ') || (*cur == '\n') || (*cur == '\r'))
  1369. cur++;
  1370. if (*cur == 0) return(0);
  1371. i = 0;
  1372. while (*cur != ' ') {
  1373. if (i < 10)
  1374. attrib[i++] = *cur;
  1375. cur++;
  1376. if (*cur == 0) return(0);
  1377. }
  1378. attrib[10] = 0;
  1379. while (*cur == ' ') cur++;
  1380. if (*cur == 0) return(0);
  1381. while ((*cur >= '0') && (*cur <= '9'))
  1382. links = (links * 10) + (*cur++ - '0');
  1383. while (*cur == ' ') cur++;
  1384. if (*cur == 0) return(0);
  1385. i = 0;
  1386. while (*cur != ' ') {
  1387. if (i < 10)
  1388. owner[i++] = *cur;
  1389. cur++;
  1390. if (*cur == 0) return(0);
  1391. }
  1392. owner[i] = 0;
  1393. while (*cur == ' ') cur++;
  1394. if (*cur == 0) return(0);
  1395. i = 0;
  1396. while (*cur != ' ') {
  1397. if (i < 10)
  1398. group[i++] = *cur;
  1399. cur++;
  1400. if (*cur == 0) return(0);
  1401. }
  1402. group[i] = 0;
  1403. while (*cur == ' ') cur++;
  1404. if (*cur == 0) return(0);
  1405. while ((*cur >= '0') && (*cur <= '9'))
  1406. size = (size * 10) + (*cur++ - '0');
  1407. while (*cur == ' ') cur++;
  1408. if (*cur == 0) return(0);
  1409. i = 0;
  1410. while (*cur != ' ') {
  1411. if (i < 3)
  1412. month[i++] = *cur;
  1413. cur++;
  1414. if (*cur == 0) return(0);
  1415. }
  1416. month[i] = 0;
  1417. while (*cur == ' ') cur++;
  1418. if (*cur == 0) return(0);
  1419. while ((*cur >= '0') && (*cur <= '9'))
  1420. day = (day * 10) + (*cur++ - '0');
  1421. while (*cur == ' ') cur++;
  1422. if (*cur == 0) return(0);
  1423. if ((cur[1] == 0) || (cur[2] == 0)) return(0);
  1424. if ((cur[1] == ':') || (cur[2] == ':')) {
  1425. while ((*cur >= '0') && (*cur <= '9'))
  1426. hour = (hour * 10) + (*cur++ - '0');
  1427. if (*cur == ':') cur++;
  1428. while ((*cur >= '0') && (*cur <= '9'))
  1429. minute = (minute * 10) + (*cur++ - '0');
  1430. } else {
  1431. while ((*cur >= '0') && (*cur <= '9'))
  1432. year = (year * 10) + (*cur++ - '0');
  1433. }
  1434. while (*cur == ' ') cur++;
  1435. if (*cur == 0) return(0);
  1436. i = 0;
  1437. while ((*cur != '\n') && (*cur != '\r')) {
  1438. if (i < 150)
  1439. filename[i++] = *cur;
  1440. cur++;
  1441. if (*cur == 0) return(0);
  1442. }
  1443. filename[i] = 0;
  1444. if ((*cur != '\n') && (*cur != '\r'))
  1445. return(0);
  1446. while ((*cur == '\n') || (*cur == '\r'))
  1447. cur++;
  1448. }
  1449. if (callback != NULL) {
  1450. callback(userData, filename, attrib, owner, group, size, links,
  1451. year, month, day, hour, minute);
  1452. }
  1453. return(cur - list);
  1454. }
  1455. /**
  1456. * xmlNanoFTPList:
  1457. * @ctx: an FTP context
  1458. * @callback: the user callback
  1459. * @userData: the user callback data
  1460. * @filename: optional files to list
  1461. *
  1462. * Do a listing on the server. All files info are passed back
  1463. * in the callbacks.
  1464. *
  1465. * Returns -1 in case of error, 0 otherwise
  1466. */
  1467. int
  1468. xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
  1469. const char *filename) {
  1470. xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
  1471. char buf[4096 + 1];
  1472. int len, res;
  1473. int indx = 0, base;
  1474. fd_set rfd, efd;
  1475. struct timeval tv;
  1476. if (ctxt == NULL) return (-1);
  1477. if (filename == NULL) {
  1478. if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
  1479. return(-1);
  1480. ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
  1481. if (ctxt->dataFd == INVALID_SOCKET)
  1482. return(-1);
  1483. snprintf(buf, sizeof(buf), "LIST -L\r\n");
  1484. } else {
  1485. if (filename[0] != '/') {
  1486. if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
  1487. return(-1);
  1488. }
  1489. ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
  1490. if (ctxt->dataFd == INVALID_SOCKET)
  1491. return(-1);
  1492. snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
  1493. }
  1494. buf[sizeof(buf) - 1] = 0;
  1495. len = strlen(buf);
  1496. res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
  1497. if (res < 0) {
  1498. __xmlIOErr(XML_FROM_FTP, 0, "send failed");
  1499. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1500. return(res);
  1501. }
  1502. res = xmlNanoFTPReadResponse(ctxt);
  1503. if (res != 1) {
  1504. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1505. return(-res);
  1506. }
  1507. do {
  1508. tv.tv_sec = 1;
  1509. tv.tv_usec = 0;
  1510. FD_ZERO(&rfd);
  1511. FD_SET(ctxt->dataFd, &rfd);
  1512. FD_ZERO(&efd);
  1513. FD_SET(ctxt->dataFd, &efd);
  1514. res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
  1515. if (res < 0) {
  1516. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1517. return(-1);
  1518. }
  1519. if (res == 0) {
  1520. res = xmlNanoFTPCheckResponse(ctxt);
  1521. if (res < 0) {
  1522. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1523. ctxt->dataFd = INVALID_SOCKET;
  1524. return(-1);
  1525. }
  1526. if (res == 2) {
  1527. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1528. return(0);
  1529. }
  1530. continue;
  1531. }
  1532. if ((len = recv(ctxt->dataFd, &buf[indx], sizeof(buf) - (indx + 1), 0)) < 0) {
  1533. __xmlIOErr(XML_FROM_FTP, 0, "recv");
  1534. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1535. ctxt->dataFd = INVALID_SOCKET;
  1536. return(-1);
  1537. }
  1538. indx += len;
  1539. buf[indx] = 0;
  1540. base = 0;
  1541. do {
  1542. res = xmlNanoFTPParseList(&buf[base], callback, userData);
  1543. base += res;
  1544. } while (res > 0);
  1545. memmove(&buf[0], &buf[base], indx - base);
  1546. indx -= base;
  1547. } while (len != 0);
  1548. xmlNanoFTPCloseConnection(ctxt);
  1549. return(0);
  1550. }
  1551. /**
  1552. * xmlNanoFTPGetSocket:
  1553. * @ctx: an FTP context
  1554. * @filename: the file to retrieve (or NULL if path is in context).
  1555. *
  1556. * Initiate fetch of the given file from the server.
  1557. *
  1558. * Returns the socket for the data connection, or <0 in case of error
  1559. */
  1560. SOCKET
  1561. xmlNanoFTPGetSocket(void *ctx, const char *filename) {
  1562. xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
  1563. char buf[300];
  1564. int res, len;
  1565. if (ctx == NULL)
  1566. return INVALID_SOCKET;
  1567. if ((filename == NULL) && (ctxt->path == NULL))
  1568. return INVALID_SOCKET;
  1569. ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
  1570. if (ctxt->dataFd == INVALID_SOCKET)
  1571. return INVALID_SOCKET;
  1572. snprintf(buf, sizeof(buf), "TYPE I\r\n");
  1573. len = strlen(buf);
  1574. res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
  1575. if (res < 0) {
  1576. __xmlIOErr(XML_FROM_FTP, 0, "send failed");
  1577. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1578. return INVALID_SOCKET;
  1579. }
  1580. res = xmlNanoFTPReadResponse(ctxt);
  1581. if (res != 2) {
  1582. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1583. return INVALID_SOCKET;
  1584. }
  1585. if (filename == NULL)
  1586. snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
  1587. else
  1588. snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
  1589. buf[sizeof(buf) - 1] = 0;
  1590. len = strlen(buf);
  1591. res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
  1592. if (res < 0) {
  1593. __xmlIOErr(XML_FROM_FTP, 0, "send failed");
  1594. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1595. return INVALID_SOCKET;
  1596. }
  1597. res = xmlNanoFTPReadResponse(ctxt);
  1598. if (res != 1) {
  1599. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1600. return INVALID_SOCKET;
  1601. }
  1602. return(ctxt->dataFd);
  1603. }
  1604. /**
  1605. * xmlNanoFTPGet:
  1606. * @ctx: an FTP context
  1607. * @callback: the user callback
  1608. * @userData: the user callback data
  1609. * @filename: the file to retrieve
  1610. *
  1611. * Fetch the given file from the server. All data are passed back
  1612. * in the callbacks. The last callback has a size of 0 block.
  1613. *
  1614. * Returns -1 in case of error, 0 otherwise
  1615. */
  1616. int
  1617. xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
  1618. const char *filename) {
  1619. xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
  1620. char buf[4096];
  1621. int len = 0, res;
  1622. fd_set rfd;
  1623. struct timeval tv;
  1624. if (ctxt == NULL) return(-1);
  1625. if ((filename == NULL) && (ctxt->path == NULL))
  1626. return(-1);
  1627. if (callback == NULL)
  1628. return(-1);
  1629. if (xmlNanoFTPGetSocket(ctxt, filename) == INVALID_SOCKET)
  1630. return(-1);
  1631. do {
  1632. tv.tv_sec = 1;
  1633. tv.tv_usec = 0;
  1634. FD_ZERO(&rfd);
  1635. FD_SET(ctxt->dataFd, &rfd);
  1636. res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
  1637. if (res < 0) {
  1638. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1639. return(-1);
  1640. }
  1641. if (res == 0) {
  1642. res = xmlNanoFTPCheckResponse(ctxt);
  1643. if (res < 0) {
  1644. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1645. ctxt->dataFd = INVALID_SOCKET;
  1646. return(-1);
  1647. }
  1648. if (res == 2) {
  1649. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1650. return(0);
  1651. }
  1652. continue;
  1653. }
  1654. if ((len = recv(ctxt->dataFd, buf, sizeof(buf), 0)) < 0) {
  1655. __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
  1656. callback(userData, buf, len);
  1657. closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
  1658. return(-1);
  1659. }
  1660. callback(userData, buf, len);
  1661. } while (len != 0);
  1662. return(xmlNanoFTPCloseConnection(ctxt));
  1663. }
  1664. /**
  1665. * xmlNanoFTPRead:
  1666. * @ctx: the FTP context
  1667. * @dest: a buffer
  1668. * @len: the buffer length
  1669. *
  1670. * This function tries to read @len bytes from the existing FTP connection
  1671. * and saves them in @dest. This is a blocking call.
  1672. *
  1673. * Returns the number of byte read. 0 is an indication of an end of connection.
  1674. * -1 indicates a parameter error.
  1675. */
  1676. int
  1677. xmlNanoFTPRead(void *ctx, void *dest, int len) {
  1678. xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
  1679. if (ctx == NULL) return(-1);
  1680. if (ctxt->dataFd == INVALID_SOCKET) return(0);
  1681. if (dest == NULL) return(-1);
  1682. if (len <= 0) return(0);
  1683. len = recv(ctxt->dataFd, dest, len, 0);
  1684. if (len <= 0) {
  1685. if (len < 0)
  1686. __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
  1687. xmlNanoFTPCloseConnection(ctxt);
  1688. }
  1689. return(len);
  1690. }
  1691. /**
  1692. * xmlNanoFTPOpen:
  1693. * @URL: the URL to the resource
  1694. *
  1695. * Start to fetch the given ftp:// resource
  1696. *
  1697. * Returns an FTP context, or NULL
  1698. */
  1699. void*
  1700. xmlNanoFTPOpen(const char *URL) {
  1701. xmlNanoFTPCtxtPtr ctxt;
  1702. SOCKET sock;
  1703. xmlNanoFTPInit();
  1704. if (URL == NULL) return(NULL);
  1705. if (strncmp("ftp://", URL, 6)) return(NULL);
  1706. ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL);
  1707. if (ctxt == NULL) return(NULL);
  1708. if (xmlNanoFTPConnect(ctxt) < 0) {
  1709. xmlNanoFTPFreeCtxt(ctxt);
  1710. return(NULL);
  1711. }
  1712. sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
  1713. if (sock == INVALID_SOCKET) {
  1714. xmlNanoFTPFreeCtxt(ctxt);
  1715. return(NULL);
  1716. }
  1717. return(ctxt);
  1718. }
  1719. /**
  1720. * xmlNanoFTPClose:
  1721. * @ctx: an FTP context
  1722. *
  1723. * Close the connection and both control and transport
  1724. *
  1725. * Returns -1 in case of error, 0 otherwise
  1726. */
  1727. int
  1728. xmlNanoFTPClose(void *ctx) {
  1729. xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
  1730. if (ctxt == NULL)
  1731. return(-1);
  1732. if (ctxt->dataFd != INVALID_SOCKET) {
  1733. closesocket(ctxt->dataFd);
  1734. ctxt->dataFd = INVALID_SOCKET;
  1735. }
  1736. if (ctxt->controlFd != INVALID_SOCKET) {
  1737. xmlNanoFTPQuit(ctxt);
  1738. closesocket(ctxt->controlFd);
  1739. ctxt->controlFd = INVALID_SOCKET;
  1740. }
  1741. xmlNanoFTPFreeCtxt(ctxt);
  1742. return(0);
  1743. }
  1744. #ifdef STANDALONE
  1745. /************************************************************************
  1746. * *
  1747. * Basic test in Standalone mode *
  1748. * *
  1749. ************************************************************************/
  1750. static
  1751. void ftpList(void *userData, const char *filename, const char* attrib,
  1752. const char *owner, const char *group, unsigned long size, int links,
  1753. int year, const char *month, int day, int hour, int minute) {
  1754. xmlGenericError(xmlGenericErrorContext,
  1755. "%s %s %s %ld %s\n", attrib, owner, group, size, filename);
  1756. }
  1757. static
  1758. void ftpData(void *userData, const char *data, int len) {
  1759. if (userData == NULL) return;
  1760. if (len <= 0) {
  1761. fclose((FILE*)userData);
  1762. return;
  1763. }
  1764. fwrite(data, len, 1, (FILE*)userData);
  1765. }
  1766. int main(int argc, char **argv) {
  1767. void *ctxt;
  1768. FILE *output;
  1769. char *tstfile = NULL;
  1770. xmlNanoFTPInit();
  1771. if (argc > 1) {
  1772. ctxt = xmlNanoFTPNewCtxt(argv[1]);
  1773. if (xmlNanoFTPConnect(ctxt) < 0) {
  1774. xmlGenericError(xmlGenericErrorContext,
  1775. "Couldn't connect to %s\n", argv[1]);
  1776. exit(1);
  1777. }
  1778. if (argc > 2)
  1779. tstfile = argv[2];
  1780. } else
  1781. ctxt = xmlNanoFTPConnectTo("localhost", 0);
  1782. if (ctxt == NULL) {
  1783. xmlGenericError(xmlGenericErrorContext,
  1784. "Couldn't connect to localhost\n");
  1785. exit(1);
  1786. }
  1787. xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
  1788. output = fopen("/tmp/tstdata", "w");
  1789. if (output != NULL) {
  1790. if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
  1791. xmlGenericError(xmlGenericErrorContext,
  1792. "Failed to get file\n");
  1793. }
  1794. xmlNanoFTPClose(ctxt);
  1795. exit(0);
  1796. }
  1797. #endif /* STANDALONE */
  1798. #else /* !LIBXML_FTP_ENABLED */
  1799. #ifdef STANDALONE
  1800. #include <stdio.h>
  1801. int main(int argc, char **argv) {
  1802. xmlGenericError(xmlGenericErrorContext,
  1803. "%s : FTP support not compiled in\n", argv[0]);
  1804. return(0);
  1805. }
  1806. #endif /* STANDALONE */
  1807. #endif /* LIBXML_FTP_ENABLED */