1#!/usr/bin/python
2# Copyright 2014 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Utility to package and upload the USB gadget framework.
7"""
8
9import argparse
10import hashlib
11import os
12import StringIO
13import urllib2
14import zipfile
15
16
17def MakeZip(directory=None, files=None):
18  """Construct a zip file.
19
20  Args:
21    directory: Include Python source files from this directory
22    files: Include these files
23
24  Returns:
25    A tuple of the buffer containing the zip file and its MD5 hash.
26  """
27  buf = StringIO.StringIO()
28  archive = zipfile.PyZipFile(buf, 'w')
29  if directory is not None:
30    archive.writepy(directory)
31  if files is not None:
32    for f in files:
33      archive.write(f, os.path.basename(f))
34  archive.close()
35  content = buf.getvalue()
36  buf.close()
37  md5 = hashlib.md5(content).hexdigest()
38  return content, md5
39
40
41def EncodeBody(filename, buf):
42  return '\r\n'.join([
43      '--foo',
44      'Content-Disposition: form-data; name="file"; filename="{}"'
45      .format(filename),
46      'Content-Type: application/octet-stream',
47      '',
48      buf,
49      '--foo--',
50      ''
51  ])
52
53
54def UploadZip(content, md5, host):
55  filename = 'usb_gadget-{}.zip'.format(md5)
56  req = urllib2.Request(url='http://{}/update'.format(host),
57                        data=EncodeBody(filename, content))
58  req.add_header('Content-Type', 'multipart/form-data; boundary=foo')
59  urllib2.urlopen(req)
60
61
62def main():
63  parser = argparse.ArgumentParser(
64      description='Package (and upload) the USB gadget framework.')
65  parser.add_argument(
66      '--dir', type=str, metavar='DIR',
67      help='package all Python files from DIR')
68  parser.add_argument(
69      '--zip-file', type=str, metavar='FILE',
70      help='save package as FILE')
71  parser.add_argument(
72      '--hash-file', type=str, metavar='FILE',
73      help='save package hash as FILE')
74  parser.add_argument(
75      '--upload', type=str, metavar='HOST[:PORT]',
76      help='upload package to target system')
77  parser.add_argument(
78      'files', metavar='FILE', type=str, nargs='*',
79      help='source files')
80
81  args = parser.parse_args()
82
83  content, md5 = MakeZip(directory=args.dir, files=args.files)
84  if args.zip_file:
85    with open(args.zip_file, 'wb') as zip_file:
86      zip_file.write(content)
87  if args.hash_file:
88    with open(args.hash_file, 'wb') as hash_file:
89      hash_file.write(md5)
90  if args.upload:
91    UploadZip(content, md5, args.upload)
92
93
94if __name__ == '__main__':
95  main()
96