Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F3769127
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
29 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 0d5c3f9..896f653 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -1,18 +1,33 @@
## Contributors
+### wurf
+
+Maintainer: Sébastien Santoro <dereckson@nasqueron.org>
+
+They have contributed to wurf:
+
+ - Jens Korte <korte@free.de>
+ - CoolCat467
+
+Third-party documentation incorporated into wurf:
+
+ - Andrea Colangelo <warp10@ubuntu.com> - man page
+
+### woof
+
Original author: Simon Budig <simon@budig.de>
They have contributed to wurf during the woof era:
- Mat Caughron <mat@phpconsulting.com> - Darwin support
- Colin Marquardt <colin.marquardt@zmd.de> - Solaris support
- Andy Gimblett <A.M.Gimblett@swansea.ac.uk> - FreeBSD support
- Stefan Reichör <stefan@xsteve.at> - Cygwin support
They have suggested ideas to woof:
- Morgan Lefieux <comete@geekandfree.org> - tarfile usage
Third-party code incorporated into woof:
- Stephen English <steve@secomputing.co.uk> - File upload support (based on)
diff --git a/LICENSE b/LICENSE
index 0dd395d..9b7dd76 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,16 +1,17 @@
+Copyright (C) 2024 Nasqueron
Copyright (C) 2004-2009 Simon Budig <simon@budig.de>
This program 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 2 of the License, or
(at your option) any later version.
This program 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.
A copy of the GNU General Public License is available at
http://www.fsf.org/licenses/gpl.txt, you can also write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
diff --git a/README.md b/README.md
index 7602329..a8ffd40 100644
--- a/README.md
+++ b/README.md
@@ -1,77 +1,82 @@
-# Simply exchange files with WOOF
+# wurf - Web Upload and Receive Files
+
+## Rationale
I guess everybody with a laptop has experienced this problem at some
point: You plug into a network and just want to exchange files with
other participants. It *always* is a pain until you can exchange
files with the person vis-a-vis.
Of course there are a lot of tools to tackle this problem. For large
scale communities there are dozens of filesharing networks. However,
they don't work for small local networks. Of course you could put your
stuff to exchange on a local web server, but who really wants to
maintain this? Tools like the ingenious
[npush/npoll](http://www.fefe.de/ncp/) are
extremely helpful, provided that both parties have it installed,
[SAFT](http://www.belwue.de/projekte/saft/)
also aims to solve this problem, but needs a permanently running daemon...
-**Woof** (Web Offer One File) tries a different approach. It
+**wurf** (Web Upload and Receive Files) tries a different approach. It
assumes that everybody has a web-browser or a commandline web-client
-installed. **Woof** is a small simple stupid webserver that can
+installed. The **wurf** command is a small simple webserver that can
easily be invoked on a single file. Your partner can access the file
with tools he trusts (e.g. **wget**). No need to enter
passwords on keyboards where you don't know about keyboard sniffers, no
need to start a huge lot of infrastructure, just do a
```
- $ woof filename
+ $ wurf filename
```
-and tell the recipient the URL **woof** spits out. When he got that
-file, **woof** will quit and everything is done.
+and tell the recipient the URL **wurf** spits out. When he got that
+file, **wurf** will quit and everything is done.
-And when someone wants to send you a file, **woof** has a switch
-to offer itself, so he can get **woof** and offer a file to you.
+And when someone wants to send you a file, **wurf** has a switch
+to offer itself, so he can get **wurf** and offer a file to you.
-### Prerequisites and usage
+## Prerequisites and usage
-**Woof** needs Python on a unix'ish operating system. Some people
+**wurf** needs Python on a unix'ish operating system. Some people
have used it successfully on Windows within the cygwin environment.
```
- Usage: woof [-i <ip_addr>] [-p <port>] [-c <count>] <file>
- woof [-i <ip_addr>] [-p <port>] [-c <count>] [-z|-j|-Z|-u] <dir>
- woof [-i <ip_addr>] [-p <port>] [-c <count>] -s
- woof [-i <ip_addr>] [-p <port>] [-c <count>] -U
+ Usage: wurf [-i <ip_addr>] [-p <port>] [-c <count>] <file>
+ wurf [-i <ip_addr>] [-p <port>] [-c <count>] [-z|-j|-Z|-u] <dir>
+ wurf [-i <ip_addr>] [-p <port>] [-c <count>] -s
+ wurf [-i <ip_addr>] [-p <port>] [-c <count>] -U
- woof <url>
+ wurf <url>
Serves a single file <count> times via http on port <port> on IP
address <ip_addr>.
When a directory is specified, an tar archive gets served. By default
it is gzip compressed. You can specify -z for gzip compression,
-j for bzip2 compression, -Z for ZIP compression or -u for no compression.
You can configure your default compression method in the configuration
file described below.
- When -s is specified instead of a filename, woof distributes itself.
+ When -s is specified instead of a filename, wurf distributes itself.
- When -U is specified, woof provides an upload form, allowing file uploads.
+ When -U is specified, wurf provides an upload form, allowing file uploads.
defaults: count = 1, port = 8080
- If started with an url as an argument, woof acts as a client,
+ If started with an url as an argument, wurf acts as a client,
downloading the file and saving it in the current directory.
- You can specify different defaults in two locations: /etc/woofrc
- and ~/.woofrc can be INI-style config files containing the default
+ You can specify different defaults in two locations: /etc/wurfrc
+ and ~/.wurfrc can be INI-style config files containing the default
port and the default count. The file in the home directory takes
precedence. The compression methods are "off", "gz", "bz2" or "zip".
Sample file:
[main]
port = 8008
count = 2
ip = 127.0.0.1
compressed = gz
```
+## Credits
+
+wurf is a fork of woof by Simon Budig <simon@budig.de>
diff --git a/doc/woof.1 b/doc/wurf.1
similarity index 69%
rename from doc/woof.1
rename to doc/wurf.1
index 07e356c..d158cef 100644
--- a/doc/woof.1
+++ b/doc/wurf.1
@@ -1,69 +1,70 @@
-.TH "woof" "1" "Last Modified: November 22, 2022"
+.TH "wurf" "1" "Last Modified: October 18, 2024"
.SH NAME
-\fBwoof\fP \- A small, simple, stupid webserver to share files
+\fBwurf\fP \- A small, simple, stupid webserver to share files
.SH SYNOPSIS
-.B woof
-.RI [ options ] " file"
+.B wurf
+.RI [ options ] " file"
.SH DESCRIPTION
-\fBwoof\fP is a tool to copy files between hosts. It can
-serve a specified file on HTTP,just for a given number of times, and then
-shutdown. It can be easily used to share files across the computers on a
-net, and given that the other ends should have just a browser, it can share
-stuff between different operating system, or different devices (e.g.: a
+\fBwurf\fP is a tool to copy files between hosts. It can
+serve a specified file on HTTP,just for a given number of times, and then
+shutdown. It can be easily used to share files across the computers on a
+net, and given that the other ends should have just a browser, it can share
+stuff between different operating system, or different devices (e.g.: a
smartphone). It can also show a simple html form in order to upload a file.
commands.
.SH OPTIONS
A summary of options is included below.
.TP
.B \-h
Show summary of options.
.TP
.B \-i <ip_addr>
IP address to share the file
.TP
.B \-p <port>
Port to be used to share the file
.TP
.B \-c <count>
Number of times to share the file
.TP
.B \-z <dir>
Used on a directory, it creates a tarball with gzip compression
.TP
.B \-j <dir>
Used on a directory, it creates a tarball with bzip2 compression
.TP
.B \-Z <dir>
Used on a directory, it creates a tarball with ZIP compression
.TP
.B \-u <dir>
Used on a directory, it creates a tarball with no compression
.TP
.B \-s
-Used to distribute woof itself
+Used to distribute wurf itself
.TP
.B \-U
-woof provides an upload form and allows uploading files
+wurf provides an upload form and allows uploading files
.SH FILES
-You can specify different defaults in two locations: /etc/woofrc
-and ~/.woofrc can be INI-style config files containing the default
+You can specify different defaults in two locations: /etc/wurfrc
+and ~/.wurfrc can be INI-style config files containing the default
port and the default count. The file in the home directory takes
precedence. The compression methods are "off", "gz", "bz2" or "zip".
Sample file:
[main]
port = 8008
count = 2
ip = 127.0.0.1
compressed = gz
.SH AUTHOR
-woof was written by Simon Budig <simon@budig.de>
+wurf is maintained by Sébastien Santoro <dereckson@nasqueron.org>,
+based on woof source code written by Simon Budig <simon@budig.de>.
This manual page was written by Andrea Colangelo <warp10@ubuntu.com>,
for the Debian project (and may be used by others).
diff --git a/woof b/src/wurf.py
similarity index 95%
rename from woof
rename to src/wurf.py
index 1a92668..8bd5304 100755
--- a/woof
+++ b/src/wurf.py
@@ -1,611 +1,618 @@
#!/usr/bin/env python3
-# woof -- an ad-hoc single file webserver
+# wurf -- an ad-hoc single file webserver
from __future__ import annotations
from typing import Generator, BinaryIO
import sys, os, errno, socket, getopt, subprocess, tempfile
import urllib.request, urllib.parse, http.server
import email.parser
import readline
import configparser
import shutil, tarfile, zipfile
import struct
from io import BytesIO, StringIO
maxdownloads = 1
cpid = -1
compressed = "gz"
upload = False
# Utility function to guess the IP (as a string) where the server can be
# reached from the outside. Quite nasty problem actually.
def find_ip():
# we get a UDP-socket for the TEST-networks reserved by IANA.
# It is highly unlikely, that there is special routing used
# for these networks, hence the socket later should give us
# the ip address of the default route.
# We're doing multiple tests, to guard against the computer being
# part of a test installation.
candidates = []
for test_ip in ["192.0.2.0", "198.51.100.0", "203.0.113.0"]:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect((test_ip, 80))
ip_addr = s.getsockname()[0]
s.close()
if ip_addr in candidates:
return ip_addr
candidates.append(ip_addr)
return candidates[0]
def decode_multipart_form_data(
multipart_data: BinaryIO, headers: email.message.Message
) -> list[tuple[dict[str, str], bytes]]:
"""Decode multipart form data"""
content_type = headers["Content-Type"].encode("ascii")
content_len = int(headers["Content-Length"])
data = multipart_data.read(content_len)
content = b"Content-Type: %b\r\n%b" % (content_type, data)
parsed = email.parser.BytesParser().parsebytes(content)
results = []
for part in parsed.get_payload():
params = part.get_params(header="content-disposition")
payload: bytes = part.get_payload(decode=True)
result = dict(params), payload
results.append(result)
return results
# our own HTTP server class, fixing up a change in python 2.7
# since we do our fork() in the request handler
# the server must not shutdown() the socket.
class ForkingHTTPServer(http.server.HTTPServer):
def process_request(self, request, client_address):
self.finish_request(request, client_address)
self.close_request(request)
# Main class implementing an HTTP-Requesthandler, that serves just a single
# file and redirects all other requests to this file (this passes the actual
# filename to the client).
# Currently it is impossible to serve different files with different
# instances of this class.
class FileServHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
server_version = "Simons FileServer"
protocol_version = "HTTP/1.0"
filename = "."
def log_request(self, code="-", size="-"):
if code == 200:
super().log_request(code, size)
def do_POST(self):
global maxdownloads, upload
if not upload:
self.send_error(501, "Unsupported method (POST)")
return
multi_form = decode_multipart_form_data(self.rfile, self.headers)
for form_dict, content in multi_form:
if form_dict.get("name") == "upfile":
break
else:
# Went through without break, did not find
self.send_error(403, "No upload provided")
return
if not content or not form_dict.get("filename"):
self.send_error(403, "No upload provided")
return
upfilename = form_dict["filename"]
if "\\" in upfilename:
upfilename = upfilename.rsplit("\\", 1)[-1]
upfilename = os.path.basename(upfilename)
destfile = None
for suffix in ["", ".1", ".2", ".3", ".4", ".5", ".6", ".7", ".8", ".9"]:
destfilename = os.path.join(".", upfilename + suffix)
try:
destfile = os.open(
destfilename, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644
)
break
except OSError as ex:
if ex.errno == errno.EEXIST:
continue
raise
if not destfile:
upfilename += "."
destfile, destfilename = tempfile.mkstemp(prefix=upfilename, dir=".")
print(
"Accepting uploaded file: %s -> %s" % (upfilename, destfilename),
file=sys.stderr,
)
with BytesIO(content) as readfile:
with open(destfile, "wb") as writefile:
shutil.copyfileobj(readfile, writefile)
# if upfile.done == -1:
# self.send_error (408, "upload interrupted")
txt = b"""\
<!DOCTYPE html>
<html>
- <head><title>Woof Upload</title></head>
+ <head><title>Wurf Upload</title></head>
<body>
- <h1>Woof Upload complete</title></h1>
+ <h1>Wurf Upload complete</title></h1>
<p>Thanks a lot!</p>
</body>
</html>
"""
self.send_response(200)
self.send_header("Content-Type", "text/html")
self.send_header("Content-Length", str(len(txt)))
self.end_headers()
self.wfile.write(txt)
maxdownloads -= 1
return
def do_GET(self):
global maxdownloads, cpid, compressed, upload
# Form for uploading a file
if upload:
txt = b"""\
<!DOCTYPE html>
<html>
- <head><title>Woof Upload</title></head>
+ <head><title>Wurf Upload</title></head>
<body>
- <h1>Woof Upload</title></h1>
+ <h1>Wurf Upload</title></h1>
<form name="upload" method="POST" enctype="multipart/form-data">
<p><input type="file" name="upfile" /></p>
<p><input type="submit" value="Upload!" /></p>
</form>
</body>
</html>
"""
self.send_response(200)
self.send_header("Content-Type", "text/html")
self.send_header("Content-Length", str(len(txt)))
self.end_headers()
self.wfile.write(txt)
return
# Redirect any request to the filename of the file to serve.
# This hands over the filename to the client.
self.path = urllib.parse.quote(urllib.parse.unquote(self.path))
location = "/" + urllib.parse.quote(os.path.basename(self.filename))
if os.path.isdir(self.filename):
if compressed == "gz":
location += ".tar.gz"
elif compressed == "bz2":
location += ".tar.bz2"
elif compressed == "zip":
location += ".zip"
else:
location += ".tar"
if self.path != location:
txt = (
"""\
<!DOCTYPE html>
<html>
<head><title>302 Found</title></head>
<body>302 Found <a href="%s">here</a>.</body>
</html>\n"""
% location
)
txt = txt.encode("ascii")
self.send_response(302)
self.send_header("Location", location)
self.send_header("Content-Type", "text/html")
self.send_header("Content-Length", str(len(txt)))
self.end_headers()
self.wfile.write(txt)
return
maxdownloads -= 1
# let a separate process handle the actual download, so that
# multiple downloads can happen simultaneously.
cpid = os.fork()
if cpid == 0:
# Child process
type = None
if os.path.isfile(self.filename):
type = "file"
elif os.path.isdir(self.filename):
type = "dir"
if not type:
print("can only serve files or directories. Aborting.", file=sys.stderr)
sys.exit(1)
self.send_response(200)
self.send_header("Content-Type", "application/octet-stream")
self.send_header(
"Content-Disposition",
"attachment;filename=%s"
% urllib.parse.quote(
os.path.basename(self.filename + self.archive_ext)
),
)
if os.path.isfile(self.filename):
self.send_header("Content-Length", str(os.path.getsize(self.filename)))
self.end_headers()
try:
if type == "file":
with open(self.filename, "rb") as datafile:
shutil.copyfileobj(datafile, self.wfile)
elif type == "dir":
if compressed == "zip":
with zipfile.ZipFile(
self.wfile, "w", zipfile.ZIP_DEFLATED
) as zfile:
stripoff = os.path.dirname(self.filename) + os.sep
for root, dirs, files in os.walk(self.filename):
for f in files:
filename = os.path.join(root, f)
if filename[: len(stripoff)] != stripoff:
raise RuntimeError(
"invalid filename assumptions, please report!"
)
zfile.write(filename, filename[len(stripoff) :])
else:
with tarfile.open(
mode=("w|" + compressed), fileobj=self.wfile
) as tfile:
tfile.add(
self.filename, arcname=os.path.basename(self.filename)
)
except Exception as ex:
print(ex)
print("Connection broke. Aborting", file=sys.stderr)
def serve_files(filename, maxdown=1, ip_addr="", port=8080):
global maxdownloads
maxdownloads = maxdown
archive_ext = ""
if filename and os.path.isdir(filename):
if compressed == "gz":
archive_ext = ".tar.gz"
elif compressed == "bz2":
archive_ext = ".tar.bz2"
elif compressed == "zip":
archive_ext = ".zip"
else:
archive_ext = ".tar"
# We have to somehow push the filename of the file to serve to the
# class handling the requests. This is an evil way to do this...
FileServHTTPRequestHandler.filename = filename
FileServHTTPRequestHandler.archive_ext = archive_ext
try:
httpd = ForkingHTTPServer((ip_addr, port), FileServHTTPRequestHandler)
except socket.error:
print(
"cannot bind to IP address '%s' port %d" % (ip_addr, port), file=sys.stderr
)
sys.exit(1)
if not ip_addr:
ip_addr = find_ip()
if ip_addr:
if filename:
location = f"http://{ip_addr}:{httpd.server_port}/" + urllib.parse.quote(
os.path.basename(filename + archive_ext)
)
else:
location = "http://%s:%s/" % (ip_addr, httpd.server_port)
print("Now serving on %s" % location)
while cpid != 0 and maxdownloads > 0:
httpd.handle_request()
def usage(defport, defmaxdown, errmsg=None):
name = os.path.basename(sys.argv[0])
print(
"""
Usage: %s [-i <ip_addr>] [-p <port>] [-c <count>] <file>
%s [-i <ip_addr>] [-p <port>] [-c <count>] [-z|-j|-Z|-u] <dir>
%s [-i <ip_addr>] [-p <port>] [-c <count>] -s
%s [-i <ip_addr>] [-p <port>] [-c <count>] -U
%s <url>
Serves a single file <count> times via http on port <port> on IP
address <ip_addr>.
When a directory is specified, an tar archive gets served. By default
it is gzip compressed. You can specify -z for gzip compression,
-j for bzip2 compression, -Z for ZIP compression or -u for no compression.
You can configure your default compression method in the configuration
file described below.
When -s is specified instead of a filename, %s distributes itself.
- When -U is specified, woof provides an upload form, allowing file uploads.
+ When -U is specified, wurf provides an upload form, allowing file uploads.
defaults: count = %d, port = %d
- If started with an url as an argument, woof acts as a client,
+ If started with an url as an argument, wurf acts as a client,
downloading the file and saving it in the current directory.
- You can specify different defaults in two locations: /etc/woofrc
- and ~/.woofrc can be INI-style config files containing the default
+ You can specify different defaults in two locations: /etc/wurfrc
+ and ~/.wurfrc can be INI-style config files containing the default
port and the default count. The file in the home directory takes
precedence. The compression methods are "off", "gz", "bz2" or "zip".
Sample file:
[main]
port = 8008
count = 2
ip = 127.0.0.1
compressed = gz
"""
% (name, name, name, name, name, name, defmaxdown, defport),
file=sys.stderr,
)
if errmsg:
print(errmsg, file=sys.stderr)
print(file=sys.stderr)
sys.exit(1)
-def woof_client(url):
+def wurf_client(url):
urlparts = urllib.parse.urlparse(url, "http")
if urlparts[0] not in ["http", "https"] or urlparts[1] == "":
return None
fname = None
f = urllib.request.urlopen(url)
f_meta = f.info()
disp = f_meta["Content-Disposition"]
if disp:
disp = disp.split(";")
if disp and disp[0].lower() == "attachment":
fname = [x[9:] for x in disp[1:] if x[:9].lower() == "filename="]
if len(fname):
fname = fname[0]
else:
fname = None
if fname == None:
url = f.geturl()
urlparts = urllib.parse.urlparse(url)
fname = urlparts[2]
if not fname:
- fname = "woof-out.bin"
+ fname = "wurf-out.bin"
if fname:
fname = urllib.parse.unquote(fname)
fname = os.path.basename(fname)
readline.set_startup_hook(lambda: readline.insert_text(fname))
fname = input("Enter target filename: ")
readline.set_startup_hook(None)
override = False
destfile = None
destfilename = os.path.join(".", fname)
try:
destfile = os.open(destfilename, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644)
except OSError as e:
if e.errno == errno.EEXIST:
override = input("File exists. Overwrite (y/n)? ")
override = override.lower() in ["y", "yes"]
else:
raise
if destfile == None:
if override == True:
destfile = os.open(destfilename, os.O_WRONLY | os.O_CREAT, 0o644)
else:
for suffix in [".1", ".2", ".3", ".4", ".5", ".6", ".7", ".8", ".9"]:
destfilename = os.path.join(".", fname + suffix)
try:
destfile = os.open(
destfilename, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644
)
break
except OSError as e:
if e.errno == errno.EEXIST:
continue
raise
if not destfile:
destfile, destfilename = tempfile.mkstemp(prefix=fname + ".", dir=".")
print("alternate filename is:", destfilename)
print("downloading file: %s -> %s" % (fname, destfilename))
shutil.copyfileobj(f, os.fdopen(destfile, "wb"))
return 1
def main():
global cpid, upload, compressed
maxdown = 1
port = 8080
ip_addr = ""
config = configparser.ConfigParser()
- config.read(["/etc/woofrc", os.path.expanduser("~/.woofrc")])
+ config.read(
+ [
+ "/etc/woofrc",
+ "/etc/wurfrc",
+ os.path.expanduser("~/.woofrc"),
+ os.path.expanduser("~/.wurfrc"),
+ ]
+ )
if config.has_option("main", "port"):
port = config.getint("main", "port")
if config.has_option("main", "count"):
maxdown = config.getint("main", "count")
if config.has_option("main", "ip"):
ip_addr = config.get("main", "ip")
if config.has_option("main", "compressed"):
formats = {
"gz": "gz",
"true": "gz",
"bz": "bz2",
"bz2": "bz2",
"zip": "zip",
"off": "",
"false": "",
}
compressed = config.get("main", "compressed")
compressed = formats.get(compressed, "gz")
defaultport = port
defaultmaxdown = maxdown
try:
options, filenames = getopt.gnu_getopt(sys.argv[1:], "hUszjZui:c:p:")
except getopt.GetoptError as desc:
usage(defaultport, defaultmaxdown, desc)
for option, val in options:
if option == "-c":
try:
maxdown = int(val)
if maxdown <= 0:
raise ValueError
except ValueError:
usage(
defaultport,
defaultmaxdown,
"invalid download count: %r. "
"Please specify an integer >= 0." % val,
)
elif option == "-i":
ip_addr = val
elif option == "-p":
try:
port = int(val)
except ValueError:
usage(
defaultport,
defaultmaxdown,
"invalid port number: %r. Please specify an integer" % val,
)
elif option == "-s":
filenames.append(__file__)
elif option == "-h":
usage(defaultport, defaultmaxdown)
elif option == "-U":
upload = True
elif option == "-z":
compressed = "gz"
elif option == "-j":
compressed = "bz2"
elif option == "-Z":
compressed = "zip"
elif option == "-u":
compressed = ""
else:
usage(defaultport, defaultmaxdown, "Unknown option: %r" % option)
if upload:
if len(filenames) > 0:
usage(
defaultport,
defaultmaxdown,
"Conflicting usage: simultaneous up- and download not supported.",
)
filename = None
else:
if len(filenames) == 1:
- if woof_client(filenames[0]) != None:
+ if wurf_client(filenames[0]) != None:
sys.exit(0)
filename = os.path.abspath(filenames[0])
else:
usage(
defaultport, defaultmaxdown, "Can only serve single files/directories."
)
if not os.path.exists(filename):
usage(
defaultport,
defaultmaxdown,
"%s: No such file or directory" % filenames[0],
)
if not (os.path.isfile(filename) or os.path.isdir(filename)):
usage(
defaultport,
defaultmaxdown,
"%s: Neither file nor directory" % filenames[0],
)
serve_files(filename, maxdown, ip_addr, port)
# wait for child processes to terminate
if cpid != 0:
try:
while 1:
os.wait()
except OSError:
pass
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print()
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Nov 25, 13:07 (1 d, 10 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2257487
Default Alt Text
(29 KB)
Attached To
Mode
rWURF wurf
Attached
Detach File
Event Timeline
Log In to Comment