Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ to offer itself, so he can get **wurf** and offer a file to you.
have used it successfully on Windows within the cygwin environment.

```
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
Usage: wurf [-i <ip_addr>] [-p <port>] [-c <count>] [-t [--cert <cert_file>] [--key <key_file>] [--keypass <key_pass>]] <file>
wurf [-i <ip_addr>] [-p <port>] [-c <count>] [-t [--cert <cert_file>] [--key <key_file>] [--keypass <key_pass>]] [-z|-j|-Z|-u] <dir>
wurf [-i <ip_addr>] [-p <port>] [-c <count>] [-t [--cert <cert_file>] [--key <key_file>] [--keypass <key_pass>]] -s
wurf [-i <ip_addr>] [-p <port>] [-c <count>] [-t [--cert <cert_file>] [--key <key_file>] [--keypass <key_pass>]] -U

wurf <url>

Expand All @@ -54,6 +54,8 @@ have used it successfully on Windows within the cygwin environment.
You can configure your default compression method in the configuration
file described below.

When -t is specified, wurf will use TLS to secure the connection. You must pass both a certificate and key in PEM format.

When -s is specified instead of a filename, wurf distributes itself.

When -U is specified, wurf provides an upload form, allowing file uploads.
Expand All @@ -75,6 +77,12 @@ have used it successfully on Windows within the cygwin environment.
count = 2
ip = 127.0.0.1
compressed = gz
tls = on

[tls]
cert = /etc/letsencrypt/live/example.com/fullchain.pem
key = /etc/letsencrypt/live/example.com/privkey.pem
keypass = my_password
```

## Credits
Expand Down
20 changes: 19 additions & 1 deletion doc/wurf.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.TH "wurf" "1" "Last Modified: October 18, 2024"
.TH "wurf" "1" "Last Modified: June 25, 2025"
.SH NAME
\fBwurf\fP \- A small, simple, stupid webserver to share files

Expand Down Expand Up @@ -45,6 +45,18 @@ Used on a directory, it creates a tarball with no compression
.B \-s
Used to distribute wurf itself
.TP
.B \-t
Enables TLS mode
.TP
.B \--cert <cert_file>
With -t, specifies a pem formatted certificate file
.TP
.B \--key <key_file>
With -t, specifies a pem formatted key file
.TP
.B \--keypass <key_password>
With -t, specifies the key password
.TP
.B \-U
wurf provides an upload form and allows uploading files

Expand All @@ -61,6 +73,12 @@ precedence. The compression methods are "off", "gz", "bz2" or "zip".
count = 2
ip = 127.0.0.1
compressed = gz
tls = on

[tls]
cert = /etc/letsencrypt/live/example.com/fullchain.pem
key = /etc/letsencrypt/live/example.com/privkey.pem
keypass = my_password

.SH AUTHOR
wurf is maintained by Sébastien Santoro <dereckson@nasqueron.org>,
Expand Down
92 changes: 80 additions & 12 deletions src/wurf.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@
import shutil, tarfile, zipfile
import struct
from io import BytesIO, StringIO
import ssl

maxdownloads = 1
cpid = -1
compressed = "gz"
upload = False
tls = False
cert = ""
key = ""
keypass = ""


# Utility function to guess the IP (as a string) where the server can be
Expand Down Expand Up @@ -325,31 +330,46 @@ def serve_files(filename, maxdown=1, ip_addr="", port=8080):
"cannot bind to IP address '%s' port %d" % (ip_addr, port), file=sys.stderr
)
sys.exit(1)

listen_protocol = "https" if tls else "http"
if not ip_addr:
ip_addr = find_ip()
if ip_addr:
if filename:
location = f"http://{ip_addr}:{httpd.server_port}/" + urllib.parse.quote(
location = f"{listen_protocol}://{ip_addr}:{httpd.server_port}/" + urllib.parse.quote(
os.path.basename(filename + archive_ext)
)
else:
location = "http://%s:%s/" % (ip_addr, httpd.server_port)
location = "%s://%s:%s/" % (listen_protocol, ip_addr, httpd.server_port)

print("Now serving on %s" % location)

while cpid != 0 and maxdownloads > 0:
httpd.handle_request()
if tls :
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
try:
context.load_cert_chain(cert, key, keypass)
except ssl.SSLError:
print("Unable to load certificate or key. Possibly missing or incorrect password.")
sys.exit(1)
except FileNotFoundError:
print("Certificate or Key file is inaccessible or incorrect.")
sys.exit(1)

with context.wrap_socket(httpd.socket, server_side=True) as ssock:
httpd.socket = ssock
while cpid != 0 and maxdownloads > 0:
httpd.handle_request()
else:
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
Usage: %s [-i <ip_addr>] [-p <port>] [-c <count>] [-t [--cert <cert_file>] [--key <key_file>] [--keypass <key_pass>]] <file>
%s [-i <ip_addr>] [-p <port>] [-c <count>] [-t [--cert <cert_file>] [--key <key_file>] [--keypass <key_pass>]] [-z|-j|-Z|-u] <dir>
%s [-i <ip_addr>] [-p <port>] [-c <count>] [-t [--cert <cert_file>] [--key <key_file>] [--keypass <key_pass>]] -s
%s [-i <ip_addr>] [-p <port>] [-c <count>] [-t [--cert <cert_file>] [--key <key_file>] [--keypass <key_pass>]] -U

%s <url>

Expand All @@ -361,6 +381,8 @@ def usage(defport, defmaxdown, errmsg=None):
You can configure your default compression method in the configuration
file described below.

When -t is specified, wurf will use TLS to secure the connection. You must pass both a certificate and key in PEM format.

When -s is specified instead of a filename, %s distributes itself.

When -U is specified, wurf provides an upload form, allowing file uploads.
Expand All @@ -382,6 +404,12 @@ def usage(defport, defmaxdown, errmsg=None):
count = 2
ip = 127.0.0.1
compressed = gz
tls = on

[tls]
cert = /etc/letsencrypt/live/example.com/fullchain.pem
key = /etc/letsencrypt/live/example.com/privkey.pem
keypass = my_password
"""
% (name, name, name, name, name, name, defmaxdown, defport),
file=sys.stderr,
Expand Down Expand Up @@ -472,7 +500,7 @@ def wurf_client(url):


def main():
global cpid, upload, compressed
global cpid, upload, compressed, tls, cert, key, keypass

maxdown = 1
port = 8080
Expand Down Expand Up @@ -510,11 +538,32 @@ def main():
compressed = config.get("main", "compressed")
compressed = formats.get(compressed, "gz")

if config.has_option("main", "tls"):
affirm = {
"yes": True,
"true": True,
"enabled": True,
"on": True
}
tls_setting = config.get("main", "tls")
tls = affirm.get(tls_setting, False)
if not config.has_option("main", "port"):
port = 8443

if config.has_option("tls", "cert"):
cert = config.get("tls", "cert")

if config.has_option("tls", "key"):
key = config.get("tls", "key")

if config.has_option("tls", "keypass"):
keypass = config.get("tls", "keypass")

defaultport = port
defaultmaxdown = maxdown

try:
options, filenames = getopt.gnu_getopt(sys.argv[1:], "hUszjZui:c:p:")
options, filenames = getopt.gnu_getopt(sys.argv[1:], "hUszjZuti:c:p:", ["cert=", "key=", "keypass="])
except getopt.GetoptError as desc:
usage(defaultport, defaultmaxdown, desc)

Expand Down Expand Up @@ -563,6 +612,17 @@ def main():
elif option == "-u":
compressed = ""

elif option == "-t":
tls = True
if '-p' not in dict(options) and not config.has_option("main", "port"):
port = 8443
elif option == "--cert":
cert = val
elif option == "--key":
key = val
elif option == "--keypass":
keypass = val

else:
usage(defaultport, defaultmaxdown, "Unknown option: %r" % option)

Expand Down Expand Up @@ -600,6 +660,14 @@ def main():
"%s: Neither file nor directory" % filenames[0],
)

if tls:
if cert == "" or key == "":
usage(
defaultport,
defaultmaxdown,
"Missing option, TLS requested but certificate/key pair not provided",
)

serve_files(filename, maxdown, ip_addr, port)

# wait for child processes to terminate
Expand Down