1 #! /usr/bin/env python
  2 
  3 from ConfigParser import ConfigParser
  4 from tempfile import TemporaryFile
  5 from optparse import OptionParser
  6 from struct import pack, unpack
  7 import tarfile
  8 import ssl
  9 import socket
 10 import os
 11 import glob
 12 import re
 13 from getpass import getpass
 14 from tempfile import NamedTemporaryFile
 15 from validate import validate
 16 
 17 from exception import MusaException
 18 from license import package_license
 19 from misc import package_misc, parse_file_args
 20 from type import package_type
 21 from text import package_text
 22 
 23 def main():
 24     cmd_parser = OptionParser(usage="%prog -c <config> [-dhqv] [-x <regexp>] [-u username] [-p password] <files>", version="%prog 0.0")
 25     cmd_parser.add_option("-r", "--recursive", dest="recursive", action="store_true", default=False, help="search for files recursively")
 26     cmd_parser.add_option("-v", "--verbose",   dest="verbose",   action="store_true", default=False, help="print extra debug information")
 27     cmd_parser.add_option("-c", "--config",    dest="config",    metavar="FILE",                     help="the configuration file")
 28     cmd_parser.add_option("-x", "--exclude",   dest="exclude",   action="append",                    help="regular expressions of files to not include")
 29     cmd_parser.add_option("-d", "--dryrun",    dest="dryrun",    action="store_true", default=False, help="perform a dry run where all tests, including remote tests, are performed but the content is not uploaded into the content system")
 30     cmd_parser.add_option("-u", "--username",  dest="username",  metavar="USERNAME",                 help="the username for uploading content")
 31     cmd_parser.add_option("-p", "--password",  dest="password",  metavar="PASSWORD",                 help="the password for uploading content")
 32 
 33     (options, args) = cmd_parser.parse_args()
 34 
 35     if options.config is None:
 36         cmd_parser.error("you must pass a configuration file")
 37 
 38     if options.verbose:
 39         print "reading configuration file %s..." % options.config
 40 
 41     ini_parser = ConfigParser()
 42     if len(ini_parser.read([ options.config ])) != 1:
 43         cmd_parser.error("could not read the configuration file")
 44 
 45     if not ini_parser.has_section("musa"):
 46         cmd_parser.error("configuration file missed musa section")
 47 
 48     try:
 49         excluders = [ re.compile("(\.svn|\.hg|\.git)") ]
 50         if options.exclude is not None:
 51             for exclude in options.exclude:
 52                 excluders.append(re.compile(exclude))
 53     except:
 54         cmd_parser.error("regular expression is invalid")
 55 
 56     tar = None
 57     tar_file = None
 58     sock = None
 59     try:
 60         if options.verbose: print "creating temporary tarball..."
 61         # In Windows the temporary file will get removed when the tar
 62         # is closed the first time, if delete is not set to False.
 63         tar_file = NamedTemporaryFile(dir=".", delete=False)
 64         try:
 65             tar = tarfile.open(tar_file.name, mode='w:gz')
 66             metadata = {}
 67 
 68             if options.verbose: print "packaging name/version information..."
 69             metadata.update(package_misc(ini_parser))
 70 
 71             if options.verbose: print "packaging license information..."
 72             metadata.update(package_license(ini_parser, tar, metadata['safe_name']))
 73 
 74             if options.verbose: print "packaging type information..."
 75             package_files = parse_file_args(args, excluders)
 76             if options.verbose:
 77                 print "the following files will be added:"
 78                 for file in package_files:
 79                     print "  - %s" % file
 80 
 81             if options.verbose: print "packaging text information..."
 82             metadata.update(package_text(ini_parser, tar, metadata['safe_name'], package_files))
 83 
 84             print "packaging files... (might take a while)"
 85             metadata.update(package_type(ini_parser, tar, metadata['safe_name'], package_files))
 86 
 87             if len(package_files) != 0:
 88                 print "the following files remained unpackaged:"
 89                 for pf in package_files:
 90                     print " - %s" % pf
 91                 raise MusaException("unpackagable files in directory")
 92 
 93             if options.verbose: print "validating files locally... "
 94             tar.close()
 95 
 96             tar = tarfile.open(tar_file.name, mode='r')
 97 
 98             validate(metadata, tar, options.verbose)
 99             if options.verbose: print "temporary tarball validated..."
100             tar.close()
101         except MusaException, inst:
102             print inst.args[0]
103             return
104         finally:
105             if tar != None and not tar.closed:
106                 tar.close()
107 
108         def send_data(sock, data, binary=False):
109             if binary:
110                 sock.send(pack('!H', len(data)))
111                 if len(data) != 0:
112                     sock.send(data)
113             else:
114                 repr_data = repr(data)
115                 sock.send(pack('!H', len(repr_data)))
116                 sock.send(repr_data)
117 
118         def send_file(sock, tar_file):
119             amount = 0
120             size = os.stat(tar_file.name).st_size
121 
122             fd = open(tar_file.name, "rb", 51200)
123             try:
124                 while True:
125                     data = fd.read(51200)
126                     if data is None or len(data) == 0:
127                         break
128 
129                     send_data(sock, data, True)
130                     amount += len(data)
131                     print "\ruploaded %d bytes of %d bytes (%d%%)" % (amount, size, (amount * 100 / size)),
132 
133                 send_data(sock, "", True)
134             finally:
135                 fd.close()
136             print ""
137 
138         if options.username is None:
139             options.username = raw_input("Please enter your username: ")
140         if options.password is None:
141             options.password = getpass("Please enter your password: ")
142         check = raw_input("are you one of the authors of this content, if so answer 'yes I am': ")
143 
144         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
145         sock.connect(("content.openttd.org", 3980))
146         sock = ssl.wrap_socket(sock, server_side=False, ssl_version=ssl.PROTOCOL_TLSv1)
147 
148         send_data(sock, "0.0.0")
149         send_data(sock, { 'username': options.username, 'password': options.password, 'check': check })
150 
151         if options.verbose: print "validating metadata at server..."
152         send_data(sock, metadata)
153         data = sock.recv(8192)
154         print data
155 
156         if data.startswith("error"):
157             print "an error occurred and the content is not uploaded"
158         elif not options.dryrun:
159             send_file(sock, tar_file)
160 
161             if options.verbose: print "validating files at server..."
162             if options.verbose: print "waiting for acknowledgement..."
163             print sock.recv(8192)
164         else:
165             send_data(sock, "", True)
166             print "not uploading tarball due to dry run"
167 
168     finally:
169         if sock != None:
170             sock.close()
171         if tar_file != None:
172             tar_file.close()
173             os.remove(tar_file.name)
174 
175 main()