Initial revision
This commit is contained in:
commit
c0f51fdd1d
|
@ -0,0 +1,4 @@
|
||||||
|
import hub
|
||||||
|
import jabber
|
||||||
|
import plugin
|
||||||
|
import xmlstream
|
|
@ -0,0 +1,65 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
## common/hub.py
|
||||||
|
##
|
||||||
|
## Gajim Team:
|
||||||
|
## - Yann Le Boulanger <asterix@crans.org>
|
||||||
|
## - Vincent Hanquez <tab@tuxfamily.org>
|
||||||
|
## - David Ferlier <krp@yazzy.org>
|
||||||
|
##
|
||||||
|
## Copyright (C) 2003 Gajim Team
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published
|
||||||
|
## by the Free Software Foundation; version 2 only.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
|
||||||
|
import Queue
|
||||||
|
import common.plugin
|
||||||
|
import common.thread
|
||||||
|
|
||||||
|
""" Hub definitions """
|
||||||
|
|
||||||
|
class GajimHub:
|
||||||
|
def __init__(self):
|
||||||
|
self.queues = {}
|
||||||
|
""" {event1:[queue1, queue2]} """
|
||||||
|
self.events = {'NOTIFY':[], 'MSG':[], 'ROSTER':[]}
|
||||||
|
self.queueIn = self.newQueue('in', 100)
|
||||||
|
# END __init__
|
||||||
|
|
||||||
|
def newQueue(self, name, size):
|
||||||
|
""" Creates a new queue """
|
||||||
|
qu = Queue.Queue(size)
|
||||||
|
self.queues[name] = qu
|
||||||
|
return qu
|
||||||
|
# END newQueue
|
||||||
|
|
||||||
|
def newPlugin(self, name):
|
||||||
|
"""Creates a new Plugin """
|
||||||
|
qu = self.newQueue(name, 100)
|
||||||
|
pl = common.plugin.GajimPlugin(name, qu, self.queueIn)
|
||||||
|
return pl
|
||||||
|
# END newPlugin
|
||||||
|
|
||||||
|
def register(self, name, event):
|
||||||
|
""" Records a plugin from an event """
|
||||||
|
qu = self.queues[name]
|
||||||
|
self.events[event].append(qu)
|
||||||
|
# END register
|
||||||
|
|
||||||
|
def sendPlugin(self, event, data):
|
||||||
|
""" Sends an event to registered plugins
|
||||||
|
NOTIFY : ('NOTIFY', (user, status, message))
|
||||||
|
MSG : ('MSG', (user, msg))
|
||||||
|
ROSTER : ('ROSTER', {jid:{'Online':_, 'Status':_, 'Show':_} ,jid:{}})"""
|
||||||
|
|
||||||
|
if self.events.has_key(event):
|
||||||
|
for i in self.events[event]:
|
||||||
|
i.put((event, data))
|
||||||
|
# END sendPlugin
|
||||||
|
# END GajimHub
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,75 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
## common/optparser.py
|
||||||
|
##
|
||||||
|
## Gajim Team:
|
||||||
|
## - Yann Le Boulanger <asterix@crans.org>
|
||||||
|
## - Vincent Hanquez <tab@tuxfamily.org>
|
||||||
|
## - David Ferlier <krp@yazzy.org>
|
||||||
|
##
|
||||||
|
## Copyright (C) 2003 Gajim Team
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published
|
||||||
|
## by the Free Software Foundation; version 2 only.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
|
||||||
|
import ConfigParser
|
||||||
|
import logging
|
||||||
|
|
||||||
|
log = logging.getLogger('common.options')
|
||||||
|
|
||||||
|
class OptionsParser:
|
||||||
|
def __init__(self, fname):
|
||||||
|
self.__fname = fname
|
||||||
|
# END __init__
|
||||||
|
|
||||||
|
def parseCfgFile(self):
|
||||||
|
try:
|
||||||
|
self.__fd = open(self.__fname)
|
||||||
|
except:
|
||||||
|
print 'error cannot open file %s\n' % (self.__fname);
|
||||||
|
return
|
||||||
|
|
||||||
|
self.__config = ConfigParser.ConfigParser()
|
||||||
|
self.__config.readfp(self.__fd)
|
||||||
|
self.__sections = self.__config.sections()
|
||||||
|
|
||||||
|
for section in self.__sections:
|
||||||
|
for option in self.__config.options(section):
|
||||||
|
value = self.__config.get(section, option, 1)
|
||||||
|
setattr(self, str(section) + '_' + \
|
||||||
|
str(option), value)
|
||||||
|
# END parseCfgFile
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "OptionsParser"
|
||||||
|
# END __str__
|
||||||
|
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
if attr.startswith('__') and attr in self.__dict__.keys():
|
||||||
|
return self.__dict__[attr]
|
||||||
|
else:
|
||||||
|
for key in self.__dict__.keys():
|
||||||
|
if key == attr:
|
||||||
|
return self.__dict__[attr]
|
||||||
|
return None
|
||||||
|
# END __getattr__
|
||||||
|
|
||||||
|
def writeCfgFile(self):
|
||||||
|
try:
|
||||||
|
self.__config.write(open(self.__fname, 'w'))
|
||||||
|
except:
|
||||||
|
log.debug("Can't write config %s" % self.__fname)
|
||||||
|
return 0
|
||||||
|
return 1
|
||||||
|
# END writeCfgFile
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
return self.writeCfgFile()
|
||||||
|
# END stop
|
||||||
|
# END OptionsParser
|
|
@ -0,0 +1,37 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
## common/plugin.py
|
||||||
|
##
|
||||||
|
## Gajim Team:
|
||||||
|
## - Yann Le Boulanger <asterix@crans.org>
|
||||||
|
## - Vincent Hanquez <tab@tuxfamily.org>
|
||||||
|
## - David Ferlier <krp@yazzy.org>
|
||||||
|
##
|
||||||
|
## Copyright (C) 2003 Gajim Team
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published
|
||||||
|
## by the Free Software Foundation; version 2 only.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
|
||||||
|
import common.thread
|
||||||
|
|
||||||
|
""" Plugin definitions """
|
||||||
|
|
||||||
|
class GajimPlugin:
|
||||||
|
def __init__(self, name, queueIn, queueOut):
|
||||||
|
""" queueIn is a queue to interact from the hub to the plugin """
|
||||||
|
self.name = name
|
||||||
|
self.queueIn = queueIn
|
||||||
|
self.queueOut= queueOut
|
||||||
|
# END __init__
|
||||||
|
|
||||||
|
def load(self):
|
||||||
|
self.thr = common.thread.GajimThread(self.name, self.queueIn, \
|
||||||
|
self.queueOut)
|
||||||
|
# END load
|
||||||
|
# END GajimPlugin
|
|
@ -0,0 +1,40 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
## common/thread.py
|
||||||
|
##
|
||||||
|
## Gajim Team:
|
||||||
|
## - Yann Le Boulanger <asterix@crans.org>
|
||||||
|
## - Vincent Hanquez <tab@tuxfamily.org>
|
||||||
|
## - David Ferlier <krp@yazzy.org>
|
||||||
|
##
|
||||||
|
## Copyright (C) 2003 Gajim Team
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published
|
||||||
|
## by the Free Software Foundation; version 2 only.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
|
||||||
|
import threading
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import plugins
|
||||||
|
|
||||||
|
class GajimThread(threading.Thread):
|
||||||
|
def __init__(self, name = None, queueIn = None, queueOut = None):
|
||||||
|
self.queueIn = queueIn
|
||||||
|
self.queueOut = queueOut
|
||||||
|
threading.Thread.__init__(self, target = self.run, \
|
||||||
|
name = name, args = () )
|
||||||
|
self.start()
|
||||||
|
# END __init__
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
if self.getName() == 'gtkgui':
|
||||||
|
plugins.gtkgui.plugin(self.queueIn, self.queueOut)
|
||||||
|
# END run
|
||||||
|
# END GajimThread
|
|
@ -0,0 +1,605 @@
|
||||||
|
## xmlstream.py
|
||||||
|
##
|
||||||
|
## Copyright (C) 2001 Matthew Allum
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU Lesser General Public License as published
|
||||||
|
## by the Free Software Foundation; either version 2, or (at your option)
|
||||||
|
## any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
|
|
||||||
|
"""\
|
||||||
|
xmlstream.py provides simple functionality for implementing
|
||||||
|
XML stream based network protocols. It is used as a base
|
||||||
|
for jabber.py.
|
||||||
|
|
||||||
|
xmlstream.py manages the network connectivity and xml parsing
|
||||||
|
of the stream. When a complete 'protocol element' ( meaning a
|
||||||
|
complete child of the xmlstreams root ) is parsed the dipatch
|
||||||
|
method is called with a 'Node' instance of this structure.
|
||||||
|
The Node class is a very simple XML DOM like class for
|
||||||
|
manipulating XML documents or 'protocol elements' in this
|
||||||
|
case.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# $Id: xmlstream.py,v 1.26 2003/02/20 10:22:33 shire Exp $
|
||||||
|
|
||||||
|
import site
|
||||||
|
site.encoding = 'UTF-8'
|
||||||
|
import time, sys, re, socket
|
||||||
|
from select import select
|
||||||
|
from string import split,find,replace,join
|
||||||
|
import xml.parsers.expat
|
||||||
|
|
||||||
|
VERSION = 0.3
|
||||||
|
|
||||||
|
False = 0
|
||||||
|
True = 1
|
||||||
|
|
||||||
|
TCP = 1
|
||||||
|
STDIO = 0
|
||||||
|
TCP_SSL = 2
|
||||||
|
|
||||||
|
ENCODING = site.encoding
|
||||||
|
|
||||||
|
BLOCK_SIZE = 1024 ## Number of bytes to get at at time via socket
|
||||||
|
## transactions
|
||||||
|
|
||||||
|
|
||||||
|
def XMLescape(txt):
|
||||||
|
"Escape XML entities"
|
||||||
|
txt = replace(txt, "&", "&")
|
||||||
|
txt = replace(txt, "<", "<")
|
||||||
|
txt = replace(txt, ">", ">")
|
||||||
|
return txt
|
||||||
|
|
||||||
|
def XMLunescape(txt):
|
||||||
|
"Unescape XML entities"
|
||||||
|
txt = replace(txt, "<", "<")
|
||||||
|
txt = replace(txt, ">", ">")
|
||||||
|
txt = replace(txt, "&", "&")
|
||||||
|
return txt
|
||||||
|
|
||||||
|
class error:
|
||||||
|
def __init__(self, value):
|
||||||
|
self.value = str(value)
|
||||||
|
def __str__(self):
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
class Node:
|
||||||
|
"""A simple XML DOM like class"""
|
||||||
|
def __init__(self, tag='', parent=None, attrs=None ):
|
||||||
|
bits = split(tag)
|
||||||
|
if len(bits) == 1:
|
||||||
|
self.name = tag
|
||||||
|
self.namespace = ''
|
||||||
|
else:
|
||||||
|
self.namespace, self.name = bits
|
||||||
|
|
||||||
|
if attrs is None:
|
||||||
|
self.attrs = {}
|
||||||
|
else:
|
||||||
|
self.attrs = attrs
|
||||||
|
|
||||||
|
self.data = []
|
||||||
|
self.kids = []
|
||||||
|
self.parent = parent
|
||||||
|
|
||||||
|
def setParent(self, node):
|
||||||
|
"Set the nodes parent node."
|
||||||
|
self.parent = node
|
||||||
|
|
||||||
|
def getParent(self):
|
||||||
|
"return the nodes parent node."
|
||||||
|
return self.parent
|
||||||
|
|
||||||
|
def getName(self):
|
||||||
|
"Set the nodes tag name."
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def setName(self,val):
|
||||||
|
"Set the nodes tag name."
|
||||||
|
self.name = val
|
||||||
|
|
||||||
|
def putAttr(self, key, val):
|
||||||
|
"Add a name/value attribute to the node."
|
||||||
|
self.attrs[key] = val
|
||||||
|
|
||||||
|
def getAttr(self, key):
|
||||||
|
"Get a value for the nodes named attribute."
|
||||||
|
try: return self.attrs[key]
|
||||||
|
except: return None
|
||||||
|
|
||||||
|
def putData(self, data):
|
||||||
|
"Set the nodes textual data"
|
||||||
|
self.data.append(data)
|
||||||
|
|
||||||
|
def insertData(self, data):
|
||||||
|
"Set the nodes textual data"
|
||||||
|
self.data.append(data)
|
||||||
|
|
||||||
|
def getData(self):
|
||||||
|
"Return the nodes textual data"
|
||||||
|
return join(self.data, '')
|
||||||
|
|
||||||
|
def getDataAsParts(self):
|
||||||
|
"Return the node data as an array"
|
||||||
|
return self.data
|
||||||
|
|
||||||
|
def getNamespace(self):
|
||||||
|
"Returns the nodes namespace."
|
||||||
|
return self.namespace
|
||||||
|
|
||||||
|
def setNamespace(self, namespace):
|
||||||
|
"Set the nodes namespace."
|
||||||
|
self.namespace = namespace
|
||||||
|
|
||||||
|
def insertTag(self, name):
|
||||||
|
""" Add a child tag of name 'name' to the node.
|
||||||
|
|
||||||
|
Returns the newly created node.
|
||||||
|
"""
|
||||||
|
newnode = Node(tag=name, parent=self)
|
||||||
|
self.kids.append(newnode)
|
||||||
|
return newnode
|
||||||
|
|
||||||
|
def insertNode(self, node):
|
||||||
|
"Add a child node to the node"
|
||||||
|
self.kids.append(node)
|
||||||
|
return node
|
||||||
|
|
||||||
|
def insertXML(self, xml_str):
|
||||||
|
"Add raw xml as a child of the node"
|
||||||
|
newnode = NodeBuilder(xml_str).getDom()
|
||||||
|
self.kids.append(newnode)
|
||||||
|
return newnode
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self._xmlnode2str()
|
||||||
|
|
||||||
|
def _xmlnode2str(self, parent=None):
|
||||||
|
"""Returns an xml ( string ) representation of the node
|
||||||
|
and it children"""
|
||||||
|
s = "<" + self.name
|
||||||
|
if self.namespace:
|
||||||
|
if parent and parent.namespace != self.namespace:
|
||||||
|
s = s + " xmlns = '%s' " % self.namespace
|
||||||
|
for key in self.attrs.keys():
|
||||||
|
val = str(self.attrs[key])
|
||||||
|
s = s + " %s='%s'" % ( key, XMLescape(val) )
|
||||||
|
s = s + ">"
|
||||||
|
cnt = 0
|
||||||
|
if self.kids != None:
|
||||||
|
for a in self.kids:
|
||||||
|
if (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt])
|
||||||
|
s = s + a._xmlnode2str(parent=self)
|
||||||
|
cnt=cnt+1
|
||||||
|
if (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt])
|
||||||
|
s = s + "</" + self.name + ">"
|
||||||
|
return s
|
||||||
|
|
||||||
|
def getTag(self, name):
|
||||||
|
"""Returns a child node with tag name. Returns None
|
||||||
|
if not found."""
|
||||||
|
for node in self.kids:
|
||||||
|
if node.getName() == name:
|
||||||
|
return node
|
||||||
|
return None
|
||||||
|
|
||||||
|
def getTags(self, name):
|
||||||
|
"""Like getTag but returns a list with matching child nodes"""
|
||||||
|
nodes=[]
|
||||||
|
for node in self.kids:
|
||||||
|
if node.getName() == name:
|
||||||
|
nodes.append(node)
|
||||||
|
return nodes
|
||||||
|
|
||||||
|
|
||||||
|
def getChildren(self):
|
||||||
|
"""Returns a nodes children"""
|
||||||
|
return self.kids
|
||||||
|
|
||||||
|
class NodeBuilder:
|
||||||
|
"""builds a 'minidom' from data parsed to it. Primarily for insertXML
|
||||||
|
method of Node"""
|
||||||
|
def __init__(self,data):
|
||||||
|
self._parser = xml.parsers.expat.ParserCreate(namespace_separator=' ')
|
||||||
|
self._parser.StartElementHandler = self.unknown_starttag
|
||||||
|
self._parser.EndElementHandler = self.unknown_endtag
|
||||||
|
self._parser.CharacterDataHandler = self.handle_data
|
||||||
|
|
||||||
|
self.__depth = 0
|
||||||
|
self.__done = 0 #needed ?
|
||||||
|
self.__space_regex = re.compile('^\s+$')
|
||||||
|
|
||||||
|
self._parser.Parse(data,1)
|
||||||
|
|
||||||
|
def unknown_starttag(self, tag, attrs):
|
||||||
|
self.__depth = self.__depth + 1
|
||||||
|
if self.__depth == 1:
|
||||||
|
self._mini_dom = Node(tag=tag, attrs=attrs)
|
||||||
|
self._ptr = self._mini_dom
|
||||||
|
elif self.__depth > 1:
|
||||||
|
self._ptr.kids.append(Node(tag =tag,
|
||||||
|
parent=self._ptr,
|
||||||
|
attrs =attrs ))
|
||||||
|
self._ptr = self._ptr.kids[-1]
|
||||||
|
else: ## fix this ....
|
||||||
|
pass
|
||||||
|
|
||||||
|
def unknown_endtag(self, tag ):
|
||||||
|
self.__depth = self.__depth - 1
|
||||||
|
if self.__depth == 0:
|
||||||
|
self.dispatch(self._mini_dom)
|
||||||
|
elif self.__depth > 0:
|
||||||
|
self._ptr = self._ptr.parent
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def handle_data(self, data):
|
||||||
|
if not self.__space_regex.match(data): ## check its not all blank
|
||||||
|
self._ptr.data.append(data)
|
||||||
|
|
||||||
|
def dispatch(self,dom):
|
||||||
|
self.__done = 1
|
||||||
|
|
||||||
|
def getDom(self):
|
||||||
|
return self._mini_dom
|
||||||
|
|
||||||
|
|
||||||
|
class Stream:
|
||||||
|
def __init__(
|
||||||
|
self, host, port, namespace,
|
||||||
|
debug=True,
|
||||||
|
log=None,
|
||||||
|
sock=None,
|
||||||
|
id=None,
|
||||||
|
connection=TCP
|
||||||
|
):
|
||||||
|
|
||||||
|
|
||||||
|
self._parser = xml.parsers.expat.ParserCreate(namespace_separator=' ')
|
||||||
|
self._parser.StartElementHandler = self._unknown_starttag
|
||||||
|
self._parser.EndElementHandler = self._unknown_endtag
|
||||||
|
self._parser.CharacterDataHandler = self._handle_data
|
||||||
|
|
||||||
|
self._host = host
|
||||||
|
self._port = port
|
||||||
|
self._namespace = namespace
|
||||||
|
self.__depth = 0
|
||||||
|
self._sock = sock
|
||||||
|
|
||||||
|
self._sslObj = None
|
||||||
|
self._sslIssuer = None
|
||||||
|
self._sslServer = None
|
||||||
|
|
||||||
|
self._incomingID = None
|
||||||
|
self._outgoingID = id
|
||||||
|
|
||||||
|
self._debug = debug
|
||||||
|
self._connection=connection
|
||||||
|
|
||||||
|
self.DEBUG("stream init called")
|
||||||
|
|
||||||
|
if log:
|
||||||
|
if type(log) is type(""):
|
||||||
|
try:
|
||||||
|
self._logFH = open(log,'w')
|
||||||
|
except:
|
||||||
|
print "ERROR: can open %s for writing"
|
||||||
|
sys.exit(0)
|
||||||
|
else: ## assume its a stream type object
|
||||||
|
self._logFH = log
|
||||||
|
else:
|
||||||
|
self._logFH = None
|
||||||
|
self._timestampLog = True
|
||||||
|
|
||||||
|
def timestampLog(self,timestamp):
|
||||||
|
""" Enable or disable the showing of a timestamp in the log.
|
||||||
|
By default, timestamping is enabled.
|
||||||
|
"""
|
||||||
|
self._timestampLog = timestamp
|
||||||
|
|
||||||
|
def DEBUG(self,txt):
|
||||||
|
if self._debug:
|
||||||
|
try:
|
||||||
|
sys.stderr.write("DEBUG: %s\n" % txt)
|
||||||
|
except:
|
||||||
|
# unicode strikes again ;)
|
||||||
|
s=u''
|
||||||
|
for i in range(len(txt)):
|
||||||
|
if ord(txt[i]) < 128:
|
||||||
|
c = txt[i]
|
||||||
|
else:
|
||||||
|
c = '?'
|
||||||
|
s=s+c
|
||||||
|
sys.stderr.write("DEBUG: %s\n" % s )
|
||||||
|
|
||||||
|
def getSocket(self):
|
||||||
|
return self._sock
|
||||||
|
|
||||||
|
def header(self):
|
||||||
|
self.DEBUG("stream: sending initial header")
|
||||||
|
str = u"<?xml version='1.0' encoding='UTF-8' ?> \
|
||||||
|
<stream:stream to='%s' xmlns='%s'" % ( self._host,
|
||||||
|
self._namespace )
|
||||||
|
|
||||||
|
if self._outgoingID: str = str + " id='%s' " % self._outgoingID
|
||||||
|
str = str + " xmlns:stream='http://etherx.jabber.org/streams'>"
|
||||||
|
self.write (str)
|
||||||
|
self.read()
|
||||||
|
|
||||||
|
def _handle_data(self, data):
|
||||||
|
"""XML Parser callback"""
|
||||||
|
self.DEBUG("data-> " + data)
|
||||||
|
## TODO: get rid of empty space
|
||||||
|
## self._ptr.data = self._ptr.data + data
|
||||||
|
self._ptr.data.append(data)
|
||||||
|
|
||||||
|
def _unknown_starttag(self, tag, attrs):
|
||||||
|
"""XML Parser callback"""
|
||||||
|
self.__depth = self.__depth + 1
|
||||||
|
self.DEBUG("DEPTH -> %i , tag -> %s, attrs -> %s" % \
|
||||||
|
(self.__depth, tag, str(attrs)) )
|
||||||
|
if self.__depth == 2:
|
||||||
|
self._mini_dom = Node(tag=tag, attrs=attrs)
|
||||||
|
self._ptr = self._mini_dom
|
||||||
|
elif self.__depth > 2:
|
||||||
|
self._ptr.kids.append(Node(tag=tag,parent=self._ptr,attrs=attrs))
|
||||||
|
self._ptr = self._ptr.kids[-1]
|
||||||
|
else: ## it the stream tag:
|
||||||
|
if attrs.has_key('id'):
|
||||||
|
self._incomingID = attrs['id']
|
||||||
|
|
||||||
|
def _unknown_endtag(self, tag ):
|
||||||
|
"""XML Parser callback"""
|
||||||
|
self.__depth = self.__depth - 1
|
||||||
|
self.DEBUG("DEPTH -> %i" % self.__depth)
|
||||||
|
if self.__depth == 1:
|
||||||
|
self.dispatch(self._mini_dom)
|
||||||
|
elif self.__depth > 1:
|
||||||
|
self._ptr = self._ptr.parent
|
||||||
|
else:
|
||||||
|
self.DEBUG("*** Server closed connection ? ****")
|
||||||
|
|
||||||
|
def dispatch(self, nodes, depth = 0):
|
||||||
|
"""Overide with the method you want to called with
|
||||||
|
a node structure of a 'protocol element."""
|
||||||
|
|
||||||
|
padding = ' '
|
||||||
|
padding = padding * depth
|
||||||
|
depth = depth + 1
|
||||||
|
for n in nodes:
|
||||||
|
if n.kids != None:
|
||||||
|
self.dispatch(n.kids, depth)
|
||||||
|
|
||||||
|
##def syntax_error(self, message):
|
||||||
|
## self.DEBUG("error " + message)
|
||||||
|
|
||||||
|
def _do_read( self, action, buff_size ):
|
||||||
|
"""workhorse for read() method.
|
||||||
|
|
||||||
|
added 021231 by jaclu"""
|
||||||
|
data=''
|
||||||
|
data_in = action(buff_size)
|
||||||
|
while data_in:
|
||||||
|
data = data + data_in
|
||||||
|
if len(data_in) != buff_size:
|
||||||
|
break
|
||||||
|
data_in = action(buff_size)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
"""Reads incoming data. Called by process() so nonblocking
|
||||||
|
|
||||||
|
changed 021231 by jaclu
|
||||||
|
"""
|
||||||
|
if self._connection == TCP:
|
||||||
|
raw_data = self._do_read(self._sock.recv, BLOCK_SIZE)
|
||||||
|
elif self._connection == TCP_SSL:
|
||||||
|
raw_data = self._do_read(self._sslObj.read, BLOCK_SIZE)
|
||||||
|
elif self._connection == STDIO:
|
||||||
|
raw_data = self._do_read(self.stdin.read, 1024)
|
||||||
|
else:
|
||||||
|
raw_data = '' # should never get here
|
||||||
|
|
||||||
|
# just encode incoming data once!
|
||||||
|
data = unicode(raw_data,'utf-8').encode(ENCODING,'replace')
|
||||||
|
self.DEBUG("got data %s" % data )
|
||||||
|
self.log(data, 'RECV:')
|
||||||
|
self._parser.Parse(data)
|
||||||
|
return data
|
||||||
|
|
||||||
|
def write(self,raw_data=u''):
|
||||||
|
"""Writes raw outgoing data. blocks
|
||||||
|
|
||||||
|
changed 021231 by jaclu, added unicode encoding
|
||||||
|
"""
|
||||||
|
if type(raw_data) == type(u''):
|
||||||
|
data_out = raw_data.encode('utf-8','replace')
|
||||||
|
else:
|
||||||
|
# since not suplied as unicode, we must guess at
|
||||||
|
# what the data is, iso-8859-1 seems reasonable.
|
||||||
|
# To avoid this auto assumption,
|
||||||
|
# send your data as a unicode string!
|
||||||
|
data_out = unicode(raw_data,'iso-8859-1').encode(ENCODING,'replace')
|
||||||
|
try:
|
||||||
|
if self._connection == TCP:
|
||||||
|
self._sock.send (data_out)
|
||||||
|
elif self._connection == TCP_SSL:
|
||||||
|
self._sslObj.write(data_out)
|
||||||
|
elif self._connection == STDIO:
|
||||||
|
self.stdout.write(data_out)
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
self.log(data_out, 'SENT:')
|
||||||
|
self.DEBUG("sent %s" % data_out)
|
||||||
|
except:
|
||||||
|
self.DEBUG("xmlstream write threw error")
|
||||||
|
self.disconnected()
|
||||||
|
|
||||||
|
def process(self,timeout):
|
||||||
|
|
||||||
|
reader=Node
|
||||||
|
|
||||||
|
if self._connection == TCP:
|
||||||
|
reader = self._sock
|
||||||
|
elif self._connection == TCP_SSL:
|
||||||
|
reader = self._sock
|
||||||
|
elif self._connection == STDIO:
|
||||||
|
reader = sys.stdin
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
ready_for_read,ready_for_write,err = \
|
||||||
|
select( [reader],[],[],timeout)
|
||||||
|
for s in ready_for_read:
|
||||||
|
if s == reader:
|
||||||
|
if not len(self.read()): # length of 0 means disconnect
|
||||||
|
## raise error("network error") ?
|
||||||
|
self.disconnected()
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def disconnect(self):
|
||||||
|
"""Close the stream and socket"""
|
||||||
|
self.write ( "</stream:stream>" )
|
||||||
|
self._sock.close()
|
||||||
|
self._sock = None
|
||||||
|
|
||||||
|
def disconnected(self): ## To be overidden ##
|
||||||
|
"""Called when a Network Error or disconnection occurs.
|
||||||
|
Designed to be overidden"""
|
||||||
|
self.DEBUG("Network Disconnection")
|
||||||
|
pass
|
||||||
|
|
||||||
|
def log(self, data, inout=''):
|
||||||
|
"""Logs data to the specified filehandle. Data is time stamped
|
||||||
|
and prefixed with inout"""
|
||||||
|
if self._logFH is not None:
|
||||||
|
if self._timestampLog:
|
||||||
|
self._logFH.write("%s - %s - %s\n" % (time.asctime(), inout, data))
|
||||||
|
else:
|
||||||
|
self._logFH.write("%s - %s\n" % (inout, data ) )
|
||||||
|
self._logFH.flush()
|
||||||
|
|
||||||
|
def getIncomingID(self):
|
||||||
|
"""Returns the streams ID"""
|
||||||
|
return self._incomingID
|
||||||
|
|
||||||
|
def getOutgoingID(self):
|
||||||
|
"""Returns the streams ID"""
|
||||||
|
return self._incomingID
|
||||||
|
|
||||||
|
|
||||||
|
class Client(Stream):
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
"""Attempt to connect to specified host"""
|
||||||
|
|
||||||
|
self.DEBUG("client connect called to %s %s type %i" % (self._host,
|
||||||
|
self._port,
|
||||||
|
self._connection) )
|
||||||
|
|
||||||
|
## TODO: check below that stdin/stdout are actually open
|
||||||
|
if self._connection == STDIO: return
|
||||||
|
|
||||||
|
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
try:
|
||||||
|
self._sock.connect((self._host, self._port))
|
||||||
|
except socket.error, e:
|
||||||
|
self.DEBUG("socket error")
|
||||||
|
raise error(e)
|
||||||
|
|
||||||
|
if self._connection == TCP_SSL:
|
||||||
|
try:
|
||||||
|
self.DEBUG("Attempting to create ssl socket")
|
||||||
|
self._sslObj = socket.ssl( self._sock, None, None )
|
||||||
|
self._sslIssuer = self._sslObj.issuer()
|
||||||
|
self._sslServer = self._sslObj.server()
|
||||||
|
except:
|
||||||
|
self.DEBUG("Socket Error: No SSL Support")
|
||||||
|
raise error("No SSL Support")
|
||||||
|
|
||||||
|
self.DEBUG("connected")
|
||||||
|
self.header()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
class Server:
|
||||||
|
|
||||||
|
def now(self): return time.ctime(time.time())
|
||||||
|
|
||||||
|
def __init__(self, maxclients=10):
|
||||||
|
|
||||||
|
self.host = ''
|
||||||
|
self.port = 5222
|
||||||
|
self.streams = []
|
||||||
|
|
||||||
|
# make main sockets for accepting new client requests
|
||||||
|
self.mainsocks, self.readsocks, self.writesocks = [], [], []
|
||||||
|
|
||||||
|
self.portsock = socket(AF_INET, SOCK_STREAM)
|
||||||
|
self.portsock.bind((self.host, self.port))
|
||||||
|
self.portsock.listen(maxclients)
|
||||||
|
|
||||||
|
self.mainsocks.append(self.portsock) # add to main list to identify
|
||||||
|
self.readsocks.append(self.portsock) # add to select inputs list
|
||||||
|
|
||||||
|
# event loop: listen and multiplex until server process killed
|
||||||
|
|
||||||
|
|
||||||
|
def serve(self):
|
||||||
|
|
||||||
|
print 'select-server loop starting'
|
||||||
|
|
||||||
|
while 1:
|
||||||
|
print "LOOPING"
|
||||||
|
readables, writeables, exceptions = select(self.readsocks,
|
||||||
|
self.writesocks, [])
|
||||||
|
for sockobj in readables:
|
||||||
|
if sockobj in self. mainsocks: # for ready input sockets
|
||||||
|
newsock, address = sockobj.accept() # accept not block
|
||||||
|
print 'Connect:', address, id(newsock)
|
||||||
|
self.readsocks.append(newsock)
|
||||||
|
self._makeNewStream(newsock)
|
||||||
|
# add to select list, wait
|
||||||
|
else:
|
||||||
|
# client socket: read next line
|
||||||
|
data = sockobj.recv(1024)
|
||||||
|
# recv should not block
|
||||||
|
print '\tgot', data, 'on', id(sockobj)
|
||||||
|
if not data: # if closed by the clients
|
||||||
|
sockobj.close() # close here and remv from
|
||||||
|
self.readsocks.remove(sockobj)
|
||||||
|
else:
|
||||||
|
# this may block: should really select for writes too
|
||||||
|
sockobj.send('Echo=>%s' % data)
|
||||||
|
|
||||||
|
def _makeNewStream(self, sckt):
|
||||||
|
new_stream = Stream('localhost', 5222,
|
||||||
|
'jabber:client',
|
||||||
|
sock=sckt)
|
||||||
|
self.streams.append(new_stream)
|
||||||
|
## maybe overide for a 'server stream'
|
||||||
|
new_stream.header()
|
||||||
|
return new_stream
|
||||||
|
|
||||||
|
def _getStreamSockets(self):
|
||||||
|
socks = [];
|
||||||
|
for s in self.streams:
|
||||||
|
socks.append(s.getSocket())
|
||||||
|
return socks
|
||||||
|
|
||||||
|
def _getStreamFromSocket(self, sock):
|
||||||
|
for s in self.streams:
|
||||||
|
if s.getSocket() == sock:
|
||||||
|
return s
|
||||||
|
return None
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
si un server deconne, se connecter a un autre
|
||||||
|
* Gui :
|
||||||
|
calcule de idle time
|
|
@ -0,0 +1,2 @@
|
||||||
|
10/10/2003 23h39 : premier passage online
|
||||||
|
12/10/2003 02h23 : affichage de la contact list
|
|
@ -0,0 +1,9 @@
|
||||||
|
[Server]
|
||||||
|
|
||||||
|
hostname = SERVER HOSTNAME
|
||||||
|
|
||||||
|
[Profile]
|
||||||
|
|
||||||
|
name = LOGIN NAME
|
||||||
|
password = PASSWORD
|
||||||
|
ressource = gajim
|
|
@ -0,0 +1 @@
|
||||||
|
import gtkgui
|
|
@ -0,0 +1,404 @@
|
||||||
|
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
|
||||||
|
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
|
||||||
|
|
||||||
|
<glade-interface>
|
||||||
|
|
||||||
|
<widget class="GtkWindow" id="Gajim">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="title" translatable="yes">Gajim</property>
|
||||||
|
<property name="type">GTK_WINDOW_TOPLEVEL</property>
|
||||||
|
<property name="window_position">GTK_WIN_POS_NONE</property>
|
||||||
|
<property name="modal">False</property>
|
||||||
|
<property name="default_width">100</property>
|
||||||
|
<property name="default_height">300</property>
|
||||||
|
<property name="resizable">True</property>
|
||||||
|
<property name="destroy_with_parent">False</property>
|
||||||
|
<signal name="destroy" handler="gtk_main_quit" last_modification_time="Wed, 24 Sep 2003 20:54:02 GMT"/>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkVBox" id="vbox1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
<property name="spacing">0</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkHandleBox" id="handlebox2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="shadow_type">GTK_SHADOW_OUT</property>
|
||||||
|
<property name="handle_position">GTK_POS_LEFT</property>
|
||||||
|
<property name="snap_edge">GTK_POS_TOP</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkMenuBar" id="menubar1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkMenuItem" id="gajim">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">_Gajim</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkMenu" id="gajim_menu">
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkMenuItem" id="separatormenuitem1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkImageMenuItem" id="quit">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label">gtk-quit</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
<signal name="activate" handler="on_quit_activate" last_modification_time="Fri, 03 Oct 2003 12:49:50 GMT"/>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkMenuItem" id="help">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">_?</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkMenu" id="help_menu">
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkMenuItem" id="about">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">_About</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<signal name="activate" handler="on_about_activate" last_modification_time="Fri, 03 Oct 2003 12:49:50 GMT"/>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkHandleBox" id="handlebox1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="shadow_type">GTK_SHADOW_OUT</property>
|
||||||
|
<property name="handle_position">GTK_POS_LEFT</property>
|
||||||
|
<property name="snap_edge">GTK_POS_TOP</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkToolbar" id="toolbar1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
|
||||||
|
<property name="toolbar_style">GTK_TOOLBAR_BOTH</property>
|
||||||
|
<property name="tooltips">True</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkScrolledWindow" id="scrolledwindow">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||||
|
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||||
|
<property name="shadow_type">GTK_SHADOW_NONE</property>
|
||||||
|
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkTreeView" id="treeview">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="headers_visible">False</property>
|
||||||
|
<property name="rules_hint">False</property>
|
||||||
|
<property name="reorderable">False</property>
|
||||||
|
<property name="enable_search">True</property>
|
||||||
|
<signal name="button_press_event" handler="on_treeview_event" last_modification_time="Tue, 30 Sep 2003 09:11:17 GMT"/>
|
||||||
|
<signal name="row_activated" handler="on_row_activated" last_modification_time="Sun, 12 Oct 2003 18:11:52 GMT"/>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkOptionMenu" id="optionmenu">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="history">0</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkMenu" id="menu_status">
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkMenuItem" id="online">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Online</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<signal name="activate" handler="on_status_changed" last_modification_time="Thu, 09 Oct 2003 19:59:52 GMT"/>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkMenuItem" id="away">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Away</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<signal name="activate" handler="on_status_changed" last_modification_time="Thu, 09 Oct 2003 19:59:52 GMT"/>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkMenuItem" id="na">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">NA</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<signal name="activate" handler="on_status_changed" last_modification_time="Thu, 09 Oct 2003 19:59:52 GMT"/>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkMenuItem" id="dnd">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">DND</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<signal name="activate" handler="on_status_changed" last_modification_time="Thu, 09 Oct 2003 19:59:52 GMT"/>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkMenuItem" id="invisible">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Invisible</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<signal name="activate" handler="on_status_changed" last_modification_time="Thu, 09 Oct 2003 19:59:52 GMT"/>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkMenuItem" id="menuitem6">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkMenuItem" id="offline">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Offline</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<signal name="activate" handler="on_status_changed" last_modification_time="Thu, 09 Oct 2003 20:00:58 GMT"/>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
|
||||||
|
<widget class="GtkWindow" id="Chat">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="title" translatable="yes">Chat</property>
|
||||||
|
<property name="type">GTK_WINDOW_TOPLEVEL</property>
|
||||||
|
<property name="window_position">GTK_WIN_POS_NONE</property>
|
||||||
|
<property name="modal">False</property>
|
||||||
|
<property name="default_width">400</property>
|
||||||
|
<property name="default_height">300</property>
|
||||||
|
<property name="resizable">True</property>
|
||||||
|
<property name="destroy_with_parent">False</property>
|
||||||
|
<signal name="destroy" handler="gtk_widget_destroy" last_modification_time="Sun, 12 Oct 2003 18:17:05 GMT"/>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkVBox" id="vbox2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
<property name="spacing">0</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkHBox" id="hbox1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="homogeneous">False</property>
|
||||||
|
<property name="spacing">0</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkButton" id="button1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label" translatable="yes">button1</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkButton" id="button2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="label" translatable="yes">button2</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="relief">GTK_RELIEF_NORMAL</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkLabel" id="label_contact">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">contact</property>
|
||||||
|
<property name="use_underline">False</property>
|
||||||
|
<property name="use_markup">False</property>
|
||||||
|
<property name="justify">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap">False</property>
|
||||||
|
<property name="selectable">False</property>
|
||||||
|
<property name="xalign">0.5</property>
|
||||||
|
<property name="yalign">0.5</property>
|
||||||
|
<property name="xpad">0</property>
|
||||||
|
<property name="ypad">0</property>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkVPaned" id="vpaned1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="position">190</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkScrolledWindow" id="scrolledwindow3">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||||
|
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||||
|
<property name="shadow_type">GTK_SHADOW_IN</property>
|
||||||
|
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkTextView" id="conversation">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="editable">False</property>
|
||||||
|
<property name="justification">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap_mode">GTK_WRAP_NONE</property>
|
||||||
|
<property name="cursor_visible">False</property>
|
||||||
|
<property name="pixels_above_lines">0</property>
|
||||||
|
<property name="pixels_below_lines">0</property>
|
||||||
|
<property name="pixels_inside_wrap">0</property>
|
||||||
|
<property name="left_margin">0</property>
|
||||||
|
<property name="right_margin">0</property>
|
||||||
|
<property name="indent">0</property>
|
||||||
|
<property name="text" translatable="yes"></property>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="shrink">True</property>
|
||||||
|
<property name="resize">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkScrolledWindow" id="scrolledwindow4">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||||
|
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
|
||||||
|
<property name="shadow_type">GTK_SHADOW_IN</property>
|
||||||
|
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<widget class="GtkTextView" id="message">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="editable">True</property>
|
||||||
|
<property name="justification">GTK_JUSTIFY_LEFT</property>
|
||||||
|
<property name="wrap_mode">GTK_WRAP_WORD</property>
|
||||||
|
<property name="cursor_visible">True</property>
|
||||||
|
<property name="pixels_above_lines">0</property>
|
||||||
|
<property name="pixels_below_lines">0</property>
|
||||||
|
<property name="pixels_inside_wrap">0</property>
|
||||||
|
<property name="left_margin">0</property>
|
||||||
|
<property name="right_margin">0</property>
|
||||||
|
<property name="indent">0</property>
|
||||||
|
<property name="text" translatable="yes"></property>
|
||||||
|
<signal name="key_press_event" handler="on_msg_key_press_event" last_modification_time="Mon, 13 Oct 2003 17:48:48 GMT"/>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="shrink">True</property>
|
||||||
|
<property name="resize">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
<packing>
|
||||||
|
<property name="padding">0</property>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
</child>
|
||||||
|
</widget>
|
||||||
|
|
||||||
|
</glade-interface>
|
|
@ -0,0 +1,244 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
## plugins/gtkgui.py
|
||||||
|
##
|
||||||
|
## Gajim Team:
|
||||||
|
## - Yann Le Boulanger <asterix@crans.org>
|
||||||
|
## - Vincent Hanquez <tab@tuxfamily.org>
|
||||||
|
## - David Ferlier <krp@yazzy.org>
|
||||||
|
##
|
||||||
|
## Copyright (C) 2003 Gajim Team
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published
|
||||||
|
## by the Free Software Foundation; version 2 only.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
|
||||||
|
import pygtk
|
||||||
|
pygtk.require('2.0')
|
||||||
|
import gtk
|
||||||
|
import gtk.glade
|
||||||
|
import gobject
|
||||||
|
|
||||||
|
class user:
|
||||||
|
def __init__(self, *args):
|
||||||
|
if len(args) == 0:
|
||||||
|
self.name = ''
|
||||||
|
self.group = ''
|
||||||
|
self.show = ''
|
||||||
|
self.status = ''
|
||||||
|
elif len(args) == 4:
|
||||||
|
self.name = args[0]
|
||||||
|
self.group = args[1]
|
||||||
|
self.show = args[2]
|
||||||
|
self.status = args[3]
|
||||||
|
elif ((len(args)) and (type (args[0]) == type (self)) and
|
||||||
|
(self.__class__ == args[0].__class__)):
|
||||||
|
self.name = args[0].name
|
||||||
|
self.group = args[0].group
|
||||||
|
self.show = args[0].show
|
||||||
|
self.status = args[0].status
|
||||||
|
else: raise TypeError, 'bad arguments'
|
||||||
|
|
||||||
|
class message:
|
||||||
|
def delete_event(self, widget):
|
||||||
|
del self.roster.tab_messages[self.jid]
|
||||||
|
self.window.destroy()
|
||||||
|
|
||||||
|
def print_conversation(self, txt, contact = None):
|
||||||
|
txt_buffer = self.conversation.get_buffer()
|
||||||
|
end_iter = txt_buffer.get_end_iter()
|
||||||
|
if contact: who = 'moi'
|
||||||
|
else: who = 'lui'
|
||||||
|
txt_buffer.insert(end_iter, '<'+who+'> '+txt+'\n', -1)
|
||||||
|
|
||||||
|
def on_msg_key_press_event(self, widget, event):
|
||||||
|
if event.keyval == gtk.keysyms.Return:
|
||||||
|
if (event.state & gtk.gdk.SHIFT_MASK):
|
||||||
|
return 0
|
||||||
|
txt_buffer = widget.get_buffer()
|
||||||
|
start_iter = txt_buffer.get_start_iter()
|
||||||
|
end_iter = txt_buffer.get_end_iter()
|
||||||
|
txt = txt_buffer.get_text(start_iter, end_iter, 0)
|
||||||
|
self.roster.queueOUT.put(('MSG',(self.jid, txt)))
|
||||||
|
txt_buffer.set_text('', -1)
|
||||||
|
self.print_conversation(txt, self.jid)
|
||||||
|
widget.grab_focus()
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def __init__(self, jid, roster):
|
||||||
|
self.jid = jid
|
||||||
|
self.roster = roster
|
||||||
|
self.xml = gtk.glade.XML('plugins/gtkgui.glade', 'Chat')
|
||||||
|
self.window = self.xml.get_widget('Chat')
|
||||||
|
self.window.set_title('Chat with ' + jid)
|
||||||
|
self.message = self.xml.get_widget('message')
|
||||||
|
self.conversation = self.xml.get_widget('conversation')
|
||||||
|
self.window.show()
|
||||||
|
self.xml.signal_connect('gtk_widget_destroy', self.delete_event)
|
||||||
|
self.xml.signal_connect('on_msg_key_press_event', self.on_msg_key_press_event)
|
||||||
|
|
||||||
|
class roster:
|
||||||
|
def get_icon_pixbuf(self, stock):
|
||||||
|
return self.tree.render_icon(stock, size = gtk.ICON_SIZE_MENU, detail = None)
|
||||||
|
|
||||||
|
def mkl_group(self):
|
||||||
|
self.l_group = []
|
||||||
|
for u in self.l_contact:
|
||||||
|
if u.group in self.l_group:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.l_group.append(u.group)
|
||||||
|
|
||||||
|
def mkroster(self):
|
||||||
|
self.treestore.clear()
|
||||||
|
for g in self.l_group:
|
||||||
|
iter_g = self.treestore.append(None, (self.pixbufs['online'], g, 'group'))
|
||||||
|
for c in self.l_contact:
|
||||||
|
if c.group == g:
|
||||||
|
# print c.status
|
||||||
|
self.treestore.append(iter_g, (self.pixbufs[c.show], c.name, c.show))
|
||||||
|
# if c.status == 'Online':
|
||||||
|
# self.treestore.append(iter_g, (self.pixbufs['Online'], c.name, 'Online'))
|
||||||
|
# elif c.status == None:
|
||||||
|
# self.treestore.append(iter_g, (self.pixbufs['away'], c.name, 'away'))
|
||||||
|
|
||||||
|
def mkroster2(self, tab):
|
||||||
|
self.l_contact = []
|
||||||
|
for jid in tab.keys():
|
||||||
|
user1 = user(jid, 'general', tab[jid]["Show"], tab[jid]["Status"])
|
||||||
|
self.l_contact.append(user1)
|
||||||
|
self.mkl_group()
|
||||||
|
self.mkroster()
|
||||||
|
self.tree.collapse_row((0,3))
|
||||||
|
|
||||||
|
def update_iter(self, widget, path, iter, data):
|
||||||
|
val = self.treestore.get_value(iter, 1)
|
||||||
|
if val == data[0]:
|
||||||
|
self.treestore.set_value(iter, 0, self.pixbufs[data[1]])
|
||||||
|
|
||||||
|
def chg_status(self, jid, show, status):
|
||||||
|
for u in self.l_contact:
|
||||||
|
if u.name == jid:
|
||||||
|
u.show = show
|
||||||
|
u.status = status
|
||||||
|
self.treestore.foreach(self.update_iter, (jid, show))
|
||||||
|
|
||||||
|
def mk_menu_c(self, event):
|
||||||
|
self.menu_c = gtk.Menu()
|
||||||
|
item = gtk.MenuItem("user1")
|
||||||
|
self.menu_c.append(item)
|
||||||
|
item = gtk.MenuItem("user2")
|
||||||
|
self.menu_c.append(item)
|
||||||
|
item = gtk.MenuItem("user3")
|
||||||
|
self.menu_c.append(item)
|
||||||
|
self.menu_c.popup(None, None, None, event.button, event.time)
|
||||||
|
self.menu_c.show_all()
|
||||||
|
|
||||||
|
def mk_menu_g(self, event):
|
||||||
|
self.menu_c = gtk.Menu()
|
||||||
|
item = gtk.MenuItem("grp1")
|
||||||
|
self.menu_c.append(item)
|
||||||
|
item = gtk.MenuItem("grp2")
|
||||||
|
self.menu_c.append(item)
|
||||||
|
item = gtk.MenuItem("grp3")
|
||||||
|
self.menu_c.append(item)
|
||||||
|
self.menu_c.popup(None, None, None, event.button, event.time)
|
||||||
|
self.menu_c.show_all()
|
||||||
|
|
||||||
|
def on_treeview_event(self, widget, event):
|
||||||
|
if (event.button == 3) & (event.type == gtk.gdk.BUTTON_PRESS):
|
||||||
|
try:
|
||||||
|
path, column, x, y = self.tree.get_path_at_pos(int(event.x), int(event.y))
|
||||||
|
|
||||||
|
except TypeError:
|
||||||
|
return
|
||||||
|
iter = self.treestore.get_iter(path)
|
||||||
|
data = self.treestore.get_value(iter, 2)
|
||||||
|
if data == 'group':
|
||||||
|
self.mk_menu_g(event)
|
||||||
|
else:
|
||||||
|
self.mk_menu_c(event)
|
||||||
|
return gtk.TRUE
|
||||||
|
return gtk.FALSE
|
||||||
|
|
||||||
|
def on_status_changed(self, widget):
|
||||||
|
self.queueOUT.put(('STATUS',widget.name))
|
||||||
|
|
||||||
|
def on_quit(self, widget):
|
||||||
|
self.queueOUT.put(('QUIT',''))
|
||||||
|
gtk.mainquit()
|
||||||
|
|
||||||
|
def on_row_activated(self, widget, path, col):
|
||||||
|
iter = self.treestore.get_iter(path)
|
||||||
|
jid = self.treestore.get_value(iter, 1)
|
||||||
|
if self.tab_messages.has_key(jid):
|
||||||
|
#NE FONCTIONNE PAS !
|
||||||
|
self.tab_messages[jid].window.grab_focus()
|
||||||
|
else:
|
||||||
|
self.tab_messages[jid]=message(jid, self)
|
||||||
|
|
||||||
|
def __init__(self, queueOUT):
|
||||||
|
#initialisation des variables
|
||||||
|
# FIXME : handle no file ...
|
||||||
|
self.xml = gtk.glade.XML('plugins/gtkgui.glade', 'Gajim')
|
||||||
|
self.tree = self.xml.get_widget('treeview')
|
||||||
|
self.treestore = gtk.TreeStore(gtk.gdk.Pixbuf, str, str)
|
||||||
|
add_pixbuf = self.get_icon_pixbuf(gtk.STOCK_ADD)
|
||||||
|
remove_pixbuf = self.get_icon_pixbuf(gtk.STOCK_REMOVE)
|
||||||
|
self.pixbufs = {"online":add_pixbuf, "away":remove_pixbuf, "xa":remove_pixbuf, "dnd":remove_pixbuf, "offline":remove_pixbuf}
|
||||||
|
self.tree.set_model(self.treestore)
|
||||||
|
self.queueOUT = queueOUT
|
||||||
|
self.optionmenu = self.xml.get_widget('optionmenu')
|
||||||
|
self.optionmenu.set_history(6)
|
||||||
|
self.tab_messages = {}
|
||||||
|
#colonnes
|
||||||
|
self.col = gtk.TreeViewColumn()
|
||||||
|
render_pixbuf = gtk.CellRendererPixbuf()
|
||||||
|
self.col.pack_start(render_pixbuf, expand = False)
|
||||||
|
self.col.add_attribute(render_pixbuf, 'pixbuf', 0)
|
||||||
|
render_text = gtk.CellRendererText()
|
||||||
|
self.col.pack_start(render_text, expand = True)
|
||||||
|
self.col.add_attribute(render_text, 'text', 1)
|
||||||
|
self.tree.append_column(self.col)
|
||||||
|
#signales
|
||||||
|
self.xml.signal_connect('gtk_main_quit', self.on_quit)
|
||||||
|
self.xml.signal_connect('on_quit_activate', self.on_quit)
|
||||||
|
self.xml.signal_connect('on_treeview_event', self.on_treeview_event)
|
||||||
|
self.xml.signal_connect('on_status_changed', self.on_status_changed)
|
||||||
|
self.xml.signal_connect('on_row_activated', self.on_row_activated)
|
||||||
|
# self.mk_menu_c()
|
||||||
|
|
||||||
|
class plugin:
|
||||||
|
def read_queue(self):
|
||||||
|
while self.queueIN.empty() == 0:
|
||||||
|
ev = self.queueIN.get()
|
||||||
|
print ev
|
||||||
|
if ev[0] == 'ROSTER':
|
||||||
|
self.r.mkroster2(ev[1])
|
||||||
|
elif ev[0] == 'NOTIFY':
|
||||||
|
self.r.chg_status(ev[1][0], ev[1][1], ev[1][2])
|
||||||
|
elif ev[0] == 'MSG':
|
||||||
|
if not self.r.tab_messages.has_key(ev[1][0]):
|
||||||
|
self.r.tab_messages[ev[1][0]] = message(ev[1][0], self.r)
|
||||||
|
self.r.tab_messages[ev[1][0]].print_conversation(ev[1][1])
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def __init__(self, quIN, quOUT):
|
||||||
|
gtk.threads_init()
|
||||||
|
gtk.threads_enter()
|
||||||
|
self.queueIN = quIN
|
||||||
|
self.r = roster(quOUT)
|
||||||
|
self.time = gtk.timeout_add(200, self.read_queue)
|
||||||
|
gtk.main()
|
||||||
|
gtk.threads_leave()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
plugin(None, None)
|
||||||
|
|
||||||
|
print "plugin gui loaded"
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
## runCore.py
|
||||||
|
##
|
||||||
|
## Gajim Team:
|
||||||
|
## - Yann Le Boulanger <asterix@crans.org>
|
||||||
|
## - Vincent Hanquez <tab@tuxfamily.org>
|
||||||
|
## - David Ferlier <krp@yazzy.org>
|
||||||
|
##
|
||||||
|
## Copyright (C) 2003 Gajim Team
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published
|
||||||
|
## by the Free Software Foundation; version 2 only.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logging.basicConfig()
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.append("..")
|
||||||
|
|
||||||
|
import common
|
||||||
|
import core
|
||||||
|
|
||||||
|
core.core.start()
|
Loading…
Reference in New Issue