1 /*
2 *** cmdftp, command line ftp client ***
3
4 Copyright (C) 2003-2009 Claudio Fontana
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program (look for the file called COPYING);
18 if not, write to the Free Software Foundation, Inc.,
19 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
21 You can contact the author (Claudio Fontana) by sending a mail
22 to claudio@gnu.org
23 */
24 #include "cmdftp.h"
25
26 /*******************************/
27 /* ERROR, WARNING, ... STRINGS */
28 /*******************************/
29
30 char* msg_err[] = {
31 0, "bad server response",
32
33 "Usage: cmdftp [OPTION]... HOSTNAME\n"
34
35 "Try '"
36 #if HAVE_GETOPT_LONG
37 "cmdftp --help"
38 #else
39 "cmdftp -h"
40 #endif
41 "' for more information.",
42
43 "connection failed", "giving up on login", "unexpected error", "heap allocation failed", "lost working directory", "double SIGINT, aborting", "no usable tmpdir"
44 };
45
46 char* msg_war[] = {
47 "bye", "cannot open:", "error writing to:", "error reading from:", "transfer not confirmed", "file not found:", "cannot open temp file:", "unknown command:", "missing argument", "ignoring argument", "login failed", "operation failed or incomplete", "missing environment variable:", "resuming connection lost during", "connection timeout during", "interrupted during", "could not resume", "invalid source:", "invalid target:", "skipping:", "(local)", "(remote)"
48 };
49
50 #if HAVE_GETOPT_LONG
51
52 char* msg_usage = "cmdftp [OPTION]... HOSTNAME\n\n"
53 "options: \n"
54 " -h, --help show this help and quit\n"
55 " -v, --version show version and quit\n"
56 " -p, --port N remote TCP port of the server (def:21)\n"
57 " -b, --buffer N size of the transfer buffer in KB (def:64)\n"
58 " -t, --timeout N broken connection check timeout in sec (def:60)\n"
59 " -a, --attempts N number of manual login attempts\n"
60 " -q, --quiet be (very) quiet\n"
61 " -D, --debug show detailed information about ftp commands\n"
62 " -n, --no-auto disable autologin (~/.netrc)\n"
63 " -m, --no-manual disable manual login\n"
64 " -g, --no-paging disable output paging\n"
65 " -d, --dotfiles do not ignore dot-files in transfers\n"
66 " -P, --no-path disable path in prompt\n";
67
68 #else
69 char* msg_usage = "cmdftp [OPTION]... HOSTNAME\n\n"
70 "options: \n"
71 " -h show this help and quit\n"
72 " -v show version and quit\n"
73 " -p N remote TCP port of the server (def:21)\n"
74 " -b N size of the transfer buffer in KB (def:64)\n"
75 " -t N broken connection check timeout in sec (def:60)\n"
76 " -a N number of manual login attempts\n"
77 " -q be (very) quiet\n"
78 " -D show detailed information about ftp commands\n"
79 " -n disable autologin (~/.netrc)\n"
80 " -m disable manual login\n"
81 " -g disable output paging\n"
82 " -d do not ignore dot-files in transfers\n"
83 " -P disable path in prompt\n";
84 #endif
85
86 char* msg_version = PACKAGE_STRING " Copyright (c) 2003-2009 Claudio Fontana";
87
88 char* msg_intro =
89 "cmdftp comes with ABSOLUTELY NO WARRANTY; this is free software, and you\n"
90 "are welcome to redistribute it under certain conditions specified by the\n"
91 "GPL (GNU General Public License) version 3 or later.\n"
92 "Look at the COPYING file for the details.\n"
93 "\nType h after the login to get help about internal commands.\n";
94
95 char* msg_help[] = {
96 "h", "display this help",
97 "l", "switch to local mode, following commands refer to local",
98 "r", "switch to remote mode, following commands refer to remote",
99 "pwd", "prompt working directory",
100 "cd PATH", "change working directory to PATH",
101 "md PATH", "make new directory PATH",
102 "rd PATH", "remove empty directory PATH",
103 "rm MASK", "delete regular files matching MASK, skip directories",
104 "ls", "list current directory contents",
105 "ls MASK", "list files/dirs matching MASK",
106 "dir", "pretty list of current directory",
107 "cp SRC TRG", "copy SRC to TRG. Behaves like /bin/cp -r",
108 "mv SRC TRG", "move SRC to TRG. Behaves like /bin/mv",
109 "u MASK DIR", "upload files/dirs matching MASK into remote DIR (recurs)",
110 "d MASK DIR", "download files/dirs matching MASK into local DIR (recurs)",
111 "dr MASK DIR", "same as above. If local file already exists, resume",
112 "ren MASK FR TO", "replace FR to TO once in all filenames matching MASK",
113 "p FILE", "print contents of the FILE on the terminal",
114 "e FILE", "edit FILE",
115 "q", "quit client",
116 "quit|exit|bye", "aliases for q command",
117 "<TAB>", "tab-completion"
118 };
119
120
121 /***********/
122 /* GLOBALS */
123 /***********/
124
125 struct cmdftp_options o = {
126 0, /* hostname */
127 64 * 1024, /* b */
128 21, /* p */
129 1, /* a */
130 60, /* t */
131 0, 0, 0, 0, 0, 0, 0 /* FLAGS */
132 };
133
134 int cmdftp_control = 0; /* control connection */
135 int cmdftp_data = 0; /* data connection */
136
137 char* user = 0; char* pass = 0;
138 char logging_in = 0; /* user_logging_in? */
139 volatile int transfer_interrupted = TRAN_INTR_NO; /* Why? */
140
141 struct hostent* server = 0; /* the remote host */
142 struct termios cmdftp_termios; /* terminal data */
143
144 char* buffer; /* in/out file transf */
145
146 char cmd_buffer[CMD_BUF_SIZE + 1]; /* in/out commands */
147 char cmd_line[CMD_BUF_SIZE + 1]; /* in line sep */
148 char cmd_userinput[CMD_BUF_SIZE + 1]; /* buf for term in */
149 char* cmd_ptr; /* ->cmd_buffer */
150
151 char* env[N_ENV]; /* used env vars */
152 char* cwd[2] = { 0 }; /* working dir {l, r} */
153
154 int mode = CMDFTP_REMOTE; /* current mode {l, r} */
155
156 struct sigaction sa;
157 char localhost[256] = { 0 }; /* local host name */
158
159 struct {
160 int mlst;
161 } feat;
162
163 /*********************************************/
164 /* USER COMMANDS and corresponding functions */
165 /*********************************************/
166
167 char* commands[] = { "bye", "cat", "cd", "cp", "d", "dir", "dr", "e", "exit", "h", "l", "ls", "md", "mkdir", "mv", "p", "pwd", "q", "quit", "r", "rd", "ren", "rename", "rm", "rmdir", "u" };
168
169 #define N_COMMANDS (sizeof(commands) / sizeof(char*))
170
171 fun_ptr cmd_functions[N_COMMANDS] =
172 { cmd_quit, cmd_p, cmd_cd, cmd_cp, cmd_d, cmd_dir, cmd_dr, cmd_e, cmd_quit, cmd_h, cmd_l, cmd_ls, cmd_md, cmd_md, cmd_mv, cmd_p, cmd_pwd, cmd_quit, cmd_quit, cmd_r, cmd_rd, cmd_ren, cmd_ren, cmd_rm, cmd_rd, cmd_u };
173
174 fun_ptr mf[2][13] = {
175 { &local_chdir, &local_mkdir, &local_rmdir, (fun_ptr)local_getcwd,
176 &local_copy, &local_move, &local_unlink,
177 &local_file, (fun_ptr)local_size,
178 &local_fetch_list, &local_fetch_pretty_list,
179 &local_print, &local_edit },
180
181 { &remote_chdir, &remote_mkdir, &remote_rmdir, (fun_ptr)remote_getcwd,
182 &remote_copy, &remote_move, &remote_unlink,
183 &remote_file, (fun_ptr)remote_size,
184 &remote_fetch_list, &remote_fetch_pretty_list,
185 &remote_print, &remote_edit }
186 };
187
188 /************************************/
189 /* ERROR, WARNING, OUTPUT FUNCTIONS */
190 /************************************/
191
192
cmdftp_err(int code,char * msg)193 void cmdftp_err(int code, char* msg)
194 { fprintf(stderr, "cmdftp: %s %s\n", msg_err[code], msg); exit(code); }
195
cmdftp_war(int code,char * msg)196 void cmdftp_war(int code, char* msg)
197 { if (!o.q) fprintf(stderr, "cmdftp: %s %s\n", msg_war[code], msg); }
198
usage(void)199 void usage(void) { printf("\n%s\n", msg_usage); }
version(void)200 void version(void) { printf("%s\n", msg_version); }
201
intro(void)202 void intro(void) {
203 int i; if (o.q) return;
204 for (i = 0; i < 79; i++) fputc('*', stdout);
205 fputc('\n', stdout);
206 version();
207 printf("\n%s\n", msg_intro);
208 for (i = 0; i < 79; i++) fputc('*', stdout);
209 fputc('\n', stdout);
210 }
211
print_progress(char * op,char * fn,size_t fn_len,off_t cur_pos,off_t total_size)212 void print_progress(char* op, char* fn, size_t fn_len, off_t cur_pos, off_t total_size) {
213
214 char* units[] = { "", "K", "M", "G", "T" }; int u_idx;
215 char line[WIDE_LINE_LEN]; size_t line_len;
216 double progress =
217 total_size > 0 ? (cur_pos * 100.0 / total_size) : 100.0;
218
219 u_idx = 0;
220
221 while ((total_size >> 10) >= 10 && u_idx < 4) {
222 total_size >>= 10; cur_pos >>= 10; u_idx++;
223 }
224
225 snprintf(line, 75, "%s " OFF_FMT "%s/" OFF_FMT "%s (%4.*f%%) ",
226 op, cur_pos, units[u_idx], total_size, units[u_idx],
227 progress < 1.0 ? 2 : 1, progress);
228
229 line[74] = '\0'; /* just to be sure that*/
230 line_len = strlen(line); /* line_len <= 74 */
231
232 if (line_len + fn_len < 79) {
233 strcpy(line + line_len, fn);
234
235 } else {
236 sprintf(line + line_len, "[...]%s", (fn + fn_len - (74 - line_len)));
237 }
238
239 printf("%s\r", line);
240 fflush(stdout);
241 }
242
243 /***************************************/
244 /* MAIN and other high level functions */
245 /***************************************/
246
print_prompt(void)247 void print_prompt(void) {
248 if (!o.q)
249 printf("\r%s:%s>", mode == CMDFTP_LOCAL ?
250 localhost : o.hostname, o.P ? "" : cwd[mode]);
251 fflush(stdout);
252 }
253
main(int argc,char * argv[])254 int main(int argc, char* argv[]) {
255
256 if (argc < 2) cmdftp_err(CMDFTP_ERR_OPT, "");
257 getoptions(argc, argv);
258
259 if (gethostname(localhost, sizeof(localhost)) < 0)
260 strcpy(localhost, "local");
261
262 intro();
263 reset_cmd_buffer(); buffer = my_malloc(o.b);
264
265 tcgetattr(0, &cmdftp_termios); atexit(&cmdftp_canon_mode);
266 init_signals();
267 init_temp();
268
269 if (!(cmdftp_control = cmdftp_connect(o.p, 8)))
270 cmdftp_err(CMDFTP_ERR_CONN, "");
271
272 greeting();
273 detect_feats();
274 login_procedure();
275
276 do_setcwd(CMDFTP_LOCAL);
277 do_setcwd(CMDFTP_REMOTE);
278
279 while (1) {
280 char* cmd;
281 print_prompt();
282 if (!dispatch(cmd = readline(1, 1))) return 0;
283 free(cmd);
284 }
285 }
286
287 #if HAVE_GETOPT_LONG
288
289 struct option long_options[] = {
290 { "help", 0, 0, 'h' },
291 { "version", 0, 0, 'v' },
292 { "attempts", 1, 0, 'a' },
293 { "port", 1, 0, 'p' },
294 { "buffer", 1, 0, 'b' },
295 { "timeout", 1, 0, 't' },
296 { "quiet", 0, 0, 'q' },
297 { "debug", 0, 0, 'D' },
298 { "no-manual", 0, 0, 'm' },
299 { "no-auto", 0, 0, 'n' },
300 { "no-paging", 0, 0, 'g' },
301 { "no-path", 0, 0, 'P' },
302 { "dotfiles", 0, 0, 'd' },
303 { 0, 0, 0, 0 }
304 };
305
306 #endif
307
getoptions(int argc,char * argv[])308 void getoptions(int argc, char* argv[]) {
309 int i, rv;
310
311 while ((i =
312
313 #if HAVE_GETOPT_LONG
314 getopt_long(argc, argv, "DPa:b:dghmnp:t:qv", long_options, 0)
315 #else
316 getopt(argc, argv, "DPa:b:dghmnp:t:qv")
317 #endif
318
319 ) != -1) {
320 rv = 1;
321
322 switch (i) {
323 case 'h' : usage(); cleanexit();
324 case '?' : case ':' : cmdftp_err(CMDFTP_ERR_OPT, "");
325 case 'v' : version(); cleanexit();
326
327 case 'a' : rv = sscanf(optarg, "%hu", &o.a); break;
328 case 'p' : rv = sscanf(optarg, "%hu", &o.p); break;
329 case 'b' : rv = sscanf(optarg, "%u", &o.b); o.b *= 1024; break;
330 case 't' : rv = sscanf(optarg, "%u", &o.t); break;
331
332 case 'q' : o.q = 1; break;
333 case 'D' : o.D = 1; break;
334 case 'm' : o.m = 1; break;
335 case 'n' : o.n = 1; break;
336 case 'g' : o.g = 1; break;
337 case 'd' : o.d = 1; break;
338 case 'P' : o.P = 1; break;
339 }
340 if (rv != 1) cmdftp_err(CMDFTP_ERR_OPT, "");
341 }
342
343 if (optind >= argc) cmdftp_err(CMDFTP_ERR_OPT, "");
344
345 o.hostname = my_strdup(argv[optind]);
346
347 env[CMDFTP_ENV_PAGER] = o.g ? 0 : getenv("PAGER");
348 env[CMDFTP_ENV_EDITOR] = getenv("EDITOR");
349 env[CMDFTP_ENV_HOME] = getenv("HOME");
350 env[CMDFTP_ENV_TMPDIR] = getenv("TMPDIR");
351 }
352
greeting(void)353 void greeting(void) {
354 if (recv_answer(0, 0) != 220)
355 cmdftp_err(CMDFTP_ERR_SERV, "(greeting)");
356
357 if (!o.q)
358 fprintf(stdout, "%s\n", cmd_buffer);
359 }
360
detect_feats(void)361 void detect_feats(void) {
362 int i; struct line_data d;
363
364 i = send_command_and_fetch("FEAT", &d, 0);
365 if (!i)
366 return;
367
368 if (i >= 300) {
369 free_lines(&d);
370 return;
371 }
372
373 for (i = 0; i < d.count; i++) {
374 if (strncmp(" MLST", d.lines[i], 5) == 0)
375 feat.mlst = 1;
376 }
377
378 free_lines(&d);
379 }
380
381
382 /*********************************/
383 /* LOGIN AND AUTOLOGIN FUNCTIONS */
384 /*********************************/
385
login_procedure(void)386 void login_procedure(void) {
387 int attempt;
388 user = pass = 0;
389
390 if (!o.n) {
391 if (auto_login()) return;
392 }
393
394 if (!o.m) {
395 for (attempt = 0; attempt < o.a; attempt++) {
396 if (user) free(user); if (pass) free(pass);
397 user = pass = 0;
398 if (manual_login()) return;
399 cmdftp_war(CMDFTP_WAR_LGIN, "");
400 }
401 }
402
403 cmdftp_err(CMDFTP_ERR_LGIN, "");
404 }
405
login(char * user,char * pass)406 int login(char* user, char* pass) {
407 int rv = 0;
408
409 logging_in = 1;
410 snprintf(cmd_buffer, CMD_BUF_SIZE, "USER %s", user);
411 rv = send_command(cmd_buffer, 0);
412 if (rv == 230) return 1;
413 if (rv != 331) return 0;
414
415 snprintf(cmd_buffer, CMD_BUF_SIZE, "PASS %s", pass);
416 if (send_command(cmd_buffer, 0) == 230) rv = 1;
417 logging_in = 0;
418 return rv;
419 }
420
manual_login(void)421 int manual_login(void) {
422 printf("Username: ");
423 if (!(user = readline(0, 1))) return 0;
424
425 cmdftp_pwd_start(); printf("Password: ");
426 pass = readline(0, 0);
427 cmdftp_pwd_end();
428
429 if (!pass) return 0;
430 return login(user, pass);
431 }
432
433 const char *netrc_skeleton = ""
434 "default\n"
435 "login anonymous\n"
436 "password cmdftp@example.com\n";
437
create_netrc(char * fname)438 static FILE *create_netrc(char *fname)
439 {
440 int fd; int len;
441
442 if ((fd = creat(fname, 0600)) < 0)
443 return 0;
444
445 len = strlen(netrc_skeleton);
446
447 if (write(fd, netrc_skeleton, len) != len) {
448 close(fd);
449 return 0;
450 }
451
452 if (close(fd) < 0)
453 return 0;
454
455 return fopen(fname, "r");
456 }
457
auto_login(void)458 int auto_login(void) {
459 FILE* f; char* fname; char line[WIDE_LINE_LEN]; char token[WIDE_LINE_LEN];
460 int state, rv;
461 char* rc = ".netrc"; state = rv = 0;
462
463 if (!env[CMDFTP_ENV_HOME])
464 return 0;
465
466 fname = fullpath(env[CMDFTP_ENV_HOME], rc);
467
468 if (local_file(fname) == FILE_NEXIST)
469 f = create_netrc(fname);
470 else
471 f = fopen(fname, "r");
472
473 free(fname);
474
475 if (!f)
476 return 0;
477
478 while ((fgets(line, WIDE_LINE_LEN, f))) {
479 char* ptr, *ptr_new, *ptr_tmp; int l;
480 l = strlen(line); if (line[l - 1] == '\n') line[l - 1] = 0;
481 if ((ptr = strchr(line, '#')))
482 if (ptr == line || ptr[-1] != '\\') *ptr = 0;
483 ptr = ptr_new = ptr_tmp = line;
484
485 tokenize:
486 if ((l = auto_login_next_token(&ptr, token)) == -1) continue;
487 if (l == 0) {
488 if (!state)
489 { if (strcmp(o.hostname, token) == 0) state++; goto tokenize; }
490 break;
491 } else if (l == 1) {
492 if (!state) { state++; goto tokenize; }
493 break;
494 } else if (l == 2) {
495 if (state && !user) user = my_strdup(token); goto tokenize;
496 } else if (l == 3) {
497 if (state && !pass) pass = my_strdup(token); goto tokenize;
498 }
499 }
500 if (user && !pass) pass = my_strdup("");
501 if (user) rv = login(user, pass);
502 if (!rv) {
503 if (user) { free(user); user = 0; }
504 if (pass) { free(pass); pass = 0; }
505 }
506 return rv;
507 }
508
auto_login_next_token(char ** hay,char * store)509 int auto_login_next_token(char** hay, char* store) {
510 #define AUTO_N_KEYS 4
511 char* keys[] = { "machine", "default", "login", "password" };
512 int keys_len[] = { 7, 7, 5, 8 };
513 char* ptr, *minptr; int i, min;
514
515 min = -1; minptr = 0;
516
517 for (i = 0; i < AUTO_N_KEYS; i++)
518 if ((ptr = strstr(*hay, keys[i])))
519 if (!minptr || ptr < minptr) { minptr = ptr; min = i; }
520
521 if (min != -1) {
522 *hay = minptr + keys_len[min];
523 if (min != 1) {
524 if (!isspace(**hay)) {
525 *store = 0;
526 } else {
527 while (isspace(**hay)) (*hay)++;
528 read_token(hay, store);
529 }
530 }
531 }
532 return min;
533 #undef AUTO_N_KEYS
534 }
535
read_token(char ** hay,char * store)536 void read_token(char** hay, char* store) {
537 int i, c;
538
539 for (i = 0; ; i++) {
540 c = **hay;
541 if (!c || isspace(c)) break;
542
543 if (c == '\\') {
544 int n = 0; (*hay)++;
545 if (!(c = **hay)) break;
546
547 if (isdigit(c)) {
548 sscanf(*hay, "%3o%n", (unsigned int*)&c, &n); *hay += n - 1;
549
550 } else if (c == 'x') {
551 (*hay)++; sscanf(*hay, "%2x%n", (unsigned int*)&c, &n); *hay += n - 1;
552
553 } else {
554 if (c == 'a') c = '\a'; else if (c == 'b') c = '\b';
555 else if (c == 'f') c = '\f'; else if (c == 'n') c = '\n';
556 else if (c == 't') c = '\t'; else if (c == 'v') c = '\v';
557 else {
558 /* get character as if no backslash present, good to escape spaces */
559 }
560 }
561 }
562 store[i] = c;
563 (*hay)++;
564 }
565
566 store[i] = 0;
567 }
568
569
570 /***********************/
571 /* FUNCTION DISPATCHER */
572 /***********************/
573
574
dispatch(char * cmd)575 int dispatch(char* cmd) {
576 int idx;
577 char* argv[4];
578
579 if (!cmd) return 0;
580 split_cmd(cmd, argv);
581
582 if (*argv[0] == 0) return 1; /* the empty, do nothing command */
583
584 if ((idx = str_binsearch(argv[0])) == -1)
585 cmdftp_war(CMDFTP_WAR_BADC, cmd);
586 else if (!cmd_functions[idx](&argv[1]))
587 cmdftp_war(CMDFTP_WAR_FAIL, "");
588
589 free_cmd(argv);
590 return 1;
591 }
592
593 /*************************************************/
594 /* LOCAL MODE SPECIFIC UTILITY FUNCTIONS: local_ */
595 /*************************************************/
596
local_chdir(char * arg)597 int local_chdir(char* arg) {
598 /* watch return value! */
599 int rv;
600
601 if ((rv = chdir(arg)) < 0)
602 cmdftp_war(CMDFTP_WAR_LERR, strerror(errno));
603
604 return rv;
605 }
606
local_mkdir(char * arg)607 int local_mkdir(char* arg) {
608 int rv;
609
610 if (!(rv = (mkdir(arg, 0777) == 0)))
611 cmdftp_war(CMDFTP_WAR_LERR, strerror(errno));
612
613 return rv;
614 }
615
local_rmdir(char * arg)616 int local_rmdir(char* arg) {
617 int rv;
618
619 if (!(rv = (rmdir(arg) == 0)))
620 cmdftp_war(CMDFTP_WAR_LERR, strerror(errno));
621
622 return rv;
623 }
624
local_getcwd(void)625 char* local_getcwd(void) {
626 return getcwd(cmd_buffer, CMD_BUF_SIZE);
627 }
628
local_copy(char * target,char * source)629 int local_copy(char* target, char* source) {
630 FILE* t, *s; off_t start_pos, total_size;
631 char* csource; char* op;
632 size_t csource_len;
633 int rv = 0;
634
635 if (!(s = fopen(source, "rb"))) return 0;
636
637 if ((total_size = local_size(s)) >= 0 && (t = fopen(target, "wb"))) {
638
639 op = "Copying";
640 csource = clean_fn(source); csource_len = strlen(csource);
641
642 start_transfer:
643 start_pos = 0;
644
645 if (!o.q)
646 print_progress(op, csource, csource_len, start_pos, total_size);
647
648 while (start_pos < total_size) {
649 int toread, bytes, written;
650 toread = ((total_size - start_pos) < o.b) ?
651 total_size - start_pos : o.b;
652
653 if (!(bytes = fread(buffer, 1, toread, s)))
654 break;
655 if ((written = fwrite(buffer, 1, bytes, t)) != bytes)
656 break;
657
658 start_pos += bytes;
659
660 if (!o.q)
661 print_progress(op, csource, csource_len, start_pos, total_size);
662 }
663
664 if (!o.q) fputc('\n', stdout);
665
666 if (start_pos < total_size) {
667 if (ferror(s)) cmdftp_war(CMDFTP_WAR_READ, source);
668 else if (ferror(t)) cmdftp_war(CMDFTP_WAR_WRIT, target);
669 else if TRANSFER_INTERRUPTED_CHECK("local copy", 0)
670 else { cmdftp_err(CMDFTP_ERR_UNER, strerror(errno)); }
671
672 } else {
673 rv = 1;
674 }
675 fclose(t);
676 }
677 fclose(s);
678 return rv;
679 }
680
local_move(char * new,char * old)681 int local_move(char* new, char* old) {
682 int rv;
683
684 if (!(rv = (rename(old, new) == 0)))
685 cmdftp_war(CMDFTP_WAR_LERR, strerror(errno));
686
687 return rv;
688 }
689
local_unlink(char * name)690 int local_unlink(char* name) {
691 int rv;
692
693 if (!(rv = (unlink(name) == 0)))
694 cmdftp_war(CMDFTP_WAR_LERR, strerror(errno));
695
696 return rv;
697 }
698
local_file(char * name)699 int local_file(char* name) {
700 struct stat buf;
701
702 if (stat(name, &buf) != 0)
703 return FILE_NEXIST;
704
705 return (S_ISREG(buf.st_mode) ? FILE_ISREG :
706 S_ISDIR(buf.st_mode) ? FILE_ISDIR :
707 FILE_OTHER);
708 }
709
local_fetch_list(char * filemask,struct list_data * d)710 int local_fetch_list(char* filemask, struct list_data* d) {
711 char* fn_mask[3], *tmpmask;
712 struct dirent* entry;
713 DIR* dir;
714 int rv = 0;
715
716 if (*filemask == 0) tmpmask = my_strdup("*");
717 else tmpmask = my_strdup(filemask);
718
719 canonized_fn(fn_mask, tmpmask);
720
721 if (!(dir = opendir(filemask ? fn_mask[0] : ".")))
722 goto end_proc;
723
724 while ((entry = readdir(dir))) {
725 if (fnmatch(fn_mask[1], entry->d_name, 0) == 0) {
726 char* full_name = fullpath(fn_mask[0], entry->d_name);
727 store_list(clean_fn(full_name), d);
728 free(full_name);
729 }
730 }
731
732 closedir(dir); rv = 1;
733
734 end_proc:
735 free(tmpmask); free_fn(fn_mask);
736 return rv;
737 }
738
local_fetch_pretty_list(struct list_data * d)739 int local_fetch_pretty_list(struct list_data* d) {
740 int pipe_fd[2]; FILE* target; char line[CMD_BUF_SIZE];
741
742 if (pipe(pipe_fd) < 0) return 0;
743
744 if (!cmdftp_execute("/bin/ls -l", 0, -1, pipe_fd[1])) {
745 close(pipe_fd[0]); close(pipe_fd[1]);
746 return local_fetch_list("", d);
747 }
748
749 close(pipe_fd[1]);
750
751 if (!(target = fdopen(pipe_fd[0], "r")))
752 { close(pipe_fd[0]); return 0; }
753
754 while (fgets(line, CMD_BUF_SIZE, target)) {
755 size_t len = strlen(line);
756 if (line[len - 1] == '\n') line[len - 1] = 0;
757 store_pretty_list(line, d);
758 }
759 fclose(target);
760 return 1;
761 }
762
local_size(FILE * f)763 off_t local_size(FILE* f) {
764 off_t rv;
765 fseeko(f, 0, SEEK_END); rv = ftello(f); fseeko(f, 0, SEEK_SET);
766 return rv;
767 }
768
local_print(char * arg)769 int local_print(char* arg) {
770 if (env[CMDFTP_ENV_PAGER])
771 return cmdftp_execute(env[CMDFTP_ENV_PAGER], arg, -1, -1);
772 else {
773 FILE* f; int c;
774 if (!(f = fopen(arg, "r")))
775 { cmdftp_war(CMDFTP_WAR_OPEN, arg); return 0; }
776 while ((c = getc(f)) != EOF) fputc(c, stdout); fputc('\n', stdout);
777 fclose(f);
778 }
779 return 1;
780 }
781
local_edit(char * arg)782 int local_edit(char* arg) {
783 return cmdftp_execute(env[CMDFTP_ENV_EDITOR], arg, -1, -1);
784 }
785
786 /***************************************************/
787 /* REMOTE MODE SPECIFIC UTILITY FUNCTIONS: remote_ */
788 /***************************************************/
789
remote_chdir(char * arg)790 int remote_chdir(char* arg) {
791 return remote_chdir_aux(arg, 0);
792 }
793
remote_chdir_aux(char * arg,char suppress_err)794 int remote_chdir_aux(char* arg, char suppress_err) {
795 /* watch the return value! */
796 int rv;
797
798 snprintf(cmd_buffer, CMD_BUF_SIZE, "CWD %s", arg);
799 if (!(rv = send_command(cmd_buffer, suppress_err)))
800 return -2; /* bad err, intr */
801
802 if (rv == 250) return 0; /* ok */
803 return -1; /* fail */
804 }
805
remote_mkdir(char * arg)806 int remote_mkdir(char* arg) {
807 snprintf(cmd_buffer, CMD_BUF_SIZE, "MKD %s", arg);
808 return (send_command(cmd_buffer, 0) == 257);
809 }
810
remote_rmdir(char * arg)811 int remote_rmdir(char* arg) {
812 snprintf(cmd_buffer, CMD_BUF_SIZE, "RMD %s", arg);
813 return (send_command(cmd_buffer, 0) == 250);
814 }
815
remote_getcwd(void)816 char* remote_getcwd(void) {
817 char* start, *end;
818 if (!send_command("PWD", 1)) return 0;
819 if ((end = strrchr(cmd_buffer, '"')) == 0 ||
820 (start = strchr(cmd_buffer, '"')) == 0) return 0;
821 *end = 0; start++;
822 return start;
823 }
824
remote_copy(char * target,char * source)825 int remote_copy(char* target, char* source) {
826 FILE* t; char* t_fn;
827 int rv = 0;
828
829 if (!(t = cmdftp_temp(&t_fn)))
830 { cmdftp_war(CMDFTP_WAR_TEMP, t_fn); return 0; }
831
832 if (download(t, source) && upload(target, t))
833 rv = 1;
834
835 fclose(t); unlink(t_fn); free(t_fn);
836 return rv;
837 }
838
remote_move(char * to,char * from)839 int remote_move(char* to, char* from) {
840 snprintf(cmd_buffer, CMD_BUF_SIZE, "RNFR %s", from);
841 if (send_command(cmd_buffer, 0) != 350) return 0;
842
843 snprintf(cmd_buffer, CMD_BUF_SIZE, "RNTO %s", to);
844 if (send_command(cmd_buffer, 0) != 250) return 0;
845
846 return 1;
847 }
848
remote_unlink(char * arg)849 int remote_unlink(char* arg) {
850 snprintf(cmd_buffer, CMD_BUF_SIZE, "DELE %s", arg);
851 return (send_command(cmd_buffer, 0) == 250);
852 }
853
remote_file_legacy(char * name)854 static int remote_file_legacy(char* name) {
855 off_t size; int rv;
856
857 rv = remote_chdir_aux(name, 1);
858
859 if (rv == 0) { /* ok */
860 return do_home(CMDFTP_REMOTE) ? FILE_ISDIR : -1;
861 }
862
863 if (rv == -2)
864 return -2;
865
866 if (!send_command("TYPE I", 1))
867 return 0;
868
869 if ((size = remote_size(name)) >= 0) {
870 return FILE_ISREG;
871
872 } else if (size == -1) {
873 return FILE_NEXIST;
874
875 } else /* (size == -2) */ {
876 return -1;
877 }
878 }
879
touppercase(char * s)880 static void touppercase(char *s)
881 {
882 while ((*s = toupper(*s)))
883 s++;
884 }
885
remote_file(char * name)886 int remote_file(char* name) {
887 int rv; struct line_data d;
888 char *start, *end;
889
890 if (feat.mlst == 0)
891 return remote_file_legacy(name);
892
893 snprintf(cmd_buffer, CMD_BUF_SIZE, "MLST %s", name);
894 rv = send_command_and_fetch(cmd_buffer, &d, 1);
895
896 if (!rv)
897 return -2;
898
899 if (rv >= 300) {
900 rv = FILE_NEXIST;
901 goto end_proc;
902 }
903
904 if (d.count == 0)
905 return -1;
906
907 touppercase(d.lines[0]);
908 start = d.lines[0];
909
910 while (*start == ' ')
911 start++;
912
913 if ((end = strchr(start, ' '))) {
914 *end = '\0';
915 }
916
917 if (!(start = strstr(start, "TYPE="))) {
918 rv = -1;
919 goto end_proc;
920 }
921 start += 5;
922
923 if (!(end = strchr(start, ';'))) {
924 rv = -1;
925 goto end_proc;
926 }
927 *end = '\0';
928
929 if (strcmp(start, "FILE") == 0) {
930 rv = FILE_ISREG;
931 goto end_proc;
932 }
933
934 if (strcmp(start, "CDIR") == 0 ||
935 strcmp(start, "DIR") == 0) {
936 rv = FILE_ISDIR;
937 goto end_proc;
938 }
939
940 rv = FILE_OTHER;
941 end_proc:
942 free_lines(&d);
943 return rv;
944 }
945
remote_fetch_list(char * filemask,struct list_data * d)946 int remote_fetch_list(char* filemask, struct list_data* d) {
947 return remote_fetch_list_aux(filemask, d, 0);
948 }
949
remote_fetch_pretty_list(struct list_data * d)950 int remote_fetch_pretty_list(struct list_data* d) {
951 return remote_fetch_list_aux(0, d, 1);
952 }
953
remote_fetch_list_aux(char * filemask,struct list_data * d,char pretty)954 int remote_fetch_list_aux(char* filemask, struct list_data* d, char pretty) {
955 char* tmpmask, *fn_mask[3], *line;
956 int cmdres, rv = 0;
957
958 #ifdef CMDFTP_LIST_HACK
959 int hack = 0;
960 #endif
961
962 int port;
963
964 if (filemask) {
965 if (*filemask == 0) tmpmask = my_strdup("*");
966 else tmpmask = my_strdup(filemask);
967
968 canonized_fn(fn_mask, tmpmask);
969 }
970
971 start_transfer:
972
973 if (!send_command("TYPE A", 0)) goto end_proc;
974
975 if (!(port = getport())) goto end_proc;
976 if (!(cmdftp_data = cmdftp_connect(port, 4))) goto end_proc;
977
978 #ifdef CMDFTP_LIST_HACK
979 if (filemask) {
980 hack = 1;
981 if (remote_chdir(fn_mask[0]) != 0) goto end_proc;
982 }
983
984 sprintf(cmd_buffer, pretty ? "LIST" : "NLST");
985 #else
986 if (filemask) {
987 snprintf(cmd_buffer, CMD_BUF_SIZE, pretty ? "LIST %s" : "NLST %s",
988 fn_mask[0]);
989 } else {
990 sprintf(cmd_buffer, pretty ? "LIST" : "NLST");
991 }
992 #endif /* CMDFTP_LIST_HACK */
993
994 if ((cmdres = (send_command(cmd_buffer, 1))) != 150 &&
995 cmdres != 125 && cmdres != 226)
996 { close(cmdftp_data); if (cmdres == 450) rv = 1; goto end_proc; }
997
998 reset_cmd_buffer();
999
1000 transfer_interrupted = TRAN_INTR_NO;
1001
1002 while ((line = recv_line(cmdftp_data))) {
1003 if (!pretty) {
1004 char* fn;
1005 if (!(fn = strrchr(line, '/'))) fn = line;
1006 else fn++;
1007
1008 if (!filemask || fnmatch(fn_mask[1], fn, 0) == 0) {
1009 char* full_name = filemask ? fullpath(fn_mask[0], fn) : fn;
1010 store_list(clean_fn(full_name), d);
1011 if (filemask) free(full_name);
1012 }
1013 } else {
1014 store_pretty_list(line, d);
1015 }
1016 }
1017
1018 close(cmdftp_data);
1019
1020 if TRANSFER_INTERRUPTED_CHECK("data transfer", 1)
1021 else {
1022 rv = 1;
1023 if (cmdres != 226 && !recv_confirm())
1024 cmdftp_war(CMDFTP_WAR_NOCT, "");
1025 }
1026
1027 end_proc:
1028 if (filemask) { free(tmpmask); free_fn(fn_mask); }
1029
1030 #ifdef CMDFTP_LIST_HACK
1031 if (hack) {
1032 rv = (remote_chdir(cwd[CMDFTP_REMOTE]) == 0) && rv;
1033 }
1034 #endif
1035
1036 return rv;
1037 }
1038
remote_size(char * filename)1039 off_t remote_size(char* filename) {
1040 off_t size; int rv;
1041 snprintf(cmd_buffer, CMD_BUF_SIZE, "SIZE %s", filename);
1042 if (!(rv = send_command(cmd_buffer, 1))) return -2;
1043 else if (rv != 213) return -1;
1044
1045 sscanf(cmd_buffer + 4, OFF_FMT, &size);
1046 return size;
1047 }
1048
remote_print(char * arg)1049 int remote_print(char* arg) {
1050 int c; char* fn; FILE* f; int rv = 0;
1051
1052 if (!(f = cmdftp_temp(&fn))) { cmdftp_war(CMDFTP_WAR_TEMP, fn); return 0; }
1053
1054 if (download(f, arg)) {
1055 if (env[CMDFTP_ENV_PAGER]) {
1056 fclose(f); f = 0;
1057 rv = cmdftp_execute(env[CMDFTP_ENV_PAGER], fn, -1, -1);
1058 } else {
1059 fseeko(f, 0, SEEK_SET);
1060 while ((c = getc(f)) != EOF) fputc(c, stdout); fputc('\n', stdout);
1061 rv = 1;
1062 }
1063 }
1064
1065 if (f) fclose(f); unlink(fn); free(fn); return rv;
1066 }
1067
remote_edit(char * arg)1068 int remote_edit(char* arg) {
1069 char* fn; FILE* f; int rv = 0;
1070
1071 if (!(f = cmdftp_temp(&fn))) { cmdftp_war(CMDFTP_WAR_TEMP, fn); return 0; }
1072
1073 if (download(f, arg)) {
1074 fclose(f); f = 0;
1075 if (cmdftp_execute(env[CMDFTP_ENV_EDITOR], fn, -1, -1)) {
1076 if ((f = fopen(fn, "r")) && upload(arg, f)) rv = 1;
1077 }
1078 }
1079 if (f) fclose(f); unlink(fn); free(fn); return rv;
1080 }
1081
1082
1083 /***********************************/
1084 /* COMPOSITE ACTIONS: do_ */
1085 /***********************************/
1086
do_home(int mode)1087 int do_home(int mode) {
1088 /* home to current directory of mode */
1089
1090 if (mf[mode][MODE_CHDIR](cwd[mode]) == 0)
1091 return 1; /* ok */
1092 else if (mf[mode][MODE_CHDIR](cwd[mode]) == 0)
1093 return 0; /* ok, but stop current op */
1094 else
1095 cmdftp_err(CMDFTP_ERR_PWD, "");
1096
1097 return 0; /* never reached */
1098 }
1099
do_copy_dir(char * target_dir,char * source_mask)1100 int do_copy_dir(char* target_dir, char* source_mask) {
1101 struct list_data d;
1102 int i, rv;
1103
1104 if ((rv = mf[mode][MODE_FILE](target_dir)) == FILE_NEXIST) {
1105 if (!mf[mode][MODE_MKDIR](target_dir))
1106 return 0;
1107
1108 } else if (rv != FILE_ISDIR) {
1109 return 0;
1110 }
1111
1112 init_list(&d);
1113
1114 if (!mf[mode][MODE_FETCH_LIST](source_mask, &d)) return 0;
1115 if (d.count == 0) return 1; /* nothing to do but ok */
1116
1117 rv = 1;
1118 for (i = 0; i < d.count; i++) {
1119 char* target; int st, tt;
1120 target = fullpath(target_dir, d.data[i].basename);
1121
1122 if ((tt = mf[mode][MODE_FILE](target)) < 0 ||
1123 (st = mf[mode][MODE_FILE](d.data[i].fullname)) < 0) {
1124 rv = 0;
1125 } else if (tt == FILE_OTHER ||
1126 (st == FILE_ISDIR && tt == FILE_ISREG) ||
1127 (st == FILE_ISREG && tt == FILE_ISDIR) ||
1128 (st != FILE_ISREG && st != FILE_ISDIR)) {
1129
1130 cmdftp_war(CMDFTP_WAR_SKIP, d.data[i].fullname);
1131
1132 } else if (st == FILE_ISREG) {
1133 if (!(rv = mf[mode][MODE_COPY](target, d.data[i].fullname)))
1134 rv = 0;
1135
1136 } else /* (st == FILE_ISDIR) */ {
1137 char* new_source = fullpath(d.data[i].fullname, "*");
1138 if (!do_copy_dir(target, new_source)) rv = 0;
1139 free(new_source);
1140 }
1141 free(target);
1142 if (!rv) break;
1143 }
1144 free_list(&d);
1145 return rv;
1146 }
1147
do_move_dir(char * target_dir,char * source_mask)1148 int do_move_dir(char* target_dir, char* source_mask) {
1149 struct list_data d;
1150 int i, rv;
1151
1152 init_list(&d);
1153
1154 if (!mf[mode][MODE_FETCH_LIST](source_mask, &d)) return 0;
1155 if (d.count == 0) return 1; /* nothing to do but ok */
1156
1157 rv = 1;
1158 for (i = 0; i < d.count; i++) {
1159 char* target; int st, tt;
1160 target = fullpath(target_dir, d.data[i].basename);
1161
1162 if ((tt = mf[mode][MODE_FILE](target)) < 0 ||
1163 (st = mf[mode][MODE_FILE](d.data[i].fullname)) < 0) {
1164 rv = 0;
1165
1166 } else if (tt == FILE_ISDIR ||
1167 tt == FILE_OTHER ||
1168 (st == FILE_ISDIR && tt == FILE_ISREG) ||
1169 (st != FILE_ISREG && st != FILE_ISDIR)) {
1170
1171 cmdftp_war(CMDFTP_WAR_SKIP, d.data[i].fullname);
1172
1173 } else {
1174 if (!mf[mode][MODE_MOVE](target, d.data[i].fullname))
1175 rv = 0;
1176 }
1177 free(target);
1178 if (!rv) break;
1179 }
1180 free_list(&d);
1181 return rv;
1182 }
1183
cmd_cp(char ** paths)1184 int cmd_cp(char** paths) {
1185 struct list_data ld;
1186 char* source[3]; char* target[3];
1187 int source_type, target_type;
1188 int rv = 0;
1189
1190 if (*paths[0] == 0 || *paths[1] == 0)
1191 { cmdftp_war(CMDFTP_WAR_MARG, ""); return 0; }
1192
1193 canonized_fn(source, paths[0]);
1194 canonized_fn(target, paths[1]);
1195
1196 init_list(&ld);
1197
1198 if (!mf[mode][MODE_FETCH_LIST](source[2], &ld)) goto end_proc;
1199
1200 if (ld.count == 0)
1201 { cmdftp_war(CMDFTP_WAR_MASK, paths[0]); rv = 1; goto end_proc; }
1202
1203 if ((target_type = mf[mode][MODE_FILE](target[2])) < 0) goto end_proc;
1204
1205 if (ld.count == 1) {
1206 if ((source_type = mf[mode][MODE_FILE](ld.data[0].fullname)) < 0)
1207 goto end_proc;
1208
1209 else if (source_type == FILE_ISREG) {
1210 /* source is a single regular file */
1211
1212 if (target_type == FILE_NEXIST || target_type == FILE_ISREG) {
1213 rv = mf[mode][MODE_COPY](target[2], ld.data[0].fullname);
1214
1215 } else if (target_type == FILE_ISDIR) {
1216 char* new_target = fullpath(target[2], ld.data[0].basename);
1217 free(target[2]); target[2] = new_target;
1218 rv = mf[mode][MODE_COPY](target[2], ld.data[0].fullname);
1219
1220 } else {
1221 cmdftp_war(CMDFTP_WAR_TRG, "exists (not a regfile or a dir)");
1222 }
1223 } else if (source_type == FILE_ISDIR) {
1224 /* source is a single directory */
1225 if (target_type == FILE_ISDIR) {
1226 rv = do_copy_dir(target[2], ld.data[0].fullname);
1227 } else {
1228 char* new_source = fullpath(ld.data[0].fullname, "*");
1229 rv = do_copy_dir(target[2], new_source);
1230 free(new_source);
1231 }
1232
1233 } else {
1234 cmdftp_war(CMDFTP_WAR_SRC, "not a regular file or a dir");
1235 }
1236
1237 } else if (target_type == FILE_ISDIR) {
1238 /* source is multiple entries, target is an existing directory */
1239 rv = do_copy_dir(target[2], source[2]);
1240
1241 } else {
1242 cmdftp_war(CMDFTP_WAR_TRG, "multiple source needs a dir target");
1243 }
1244
1245 end_proc:
1246 free_list(&ld);
1247 free_fn(source); free_fn(target);
1248
1249 return rv;
1250 }
1251
cmd_mv(char ** paths)1252 int cmd_mv(char** paths) {
1253 struct list_data ld;
1254 char* source[3]; char* target[3];
1255 int source_type, target_type;
1256 int rv = 0;
1257
1258 if (*paths[0] == 0 || *paths[1] == 0)
1259 { cmdftp_war(CMDFTP_WAR_MARG, ""); return 0; }
1260
1261 canonized_fn(source, paths[0]);
1262 canonized_fn(target, paths[1]);
1263
1264 init_list(&ld);
1265
1266 if (!mf[mode][MODE_FETCH_LIST](source[2], &ld)) goto end_proc;
1267
1268 if (ld.count == 0)
1269 { cmdftp_war(CMDFTP_WAR_MASK, paths[0]); rv = 1; goto end_proc; }
1270
1271 if ((target_type = mf[mode][MODE_FILE](target[2])) < 0) goto end_proc;
1272
1273 if (ld.count == 1) {
1274 if ((source_type = mf[mode][MODE_FILE](ld.data[0].fullname)) < 0)
1275 goto end_proc;
1276
1277 else if (source_type == FILE_ISREG) {
1278 if (target_type == FILE_NEXIST || target_type == FILE_ISREG) {
1279 rv = mf[mode][MODE_MOVE](target[2], ld.data[0].fullname);
1280
1281 } else if (target_type == FILE_ISDIR) {
1282 char* new_target = fullpath(target[2], ld.data[0].basename);
1283 free(target[2]); target[2] = new_target;
1284 rv = mf[mode][MODE_MOVE](target[2], ld.data[0].fullname);
1285
1286 } else {
1287 cmdftp_war(CMDFTP_WAR_TRG, "exists (not a regfile or a dir)");
1288 }
1289
1290 } else if (source_type == FILE_ISDIR) {
1291 if (target_type == FILE_NEXIST) {
1292 rv = mf[mode][MODE_MOVE](target[2], ld.data[0].fullname);
1293
1294 } else if (target_type == FILE_ISDIR) {
1295 rv = do_move_dir(target[2], ld.data[0].fullname);
1296
1297 } else {
1298 cmdftp_war(CMDFTP_WAR_TRG, "exists (not a dir)");
1299 }
1300
1301 } else {
1302 cmdftp_war(CMDFTP_WAR_SRC, "not a regular file or a dir");
1303 }
1304
1305 } else if (target_type == FILE_ISDIR) {
1306 /* source is multiple entries, target is an existing directory */
1307 rv = do_move_dir(target[2], source[2]);
1308
1309 } else {
1310 cmdftp_war(CMDFTP_WAR_TRG, "multiple source needs a dir target");
1311 }
1312
1313 end_proc:
1314 free_list(&ld);
1315 free_fn(source); free_fn(target);
1316
1317 return rv;
1318 }
1319
1320
do_ren(char * mask,char * from,char * to)1321 int do_ren(char* mask, char* from, char* to) {
1322 struct list_data ld;
1323 int i, rv;
1324 size_t len_from, len_to;
1325
1326 len_from = strlen(from); len_to = strlen(to);
1327
1328 init_list(&ld);
1329
1330 if (!mf[mode][MODE_FETCH_LIST](mask, &ld)) return 0;
1331 if (ld.count == 0) { cmdftp_war(CMDFTP_WAR_MASK, mask); return 1; }
1332
1333 rv = 1;
1334 for (i = 0; i < ld.count; i++) {
1335 char* match = strstr(ld.data[i].basename, from);
1336
1337 if (match) {
1338 char* new_name; size_t new_len;
1339
1340 new_len = strlen(ld.data[i].dirname) + 1 +
1341 strlen(ld.data[i].basename) - len_from + len_to;
1342 new_name = my_malloc(new_len + 1);
1343
1344 *match = 0;
1345
1346 sprintf(new_name, "%s/%s%s%s", ld.data[i].dirname, ld.data[i].basename,
1347 to, match + len_from);
1348
1349 if (!mf[mode][MODE_MOVE](new_name, ld.data[i].fullname))
1350 rv = 0;
1351
1352 free(new_name);
1353 }
1354 if (!rv) break;
1355 }
1356 free_list(&ld);
1357 return rv;
1358 }
1359
do_setcwd(int mode)1360 void do_setcwd(int mode) {
1361 /* cast between function pointer types is always ok, as soon as
1362 we remember to cast back to the right type before using one. */
1363 char* (*fun_getcwd)(void) = (char* (*)(void))mf[mode][MODE_GETCWD];
1364
1365 if (cwd[mode]) free(cwd[mode]);
1366
1367 if ((cwd[mode] = fun_getcwd()))
1368 cwd[mode] = my_strdup(cwd[mode]);
1369 else if ((cwd[mode] = fun_getcwd()))
1370 cwd[mode] = my_strdup(cwd[mode]);
1371 else
1372 cmdftp_err(CMDFTP_ERR_PWD, "");
1373 }
1374
1375 /**************************************/
1376 /* DISPATCHED COMMANDS */
1377 /**************************************/
1378
cmd_quit(char ** argv)1379 int cmd_quit(char** argv) {
1380 argv = 0;
1381 cmdftp_war(CMDFTP_WAR_BYE, "");
1382 cleanexit();
1383 return 0; /* never reached */
1384 }
1385
cmd_h(char ** argv)1386 int cmd_h(char** argv) {
1387 char* arg;
1388 unsigned int i;
1389 arg = argv[0];
1390
1391 if (*arg != 0) cmdftp_war(CMDFTP_WAR_IARG, arg);
1392
1393 for (i = 0; i < (sizeof(msg_help) / sizeof(char*)); i += 2)
1394 printf("%-14s %s\n", msg_help[i], msg_help[i + 1]);
1395 return 1;
1396 }
1397
cmd_l(char ** argv)1398 int cmd_l(char** argv) {
1399 char* arg;
1400 arg = argv[0];
1401
1402 if (*arg != 0) cmdftp_war(CMDFTP_WAR_IARG, arg);
1403
1404 mode = CMDFTP_LOCAL;
1405 return 1;
1406 }
1407
cmd_r(char ** argv)1408 int cmd_r(char** argv) {
1409 char* arg;
1410 arg = argv[0];
1411
1412 if (*arg != 0) cmdftp_war(CMDFTP_WAR_IARG, arg);
1413
1414 mode = CMDFTP_REMOTE;
1415 return 1;
1416 }
1417
cmd_pwd(char ** argv)1418 int cmd_pwd(char** argv) {
1419 char* arg;
1420 arg = argv[0];
1421
1422 if (*arg != 0) cmdftp_war(CMDFTP_WAR_IARG, arg);
1423
1424 printf("%s\n", cwd[mode]);
1425 return 1;
1426 }
1427
cmd_cd(char ** argv)1428 int cmd_cd(char** argv) {
1429 char* arg;
1430 int rv;
1431 arg = argv[0];
1432
1433 if (*arg == 0) { cmdftp_war(CMDFTP_WAR_MARG, ""); return 0; }
1434
1435 if ((rv = mf[mode][MODE_CHDIR](arg)) == -2)
1436 return 0;
1437
1438 do_setcwd(mode);
1439 return (rv == 0);
1440 }
1441
cmd_ren(char ** argv)1442 int cmd_ren(char** argv) {
1443 int rv;
1444
1445 if (*argv[0] == 0 || *argv[1] == 0)
1446 { cmdftp_war(CMDFTP_WAR_MARG, ""); return 0; }
1447
1448 rv = do_ren(argv[0], argv[1], argv[2]);
1449 return rv;
1450 }
1451
cmd_rm(char ** argv)1452 int cmd_rm(char** argv) {
1453 char* arg;
1454 struct list_data d;
1455 int i, rv = 0;
1456 arg = argv[0];
1457
1458 if (*arg == 0) { cmdftp_war(CMDFTP_WAR_MARG, ""); return 0; }
1459
1460 init_list(&d);
1461 if (!mf[mode][MODE_FETCH_LIST](arg, &d)) return 0;
1462 if (d.count == 0) { cmdftp_war(CMDFTP_WAR_MASK, arg); return 1; }
1463
1464 for (i = 0; i < d.count; i++) {
1465 int ft;
1466 if ((ft = mf[mode][MODE_FILE](d.data[i].fullname)) < 0 ||
1467 (ft == FILE_ISREG && !mf[mode][MODE_UNLINK](d.data[i].fullname)))
1468 break;
1469 if (i == d.count - 1) rv = 1;
1470 }
1471 free_list(&d);
1472 return rv;
1473 }
1474
cmd_ls(char ** argv)1475 int cmd_ls(char** argv) {
1476 char* arg;
1477 struct list_data d;
1478 int rv;
1479 arg = argv[0];
1480
1481 init_list(&d);
1482 if (!mf[mode][MODE_FETCH_LIST](arg, &d)) return 0;
1483 if (d.count == 0) { cmdftp_war(CMDFTP_WAR_MASK, arg); return 1; }
1484
1485 rv = ls(&d); free_list(&d); return rv;
1486 }
1487
cmd_dir(char ** argv)1488 int cmd_dir(char** argv) {
1489 char* arg;
1490 struct list_data d;
1491 int rv;
1492 arg = argv[0];
1493
1494 if (*arg != 0) cmdftp_war(CMDFTP_WAR_IARG, arg);
1495
1496 init_list(&d);
1497 if (!mf[mode][MODE_FETCH_PRETTY_LIST](&d)) return 0;
1498 if (d.count == 0) { cmdftp_war(CMDFTP_WAR_MASK, ""); return 1; }
1499
1500 rv = ls(&d); free_list(&d); return rv;
1501 }
1502
cmd_md(char ** argv)1503 int cmd_md(char** argv) {
1504 char* arg;
1505 arg = argv[0];
1506
1507 if (*arg == 0) { cmdftp_war(CMDFTP_WAR_MARG, ""); return 0; }
1508 return mf[mode][MODE_MKDIR](arg);
1509 }
1510
cmd_rd(char ** argv)1511 int cmd_rd(char** argv) {
1512 char* arg;
1513 arg = argv[0];
1514
1515 if (*arg == 0) { cmdftp_war(CMDFTP_WAR_MARG, ""); return 0; }
1516 return mf[mode][MODE_RMDIR](arg);
1517 }
1518
1519
cmd_p(char ** argv)1520 int cmd_p(char** argv) {
1521 char* arg;
1522 arg = argv[0];
1523
1524 if (*arg == 0) { cmdftp_war(CMDFTP_WAR_MARG, ""); return 0; }
1525 return mf[mode][MODE_PRINT](arg);
1526 }
1527
cmd_e(char ** argv)1528 int cmd_e(char** argv) {
1529 char* arg;
1530 arg = argv[0];
1531
1532 if (*arg == 0) { cmdftp_war(CMDFTP_WAR_MARG, ""); return 0; }
1533
1534 if (!env[CMDFTP_ENV_EDITOR])
1535 { cmdftp_war(CMDFTP_WAR_MENV, "EDITOR"); return 0; }
1536
1537 return mf[mode][MODE_EDIT](arg);
1538 }
1539
1540
1541
1542 /**************************************/
1543 /* UPLOAD AND DOWNLOAD */
1544 /**************************************/
1545
1546
cmd_u(char ** paths)1547 int cmd_u(char** paths) {
1548 char* source_mask;
1549 int rv; int tt; struct list_data ld;
1550
1551 if (*paths[0] == 0 || *paths[1] == 0)
1552 { cmdftp_war(CMDFTP_WAR_MARG, ""); return 0; }
1553
1554 if ((tt = remote_file(paths[1])) < 0)
1555 return 0;
1556
1557 if (tt != FILE_NEXIST && tt != FILE_ISDIR)
1558 { cmdftp_war(CMDFTP_WAR_TRG, "exists (not a dir)"); return 0; }
1559
1560 source_mask = my_strdup(paths[0]);
1561
1562 init_list(&ld);
1563 if (!local_fetch_list(paths[0], &ld)) return 0;
1564
1565 if (ld.count == 0)
1566 { cmdftp_war(CMDFTP_WAR_MASK, paths[0]); return 1; }
1567
1568 if (ld.count == 1) {
1569 int st = local_file(ld.data[0].fullname);
1570 if (st == FILE_ISDIR && tt == FILE_NEXIST) {
1571 free(source_mask);
1572 source_mask = fullpath(paths[0], "*");
1573 }
1574 }
1575
1576 free_list(&ld);
1577 rv = u_aux(paths[1], source_mask);
1578 free(source_mask);
1579 return rv;
1580 }
1581
u_aux(char * target_dir,char * source_mask)1582 int u_aux(char* target_dir, char* source_mask) {
1583 struct list_data ld;
1584 char* target[3];
1585 int i, rv;
1586
1587 if ((rv = remote_file(target_dir)) == FILE_NEXIST) {
1588 if (!remote_mkdir(target_dir)) return 0;
1589
1590 } else if (rv != FILE_ISDIR) {
1591 return 0;
1592 }
1593
1594 init_list(&ld);
1595
1596 if (!local_fetch_list(source_mask, &ld)) return 0;
1597 if (ld.count == 0) return 1;
1598
1599 canonized_fn(target, target_dir);
1600
1601 rv = 1;
1602 for (i = 0; i < ld.count; i++) {
1603 int file_type = local_file(ld.data[i].fullname);
1604
1605 if (file_type == FILE_ISREG) {
1606 FILE* f = fopen(ld.data[i].fullname, "rb");
1607 if (!f)
1608 cmdftp_war(CMDFTP_WAR_OPEN, ld.data[i].fullname);
1609 else {
1610 char* fn;
1611 fn = fullpath(target[2], ld.data[i].basename);
1612 rv = upload(fn, f);
1613 fclose(f); free(fn);
1614 }
1615 } else if (file_type == FILE_ISDIR) {
1616 char* tmp_src, *tmp_trg;
1617
1618 tmp_src = fullpath(ld.data[i].fullname, "*");
1619 tmp_trg = fullpath(target[2], ld.data[i].basename);
1620
1621 rv = u_aux(tmp_trg, tmp_src);
1622 free(tmp_trg); free(tmp_src);
1623
1624 } else {
1625 cmdftp_war(CMDFTP_WAR_SKIP, ld.data[i].fullname);
1626 }
1627 if (!rv) break;
1628 }
1629
1630 free_list(&ld); free_fn(target);
1631 return rv;
1632 }
1633
upload(char * target,FILE * source)1634 int upload(char* target, FILE* source) {
1635 off_t start_pos, total_size;
1636 char* ctarget; char* op;
1637 size_t ctarget_len;
1638 int port, rv, cmdres;
1639
1640 rv = 0;
1641
1642 op = "Uploading";
1643 ctarget = clean_fn(target); ctarget_len = strlen(ctarget);
1644
1645 start_transfer:
1646
1647 if (!send_command("TYPE I", 0)) return 0;
1648 if ((total_size = local_size(source)) < 0) return 0;
1649
1650 start_pos = 0;
1651
1652 if (!(port = getport())) return 0;
1653 if (!(cmdftp_data = cmdftp_connect(port, 4))) return 0;
1654
1655 snprintf(cmd_buffer, CMD_BUF_SIZE, "STOR %s", target);
1656 if ((cmdres = send_command(cmd_buffer, 0)) != 150 &&
1657 cmdres != 125 && cmdres != 226)
1658 { close(cmdftp_data); return 0; }
1659
1660 if (!o.q)
1661 print_progress(op, ctarget, ctarget_len, start_pos, total_size);
1662
1663 transfer_interrupted = TRAN_INTR_NO;
1664
1665 while (start_pos < total_size) {
1666 int toread, bytes, written;
1667 toread = ((total_size - start_pos) < o.b) ?
1668 total_size - start_pos : o.b;
1669
1670 if (!(bytes = fread(buffer, 1, toread, source)))
1671 break;
1672 if ((written = my_raw_write(buffer, bytes, cmdftp_data)) != bytes)
1673 break;
1674
1675 start_pos += bytes;
1676
1677 if (!o.q)
1678 print_progress(op, ctarget, ctarget_len, start_pos, total_size);
1679 }
1680
1681 if (!o.q) fputc('\n', stdout);
1682
1683 close(cmdftp_data);
1684
1685 if (start_pos < total_size) {
1686 if (ferror(source)) cmdftp_war(CMDFTP_WAR_READ, basename(target));
1687 else if TRANSFER_INTERRUPTED_CHECK("data transfer", 1)
1688 else { cmdftp_err(CMDFTP_ERR_UNER, strerror(errno)); }
1689
1690 } else {
1691 rv = 1;
1692 if (cmdres != 226 && !recv_confirm())
1693 cmdftp_war(CMDFTP_WAR_NOCT, "");
1694 }
1695
1696 return rv;
1697 }
1698
cmd_d(char ** argv)1699 int cmd_d(char** argv) {
1700 return do_d(argv, 0);
1701 }
1702
cmd_dr(char ** argv)1703 int cmd_dr(char** argv) {
1704 return do_d(argv, 1);
1705 }
1706
do_d(char ** paths,int resume)1707 int do_d(char** paths, int resume) {
1708 char* source_mask;
1709 int rv; int tt; struct list_data ld;
1710
1711 if (*paths[0] == 0 || *paths[1] == 0)
1712 { cmdftp_war(CMDFTP_WAR_MARG, ""); return 0; }
1713
1714 if ((tt = local_file(paths[1])) != FILE_NEXIST && tt != FILE_ISDIR)
1715 { cmdftp_war(CMDFTP_WAR_TRG, "exists (not a dir)"); return 0; }
1716
1717 source_mask = my_strdup(paths[0]);
1718
1719 init_list(&ld);
1720 if (!remote_fetch_list(paths[0], &ld)) return 0;
1721
1722 if (ld.count == 0)
1723 { cmdftp_war(CMDFTP_WAR_MASK, paths[0]); return 1; }
1724
1725 if (ld.count == 1) {
1726 int st = remote_file(ld.data[0].fullname);
1727 if (st < 0) { free_list(&ld); return 0; }
1728 if (st == FILE_ISDIR && tt == FILE_NEXIST) {
1729 free(source_mask);
1730 source_mask = fullpath(paths[0], "*");
1731 }
1732 }
1733
1734 free_list(&ld);
1735 rv = d_aux(paths[1], source_mask, resume);
1736 free(source_mask);
1737 return rv;
1738 }
1739
d_aux(char * target_dir,char * source_mask,int resume)1740 int d_aux(char* target_dir, char* source_mask, int resume) {
1741 struct list_data ld;
1742 char* target[3];
1743 int i, rv;
1744
1745 if ((rv = local_file(target_dir)) == FILE_NEXIST) {
1746 if (!local_mkdir(target_dir)) return 0;
1747
1748 } else if (rv != FILE_ISDIR) {
1749 return 0;
1750 }
1751
1752 init_list(&ld);
1753
1754 if (!remote_fetch_list(source_mask, &ld)) return 0;
1755 if (ld.count == 0) return 1;
1756
1757 canonized_fn(target, target_dir);
1758
1759 rv = 1;
1760 for (i = 0; i < ld.count; i++) {
1761 int file_type;
1762 if ((file_type = remote_file(ld.data[i].fullname)) < 0) {
1763 rv = 0;
1764
1765 } else if (file_type == FILE_ISREG) {
1766 char* fn; FILE* f;
1767
1768 fn = fullpath(target[2], ld.data[i].basename);
1769 if (!(f = fopen(fn, resume ? "ab" : "wb")))
1770 cmdftp_war(CMDFTP_WAR_OPEN, fn);
1771 else {
1772 rv = download(f, ld.data[i].fullname);
1773 fclose(f);
1774 }
1775 free(fn);
1776
1777 } else if (file_type == FILE_ISDIR) {
1778 char* tmp_src, *tmp_trg;
1779
1780 tmp_src = fullpath(ld.data[i].fullname, "*");
1781 tmp_trg = fullpath(target[2], ld.data[i].basename);
1782 rv = d_aux(tmp_trg, tmp_src, resume);
1783 free(tmp_trg); free(tmp_src);
1784
1785 } else {
1786 cmdftp_war(CMDFTP_WAR_SKIP, ld.data[i].fullname);
1787 }
1788
1789 if (!rv) break;
1790 }
1791
1792 free_list(&ld); free_fn(target);
1793 return rv;
1794 }
1795
download(FILE * target,char * source)1796 int download(FILE* target, char* source) {
1797 off_t start_pos, total_size;
1798 char* csource; char* op;
1799 size_t csource_len;
1800 int answer, port, rv, cmdres;
1801
1802 rv = 0;
1803
1804 op = "Downloading";
1805 csource = clean_fn(source); csource_len = strlen(csource);
1806
1807 start_transfer:
1808
1809 if (!send_command("TYPE I", 0)) return 0;
1810 if ((total_size = remote_size(source)) < 0) return 0;
1811 if ((start_pos = ftello(target)) == -1) return 0;
1812
1813 if (start_pos > 0) {
1814 snprintf(cmd_buffer, CMD_BUF_SIZE, "REST " OFF_FMT, start_pos);
1815 if ((answer = send_command(cmd_buffer, 0)) != 350) {
1816 cmdftp_war(CMDFTP_WAR_REST, "");
1817 fseeko(target, 0, SEEK_SET); start_pos = 0;
1818 }
1819 }
1820
1821 if (!(port = getport())) return 0;
1822 if (!(cmdftp_data = cmdftp_connect(port, 4))) return 0;
1823
1824 snprintf(cmd_buffer, CMD_BUF_SIZE, "RETR %s", source);
1825 if ((cmdres = send_command(cmd_buffer, 0)) != 150 &&
1826 cmdres != 125 && cmdres != 226)
1827 { close(cmdftp_data); return 0; }
1828
1829 if (!o.q)
1830 print_progress(op, csource, csource_len, start_pos, total_size);
1831
1832 transfer_interrupted = TRAN_INTR_NO;
1833
1834 while (start_pos < total_size) {
1835 int bytes, toread, written;
1836 toread = ((total_size - start_pos) < o.b) ?
1837 total_size - start_pos : o.b;
1838
1839 if ((bytes = my_raw_read(buffer, toread, cmdftp_data)) <= 0) break;
1840 if ((written = fwrite(buffer, 1, bytes, target)) != bytes) break;
1841
1842 start_pos += bytes;
1843
1844 if (!o.q)
1845 print_progress(op, csource, csource_len, start_pos, total_size);
1846 }
1847
1848 if (!o.q) fputc('\n', stdout);
1849
1850 close(cmdftp_data);
1851
1852 if (start_pos < total_size) {
1853 if (ferror(target))
1854 cmdftp_war(CMDFTP_WAR_WRIT, basename(source));
1855 else if TRANSFER_INTERRUPTED_CHECK("data transfer", 1)
1856 else { cmdftp_err(CMDFTP_ERR_UNER, strerror(errno)); }
1857
1858 } else {
1859 rv = 1;
1860 if (cmdres != 226 && !recv_confirm())
1861 cmdftp_war(CMDFTP_WAR_NOCT, "");
1862 }
1863
1864 return rv;
1865 }
1866
1867 /***********/
1868 /* listing */
1869 /***********/
1870
ls(struct list_data * d)1871 int ls(struct list_data* d) {
1872 FILE* target; int pipe_fd[2];
1873 int i, rv = 0;
1874
1875 if (env[CMDFTP_ENV_PAGER]) {
1876 if (pipe(pipe_fd) < 0) return 0;
1877 if (!(target = fdopen(pipe_fd[1], "w")))
1878 { close(pipe_fd[0]); close(pipe_fd[1]); return 0; }
1879 } else target = stdout;
1880
1881 for (i = 0; i < d->count; i++)
1882 fprintf(target, "%s\n", d->data[i].fullname);
1883
1884 if (!env[CMDFTP_ENV_PAGER]) return 1;
1885
1886 fclose(target);
1887 rv = cmdftp_execute(env[CMDFTP_ENV_PAGER], 0, pipe_fd[0], -1);
1888
1889 close(pipe_fd[0]);
1890 return rv;
1891 }
1892
1893 /*****************************/
1894 /* NETWORK RELATED FUNCTIONS */
1895 /*****************************/
1896
1897
cmdftp_connect(int port,int tos)1898 int cmdftp_connect(int port, int tos) {
1899 struct sockaddr_in address; int s;
1900 socklen_t slen; slen = sizeof(tos);
1901 memset(&address, 0, sizeof(address));
1902
1903 if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0 ||
1904 setsockopt(s, IPPROTO_IP, IP_TOS, &tos, slen) < 0)
1905 return 0;
1906
1907 address.sin_family = AF_INET;
1908 address.sin_port = htons((unsigned short)port);
1909
1910 if (!server)
1911 if (!(server = gethostbyname(o.hostname))) return 0;
1912
1913 memcpy(&address.sin_addr.s_addr, server->h_addr, server->h_length);
1914
1915 if (connect(s, (struct sockaddr*) &address, sizeof(address)) == -1)
1916 return 0;
1917
1918 return s;
1919 }
1920
cmdftp_reconnect(void)1921 void cmdftp_reconnect(void) {
1922 if (!(cmdftp_control = cmdftp_connect(o.p, 8)))
1923 cmdftp_err(CMDFTP_ERR_CONN, "");
1924 greeting();
1925 if (!logging_in) {
1926 if (!login(user, pass)) cmdftp_err(CMDFTP_ERR_LGIN, "");
1927 if (cwd[CMDFTP_LOCAL])
1928 do_home(CMDFTP_LOCAL);
1929 if (cwd[CMDFTP_REMOTE])
1930 do_home(CMDFTP_REMOTE);
1931 }
1932 }
1933
my_raw_read(char * buf,size_t n,int sc)1934 ssize_t my_raw_read(char* buf, size_t n, int sc) {
1935 ssize_t rv;
1936
1937 alarm(o.t);
1938 rv = recv(sc, buf, n, 0);
1939 alarm(0);
1940
1941 return rv;
1942 }
1943
my_raw_write(char * buf,size_t n,int sc)1944 ssize_t my_raw_write(char* buf, size_t n, int sc) {
1945 ssize_t rv;
1946
1947 alarm(o.t);
1948 rv = send(sc, buf, n, 0);
1949 alarm(0);
1950
1951 return rv;
1952 }
1953
send_command(char * cmd,char suppress_err)1954 int send_command(char* cmd, char suppress_err) {
1955 char* s; size_t len, rv = 0;
1956
1957 s = my_malloc((len = strlen(cmd) + 2) + 1);
1958 sprintf(s, "%s\r\n", cmd);
1959
1960 start_transfer:
1961 transfer_interrupted = TRAN_INTR_NO;
1962 if (o.D)
1963 fprintf(stderr, "DEBUG command: %s\n", s);
1964
1965 if (my_raw_write(s, len, cmdftp_control) < 0) {
1966 s[len - 2] = 0;
1967 if TRANSFER_INTERRUPTED_CHECK(s, 0)
1968 } else {
1969 s[len - 2] = 0;
1970 if (!(rv = recv_answer(0, suppress_err))) {
1971 if TRANSFER_INTERRUPTED_CHECK(s, 1)
1972 }
1973 }
1974 free(s);
1975 return rv;
1976 }
1977
send_command_and_fetch(char * cmd,struct line_data * d,char suppress_err)1978 int send_command_and_fetch(char* cmd, struct line_data* d,
1979 char suppress_err)
1980 {
1981 char* s; size_t len, rv = 0;
1982
1983 init_lines(d);
1984
1985 s = my_malloc((len = strlen(cmd) + 2) + 1);
1986 sprintf(s, "%s\r\n", cmd);
1987
1988 start_transfer:
1989 transfer_interrupted = TRAN_INTR_NO;
1990 if (o.D)
1991 fprintf(stderr, "DEBUG command: %s\n", s);
1992
1993 if (my_raw_write(s, len, cmdftp_control) < 0) {
1994 s[len - 2] = 0;
1995 if TRANSFER_INTERRUPTED_CHECK(s, 0)
1996 } else {
1997 s[len - 2] = 0;
1998 if (!(rv = recv_answer(d, suppress_err))) {
1999 if TRANSFER_INTERRUPTED_CHECK(s, 1)
2000 }
2001 }
2002 free(s);
2003 return rv;
2004 }
2005
reset_cmd_buffer(void)2006 void reset_cmd_buffer(void) {
2007 cmd_ptr = cmd_buffer + CMD_BUF_SIZE;
2008 }
2009
recv_line_next(char * from)2010 static char *recv_line_next(char *from)
2011 {
2012 char *new_ptr;
2013
2014 if ((new_ptr = strchr(from + 1, '\n'))) {
2015 if (new_ptr[-1] == '\r')
2016 new_ptr--;
2017
2018 } else if (!(new_ptr = strchr(from + 1, '\r'))) {
2019 new_ptr = cmd_buffer + CMD_BUF_SIZE;
2020 }
2021 return new_ptr;
2022 }
2023
recv_line(int sc)2024 char* recv_line(int sc) {
2025 char* new_ptr;
2026 ptrdiff_t n; size_t len;
2027
2028 if (*cmd_ptr == 0) {
2029 ssize_t bytes = my_raw_read(cmd_buffer, CMD_BUF_SIZE, sc);
2030 if (bytes <= 0)
2031 return 0;
2032
2033 cmd_buffer[bytes] = 0;
2034 cmd_ptr = cmd_buffer;
2035 if (*cmd_ptr == '\n')
2036 cmd_ptr++;
2037 }
2038
2039 new_ptr = recv_line_next(cmd_ptr + 1);
2040 n = new_ptr - cmd_ptr;
2041
2042 strncpy(cmd_line, cmd_ptr, n); cmd_line[n] = 0;
2043 cmd_ptr = new_ptr;
2044
2045 if (cmd_ptr == cmd_buffer + CMD_BUF_SIZE) {
2046 ssize_t bytes = my_raw_read(cmd_buffer, CMD_BUF_SIZE, sc);
2047 if (bytes <= 0) return 0;
2048 cmd_buffer[bytes] = 0;
2049
2050 cmd_ptr = cmd_buffer;
2051
2052 new_ptr = recv_line_next(cmd_ptr + 1);
2053 n = new_ptr - cmd_ptr; len = strlen(cmd_line);
2054
2055 strncat(cmd_line, cmd_ptr, n); cmd_line[len + n] = 0;
2056 cmd_ptr = new_ptr;
2057 }
2058
2059 if (*cmd_ptr == '\r' && cmd_ptr[1] == '\n')
2060 cmd_ptr += 2;
2061 else if (*cmd_ptr == '\n')
2062 cmd_ptr++;
2063
2064 return cmd_line;
2065 }
2066
recv_confirm(void)2067 int recv_confirm(void) {
2068 int rv = 0;
2069
2070 if (recv_answer(0, 0) == 226) rv = 1;
2071 else if TRANSFER_INTERRUPTED_CHECK("recv_confirm", 1);
2072
2073 start_transfer:
2074 return rv;
2075 }
2076
recv_answer(struct line_data * d,char suppress_err)2077 int recv_answer(struct line_data* d, char suppress_err) {
2078 int len; char* answer; int code;
2079
2080 reset_cmd_buffer();
2081
2082 answer = recv_line(cmdftp_control);
2083 if (!answer || (len = strlen(answer)) < 4)
2084 return 0;
2085
2086 if (o.D)
2087 fprintf(stderr, "DEBUG answer: %s\n", answer);
2088
2089 while (*cmd_ptr != 0 || len < 4 ||
2090 !isdigit(answer[0]) || !isdigit(answer[1]) || !isdigit(answer[2]) ||
2091 answer[3] == '-') {
2092
2093 if (!(answer = recv_line(cmdftp_control)))
2094 return 0;
2095
2096 len = strlen(answer);
2097
2098 if (d && *cmd_ptr != 0)
2099 store_line(answer, d);
2100 }
2101
2102 if (sscanf(answer, "%i", &code) != 1)
2103 return 0;
2104
2105 if (code >= 400 && !suppress_err) {
2106 cmdftp_war(CMDFTP_WAR_RERR, answer);
2107 }
2108
2109 return code;
2110 }
2111
getport()2112 int getport() {
2113 unsigned int b[6];
2114 int i, answer, len; char* port_str;
2115
2116 if ((answer = send_command("PASV", 0)) != 227) return 0;
2117
2118 port_str = cmd_buffer + 4;
2119 len = strlen(port_str += 4);
2120
2121 for (i = 0; i < len; i++)
2122 if (!isdigit(port_str[i])) port_str[i] = ' ';
2123
2124 if (sscanf(port_str, "%u %u %u %u %u %u",
2125 b, b + 1, b + 2, b + 3, b + 4, b + 5) != 6)
2126 return 0;
2127
2128 return b[4] * 256 + b[5];
2129 }
2130
2131
2132
2133 /*******************************/
2134 /* MEMORY AND STRING FUNCTIONS */
2135 /*******************************/
2136
2137
my_malloc(size_t s)2138 void* my_malloc(size_t s) {
2139 void* rv;
2140 if (!(rv = malloc(s))) cmdftp_err(CMDFTP_ERR_HEAP, "");
2141 return rv;
2142 }
2143
my_realloc(void * ptr,size_t s)2144 void* my_realloc(void* ptr, size_t s) {
2145 void* rv;
2146 if (!(rv = realloc(ptr, s))) cmdftp_err(CMDFTP_ERR_HEAP, "");
2147 return rv;
2148 }
2149
2150 #if !HAVE_STRDUP
2151
strdup(char * s)2152 char* strdup(char* s) {
2153 char* rv = malloc(strlen(s) + 1);
2154 strcpy(rv, s);
2155 return rv;
2156 }
2157
2158 #endif
2159
my_strdup(char * s)2160 char* my_strdup(char* s) {
2161 char* rv;
2162 if (!(rv = strdup(s))) cmdftp_err(CMDFTP_ERR_HEAP, "");
2163 return rv;
2164 }
2165
2166
2167 /***************************/
2168 /* OTHER UTILITY FUNCTIONS */
2169 /***************************/
2170
cleanexit(void)2171 void cleanexit(void) {
2172 exit(fclose(stdout) == 0 ? 0 : -1);
2173 }
2174
split_cmd(char * cmd,char ** argv)2175 void split_cmd(char* cmd, char** argv) {
2176 size_t len; int i;
2177 len = strlen(cmd);
2178
2179 for (i = 0; i < 4; i++) {
2180 argv[i] = my_malloc(len + 1);
2181 while (isspace(*cmd)) cmd++;
2182 read_token(&cmd, argv[i]);
2183 }
2184 }
2185
free_cmd(char ** argv)2186 void free_cmd(char** argv) {
2187 int i;
2188
2189 for (i = 0; i < 4; i++) {
2190 if (argv[i]) {
2191 free(argv[i]); argv[i] = 0;
2192 }
2193 }
2194 }
2195
str_binsearch(char * key)2196 int str_binsearch(char* key) {
2197 int low, high, mid, chk;
2198
2199 low = 0; high = N_COMMANDS - 1;
2200
2201 while (low <= high) {
2202 mid = (low + high) / 2;
2203 if ((chk = strcmp(key, commands[mid])) > 0) low = mid + 1;
2204 else if (chk < 0) high = mid - 1;
2205 else return mid;
2206 }
2207
2208 return -1;
2209 }
2210
readline(int enable_tab,int enable_echo)2211 char* readline(int enable_tab, int enable_echo) {
2212 char* ptr; int c;
2213
2214 ptr = cmd_userinput; memset(cmd_userinput, 0, sizeof(cmd_userinput));
2215 cmdftp_raw_mode();
2216
2217 while (1) {
2218 c = fgetc(stdin);
2219 if (c == '\n')
2220 { *ptr = 0; fputc('\n', stdout); break; }
2221 else if ((c == '\t') && (enable_tab) &&
2222 (ptr - cmd_userinput) < (CMD_BUF_SIZE - 10))
2223 { readline_tab(); ptr = cmd_userinput + strlen(cmd_userinput); }
2224 else if (c == cmdftp_termios.c_cc[VERASE] || c == 8)
2225 readline_bs(&ptr, enable_echo);
2226 else if ((c >= 32) && (c <= 127)) {
2227 if ((ptr - cmd_userinput) < (CMD_BUF_SIZE - 10))
2228 { *ptr++ = c; if (enable_echo) fputc(c, stdout); }
2229 }
2230 else if (c == EOF) break;
2231 }
2232 cmdftp_canon_mode();
2233 return (c != EOF) ? my_strdup(cmd_userinput) : 0;
2234 }
2235
readline_bs(char ** ptr,int enable_echo)2236 void readline_bs(char** ptr, int enable_echo) {
2237 if (*ptr > cmd_userinput) {
2238 *(--(*ptr)) = 0;
2239 if (enable_echo) fprintf(stdout, "\b \b");
2240 }
2241 }
2242
readline_tab(void)2243 void readline_tab(void) {
2244 char* argv[4];
2245 char* mask, *completion; struct list_data ld;
2246 int c, i, j, lc_mode, len, len_buffer;
2247
2248 split_cmd(cmd_userinput, argv);
2249 if (!*argv[1])
2250 { free_cmd(argv); return; }
2251
2252 mask = 0;
2253 c = i = j = len = len_buffer = 0;
2254
2255 init_list(&ld);
2256
2257 if (strcmp(argv[0], "d") == 0 || strcmp(argv[0], "dr") == 0) {
2258 lc_mode = *argv[2] ? CMDFTP_LOCAL : CMDFTP_REMOTE;
2259 } else if (strcmp(argv[0], "u") == 0) {
2260 lc_mode = *argv[2] ? CMDFTP_REMOTE : CMDFTP_LOCAL;
2261 } else lc_mode = mode;
2262
2263 if (*(mask = argv[2]) != '\0') {
2264 char* tmp; tmp = escape_string(argv[1]);
2265 free(argv[1]); argv[1] = tmp;
2266
2267 } else {
2268 mask = argv[1];
2269 }
2270
2271 strcat(mask, "*");
2272 len_buffer = strlen(mask) - 1;
2273
2274 if (!mf[lc_mode][MODE_FETCH_LIST](mask, &ld) || ld.count == 0) goto endtab;
2275 escape_list(&ld);
2276 len = strlen(ld.data[0].escaped_fullname);
2277
2278 for (i = 0; i < len; i++) {
2279 c = ld.data[0].escaped_fullname[i];
2280 for (j = 1; j < ld.count; j++) {
2281 if (ld.data[j].escaped_fullname[i] != c) goto endcommon;
2282 }
2283 }
2284
2285 endcommon:
2286
2287 endtab:
2288 if (ld.count) {
2289 completion = my_malloc(i + 2);
2290 strncpy(completion, ld.data[0].escaped_fullname, i);
2291
2292 if (ld.count == 1 &&
2293 mf[lc_mode][MODE_FILE](ld.data[0].fullname) == FILE_ISDIR)
2294 completion[i++] = '/';
2295
2296 completion[i] = 0;
2297
2298 } else {
2299 completion = my_malloc(len_buffer + 1);
2300 strncpy(completion, mask, len_buffer);
2301 completion[len_buffer] = 0;
2302 }
2303
2304 snprintf(cmd_userinput, CMD_BUF_SIZE, "%s%s%s%s%s", argv[0],
2305 *argv[1] ? " " : "", *argv[2] ? argv[1] : "", *argv[2] ? " " : "",
2306 completion);
2307 free(completion);
2308
2309 if (ld.count > 1) {
2310 fputc('\n', stdout);
2311 for (j = 0; j < ld.count; j++) {
2312 printf("%s\n", ld.data[j].escaped_fullname);
2313 }
2314 } else {
2315 fputc('\r', stdout);
2316 }
2317
2318 print_prompt(); printf("%s", cmd_userinput);
2319
2320 free_list(&ld);
2321 free_cmd(argv);
2322 }
2323
cmdftp_pwd_start(void)2324 void cmdftp_pwd_start(void) {
2325 cmdftp_termios.c_lflag &= ~ECHO; tcsetattr(0, TCSANOW, &cmdftp_termios);
2326 }
2327
cmdftp_pwd_end(void)2328 void cmdftp_pwd_end(void) {
2329 cmdftp_termios.c_lflag |= ECHO; tcsetattr(0, TCSANOW, &cmdftp_termios);
2330 }
2331
cmdftp_raw_mode(void)2332 void cmdftp_raw_mode(void) {
2333 cmdftp_termios.c_lflag &= ~ICANON;
2334 cmdftp_termios.c_lflag &= ~ECHO;
2335 cmdftp_termios.c_cc[VMIN] = 1;
2336 cmdftp_termios.c_cc[VTIME] = 0;
2337 tcsetattr(0, TCSANOW, &cmdftp_termios);
2338 }
2339
cmdftp_canon_mode(void)2340 void cmdftp_canon_mode(void) {
2341 cmdftp_termios.c_lflag |= ICANON; cmdftp_termios.c_lflag |= ECHO;
2342 tcsetattr(0, TCSANOW, &cmdftp_termios);
2343 }
2344
cmdftp_execute(char * p1,char * p2,int read_fd,int write_fd)2345 int cmdftp_execute(char* p1, char* p2, int read_fd, int write_fd) {
2346 char** argv = 0; char* argptr;
2347 pid_t pid; int status, rv, count;
2348 rv = count = 0;
2349
2350 argptr = p1 = my_strdup(p1);
2351
2352 do {
2353 argv = my_realloc(argv, sizeof(char*) * (count + 1));
2354 argv[count++] = argptr;
2355 argptr = strchr(argptr + 1, ' ');
2356
2357 if (argptr) *argptr++ = 0;
2358
2359 } while (argptr);
2360
2361 count += (p2 ? 2 : 1); argv = my_realloc(argv, sizeof(char*) * count);
2362 argv[count - 1] = 0; if (p2) argv[count - 2] = p2;
2363
2364 if ((pid = fork()) < 0) goto end_proc;
2365 else if (pid > 0) {
2366 if (waitpid(pid, &status, 0) > 0) {
2367 if (WIFEXITED(status) && (WEXITSTATUS(status) == 0)) rv = 1;
2368 }
2369 } else {
2370 if (read_fd != -1) { close(0); dup(read_fd); close(read_fd); }
2371 if (write_fd != -1) { close(1); dup(write_fd); close(write_fd); }
2372 execvp(argv[0], argv);
2373 exit(-1);
2374 }
2375
2376 end_proc:
2377 free(p1); free(argv);
2378 return rv;
2379 }
2380
2381 /* temporary file handling */
2382
2383 char* cmdftp_temp_fn;
2384 int cmdftp_temp_fn_len;
2385
is_good_tmpdir(char * candidate)2386 int is_good_tmpdir(char* candidate) {
2387 struct stat buf;
2388
2389 if (stat(candidate, &buf) != 0)
2390 return 0;
2391
2392 return (S_ISDIR(buf.st_mode) && (buf.st_mode & S_IRWXU));
2393 }
2394
init_temp(void)2395 void init_temp(void) {
2396 if (env[CMDFTP_ENV_TMPDIR] && is_good_tmpdir(env[CMDFTP_ENV_TMPDIR])) {
2397 cmdftp_temp_fn = fullpath(env[CMDFTP_ENV_TMPDIR], "cmdftpXXXXXX");
2398
2399 #ifdef P_tmpdir
2400 } else if (is_good_tmpdir(P_tmpdir)) {
2401 cmdftp_temp_fn = fullpath(P_tmpdir, "cmdftpXXXXXX");
2402 #endif
2403
2404 } else if (is_good_tmpdir("/tmp")) {
2405 cmdftp_temp_fn = fullpath("/tmp", "cmdftpXXXXXX");
2406
2407 } else {
2408 cmdftp_err(CMDFTP_ERR_TMPD, "");
2409 }
2410
2411 cmdftp_temp_fn_len = strlen(cmdftp_temp_fn);
2412 }
2413
2414 #if !HAVE_MKSTEMP
2415
mkstemp(char * template)2416 int mkstemp(char* template) {
2417 #define MKSTEMP_ATTEMPTS 10
2418 int rv, attempt; size_t len; char *ptr; off_t n;
2419 char ascii[] =
2420 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
2421
2422 len = strlen(template); /* must be >= 6 */
2423 ptr = template + len - 6; /* point to XXXXXX part */
2424 n = *((off_t*)o.hostname); /* a crazy thing */
2425 if (n < 0) n = -n;
2426
2427 for (attempt = 0; attempt < MKSTEMP_ATTEMPTS; attempt++) {
2428 int i;
2429 for (i = 0; i < 6; i++) {
2430 ptr[i] = ascii[n % (sizeof(ascii) - 1)]; n /= sizeof(ascii) - 1;
2431 }
2432 rv = open(template, O_RDWR | O_CREAT | O_EXCL, 0600);
2433 if (rv >= 0 || errno != EEXIST) return rv;
2434 }
2435 errno = EEXIST;
2436 return -1;
2437 #undef MKSTEMP_ATTEMPTS
2438 }
2439
2440 #endif
2441
cmdftp_temp(char ** fn)2442 FILE* cmdftp_temp(char** fn) {
2443 int i; FILE* rv;
2444
2445 *fn = 0;
2446
2447 for (i = cmdftp_temp_fn_len - 6; i < cmdftp_temp_fn_len; i++)
2448 cmdftp_temp_fn[i] = 'X';
2449
2450 if ((i = mkstemp(cmdftp_temp_fn)) < 0) return 0;
2451 if (!(rv = fdopen(i, "r+"))) return 0;
2452 *fn = my_strdup(cmdftp_temp_fn);
2453 return rv;
2454 }
2455
canonized_fn(char * des[3],char * arg)2456 void canonized_fn(char* des[3], char* arg) {
2457 char* tmpmask;
2458
2459 tmpmask = my_strdup(arg);
2460 des[0] = my_strdup(dirname(tmpmask));
2461 free(tmpmask);
2462
2463 tmpmask = my_strdup(arg);
2464 des[1] = my_strdup(basename(tmpmask));
2465 free(tmpmask);
2466
2467 des[2] = fullpath(des[0], des[1]);
2468 }
2469
2470 /*****************************************************************************/
2471
free_fn(char * des[3])2472 void free_fn(char* des[3]) {
2473 int i;
2474 for (i = 0; i < 3; i++) free(des[i]);
2475 }
2476
2477 /*****************************************************************************/
2478
clean_fn(char * fn)2479 char* clean_fn(char* fn) {
2480 if (strncmp(fn, "./", 2) == 0) return fn + 2;
2481 else if(strncmp(fn, "//", 2) == 0) return fn + 1;
2482 else return fn;
2483 }
2484
2485 /*****************************************************************************/
2486
fullpath(char * s,char * q)2487 char* fullpath(char* s, char* q) {
2488 char* rv = my_malloc(strlen(s) + strlen(q) + 2);
2489 sprintf(rv, "%s/%s", s, q);
2490 return rv;
2491 }
2492
2493 /********************/
2494 /* SIGNAL FUNCTIONS */
2495 /********************/
2496
init_signals(void)2497 void init_signals(void) {
2498 memset(&sa, 0, sizeof(sa));
2499 sa.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sa, 0);
2500 sa.sa_handler = &handler_INT; sigaction(SIGINT, &sa, 0);
2501 sa.sa_handler = &handler_ALRM; sigaction(SIGALRM, &sa, 0);
2502 }
2503
handler_INT(int i)2504 void handler_INT(int i) {
2505 i = 0;
2506
2507 if (transfer_interrupted == TRAN_INTR_INT) {
2508 cmdftp_err(CMDFTP_ERR_INTR, "");
2509
2510 } else {
2511 transfer_interrupted = TRAN_INTR_INT;
2512 }
2513 }
2514
handler_ALRM(int i)2515 void handler_ALRM(int i) {
2516 i = 0;
2517 transfer_interrupted = TRAN_INTR_ALRM;
2518 }
2519
2520 /******************************/
2521 /* STRUCT LINE_DATA FUNCTIONS */
2522 /******************************/
2523
2524
init_lines(struct line_data * d)2525 void init_lines(struct line_data* d) {
2526 d->count = 0; d->lines = 0;
2527 }
2528
init_list(struct list_data * d)2529 void init_list(struct list_data* d) {
2530 d->count = 0; d->data = 0;
2531 }
2532
escape_list(struct list_data * d)2533 void escape_list(struct list_data* d) {
2534 int i;
2535 for (i = 0; i < d->count; i++) {
2536 d->data[i].escaped_fullname =
2537 escape_string(d->data[i].fullname);
2538 }
2539 }
2540
escape_string(char * filestring)2541 char* escape_string(char* filestring) {
2542 char* src, *dst, *fs_new;
2543 size_t len;
2544
2545 len = strlen(src = filestring);
2546 dst = fs_new = my_malloc(len * 2 + 1);
2547
2548 do {
2549 if (*src == '\\') {
2550 *dst++ = '\\';
2551 } else if (*src == ' ') {
2552 *dst++ = '\\';
2553 }
2554 } while ((*dst++ = *src++));
2555
2556 return fs_new;
2557 }
2558
store_line(char * line,struct line_data * d)2559 void store_line(char* line, struct line_data* d) {
2560 int i = d->count;
2561 d->lines = my_realloc(d->lines, sizeof(char*) * (++d->count));
2562 d->lines[i] = my_malloc(CMD_BUF_SIZE - 9);
2563 strncpy(d->lines[i], line, CMD_BUF_SIZE - 10);
2564 d->lines[i][CMD_BUF_SIZE - 10] = 0;
2565 }
2566
store_list(char * fullname,struct list_data * d)2567 void store_list(char* fullname, struct list_data* d) {
2568 int i; char* base;
2569 base = basename(fullname);
2570
2571 if ((o.d && (strcmp(base, ".") == 0 || strcmp(base, "..") == 0)) ||
2572 (!o.d && *base == '.')) return;
2573
2574 i = d->count;
2575 d->data = my_realloc(d->data, sizeof(struct list_entry) * (++d->count));
2576 d->data[i].escaped_fullname = 0;
2577 d->data[i].fullname = my_strdup(fullname);
2578 d->data[i].basename = my_strdup(base);
2579 d->data[i].dirname = my_strdup(dirname(fullname));
2580 }
2581
store_pretty_list(char * fullname,struct list_data * d)2582 void store_pretty_list(char* fullname, struct list_data* d) {
2583 int i = d->count;
2584 d->data = my_realloc(d->data, sizeof(struct list_entry) * (++d->count));
2585 d->data[i].escaped_fullname = 0;
2586 d->data[i].fullname = my_strdup(fullname);
2587 d->data[i].basename = d->data[i].dirname = 0;
2588 }
2589
free_lines(struct line_data * d)2590 void free_lines(struct line_data* d) {
2591 int i;
2592 if (d->lines) {
2593 for (i = 0; i < d->count; i++) free(d->lines[i]);
2594 free(d->lines); d->lines = 0;
2595 }
2596 d->count = 0;
2597 }
2598
free_list(struct list_data * d)2599 void free_list(struct list_data* d) {
2600 int i;
2601 if (d->data) {
2602 for (i = 0; i < d->count; i++) {
2603 if (d->data[i].escaped_fullname) free(d->data[i].escaped_fullname);
2604 if (d->data[i].fullname) free(d->data[i].fullname);
2605 if (d->data[i].basename) free(d->data[i].basename);
2606 if (d->data[i].dirname) free(d->data[i].dirname);
2607 }
2608 free(d->data); d->data = 0;
2609 }
2610 d->count = 0;
2611 }
2612
2613