LinuxParty
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, subprocess, tempfile import cgi, urllib.request, urllib.parse, http.server import readline import configparser import shutil, tarfile, zipfile import struct 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: # the zipfile module tries to fix up the file header. # write Data descriptor header instead, # the next write from zipfile # is CRC, compressed_size and file_size (as required) self.write (b"PK\007\010") elif offset == self.tells[1]: # the zipfile module goes to the end of the file. The next # data written definitely is infrastructure (in_file_data = 0) self.tells = [] self.in_file_data = 0 else: raise IOError ("unexpected seek for EvilZipStreamWrapper") def write (self, data): # only test for headers if we know that we're not writing # (potentially compressed) data. if self.in_file_data == 0: if data[:4] == zipfile.stringFileHeader: # fix the file header for extra Data descriptor 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: # fix the directory entry to match file header. 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) # 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] # 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: http.server.BaseHTTPRequestHandler.log_request (self, code, size) def do_POST (self): global maxdownloads, upload if not upload: self.send_error (501, "Unsupported method (POST)") return # taken from # https://mail.python.org/pipermail/python-list/2006-September/402441.html ctype, pdict = cgi.parse_header (self.headers['Content-Type']) form = cgi.FieldStorage (fp = self.rfile, headers = self.headers, environ = {'REQUEST_METHOD' : 'POST'}, keep_blank_values = 1, strict_parsing = 1) if "upfile" not in form: self.send_error (403, "No upload provided") return upfile = form["upfile"] if not upfile.file or not upfile.filename: self.send_error (403, "No upload provided") return upfilename = upfile.filename if "\\" in upfilename: upfilename = upfilename.split ("\\")[-1] upfilename = os.path.basename (upfile.filename) 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 = ".") print ("accepting uploaded file: %s -> %s" % (upfilename, destfilename), file=sys.stderr) shutil.copyfileobj (upfile.file, os.fdopen (destfile, "wb")) if upfile.done == -1: self.send_error (408, "upload interrupted") txt = b"""\ <html> <head><title>Woof Upload</title></head> <body> <h1>Woof Upload complete</title></h1> <p>Thanks a lot!</p> <h1> <p> <br> </p> <p> <br> </p> <p> <br> </p> <p> <br> </p> <p> <br> </p> <p> <br> </p> <center> <p style="font-size:250%" ><a href="/"> INICIO </h1></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 # txt = b"""\ # <html> # <head><title>Woof Upload</title></head> # <body> # <h1>Woof 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> # """ # Form for uploading a file 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 # 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 = """\ <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 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 RuntimeException ("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 # 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 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 = "https://%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 = "https://%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. defaults: count = %d, port = %d If started with an url as an argument, woof 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 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): 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" 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')]) 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.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: 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 ()
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
