Better error handling.

On uploading an invalid file, abort immediately and delete the blob.
If a python exception occurs, print it directly to the http client (which is
a bit lame, but much better than just silently being weird about it).
diff --git a/app.py b/app.py
index 6711be5..1468371 100644
--- a/app.py
+++ b/app.py
@@ -95,6 +95,12 @@
     upload_files = self.get_uploads()
     sys.stderr.write('upload: %r\n' % upload_files)
     blob_info = upload_files[0]
+    reader = blob_info.open()
+    try:
+      wifipacket.Packetize(reader).next()  # just check basic file header
+    except wifipacket.Error:
+      blob_info.delete()
+      raise
     self.redirect('/view/%s' % blob_info.key())
 
 
@@ -122,13 +128,8 @@
                                       filename=blob_info.filename,
                                       show_hosts=[], aliases={},
                                       show_fields=[])
-    try:
-      boxes = _Boxes(blob_info)
-    except ValueError as e:
-      self.response.set_status(500, 'Server error')
-      self.response.write('<pre>%s</pre>' % traceback.format_exc())
-      return
 
+    boxes = _Boxes(blob_info)
     cutoff = max(boxes.itervalues()) * 0.01
     cutboxes = [(b, n)
                 for b, n
@@ -236,6 +237,14 @@
       j = '%s(%s)' % (self.request.get('jsonp'), j)
     self.response.write(j)
 
+
+def Handle500(req, resp, exc):
+  resp.clear()
+  resp.headers['Content-type'] = 'text/plain'
+  resp.write(traceback.format_exc(exc))
+  resp.set_status(500)
+
+
 settings = dict(
     debug = 1,
 )
@@ -243,7 +252,10 @@
 wsgi_app = webapp2.WSGIApplication([
     (r'/', MainHandler),
     (r'/upload', UploadHandler),
+    (r'/download/([^/]+)$', DownloadHandler),
     (r'/view/([^/]+)$', ViewHandler),
     (r'/save/([^/]+)$', SaveHandler),
     (r'/json/([^/]+)$', JsonHandler),
 ], **settings)
+
+wsgi_app.error_handlers[500] = Handle500
diff --git a/wifipacket.py b/wifipacket.py
index 240ea13..08f9989 100755
--- a/wifipacket.py
+++ b/wifipacket.py
@@ -9,6 +9,18 @@
 import sys
 
 
+class Error(Exception):
+  pass
+
+
+class FileError(Error):
+  pass
+
+
+class PacketError(Error):
+  pass
+
+
 class Struct(dict):
   """Helper to allow accessing dict members using this.that notation."""
 
@@ -219,7 +231,7 @@
   elif struct.unpack('>I', magicbytes) == (TCPDUMP_MAGIC,):
     byteorder = '>'
   else:
-    raise ValueError('unexpected tcpdump magic %r' % magicbytes)
+    raise FileError('unexpected tcpdump magic %r' % magicbytes)
   (version_major, version_minor,
    unused_thiszone,
    unused_sigfigs,
@@ -227,9 +239,9 @@
    network) = struct.unpack(byteorder + 'HHiIII', stream.read(20))
   version = (version_major, version_minor)
   if version != TCPDUMP_VERSION:
-    raise ValueError('unexpected tcpdump version %r' % version)
+    raise FileError('unexpected tcpdump version %r' % version)
   if network != LINKTYPE_IEEE802_11_RADIOTAP:
-    raise ValueError('unexpected tcpdump network type %r' % network)
+    raise FileError('unexpected tcpdump network type %r' % network)
 
   last_ta = None
   last_ra = None
@@ -242,11 +254,11 @@
     (ts_sec, ts_usec,
      incl_len, orig_len) = struct.unpack(byteorder + 'IIII', pcaphdr)
     if incl_len > orig_len:
-      raise ValueError('packet incl_len(%d) > orig_len(%d): invalid'
-                       % (incl_len, orig_len))
+      raise FileError('packet incl_len(%d) > orig_len(%d): invalid'
+                      % (incl_len, orig_len))
     if incl_len > snaplen:
-      raise ValueError('packet incl_len(%d) > snaplen(%d): invalid'
-                       % (incl_len, snaplen))
+      raise FileError('packet incl_len(%d) > snaplen(%d): invalid'
+                      % (incl_len, snaplen))
 
     opt.pcap_secs = ts_sec + (ts_usec / 1e6)
 
@@ -261,7 +273,7 @@
     (it_version, unused_it_pad,
      it_len, it_present) = struct.unpack('<BBHI', radiotap[:8])
     if it_version != 0:
-      raise ValueError('unknown radiotap version %d' % it_version)
+      raise PacketError('unknown radiotap version %d' % it_version)
     frame = radiotap[it_len:]
     optbytes = radiotap[8:it_len]