Esempio n. 1
0
    def __init__(self, port=False):
        """
        Initialisierung und Binden des Ports.
        Der Port kann via Shell-Argument uebergeben werden, sonst wird versucht, Port 32323 zu binden.

        @return: None
        """

        self.database = DatabaseHandler("gu.db")
        self.crypt = EncryptionHandler()
        self.sid_Pool = Pool(0)

        self.file_storage = "./files/"
        self.max_rcv = 2048

        self.users = []         # Nutzer, die zum Index Session-ID gehoeren
        self.ivs = []           # Initialiserungsvektoren
        self.ctr = []           # Counter
        self.sesskey = []       # Sessionkeys
        self.uidstrings = []    # User-ID-Strings
        
        #serv_soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        #file_soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        serv_soc = socket.socket()

        if port != False:
            serv_soc.bind(("", port))
        elif len(sys.argv) < 2:
            serv_soc.bind(("", 32323))
        else:
            serv_soc.bind(("", int(sys.argv[1])))

        serv_soc.listen(1)
        
        try:
            while True:

                komm, addr = serv_soc.accept()
                data = ""
                data = komm.recv(self.max_rcv)

                # Default-Antwort
                resp = "error - invalid-client-request"

                # Leere Verbindung
                if not data: 
                    komm.close()
                    continue

                # Datenpaket ist verschluesslt (= Kein DHEX-Paket)    
                if self.crypt.is_encrypted(data):
                    #body = self.decrypt(data)

                    tmp = split(";", data, 1)
                    body = tmp[1]
                    #print "getting messages :" + body

                    # Wenn nicht entschluesselbar -> Fehler
                    if body == None:
                       komm.send(resp)
                       komm.close()
                       continue 

                    body = body.decode("utf-8", "ignore")

                try:
                    # Kopfdaten und Nutzdaten trennen
                    data = split(";", data, 1)
                    self.header = self.parse_header(data[0])
                    data = data[1]
                except IndexError:
                    komm.send(resp)
                    komm.close()
                    continue

                # Datenpaket encoden
                #if self.header[0] != "dhex":

                try:

                    if self.header[0] == "dhex":
                        resp = self.init_dh(data)                      
                    elif self.header[0] == "auth":
                        resp = self.auth_user(body)
                    elif self.header[0] == "msg":
                        resp = self.recv_msg(body)
                    elif self.header[0] == "getmsg":
                        resp = self.get_msg(body)
                    elif self.header[0] == "gmsg":
                        resp = self.recv_gmsg(body)
                    elif self.header[0] == "getgmsg":
                        resp = self.get_gmsg(body)
                    elif self.header[0] == "reqfile":
                        resp = self.request_file()
                    elif self.header[0] == "regfile":
                        resp = self.register_file(body)
                    elif self.header[0] == "getfile":
                        resp = self.get_globalname(body)
                    elif self.header[0] == "getpic":
                        resp = self.get_profile_pic(body)
                    elif self.header[0] == "adduser":
                        resp = self.add_user(body)
                    elif self.header[0] == "adressbook":
                        resp = self.get_address_book(body)


                    # Antwortpaket senden
                    if self.header[0] == "dhex":
                        komm.send(resp)

                    elif isinstance(resp, list):
                        #for item in resp:
                            #item = (str(item[0]).encode("utf-8", "ignore"), item[1].encode("utf-8", "ignore"))

                        komm.send(self.build_pack(json.dumps(resp)))

                    else:
                        if self.is_error(resp):
                            print "error:  " + resp
                            komm.send(resp)
                        else:
                            resp = self.crypt.encode_string(resp)
                            komm.send(self.build_pack(resp))
                        
                    komm.close()

                except IndexError:
                    komm.send(resp)
                    komm.close()

                        
        finally:
            #for client in clients: 
            #    client.close() 
            serv_soc.close()
Esempio n. 2
0
class ConnectionHandler:
    """
    Infolib.ConnectionHandler ist eine Klasse, die die Grundfunktionalitaet des Infoservers bereitstellt.
    Sie bindet einen Port, an dem alle Verbindungen eigehen und verteilt die Pakete nach den entsprechenden Kopfinformationen.
    Sie baut auf allen anderen Klassen in der Infolib auf.

    Eine Verwendung ausserhalb des Infobook-Projektes wird nicht empfohlen.

    Eine Dokumentation des Ablaufes finden Sie im GitHub-Wiki unter https://github.org/hansau22/infobook/
    """

    def __init__(self, port=False):
        """
        Initialisierung und Binden des Ports.
        Der Port kann via Shell-Argument uebergeben werden, sonst wird versucht, Port 32323 zu binden.

        @return: None
        """

        self.database = DatabaseHandler("gu.db")
        self.crypt = EncryptionHandler()
        self.sid_Pool = Pool(0)

        self.file_storage = "./files/"
        self.max_rcv = 2048

        self.users = []         # Nutzer, die zum Index Session-ID gehoeren
        self.ivs = []           # Initialiserungsvektoren
        self.ctr = []           # Counter
        self.sesskey = []       # Sessionkeys
        self.uidstrings = []    # User-ID-Strings
        
        #serv_soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        #file_soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        serv_soc = socket.socket()

        if port != False:
            serv_soc.bind(("", port))
        elif len(sys.argv) < 2:
            serv_soc.bind(("", 32323))
        else:
            serv_soc.bind(("", int(sys.argv[1])))

        serv_soc.listen(1)
        
        try:
            while True:

                komm, addr = serv_soc.accept()
                data = ""
                data = komm.recv(self.max_rcv)

                # Default-Antwort
                resp = "error - invalid-client-request"

                # Leere Verbindung
                if not data: 
                    komm.close()
                    continue

                # Datenpaket ist verschluesslt (= Kein DHEX-Paket)    
                if self.crypt.is_encrypted(data):
                    #body = self.decrypt(data)

                    tmp = split(";", data, 1)
                    body = tmp[1]
                    #print "getting messages :" + body

                    # Wenn nicht entschluesselbar -> Fehler
                    if body == None:
                       komm.send(resp)
                       komm.close()
                       continue 

                    body = body.decode("utf-8", "ignore")

                try:
                    # Kopfdaten und Nutzdaten trennen
                    data = split(";", data, 1)
                    self.header = self.parse_header(data[0])
                    data = data[1]
                except IndexError:
                    komm.send(resp)
                    komm.close()
                    continue

                # Datenpaket encoden
                #if self.header[0] != "dhex":

                try:

                    if self.header[0] == "dhex":
                        resp = self.init_dh(data)                      
                    elif self.header[0] == "auth":
                        resp = self.auth_user(body)
                    elif self.header[0] == "msg":
                        resp = self.recv_msg(body)
                    elif self.header[0] == "getmsg":
                        resp = self.get_msg(body)
                    elif self.header[0] == "gmsg":
                        resp = self.recv_gmsg(body)
                    elif self.header[0] == "getgmsg":
                        resp = self.get_gmsg(body)
                    elif self.header[0] == "reqfile":
                        resp = self.request_file()
                    elif self.header[0] == "regfile":
                        resp = self.register_file(body)
                    elif self.header[0] == "getfile":
                        resp = self.get_globalname(body)
                    elif self.header[0] == "getpic":
                        resp = self.get_profile_pic(body)
                    elif self.header[0] == "adduser":
                        resp = self.add_user(body)
                    elif self.header[0] == "adressbook":
                        resp = self.get_address_book(body)


                    # Antwortpaket senden
                    if self.header[0] == "dhex":
                        komm.send(resp)

                    elif isinstance(resp, list):
                        #for item in resp:
                            #item = (str(item[0]).encode("utf-8", "ignore"), item[1].encode("utf-8", "ignore"))

                        komm.send(self.build_pack(json.dumps(resp)))

                    else:
                        if self.is_error(resp):
                            print "error:  " + resp
                            komm.send(resp)
                        else:
                            resp = self.crypt.encode_string(resp)
                            komm.send(self.build_pack(resp))
                        
                    komm.close()

                except IndexError:
                    komm.send(resp)
                    komm.close()

                        
        finally:
            #for client in clients: 
            #    client.close() 
            serv_soc.close()



    def is_error(self, data):
        """
        Prueft ob eine Antwort eine Fehlermeldung ist

        @param data: Antwort
        @type data: str

        @return: Boolean Ergebnis
        """

        if string.find(data, "error", 0, 4) == -1 :
            return False
        else:
            return True


    def init_dh(self, data):
        """
        Initialisiert den DH-Schluesselaustausch anhand der Informationen aus der Anfrage des Clients.

        @param data: Enthaelt den Oeffentlichen Teil vom Partner
        @type data: str

        @return: str - Sessionkey
        """

        # DH-Antwort (B) auf die Anfrage (A)
        ret = self.crypt.init_dh_b(self.sid_Pool.give_next(),data)

        if ret == False:
            return "error - DH-initiation-error - DHEX"

        try:
            # Alle Felder fuer die neu Initialiserte Session reservieren (befuellen)
            self.users.append("")
            self.uidstrings.append("")
            self.ivs.append(ret[0])
            self.ctr.append(ret[1])
            self.sesskey.append(ret[2])

            #print "sesskey :  " + ret[2]

            return ret[3]
        except IndexError:
            return "error - DH-initiation-server-error - DHEX"
        



    def decrypt(self, data):
        """
        Entschluesselt ein Datenpaket mit dem Sessionkey, der zur Session-ID gehoert.

        @param data: Verschluesseltes Paket mit unverschluesselten Kopfinformationen
        @type data: str

        @return: str - Unverschluesseltes Paket ohne Kopfinformationen
        """

        try:
            tmp = split(";", data, 1)       # ";" Seperiert Nutz- und Kopfdaten
            sid = split(":", tmp[0], 2)     # Extrahiere Session-ID
            sid = int(sid[2])
            data = self.crypt.decrypt(self.sesskey[sid], self.ctr[sid], tmp[1])
            return data
        except IndexError:
            return None



    
    def encrypt(self, data):
        """
        Verschluesselt die Daten fuer ein Paket.

        @param data: Daten-String ohne Kopfinformationen
        @type data: str

        @return: Verschluesselt Datenpaket ohne Kopfinformationen
        """
        try:
            sid = self.header[2]
            #return self.crypt.encrypt(self.sesskey[sid], self.ctr[sid], data)
            return data
        except IndexError:
            return None

            
    def parse_header(self, data):
        """
        Verarbeitet Kopfinformationen und wandelt die Informationen in benoetigte Typen

        @param data: Kopfinformationen
        @type data: str

        @return: Array - Kopfinformationen
        """

        try:
            header = split(":", data, 2)
            if not header[0] == "dhex":
                header[2] = int(header[2])
            return header
        except IndexError:
            pass
        


    def build_pack(self, msg):
        """
        Erstellt die Kopfinformationen fuer ein Datenpaket und fuegt die Nachricht an.

        @param msg: Nachricht ohne Kopfinformationen
        @type msg: str

        @return: str - Nachrichtenpaket mit Kopfinformationen
        """

        try:
            package = "sresp" + ":" + str(self.header[2]) + ";"
            #enc_msg = self.encrypt(msg)
            #if enc_msg == None:
            #    return msg
            package += msg
            return package
        except IndexError:
            return msg



    def auth_user(self, data):
        """
        Prueft ob eine Nutzer-Passwort Kombination valid ist.

        @param data: Paket des Clients ohne Kopfinformationen
        @type data: str

        @return: str - Digest, der den Nutzer-ID-String enthaelt
        """
        cred = split(":", data, 1)
        if len(cred) < 2:
            return "error - not-enough-arguments - AUTH"
        try:
            if self.database.auth_user(cred[0], cred[1]) == True:

                # User-ID String erzeugen
                dig = self.crypt.get_hash(self.sesskey[self.header[2]] + str(cred[0])) 

                self.uidstrings[self.header[2]] = dig
                self.users[self.header[2]] = self.database.get_user_id(cred[0])

                return dig
            else:
                return "error - wrong-credentials - AUTH"
        except IndexError:
            return "error - invalid-header - AUTH"



    def check_uidstring(self, index, string):
        """
        Vergleicht einen Nutzer-ID-String mit dem, der zu dem Nutzer mit der Session-ID gehoert.

        @param index: Session-ID
        @type index: int

        @param string: Nutzer-ID-String
        @type string: str

        @return: Boolean - Ergebnis
        """

        try:
            if self.uidstrings[index] == string:
                return True
            return False
        except IndexError:
            return False


        
    def recv_msg(self, data):
        """
        Traegt eine Nachricht in die Datenbank ein.

        @param data: Datenpaket des Clients ohne Kopfinformationen
        @type data: str

        @return: str - Erfolgs-/Fehlermeldung
        """

        try:
            sid = self.header[2]
            tmp = split(":", data, 2)


            # Nicht alle Felder gegeben
            if len(tmp) != 3:
                return "error - not-long-enough - MSG"

            if self.check_uidstring(sid, tmp[0]):
                rcv_uid = self.database.get_user_id(tmp[1])
                snd_uid = self.users[self.header[2]]
                print "writing message:" + tmp[2]
                if not self.database.rcv_message(snd_uid, rcv_uid, tmp[2]):
                    return "error - server-database-error - MSG"
                return "success - MSG"
            else:
                return "error - wrong-uidstring - MSG"
        except IndexError:
            return "error - invalid-header - MSG"



    def get_msg(self, data):
        """
        Gibt dem Client die Nachrichten zurueck. 

        @param data: Letzte MID, die der Client an den Server gibt.
        @type data: str

        @return: Array - Nachrichten
        """

        data = split(":", data, 2)

        try:

            if not self.check_uidstring(self.header[2], data[0]):
              #  print "wrong uid string :" + data[0]
                return "error - wrong-credentials - GetMessage"

            messages = self.database.get_messages_by_last_mid(self.header[2], data[1])
            ret_msg = []

            for item in messages:
                username = self.database.get_user_by_id(item[0])

                if (username == None) or (item[0] == "False"):
                    username = "******"

                ret_msg.append((username, item[1]))

            return ret_msg
        except IndexError:
            return "error - internal-database-request-error - MSG"



    def get_gmsg(self, data):
        """
        Gibt dem Client die Gruppennachrichten zurueck.

        @param data: Letzte GID, die der Client an den Server gibt.
        @type data: str

        @return: Array - Nachrichten
        """

        data = split(":", data, 2)

        try:

            if not self.check_uidstring(self.header[2], data[0]):
                print "wrong uid string"
                return "error - wrong-credentials - GetGroupMessage"

            messages = self.database.get_messages_by_last_gid(self.header[2], data[1])
            ret_msg = []

            for item in messages:
                groupname = self.database.get_group_by_id(item[0])

                if groupname == None:
                    groupname = "Gruppenname unbekannt"

                ret_msg.append(item[1] + ":" + groupname + ":" + item[2])

            return ret_msg
        except IndexError:
            return "error - internal-database-request-error - GroupMessage"



    def recv_gmsg(self, data):
        """
        Traegt eine Broadcast-Nachricht in die Datenbank ein.

        @param data: Datenpaket des Clients ohne Kopfinformationen
        @type data: str

        @return: str - Erfolgs-/Fehlermeldung
        """

        try:
            sid = self.header[2]
            tmp = split(":", data, 2)

            # Nicht alle Felder gegeben
            if len(tmp) != 3:
                return "error - not-enough-arguments - GroupMessage"

            if self.check_uidstring(sid, tmp[0]):
                rcv_gid = self.database.get_group_id(tmp[1])
                snd_uid = self.users[self.header[2]]
                print "writing group message:" + tmp[2]
                if not self.database.rcv_brdc_message(snd_uid, rcv_gid, tmp[2]):
                    return "error - server-database-error - GroupMessage"
                return "success - GroupMessage"
            else:
                print "error in uidstring"
                return "error - wrong-uidstring - GroupMessage"
        except IndexError:
            return "error - invalid-header - GroupMessage"


    def request_file(self):
        """
        Gibt einen Dateistring fuer eine neue Datei zurueck, die ueber FTP hochgeladen werden kann

        @return: str - Dateistring
        """

        ret_value = self.generate_file_string()

        if ret_value == None:
            return "error - storage-full - REQFILE"
        else:
            self.database.add_file(ret_value)
            return ret_value



    def register_file(self, data):
        """
        Registriert eine Datei (Setzt den Besitzer zu einem Upload und gibt Dateinamen an)

        @param data: Datenpaket des Clients ohne Kopfinformationen
        @type data: str

        @return: str - Erfolgs-/Fehlermeldung
        """
        values = split(":", data, 2)
        print  values
    
        try: 
               

            filestring = values[0]
            global_name = values[1]
            del values


            if not self.database.check_filestring(filestring):
                return "error - wrong-filestring"

            if not self.database.register_file(self.users[self.header[2]], global_name, filestring):
                return "error - server-storage-error"
            else:
                return "success - FILE"
        except IndexError:
            return "error - invalid-header - FILE"


    def get_globalname(self, data):
        """
        Sucht eine Datei nach dem Dateinamen und gibt den filestring zuruekc

        @param data: Datenpaket des Clients ohne Kopfinformationen
        @type data: str

        @return: str - filestring / str - error
        """

        filestring = self.database.get_name_by_filestring(data)

        if not filestring:
            return "error - file-not-found - FILE"
        return filestring


    def generate_file_string(self):
        """
        Generiert einen Datei-String fuer eine neu empfangene Datei
        Prueft ausserdem, ob eine Datei mit diesem Namen in ./files vorhanden ist

        @return: str - Dateistring
        """
        for i in range(0, 14):
            filestring = ""
            for i in range(0, 10):
                filestring = filestring + choice(string.ascii_letters)

            if os.path.exists(self.file_storage + filestring):
                break
            else:
                return filestring

        return None



    def get_profile_pic(self, data):
        """
        Gibt dem Nutzer den String zum Profilfoto zurueck

        @param data: Datenpaket des Clients ohne Kopfinformationen
        @type data: str

        @return: str - Dateistring zum Profilbild
        """

        string = self.database.get_profile_pic(data)

        if not string:
            return "-"
        return string


    def add_user(self, data):
        """
        Fuegt einen neuen Benutzer hinzu

        @param data: Daten vom Client
        @type data: str

        @return: str - Nachricht an den Client
        """

        tmp = split(":", data, 2)

        if not len(tmp) == 2:
            return "error - not-enough-arguments - ADDUSER"        

        username = tmp[0]
        pwhash = tmp[1]

        if not self.database.check_user(username):
            return "error - already-present-username - ADDUSER"

        self.database.add_user(username, pwhash)
        return "success - ADDUSER"


    def get_address_book(self, data):
        """
        Gibt das Nutzer-Addressbuch zurueck

        @param data: uidstring
        @type data: str

        @return: Array - [int UID, str Name]
        """

        if not self.check_uidstring(int(self.header[2]), data):
            return "error - wrong-uidstring - GETADRBOOK"

        return self.database.get_address_book(self.users[int(self.header[2])])