GithubHelp home page GithubHelp logo

woof's People

Contributors

pklapperich avatar simon-budig avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

woof's Issues

Clarification: -Z zipped tarball OR actual zip file?

I think I've spotted something in the help/documentation on Debian; the manpage says:

       -Z <dir>
              Used on a directory, it creates a tarball with ZIP compression

However, I am fairly confident, that would actually create a proper zip archive file. Otherwise this is a kind of a trap for people if they share files with non-*nix users.

Tagging versions

Hi, we're working on updating the woof formula in Homebrew (Homebrew/homebrew-core#94258) to a version that supports Python 3, in anticipation of the system Python 2 being removed in macOS 12.3. However, the woof repository doesn't have any version tags or releases and one of our Project Leadership Committee (PLC) members has made it clear that we should only update the formula to a new upstream release.

That is to say, it's not appropriate for us to simply pick the newest commit and use the commit date as the version, as it's not an official release. Not having any version information to reference also makes it difficult for us to check for new versions, as we would have to manually check the commit history instead of programmatically checking the repository tags/releases for new versions.

The homepage previously published date-based versions (e.g., woof-2012-05-31.py, woof-2009-12-27.py, etc.) but a new version hasn't been officially released since 2012-05-31. Unfortunately, that release came from the commit before Python 3 support was added, so the woof formula is facing removal if it can't be updated to a newer version.

With this in mind, could you please create version tags (e.g., 2021-06-09) for releases? If it makes sense for you, it would be helpful to create a version tag for an up-to-date version, so there's something we can use in the related Homebrew PR.

Crashing on python 3.9.6

Now serving on http://192.168.0.3:8091/1.tar.gz
192.168.0.100 - - [09/Jul/2021 00:42:12] "GET /1.tar.gz HTTP/1.1" 200 -
[Errno 104] Connection reset by peer
Connection broke. Aborting
Exception ignored in: <function _Stream.__del__ at 0x7f9901c31f70>
Traceback (most recent call last):
  File "/usr/lib/python3.9/tarfile.py", line 410, in __del__
    self.close()
  File "/usr/lib/python3.9/tarfile.py", line 460, in close
    self.fileobj.write(self.buf)
  File "/usr/lib/python3.9/socketserver.py", line 826, in write
    self._sock.sendall(b)
OSError: [Errno 9] Bad file descriptor
192.168.0.100 - - [09/Jul/2021 00:42:14] "GET /1.tar.gz HTTP/1.1" 200 -
[Errno 104] Connection reset by peer
Connection broke. Aborting
Exception ignored in: <function _Stream.__del__ at 0x7f9901c31f70>
Traceback (most recent call last):
  File "/usr/lib/python3.9/tarfile.py", line 410, in __del__
    self.close()
  File "/usr/lib/python3.9/tarfile.py", line 460, in close
    self.fileobj.write(self.buf)
  File "/usr/lib/python3.9/socketserver.py", line 826, in write
    self._sock.sendall(b)
OSError: [Errno 9] Bad file descriptor

Can you support the HEAD method?

And possibly give it its own counter that defaults to unlimited or the same as the GET counter's value?

$ woof foo.bar
Now serving on http://0.0.0.0:54321/foo.bar
127.0.0.1 - - [28/Jun/2022 11:56:53] code 501, message Unsupported method ('HEAD')

DeprecationWarning: 'cgi' is deprecated and slated for removal in Python 3.13

Using woof on Python 3.11+ now throws this warning every time:

woof:30: DeprecationWarning: 'cgi' is deprecated and slated for removal in Python 3.13
  import cgi, urllib.request, urllib.parse, http.server

The Python documentation on cgi recommends:

Deprecated since version 3.11, will be removed in version 3.13: The cgi module is deprecated (see PEP 594 for details and alternatives).

The FieldStorage class can typically be replaced with urllib.parse.parse_qsl() for GET and HEAD requests, and the email.message module or multipart for POST and PUT. Most utility functions have replacements.

When using woof to send directories, client saves filename without compression extention

When you use woof to send (in my testing) any directory, the default filename sent to clients to use is missing the compression file extension.

For example, trying to sent the directory "Documents" to someone with gzip compression, the filename should be "Documents.tar.gz", but when you download the file, firefox and woof's own tiny web browser (and probably everything else) wants to save the file as only "Documents", no "tar.gz" extension.

Attempting to send a file using ZIP compression crashes

If you try to send a directory with woof using ZIP compression, woof crashes with this traceback (at least in my testing):

Now serving on http://192.168.1.200:8080/<directory name here>.zip 192.168.1.200 - - [08/Jun/2021 20:45:34] "GET /<directory name here>.zip HTTP/1.1" 200 - unexpected seek for EvilZipStreamWrapper Connection broke. Aborting Exception ignored in: <function ZipFile.__del__ at 0x7f690e446700> Traceback (most recent call last): File "/usr/lib/python3.8/zipfile.py", line 1821, in __del__ self.close() File "/usr/lib/python3.8/zipfile.py", line 1838, in close self.fp.seek(self.start_dir) File "Desktop/interwebs-woof.py", line 68, in seek raise IOError ("unexpected seek for EvilZipStreamWrapper") OSError: unexpected seek for EvilZipStreamWrapper

As you can see, the "EvilZipStreamWrapper" is getting an invalid seek value. And to be honest, looking at the code, I'm not quite sure how you'd fix it, or what purpose it serves in the first place.

Another py3 version

Hi,

I started to create a py3 version of woof a couple of months ago, and now I noticed that you already pushed it here.

It's practically the same, but:

  • I got rid of the evil zip wrapper (it is no longer needed)
  • I added some tests
  • Some other pyton3 things
  • Added doctype to the HTML

Just wanted to put this on your radar. I auropep8ed my code, so below a diff between our versions.

My code is at https://github.com/beardhatcode/python3-woof

If you are interested, I can make a PR for the tests and/or the removal of the evilZipWrapper (or you can just steal it).

1c1
< #!/usr/bin/env python3
---
> #!/usr/bin/env python
27a28
> # Python 3 support by Robbert Gurdeep Singh <[email protected]>
34d34
< import subprocess
37c37
< import urllib.request
---
> import urllib
54,103d53
< 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)
< 
< 
164d113
<         ctype, pdict = cgi.parse_header(self.headers['Content-Type'])
170c119
<         if "upfile" not in form:
---
>         if not "upfile" in form:
191,192c140,141
<                 destfile = os.open(destfilename, os.O_WRONLY |
<                                    os.O_CREAT | os.O_EXCL, 0o644)
---
>                 destfile = os.open(destfilename, os.O_WRONLY | os.O_CREAT |
>                                    os.O_EXCL, 0o644)
204,205c153,154
<         print("accepting uploaded file: %s -> %s" %
<               (upfilename, destfilename), file=sys.stderr)
---
>         print(
>             f"accepting uploaded file: {upfilename} -> {destfilename}", file=sys.stderr)
212c161,162
<         txt = b"""\
---
>         txt = """\
>               <!DOCTYPE html>
225c175
<         self.wfile.write(txt)
---
>         self.wfile.write(txt.encode("utf-8"))
236,247c186,201
<             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>
<                """
---
>             txt = """\
>             <!DOCTYPE html>
>             <html lang="en">
>               <head>
>                 <meta charset="utf-8"/>
>                 <title>Woof Upload</title>
>               </head>
>               <body>
>                 <h1>Woof Upload</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>"""
> 
252c206
<             self.wfile.write(txt)
---
>             self.wfile.write(txt.encode("utf-8"))
271c225,226
<             txt = """\
---
>             txt = f"""\
>                 <!DOCTYPE html>
274,276c229,230
<                    <body>302 Found <a href="%s">here</a>.</body>
<                 </html>\n""" % location
<             txt = txt.encode('ascii')
---
>                    <body>302 Found <a href="{location}">here</a>.</body>
>                 </html>\n"""
282c236
<             self.wfile.write(txt)
---
>             self.wfile.write(txt.encode("utf-8"))
303c257
<                 print("can only serve files or directories. Aborting.",
---
>                 print("can only serve files or directories.  Aborting.",
309,310c263,265
<             self.send_header("Content-Disposition", "attachment;filename=%s" %
<                              urllib.parse.quote(os.path.basename(self.filename)))
---
>             safe_filename = urllib.parse.quote(os.path.basename(self.filename))
>             self.send_header("Content-Disposition",
>                              f"attachment;filename={safe_filename}")
318,320c273,274
<                     datafile = open(self.filename, "rb")
<                     shutil.copyfileobj(datafile, self.wfile)
<                     datafile.close()
---
>                     with open(self.filename, "rb") as datafile:
>                         shutil.copyfileobj(datafile, self.wfile)
323,335c277,288
<                         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()
---
>                         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 Exception(
>                                             "invalid filename assumptions, please report!")
>                                     zfile.write(
>                                         filename, filename[len(stripoff):])
337,341c290,293
<                         tfile = tarfile.open(mode=('w|' + compressed),
<                                              fileobj=self.wfile)
<                         tfile.add(self.filename,
<                                   arcname=os.path.basename(self.filename))
<                         tfile.close()
---
>                         with tarfile.open(mode=('w|' + compressed),
>                                           fileobj=self.wfile) as tfile:
>                             tfile.add(self.filename,
>                                       arcname=os.path.basename(self.filename))
343a296
>                 print(e.with_traceback())
360,361c313,314
<         print("cannot bind to IP address '%s' port %d" %
<               (ip_addr, port), file=sys.stderr)
---
>         print(f"cannot bind to IP address '{ip_addr}' port {port}",
>               file=sys.stderr)
368,369c321
<             location = "http://%s:%s/%s" % (ip_addr, httpd.server_port,
<                                             urllib.parse.quote(os.path.basename(filename)))
---
>             location = f"http://{ip_addr}:{httpd.server_port}/{urllib.parse.quote(os.path.basename(filename))}"
380c332
<             location = "http://%s:%s/" % (ip_addr, httpd.server_port)
---
>             location = f"http://{ip_addr}:{httpd.server_port}"
382c334
<         print("Now serving on %s" % location)
---
>         print(f"Now serving on {location}")
390,394c342,348
<     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
---
>     print(f"""
>     Usage: {name} [-i <ip_addr>] [-p <port>] [-c <count>] <file>
>            {name} [-i <ip_addr>] [-p <port>] [-c <count>] [-z|-j|-Z|-u] <dir>
>            {name} [-i <ip_addr>] [-p <port>] [-c <count>] -s
>            {name} [-i <ip_addr>] [-p <port>] [-c <count>] -U
> 
>            {name} <url>
396,397d349
<            %s <url>
<    
401,403c353,355
<     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 
---
>     it is gzip compressed. You can specify -z for gzip compression, -j for
>     bzip2 compression, -Z for ZIP compression or -u for no compression (tar).
>     You can configure your default compression method in the configuration
406c358
<     When -s is specified instead of a filename, %s distributes itself.
---
>     When -s is specified instead of a filename, {name} distributes itself.
409,410c361,362
<    
<     defaults: count = %d, port = %d
---
> 
>     defaults: count = {defmaxdown}, port = {defport}
427c379
<    """ % (name, name, name, name, name, name, defmaxdown, defport), file=sys.stderr)
---
>    """, file=sys.stderr)
431c383
<         print(file=sys.stderr)
---
>         print("", file=sys.stderr)
442c394
<     f = urllib.request.urlopen(url)
---
>     f = urllib.urlopen(url)
445c397
<     disp = f_meta["Content-Disposition"]
---
>     disp = f_meta.getheader("Content-Disposition")
507c459
<     print("downloading file: %s -> %s" % (fname, destfilename))
---
>     print(f"downloading file: {fname} -> {destfilename}")
509c461
<     shutil.copyfileobj(f, os.fdopen(destfile, "wb"))
---
>     shutil.copyfileobj(f, os.fdopen(destfile, "w"))
560,561c512,513
<                       "invalid download count: %r. "
<                       "Please specify an integer >= 0." % val)
---
>                       f"invalid download count: {val}. "
>                       "Please specify an integer >= 0.")
571c523
<                       "invalid port number: %r. Please specify an integer" % val)
---
>                       f"invalid port number: {val}. Please specify an integer")
592c544
<             usage(defaultport, defaultmaxdown, "Unknown option: %r" % option)
---
>             usage(defaultport, defaultmaxdown, f"Unknown option: {option}")
602c554
<             if woof_client(filenames[0]) != None:
---
>             if woof_client(filenames[0]) is not None:
612c564
<                   "%s: No such file or directory" % filenames[0])
---
>                   f"{filenames[0]}: No such file or directory")
616c568
<                   "%s: Neither file nor directory" % filenames[0])
---
>                   f"{filenames[0]}: Neither file nor directory")
633c585
<         print()
---
>         print("")

Allow intermixing of options and arguments

One of the things that's always annoyed me with woof is that it requires all of the options before the arguments.

Example:

$ woof /tmp/Screenshot_2022-01-13_11-12-40.png 
cannot bind to IP address '' port 8080
$ woof /tmp/Screenshot_2022-01-13_11-12-40.png -p 8000

    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

           woof <url> ... [Snip]

But this is easily fixed by just calling gnu_getopt() instead of getopt()

$ git diff
diff --git a/woof b/woof
index 74784af..414deca 100755
--- a/woof
+++ b/woof
@@ -483,7 +483,7 @@ def main ():
    defaultmaxdown = maxdown
 
    try:
-      options, filenames = getopt.getopt (sys.argv[1:], "hUszjZui:c:p:")
+      options, filenames = getopt.gnu_getopt (sys.argv[1:], "hUszjZui:c:p:")
    except getopt.GetoptError as desc:
       usage (defaultport, defaultmaxdown, desc)

$ ./woof /tmp/Screenshot_2022-01-13_11-12-40.png 
cannot bind to IP address '' port 8080
$ ./woof /tmp/Screenshot_2022-01-13_11-12-40.png -p 8000
Now serving on http://10.0.1.104:8000/Screenshot_2022-01-13_11-12-40.png

What is this project licensed under?

I would like to use a few parts of your code in a project of mine, and I was wondering what license this project is under. I don't see a license file, so I thought I'd ask.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.