Initial revision

This commit is contained in:
Vincent Hanquez 2003-10-22 18:45:13 +00:00
commit c0f51fdd1d
15 changed files with 3166 additions and 0 deletions

4
common/__init__.py Normal file
View File

@ -0,0 +1,4 @@
import hub
import jabber
import plugin
import xmlstream

65
common/hub.py Normal file
View File

@ -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

1646
common/jabber.py Normal file

File diff suppressed because it is too large Load Diff

75
common/optparser.py Normal file
View File

@ -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

37
common/plugin.py Normal file
View File

@ -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

40
common/thread.py Normal file
View File

@ -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

605
common/xmlstream.py Normal file
View File

@ -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, "&", "&amp;")
txt = replace(txt, "<", "&lt;")
txt = replace(txt, ">", "&gt;")
return txt
def XMLunescape(txt):
"Unescape XML entities"
txt = replace(txt, "&lt;", "<")
txt = replace(txt, "&gt;", ">")
txt = replace(txt, "&amp;", "&")
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

1
doc/BUGS Normal file
View File

@ -0,0 +1 @@
what to do when a queue is full

3
doc/FEATURE Normal file
View File

@ -0,0 +1,3 @@
si un server deconne, se connecter a un autre
* Gui :
calcule de idle time

2
doc/HISTORY Normal file
View File

@ -0,0 +1,2 @@
10/10/2003 23h39 : premier passage online
12/10/2003 02h23 : affichage de la contact list

9
doc/gajimrc Normal file
View File

@ -0,0 +1,9 @@
[Server]
hostname = SERVER HOSTNAME
[Profile]
name = LOGIN NAME
password = PASSWORD
ressource = gajim

1
plugins/__init__.py Normal file
View File

@ -0,0 +1 @@
import gtkgui

404
plugins/gtkgui.glade Normal file
View File

@ -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>

244
plugins/gtkgui.py Normal file
View File

@ -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"

30
runCore.py Normal file
View File

@ -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()