mirror of
https://git.savannah.gnu.org/git/inetutils.git
synced 2026-01-12 00:19:39 +08:00
2716 lines
50 KiB
C
2716 lines
50 KiB
C
/*
|
|
Copyright (C) 1995-2025 Free Software Foundation, Inc.
|
|
|
|
This file is part of GNU Inetutils.
|
|
|
|
GNU Inetutils is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or (at
|
|
your option) any later version.
|
|
|
|
GNU Inetutils is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see `http://www.gnu.org/licenses/'. */
|
|
|
|
/*
|
|
* Copyright (c) 1985, 1989, 1993, 1994
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* FTP User Program -- Command Routines.
|
|
*/
|
|
#include <config.h>
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/ftp.h>
|
|
|
|
#include <ctype.h>
|
|
#include <error.h>
|
|
#include <errno.h>
|
|
#include <netdb.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h> /* intmax_t */
|
|
#include <string.h>
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
/* Include glob.h last, because it may define "const" which breaks
|
|
system headers on some platforms. */
|
|
#include <glob.h>
|
|
|
|
#ifdef HAVE_READLINE_READLINE_H
|
|
# include <readline/readline.h>
|
|
#elif defined HAVE_EDITLINE_READLINE_H
|
|
# include <editline/readline.h>
|
|
#endif
|
|
#ifdef HAVE_READLINE_HISTORY_H
|
|
# include <readline/history.h>
|
|
#elif defined HAVE_EDITLINE_HISTORY_H
|
|
# include <editline/history.h>
|
|
#endif
|
|
|
|
#include "ftp_var.h"
|
|
#include "attribute.h"
|
|
#include "xalloc.h"
|
|
#include "xgetcwd.h"
|
|
|
|
#ifndef DEFPORT
|
|
# ifdef IPPORT_FTP
|
|
# define DEFPORT IPPORT_FTP
|
|
# else/* !IPPORT_FTP */
|
|
# define DEFPORT 21
|
|
# endif
|
|
#endif /* !DEFPORT */
|
|
|
|
/* Returns true if STR is entirely lower case. */
|
|
static int
|
|
all_lower (char *str)
|
|
{
|
|
while (*str)
|
|
if (isupper (*str++))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/* Returns true if STR is entirely upper case. */
|
|
static int
|
|
all_upper (char *str)
|
|
{
|
|
while (*str)
|
|
if (islower (*str++))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/* Destructively converts STR to lower case. */
|
|
static char *
|
|
strdown (char *str)
|
|
{
|
|
char *p;
|
|
for (p = str; *p; p++)
|
|
if (isupper (*p))
|
|
*p = tolower (*p);
|
|
return str;
|
|
}
|
|
|
|
jmp_buf jabort;
|
|
char *mname;
|
|
char *home = "/";
|
|
|
|
char *mapin = 0;
|
|
char *mapout = 0;
|
|
|
|
/*
|
|
* `Another' gets another argument, and stores the new argc and argv.
|
|
* It reverts to the top level (via main.c's intr()) on EOF/error.
|
|
*
|
|
* Returns false if no new arguments have been added.
|
|
*/
|
|
int
|
|
another (int *pargc, char ***pargv, const char *prompt)
|
|
{
|
|
char *arg = NULL;
|
|
char *buffer, *new;
|
|
size_t size = 0, len = strlen (line);
|
|
int ret;
|
|
|
|
buffer = (char *) malloc (sizeof (char) * (strlen (prompt) + 4));
|
|
if (!buffer)
|
|
intr (0);
|
|
|
|
sprintf (buffer, "(%s) ", prompt);
|
|
|
|
#if HAVE_READLINE
|
|
if (usereadline)
|
|
arg = readline (buffer);
|
|
else
|
|
#endif /* HAVE_READLINE */
|
|
{
|
|
char *nl;
|
|
|
|
fprintf (stdout, "%s", buffer);
|
|
fflush (stdout);
|
|
|
|
if (getline (&arg, &size, stdin) <= 0)
|
|
{
|
|
free (buffer);
|
|
free (arg);
|
|
intr (0);
|
|
}
|
|
|
|
nl = strchr (arg, '\n');
|
|
if (nl)
|
|
*nl = '\0';
|
|
}
|
|
|
|
free (buffer);
|
|
|
|
#if HAVE_READLINE
|
|
if (usereadline && arg && *arg)
|
|
add_history (arg);
|
|
#endif /* HAVE_READLINE */
|
|
|
|
if (!arg)
|
|
intr (0);
|
|
else if (!*arg)
|
|
{
|
|
free (arg);
|
|
return 0;
|
|
}
|
|
|
|
new = realloc (line, sizeof (char) *
|
|
((linelen ? linelen : len) + strlen (arg) + 2));
|
|
if (!new)
|
|
{
|
|
free (arg);
|
|
intr (0);
|
|
}
|
|
|
|
line = new;
|
|
linelen = sizeof (char) * ((linelen ? linelen : len) + strlen (arg) + 2);
|
|
line[len++] = ' ';
|
|
strcpy (&line[len], arg);
|
|
free (arg);
|
|
|
|
makeargv ();
|
|
ret = margc > *pargc;
|
|
*pargc = margc;
|
|
*pargv = margv;
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* Connect to peer server and
|
|
* auto-login, if possible.
|
|
*/
|
|
void
|
|
setpeer (int argc, char **argv)
|
|
{
|
|
char *host = NULL;
|
|
int port;
|
|
|
|
if (connected && command ("NOOP") != COMPLETE)
|
|
disconnect (0, 0);
|
|
else if (connected)
|
|
{
|
|
printf ("Already connected to %s, use close first.\n", hostname);
|
|
code = -1;
|
|
return;
|
|
}
|
|
|
|
if (argc < 2)
|
|
{
|
|
if (hostname)
|
|
{
|
|
host = hostname;
|
|
argc = 2;
|
|
}
|
|
else
|
|
another (&argc, &argv, "to");
|
|
}
|
|
|
|
if (argc < 2 || argc > 3)
|
|
{
|
|
printf ("usage: %s host-name [port]\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
|
|
if (!host)
|
|
host = argv[1];
|
|
|
|
if (argc == 3)
|
|
{
|
|
if (isdigit (argv[2][0]) || argv[2][0] == '-')
|
|
port = atoi (argv[2]);
|
|
else
|
|
{
|
|
struct servent *sp;
|
|
|
|
sp = getservbyname (argv[2], "tcp");
|
|
port = (sp) ? ntohs (sp->s_port) : 0;
|
|
}
|
|
|
|
if (port <= 0 || port > 65535)
|
|
{
|
|
printf ("%s: bad port -- %s\n", argv[1], argv[2]);
|
|
printf ("usage: %s host-name [port]\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
struct servent *sp;
|
|
|
|
sp = getservbyname ("ftp", "tcp");
|
|
port = (sp) ? ntohs (sp->s_port) : DEFPORT;
|
|
}
|
|
|
|
/* After hookup(), the global variable `hostname' contains
|
|
* the canonical host name corresponding to the alias name
|
|
* contained in HOST. The return value of hookup() is not
|
|
* NULL only if the server has answered our call. The value
|
|
* of HOST should be preserved for reporting inside login(),
|
|
* which also detects a correct stanza of the netrc file.
|
|
*/
|
|
if (hookup (host, port))
|
|
{
|
|
int overbose;
|
|
|
|
connected = 1;
|
|
/*
|
|
* Set up defaults for FTP.
|
|
*/
|
|
strcpy (typename, "ascii"), type = TYPE_A;
|
|
curtype = TYPE_A;
|
|
strcpy (formname, "non-print"), form = FORM_N;
|
|
strcpy (modename, "stream"), mode = MODE_S;
|
|
strcpy (structname, "file"), stru = STRU_F;
|
|
strcpy (bytename, "8"), bytesize = 8;
|
|
if (autologin)
|
|
login (host);
|
|
|
|
#if (defined unix || defined __unix || defined __unix__) && NBBY == 8
|
|
/*
|
|
* this ifdef is to keep someone form "porting" this to an incompatible
|
|
* system and not checking this out. This way they have to think about it.
|
|
*/
|
|
overbose = verbose;
|
|
if (debug == 0)
|
|
verbose = -1;
|
|
if (command ("SYST") == COMPLETE && overbose)
|
|
{
|
|
char *cp, c;
|
|
cp = strchr (reply_string + 4, ' ');
|
|
if (cp == NULL)
|
|
cp = strchr (reply_string + 4, '\r');
|
|
if (cp)
|
|
{
|
|
if (cp[-1] == '.')
|
|
cp--;
|
|
c = *cp;
|
|
*cp = '\0';
|
|
}
|
|
|
|
printf ("Remote system type is %s.\n", reply_string + 4);
|
|
if (cp)
|
|
*cp = c;
|
|
}
|
|
if (!strncmp (reply_string, "215 UNIX Type: L8", 17))
|
|
{
|
|
if (proxy)
|
|
unix_proxy = 1;
|
|
else
|
|
unix_server = 1;
|
|
/*
|
|
* Set type to 0 (not specified by user),
|
|
* meaning binary by default, but don't bother
|
|
* telling server. We can use binary
|
|
* for text files unless changed by the user.
|
|
*/
|
|
type = 0;
|
|
strcpy (typename, "binary");
|
|
if (overbose)
|
|
printf ("Using %s mode to transfer files.\n", typename);
|
|
}
|
|
else
|
|
{
|
|
if (proxy)
|
|
unix_proxy = 0;
|
|
else
|
|
unix_server = 0;
|
|
if (overbose && !strncmp (reply_string, "215 TOPS20", 10))
|
|
printf
|
|
("Remember to set tenex mode when transferring binary files from this machine.\n");
|
|
}
|
|
verbose = overbose;
|
|
#endif /* (unix || __unix || __unix__) && (NBBY == 8) */
|
|
}
|
|
}
|
|
|
|
struct types
|
|
{
|
|
char *t_name;
|
|
char *t_mode;
|
|
int t_type;
|
|
char *t_arg;
|
|
} types[] = {
|
|
{"ascii", "A", TYPE_A, 0},
|
|
{"binary", "I", TYPE_I, 0},
|
|
{"image", "I", TYPE_I, 0},
|
|
{"ebcdic", "E", TYPE_E, 0},
|
|
{"tenex", "L", TYPE_L, bytename},
|
|
{NULL, NULL, 0, NULL}
|
|
};
|
|
|
|
/*
|
|
* Set transfer type.
|
|
*/
|
|
void
|
|
settype (int argc, char **argv)
|
|
{
|
|
struct types *p;
|
|
int comret;
|
|
|
|
if (argc > 2)
|
|
{
|
|
char *sep;
|
|
|
|
printf ("usage: %s [", argv[0]);
|
|
sep = " ";
|
|
for (p = types; p->t_name; p++)
|
|
{
|
|
printf ("%s%s", sep, p->t_name);
|
|
sep = " | ";
|
|
}
|
|
printf (" ]\n");
|
|
code = -1;
|
|
return;
|
|
}
|
|
if (argc < 2)
|
|
{
|
|
printf ("Using %s mode to transfer files.\n", typename);
|
|
code = 0;
|
|
return;
|
|
}
|
|
for (p = types; p->t_name; p++)
|
|
if (strcmp (argv[1], p->t_name) == 0)
|
|
break;
|
|
if (p->t_name == 0)
|
|
{
|
|
printf ("%s: unknown mode\n", argv[1]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
|
|
comret = command ("TYPE %s %s", p->t_mode, p->t_arg);
|
|
else
|
|
comret = command ("TYPE %s", p->t_mode);
|
|
if (comret == COMPLETE)
|
|
{
|
|
strcpy (typename, p->t_name);
|
|
curtype = type = p->t_type;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Internal form of settype; changes current type in use with server
|
|
* without changing our notion of the type for data transfers.
|
|
* Used to change to and from ascii for listings.
|
|
*/
|
|
void
|
|
changetype (int newtype, int show)
|
|
{
|
|
struct types *p;
|
|
int comret, oldverbose = verbose;
|
|
|
|
if (newtype == 0)
|
|
newtype = TYPE_I;
|
|
if (newtype == curtype)
|
|
return;
|
|
if (debug == 0 && show == 0)
|
|
verbose = 0;
|
|
for (p = types; p->t_name; p++)
|
|
if (newtype == p->t_type)
|
|
break;
|
|
if (p->t_name == 0)
|
|
{
|
|
printf ("ftp: internal error: unknown type %d\n", newtype);
|
|
return;
|
|
}
|
|
if (newtype == TYPE_L && bytename[0] != '\0')
|
|
comret = command ("TYPE %s %s", p->t_mode, bytename);
|
|
else
|
|
comret = command ("TYPE %s", p->t_mode);
|
|
if (comret == COMPLETE)
|
|
curtype = newtype;
|
|
verbose = oldverbose;
|
|
}
|
|
|
|
char *stype[] = {
|
|
"type",
|
|
"",
|
|
0
|
|
};
|
|
|
|
/*
|
|
* Set binary transfer type.
|
|
*/
|
|
void
|
|
setbinary (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
stype[1] = "binary";
|
|
settype (2, stype);
|
|
}
|
|
|
|
/*
|
|
* Set ascii transfer type.
|
|
*/
|
|
void
|
|
setascii (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
stype[1] = "ascii";
|
|
settype (2, stype);
|
|
}
|
|
|
|
/*
|
|
* Set tenex transfer type.
|
|
*/
|
|
void
|
|
settenex (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
stype[1] = "tenex";
|
|
settype (2, stype);
|
|
}
|
|
|
|
/*
|
|
* Set file transfer mode.
|
|
*/
|
|
void
|
|
setftmode (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
printf ("We only support %s mode, sorry.\n", modename);
|
|
code = -1;
|
|
}
|
|
|
|
/*
|
|
* Set file transfer format.
|
|
*/
|
|
void
|
|
setform (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
printf ("We only support %s format, sorry.\n", formname);
|
|
code = -1;
|
|
}
|
|
|
|
/*
|
|
* Set file transfer structure.
|
|
*/
|
|
void
|
|
setstruct (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
printf ("We only support %s structure, sorry.\n", structname);
|
|
code = -1;
|
|
}
|
|
|
|
/*
|
|
* Send a single file.
|
|
*/
|
|
void
|
|
put (int argc, char **argv)
|
|
{
|
|
char *cmd, *local, *remote;
|
|
int loc = 0;
|
|
|
|
if (argc == 2)
|
|
{
|
|
argc++;
|
|
argv[2] = argv[1];
|
|
loc++;
|
|
}
|
|
if (argc < 2 && !another (&argc, &argv, "local-file"))
|
|
goto usage;
|
|
if (argc < 3 && !another (&argc, &argv, "remote-file"))
|
|
{
|
|
usage:
|
|
printf ("usage: %s local-file remote-file\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
|
|
local = globulize (argv[1]);
|
|
if (!local)
|
|
{
|
|
code = -1;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If "globulize" modifies argv[1], and argv[2] is a copy of
|
|
* the old argv[1], make it a copy of the new argv[1].
|
|
*/
|
|
if (loc)
|
|
remote = strdup (local);
|
|
else
|
|
remote = strdup (argv[2]);
|
|
|
|
cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
|
|
if (loc && ntflag)
|
|
{
|
|
char *new = dotrans (remote);
|
|
free (remote);
|
|
remote = new;
|
|
}
|
|
if (loc && mapflag)
|
|
{
|
|
char *new = domap (remote);
|
|
if (new != remote)
|
|
{
|
|
free (remote);
|
|
remote = new;
|
|
}
|
|
}
|
|
sendrequest (cmd, local, remote,
|
|
strcmp (argv[1], local) != 0 || strcmp (argv[2], remote) != 0);
|
|
free (local);
|
|
free (remote);
|
|
}
|
|
|
|
/*
|
|
* Send multiple files.
|
|
*/
|
|
void
|
|
mput (int argc, char **argv)
|
|
{
|
|
int i;
|
|
sighandler_t oldintr;
|
|
int ointer;
|
|
|
|
if (argc < 2 && !another (&argc, &argv, "local-files"))
|
|
{
|
|
printf ("usage: %s local-files\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
mname = argv[0];
|
|
mflag = 1;
|
|
oldintr = signal (SIGINT, mabort);
|
|
setjmp (jabort);
|
|
if (proxy)
|
|
{
|
|
char *cp;
|
|
|
|
while ((cp = remglob (argv, 0)) != NULL)
|
|
{
|
|
if (*cp == 0)
|
|
mflag = 0;
|
|
if (mflag && confirm (argv[0], cp))
|
|
{
|
|
char *tp = cp;
|
|
|
|
if (mcase)
|
|
{
|
|
if (all_upper (tp))
|
|
tp = strdown (strdup (tp));
|
|
}
|
|
if (ntflag)
|
|
{
|
|
char *new = dotrans (tp);
|
|
if (tp != cp)
|
|
free (tp);
|
|
tp = new;
|
|
}
|
|
if (mapflag)
|
|
{
|
|
char *new = domap (tp);
|
|
if (new != tp)
|
|
{
|
|
if (tp != cp)
|
|
free (tp);
|
|
tp = new;
|
|
}
|
|
}
|
|
sendrequest ((sunique) ? "STOU" : "STOR",
|
|
cp, tp, cp != tp || !interactive);
|
|
if (!mflag && fromatty)
|
|
{
|
|
ointer = interactive;
|
|
interactive = 1;
|
|
if (confirm ("Continue with", "mput"))
|
|
{
|
|
mflag++;
|
|
}
|
|
interactive = ointer;
|
|
}
|
|
|
|
if (tp != cp)
|
|
free (tp);
|
|
}
|
|
|
|
free (cp);
|
|
}
|
|
signal (SIGINT, oldintr);
|
|
mflag = 0;
|
|
return;
|
|
}
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
char **cpp;
|
|
glob_t gl;
|
|
int flags;
|
|
|
|
if (!doglob)
|
|
{
|
|
if (mflag && confirm (argv[0], argv[i]))
|
|
{
|
|
char *tp = argv[i];
|
|
if (ntflag)
|
|
tp = dotrans (tp);
|
|
if (mapflag)
|
|
{
|
|
char *new = domap (tp);
|
|
if (new != tp)
|
|
{
|
|
if (tp != argv[i])
|
|
free (tp);
|
|
tp = new;
|
|
}
|
|
}
|
|
sendrequest ((sunique) ? "STOU" : "STOR",
|
|
argv[i], tp, tp != argv[i] || !interactive);
|
|
if (!mflag && fromatty)
|
|
{
|
|
ointer = interactive;
|
|
interactive = 1;
|
|
if (confirm ("Continue with", "mput"))
|
|
{
|
|
mflag++;
|
|
}
|
|
interactive = ointer;
|
|
}
|
|
if (tp != argv[i])
|
|
free (tp);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
memset (&gl, 0, sizeof (gl));
|
|
flags = GLOB_NOCHECK;
|
|
#ifdef GLOB_BRACE
|
|
flags |= GLOB_BRACE;
|
|
#endif
|
|
#ifdef GLOB_TILDE
|
|
flags |= GLOB_TILDE;
|
|
#endif
|
|
#ifdef GLOB_QUOTE
|
|
flags |= GLOB_QUOTE;
|
|
#endif
|
|
if (glob (argv[i], flags, NULL, &gl) || gl.gl_pathc == 0)
|
|
{
|
|
error (0, 0, "%s: not found", argv[i]);
|
|
globfree (&gl);
|
|
continue;
|
|
}
|
|
for (cpp = gl.gl_pathv; cpp && *cpp != NULL; cpp++)
|
|
{
|
|
if (mflag && confirm (argv[0], *cpp))
|
|
{
|
|
char *tp = *cpp;
|
|
if (ntflag)
|
|
tp = dotrans (tp);
|
|
if (mapflag)
|
|
{
|
|
char *new = domap (tp);
|
|
if (new != tp)
|
|
{
|
|
if (tp != *cpp)
|
|
free (tp);
|
|
tp = new;
|
|
}
|
|
}
|
|
sendrequest ((sunique) ? "STOU" : "STOR",
|
|
*cpp, tp, *cpp != tp || !interactive);
|
|
if (!mflag && fromatty)
|
|
{
|
|
ointer = interactive;
|
|
interactive = 1;
|
|
if (confirm ("Continue with", "mput"))
|
|
{
|
|
mflag++;
|
|
}
|
|
interactive = ointer;
|
|
}
|
|
if (tp != *cpp)
|
|
free (tp);
|
|
}
|
|
}
|
|
globfree (&gl);
|
|
}
|
|
signal (SIGINT, oldintr);
|
|
mflag = 0;
|
|
}
|
|
|
|
void
|
|
reget (int argc, char **argv)
|
|
{
|
|
|
|
getit (argc, argv, 1, "r+w");
|
|
}
|
|
|
|
void
|
|
get (int argc, char **argv)
|
|
{
|
|
|
|
getit (argc, argv, 0, restart_point ? "r+w" : "w");
|
|
}
|
|
|
|
/*
|
|
* Receive one file.
|
|
*/
|
|
int
|
|
getit (int argc, char **argv, int restartit, char *mode)
|
|
{
|
|
int loc = 0;
|
|
char *local;
|
|
|
|
if (argc == 2)
|
|
{
|
|
argc++;
|
|
argv[2] = argv[1];
|
|
loc++;
|
|
}
|
|
if (argc < 2 && !another (&argc, &argv, "remote-file"))
|
|
goto usage;
|
|
if (argc < 3 && !another (&argc, &argv, "local-file"))
|
|
{
|
|
usage:
|
|
printf ("usage: %s remote-file [ local-file ]\n", argv[0]);
|
|
code = -1;
|
|
return (0);
|
|
}
|
|
|
|
local = globulize (argv[2]);
|
|
if (!local)
|
|
{
|
|
code = -1;
|
|
return (0);
|
|
}
|
|
if (loc && mcase && all_upper (local))
|
|
strdown (local);
|
|
if (loc && ntflag)
|
|
{
|
|
char *new = dotrans (local);
|
|
free (local);
|
|
local = new;
|
|
}
|
|
if (loc && mapflag)
|
|
{
|
|
char *new = domap (local);
|
|
if (new != local)
|
|
{
|
|
free (local);
|
|
local = new;
|
|
}
|
|
}
|
|
if (restartit)
|
|
{
|
|
struct stat stbuf;
|
|
int ret;
|
|
|
|
ret = stat (local, &stbuf);
|
|
if (restartit == 1)
|
|
{
|
|
if (ret < 0)
|
|
{
|
|
error (0, errno, "local: %s", local);
|
|
free (local);
|
|
return (0);
|
|
}
|
|
restart_point = stbuf.st_size;
|
|
}
|
|
else
|
|
{
|
|
if (ret == 0)
|
|
{
|
|
int overbose;
|
|
|
|
overbose = verbose;
|
|
if (debug == 0)
|
|
verbose = -1;
|
|
if (command ("MDTM %s", argv[1]) == COMPLETE)
|
|
{
|
|
int yy, mo, day, hour, min, sec;
|
|
struct tm *tm;
|
|
verbose = overbose;
|
|
sscanf (reply_string,
|
|
"%*s %04d%02d%02d%02d%02d%02d",
|
|
&yy, &mo, &day, &hour, &min, &sec);
|
|
tm = gmtime (&stbuf.st_mtime);
|
|
tm->tm_mon++;
|
|
if (tm->tm_year + 1900 > yy)
|
|
{
|
|
free (local);
|
|
return (1);
|
|
}
|
|
if ((tm->tm_year + 1900 == yy &&
|
|
tm->tm_mon > mo) ||
|
|
(tm->tm_mon == mo &&
|
|
tm->tm_mday > day) ||
|
|
(tm->tm_mday == day &&
|
|
tm->tm_hour > hour) ||
|
|
(tm->tm_hour == hour &&
|
|
tm->tm_min > min) ||
|
|
(tm->tm_min == min && tm->tm_sec > sec))
|
|
{
|
|
free (local);
|
|
return (1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf ("%s\n", reply_string);
|
|
verbose = overbose;
|
|
free (local);
|
|
return (0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
recvrequest ("RETR", local, argv[1], mode, strcmp (local, argv[2]) != 0);
|
|
restart_point = 0;
|
|
free (local);
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
mabort (int signo MAYBE_UNUSED)
|
|
{
|
|
int ointer;
|
|
|
|
printf ("\n");
|
|
fflush (stdout);
|
|
if (mflag && fromatty)
|
|
{
|
|
ointer = interactive;
|
|
interactive = 1;
|
|
if (confirm ("Continue with", mname))
|
|
{
|
|
interactive = ointer;
|
|
longjmp (jabort, 0);
|
|
}
|
|
interactive = ointer;
|
|
}
|
|
mflag = 0;
|
|
longjmp (jabort, 0);
|
|
}
|
|
|
|
/*
|
|
* Get multiple files.
|
|
*/
|
|
void
|
|
mget (int argc, char **argv)
|
|
{
|
|
sighandler_t oldintr;
|
|
int ointer;
|
|
char *cp, *tp;
|
|
|
|
if (argc < 2 && !another (&argc, &argv, "remote-files"))
|
|
{
|
|
printf ("usage: %s remote-files\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
mname = argv[0];
|
|
mflag = 1;
|
|
oldintr = signal (SIGINT, mabort);
|
|
setjmp (jabort);
|
|
while ((cp = remglob (argv, proxy)) != NULL)
|
|
{
|
|
if (*cp == '\0')
|
|
{
|
|
mflag = 0;
|
|
continue;
|
|
}
|
|
if (mflag && confirm (argv[0], cp))
|
|
{
|
|
tp = cp;
|
|
if (mcase && !all_lower (tp))
|
|
tp = strdown (strdup (tp));
|
|
if (ntflag)
|
|
{
|
|
char *new = dotrans (tp);
|
|
if (tp != cp)
|
|
free (tp);
|
|
tp = new;
|
|
}
|
|
if (mapflag)
|
|
{
|
|
char *new = domap (tp);
|
|
if (new != tp)
|
|
{
|
|
if (tp != cp)
|
|
free (tp);
|
|
tp = new;
|
|
}
|
|
}
|
|
recvrequest ("RETR", tp, cp, "w", tp != cp || !interactive);
|
|
if (!mflag && fromatty)
|
|
{
|
|
ointer = interactive;
|
|
interactive = 1;
|
|
if (confirm ("Continue with", "mget"))
|
|
{
|
|
mflag++;
|
|
}
|
|
interactive = ointer;
|
|
}
|
|
if (tp != cp)
|
|
free (tp);
|
|
}
|
|
free (cp);
|
|
}
|
|
signal (SIGINT, oldintr);
|
|
mflag = 0;
|
|
}
|
|
|
|
char *
|
|
remglob (char **argv, int doswitch)
|
|
{
|
|
static FILE *ftemp = NULL;
|
|
static char **args;
|
|
int buf_len = 0;
|
|
char *buf = 0;
|
|
int sofar = 0;
|
|
int oldverbose, oldhash;
|
|
int fd;
|
|
char *cp, *mode;
|
|
|
|
if (!mflag)
|
|
{
|
|
if (!doglob)
|
|
{
|
|
args = NULL;
|
|
}
|
|
else
|
|
{
|
|
if (ftemp)
|
|
{
|
|
fclose (ftemp);
|
|
ftemp = NULL;
|
|
}
|
|
}
|
|
return (NULL);
|
|
}
|
|
if (!doglob)
|
|
{
|
|
if (args == NULL)
|
|
args = argv;
|
|
if ((cp = *++args) == NULL)
|
|
args = NULL;
|
|
return cp ? strdup (cp) : NULL;
|
|
}
|
|
if (ftemp == NULL)
|
|
{
|
|
char temp[sizeof PATH_TMP + sizeof "XXXXXX"];
|
|
|
|
strcpy (temp, PATH_TMP);
|
|
strcat (temp, "XXXXXX");
|
|
#ifdef HAVE_MKSTEMP
|
|
fd = mkstemp (temp);
|
|
#else
|
|
if (mktemp (temp) != NULL)
|
|
fd = open (temp, O_CREAT | O_EXCL | O_RDWR, 0600);
|
|
else
|
|
fd = -1;
|
|
#endif
|
|
if (fd < 0)
|
|
{
|
|
printf ("unable to create temporary file %s: %s\n", temp,
|
|
strerror (errno));
|
|
return (NULL);
|
|
}
|
|
close (fd);
|
|
|
|
oldverbose = verbose, verbose = 0;
|
|
oldhash = hash, hash = 0;
|
|
if (doswitch)
|
|
{
|
|
pswitch (!proxy);
|
|
}
|
|
for (mode = "w"; *++argv != NULL; mode = "a")
|
|
recvrequest ("NLST", temp, *argv, mode, 0);
|
|
if (doswitch)
|
|
{
|
|
pswitch (!proxy);
|
|
}
|
|
verbose = oldverbose;
|
|
hash = oldhash;
|
|
ftemp = fopen (temp, "r");
|
|
unlink (temp);
|
|
if (ftemp == NULL)
|
|
{
|
|
printf ("can't find list of remote files, oops\n");
|
|
return (NULL);
|
|
}
|
|
}
|
|
|
|
buf_len = 100; /* Any old size */
|
|
buf = malloc (buf_len + 1);
|
|
|
|
sofar = 0;
|
|
for (;;)
|
|
{
|
|
if (!buf)
|
|
{
|
|
printf ("malloc failure\n");
|
|
return 0;
|
|
}
|
|
if (!fgets (buf + sofar, buf_len - sofar, ftemp))
|
|
{
|
|
fclose (ftemp);
|
|
ftemp = NULL;
|
|
free (buf);
|
|
return 0;
|
|
}
|
|
|
|
sofar = strlen (buf);
|
|
if (buf[sofar - 1] == '\n')
|
|
{
|
|
buf[sofar - 1] = '\0';
|
|
return buf;
|
|
}
|
|
|
|
/* Make more room and read some more... */
|
|
buf_len += buf_len;
|
|
buf = realloc (buf, buf_len);
|
|
}
|
|
}
|
|
|
|
char *
|
|
onoff (int val)
|
|
{
|
|
return (val ? "on" : "off");
|
|
}
|
|
|
|
/*
|
|
* Show status.
|
|
*/
|
|
void
|
|
status (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
int i;
|
|
|
|
if (connected)
|
|
printf ("Connected to %s.\n", hostname);
|
|
else
|
|
printf ("Not connected.\n");
|
|
printf ("Connection addressing: %s\n",
|
|
(usefamily == AF_UNSPEC) ? "any"
|
|
: (usefamily == AF_INET6) ? "IPv6" : "IPv4");
|
|
if (!proxy)
|
|
{
|
|
pswitch (1);
|
|
if (connected)
|
|
{
|
|
printf ("Connected for proxy commands to %s.\n", hostname);
|
|
}
|
|
else
|
|
{
|
|
printf ("No proxy connection.\n");
|
|
}
|
|
pswitch (0);
|
|
}
|
|
printf ("Mode: %s; Type: %s; Form: %s; Structure: %s\n",
|
|
modename, typename, formname, structname);
|
|
printf ("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n",
|
|
onoff (verbose), onoff (bell), onoff (interactive), onoff (doglob));
|
|
printf ("Store unique: %s; Receive unique: %s\n", onoff (sunique),
|
|
onoff (runique));
|
|
printf ("Case: %s; CR stripping: %s\n", onoff (mcase), onoff (crflag));
|
|
if (ntflag)
|
|
{
|
|
printf ("Ntrans: (in) %s (out) %s\n", ntin, ntout);
|
|
}
|
|
else
|
|
{
|
|
printf ("Ntrans: off\n");
|
|
}
|
|
if (mapflag)
|
|
{
|
|
printf ("Nmap: (in) %s (out) %s\n", mapin, mapout);
|
|
}
|
|
else
|
|
{
|
|
printf ("Nmap: off\n");
|
|
}
|
|
printf ("Hash mark printing: %s; Use of PORT cmds: %s\n",
|
|
onoff (hash), onoff (sendport));
|
|
printf ("Use of EPRT/EPSV for IPv4: %s\n", onoff (doepsv4));
|
|
if (macnum > 0)
|
|
{
|
|
printf ("Macros:\n");
|
|
for (i = 0; i < macnum; i++)
|
|
{
|
|
printf ("\t%s\n", macros[i].mac_name);
|
|
}
|
|
}
|
|
code = 0;
|
|
}
|
|
|
|
/*
|
|
* Set beep on cmd completed mode.
|
|
*/
|
|
void
|
|
setbell (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
bell = !bell;
|
|
printf ("Bell mode %s.\n", onoff (bell));
|
|
code = bell;
|
|
}
|
|
|
|
/*
|
|
* Turn on packet tracing.
|
|
*/
|
|
void
|
|
settrace (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
trace = !trace;
|
|
printf ("Packet tracing %s.\n", onoff (trace));
|
|
code = trace;
|
|
}
|
|
|
|
/*
|
|
* Toggle hash mark printing during transfers.
|
|
*
|
|
* Parse multipliers 'k', 'K', 'm', 'M', and
|
|
* 'g', 'G' to simplify the size step.
|
|
*
|
|
* With a numerical argument, hash marking is
|
|
* made active, and the step size is updated.
|
|
* Toggle state only in absence of an argument.
|
|
*/
|
|
void
|
|
sethash (int argc MAYBE_UNUSED, char **argv)
|
|
{
|
|
char *p = argv[1];
|
|
|
|
/* P is NULL when no argument was passed with `hash'. */
|
|
while (p && isdigit (*p))
|
|
p++;
|
|
|
|
if (argv[1] != NULL)
|
|
sscanf (argv[1], "%d", &hashbytes);
|
|
|
|
/* Apply a multiplier only if a numerical part exists. */
|
|
if (argv[1] && isdigit (*argv[1]))
|
|
{
|
|
hash = 1; /* Enforce markers. */
|
|
|
|
switch (*p)
|
|
{
|
|
case 'g':
|
|
case 'G':
|
|
hashbytes *= 1024 * 1024 * 1024;
|
|
break;
|
|
case 'm':
|
|
case 'M':
|
|
hashbytes *= 1024 * 1024;
|
|
break;
|
|
case 'k':
|
|
case 'K':
|
|
hashbytes *= 1024;
|
|
}
|
|
}
|
|
|
|
if (hashbytes <= 0)
|
|
hashbytes = 1024;
|
|
|
|
if (!argv[1]) /* Toggle when argument is absent. */
|
|
hash = !hash;
|
|
|
|
printf ("Hash mark printing %s", onoff (hash));
|
|
if (hash)
|
|
printf (" (%d bytes/hash mark)", hashbytes);
|
|
printf (".\n");
|
|
|
|
code = hash;
|
|
}
|
|
|
|
/*
|
|
* Turn on printing of server echo's.
|
|
*/
|
|
void
|
|
setverbose (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
verbose = !verbose;
|
|
printf ("Verbose mode %s.\n", onoff (verbose));
|
|
code = verbose;
|
|
}
|
|
|
|
/*
|
|
* Allow any address family.
|
|
*/
|
|
void
|
|
setipany (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
usefamily = AF_UNSPEC;
|
|
printf ("Selecting addresses: %s.\n", "any");
|
|
code = usefamily;
|
|
}
|
|
|
|
/*
|
|
* Restrict to IPv4 addresses.
|
|
*/
|
|
void
|
|
setipv4 (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
usefamily = AF_INET;
|
|
printf ("Selecting addresses: %s.\n", "IPv4");
|
|
code = usefamily;
|
|
}
|
|
|
|
/*
|
|
* Restrict to IPv6 addresses.
|
|
*/
|
|
void
|
|
setipv6 (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
usefamily = AF_INET6;
|
|
printf ("Selecting addresses: %s.\n", "IPv6");
|
|
code = usefamily;
|
|
}
|
|
|
|
/*
|
|
* Toggle use of EPRT/EPRT for IPv4.
|
|
*/
|
|
void
|
|
setepsv4 (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
doepsv4 = !doepsv4;
|
|
printf ("Use of EPRT/EPSV for IPv4: %s.\n", onoff (doepsv4));
|
|
code = doepsv4;
|
|
}
|
|
|
|
/*
|
|
* Toggle PORT cmd use before each data connection.
|
|
*/
|
|
void
|
|
setport (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
sendport = !sendport;
|
|
printf ("Use of PORT cmds %s.\n", onoff (sendport));
|
|
code = sendport;
|
|
}
|
|
|
|
/*
|
|
* Turn on interactive prompting
|
|
* during mget, mput, and mdelete.
|
|
*/
|
|
void
|
|
setprompt (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
interactive = !interactive;
|
|
printf ("Interactive mode %s.\n", onoff (interactive));
|
|
code = interactive;
|
|
}
|
|
|
|
/*
|
|
* Toggle metacharacter interpretation
|
|
* on local file names.
|
|
*/
|
|
void
|
|
setglob (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
doglob = !doglob;
|
|
printf ("Globbing %s.\n", onoff (doglob));
|
|
code = doglob;
|
|
}
|
|
|
|
/*
|
|
* Set debugging mode on/off and/or
|
|
* set level of debugging.
|
|
*/
|
|
void
|
|
setdebug (int argc, char **argv)
|
|
{
|
|
int val;
|
|
|
|
if (argc > 1)
|
|
{
|
|
val = atoi (argv[1]);
|
|
if (val < 0)
|
|
{
|
|
printf ("%s: bad debugging value.\n", argv[1]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
val = !debug;
|
|
debug = val;
|
|
if (debug)
|
|
options |= SO_DEBUG;
|
|
else
|
|
options &= ~SO_DEBUG;
|
|
printf ("Debugging %s (debug=%d).\n", onoff (debug), debug);
|
|
code = debug > 0;
|
|
}
|
|
|
|
/*
|
|
* Set current working directory
|
|
* on remote machine.
|
|
*/
|
|
void
|
|
cd (int argc, char **argv)
|
|
{
|
|
|
|
if (argc < 2 && !another (&argc, &argv, "remote-directory"))
|
|
{
|
|
printf ("usage: %s remote-directory\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
if (command ("CWD %s", argv[1]) == ERROR && code == 500)
|
|
{
|
|
if (verbose)
|
|
printf ("CWD command not recognized, trying XCWD\n");
|
|
command ("XCWD %s", argv[1]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set current working directory
|
|
* on local machine.
|
|
*/
|
|
void
|
|
lcd (int argc, char **argv)
|
|
{
|
|
char *dir;
|
|
|
|
if (argc < 2)
|
|
argc++, argv[1] = home;
|
|
if (argc != 2)
|
|
{
|
|
printf ("usage: %s local-directory\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
|
|
dir = globulize (argv[1]);
|
|
if (!dir)
|
|
{
|
|
code = -1;
|
|
return;
|
|
}
|
|
|
|
if (chdir (dir) < 0)
|
|
{
|
|
error (0, errno, "dir: %s", dir);
|
|
free (dir);
|
|
code = -1;
|
|
return;
|
|
}
|
|
|
|
free (dir);
|
|
|
|
dir = xgetcwd ();
|
|
if (dir)
|
|
{
|
|
printf ("Local directory now %s\n", dir);
|
|
free (dir);
|
|
}
|
|
else
|
|
error (0, errno, "getcwd");
|
|
code = 0;
|
|
}
|
|
|
|
/*
|
|
* Delete a single file.
|
|
*/
|
|
void
|
|
delete (int argc, char **argv)
|
|
{
|
|
|
|
if (argc < 2 && !another (&argc, &argv, "remote-file"))
|
|
{
|
|
printf ("usage: %s remote-file\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
command ("DELE %s", argv[1]);
|
|
}
|
|
|
|
/*
|
|
* Delete multiple files.
|
|
*/
|
|
void
|
|
mdelete (int argc, char **argv)
|
|
{
|
|
sighandler_t oldintr;
|
|
int ointer;
|
|
char *cp;
|
|
|
|
if (argc < 2 && !another (&argc, &argv, "remote-files"))
|
|
{
|
|
printf ("usage: %s remote-files\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
mname = argv[0];
|
|
mflag = 1;
|
|
oldintr = signal (SIGINT, mabort);
|
|
setjmp (jabort);
|
|
while ((cp = remglob (argv, 0)) != NULL)
|
|
{
|
|
if (*cp == '\0')
|
|
{
|
|
mflag = 0;
|
|
continue;
|
|
}
|
|
if (mflag && confirm (argv[0], cp))
|
|
{
|
|
command ("DELE %s", cp);
|
|
if (!mflag && fromatty)
|
|
{
|
|
ointer = interactive;
|
|
interactive = 1;
|
|
if (confirm ("Continue with", "mdelete"))
|
|
{
|
|
mflag++;
|
|
}
|
|
interactive = ointer;
|
|
}
|
|
}
|
|
free (cp);
|
|
}
|
|
signal (SIGINT, oldintr);
|
|
mflag = 0;
|
|
}
|
|
|
|
/*
|
|
* Rename a remote file.
|
|
*/
|
|
void
|
|
renamefile (int argc, char **argv)
|
|
{
|
|
|
|
if (argc < 2 && !another (&argc, &argv, "from-name"))
|
|
goto usage;
|
|
if (argc < 3 && !another (&argc, &argv, "to-name"))
|
|
{
|
|
usage:
|
|
printf ("%s from-name to-name\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
if (command ("RNFR %s", argv[1]) == CONTINUE)
|
|
command ("RNTO %s", argv[2]);
|
|
}
|
|
|
|
/*
|
|
* Get a directory listing
|
|
* of remote files.
|
|
*/
|
|
void
|
|
ls (int argc, char **argv)
|
|
{
|
|
char *cmd, *dest;
|
|
|
|
if (argc < 2)
|
|
argc++, argv[1] = NULL;
|
|
if (argc < 3)
|
|
argc++, argv[2] = "-";
|
|
if (argc > 3)
|
|
{
|
|
printf ("usage: %s remote-directory local-file\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
cmd = argv[0][0] == 'n' ? "NLST" : "LIST";
|
|
|
|
if (strcmp (argv[2], "-") != 0)
|
|
{
|
|
dest = globulize (argv[2]);
|
|
if (!dest)
|
|
{
|
|
code = -1;
|
|
return;
|
|
}
|
|
if (*dest != '|' && !confirm ("output to local-file:", dest))
|
|
{
|
|
code = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
else
|
|
dest = 0;
|
|
|
|
recvrequest (cmd, dest ? dest : "-", argv[1], "w", 0);
|
|
out:
|
|
free (dest);
|
|
}
|
|
|
|
/*
|
|
* Get a directory listing
|
|
* of multiple remote files.
|
|
*/
|
|
void
|
|
mls (int argc, char **argv)
|
|
{
|
|
sighandler_t oldintr;
|
|
int ointer, i;
|
|
char *cmd, mode[1], *dest;
|
|
|
|
if (argc < 2 && !another (&argc, &argv, "remote-files"))
|
|
goto usage;
|
|
if (argc < 3 && !another (&argc, &argv, "local-file"))
|
|
{
|
|
usage:
|
|
printf ("usage: %s remote-files local-file\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
|
|
dest = argv[argc - 1];
|
|
argv[argc - 1] = NULL;
|
|
if (strcmp (dest, "-") && *dest != '|')
|
|
{
|
|
dest = globulize (dest);
|
|
if (!dest)
|
|
{
|
|
code = -1;
|
|
return;
|
|
}
|
|
if (!confirm ("output to local-file:", dest))
|
|
{
|
|
code = -1;
|
|
free (dest);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
dest = strdup (dest);
|
|
|
|
cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
|
|
mname = argv[0];
|
|
mflag = 1;
|
|
oldintr = signal (SIGINT, mabort);
|
|
setjmp (jabort);
|
|
for (i = 1; mflag && i < argc - 1; ++i)
|
|
{
|
|
*mode = (i == 1) ? 'w' : 'a';
|
|
recvrequest (cmd, dest, argv[i], mode, 0);
|
|
if (!mflag && fromatty)
|
|
{
|
|
ointer = interactive;
|
|
interactive = 1;
|
|
if (confirm ("Continue with", argv[0]))
|
|
{
|
|
mflag++;
|
|
}
|
|
interactive = ointer;
|
|
}
|
|
}
|
|
|
|
signal (SIGINT, oldintr);
|
|
mflag = 0;
|
|
free (dest);
|
|
}
|
|
|
|
/*
|
|
* Do a shell escape
|
|
*/
|
|
void
|
|
shell (int argc, char **argv MAYBE_UNUSED)
|
|
{
|
|
pid_t pid;
|
|
sighandler_t old1, old2;
|
|
char shellnam[40], *shell, *namep;
|
|
|
|
old1 = signal (SIGINT, SIG_IGN);
|
|
old2 = signal (SIGQUIT, SIG_IGN);
|
|
if ((pid = fork ()) == 0)
|
|
{
|
|
for (pid = 3; pid < 20; pid++)
|
|
close (pid);
|
|
signal (SIGINT, SIG_DFL);
|
|
signal (SIGQUIT, SIG_DFL);
|
|
shell = getenv ("SHELL");
|
|
if (shell == NULL)
|
|
shell = PATH_BSHELL;
|
|
namep = strrchr (shell, '/');
|
|
if (namep == NULL)
|
|
namep = shell; /* No slash in this name. */
|
|
else
|
|
namep++; /* Skip the slash character. */
|
|
strcpy (shellnam, "-");
|
|
strncat (shellnam, namep, sizeof (shellnam) - 2);
|
|
if (strcmp (namep, "sh") != 0)
|
|
shellnam[0] = '+';
|
|
if (debug)
|
|
{
|
|
printf ("%s\n", shell);
|
|
fflush (stdout);
|
|
}
|
|
if (argc > 1)
|
|
{
|
|
execl (shell, shellnam, "-c", altarg, (char *) 0);
|
|
}
|
|
else
|
|
{
|
|
execl (shell, shellnam, (char *) 0);
|
|
}
|
|
error (0, errno, "shell");
|
|
code = -1;
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
if (pid > 0)
|
|
while (wait (0) != pid)
|
|
;
|
|
signal (SIGINT, old1);
|
|
signal (SIGQUIT, old2);
|
|
if (pid == -1)
|
|
{
|
|
error (0, errno, "Try again later");
|
|
code = -1;
|
|
}
|
|
else
|
|
{
|
|
code = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Send new user information (re-login)
|
|
*/
|
|
void
|
|
user (int argc, char **argv)
|
|
{
|
|
char acct[80];
|
|
int n, aflag = 0;
|
|
|
|
if (argc < 2)
|
|
another (&argc, &argv, "username");
|
|
if (argc < 2 || argc > 4)
|
|
{
|
|
printf ("usage: %s username [password] [account]\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
n = command ("USER %s", argv[1]);
|
|
if (n == CONTINUE)
|
|
{
|
|
/* Is this a case of challenge-response?
|
|
* RFC 2228 stipulates code 336 for this.
|
|
* Suppress message in verbose mode, since
|
|
* it has already been displayed.
|
|
*/
|
|
if (code == 336 && !verbose)
|
|
printf ("%s\n", reply_string + strlen ("336 "));
|
|
/* In addition, any password given on the
|
|
* command line is irrelevant, so ignore it.
|
|
*/
|
|
if (argc < 3 || code == 336)
|
|
argv[2] = getpass ("Password: ");
|
|
if (argc < 3)
|
|
argc++;
|
|
n = command ("PASS %s", argv[2]);
|
|
if (argv[2])
|
|
memset (argv[2], 0, strlen (argv[2]));
|
|
}
|
|
if (n == CONTINUE)
|
|
{
|
|
if (argc < 4)
|
|
{
|
|
printf ("Account: ");
|
|
fflush (stdout);
|
|
if (fgets (acct, sizeof (acct) - 1, stdin))
|
|
acct[strlen (acct) - 1] = '\0'; /* Erase newline. */
|
|
else
|
|
acct[0] = '\0'; /* Set empty name. */
|
|
argv[3] = acct;
|
|
argc++;
|
|
}
|
|
n = command ("ACCT %s", argv[3]);
|
|
aflag++;
|
|
}
|
|
if (n != COMPLETE)
|
|
{
|
|
fprintf (stdout, "Login failed.\n");
|
|
return;
|
|
}
|
|
if (!aflag && argc == 4)
|
|
{
|
|
command ("ACCT %s", argv[3]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Print working directory.
|
|
*/
|
|
void
|
|
pwd (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
int oldverbose = verbose;
|
|
|
|
/*
|
|
* If we aren't verbose, this doesn't do anything!
|
|
*/
|
|
verbose = 1;
|
|
if (command ("PWD") == ERROR && code == 500)
|
|
{
|
|
printf ("PWD command not recognized, trying XPWD\n");
|
|
command ("XPWD");
|
|
}
|
|
verbose = oldverbose;
|
|
}
|
|
|
|
/*
|
|
* Print local working directory.
|
|
*/
|
|
void
|
|
lpwd (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
char *dir = xgetcwd ();
|
|
|
|
if (dir)
|
|
{
|
|
printf ("Local directory is %s\n", dir);
|
|
free (dir);
|
|
}
|
|
else
|
|
error (0, errno, "getcwd");
|
|
|
|
code = 0;
|
|
}
|
|
|
|
/*
|
|
* Make a directory.
|
|
*/
|
|
void
|
|
makedir (int argc, char **argv)
|
|
{
|
|
|
|
if (argc < 2 && !another (&argc, &argv, "directory-name"))
|
|
{
|
|
printf ("usage: %s directory-name\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
if (command ("MKD %s", argv[1]) == ERROR && code == 500)
|
|
{
|
|
if (verbose)
|
|
printf ("MKD command not recognized, trying XMKD\n");
|
|
command ("XMKD %s", argv[1]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Remove a directory.
|
|
*/
|
|
void
|
|
removedir (int argc, char **argv)
|
|
{
|
|
|
|
if (argc < 2 && !another (&argc, &argv, "directory-name"))
|
|
{
|
|
printf ("usage: %s directory-name\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
if (command ("RMD %s", argv[1]) == ERROR && code == 500)
|
|
{
|
|
if (verbose)
|
|
printf ("RMD command not recognized, trying XRMD\n");
|
|
command ("XRMD %s", argv[1]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Send a line, verbatim, to the remote machine.
|
|
*/
|
|
void
|
|
quote (int argc, char **argv)
|
|
{
|
|
|
|
if (argc < 2 && !another (&argc, &argv, "command line to send"))
|
|
{
|
|
printf ("usage: %s line-to-send\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
quote1 ("", argc, argv);
|
|
}
|
|
|
|
/*
|
|
* Send a SITE command to the remote machine. The line
|
|
* is sent verbatim to the remote machine, except that the
|
|
* word "SITE" is added at the front.
|
|
*/
|
|
void
|
|
site (int argc, char **argv)
|
|
{
|
|
|
|
if (argc < 2 && !another (&argc, &argv, "arguments to SITE command"))
|
|
{
|
|
printf ("usage: %s line-to-send\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
quote1 ("SITE ", argc, argv);
|
|
}
|
|
|
|
/*
|
|
* Turn argv[1..argc) into a space-separated string, then prepend initial text.
|
|
* Send the result as a one-line command and get response.
|
|
*/
|
|
void
|
|
quote1 (char *initial, int argc, char **argv)
|
|
{
|
|
int i, len;
|
|
char buf[BUFSIZ]; /* must be >= sizeof(line) */
|
|
|
|
strcpy (buf, initial);
|
|
if (argc > 1)
|
|
{
|
|
len = strlen (buf);
|
|
len += strlen (strcpy (&buf[len], argv[1]));
|
|
for (i = 2; i < argc; i++)
|
|
{
|
|
buf[len++] = ' ';
|
|
len += strlen (strcpy (&buf[len], argv[i]));
|
|
}
|
|
}
|
|
if (command (buf) == PRELIM)
|
|
{
|
|
while (getreply (0) == PRELIM)
|
|
continue;
|
|
}
|
|
}
|
|
|
|
void
|
|
do_chmod (int argc, char **argv)
|
|
{
|
|
|
|
if (argc < 2 && !another (&argc, &argv, "mode"))
|
|
goto usage;
|
|
if (argc < 3 && !another (&argc, &argv, "file-name"))
|
|
{
|
|
usage:
|
|
printf ("usage: %s mode file-name\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
command ("SITE CHMOD %s %s", argv[1], argv[2]);
|
|
}
|
|
|
|
void
|
|
do_umask (int argc, char **argv)
|
|
{
|
|
int oldverbose = verbose;
|
|
|
|
verbose = 1;
|
|
command (argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]);
|
|
verbose = oldverbose;
|
|
}
|
|
|
|
void
|
|
site_idle (int argc, char **argv)
|
|
{
|
|
int oldverbose = verbose;
|
|
|
|
verbose = 1;
|
|
command (argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]);
|
|
verbose = oldverbose;
|
|
}
|
|
|
|
/*
|
|
* Ask the other side for help.
|
|
*/
|
|
void
|
|
rmthelp (int argc, char **argv)
|
|
{
|
|
int oldverbose = verbose;
|
|
|
|
verbose = 1;
|
|
command (argc == 1 ? "HELP" : "HELP %s", argv[1]);
|
|
verbose = oldverbose;
|
|
}
|
|
|
|
/*
|
|
* Terminate session and exit.
|
|
*/
|
|
void
|
|
quit (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
if (connected)
|
|
disconnect (0, 0);
|
|
pswitch (1);
|
|
if (connected)
|
|
{
|
|
disconnect (0, 0);
|
|
}
|
|
exit (EXIT_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* Terminate session, but don't exit.
|
|
*/
|
|
void
|
|
disconnect (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
if (!connected)
|
|
return;
|
|
command ("QUIT");
|
|
if (cout)
|
|
{
|
|
fclose (cout);
|
|
}
|
|
cout = NULL;
|
|
connected = 0;
|
|
data = -1;
|
|
if (!proxy)
|
|
{
|
|
macnum = 0;
|
|
}
|
|
}
|
|
|
|
int
|
|
confirm (char *cmd, char *file)
|
|
{
|
|
char input[BUFSIZ];
|
|
|
|
if (!interactive)
|
|
return (1);
|
|
printf ("%s %s? ", cmd, file);
|
|
fflush (stdout);
|
|
if (fgets (input, sizeof input, stdin) == NULL)
|
|
return (0);
|
|
return (*input != 'n' && *input != 'N');
|
|
}
|
|
|
|
void
|
|
fatal (char *msg)
|
|
{
|
|
|
|
error (EXIT_FAILURE, 0, "%s", msg);
|
|
}
|
|
|
|
/*
|
|
* Glob a local file name specification with
|
|
* the expectation of a single return value.
|
|
* Can't control multiple values being expanded
|
|
* from the expression, we return only the first.
|
|
*/
|
|
char *
|
|
globulize (char *cp)
|
|
{
|
|
glob_t gl;
|
|
int flags;
|
|
|
|
if (!doglob)
|
|
return strdup (cp);
|
|
|
|
flags = GLOB_BRACE | GLOB_NOCHECK | GLOB_TILDE;
|
|
#ifdef GLOB_QUOTE
|
|
flags |= GLOB_QUOTE;
|
|
#endif
|
|
|
|
memset (&gl, 0, sizeof (gl));
|
|
if (glob (cp, flags, NULL, &gl) || gl.gl_pathc == 0)
|
|
{
|
|
error (0, 0, "%s: not found", cp);
|
|
globfree (&gl);
|
|
return (0);
|
|
}
|
|
|
|
cp = strdup (gl.gl_pathv[0]);
|
|
globfree (&gl);
|
|
|
|
return cp;
|
|
}
|
|
|
|
void
|
|
account (int argc, char **argv)
|
|
{
|
|
char acct[50], *ap;
|
|
|
|
if (argc > 1)
|
|
{
|
|
++argv;
|
|
--argc;
|
|
strncpy (acct, *argv, sizeof (acct) - 1);
|
|
acct[sizeof (acct) - 1] = '\0';
|
|
while (argc > 1)
|
|
{
|
|
--argc;
|
|
++argv;
|
|
strncat (acct, *argv, (sizeof (acct) - 1) - strlen (acct));
|
|
}
|
|
ap = acct;
|
|
}
|
|
else
|
|
{
|
|
ap = getpass ("Account:");
|
|
}
|
|
command ("ACCT %s", ap);
|
|
if (ap)
|
|
memset (ap, 0, strlen (ap));
|
|
}
|
|
|
|
jmp_buf abortprox;
|
|
|
|
void
|
|
proxabort (int sig MAYBE_UNUSED)
|
|
{
|
|
|
|
if (!proxy)
|
|
{
|
|
pswitch (1);
|
|
}
|
|
if (connected)
|
|
{
|
|
proxflag = 1;
|
|
}
|
|
else
|
|
{
|
|
proxflag = 0;
|
|
}
|
|
pswitch (0);
|
|
longjmp (abortprox, 1);
|
|
}
|
|
|
|
void
|
|
doproxy (int argc, char **argv)
|
|
{
|
|
struct cmd *c;
|
|
sighandler_t oldintr;
|
|
|
|
if (argc < 2 && !another (&argc, &argv, "command"))
|
|
{
|
|
printf ("usage: %s command\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
c = getcmd (argv[1]);
|
|
if (c == (struct cmd *) -1)
|
|
{
|
|
printf ("?Ambiguous command\n");
|
|
fflush (stdout);
|
|
code = -1;
|
|
return;
|
|
}
|
|
if (c == 0)
|
|
{
|
|
printf ("?Invalid command\n");
|
|
fflush (stdout);
|
|
code = -1;
|
|
return;
|
|
}
|
|
if (!c->c_proxy)
|
|
{
|
|
printf ("?Invalid proxy command\n");
|
|
fflush (stdout);
|
|
code = -1;
|
|
return;
|
|
}
|
|
if (setjmp (abortprox))
|
|
{
|
|
code = -1;
|
|
return;
|
|
}
|
|
oldintr = signal (SIGINT, proxabort);
|
|
pswitch (1);
|
|
if (c->c_conn && !connected)
|
|
{
|
|
printf ("Not connected\n");
|
|
fflush (stdout);
|
|
pswitch (0);
|
|
signal (SIGINT, oldintr);
|
|
code = -1;
|
|
return;
|
|
}
|
|
(*c->c_handler) (argc - 1, argv + 1);
|
|
if (connected)
|
|
{
|
|
proxflag = 1;
|
|
}
|
|
else
|
|
{
|
|
proxflag = 0;
|
|
}
|
|
pswitch (0);
|
|
signal (SIGINT, oldintr);
|
|
}
|
|
|
|
void
|
|
setcase (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
mcase = !mcase;
|
|
printf ("Case mapping %s.\n", onoff (mcase));
|
|
code = mcase;
|
|
}
|
|
|
|
void
|
|
setcr (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
crflag = !crflag;
|
|
printf ("Carriage Return stripping %s.\n", onoff (crflag));
|
|
code = crflag;
|
|
}
|
|
|
|
void
|
|
setntrans (int argc, char **argv)
|
|
{
|
|
if (argc == 1)
|
|
{
|
|
ntflag = 0;
|
|
printf ("Ntrans off.\n");
|
|
code = ntflag;
|
|
return;
|
|
}
|
|
ntflag++;
|
|
code = ntflag;
|
|
strncpy (ntin, argv[1], sizeof (ntin) - 1);
|
|
ntin[sizeof (ntin) - 1] = '\0';
|
|
if (argc == 2)
|
|
{
|
|
ntout[0] = '\0';
|
|
return;
|
|
}
|
|
strncpy (ntout, argv[2], sizeof (ntout) - 1);
|
|
ntout[sizeof (ntout) - 1] = '\0';
|
|
}
|
|
|
|
/* NOTE: dotrans() always returns a newly allocated string.
|
|
*/
|
|
|
|
char *
|
|
dotrans (char *name)
|
|
{
|
|
char *new = xmalloc (strlen (name) + 1);
|
|
char *cp1, *cp2 = new;
|
|
size_t i, ostop, found;
|
|
|
|
for (ostop = 0; *(ntout + ostop) && ostop < sizeof (ntout) - 1; ostop++)
|
|
continue;
|
|
for (cp1 = name; *cp1; cp1++)
|
|
{
|
|
found = 0;
|
|
for (i = 0; *(ntin + i) && i < sizeof (ntin) - 1; i++)
|
|
{
|
|
if (*cp1 == *(ntin + i))
|
|
{
|
|
found++;
|
|
if (i < ostop)
|
|
{
|
|
*cp2++ = *(ntout + i);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
{
|
|
*cp2++ = *cp1;
|
|
}
|
|
}
|
|
*cp2 = '\0';
|
|
return (new);
|
|
}
|
|
|
|
void
|
|
setpassive (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
passivemode = !passivemode;
|
|
printf ("Passive mode %s.\n", onoff (passivemode));
|
|
code = passivemode;
|
|
}
|
|
|
|
void
|
|
setnmap (int argc, char **argv)
|
|
{
|
|
char *cp;
|
|
|
|
if (argc == 1)
|
|
{
|
|
mapflag = 0;
|
|
printf ("Nmap off.\n");
|
|
code = mapflag;
|
|
return;
|
|
}
|
|
if (argc < 3 && !another (&argc, &argv, "mapout"))
|
|
{
|
|
printf ("Usage: %s [mapin mapout]\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
mapflag = 1;
|
|
code = 1;
|
|
cp = altarg;
|
|
while (*cp && !isblank (*cp))
|
|
cp++;
|
|
if (proxy)
|
|
{
|
|
while (*cp && isblank (*cp))
|
|
cp++;
|
|
altarg = cp;
|
|
while (*cp && !isblank (*cp))
|
|
cp++;
|
|
}
|
|
*cp = '\0';
|
|
|
|
free (mapin);
|
|
mapin = strdup (altarg);
|
|
|
|
do
|
|
cp++;
|
|
while (*cp && isblank (*cp));
|
|
free (mapout);
|
|
mapout = strdup (cp);
|
|
}
|
|
|
|
static int
|
|
cp_subst (char **from_p, char **to_p, int *toks, char **tp, char **te,
|
|
char *tok0, char **buf_p, int *buf_len_p)
|
|
{
|
|
int toknum;
|
|
char *src;
|
|
size_t src_len;
|
|
|
|
if (*++(*from_p) == '0')
|
|
{
|
|
src = tok0;
|
|
src_len = strlen (tok0);
|
|
}
|
|
else if (toks[toknum = **from_p - '1'])
|
|
{
|
|
src = tp[toknum];
|
|
src_len = te[toknum] - src;
|
|
}
|
|
else
|
|
return 0;
|
|
|
|
if (src_len > strlen ("$2"))
|
|
{
|
|
/* This substitution will be longer than the original text.
|
|
* Allocate a larger buffer and update the cursor, pointing
|
|
* within the new memory area.
|
|
*/
|
|
size_t offset = *to_p - *buf_p;
|
|
|
|
*buf_len_p += src_len - strlen ("$2");
|
|
*buf_p = realloc (*buf_p, *buf_len_p);
|
|
*to_p = *buf_p + offset;
|
|
}
|
|
|
|
while (src_len--)
|
|
*(*to_p)++ = *src++;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* NOTE: domap() can return a newly allocated string,
|
|
* but need not do so every time.
|
|
*/
|
|
|
|
char *
|
|
domap (char *name)
|
|
{
|
|
/* The string `mapout' will have its tokens expanded,
|
|
* but is essentially the minimal output string.
|
|
* Some brackets and some alternate strings might
|
|
* need to be suppressed.
|
|
*/
|
|
int buf_len = strlen (mapout) + 1;
|
|
char *buf = xmalloc (buf_len);
|
|
char *cp1 = name, *cp2 = mapin;
|
|
char *tp[9], *te[9];
|
|
int i, toks[9], toknum = 0, match = 1;
|
|
|
|
for (i = 0; i < 9; ++i)
|
|
{
|
|
toks[i] = 0;
|
|
}
|
|
|
|
/* Tokenize the input pattern against incoming file name.
|
|
*/
|
|
while (match && *cp1 && *cp2)
|
|
{
|
|
switch (*cp2)
|
|
{
|
|
case '\\':
|
|
if (*++cp2 != *cp1)
|
|
{
|
|
match = 0;
|
|
}
|
|
break;
|
|
case '$':
|
|
if (*(cp2 + 1) >= '1' && (*cp2 + 1) <= '9')
|
|
{
|
|
if (*cp1 != *(++cp2 + 1)) /* Break at delimiter. */
|
|
{
|
|
toks[toknum = *cp2 - '1']++;
|
|
tp[toknum] = cp1;
|
|
while (*++cp1 && *(cp2 + 1) != *cp1)
|
|
;
|
|
te[toknum] = cp1;
|
|
}
|
|
cp2++;
|
|
break;
|
|
}
|
|
/* Fall through, as '$' must be used verbatim. */
|
|
/* FALLTHROUGH */
|
|
default:
|
|
if (*cp2 != *cp1)
|
|
{
|
|
match = 0;
|
|
}
|
|
break;
|
|
}
|
|
if (match && *cp1)
|
|
{
|
|
cp1++;
|
|
}
|
|
if (match && *cp2)
|
|
{
|
|
cp2++;
|
|
}
|
|
}
|
|
if (!match && *cp1) /* last token mismatch */
|
|
{
|
|
toks[toknum] = 0;
|
|
}
|
|
|
|
/* Back substitute tokens into output template
|
|
* string `mapout'. All fixed characters were
|
|
* already accounted for in presetting BUF_LEN.
|
|
*/
|
|
cp1 = buf;
|
|
*cp1 = '\0';
|
|
cp2 = mapout;
|
|
while (*cp2)
|
|
{
|
|
match = 0;
|
|
switch (*cp2)
|
|
{
|
|
case '\\':
|
|
if (*(cp2 + 1))
|
|
{
|
|
*cp1++ = *++cp2;
|
|
}
|
|
break;
|
|
case '[':
|
|
LOOP:
|
|
if (*++cp2 == '$' && isdigit (*(cp2 + 1)))
|
|
{
|
|
if (cp_subst (&cp2, &cp1, toks, tp, te, name, &buf, &buf_len))
|
|
match = 1;
|
|
}
|
|
else
|
|
{
|
|
while (*cp2 && *cp2 != ',' && *cp2 != ']')
|
|
{
|
|
if (*cp2 == '\\')
|
|
{
|
|
cp2++;
|
|
}
|
|
else if (*cp2 == '$' && isdigit (*(cp2 + 1)))
|
|
{
|
|
if (cp_subst (&cp2,
|
|
&cp1, toks, tp, te, name, &buf, &buf_len))
|
|
match = 1;
|
|
}
|
|
else if (*cp2)
|
|
*cp1++ = *cp2++;
|
|
}
|
|
if (!*cp2)
|
|
{
|
|
printf ("nmap: unbalanced brackets\n");
|
|
return (name);
|
|
}
|
|
match = 1;
|
|
cp2--;
|
|
}
|
|
if (match)
|
|
{
|
|
/* Skip over all alternate text. */
|
|
while (*++cp2 && *cp2 != ']')
|
|
{
|
|
if (*cp2 == '\\' && *(cp2 + 1))
|
|
{
|
|
cp2++;
|
|
}
|
|
}
|
|
if (!*cp2)
|
|
{
|
|
printf ("nmap: unbalanced brackets\n");
|
|
return (name);
|
|
}
|
|
break;
|
|
}
|
|
switch (*++cp2)
|
|
{
|
|
case ',':
|
|
goto LOOP;
|
|
case ']':
|
|
break;
|
|
default:
|
|
cp2--;
|
|
goto LOOP;
|
|
}
|
|
break;
|
|
case '$':
|
|
if (isdigit (*(cp2 + 1)))
|
|
{
|
|
if (cp_subst (&cp2, &cp1, toks, tp, te, name, &buf, &buf_len))
|
|
match = 1;
|
|
break;
|
|
}
|
|
/* intentional fall through */
|
|
default:
|
|
*cp1++ = *cp2;
|
|
break;
|
|
}
|
|
cp2++;
|
|
}
|
|
*cp1 = '\0';
|
|
|
|
if (!*buf)
|
|
strcpy (buf, name);
|
|
|
|
return buf;
|
|
}
|
|
|
|
void
|
|
setsunique (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
sunique = !sunique;
|
|
printf ("Store unique %s.\n", onoff (sunique));
|
|
code = sunique;
|
|
}
|
|
|
|
void
|
|
setrunique (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
runique = !runique;
|
|
printf ("Receive unique %s.\n", onoff (runique));
|
|
code = runique;
|
|
}
|
|
|
|
/* change directory to parent directory */
|
|
void
|
|
cdup (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
if (command ("CDUP") == ERROR && code == 500)
|
|
{
|
|
if (verbose)
|
|
printf ("CDUP command not recognized, trying XCUP\n");
|
|
command ("XCUP");
|
|
}
|
|
}
|
|
|
|
/* restart transfer at specific point */
|
|
void
|
|
restart (int argc, char **argv)
|
|
{
|
|
|
|
if (argc != 2)
|
|
printf ("restart: offset not specified\n");
|
|
else
|
|
{
|
|
restart_point = atoll (argv[1]);
|
|
printf ("restarting at %jd. %s\n", (intmax_t) restart_point,
|
|
"execute get, put or append to initiate transfer");
|
|
}
|
|
}
|
|
|
|
/* show remote system type */
|
|
void
|
|
syst (int argc MAYBE_UNUSED, char **argv MAYBE_UNUSED)
|
|
{
|
|
|
|
command ("SYST");
|
|
}
|
|
|
|
void
|
|
macdef (int argc, char **argv)
|
|
{
|
|
char *tmp;
|
|
int c;
|
|
|
|
if (macnum == 16)
|
|
{
|
|
printf ("Limit of 16 macros have already been defined\n");
|
|
code = -1;
|
|
return;
|
|
}
|
|
if (argc < 2 && !another (&argc, &argv, "macro name"))
|
|
{
|
|
printf ("Usage: %s macro_name\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
if (interactive)
|
|
{
|
|
printf ("Enter macro line by line, terminating it with a null line\n");
|
|
}
|
|
strncpy (macros[macnum].mac_name, argv[1],
|
|
sizeof (macros[macnum].mac_name) - 1);
|
|
if (macnum == 0)
|
|
{
|
|
macros[macnum].mac_start = macbuf;
|
|
}
|
|
else
|
|
{
|
|
macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
|
|
}
|
|
tmp = macros[macnum].mac_start;
|
|
while (tmp < macbuf + sizeof (macbuf))
|
|
{
|
|
if ((c = getchar ()) == EOF)
|
|
{
|
|
printf ("macdef:end of file encountered\n");
|
|
code = -1;
|
|
return;
|
|
}
|
|
if ((*tmp = c) == '\n')
|
|
{
|
|
if (tmp == macros[macnum].mac_start)
|
|
{
|
|
macros[macnum++].mac_end = tmp;
|
|
code = 0;
|
|
return;
|
|
}
|
|
if (*(tmp - 1) == '\0')
|
|
{
|
|
macros[macnum++].mac_end = tmp - 1;
|
|
code = 0;
|
|
return;
|
|
}
|
|
*tmp = '\0';
|
|
}
|
|
tmp++;
|
|
}
|
|
while (1)
|
|
{
|
|
while ((c = getchar ()) != '\n' && c != EOF)
|
|
/* LOOP */ ;
|
|
if (c == EOF || getchar () == '\n')
|
|
{
|
|
printf ("Macro not defined - 4k buffer exceeded\n");
|
|
code = -1;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* get size of file on remote machine
|
|
*/
|
|
void
|
|
sizecmd (int argc, char **argv)
|
|
{
|
|
|
|
if (argc < 2 && !another (&argc, &argv, "filename"))
|
|
{
|
|
printf ("usage: %s filename\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
command ("SIZE %s", argv[1]);
|
|
}
|
|
|
|
/*
|
|
* get last modification time of file on remote machine
|
|
*/
|
|
void
|
|
modtime (int argc, char **argv)
|
|
{
|
|
int overbose;
|
|
|
|
if (argc < 2 && !another (&argc, &argv, "filename"))
|
|
{
|
|
printf ("usage: %s filename\n", argv[0]);
|
|
code = -1;
|
|
return;
|
|
}
|
|
overbose = verbose;
|
|
if (debug == 0)
|
|
verbose = -1;
|
|
if (command ("MDTM %s", argv[1]) == COMPLETE)
|
|
{
|
|
int yy, mo, day, hour, min, sec;
|
|
sscanf (reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
|
|
&day, &hour, &min, &sec);
|
|
/* might want to print this in local time */
|
|
printf ("%s\t%02d/%02d/%04d %02d:%02d:%02d GMT\n", argv[1],
|
|
mo, day, yy, hour, min, sec);
|
|
}
|
|
else
|
|
printf ("%s\n", reply_string);
|
|
verbose = overbose;
|
|
}
|
|
|
|
/*
|
|
* show status on remote machine
|
|
*/
|
|
void
|
|
rmtstatus (int argc, char **argv)
|
|
{
|
|
|
|
command (argc > 1 ? "STAT %s" : "STAT", argv[1]);
|
|
}
|
|
|
|
/*
|
|
* get file if modtime is more recent than current file
|
|
*/
|
|
void
|
|
newer (int argc, char **argv)
|
|
{
|
|
|
|
if (getit (argc, argv, -1, "w"))
|
|
printf ("Local file \"%s\" is newer than remote file \"%s\"\n",
|
|
argv[2], argv[1]);
|
|
}
|