Bug Summary

File:src/usr.bin/ftp/util.c
Warning:line 996, column 8
Null pointer passed as 1st argument to string length function

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name util.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/ftp/obj -resource-dir /usr/local/llvm16/lib/clang/16 -internal-isystem /usr/local/llvm16/lib/clang/16/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/ftp/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fcf-protection=branch -fno-jump-tables -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/scan/2024-01-11-140451-98009-1 -x c /usr/src/usr.bin/ftp/util.c
1/* $OpenBSD: util.c,v 1.98 2023/03/08 04:43:11 guenther Exp $ */
2/* $NetBSD: util.c,v 1.12 1997/08/18 10:20:27 lukem Exp $ */
3
4/*-
5 * Copyright (c) 1997-1999 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Luke Mewburn.
10 *
11 * This code is derived from software contributed to The NetBSD Foundation
12 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
13 * NASA Ames Research Center.
14 *
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
17 * are met:
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
28 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37/*
38 * Copyright (c) 1985, 1989, 1993, 1994
39 * The Regents of the University of California. All rights reserved.
40 *
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that the following conditions
43 * are met:
44 * 1. Redistributions of source code must retain the above copyright
45 * notice, this list of conditions and the following disclaimer.
46 * 2. Redistributions in binary form must reproduce the above copyright
47 * notice, this list of conditions and the following disclaimer in the
48 * documentation and/or other materials provided with the distribution.
49 * 3. Neither the name of the University nor the names of its contributors
50 * may be used to endorse or promote products derived from this software
51 * without specific prior written permission.
52 *
53 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63 * SUCH DAMAGE.
64 */
65
66/*
67 * FTP User Program -- Misc support routines
68 */
69#include <sys/ioctl.h>
70#include <sys/socket.h>
71#include <sys/time.h>
72#include <arpa/ftp.h>
73
74#include <ctype.h>
75#include <err.h>
76#include <errno(*__errno()).h>
77#include <fcntl.h>
78#include <libgen.h>
79#include <glob.h>
80#include <poll.h>
81#include <pwd.h>
82#include <signal.h>
83#include <stdio.h>
84#include <stdlib.h>
85#include <string.h>
86#include <time.h>
87#include <unistd.h>
88
89#include "ftp_var.h"
90#include "pathnames.h"
91
92#define MINIMUM(a, b)(((a) < (b)) ? (a) : (b)) (((a) < (b)) ? (a) : (b))
93#define MAXIMUM(a, b)(((a) > (b)) ? (a) : (b)) (((a) > (b)) ? (a) : (b))
94
95static void updateprogressmeter(int);
96
97/*
98 * Connect to peer server and
99 * auto-login, if possible.
100 */
101void
102setpeer(int argc, char *argv[])
103{
104 char *host, *port;
105
106 if (connected) {
107 fprintf(ttyout, "Already connected to %s, use close first.\n",
108 hostname);
109 code = -1;
110 return;
111 }
112#ifndef SMALL
113 if (argc < 2)
114 (void)another(&argc, &argv, "to");
115 if (argc < 2 || argc > 3) {
116 fprintf(ttyout, "usage: %s host [port]\n", argv[0]);
117 code = -1;
118 return;
119 }
120#endif /* !SMALL */
121 if (gatemode)
122 port = gateport;
123 else
124 port = ftpport;
125 if (argc > 2)
126 port = argv[2];
127
128 if (gatemode) {
129 if (gateserver == NULL((void *)0) || *gateserver == '\0')
130 errx(1, "gateserver not defined (shouldn't happen)");
131 host = hookup(gateserver, port);
132 } else
133 host = hookup(argv[1], port);
134
135 if (host) {
136 int overbose;
137
138 if (gatemode) {
139 if (command("PASSERVE %s", argv[1]) != COMPLETE2)
140 return;
141 if (verbose)
142 fprintf(ttyout,
143 "Connected via pass-through server %s\n",
144 gateserver);
145 }
146
147 connected = 1;
148 /*
149 * Set up defaults for FTP.
150 */
151 (void)strlcpy(formname, "non-print", sizeof formname);
152 form = FORM_N1;
153 (void)strlcpy(modename, "stream", sizeof modename);
154 mode = MODE_S1;
155 (void)strlcpy(structname, "file", sizeof structname);
156 stru = STRU_F1;
157 (void)strlcpy(bytename, "8", sizeof bytename);
158 bytesize = 8;
159
160 /*
161 * Set type to 0 (not specified by user),
162 * meaning binary by default, but don't bother
163 * telling server. We can use binary
164 * for text files unless changed by the user.
165 */
166 (void)strlcpy(typename, "binary", sizeof typename);
167 curtype = TYPE_A1;
168 type = 0;
169 if (autologin)
170 (void)ftp_login(argv[1], NULL((void *)0), NULL((void *)0));
171
172 overbose = verbose;
173#ifndef SMALL
174 if (!debug)
175#endif /* !SMALL */
176 verbose = -1;
177 if (command("SYST") == COMPLETE2 && overbose) {
178 char *cp, c;
179 c = 0;
180 cp = strchr(reply_string + 4, ' ');
181 if (cp == NULL((void *)0))
182 cp = strchr(reply_string + 4, '\r');
183 if (cp) {
184 if (cp[-1] == '.')
185 cp--;
186 c = *cp;
187 *cp = '\0';
188 }
189
190 fprintf(ttyout, "Remote system type is %s.\n", reply_string + 4);
191 if (cp)
192 *cp = c;
193 }
194 if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) {
195 if (proxy)
196 unix_proxy = 1;
197 else
198 unix_server = 1;
199 if (overbose)
200 fprintf(ttyout, "Using %s mode to transfer files.\n",
201 typename);
202 } else {
203 if (proxy)
204 unix_proxy = 0;
205 else
206 unix_server = 0;
207 }
208 verbose = overbose;
209 }
210}
211
212/*
213 * login to remote host, using given username & password if supplied
214 */
215int
216ftp_login(const char *host, char *user, char *pass)
217{
218 char tmp[80], *acctname = NULL((void *)0), host_name[HOST_NAME_MAX255+1];
219 char anonpass[LOGIN_NAME_MAX32 + 1 + HOST_NAME_MAX255+1]; /* "user@hostname" */
220 int n, aflag = 0, retry = 0;
221 struct passwd *pw;
222
223#ifndef SMALL
224 if (user == NULL((void *)0) && !anonftp) {
225 if (ruserpass(host, &user, &pass, &acctname) < 0) {
226 code = -1;
227 return (0);
228 }
229 }
230#endif /* !SMALL */
231
232 /*
233 * Set up arguments for an anonymous FTP session, if necessary.
234 */
235 if ((user == NULL((void *)0) || pass == NULL((void *)0)) && anonftp) {
236 memset(anonpass, 0, sizeof(anonpass));
237 memset(host_name, 0, sizeof(host_name));
238
239 /*
240 * Set up anonymous login password.
241 */
242 if ((user = getlogin()) == NULL((void *)0)) {
243 if ((pw = getpwuid(getuid())) == NULL((void *)0))
244 user = "anonymous";
245 else
246 user = pw->pw_name;
247 }
248 gethostname(host_name, sizeof(host_name));
249#ifndef DONT_CHEAT_ANONPASS
250 /*
251 * Every anonymous FTP server I've encountered
252 * will accept the string "username@", and will
253 * append the hostname itself. We do this by default
254 * since many servers are picky about not having
255 * a FQDN in the anonymous password. - thorpej@netbsd.org
256 */
257 snprintf(anonpass, sizeof(anonpass) - 1, "%s@",
258 user);
259#else
260 snprintf(anonpass, sizeof(anonpass) - 1, "%s@%s",
261 user, hp->h_name);
262#endif
263 pass = anonpass;
264 user = "anonymous"; /* as per RFC 1635 */
265 }
266
267tryagain:
268 if (retry)
269 user = "ftp"; /* some servers only allow "ftp" */
270
271 while (user == NULL((void *)0)) {
272 char *myname = getlogin();
273
274 if (myname == NULL((void *)0) && (pw = getpwuid(getuid())) != NULL((void *)0))
275 myname = pw->pw_name;
276 if (myname)
277 fprintf(ttyout, "Name (%s:%s): ", host, myname);
278 else
279 fprintf(ttyout, "Name (%s): ", host);
280 user = myname;
281 if (fgets(tmp, sizeof(tmp), stdin(&__sF[0])) != NULL((void *)0)) {
282 tmp[strcspn(tmp, "\n")] = '\0';
283 if (tmp[0] != '\0')
284 user = tmp;
285 } else
286 exit(0);
287 }
288 n = command("USER %s", user);
289 if (n == CONTINUE3) {
290 if (pass == NULL((void *)0))
291 pass = getpass("Password:");
292 n = command("PASS %s", pass);
293 }
294 if (n == CONTINUE3) {
295 aflag++;
296 if (acctname == NULL((void *)0))
297 acctname = getpass("Account:");
298 n = command("ACCT %s", acctname);
299 }
300 if ((n != COMPLETE2) ||
301 (!aflag && acctname != NULL((void *)0) && command("ACCT %s", acctname) != COMPLETE2)) {
302 warnx("Login %s failed.", user);
303 if (retry || !anonftp)
304 return (0);
305 else
306 retry = 1;
307 goto tryagain;
308 }
309 if (proxy)
310 return (1);
311 connected = -1;
312#ifndef SMALL
313 for (n = 0; n < macnum; ++n) {
314 if (!strcmp("init", macros[n].mac_name)) {
315 (void)strlcpy(line, "$init", sizeof line);
316 makeargv();
317 domacro(margc, margv(marg_sl->sl_str));
318 break;
319 }
320 }
321#endif /* SMALL */
322 return (1);
323}
324
325/*
326 * `another' gets another argument, and stores the new argc and argv.
327 * It reverts to the top level (via main.c's intr()) on EOF/error.
328 *
329 * Returns false if no new arguments have been added.
330 */
331#ifndef SMALL
332int
333another(int *pargc, char ***pargv, const char *prompt)
334{
335 int len = strlen(line), ret;
336
337 if (len >= sizeof(line) - 3) {
338 fputs("sorry, arguments too long.\n", ttyout);
339 intr();
340 }
341 fprintf(ttyout, "(%s) ", prompt);
342 line[len++] = ' ';
343 if (fgets(&line[len], (int)(sizeof(line) - len), stdin(&__sF[0])) == NULL((void *)0)) {
344 clearerr(stdin)(!__isthreaded ? ((void)(((&__sF[0]))->_flags &= ~
(0x0040|0x0020))) : (clearerr)((&__sF[0])))
;
345 intr();
346 }
347 len += strlen(&line[len]);
348 if (len > 0 && line[len - 1] == '\n')
349 line[len - 1] = '\0';
350 makeargv();
351 ret = margc > *pargc;
352 *pargc = margc;
353 *pargv = margv(marg_sl->sl_str);
354 return (ret);
355}
356#endif /* !SMALL */
357
358/*
359 * glob files given in argv[] from the remote server.
360 * if errbuf isn't NULL, store error messages there instead
361 * of writing to the screen.
362 * if type isn't NULL, use LIST instead of NLST, and store filetype.
363 * 'd' means directory, 's' means symbolic link, '-' means plain
364 * file.
365 */
366char *
367remglob2(char *argv[], int doswitch, char **errbuf, FILE **ftemp, char *type)
368{
369 char temp[PATH_MAX1024], *bufp, *cp, *lmode;
370 static char buf[PATH_MAX1024], **args;
371 int oldverbose, oldhash, fd;
372
373 if (!mflag) {
374 if (!doglob)
375 args = NULL((void *)0);
376 else {
377 if (*ftemp) {
378 (void)fclose(*ftemp);
379 *ftemp = NULL((void *)0);
380 }
381 }
382 return (NULL((void *)0));
383 }
384 if (!doglob) {
385 if (args == NULL((void *)0))
386 args = argv;
387 if ((cp = *++args) == NULL((void *)0))
388 args = NULL((void *)0);
389 return (cp);
390 }
391 if (*ftemp == NULL((void *)0)) {
392 int len;
393
394 cp = _PATH_TMP"/tmp/";
395 len = strlen(cp);
396 if (len + sizeof(TMPFILE"ftpXXXXXXXXXX") + (cp[len-1] != '/') > sizeof(temp)) {
397 warnx("unable to create temporary file: %s",
398 strerror(ENAMETOOLONG63));
399 return (NULL((void *)0));
400 }
401
402 (void)strlcpy(temp, cp, sizeof temp);
403 if (temp[len-1] != '/')
404 temp[len++] = '/';
405 (void)strlcpy(&temp[len], TMPFILE"ftpXXXXXXXXXX", sizeof temp - len);
406 if ((fd = mkstemp(temp)) == -1) {
407 warn("unable to create temporary file: %s", temp);
408 return (NULL((void *)0));
409 }
410 close(fd);
411 oldverbose = verbose;
412 verbose = (errbuf != NULL((void *)0)) ? -1 : 0;
413 oldhash = hash;
414 hash = 0;
415 if (doswitch)
416 pswitch(!proxy);
417 for (lmode = "w"; *++argv != NULL((void *)0); lmode = "a")
418 recvrequest(type ? "LIST" : "NLST", temp, *argv, lmode,
419 0, 0);
420 if ((code / 100) != COMPLETE2) {
421 if (errbuf != NULL((void *)0))
422 *errbuf = reply_string;
423 }
424 if (doswitch)
425 pswitch(!proxy);
426 verbose = oldverbose;
427 hash = oldhash;
428 *ftemp = fopen(temp, "r");
429 (void)unlink(temp);
430 if (*ftemp == NULL((void *)0)) {
431 if (errbuf == NULL((void *)0))
432 fputs("can't find list of remote files, oops.\n",
433 ttyout);
434 else
435 *errbuf =
436 "can't find list of remote files, oops.";
437 return (NULL((void *)0));
438 }
439 }
440#ifndef SMALL
441again:
442#endif
443 if (fgets(buf, sizeof(buf), *ftemp) == NULL((void *)0)) {
444 (void)fclose(*ftemp);
445 *ftemp = NULL((void *)0);
446 return (NULL((void *)0));
447 }
448
449 buf[strcspn(buf, "\n")] = '\0';
450 bufp = buf;
451
452#ifndef SMALL
453 if (type) {
454 parse_list(&bufp, type);
455 if (!bufp ||
456 (bufp[0] == '.' && /* LIST defaults to -a on some ftp */
457 (bufp[1] == '\0' || /* servers. Ignore '.' and '..'. */
458 (bufp[1] == '.' && bufp[2] == '\0'))))
459 goto again;
460 }
461#endif /* !SMALL */
462
463 return (bufp);
464}
465
466/*
467 * wrapper for remglob2
468 */
469char *
470remglob(char *argv[], int doswitch, char **errbuf)
471{
472 static FILE *ftemp = NULL((void *)0);
473
474 return remglob2(argv, doswitch, errbuf, &ftemp, NULL((void *)0));
475}
476
477#ifndef SMALL
478int
479confirm(const char *cmd, const char *file)
480{
481 char str[BUFSIZ1024];
482
483 if (file && (confirmrest || !interactive))
484 return (1);
485top:
486 if (file)
487 fprintf(ttyout, "%s %s? ", cmd, file);
488 else
489 fprintf(ttyout, "Continue with %s? ", cmd);
490 (void)fflush(ttyout);
491 if (fgets(str, sizeof(str), stdin(&__sF[0])) == NULL((void *)0))
492 goto quit;
493 switch (tolower((unsigned char)*str)) {
494 case '?':
495 fprintf(ttyout,
496 "? help\n"
497 "a answer yes to all\n"
498 "n answer no\n"
499 "p turn off prompt mode\n"
500 "q answer no to all\n"
501 "y answer yes\n");
502 goto top;
503 case 'a':
504 confirmrest = 1;
505 fprintf(ttyout, "Prompting off for duration of %s.\n",
506 cmd);
507 break;
508 case 'n':
509 return (0);
510 case 'p':
511 interactive = 0;
512 fputs("Interactive mode: off.\n", ttyout);
513 break;
514 case 'q':
515quit:
516 mflag = 0;
517 clearerr(stdin)(!__isthreaded ? ((void)(((&__sF[0]))->_flags &= ~
(0x0040|0x0020))) : (clearerr)((&__sF[0])))
;
518 return (0);
519 case 'y':
520 return(1);
521 break;
522 default:
523 fprintf(ttyout, "?, a, n, p, q, y "
524 "are the only acceptable commands!\n");
525 goto top;
526 break;
527 }
528 return (1);
529}
530#endif /* !SMALL */
531
532/*
533 * Glob a local file name specification with
534 * the expectation of a single return value.
535 * Can't control multiple values being expanded
536 * from the expression, we return only the first.
537 */
538int
539globulize(char **cpp)
540{
541 glob_t gl;
542 int flags;
543
544 if (!doglob)
545 return (1);
546
547 flags = GLOB_BRACE0x0080|GLOB_NOCHECK0x0010|GLOB_QUOTE0x0400|GLOB_TILDE0x0800;
548 memset(&gl, 0, sizeof(gl));
549 if (glob(*cpp, flags, NULL((void *)0), &gl) ||
550 gl.gl_pathc == 0) {
551 warnx("%s: not found", *cpp);
552 globfree(&gl);
553 return (0);
554 }
555 /* XXX: caller should check if *cpp changed, and
556 * free(*cpp) if that is the case
557 */
558 *cpp = strdup(gl.gl_pathv[0]);
559 if (*cpp == NULL((void *)0))
560 err(1, NULL((void *)0));
561 globfree(&gl);
562 return (1);
563}
564
565/*
566 * determine size of remote file
567 */
568off_t
569remotesize(const char *file, int noisy)
570{
571 int overbose;
572 off_t size;
573
574 overbose = verbose;
575 size = -1;
576#ifndef SMALL
577 if (!debug)
578#endif /* !SMALL */
579 verbose = -1;
580 if (command("SIZE %s", file) == COMPLETE2) {
581 char *cp, *ep;
582
583 cp = strchr(reply_string, ' ');
584 if (cp != NULL((void *)0)) {
585 cp++;
586 size = strtoll(cp, &ep, 10);
587 if (*ep != '\0' && !isspace((unsigned char)*ep))
588 size = -1;
589 }
590 } else if (noisy
591#ifndef SMALL
592 && !debug
593#endif /* !SMALL */
594 ) {
595 fputs(reply_string, ttyout);
596 fputc('\n', ttyout);
597 }
598 verbose = overbose;
599 return (size);
600}
601
602/*
603 * determine last modification time (in GMT) of remote file
604 */
605time_t
606remotemodtime(const char *file, int noisy)
607{
608 int overbose;
609 time_t rtime;
610 int ocode;
611
612 overbose = verbose;
613 ocode = code;
614 rtime = -1;
615#ifndef SMALL
616 if (!debug)
617#endif /* !SMALL */
618 verbose = -1;
619 if (command("MDTM %s", file) == COMPLETE2) {
620 struct tm timebuf;
621 int yy, mo, day, hour, min, sec;
622 /*
623 * time-val = 14DIGIT [ "." 1*DIGIT ]
624 * YYYYMMDDHHMMSS[.sss]
625 * mdtm-response = "213" SP time-val CRLF / error-response
626 */
627 /* TODO: parse .sss as well, use timespecs. */
628 char *timestr = reply_string;
629
630 /* Repair `19%02d' bug on server side */
631 while (!isspace((unsigned char)*timestr))
632 timestr++;
633 while (isspace((unsigned char)*timestr))
634 timestr++;
635 if (strncmp(timestr, "191", 3) == 0) {
636 fprintf(ttyout,
637 "Y2K warning! Fixed incorrect time-val received from server.\n");
638 timestr[0] = ' ';
639 timestr[1] = '2';
640 timestr[2] = '0';
641 }
642 sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
643 &day, &hour, &min, &sec);
644 memset(&timebuf, 0, sizeof(timebuf));
645 timebuf.tm_sec = sec;
646 timebuf.tm_min = min;
647 timebuf.tm_hour = hour;
648 timebuf.tm_mday = day;
649 timebuf.tm_mon = mo - 1;
650 timebuf.tm_year = yy - 1900;
651 timebuf.tm_isdst = -1;
652 rtime = mktime(&timebuf);
653 if (rtime == -1 && (noisy
654#ifndef SMALL
655 || debug
656#endif /* !SMALL */
657 ))
658 fprintf(ttyout, "Can't convert %s to a time.\n", reply_string);
659 else
660 rtime += timebuf.tm_gmtoff; /* conv. local -> GMT */
661 } else if (noisy
662#ifndef SMALL
663 && !debug
664#endif /* !SMALL */
665 ) {
666 fputs(reply_string, ttyout);
667 fputc('\n', ttyout);
668 }
669 verbose = overbose;
670 if (rtime == -1)
671 code = ocode;
672 return (rtime);
673}
674
675/*
676 * Ensure file is in or under dir.
677 * Returns 1 if so, 0 if not (or an error occurred).
678 */
679int
680fileindir(const char *file, const char *dir)
681{
682 char parentdirbuf[PATH_MAX1024], *parentdir;
683 char realdir[PATH_MAX1024];
684 size_t dirlen;
685
686 /* determine parent directory of file */
687 (void)strlcpy(parentdirbuf, file, sizeof(parentdirbuf));
688 parentdir = dirname(parentdirbuf);
689 if (strcmp(parentdir, ".") == 0)
690 return 1; /* current directory is ok */
691
692 /* find the directory */
693 if (realpath(parentdir, realdir) == NULL((void *)0)) {
694 warn("Unable to determine real path of `%s'", parentdir);
695 return 0;
696 }
697 if (realdir[0] != '/') /* relative result is ok */
698 return 1;
699
700 dirlen = strlen(dir);
701 if (strncmp(realdir, dir, dirlen) == 0 &&
702 (realdir[dirlen] == '/' || realdir[dirlen] == '\0'))
703 return 1;
704 return 0;
705}
706
707
708/*
709 * Returns true if this is the controlling/foreground process, else false.
710 */
711int
712foregroundproc(void)
713{
714 static pid_t pgrp = -1;
715 int ctty_pgrp;
716
717 if (pgrp == -1)
718 pgrp = getpgrp();
719
720 return((ioctl(STDOUT_FILENO1, TIOCGPGRP((unsigned long)0x40000000 | ((sizeof(int) & 0x1fff) <<
16) | ((('t')) << 8) | ((119)))
, &ctty_pgrp) != -1 &&
721 ctty_pgrp == pgrp));
722}
723
724static void
725updateprogressmeter(int signo)
726{
727 int save_errno = errno(*__errno());
728
729 /* update progressmeter if foreground process or in -m mode */
730 if (foregroundproc() || progress == -1)
731 progressmeter(0, NULL((void *)0));
732 errno(*__errno()) = save_errno;
733}
734
735/*
736 * Display a transfer progress bar if progress is non-zero.
737 * SIGALRM is hijacked for use by this function.
738 * - Before the transfer, set filesize to size of file (or -1 if unknown),
739 * and call with flag = -1. This starts the once per second timer,
740 * and a call to updateprogressmeter() upon SIGALRM.
741 * - During the transfer, updateprogressmeter will call progressmeter
742 * with flag = 0
743 * - After the transfer, call with flag = 1
744 */
745static struct timespec start;
746
747char *action;
748
749void
750progressmeter(int flag, const char *filename)
751{
752 /*
753 * List of order of magnitude prefixes.
754 * The last is `P', as 2^64 = 16384 Petabytes
755 */
756 static const char prefixes[] = " KMGTP";
757
758 static struct timespec lastupdate;
759 static off_t lastsize;
760 static char *title = NULL((void *)0);
761 struct timespec now, td, wait;
762 off_t cursize, abbrevsize;
763 double elapsed;
764 int ratio, barlength, i, remaining, overhead = 30;
765 char buf[512], *filenamebuf;
766
767 if (flag == -1) {
768 clock_gettime(CLOCK_MONOTONIC3, &start);
769 lastupdate = start;
770 lastsize = restart_point;
771 }
772 clock_gettime(CLOCK_MONOTONIC3, &now);
773 if (!progress || filesize < 0)
774 return;
775 cursize = bytes + restart_point;
776
777 if (filesize)
778 ratio = cursize * 100 / filesize;
779 else
780 ratio = 100;
781 ratio = MAXIMUM(ratio, 0)(((ratio) > (0)) ? (ratio) : (0));
782 ratio = MINIMUM(ratio, 100)(((ratio) < (100)) ? (ratio) : (100));
783 if (!verbose && flag == -1) {
784 if ((filenamebuf = strdup(filename)) != NULL((void *)0) &&
785 (filename = basename(filenamebuf)) != NULL((void *)0)) {
786 free(title);
787 title = strdup(filename);
788 }
789 free(filenamebuf);
790 }
791
792 buf[0] = 0;
793 if (!verbose && action != NULL((void *)0)) {
794 int l = strlen(action);
795 char *dotdot = "";
796
797 if (l < 7)
798 l = 7;
799 else if (l > 12) {
800 l = 12;
801 dotdot = "...";
802 overhead += 3;
803 }
804 snprintf(buf, sizeof(buf), "\r%-*.*s%s ", l, l, action,
805 dotdot);
806 overhead += l + 1;
807 } else
808 snprintf(buf, sizeof(buf), "\r");
809
810 if (!verbose && title != NULL((void *)0)) {
811 int l = strlen(title);
812 char *dotdot = "";
813
814 if (l < 12)
815 l = 12;
816 else if (l > 25) {
817 l = 22;
818 dotdot = "...";
819 overhead += 3;
820 }
821 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
822 "%-*.*s%s %3d%% ", l, l, title,
823 dotdot, ratio);
824 overhead += l + 1;
825 } else
826 snprintf(buf, sizeof(buf), "\r%3d%% ", ratio);
827
828 barlength = ttywidth - overhead;
829 if (barlength > 0) {
830 i = barlength * ratio / 100;
831 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
832 "|%.*s%*s|", i,
833 "*******************************************************"
834 "*******************************************************"
835 "*******************************************************"
836 "*******************************************************"
837 "*******************************************************"
838 "*******************************************************"
839 "*******************************************************",
840 barlength - i, "");
841 }
842
843 i = 0;
844 abbrevsize = cursize;
845 while (abbrevsize >= 100000 && i < sizeof(prefixes)-1) {
846 i++;
847 abbrevsize >>= 10;
848 }
849 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
850 " %5lld %c%c ", (long long)abbrevsize, prefixes[i],
851 prefixes[i] == ' ' ? ' ' : 'B');
852
853 timespecsub(&now, &lastupdate, &wait)do { (&wait)->tv_sec = (&now)->tv_sec - (&lastupdate
)->tv_sec; (&wait)->tv_nsec = (&now)->tv_nsec
- (&lastupdate)->tv_nsec; if ((&wait)->tv_nsec
< 0) { (&wait)->tv_sec--; (&wait)->tv_nsec +=
1000000000L; } } while (0)
;
854 if (cursize > lastsize) {
855 lastupdate = now;
856 lastsize = cursize;
857 if (wait.tv_sec >= STALLTIME5) { /* fudge out stalled time */
858 start.tv_sec += wait.tv_sec;
859 start.tv_nsec += wait.tv_nsec;
860 }
861 wait.tv_sec = 0;
862 }
863
864 timespecsub(&now, &start, &td)do { (&td)->tv_sec = (&now)->tv_sec - (&start
)->tv_sec; (&td)->tv_nsec = (&now)->tv_nsec -
(&start)->tv_nsec; if ((&td)->tv_nsec < 0) {
(&td)->tv_sec--; (&td)->tv_nsec += 1000000000L
; } } while (0)
;
865 elapsed = td.tv_sec + (td.tv_nsec / 1000000000.0);
866
867 if (flag == 1) {
868 i = (int)elapsed / 3600;
869 if (i)
870 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
871 "%2d:", i);
872 else
873 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
874 " ");
875 i = (int)elapsed % 3600;
876 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
877 "%02d:%02d ", i / 60, i % 60);
878 } else if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) {
879 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
880 " --:-- ETA");
881 } else if (wait.tv_sec >= STALLTIME5) {
882 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
883 " - stalled -");
884 } else {
885 remaining = (int)((filesize - restart_point) /
886 (bytes / elapsed) - elapsed);
887 i = remaining / 3600;
888 if (i)
889 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
890 "%2d:", i);
891 else
892 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
893 " ");
894 i = remaining % 3600;
895 snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
896 "%02d:%02d ETA", i / 60, i % 60);
897 }
898 (void)write(fileno(ttyout)(!__isthreaded ? ((ttyout)->_file) : (fileno)(ttyout)), buf, strlen(buf));
899
900 if (flag == -1) {
901 (void)signal(SIGALRM14, updateprogressmeter);
902 alarmtimer(1); /* set alarm timer for 1 Hz */
903 } else if (flag == 1) {
904 alarmtimer(0);
905 (void)putc('\n', ttyout)(!__isthreaded ? __sputc('\n', ttyout) : (putc)('\n', ttyout)
)
;
906 free(title);
907 title = NULL((void *)0);
908 }
909 fflush(ttyout);
910}
911
912/*
913 * Display transfer statistics.
914 * Requires start to be initialised by progressmeter(-1),
915 * direction to be defined by xfer routines, and filesize and bytes
916 * to be updated by xfer routines
917 * If siginfo is nonzero, an ETA is displayed, and the output goes to STDERR
918 * instead of TTYOUT.
919 */
920void
921ptransfer(int siginfo)
922{
923 struct timespec now, td;
924 double elapsed, pace;
925 off_t bs;
926 int meg, remaining, hh;
927 char buf[100];
928
929 if (!verbose && !siginfo)
930 return;
931
932 clock_gettime(CLOCK_MONOTONIC3, &now);
933 timespecsub(&now, &start, &td)do { (&td)->tv_sec = (&now)->tv_sec - (&start
)->tv_sec; (&td)->tv_nsec = (&now)->tv_nsec -
(&start)->tv_nsec; if ((&td)->tv_nsec < 0) {
(&td)->tv_sec--; (&td)->tv_nsec += 1000000000L
; } } while (0)
;
934 elapsed = td.tv_sec + (td.tv_nsec / 1000000000.0);
935 bs = bytes / (elapsed == 0.0 ? 1 : elapsed);
936 meg = 0;
937 if (bs > (1024 * 1024))
938 meg = 1;
939
940 pace = bs / (1024.0 * (meg ? 1024.0 : 1.0));
941 (void)snprintf(buf, sizeof(buf),
942 "%lld byte%s %s in %lld.%02d seconds (%lld.%02d %sB/s)\n",
943 (long long)bytes, bytes == 1 ? "" : "s", direction,
944 (long long)elapsed, (int)(elapsed * 100.0) % 100,
945 (long long)pace, (int)(pace * 100.0) % 100,
946 meg ? "M" : "K");
947
948 if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0 &&
949 bytes + restart_point <= filesize) {
950 remaining = (int)((filesize - restart_point) /
951 (bytes / elapsed) - elapsed);
952 hh = remaining / 3600;
953 remaining %= 3600;
954
955 /* "buf+len(buf) -1" to overwrite \n */
956 snprintf(buf + strlen(buf) - 1, sizeof(buf) - strlen(buf),
957 " ETA: %02d:%02d:%02d\n", hh, remaining / 60,
958 remaining % 60);
959 }
960 (void)write(siginfo ? STDERR_FILENO2 : fileno(ttyout)(!__isthreaded ? ((ttyout)->_file) : (fileno)(ttyout)), buf, strlen(buf));
961}
962
963/*
964 * List words in stringlist, vertically arranged
965 */
966#ifndef SMALL
967void
968list_vertical(StringList *sl)
969{
970 int i, j, w;
971 int columns, width, lines;
972 char *p;
973
974 width = 0;
975
976 for (i = 0 ; i < sl->sl_cur ; i++) {
1
Assuming 'i' is >= field 'sl_cur'
2
Loop condition is false. Execution continues on line 981
977 w = strlen(sl->sl_str[i]);
978 if (w > width)
979 width = w;
980 }
981 width = (width + 8) &~ 7;
982
983 columns = ttywidth / width;
984 if (columns == 0)
3
Assuming 'columns' is not equal to 0
4
Taking false branch
985 columns = 1;
986 lines = (sl->sl_cur + columns - 1) / columns;
987 for (i = 0; i < lines; i++) {
5
Assuming 'i' is < 'lines'
6
Loop condition is true. Entering loop body
15
Assuming 'i' is < 'lines'
16
Loop condition is true. Entering loop body
988 for (j = 0; j
16.1
'j' is < 'columns'
< columns
; j++) {
7
Assuming 'j' is < 'columns'
8
Loop condition is true. Entering loop body
17
Loop condition is true. Entering loop body
989 p = sl->sl_str[j * lines + i];
18
Value assigned to 'p'
990 if (p)
9
Assuming 'p' is null
10
Taking false branch
19
Assuming 'p' is null
20
Taking false branch
991 fputs(p, ttyout);
992 if (j * lines + i + lines >= sl->sl_cur) {
11
Taking true branch
21
Assuming the condition is false
22
Taking false branch
993 putc('\n', ttyout)(!__isthreaded ? __sputc('\n', ttyout) : (putc)('\n', ttyout)
)
;
12
Assuming '__isthreaded' is not equal to 0
13
'?' condition is false
994 break;
14
Execution continues on line 987
995 }
996 w = strlen(p);
23
Null pointer passed as 1st argument to string length function
997 while (w < width) {
998 w = (w + 8) &~ 7;
999 (void)putc('\t', ttyout)(!__isthreaded ? __sputc('\t', ttyout) : (putc)('\t', ttyout)
)
;
1000 }
1001 }
1002 }
1003}
1004#endif /* !SMALL */
1005
1006/*
1007 * Update the global ttywidth value, using TIOCGWINSZ.
1008 */
1009void
1010setttywidth(int signo)
1011{
1012 int save_errno = errno(*__errno());
1013 struct winsize winsize;
1014
1015 if (ioctl(fileno(ttyout)(!__isthreaded ? ((ttyout)->_file) : (fileno)(ttyout)), TIOCGWINSZ((unsigned long)0x40000000 | ((sizeof(struct winsize) & 0x1fff
) << 16) | ((('t')) << 8) | ((104)))
, &winsize) != -1)
1016 ttywidth = winsize.ws_col ? winsize.ws_col : 80;
1017 else
1018 ttywidth = 80;
1019 errno(*__errno()) = save_errno;
1020}
1021
1022/*
1023 * Set the SIGALRM interval timer for wait seconds, 0 to disable.
1024 */
1025void
1026alarmtimer(int wait)
1027{
1028 int save_errno = errno(*__errno());
1029 struct itimerval itv;
1030
1031 itv.it_value.tv_sec = wait;
1032 itv.it_value.tv_usec = 0;
1033 itv.it_interval = itv.it_value;
1034 setitimer(ITIMER_REAL0, &itv, NULL((void *)0));
1035 errno(*__errno()) = save_errno;
1036}
1037
1038/*
1039 * Setup or cleanup EditLine structures
1040 */
1041#ifndef SMALL
1042void
1043controlediting(void)
1044{
1045 HistEvent hev;
1046
1047 if (editing && el == NULL((void *)0) && hist == NULL((void *)0)) {
1048 el = el_init(__progname, stdin(&__sF[0]), ttyout, stderr(&__sF[2])); /* init editline */
1049 hist = history_init(); /* init the builtin history */
1050 history(hist, &hev, H_SETSIZE1, 100); /* remember 100 events */
1051 el_set(el, EL_HIST10, history, hist); /* use history */
1052
1053 el_set(el, EL_EDITOR2, "emacs"); /* default editor is emacs */
1054 el_set(el, EL_PROMPT0, prompt); /* set the prompt function */
1055
1056 /* add local file completion, bind to TAB */
1057 el_set(el, EL_ADDFN9, "ftp-complete",
1058 "Context sensitive argument completion",
1059 complete);
1060 el_set(el, EL_BIND4, "^I", "ftp-complete", NULL((void *)0));
1061
1062 el_source(el, NULL((void *)0)); /* read ~/.editrc */
1063 el_set(el, EL_SIGNAL3, 1);
1064 } else if (!editing) {
1065 if (hist) {
1066 history_end(hist);
1067 hist = NULL((void *)0);
1068 }
1069 if (el) {
1070 el_end(el);
1071 el = NULL((void *)0);
1072 }
1073 }
1074}
1075#endif /* !SMALL */
1076
1077/*
1078 * connect(2) with an optional timeout if secs > 0.
1079 */
1080int
1081timed_connect(int s, const struct sockaddr *name, socklen_t namelen, int secs)
1082{
1083 struct timespec now, target, timebuf, *timeout = NULL((void *)0);
1084 int flags, nready, optval, ret = -1;
1085 socklen_t optlen;
1086 struct pollfd pfd;
1087
1088 if (secs > 0) {
1089 timebuf.tv_sec = secs;
1090 timebuf.tv_nsec = 0;
1091 timeout = &timebuf;
1092 clock_gettime(CLOCK_MONOTONIC3, &target);
1093 timespecadd(&target, timeout, &target)do { (&target)->tv_sec = (&target)->tv_sec + (timeout
)->tv_sec; (&target)->tv_nsec = (&target)->tv_nsec
+ (timeout)->tv_nsec; if ((&target)->tv_nsec >=
1000000000L) { (&target)->tv_sec++; (&target)->
tv_nsec -= 1000000000L; } } while (0)
;
1094 }
1095
1096 flags = fcntl(s, F_GETFL3, 0);
1097 if (flags == -1) {
1098 warn("fcntl(F_GETFL)");
1099 return -1;
1100 }
1101 if (!(flags & O_NONBLOCK0x0004)) {
1102 if (fcntl(s, F_SETFL4, flags | O_NONBLOCK0x0004) == -1) {
1103 warn("fcntl(F_SETFL)");
1104 return -1;
1105 }
1106 }
1107
1108 ret = connect(s, name, namelen);
1109 if (ret == 0 || errno(*__errno()) != EINPROGRESS36)
1110 goto done;
1111
1112 for (;;) {
1113 pfd.fd = s;
1114 pfd.events = POLLOUT0x0004;
1115 nready = ppoll(&pfd, 1, timeout, NULL((void *)0));
1116 switch (nready) {
1117 case -1:
1118 if (errno(*__errno()) != EINTR4 && errno(*__errno()) != EAGAIN35) {
1119 warn("ppoll");
1120 goto done;
1121 }
1122 if (timeout == NULL((void *)0))
1123 continue;
1124 clock_gettime(CLOCK_MONOTONIC3, &now);
1125 timespecsub(&now, &target, timeout)do { (timeout)->tv_sec = (&now)->tv_sec - (&target
)->tv_sec; (timeout)->tv_nsec = (&now)->tv_nsec -
(&target)->tv_nsec; if ((timeout)->tv_nsec < 0)
{ (timeout)->tv_sec--; (timeout)->tv_nsec += 1000000000L
; } } while (0)
;
1126 if (timeout->tv_sec >= 0)
1127 continue;
1128 /* FALLTHROUGH */
1129 case 0:
1130 errno(*__errno()) = ETIMEDOUT60;
1131 goto done;
1132 default:
1133 optlen = sizeof(optval);
1134 ret = getsockopt(s, SOL_SOCKET0xffff, SO_ERROR0x1007, &optval,
1135 &optlen);
1136 if (ret == 0 && optval != 0) {
1137 ret = -1;
1138 errno(*__errno()) = optval;
1139 }
1140 goto done;
1141 }
1142 }
1143
1144done:
1145 if (!(flags & O_NONBLOCK0x0004)) {
1146 if (fcntl(s, F_SETFL4, flags) == -1) {
1147 warn("fcntl(F_SETFL)");
1148 ret = -1;
1149 }
1150 }
1151
1152 return ret;
1153}
1154
1155#ifndef SMALL
1156ssize_t
1157http_time(time_t t, char *tmbuf, size_t len)
1158{
1159 struct tm tm;
1160
1161 /* New HTTP/1.1 RFC 7231 prefers IMF-fixdate from RFC 5322 */
1162 if (gmtime_r(&t, &tm) == NULL((void *)0))
1163 return 0;
1164 else
1165 return (strftime(tmbuf, len, "%a, %d %h %Y %T %Z", &tm));
1166}
1167#endif /* !SMALL */