[sugar] review: Guillaume's 'roster' branch of Gadget
Guillaume Desmottes
guillaume.desmottes at collabora.co.uk
Tue May 27 11:42:39 EDT 2008
Le mardi 27 mai 2008 à 14:08 +0100, Dafydd Harries a écrit :
> > + for jid in self.roster.jids:
> > + # FIXME: How can I find the right 'from' ?
> > + self.send_presence("gadget.localhost", jid)
> > +
>
> Isn't the right from address the same address as we authenticated to the
> jabber server with? This is the same as config['domain'], which can be
> accessed here as self.parent.thisEntity.full().
>
I got a problem here.
When Gadget is run as a daemon (using twisted), self.parent is a
ServiceManager manager instance and we have its jid in
self.parent.jabberId.
But when it's run in the test environnement, self.parent is a XmlStream
and we have the jid in self.parent.thisHost.
I guess the test environnement is wrong?
Didn't find how to solve it (tbh, I'm a bit lost in all the twisted
component involved).
> Should we send presence probes to all our contacts here too?
>
Not sure. Gadget never sends presence probe. Just "normal" presence.
According to http://www.xmpp.org/rfcs/rfc3921.html#presence 5.1.3.,
presence probes are only used by peer's server, so I'm not sure if
Gadget should send one or not.
> > +class GadgetRoster:
> > + def __init__(self, path):
> > + self.path = path
> > +
> > + try:
> > + lines = file(path, 'r').readlines()
> > + except IOError:
> > + self.jids = set()
>
> I'm a bit worried that we might lose errors here. Not all IO errors are "file
> not found".
good point. I changed that to use os.path.isfile instead.
> > + else:
> > + # JID's have to contain one and only one '@'
> > + jids = filter(lambda x: x.count('@') == 1, lines)
>
> Are we expecting that the roster might contain invalid JIDs somehow? This
> seems a bit strange for me.
>
It shouldn't but it doesn't hurt to be prudent and check IMHO.
> At any rate, if we are, we should use twisted.jabber.words.jid.parse() to
> validate JIDs.
>
done.
> > + # remove ending '\n'
> > + self.jids = set(map(lambda x: x.strip(), jids))
> > +
> > + def save(self):
>
> Perhaps this method should be prefixed with an underscore.
>
done.
> > + file(self.path, 'w').write(reduce(lambda x,y: "%s\n%s" % (x,y), self.jids))
>
> There's a problem here: opening the file for writing might succeed, in which
> case it will be truncated, but writing to the file might fail, in which case
> we would lose the roster data. The correct approach is to write the new roster
> to a temporary file and rename it over the old one.
> twisted.python.filepath.FilePath implements this behaviour; we could use that.
>
done.
> reduce() can be inefficient for large lists. Perhaps this would be better:
>
> ''.join('%s\n' % x for x in jids)
>
> (By using a generator expression, the temporary strings can be garbage
> collected as they are used.)
>
Didn't know that, thanks for explanation.
fixed.
> In terms of IO, writing the entire roster out on every change might be
> expensive. Another option would be to append JIDs to the file. A third option
> would be to use SQLite, which would also take care of the data loss problem.
>
SQLite is not a bad idea. I added a comment about it and will
investigate it later.
> > +# FIXME: can we use this path?
> > +ROSTER_PATH = '/tmp/roster'
> > +
>
> No we can't! This is vulnerable to a symlink attack bug where an attacker
> makes /tmp/roster point to one of your files. Unlinking the file first won't
> help you because there is always a race. Writing to fixed locations in shared
> directories is never a good idea. Always use the tempfile module for such
> things.
>
> This is moot, however, because I'm going to recommend that we use an in-memory
> roster object for the tests. In fact, the roster object can just be a set,
> because the API is just the add() and remove() functions.
>
Good idea. Done.
> > + # FIXME: presence2 is called twice if we send this stanza
> > + self.done.callback(None)
> > +
>
> This is surprising and should be investigated. This is not how
> addOnetimeObserver() is supposed to behave.
>
nod.
> > + def later(self):
> > + assert self.roster.jids == set(['bob at foo.org'])
> > self.done.callback(None)
> >
> > class ProbePresenceTest(TestCase):
> > diff --git a/gadget/test_roster.py b/gadget/test_roster.py
> > new file mode 100644
> > index 0000000..e4af0ff
> > --- /dev/null
> > +++ b/gadget/test_roster.py
> > @@ -0,0 +1,44 @@
> > +import os
> > +import unittest
> > +
> > +from gadget.roster import GadgetRoster
> > +
> > +# FIXME: can we use this path?
> > +ROSTER_PATH = '/tmp/roster'
> > +
>
> Again, we should change this.
>
fixed.
thanks for the review,
G.
More information about the Sugar
mailing list