aurweb/tupkg/client/tupkg

215 lines
5.2 KiB
Python
Executable file

#!/usr/bin/python -O
#
# Description:
# ------------
# This is the client-side portion of the Trusted User package
# manager. The TUs will use this program to upload packages into
# the AUR. For more information, see the ../README.txt file.
#
# Python Indentation:
# -------------------
# For a vim: line to be effective, it must be at the end of the
# file. See the end of the file for more information.
#
import sys
import socket
import os
import struct
import os.path
import cgi
import urllib
import md5
import getopt
import ConfigParser
class ClientFile:
def __init__(self, pathname):
self.pathname = pathname
self.filename = os.path.basename(pathname)
self.fd = open(pathname, "rb")
self.fd.seek(0, 2)
self.size = self.fd.tell()
self.fd.seek(0)
self.makeMd5()
def makeMd5(self):
md5sum = md5.new()
while self.fd.tell() != self.size:
md5sum.update(self.fd.read(1024))
self.md5 = md5sum.hexdigest()
class ClientSocket:
def __init__(self, files, host, port, username, password):
self.files = files
self.host = host
self.port = port
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.username = username
self.password = password
def connect(self):
self.socket.connect((self.host, self.port))
def reliableRead(self, size):
totalread = ""
while len(totalread) < size:
read = self.socket.recv(size-len(totalread))
if read == 0:
raise RuntimeError, "socket connection broken"
totalread += read
return totalread
def sendMsg(self, msg):
if type(msg) == dict:
msg = urllib.urlencode(msg,1)
length = struct.pack("H", socket.htons(len(msg)))
self.socket.sendall(length)
self.socket.sendall(msg)
def readMsg(self, format=0):
initsize = self.reliableRead(2)
(length,) = struct.unpack("H", initsize)
length = socket.ntohs(length)
data = self.reliableRead(length)
if format == 1:
qs = cgi.parse_qs(data)
return qs
else:
return data
def close(self):
self.socket.close()
def auth(self):
msg = {'username': self.username, 'password': self.password}
self.sendMsg(msg)
reply = self.readMsg(1)
if reply['result'] == ["PASS"]:
return 1
elif reply['result'] == ["SQLERR"]:
print "SQL server-side error"
return 0
else:
return 0
def sendFileMeta(self):
msg = {'numpkgs': len(self.files)}
for i, v in enumerate(self.files):
msg['name'+str(i)] = v.filename
msg['size'+str(i)] = v.size
msg['md5sum'+str(i)] = v.md5
self.sendMsg(msg)
reply = self.readMsg(1)
print reply
for i in reply:
if i[:4] == 'size':
self.files[int(i[4:])].cur_done = int(reply[i][0])
def sendFiles(self):
for i in self.files:
i.fd.seek(i.cur_done)
print "Uploading:", i.filename, str(i.size/1024), "kb"
sdone = 0
while i.fd.tell() < i.size:
sdone+=1
self.socket.sendall(i.fd.read(1024))
if sdone % 100 == 0:
print "\r",
print str(sdone), "of", str(i.size/1024), "kb",
sys.stdout.flush()
reply = self.readMsg(1)
print reply
self.sendMsg("ack")
def usage():
print "usage: tupkg [options] <package file>"
print "options:"
print " -u, --user Connect with username"
print " -P, --password Connect with password"
print " -h, --host Connect to host"
print " -p, --port Connect to host on port (default 1034)"
print "May also use conf file: ~/.tupkg"
def main(argv=None):
if argv is None:
argv = sys.argv
confdict = {}
conffile = os.path.join(os.getenv("HOME"),".tupkg") #try the standard location
#Set variables from file now, may be overridden on command line
if os.path.isfile(conffile):
config = ConfigParser.ConfigParser()
config.read(conffile)
confdict['user'] = config.get('tupkg','username')
confdict['password'] = config.get('tupkg','password')
try:
confdict['host'] = config.get('tupkg','host')
except:
confdict['host'] = 'aur.archlinux.org'
try:
confdict['port'] = config.getint('tupkg','port')
except:
confdict['port'] = 1034
else:
confdict['user'] = ""
confdict['password'] = ""
confdict['host'] = 'aur.archlinux.org'
confdict['port'] = 1034
if len(argv) == 1: #no config file and no args, bail
usage()
return 1
try:
optlist, args = getopt.getopt(argv[1:], "u:P:h:p:", ["user=", "password=", "host=", "port="])
except getopt.GetoptError:
usage()
return 1
for i, k in optlist:
if i in ('-u', '--user'):
confdict['user'] = k
if i in ('-P', '--password'):
confdict['password'] = k
if i in ('-h', '--host'):
confdict['host'] = k
if i in ('-p', '--port'):
confdict['port'] = int(k)
files = []
for i in args:
try:
files.append(ClientFile(i))
except IOError, err:
print "Error: " + err.strerror + ": '" + err.filename + "'"
usage()
return 1
cs = ClientSocket(files, confdict['host'], confdict['port'], confdict['user'], confdict['password'])
try:
cs.connect()
if not cs.auth():
print "Error authenticating you, you bastard"
return 1
cs.sendFileMeta()
cs.sendFiles()
cs.close()
except KeyboardInterrupt:
print "Cancelling"
cs.close()
return 0
if __name__ == "__main__":
sys.exit(main())
# Python Indentation:
# -------------------
# Use tabs not spaces. If you use vim, the following comment will
# configure it to use tabs.
#
# vim:noet:ts=2 sw=2 ft=python