root/trunk/lib/DJabberd/IQ.pm @ 65

Revision 65, 7.2 kB (checked in by bradfitz, 4 years ago)

start of processing roster adds/updates/deletes. this add hook chain call, but not pretty half, the easy implementation in RosterStorage.pm

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1package DJabberd::IQ;
2use strict;
3use base qw(DJabberd::Stanza);
4use fields (
5            'connection',   # Store the connection the IQ came in on so we can respond.  may be undef, as it's a weakref.
6            );
7
8# DO NOT OVERRIDE THIS
9sub process {
10    my DJabberd::IQ $self = shift;
11    my $conn = shift;
12
13    $self->{connection} = $conn;
14    Scalar::Util::weaken($self->{connection});
15
16    my $handler = {
17        'get-{jabber:iq:roster}query' => \&process_iq_getroster,
18        'set-{jabber:iq:roster}query' => \&process_iq_setroster,
19        'get-{jabber:iq:auth}query' => \&process_iq_getauth,
20        'set-{jabber:iq:auth}query' => \&process_iq_setauth,
21    };
22
23    $conn->run_hook_chain(phase    => "c2s-iq",
24                          args     => [ $self ],
25                          fallback => sub {
26                              my $sig = $self->signature;
27                              my $meth = $handler->{$sig};
28                              unless ($meth) {
29                                  warn "Unknown IQ packet: $sig";
30                                  return;
31                              }
32                              $meth->($conn, $self);
33                          });
34}
35
36sub signature {
37    my $iq = shift;
38    my $fc = $iq->first_element;
39    # FIXME: should signature ever get called on a bogus IQ packet?
40    return $iq->type . "-" . ($fc ? $fc->element : "(BOGUS)");
41}
42
43sub send_result {
44    my DJabberd::IQ $self = shift;
45    $self->send_reply("result");
46}
47
48sub send_error {
49    my DJabberd::IQ $self = shift;
50    # TODO: take more parameters
51    $self->send_reply("error");
52}
53
54# caller must send well-formed XML (but we do the wrapping element)
55sub send_result_raw {
56    my DJabberd::IQ $self = shift;
57    my $raw = shift;
58    return $self->send_reply("result", $raw);
59}
60
61sub send_reply {
62    my DJabberd::IQ $self = shift;
63    my ($type, $raw) = @_;
64
65    my $conn = $self->{connection}
66        or return;
67
68    $raw ||= "";
69    my $id = $self->id;
70    my $to = $conn->bound_jid->as_string;
71    my $xml = qq{<iq to='$to' type='$type' id='$id'>$raw</iq>};
72    warn "About to send IQ reply: $xml\n";
73    $conn->write(\$xml);
74}
75
76sub process_iq_getroster {
77    my ($conn, $iq) = @_;
78    $conn->set_requested_roster(1);
79
80    my $send_roster = sub {
81        my $roster = shift;
82        # TODO: walk roster and add presence subscriptions for everybody
83        $iq->send_result_raw($roster->as_xml);
84    };
85
86    $conn->run_hook_chain(phase => "RosterGet",
87                          methods => {
88                              set_roster => sub {
89                                  my ($self, $roster) = @_;
90                                  $send_roster->($roster);
91                              },
92                          },
93                          fallback => sub {
94                              $send_roster->(DJabberd::Roster->new()),
95                          });
96    return 1;
97}
98
99sub process_iq_setroster {
100    my ($conn, $iq) = @_;
101
102    $DB::single = 1;
103
104    my $item = $iq->query->first_element;
105    unless ($item && $item->element eq "{jabber:iq:roster}item") {
106        $iq->send_error;
107        return;
108    }
109
110    # {=xmpp-ip-7.6-must-ignore-subscription-values}
111    my $subattr  = $item->attr('subscription') || "";
112    my $removing = $subattr eq "remove" ? 1 : 0;
113
114    my $jid = $item->attr("{jabber:iq:roster}jid")
115        or return $iq->send_error;
116
117    my $name = $item->attr("{jabber:iq:roster}name");
118
119    # find list of group names to add/update.  can ignore
120    # if we're just removing.
121    my @groups;  # scalars of names
122    unless ($removing) {
123        foreach my $ele ($item->children_elements) {
124            next unless $ele->element eq "{jabber:iq:roster}group";
125            push @groups, $ele->first_child;
126        }
127    }
128
129    my $ritem = DJabberd::RosterItem->new(jid    => $jid,
130                                          name   => $name,
131                                          groups => \@groups);
132
133    my $phase = $removing ? "RosterRemoveItem" : "RosterSetItem";
134    $conn->run_hook_chain(phase   => $phase,
135                          args    => [ $ritem ],
136                          methods => {
137                              done => sub {
138                                  my ($self) = @_;
139                                  $iq->send_result;
140                                  # TODO: roster push
141                              },
142                          },
143                          fallback => sub {
144                              $iq->send_error;
145                              # TODO: roster push
146                          });
147
148    return 1;
149}
150
151sub process_iq_getauth {
152    my ($conn, $iq) = @_;
153    # <iq type='get' id='gaimf46fbc1e'><query xmlns='jabber:iq:auth'><username>brad</username></query></iq>
154
155    my $child = $iq->query->first_element
156        or return;
157
158    return 0 unless $child->element eq "{jabber:iq:auth}username";
159
160    my $username = $child->first_child;
161    die "Element in username field?" if ref $username;
162
163    my $id = $iq->id;
164
165    $conn->write("<iq id='$id' type='result'><query xmlns='jabber:iq:auth'><username>$username</username><digest/><resource/></query></iq>");
166
167    return 1;
168}
169
170sub process_iq_setauth {
171    my ($conn, $iq) = @_;
172    # <iq type='set' id='gaimbb822399'><query xmlns='jabber:iq:auth'><username>brad</username><resource>work</resource><digest>ab2459dc7506d56247e2dc684f6e3b0a5951a808</digest></query></iq>
173    my $id = $iq->id;
174
175    my $query = $iq->query
176        or die;
177    my @children = $query->children;
178
179    my $get = sub {
180        my $lname = shift;
181        foreach my $c (@children) {
182            next unless ref $c && $c->element eq "{jabber:iq:auth}$lname";
183            my $text = $c->first_child;
184            return undef if ref $text;
185            return $text;
186        }
187        return undef;
188    };
189
190    my $username = $get->("username");
191    my $resource = $get->("resource");
192    my $digest   = $get->("digest");
193
194    return unless $username =~ /^\w+$/;
195
196    my $accept = sub {
197        $conn->{authed}   = 1;
198        $conn->{username} = $username;
199        $conn->{resource} = $resource;
200
201        # register
202        my $sname = $conn->server->name;
203        my $jid = DJabberd::JID->new("$username\@$sname/$resource");
204
205        $conn->server->register_jid($jid, $conn);
206        $conn->set_bound_jid($jid);
207
208        # FIXME: escape, or make $iq->send_good_result, or something
209        $conn->write(qq{<iq id='$id' type='result' />});
210        return;
211    };
212
213    my $reject = sub {
214        warn " BAD LOGIN!\n";
215        # FIXME: FAIL
216        return 1;
217    };
218
219    $conn->run_hook_chain(phase => "Auth",
220                          args  => [ { username => $username, resource => $resource, digest => $digest } ],
221                          methods => {
222                              accept => $accept,
223                              reject => $reject,
224                          },
225                          fallback => $reject);
226
227    return 1;  # signal that we've handled it
228}
229
230
231sub id {
232    return $_[0]->attr("{jabber:client}id");
233}
234
235sub type {
236    return $_[0]->attr("{jabber:client}type");
237}
238
239sub query {
240    my $self = shift;
241    my $child = $self->first_element
242        or return;
243    my $ele = $child->element
244        or return;
245    return undef unless $child->element =~ /\}query$/;
246    return $child;
247}
248
2491;
Note: See TracBrowser for help on using the browser.