LinuxParty
Última Actualización: 26/09/2026
Este código fuente de Woof, contiene las modificaciones del artículo:
#!/usr/bin/env python3 ################################################################ # Para trabajar con varios archivos, meterlo en un bucle... # # for i in {1..10} ; do echo $i ; woof -U ; done # # o utilizar el script "multiples-woof.sh", abajo descrito. ############################################################### # -*- encoding: utf-8 -*- # # woof -- an ad-hoc single file webserver # Copyright (C) 2004-2009 Simon Budig <Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo.> # # 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 # https://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. # Darwin support with the help from Mat Caughron, <Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo.> # Solaris support by Colin Marquardt, <Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo.> # FreeBSD support with the help from Andy Gimblett, <Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo.> # Cygwin support by Stefan Reichör <Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo.> # tarfile usage suggested by Morgan Lefieux <Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo.> # File upload support loosely based on code from Stephen English <Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo.>
import sys, os, errno, socket, getopt, tempfile
import urllib.request, urllib.parse, http.server
import readline
import configparser
import shutil, tarfile, zipfile
import struct
from email.parser import BytesFeedParser
from email.policy import HTTP
maxdownloads = 1
TM = object
cpid = -1
compressed = 'gz'
upload = False
class EvilZipStreamWrapper(TM):
def __init__ (self, victim):
self.victim_fd = victim
self.position = 0
self.tells = []
self.in_file_data = 0
def tell (self):
self.tells.append (self.position)
return self.position
def seek (self, offset, whence = 0):
if offset != 0:
if offset == self.tells[0] + 14:
self.write (b"PK\007\010")
elif offset == self.tells[1]:
self.tells = []
self.in_file_data = 0
else:
raise IOError ("unexpected seek for EvilZipStreamWrapper")
def write (self, data):
if self.in_file_data == 0:
if data[:4] == zipfile.stringFileHeader:
hdr = list (struct.unpack (zipfile.structFileHeader, data[:30]))
hdr[3] |= (1 << 3)
data = struct.pack (zipfile.structFileHeader, *hdr) + data[30:]
self.in_file_data = 1
elif data[:4] == zipfile.stringCentralDir:
hdr = list (struct.unpack (zipfile.structCentralDir, data[:46]))
hdr[5] |= (1 << 3)
data = struct.pack (zipfile.structCentralDir, *hdr) + data[46:]
self.position += len (data)
self.victim_fd.write (data)
def __getattr__ (self, name):
return getattr (self.victim_fd, name)
def find_ip ():
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]
class ForkingHTTPServer (http.server.HTTPServer):
def process_request (self, request, client_address):
self.finish_request (request, client_address)
self.close_request (request)
class FileServHTTPRequestHandler (http.server.BaseHTTPRequestHandler):
server_version = "Simons FileServer"
protocol_version = "HTTP/1.0"
filename = "."
def log_request (self, code='-', size='-'):
if code == 200:
http.server.BaseHTTPRequestHandler.log_request (self, code, size)
def parse_multipart_binary(self):
content_length = int(self.headers.get('Content-Length', 0))
if content_length <= 0:
return None, None
parser = BytesFeedParser(policy=HTTP)
headers_raw = f"Content-Type: {self.headers.get('Content-Type')}\r\n\r\n".encode('iso-8859-1')
parser.feed(headers_raw)
remaining = content_length
chunk_size = 65536
received = 0
while remaining > 0:
chunk = self.rfile.read(min(remaining, chunk_size))
if not chunk:
break
parser.feed(chunk)
received += len(chunk)
remaining -= len(chunk)
# Barra de progreso numérica en la terminal
print(f"\rRecibiendo: {received / (1024*1024):.2f} MB / {content_length / (1024*1024):.2f} MB ({int(received/content_length*100)}%)", end="", file=sys.stderr)
print("", file=sys.stderr)
msg = parser.close()
if not msg.is_multipart():
return None, None
for part in msg.walk():
if part.get_param('name', header='content-disposition') == 'upfile':
filename = part.get_filename()
payload = part.get_payload(decode=True)
return filename, payload
return None, None
def do_POST (self):
global maxdownloads, upload
if not upload:
self.send_error (501, "Unsupported method (POST)")
return
print ("\nSe pulsó enviar. Recibiendo archivo...", file=sys.stderr)
upfilename, file_data = self.parse_multipart_binary()
if not upfilename or file_data is None:
self.send_error (403, "No upload provided")
return
if "\\" in upfilename:
upfilename = upfilename.split ("\\")[-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 e:
if e.errno == errno.EEXIST:
continue
raise
if not destfile:
upfilename += "."
destfile, destfilename = tempfile.mkstemp (prefix = upfilename, dir = ".")
with os.fdopen (destfile, "wb") as f:
f.write(file_data)
print ("Descarga finalizada. Guardado en: %s -> %s\n" % (upfilename, destfilename), file=sys.stderr)
txt = b"""<html>
<head><title>Woof Upload</title></head>
<body>
<h1>Woof Upload complete</h1>
<p>Thanks a lot!</p>
<center>
<p style="font-size:250%"><a href="/"> INICIO </a></p>
</center>
</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
if upload:
txt = b"""<html>
<head><title>Woof Upload</title></head>
<body>
<table border="1" cellpadding="1" cellspacing="1" style="height:100%; width:100%">
<tr><td><h1><span style="font-size:45px">Woof Upload</span></h1></td></tr>
<tr><td>
<form class="formatoReporte" name="upload" method="POST" enctype="multipart/form-data">
<p><input style="font-size:250%" type="file" name="upfile" /></p>
<p><input style="font-size:250%" type="submit" value="Enviar!" /></p>
</form>
</td></tr>
</table>
</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
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 = ("""<html><head><title>302 Found</title></head>"""
"""<body>302 Found <a href="/%s">here</a>.</body></html>\n""" % location).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
cpid = os.fork ()
if cpid == 0:
child = None
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)))
if os.path.isfile (self.filename):
self.send_header ("Content-Length", os.path.getsize (self.filename))
self.end_headers ()
try:
if type == "file":
datafile = open (self.filename, "rb")
shutil.copyfileobj (datafile, self.wfile)
datafile.close ()
elif type == "dir":
if compressed == 'zip':
ezfile = EvilZipStreamWrapper (self.wfile)
zfile = zipfile.ZipFile (ezfile, 'w', zipfile.ZIP_DEFLATED)
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):])
zfile.close ()
else:
tfile = tarfile.open (mode=('w|' + compressed), fileobj=self.wfile)
tfile.add (self.filename, arcname=os.path.basename (self.filename))
tfile.close ()
except Exception as e:
print (e)
print ("Connection broke. Aborting", file=sys.stderr)
def serve_files (filename, maxdown = 1, ip_addr = '', port = 8080):
global maxdownloads
maxdownloads = maxdown
FileServHTTPRequestHandler.filename = filename
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 = "http://%s:%s/%s" % (ip_addr, httpd.server_port,
urllib.parse.quote (os.path.basename (filename)))
if os.path.isdir (filename):
if compressed == 'gz':
location += ".tar.gz"
elif compressed == 'bz2':
location += ".tar.bz2"
elif compressed == 'zip':
location += ".zip"
else:
location += ".tar"
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>
""" % (name, name, name, name, name), file=sys.stderr)
if errmsg:
print (errmsg, file=sys.stderr)
sys.exit (1)
def woof_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="]
fname = fname[0] if len(fname) else None
if fname == None:
url = f.geturl ()
urlparts = urllib.parse.urlparse (url)
fname = urlparts[2]
if not fname:
fname = "woof-out.bin"
fname = os.path.basename (urllib.parse.unquote (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)? ").lower() in [ "y", "yes" ]
else:
raise
if destfile == None:
if override:
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')])
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 = formats.get (config.get ('main', 'compressed'), 'gz')
defaultport = port
defaultmaxdown = maxdown
try:
options, filenames = getopt.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" % val)
elif option == '-i':
ip_addr = val
elif option == '-p':
try:
port = int (val)
except ValueError:
usage (defaultport, defaultmaxdown, "invalid port number: %r" % 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 = ''
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:
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])
serve_files (filename, maxdown, ip_addr, port)
if cpid != 0:
try:
while 1:
os.wait ()
except OSError:
pass
if __name__=='__main__':
try:
main ()
except KeyboardInterrupt:
print ()
Con el objetivo de poder meter "woof" en una iteración (un bucle) y poder sacar varios archivos del... por ejemplo móvil al portatil creamos este script, que lo puedes ejecutar desde consola o crear un acceso en el escritorio (recuerda hacerlo visible desde la consola, si quieres) para hacerte más fácil éste trabajo... si lo quieres mejorar tú... mejor.
#!/bin/bash # Nombre del script: multiples-woof.sh # Para enviar multiples archivos... # for i in {1..10} ; do echo $i ; woof -U ; done exit
-
Scripting
- Script en Bash para pausas interactivas con timeout y confirmación
- Monitoriza el uso de CPU y memoria en Linux con el script queprocesos.sh
- Cómo hacer operaciones matemáticas en Bash: cálculos con enteros, decimales, `bc` y `awk`
- Script para realizar copias de seguridad en un servidor en Producción con Dominios en Plesk
- Script para escuchar la Radio en Linux
- 🧠 Cómo forzar evitar distracciones con tus propios scripts en Linux
- Script copiacompleta2, un Script para hacer copias de seguridad completa de tu usuario.
- Iniciar una aplicación o un programa automáticamente desde el Cron en Linux
- Cómo instalar y habilitar el completado automático de Bash Linux
- Operaciones matemáticas en Shell Scripts
- 30 formas de validar archivos de configuración o scripts en Linux
- Operaciones artiméticas en Shell Scripts


