[BACK]Return to util.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ftp

Annotation of src/usr.bin/ftp/util.c, Revision 1.89

1.89    ! florian     1: /*     $OpenBSD: util.c,v 1.86 2017/12/23 20:04:23 cheloha 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:  */
1.88      jasper     36:
1.1       millert    37: /*
1.89    ! florian    38:  * Copyright (c) 1985, 1989, 1993, 1994
        !            39:  *     The Regents of the University of California.  All rights reserved.
1.1       millert    40:  *
1.89    ! florian    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.
1.87      kmos       52:  *
1.89    ! florian    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.
1.1       millert    64:  */
                     65:
1.89    ! florian    66: /*
        !            67:  * FTP User Program -- Misc support routines
        !            68:  */
        !            69: #include <sys/ioctl.h>
1.81      millert    70: #include <sys/socket.h>
1.89    ! florian    71: #include <sys/time.h>
        !            72: #include <arpa/ftp.h>
1.1       millert    73:
1.89    ! florian    74: #include <ctype.h>
1.1       millert    75: #include <err.h>
1.3       millert    76: #include <errno.h>
1.89    ! florian    77: #include <fcntl.h>
        !            78: #include <libgen.h>
        !            79: #include <glob.h>
1.80      millert    80: #include <poll.h>
1.89    ! florian    81: #include <pwd.h>
1.3       millert    82: #include <signal.h>
1.1       millert    83: #include <stdio.h>
1.89    ! florian    84: #include <stdlib.h>
1.87      kmos       85: #include <string.h>
1.89    ! florian    86: #include <time.h>
1.1       millert    87: #include <unistd.h>
                     88:
1.89    ! florian    89: #include "ftp_var.h"
        !            90: #include "pathnames.h"
1.68      deraadt    91:
1.89    ! florian    92: #define MINIMUM(a, b)  (((a) < (b)) ? (a) : (b))
        !            93: #define MAXIMUM(a, b)  (((a) > (b)) ? (a) : (b))
        !            94:
        !            95: static void updateprogressmeter(int);
1.20      millert    96:
1.1       millert    97: /*
1.89    ! florian    98:  * Connect to peer server and
        !            99:  * auto-login, if possible.
        !           100:  */
        !           101: void
        !           102: setpeer(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 || *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]) != COMPLETE)
        !           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_N;
        !           153:                (void)strlcpy(modename, "stream", sizeof modename);
        !           154:                mode = MODE_S;
        !           155:                (void)strlcpy(structname, "file", sizeof structname);
        !           156:                stru = STRU_F;
        !           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_A;
        !           168:                type = 0;
        !           169:                if (autologin)
        !           170:                        (void)ftp_login(argv[1], NULL, NULL);
        !           171:
        !           172:                overbose = verbose;
        !           173: #ifndef SMALL
        !           174:                if (!debug)
        !           175: #endif /* !SMALL */
        !           176:                        verbose = -1;
        !           177:                if (command("SYST") == COMPLETE && overbose) {
        !           178:                        char *cp, c;
        !           179:                        c = 0;
        !           180:                        cp = strchr(reply_string + 4, ' ');
        !           181:                        if (cp == NULL)
        !           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
1.1       millert   214:  */
1.87      kmos      215: int
1.89    ! florian   216: ftp_login(const char *host, char *user, char *pass)
1.1       millert   217: {
1.89    ! florian   218:        char tmp[80], *acctname = NULL, host_name[HOST_NAME_MAX+1];
        !           219:        char anonpass[LOGIN_NAME_MAX + 1 + HOST_NAME_MAX+1];    /* "user@hostname" */
        !           220:        int n, aflag = 0, retry = 0;
        !           221:        struct passwd *pw;
        !           222:
        !           223: #ifndef SMALL
        !           224:        if (user == NULL && !anonftp) {
        !           225:                if (ruserpass(host, &user, &pass, &acctname) < 0) {
        !           226:                        code = -1;
        !           227:                        return (0);
        !           228:                }
        !           229:        }
        !           230: #endif /* !SMALL */
1.1       millert   231:
1.89    ! florian   232:        /*
        !           233:         * Set up arguments for an anonymous FTP session, if necessary.
        !           234:         */
        !           235:        if ((user == NULL || pass == NULL) && 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) {
        !           243:                        if ((pw = getpwuid(getuid())) == NULL)
        !           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:        }
1.13      millert   266:
1.89    ! florian   267: tryagain:
        !           268:        if (retry)
        !           269:                user = "ftp";           /* some servers only allow "ftp" */
        !           270:
        !           271:        while (user == NULL) {
        !           272:                char *myname = getlogin();
        !           273:
        !           274:                if (myname == NULL && (pw = getpwuid(getuid())) != NULL)
        !           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) != NULL) {
        !           282:                        tmp[strcspn(tmp, "\n")] = '\0';
        !           283:                        if (tmp[0] != '\0')
        !           284:                                user = tmp;
        !           285:                }
        !           286:                else
        !           287:                        exit(0);
        !           288:        }
        !           289:        n = command("USER %s", user);
        !           290:        if (n == CONTINUE) {
        !           291:                if (pass == NULL)
        !           292:                        pass = getpass("Password:");
        !           293:                n = command("PASS %s", pass);
        !           294:        }
        !           295:        if (n == CONTINUE) {
        !           296:                aflag++;
        !           297:                if (acctname == NULL)
        !           298:                        acctname = getpass("Account:");
        !           299:                n = command("ACCT %s", acctname);
        !           300:        }
        !           301:        if ((n != COMPLETE) ||
        !           302:            (!aflag && acctname != NULL && command("ACCT %s", acctname) != COMPLETE)) {
        !           303:                warnx("Login %s failed.", user);
        !           304:                if (retry || !anonftp)
        !           305:                        return (0);
        !           306:                else
        !           307:                        retry = 1;
        !           308:                goto tryagain;
        !           309:        }
        !           310:        if (proxy)
        !           311:                return (1);
        !           312:        connected = -1;
        !           313: #ifndef SMALL
        !           314:        for (n = 0; n < macnum; ++n) {
        !           315:                if (!strcmp("init", macros[n].mac_name)) {
        !           316:                        (void)strlcpy(line, "$init", sizeof line);
        !           317:                        makeargv();
        !           318:                        domacro(margc, margv);
        !           319:                        break;
        !           320:                }
1.1       millert   321:        }
1.89    ! florian   322: #endif /* SMALL */
        !           323:        return (1);
1.6       millert   324: }
                    325:
1.89    ! florian   326: /*
        !           327:  * `another' gets another argument, and stores the new argc and argv.
        !           328:  * It reverts to the top level (via main.c's intr()) on EOF/error.
        !           329:  *
        !           330:  * Returns false if no new arguments have been added.
        !           331:  */
        !           332: #ifndef SMALL
        !           333: int
        !           334: another(int *pargc, char ***pargv, const char *prompt)
1.6       millert   335: {
1.89    ! florian   336:        int len = strlen(line), ret;
        !           337:
        !           338:        if (len >= sizeof(line) - 3) {
        !           339:                fputs("sorry, arguments too long.\n", ttyout);
        !           340:                intr();
        !           341:        }
        !           342:        fprintf(ttyout, "(%s) ", prompt);
        !           343:        line[len++] = ' ';
        !           344:        if (fgets(&line[len], (int)(sizeof(line) - len), stdin) == NULL) {
        !           345:                clearerr(stdin);
        !           346:                intr();
        !           347:        }
        !           348:        len += strlen(&line[len]);
        !           349:        if (len > 0 && line[len - 1] == '\n')
        !           350:                line[len - 1] = '\0';
        !           351:        makeargv();
        !           352:        ret = margc > *pargc;
        !           353:        *pargc = margc;
        !           354:        *pargv = margv;
        !           355:        return (ret);
1.1       millert   356: }
1.89    ! florian   357: #endif /* !SMALL */
1.1       millert   358:
1.89    ! florian   359: /*
        !           360:  * glob files given in argv[] from the remote server.
        !           361:  * if errbuf isn't NULL, store error messages there instead
        !           362:  * of writing to the screen.
        !           363:  * if type isn't NULL, use LIST instead of NLST, and store filetype.
        !           364:  * 'd' means directory, 's' means symbolic link, '-' means plain
        !           365:  * file.
        !           366:  */
        !           367: char *
        !           368: remglob2(char *argv[], int doswitch, char **errbuf, FILE **ftemp, char *type)
1.1       millert   369: {
1.89    ! florian   370:        char temp[PATH_MAX], *bufp, *cp, *lmode;
        !           371:        static char buf[PATH_MAX], **args;
        !           372:        int oldverbose, oldhash, fd;
        !           373:
        !           374:        if (!mflag) {
        !           375:                if (!doglob)
        !           376:                        args = NULL;
        !           377:                else {
        !           378:                        if (*ftemp) {
        !           379:                                (void)fclose(*ftemp);
        !           380:                                *ftemp = NULL;
        !           381:                        }
        !           382:                }
        !           383:                return (NULL);
        !           384:        }
        !           385:        if (!doglob) {
        !           386:                if (args == NULL)
        !           387:                        args = argv;
        !           388:                if ((cp = *++args) == NULL)
        !           389:                        args = NULL;
        !           390:                return (cp);
        !           391:        }
        !           392:        if (*ftemp == NULL) {
        !           393:                int len;
        !           394:
        !           395:                cp = _PATH_TMP;
        !           396:                len = strlen(cp);
        !           397:                if (len + sizeof(TMPFILE) + (cp[len-1] != '/') > sizeof(temp)) {
        !           398:                        warnx("unable to create temporary file: %s",
        !           399:                            strerror(ENAMETOOLONG));
        !           400:                        return (NULL);
        !           401:                }
1.1       millert   402:
1.89    ! florian   403:                (void)strlcpy(temp, cp, sizeof temp);
        !           404:                if (temp[len-1] != '/')
        !           405:                        temp[len++] = '/';
        !           406:                (void)strlcpy(&temp[len], TMPFILE, sizeof temp - len);
        !           407:                if ((fd = mkstemp(temp)) < 0) {
        !           408:                        warn("unable to create temporary file: %s", temp);
        !           409:                        return (NULL);
        !           410:                }
        !           411:                close(fd);
        !           412:                oldverbose = verbose;
        !           413:                verbose = (errbuf != NULL) ? -1 : 0;
        !           414:                oldhash = hash;
        !           415:                hash = 0;
        !           416:                if (doswitch)
        !           417:                        pswitch(!proxy);
        !           418:                for (lmode = "w"; *++argv != NULL; lmode = "a")
        !           419:                        recvrequest(type ? "LIST" : "NLST", temp, *argv, lmode,
        !           420:                            0, 0);
        !           421:                if ((code / 100) != COMPLETE) {
        !           422:                        if (errbuf != NULL)
        !           423:                                *errbuf = reply_string;
        !           424:                }
        !           425:                if (doswitch)
        !           426:                        pswitch(!proxy);
        !           427:                verbose = oldverbose;
        !           428:                hash = oldhash;
        !           429:                *ftemp = fopen(temp, "r");
        !           430:                (void)unlink(temp);
        !           431:                if (*ftemp == NULL) {
        !           432:                        if (errbuf == NULL)
        !           433:                                fputs("can't find list of remote files, oops.\n",
        !           434:                                    ttyout);
        !           435:                        else
        !           436:                                *errbuf =
        !           437:                                    "can't find list of remote files, oops.";
        !           438:                        return (NULL);
        !           439:                }
        !           440:        }
        !           441: #ifndef SMALL
        !           442: again:
        !           443: #endif
        !           444:        if (fgets(buf, sizeof(buf), *ftemp) == NULL) {
        !           445:                (void)fclose(*ftemp);
        !           446:                *ftemp = NULL;
        !           447:                return (NULL);
1.1       millert   448:        }
1.87      kmos      449:
1.89    ! florian   450:        buf[strcspn(buf, "\n")] = '\0';
        !           451:        bufp = buf;
        !           452:
        !           453: #ifndef SMALL
        !           454:        if (type) {
        !           455:                parse_list(&bufp, type);
        !           456:                if (!bufp ||
        !           457:                    (bufp[0] == '.' &&  /* LIST defaults to -a on some ftp */
        !           458:                    (bufp[1] == '\0' || /* servers.  Ignore '.' and '..'. */
        !           459:                    (bufp[1] == '.' && bufp[2] == '\0'))))
        !           460:                        goto again;
1.49      martynas  461:        }
1.89    ! florian   462: #endif /* !SMALL */
        !           463:
        !           464:        return (bufp);
        !           465: }
        !           466:
        !           467: /*
        !           468:  * wrapper for remglob2
        !           469:  */
        !           470: char *
        !           471: remglob(char *argv[], int doswitch, char **errbuf)
        !           472: {
        !           473:        static FILE *ftemp = NULL;
        !           474:
        !           475:        return remglob2(argv, doswitch, errbuf, &ftemp, NULL);
        !           476: }
        !           477:
        !           478: #ifndef SMALL
        !           479: int
        !           480: confirm(const char *cmd, const char *file)
        !           481: {
        !           482:        char str[BUFSIZ];
1.1       millert   483:
1.89    ! florian   484:        if (file && (confirmrest || !interactive))
        !           485:                return (1);
        !           486: top:
        !           487:        if (file)
        !           488:                fprintf(ttyout, "%s %s? ", cmd, file);
        !           489:        else
        !           490:                fprintf(ttyout, "Continue with %s? ", cmd);
        !           491:        (void)fflush(ttyout);
        !           492:        if (fgets(str, sizeof(str), stdin) == NULL)
        !           493:                goto quit;
        !           494:        switch (tolower((unsigned char)*str)) {
        !           495:                case '?':
        !           496:                        fprintf(ttyout,
        !           497:                            "?  help\n"
        !           498:                            "a  answer yes to all\n"
        !           499:                            "n  answer no\n"
        !           500:                            "p  turn off prompt mode\n"
        !           501:                            "q  answer no to all\n"
        !           502:                            "y  answer yes\n");
        !           503:                        goto top;
        !           504:                case 'a':
        !           505:                        confirmrest = 1;
        !           506:                        fprintf(ttyout, "Prompting off for duration of %s.\n",
        !           507:                            cmd);
        !           508:                        break;
        !           509:                case 'n':
        !           510:                        return (0);
        !           511:                case 'p':
        !           512:                        interactive = 0;
        !           513:                        fputs("Interactive mode: off.\n", ttyout);
        !           514:                        break;
        !           515:                case 'q':
        !           516: quit:
        !           517:                        mflag = 0;
        !           518:                        clearerr(stdin);
        !           519:                        return (0);
        !           520:                case 'y':
        !           521:                        return(1);
        !           522:                        break;
        !           523:                default:
        !           524:                        fprintf(ttyout, "?, a, n, p, q, y "
        !           525:                            "are the only acceptable commands!\n");
        !           526:                        goto top;
        !           527:                        break;
1.40      deraadt   528:        }
1.89    ! florian   529:        return (1);
        !           530: }
        !           531: #endif /* !SMALL */
        !           532:
        !           533: /*
        !           534:  * Glob a local file name specification with
        !           535:  * the expectation of a single return value.
        !           536:  * Can't control multiple values being expanded
        !           537:  * from the expression, we return only the first.
        !           538:  */
        !           539: int
        !           540: globulize(char **cpp)
        !           541: {
        !           542:        glob_t gl;
        !           543:        int flags;
1.3       millert   544:
1.89    ! florian   545:        if (!doglob)
        !           546:                return (1);
1.3       millert   547:
1.89    ! florian   548:        flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
        !           549:        memset(&gl, 0, sizeof(gl));
        !           550:        if (glob(*cpp, flags, NULL, &gl) ||
        !           551:            gl.gl_pathc == 0) {
        !           552:                warnx("%s: not found", *cpp);
        !           553:                globfree(&gl);
        !           554:                return (0);
        !           555:        }
        !           556:                /* XXX: caller should check if *cpp changed, and
        !           557:                 *      free(*cpp) if that is the case
        !           558:                 */
        !           559:        *cpp = strdup(gl.gl_pathv[0]);
        !           560:        if (*cpp == NULL)
        !           561:                err(1, NULL);
        !           562:        globfree(&gl);
        !           563:        return (1);
        !           564: }
1.47      gilles    565:
1.89    ! florian   566: /*
        !           567:  * determine size of remote file
        !           568:  */
        !           569: off_t
        !           570: remotesize(const char *file, int noisy)
        !           571: {
        !           572:        int overbose;
        !           573:        off_t size;
1.54      martynas  574:
1.89    ! florian   575:        overbose = verbose;
        !           576:        size = -1;
        !           577: #ifndef SMALL
        !           578:        if (!debug)
        !           579: #endif /* !SMALL */
        !           580:                verbose = -1;
        !           581:        if (command("SIZE %s", file) == COMPLETE) {
        !           582:                char *cp, *ep;
        !           583:
        !           584:                cp = strchr(reply_string, ' ');
        !           585:                if (cp != NULL) {
        !           586:                        cp++;
        !           587:                        size = strtoll(cp, &ep, 10);
        !           588:                        if (*ep != '\0' && !isspace((unsigned char)*ep))
        !           589:                                size = -1;
1.87      kmos      590:                }
1.89    ! florian   591:        } else if (noisy
        !           592: #ifndef SMALL
        !           593:            && !debug
        !           594: #endif /* !SMALL */
        !           595:            ) {
        !           596:                fputs(reply_string, ttyout);
        !           597:                fputc('\n', ttyout);
1.1       millert   598:        }
1.89    ! florian   599:        verbose = overbose;
        !           600:        return (size);
        !           601: }
1.1       millert   602:
1.89    ! florian   603: /*
        !           604:  * determine last modification time (in GMT) of remote file
        !           605:  */
        !           606: time_t
        !           607: remotemodtime(const char *file, int noisy)
        !           608: {
        !           609:        int overbose;
        !           610:        time_t rtime;
        !           611:        int ocode;
        !           612:
        !           613:        overbose = verbose;
        !           614:        ocode = code;
        !           615:        rtime = -1;
        !           616: #ifndef SMALL
        !           617:        if (!debug)
        !           618: #endif /* !SMALL */
        !           619:                verbose = -1;
        !           620:        if (command("MDTM %s", file) == COMPLETE) {
        !           621:                struct tm timebuf;
        !           622:                int yy, mo, day, hour, min, sec;
        !           623:                /*
        !           624:                 * time-val = 14DIGIT [ "." 1*DIGIT ]
        !           625:                 *              YYYYMMDDHHMMSS[.sss]
        !           626:                 * mdtm-response = "213" SP time-val CRLF / error-response
        !           627:                 */
        !           628:                /* TODO: parse .sss as well, use timespecs. */
        !           629:                char *timestr = reply_string;
        !           630:
        !           631:                /* Repair `19%02d' bug on server side */
        !           632:                while (!isspace((unsigned char)*timestr))
        !           633:                        timestr++;
        !           634:                while (isspace((unsigned char)*timestr))
        !           635:                        timestr++;
        !           636:                if (strncmp(timestr, "191", 3) == 0) {
        !           637:                        fprintf(ttyout,
        !           638:            "Y2K warning! Fixed incorrect time-val received from server.\n");
        !           639:                        timestr[0] = ' ';
        !           640:                        timestr[1] = '2';
        !           641:                        timestr[2] = '0';
        !           642:                }
        !           643:                sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
        !           644:                        &day, &hour, &min, &sec);
        !           645:                memset(&timebuf, 0, sizeof(timebuf));
        !           646:                timebuf.tm_sec = sec;
        !           647:                timebuf.tm_min = min;
        !           648:                timebuf.tm_hour = hour;
        !           649:                timebuf.tm_mday = day;
        !           650:                timebuf.tm_mon = mo - 1;
        !           651:                timebuf.tm_year = yy - 1900;
        !           652:                timebuf.tm_isdst = -1;
        !           653:                rtime = mktime(&timebuf);
        !           654:                if (rtime == -1 && (noisy
        !           655: #ifndef SMALL
        !           656:                    || debug
        !           657: #endif /* !SMALL */
        !           658:                    ))
        !           659:                        fprintf(ttyout, "Can't convert %s to a time.\n", reply_string);
        !           660:                else
        !           661:                        rtime += timebuf.tm_gmtoff;     /* conv. local -> GMT */
        !           662:        } else if (noisy
        !           663: #ifndef SMALL
        !           664:            && !debug
        !           665: #endif /* !SMALL */
        !           666:            ) {
        !           667:                fputs(reply_string, ttyout);
        !           668:                fputc('\n', ttyout);
1.7       deraadt   669:        }
1.89    ! florian   670:        verbose = overbose;
        !           671:        if (rtime == -1)
        !           672:                code = ocode;
        !           673:        return (rtime);
        !           674: }
1.1       millert   675:
1.89    ! florian   676: /*
        !           677:  * Ensure file is in or under dir.
        !           678:  * Returns 1 if so, 0 if not (or an error occurred).
        !           679:  */
        !           680: int
        !           681: fileindir(const char *file, const char *dir)
        !           682: {
        !           683:        char    parentdirbuf[PATH_MAX], *parentdir;
        !           684:        char    realdir[PATH_MAX];
        !           685:        size_t  dirlen;
        !           686:
        !           687:                                        /* determine parent directory of file */
        !           688:        (void)strlcpy(parentdirbuf, file, sizeof(parentdirbuf));
        !           689:        parentdir = dirname(parentdirbuf);
        !           690:        if (strcmp(parentdir, ".") == 0)
        !           691:                return 1;               /* current directory is ok */
        !           692:
        !           693:                                        /* find the directory */
        !           694:        if (realpath(parentdir, realdir) == NULL) {
        !           695:                warn("Unable to determine real path of `%s'", parentdir);
        !           696:                return 0;
1.7       deraadt   697:        }
1.89    ! florian   698:        if (realdir[0] != '/')          /* relative result is ok */
        !           699:                return 1;
1.41      otto      700:
1.89    ! florian   701:        dirlen = strlen(dir);
        !           702:        if (strncmp(realdir, dir, dirlen) == 0 &&
        !           703:            (realdir[dirlen] == '/' || realdir[dirlen] == '\0'))
        !           704:                return 1;
        !           705:        return 0;
1.41      otto      706: }
                    707:
1.89    ! florian   708:
        !           709: /*
        !           710:  * Returns true if this is the controlling/foreground process, else false.
        !           711:  */
1.10      millert   712: int
1.89    ! florian   713: foregroundproc(void)
1.10      millert   714: {
1.89    ! florian   715:        static pid_t pgrp = -1;
        !           716:        int ctty_pgrp;
1.10      millert   717:
1.89    ! florian   718:        if (pgrp == -1)
        !           719:                pgrp = getpgrp();
1.10      millert   720:
1.89    ! florian   721:        return((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 &&
        !           722:            ctty_pgrp == pgrp));
        !           723: }
1.10      millert   724:
1.89    ! florian   725: /* ARGSUSED */
        !           726: static void
        !           727: updateprogressmeter(int signo)
        !           728: {
        !           729:        int save_errno = errno;
1.1       millert   730:
1.89    ! florian   731:        /* update progressmeter if foreground process or in -m mode */
        !           732:        if (foregroundproc() || progress == -1)
        !           733:                progressmeter(0, NULL);
1.19      deraadt   734:        errno = save_errno;
1.1       millert   735: }
                    736:
1.89    ! florian   737: /*
        !           738:  * Display a transfer progress bar if progress is non-zero.
        !           739:  * SIGALRM is hijacked for use by this function.
        !           740:  * - Before the transfer, set filesize to size of file (or -1 if unknown),
        !           741:  *   and call with flag = -1. This starts the once per second timer,
        !           742:  *   and a call to updateprogressmeter() upon SIGALRM.
        !           743:  * - During the transfer, updateprogressmeter will call progressmeter
        !           744:  *   with flag = 0
        !           745:  * - After the transfer, call with flag = 1
        !           746:  */
        !           747: static struct timespec start;
        !           748:
        !           749: char *action;
        !           750:
1.1       millert   751: void
1.89    ! florian   752: progressmeter(int flag, const char *filename)
1.1       millert   753: {
1.89    ! florian   754:        /*
        !           755:         * List of order of magnitude prefixes.
        !           756:         * The last is `P', as 2^64 = 16384 Petabytes
        !           757:         */
        !           758:        static const char prefixes[] = " KMGTP";
        !           759:
        !           760:        static struct timespec lastupdate;
        !           761:        static off_t lastsize;
        !           762:        static char *title = NULL;
        !           763:        struct timespec now, td, wait;
        !           764:        off_t cursize, abbrevsize;
        !           765:        double elapsed;
        !           766:        int ratio, barlength, i, remaining, overhead = 30;
        !           767:        char buf[512];
        !           768:
        !           769:        if (flag == -1) {
        !           770:                clock_gettime(CLOCK_MONOTONIC, &start);
        !           771:                lastupdate = start;
        !           772:                lastsize = restart_point;
        !           773:        }
        !           774:        clock_gettime(CLOCK_MONOTONIC, &now);
        !           775:        if (!progress || filesize < 0)
        !           776:                return;
        !           777:        cursize = bytes + restart_point;
        !           778:
        !           779:        if (filesize)
        !           780:                ratio = cursize * 100 / filesize;
        !           781:        else
        !           782:                ratio = 100;
        !           783:        ratio = MAXIMUM(ratio, 0);
        !           784:        ratio = MINIMUM(ratio, 100);
        !           785:        if (!verbose && flag == -1) {
        !           786:                filename = basename(filename);
        !           787:                if (filename != NULL) {
        !           788:                        free(title);
        !           789:                        title = strdup(filename);
        !           790:                }
        !           791:        }
        !           792:
        !           793:        buf[0] = 0;
        !           794:        if (!verbose && action != NULL) {
        !           795:                int l = strlen(action);
        !           796:                char *dotdot = "";
        !           797:
        !           798:                if (l < 7)
        !           799:                        l = 7;
        !           800:                else if (l > 12) {
        !           801:                        l = 12;
        !           802:                        dotdot = "...";
        !           803:                        overhead += 3;
        !           804:                }
        !           805:                snprintf(buf, sizeof(buf), "\r%-*.*s%s ", l, l, action,
        !           806:                    dotdot);
        !           807:                overhead += l + 1;
        !           808:        } else
        !           809:                snprintf(buf, sizeof(buf), "\r");
        !           810:
        !           811:        if (!verbose && title != NULL) {
        !           812:                int l = strlen(title);
        !           813:                char *dotdot = "";
        !           814:
        !           815:                if (l < 12)
        !           816:                        l = 12;
        !           817:                else if (l > 25) {
        !           818:                        l = 22;
        !           819:                        dotdot = "...";
        !           820:                        overhead += 3;
        !           821:                }
        !           822:                snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
        !           823:                    "%-*.*s%s %3d%% ", l, l, title,
        !           824:                    dotdot, ratio);
        !           825:                overhead += l + 1;
        !           826:        } else
        !           827:                snprintf(buf, sizeof(buf), "\r%3d%% ", ratio);
        !           828:
        !           829:        barlength = ttywidth - overhead;
        !           830:        if (barlength > 0) {
        !           831:                i = barlength * ratio / 100;
        !           832:                snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
        !           833:                    "|%.*s%*s|", i,
        !           834:                    "*******************************************************"
        !           835:                    "*******************************************************"
        !           836:                    "*******************************************************"
        !           837:                    "*******************************************************"
        !           838:                    "*******************************************************"
        !           839:                    "*******************************************************"
        !           840:                    "*******************************************************",
        !           841:                    barlength - i, "");
        !           842:        }
        !           843:
        !           844:        i = 0;
        !           845:        abbrevsize = cursize;
        !           846:        while (abbrevsize >= 100000 && i < sizeof(prefixes)-1) {
        !           847:                i++;
        !           848:                abbrevsize >>= 10;
        !           849:        }
        !           850:        snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
        !           851:            " %5lld %c%c ", (long long)abbrevsize, prefixes[i],
        !           852:            prefixes[i] == ' ' ? ' ' : 'B');
        !           853:
        !           854:        timespecsub(&now, &lastupdate, &wait);
        !           855:        if (cursize > lastsize) {
        !           856:                lastupdate = now;
        !           857:                lastsize = cursize;
        !           858:                if (wait.tv_sec >= STALLTIME) { /* fudge out stalled time */
        !           859:                        start.tv_sec += wait.tv_sec;
        !           860:                        start.tv_nsec += wait.tv_nsec;
        !           861:                }
        !           862:                wait.tv_sec = 0;
        !           863:        }
        !           864:
        !           865:        timespecsub(&now, &start, &td);
        !           866:        elapsed = td.tv_sec + (td.tv_nsec / 1000000000.0);
1.1       millert   867:
1.89    ! florian   868:        if (flag == 1) {
        !           869:                i = (int)elapsed / 3600;
        !           870:                if (i)
        !           871:                        snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
        !           872:                            "%2d:", i);
        !           873:                else
        !           874:                        snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
        !           875:                            "   ");
        !           876:                i = (int)elapsed % 3600;
        !           877:                snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
        !           878:                    "%02d:%02d    ", i / 60, i % 60);
        !           879:        } else if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) {
        !           880:                snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
        !           881:                    "   --:-- ETA");
        !           882:        } else if (wait.tv_sec >= STALLTIME) {
        !           883:                snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
        !           884:                    " - stalled -");
        !           885:        } else {
        !           886:                remaining = (int)((filesize - restart_point) /
        !           887:                                  (bytes / elapsed) - elapsed);
        !           888:                i = remaining / 3600;
        !           889:                if (i)
        !           890:                        snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
        !           891:                            "%2d:", i);
        !           892:                else
        !           893:                        snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
        !           894:                            "   ");
        !           895:                i = remaining % 3600;
        !           896:                snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
        !           897:                    "%02d:%02d ETA", i / 60, i % 60);
        !           898:        }
        !           899:        (void)write(fileno(ttyout), buf, strlen(buf));
        !           900:
        !           901:        if (flag == -1) {
        !           902:                (void)signal(SIGALRM, updateprogressmeter);
        !           903:                alarmtimer(1);          /* set alarm timer for 1 Hz */
        !           904:        } else if (flag == 1) {
        !           905:                alarmtimer(0);
        !           906:                (void)putc('\n', ttyout);
        !           907:                free(title);
        !           908:                title = NULL;
        !           909:        }
        !           910:        fflush(ttyout);
1.87      kmos      911: }
1.65      deraadt   912:
1.89    ! florian   913: /*
        !           914:  * Display transfer statistics.
        !           915:  * Requires start to be initialised by progressmeter(-1),
        !           916:  * direction to be defined by xfer routines, and filesize and bytes
        !           917:  * to be updated by xfer routines
        !           918:  * If siginfo is nonzero, an ETA is displayed, and the output goes to STDERR
        !           919:  * instead of TTYOUT.
        !           920:  */
        !           921: void
        !           922: ptransfer(int siginfo)
1.87      kmos      923: {
1.89    ! florian   924:        struct timespec now, td;
        !           925:        double elapsed;
        !           926:        off_t bs;
        !           927:        int meg, remaining, hh;
        !           928:        char buf[100];
        !           929:
        !           930:        if (!verbose && !siginfo)
        !           931:                return;
1.1       millert   932:
1.89    ! florian   933:        clock_gettime(CLOCK_MONOTONIC, &now);
        !           934:        timespecsub(&now, &start, &td);
        !           935:        elapsed = td.tv_sec + (td.tv_nsec / 1000000000.0);
        !           936:        bs = bytes / (elapsed == 0.0 ? 1 : elapsed);
        !           937:        meg = 0;
        !           938:        if (bs > (1024 * 1024))
        !           939:                meg = 1;
        !           940:
        !           941:        /* XXX floating point printf in signal handler */
        !           942:        (void)snprintf(buf, sizeof(buf),
        !           943:            "%lld byte%s %s in %.2f seconds (%.2f %sB/s)\n",
        !           944:            (long long)bytes, bytes == 1 ? "" : "s", direction, elapsed,
        !           945:            bs / (1024.0 * (meg ? 1024.0 : 1.0)), meg ? "M" : "K");
        !           946:
        !           947:        if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0 &&
        !           948:            bytes + restart_point <= filesize) {
        !           949:                remaining = (int)((filesize - restart_point) /
        !           950:                    (bytes / elapsed) - elapsed);
        !           951:                hh = remaining / 3600;
        !           952:                remaining %= 3600;
        !           953:
        !           954:                /* "buf+len(buf) -1" to overwrite \n */
        !           955:                snprintf(buf + strlen(buf) - 1, sizeof(buf) - strlen(buf),
        !           956:                    "  ETA: %02d:%02d:%02d\n", hh, remaining / 60,
        !           957:                    remaining % 60);
        !           958:        }
        !           959:        (void)write(siginfo ? STDERR_FILENO : fileno(ttyout), buf, strlen(buf));
        !           960: }
1.1       millert   961:
1.89    ! florian   962: /*
        !           963:  * List words in stringlist, vertically arranged
        !           964:  */
        !           965: #ifndef SMALL
        !           966: void
        !           967: list_vertical(StringList *sl)
        !           968: {
        !           969:        int i, j, w;
        !           970:        int columns, width, lines;
        !           971:        char *p;
        !           972:
        !           973:        width = 0;
        !           974:
        !           975:        for (i = 0 ; i < sl->sl_cur ; i++) {
        !           976:                w = strlen(sl->sl_str[i]);
        !           977:                if (w > width)
        !           978:                        width = w;
        !           979:        }
        !           980:        width = (width + 8) &~ 7;
1.1       millert   981:
1.89    ! florian   982:        columns = ttywidth / width;
        !           983:        if (columns == 0)
        !           984:                columns = 1;
        !           985:        lines = (sl->sl_cur + columns - 1) / columns;
        !           986:        for (i = 0; i < lines; i++) {
        !           987:                for (j = 0; j < columns; j++) {
        !           988:                        p = sl->sl_str[j * lines + i];
        !           989:                        if (p)
        !           990:                                fputs(p, ttyout);
        !           991:                        if (j * lines + i + lines >= sl->sl_cur) {
        !           992:                                putc('\n', ttyout);
        !           993:                                break;
        !           994:                        }
        !           995:                        w = strlen(p);
        !           996:                        while (w < width) {
        !           997:                                w = (w + 8) &~ 7;
        !           998:                                (void)putc('\t', ttyout);
        !           999:                        }
        !          1000:                }
        !          1001:        }
1.1       millert  1002: }
1.89    ! florian  1003: #endif /* !SMALL */
1.1       millert  1004:
1.89    ! florian  1005: /*
        !          1006:  * Update the global ttywidth value, using TIOCGWINSZ.
        !          1007:  */
        !          1008: /* ARGSUSED */
1.1       millert  1009: void
1.89    ! florian  1010: setttywidth(int signo)
1.1       millert  1011: {
1.89    ! florian  1012:        int save_errno = errno;
        !          1013:        struct winsize winsize;
1.1       millert  1014:
1.89    ! florian  1015:        if (ioctl(fileno(ttyout), TIOCGWINSZ, &winsize) != -1)
        !          1016:                ttywidth = winsize.ws_col ? winsize.ws_col : 80;
        !          1017:        else
        !          1018:                ttywidth = 80;
        !          1019:        errno = save_errno;
        !          1020: }
        !          1021:
        !          1022: /*
        !          1023:  * Set the SIGALRM interval timer for wait seconds, 0 to disable.
        !          1024:  */
        !          1025: void
        !          1026: alarmtimer(int wait)
        !          1027: {
        !          1028:        int save_errno = errno;
        !          1029:        struct itimerval itv;
1.1       millert  1030:
1.89    ! florian  1031:        itv.it_value.tv_sec = wait;
        !          1032:        itv.it_value.tv_usec = 0;
        !          1033:        itv.it_interval = itv.it_value;
        !          1034:        setitimer(ITIMER_REAL, &itv, NULL);
        !          1035:        errno = save_errno;
1.1       millert  1036: }
                   1037:
1.89    ! florian  1038: /*
        !          1039:  * Setup or cleanup EditLine structures
        !          1040:  */
        !          1041: #ifndef SMALL
1.1       millert  1042: void
1.89    ! florian  1043: controlediting(void)
1.1       millert  1044: {
1.89    ! florian  1045:        HistEvent hev;
1.1       millert  1046:
1.89    ! florian  1047:        if (editing && el == NULL && hist == NULL) {
        !          1048:                el = el_init(__progname, stdin, ttyout, stderr); /* init editline */
        !          1049:                hist = history_init();          /* init the builtin history */
        !          1050:                history(hist, &hev, H_SETSIZE, 100);    /* remember 100 events */
        !          1051:                el_set(el, EL_HIST, history, hist);     /* use history */
        !          1052:
        !          1053:                el_set(el, EL_EDITOR, "emacs"); /* default editor is emacs */
        !          1054:                el_set(el, EL_PROMPT, prompt);  /* set the prompt function */
        !          1055:
        !          1056:                /* add local file completion, bind to TAB */
        !          1057:                el_set(el, EL_ADDFN, "ftp-complete",
        !          1058:                    "Context sensitive argument completion",
        !          1059:                    complete);
        !          1060:                el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
        !          1061:
        !          1062:                el_source(el, NULL);    /* read ~/.editrc */
        !          1063:                el_set(el, EL_SIGNAL, 1);
        !          1064:        } else if (!editing) {
        !          1065:                if (hist) {
        !          1066:                        history_end(hist);
        !          1067:                        hist = NULL;
        !          1068:                }
        !          1069:                if (el) {
        !          1070:                        el_end(el);
        !          1071:                        el = NULL;
        !          1072:                }
1.1       millert  1073:        }
1.89    ! florian  1074: }
        !          1075: #endif /* !SMALL */
1.1       millert  1076:
1.89    ! florian  1077: /*
        !          1078:  * Wait for an asynchronous connect(2) attempt to finish.
        !          1079:  */
        !          1080: int
        !          1081: connect_wait(int s)
        !          1082: {
        !          1083:        struct pollfd pfd[1];
        !          1084:        int error = 0;
        !          1085:        socklen_t len = sizeof(error);
1.80      millert  1086:
1.89    ! florian  1087:        pfd[0].fd = s;
        !          1088:        pfd[0].events = POLLOUT;
1.81      millert  1089:
1.89    ! florian  1090:        if (poll(pfd, 1, -1) == -1)
        !          1091:                return -1;
        !          1092:        if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
        !          1093:                return -1;
        !          1094:        if (error != 0) {
        !          1095:                errno = error;
        !          1096:                return -1;
        !          1097:        }
        !          1098:        return 0;
1.80      millert  1099: }