#!/usr/bin/python
import sys
import os
import time
import select
import logging
import libxml2
from optparse import *
from M2Crypto.SSL import Context

# pubsub import must come first because it overloads part of the
# StanzaProcessor class
from ligo.lvalert import pubsub

from pyxmpp.all import JID,Iq,Presence,Message,StreamError,TLSSettings
from pyxmpp.jabber.all import Client
from pyxmpp.jabber.dataforms import Form,DATAFORM_NS
from pyxmpp.interface import implements
from pyxmpp.interfaces import *
from pyxmpp.exceptions import ClientError, FatalClientError

"""
A tool to administer the pubsub service of openfire
"""

__version__ = "$Revision$"
__date__ = "$Date$"
__name__ = "lvalert_admin"
__Id__ = "$Id$"
__title__ = "LIGO-Virgo Alert Administration"


#################################################################
# help message
usage = """\
%prog [options]
------------------------------------------------------------------------------
  A tool to administer the pubsub service of openfire at
  lvalert.phys.uwm.edu. LSC-Virgo members can activate their accounts 
  on this server by completing the form at
  
    https://www.lsc-group.phys.uwm.edu/cgi-bin/jabber-acct.cgi 

  and typing your password. Then you can use this program to do the
  following things:

  1.  create a pubsub node called small_steps

  %prog --username albert.einstein --password secret --create --node small_steps

  2.  delete a pubsub node called small_steps

  %prog --username albert.einstein --password secret --delete --node small_steps

  4.  subscribe to a pubsub node called lvalert_test

  %prog --username albert.einstein --password secret --subscribe --node lvalert_test

  The owner (person who creates the node by default) can delete and
  publish information to the node. The owner can also add other
  publishers to the node using the command
  
  %prog --username albert.einstein --password secret --add-publisher patrick.brady --node small_steps
  
  Others can subscribe. To see a list of available nodes:
  
  %prog --username albert.einstein --password secret --get-nodes 

  To see the nodes to which you are subscribed:

  %prog --username albert.einstein --password secret --subscriptions
  
  There are two programs called lvalert_listen and lvalert_send which
  provide ways to listen for content on the node and to publish
  information to the node respectively.

"""

#################################################################
def parse_command_line():
  """
  Parser function dedicated
  """
  parser = OptionParser( usage=usage, \
      version= "%prog CVS\n" +
      "$Id$\n" +
      "$Name$\n")

  #username and password
  parser.add_option("-a","--username",action="store",type="string",\
      default="", help="the username of the publisher or listener" )
  parser.add_option("-b","--password",action="store",type="string",\
      default="", help="the password of the publisher or listener" )
  parser.add_option("-c","--server",action="store",type="string",\
      default="lvalert.phys.uwm.edu", help="the pubsub server" )
  parser.add_option("-r","--resource",action="store",type="string",\
      default="admin", help="resource to use in JID" )

  # access information about root nodes
  parser.add_option("-d","--create",action="store_true",\
      default=False, help="create a new pubsub node" )
  parser.add_option("-e","--delete",action="store_true",\
      default=False, help="delete a new pubsub node" )
  parser.add_option("-f","--subscribe",action="store_true",\
      default=False, help="subscribe to a new pubsub node" )
  parser.add_option("-g","--unsubscribe",action="store_true",\
      default=False, help="unsubscribe from a new pubsub node" )
  parser.add_option("-n","--unsubscribe-subid",action="store",type="string",\
      default=None, help="subid of subscription obtained by running subscriptions" )
  parser.add_option("-i","--subscriptions",action="store_true",\
      default=False, help="return a list of subscriptions" )
  parser.add_option("-j","--add-publisher",action="store",type="string",\
      default=None, help="jabber id of person allowed to publish to node" )
  parser.add_option("-m","--get-nodes",action="store_true",\
      default=False, help="get the list of existing nodes" )
  parser.add_option("-o","--affiliations",action="store",type="string",\
      default=None, help="get the list of affiliations to a node" )
  parser.add_option("-p","--delete-publisher",action="store",type="string",\
      default=None, help="jid of publisher to delete from a node" )
  parser.add_option("-q","--node",action="store",type="string",\
      default=None, help="name of node" )
  # debugging options
  parser.add_option("-l","--debug",action="store_true",\
      default=False, help="should  print out lots of information" )
  parser.add_option("-v","--verbose",action="store_true",\
      default=False, help="be verbose as you process the request" )
  
  (options,args) = parser.parse_args()

  if not options.username:
    raise ValueError, "--username must be provided"

  if (options.add_publisher or options.create or options.delete or options.subscribe or options.unsubscribe or options.delete_publisher) and not options.node:
    raise ValueError, "--node must be provided"

  return options, sys.argv[1:]
  
# ============================================================================
# -- get command line arguments
opts, args = parse_command_line()

class MyClient(Client):
    def __init__(self, jid, password):
        # if bare JID is provided add a resource -- it is required
        if not jid.resource:
            jid=JID(jid.node, jid.domain, "admin")

        # we require a TLS connection
        #  Specify sslv3 to get around Sun Java SSL bug handling session ticket
        #  https://rt.phys.uwm.edu/Ticket/Display.html?id=1825
        #  http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6728126
        t=TLSSettings(require=True,verify_peer=False, ctx=Context('sslv3'))

        # setup client with provided connection information
        # and identity data
        Client.__init__(self, jid, password, \
            auth_methods=["sasl:GSSAPI","sasl:PLAIN"], tls_settings=t)


    def stream_state_changed(self,state,arg):
        """This one is called when the state of stream connecting the
        component to a server changes. This will usually be used to
        let the user know what is going on."""
        if opts.verbose:
            print "*** State changed: %s %r ***" % (state,arg)
        else:
            pass

    def session_started(self):
        self.stream.send(Presence())
        if opts.get_nodes:
            self.stream.set_response_handlers(pspl, \
                pspl.get_nodes_result,pspl.create_error,\
                pspl.create_timeout)
        if opts.create:
            self.stream.set_response_handlers(pspl, \
                pspl.generic_result,pspl.create_error,\
                pspl.create_timeout)
        if opts.delete:
            self.stream.set_response_handlers(pspl, \
                pspl.generic_result,pspl.generic_error,\
                pspl.generic_timeout)
        if opts.subscribe:
            self.stream.set_response_handlers(pspl, \
                pspl.subscribe_result,pspl.subscribe_error,\
                pspl.subscribe_timeout)
        if opts.affiliations:
            self.stream.set_response_handlers(pspl, \
                pspl.affiliations_result,pspl.generic_error,\
                pspl.generic_timeout)
        if opts.add_publisher:
            self.stream.set_response_handlers(pspl, \
                pspl.generic_result,pspl.generic_error,\
                pspl.generic_timeout)
        if opts.delete_publisher:
            self.stream.set_response_handlers(pspl, \
                pspl.generic_result,pspl.generic_error,\
                pspl.generic_timeout)
        if opts.unsubscribe:
            self.stream.set_response_handlers(pspl, \
                pspl.generic_result,pspl.generic_error,\
                pspl.generic_timeout)
        if opts.subscriptions:
            self.stream.set_response_handlers(pspl, \
                pspl.subscriptions_result,pspl.subscribe_error,\
                pspl.subscribe_timeout)
        self.stream.send(pspl)

    def idle(self):
        if self.stream and self.session_established:
            if opts.verbose:
                print "Disconnecting"
            self.disconnect()
        if opts.verbose:
            print "idle"
        time.sleep(4)

    def post_disconnect(self):
        print "Disconnected"
        raise Disconnected


# add a logger so that we can see what's going
if opts.debug:
    logger=logging.getLogger()
    logger.addHandler(logging.StreamHandler())
    logger.setLevel(logging.DEBUG)

# debug the memore
libxml2.debugMemory(1)

# set up the stream
myjid=JID(opts.username+"@"+opts.server+"/"+opts.resource)
s=MyClient(jid=myjid,password=opts.password)

if opts.verbose:
    print "connecting..."
s.connect()

if opts.verbose:
    print "build pubsub stanza..."
recpt=JID("pubsub."+opts.server)
pspl=pubsub.PubSub(from_jid = myjid, to_jid = recpt, stream = s,\
stanza_type="get")
if opts.get_nodes:
    print "Getting nodes"
    pspl.get_nodes()
elif opts.create:
    print "Creating node " + opts.node
    pspl.create_node(opts.node)
elif opts.delete:
    pspl.delete_node(opts.node)
elif opts.subscribe:
    pspl.subscribe(myjid,opts.node)
elif opts.unsubscribe:
    pspl.unsubscribe(myjid,opts.node,opts.unsubscribe_subid)
elif opts.subscriptions:
    pspl.subscriptions(myjid)
elif opts.affiliations:
    pspl.affiliations(myjid,opts.affiliations)
elif opts.add_publisher:
    publisher_jid = opts.add_publisher.split('@')[0]
    publisher_jid=JID(publisher_jid+"@"+opts.server+"/Home")
    pspl.publisher(publisher_jid,opts.node,"publisher")
elif opts.delete_publisher:
    publisher_jid = opts.delete_publisher.split('@')[0]
    publisher_jid=JID(publisher_jid+"@"+opts.server+"/Home")
    pspl.publisher(publisher_jid,opts.node,"none")
else:
    pspl.get_nodes()

if opts.verbose:
    print "sending message..."

try:
    s.loop(1)
except KeyboardInterrupt:
    print u"disconnecting..."
    s.disconnect()

# vi: sts=4 et sw=4
