# -*- coding: utf-8 -*- import webapp2,cgi,re,cookielib,urllib2,urllib,json,datetime,logging from google.appengine.ext import db from google.appengine.api import users from google.appengine.api import memcache ripe = 'https://apps.db.ripe.net/search/query.html?search%3AdoSearch=Search&searchtext=' class Options(db.Model): admin_username= db.StringProperty() admin_password= db.StringProperty() charset= db.StringProperty(default='utf-8') # modèle d'un message de log class Log(db.Model): id_to= db.IntegerProperty() str_to= db.StringProperty() id_from= db.IntegerProperty() str_from= db.StringProperty() num= db.IntegerProperty() date= db.DateTimeProperty(auto_now_add=True) raison= db.StringProperty() ip= db.StringProperty() # fonction pour prendre les options def get_options(domain): options= memcache.get(domain+'_options') if options is None: options= Options.get_by_key_name(domain+'_options') if options and options.charset is None: options.charset= 'utf-8' return options # page pour administration class Change(webapp2.RequestHandler): # retour d'un message def rep(self, message, erreur=True): if erreur: erreur='1' else: erreur='0' self.response.write('fa_money_callback('+erreur+','+json.dumps(message)+')') # affichage def get(self,domain): scheme = self.request.scheme # on déclare que le document renvoyé est du javascript self.response.content_type= 'application/javascript' # saisie des options du compte admin options= get_options(domain) if options is None: self.rep("l'outil de transfert n'a pas été configuré") return # on récupère le &from= id_from=self.request.get('from') if id_from is None or re.match('^[1-9][0-9]*$',id_from) is None: self.rep("probleme de requête") return # on récupère le &from_username str_from=self.request.get('from_username') if str_from is None: self.rep("probleme de requête") return # on récupère le &to= id_to=self.request.get('to') if id_to is None or re.match('^[1-9][0-9]*$',id_to) is None: self.rep("probleme de requête") return if id_to==id_from: self.rep("vous ne pouvez vous envoyez vous-même des points") return # on récupère le &to_username str_to=self.request.get('to_username') if str_to is None: self.rep("probleme de requête") return # on récupère le &num= num=self.request.get('num') if num is None or re.match('^[1-9][0-9]*$',num) is None: self.rep("probleme de requête") return # on récupère le &raison= raison=self.request.get('raison') if raison is None: raison = ""; # on conserve les cookies cj = cookielib.CookieJar() opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj)) # connexion au forum original_url = scheme+'://'+domain+'/login.forum' opener.open(original_url,'username='+urllib.quote_plus(options.admin_username.encode(options.charset))+'&password='+urllib.quote_plus(options.admin_password.encode(options.charset))+'&login=1&redirect=/admin/&admin=1') # ouverture du panneau d'admin afin de reçevoir un tid qui permet de visiter le panneau r = opener.open(scheme+'://'+domain+'/admin') # on prend le tid de l'adresse vers laquelle on a été redirigé charset_match = re.search('charset=([^"]*)', r.read()) if charset_match is None: charset = "utf-8" else: charset = charset_match.group(1) return_url = r.geturl() tids = re.findall('[&\?]tid=([a-f0-9]{32})', return_url) if not tids: logging.error('error configuration: {}'.format({'original_url': original_url, 'unmatched_url': return_url, 'charset': options.charset, 'username': options.admin_username, 'password_length': len(options.admin_password)})) self.rep("l'outil de transfert a été mal configuré") return tid = tids[0] # on va rechercher la page avec le nombre de point du donateur r= opener.open(scheme+'://'+domain+'/admin/index.forum?part=modules&sub=point&mode=don&extended_admin=1&tid='+tid, "action=add_points_for_user&search_user="+urllib.quote_plus(re.sub(r'([\\\*%_])',r'\\\1',str_from).encode(charset))+"&submit_search_user=1") # on prend le nombre de point du donateur from_match= re.search('',r.read()) if from_match is None: memcache.delete('lock'+id_from) memcache.delete('lock'+id_to) self.rep("probleme pour prendre la valeur actuelle des points de %s" % str_from) return # on regarde si son nombre de point est plus petit que le nombre de point qu'il donne if int(from_match.group(1)) < int(num): memcache.delete('lock'+id_from) memcache.delete('lock'+id_to) self.rep("vous n'avez que %d points, ce n'est pas assez pour en donner %d" % (int(from_match.group(1)), int(num))) return # on va rechercher la page avec le nombre de point du reçeveur r= opener.open(scheme+'://'+domain+'/admin/index.forum?part=modules&sub=point&mode=don&extended_admin=1&tid='+tid, "action=add_points_for_user&search_user="+urllib.quote_plus(re.sub(r'([\\\*%_])',r'\\\1',str_to).encode(charset))+"&submit_search_user=1") # on prend le nombre de point du reçeveur to_match= re.search('',r.read()) if to_match is None: self.rep(u"probleme pour prendre la valeur actuelle des points de %s" % str_to) return # on envoit les nouveaux nombres de point opener.open(scheme+'://'+domain+'/admin/index.forum?part=modules&sub=point&mode=don&extended_admin=1&tid='+tid, "action=add_points_for_user&points_new_value["+id_from+"]="+str(int(from_match.group(1))-int(num))+"&points_new_value["+id_to+"]="+str(int(to_match.group(1))+int(num))+"&submit=1") # on enregistre la transmission Log(parent=Options.get_by_key_name(domain+'_options'),id_to=int(id_to), str_to=str_to, id_from=int(id_from), str_from=str_from, num=int(num), raison=raison, ip=self.request.remote_addr).put() # on envoie le nouveau nombre de point du reçeveur self.rep(str(int(to_match.group(1))+int(num)), False) # page pour l'affichage de l'historique d'un membre class History(webapp2.RequestHandler): # affichage de la page def get(self, domain, user): scheme = self.request.scheme # on récupère les options pour le domaine ancestor= get_options(domain) # on va rechercher toutes les fois où le membre a été reçeveur receiver= [] if ancestor: for log in Log.all().ancestor(ancestor).filter('id_to =',int(user)).order('-date'): if log.raison is None: log_raison = "" else: log_raison = log.raison if log.ip is None: ip = "" else: ip = log.ip log.date=log.date.replace(tzinfo=UTC()).astimezone(CET()) receiver.append(''+cgi.escape(log.str_from)+''+str(log.num)+''+cgi.escape(log_raison)+''+log.date.strftime("%d/%m/%y %Hh%M")+''+(''+ip+'' if users.is_current_user_admin() else '')) if len(receiver)<1: receiver= '
Jamais reçu de points
'; else: receiver= ''+('' if users.is_current_user_admin() else '')+''+''.join(receiver)+'
EnvoyeurNombre de pointRaisonDateIP
' # on va rechercher toutes les fois où le membre a été envoyeur sender= [] if ancestor: for log in Log.all().ancestor(ancestor).filter('id_from =',int(user)).order('-date'): if log.raison is None: log_raison = "" else: log_raison = log.raison if log.ip is None: ip = "" else: ip = log.ip log.date=log.date.replace(tzinfo=UTC()).astimezone(CET()) sender.append(''+cgi.escape(log.str_to)+''+str(log.num)+''+cgi.escape(log_raison)+''+log.date.strftime("%d/%m/%y %Hh%M")+''+(''+ip+'' if users.is_current_user_admin() else '')) if len(sender)<1: sender= '
Jamais envoyé de points
'; else: sender= ''+('' if users.is_current_user_admin() else '')+''+''.join(sender)+'
ReçeveurNombre de pointRaisonDateIP
' # affichage de la page self.response.out.write(''' Administration

Réception de point

%s

Don de point

%s

Profil

Lien ''' % ( receiver, sender, scheme+'://'+domain+'/u'+user ) ) # page pour administration class Admin(webapp2.RequestHandler): # affichage def get(self, domain): # saisie des options options= get_options(domain) if options is None: options=Options(admin_username='',admin_password='') # on va rechercher tout les 100 derniers logs logs= [] ancestor= get_options(domain) if ancestor: for log in Log.all().ancestor(ancestor).order('-date').fetch(100): if log.raison is None: log_raison = "" else: log_raison = log.raison if log.ip is None: ip = "" else: ip = log.ip log.date=log.date.replace(tzinfo=UTC()).astimezone(CET()) logs.append(''+cgi.escape(log.str_from)+''+cgi.escape(log.str_to)+''+str(log.num)+''+cgi.escape(log_raison)+''+log.date.strftime("%d/%m/%y %Hh%M")+''+(''+ip+'' if users.is_current_user_admin() else '')) if len(logs)<1: logs= '
Aucune transmission de point réalisée
'; else: logs= ''+('' if users.is_current_user_admin() else '')+''+''.join(logs)+'
EnvoyeurReçeveurNombre de pointRaisonDateIP
' # affichage de la page self.response.out.write(''' Administration

Identification

Script

Historique

%s

Déconnexion

Se déconnecter ''' % ( cgi.escape(options.admin_username, True), (self.request.host_url+self.request.path[:-6]).partition(':')[2], logs, users.create_logout_url(self.request.uri) ) ) # traitement formulaire def post(self, domain): # on prend le lien jusqu'au troisieme slash link=re.sub(r'^(https?://[^/]+)/?.*$',r'\1',self.request.get('link')) # on va chercher charset charset_match = re.search('charset=([^"]*)', urllib2.urlopen('http://'+domain+'/admin/login.forum?redirect=/admin').read()) if charset_match is None: charset= 'utf-8' else: charset= charset_match.group(1) # on enregistre les options dans la base de donnée Options(key_name=domain+'_options', admin_username=self.request.get('username'), admin_password=self.request.get('password'), charset=charset).put() # on supprime les options pouvant se trouver dans le cache memcache.delete(domain+'_options') # on redirige vers le formulaire self.redirect('./admin') # fuseau horaire UTC class UTC(datetime.tzinfo): def utcoffset(self, dt): return datetime.timedelta(0) def dst(self, dt): return datetime.timedelta(0) def tzname(self, dt): return "UTC" # fuseau horaire de l'Europe centrale class CET(datetime.tzinfo): def __init__(self): dt = datetime.datetime.utcnow() d = datetime.datetime(dt.year, 4, 1) self.dston = d - datetime.timedelta(days = d.weekday() + 1) d = datetime.datetime(dt.year, 11, 1) self.dstoff = d - datetime.timedelta(days = d.weekday() + 1) def utcoffset(self, dt): return datetime.timedelta(hours = 1) + self.dst(dt) def dst(self, dt): if self.dston <= dt.replace(tzinfo = None) < self.dstoff: return datetime.timedelta(hours = 1) else: return datetime.timedelta() def tzname(self, dt): if self.dston <= dt.replace(tzinfo = None) < self.dstoff: return "CEST" else: return "CET" app = webapp2.WSGIApplication( [ ('/fa_money/([a-z0-9.-]+)/admin', Admin), ('/fa_money/([a-z0-9.-]+)/history/([1-9][0-9]*)', History), ('/fa_money/([a-z0-9.-]+)/', Change) ], debug=True)