📄 x7499.htm
字号:
are inextricably linked. And if we look briefly under the covers, we
see how this is so. When a presence subscription request is sent to a
user, it runs the gauntlet of modules in the JSM (see
<A
HREF="x1740.htm#JABTDG-CH-4-SECT-4.3.1.4"
>the section called <I
>Component Connection Method</I
> in Chapter 4</A
> for details on what these
modules are). The roster-handling module <TT
CLASS="LITERAL"
>mod_roster</TT
>
grabs this request, and, just in case the recipient turns out not to
be connected, <I
CLASS="EMPHASIS"
>stores</I
> it.</P
><P
>And here's how intertwined the presence subscription mechanism and rosters
really are: <I
CLASS="EMPHASIS"
>The request is stored as a cluster of attribute details
within an
<TT
CLASS="LITERAL"
><item/></TT
> tag in the roster
belonging to the</I
> recipient <I
CLASS="EMPHASIS"
>of the presence subscription
request.</I
> It looks like this:</P
><P
><PRE
CLASS="SCREEN"
><item jid='user@hostname' subscription='none' subscribe='' hidden=''/></PRE
></P
><P
>On receipt of a presence subscription request,
the <TT
CLASS="LITERAL"
>mod_roster</TT
> module will create the roster item
if it doesn't exist already, and then assign the attributes related to
presence subscription—<TT
CLASS="LITERAL"
>subscription='none'</TT
>
and <TT
CLASS="LITERAL"
>subscribe=''</TT
>—to it. There's no
<TT
CLASS="LITERAL"
>ask</TT
> attribute, as this is only assigned to the
item on the roster belonging to the <I
CLASS="EMPHASIS"
>sender</I
>, not
the <I
CLASS="EMPHASIS"
>receiver</I
>, of the subscription request.</P
><P
>The <TT
CLASS="LITERAL"
>subscribe</TT
> attribute is used to store the
reason for the request, that, if specified, is carried in the
<TT
CLASS="LITERAL"
><status/></TT
> tag of the
<TT
CLASS="LITERAL"
><presence/></TT
> element that conveys the
request. If no reason is given, the value for the attribute is
empty, as shown here. Otherwise it will contain what was stored
in the <TT
CLASS="LITERAL"
><status/></TT
> tag.
<A
HREF="x7499.htm#JABTDG-CH-7-EX-7"
>Example 7-7</A
> shows a presence subscription request
that carries a reason.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="JABTDG-CH-7-EX-7"
></A
><P
><B
>Example 7-7. A presence subscription request with a reason</B
></P
><P
><PRE
CLASS="SCREEN"
><presence to='dj@gnu.pipetree.com'>
<status>I'd like to keep my eye on you!</status>
</presence></PRE
></P
></DIV
><P
>The <TT
CLASS="LITERAL"
>hidden</TT
> attribute is used internally by
<TT
CLASS="LITERAL"
>mod_roster</TT
> to mark the item as non-displayable;
it effectively is a pseudo <TT
CLASS="LITERAL"
><item/></TT
>, that,
when brought to life, actually turns out to be a
<TT
CLASS="LITERAL"
><presence/></TT
> element. So when a request
for the roster is made, <TT
CLASS="LITERAL"
>mod_roster</TT
> makes sure that
it doesn't send these "hidden" items. The <TT
CLASS="LITERAL"
>hidden</TT
>
attribute always has an empty value, as shown here.</P
><P
>After storing the subscription request, <TT
CLASS="LITERAL"
>mod_roster</TT
>
will actually send the original <TT
CLASS="LITERAL"
><presence/></TT
>
element that conveyed that request. If the recipient is online,
<I
CLASS="EMPHASIS"
>and</I
> if the recipient has already made a request for
their roster. As sending an availability presence packet:</P
><P
><PRE
CLASS="SCREEN"
><presence/></PRE
></P
><P
>is a kick to the <TT
CLASS="LITERAL"
>mod_offline</TT
> module to punt any
messages stored offline in that user's absence, so requesting the
roster:</P
><P
><PRE
CLASS="SCREEN"
><iq type='get'><query xmlns='jabber:iq:roster'/></iq></PRE
></P
><P
>is a kick to the <TT
CLASS="LITERAL"
>mod_roster</TT
> module to punt any
subscription requests stored offline in that user's absence.</P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="JABTDG-CH-7-SECT-3.2.8"
>Sending of availability information</A
></H3
><P
>Ok. We've connected, authenticated, defined and registered our
<TT
CLASS="LITERAL"
><presence/></TT
> callback, and kicked
<TT
CLASS="LITERAL"
>mod_roster</TT
>. Now we need to announce our own
availability in the form of a simple
<TT
CLASS="LITERAL"
><presence/></TT
> element:</P
><P
><PRE
CLASS="SCREEN"
><presence/></PRE
></P
><P
>We can do this by calling the <TT
CLASS="FUNCTION"
>sendInitPresence()</TT
>
method on our connection object:</P
><P
><PRE
CLASS="SCREEN"
>con.sendInitPresence()</PRE
></P
><P
>This availability information will be distributed to all the
entities that are subscribed to the script's presence and are
online at that moment. It will also signify to the Jabber server
that we are properly online—in which case it can forward
to us any messages that had been stored up in our absence.</P
><P
>We're not really expecting any <TT
CLASS="LITERAL"
><message/></TT
>
elements; indeed, we haven't set up any subroutine to handle them so
they'd just be thrown away by the library anyway. The real reason for
sending presence is so that the server will actively go and
probe those in a presence subscription relationship with the script and
report back on those who are available (who have themselves sent their
presence during their current session). This causes
<TT
CLASS="LITERAL"
><presence/></TT
> elements to arrive on the
stream, which make their way to our <TT
CLASS="FUNCTION"
>presenceCB()</TT
>
handler. </P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="JABTDG-CH-7-SECT-3.2.9"
>Waiting for packets</A
></H3
><P
>Once eveything is set up, and the script has announced its presence,
it really just needs to sit back and listen to the
<TT
CLASS="LITERAL"
><presence/></TT
> elements that come in.
If one of these is from our intended notification recipient, and the
availability state is right (i.e., not in "Do Not Disturb" mode),
bingo.</P
><P
>But the elements being sent over the stream from the server don't
spontaneously get received, parsed, and dispatched; we can control
when that happens from our script.
This is the nub of the symbiosis between the
element events and our procedural routines, and it's name is
<TT
CLASS="FUNCTION"
>process()</TT
>.</P
><P
>Calling <TT
CLASS="FUNCTION"
>process()</TT
> will check on the stream to
see if any XML fragments have arrived and are waiting to be picked
up. If there are any, steps 3 through 5, shown in
<A
HREF="x7499.htm#JABTDG-CH-7-FIG-5"
>Figure 7-5</A
>, are executed. The numeric value
specified in the call to <TT
CLASS="FUNCTION"
>process()</TT
> is the number
of seconds to wait for incoming fragments if none are currently
waiting to be picked up. Specifying no value (or zero) means that
the method won't hang around if nothing has arrived. Specifying a
value of 30 means that it will wait up to half a minute. We really
want something in between, and it turns out that waiting for up to a
second for fragments in a finite loop like this:</P
><P
><PRE
CLASS="SCREEN"
>for i in range(5):
con.process(1)</PRE
></P
><P
>will allow for a slightly stuttered arrival of the
<TT
CLASS="LITERAL"
><presence/></TT
> elements that are punted to
the script as a result of the server-initiated probes. </P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="JABTDG-CH-7-SECT-3.2.10"
>Finishing up</A
></H3
><P
>We're just about done. The <TT
CLASS="LITERAL"
><presence/></TT
>
elements that arrive and find their way to our callback are examined, and
the CVS notification message is sent off if appropriate. Once the
<TT
CLASS="FUNCTION"
>process()</TT
> calls have finished, and, implicitly,
the (potentially) multiple calls to <TT
CLASS="FUNCTION"
>presenceCB</TT
>,
there's nothing left to do. So we simply disconnect from the Jabber
server, as before:</P
><P
><PRE
CLASS="SCREEN"
>con.disconnect()</PRE
></P
></DIV
></DIV
><DIV
CLASS="SECT2"
><H2
CLASS="SECT2"
><A
NAME="JABTDG-CH-7-SECT-3.3"
>The script in its entirety</A
></H2
><P
>As that was probably a little disjointed, here's the presence-sensitive
version of the CVS notification script in its entirety.</P
><P
><PRE
CLASS="SCREEN"
>import Jabber, XMLStream
import sys
from string import split
cvsuser = sys.argv[1]
message = ''
def presenceCB(con, prs):
type = prs.getType()
parts = split(prs.getFrom(), '/')
who = parts[0]
if type == None: type = 'available'
# subscription request:
# - accept their subscription
# - send request for subscription to their presence
if type == 'subscribe':
print "subscribe request from %s" % (who)
con.send(Jabber.Presence(to=who, type='subscribed'))
con.send(Jabber.Presence(to=who, type='subscribe'))
# unsubscription request:
# - accept their unsubscription
# - send request for unsubscription to their presence
elif type == 'unsubscribe':
print "unsubscribe request from %s" % (who)
con.send(Jabber.Presence(to=who, type='unsubscribed'))
con.send(Jabber.Presence(to=who, type='unsubscribe'))
elif type == 'subscribed':
print "we are now subscribed to %s" % (who)
elif type == 'unsubscribed':
print "we are now unsubscribed to %s" % (who)
elif type == 'available':
print "%s is available (%s/%s)" % (who, prs.getShow(), prs.getStatus())
if prs.getShow() != 'dnd' and who == cvsuser:
con.send(Jabber.Message(cvsuser, message, subject="CVS Watch Alarm"))
elif type == 'unavailable':
print "%s is unavailable" % (who)
for line in sys.stdin.readlines(): message = message + line
con = Jabber.Connection(host=Server)
try:
con.connect()
except XMLStream.error, e:
print "Couldn't connect: %s" % e
sys.exit(0)
con.auth(Username,Password,Resource)
con.setPresenceHandler(presenceCB)
con.requestRoster()
con.sendInitPresence()
for i in range(5):
con.process(1)
con.disconnect()</PRE
></P
></DIV
></DIV
><H3
CLASS="FOOTNOTES"
>Notes</H3
><TABLE
BORDER="0"
CLASS="FOOTNOTES"
WIDTH="100%"
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN7510"
HREF="x7499.htm#AEN7510"
>[1]</A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>The process of adding a JID to the roster and making a subscription request
is an atomic action in many clients. It is generally assumed that
adding a JID to your roster means that you're going to want to know
when that entity is available, so the action of adding a JID to the
roster will often generate a presence subscription request automatically.</P
></TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN7607"
HREF="x7499.htm#AEN7607"
>[2]</A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>This session ID is currently part of a new development within the
<TT
CLASS="LITERAL"
>Net::Jabber</TT
> library and is currently not used for
anything.</P
></TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN7668"
HREF="x7499.htm#AEN7668"
>[3]</A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>This is to reduce the possibility of JID spoofing.</P
></TD
></TR
></TABLE
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="x7229.htm"
>Prev</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="book1.htm"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="c7982.htm"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>Dialup system watch</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="c6941.htm"
>Up</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Extending Messages, Groupchat, Components, and Event Models</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -