1 /*
2  * transfer.c --
3  *
4  * Yet Another FTP Client
5  * Copyright (C) 1998-2001, Martin Hedenfalk <mhe@stacken.kth.se>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version. See COPYING for more details.
11  */
12 
13 #include "syshdr.h"
14 #include "ftp.h"
15 #include "shortpath.h"
16 #include "strq.h"
17 #include "gvars.h"
18 #include "args.h"
19 #include "transfer.h"
20 #include "utils.h"
21 
22 static FILE *logfp = 0;
23 static FILE *mailfp = 0;
24 static char *nohup_logfile = 0;
25 static char *nohup_command = 0;
26 static time_t nohup_start_time;
27 static time_t nohup_end_time;
28 
29 static int max_printf(FILE *fp, int max, const char *fmt, ...) YAFC_PRINTF(3, 4);
30 
max_printf(FILE * fp,int max,const char * fmt,...)31 static int max_printf(FILE *fp, int max, const char *fmt, ...)
32 {
33 	if(!fmt)
34 		return 0;
35 
36   va_list ap;
37 	va_start(ap, fmt);
38   char* e = NULL;
39 	int ret = vasprintf(&e, fmt, ap);
40 	va_end(ap);
41   if (ret == -1)
42     return 0;
43 
44 	size_t len = strlen(e);
45   ret = 0;
46 
47 	if(max < 0) {
48 		/* left justified */
49 		max = -max;
50 		if(len >= max)
51 			ret += fprintf(fp, "%s", e + (len-max));
52 		else {
53 			int i;
54 			ret += fprintf(fp, "%s", e);
55 			for(i=len; i<max; i++, ret++)
56 				fputc(' ', fp);
57 		}
58 	} else if(max > 0) {
59 		/* right justified */
60 		if(len >= max)
61 			ret += fprintf(fp, "%s", e + (len-max));
62 		else {
63 			int i;
64 			for(i=len; i<max; i++, ret++)
65 				fputc(' ', fp);
66 			ret += fprintf(fp, "%s", e);
67 		}
68 	} else
69 		ret += fprintf(fp, "%s", e);
70 
71 	free(e);
72 	return ret;
73 }
74 
75 /* transfer status line codes
76  *
77  * %r   - remote filename
78  * %R   - remote filename w/path
79  * %l   - local filename
80  * %L   - local filename w/path
81  * %s   - size transferred so far
82  * %S   - total size (if available)
83  * %e   - ETA (time left)
84  * %p   - percent transferred
85  * %%   - percent sign
86  * %b   - transfer rate (Bps)
87  * %B   - transfer rate (Bps) or "stalled" if stalled
88  * %t   - time elapsed
89  * %T   - time transfer has been stalled
90  * %v   - visual progress bar
91  */
92 
print_transfer_string(char * str,FILE * fp,transfer_info * ti,float bps,unsigned int secs,unsigned long long int eta)93 static int print_transfer_string(char *str,
94 								 FILE *fp,
95 								 transfer_info *ti,
96 								 float bps,
97 								 unsigned int secs,
98 								 unsigned long long int eta)
99 {
100 	int i;
101 	int len = 0;
102 	char *e = str;
103 	bool leftjust;
104 	int inescape = 0;
105 	int minlen;
106 	int number_of_dots;  /* used by visual progress bar (%v) */
107 
108 	while(e && *e) {
109 		if(*e == '%') {
110 			leftjust = false;
111 			minlen = 0;
112 			e++;
113 			if(!*e)
114 				break;
115 			if(*e == '-') {
116 				leftjust = true;
117 				e++;
118 			}
119 			if(isdigit((int)*e)) {
120 				minlen = atoi(e);
121 				while(isdigit((int)*e))
122 					e++;
123 			}
124 			if(leftjust)
125 				minlen = -minlen;
126 
127 			char* sp = NULL;
128 			switch(*e) {
129 			case 'r':
130 				sp = shortpath(base_name_ptr(ti->remote_name),
131 											leftjust ? -minlen : minlen,
132 											ftp->homedir);
133 				len += max_printf(fp, minlen, "%s", sp);
134 				break;
135 			case 'R':
136 				sp = shortpath(ti->remote_name,
137 											leftjust ? -minlen : minlen,
138 											ftp->homedir);
139 				len += max_printf(fp, minlen, "%s", sp);
140 				break;
141 			case 'l':
142 				sp = shortpath(base_name_ptr(ti->local_name),
143 											leftjust ? -minlen : minlen,
144 											gvLocalHomeDir);
145 				len += max_printf(fp, minlen, "%s", sp);
146 				break;
147 			case 'L':
148 				sp = shortpath(ti->local_name,
149 											leftjust ? -minlen : minlen,
150 											gvLocalHomeDir);
151 				len += max_printf(fp, minlen, "%s", sp);
152 				break;
153 			case 's':
154 				len += max_printf(fp, minlen, "%sB", human_size(ti->size));
155 				break;
156 			case 'S':
157 				len += max_printf(fp, minlen, "%sB",
158 								  (ti->total_size == -1L
159 								   ? "??"
160 								   : human_size(ti->total_size)));
161 				break;
162 			case 'b':
163 				len += max_printf(fp, minlen < 2 ? minlen : minlen-2,
164 								  "%sB/s", human_size(bps));
165 				break;
166 			case 'B':
167 				if(ti->stalled >= 5)
168 					len += max_printf(fp, minlen, "%s", _("stalled"));
169 				else
170 					len += max_printf(fp, minlen < 2 ? minlen : minlen-2,
171 									  "%sB/s", human_size(bps));
172 				break;
173 			case 'e':
174 				if(eta != (unsigned) -1)
175 					len += max_printf(fp, minlen, "%s", human_time(eta));
176 				else
177 					len += max_printf(fp, minlen, "%s", "--:--");
178 				break;
179 			case 't':
180 				len += max_printf(fp, minlen, "%s", human_time(secs));
181 				break;
182 			case '%':
183 				len += fprintf(fp, "%%");
184 				break;
185 			case 'p':
186 				if(ti->total_size != -1L)
187 					len += max_printf(fp, minlen, "%.1f",
188 									  (double)100*ti->size /
189 									  (ti->total_size +
190 									   (ti->total_size ? 0 : 1)));
191 				else
192 					len += fprintf(fp, "?");
193 				break;
194 			case 'v':
195 				if(ti->total_size != -1L) {
196 					if(ti->total_size == ti->size)
197 						number_of_dots = minlen;
198 					else
199 						number_of_dots = (double)minlen *
200 							ti->size / (ti->total_size + 1);
201 					if(number_of_dots > minlen || number_of_dots < 0)
202 						/* just in case */
203 						number_of_dots = minlen;
204 					i = minlen - number_of_dots;
205 					while(number_of_dots--)
206 						len += fprintf(fp, "#");
207 					while(i--)
208 						len += fprintf(fp, " ");
209 				} else {
210 					number_of_dots = minlen / 2;
211 					i = minlen - number_of_dots;
212 					while(number_of_dots--)
213 						len += fprintf(fp, " ");
214 					if(i) {
215 						i--;
216 						len += fprintf(fp, "?");
217 						while(i--)
218 							len += fprintf(fp, " ");
219 					}
220 				}
221 				break;
222 			case '{':
223 				inescape++;
224 				break;
225 			case '}':
226 				inescape--;
227 				break;
228 			default:
229 				len += fprintf(fp, "%%%c", *e);
230 				break;
231 			}
232 			free(sp);
233 		} else {
234 			fputc(*e, fp);
235 			if (inescape <= 0) len++;
236 		}
237 		e++;
238 	}
239 
240 	return len;
241 }
242 
transfer(transfer_info * ti)243 void transfer(transfer_info *ti)
244 {
245 	static int lastlen = 0;
246 	struct timeval now;
247 	unsigned int secs, eta;
248 	float bps;
249 
250 	if(gvSighupReceived)
251 		return;
252 
253 	while(lastlen--)
254 		fprintf(stderr, " ");
255 	fprintf(stderr, "\r");
256 
257 	if(!ti)
258 		return;
259 
260 	if(ti->finished) {
261 		if(ti->interrupted)
262 			printf(_("transfer interrupted\n"));
263 		else if(ti->ioerror)
264 			printf(_("transfer I/O error\n"));
265 	}
266 
267 	if(ti->size > ti->total_size)
268 		ti->total_size = ti->size;
269 
270 	gettimeofday(&now, 0);
271 	secs = now.tv_sec - ti->start_time.tv_sec;
272 	bps = (ti->size - ti->restart_size) / (secs ? secs : 1);
273 	if(ti->total_size != -1L) {
274 		eta = (ti->total_size - ti->size) / (bps ? bps : 1);
275 		if(eta == 0)
276 			eta += !ftp->ti.finished;
277 	}
278 	else
279 		eta = (unsigned) -1;
280 
281 	lastlen = print_transfer_string(ti->finished ? gvTransferEndString
282 									: (ti->begin ? gvTransferBeginString
283 									   : gvTransferString),
284 									stdout,
285 									ti,
286 									bps,
287 									secs,
288 									eta);
289 
290 	if(gvTransferXtermString && gvXtermTitleTerms
291 	   && strstr(gvXtermTitleTerms, gvTerm) != 0)
292 		{
293 			print_transfer_string(gvTransferXtermString,
294 								  stderr,
295 								  ti,
296 								  bps,
297 								  secs,
298 								  eta);
299 		}
300 
301 	fputc('\r', stdout);
302 	fflush(stdout);
303 }
304 
transfer_init_nohup(const char * str)305 int transfer_init_nohup(const char *str)
306 {
307 	if(!str)
308   {
309 		if (asprintf(&nohup_logfile, "%s/nohup/nohup.%u",
310 				 gvWorkingDirectory, getpid()) == -1)
311       return -1;
312   }
313 	else
314 		nohup_logfile = tilde_expand_home(str, gvLocalHomeDir);
315 
316 	if(logfp)
317 		fclose(logfp);
318 
319 	logfp = fopen(nohup_logfile, "w");
320 	if(!logfp) {
321 		perror(nohup_logfile);
322 		free(nohup_logfile);
323     nohup_logfile = NULL;
324 		logfp = NULL;
325 		return -1;
326 	}
327 
328 	ftp_err(_("Redirecting output to %s\n"), nohup_logfile);
329 
330 	setbuf(logfp, 0); /* change buffering */
331 	return 0;
332 }
333 
term_handler(int signum)334 static void term_handler(int signum)
335 {
336 	time_t now = time(0);
337 
338 	fprintf(stderr, "%s [%sB of ", ftp->ti.remote_name,
339 			human_size(ftp->ti.size));
340 	fprintf(stderr, "%sB]\n", human_size(ftp->ti.total_size));
341 	printf(_("SIGTERM (terminate) received, exiting...\n"));
342 	printf(_("Transfer aborted %s"), ctime(&now));
343 	if(ftp->ti.remote_name)
344 		printf(_("%s may not have transferred correctly\n"),
345 			   ftp->ti.remote_name);
346 
347 	transfer_mail_msg(_("SIGTERM (terminate) received, exiting...\n"));
348 	transfer_mail_msg(_("Transfer aborted %s"), ctime(&now));
349 	transfer_mail_msg(_("%s may not have transferred correctly\n"),
350 					  ftp->ti.remote_name);
351 
352 	transfer_end_nohup();
353 }
354 
transfer_begin_nohup(int argc,char ** argv)355 void transfer_begin_nohup(int argc, char **argv)
356 {
357 	nohup_start_time = time(0);
358 
359 	ftp_set_signal(SIGHUP, SIG_IGN); /* ignore signals */
360 	ftp_set_signal(SIGINT, SIG_IGN);
361 	ftp_set_signal(SIGQUIT, SIG_IGN);
362 	ftp_set_signal(SIGTSTP, SIG_IGN);
363 	ftp_set_signal(SIGTERM, term_handler);
364 	dup2(fileno(logfp), fileno(stdout));
365 	dup2(fileno(logfp), fileno(stderr));
366 #if 0 && (defined(HAVE_SETPROCTITLE) || defined(linux))
367 	if(gvUseEnvString)
368 		setproctitle("%s, nohup, %s", ftp->url->hostname, nohup_logfile);
369 #endif
370 
371 	printf(_("Connected to %s as user %s\n"),
372 		   ftp->url->hostname,
373 		   ftp->url->username);
374 	if(argv)
375 		printf(_("Transfer started %s\n"), ctime(&nohup_start_time));
376 	else
377 		printf(_("Transfer received SIGHUP %s\n"), ctime(&nohup_start_time));
378 	nohup_command = argv ? args_cat(argc, argv, 0) : 0;
379 	if(argv)
380 		printf(_("Command: '%s'\n"), nohup_command);
381 	printf("pid: %u\n\n", getpid());
382 }
383 
transfer_mail_msg(const char * fmt,...)384 void transfer_mail_msg(const char *fmt, ...)
385 {
386 	static bool opened = false;
387 	va_list ap;
388 
389 	if(!gvNohupMailAddress)
390 		return;
391 
392 	if(!opened) {
393 		mailfp = tmpfile();
394 		opened = true;
395 		if(mailfp) {
396 			setbuf(logfp, 0); /* change buffering */
397 			transfer_mail_msg(_("From: yafc@%s\n"
398 								"Subject: yafc transfer finished on %s\n"
399 								"\n"
400 								"This is an automatic message sent by yafc\n"
401 								"Your transfer is finished!\n"
402 								"\n"
403 								"connected to %s as user %s\n"
404 								"command was: %s\n"
405 								"started %s\n"
406 								"\n"
407 								"(please do not reply to this mail)\n"
408 								"\n"),
409 							  gvLocalHost, gvLocalHost,
410 							  ftp->url->hostname, ftp->url->username,
411 							  nohup_command ? nohup_command :
412 							  "(unknown, SIGHUPed)",
413 							  ctime(&nohup_start_time));
414 		}
415 	}
416 
417 	if(mailfp == 0)
418 		return;
419 
420 	va_start(ap, fmt);
421 	vfprintf(mailfp, fmt, ap);
422 	va_end(ap);
423 }
424 
transfer_end_nohup(void)425 void transfer_end_nohup(void)
426 {
427 	nohup_end_time = time(0);
428 	printf(_("Done\nTransfer ended %s\n"), ctime(&nohup_end_time));
429 
430 	if(gvNohupMailAddress) {
431 		char *cmd;
432 
433 		if (asprintf(&cmd, "%s %s", gvSendmailPath, gvNohupMailAddress) == -1)
434     {
435       fprintf(stderr, _("Failed to allocate memory.\n"));
436       return;
437     }
438 		FILE* fp = popen(cmd, "w");
439 		free(cmd);
440 
441 		if(fp == 0)
442 			printf(_("Unable to send mail (using %s)\n"), gvSendmailPath);
443 		else {
444 			transfer_mail_msg("\n");
445 			rewind(mailfp);
446 			while(true) {
447 				int c = fgetc(mailfp);
448 				if(c == EOF) {
449 					fclose(mailfp);
450 					break;
451 				}
452 				fputc(c, fp);
453 			}
454 			pclose(fp);
455 		}
456 	}
457 
458 	ftp_quit_all();
459 	list_free(gvFtpList);
460   gvFtpList = NULL;
461 	free(nohup_logfile);
462 	free(nohup_command);
463 	gvars_destroy();
464 	exit(0);
465 }
466 
ascii_transfer(const char * mask)467 bool ascii_transfer(const char *mask)
468 {
469 	listitem *li;
470 
471 	li = gvAsciiMasks->first;
472 	while(li) {
473 		if(fnmatch((char *)li->data, mask, 0) != FNM_NOMATCH)
474 			return true;
475 		li = li->next;
476 	}
477 	return false;
478 }
479 
transfer_first(const char * mask)480 bool transfer_first(const char *mask)
481 {
482 	listitem *li;
483 
484 	li = gvTransferFirstMasks->first;
485 	while(li) {
486 		if(fnmatch((char *)li->data, mask, 0) != FNM_NOMATCH)
487 			return true;
488 		li = li->next;
489 	}
490 	return false;
491 }
492 
ignore(const char * mask)493 bool ignore(const char *mask)
494 {
495 	listitem *li;
496 
497 	li = gvIgnoreMasks->first;
498 	while(li) {
499 		if(fnmatch((char *)li->data, mask, 0) != FNM_NOMATCH)
500 			return true;
501 		li = li->next;
502 	}
503 	return false;
504 }
505 
transfer_nextfile(list * gl,listitem ** li,bool removeitem)506 void transfer_nextfile(list *gl, listitem **li, bool removeitem)
507 {
508 	if(removeitem) {
509 		listitem *tmp = (*li)->next;
510 		list_removeitem(gl, *li);
511 		*li = tmp;
512 	} else
513 		*li = (*li)->next;
514 }
515