#!/usr/bin/python # Stephen Skory # stephenskory@yahoo.com # Part of a hack to make Gallery2 work with Amazon's S3 service # uncomment this below if you want to do some debugging. #print "Content-Type: text/plain" #print # User edited values AWSBase = 'http://bucketname.s3.amazonaws.com/' # yes trailing slash albumsAWSBase = AWSBase + 'albums/' # yes trailing slash derivativeAWSBase = AWSBase + 'derivative/' # yes trailing slash s3fsBase = '/path/to/bucket/' # yes trailing slash galleryBase = '/gallery2/' # your web location for gallery2, minus your domain name # if your gallery is at http://mysite.com/gallery2/main.php # put /gallery2/ here, with the trailing slash table_prefix = 'g2_' column_prefix = 'g_' refreshTime = '180' # days myHost = 'localhost' # 99% of the time myUser = 'database_username' myPasswd = 'database_password' myDB = 'database_name' # --- EDIT ABOVE --- # # get the needed modules import cgi, time, sys, re, MySQLdb, urllib2 #import cgitb; cgitb.enable() # for debugging from path import path # for 'touch'ing files, requries path.py: http://www.jorendorff.com/articles/python/path/ # if the image is a full sized image, get the pieces of its pathname def fullSizedImage(numbers): path = [] # loop over the numbers in its location for number in numbers: if (number != '7'): # 7 is the top album db.query("""SELECT %spathComponent from %sFileSystemEntity where g_id=%s""" \ % (column_prefix,table_prefix,number)) r = db.store_result() # store the results in path path.append(r.fetch_row()[0][0]) # get the file name db.query("""SELECT %spathComponent from %sFileSystemEntity where g_id=%s""" \ % (column_prefix,table_prefix,line)) r = db.store_result() for row in r.fetch_row(1): path.append(row[0]) # make the path to the full S3 version string = '' string = "/".join("%s" % part for part in path) string = albumsAWSBase + string return string # if the id is for a reduced size image, it's easy to make the S3 location def reducedSizeImage(line): if (int(line) <= 100): # the id=100 is a weird case I'm not 100% on... string = derivativeAWSBase + '0/%s/' % line[0] string = string + line.strip() + '.dat' else: string = derivativeAWSBase + '%s/%s/' % (line[0], line[1]) string = string + line.strip() + '.dat' return string # we call this after we've formed a S3 location to see if it's there already. # if it is and it's new enough, we return 0 and preserve the S3 version # otherwise we return 1 and will create a new local location def chooseRefreshedVersion(Location): # try to get the file off of S3 req = urllib2.Request(Location) try: url_handle = urllib2.urlopen(req) # if it's not there, return 1 except urllib2.HTTPError: return 1 # otherwise, get the file headers headers = url_handle.info() # get the date of last change for the S3 version last_modified = headers.getheader("Last-Modified") time_mod = time.mktime(time.strptime(last_modified, "%a, %d %b %Y %H:%M:%S %Z")) # now time_now = time.mktime(time.localtime()) # calculate the difference timeDiff = time_now - time_mod # calculate the refresh time in seconds rTimeSecs = 60*60*24*refreshTime # if the S3 file is too old, return 1 # but also we'll 'touch' it to 'refresh' it. If it needs regeneration, Gallery will do it # because we're going through the 'official' channel, so an extra touch won't hurt it if (timeDiff > rTimeSecs): # take off the s3 address part localFileName = sb.subn('',Location)[0] # add the local stuff localFileName = s3fsBase + localFileName # create f under path framework f = path(localFileName) # touch it f.touch() return 1 # if the S3 version is not too old, or if it exists on S3, return 0 return 0 # Establish a database connection # yeah, yeah, it's database specific. You get what you pay for! db = MySQLdb.connection(host=myHost, user=myUser, passwd=myPasswd, db=myDB) # Make some regular expressions # to find the numbers from parentSequence p = re.compile('\d+') # to strip off stuff that's not a number D = re.compile('\D') # for updating refresh time sb = re.compile(AWSBase) # get the values from the request inputValue = cgi.FieldStorage() # get the ID if(inputValue.has_key('id')): id = inputValue['id'].value line = inputValue['id'].value # get the serial number if(inputValue.has_key('sn')): sn = inputValue['sn'].value # get the file name if(inputValue.has_key('fn')): fn = inputValue['fn'].value # clean the line of anything but digits, i.e. SQL injection queries line = D.subn('',line)[0] # if line is too long, or there's nothing, let's just exit 'cause something's wrong # 20 is an arbitrary number if (len(line) > 20 or len(line) == 0): sys.exit() # Make the SQL query db.query("""SELECT %sparentSequence FROM %sItemAttributesMap WHERE g_itemId=%s""" \ % (column_prefix,table_prefix,line)) r = db.store_result() string = '' # if the id queried is in ItemAttributesMap, it's a full sized image, # on error (as in nothing returned), we know it's a resized version, and make the path try: numbers = p.findall(r.fetch_row()[0][0]) except IndexError: string = reducedSizeImage(line) # here, if we haven't filled 'string' it's a full sized image if (len(string) == 0): string = fullSizedImage(numbers) # after making the S3 version, we test to see if it's fresh or not, or even on S3 if (chooseRefreshedVersion(string)): string = '%smain.php?g2_view=core.DownloadItem&g2_itemId=%s&g2_serialNumber=%s&g2_fileName=%s' \ % (galleryBase,id,sn,fn) # after all that, we forward the client to the correct address print 'Location: %s' % string print