File Coverage

blib/lib/Net/OSCAR/_BLInternal.pm
Criterion Covered Total %
statement 22 344 6.4
branch 0 128 0.0
condition 0 98 0.0
subroutine 8 20 40.0
pod 0 8 0.0
total 30 598 5.0


line stmt bran cond sub pod time code
1             =pod
2              
3             =head1 NAME
4              
5             Net::OSCAR::_BLInternal -- internal buddylist stuff
6              
7             =head1 VERSION
8              
9             version 1.928
10              
11             =head1 DESCRIPTION
12              
13             This handles conversion of Net::OSCAR to "OSCAR buddylist format",
14             and the sending of buddylist changes to the OSCAR server.
15              
16             =cut
17              
18             package Net::OSCAR::_BLInternal;
19             BEGIN {
20 4     4   87 $Net::OSCAR::_BLInternal::VERSION = '1.928';
21             }
22              
23 4     4   21 use strict;
  4         8  
  4         125  
24 4     4   20 use Net::OSCAR::Common qw(:all);
  4         16  
  4         1511  
25 4     4   22 use Net::OSCAR::Constants;
  4         8  
  4         491  
26 4     4   20 use Net::OSCAR::Utility;
  4         7  
  4         443  
27 4     4   21 use Net::OSCAR::TLV;
  4         7  
  4         166  
28 4     4   2199 use Net::OSCAR::XML;
  4         13  
  4         352  
29              
30 4     4   21 use vars qw($REVISION);
  4         10  
  4         20356  
31             $REVISION = '$Revision$';
32              
33             sub init_entry($$$$) {
34 0     0 0   my($blinternal, $type, $gid, $bid) = @_;
35              
36 0   0       $blinternal->{$type} ||= tlv();
37 0   0       $blinternal->{$type}->{$gid} ||= tlv();
38 0   0       $blinternal->{$type}->{$gid}->{$bid} ||= {};
39 0   0       $blinternal->{$type}->{$gid}->{$bid}->{name} ||= "";
40 0   0       $blinternal->{$type}->{$gid}->{$bid}->{data} ||= tlv();
41 0           $blinternal->{$type}->{$gid}->{$bid}->{__BLI_DIRTY} = 1;
42 0           $blinternal->{$type}->{$gid}->{$bid}->{__BLI_DELETED} = 0;
43             }
44              
45             sub blentry_clear($%) {
46 0     0 0   my($session, %data) = @_;
47              
48 0 0         if(chain_exists($session->{blinternal}, $data{entry_type}, $data{group_id}, $data{buddy_id})) {
49 0           $session->{blinternal}->{$data{entry_type}}->{$data{group_id}}->{$data{buddy_id}}->{__BLI_DELETED} = 1;
50             }
51             }
52              
53             sub blentry_set($%) {
54 0     0 0   my($session, %data) = @_;
55              
56 0           init_entry($session->{blinternal}, $data{entry_type}, $data{group_id}, $data{buddy_id});
57 0           my $typedata = tlv_decode($data{entry_data});
58              
59 0 0         $session->{blinternal}->{$data{entry_type}}->{$data{group_id}}->{$data{buddy_id}}->{name} = $data{entry_name} if $data{entry_name};
60 0           while(my($key, $value) = each %$typedata) {
61 0           $session->{blinternal}->{$data{entry_type}}->{$data{group_id}}->{$data{buddy_id}}->{data}->{$key} = $value;
62             }
63 0     0     $session->log_printf_cond(OSCAR_DBG_DEBUG, sub { "Got BLI entry %s 0x%04X/0x%04X/0x%04X with %d bytes of data:%s", $data{entry_name}, $data{entry_type}, $data{group_id}, $data{buddy_id}, length($typedata), hexdump($data{entry_data}) });
  0            
64             }
65              
66             sub blparse($$) {
67 0     0 0   my($session, $data) = @_;
68              
69 0           $session->{visibility} = VISMODE_PERMITALL; # If we don't have p/d data, this is default.
70              
71 0           delete $session->{blinternal};
72 0           $session->{blinternal} = tlv();
73              
74 0           while(length($data) > 4) {
75 0           my($name) = unpack("n/a*", $data);
76 0           substr($data, 0, 2+length($name)) = "";
77 0           my($gid, $bid, $type, $sublen) = unpack("n4", substr($data, 0, 8, ""));
78 0           my $typedata = substr($data, 0, $sublen, "");
79 0           blentry_set($session,
80             entry_type => $type,
81             group_id => $gid,
82             buddy_id => $bid,
83             entry_name => $name,
84             entry_data => $typedata
85             );
86             }
87              
88 0           BLI_to_NO($session);
89             }
90              
91             # Buddylist-Internal -> Net::OSCAR
92             # Sets various $session hashkeys from blinternal.
93             # That's what Brian Bli-to-no'd do. ;)
94             sub BLI_to_NO($) {
95 0     0 0   my($session) = @_;
96 0           my $bli = $session->{blinternal};
97              
98 0           delete $session->{blinternal_visbid};
99 0           delete $session->{blinternal_iconbid};
100              
101 0   0       $session->{buddies} ||= bltie(1);
102 0           $session->{buddies}->{__BLI_DIRTY} = 0;
103              
104 0   0       $session->{permit} ||= bltie;
105 0   0       $session->{deny} ||= bltie;
106              
107              
108 0           foreach my $type ([2, "permit"], [3, "deny"]) {
109 0           my($num, $name) = @$type;
110              
111 0 0         if(exists $bli->{$num}) {
112 0           foreach my $bid(keys(%{$bli->{$num}->{0}})) {
  0            
113 0           my $item = $bli->{$num}->{0}->{$bid};
114              
115 0 0         if($item->{__BLI_DELETED}) {
    0          
116 0           delete $session->{$name}->{$item->{name}};
117 0           delete $bli->{$num}->{0}->{$bid};
118             } elsif($item->{__BLI_DIRTY}) {
119 0           $session->{$name}->{$item->{name}} = {buddyid => $bid};
120 0           $item->{__BLI_DIRTY} = 0;
121             }
122             }
123             }
124             }
125              
126              
127 0           foreach my $type (4, 5, 0x14) {
128 0           delete $bli->{$type}->{0}->{$_} foreach grep { $bli->{$type}->{0}->{$_}->{__BLI_DELETED} } keys %{$bli->{$type}->{0}};
  0            
  0            
129             }
130              
131 0 0 0       if(exists $bli->{4} and exists $bli->{4}->{0} and (my($visbid) = grep {exists($bli->{4}->{0}->{$_}->{data}->{0xCB})} keys %{$bli->{4}->{0}})) {
  0   0        
  0            
132 0           $session->{blinternal_visbid} = $visbid;
133 0           my $typedata = $bli->{4}->{0}->{$visbid}->{data};
134 0 0         if($bli->{4}->{0}->{$visbid}->{__BLI_DIRTY}) {
135 0 0         ($session->{visibility}) = unpack("C", $typedata->{0xCA}) if $typedata->{0xCA};
136              
137 0           my $groupperms = $typedata->{0xCB};
138 0 0         ($session->{groupperms}) = unpack("N", $groupperms) if $groupperms;
139 0 0         $session->{profile} = $typedata->{0x0100} if exists($typedata->{0x0100});
140 0 0         ($session->{icon_checksum}) = unpack("n", $typedata->{0x0101}) if exists($typedata->{0x0101});
141 0 0         ($session->{icon_timestamp}) = unpack("N", $typedata->{0x0102}) if exists($typedata->{0x0102});
142 0 0         ($session->{icon_length}) = unpack("N", $typedata->{0x0103}) if exists($typedata->{0x0103});
143              
144 0           $session->{appdata} = $typedata;
145              
146 0 0         $session->set_info($session->{profile}) if exists($session->{profile});
147              
148 0           $bli->{4}->{0}->{$visbid}->{__BLI_DIRTY} = 0;
149             }
150             } else {
151             # No permit info - we permit everyone
152 0           $session->{visibility} = VISMODE_PERMITALL;
153 0           $session->{groupperms} = 0xFFFFFFFF;
154             }
155              
156 0 0 0       if(exists $bli->{0x14} and exists $bli->{0x14}->{0} and (my($iconbid) = grep {exists($bli->{0x14}->{0}->{$_}->{data}->{0xD5})} keys %{$bli->{0x14}->{0}})) {
  0   0        
  0            
157 0           $session->{blinternal_iconbid} = $iconbid;
158 0           my $typedata = $bli->{0x14}->{0}->{$iconbid}->{data};
159 0           $session->{icon_md5sum} = $typedata->{0xD5};
160             }
161              
162              
163 0           my @ret;
164              
165 0           foreach my $gid (keys %{$bli->{1}}) {
  0            
166 0 0         next unless exists $bli->{1}->{$gid}->{0};
167 0           my $item = $bli->{1}->{$gid}->{0};
168              
169 0 0         if($item->{__BLI_DELETED}) {
    0          
170 0           delete $bli->{1}->{$gid}->{0};
171 0 0 0       next if $gid == 0 or !$item->{name};
172              
173 0           delete $session->{buddies}->{$item->{name}};
174 0           push @ret, {type => MODBL_WHAT_GROUP, action => MODBL_ACTION_DEL, group => $item->{name}};
175             } elsif($item->{__BLI_DIRTY}) {
176 0           $item->{__BLI_DIRTY} = 0;
177 0 0 0       next if $gid == 0 or !$item->{name};
178              
179 0   0       $session->{buddies}->{$item->{name}} ||= {};
180 0           my $entry = $session->{buddies}->{$item->{name}};
181              
182 0           $entry->{__BLI_DIRTY} = 0;
183 0           $entry->{__BLI_DELETED} = 0;
184 0           $entry->{groupid} = $gid;
185 0 0         $entry->{members} = bltie unless $entry->{members};
186 0           $entry->{data} = $item->{data};
187              
188 0           push @ret, {type => MODBL_WHAT_GROUP, action => MODBL_ACTION_ADD, group => $item->{name}};
189             }
190             }
191              
192 0           foreach my $gid (keys %{$bli->{0}}) {
  0            
193 0           foreach my $bid (keys %{$bli->{0}->{$gid}}) {
  0            
194 0           my $item = $bli->{0}->{$gid}->{$bid};
195 0           my $group = "";
196 0 0         $group = $bli->{1}->{$gid}->{0}->{name} if chain_exists($bli, 1, $gid, 0);
197              
198 0 0         if($item->{__BLI_DELETED}) {
    0          
199 0           delete $bli->{0}->{$gid}->{$bid};
200 0 0 0       next if $gid == 0 or !$group;
201              
202 0 0         delete $session->{buddies}->{$group}->{members}->{$item->{name}} if $group;
203 0           push @ret, {type => MODBL_WHAT_BUDDY, action => MODBL_ACTION_DEL, group => $group, buddy => $item->{name}};
204             } elsif($item->{__BLI_DIRTY}) {
205 0           $item->{__BLI_DIRTY} = 0;
206 0 0 0       next if $gid == 0 or !$group;
207              
208 0           my $comment = undef;
209 0 0         $comment = $item->{data}->{0x13C} if exists($item->{data}->{0x13C});
210              
211 0           my $alias = undef;
212 0 0         $alias = $item->{data}->{0x131} if exists($item->{data}->{0x131});
213              
214 0   0       $session->{buddies}->{$group}->{members}->{$item->{name}} ||= {};
215 0           my $entry = $session->{buddies}->{$group}->{members}->{$item->{name}};
216 0           $entry->{__BLI_DIRTY} = 0;
217 0           $entry->{__BLI_DELETED} = 0;
218 0           $entry->{buddyid} = $bid;
219 0 0         $entry->{online} = 0 unless exists($entry->{online});
220 0           $entry->{comment} = $comment;
221 0           $entry->{alias} = $alias;
222 0           $entry->{data} = $item->{data};
223 0           $entry->{screenname} = Net::OSCAR::Screenname->new($item->{name});
224              
225 0           push @ret, {type => MODBL_WHAT_BUDDY, action => MODBL_ACTION_ADD, group => $group, buddy => $item->{name}};
226             }
227             }
228             }
229              
230 0           return @ret;
231             }
232              
233             # Gee, guess what this does? Hint: see sub BLI_to_NO.
234             sub NO_to_BLI($) {
235 0     0 0   my $session = shift;
236              
237 0           my $bli = tlv();
238 0           my $oldbli = $session->{blinternal};
239              
240             # Copy old data
241 0   0       my $visbid = $session->{blinternal_visbid} || int(rand(30000)) + 1;
242 0   0       my $iconbid = $session->{blinternal_iconbid} || 0x51F4;
243 0           foreach my $type (keys %$oldbli) {
244 0 0 0       next if $type == 2 or $type == 3;
245 0           foreach my $gid (keys %{$oldbli->{$type}}) {
  0            
246 0           foreach my $bid (keys %{$oldbli->{$type}->{$gid}}) {
  0            
247 0 0 0       next if $type == 4 and $bid == $visbid;
248 0 0 0       next if $type == 0x14 and $bid == $iconbid;
249              
250 0           init_entry($bli, $type, $gid, $bid);
251 0           $bli->{$type}->{$gid}->{$bid}->{name} = $oldbli->{$type}->{$gid}->{$bid}->{name};
252 0           foreach my $data (keys %{$oldbli->{$type}->{$gid}->{$bid}->{data}}) {
  0            
253 0           $bli->{$type}->{$gid}->{$bid}->{data}->{$data} = $oldbli->{$type}->{$gid}->{$bid}->{data}->{$data};
254             }
255             }
256             }
257             }
258              
259              
260 0           foreach my $permit (keys %{$session->{permit}}) {
  0            
261 0           init_entry($bli, 2, 0, $session->{permit}->{$permit}->{buddyid});
262 0           $bli->{2}->{0}->{$session->{permit}->{$permit}->{buddyid}}->{name} = $permit;
263             }
264              
265 0           foreach my $deny (keys %{$session->{deny}}) {
  0            
266 0           init_entry($bli, 3, 0, $session->{deny}->{$deny}->{buddyid});
267 0           $bli->{3}->{0}->{$session->{deny}->{$deny}->{buddyid}}->{name} = $deny;
268             }
269              
270 0           init_entry($bli, 4, 0, $visbid);
271 0   0       $bli->{4}->{0}->{$visbid}->{data}->{0xCA} = pack("C", $session->{visibility} || VISMODE_PERMITALL);
272 0   0       $bli->{4}->{0}->{$visbid}->{data}->{0xCB} = pack("N", $session->{groupperms} || 0xFFFFFFFF);
273              
274             #Net::OSCAR protocol extensions
275 0 0         $bli->{4}->{0}->{$visbid}->{data}->{0x0100} = $session->{profile} if $session->{profile};
276 0 0         $bli->{4}->{0}->{$visbid}->{data}->{0x0101} = pack("n", $session->{icon_checksum}) if $session->{icon_checksum};
277 0 0         $bli->{4}->{0}->{$visbid}->{data}->{0x0102} = pack("N", $session->{icon_timestamp}) if $session->{icon_timestamp};
278 0 0         $bli->{4}->{0}->{$visbid}->{data}->{0x0103} = pack("N", $session->{icon_length}) if $session->{icon_length};
279              
280 0           foreach my $appdata(keys %{$session->{appdata}}) {
  0            
281 0           $bli->{4}->{0}->{$visbid}->{data}->{$appdata} = $session->{appdata}->{$appdata};
282             }
283              
284 0 0 0       if(exists($session->{icon_md5sum}) || chain_exists($oldbli, 0x14, 0, $iconbid)) {
285 0           init_entry($bli, 0x14, 0, $iconbid);
286              
287 0 0         if(chain_exists($oldbli, 0x14, 0, $iconbid)) {
288 0           $bli->{0x14}->{0}->{$iconbid}->{name} = $oldbli->{0x14}->{0}->{$iconbid}->{name};
289              
290 0           $bli->{0x14}->{0}->{$iconbid}->{data}->{$_} = $oldbli->{0x14}->{0}->{$iconbid}->{data}->{$_}
291 0           foreach grep { $_ != 0xD5 } keys %{$oldbli->{0x14}->{0}->{$iconbid}->{data}};
  0            
292             } else {
293 0           $bli->{0x14}->{0}->{$iconbid}->{name} = "1";
294             }
295              
296 0 0         if(exists($session->{icon_md5sum})) {
297 0           $bli->{0x14}->{0}->{$iconbid}->{data}->{0xD5} = $session->{icon_md5sum};
298             }
299             }
300              
301 0           init_entry($bli, 1, 0, 0);
302 0 0         if($session->{buddies}->{__BLI_DIRTY}) {
303 0           $bli->{1}->{0}->{0}->{data}->{0xC8} = pack("n*", map { $_->{groupid} } grep { ref($_) } values %{$session->{buddies}});
  0            
  0            
  0            
304 0           $session->{buddies}->{__BLI_DIRTY} = 0;
305             } else {
306 0           $bli->{1}->{0}->{0}->{__BLI_SKIP} = 1;
307 0           $oldbli->{1}->{0}->{0}->{__BLI_SKIP} = 1;
308             }
309              
310 0           while(my($grpname, $grp) = each(%{$session->{buddies}})) {
  0            
311 0 0         next if $grpname eq "__BLI_DIRTY";
312              
313 0           my $gid = $grp->{groupid};
314              
315 0 0         if($grp->{__BLI_DELETED}) {
316 0           delete $session->{buddies}->{$grpname};
317 0           delete $bli->{1}->{$gid}->{0};
318 0           next;
319             }
320              
321 0 0         if(not $grp->{__BLI_DIRTY}) {
322 0           $bli->{1}->{$gid}->{0}->{__BLI_SKIP} = 1;
323 0           $oldbli->{1}->{$gid}->{0}->{__BLI_SKIP} = 1;
324 0           next;
325             } else {
326 0           $grp->{__BLI_DIRTY} = 0;
327             }
328              
329 0           init_entry($bli, 1, $gid, 0);
330 0           my $bligrp = $bli->{1}->{$gid}->{0};
331 0           $bligrp->{name} = $grpname;
332              
333              
334             # Clear out data, since the user may have deleted keys.
335 0           $bli->{1}->{$gid}->{0}->{data} = tlv();
336              
337             # It seems that WinAIM can now have groups without 0xC8 data, and gets pissed if we create such data where it doesn't exist.
338 0 0 0       if(!exists($oldbli->{1}->{$gid}) or chain_exists($oldbli, 1, $gid, 0, "data", 0xC8)) {
339 0           $bligrp->{data}->{0xC8} = pack("n*",
340 0           map { $_->{buddyid} }
341 0           grep { not $_->{__BLI_DELETED} }
342 0           values %{$grp->{members}});
343             }
344              
345 0 0         if(chain_exists($oldbli, 1, $gid, 0)) {
346 0           $bli->{1}->{$gid}->{0}->{data}->{$_} = $oldbli->{1}->{$gid}->{0}->{data}->{$_}
347 0           foreach grep { $_ != 0xC8 } keys %{$oldbli->{1}->{$gid}->{0}->{data}};
  0            
348             }
349              
350              
351 0           while(my($buddy, $bud) = each(%{$grp->{members}})) {
  0            
352 0           my $bid = $bud->{buddyid};
353              
354 0 0         if($bud->{__BLI_DELETED}) {
355 0           delete $grp->{members}->{$buddy};
356 0           delete $bli->{0}->{$gid}->{$bid};
357 0           next;
358             }
359              
360 0 0         if(not $bud->{__BLI_DIRTY}) {
361 0           $bli->{0}->{$gid}->{$bid}->{__BLI_SKIP} = 1;
362 0           $oldbli->{0}->{$gid}->{$bid}->{__BLI_SKIP} = 1;
363 0           next;
364             } else {
365 0           $bud->{__BLI_DIRTY} = 0;
366             }
367              
368 0 0         next unless $bid;
369 0           init_entry($bli, 0, $gid, $bid);
370 0           my $blibud = $bli->{0}->{$gid}->{$bid};
371 0           $blibud->{name} = "$buddy"; # Make sure to get strinfied version of Screenname
372              
373 0           $blibud->{data} = tlv();
374 0           while(my ($key, $value) = each(%{$bud->{data}})) {
  0            
375 0           $blibud->{data}->{$key} = $value;
376             }
377 0 0         $blibud->{data}->{0x13C} = $bud->{comment} if defined $bud->{comment};
378 0 0         $blibud->{data}->{0x131} = $bud->{alias} if defined $bud->{alias};
379             }
380             }
381              
382 0           BLI_to_OSCAR($session, $bli);
383             }
384              
385             # Send changes to BLI over to OSCAR
386             sub BLI_to_OSCAR($$) {
387 0     0 0   my($session, $newbli) = @_;
388 0           my $oldbli = $session->{blinternal};
389 0           my (@adds, @modifies, @deletes);
390 0 0         $session->crapout($session->{services}->{0+CONNTYPE_BOS}, "You must wait for a buddylist_ok or buddylist_error callback before calling commit_buddylist again.") if $session->{budmods};
391 0           $session->{budmods} = [];
392              
393 0           my %budmods;
394 0           $budmods{add} = [];
395 0           $budmods{modify} = [];
396 0           $budmods{delete} = [];
397              
398             # First, delete stuff that we no longer use and modify everything else
399 0           foreach my $type(keys %$oldbli) {
400              
401 0   0       my $budtype = (BUDTYPES)[$type] || "unknown type $type";
402              
403 0           foreach my $gid(keys %{$oldbli->{$type}}) {
  0            
404 0           foreach my $bid(keys %{$oldbli->{$type}->{$gid}}) {
  0            
405 0           my $oldentry = $oldbli->{$type}->{$gid}->{$bid};
406 0 0         if($oldentry->{__BLI_SKIP}) {
407 0           delete $oldentry->{__BLI_SKIP};
408 0           next;
409             }
410              
411 0           my $olddata = tlv_encode($oldentry->{data});
412 0     0     $session->log_printf_cond(OSCAR_DBG_DEBUG, sub { "Old BLI entry %s 0x%04X/0x%04X/0x%04X with %d bytes of data:%s", $oldentry->{name}, $type, $gid, $bid, length($olddata), hexdump($olddata) });
  0            
413 0           my $delete = 0;
414 0 0 0       if(exists($newbli->{$type}) and exists($newbli->{$type}->{$gid}) and exists($newbli->{$type}->{$gid}->{$bid})) {
      0        
415 0           my $newentry = $newbli->{$type}->{$gid}->{$bid};
416 0           my $newdata = tlv_encode($newentry->{data});
417 0     0     $session->log_printf_cond(OSCAR_DBG_DEBUG, sub { "New BLI entry %s 0x%04X/0x%04X/0x%04X with %d bytes of data:%s", $newentry->{name}, $type, $gid, $bid, length($newdata), hexdump($newdata) });
  0            
418              
419             next if
420 0 0 0       $newentry->{name} eq $oldentry->{name}
421             and $newdata eq $olddata;
422              
423             # Apparently, we can't modify the name of a buddylist entry?
424 0 0         if($newentry->{name} ne $oldentry->{name}) {
425 0           $delete = 1;
426             } else {
427 0           $session->log_print(OSCAR_DBG_DEBUG, "Modifying.");
428              
429 0           push @{$budmods{modify}}, {
  0            
430             reqdata => {desc => "modifying $budtype $newentry->{name}", type => $type, gid => $gid, bid => $bid},
431             protodata => {
432             entry_name => $newentry->{name},
433             group_id => $gid,
434             buddy_id => $bid,
435             entry_type => $type,
436             entry_data => $newdata
437             }
438             };
439             }
440             } else {
441 0           $delete = 1;
442             }
443              
444 0 0         if($delete) {
445 0           $session->log_print(OSCAR_DBG_DEBUG, "Deleting.");
446              
447 0           push @{$budmods{delete}}, {
  0            
448             reqdata => {desc => "deleting $budtype $oldentry->{name}", type => $type, gid => $gid, bid => $bid},
449             protodata => {
450             entry_name => $oldentry->{name},
451             group_id => $gid,
452             buddy_id => $bid,
453             entry_type => $type,
454             entry_data => $olddata
455             }
456             };
457             }
458             }
459             }
460             }
461              
462             # Now, add the new stuff
463 0           foreach my $type(keys %$newbli) {
464              
465 0   0       my $budtype = (BUDTYPES)[$type] || "unknown type $type";
466              
467 0           foreach my $gid(keys %{$newbli->{$type}}) {
  0            
468 0           foreach my $bid(keys %{$newbli->{$type}->{$gid}}) {
  0            
469 0           my $entry = $newbli->{$type}->{$gid}->{$bid};
470 0 0         if($entry->{__BLI_SKIP}) {
471 0           delete $entry->{__BLI_SKIP};
472 0           next;
473             }
474              
475 0 0 0       next if exists($oldbli->{$type}) and exists($oldbli->{$type}->{$gid}) and exists($oldbli->{$type}->{$gid}->{$bid}) and $oldbli->{$type}->{$gid}->{$bid}->{name} eq $newbli->{$type}->{$gid}->{$bid}->{name};
      0        
      0        
476              
477 0           my $data = tlv_encode($entry->{data});
478              
479 0     0     $session->log_printf_cond(OSCAR_DBG_DEBUG, sub { "New BLI entry %s 0x%04X/0x%04X/0x%04X with %d bytes of data:%s", $entry->{name}, $type, $gid, $bid, length($data), hexdump($data) });
  0            
480              
481 0           push @{$budmods{add}}, {
  0            
482             reqdata => {desc => "adding $budtype $entry->{name}", type => $type, gid => $gid, bid => $bid},
483             protodata => {
484             entry_name => $entry->{name},
485             group_id => $gid,
486             buddy_id => $bid,
487             entry_type => $type,
488             entry_data => $data
489             }
490             };
491             }
492             }
493             }
494              
495             # Actually send the changes. Don't send more than 7K in a single SNAC.
496             # FLAP size limit is 8K, but that includes headers - good to have a safety margin
497 0           foreach my $type (qw(add modify delete)) {
498 0           my $changelist = $budmods{$type};
499              
500 0           my(@reqdata, @packets);
501 0           my $packet = "";
502 0           foreach my $change(@$changelist) {
503 0           $packet .= protoparse($session, "buddylist_modification")->pack(%{$change->{protodata}});
  0            
504 0           push @reqdata, $change->{reqdata};
505              
506 0 0         if(length($packet) > 7*1024) {
507             #$session->log_print(OSCAR_DBG_INFO, "Adding to blmod queue (max packet size reached): type $type, payload size ", scalar(@reqdata));
508 0           push @packets, {
509             type => $type,
510             data => $packet,
511             reqdata => [@reqdata],
512             };
513 0           $packet = "";
514 0           @reqdata = ();
515             }
516             }
517 0 0         if($packet) {
518             #$session->log_print(OSCAR_DBG_INFO, "Adding to blmod queue (no more changes): type $type, payload size ", scalar(@reqdata));
519 0           push @packets, {
520             type => $type,
521             data => $packet,
522             reqdata => [@reqdata],
523             };
524             }
525              
526 0           push @{$session->{budmods}}, map {
  0            
527 0           {
528             protobit => "buddylist_" . $_->{type},
529             reqdata => $_->{reqdata},
530             protodata => {mods => $_->{data}}
531             };
532             } @packets;
533             }
534              
535 0           push @{$session->{budmods}}, {protobit => "end_buddylist_modifications"}; # End BL mods
  0            
536             #$session->log_print(OSCAR_DBG_INFO, "Adding terminator to blmod queue.");
537              
538 0           $session->{blold} = $oldbli;
539 0           $session->{blinternal} = $newbli;
540              
541 0 0         if(@{$session->{budmods}} <= 1) { # We only have the start/end modification packets, no actual changes
  0            
542             #$session->log_print(OSCAR_DBG_INFO, "Empty blmod queue - calling buddylist_ok.");
543 0           delete $session->{budmods};
544 0           $session->callback_buddylist_ok();
545             } else {
546             #$session->log_print(OSCAR_DBG_INFO, "Non-empty blmod queue - sending initiator and first change packet.");
547 0           $session->svcdo(CONNTYPE_BOS, protobit => "start_buddylist_modifications");
548 0           $session->svcdo(CONNTYPE_BOS, %{shift @{$session->{budmods}}}); # Send the first modification
  0            
  0            
549             }
550             }
551              
552             sub chain_exists($@) {
553 0     0 0   my($tlv, @refs) = @_;
554              
555 0           while(@refs) {
556 0           my $ref = shift @refs;
557 0 0         if(exists($tlv->{$ref})) {
558 0           $tlv = $tlv->{$ref};
559             } else {
560 0           return 0;
561             }
562             }
563              
564 0 0         return defined($tlv) ? 1 : 0;
565             }
566              
567             1;