New libfetch-based fetch.
This commit is contained in:
parent
b112ea1321
commit
9b9369d90e
@ -1,9 +1,10 @@
|
||||
PROG = fetch
|
||||
SRCS = file.c ftp.c http.c main.c util.c uri.c
|
||||
# $FreeBSD$
|
||||
|
||||
CFLAGS+= -Wall -Wwrite-strings -Wmissing-prototypes
|
||||
|
||||
DPADD= ${LIBFTPIO} ${LIBMD}
|
||||
LDADD= -lftpio -lmd
|
||||
MAINTAINER= des@freebsd.org
|
||||
PROG= fetch
|
||||
CFLAGS+= -Wall -pedantic
|
||||
SRCS= fetch.c
|
||||
DPADD= ${LIBFETCH}
|
||||
LDADD= -lfetch
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
@ -1,378 +1,209 @@
|
||||
.\" $FreeBSD$
|
||||
.Dd February 22, 1999
|
||||
.\"-
|
||||
.\" Copyright (c) 2000 Dag-Erling Coïdan Smørgrav
|
||||
.\" 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
|
||||
.\" in this position and unchanged.
|
||||
.\" 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. The name of the author may not be used to endorse or promote products
|
||||
.\" derived from this software without specific prior written permission.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd June 28, 2000
|
||||
.Dt FETCH 1
|
||||
.Os FreeBSD 4.0
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm fetch
|
||||
.Nd retrieve a file by Uniform Resource Locator
|
||||
.Sh SYNOPSIS
|
||||
.Nm fetch
|
||||
.Op Fl AFMPablmnpqrtv
|
||||
.Op Fl S Ar size
|
||||
.Op Fl T Ar timeout
|
||||
.Nm
|
||||
.Op Fl 146AFHMPRalmnpqrsv
|
||||
.Op Fl B Ar bytes
|
||||
.Op Fl S Ar bytes
|
||||
.Op Fl T Ar seconds
|
||||
.Op Fl o Ar file
|
||||
.Op Fl w Ar seconds
|
||||
.Ar URL
|
||||
.Op Ar ...
|
||||
.Nm fetch
|
||||
.Op Fl FMPRlmnpqrv
|
||||
.Op Fl S Ar size
|
||||
.Op Fl o Ar file
|
||||
.Op Fl c Ar dir
|
||||
.Fl f Ar file
|
||||
.Fl h Ar host
|
||||
.Sh DESCRIPTION
|
||||
.Nm fetch
|
||||
allows a user to transfer files from a remote network site using
|
||||
either the
|
||||
.Tn FTP
|
||||
or the
|
||||
.Tn HTTP
|
||||
protocol.
|
||||
In the first form of the command, the
|
||||
.Ar URL
|
||||
may be of the form
|
||||
.Li http://site.domain/path/to/the/file
|
||||
or
|
||||
.Li ftp://site.domain/path/to/the/file .
|
||||
To denote a local filename to be copied or linked to (see the
|
||||
.Fl l
|
||||
flag below), the
|
||||
.Em file:/path/to/the/file
|
||||
URL form is used. See URL SYNTAX, below.
|
||||
.Pp
|
||||
The second form of the command can be used to get a file using the
|
||||
.Tn FTP
|
||||
protocol, specifying the file name and the remote host with the
|
||||
.Fl h
|
||||
and the
|
||||
.Fl f
|
||||
flags.
|
||||
.Nm Fetch
|
||||
provides a command-line interface to the
|
||||
.Xr fetch 3
|
||||
library.
|
||||
Its purpose is to retrieve the file(s) pointed to by the URL(s) on the
|
||||
command line.
|
||||
.Pp
|
||||
The following options are available:
|
||||
.Bl -tag -width Fl
|
||||
.It Fl \&1
|
||||
Stop and return exit code 0 at the first successfully retrieved file.
|
||||
.It Fl 4
|
||||
Forces
|
||||
.Nm
|
||||
to use IPv4 addresses only.
|
||||
.It Fl 6
|
||||
Forces
|
||||
.Nm
|
||||
to use IPv6 addresses only.
|
||||
.It Fl A
|
||||
Do not automatically follow ``temporary'' (302) redirects. Some
|
||||
broken Web sites will return a redirect instead of a not-found error
|
||||
when the requested object does not exist.
|
||||
Do not automatically follow ``temporary'' (302) redirects.
|
||||
Some broken Web sites will return a redirect instead of a not-found
|
||||
error when the requested object does not exist.
|
||||
.It Fl a
|
||||
Automatically retry the transfer upon soft failures.
|
||||
.It Fl b
|
||||
Work around a bug in some
|
||||
.Tn HTTP
|
||||
servers which fail to correctly implement the
|
||||
.Tn TCP
|
||||
protocol.
|
||||
.It Fl B Ar bytes
|
||||
Specify the read buffer size in bytes.
|
||||
The default is 4096 bytes.
|
||||
Attempts to set a buffer size lower than this will be silently
|
||||
ignored.
|
||||
The number of reads actually performed is reported at verbosity level
|
||||
two or higher (see the
|
||||
.Fl v
|
||||
flag).
|
||||
.It Fl c Ar dir
|
||||
The file to retrieve is in directory
|
||||
.Ar dir
|
||||
on the remote host.
|
||||
.It Fl F
|
||||
Force restart without checking for the local file's date matching
|
||||
that of the remote file. Use this with
|
||||
In combination with the
|
||||
.Fl r
|
||||
to restart a transfer of a partial file where the modification
|
||||
time on the local file has been changed and you are sure that the
|
||||
remote file is still the same, as this will prevent retrieval from
|
||||
starting anew.
|
||||
flag, forces a restart even if the local and remote files have
|
||||
different modification times.
|
||||
.It Fl f Ar file
|
||||
The file to retrieve is named
|
||||
.Ar file
|
||||
on the remote host.
|
||||
.It Fl H
|
||||
When using passive FTP, allocate a high port for the data connection.
|
||||
See
|
||||
.Xr ip 4
|
||||
for details on how to specify which port range this corresponds to.
|
||||
.It Fl h Ar host
|
||||
The file to retrieve is located on the host
|
||||
.Ar host .
|
||||
.It Fl l
|
||||
If target is a
|
||||
.Ar file:/
|
||||
style of URL, make a link to the target rather than trying
|
||||
to copy it.
|
||||
If the target is a file-scheme URL, make a symbolic link to the target
|
||||
rather than trying to copy it.
|
||||
.It Fl M
|
||||
.It Fl m
|
||||
Mirror mode: Set the modification time of the file so that it is
|
||||
identical to the modification time of the file at the remote host.
|
||||
If the file already exists on the local host and is identical (as
|
||||
gauged by size and modification time), no transfer is done.
|
||||
Mirror mode: set the modification time of the local file to that of
|
||||
the remote file.
|
||||
If the file already exists locally and has the same size and
|
||||
modification time as the remote file, it will not be fetched.
|
||||
.It Fl n
|
||||
Don't preserve the modtime of the transferred file, use the current time.
|
||||
Don't preserve the modtime of the transfered file, use the current
|
||||
time.
|
||||
.It Fl o Ar file
|
||||
Set the output file name to
|
||||
.Ar file .
|
||||
By default, a ``pathname'' is extracted from the specified URI, and
|
||||
its basename is used as the name of the output file. A
|
||||
its basename is used as the name of the output file.
|
||||
A
|
||||
.Ar file
|
||||
argument of
|
||||
.Sq Li \&-
|
||||
indicates that results are to be directed to the standard output.
|
||||
.It Fl P
|
||||
.It Fl p
|
||||
Use the passive mode of the
|
||||
.Tn FTP
|
||||
protocol. This is useful for crossing certain sorts of firewalls.
|
||||
Use passive FTP.
|
||||
This is useful if you are behind a firewall which blocks incoming
|
||||
connections.
|
||||
Try this flag if
|
||||
.Nm
|
||||
seems to hang when retrieving FTP URLs.
|
||||
.It Fl q
|
||||
Quiet mode.
|
||||
Do not report transfer progress on the terminal.
|
||||
This works by setting the verbosity level to zero; see the
|
||||
.Fl v
|
||||
option.
|
||||
.It Fl R
|
||||
The filenames specified are ``precious'', and should not be deleted
|
||||
under any circumstances, even if the transfer failed or was incomplete.
|
||||
The output files are precious, and should not be deleted under any
|
||||
circumstances, even if the transfer failed or was incomplete.
|
||||
.It Fl r
|
||||
Restart a previously interrupted transfer.
|
||||
.It Fl S Ar bytes
|
||||
Require the file size reported by
|
||||
.Tn FTP
|
||||
or
|
||||
.Tn HTTP
|
||||
server to match the value specified with this option.
|
||||
On mismatch, a message is printed and the file will not be fetched.
|
||||
If the server does not support reporting of file sizes, the option
|
||||
will be ignored and the file will be retrieved anyway.
|
||||
This option is useful to prevent
|
||||
.Nm fetch
|
||||
from downloading a file that is either incomplete or the wrong version,
|
||||
given the correct size of the file in advance.
|
||||
Require the file size reported by the server to match the specified
|
||||
value.
|
||||
If it does not, a message is printed and the file is not fetched.
|
||||
If the server does not support reporting file sizes, this option is
|
||||
ignored and the file is fetched unconditionally.
|
||||
.It Fl s
|
||||
Ask server for size of file in bytes and print it to stdout.
|
||||
Do not
|
||||
actually fetch the file.
|
||||
Print the size in bytes of each requested file, without fetching it.
|
||||
.It Fl T Ar seconds
|
||||
Set timeout value to
|
||||
.Ar seconds.
|
||||
Overrides the environment variables
|
||||
.Ev FTP_TIMEOUT
|
||||
for ftp transfers or
|
||||
for FTP transfers or
|
||||
.Ev HTTP_TIMEOUT
|
||||
for http transfers if set.
|
||||
for HTTP transfers if set.
|
||||
.It Fl t
|
||||
Work around a different set of buggy
|
||||
.Tn TCP
|
||||
implementations.
|
||||
.It Fl v
|
||||
Increase verbosity. More
|
||||
.Fl v Ns \&'s
|
||||
result in more information.
|
||||
Each instance of this flag increases the verbosity level by one.
|
||||
Level one (the default) only gives a summary after each file; level
|
||||
two show a running count during the transfer, provided that the
|
||||
standard output goes to a terminal; level three enables messages from
|
||||
the
|
||||
.Xr fetch 3
|
||||
library.
|
||||
.It Fl w Ar seconds
|
||||
When the
|
||||
.Fl a
|
||||
flag is specified, wait this many seconds between successive retries.
|
||||
.El
|
||||
.Pp
|
||||
Many options are also controlled solely by the environment (this is a
|
||||
bug).
|
||||
.Sh URL SYNTAX
|
||||
.Nm
|
||||
accepts
|
||||
.Tn http
|
||||
and
|
||||
.Tn ftp
|
||||
URL's, as described in RFC1738. For
|
||||
.Tn ftp
|
||||
URL's, a username and password may be specified, using the syntax
|
||||
.Li ftp://user:password@host/.
|
||||
If the path is to be absolute, as opposed to relative to the user's
|
||||
home directory, it must start with %2F, as in
|
||||
.Li ftp://root:mypass@localhost/%2Fetc/passwd .
|
||||
.Nm Fetch
|
||||
condenses multiple slashes in an
|
||||
.Tn ftp
|
||||
URL into a single slash; literal multiple slashes translate to an
|
||||
.Tn ftp
|
||||
protocol error.
|
||||
.Sh PROXY SERVERS
|
||||
Many sites use application gateways (``proxy servers'') in their
|
||||
firewalls in order to allow communication across the firewall using a
|
||||
trusted protocol. The
|
||||
.Nm fetch
|
||||
program can use both the
|
||||
.Tn FTP
|
||||
and the
|
||||
.Tn HTTP
|
||||
protocol with a proxy server.
|
||||
.Tn FTP
|
||||
proxy servers can only relay
|
||||
.Tn FTP
|
||||
requests;
|
||||
.Tn HTTP
|
||||
proxy servers can relay both
|
||||
.Tn FTP
|
||||
and
|
||||
.Tn HTTP
|
||||
requests.
|
||||
A proxy server can be configured by defining an environment variable
|
||||
named
|
||||
.Dq Va PROTO Ns Ev _PROXY ,
|
||||
where
|
||||
.Va PROTO
|
||||
is the name of the protocol in upper case. The value of the
|
||||
environment variable specifies a hostname, optionally followed by a
|
||||
colon and a port number.
|
||||
.Pp
|
||||
The
|
||||
.Tn FTP
|
||||
proxy client passes the remote username, host and port as the
|
||||
.Tn FTP
|
||||
session's username, in the form
|
||||
.Do Va remoteuser Ns Li \&@ Ns Va remotehost
|
||||
.Op Li \&@ Ns Va port
|
||||
.Dc .
|
||||
The
|
||||
.Tn HTTP
|
||||
proxy client simply passes the originally-requested URI to the remote
|
||||
server in an
|
||||
.Tn HTTP
|
||||
.Dq Li GET
|
||||
request. HTTP proxy authentication is not yet implemented.
|
||||
.Sh HTTP AUTHENTICATION
|
||||
The
|
||||
.Tn HTTP
|
||||
protocol includes support for various methods of authentication.
|
||||
Currently, the
|
||||
.Dq basic
|
||||
method, which provides no security from packet-sniffing or
|
||||
man-in-the-middle attacks, is the only method supported in
|
||||
.Nm fetch .
|
||||
Authentication is enabled by the
|
||||
.Ev HTTP_AUTH
|
||||
and
|
||||
.Ev HTTP_PROXY_AUTH
|
||||
environment variables. Both variables have the same format, which
|
||||
consists of space-separated list of parameter settings, where each
|
||||
setting consists of a colon-separated list of parameters. The first
|
||||
two parameters are always the (case-insensitive) authentication scheme
|
||||
name and the realm in which authentication is to be performed. If the
|
||||
realm is specified as
|
||||
.Sq Li \&* ,
|
||||
then it will match all realms not specified otherwise.
|
||||
.Pp
|
||||
The
|
||||
.Li basic
|
||||
authentication scheme uses two additional optional parameters; the
|
||||
first is a user name, and the second is the password associated with
|
||||
it. If either the password or both parameters are not specified in
|
||||
the environment, and the standard input of
|
||||
.Nm
|
||||
is connected to a terminal, then
|
||||
.Nm
|
||||
will prompt the user to enter the missing parameters. Thus, if the
|
||||
user is known as
|
||||
.Dq Li jane
|
||||
in the
|
||||
.Dq Li WallyWorld
|
||||
realm, and has a password of
|
||||
.Dq Li QghiLx79
|
||||
there, then she might set her
|
||||
.Ev HTTP_AUTH
|
||||
variable to:
|
||||
.Bl -enum -offset indent
|
||||
.It
|
||||
.Dq Li basic:WallyWorld:jane:QghiLx79
|
||||
.It
|
||||
.Dq Li basic:WallyWorld:jane ,
|
||||
or
|
||||
.It
|
||||
.Dq Li basic:WallyWorld
|
||||
.El
|
||||
.Pp
|
||||
and
|
||||
.Nm
|
||||
will prompt for any missing information when it is required. She might
|
||||
also specify a realm of
|
||||
.Dq Li \&*
|
||||
instead of
|
||||
.Dq Li WallyWorld
|
||||
to indicate that the parameters can be applied to any realm. (This is
|
||||
most commonly used in a construction such as
|
||||
.Dq Li basic:* ,
|
||||
which indicates to
|
||||
.Nm
|
||||
that it may offer to do
|
||||
.Li basic
|
||||
authentication for any realm.
|
||||
.Sh ERRORS
|
||||
.Sh DIAGNOSTICS
|
||||
The
|
||||
.Nm
|
||||
command returns zero on success, or a non-zero value from
|
||||
.Aq Pa sysexits.h
|
||||
on failure. If multiple URIs are given for retrieval,
|
||||
command returns zero on success, or one on failure.
|
||||
If multiple URLs are listed on the command line,
|
||||
.Nm
|
||||
will attempt all of them and return zero only if all succeeded
|
||||
(otherwise it will return the error from the last failure).
|
||||
will attempt to retrieve them each of them in turn, and return zero
|
||||
only if they were all successfully retrieved.
|
||||
.Sh ENVIRONMENT
|
||||
.Bl -tag -width FTP_PASSIVE_MODE -offset indent
|
||||
.It Ev FTP_TIMEOUT
|
||||
maximum time, in seconds, to wait before aborting an
|
||||
.Tn FTP
|
||||
connection.
|
||||
.It Ev FTP_LOGIN
|
||||
the login name used for
|
||||
.Tn FTP
|
||||
transfers (default
|
||||
.Dq Li anonymous )
|
||||
.It Ev FTP_PASSIVE_MODE
|
||||
force the use of passive mode FTP
|
||||
.It Ev FTP_PASSWORD
|
||||
the password used for
|
||||
.Tn FTP
|
||||
transfers (default
|
||||
.Dq Va yourname Ns Li \&@ Ns Va yourhost )
|
||||
.It Ev FTP_PROXY
|
||||
the address (in the form
|
||||
.Do Va hostname Ns
|
||||
.Op Li : Ns Va port
|
||||
.Dc )
|
||||
of a proxy server which understands
|
||||
.Tn FTP
|
||||
.It Ev HTTP_AUTH
|
||||
defines authentication parameters for
|
||||
.Tn HTTP
|
||||
.It Ev HTTP_PROXY
|
||||
the address (in the form
|
||||
.Do Va hostname Ns
|
||||
.Op Li : Ns Va port
|
||||
.Dc )
|
||||
of a proxy server which understands
|
||||
.Tn HTTP
|
||||
.It Ev HTTP_PROXY_AUTH
|
||||
defines authentication parameters for
|
||||
.Tn HTTP
|
||||
proxy servers
|
||||
.It Ev HTTP_TIMEOUT
|
||||
maximum time, in seconds, to wait before aborting an
|
||||
.Tn HTTP
|
||||
connection.
|
||||
.El
|
||||
.Pp
|
||||
All environment variables mentioned in the documentation for the
|
||||
.Xr fetch 3
|
||||
library are supported.
|
||||
.Sh SEE ALSO
|
||||
.Xr ftp 1 ,
|
||||
.Xr tftp 1
|
||||
.Rs
|
||||
.%A R. Fielding
|
||||
.%A J. Gettys
|
||||
.%A J. Mogul
|
||||
.%A H. Frystyk
|
||||
.%A T. Berners-Lee
|
||||
.%T "Hypertext Transfer Protocol \-\- HTTP/1.1"
|
||||
.%O RFC 2068
|
||||
.%D January 1997
|
||||
.Re
|
||||
.Rs
|
||||
.%A T. Berners-Lee
|
||||
.%A L. Masinter
|
||||
.%A M. McCahill
|
||||
.%T "Uniform Resource Locators (URL)"
|
||||
.%O RFC 1738
|
||||
.%D December 1994
|
||||
.Re
|
||||
.Rs
|
||||
.%A J. Postel
|
||||
.%A J.K. Reynolds
|
||||
.%T "File Transfer Protocol"
|
||||
.%O RFC 959 / STD 9
|
||||
.%D October 1985
|
||||
.Re
|
||||
.Rs
|
||||
.%A M.R. Horton
|
||||
.%T "Standard for interchange of USENET messages."
|
||||
.%O RFC 850
|
||||
.%D June 1983
|
||||
.Re
|
||||
.Xr fetch 3
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm fetch
|
||||
.Nm
|
||||
command appeared in
|
||||
.Fx 2.1.5 .
|
||||
This implementation first appeared in
|
||||
.Fx 4.1 .
|
||||
.Sh AUTHORS
|
||||
The original implementation of
|
||||
.Nm
|
||||
@ -381,63 +212,27 @@ was done by
|
||||
It was extensively re-worked for
|
||||
.Fx 2.2
|
||||
by
|
||||
.An Garrett Wollman .
|
||||
.Sh BUGS
|
||||
There are too many environment variables and command-line options.
|
||||
.Pp
|
||||
.An Garrett Wollman ,
|
||||
and later completely rewritten to use the
|
||||
.Xr fetch 3
|
||||
library by
|
||||
.An Dag-Erling Smørgrav .
|
||||
.Sh NOTES
|
||||
The
|
||||
.Fl a
|
||||
option is only implemented for certain kinds of
|
||||
.Tn HTTP
|
||||
failures, and no
|
||||
.Tn FTP
|
||||
failures.
|
||||
.Pp
|
||||
Only the
|
||||
.Dq basic
|
||||
authentication mode is implemented for
|
||||
.Tn HTTP .
|
||||
This should be replaced by digest authentication.
|
||||
.Pp
|
||||
Some
|
||||
.Tn TCP
|
||||
implementations (other than
|
||||
.Tn FreeBSD )
|
||||
fail to correctly implement cases where the
|
||||
.Dv SYN
|
||||
and/or
|
||||
.Dv FIN
|
||||
control flags are specified in packets which also contain data.
|
||||
The
|
||||
.Sq Fl t
|
||||
flag works around the latter deficiency and the
|
||||
.Sq Fl b
|
||||
flag works around the former. Since these are errors of the server's
|
||||
.Tn TCP
|
||||
stack, the best we can do is provide these workarounds. Given a correct
|
||||
server, an optimal
|
||||
.Tn HTTP
|
||||
transfer without
|
||||
.Fl t
|
||||
and
|
||||
.Fl b
|
||||
involves a minimum of two round trips (for small replies), one less than
|
||||
other implementations.
|
||||
and
|
||||
.Fl t
|
||||
options are no longer supported and will generate warnings.
|
||||
They were workarounds for bugs in other OSes which this implementation
|
||||
does not trigger.
|
||||
.Pp
|
||||
The
|
||||
.Tn HTTP
|
||||
standard requires interpretation of the
|
||||
.Tn RFC 850
|
||||
date format, which does not provide a century indication. Versions of
|
||||
.Nm fetch
|
||||
prior to
|
||||
.Fx 3.1
|
||||
would interpret all such dates as being in the 1900s. This version of
|
||||
.Nm fetch
|
||||
interprets such dates according to the rule given in
|
||||
.Tn RFC 2068 :
|
||||
.Bd -literal -offset indent
|
||||
o HTTP/1.1 clients and caches should assume that an RFC-850 date
|
||||
which appears to be more than 50 years in the future is in fact
|
||||
in the past (this helps solve the "year 2000" problem).
|
||||
.Ed
|
||||
.Fl f
|
||||
and
|
||||
.Fl h
|
||||
options (used for specifying an file to fetch and a host to fetch
|
||||
from) are no longer supported and will generate errors.
|
||||
Use URLs.
|
||||
RFC1738 is your friend.
|
||||
.Xr fetch 3
|
||||
library.
|
||||
|
644
usr.bin/fetch/fetch.c
Normal file
644
usr.bin/fetch/fetch.c
Normal file
@ -0,0 +1,644 @@
|
||||
/*-
|
||||
* Copyright (c) 2000 Dag-Erling Coïdan Smørgrav
|
||||
* 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
|
||||
* in this position and unchanged.
|
||||
* 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. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <fetch.h>
|
||||
|
||||
#define MINBUFSIZE 4096
|
||||
|
||||
/* Option flags */
|
||||
int A_flag; /* -A: do not follow 302 redirects */
|
||||
int a_flag; /* -a: auto retry */
|
||||
size_t B_size; /* -B: buffer size */
|
||||
int b_flag; /*! -b: workaround TCP bug */
|
||||
int d_flag; /* -d: direct connection */
|
||||
int F_flag; /* -F: restart without checking mtime */
|
||||
char *f_filename; /* -f: file to fetch */
|
||||
int H_flag; /* -H: use high port */
|
||||
char *h_hostname; /* -h: host to fetch from */
|
||||
int l_flag; /* -l: link rather than copy file: URLs */
|
||||
int m_flag; /* -[Mm]: set local timestamp to remote timestamp */
|
||||
int o_flag; /* -o: specify output file */
|
||||
int o_directory; /* output file is a directory */
|
||||
char *o_filename; /* name of output file */
|
||||
int o_stdout; /* output file is stdout */
|
||||
int once_flag; /* -1: stop at first successful file */
|
||||
int p_flag = 1; /* -[Pp]: use passive FTP */
|
||||
int R_flag; /* -R: don't delete partially transferred files */
|
||||
int r_flag; /* -r: restart previously interrupted transfer */
|
||||
u_int T_secs = 0; /* -T: transfer timeout in seconds */
|
||||
int s_flag; /* -s: show size, don't fetch */
|
||||
off_t S_size; /* -S: require size to match */
|
||||
int t_flag; /*! -t: workaround TCP bug */
|
||||
int v_level = 1; /* -v: verbosity level */
|
||||
int v_tty; /* stdout is a tty */
|
||||
u_int w_secs; /* -w: retry delay */
|
||||
int family = PF_UNSPEC; /* -[46]: address family to use */
|
||||
|
||||
|
||||
u_int ftp_timeout; /* default timeout for FTP transfers */
|
||||
u_int http_timeout; /* default timeout for HTTP transfers */
|
||||
u_char *buf; /* transfer buffer */
|
||||
|
||||
|
||||
void
|
||||
sig_handler(int sig)
|
||||
{
|
||||
errx(1, "Transfer timed out");
|
||||
}
|
||||
|
||||
struct xferstat {
|
||||
char name[40];
|
||||
struct timeval start;
|
||||
struct timeval end;
|
||||
struct timeval last;
|
||||
off_t size;
|
||||
off_t offset;
|
||||
off_t rcvd;
|
||||
};
|
||||
|
||||
void
|
||||
stat_start(struct xferstat *xs, char *name, off_t size, off_t offset)
|
||||
{
|
||||
snprintf(xs->name, sizeof xs->name, "%s", name);
|
||||
xs->size = size;
|
||||
xs->offset = offset;
|
||||
if (v_level) {
|
||||
fprintf(stderr, "Receiving %s", xs->name);
|
||||
if (xs->size != -1)
|
||||
fprintf(stderr, " (%lld bytes)", xs->size - xs->offset);
|
||||
}
|
||||
gettimeofday(&xs->start, NULL);
|
||||
xs->last = xs->start;
|
||||
}
|
||||
|
||||
void
|
||||
stat_update(struct xferstat *xs, off_t rcvd)
|
||||
{
|
||||
struct timeval now;
|
||||
|
||||
xs->rcvd = rcvd;
|
||||
|
||||
if (v_level <= 1 || !v_tty)
|
||||
return;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
if (now.tv_sec <= xs->last.tv_sec)
|
||||
return;
|
||||
xs->last = now;
|
||||
|
||||
fprintf(stderr, "\rReceiving %s", xs->name);
|
||||
if (xs->size == -1)
|
||||
fprintf(stderr, ": %lld bytes", xs->rcvd - xs->offset);
|
||||
else
|
||||
fprintf(stderr, " (%lld bytes): %d%%", xs->size - xs->offset,
|
||||
(int)((100.0 * xs->rcvd) / (xs->size - xs->offset)));
|
||||
}
|
||||
|
||||
void
|
||||
stat_end(struct xferstat *xs)
|
||||
{
|
||||
double delta;
|
||||
double bps;
|
||||
|
||||
gettimeofday(&xs->end, NULL);
|
||||
|
||||
if (!v_level)
|
||||
return;
|
||||
|
||||
fputc('\n', stderr);
|
||||
delta = (xs->end.tv_sec + (xs->end.tv_usec / 1.e6))
|
||||
- (xs->start.tv_sec + (xs->start.tv_usec / 1.e6));
|
||||
fprintf(stderr, "%lld bytes transferred in %.1f seconds ",
|
||||
xs->size - xs->offset, delta);
|
||||
bps = (xs->size - xs->offset) / delta;
|
||||
if (bps > 1024*1024)
|
||||
fprintf(stderr, "(%.2f MBps)\n", bps / (1024*1024));
|
||||
else if (bps > 1024)
|
||||
fprintf(stderr, "(%.2f kBps)\n", bps / 1024);
|
||||
else
|
||||
fprintf(stderr, "(%.2f Bps)\n", bps);
|
||||
}
|
||||
|
||||
int
|
||||
fetch(char *URL, char *path)
|
||||
{
|
||||
struct url *url;
|
||||
struct url_stat us;
|
||||
struct stat sb;
|
||||
struct xferstat xs;
|
||||
FILE *f, *of;
|
||||
size_t size;
|
||||
off_t count;
|
||||
char flags[8];
|
||||
int ch, n, r;
|
||||
u_int timeout;
|
||||
|
||||
f = of = NULL;
|
||||
|
||||
/* parse URL */
|
||||
if ((url = fetchParseURL(URL)) == NULL) {
|
||||
warnx("%s: parse error", URL);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
timeout = 0;
|
||||
*flags = 0;
|
||||
|
||||
/* common flags */
|
||||
if (v_level > 2)
|
||||
strcat(flags, "v");
|
||||
switch (family) {
|
||||
case PF_INET:
|
||||
strcat(flags, "4");
|
||||
break;
|
||||
case PF_INET6:
|
||||
strcat(flags, "6");
|
||||
break;
|
||||
}
|
||||
|
||||
/* FTP specific flags */
|
||||
if (strcmp(url->scheme, "ftp") == 0) {
|
||||
if (p_flag)
|
||||
strcat(flags, "p");
|
||||
if (d_flag)
|
||||
strcat(flags, "d");
|
||||
if (H_flag)
|
||||
strcat(flags, "h");
|
||||
timeout = T_secs ? T_secs : ftp_timeout;
|
||||
}
|
||||
|
||||
/* HTTP specific flags */
|
||||
if (strcmp(url->scheme, "http") == 0) {
|
||||
if (d_flag)
|
||||
strcat(flags, "d");
|
||||
if (A_flag)
|
||||
strcat(flags, "A");
|
||||
timeout = T_secs ? T_secs : http_timeout;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the protocol timeout.
|
||||
* This currently only works for FTP, so we still use
|
||||
* alarm(timeout) further down.
|
||||
*/
|
||||
fetchTimeout = timeout;
|
||||
|
||||
/* stat remote file */
|
||||
alarm(timeout);
|
||||
if (fetchStat(url, &us, flags) == -1)
|
||||
warnx("%s: size not known", path);
|
||||
alarm(timeout);
|
||||
|
||||
/* just print size */
|
||||
if (s_flag) {
|
||||
if (us.size == -1)
|
||||
printf("Unknown\n");
|
||||
else
|
||||
printf("%lld\n", us.size);
|
||||
goto success;
|
||||
}
|
||||
|
||||
/* check that size is as expected */
|
||||
if (S_size && us.size != -1 && us.size != S_size) {
|
||||
warnx("%s: size mismatch: expected %lld, actual %lld",
|
||||
path, S_size, us.size);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* symlink instead of copy */
|
||||
if (l_flag && strcmp(url->scheme, "file") == 0 && !o_stdout) {
|
||||
if (symlink(url->doc, path) == -1) {
|
||||
warn("%s: symlink()", path);
|
||||
goto failure;
|
||||
}
|
||||
goto success;
|
||||
}
|
||||
|
||||
if (o_stdout) {
|
||||
/* output to stdout */
|
||||
of = stdout;
|
||||
} else if (r_flag && us.size != -1 && stat(path, &sb) != -1
|
||||
&& (F_flag || (us.mtime && sb.st_mtime == us.mtime))) {
|
||||
/* output to file, restart aborted transfer */
|
||||
if (us.size == sb.st_size)
|
||||
goto success;
|
||||
else if (sb.st_size > us.size && truncate(path, us.size) == -1) {
|
||||
warn("%s: truncate()", path);
|
||||
goto failure;
|
||||
}
|
||||
if ((of = fopen(path, "a")) == NULL) {
|
||||
warn("%s: open()", path);
|
||||
goto failure;
|
||||
}
|
||||
url->offset = sb.st_size;
|
||||
} else if (m_flag && us.size != -1 && stat(path, &sb) != -1) {
|
||||
/* output to file, mirror mode */
|
||||
warnx(" local: %lld bytes, mtime %ld", sb.st_size, sb.st_mtime);
|
||||
warnx("remote: %lld bytes, mtime %ld", us.size, us.mtime);
|
||||
if (sb.st_size == us.size && sb.st_mtime == us.mtime)
|
||||
return 0;
|
||||
if ((of = fopen(path, "w")) == NULL) {
|
||||
warn("%s: open()", path);
|
||||
goto failure;
|
||||
}
|
||||
} else {
|
||||
/* output to file, all other cases */
|
||||
if ((of = fopen(path, "w")) == NULL) {
|
||||
warn("%s: open()", path);
|
||||
goto failure;
|
||||
}
|
||||
}
|
||||
count = url->offset;
|
||||
|
||||
/* start the transfer */
|
||||
if ((f = fetchGet(url, flags)) == NULL) {
|
||||
warnx("%s", fetchLastErrString);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* start the counter */
|
||||
stat_start(&xs, path, us.size, count);
|
||||
|
||||
n = 0;
|
||||
|
||||
if (us.size == -1) {
|
||||
/*
|
||||
* We have no idea how much data to expect, so do it byte by
|
||||
* byte. This is incredibly inefficient, but there's not much
|
||||
* we can do about it... :(
|
||||
*/
|
||||
while (1) {
|
||||
if (timeout)
|
||||
alarm(timeout);
|
||||
#ifdef STDIO_HACK
|
||||
/*
|
||||
* This is a non-portable hack, but it makes things go
|
||||
* faster. Basically, if there is data in the input file's
|
||||
* buffer, write it out; then fall through to the fgetc()
|
||||
* which forces a refill. It saves a memcpy() and reduces
|
||||
* the number of iterations, i.e the number of calls to
|
||||
* alarm(). Empirical evidence shows this can cut user
|
||||
* time by up to 90%. There may be better (even portable)
|
||||
* ways to do this.
|
||||
*/
|
||||
if (f->_r && (f->_ub._base == NULL)) {
|
||||
if (fwrite(f->_p, f->_r, 1, of) < 1)
|
||||
break;
|
||||
count += f->_r;
|
||||
f->_p += f->_r;
|
||||
f->_r = 0;
|
||||
}
|
||||
#endif
|
||||
if ((ch = fgetc(f)) == EOF || fputc(ch, of) == EOF)
|
||||
break;
|
||||
stat_update(&xs, count++);
|
||||
n++;
|
||||
}
|
||||
} else {
|
||||
/* we know exactly how much to transfer, so do it efficiently */
|
||||
for (size = B_size; count != us.size; n++) {
|
||||
if (us.size - count < B_size)
|
||||
size = us.size - count;
|
||||
if (timeout)
|
||||
alarm(timeout);
|
||||
if (fread(buf, size, 1, f) != 1 || fwrite(buf, size, 1, of) != 1)
|
||||
break;
|
||||
stat_update(&xs, count += size);
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout)
|
||||
alarm(0);
|
||||
|
||||
stat_end(&xs);
|
||||
|
||||
/* check the status of our files */
|
||||
if (ferror(f))
|
||||
warn("%s", URL);
|
||||
if (ferror(of))
|
||||
warn("%s", path);
|
||||
if (ferror(f) || ferror(of)) {
|
||||
if (!R_flag && !o_stdout)
|
||||
unlink(path);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* need to close the file before setting mtime */
|
||||
if (of != stdout) {
|
||||
fclose(of);
|
||||
of = NULL;
|
||||
}
|
||||
|
||||
/* Set mtime of local file */
|
||||
if (m_flag && us.size != -1 && !o_stdout) {
|
||||
struct timeval tv[2];
|
||||
|
||||
tv[0].tv_sec = (long)us.atime;
|
||||
tv[1].tv_sec = (long)us.mtime;
|
||||
tv[0].tv_usec = tv[1].tv_usec = 0;
|
||||
if (utimes(path, tv))
|
||||
warn("%s: utimes()", path);
|
||||
}
|
||||
|
||||
success:
|
||||
r = 0;
|
||||
goto done;
|
||||
failure:
|
||||
r = -1;
|
||||
goto done;
|
||||
done:
|
||||
if (f)
|
||||
fclose(f);
|
||||
if (of && of != stdout)
|
||||
fclose(of);
|
||||
fetchFreeURL(url);
|
||||
return r;
|
||||
}
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
/* XXX badly out of synch */
|
||||
fprintf(stderr,
|
||||
"Usage: fetch [-1AFHMPRabdlmnpqrstv] [-o outputfile] [-S bytes]\n"
|
||||
" [-B bytes] [-T seconds] [-w seconds]\n"
|
||||
" [-f file -h host [-c dir] | URL ...]\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
#define PARSENUM(NAME, TYPE) \
|
||||
int \
|
||||
NAME(char *s, TYPE *v) \
|
||||
{ \
|
||||
*v = 0; \
|
||||
for (*v = 0; *s; s++) \
|
||||
if (isdigit(*s)) \
|
||||
*v = *v * 10 + *s - '0'; \
|
||||
else \
|
||||
return -1; \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
PARSENUM(parseint, u_int)
|
||||
PARSENUM(parsesize, size_t)
|
||||
PARSENUM(parseoff, off_t)
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct stat sb;
|
||||
char *p, *q, *s;
|
||||
int c, e, r;
|
||||
|
||||
while ((c = getopt(argc, argv,
|
||||
"146AaB:bdFf:h:lHMmnPpo:qRrS:sT:tvw:")) != EOF)
|
||||
switch (c) {
|
||||
case '1':
|
||||
once_flag = 1;
|
||||
break;
|
||||
case '4':
|
||||
family = PF_INET;
|
||||
break;
|
||||
case '6':
|
||||
family = PF_INET6;
|
||||
break;
|
||||
case 'A':
|
||||
A_flag = 1;
|
||||
break;
|
||||
case 'a':
|
||||
a_flag = 1;
|
||||
break;
|
||||
case 'B':
|
||||
if (parsesize(optarg, &B_size) == -1)
|
||||
errx(1, "invalid buffer size");
|
||||
break;
|
||||
case 'b':
|
||||
warnx("warning: the -b option is deprecated");
|
||||
b_flag = 1;
|
||||
break;
|
||||
case 'd':
|
||||
d_flag = 1;
|
||||
break;
|
||||
case 'F':
|
||||
F_flag = 1;
|
||||
break;
|
||||
case 'f':
|
||||
f_filename = optarg;
|
||||
break;
|
||||
case 'H':
|
||||
H_flag = 1;
|
||||
break;
|
||||
case 'h':
|
||||
h_hostname = optarg;
|
||||
break;
|
||||
case 'l':
|
||||
l_flag = 1;
|
||||
break;
|
||||
case 'o':
|
||||
o_flag = 1;
|
||||
o_filename = optarg;
|
||||
break;
|
||||
case 'M':
|
||||
case 'm':
|
||||
m_flag = 1;
|
||||
break;
|
||||
case 'n':
|
||||
m_flag = 0;
|
||||
break;
|
||||
case 'P':
|
||||
case 'p':
|
||||
p_flag = 1;
|
||||
break;
|
||||
case 'q':
|
||||
v_level = 0;
|
||||
break;
|
||||
case 'R':
|
||||
R_flag = 1;
|
||||
break;
|
||||
case 'r':
|
||||
r_flag = 1;
|
||||
break;
|
||||
case 'S':
|
||||
if (parseoff(optarg, &S_size) == -1)
|
||||
errx(1, "invalid size");
|
||||
break;
|
||||
case 's':
|
||||
s_flag = 1;
|
||||
break;
|
||||
case 'T':
|
||||
if (parseint(optarg, &T_secs) == -1)
|
||||
errx(1, "invalid timeout");
|
||||
break;
|
||||
case 't':
|
||||
t_flag = 1;
|
||||
warnx("warning: the -t option is deprecated");
|
||||
break;
|
||||
case 'v':
|
||||
v_level++;
|
||||
break;
|
||||
case 'w':
|
||||
a_flag = 1;
|
||||
if (parseint(optarg, &w_secs) == -1)
|
||||
errx(1, "invalid delay");
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
exit(EX_USAGE);
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (h_hostname || f_filename) {
|
||||
if (!h_hostname || !f_filename || argc) {
|
||||
usage();
|
||||
exit(EX_USAGE);
|
||||
}
|
||||
/* XXX this is a hack. */
|
||||
if (strcspn(h_hostname, "@:/") != strlen(h_hostname))
|
||||
errx(1, "invalid hostname");
|
||||
if (asprintf(argv, "ftp://%s/%s", h_hostname, f_filename) == -1)
|
||||
errx(1, strerror(ENOMEM));
|
||||
argc++;
|
||||
}
|
||||
|
||||
if (!argc) {
|
||||
usage();
|
||||
exit(EX_USAGE);
|
||||
}
|
||||
|
||||
/* allocate buffer */
|
||||
if (B_size < MINBUFSIZE)
|
||||
B_size = MINBUFSIZE;
|
||||
if ((buf = malloc(B_size)) == NULL)
|
||||
errx(1, strerror(ENOMEM));
|
||||
|
||||
/* timeout handling */
|
||||
signal(SIGALRM, sig_handler);
|
||||
if ((s = getenv("FTP_TIMEOUT")) != NULL) {
|
||||
if (parseint(s, &ftp_timeout) == -1) {
|
||||
warnx("FTP_TIMEOUT is not a positive integer");
|
||||
ftp_timeout = 0;
|
||||
}
|
||||
}
|
||||
if ((s = getenv("HTTP_TIMEOUT")) != NULL) {
|
||||
if (parseint(s, &http_timeout) == -1) {
|
||||
warnx("HTTP_TIMEOUT is not a positive integer");
|
||||
http_timeout = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* output file */
|
||||
if (o_flag) {
|
||||
if (strcmp(o_filename, "-") == 0) {
|
||||
o_stdout = 1;
|
||||
} else if (stat(o_filename, &sb) == -1) {
|
||||
if (errno == ENOENT) {
|
||||
if (argc > 1)
|
||||
errx(EX_USAGE, "%s is not a directory", o_filename);
|
||||
} else {
|
||||
err(EX_IOERR, "%s", o_filename);
|
||||
}
|
||||
} else {
|
||||
if (sb.st_mode & S_IFDIR)
|
||||
o_directory = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* check if output is to a tty (for progress report) */
|
||||
v_tty = isatty(STDOUT_FILENO);
|
||||
r = 0;
|
||||
|
||||
while (argc) {
|
||||
if ((p = strrchr(*argv, '/')) == NULL)
|
||||
p = *argv;
|
||||
else
|
||||
p++;
|
||||
|
||||
if (!*p)
|
||||
p = "fetch.out";
|
||||
|
||||
fetchLastErrCode = 0;
|
||||
|
||||
if (o_flag) {
|
||||
if (o_stdout) {
|
||||
e = fetch(*argv, "-");
|
||||
} else if (o_directory) {
|
||||
asprintf(&q, "%s/%s", o_filename, p);
|
||||
e = fetch(*argv, q);
|
||||
free(q);
|
||||
} else {
|
||||
e = fetch(*argv, o_filename);
|
||||
}
|
||||
} else {
|
||||
e = fetch(*argv, p);
|
||||
}
|
||||
|
||||
if (e == 0 && once_flag)
|
||||
exit(0);
|
||||
|
||||
if (e) {
|
||||
r = 1;
|
||||
if ((fetchLastErrCode
|
||||
&& fetchLastErrCode != FETCH_UNAVAIL
|
||||
&& fetchLastErrCode != FETCH_MOVED
|
||||
&& fetchLastErrCode != FETCH_URL
|
||||
&& fetchLastErrCode != FETCH_RESOLV
|
||||
&& fetchLastErrCode != FETCH_UNKNOWN)) {
|
||||
if (w_secs) {
|
||||
if (v_level)
|
||||
fprintf(stderr, "Waiting %d seconds before retrying\n", w_secs);
|
||||
sleep(w_secs);
|
||||
}
|
||||
if (a_flag)
|
||||
continue;
|
||||
fprintf(stderr, "Skipping %s\n", *argv);
|
||||
}
|
||||
}
|
||||
|
||||
argc--, argv++;
|
||||
}
|
||||
|
||||
exit(r);
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright 1997 Massachusetts Institute of Technology
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software and
|
||||
* its documentation for any purpose and without fee is hereby
|
||||
* granted, provided that both the above copyright notice and this
|
||||
* permission notice appear in all copies, that both the above
|
||||
* copyright notice and this permission notice appear in all
|
||||
* supporting documentation, and that the name of M.I.T. not be used
|
||||
* in advertising or publicity pertaining to distribution of the
|
||||
* software without specific, written prior permission. M.I.T. makes
|
||||
* no representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied
|
||||
* warranty.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
|
||||
* ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
|
||||
* SHALL M.I.T. 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef fetch_h
|
||||
#define fetch_h 1
|
||||
|
||||
|
||||
#define BUFFER_SIZE 1024
|
||||
#define FETCH_VERSION "fetch/1.0"
|
||||
#define PATH_CP "/bin/cp"
|
||||
|
||||
struct fetch_state {
|
||||
const char *fs_status;
|
||||
const char *fs_outputfile;
|
||||
int fs_verbose; /* -q, -v option */
|
||||
int fs_newtime; /* -n option */
|
||||
int fs_mirror; /* -m option */
|
||||
int fs_restart; /* -r option */
|
||||
int fs_timeout; /* -T option */
|
||||
int fs_passive_mode; /* -p option */
|
||||
int fs_linkfile; /* -l option */
|
||||
int fs_precious; /* -R option */
|
||||
int fs_auto_retry; /* -a option */
|
||||
int fs_linux_bug; /* -b option */
|
||||
int fs_use_connect; /* -t option */
|
||||
off_t fs_expectedsize; /* -S option */
|
||||
int fs_reportsize; /* -s option */
|
||||
int fs_forcerestart; /* -F option */
|
||||
time_t fs_modtime;
|
||||
void *fs_proto;
|
||||
int (*fs_retrieve)(struct fetch_state *);
|
||||
int (*fs_close)(struct fetch_state *);
|
||||
};
|
||||
|
||||
struct uri_scheme {
|
||||
const char *sc_name; /* name of the scheme, <32 characters */
|
||||
int (*sc_parse)(struct fetch_state *, const char *);
|
||||
/* routine to parse a URI and build state */
|
||||
int (*sc_proxy_parse)(struct fetch_state *, const char *);
|
||||
/* same, but for proxy case */
|
||||
const char *sc_proxy_envar; /* envar used to determine proxy */
|
||||
const char *sc_proxy_by; /* list of protos which can proxy us */
|
||||
|
||||
/* The rest is filled in dynamically... */
|
||||
int sc_can_proxy;
|
||||
struct uri_scheme *sc_proxyproto;
|
||||
};
|
||||
|
||||
extern struct uri_scheme file_scheme, ftp_scheme, http_scheme;
|
||||
|
||||
void adjmodtime(struct fetch_state *fs);
|
||||
void catchsig(int signo);
|
||||
int display(struct fetch_state *fs, off_t total, ssize_t thisincr);
|
||||
void init_schemes(void);
|
||||
void rm(struct fetch_state *fs);
|
||||
void setup_sigalrm(void);
|
||||
void unsetup_sigalrm(void);
|
||||
void *safe_malloc(size_t len);
|
||||
char *percent_decode(const char *orig);
|
||||
char *safe_strdup(const char *orig);
|
||||
char *safe_strndup(const char *orig, size_t len);
|
||||
char *to_base64(const unsigned char *buf, size_t len);
|
||||
int from_base64(const char *orig, unsigned char *buf, size_t *lenp);
|
||||
int parse_host_port(const char *str, char **hostname, int *port);
|
||||
int parse_uri(struct fetch_state *fs, const char *uri);
|
||||
#endif /* ! fetch_h */
|
@ -1,168 +0,0 @@
|
||||
/*-
|
||||
* Copyright 1997 Massachusetts Institute of Technology
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software and
|
||||
* its documentation for any purpose and without fee is hereby
|
||||
* granted, provided that both the above copyright notice and this
|
||||
* permission notice appear in all copies, that both the above
|
||||
* copyright notice and this permission notice appear in all
|
||||
* supporting documentation, and that the name of M.I.T. not be used
|
||||
* in advertising or publicity pertaining to distribution of the
|
||||
* software without specific, written prior permission. M.I.T. makes
|
||||
* no representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied
|
||||
* warranty.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
|
||||
* ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
|
||||
* SHALL M.I.T. 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "fetch.h"
|
||||
|
||||
static int file_retrieve(struct fetch_state *fs);
|
||||
static int file_close(struct fetch_state *fs);
|
||||
static int file_parse(struct fetch_state *fs, const char *uri);
|
||||
|
||||
struct uri_scheme file_scheme =
|
||||
{ "file", file_parse, 0, 0, 0 };
|
||||
|
||||
/*
|
||||
* Again, we slightly misinterpret the slash after the hostname as
|
||||
* being the start of the pathname rather than merely a separator.
|
||||
*/
|
||||
static int
|
||||
file_parse(struct fetch_state *fs, const char *uri)
|
||||
{
|
||||
const char *p;
|
||||
|
||||
p = uri + 5; /* skip past `file:' */
|
||||
if (p[0] == '/' && p[1] == '/') {
|
||||
/* skip past `//localhost', if any */
|
||||
p += 2;
|
||||
while (*p && *p != '/')
|
||||
p++;
|
||||
}
|
||||
|
||||
if (p[0] != '/') {
|
||||
warnx("`%s': expected absolute pathname in `file' URL", uri);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
fs->fs_proto = percent_decode(p);
|
||||
/* guaranteed to succeed because of above test */
|
||||
p = strrchr(fs->fs_proto, '/');
|
||||
if (fs->fs_outputfile == 0) /* only set if not overridden by user */
|
||||
fs->fs_outputfile = p + 1;
|
||||
fs->fs_retrieve = file_retrieve;
|
||||
fs->fs_close = file_close;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
file_close(struct fetch_state *fs)
|
||||
{
|
||||
free(fs->fs_proto);
|
||||
fs->fs_proto = 0;
|
||||
fs->fs_outputfile = 0;
|
||||
fs->fs_status = "free";
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
file_retrieve(struct fetch_state *fs)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
/* XXX - this seems bogus to me! */
|
||||
if (access(fs->fs_outputfile, F_OK) == 0) {
|
||||
errno = EEXIST;
|
||||
warn("%s", fs->fs_outputfile);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
if (fs->fs_linkfile) {
|
||||
fs->fs_status = "checking path";
|
||||
if (stat(fs->fs_proto, &sb) == -1) {
|
||||
warn("%s", (char *)fs->fs_proto);
|
||||
return EX_NOINPUT;
|
||||
}
|
||||
fs->fs_status = "symlink";
|
||||
if (symlink(fs->fs_proto, fs->fs_outputfile) == -1) {
|
||||
warn("symlink");
|
||||
return EX_OSERR;
|
||||
}
|
||||
fs->fs_status = "done";
|
||||
} else if (strcmp(fs->fs_outputfile, "-") == 0) {
|
||||
FILE *f;
|
||||
int ch;
|
||||
|
||||
if ((f = fopen(fs->fs_proto, "r")) == NULL) {
|
||||
warn("fopen");
|
||||
return EX_OSERR;
|
||||
}
|
||||
while ((ch = fgetc(f)) != EOF)
|
||||
fputc(ch, stdout);
|
||||
if (ferror(f)) {
|
||||
warn("fgetc");
|
||||
fclose(f);
|
||||
return EX_OSERR;
|
||||
}
|
||||
fclose(f);
|
||||
} else {
|
||||
pid_t pid;
|
||||
int status;
|
||||
|
||||
fflush(stderr);
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
warn("fork");
|
||||
return EX_TEMPFAIL;
|
||||
} else if (pid == 0) {
|
||||
execl(PATH_CP, "cp", "-p", fs->fs_proto,
|
||||
fs->fs_outputfile, (char *)0);
|
||||
warn("execl: " PATH_CP);
|
||||
fflush(stderr);
|
||||
_exit(EX_OSERR);
|
||||
} else {
|
||||
fs->fs_status = "copying";
|
||||
if (waitpid(pid, &status, 0) < 0) {
|
||||
warn("waitpid(%ld)", (long)pid);
|
||||
return EX_OSERR;
|
||||
}
|
||||
if (WIFEXITED(status))
|
||||
return WEXITSTATUS(status);
|
||||
if (WIFSIGNALED(status))
|
||||
warn(PATH_CP " exited on signal: %s",
|
||||
sys_signame[WTERMSIG(status)]);
|
||||
return EX_OSERR;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,521 +0,0 @@
|
||||
/*-
|
||||
* Copyright 1997 Massachusetts Institute of Technology
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software and
|
||||
* its documentation for any purpose and without fee is hereby
|
||||
* granted, provided that both the above copyright notice and this
|
||||
* permission notice appear in all copies, that both the above
|
||||
* copyright notice and this permission notice appear in all
|
||||
* supporting documentation, and that the name of M.I.T. not be used
|
||||
* in advertising or publicity pertaining to distribution of the
|
||||
* software without specific, written prior permission. M.I.T. makes
|
||||
* no representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied
|
||||
* warranty.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
|
||||
* ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
|
||||
* SHALL M.I.T. 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <ftpio.h>
|
||||
#include <limits.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "fetch.h"
|
||||
|
||||
struct ftp_state {
|
||||
char *ftp_hostname;
|
||||
char *ftp_user;
|
||||
char *ftp_password;
|
||||
char *ftp_remote_file;
|
||||
char **ftp_remote_dirs;
|
||||
int ftp_remote_ndirs;
|
||||
char *ftp_remote_path;
|
||||
char *ftp_type;
|
||||
unsigned ftp_port;
|
||||
};
|
||||
|
||||
static int ftp_close(struct fetch_state *fs);
|
||||
static int ftp_retrieve(struct fetch_state *fs);
|
||||
static int ftp_parse(struct fetch_state *fs, const char *uri);
|
||||
static int ftp_proxy_parse(struct fetch_state *fs, const char *uri);
|
||||
|
||||
struct uri_scheme ftp_scheme =
|
||||
{ "ftp", ftp_parse, ftp_proxy_parse, "FTP_PROXY", "ftp,http" };
|
||||
|
||||
static int
|
||||
ftp_parse(struct fetch_state *fs, const char *uri)
|
||||
{
|
||||
const char *p, *slash, *q;
|
||||
char *hostname, *atsign, *colon, *path, *r, *s, **dp;
|
||||
unsigned port;
|
||||
struct ftp_state *ftps;
|
||||
|
||||
p = uri + 4;
|
||||
port = 0;
|
||||
|
||||
if (p[0] != '/' || p[1] != '/') {
|
||||
warnx("`%s': invalid `ftp' URL", uri);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
p += 2;
|
||||
slash = strchr(p, '/');
|
||||
if (slash == 0) {
|
||||
warnx("`%s': malformed `ftp' URL", uri);
|
||||
return EX_USAGE;
|
||||
}
|
||||
hostname = alloca(slash - p + 1);
|
||||
hostname[0] = '\0';
|
||||
strncat(hostname, p, slash - p);
|
||||
|
||||
if ((atsign = strrchr(hostname, '@')) == 0)
|
||||
q = hostname;
|
||||
else
|
||||
q = atsign + 1;
|
||||
|
||||
if ((colon = strchr(q, ':')) != 0)
|
||||
*colon = '\0';
|
||||
|
||||
if (colon && *(colon + 1)) {
|
||||
unsigned long ul;
|
||||
char *ep;
|
||||
|
||||
errno = 0;
|
||||
ul = strtoul(colon + 1, &ep, 10);
|
||||
if (*ep || errno != 0 || ul < 1 || ul > 65534) {
|
||||
if (errno)
|
||||
warn("`%s': invalid port in URL", uri);
|
||||
else
|
||||
warnx("`%s': invalid port in URL", uri);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
port = ul;
|
||||
} else {
|
||||
port = 21;
|
||||
}
|
||||
|
||||
p = slash + 1;
|
||||
|
||||
ftps = safe_malloc(sizeof *ftps);
|
||||
ftps->ftp_password = 0;
|
||||
ftps->ftp_user = 0;
|
||||
|
||||
/*
|
||||
* Now, we have a copy of the hostname in hostname, the specified port
|
||||
* (or the default value) in port, and p points to the filename part
|
||||
* of the URI. We just need to check for a user in the hostname,
|
||||
* and then save all the bits in our state.
|
||||
*/
|
||||
if (atsign) {
|
||||
if (atsign[1] == '\0') {
|
||||
warnx("`%s': malformed `ftp' hostname", hostname);
|
||||
free(ftps);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
*atsign = '\0';
|
||||
if ((colon = strchr(hostname, ':')) != 0)
|
||||
*colon = '\0';
|
||||
if (hostname[0] == '\0') {
|
||||
warnx("`%s': malformed `ftp' user", atsign + 1);
|
||||
free(ftps);
|
||||
return EX_USAGE;
|
||||
}
|
||||
if (colon != 0)
|
||||
ftps->ftp_password = percent_decode(colon + 1);
|
||||
ftps->ftp_user = percent_decode(hostname);
|
||||
ftps->ftp_hostname = safe_strdup(atsign + 1);
|
||||
} else
|
||||
ftps->ftp_hostname = safe_strdup(hostname);
|
||||
ftps->ftp_port = port;
|
||||
|
||||
/* Save the full path for error messages. */
|
||||
ftps->ftp_remote_path = percent_decode(p);
|
||||
|
||||
/* Build a list of directory components plus the filename. */
|
||||
ftps->ftp_remote_ndirs = 0;
|
||||
q = p;
|
||||
while ((q = strchr(q, '/')) != 0) {
|
||||
q++;
|
||||
ftps->ftp_remote_ndirs++;
|
||||
}
|
||||
path = safe_strdup(p);
|
||||
if (ftps->ftp_remote_ndirs != 0) {
|
||||
ftps->ftp_remote_dirs = safe_malloc(ftps->ftp_remote_ndirs *
|
||||
sizeof(char *));
|
||||
r = s = path = safe_strdup(p);
|
||||
dp = ftps->ftp_remote_dirs;
|
||||
while ((s = strchr(s, '/')) != 0) {
|
||||
*s++ = '\0';
|
||||
/*
|
||||
* Skip double-slashes. According to RFC1738,
|
||||
* double-slashes mean "send 'CWD '", which is
|
||||
* a syntax error to most FTP servers. Instead,
|
||||
* we just pretend that multiple slashes are a
|
||||
* single slash.
|
||||
*/
|
||||
if (*r == '\0') {
|
||||
warnx("skipping double slash in FTP URL; see man page or RFC1738.");
|
||||
ftps->ftp_remote_ndirs--;
|
||||
} else
|
||||
*dp++ = percent_decode(r);
|
||||
r = s;
|
||||
}
|
||||
} else {
|
||||
ftps->ftp_remote_dirs = 0;
|
||||
r = path;
|
||||
}
|
||||
if ((s = strchr(r, ';')) != 0 && strncmp(s, ";type=", 6) == 0) {
|
||||
*s = '\0';
|
||||
ftps->ftp_type = percent_decode(s+6);
|
||||
} else
|
||||
ftps->ftp_type = 0;
|
||||
ftps->ftp_remote_file = percent_decode(r);
|
||||
free(path);
|
||||
|
||||
if (fs->fs_outputfile == 0) {
|
||||
fs->fs_outputfile = ftps->ftp_remote_file;
|
||||
}
|
||||
|
||||
if (ftps->ftp_password == 0)
|
||||
ftps->ftp_password = getenv("FTP_PASSWORD");
|
||||
if (ftps->ftp_password != 0) {
|
||||
ftps->ftp_password = safe_strdup(ftps->ftp_password);
|
||||
} else {
|
||||
char *pw;
|
||||
const char *logname;
|
||||
char localhost[MAXHOSTNAMELEN];
|
||||
|
||||
logname = getlogin();
|
||||
if (logname == 0)
|
||||
logname = "root";
|
||||
gethostname(localhost, sizeof localhost);
|
||||
pw = safe_malloc(strlen(logname) + 1 + strlen(localhost) + 1);
|
||||
strcpy(pw, logname);
|
||||
strcat(pw, "@");
|
||||
strcat(pw, localhost);
|
||||
ftps->ftp_password = pw;
|
||||
setenv("FTP_PASSWORD", pw, 0); /* cache the result */
|
||||
}
|
||||
|
||||
if (ftps->ftp_user == 0)
|
||||
ftps->ftp_user = getenv("FTP_LOGIN");
|
||||
if (ftps->ftp_user != 0)
|
||||
ftps->ftp_user = safe_strdup(ftps->ftp_user);
|
||||
|
||||
fs->fs_proto = ftps;
|
||||
fs->fs_close = ftp_close;
|
||||
fs->fs_retrieve = ftp_retrieve;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The only URIs we can handle in the FTP proxy are FTP URLs.
|
||||
* This makes it possible to take a few short cuts.
|
||||
*/
|
||||
static int
|
||||
ftp_proxy_parse(struct fetch_state *fs, const char *uri)
|
||||
{
|
||||
int rv;
|
||||
char *hostname;
|
||||
char *port;
|
||||
const char *user;
|
||||
char *newuser;
|
||||
unsigned portno;
|
||||
struct ftp_state *ftps;
|
||||
|
||||
hostname = getenv("FTP_PROXY");
|
||||
port = strchr(hostname, ':');
|
||||
if (port == 0) {
|
||||
portno = 21;
|
||||
} else {
|
||||
unsigned long ul;
|
||||
char *ep;
|
||||
|
||||
/* All this to avoid modifying the environment. */
|
||||
ep = alloca(strlen(hostname) + 1);
|
||||
strcpy(ep, hostname);
|
||||
port = ep + (port - hostname);
|
||||
hostname = ep;
|
||||
|
||||
*port++ = '\0';
|
||||
errno = 0;
|
||||
ul = strtoul(port, &ep, 0);
|
||||
if (*ep || !*port || errno != 0 || ul < 1 || ul > 65534) {
|
||||
warnx("`%s': invalid port specification for FTP proxy",
|
||||
port);
|
||||
return EX_USAGE;
|
||||
}
|
||||
portno = ul;
|
||||
}
|
||||
|
||||
/* ftp_parse() does most of the work; we can just fix things up */
|
||||
rv = ftp_parse(fs, uri);
|
||||
if (rv)
|
||||
return rv;
|
||||
/* Oops.. it got turned into a file: */
|
||||
if (fs->fs_retrieve != ftp_retrieve) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ftps = fs->fs_proto;
|
||||
|
||||
user = ftps->ftp_user ? ftps->ftp_user : "anonymous";
|
||||
/* user @ hostname [ @port ] \0 */
|
||||
newuser = safe_malloc(strlen(user) + 1 + strlen(ftps->ftp_hostname)
|
||||
+ ((ftps->ftp_port != 21) ? 6 : 0) + 1);
|
||||
|
||||
strcpy(newuser, user);
|
||||
strcat(newuser, "@");
|
||||
strcat(newuser, ftps->ftp_hostname);
|
||||
if (ftps->ftp_port != 21) {
|
||||
char numbuf[6];
|
||||
|
||||
snprintf(numbuf, sizeof(numbuf), "%d", ftps->ftp_port);
|
||||
numbuf[sizeof(numbuf)-1] = '\0';
|
||||
strcat(newuser, "@");
|
||||
strcat(newuser, numbuf);
|
||||
}
|
||||
|
||||
ftps->ftp_port = portno;
|
||||
free(ftps->ftp_hostname);
|
||||
ftps->ftp_hostname = safe_strdup(hostname);
|
||||
free(ftps->ftp_user);
|
||||
ftps->ftp_user = newuser;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ftp_close(struct fetch_state *fs)
|
||||
{
|
||||
struct ftp_state *ftps = fs->fs_proto;
|
||||
int i;
|
||||
char **dp;
|
||||
|
||||
if (ftps->ftp_user)
|
||||
free(ftps->ftp_user);
|
||||
free(ftps->ftp_hostname);
|
||||
free(ftps->ftp_password);
|
||||
free(ftps->ftp_remote_file);
|
||||
for (i = 0, dp = ftps->ftp_remote_dirs; i < ftps->ftp_remote_ndirs; i++, dp++)
|
||||
free(*dp);
|
||||
if (ftps->ftp_remote_dirs)
|
||||
free(ftps->ftp_remote_dirs);
|
||||
free(ftps->ftp_remote_path);
|
||||
if (ftps->ftp_type)
|
||||
free(ftps->ftp_type);
|
||||
free(ftps);
|
||||
fs->fs_proto = 0;
|
||||
fs->fs_outputfile = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ftp_retrieve(struct fetch_state *fs)
|
||||
{
|
||||
struct ftp_state *ftps = fs->fs_proto;
|
||||
FILE *ftp, *remote, *local;
|
||||
char **dp;
|
||||
int i, status;
|
||||
off_t size;
|
||||
off_t seekloc, wehave;
|
||||
time_t modtime;
|
||||
size_t readresult, writeresult;
|
||||
|
||||
fs->fs_status = "logging in to FTP server";
|
||||
ftp = ftpLogin(ftps->ftp_hostname,
|
||||
(char *)(ftps->ftp_user ? ftps->ftp_user : "anonymous"),
|
||||
/* XXX ^^^^ bad API */
|
||||
ftps->ftp_password, ftps->ftp_port, fs->fs_verbose > 1,
|
||||
&status);
|
||||
if (ftp == 0) {
|
||||
warnx("%s: %s", ftps->ftp_hostname,
|
||||
status ? ftpErrString(status) : hstrerror(h_errno));
|
||||
return EX_IOERR;
|
||||
}
|
||||
fs->fs_status = "preparing for FTP transfer";
|
||||
if (ftps->ftp_type && strcasecmp(ftps->ftp_type, "i") != 0) {
|
||||
if (strcasecmp(ftps->ftp_type, "a") == 0)
|
||||
ftpAscii(ftp);
|
||||
else {
|
||||
warnx("unknown or unsupported type %s", ftps->ftp_type);
|
||||
return EX_USAGE;
|
||||
}
|
||||
} else
|
||||
ftpBinary(ftp);
|
||||
ftpPassive(ftp, fs->fs_passive_mode);
|
||||
for (i = 0, dp = ftps->ftp_remote_dirs; i < ftps->ftp_remote_ndirs; i++, dp++) {
|
||||
if ((status = ftpChdir(ftp, *dp)) != 0) {
|
||||
warnx("%s: %s: %s", ftps->ftp_hostname,
|
||||
*dp, ftpErrString(status));
|
||||
return EX_IOERR;
|
||||
}
|
||||
}
|
||||
size = ftpGetSize(ftp, ftps->ftp_remote_file);
|
||||
|
||||
if (fs->fs_reportsize) {
|
||||
fclose(ftp);
|
||||
if (size == -1) {
|
||||
warnx("%s: size not known\n", fs->fs_outputfile);
|
||||
printf("Unknown\n");
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
printf("%qd\n", (quad_t)size);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (size > 0 && fs->fs_expectedsize != -1 && size != fs->fs_expectedsize) {
|
||||
warnx("%s: size mismatch, expected=%lu / actual=%lu",
|
||||
ftps->ftp_remote_path,
|
||||
(unsigned long)fs->fs_expectedsize,
|
||||
(unsigned long)size);
|
||||
return EX_DATAERR;
|
||||
}
|
||||
modtime = ftpGetModtime(ftp, ftps->ftp_remote_file);
|
||||
if (modtime <= 0) { /* xxx */
|
||||
warnx("%s: cannot get remote modification time",
|
||||
ftps->ftp_remote_path);
|
||||
modtime = -1;
|
||||
}
|
||||
fs->fs_modtime = modtime;
|
||||
seekloc = wehave = 0;
|
||||
if (fs->fs_restart || fs->fs_mirror) {
|
||||
struct stat stab;
|
||||
|
||||
if (fs->fs_outputfile[0] == '-'
|
||||
&& fs->fs_outputfile[1] == '\0')
|
||||
status = fstat(STDOUT_FILENO, &stab);
|
||||
else
|
||||
status = stat(fs->fs_outputfile, &stab);
|
||||
if (status < 0) {
|
||||
stab.st_mtime = -1;
|
||||
stab.st_size = 0;
|
||||
}
|
||||
if (status == 0 && !S_ISREG(stab.st_mode)) {
|
||||
fs->fs_restart = 0;
|
||||
fs->fs_mirror = 0;
|
||||
}
|
||||
if (fs->fs_mirror && stab.st_size == size
|
||||
&& modtime <= stab.st_mtime) {
|
||||
fclose(ftp);
|
||||
return 0;
|
||||
}
|
||||
if (fs->fs_restart) {
|
||||
if (stab.st_size != 0 && stab.st_size < size)
|
||||
seekloc = wehave = stab.st_size;
|
||||
}
|
||||
}
|
||||
|
||||
fs->fs_status = "retrieving file from FTP server";
|
||||
remote = ftpGet(ftp, ftps->ftp_remote_file, &seekloc);
|
||||
if (remote == 0) {
|
||||
if (ftpErrno(ftp)) {
|
||||
warnx("ftp://%s/%s: FTP error:",
|
||||
ftps->ftp_hostname, ftps->ftp_remote_path);
|
||||
warnx("%s", ftpErrString(ftpErrno(ftp)));
|
||||
fclose(ftp);
|
||||
return EX_IOERR;
|
||||
} else {
|
||||
warn("ftpGet");
|
||||
return EX_OSERR;
|
||||
}
|
||||
}
|
||||
|
||||
if (fs->fs_outputfile[0] == '-' && fs->fs_outputfile[1] == '\0')
|
||||
local = fopen("/dev/stdout", wehave ? "a" : "w");
|
||||
else
|
||||
local = fopen(fs->fs_outputfile, wehave ? "a" : "w");
|
||||
if (local == 0) {
|
||||
warn("%s", fs->fs_outputfile);
|
||||
fclose(remote);
|
||||
fclose(ftp);
|
||||
return EX_OSERR;
|
||||
}
|
||||
|
||||
if (fs->fs_timeout) {
|
||||
char buf[sizeof("18446744073709551616")]; /* 2**64 */
|
||||
snprintf(buf, sizeof buf, "%d", fs->fs_timeout);
|
||||
setenv("FTP_TIMEOUT", buf, 1);
|
||||
} else {
|
||||
char *env = getenv("FTP_TIMEOUT");
|
||||
char *ep;
|
||||
unsigned long ul;
|
||||
|
||||
if (env) {
|
||||
errno = 0;
|
||||
ul = strtoul(env, &ep, 0);
|
||||
if (*env && *ep == '\0' && errno == 0 && ul <= INT_MAX)
|
||||
fs->fs_timeout = ul;
|
||||
else
|
||||
warnx("`%s': invalid FTP timeout", env);
|
||||
}
|
||||
}
|
||||
|
||||
display(fs, size, wehave);
|
||||
setup_sigalrm();
|
||||
|
||||
do {
|
||||
char buf[BUFFER_SIZE];
|
||||
|
||||
alarm(fs->fs_timeout);
|
||||
readresult = fread(buf, 1, sizeof buf, remote);
|
||||
alarm(0);
|
||||
if (readresult == 0)
|
||||
break;
|
||||
display(fs, size, readresult);
|
||||
writeresult = fwrite(buf, 1, readresult, local);
|
||||
} while (writeresult == readresult);
|
||||
unsetup_sigalrm();
|
||||
|
||||
if (ferror(remote)) {
|
||||
warn("reading remote file from %s", ftps->ftp_hostname);
|
||||
fclose(local);
|
||||
fclose(remote);
|
||||
fclose(ftp);
|
||||
rm(fs);
|
||||
return EX_IOERR;
|
||||
} else if(ferror(local)) {
|
||||
warn("%s", fs->fs_outputfile);
|
||||
fclose(local);
|
||||
fclose(remote);
|
||||
fclose(ftp);
|
||||
rm(fs);
|
||||
return EX_IOERR;
|
||||
}
|
||||
|
||||
fclose(local);
|
||||
fclose(remote);
|
||||
fclose(ftp);
|
||||
if (display(fs, size, -1) != 0)
|
||||
return EX_PROTOCOL;
|
||||
adjmodtime(fs);
|
||||
return 0;
|
||||
}
|
1846
usr.bin/fetch/http.c
1846
usr.bin/fetch/http.c
File diff suppressed because it is too large
Load Diff
@ -1,402 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 1996
|
||||
* Jean-Marc Zucconi
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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.
|
||||
*/
|
||||
|
||||
/* $FreeBSD$ */
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h> /* needed for INT_MAX */
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/param.h> /* for MAXHOSTNAMELEN */
|
||||
#include <sys/time.h> /* for struct timeval, gettimeofday */
|
||||
|
||||
#include "fetch.h"
|
||||
|
||||
static struct fetch_state clean_fetch_state;
|
||||
static sigjmp_buf sigbuf;
|
||||
static int get(struct fetch_state *volatile fs);
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: fetch [-ADHILMNPRTVablFmnpqrstv] [-o outputfile] "
|
||||
"[-S bytes]\n"
|
||||
" [-f file -h host [-c dir] | URL]\n");
|
||||
exit(EX_USAGE);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char *const *argv)
|
||||
{
|
||||
int c;
|
||||
char *ep;
|
||||
struct fetch_state fs;
|
||||
const char *change_to_dir, *file_to_get, *hostname;
|
||||
int error, rv;
|
||||
unsigned long l;
|
||||
|
||||
init_schemes();
|
||||
fs = clean_fetch_state;
|
||||
fs.fs_verbose = 1;
|
||||
fs.fs_reportsize = 0;
|
||||
fs.fs_expectedsize = -1;
|
||||
change_to_dir = file_to_get = hostname = 0;
|
||||
|
||||
#define OPT_STRING "Aabc:D:Ff:h:HIlLmMnNo:pPqRrS:stT:vV:"
|
||||
while ((c = getopt(argc, argv, OPT_STRING)) != -1) {
|
||||
switch (c) {
|
||||
case 'A':
|
||||
fs.fs_auto_retry = -1;
|
||||
break;
|
||||
|
||||
case 'D': case 'H': case 'I': case 'L': case 'N': case 'V':
|
||||
break; /* ncftp compatibility */
|
||||
|
||||
case 'F':
|
||||
fs.fs_forcerestart = 1;
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
fs.fs_auto_retry = 1;
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
fs.fs_linux_bug = 1;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
change_to_dir = optarg;
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
file_to_get = optarg;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
hostname = optarg;
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
fs.fs_linkfile = 1;
|
||||
break;
|
||||
|
||||
case 'm': case 'M':
|
||||
fs.fs_mirror = 1;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
fs.fs_newtime = 1;
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
fs.fs_outputfile = optarg;
|
||||
break;
|
||||
|
||||
case 'p': case 'P':
|
||||
fs.fs_passive_mode = 1;
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
fs.fs_verbose = 0;
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
fs.fs_restart = 1;
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
fs.fs_precious = 1;
|
||||
break;
|
||||
|
||||
case 't':
|
||||
fs.fs_use_connect = 1;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
fs.fs_reportsize = 1;
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
/* strtol sets errno to ERANGE in the case of overflow */
|
||||
errno = 0;
|
||||
l = strtoul(optarg, &ep, 0);
|
||||
if (!optarg[0] || *ep || errno != 0 || l > INT_MAX)
|
||||
errx(EX_USAGE, "invalid size value: `%s'",
|
||||
optarg);
|
||||
fs.fs_expectedsize = l;
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
/* strtol sets errno to ERANGE in the case of overflow */
|
||||
errno = 0;
|
||||
l = strtoul(optarg, &ep, 0);
|
||||
if (!optarg[0] || *ep || errno != 0 || l > INT_MAX)
|
||||
errx(EX_USAGE, "invalid timeout value: `%s'",
|
||||
optarg);
|
||||
fs.fs_timeout = l;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
if (fs.fs_verbose < 2)
|
||||
fs.fs_verbose = 2;
|
||||
else
|
||||
fs.fs_verbose++;
|
||||
break;
|
||||
|
||||
default:
|
||||
case '?':
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
clean_fetch_state = fs; /* preserve option settings */
|
||||
|
||||
if (argv[optind] && (hostname || change_to_dir || file_to_get)) {
|
||||
warnx("cannot use -h, -c, or -f with a URI argument");
|
||||
usage();
|
||||
}
|
||||
|
||||
if (fs.fs_mirror && fs.fs_restart)
|
||||
errx(EX_USAGE, "-m and -r are mutually exclusive.");
|
||||
|
||||
if (argv[optind] == 0) {
|
||||
char *uri;
|
||||
|
||||
if (hostname == 0) hostname = "localhost";
|
||||
if (change_to_dir == 0) change_to_dir = "";
|
||||
if (file_to_get == 0) {
|
||||
usage();
|
||||
}
|
||||
|
||||
uri = alloca(sizeof("ftp://") + strlen(hostname) +
|
||||
strlen(change_to_dir) + 5 + strlen(file_to_get));
|
||||
strcpy(uri, "ftp://");
|
||||
strcat(uri, hostname);
|
||||
strcat(uri, "/");
|
||||
if (change_to_dir[0] == '/') {
|
||||
strcat(uri, "%2f");
|
||||
strcat(uri, change_to_dir+1);
|
||||
}
|
||||
else strcat(uri, change_to_dir);
|
||||
if (file_to_get[0] != '/' && uri[strlen(uri) - 1] != '/')
|
||||
strcat(uri, "/");
|
||||
strcat(uri, file_to_get);
|
||||
|
||||
error = parse_uri(&fs, uri);
|
||||
if (error)
|
||||
return error;
|
||||
return get(&fs);
|
||||
}
|
||||
|
||||
for (rv = 0; argv[optind] != 0; optind++) {
|
||||
error = parse_uri(&fs, argv[optind]);
|
||||
if (error) {
|
||||
rv = error;
|
||||
continue;
|
||||
}
|
||||
|
||||
error = get(&fs);
|
||||
if (error) {
|
||||
rv = error;
|
||||
}
|
||||
fs = clean_fetch_state;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* The signal handling is probably more complex than it needs to be,
|
||||
* but it doesn't cost a lot, so we'll be extra-careful. Using
|
||||
* siglongjmp() to get out of the signal handler allows us to
|
||||
* call rm() without having to store the state variable in some global
|
||||
* spot where the signal handler can get at it. We also obviate the need
|
||||
* for a separate timeout signal handler.
|
||||
*/
|
||||
static int
|
||||
get(struct fetch_state *volatile fs)
|
||||
{
|
||||
volatile int error;
|
||||
struct sigaction oldhup, oldint, oldquit, oldterm;
|
||||
struct sigaction catch;
|
||||
volatile sigset_t omask;
|
||||
|
||||
sigemptyset(&catch.sa_mask);
|
||||
sigaddset(&catch.sa_mask, SIGHUP);
|
||||
sigaddset(&catch.sa_mask, SIGINT);
|
||||
sigaddset(&catch.sa_mask, SIGQUIT);
|
||||
sigaddset(&catch.sa_mask, SIGTERM);
|
||||
sigaddset(&catch.sa_mask, SIGALRM);
|
||||
catch.sa_handler = catchsig;
|
||||
catch.sa_flags = 0;
|
||||
|
||||
sigprocmask(SIG_BLOCK, &catch.sa_mask, (sigset_t *)&omask);
|
||||
sigaction(SIGHUP, &catch, &oldhup);
|
||||
sigaction(SIGINT, &catch, &oldint);
|
||||
sigaction(SIGQUIT, &catch, &oldquit);
|
||||
sigaction(SIGTERM, &catch, &oldterm);
|
||||
|
||||
error = sigsetjmp(sigbuf, 0);
|
||||
if (error == SIGALRM) {
|
||||
rm(fs);
|
||||
unsetup_sigalrm();
|
||||
fprintf(stderr, "\n"); /* just in case */
|
||||
warnx("%s: %s: timed out", fs->fs_outputfile, fs->fs_status);
|
||||
goto close;
|
||||
} else if (error) {
|
||||
rm(fs);
|
||||
fprintf(stderr, "\n"); /* just in case */
|
||||
warnx("%s: interrupted by signal: %s", fs->fs_status,
|
||||
sys_signame[error]);
|
||||
sigdelset(&omask, error);
|
||||
signal(error, SIG_DFL);
|
||||
sigprocmask(SIG_SETMASK, (sigset_t *)&omask, 0);
|
||||
raise(error); /* so that it gets reported as such */
|
||||
}
|
||||
|
||||
sigprocmask(SIG_SETMASK, (sigset_t *)&omask, 0);
|
||||
error = fs->fs_retrieve(fs);
|
||||
|
||||
close:
|
||||
sigaction(SIGHUP, &oldhup, 0);
|
||||
sigaction(SIGINT, &oldint, 0);
|
||||
sigaction(SIGQUIT, &oldquit, 0);
|
||||
sigaction(SIGTERM, &oldterm, 0);
|
||||
fs->fs_close(fs);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Utility functions
|
||||
*/
|
||||
|
||||
/*
|
||||
* Handle all signals by jumping back into get().
|
||||
*/
|
||||
void
|
||||
catchsig(int sig)
|
||||
{
|
||||
siglongjmp(sigbuf, sig);
|
||||
}
|
||||
|
||||
/*
|
||||
* Used to generate the progress display when not in quiet mode.
|
||||
* Return != 0 when the file appears to be truncated.
|
||||
*/
|
||||
int
|
||||
display(struct fetch_state *fs, off_t size, ssize_t n)
|
||||
{
|
||||
static off_t bytes;
|
||||
static off_t bytestart;
|
||||
static int pr, stdoutatty, init = 0;
|
||||
static struct timeval t0, t_start;
|
||||
static char *s;
|
||||
struct timezone tz;
|
||||
struct timeval t;
|
||||
float d;
|
||||
int truncated;
|
||||
|
||||
if (size != -1 && n == -1 && bytes != size) {
|
||||
truncated = 1;
|
||||
} else
|
||||
truncated = 0;
|
||||
if (init == 0) {
|
||||
init = 1;
|
||||
gettimeofday(&t0, &tz);
|
||||
t_start = t0;
|
||||
bytes = pr = 0;
|
||||
stdoutatty = isatty(STDOUT_FILENO);
|
||||
if (size > 0)
|
||||
asprintf (&s, "Receiving %s (%qd bytes)%s", fs->fs_outputfile,
|
||||
(quad_t)size,
|
||||
size ? "" : " [appending]");
|
||||
else
|
||||
asprintf (&s, "Receiving %s", fs->fs_outputfile);
|
||||
if (fs->fs_verbose)
|
||||
fprintf (stderr, "%s", s);
|
||||
bytestart = bytes = n;
|
||||
goto out;
|
||||
}
|
||||
gettimeofday(&t, &tz);
|
||||
if (n == -1) {
|
||||
if(stdoutatty && fs->fs_verbose) {
|
||||
if (size > 0)
|
||||
fprintf (stderr, "\r%s: 100%%", s);
|
||||
else
|
||||
fprintf (stderr, "\r%s: %qd Kbytes", s, (long long)bytes/1024);
|
||||
}
|
||||
bytes -= bytestart;
|
||||
d = t.tv_sec + t.tv_usec/1.e6 - t_start.tv_sec - t_start.tv_usec/1.e6;
|
||||
if (fs->fs_verbose)
|
||||
fprintf (stderr, "\n%qd bytes transferred in %.1f seconds",
|
||||
(long long)bytes, d);
|
||||
d = bytes/d;
|
||||
if (fs->fs_verbose) {
|
||||
if (d < 1000)
|
||||
fprintf (stderr, " (%.0f bytes/s)\n", d);
|
||||
else {
|
||||
d /=1024;
|
||||
fprintf (stderr, " (%.2f Kbytes/s)\n", d);
|
||||
}
|
||||
}
|
||||
free(s);
|
||||
init = 0;
|
||||
goto out;
|
||||
}
|
||||
bytes += n;
|
||||
d = t.tv_sec + t.tv_usec/1.e6 - t0.tv_sec - t0.tv_usec/1.e6;
|
||||
if (d < 5) /* display every 5 sec. */
|
||||
goto out;
|
||||
t0 = t;
|
||||
pr++;
|
||||
if(stdoutatty && fs->fs_verbose) {
|
||||
if (size > 1000000)
|
||||
fprintf (stderr, "\r%s: %2qd%%", s, (long long)(bytes/(size/100)));
|
||||
else if (size > 0)
|
||||
fprintf (stderr, "\r%s: %2qd%%", s, (long long)(100*bytes/size));
|
||||
else
|
||||
fprintf (stderr, "\r%s: %qd Kbytes", s, (long long)(bytes/1024));
|
||||
}
|
||||
out:
|
||||
if (truncated != 0)
|
||||
fprintf(stderr, "WARNING: File %s appears to be truncated: "
|
||||
"%qd/%qd bytes\n",
|
||||
fs->fs_outputfile,
|
||||
(quad_t)bytes, (quad_t)size);
|
||||
return truncated;
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
/*-
|
||||
* Copyright 1997 Massachusetts Institute of Technology
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software and
|
||||
* its documentation for any purpose and without fee is hereby
|
||||
* granted, provided that both the above copyright notice and this
|
||||
* permission notice appear in all copies, that both the above
|
||||
* copyright notice and this permission notice appear in all
|
||||
* supporting documentation, and that the name of M.I.T. not be used
|
||||
* in advertising or publicity pertaining to distribution of the
|
||||
* software without specific, written prior permission. M.I.T. makes
|
||||
* no representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied
|
||||
* warranty.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
|
||||
* ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
|
||||
* SHALL M.I.T. 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "fetch.h"
|
||||
|
||||
struct uri_scheme *schemes[] = {
|
||||
&http_scheme, &ftp_scheme, &file_scheme, 0
|
||||
};
|
||||
|
||||
static struct uri_scheme *
|
||||
find_scheme(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; schemes[i]; i++) {
|
||||
if (strcasecmp(schemes[i]->sc_name, name) == 0)
|
||||
return schemes[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
init_schemes(void)
|
||||
{
|
||||
int i;
|
||||
char schemebuf[32];
|
||||
const char *s, *t;
|
||||
struct uri_scheme *scp;
|
||||
|
||||
for (i = 0; schemes[i]; i++) {
|
||||
if (getenv(schemes[i]->sc_proxy_envar) != 0)
|
||||
schemes[i]->sc_can_proxy = 1;
|
||||
}
|
||||
|
||||
for (i = 0; schemes[i]; i++) {
|
||||
s = schemes[i]->sc_proxy_by;
|
||||
while (s && *s) {
|
||||
t = strchr(s, ',');
|
||||
if (t) {
|
||||
schemebuf[0] = '\0';
|
||||
strncat(schemebuf, s, t - s);
|
||||
s = t + 1;
|
||||
} else {
|
||||
strcpy(schemebuf, s);
|
||||
s = 0;
|
||||
}
|
||||
scp = find_scheme(schemebuf);
|
||||
if (scp && scp->sc_can_proxy) {
|
||||
schemes[i]->sc_proxyproto = scp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
parse_uri(struct fetch_state *fs, const char *uri)
|
||||
{
|
||||
const char *colon, *slash;
|
||||
char *scheme;
|
||||
struct uri_scheme *scp;
|
||||
|
||||
fs->fs_status = "parsing URI";
|
||||
colon = strchr(uri, ':');
|
||||
slash = strchr(uri, '/');
|
||||
if (!colon || !slash || slash < colon) {
|
||||
warnx("%s: an absolute URI is required", uri);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
scheme = alloca(colon - uri + 1);
|
||||
scheme[0] = '\0';
|
||||
strncat(scheme, uri, colon - uri);
|
||||
scp = find_scheme(scheme);
|
||||
|
||||
if (scp == 0) {
|
||||
warnx("%s: unknown URI scheme", scheme);
|
||||
return EX_USAGE;
|
||||
}
|
||||
if (scp->sc_proxyproto)
|
||||
return scp->sc_proxyproto->sc_proxy_parse(fs, uri);
|
||||
else
|
||||
return scp->sc_parse(fs, uri);
|
||||
}
|
||||
|
@ -1,334 +0,0 @@
|
||||
/*-
|
||||
* Copyright 1997 Massachusetts Institute of Technology
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software and
|
||||
* its documentation for any purpose and without fee is hereby
|
||||
* granted, provided that both the above copyright notice and this
|
||||
* permission notice appear in all copies, that both the above
|
||||
* copyright notice and this permission notice appear in all
|
||||
* supporting documentation, and that the name of M.I.T. not be used
|
||||
* in advertising or publicity pertaining to distribution of the
|
||||
* software without specific, written prior permission. M.I.T. makes
|
||||
* no representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied
|
||||
* warranty.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
|
||||
* ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
|
||||
* SHALL M.I.T. 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
#include <time.h> /* for time() */
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/time.h> /* for struct timeval */
|
||||
|
||||
#include "fetch.h"
|
||||
|
||||
|
||||
/* Signal handling functions */
|
||||
|
||||
/*
|
||||
* If this were Scheme we could make this variable private to just these two
|
||||
* functions...
|
||||
*/
|
||||
static struct sigaction oldalrm;
|
||||
|
||||
void
|
||||
setup_sigalrm(void)
|
||||
{
|
||||
struct sigaction catch;
|
||||
|
||||
sigemptyset(&catch.sa_mask);
|
||||
sigaddset(&catch.sa_mask, SIGHUP);
|
||||
sigaddset(&catch.sa_mask, SIGINT);
|
||||
sigaddset(&catch.sa_mask, SIGQUIT);
|
||||
sigaddset(&catch.sa_mask, SIGTERM);
|
||||
sigaddset(&catch.sa_mask, SIGALRM);
|
||||
catch.sa_handler = catchsig;
|
||||
catch.sa_flags = 0;
|
||||
|
||||
sigaction(SIGALRM, &catch, &oldalrm);
|
||||
}
|
||||
|
||||
void
|
||||
unsetup_sigalrm(void)
|
||||
{
|
||||
sigaction(SIGALRM, &oldalrm, 0);
|
||||
}
|
||||
|
||||
|
||||
/* File-handling functions */
|
||||
|
||||
/*
|
||||
* Set the last-modified time of the output file to be that returned by
|
||||
* the server.
|
||||
*/
|
||||
void
|
||||
adjmodtime(struct fetch_state *fs)
|
||||
{
|
||||
struct timeval tv[2];
|
||||
time_t tt;
|
||||
|
||||
/* XXX - not strictly correct, since (time_t)-1 does not have to be
|
||||
> 0. This also catches some of the other routines which erroneously
|
||||
return 0 for invalid times rather than -1. */
|
||||
if (!fs->fs_newtime && fs->fs_modtime > 0) {
|
||||
tv[0].tv_usec = tv[1].tv_usec = 0;
|
||||
time(&tt);
|
||||
tv[0].tv_sec = tt;
|
||||
tv[1].tv_sec = fs->fs_modtime;
|
||||
utimes(fs->fs_outputfile, tv);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete the file when exiting on error, if it is not `precious'.
|
||||
*/
|
||||
void
|
||||
rm(struct fetch_state *fs)
|
||||
{
|
||||
if (!(fs->fs_outputfile[0] == '-' && fs->fs_outputfile[1] == '\0')) {
|
||||
if (!fs->fs_restart && !fs->fs_mirror && !fs->fs_precious)
|
||||
unlink(fs->fs_outputfile);
|
||||
else
|
||||
adjmodtime(fs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* String-handling and -parsing functions */
|
||||
|
||||
/*
|
||||
* Undo the standard %-sign encoding in URIs (e.g., `%2f' -> `/'). This
|
||||
* must be done after the URI is parsed, since the principal purpose of
|
||||
* the encoding is to hide characters which would otherwise be significant
|
||||
* to the parser (like `/').
|
||||
*/
|
||||
char *
|
||||
percent_decode(const char *uri)
|
||||
{
|
||||
char *rv, *s;
|
||||
|
||||
rv = s = safe_malloc(strlen(uri) + 1);
|
||||
|
||||
while (*uri) {
|
||||
if (*uri == '%' && uri[1]
|
||||
&& isxdigit(uri[1]) && isxdigit(uri[2])) {
|
||||
int c;
|
||||
static char buf[] = "xx";
|
||||
|
||||
buf[0] = uri[1];
|
||||
buf[1] = uri[2];
|
||||
sscanf(buf, "%x", &c);
|
||||
uri += 3;
|
||||
*s++ = c;
|
||||
} else {
|
||||
*s++ = *uri++;
|
||||
}
|
||||
}
|
||||
*s = '\0';
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode a standard host:port string into its constituents, allocating
|
||||
* memory for a new copy of the host part.
|
||||
*/
|
||||
int
|
||||
parse_host_port(const char *s, char **hostname, int *port)
|
||||
{
|
||||
const char *colon;
|
||||
char *ep;
|
||||
unsigned long ul;
|
||||
|
||||
colon = strchr(s, ':');
|
||||
if (colon != 0) {
|
||||
errno = 0;
|
||||
ul = strtoul(colon + 1, &ep, 10);
|
||||
if (*ep != '\0' || colon[1] == '\0' || errno != 0
|
||||
|| ul < 1 || ul > 65534) {
|
||||
warnx("`%s': invalid port number", colon + 1);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
*hostname = safe_strndup(s, colon - s);
|
||||
*port = ul;
|
||||
} else {
|
||||
*hostname = safe_strdup(s);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* safe_malloc is like malloc, but aborts on error.
|
||||
*/
|
||||
void *
|
||||
safe_malloc(size_t len)
|
||||
{
|
||||
void *rv;
|
||||
|
||||
rv = malloc(len);
|
||||
if (rv == 0)
|
||||
err(EX_OSERR, "malloc(%qu)", (u_quad_t)len);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* safe_strdup is like strdup, but aborts on error.
|
||||
*/
|
||||
char *
|
||||
safe_strdup(const char *orig)
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = safe_malloc(strlen(orig) + 1);
|
||||
strcpy(s, orig);
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
* safe_strndup is like safe_strdup, but copies at most `len'
|
||||
* characters from `orig'.
|
||||
*/
|
||||
char *
|
||||
safe_strndup(const char *orig, size_t len)
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = safe_malloc(len + 1);
|
||||
s[0] = '\0';
|
||||
strncat(s, orig, len);
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
* Implement the `base64' encoding as described in RFC 1521.
|
||||
*/
|
||||
static const char base64[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
char *
|
||||
to_base64(const unsigned char *buf, size_t len)
|
||||
{
|
||||
char *s, *rv;
|
||||
unsigned tmp;
|
||||
|
||||
s = safe_malloc((4 * (len + 1)) / 3 + 1);
|
||||
|
||||
rv = s;
|
||||
while (len >= 3) {
|
||||
tmp = buf[0] << 16 | buf[1] << 8 | buf[2];
|
||||
s[0] = base64[tmp >> 18];
|
||||
s[1] = base64[(tmp >> 12) & 077];
|
||||
s[2] = base64[(tmp >> 6) & 077];
|
||||
s[3] = base64[tmp & 077];
|
||||
len -= 3;
|
||||
buf += 3;
|
||||
s += 4;
|
||||
}
|
||||
|
||||
/* RFC 1521 enumerates these three possibilities... */
|
||||
switch(len) {
|
||||
case 2:
|
||||
tmp = buf[0] << 16 | buf[1] << 8;
|
||||
s[0] = base64[(tmp >> 18) & 077];
|
||||
s[1] = base64[(tmp >> 12) & 077];
|
||||
s[2] = base64[(tmp >> 6) & 077];
|
||||
s[3] = '=';
|
||||
s[4] = '\0';
|
||||
break;
|
||||
case 1:
|
||||
tmp = buf[0] << 16;
|
||||
s[0] = base64[(tmp >> 18) & 077];
|
||||
s[1] = base64[(tmp >> 12) & 077];
|
||||
s[2] = s[3] = '=';
|
||||
s[4] = '\0';
|
||||
break;
|
||||
case 0:
|
||||
s[0] = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
from_base64(const char *orig, unsigned char *buf, size_t *lenp)
|
||||
{
|
||||
int len, len2;
|
||||
const char *equals;
|
||||
unsigned tmp;
|
||||
|
||||
len = strlen(orig);
|
||||
while (isspace(orig[len - 1]))
|
||||
len--;
|
||||
|
||||
if (len % 4)
|
||||
return -1;
|
||||
|
||||
len2 = 3 * (len / 4);
|
||||
equals = strchr(orig, '=');
|
||||
if (equals != 0) {
|
||||
if (equals[1] == '=')
|
||||
len2 -= 2;
|
||||
else
|
||||
len2 -= 1;
|
||||
}
|
||||
|
||||
/* Now the length is len2 is the actual length of the original. */
|
||||
if (len2 > *lenp)
|
||||
return -1;
|
||||
*lenp = len2;
|
||||
|
||||
while (len > 0) {
|
||||
int i;
|
||||
const char *off;
|
||||
int forget;
|
||||
|
||||
tmp = 0;
|
||||
forget = 0;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (orig[i] == '=') {
|
||||
off = base64;
|
||||
forget++;
|
||||
} else {
|
||||
off = strchr(base64, orig[i]);
|
||||
}
|
||||
if (off == 0)
|
||||
return -1;
|
||||
tmp = (tmp << 6) | (off - base64);
|
||||
}
|
||||
|
||||
buf[0] = (tmp >> 16) & 0xff;
|
||||
if (forget < 2)
|
||||
buf[1] = (tmp >> 8) & 0xff;
|
||||
if (forget < 1)
|
||||
buf[2] = (tmp >> 8) & 0xff;
|
||||
len -= 4;
|
||||
orig += 4;
|
||||
buf += 3 - forget;
|
||||
}
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user