This source file includes following definitions.
- get_ftp_result
- php_stream_ftp_stream_stat
- php_stream_ftp_stream_close
- php_ftp_fopen_connect
- php_fopen_do_pasv
- php_stream_url_wrap_ftp
- php_ftp_dirstream_read
- php_ftp_dirstream_close
- php_stream_ftp_opendir
- php_stream_ftp_url_stat
- php_stream_ftp_unlink
- php_stream_ftp_rename
- php_stream_ftp_mkdir
- php_stream_ftp_rmdir
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 #include "php.h"
24 #include "php_globals.h"
25 #include "php_network.h"
26 #include "php_ini.h"
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34
35 #ifdef PHP_WIN32
36 #include <winsock2.h>
37 #define O_RDONLY _O_RDONLY
38 #include "win32/param.h"
39 #else
40 #include <sys/param.h>
41 #endif
42
43 #include "php_standard.h"
44
45 #include <sys/types.h>
46 #if HAVE_SYS_SOCKET_H
47 #include <sys/socket.h>
48 #endif
49
50 #ifdef PHP_WIN32
51 #include <winsock2.h>
52 #elif defined(NETWARE) && defined(USE_WINSOCK)
53 #include <novsock2.h>
54 #else
55 #include <netinet/in.h>
56 #include <netdb.h>
57 #if HAVE_ARPA_INET_H
58 #include <arpa/inet.h>
59 #endif
60 #endif
61
62 #if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE)
63 #undef AF_UNIX
64 #endif
65
66 #if defined(AF_UNIX)
67 #include <sys/un.h>
68 #endif
69
70 #include "php_fopen_wrappers.h"
71
72 #define FTPS_ENCRYPT_DATA 1
73 #define GET_FTP_RESULT(stream) get_ftp_result((stream), tmp_line, sizeof(tmp_line))
74
75 typedef struct _php_ftp_dirstream_data {
76 php_stream *datastream;
77 php_stream *controlstream;
78 php_stream *dirstream;
79 } php_ftp_dirstream_data;
80
81
82
83 static inline int get_ftp_result(php_stream *stream, char *buffer, size_t buffer_size)
84 {
85 while (php_stream_gets(stream, buffer, buffer_size-1) &&
86 !(isdigit((int) buffer[0]) && isdigit((int) buffer[1]) &&
87 isdigit((int) buffer[2]) && buffer[3] == ' '));
88 return strtol(buffer, NULL, 10);
89 }
90
91
92
93
94 static int php_stream_ftp_stream_stat(php_stream_wrapper *wrapper, php_stream *stream, php_stream_statbuf *ssb)
95 {
96
97
98 return -1;
99 }
100
101
102
103
104 static int php_stream_ftp_stream_close(php_stream_wrapper *wrapper, php_stream *stream)
105 {
106 php_stream *controlstream = stream->wrapperthis;
107 int ret = 0;
108
109 if (controlstream) {
110 if (strpbrk(stream->mode, "wa+")) {
111 char tmp_line[512];
112 int result;
113
114
115 result = GET_FTP_RESULT(controlstream);
116 if (result != 226 && result != 250) {
117 php_error_docref(NULL, E_WARNING, "FTP server error %d:%s", result, tmp_line);
118 ret = EOF;
119 }
120 }
121
122 php_stream_write_string(controlstream, "QUIT\r\n");
123 php_stream_close(controlstream);
124 stream->wrapperthis = NULL;
125 }
126
127 return ret;
128 }
129
130
131
132
133 static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char *path, const char *mode, int options,
134 zend_string **opened_path, php_stream_context *context, php_stream **preuseid,
135 php_url **presource, int *puse_ssl, int *puse_ssl_on_data)
136 {
137 php_stream *stream = NULL, *reuseid = NULL;
138 php_url *resource = NULL;
139 int result, use_ssl, use_ssl_on_data = 0, tmp_len;
140 char tmp_line[512];
141 char *transport;
142 int transport_len;
143
144 resource = php_url_parse(path);
145 if (resource == NULL || resource->path == NULL) {
146 if (resource && presource) {
147 *presource = resource;
148 }
149 return NULL;
150 }
151
152 use_ssl = resource->scheme && (strlen(resource->scheme) > 3) && resource->scheme[3] == 's';
153
154
155 if (resource->port == 0)
156 resource->port = 21;
157
158 transport_len = (int)spprintf(&transport, 0, "tcp://%s:%d", resource->host, resource->port);
159 stream = php_stream_xport_create(transport, transport_len, REPORT_ERRORS, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, NULL, NULL, context, NULL, NULL);
160 efree(transport);
161 if (stream == NULL) {
162 result = 0;
163 goto connect_errexit;
164 }
165
166 php_stream_context_set(stream, context);
167 php_stream_notify_info(context, PHP_STREAM_NOTIFY_CONNECT, NULL, 0);
168
169
170 result = GET_FTP_RESULT(stream);
171 if (result > 299 || result < 200) {
172 php_stream_notify_error(context, PHP_STREAM_NOTIFY_FAILURE, tmp_line, result);
173 goto connect_errexit;
174 }
175
176 if (use_ssl) {
177
178
179 php_stream_write_string(stream, "AUTH TLS\r\n");
180
181
182 result = GET_FTP_RESULT(stream);
183 if (result != 234) {
184
185 php_stream_write_string(stream, "AUTH SSL\r\n");
186
187
188 result = GET_FTP_RESULT(stream);
189 if (result != 334) {
190 use_ssl = 0;
191 } else {
192
193
194 reuseid = stream;
195 }
196 } else {
197
198
199
200 }
201
202 }
203
204 if (use_ssl) {
205 if (php_stream_xport_crypto_setup(stream,
206 STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0
207 || php_stream_xport_crypto_enable(stream, 1) < 0) {
208 php_stream_wrapper_log_error(wrapper, options, "Unable to activate SSL mode");
209 php_stream_close(stream);
210 stream = NULL;
211 goto connect_errexit;
212 }
213
214
215 php_stream_write_string(stream, "PBSZ 0\r\n");
216
217
218 result = GET_FTP_RESULT(stream);
219
220
221 #if FTPS_ENCRYPT_DATA
222 php_stream_write_string(stream, "PROT P\r\n");
223
224
225 result = GET_FTP_RESULT(stream);
226 use_ssl_on_data = (result >= 200 && result<=299) || reuseid;
227 #else
228 php_stream_write_string(stream, "PROT C\r\n");
229
230
231 result = GET_FTP_RESULT(stream);
232 #endif
233 }
234
235 #define PHP_FTP_CNTRL_CHK(val, val_len, err_msg) { \
236 unsigned char *s = (unsigned char *) val, *e = (unsigned char *) s + val_len; \
237 while (s < e) { \
238 if (iscntrl(*s)) { \
239 php_stream_wrapper_log_error(wrapper, options, err_msg, val); \
240 goto connect_errexit; \
241 } \
242 s++; \
243 } \
244 }
245
246
247 if (resource->user != NULL) {
248 tmp_len = (int)php_raw_url_decode(resource->user, (int)strlen(resource->user));
249
250 PHP_FTP_CNTRL_CHK(resource->user, tmp_len, "Invalid login %s")
251
252 php_stream_printf(stream, "USER %s\r\n", resource->user);
253 } else {
254 php_stream_write_string(stream, "USER anonymous\r\n");
255 }
256
257
258 result = GET_FTP_RESULT(stream);
259
260
261 if (result >= 300 && result <= 399) {
262 php_stream_notify_info(context, PHP_STREAM_NOTIFY_AUTH_REQUIRED, tmp_line, 0);
263
264 if (resource->pass != NULL) {
265 tmp_len = (int)php_raw_url_decode(resource->pass, (int)strlen(resource->pass));
266
267 PHP_FTP_CNTRL_CHK(resource->pass, tmp_len, "Invalid password %s")
268
269 php_stream_printf(stream, "PASS %s\r\n", resource->pass);
270 } else {
271
272
273 if (FG(from_address)) {
274 php_stream_printf(stream, "PASS %s\r\n", FG(from_address));
275 } else {
276 php_stream_write_string(stream, "PASS anonymous\r\n");
277 }
278 }
279
280
281 result = GET_FTP_RESULT(stream);
282
283 if (result > 299 || result < 200) {
284 php_stream_notify_error(context, PHP_STREAM_NOTIFY_AUTH_RESULT, tmp_line, result);
285 } else {
286 php_stream_notify_info(context, PHP_STREAM_NOTIFY_AUTH_RESULT, tmp_line, result);
287 }
288 }
289 if (result > 299 || result < 200) {
290 goto connect_errexit;
291 }
292
293 if (puse_ssl) {
294 *puse_ssl = use_ssl;
295 }
296 if (puse_ssl_on_data) {
297 *puse_ssl_on_data = use_ssl_on_data;
298 }
299 if (preuseid) {
300 *preuseid = reuseid;
301 }
302 if (presource) {
303 *presource = resource;
304 }
305
306 return stream;
307
308 connect_errexit:
309 if (resource) {
310 php_url_free(resource);
311 }
312
313 if (stream) {
314 php_stream_close(stream);
315 }
316
317 return NULL;
318 }
319
320
321
322
323 static unsigned short php_fopen_do_pasv(php_stream *stream, char *ip, size_t ip_size, char **phoststart)
324 {
325 char tmp_line[512];
326 int result, i;
327 unsigned short portno;
328 char *tpath, *ttpath, *hoststart=NULL;
329
330 #ifdef HAVE_IPV6
331
332 php_stream_write_string(stream, "EPSV\r\n");
333 result = GET_FTP_RESULT(stream);
334
335
336 if (result != 229) {
337 #endif
338
339 php_stream_write_string(stream, "PASV\r\n");
340 result = GET_FTP_RESULT(stream);
341
342
343 if (result != 227) {
344 return 0;
345 }
346
347
348 tpath = tmp_line;
349
350 for (tpath += 4; *tpath && !isdigit((int) *tpath); tpath++);
351 if (!*tpath) {
352 return 0;
353 }
354
355 hoststart = tpath;
356 for (i = 0; i < 4; i++) {
357 for (; isdigit((int) *tpath); tpath++);
358 if (*tpath != ',') {
359 return 0;
360 }
361 *tpath='.';
362 tpath++;
363 }
364 tpath[-1] = '\0';
365 memcpy(ip, hoststart, ip_size);
366 ip[ip_size-1] = '\0';
367 hoststart = ip;
368
369
370 portno = (unsigned short) strtoul(tpath, &ttpath, 10) * 256;
371 if (ttpath == NULL) {
372
373 return 0;
374 }
375 tpath = ttpath;
376 if (*tpath != ',') {
377 return 0;
378 }
379 tpath++;
380
381 portno += (unsigned short) strtoul(tpath, &ttpath, 10);
382 #ifdef HAVE_IPV6
383 } else {
384
385 for (i = 0, tpath = tmp_line + 4; *tpath; tpath++) {
386 if (*tpath == '|') {
387 i++;
388 if (i == 3)
389 break;
390 }
391 }
392 if (i < 3) {
393 return 0;
394 }
395
396 portno = (unsigned short) strtoul(tpath + 1, &ttpath, 10);
397 }
398 #endif
399 if (ttpath == NULL) {
400
401 return 0;
402 }
403
404 if (phoststart) {
405 *phoststart = hoststart;
406 }
407
408 return portno;
409 }
410
411
412
413
414 php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, const char *path, const char *mode,
415 int options, zend_string **opened_path, php_stream_context *context STREAMS_DC)
416 {
417 php_stream *stream = NULL, *datastream = NULL;
418 php_url *resource = NULL;
419 char tmp_line[512];
420 char ip[sizeof("123.123.123.123")];
421 unsigned short portno;
422 char *hoststart = NULL;
423 int result = 0, use_ssl, use_ssl_on_data=0;
424 php_stream *reuseid=NULL;
425 size_t file_size = 0;
426 zval *tmpzval;
427 zend_bool allow_overwrite = 0;
428 int8_t read_write = 0;
429 char *transport;
430 int transport_len;
431
432 tmp_line[0] = '\0';
433
434 if (strpbrk(mode, "r+")) {
435 read_write = 1;
436 }
437 if (strpbrk(mode, "wa+")) {
438 if (read_write) {
439 php_stream_wrapper_log_error(wrapper, options, "FTP does not support simultaneous read/write connections");
440 return NULL;
441 }
442 if (strchr(mode, 'a')) {
443 read_write = 3;
444 } else {
445 read_write = 2;
446 }
447 }
448 if (!read_write) {
449
450 php_stream_wrapper_log_error(wrapper, options, "Unknown file open mode");
451 return NULL;
452 }
453
454 if (context &&
455 (tmpzval = php_stream_context_get_option(context, "ftp", "proxy")) != NULL) {
456 if (read_write == 1) {
457
458 return php_stream_url_wrap_http(wrapper, path, mode, options, opened_path, context STREAMS_CC);
459 } else {
460
461 php_stream_wrapper_log_error(wrapper, options, "FTP proxy may only be used in read mode");
462 return NULL;
463 }
464 }
465
466 stream = php_ftp_fopen_connect(wrapper, path, mode, options, opened_path, context, &reuseid, &resource, &use_ssl, &use_ssl_on_data);
467 if (!stream) {
468 goto errexit;
469 }
470
471
472 php_stream_write_string(stream, "TYPE I\r\n");
473 result = GET_FTP_RESULT(stream);
474 if (result > 299 || result < 200)
475 goto errexit;
476
477
478 php_stream_printf(stream, "SIZE %s\r\n", resource->path);
479
480
481 result = GET_FTP_RESULT(stream);
482 if (read_write == 1) {
483
484 char *sizestr;
485
486
487 if (result > 299 || result < 200) {
488 errno = ENOENT;
489 goto errexit;
490 }
491
492 sizestr = strchr(tmp_line, ' ');
493 if (sizestr) {
494 sizestr++;
495 file_size = atoi(sizestr);
496 php_stream_notify_file_size(context, file_size, tmp_line, result);
497 }
498 } else if (read_write == 2) {
499
500 if (context && (tmpzval = php_stream_context_get_option(context, "ftp", "overwrite")) != NULL) {
501 allow_overwrite = Z_LVAL_P(tmpzval) ? 1 : 0;
502 }
503 if (result <= 299 && result >= 200) {
504 if (allow_overwrite) {
505
506
507 php_stream_printf(stream, "DELE %s\r\n", resource->path);
508 result = GET_FTP_RESULT(stream);
509 if (result >= 300 || result <= 199) {
510 goto errexit;
511 }
512 } else {
513 php_stream_wrapper_log_error(wrapper, options, "Remote file already exists and overwrite context option not specified");
514 errno = EEXIST;
515 goto errexit;
516 }
517 }
518 }
519
520
521 portno = php_fopen_do_pasv(stream, ip, sizeof(ip), &hoststart);
522
523 if (!portno) {
524 goto errexit;
525 }
526
527
528 if (read_write == 1) {
529
530 if (context &&
531 (tmpzval = php_stream_context_get_option(context, "ftp", "resume_pos")) != NULL &&
532 Z_TYPE_P(tmpzval) == IS_LONG &&
533 Z_LVAL_P(tmpzval) > 0) {
534 php_stream_printf(stream, "REST %pd\r\n", Z_LVAL_P(tmpzval));
535 result = GET_FTP_RESULT(stream);
536 if (result < 300 || result > 399) {
537 php_stream_wrapper_log_error(wrapper, options, "Unable to resume from offset %pd", Z_LVAL_P(tmpzval));
538 goto errexit;
539 }
540 }
541
542
543 memcpy(tmp_line, "RETR", sizeof("RETR"));
544 } else if (read_write == 2) {
545
546 memcpy(tmp_line, "STOR", sizeof("STOR"));
547 } else {
548
549 memcpy(tmp_line, "APPE", sizeof("APPE"));
550 }
551 php_stream_printf(stream, "%s %s\r\n", tmp_line, (resource->path != NULL ? resource->path : "/"));
552
553
554 if (hoststart == NULL) {
555 hoststart = resource->host;
556 }
557 transport_len = (int)spprintf(&transport, 0, "tcp://%s:%d", hoststart, portno);
558 datastream = php_stream_xport_create(transport, transport_len, REPORT_ERRORS, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, NULL, NULL, context, NULL, NULL);
559 efree(transport);
560 if (datastream == NULL) {
561 goto errexit;
562 }
563
564 result = GET_FTP_RESULT(stream);
565 if (result != 150 && result != 125) {
566
567
568
569 php_stream_close(datastream);
570 datastream = NULL;
571 goto errexit;
572 }
573
574 php_stream_context_set(datastream, context);
575 php_stream_notify_progress_init(context, 0, file_size);
576
577 if (use_ssl_on_data && (php_stream_xport_crypto_setup(datastream,
578 STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 ||
579 php_stream_xport_crypto_enable(datastream, 1) < 0)) {
580
581 php_stream_wrapper_log_error(wrapper, options, "Unable to activate SSL mode");
582 php_stream_close(datastream);
583 datastream = NULL;
584 goto errexit;
585 }
586
587
588 datastream->wrapperthis = stream;
589
590 php_url_free(resource);
591 return datastream;
592
593 errexit:
594 if (resource) {
595 php_url_free(resource);
596 }
597 if (stream) {
598 php_stream_notify_error(context, PHP_STREAM_NOTIFY_FAILURE, tmp_line, result);
599 php_stream_close(stream);
600 }
601 if (tmp_line[0] != '\0')
602 php_stream_wrapper_log_error(wrapper, options, "FTP server reports %s", tmp_line);
603 return NULL;
604 }
605
606
607
608
609 static size_t php_ftp_dirstream_read(php_stream *stream, char *buf, size_t count)
610 {
611 php_stream_dirent *ent = (php_stream_dirent *)buf;
612 php_stream *innerstream;
613 size_t tmp_len;
614 zend_string *basename;
615
616 innerstream = ((php_ftp_dirstream_data *)stream->abstract)->datastream;
617
618 if (count != sizeof(php_stream_dirent)) {
619 return 0;
620 }
621
622 if (php_stream_eof(innerstream)) {
623 return 0;
624 }
625
626 if (!php_stream_get_line(innerstream, ent->d_name, sizeof(ent->d_name), &tmp_len)) {
627 return 0;
628 }
629
630 basename = php_basename(ent->d_name, tmp_len, NULL, 0);
631
632 tmp_len = MIN(sizeof(ent->d_name), ZSTR_LEN(basename) - 1);
633 memcpy(ent->d_name, ZSTR_VAL(basename), tmp_len);
634 ent->d_name[tmp_len - 1] = '\0';
635 zend_string_release(basename);
636
637
638 while (tmp_len > 0 &&
639 (ent->d_name[tmp_len - 1] == '\n' || ent->d_name[tmp_len - 1] == '\r' ||
640 ent->d_name[tmp_len - 1] == '\t' || ent->d_name[tmp_len - 1] == ' ')) {
641 ent->d_name[--tmp_len] = '\0';
642 }
643
644 return sizeof(php_stream_dirent);
645 }
646
647
648
649
650 static int php_ftp_dirstream_close(php_stream *stream, int close_handle)
651 {
652 php_ftp_dirstream_data *data = stream->abstract;
653
654
655 if (data->controlstream) {
656 php_stream_close(data->controlstream);
657 data->controlstream = NULL;
658 }
659
660 php_stream_close(data->datastream);
661 data->datastream = NULL;
662
663 efree(data);
664 stream->abstract = NULL;
665
666 return 0;
667 }
668
669
670
671
672 static php_stream_ops php_ftp_dirstream_ops = {
673 NULL,
674 php_ftp_dirstream_read,
675 php_ftp_dirstream_close,
676 NULL,
677 "ftpdir",
678 NULL,
679 NULL,
680 NULL,
681 NULL
682 };
683
684
685
686 php_stream * php_stream_ftp_opendir(php_stream_wrapper *wrapper, const char *path, const char *mode, int options,
687 zend_string **opened_path, php_stream_context *context STREAMS_DC)
688 {
689 php_stream *stream, *reuseid, *datastream = NULL;
690 php_ftp_dirstream_data *dirsdata;
691 php_url *resource = NULL;
692 int result = 0, use_ssl, use_ssl_on_data = 0;
693 char *hoststart = NULL, tmp_line[512];
694 char ip[sizeof("123.123.123.123")];
695 unsigned short portno;
696
697 tmp_line[0] = '\0';
698
699 stream = php_ftp_fopen_connect(wrapper, path, mode, options, opened_path, context, &reuseid, &resource, &use_ssl, &use_ssl_on_data);
700 if (!stream) {
701 goto opendir_errexit;
702 }
703
704
705 php_stream_write_string(stream, "TYPE A\r\n");
706 result = GET_FTP_RESULT(stream);
707 if (result > 299 || result < 200)
708 goto opendir_errexit;
709
710
711 portno = php_fopen_do_pasv(stream, ip, sizeof(ip), &hoststart);
712
713 if (!portno) {
714 goto opendir_errexit;
715 }
716
717 php_stream_printf(stream, "NLST %s\r\n", (resource->path != NULL ? resource->path : "/"));
718
719
720 if (hoststart == NULL) {
721 hoststart = resource->host;
722 }
723 datastream = php_stream_sock_open_host(hoststart, portno, SOCK_STREAM, 0, 0);
724 if (datastream == NULL) {
725 goto opendir_errexit;
726 }
727
728 result = GET_FTP_RESULT(stream);
729 if (result != 150 && result != 125) {
730
731
732
733 php_stream_close(datastream);
734 datastream = NULL;
735 goto opendir_errexit;
736 }
737
738 php_stream_context_set(datastream, context);
739
740 if (use_ssl_on_data && (php_stream_xport_crypto_setup(stream,
741 STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0 ||
742 php_stream_xport_crypto_enable(stream, 1) < 0)) {
743
744 php_stream_wrapper_log_error(wrapper, options, "Unable to activate SSL mode");
745 php_stream_close(datastream);
746 datastream = NULL;
747 goto opendir_errexit;
748 }
749
750 php_url_free(resource);
751
752 dirsdata = emalloc(sizeof *dirsdata);
753 dirsdata->datastream = datastream;
754 dirsdata->controlstream = stream;
755 dirsdata->dirstream = php_stream_alloc(&php_ftp_dirstream_ops, dirsdata, 0, mode);
756
757 return dirsdata->dirstream;
758
759 opendir_errexit:
760 if (resource) {
761 php_url_free(resource);
762 }
763 if (stream) {
764 php_stream_notify_error(context, PHP_STREAM_NOTIFY_FAILURE, tmp_line, result);
765 php_stream_close(stream);
766 }
767 if (tmp_line[0] != '\0') {
768 php_stream_wrapper_log_error(wrapper, options, "FTP server reports %s", tmp_line);
769 }
770 return NULL;
771 }
772
773
774
775
776 static int php_stream_ftp_url_stat(php_stream_wrapper *wrapper, const char *url, int flags, php_stream_statbuf *ssb, php_stream_context *context)
777 {
778 php_stream *stream = NULL;
779 php_url *resource = NULL;
780 int result;
781 char tmp_line[512];
782
783
784 if (!ssb) return -1;
785
786 stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, context, NULL, &resource, NULL, NULL);
787 if (!stream) {
788 goto stat_errexit;
789 }
790
791 ssb->sb.st_mode = 0644;
792 php_stream_printf(stream, "CWD %s\r\n", (resource->path != NULL ? resource->path : "/"));
793 result = GET_FTP_RESULT(stream);
794 if (result < 200 || result > 299) {
795 ssb->sb.st_mode |= S_IFREG;
796 } else {
797 ssb->sb.st_mode |= S_IFDIR;
798 }
799
800 php_stream_write_string(stream, "TYPE I\r\n");
801
802 result = GET_FTP_RESULT(stream);
803
804 if(result < 200 || result > 299) {
805 goto stat_errexit;
806 }
807
808 php_stream_printf(stream, "SIZE %s\r\n", (resource->path != NULL ? resource->path : "/"));
809 result = GET_FTP_RESULT(stream);
810 if (result < 200 || result > 299) {
811
812
813
814 if (ssb->sb.st_mode & S_IFDIR) {
815 ssb->sb.st_size = 0;
816 } else {
817 goto stat_errexit;
818 }
819 } else {
820 ssb->sb.st_size = atoi(tmp_line + 4);
821 }
822
823 php_stream_printf(stream, "MDTM %s\r\n", (resource->path != NULL ? resource->path : "/"));
824 result = GET_FTP_RESULT(stream);
825 if (result == 213) {
826 char *p = tmp_line + 4;
827 int n;
828 struct tm tm, tmbuf, *gmt;
829 time_t stamp;
830
831 while (p - tmp_line < sizeof(tmp_line) && !isdigit(*p)) {
832 p++;
833 }
834
835 if (p - tmp_line > sizeof(tmp_line)) {
836 goto mdtm_error;
837 }
838
839 n = sscanf(p, "%4u%2u%2u%2u%2u%2u", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
840 if (n != 6) {
841 goto mdtm_error;
842 }
843
844 tm.tm_year -= 1900;
845 tm.tm_mon--;
846 tm.tm_isdst = -1;
847
848
849 stamp = time(NULL);
850 gmt = php_gmtime_r(&stamp, &tmbuf);
851 if (!gmt) {
852 goto mdtm_error;
853 }
854 gmt->tm_isdst = -1;
855
856
857 tm.tm_sec += (long)(stamp - mktime(gmt));
858 tm.tm_isdst = gmt->tm_isdst;
859
860 ssb->sb.st_mtime = mktime(&tm);
861 } else {
862
863 mdtm_error:
864 ssb->sb.st_mtime = -1;
865 }
866
867 ssb->sb.st_ino = 0;
868 ssb->sb.st_dev = 0;
869 ssb->sb.st_uid = 0;
870 ssb->sb.st_gid = 0;
871 ssb->sb.st_atime = -1;
872 ssb->sb.st_ctime = -1;
873
874 ssb->sb.st_nlink = 1;
875 ssb->sb.st_rdev = -1;
876 #ifdef HAVE_ST_BLKSIZE
877 ssb->sb.st_blksize = 4096;
878 #ifdef HAVE_ST_BLOCKS
879 ssb->sb.st_blocks = (int)((4095 + ssb->sb.st_size) / ssb->sb.st_blksize);
880 #endif
881 #endif
882 php_stream_close(stream);
883 php_url_free(resource);
884 return 0;
885
886 stat_errexit:
887 if (resource) {
888 php_url_free(resource);
889 }
890 if (stream) {
891 php_stream_close(stream);
892 }
893 return -1;
894 }
895
896
897
898
899 static int php_stream_ftp_unlink(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context)
900 {
901 php_stream *stream = NULL;
902 php_url *resource = NULL;
903 int result;
904 char tmp_line[512];
905
906 stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, NULL, NULL, &resource, NULL, NULL);
907 if (!stream) {
908 if (options & REPORT_ERRORS) {
909 php_error_docref(NULL, E_WARNING, "Unable to connect to %s", url);
910 }
911 goto unlink_errexit;
912 }
913
914 if (resource->path == NULL) {
915 if (options & REPORT_ERRORS) {
916 php_error_docref(NULL, E_WARNING, "Invalid path provided in %s", url);
917 }
918 goto unlink_errexit;
919 }
920
921
922 php_stream_printf(stream, "DELE %s\r\n", (resource->path != NULL ? resource->path : "/"));
923
924 result = GET_FTP_RESULT(stream);
925 if (result < 200 || result > 299) {
926 if (options & REPORT_ERRORS) {
927 php_error_docref(NULL, E_WARNING, "Error Deleting file: %s", tmp_line);
928 }
929 goto unlink_errexit;
930 }
931
932 php_url_free(resource);
933 php_stream_close(stream);
934 return 1;
935
936 unlink_errexit:
937 if (resource) {
938 php_url_free(resource);
939 }
940 if (stream) {
941 php_stream_close(stream);
942 }
943 return 0;
944 }
945
946
947
948
949 static int php_stream_ftp_rename(php_stream_wrapper *wrapper, const char *url_from, const char *url_to, int options, php_stream_context *context)
950 {
951 php_stream *stream = NULL;
952 php_url *resource_from = NULL, *resource_to = NULL;
953 int result;
954 char tmp_line[512];
955
956 resource_from = php_url_parse(url_from);
957 resource_to = php_url_parse(url_to);
958
959
960
961 if (!resource_from ||
962 !resource_to ||
963 !resource_from->scheme ||
964 !resource_to->scheme ||
965 strcmp(resource_from->scheme, resource_to->scheme) ||
966 !resource_from->host ||
967 !resource_to->host ||
968 strcmp(resource_from->host, resource_to->host) ||
969 (resource_from->port != resource_to->port &&
970 resource_from->port * resource_to->port != 0 &&
971 resource_from->port + resource_to->port != 21) ||
972 !resource_from->path ||
973 !resource_to->path) {
974 goto rename_errexit;
975 }
976
977 stream = php_ftp_fopen_connect(wrapper, url_from, "r", 0, NULL, NULL, NULL, NULL, NULL, NULL);
978 if (!stream) {
979 if (options & REPORT_ERRORS) {
980 php_error_docref(NULL, E_WARNING, "Unable to connect to %s", resource_from->host);
981 }
982 goto rename_errexit;
983 }
984
985
986 php_stream_printf(stream, "RNFR %s\r\n", (resource_from->path != NULL ? resource_from->path : "/"));
987
988 result = GET_FTP_RESULT(stream);
989 if (result < 300 || result > 399) {
990 if (options & REPORT_ERRORS) {
991 php_error_docref(NULL, E_WARNING, "Error Renaming file: %s", tmp_line);
992 }
993 goto rename_errexit;
994 }
995
996
997 php_stream_printf(stream, "RNTO %s\r\n", (resource_to->path != NULL ? resource_to->path : "/"));
998
999 result = GET_FTP_RESULT(stream);
1000 if (result < 200 || result > 299) {
1001 if (options & REPORT_ERRORS) {
1002 php_error_docref(NULL, E_WARNING, "Error Renaming file: %s", tmp_line);
1003 }
1004 goto rename_errexit;
1005 }
1006
1007 php_url_free(resource_from);
1008 php_url_free(resource_to);
1009 php_stream_close(stream);
1010 return 1;
1011
1012 rename_errexit:
1013 if (resource_from) {
1014 php_url_free(resource_from);
1015 }
1016 if (resource_to) {
1017 php_url_free(resource_to);
1018 }
1019 if (stream) {
1020 php_stream_close(stream);
1021 }
1022 return 0;
1023 }
1024
1025
1026
1027
1028 static int php_stream_ftp_mkdir(php_stream_wrapper *wrapper, const char *url, int mode, int options, php_stream_context *context)
1029 {
1030 php_stream *stream = NULL;
1031 php_url *resource = NULL;
1032 int result, recursive = options & PHP_STREAM_MKDIR_RECURSIVE;
1033 char tmp_line[512];
1034
1035 stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, NULL, NULL, &resource, NULL, NULL);
1036 if (!stream) {
1037 if (options & REPORT_ERRORS) {
1038 php_error_docref(NULL, E_WARNING, "Unable to connect to %s", url);
1039 }
1040 goto mkdir_errexit;
1041 }
1042
1043 if (resource->path == NULL) {
1044 if (options & REPORT_ERRORS) {
1045 php_error_docref(NULL, E_WARNING, "Invalid path provided in %s", url);
1046 }
1047 goto mkdir_errexit;
1048 }
1049
1050 if (!recursive) {
1051 php_stream_printf(stream, "MKD %s\r\n", resource->path);
1052 result = GET_FTP_RESULT(stream);
1053 } else {
1054
1055 char *p, *e, *buf;
1056
1057 buf = estrdup(resource->path);
1058 e = buf + strlen(buf);
1059
1060
1061 while ((p = strrchr(buf, '/'))) {
1062 *p = '\0';
1063 php_stream_printf(stream, "CWD %s\r\n", buf);
1064 result = GET_FTP_RESULT(stream);
1065 if (result >= 200 && result <= 299) {
1066 *p = '/';
1067 break;
1068 }
1069 }
1070 if (p == buf) {
1071 php_stream_printf(stream, "MKD %s\r\n", resource->path);
1072 result = GET_FTP_RESULT(stream);
1073 } else {
1074 php_stream_printf(stream, "MKD %s\r\n", buf);
1075 result = GET_FTP_RESULT(stream);
1076 if (result >= 200 && result <= 299) {
1077 if (!p) {
1078 p = buf;
1079 }
1080
1081 while (++p != e) {
1082 if (*p == '\0' && *(p + 1) != '\0') {
1083 *p = '/';
1084 php_stream_printf(stream, "MKD %s\r\n", buf);
1085 result = GET_FTP_RESULT(stream);
1086 if (result < 200 || result > 299) {
1087 if (options & REPORT_ERRORS) {
1088 php_error_docref(NULL, E_WARNING, "%s", tmp_line);
1089 }
1090 break;
1091 }
1092 }
1093 }
1094 }
1095 }
1096 efree(buf);
1097 }
1098
1099 php_url_free(resource);
1100 php_stream_close(stream);
1101
1102 if (result < 200 || result > 299) {
1103
1104 return 0;
1105 }
1106
1107 return 1;
1108
1109 mkdir_errexit:
1110 if (resource) {
1111 php_url_free(resource);
1112 }
1113 if (stream) {
1114 php_stream_close(stream);
1115 }
1116 return 0;
1117 }
1118
1119
1120
1121
1122 static int php_stream_ftp_rmdir(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context)
1123 {
1124 php_stream *stream = NULL;
1125 php_url *resource = NULL;
1126 int result;
1127 char tmp_line[512];
1128
1129 stream = php_ftp_fopen_connect(wrapper, url, "r", 0, NULL, NULL, NULL, &resource, NULL, NULL);
1130 if (!stream) {
1131 if (options & REPORT_ERRORS) {
1132 php_error_docref(NULL, E_WARNING, "Unable to connect to %s", url);
1133 }
1134 goto rmdir_errexit;
1135 }
1136
1137 if (resource->path == NULL) {
1138 if (options & REPORT_ERRORS) {
1139 php_error_docref(NULL, E_WARNING, "Invalid path provided in %s", url);
1140 }
1141 goto rmdir_errexit;
1142 }
1143
1144 php_stream_printf(stream, "RMD %s\r\n", resource->path);
1145 result = GET_FTP_RESULT(stream);
1146
1147 if (result < 200 || result > 299) {
1148 if (options & REPORT_ERRORS) {
1149 php_error_docref(NULL, E_WARNING, "%s", tmp_line);
1150 }
1151 goto rmdir_errexit;
1152 }
1153
1154 php_url_free(resource);
1155 php_stream_close(stream);
1156
1157 return 1;
1158
1159 rmdir_errexit:
1160 if (resource) {
1161 php_url_free(resource);
1162 }
1163 if (stream) {
1164 php_stream_close(stream);
1165 }
1166 return 0;
1167 }
1168
1169
1170 static php_stream_wrapper_ops ftp_stream_wops = {
1171 php_stream_url_wrap_ftp,
1172 php_stream_ftp_stream_close,
1173 php_stream_ftp_stream_stat,
1174 php_stream_ftp_url_stat,
1175 php_stream_ftp_opendir,
1176 "ftp",
1177 php_stream_ftp_unlink,
1178 php_stream_ftp_rename,
1179 php_stream_ftp_mkdir,
1180 php_stream_ftp_rmdir
1181 };
1182
1183 PHPAPI php_stream_wrapper php_stream_ftp_wrapper = {
1184 &ftp_stream_wops,
1185 NULL,
1186 1
1187 };
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197