#!/usr/bin/env python
#
#Copyright (c) 2009, Perforce Software, Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#*******************************************************************************
# Author: Stephen Moon
# Date: 3/5/09
# Last Modified Date: 7/13/09
#
# Summary: Retrieves Perforce binaries or ASCII files from ftp.perforce.com
#
# Usage:
#
# Unix/Linux:
# Invoke it from the command line (make sure that Python 2.4 or higher
# release version is installed although it should work with older versions
#
# Windows:
# Need to have Python 2.4 or higher release version installed
# Remove the She-bang line and invoke it by typing "python fetch.py"
# at the command prompt.
#
#*******************************************************************************
import ftplib, re, os, sys, getpass
HOST = 'ftp.perforce.com'
DIR_NAME = 'perforce'
#holds each displayed line entry
class DirEntry(object): #inherits from class object
def __init__(self,line): #only accept one argument line
self.parts = line.split(None,8) #split into 8 parts for each line
def isValid(self):
return len(self.parts) >= 6 #if num of parts greater than 6 then it's valid
def getType(self):
return self.parts[0][0] #get the very first character on the line (i.e.
def getFilename(self):
if self.getType() != 'l': #if it is not a link
return self.parts[-1] #return the last part
else:
return self.parts[-1].split(' -> ', 1)[0] #if it is, return shortcut
def getSize(self):
if self.getType() == '-':
return int(self.parts[4])/1024
def getMonth(self):
if self.getType() == '-':
return self.parts[5]
def getDay(self):
if self.getType() == '-':
return self.parts[6]
def getLinkDest(self): #return the original destination of link
if self.getType() == 'l':
return self.parts[-1].split(' -> ', 1)[1]
else:
raise RuntimeError, "getLinkDest() called on non-link item"
#creates a key (fname) and value (line for the filename)
class DirScanner(dict): #inherits from class dict
def addline(self,line): #only accepts one arugment line
obj = DirEntry(line)
if obj.isValid(): #line object contains more than 6 parts
self[obj.getFilename()] = obj #creates a key/value pair, key = fname
#downloads a file
def downloadFile(ftpobj, filename):
ftpobj.voidcmd("Type I")
#creating of a set of 0...100 incremented by 10
s = set([])
for i in range(0,100,10):
s.add(i)
#based on the filename, it returns a tuple of the data connection and the expected
#size of data
datasock, estimatedSize = ftpobj.ntransfercmd("RETR %s" % filename)
transbytes = 0
fd = file(filename, 'wb')
while True:
buf = datasock.recv(2048)
if not len(buf):
break;
fd.write(buf)
transbytes += len(buf)
percent = 100.0*float(transbytes/float(estimatedSize))
if (int(percent) % 10) == 0 and int(percent) in s:
s.remove(int(percent)) #remove the percent from the set
sys.stdout.write("%s: Received %d " % (filename, transbytes))
if estimatedSize:
sys.stdout.write("of %d bytes (%0.1f%%)\n" % (estimatedSize,
100.0 * float(transbytes) / float(estimatedSize)))
else:
sys.stdout.write("bytes")
fd.close()
datasock.close()
ftpobj.voidresp()
sys.stdout.write("\n")
#log in anonymously to ftp.perforce.com and then change directory to perforce
def initialize():
try:
ftp = ftplib.FTP(HOST)
except (socket.error, socket.gaierror), e:
print 'Error: cannot reach "%s"' % HOST
return
print '\n*** Connected to host "%s" ***' % HOST
while True:
print '\nPlease log in with "anonymous" as a login name \
\nand type your email address as your password\n'
username = raw_input("login: ")
password = getpass.getpass("password: ")
try:
if ftp.login(username,password):
break
except ftplib.error_perm:
print 'Error: wrong login and/or password'
print 'Try again'
try:
ftp.cwd(DIR_NAME)
except ftplib.error_perm:
print '\nError: cannot change directory to "%s"' % DIR_NAME
print 'Changed the directory to a default directory'
return ftp
#displays the entries for a chosen directory
def displayFiles(ftp):
data = {}
data = DirScanner() #create data object of class DirScanner
ftp.dir(data.addline) #add lines to dict when dir is run
f = '[FILE]'
d = '[DIR]'
keys = data.keys() #get the keys
keys.sort() #sort it
#print it to the sorted filenames
print "\nCurrent Working Directory: ",ftp.pwd(),"\n"
for i,eachEntry in enumerate(keys):
if data[eachEntry].getType() == '-': #DirEntry object using fileName key
print '%2d:%6s %5sK %-3s %2s %s' % (i,f,data[eachEntry].getSize(),
data[eachEntry].getMonth(),data[eachEntry].getDay(),eachEntry)
elif data[eachEntry].getType() == 'd': #same as above but directory
print '%2d:%6s %s' % (i,d,eachEntry)
#print '('+str(i)+'):\t [DIR] ',eachEntry
print '\n%2s: go up one directory' % 'u'
print '%2s: exit\n' % 'e'
return data
#main starts here
def main():
ftp = initialize()
data = displayFiles(ftp)
choice = raw_input("Choose a directory or file from the above choices: ")
while True:
try:
if choice is 'u':
ftp.cwd("..")
elif choice is 'e':
break;
elif choice.isalpha():
print "\n*** You have entered alpha character other than given choices ***"
elif(int(choice) <= len(data) - 1):
keys = data.keys()
keys.sort() # filenames sorted
for i, fileName in enumerate(keys):
if i == int(choice):
if data[fileName].getType() == '-': #DirEntry object using fileName key
try:
downloadFile(ftp,fileName)
except ftplib.error_perm:
print 'Error: cannot download file "%s"' % fileName
os.unlink(fileName)
else:
print '*** Downloaded "%s" to current local directory ***' % fileName
elif data[fileName].getType() == 'd': #same as above but directory
try:
ftp.cwd(fileName)
except ftplib.error_perm:
print 'Error: cannot cd to "%s"' % fileName
return
else:
print "\n*** You have made an invalid choice ***"
except ValueError:
print "\n*** You have entered alphanumeric character other than given choices ***"
data = displayFiles(ftp)
choice = raw_input("Choose a directory or file from the above choices: ")
ftplib.FTP(HOST).quit()
if __name__ == '__main__':
main()