#!/usr/local/bin/python
#
# Copyright (c) 2002 Neil Blakey-Milner
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#

#import jabber
import sha
import sys
from twisted.internet import protocol, reactor
from twisted.protocols import sux
from twisted.python import log
from twisted.web import microdom

LOGIN = 1
AUTH_GET = 2
GETROSTER = 3

def jabberheader(host, namespace, id = None):
    str = "<?xml version='1.0' encoding='UTF-8' ?>   \
            <stream:stream to='%s' xmlns='%s'" % (host, namespace)

    if id: str = str + " id='%s' " % id
    str = str + " xmlns:stream='http://etherx.jabber.org/streams'>"
    return str

def jabberlogin(jid, method, password, resource, queryid):
    iq = microdom.Element("iq", { "type": "set", "id": queryid })
    query = microdom.Element("query", { "xmlns": "jabber:iq:auth" })
    username = ElementWithText("username", jid)
    password = ElementWithText(method, password)
    resource = ElementWithText("resource", resource)
    iq.appendChild(query)
    query.appendChild(username)
    query.appendChild(password)
    query.appendChild(resource)
    return iq

def jabbergetauthtypes(user, queryid):
    iq = microdom.Element("iq", { "type": "get", "id": queryid })
    query = microdom.Element("query", { "xmlns": "jabber:iq:auth" })
    username = ElementWithText("username", user)
    iq.appendChild(query)
    query.appendChild(username)
    return iq

def jabbergetroster(queryid):
    iq = microdom.Element("iq", { "type": "get", "id": queryid })
    query = microdom.Element("query", { "xmlns": "jabber:iq:roster" })
    iq.appendChild(query)
    return iq

def jabbersetonline():
    presence = microdom.Element("presence", { "type": "available" })
    show = ElementWithText("show", "online")
    status = ElementWithText("status", "online")
    priority = ElementWithText("priority", "5")
    presence.appendChild(show)
    presence.appendChild(status)
    presence.appendChild(priority)
    return presence

def jabbermessage(to, body):
    message = microdom.Element("message", { "to": to })
    body = ElementWithText("body", body)
    message.appendChild(body)
    return message

class ElementWithText(microdom.Element):
    def __init__(self, tagName, text, attributes=None, parentNode=None, filename=None, markpos=None):
        microdom.Element.__init__(self, tagName, attributes, parentNode, filename, markpos)
        self.text = microdom.Text(text)
        self.appendChild(self.text)

class Jabber(microdom.MicroDOMParser):
    def __init__(self):
        self.soonClosers = []
        self.laterClosers = {}
        microdom.MicroDOMParser.__init__(self, beExtremelyLenient = 1, caseInsensitive = 0)

    def connectionMade(self):
        self.queries = {}
        microdom.MicroDOMParser.connectionMade(self)
        self.factory.protocolobj = self
        self.transport.write(jabberheader(self.factory.host, "jabber:client"))

    def connectionLost(self, reason):
        microdom.MicroDOMParser.connectionLost(self, reason)
        self.factory.protocolobj = None

    def gotTagStart(self, name, attributes):
        microdom.MicroDOMParser.gotTagStart(self, name, attributes)
        if name == "stream:stream":
            el = self.elementstack.pop()
            self.jabberConnected(el)

    def gotTagEnd(self, name):
        microdom.MicroDOMParser.gotTagEnd(self, name)
        #print "Tag ended: %s" % (name)
        for document in self.documents:
            func = getattr(self, "jabber_%s" % document.tagName, self.jabberUnhandled)
            #print func
            func(document)
        self.documents = []

    def jabberUnhandled(self, document):
        print "Unhandled: \n  %s" % (document.toxml())

    def jabber_iq(self, document):
        queryid = document.attributes["id"]
        if document.attributes["type"] == "result":
            if not self.queries.has_key(queryid):
                # This shouldn't happen
                return
            if self.queries[queryid] == AUTH_GET:
                self.jabber_gotauthtypes(document)
                return
            if self.queries[queryid] == LOGIN:
                self.jabber_loginsuccessful(document)
                return

            if self.queries[queryid] == GETROSTER:
                self.jabber_gotroster(document)
                return

        if document.attributes["type"] == "error":
            if self.queries[queryid] == LOGIN:
                self.jabber_loginfailed(document)
                return

        self.jabberUnhandled(document)

    def jabber_gotroster(self, document):
        self.jabberGotRosterStart(document)
        for entry in document.childNodes[0].childNodes:
            jid = entry.attributes["jid"]
            ask = entry.attributes["ask"]
            subscription = entry.attributes["subscription"]
            if subscription == "none":
                subscription = None
            self.jabberGotRosterEntry(jid, ask, subscription, entry)
        self.jabberGotRosterEnd(document)


    def jabberGotRosterStart(self, document):
        print "Roster:"
        print "---------------------------"

    def jabberGotRosterEntry(self, jid, ask, subscription, document):
        print "User %s: " % (jid),
        if subscription:
            print "%s" % (subscription),
        if ask:
            print "%s-request" % (ask),
        print
        #print document.xml()

    def jabberGotRosterEnd(self, document):
        print "---------------------------"

    def jabber_message(self, document):
        # <message to="test@jabber.moria.org" from="nbm@jabber.moria.org/imcom"><body>hello</body></message>
        print "message"
        recipient = document.attributes["to"]
        sender = document.attributes["from"]
        body = document.childNodes[0].childNodes[0].nodeValue

        self.jabberGotMessage(sender, recipient, body, document)

    def jabberGotMessage(self, sender, recipient, body, document):
        print "Message from %s:" % (sender)
        print "\t%s" % (body)
        self.jabberSendMessage(sender, "I got your message: %s" % (body))

    def jabberSendMessage(self, recipient, body):
        message = jabbermessage(recipient, body)
        self.transport.write(message.toxml())

    def jabber_presence(self, document):
        # <message to="test@jabber.moria.org" from="nbm@jabber.moria.org/imcom"><body>hello</body></message>
        if document.attributes["type"] == "subscribe":
            sender = document.attributes["from"]
            self.jabberSubscribeRequest(sender, document)
            return

        self.jabberUnhandled(document)

    def jabberSubscribeRequest(self, sender, document):
        print "Subscribe request from %s:" % (sender)

    def jabber_loginsuccessful(self, document):
        self.jabberLoggedIn(document)

        queryid = "getroster"
        roster = jabbergetroster(queryid)
        self.transport.write(roster.toxml())
        #print roster.toxml()
        self.queries[queryid] = GETROSTER
        
        presence = jabbersetonline()
        #print presence.toxml()
        self.transport.write(presence.toxml())

    def jabberLoggedIn(self, document):
        print "Logged in as %s on %s" % (self.factory.jid, self.factory.host)
        
    def jabber_loginfailed(self, document):
        error = document.childNodes[1]
        code = error.attributes["code"]
        reason = error.childNodes[0].nodeValue
        self.jabberFailedLogIn(error, code, reason, document)

    def jabberFailedLogIn(self, error, code, reason, document):
        print "Login failed (code %s), reason: %s" % (code, reason)

    def jabberConnected(self, element):
        self.logOn(element)

    def jabber_gotauthtypes(self, document):
        types = []
        for child in document.childNodes[0].childNodes:
            if child.tagName != "username":
                types.append(child.tagName)

        self.jabberGotAuthenticationTypes(types, document)

        #hd = sha.new(id+password).hexdigest()
        queryid = "auth"
        iq = jabberlogin(self.factory.jid, "password", self.factory.password, self.factory.resource, queryid)
        self.transport.write(iq.toxml())
        self.queries[queryid] = LOGIN

    def jabberGotAuthenticationTypes(self, types, document):
        print "Authentication types supported: ",
        print ", ".join(types)

    def logOn(self, element):
        queryid = "auth-get"
        iq = jabbergetauthtypes(self.factory.jid, queryid)
        self.queries[queryid] = AUTH_GET
        self.transport.write(iq.toxml())

    def everyTenSeconds(self):
        print self.documents
        for item in self.elementstack:
            print item
            #item.writexml(sys.stdout)

class JabberClientFactory(protocol.ClientFactory):
    protocol = Jabber

    def __init__(self, host, jid, password, resource = "tpjabber"):
        self.host = host
        self.jid = jid
        self.password = password
        self.resource = resource

    def everyTenSeconds(self):
        if self.protocolobj:
            self.protocolobj.everyTenSeconds()
        reactor.callLater(10, self.everyTenSeconds)

def main():
    host = "jabber.moria.org"
    log.startLogging(sys.stdout)
    factory = JabberClientFactory(host, "test", "test")
    reactor.connectTCP(host, 5222, factory)
    reactor.callLater(2, factory.everyTenSeconds)
    reactor.run()

if __name__ == "__main__":
    main()
