class UpdateEngine(object): NORMAL_UPDATE = 0 FORCED_UPDATE = 1 CUSTOM_UPDATE = 2 def __init__(self, database="old.db"): self.new_db = None self.old_db = DatabaseReader(None, database) self.locked = False #self.startThread(self.dumpDB, "/~stack/pyupdate/pyacqua.db") #self.startThread(self.listInfo, "/~stack/pyupdate/index.xml") def reportError(self, data, response, args, where): """Ovverride this method""" self.lockInterface(False) def lockInterface(self, lock=True): """Ovverride this method""" pass def fetchComponents(self, callback): """ callback: for i in report.programs: print "-> %s" % i self.startThread(self.dumpXml, "/~stack/pyupdate/%s-update.xml" % i, i) """ self.startThread(self.__listInfo, "/~stack/pyupdate/index.xml", callback) def startThread(self, callback, url, args=None): print _(">> Creo un thread per %s") % url f = Fetcher(callback, url, args) f.setDaemon(True) f.start() def __listInfo(self, data, response, args): if data == None or response.status != 200: return self.reportError(data, response, args, STEP_INDEX) args(Indexer(data).programs) def getVersion(self, schema): a = (schema.get("mainversion"), schema.get("secondversion"), schema.get("revision")) return "-r".join((".".join(map(str,a[:2])), str(a[2]))) def dumpXml(self, data, response, args): new_schema = ReportReader(data) old_schema = ReportReader(None, os.path.join(utils.DHOME_DIR, "%s.xml" % args)) ret = old_schema.checkDiff(new_schema) if ret == 0: print "NO_UPDATE" elif ret == 1: print "UPDATE" elif ret == 2: print "UNSECURE_UPDATE" elif ret == 3: print "IMPOSSIBLE" print "LOCAL version: %s" % self.getVersion(old_schema) print "REMOTE version: %s" % self.getVersion(new_schema) self.updateProgram(args) def __updateFirstPass(self, program, u_type, new_pid, old_pid): # @update standard@ - second pass - soggetto new_db # Controlliamo l'esistenza della stessa dir # -> se nn c'e' aggiungiamo tutti i file # Se esiste # -> controlliamo la revision (se la nuova e' maggiore altrimenti ignore (custom update precedente)) # -> for sui file # controlla revision (se la nuova e' maggiore scarica | se minore nulla | se uguale controlla consistenza in caso riscarica) # @update forzato@ clone (update standard) # Controlliamo l'esistenza della stessa dir # -> se nn c'e' aggiungiamo tutti i file # Se esiste # -> controlliamo la revision (se uguale ignore) # -> for sui file # controlla revision (se diversa riscarica | se uguale controlla consistenza in caso riscarica) for dir_entry in self.new_db.select("SELECT * FROM directory WHERE program_id=%d" % self.new_db.sanitize(new_pid)): # dir_entry => (idx, name, revision, filenum, pid) new_did, new_dname, new_drev, new_dfilenum, new_pid = dir_entry print "[1] Checking %s" % new_dname old_did, old_drev = self.old_db.getDirRevision(new_dname, old_pid) if u_type == Update.FORCED_UPDATE: if old_drev == new_drev: continue else: # TODO: add custom # Esiste e la revision e' maggiore o uguale if old_drev >= new_drev: print "[1] Skipping %d >= %d" % (old_drev, new_drev) continue # -> for sui file # controlla revision (se la nuova e' maggiore scarica | se minore nulla | se uguale controlla consistenza in caso riscarica) for file_entry in self.new_db.select("SELECT * FROM file WHERE program_id=%d AND directory_id=%d" % self.new_db.sanitize((new_pid, new_did))): # file_entry => (idx, name, revision, bytes, md5, did, pid) new_fid, new_fname, new_frev, new_fbytes, new_fmd5, new_did, new_pid = file_entry old_fid, old_frev = self.old_db.getFileRevision(new_fname, old_did, old_pid) if u_type == Update.FORCED_UPDATE: if old_frev == new_frev and self.old_db.checkConsistence(old_fid, new_frev, new_fbytes, new_fmd5): continue # il file e' ok elif old_frev != new_frev: self.downloadFile(old_fid, new_fname, new_frev, new_fbytes, new_fmd5) else: # TODO: add custom if (old_frev == new_frev and not self.old_db.checkConsistence(old_fid, new_frev, new_fbytes, new_fmd5)) or (old_frev < new_frev): self.downloadFile(old_fid, new_fname, new_frev, new_fbytes, new_fmd5) def __updateSecondPass(self, program, u_type, new_pid, old_pid): # @update standard@ - removing - soggetto old_db # Controlliamo se la dir non esiste # -> Rimuoviamo la dir con tutti i file all'interno # se esiste # -> for sui file # se esite controlla md5 e bytes (versione minore | se maggiore non far nulla) # -> se non ok riscarica (o errore di update sul server) # se non esiste # -> elimina file # @update forzato@ - removing - soggetto old_db # Controlliamo se la dir non esiste # -> Rimuoviamo la dir con tutti i file all'interno # se esiste # -> for sui file # se esite controlla md5 e bytes (sempre) # -> se non ok riscarica # se non esiste # -> elimina file for dir_entry in self.old_db.select("SELECT * FROM directory WHERE program_id=%d" % self.old_db.sanitize(old_pid)): old_did, old_dname, old_drev, old_dfilenum, old_pid = dir_entry print "[2] Checking %s" % old_dname new_did, new_drev = self.new_db.getDirRevision(old_dname, new_pid) if new_did == -1 and new_drev == -1: print "[2] Full removing %s (and all contents) ..." % old_dname continue for file_entry in self.old_db.select("SELECT * FROM file WHERE program_id=%d AND directory_id=%d" % self.new_db.sanitize((old_pid, old_did))): # file_entry => (idx, name, revision, bytes, md5, did, pid) old_fid, old_fname, old_frev, old_fbytes, old_fmd5, old_did, old_pid = file_entry new_fid, new_frev = self.new_db.getFileRevision(old_fname, new_did, new_pid) if new_fid == -1 and new_frev == -1: print "[2] Removing file %s ..." % old_fname else: if u_type == Update.FORCED_UPDATE: if not self.new_db.checkConsistence(new_fid, old_frev, old_fbytes, old_fmd5): print "[2] Consistence check failed for file %s." % old_fname # riscarica else: # TODO: add custom if new_frev <= old_frev and not self.new_db.checkConsistence(new_fid, old_frev, old_fbytes, old_fmd5): print "[2] Consistence check failed for file %s." % old_fname def updateProgram(self, program, u_type=NORMAL_UPDATE): if not self.new_db: raise "No db." new_pid = self.new_db.select("SELECT id FROM program WHERE name='%s'" % self.new_db.sanitize(program))[0][0] old_pid = self.old_db.select("SELECT id FROM program WHERE name='%s'" % self.old_db.sanitize(program))[0][0] print "Pid: old -> %d new -> %d" % (old_pid, new_pid) self.__updateFirstPass(program, u_type, new_pid, old_pid) self.__updateSecondPass(program, u_type, new_pid, old_pid) def downloadFile(self, file_id, fname, frev, fbytes, fmd5): self.startThread(self.checkConsistence, "/~stack/pyupdate/source/%s" % fname, (file_id, fname, frev, fbytes, fmd5)) def checkConsistence(self, data, response, args): file_id, fname, frev, fbytes, fmd5 = args print "\t%s -> " % fname, if data == None: print "Error in recv." return if response.status != 200: print "Error in response." return if len(data) == fbytes and self.MD5(data) == fmd5: print "File is ok. merge." self.old_db.execute("UPDATE file SET revision=%d, bytes=%d, md5=\"%s\" WHERE id=%d" % self.sanitize((frev, fbytes, fmd5, file_id))) else: print "Error in file." def MD5(self, data): m = md5.new() m.update(data) return m.hexdigest() def dumpDB(self, data, response, args): f = open("tmp.database", "wb") f.write(data) f.close() self.new_db = DatabaseReader(None, "tmp.database") print "Database downloaded."