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, &param[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, &param[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, &param[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, &param[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