Great Migration: spline.plugins => splinext
[zzz-spline-gts.git] / spline / plugins / gts / controllers / gts.py
diff --git a/spline/plugins/gts/controllers/gts.py b/spline/plugins/gts/controllers/gts.py
deleted file mode 100644 (file)
index 98526a8..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-# encoding: utf8
-from __future__ import absolute_import, division
-
-from base64 import urlsafe_b64decode
-from collections import namedtuple
-from itertools import izip
-import logging
-from random import sample
-from string import uppercase, lowercase, digits
-import struct
-
-import pokedex.db
-import pokedex.db.tables as tables
-import pokedex.formulae
-from pokedex.savefile import PokemonSave
-from pylons import config, request, response, session, tmpl_context as c, url
-from pylons.controllers.util import abort, redirect_to
-from sqlalchemy import and_, or_, not_
-from sqlalchemy.orm import aliased, contains_eager, eagerload, eagerload_all, join
-from sqlalchemy.orm.exc import NoResultFound
-from sqlalchemy.sql import func
-
-from spline import model
-from spline.model import meta
-from spline.lib.base import BaseController, render
-from spline.lib import helpers as h
-
-from spline.plugins.gts.model import GTSPokemon
-
-log = logging.getLogger(__name__)
-
-
-### Utility functions
-
-def gts_prng(seed):
-    """Implements the GTS's linear congruential generator.
-
-    I love typing that out.  It makes me sounds so smart.
-
-    Yields magical numbers."""
-    # 0xabcd => 0xabcdabcd
-    seed = seed | (seed << 16)
-    while True:
-        seed = (seed * 0x45 + 0x1111) & 0x7fffffff  # signed dword!
-        yield (seed >> 16) & 0xff
-
-def stream_decipher(data, keystream):
-    """Reverses a stream cipher, given iterable data and keystream.
-
-    Yields decrypted bytes.
-    """
-    for c, key in izip(data, keystream):
-        new_c = (ord(c) ^ key) & 0xff
-        yield chr(new_c)
-
-
-def decrypt_data(data):
-    """Takes a binary blob uploaded from a game and returns the original binary
-    blob.  Depending on your perspective, the returned value may be more
-    intelligible.
-    """
-    # GTS encryption is a simple stream cipher.
-    # The first four bytes of the data are a header containing an obfuscated
-    # key; the rest is the message
-    obf_key_blob, message = data[0:4], data[4:]
-    obf_key, = struct.unpack('>I', obf_key_blob)
-    key = obf_key ^ 0x4a3b2c1d
-
-    # Data is XORed with the output of an LCG, like everything else in Pokémon
-    return ''.join( stream_decipher(message, gts_prng(key)) )
-
-
-### Controller!
-
-def dbg(*args):
-    #print ' '.join(args)
-    pass
-
-class GTSController(BaseController):
-
-    def dispatch(self, page):
-        """We do our own dispatching for two reasons:
-
-        1. All requests do challenge/response before anything, and it's easier
-        to do that and then dispatch than copy/paste some block of code to
-        every action.
-
-        2. Easier to dump stuff if desired.
-        """
-
-        dbg(request)
-
-        # Always return binary!
-        response.headers['Content-type'] = 'application/octet-stream'
-
-        if 'hash' in request.params:
-            # Okay, already done the response.  Decrypt, then dispatch!
-            if request.params['data']:
-                # Note: base64 doesn't like unicode.  Go figure.  It's binary
-                # junk, anyway.
-                encrypted_data = urlsafe_b64decode(str(request.params['data']))
-                data = decrypt_data(encrypted_data)
-                data = data[4:]  # data always starts with pid; we don't care
-            else:
-                data = ''
-
-            method = 'page_' + page
-            if not hasattr(self, method):
-                dbg("NOT YET IMPLEMENTED?")
-                abort(404)
-
-            res = getattr(self, method)(request.params['pid'], data)
-            dbg("RESPONDING WITH ", type(res), len(res), repr(res))
-            return res
-
-        # No hash.  Need to issue a challenge.  It's random, so whatever
-        return ''.join(sample( uppercase + lowercase + digits, 32 ))
-
-    ### Actual pages
-
-    def page_info(self, pid, data):
-        """info.asp
-
-        Apparently always returns 0x0001.  Probably just a ping to see if the
-        server is up.
-        """
-
-        return '\x01\x00'
-
-    def page_setProfile(self, pid, data):
-        """setProfile.asp
-
-        Only Pt and later check this page.  Don't know why.  It returns eight
-        NULs, every time.
-        """
-
-        return '\x00' * 8
-
-    def page_result(self, pid, data):
-        u"""result.asp
-
-        The good part!
-
-        This checks the game's status on the GTS.  If there's nothing
-        interesting to report, it returns 0x0005.  If the game has a Pokémon up
-        on the GTS, it returns 0x0004.
-
-        However...  if the game has a Pokémon waiting to come to it, it returns
-        that entire Pokémon struct!  No header, no encryption.  Just a regular
-        ol' Pokémon blob.
-        """
-
-        # Check for an existing Pokémon
-        # TODO support multiple!
-        try:
-            stored_pokemon = meta.Session.query(model.GTSPokemon) \
-                .filter_by(pid=pid) \
-                .one()
-            # We've got one!  Cool, send it back.  The game will ask us to
-            # delete it after receiving successfully
-            pokemon_save = PokemonSave(stored_pokemon.pokemon_blob)
-            return pokemon_save.as_encrypted
-
-        except:
-            # Nothing
-            return '\x05\x00'
-
-    def page_delete(self, pid, data):
-        u"""delete.asp
-
-        If a Pokémon is received from result.asp, the game requests this page
-        to confirm that the incoming Pokémon may be deleted.
-
-        Returns 0x0001.
-        """
-
-        meta.Session.query(model.GTSPokemon).filter_by(pid=pid).delete()
-        meta.Session.commit()
-
-        return '\x01\x00'
-
-    def page_post(self, pid, data):
-        u"""post.asp
-
-        Deposits a Pokémon in the GTS.  Returns 0x0001 on success, or 0x000c if
-        the deposit is rejected.
-        """
-
-        try:
-            # The uploaded Pokémon is encrypted, which is not very useful
-            pokemon_save = PokemonSave(data, encrypted=True)
-
-            # Create a record...
-            stored_pokemon = model.GTSPokemon(
-                pid=pid,
-                pokemon_blob=pokemon_save.as_struct,
-            )
-            meta.Session.add(stored_pokemon)
-            meta.Session.commit()
-            return '\x01\x00'
-        except:
-            # If that failed (presumably due to unique key collision), we're
-            # already storing something.  Reject!
-            return '\x0c\x00'
-
-    def page_post_finish(self, pid, data):
-        u"""post_finish.asp
-
-        Surely this does something, but for the life of me I can't figure out
-        what.
-
-        Returns 0x0001.
-        """
-        return '\x01\x00'