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