1 /* FTP Functions
2 $Revision: 1.5 $
3 $Date: 2003/03/10 22:31:26 $
4 vim: sw=4 ts=4
5 */
6
7 #define _XOPEN_SOURCE 500
8 #if defined(__DragonFly__)
9 #define __BSD_VISIBLE 1 /* for h_addr */
10 #endif
11
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <ctype.h>
17 #include <unistd.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <netdb.h>
23 #include <time.h>
24 #include <arpa/inet.h>
25 #include <fnmatch.h>
26 #include <string.h>
27 #include "ftp.h"
28 #include "quftp.h"
29 #include "text.h"
30
31 /*
32 * Global Variables
33 */
34
35 /* STAT or LIST Formats
36 * %P UNIX style permissions
37 * %L Number of hard links
38 * %O Owner
39 * %G Group
40 * %S Size
41 * %N File Name
42 *
43 * %Y Year
44 * %M Month name
45 * %n Month number
46 * %D Day of month
47 * %h Hour
48 * %m Minute
49 * %s Second
50 */
51
52 char *stat_formats[] = {
53 /* drwxr-xr-x 2 ftp ftp 1024 Jan 23 21:37 incoming */
54 "%P %L %O %G %S %M %D %h:%m %N",
55
56 /* d--x--x--x 2 ftp ftp 1024 Oct 17 1998 bin */
57 "%P %L %O %G %S %M %D %Y %N"
58 };
59
ftp_new(void)60 struct ftpconnection *ftp_new(void) {
61 struct ftpconnection *connection;
62 connection = malloc(sizeof(struct ftpconnection));
63 log(LOG_CALLS, "Creating new ftp structure at %p", connection);
64
65 memset(connection->hostname, 0, 64);
66 memset(connection->system, 0, 128);
67 memset(connection->username, 0, 32);
68 memset(connection->password, 0, 32);
69 memset(connection->remotedir, 0, 256);
70 memset(connection->localdir, 0, 256);
71 connection->port = 0;
72 connection->status = STATUS_NEW;
73 connection->controlfd = 0;
74 connection->datafd = 0;
75 connection->dataconfd = 0;
76 return connection;
77 }
78
ftp_connect(struct ftpconnection * connection)79 int ftp_connect(struct ftpconnection *connection) {
80 struct hostent *hp;
81 int response, len = 128;
82 int readynow = 0;
83 char *ipaddr;
84 char resultstring[4096];
85 struct sockaddr_in controladdr;
86 log(LOG_CALLS, "%p", connection);
87 if (connection->status < STATUS_WAIT_CONNECT) {
88 return -1;
89 }
90 if (!connection->hostname || !*connection->hostname) {
91 log(LOG_CRITICAL, "Must initialize struct ftpconnection first\n");
92 return -1;
93 }
94 if (!(hp = (struct hostent *)gethostbyname(connection->hostname))) {
95 log(LOG_ALERT, "Can't resolve %s: %s\n", connection->hostname, HERROR);
96 return -1;
97 }
98
99 if ((connection->controlfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
100 perror("socket");
101 exit(0);
102 }
103 log(LOG_MESSAGE, "Resolving %s", connection->hostname);
104 fflush(stdout);
105 memset(&controladdr, 0, sizeof(controladdr));
106 controladdr.sin_family = AF_INET;
107 controladdr.sin_port = htons(connection->port);
108 memcpy((char *)&controladdr.sin_addr, hp->h_addr, hp->h_length);
109 ipaddr = inet_ntoa(controladdr.sin_addr);
110 log(LOG_MESSAGE, "%s resolves to %s", connection->hostname, ipaddr);
111 if (connect(connection->controlfd, (SOCKADDR *)&controladdr, sizeof(controladdr)) < 0) {
112 log(LOG_ALERT, "Connection refused to %s\n", connection->hostname);
113 return -1;
114 }
115 if (getsockname(connection->controlfd, (SOCKADDR *)&controladdr,&len)< 0) {
116 perror("getsockname");
117 exit(0);
118 }
119 connection->localip = controladdr.sin_addr.s_addr;
120 connection->status = STATUS_WAIT_LOGIN;
121 while (!readynow) {
122 response = ftp_getrc(connection, resultstring, 4096, 0);
123 switch (response) {
124 case 120 : log(LOG_ALERT, "Service not yet ready.. waiting for 15 seconds");
125 sleep(15);
126 break;
127 case 220 : readynow = 1;
128 log(LOG_NOTICE, "Connected to %s on port %d",
129 connection->hostname, connection->port);
130 break;
131 default : log(LOG_ALERT, "%s", ftp_error_message(response));
132 return response;
133 break;
134 }
135 }
136 return 0;
137 }
138
ftp_disconnect(struct ftpconnection * connection)139 int ftp_disconnect(struct ftpconnection *connection) {
140 if (connection->status >= STATUS_IDLE) {
141 int response;
142 response = ftp_sendline(connection, "QUIT\r\n");
143 response = ftp_getrc(connection, NULL, 0, 0);
144 if (response == 500)
145 log(LOG_MESSAGE, "Braindead server doesn't understand QUIT.. closing connection\n");
146 if (close(connection->controlfd) < 0) {
147 perror("close");
148 exit(0);
149 }
150 }
151 if (connection->hostname)
152 log(LOG_NOTICE, "Disconnected from %s", connection->hostname);
153 connection->controlfd = 0;
154 return 0;
155 }
156
ftp_reset(struct ftpconnection * connection)157 int ftp_reset(struct ftpconnection *connection) {
158 if (connection->status >= STATUS_WAIT_LOGIN) {
159 log(LOG_WARNING, "Still connected. Call ftp_disconnect() first!\n");
160 return 1;
161 }
162 memset(connection, 0, sizeof(struct ftpconnection));
163 connection->status = STATUS_NEW;
164 return 0;
165 }
166
ftp_readline(struct ftpconnection * connection)167 char *ftp_readline(struct ftpconnection *connection) {
168 int n = 0;
169 int length = 0;
170 char *temp, *buffer;
171 static char *read_line = 0;
172
173 if (read_line != 0) free(read_line);
174 read_line = (char *)NULL;
175
176 buffer = (char *)malloc(MAX_LINE_SIZE);
177
178 if (connection->controlfd <= 0) {
179 log(LOG_CRITICAL, "ftp_readline: Not connected\n");
180 return 0;
181 }
182 temp = buffer;
183 while ((n = read(connection->controlfd, temp, 1)) > 0) {
184 if (*temp == '\r') continue;
185 if (*temp == '\n') break;
186 if (*temp == '\0') break;
187 if (length == MAX_LINE_SIZE) break;
188 temp++; length++;
189 }
190 if (n < 0) {
191 perror("read");
192 exit(0);
193 }
194 if (n == 0)
195 {
196 log(LOG_WARNING, "FTP server disconnected");
197 return NULL;
198 }
199 read_line = (char *)malloc(length + 1);
200 strncpy(read_line, buffer, length);
201 read_line[length] = '\0';
202 log_hex(LOG_DATA, "Read", read_line, length);
203 return read_line;
204 }
205
ftp_sendline(struct ftpconnection * connection,char * line)206 int ftp_sendline(struct ftpconnection *connection, char *line) {
207 int n = 0;
208 int size;
209 if (!line || !*line) return 0;
210 size = strlen(line);
211 if (size == 0) return 0;
212 if (connection->status <= STATUS_WAIT_CONNECT) {
213 log(LOG_CRITICAL, "ftp_sendline: Not connected\n");
214 return 0;
215 }
216 log_hex(LOG_DATA, "Sent", line, size);
217 if ((n = write(connection->controlfd, line, size)) < 0) {
218 perror("ftp_sendline: write");
219 }
220 return n;
221 }
222
ftp_getrc(struct ftpconnection * connection,char * data,int max_size,int print_line)223 int ftp_getrc(struct ftpconnection *connection, char *data, int max_size, int print_line) {
224 char *line;
225 int response;
226
227 if (connection->status <= STATUS_WAIT_CONNECT) {
228 log(LOG_CRITICAL, "ftp_getrc: Not connected");
229 return 0;
230 }
231 while ((line = ftp_readline(connection))) {
232 if (!*line) {
233 log(LOG_CRITICAL, "Big bad error.. empty string returned by ftp_readline\n");
234 break;
235 }
236 if (print_line)
237 {
238 if (line[0] == ' ')
239 log(LOG_WARNING, "%s", (char *)(line + 1));
240 else
241 log(LOG_WARNING, "%s", (char *)(line + 4));
242 }
243 if (IS_NUMBER(line[0]) && IS_NUMBER(line[1]) && IS_NUMBER(line[2]) && line[3] == ' ')
244 break;
245 }
246 if (!line || !*line) return 0;
247 sscanf(line, "%d ", &response);
248 log(LOG_INFO, "ftp_getrc() = %d", response);
249 if (data)
250 strncpy(data, (line + 4), max_size);
251 if (response >= 100)
252 return response;
253 return 0;
254 }
255
ftp_parse_pasv(char * line,char * address)256 int ftp_parse_pasv(char *line, char *address) {
257 int h1, h2, h3, h4, p1, p2, port;
258 if (line == NULL) return 0;
259 sscanf(line, "%i,%i,%i,%i,%i,%i", &h1, &h2, &h3, &h4, &p1, &p2);
260 port = (p1 << 8) + p2;
261 sprintf(address, "%d.%d.%d.%d", h1, h2, h3, h4);
262 return port;
263 }
264
ftp_parse_permissions(char * perm)265 int ftp_parse_permissions(char *perm) {
266 int permissions = 0;
267 if (perm[1] != 'r' && perm[1] != '-') return -1;
268 if (perm[0] == 'd') permissions |= 2048;
269 if (perm[1] == 'r') permissions |= 256;
270 if (perm[2] == 'w') permissions |= 128;
271 if (perm[3] == 'x') permissions |= 64;
272 if (perm[4] == 'r') permissions |= 32;
273 if (perm[5] == 'w') permissions |= 16;
274 if (perm[6] == 'x') permissions |= 8;
275 if (perm[7] == 'r') permissions |= 4;
276 if (perm[8] == 'w') permissions |= 2;
277 if (perm[9] == 'x') permissions |= 1;
278 return permissions;
279 }
280
ftp_check_ready(struct ftpconnection * connection,int print)281 int ftp_check_ready(struct ftpconnection *connection, int print) {
282 if (connection->status <= STATUS_WAIT_CONNECT) {
283 if (print) log(LOG_CRITICAL, "Not connected\n");
284 return 0;
285 }
286 if (connection->status == STATUS_WAIT_LOGIN) {
287 if (print) log(LOG_ALERT, "Not logged in\n");
288 return 0;
289 }
290 if (connection->status == STATUS_ERROR) {
291 if (print) log(LOG_ALERT, "Connection is in error state!\n");
292 return 0;
293 }
294 if (connection->status >= STATUS_WAITING) {
295 if (print) log(LOG_ALERT, "Server is busy. Please wait\n");
296 return 0;
297 }
298 return 1;
299 }
300
ftp_stat(struct ftpconnection * connection,char * filename,struct filedetails * details)301 int ftp_stat(struct ftpconnection *connection, char *filename, struct filedetails *details) {
302 char *buffer = NULL;
303 int result = 0;
304 if (!details) return -1;
305 log(LOG_CALLS, "\"%s\"", filename);
306 if (!ftp_check_ready(connection, 1)) return -1;
307 if (details) memset(details, 0, sizeof(struct filedetails));
308 buffer = (char *)malloc(strlen(filename) + 15);
309 sprintf(buffer, "STAT %s\r\n", filename);
310 ftp_sendline(connection, buffer);
311 free(buffer);
312 buffer = ftp_readline(connection);
313 result = atoi(buffer);
314 switch (result) {
315 case 211 :
316 case 212 :
317 case 213 :
318 case 250 : break;
319 case 500 : log(LOG_ALERT, "Server doesn't understand STAT\n"); return result; break;
320 case 501 : log(LOG_ALERT, "Bad parameters to STAT\n"); return result; break;
321 case 502 : log(LOG_ALERT, "Server doesn't understand STAT\n"); return result; break;
322 case 421 : log(LOG_ALERT, "Service not available\n"); return result; break;
323 default : log(LOG_ALERT, "Unknown response to STAT: %d\n", result); return result; break;
324 }
325 if (buffer[3] == '-') {
326 /* Read a line at a time */
327 while ((buffer = ftp_readline(connection))) {
328 if (strstr(buffer, "total ") == buffer) continue;
329 break;
330 }
331 if (buffer[0] == '2' && buffer[1] == '1') return 1;
332 if (!buffer) {
333 log(LOG_CRITICAL, "Disconnected!\n");
334 ftp_disconnect(connection);
335 return 1;
336 }
337 if (strstr(buffer, "/bin/ls") != 0) {
338 ftp_readline(connection);
339 return 10;
340 }
341 if (ftp_split_ls(buffer, details) == 1) {;
342 ftp_strip_illegal(details->filename);
343 ftp_getrc(connection, NULL, 0, 0);
344 return 0;
345 } else {
346 ftp_getrc(connection, NULL, 0, 0);
347 return 1;
348 }
349 } else {
350 log(LOG_ALERT, "Bad response to STAT: %s\n", buffer);
351 }
352 return 0;
353 }
354
ftp_split_ls(char * buffer,struct filedetails * details)355 int ftp_split_ls(char *buffer, struct filedetails *details) {
356 int stat_index, complete_match = 0;
357
358 if (!buffer || !*buffer) return 0;
359
360 /* Try matching STAT output */
361 stat_index = 0;
362 while (stat_formats[stat_index]) {
363 char *format_p, *buffer_p;
364 complete_match = 1;
365 buffer_p = buffer;
366 format_p = stat_formats[stat_index];
367
368 while (*format_p) {
369 while (*buffer_p == ' ' || *buffer_p == '\t')
370 buffer_p++; /* Skip whitespace */
371 if (*format_p == '%') {
372 switch (*++format_p) {
373 case '%' : if (*buffer_p == '%') { /* %% means a literal % character */
374 buffer_p++;
375 } else {
376 complete_match = 0;
377 }
378 break;
379 case 'P' : { char *permissions_p; /* UNIX style permissions */
380 char *permissions = (char *)malloc(4096);
381 memset(permissions, 0, 4096);
382 permissions_p = permissions;
383 format_p++;
384 while (*buffer_p && (*buffer_p != *format_p))
385 *permissions_p++ = *buffer_p++; /* Loop and grab everything up until the next character in the format */
386 details->permissions = ftp_parse_permissions(permissions);
387 if (details->permissions == 0) complete_match = 0;
388 free(permissions);
389 break; }
390 case 'L' : { char *links_p; /* UNIX style links */
391 char *links = (char *)malloc(4096);
392 memset(links, 0, 4096);
393 links_p = links;
394 format_p++;
395 while (*buffer_p && (*buffer_p != *format_p))
396 *links_p++ = *buffer_p++; /* Loop and grab everything up until the next character in the format */
397 for (links_p = links; *links_p; links_p++)
398 if (*links_p < '0' || *links_p > '9') complete_match = 0;
399 details->links = atoi(links);
400 free(links);
401 break; }
402 case 'O' : { char *owner_p; /* UNIX style owner */
403 char *owner = (char *)malloc(4096);
404 memset(owner, 0, 4096);
405 owner_p = owner;
406 format_p++;
407 while (*buffer_p && (*buffer_p != *format_p))
408 *owner_p++ = *buffer_p++; /* Loop and grab everything up until the next character in the format */
409 strncpy(details->owner, owner, 63);
410 free(owner);
411 break; }
412 case 'G' : { char *group_p; /* UNIX style group */
413 char *group = (char *)malloc(4096);
414 memset(group, 0, 4096);
415 group_p = group;
416 format_p++;
417 while (*buffer_p && (*buffer_p != *format_p))
418 *group_p++ = *buffer_p++; /* Loop and grab everything up until the next character in the format */
419 strncpy(details->group, group, 63);
420 free(group);
421 break; }
422 case 'S' : { char *size_p; /* UNIX style size */
423 char *size = (char *)malloc(4096);
424 memset(size, 0, 4096);
425 size_p = size;
426 format_p++;
427 while (*buffer_p && (*buffer_p != *format_p))
428 *size_p++ = *buffer_p++; /* Loop and grab everything up until the next character in the format */
429 for (size_p = size; *size_p; size_p++)
430 if (*size_p < '0' || *size_p > '9') complete_match = 0;
431 details->size = atol(size);
432 free(size);
433 break; }
434 case 'N' : { char *filename_p; /* UNIX style filename */
435 char *filename = (char *)malloc(4096);
436 memset(filename, 0, 4096);
437 filename_p = filename;
438 format_p++;
439 while (*buffer_p && (*buffer_p != *format_p))
440 *filename_p++ = *buffer_p++; /* Loop and grab everything up until the next character in the format */
441 strncpy(details->filename, filename, 254);
442 free(filename);
443 break; }
444 case 'Y' : { char *year_p; /* UNIX style year */
445 char *year = (char *)malloc(4096);
446 memset(year, 0, 4096);
447 year_p = year;
448 format_p++;
449 while (*buffer_p && (*buffer_p != *format_p))
450 *year_p++ = *buffer_p++; /* Loop and grab everything up until the next character in the format */
451 for (year_p = year; *year_p; year_p++)
452 if (*year_p < '0' || *year_p > '9') complete_match = 0;
453 free(year);
454 break; }
455 case 'M' : { char *month_p; /* UNIX style month name */
456 char *month = (char *)malloc(4096);
457 memset(month, 0, 4096);
458 month_p = month;
459 format_p++;
460 while (*buffer_p && (*buffer_p != *format_p))
461 *month_p++ = *buffer_p++; /* Loop and grab everything up until the next character in the format */
462 free(month);
463 break; }
464 case 'n' : { char *month_p; /* UNIX style month number */
465 char *month = (char *)malloc(4096);
466 memset(month, 0, 4096);
467 month_p = month;
468 format_p++;
469 while (*buffer_p && (*buffer_p != *format_p))
470 *month_p++ = *buffer_p++; /* Loop and grab everything up until the next character in the format */
471 for (month_p = month; *month_p; month_p++)
472 if (*month_p < '0' || *month_p > '9') complete_match = 0;
473 free(month);
474 break; }
475 case 'm' : { char *minute_p; /* UNIX style minute number */
476 char *minute = (char *)malloc(4096);
477 memset(minute, 0, 4096);
478 minute_p = minute;
479 format_p++;
480 while (*buffer_p && (*buffer_p != *format_p))
481 *minute_p++ = *buffer_p++; /* Loop and grab everything up until the next character in the format */
482 for (minute_p = minute; *minute_p; minute_p++)
483 if (*minute_p < '0' || *minute_p > '9') complete_match = 0;
484 free(minute);
485 break; }
486 case 'D' : { char *mday_p; /* UNIX style mday number */
487 char *mday = (char *)malloc(4096);
488 memset(mday, 0, 4096);
489 mday_p = mday;
490 format_p++;
491 while (*buffer_p && (*buffer_p != *format_p))
492 *mday_p++ = *buffer_p++; /* Loop and grab everything up until the next character in the format */
493 for (mday_p = mday; *mday_p; mday_p++)
494 if (*mday_p < '0' || *mday_p > '9') complete_match = 0;
495 free(mday);
496 break; }
497 case 's' : { char *second_p; /* UNIX style second number */
498 char *second = (char *)malloc(4096);
499 memset(second, 0, 4096);
500 second_p = second;
501 format_p++;
502 while (*buffer_p && (*buffer_p != *format_p))
503 *second_p++ = *buffer_p++; /* Loop and grab everything up until the next character in the format */
504 for (second_p = second; *second_p; second_p++)
505 if (*second_p < '0' || *second_p > '9') complete_match = 0;
506 free(second);
507 break; }
508 case 'h' : { char *hour_p; /* UNIX style hour number */
509 char *hour = (char *)malloc(4096);
510 memset(hour, 0, 4096);
511 hour_p = hour;
512 format_p++;
513 while (*buffer_p && (*buffer_p != *format_p))
514 *hour_p++ = *buffer_p++; /* Loop and grab everything up until the next character in the format */
515 for (hour_p = hour; *hour_p; hour_p++)
516 if (*hour_p < '0' || *hour_p > '9') complete_match = 0;
517 free(hour);
518 break; }
519 default : log(LOG_CRITICAL, "Unknown character %c in STAT format description\n", *format_p);
520 break;
521 }
522 }
523 if (complete_match == 0) break;
524 if (!*format_p++) break;
525 }
526 if (complete_match == 1) break;
527 stat_index++;
528 }
529 return complete_match;
530 }
531
ftp_delete(struct ftpconnection * connection,char * filename)532 int ftp_delete(struct ftpconnection *connection, char *filename) {
533 int result;
534 char line[4096];
535 char resultstring[4096];
536 if (!filename) return 0;
537 if (!ftp_check_ready(connection, 1)) return 0;
538 sprintf(line, "DELE %s\r\n", filename);
539 ftp_sendline(connection, line);
540 result = ftp_getrc(connection, resultstring, 4096, 0);
541 switch (result) {
542 case 250 : log(LOG_INFO, "Deleted %s\n", filename); return 1; break;
543 case 450 : log(LOG_ALERT, "Can't delete %s: %s\n", filename, resultstring); break;
544 case 421 : log(LOG_ALERT, "%s\n", resultstring); break;
545 case 500 : log(LOG_ALERT, "This server does not support the DELE command\n"); break;
546 case 501 : log(LOG_ALERT, "Invalid parameters to DELE command\n"); break;
547 case 502 : log(LOG_ALERT, "This server does not support the DELE command\n"); break;
548 case 530 : log(LOG_ALERT, "Not logged in correctly\n"); break;
549 case 550 : log(LOG_ALERT, "Can't delete %s: %s\n", filename, resultstring); break;
550 case 553 : log(LOG_ALERT, "Not allowed to delete %s\n", filename); break;
551 default : log(LOG_ALERT, "Unrecognised response to DELE: %d %s\n", result, resultstring);
552 break;
553 }
554 return 0;
555 }
556
ftp_glob(struct ftpconnection * connection,char * wildcard,int status)557 char *ftp_glob(struct ftpconnection *connection, char *wildcard, int status) {
558 static int index = 0;
559 static char pathname[4096], working[4096];
560 int response;
561 static char *return_list[500];
562 char *retstring;
563 if (!ftp_check_ready(connection, 1)) return NULL;
564 if (status == 0) {
565 memset(pathname, 0, 4096);
566 memset(working, 0, 4096);
567 memset(return_list, 0, sizeof(*return_list) * 500);
568 // Separate the path and filename
569 index = strlen(wildcard);
570 while (index) {
571 if (wildcard[index] == '/') {
572 strncpy(pathname, wildcard, index + 1);
573 strncpy(working, &wildcard[index + 1], 4096);
574 break;
575 }
576 index--;
577 }
578 if (strlen(working) == 0) strncpy(working, wildcard, 4096);
579 response = ftp_nlst(connection, pathname, return_list, 500);
580 if (response <= 0) return NULL;
581 index = 0;
582 }
583 while (return_list[index] != NULL) {
584 int eflags = FNM_PATHNAME | FNM_PERIOD | (1 << 3);
585 if (fnmatch(wildcard, return_list[index], eflags) != 0) {
586 index++;
587 continue;
588 }
589 retstring = (char *)strdup(return_list[index]);
590 return_list[index] = 0;
591 index++;
592 return retstring;
593 }
594 return NULL;
595 }
596
ftp_exists(struct ftpconnection * connection,char * filename)597 int ftp_exists(struct ftpconnection *connection, char *filename) {
598 /* Check if a file exists on the server */
599 /* Return codes
600 < 0 Don't know
601 0 Doesn't exist
602 > 0 Does exist
603 */
604 char line[4096];
605 int result;
606
607 if (!filename) return 0;
608 if (!ftp_check_ready(connection, 1)) return 0;
609
610 log(LOG_CALLS, "(%p, %s)", connection, filename);
611 sprintf(line, "STAT %s\r\n", filename);
612 ftp_sendline(connection, line);
613 result = ftp_getrc(connection, NULL, 0, 0);
614 switch (result) {
615 case 213 : return 1;
616 case 211 : return 1;
617 case 212 : return 1;
618 case 421 : log(LOG_ALERT, "STAT error.. %d\n", result); return -1; break;
619 case 450 : return 0;
620 case 500 : break;
621 case 501 : break;
622 case 502 : break;
623 case 530 : log(LOG_ALERT, "Not logged in correctly\n"); return -1; break;
624 default : break;
625 }
626 return -1;
627 }
628
ftp_error_message(int error)629 char *ftp_error_message(int error) {
630 switch(error) {
631 case 110 : return "Restart marker reply"; break;
632 case 120 : return "Service ready in nnn minutes."; break;
633 case 125 : return "Data connection already open; transfer starting."; break;
634 case 150 : return "File status okay; about to open data connection."; break;
635 case 200 : return "Command okay."; break;
636 case 202 : return "Command not implemented, superfluous at this site."; break;
637 case 211 : return "System status, or system help reply."; break;
638 case 212 : return "Directory status."; break;
639 case 213 : return "File status."; break;
640 case 214 : return "Help message."; break;
641 case 215 : return "NAME system type."; break;
642 case 220 : return "Service ready for new user."; break;
643 case 221 : return "Service closing control connection."; break;
644 case 225 : return "Data connection open; no transfer in progress."; break;
645 case 226 : return "Closing data connection successfully."; break;
646 case 227 : return "Entering Passive Mode (h1,h2,h3,h4,p1,p2)."; break;
647 case 230 : return "User logged in, proceed."; break;
648 case 250 : return "Requested file action okay, completed."; break;
649 case 257 : return "PATHNAME created."; break;
650 case 331 : return "User name okay, need password."; break;
651 case 332 : return "Need account for login."; break;
652 case 350 : return "Requested file action pending further information."; break;
653 case 421 : return "Service not available, closing control connection."; break;
654 case 425 : return "Can't open data connection."; break;
655 case 426 : return "Connection closed; transfer aborted."; break;
656 case 450 : return "Requested file action not taken - File unavailable."; break;
657 case 451 : return "Requested action aborted: local error in processing."; break;
658 case 452 : return "Requested action not taken - Insufficient storage space in system."; break;
659 case 500 : return "Syntax error, command unrecognized."; break;
660 case 501 : return "Syntax error in parameters or arguments."; break;
661 case 502 : return "Command not implemented."; break;
662 case 503 : return "Bad sequence of commands."; break;
663 case 504 : return "Command not implemented for that parameter."; break;
664 case 530 : return "Not logged in."; break;
665 case 532 : return "Need account for storing files."; break;
666 case 550 : return "Requested action not taken - File unavailable."; break;
667 case 551 : return "Requested action aborted: page type unknown."; break;
668 case 552 : return "Requested file action aborted - Exceeded storage allocation"; break;
669 case 553 : return "Requested action not taken - File name not allowed."; break;
670 default : return "Unknown error";
671 }
672 }
673
674