1 /*
2 * Queued FTP Client
3 *
4 * $Revision: 1.9 $
5 * $Date: 2003/03/11 01:26:39 $
6 * vim: sw=4 ts=4
7 *
8 */
9
10 #include <stdio.h>
11 #include <string.h>
12 #include <errno.h>
13 #include <unistd.h>
14 #include <netdb.h>
15 #include <stdlib.h>
16 #include <readline/readline.h>
17 #include <readline/history.h>
18 #include <sys/wait.h>
19 #include <sys/time.h>
20 #include <unistd.h>
21 #include <sys/stat.h>
22 #include <dirent.h>
23 #include <glob.h>
24 #ifndef __GLIBC__
25 #include <term.h>
26 #endif
27 #include "ftp.h"
28 #include "text.h"
29 #include "rc.h"
30 #include "quftp.h"
31
32 /* Increase this for a steadier but less accurate ETA and Speed readout */
33 #define SPEEDS_AVERAGE 5
34
35 /* Maximum number of concurrent connections */
36 #define MAX_CONNECTIONS 3
37
38 /* Define a couple of very handy macros */
39 #define CURRENT_CONNECTION connection[atoi(config(variables, "CURRENT_CONNECTION"))]
40 #define LAST_CONNECTION connection[atoi(config(variables, "LAST_CONNECTION"))]
41
42 struct ftpconnection *connection[MAX_CONNECTIONS];
43 struct qitem *leftqueue = NULL;
44 struct variable_list *variables = NULL;
45 struct alias_list *aliases = NULL;
46
47 struct COMMAND command_list[] = {
48 {"ls", cmd_ls, "List current directory", NULL},
49 {"lls", cmd_lls, "List local directory", NULL},
50 {"dir", cmd_ls, "Synonym for \"ls\"", NULL},
51 {"cd", cmd_cd, "Change the current directory", NULL},
52 {"lcd", cmd_lcd, "Change the current local directory", NULL},
53 {"pwd", cmd_pwd, "Show current working directory", NULL},
54 {"get", cmd_get, "Queue a file for download", NULL},
55 {"put", cmd_put, "Queue a file for upload", NULL},
56 {"fxp", cmd_fxp, "Queue a file to be transferred between connections", NULL},
57 {"go", cmd_go, "Start transferring the queue", NULL},
58 {"clear", cmd_clear, "Delete all items from the queue", NULL},
59 {"queue", cmd_queue, "List all items on the queue", NULL},
60 {"quit", cmd_quit, "Exit the program", NULL},
61 {"close", cmd_close, "Disconnect from the server", NULL},
62 {"open", cmd_open, "Connect to a server", NULL},
63 {"status", cmd_status, "Show current status", NULL},
64 {"stat", cmd_stat, "Display file statistics", NULL},
65 {"nlst", cmd_nlst, "List filenames in current directory only", NULL},
66 {"switch", NULL, "Switch to another connection", NULL},
67 {"debug", NULL, "Set debugging level", "Specify the debug level from 1 to 5"},
68 {"user", cmd_user, "Enter username and password for the server", "Syntax: user username [password]\nPassword is optional and should be separated from username by a space."},
69 {"cat", cmd_cat, "Output a text file to stdout", NULL},
70 {"more", cmd_more, "Page a text file", NULL},
71 {"set", cmd_set, "Set a variable", NULL},
72 {"unset", cmd_unset, "Remove a variable", NULL},
73 {"rm", cmd_dele, "Delete a remote file", NULL},
74 {"run", NULL, "Run a script file", NULL},
75 {"alias", cmd_alias, "Define a command alias", NULL},
76 {"getlist", cmd_getlist, "Get all files listed in param.txt", NULL},
77 {"assert", cmd_assert, "Die if the last operation was not successful", NULL},
78 {"quote", cmd_quote, "Send a command directly to the server", NULL},
79 {"exit", cmd_quit, "Synonym for quit", NULL},
80 {"mkdir", cmd_mkdir, "Make directory", NULL},
81 {"rmdir", cmd_rmdir, "Remove directory", NULL},
82 {NULL, NULL, NULL, NULL}
83 };
84
main(int argc,char ** argv)85 int main(int argc, char **argv) {
86 int arg, ignoreerrors = 0;
87 int i;
88 FILE *scriptfile = NULL;
89 FILE *rcfilehandle = NULL;
90 char *rcfile = NULL;
91
92 log_set_level(5);
93 log_show_function = false;
94 log_show_pid = false;
95 log_show_file = false;
96 log_show_level = false;
97 log_show_date = false;
98
99 setbuf(stdout, NULL);
100 log(LOG_CRITICAL, "quftp version %s David Parrish (david@dparrish.com)\n\n", VERSION);
101 leftqueue = NULL;
102
103 rl_attempted_completion_function = (CPPFunction *)quftp_completion;
104
105 setconfig(&variables, "PROGRAM_NAME", "quftp");
106 setconfig(&variables, "VERSION", VERSION);
107 setconfig(&variables, "CURRENT_CONNECTION", "0");
108 setconfig(&variables, "LAST_CONNECTION", "1");
109
110 for (i = 0; i < MAX_CONNECTIONS; i++) {
111 connection[i] = ftp_new();
112 connection[i]->variables = variables;
113 getcwd((char *)connection[i]->localdir, 256);
114 }
115
116 /* Process command line arguments */
117 while ((arg = getopt(argc, argv, "d::r:s:ih")) != -1)
118 switch (arg) {
119 case 'd': if (optarg)
120 log_set_level(atoi(optarg));
121 else
122 log_set_level(log_get_level() + 1);
123 log(log_get_level(), __FILE__, __LINE__, __FUNCTION__, "Debugging level is now %d", log_get_level());
124 break;
125 case 'i': ignoreerrors = 1;
126 break;
127 case 'r': rcfile = strdup(optarg);
128 log(LOG_WARNING, "Reading configuration from %s\n", rcfile);
129 break;
130 case 's': if (!(scriptfile = fopen(optarg, "r"))) {
131 perror("fopen");
132 if (!ignoreerrors) exit(0);
133 }
134 break;
135 case 'h': printf("Usage: quftp [-vhi] [-d[n]] [-rfilename] [-sfilename] [URL]\n\n");
136 printf(" -h Print this help, then exit\n");
137 printf(" -d Increase debugging level (optional argument)\n");
138 printf(" -i Ignore errors in a script\n");
139 printf(" -r Read configuration from filename instead of %s\n", DEFAULT_RC_FILENAME);
140 printf(" -s Process script filename\n");
141 printf(" URL Connect to any valid ftp url.. Read man 1 quftp for more information\n");
142 printf("\n");
143 exit(0);
144 break;
145 }
146 if (!rcfile) {
147 rcfile = strdup(DEFAULT_RC_FILENAME);
148 }
149 if (rcfile) {
150 rcfile = expand_home(rcfile);
151 rcfilehandle = fopen(rcfile, "r");
152 if (!rcfilehandle) log(LOG_WARNING, "Can't find rcfile %s", rcfile);
153 }
154
155 if (optind < argc) {
156 if (!parseurl(argv[optind], CURRENT_CONNECTION)) {
157 log(LOG_ALERT, "There was an error parsing \"%s\"\n", argv[optind]);
158 } else {
159 if (strlen(CURRENT_CONNECTION->password) == 0) {
160 if (config(variables, "email"))
161 strncpy((char *)CURRENT_CONNECTION->password, config(variables, "email"), 32);
162 else
163 strncpy((char *)CURRENT_CONNECTION->password, "user@quftp.com", 32);
164 }
165 CURRENT_CONNECTION->status = STATUS_WAIT_CONNECT;
166 if (CURRENT_CONNECTION->remotedir[0] != '\0')
167 (void)getconnected(CURRENT_CONNECTION);
168 }
169 }
170
171 while (1) {
172 char *line = NULL, *command_word = NULL;
173 struct COMMAND *command = NULL;
174 register int i;
175 int result;
176
177 if (line) {
178 free(line);
179 line = NULL;
180 }
181 if (rcfilehandle) {
182 char *temp;
183 line = (char *)malloc(4096);
184 fgets(line, 4096, rcfilehandle);
185 if (!*line) { /* End of script? */
186 fclose(rcfilehandle);
187 rcfilehandle = NULL;
188 continue;
189 }
190 if (*line == '#') continue; // Comment
191 temp = strchr(line, '\n');
192 if (temp == line) continue; // Zero length line?
193 else *temp = '\0'; // Strip \n
194 log(LOG_INFO, "%s> %s", rcfile, line);
195 } else if (scriptfile) { /* Reading from a script or command line? */
196 char *temp;
197 line = (char *)malloc(4096);
198 fgets(line, 4096, scriptfile);
199 if (!*line) { /* End of script? */
200 fclose(scriptfile);
201 scriptfile = NULL;
202 continue;
203 }
204 if (*line == '#') continue;
205 temp = strchr(line, '\n');
206 if (temp == line) continue; // Zero length line?
207 else *temp = '\0'; // Strip \n
208 add_history(line);
209 printf("quftp> %s\n", line);
210 } else {
211 line = readline("quftp> ");
212 if (!line) { // NULL returned from readline is CTRL-D
213 line=strdup("quit");
214 } else if(!*line) { // "" returned from readline is an empty line
215 continue;
216 }
217 add_history(line);
218 }
219
220 line = parseline(line);
221 line = expand_alias(line);
222
223 command_word = getnextword(line, GNW_START);
224 if (!command_word || !*command_word) continue;
225
226 /* Match core command set */
227 if (strcasecmp(command_word, "switch") == 0) {
228 char *temp;
229 char *param = getnextword(line, GNW_NEXT);
230 if (!param) {
231 int curr = atoi(config(variables, "CURRENT_CONNECTION"));
232 int last = atoi(config(variables, "LAST_CONNECTION"));
233 temp = (char *)malloc(4096);
234 sprintf(temp, "%d", last);
235 setconfig(&variables, "CURRENT_CONNECTION", temp);
236 sprintf(temp, "%d", curr);
237 setconfig(&variables, "LAST_CONNECTION", temp);
238 free(temp);
239 } else {
240 int new_connection = atoi(param);
241 if (new_connection >= MAX_CONNECTIONS) {
242 log(LOG_CRITICAL, "Only %d number of connections avaliable (0-%d)\n",
243 MAX_CONNECTIONS, MAX_CONNECTIONS-1);
244 continue;
245 } else {
246 temp = (char *)malloc(4096);
247 if (new_connection == atoi(config(variables, "CURRENT_CONNECTION"))) continue;
248 setconfig(&variables, "LAST_CONNECTION", config(variables, "CURRENT_CONNECTION"));
249 sprintf(temp, "%d", new_connection);
250 setconfig(&variables, "CURRENT_CONNECTION", temp);
251 free(temp);
252 }
253 }
254 log(LOG_WARNING, "Switched to connection %s\n", config(variables, "CURRENT_CONNECTION"));
255 setconfig(&variables, "HOSTNAME", *CURRENT_CONNECTION->hostname ? CURRENT_CONNECTION->hostname : NULL);
256 setconfig(&variables, "USERNAME", *CURRENT_CONNECTION->username ? CURRENT_CONNECTION->username : NULL);
257 continue;
258 } else if (strstr(command_word, "!") == command_word) {
259 char *temp;
260 int rval;
261 temp = line + 1;
262 rval = system(temp);
263 if (rval) log(LOG_ALERT, "Return value from \"%s\": %d\n", temp, rval);
264 continue;
265 } else if (strcasecmp(command_word, "run") == 0) {
266 char *filename;
267 filename = getnextword(line, GNW_NEXT);
268 if (!filename || !*filename) {
269 log(LOG_CRITICAL, "You must specify a script file to run\n");
270 continue;
271 }
272 if (!(scriptfile = fopen(filename, "r"))) {
273 perror("fopen");
274 if (!ignoreerrors) continue;
275 }
276 continue;
277 } else if (strcasecmp(command_word, "debug") == 0) {
278 char *param;
279 if ((param = getnextword(line, GNW_NEXT)))
280 log_set_level(atoi(param));
281 else
282 log_set_level(log_get_level());
283 log(log_get_level(), __FILE__, __LINE__, __FUNCTION__, "Debugging level is now %d", log_get_level());
284 continue;
285 } else if (strcasecmp(command_word, "help") == 0 || strcmp(command_word, "?") == 0) {
286 char *param;
287 int foundit = 0;
288 if ((param = getnextword(line, GNW_NEXT))) {
289 for (i = 0; command_list[i].name && *command_list[i].name; i++) {
290 if (strcmp(command_list[i].name, param) == 0) {
291 if (command_list[i].longhelp) {
292 printf("%s\n", command_list[i].longhelp);
293 } else {
294 printf("No extra help available for %s\n", param);
295 }
296 foundit = 1;
297 }
298 }
299 if (!foundit) {
300 printf("Command %s does not exist\n", param);
301 }
302 } else {
303 for (i = 0; command_list[i].name && *command_list[i].name; i++)
304 printf("%-10s%s\n", command_list[i].name,
305 command_list[i].description);
306 }
307 continue;
308 }
309
310 /* Match dynamic command set */
311 for (i = 0; command_list[i].name; i++) {
312 if (strcasecmp (command_word, command_list[i].name) == 0) {
313 command = &command_list[i];
314 break;
315 }
316 }
317 if (!command) {
318 printf("Unknown command \"%s\"\n", command_word);
319 if (scriptfile && !ignoreerrors) break;
320 continue;
321 }
322 result = (command->function)(line);
323 if (result < 0) {
324 break;
325 } else if (result > 0) { // Successful command
326 setconfig(&variables, "SUCCESSFUL_OPERATION", "true");
327 } else { // Unsuccessful command
328 setconfig(&variables, "SUCCESSFUL_OPERATION", "false");
329 if (scriptfile && !ignoreerrors) {
330 log(LOG_CRITICAL, "Error processing script\n");
331 break;
332 }
333 }
334 continue;
335 }
336 ftp_disconnect(CURRENT_CONNECTION);
337 return 0;
338 }
339
addtoqueue(struct qitem ** queue,struct qitem * item)340 struct qitem *addtoqueue(struct qitem **queue, struct qitem *item) {
341 struct qitem *newitem = 0;
342 struct qitem *tempitem = 0, *lastgooditem = 0;
343 int id = 0;
344 if (!(newitem = (struct qitem *)malloc(sizeof(struct qitem)))) {
345 perror("malloc");
346 exit(-1);
347 }
348 memcpy(newitem, item, sizeof(struct qitem));
349 tempitem = *queue; /* Find the last item on the queue */
350 while (tempitem) {
351 lastgooditem = tempitem;
352 if (strcmp(newitem->remotefilename, tempitem->remotefilename) == 0) return NULL;
353 if (tempitem->id > id) id = tempitem->id;
354 tempitem = tempitem->next;
355 }
356 newitem->id = ++id;
357 if (lastgooditem) lastgooditem->next = newitem;
358 if (!*queue) *queue = newitem;
359 newitem->next = 0;
360 return newitem;
361 }
362
delfromqueue(struct qitem ** queue,int id)363 struct qitem *delfromqueue(struct qitem **queue, int id) {
364 struct qitem *item, *last;
365 int counter;
366 item = last = *queue;
367 while (item) {
368 if (item->id == id) {
369 last->next = item->next;
370 if (item == *queue) {
371 if (item->next) *queue = item->next;
372 else *queue = NULL;
373 }
374 free(item);
375 item = *queue;
376 counter = 1; /* Straighten out counter */
377 while (item) {
378 item->id = counter;
379 counter++;
380 item = item->next;
381 }
382 return NULL;
383 }
384 last = item;
385 item = item->next;
386 }
387 return *queue;
388 }
389
clearqueue(struct qitem * queue)390 void clearqueue(struct qitem *queue) {
391 struct qitem *item, *next = NULL;
392 item = queue;
393 while (item) {
394 next = item->next;
395 free(item);
396 item = next;
397 }
398 }
399
runqueue(struct qitem * queue)400 struct qitem *runqueue(struct qitem *queue) {
401 struct qitem *item;
402 int id, n;
403 double starttime, timelength, endtime, speed;
404 struct timeval tv;
405 item = queue;
406 while (item) {
407 id = item->id;
408 if (item->direction == DIRECTION_GET) {
409 int result;
410 result = chdir(item->localdir);
411 if (result) switch(errno) {
412 case ENOENT : log(LOG_ERROR, "Creating directory %s\n", item->localdir);
413 mkdir(item->localdir, 0755);
414 break;
415 case ENOTDIR : log(LOG_ERROR, "A component of path is not a directory.\n");
416 item = item->next;
417 delfromqueue(&queue, id);
418 break;
419 case EACCES :
420 default : log(LOG_ERROR, "Can't change to %s: ", item->localdir, strerror(errno));
421 item = item->next;
422 delfromqueue(&queue, id);
423 continue;
424 break;
425 }
426 result = chdir(item->localdir);
427 ftp_cwd(item->remoteconnection, item->remotedir);
428 gettimeofday(&tv, NULL);
429 if ((item->permissions & 2048) == 2048) {
430 struct qitem *newitem, *newitem2;
431 char *param, *filenamelist[500];
432 char path[4096];
433 struct filedetails details;
434 int count = 0;
435 sprintf(path, "%s/%s", item->remotedir, item->remotefilename);
436 ftp_cwd(item->remoteconnection, path);
437 memset(filenamelist, 0, 500 * sizeof(char *));
438 ftp_nlst(item->remoteconnection, NULL, filenamelist, 500);
439 while (count < 500 && filenamelist[count]) {
440 param = filenamelist[count];
441 if (!param || !*param) {
442 free(param);
443 count++;
444 continue;
445 }
446 if (!(newitem = malloc(sizeof(struct qitem)))) {
447 perror("malloc");
448 exit(1);
449 }
450 memset(newitem, 0, sizeof(struct qitem));
451 strncpy(newitem->localfilename, param, 256);
452 strncpy(newitem->remotefilename, param, 256);
453
454 result = ftp_exists(CURRENT_CONNECTION, param);
455 if (result == 0) {
456 log(LOG_ALERT, "%s does not exist\n", param);
457 free(newitem);
458 free(param);
459 continue;
460 }
461 if (result < 0) {
462 log(LOG_ALERT, "Can't verify existance of %s.. Don't blame me if this fails!\n", param);
463 }
464 if (result > 0) {
465 ftp_stat(CURRENT_CONNECTION, param, &details);
466 newitem->permissions = details.permissions;
467 newitem->size = details.size;
468 }
469 newitem->direction = DIRECTION_GET;
470 sprintf(newitem->remotedir, "%s/%s", item->remotedir,
471 item->remotefilename);
472 sprintf(newitem->localdir, "%s/%s", item->localdir,
473 item->localfilename);
474 newitem->remoteconnection = item->remoteconnection;
475 newitem->localconnection = item->localconnection;
476 if ((newitem2 = addtoqueue(&queue, newitem))) {
477 log(LOG_INFO, "Queued %s/%s for download, position %d\n", newitem2->remotedir, newitem2->localfilename, newitem2->id);
478 }
479 free(newitem);
480 free(param);
481 count++;
482 }
483 item = item->next;
484 delfromqueue(&queue, id);
485 continue;
486 }
487
488 starttime = ((tv.tv_sec * 1.0) + (tv.tv_usec / 1000000.0));
489 n = ftp_get_file(item->remoteconnection, item);
490 if (n > 0) {
491 gettimeofday(&tv, NULL);
492 endtime = ((double)tv.tv_sec + (tv.tv_usec / 1000000));
493 timelength = abs(endtime - starttime);
494 if (timelength == 0) timelength = 1;
495 log(LOG_INFO, "Received %s (%d bytes)",
496 item->remotefilename, n);
497 speed = (n / 1000) / timelength;
498 log(LOG_INFO, " in %lu seconds (%1.2fKb/s)\n",
499 (unsigned long)timelength, speed);
500 }
501 } else if (item->direction == DIRECTION_PUT) {
502 if (!ftp_cwd(item->remoteconnection, item->remotedir)) {
503 if (!ftp_mkd(item->remoteconnection, item->remotedir)) {
504 item = item->next;
505 delfromqueue(&queue, id);
506 continue;
507 }
508 if (!ftp_cwd(item->remoteconnection, item->remotedir)) {
509 item = item->next;
510 delfromqueue(&queue, id);
511 continue;
512 }
513 }
514 chdir(item->localdir);
515 if (S_ISDIR(item->permissions)) {
516 struct qitem *newitem, *newitem2;
517 struct dirent *dirent;
518 DIR *dir;
519 char path[4096];
520 int count = 0;
521 sprintf(path, "%s/%s", item->localdir, item->localfilename);
522 dir = opendir(path);
523 if (!dir) {
524 log(LOG_ALERT, "Can't recursively put %s\n", path);
525 item = item->next;
526 delfromqueue(&queue, id);
527 continue;
528 }
529 while ((dirent = readdir(dir))) {
530 char fullname[4096];
531 struct stat statbuf;
532 if (strcmp(dirent->d_name, ".") == 0) continue;
533 if (strcmp(dirent->d_name, "..") == 0) continue;
534 if (strlen(dirent->d_name) == 0) continue;
535 if (!(newitem = malloc(sizeof(struct qitem)))) {
536 perror("malloc");
537 exit(1);
538 }
539 sprintf(fullname, "%s/%s", path, dirent->d_name);
540 if (stat(fullname, &statbuf)) {
541 log(LOG_ALERT, "%s does not exist\n", fullname);
542 free(newitem);
543 continue;
544 }
545 memset(newitem, 0, sizeof(struct qitem));
546 strncpy(newitem->localfilename, dirent->d_name, 256);
547 strncpy(newitem->remotefilename, dirent->d_name, 256);
548 newitem->permissions = statbuf.st_mode;
549 newitem->size = statbuf.st_size;
550 newitem->direction = DIRECTION_PUT;
551 sprintf(newitem->remotedir, "%s/%s", item->remotedir,
552 item->remotefilename);
553 sprintf(newitem->localdir, "%s/%s", item->localdir,
554 item->localfilename);
555 newitem->remoteconnection = item->remoteconnection;
556 newitem->localconnection = item->localconnection;
557 if ((newitem2 = addtoqueue(&queue, newitem))) {
558 log(LOG_INFO, "Queued %s/%s for upload, position %d\n", newitem2->remotedir, newitem2->localfilename, newitem2->id);
559 }
560 free(newitem);
561 count++;
562 }
563 item = item->next;
564 delfromqueue(&queue, id);
565 continue;
566 }
567 starttime = ((tv.tv_sec * 1.0) + (tv.tv_usec / 1000000.0));
568 n = ftp_put_file(item->remoteconnection, item);
569 if (n > 0) {
570 gettimeofday(&tv, NULL);
571 endtime = ((double)tv.tv_sec + (tv.tv_usec / 1000000));
572 timelength = abs(endtime - starttime);
573 if (timelength == 0) timelength = 1;
574 log(LOG_INFO, "Sent %s (%d bytes)",
575 item->remotefilename, n);
576 speed = (n / 1000) / timelength;
577 log(LOG_INFO, " in %lu seconds (%1.2fKb/s)\n",
578 (unsigned long)timelength, speed);
579 }
580 } else if (item->direction == DIRECTION_FXP) {
581 char sourcefile[4096], destfile[4096];
582 if ((item->permissions & 2048) == 2048) {
583 struct qitem *newitem, *newitem2;
584 char *param, *filenamelist[500];
585 char path[4096];
586 struct filedetails details;
587 int count = 0;
588 sprintf(path, "%s/%s", item->remotedir, item->remotefilename);
589 ftp_cwd(item->localconnection, path);
590 memset(filenamelist, 0, 500 * sizeof(char *));
591 ftp_nlst(item->localconnection, NULL, filenamelist, 500);
592 while (count < 500 && filenamelist[count]) {
593 int result;
594 param = filenamelist[count];
595 if (!param || !*param) {
596 free(param);
597 count++;
598 continue;
599 }
600 if (!(newitem = malloc(sizeof(struct qitem)))) {
601 perror("malloc");
602 exit(1);
603 }
604
605 result = ftp_exists(CURRENT_CONNECTION, param);
606 if (result == 0) {
607 log(LOG_WARNING, "%s does not exist\n", param);
608 free(newitem);
609 free(param);
610 continue;
611 }
612 if (result < 0) {
613 log(LOG_WARNING, "Can't verify existance of %s.. Don't blame me if this fails!\n", param);
614 }
615 if (result > 0) {
616 ftp_stat(CURRENT_CONNECTION, param, &details);
617 newitem->permissions = details.permissions;
618 newitem->size = details.size;
619 }
620
621 memset(newitem, 0, sizeof(struct qitem));
622 strncpy(newitem->localfilename, param, 256);
623 strncpy(newitem->remotefilename, param, 256);
624 newitem->direction = DIRECTION_GET;
625 sprintf(newitem->remotedir, "%s/%s", item->remotedir,
626 item->remotefilename);
627 sprintf(newitem->localdir, "%s/%s", item->localdir,
628 item->localfilename);
629 newitem->remoteconnection = item->remoteconnection;
630 newitem->localconnection = item->localconnection;
631 if ((newitem2 = addtoqueue(&queue, newitem))) {
632 log(LOG_INFO, "Queued %s/%s for FxP, position %d\n", newitem2->remotedir, newitem2->localfilename, newitem2->id);
633 }
634 free(newitem);
635 free(param);
636 count++;
637 }
638 item = item->next;
639 delfromqueue(&queue, id);
640 continue;
641 }
642 sprintf(sourcefile, "%s/%s", item->localdir, item->localfilename);
643 sprintf(destfile, "%s/%s", item->remotedir, item->remotefilename);
644 starttime = ((tv.tv_sec * 1.0) + (tv.tv_usec / 1000000.0));
645 log(LOG_WARNING, "Progress information not available for FxP transfers\n");
646 n = ftp_fxp(item->localconnection, item->remoteconnection,
647 sourcefile, destfile);
648 if (n == 0) {
649 gettimeofday(&tv, NULL);
650 endtime = ((double)tv.tv_sec + (tv.tv_usec / 1000000));
651 timelength = abs(endtime - starttime);
652 if (timelength == 0) timelength = 1;
653 log(LOG_INFO, "Sent %s (%lu bytes)",
654 item->remotefilename, item->size);
655 speed = (item->size / 1000) / timelength;
656 log(LOG_INFO, " in %lu seconds (%1.2fKb/s)\n",
657 (unsigned long)timelength, speed);
658 }
659 } else {
660 log(LOG_CRITICAL, "Corrupted item %d\n", id);
661 }
662 item = item->next;
663 delfromqueue(&queue, id);
664 }
665 return queue;
666 }
667
printqueue(struct qitem * queue)668 void printqueue(struct qitem *queue) {
669 struct qitem *item;
670 char fullname[200];
671 long totalsize = 0;
672 log(LOG_CALLS, "%p", queue);
673 item = queue;
674 printf("%-3s %-30s %-10s%-30s\n", "ID", "Source",
675 "Size", "Destination");
676 while (item) {
677 if (item->direction == DIRECTION_GET) {
678 sprintf(fullname, "%s/%s", item->remotedir, item->remotefilename);
679 printf("%-3d <- %-30s %-10lu%-30s\n", item->id, fullname, item->size,
680 item->localdir);
681 } else if (item->direction == DIRECTION_PUT) {
682 sprintf(fullname, "%s/%s", item->localdir, item->localfilename);
683 printf("%-3d -> %-30s %-10lu%-30s\n", item->id, fullname,
684 item->size, item->remotedir);
685 } else if (item->direction == DIRECTION_FXP) {
686 char remotename[200];
687 sprintf(fullname, "%s:%s%s%s%s", item->localconnection->hostname,
688 item->localdir, (item->remotefilename[0] == '/')
689 ? "" : "/", item->localfilename,
690 ((item->permissions & 2048) == 2048) ? "/" : "");
691 sprintf(remotename, "%s:%s%s%s%s", item->remoteconnection->hostname,
692 item->remotedir, (item->remotefilename[0] == '/')
693 ? "" : "/", item->remotefilename,
694 ((item->permissions & 2048) == 2048) ? "/" : "");
695 printf("%-3d -> %-30s %-10lu%-30s\n", item->id, fullname,
696 item->size, remotename);
697 } else {
698 log(LOG_CRITICAL, "Bad entry %d\n", item->id);
699 }
700 totalsize += item->size;
701 item = item->next;
702 }
703 printf("\nTotal queue size: %li bytes\n\n", totalsize);
704 }
705
getconnected(struct ftpconnection * connection)706 int getconnected(struct ftpconnection *connection) {
707 char *dir = NULL;
708 log(LOG_CALLS, "%p", connection);
709 if (!connection) return 0;
710 if (!connection->hostname) return 0;
711 if (connection->status > STATUS_WAIT_CONNECT) return 1;
712 if (connection->remotedir[0] != '\0') {
713 dir = strdup(connection->remotedir);
714 }
715 if (ftp_connect(connection)) {
716 if (dir) free(dir);
717 return 0;
718 }
719 if (ftp_login(connection)) {
720 if (dir) free(dir);
721 ftp_disconnect(connection);
722 return 0;
723 }
724 if (dir) {
725 if (!ftp_cwd(connection, dir)) log(LOG_ALERT, "Can't change directory to %s\n", dir);
726 free(dir);
727 }
728 connection->status = STATUS_IDLE;
729 setconfig(&variables, "USERNAME", connection->username);
730 return 1;
731 }
732
complete_remote_filename(const char * text,int state)733 char *complete_remote_filename(const char *text, int state) {
734 static char *filenamelist[500];
735 static int index = 0;
736 int response;
737 char *retstring = (char *)NULL;
738
739 if (state == 0) {
740 char *path;
741 memset(&filenamelist[0], 0, sizeof(char *) * 500);
742 if (!text || !*text) return NULL;
743 path = getpath(text);
744 response = ftp_nlst(CURRENT_CONNECTION, path, filenamelist, 500);
745 if (response < 0) return NULL;
746 index = 0;
747 }
748
749 while (filenamelist[index]) {
750 if (strstr(filenamelist[index], text) == filenamelist[index]) {
751 retstring = strdup(filenamelist[index]);
752 free(filenamelist[index]);
753 index++;
754 return retstring;
755 }
756 free(filenamelist[index]);
757 index++;
758 }
759 index = 0;
760 return NULL;
761 }
762
complete_command(const char * text,int state)763 char *complete_command(const char *text, int state) {
764 /* Don't know how I'm going to do this yet */
765 return NULL;
766 }
767
quftp_completion(char * text,int start,int end)768 char **quftp_completion(char *text, int start, int end) {
769 char **matches;
770 matches = (char **)NULL;
771 if (start == 0) {
772 /* It's a command */
773 matches = rl_completion_matches(text, complete_command);
774 } else if (strstr(text, "put") != text){
775 /* It's a filename */
776 matches = rl_completion_matches(text, complete_remote_filename);
777 }
778 return (matches);
779 }
780
cmd_ls(char * command_line)781 int cmd_ls(char *command_line) {
782 int result;
783 char *params = NULL;
784 params = getnextword(command_line, GNW_REST);
785 if (!getconnected(CURRENT_CONNECTION)) {
786 log(LOG_ALERT, "ls: Not connected\n");
787 return 0;
788 }
789 result = ftp_list(CURRENT_CONNECTION, params, 1);
790 printf("\n");
791 if (result < 0) return 0;
792 return 1;
793 }
794
795
cmd_cd(char * command_line)796 int cmd_cd(char *command_line) {
797 char *param;
798 if (!getconnected(CURRENT_CONNECTION)) {
799 log(LOG_ALERT, "cd: Not connected\n");
800 return 0;
801 }
802 getnextword(command_line, GNW_START);
803 param = getnextword(command_line, GNW_NEXT);
804 if (!param || !*param) {
805 log(LOG_ALERT, "Must specify a directory to change\n");
806 return 0;
807 } else {
808 if (!ftp_cwd(CURRENT_CONNECTION, param)) return 0;
809 }
810 return 1;
811 }
812
cmd_cdup(char * command_line)813 int cmd_cdup (char *command_line) {
814 if (!getconnected(CURRENT_CONNECTION)) {
815 log(LOG_ALERT, "cdup: Not connected\n");
816 return 0;
817 }
818 if (!ftp_cwd(CURRENT_CONNECTION, "..")) return 0;
819 return 1;
820 }
821
cmd_pwd(char * command_line)822 int cmd_pwd (char *command_line) {
823 if (!getconnected(CURRENT_CONNECTION)) {
824 log(LOG_ALERT, "pwd: Not connected\n");
825 return 0;
826 }
827 ftp_pwd(CURRENT_CONNECTION);
828 log(LOG_INFO, "The current remote directory is %s\n", CURRENT_CONNECTION->remotedir);
829 log(LOG_INFO, "The current local directory is %s\n", CURRENT_CONNECTION->localdir);
830 return 1;
831 }
832
cmd_lcd(char * command_line)833 int cmd_lcd(char *command_line) {
834 char *param = NULL;
835 int rval;
836 getnextword(command_line, GNW_START);
837 param = getnextword(command_line, GNW_NEXT);
838 if (!param || !*param) {
839 log(LOG_ALERT, "Specify a directory to change to\n");
840 return 0;
841 }
842 rval = chdir(param);
843 if (rval) {
844 log(LOG_ALERT, "Can't change to \"%s\"\n", param);
845 return 0;
846 }
847 getcwd(CURRENT_CONNECTION->localdir, 256);
848 log(LOG_INFO, "Current local directory is now \"%s\"\n", CURRENT_CONNECTION->localdir);
849 return 1;
850 }
851
cmd_get(char * command_line)852 int cmd_get(char *command_line) {
853 struct qitem *item, *newitem;
854 char *expanded_line;
855 char *param;
856 if (!getconnected(CURRENT_CONNECTION)) {
857 log(LOG_ALERT, "get: Not connected\n");
858 return 0;
859 }
860 expanded_line = expand_wildcard(command_line);
861 getnextword(expanded_line, GNW_START);
862 while ((param = getnextword(expanded_line, GNW_NEXT))) {
863 char path[4096], filename[4096];
864 int index;
865 struct filedetails details;
866 if (!param || !*param) continue;
867 if (ftp_stat(CURRENT_CONNECTION, param, &details)) {
868 log(LOG_ALERT, "%s does not exist\n", param);
869 continue;
870 }
871 if (!(item = malloc(sizeof(struct qitem)))) {
872 perror("malloc");
873 exit(1);
874 }
875 memset(item, 0, sizeof(struct qitem));
876 memset(path, 0, 4096);
877 memset(filename, 0, 4096);
878 item->permissions = details.permissions;
879 item->size = details.size;
880 /* Split the path and filename from the paramater */
881 index = strlen(param);
882 while (index) {
883 if (param[index] == '/') {
884 strncpy(path, param, index);
885 strncpy(filename, ¶m[index + 1], 4096);
886 break;
887 }
888 index--;
889 }
890 if (strlen(filename) == 0) strncpy(filename, param, 4096);
891 strncpy(item->localfilename, filename, 256);
892 strncpy(item->remotefilename, filename, 256);
893 item->direction = DIRECTION_GET;
894 if (path[0] == '/') {
895 strncpy(item->remotedir, path, 256);
896 } else {
897 sprintf(item->remotedir, "%s%s%s", CURRENT_CONNECTION->remotedir,
898 (CURRENT_CONNECTION->remotedir[strlen(CURRENT_CONNECTION->remotedir)-1] != '/') ? "/" : "", path);
899 }
900 strncpy(item->localdir, CURRENT_CONNECTION->localdir, 256);
901 if (item->remotedir[strlen(item->remotedir) - 1] == '/')
902 item->remotedir[strlen(item->remotedir) - 1] = '\0';
903 if (item->localdir[strlen(item->localdir) - 1] == '/')
904 item->localdir[strlen(item->localdir) - 1] = '\0';
905 item->remoteconnection = CURRENT_CONNECTION;
906 item->localconnection = NULL;
907 newitem = addtoqueue(&leftqueue, item);
908 if (newitem) log(LOG_INFO, "Queued %s/%s for download, position %d\n",
909 item->remotedir, item->localfilename, newitem->id);
910 free(item);
911 }
912 if (config(variables, "queuefiles")) {
913 if (strcasecmp(config(variables, "queuefiles"), "false") == 0)
914 cmd_go(expanded_line);
915 }
916 free(expanded_line);
917 return 1;
918 }
919
cmd_put(char * command_line)920 int cmd_put(char *command_line) {
921 struct qitem *item, *newitem;
922 char *param, *expanded_line;
923 if (!getconnected(CURRENT_CONNECTION)) {
924 log(LOG_ALERT, "put: Not connected\n");
925 return 0;
926 }
927 expanded_line = expand_wildcard(command_line);
928 getnextword(expanded_line, GNW_START);
929 while ((param = getnextword(expanded_line, GNW_NEXT))) {
930 char path[4096], filename[4096];
931 struct stat stat_buf;
932 int index;
933 if (!param || !*param) continue;
934 if (strcmp(param, "End") == 0) continue;
935 if (stat(param, &stat_buf) != 0)
936 switch (errno) {
937 case ENOENT : log(LOG_ALERT, "File %s does not exist\n", param);
938 continue;
939 break;
940 case EACCES : log(LOG_ALERT, "Permission denied to stat\n.");
941 continue;
942 break;
943 case ENOMEM : log(LOG_ALERT, "Out of memory\n");
944 exit(1);
945 break;
946 case ENAMETOOLONG : log(LOG_ALERT, "File name \"%s\" too long\n", param);
947 continue;
948 break;
949 default : log(LOG_ALERT, "Unknown error doing stat: %d\n", errno);
950 continue;
951 break;
952 }
953 if (!(item = malloc(sizeof(struct qitem)))) {
954 perror("malloc");
955 exit(1);
956 }
957 memset(item, 0, sizeof(struct qitem));
958 memset(path, 0, 4096);
959 memset(filename, 0, 4096);
960 /* Split the path and filename from the paramater */
961 index = strlen(param);
962 while (index) {
963 if (param[index] == '/') {
964 strncpy(path, param, index);
965 strncpy(filename, ¶m[index + 1], 4096);
966 break;
967 }
968 index--;
969 }
970 if (strlen(filename) == 0) strncpy(filename, param, 4096);
971 strncpy(item->localfilename, filename, 256);
972 strncpy(item->remotefilename, filename, 256);
973 item->direction = DIRECTION_PUT;
974 if (path[0] == '/') {
975 strncpy(item->localdir, path, 256);
976 } else {
977 sprintf(item->localdir, "%s%s%s", CURRENT_CONNECTION->localdir,
978 (item->localdir[strlen(item->localdir)] != '/') ? "/" : "",
979 path);
980 }
981 strncpy(item->remotedir, CURRENT_CONNECTION->remotedir, 256);
982 if (strlen(item->remotedir) > 1)
983 if (item->remotedir[strlen(item->remotedir) - 1] == '/')
984 item->remotedir[strlen(item->remotedir) - 1] = '\0';
985 if (strlen(item->localdir) > 1)
986 if (item->localdir[strlen(item->localdir) - 1] == '/')
987 item->localdir[strlen(item->localdir) - 1] = '\0';
988 item->remoteconnection = CURRENT_CONNECTION;
989 item->localconnection = NULL;
990 item->permissions = stat_buf.st_mode;
991 item->size = stat_buf.st_size;
992 newitem = addtoqueue(&leftqueue, item);
993 if (newitem) log(LOG_INFO, "Queued %s/%s for upload, position %d\n",
994 item->remotedir, param, newitem->id);
995 free(item);
996 }
997 if (config(variables, "queuefiles")) {
998 if (strcasecmp(config(variables, "queuefiles"), "false") == 0)
999 cmd_go(expanded_line);
1000 }
1001 free(expanded_line);
1002 return 1;
1003 }
1004
cmd_fxp(char * command_line)1005 int cmd_fxp(char *command_line) {
1006 struct qitem *item, *newitem;
1007 char *param, *expanded_line;
1008
1009 if (!getconnected(CURRENT_CONNECTION)) {
1010 log(LOG_ALERT, "fxp: Not connected\n");
1011 return 0;
1012 }
1013 if (!getconnected(LAST_CONNECTION)) {
1014 log(LOG_ALERT, "fxp: Not connected\n");
1015 return 0;
1016 }
1017 expanded_line = expand_wildcard(command_line);
1018 getnextword(expanded_line, GNW_START);
1019 while ((param = getnextword(expanded_line, GNW_NEXT))) {
1020 char path[4096], filename[4096];
1021 int index;
1022 struct filedetails details;
1023 if (!param || !*param) continue;
1024 log(LOG_NOTICE, "FxP'ing \"%s\" from %s to %s", param,
1025 CURRENT_CONNECTION->hostname,
1026 LAST_CONNECTION->hostname);
1027 if (!(item = malloc(sizeof(struct qitem)))) {
1028 perror("malloc");
1029 exit(1);
1030 }
1031 if (ftp_stat(CURRENT_CONNECTION, param, &details)) {
1032 log(LOG_ALERT, "%s does not exist\n", param);
1033 free(item);
1034 continue;
1035 }
1036 memset(item, 0, sizeof(struct qitem));
1037 memset(path, 0, 4096);
1038 memset(filename, 0, 4096);
1039 /* Split the path and filename from the paramater */
1040 index = strlen(param);
1041 while (index) {
1042 if (param[index] == '/') {
1043 strncpy(path, param, index);
1044 strncpy(filename, ¶m[index + 1], 4096);
1045 break;
1046 }
1047 index--;
1048 }
1049 if (strlen(filename) == 0) strncpy(filename, param, 4096);
1050 strncpy(item->localfilename, param, 256);
1051 strncpy(item->remotefilename, param, 256);
1052 item->permissions = details.permissions;
1053 item->size = details.size;
1054 item->direction = DIRECTION_FXP;
1055 if (path[0] == '/') {
1056 strncpy(item->remotedir, path, 256);
1057 } else {
1058 sprintf(item->remotedir, "%s%s%s", LAST_CONNECTION->remotedir, (LAST_CONNECTION->remotedir[strlen(LAST_CONNECTION->remotedir)-1] != '/') ? "/" : "", path);
1059 }
1060 strncpy(item->localdir, CURRENT_CONNECTION->remotedir, 256);
1061 if (item->remotedir[strlen(item->remotedir) - 1] == '/')
1062 item->remotedir[strlen(item->remotedir) - 1] = '\0';
1063 if (item->localdir[strlen(item->localdir) - 1] == '/')
1064 item->localdir[strlen(item->localdir) - 1] = '\0';
1065 item->remoteconnection = LAST_CONNECTION;
1066 item->localconnection = CURRENT_CONNECTION;
1067 newitem = addtoqueue(&leftqueue, item);
1068 if (newitem) log(LOG_INFO, "Queued %s/%s for FxP, position %d\n",
1069 item->remotedir, item->localfilename, newitem->id);
1070 free(item);
1071 }
1072 if (config(variables, "queuefiles")) {
1073 if (strcasecmp(config(variables, "queuefiles"), "false") == 0)
1074 cmd_go(expanded_line);
1075 }
1076 free(expanded_line);
1077 return 1;
1078 }
1079
cmd_go(char * command_line)1080 int cmd_go (char *command_line) {
1081 if (!getconnected(CURRENT_CONNECTION)) {
1082 log(LOG_ALERT, "go: Not connected\n");
1083 return 0;
1084 }
1085 leftqueue = runqueue(leftqueue);
1086 return 1;
1087 }
1088
cmd_clear(char * command_line)1089 int cmd_clear (char *command_line) {
1090 clearqueue(leftqueue);
1091 leftqueue = NULL;
1092 log(LOG_ALERT, "Queue cleared\n");
1093 return 1;
1094 }
1095
cmd_queue(char * command_line)1096 int cmd_queue (char *command_line) {
1097 if (!getconnected(CURRENT_CONNECTION)) {
1098 log(LOG_ALERT, "queue: Not connected\n");
1099 return 0;
1100 }
1101 printqueue(leftqueue);
1102 return 1;
1103 }
1104
cmd_rem(char * command_line)1105 int cmd_rem (char *command_line) {
1106 char *param;
1107 int id;
1108 getnextword(command_line, GNW_START);
1109 while ((param = getnextword(command_line, GNW_NEXT))) {
1110 id = atoi(param);
1111 if (id > 0) {
1112 if (delfromqueue(&leftqueue, id) == 0) {
1113 log(LOG_WARNING, "Deleted item %d\n", id);
1114 return 1;
1115 } else {
1116 log(LOG_ALERT, "Item %d doesn't exist!\n", id);
1117 return 0;
1118 }
1119 }
1120 }
1121 return 1;
1122 }
1123
cmd_quit(char * command_line)1124 int cmd_quit (char *command_line) {
1125 printf("\n");
1126 return -1;
1127 }
1128
cmd_close(char * command_line)1129 int cmd_close (char *command_line) {
1130 if (!getconnected(CURRENT_CONNECTION)) {
1131 log(LOG_ALERT, "close: Not connected\n");
1132 return 0;
1133 }
1134 if (leftqueue) {
1135 log(LOG_ALERT, "You still have untransferred items\n");
1136 return 0;
1137 }
1138 ftp_disconnect(CURRENT_CONNECTION);
1139 CURRENT_CONNECTION->status = STATUS_WAIT_CONNECT;
1140 return 1;
1141 }
1142
cmd_open(char * command_line)1143 int cmd_open(char *command_line) {
1144 char *param;
1145 getnextword(command_line, GNW_START);
1146 if (!(param = getnextword(command_line, GNW_NEXT))) {
1147 log(LOG_ALERT, "No address specified!\n");
1148 return 1;
1149 }
1150 if (!*param) return 0;
1151 if (!parseurl(param, CURRENT_CONNECTION)) {
1152 log(LOG_ALERT, "Can't open %s\n", param);
1153 return 0;
1154 }
1155 if (strlen(CURRENT_CONNECTION->password) == 0) {
1156 if (config(variables, "email"))
1157 strncpy(CURRENT_CONNECTION->password, config(variables, "email"), 32);
1158 else
1159 strncpy(CURRENT_CONNECTION->password, "user@quftp.com", 32);
1160 }
1161 if (CURRENT_CONNECTION->remotedir[0] != '\0') getconnected(CURRENT_CONNECTION);
1162 setconfig(&variables, "HOSTNAME", CURRENT_CONNECTION->hostname);
1163 return 1;
1164 }
1165
cmd_user(char * command_line)1166 int cmd_user (char *command_line) {
1167 char *param;
1168 getnextword(command_line, GNW_START);
1169 if (!(param = getnextword(command_line, GNW_NEXT))) {
1170 log(LOG_ALERT, "No username specified!\n");
1171 return 0;
1172 }
1173 if (CURRENT_CONNECTION->status >= STATUS_IDLE) {
1174 ftp_disconnect(CURRENT_CONNECTION);
1175 }
1176 strncpy(CURRENT_CONNECTION->username, param, 32);
1177 if ((param = getnextword(command_line, GNW_NEXT))) {
1178 strncpy(CURRENT_CONNECTION->password, param, 32);
1179 } else {
1180 strncpy(CURRENT_CONNECTION->password, getpassword(CURRENT_CONNECTION->username), 32);
1181 }
1182 CURRENT_CONNECTION->status = STATUS_WAIT_CONNECT;
1183 return 1;
1184 }
1185
cmd_stat(char * command_line)1186 int cmd_stat(char *command_line) {
1187 struct filedetails details;
1188 char *param;
1189 if (!getconnected(CURRENT_CONNECTION)) {
1190 log(LOG_ALERT, "stat: Not connected\n");
1191 return 0;
1192 }
1193 getnextword(command_line, GNW_START);
1194 while ((param = getnextword(command_line, GNW_NEXT))) {
1195 if (!*param) continue;
1196 if (ftp_stat(CURRENT_CONNECTION, param, &details) == 0) {
1197 log(LOG_INFO, "Details for %s\n", details.filename);
1198 log(LOG_INFO, "File Size: %lu\n", details.size);
1199 log(LOG_INFO, "Number of Hard Links: %d\n", details.links);
1200 if (*details.owner) log(LOG_INFO, "Owner: %s\n", (char *)details.owner);
1201 if (*details.group) log(LOG_INFO, "Group: %s\n", (char *)details.group);
1202 if (details.permissions) log(LOG_INFO, "Permission: %o\n", (unsigned int)details.permissions);
1203 }
1204 }
1205 return 1;
1206 }
1207
cmd_status(char * command_line)1208 int cmd_status (char *command_line) {
1209 int conn;
1210 log(LOG_INFO, "Current connection: %s\n", config(variables, "CURRENT_CONNECTION"));
1211 log(LOG_INFO, "\n");
1212 for (conn = 0; conn < MAX_CONNECTIONS; conn++) {
1213 if (connection[conn]->status == 0) continue;
1214 log(LOG_INFO, "Connection %d:\n", conn);
1215 log(LOG_INFO, " Host: %s:%d\n", connection[conn]->hostname, connection[conn]->port);
1216 log(LOG_INFO, " Username: %s\n", connection[conn]->username);
1217 log(LOG_INFO, " Directory: %s\n", connection[conn]->remotedir);
1218 log(LOG_INFO, " Status: ");
1219 switch(connection[conn]->status) {
1220 case 0 : log(LOG_INFO, "No connection\n"); break;
1221 case 1 : log(LOG_INFO, "Ready to connect\n"); break;
1222 case 2 : log(LOG_INFO, "Waiting for login\n"); break;
1223 case 3 : log(LOG_INFO, "Idle\n"); break;
1224 case 4 : log(LOG_INFO, "Waiting...\n"); break;
1225 case 5 : log(LOG_INFO, "Busy transferring\n"); break;
1226 case 99 : log(LOG_INFO, "Error!\n"); break;
1227 default : log(LOG_INFO, "Unknown!\n"); break;
1228 }
1229 }
1230 return 1;
1231 }
1232
cmd_nlst(char * command_line)1233 int cmd_nlst(char *command_line) {
1234 char *list[200];
1235 char *params = NULL;
1236 register int index = 0;
1237 getnextword(command_line, GNW_START);
1238 params = getnextword(command_line, GNW_REST);
1239 if (!getconnected(CURRENT_CONNECTION)) {
1240 log(LOG_ALERT, "nlst: Not connected\n");
1241 return 0;
1242 }
1243 if (ftp_nlst(CURRENT_CONNECTION, params, list, 200) < 0) return 0;
1244 for (index = 0; list[index]; index++) {
1245 printf("%s\n", list[index]);
1246 free(list[index]);
1247 }
1248 return 1;
1249 }
1250
cmd_lls(char * command_line)1251 int cmd_lls(char *command_line) {
1252 char *param = command_line;
1253 param++;
1254 system(param);
1255 return 1;
1256 }
1257
cmd_cat(char * command_line)1258 int cmd_cat(char *command_line) {
1259 char *param = (char *)NULL;
1260 char *expanded_line;
1261 expanded_line = expand_wildcard(command_line);
1262 param = getnextword(expanded_line, GNW_START);
1263 while ((param = getnextword(expanded_line, GNW_NEXT))) {
1264 char *buffer;
1265 int n;
1266 buffer = (char *)malloc(4096);
1267 sprintf(buffer, "%s", param);
1268 if (ftp_read_file(CURRENT_CONNECTION, buffer, 4095, TRANS_START) < 0) {
1269 free(expanded_line);
1270 return 0;
1271 }
1272 while ((n = ftp_read_file(CURRENT_CONNECTION, buffer, 4095, TRANS_TRANS)) > 0) {
1273 buffer[4095] = '\0';
1274 printf("%s", buffer);
1275 }
1276 ftp_read_file(CURRENT_CONNECTION, buffer, 4095, TRANS_CLOSE);
1277 free(buffer);
1278 }
1279 free(expanded_line);
1280 return 1;
1281 }
1282
cmd_more(char * command_line)1283 int cmd_more(char *command_line) {
1284 char *param = (char *)NULL;
1285 char *expanded_line;
1286 FILE *fh;
1287 expanded_line = expand_wildcard(command_line);
1288 param = getnextword(expanded_line, GNW_START);
1289 while ((param = getnextword(expanded_line, GNW_NEXT))) {
1290 char *pager;
1291 char *buffer;
1292 int n;
1293 if (ftp_read_file(CURRENT_CONNECTION, param, 4095, TRANS_START) < 0) {
1294 free(expanded_line);
1295 return 0;
1296 }
1297 pager = config(variables, "pager");
1298 if (!pager) pager = strdup("/bin/more");
1299 fh = popen(pager, "w");
1300 if (!fh) {
1301 perror("popen");
1302 ftp_read_file(CURRENT_CONNECTION, NULL, 0, TRANS_CLOSE);
1303 free(expanded_line);
1304 return 0;
1305 }
1306 buffer = (char *)malloc(4096);
1307 while ((n = ftp_read_file(CURRENT_CONNECTION, buffer, 4095, TRANS_TRANS)) > 0) {
1308 buffer[4095] = '\0';
1309 fputs(buffer, fh);
1310 }
1311 ftp_read_file(CURRENT_CONNECTION, buffer, 4095, TRANS_CLOSE);
1312 free(buffer);
1313 pclose(fh);
1314 }
1315 free(expanded_line);
1316 return 1;
1317 }
1318
cmd_set(char * command_line)1319 int cmd_set(char *command_line) {
1320 char *key = NULL, *value;
1321 value = getnextword(command_line, GNW_START);
1322 value = getnextword(command_line, GNW_NEXT);
1323 if (value) {
1324 key = strdup(value);
1325 value = getnextword(command_line, GNW_NEXT);
1326 if (value) {
1327 setconfig(&variables, strlower(key), value);
1328 } else {
1329 char *data = config(variables, key);
1330 if (data) printf("%s: %s\n", key, data);
1331 else log(LOG_ALERT, "%s does not exist\n", key);
1332 }
1333 } else {
1334 struct variable_list *list = variables;
1335 while (list) {
1336 printf("%s: %s\n", list->key, list->value);
1337 list = list->next;
1338 }
1339 }
1340 if (key) free(key);
1341 return 1;
1342 }
1343
cmd_unset(char * command_line)1344 int cmd_unset(char *command_line) {
1345 char *key = NULL;
1346 getnextword(command_line, GNW_START);
1347 key = strlower(getnextword(command_line, GNW_NEXT));
1348 if (key) {
1349 setconfig(&variables, key, NULL);
1350 free(key);
1351 return 1;
1352 }
1353 return 0;
1354 }
1355
cmd_assert(char * command_line)1356 int cmd_assert(char *command_line) {
1357 char *result;
1358 result = config(variables, "SUCCESSFUL_OPERATION");
1359 if (result && strcmp(result, "false") == 0) {
1360 log(LOG_CRITICAL, "Assertion failed... quitting\n");
1361 return -1;
1362 }
1363 return 1;
1364 }
1365
parseline(char * line)1366 char *parseline(char *line) { /* Expand variables */
1367 int index = 0;
1368 char *linestart = line;
1369 char *temp;
1370 temp = (char *)malloc(4096);
1371 memset(temp, 0, 4096);
1372 while (*line) {
1373 if (*line == '$') { // Expand variables
1374 char *endbit;
1375 line++;
1376 endbit = line;
1377 while (endbit && *endbit && *endbit != ' ') endbit++;
1378 *endbit = '\0';
1379 if (config(variables, line)) strcat((temp + index), config(variables, line));
1380 index += (endbit - line);
1381 line = endbit + 1;
1382 } else {
1383 temp[index] = *line;
1384 }
1385 line++;
1386 if (line > (linestart + strlen(linestart))) break;
1387 index++;
1388 }
1389 return temp;
1390 }
1391
cmd_dele(char * command_line)1392 int cmd_dele(char *command_line) {
1393 char *filename, *expanded_line;
1394 expanded_line = expand_wildcard(command_line);
1395 getnextword(expanded_line, GNW_START);
1396 while ((filename = getnextword(expanded_line, GNW_NEXT))) {
1397 ftp_delete(CURRENT_CONNECTION, filename);
1398 }
1399 free(expanded_line);
1400 return 1;
1401 }
1402
cmd_alias(char * command_line)1403 int cmd_alias(char *command_line) {
1404 struct alias_list *item = aliases, *last = aliases;
1405 char *alias = NULL, *expanded = NULL;
1406 getnextword(command_line, GNW_START);
1407 expanded = getnextword(command_line, GNW_NEXT);
1408 if (expanded) {
1409 alias = strdup(expanded);
1410 }
1411 expanded = getnextword(command_line, GNW_REST);
1412 if (!alias || !*alias) {
1413 if (!item) {
1414 log(LOG_ALERT, "No aliases have been defined\n");
1415 return 1;
1416 }
1417 printf("%-20s%s\n", "Alias", "Expands to");
1418 while (item) {
1419 printf("%-20s%s\n", item->alias, item->expanded);
1420 item = item->next;
1421 }
1422 printf("\n");
1423 return 1;
1424 }
1425 if (!expanded || !*expanded) {
1426 while (item) {
1427 if (strcasecmp(item->alias, alias) == 0) {
1428 free(item->expanded);
1429 free(item->alias);
1430 last->next = item->next;
1431 if (item == aliases) aliases = NULL;
1432 free(item);
1433 return 1;
1434 }
1435 last = item;
1436 item = item->next;
1437 }
1438 log(LOG_ALERT, "Can't find \"%s\"\n", alias);
1439 return 0;
1440 }
1441 while (item) {
1442 if (strcasecmp(item->alias, alias) == 0) {
1443 free(item->expanded);
1444 item->expanded = strdup(expanded);
1445 item->next = NULL;
1446 return 1;
1447 }
1448 last = item;
1449 item = item->next;
1450 }
1451 item = (struct alias_list *)malloc(sizeof(struct alias_list));
1452 if (!aliases) aliases = item;
1453 if (last) last->next = item;
1454 item->alias = strdup(alias);
1455 item->expanded = strdup(expanded);
1456 item->next = NULL;
1457 return 1;
1458 }
1459
expand_wildcard(char * inputstring)1460 char *expand_wildcard(char *inputstring) {
1461 char *word, *tempstring, *retstring;
1462
1463 if (!inputstring) return NULL;
1464 if ((strstr(inputstring, "*") == 0) && (strstr(inputstring, "?")) == 0)
1465 return strdup(inputstring);
1466 tempstring = (char *)malloc(65535);
1467 sprintf(tempstring, "%s ", getnextword(inputstring, GNW_START));
1468 if (strcasecmp(tempstring, "get ") == 0 || strcasecmp(tempstring, "fxp ") == 0) {
1469 while ((word = getnextword(inputstring, GNW_NEXT))) {
1470 int index = 0;
1471 char *temp;
1472 while ((temp = ftp_glob(CURRENT_CONNECTION, word, index++))) {
1473 sprintf(tempstring, "%s %s", tempstring, temp);
1474 }
1475 }
1476 retstring = strdup(tempstring);
1477 free(tempstring);
1478 return retstring;
1479 } else if (strcasecmp(tempstring, "put ") == 0) {
1480 glob_t globvec;
1481 sprintf(tempstring, "%s ", getnextword(inputstring, GNW_START));
1482 while ((word = getnextword(inputstring, GNW_NEXT))) {
1483 int result, index;
1484 memset(&globvec, 0, sizeof(glob_t));
1485 result = glob(word, GLOB_TILDE, NULL, &globvec);
1486 switch (result) {
1487 case 0: for (index = 0; index < globvec.gl_pathc; index++)
1488 sprintf(tempstring, "%s %s", tempstring,
1489 globvec.gl_pathv[index]);
1490 globfree(&globvec);
1491 break;
1492 case GLOB_NOSPACE: log(LOG_CRITICAL, "Ran out of memory for glob\n");
1493 free(tempstring);
1494 return strdup(inputstring);
1495 case GLOB_ABORTED: log(LOG_CRITICAL, "Glob Read error\n");
1496 free(tempstring);
1497 return strdup(inputstring);
1498 case GLOB_NOMATCH: log(LOG_ALERT, "Glob found no matches\n");
1499 free(tempstring);
1500 return strdup(inputstring);
1501 default : log(LOG_ALERT, "Unknown glob error %d\n", result);
1502 break;
1503 }
1504 }
1505 retstring = strdup(tempstring);
1506 free(tempstring);
1507 return retstring;
1508 } else {
1509 free(tempstring);
1510 return strdup(inputstring);
1511 }
1512 }
1513
expand_alias(char * string)1514 char *expand_alias(char *string) {
1515 struct alias_list *item;
1516 char *returnstring = (char *)NULL;
1517 char firstword[500], *rest;
1518 if (!string || !*string) return string;
1519 memset(firstword, 0, 500);
1520 strncpy(firstword, getnextword(string, GNW_START), 500);
1521 rest = getnextword(string, GNW_REST);
1522 item = aliases;
1523 while (item) {
1524 if (strcasecmp(item->alias, firstword) == 0) {
1525 returnstring = (char *)malloc(strlen(item->expanded) + strlen(rest) + 2);
1526 sprintf(returnstring, "%s %s", item->expanded, rest);
1527 return returnstring;
1528 }
1529 item = item->next;
1530 }
1531 return strdup(string);
1532 }
1533
print_permissions(int permissions)1534 char *print_permissions(int permissions) {
1535 char *returnstring;
1536 returnstring = (char *)malloc(11);
1537 memset(returnstring, '-', 10);
1538 if ((permissions & 512) == 512) returnstring[0] = 'd';
1539 if ((permissions & 256) == 256) returnstring[1] = 'r';
1540 if ((permissions & 128) == 128) returnstring[2] = 'w';
1541 if ((permissions & 64) == 64) returnstring[3] = 'x';
1542 if ((permissions & 32) == 32) returnstring[4] = 'r';
1543 if ((permissions & 16) == 16) returnstring[5] = 'w';
1544 if ((permissions & 8) == 8) returnstring[6] = 'x';
1545 if ((permissions & 4) == 4) returnstring[7] = 'r';
1546 if ((permissions & 2) == 2) returnstring[8] = 'w';
1547 if ((permissions & 1) == 1) returnstring[9] = 'x';
1548 return returnstring;
1549 }
1550
progress_bar(unsigned long current,unsigned long max,double speed)1551 int progress_bar(unsigned long current, unsigned long max, double speed) {
1552 unsigned long etasec;
1553 int index, width, count = 0;
1554 double percent;
1555 static double oldpercent = -1;
1556 static double speeds[SPEEDS_AVERAGE];
1557 static int speedindex = 0;
1558 char *temp;
1559
1560 /* Don't display a progress bar for a 0 byte file */
1561 if (max == 0) {
1562 return 0;
1563 }
1564
1565 /* If a new progress bar, clear the speed history */
1566 if (current == 0) {
1567 for (index = 0; index < SPEEDS_AVERAGE; index++) speeds[index] = 0.0;
1568 speedindex = 0;
1569 }
1570
1571 /* Record current speed */
1572 speeds[++speedindex % SPEEDS_AVERAGE] = speed;
1573
1574 /* Get current average speed */
1575 for (index = 0; index < SPEEDS_AVERAGE; index++) {
1576 if (speeds[index] == 0.0) continue;
1577 speed += speeds[index];
1578 count++;
1579 }
1580 speed /= (count * 1.0);
1581 percent = (current * 1.0) / (max * 1.0);
1582
1583 /* No movement? Return without displaying anything */
1584 if (percent == oldpercent) return 0;
1585
1586 etasec = ((max - current) / 1024.00) / speed;
1587 if ((temp = config(variables, "screenwidth"))) width = atoi(temp);
1588 else if ((temp = getenv("COLUMNS"))) width = atoi(temp);
1589 else width = 80;
1590 width -= 20;
1591 for (index = 0; index <= (width * percent); index++) printf("#");
1592 for (index = (width * percent) + 1; index <= width; index++) printf("-");
1593 printf(" %-6.6s %3.1fKb/s", timestring(etasec), speed);
1594 printf("\r");
1595 if (current == max) {
1596 for (index = 0; index < (width + 20); index++) printf(" ");
1597 printf("\r");
1598 }
1599 oldpercent = percent;
1600 return 0;
1601 }
1602
timestring(unsigned long seconds)1603 char *timestring(unsigned long seconds) {
1604 char *returnstring;
1605 returnstring = (char *)malloc(15);
1606 sprintf(returnstring, "%02lu:%02lu", seconds / 60, seconds % 60);
1607 return returnstring;
1608 }
1609
cmd_getlist(char * command_line)1610 int cmd_getlist(char *command_line) {
1611 char *infile = NULL;
1612 char line[MAX_LINE_SIZE];
1613 FILE *fh;
1614 getnextword(command_line, GNW_START);
1615 infile = getnextword(command_line, GNW_NEXT);
1616 fh = fopen(infile, "r");
1617 if (!fh) {
1618 log(LOG_CRITICAL, "Can't open file %s\n", infile);
1619 return 0;
1620 }
1621 while (fgets(line, MAX_LINE_SIZE, fh)) {
1622 char path[MAX_LINE_SIZE], filename[MAX_LINE_SIZE], *param;
1623 int index;
1624 int result;
1625 struct qitem *item = NULL, *newitem;
1626 struct filedetails details;
1627 param = strchr(line, '\n');
1628 *param = '\0';
1629 if (line[0] == 0) continue;
1630 param = getnextword(line, GNW_START);
1631 result = ftp_stat(CURRENT_CONNECTION, param, &details);
1632 switch (result) {
1633 case 211 :
1634 case 212 : log(LOG_ALERT, "%s does not exist\n", param);
1635 continue;
1636 break;
1637 case 500 :
1638 case 501 :
1639 case 502 : break;
1640 case 421 :
1641 default : continue;
1642 break;
1643 }
1644 item = (struct qitem *)malloc(sizeof(struct qitem));
1645 memset(item, 0, sizeof(struct qitem));
1646 memset(path, 0, MAX_LINE_SIZE);
1647 memset(filename, 0, MAX_LINE_SIZE);
1648 /* Split the path and filename from the paramater */
1649 index = strlen(param);
1650 while (index) {
1651 if (param[index] == '/') {
1652 strncpy(path, param, index);
1653 strncpy(filename, ¶m[index + 1], MAX_LINE_SIZE);
1654 break;
1655 }
1656 index--;
1657 }
1658 if (filename[0] == 0) strncpy(filename, param, MAX_LINE_SIZE);
1659 strncpy(item->localfilename, filename, 256);
1660 strncpy(item->remotefilename, filename, 256);
1661 item->permissions = details.permissions;
1662 item->size = details.size;
1663 item->direction = DIRECTION_GET;
1664 if (path[0] == '/') {
1665 strncpy(item->remotedir, path, 256);
1666 } else {
1667 sprintf(item->remotedir, "%s%s%s", CURRENT_CONNECTION->remotedir,
1668 (CURRENT_CONNECTION->remotedir[strlen(CURRENT_CONNECTION->remotedir)-1] != '/') ? "/" : "",
1669 path);
1670 }
1671 strncpy(item->localdir, CURRENT_CONNECTION->localdir, 256);
1672 if (item->remotedir[strlen(item->remotedir) - 1] == '/')
1673 item->remotedir[strlen(item->remotedir) - 1] = '\0';
1674 if (item->localdir[strlen(item->localdir) - 1] == '/')
1675 item->localdir[strlen(item->localdir) - 1] = '\0';
1676 item->remoteconnection = CURRENT_CONNECTION;
1677 item->localconnection = NULL;
1678 newitem = addtoqueue(&leftqueue, item);
1679 if (newitem) log(LOG_INFO, "Queued %s/%s for download, position %d\n",
1680 item->remotedir, item->localfilename, newitem->id);
1681 free(item);
1682 }
1683 fclose(fh);
1684 return 1;
1685 }
1686
cmd_quote(char * command_line)1687 int cmd_quote(char *command_line) {
1688 char command[4096];
1689 sprintf(command, "%s\r\n", getnextword(command_line, GNW_REST));
1690 ftp_sendline(CURRENT_CONNECTION, command);
1691 ftp_getrc(CURRENT_CONNECTION, NULL, 0, 1);
1692 return 1;
1693 }
1694
cmd_mkdir(char * command_line)1695 int cmd_mkdir(char *command_line) {
1696 char *param;
1697 if (!getconnected(CURRENT_CONNECTION)) {
1698 log(LOG_ALERT, "mkdir: Not connected\n");
1699 return 0;
1700 }
1701 getnextword(command_line, GNW_START);
1702 param = getnextword(command_line, GNW_NEXT);
1703 if (!param || !*param) {
1704 log(LOG_ALERT, "Must specify a directory to create\n");
1705 return 0;
1706 } else {
1707 if (!ftp_mkd(CURRENT_CONNECTION, param)) return 0;
1708 else log(LOG_INFO, "Created directory %s on %s in %s\n", param, CURRENT_CONNECTION->hostname,
1709 CURRENT_CONNECTION->remotedir);
1710 }
1711 return 1;
1712 }
1713
cmd_rmdir(char * command_line)1714 int cmd_rmdir(char *command_line) {
1715 char *param;
1716 if (!getconnected(CURRENT_CONNECTION)) {
1717 log(LOG_ALERT, "rmdir: Not connected\n");
1718 return 0;
1719 }
1720 getnextword(command_line, GNW_START);
1721 param = getnextword(command_line, GNW_NEXT);
1722 if (!param || !*param) {
1723 log(LOG_ALERT, "Must specify a directory to remove\n");
1724 return 0;
1725 } else {
1726 if (!ftp_rmd(CURRENT_CONNECTION, param)) return 0;
1727 else log(LOG_INFO, "Removed directory %s on %s in %s\n", param, CURRENT_CONNECTION->hostname,
1728 CURRENT_CONNECTION->remotedir);
1729 }
1730 return 1;
1731 }
1732
1733