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