1# $Id$ 2 3############################################################################### 4 5array set long_statusdesc [list \ 6 available [::msgcat::mc "is available"] \ 7 chat [::msgcat::mc "is free to chat"] \ 8 away [::msgcat::mc "is away"] \ 9 xa [::msgcat::mc "is extended away"] \ 10 dnd [::msgcat::mc "doesn't want to be disturbed"] \ 11 invisible [::msgcat::mc "is invisible"] \ 12 unavailable [::msgcat::mc "is unavailable"]] 13 14proc get_long_status_desc {status} { 15 set ::long_statusdesc($status) 16} 17 18############################################################################### 19 20proc client:presence {xlib from type x args} { 21 global presence 22 global processed_presence 23 24 debugmsg presence "PRESENCE: $from; $type; $x; $args" 25 26 set from [::xmpp::jid::normalize $from] 27 28 switch -- $type { 29 error - 30 unavailable { 31 catch { unset presence(type,$xlib,$from) } 32 catch { unset presence(status,$xlib,$from) } 33 catch { unset presence(priority,$xlib,$from) } 34 catch { unset presence(show,$xlib,$from) } 35 catch { unset presence(x,$xlib,$from) } 36 catch { unset presence(error,$xlib,$from) } 37 38 set user [::xmpp::jid::stripResource $from] 39 if {[info exists presence(user_jids,$xlib,$user)]} { 40 set idx [lsearch -exact $presence(user_jids,$xlib,$user) $from] 41 set presence(user_jids,$xlib,$user) \ 42 [lreplace $presence(user_jids,$xlib,$user) $idx $idx] 43 } 44 cache_preferred_jid_on_unavailable $xlib $from $user 45 cache_user_status $xlib $user 46 47 foreach {attr val} $args { 48 switch -- $attr { 49 -status { 50 set presence(status,$xlib,$from) $val 51 if {[get_user_status $xlib $user] == "unavailable"} { 52 set presence(status,$xlib,$user) $val 53 } 54 } 55 -error { 56 set presence(error,$xlib,$from) $val 57 } 58 -lang { 59 set presence(lang,$xlib,$from) $val 60 } 61 } 62 } 63 64 debugmsg presence "$xlib $from unavailable" 65 } 66 subscribe {} 67 subscribed {} 68 unsubscribe {} 69 unsubscribed {} 70 probe {} 71 default { 72 set type available 73 set presence(type,$xlib,$from) available 74 set presence(status,$xlib,$from) "" 75 set presence(priority,$xlib,$from) 0 76 set presence(show,$xlib,$from) available 77 set presence(x,$xlib,$from) $x 78 catch { unset presence(error,$xlib,$from) } 79 80 foreach {attr val} $args { 81 switch -- $attr { 82 -status {set presence(status,$xlib,$from) $val} 83 -priority {set presence(priority,$xlib,$from) $val} 84 -show {set presence(show,$xlib,$from) $val} 85 -lang {set presence(lang,$xlib,$from) $val} 86 } 87 } 88 89 set presence(show,$xlib,$from) \ 90 [normalize_show $presence(show,$xlib,$from)] 91 92 set user [::xmpp::jid::stripResource $from] 93 if {![info exists presence(user_jids,$xlib,$user)] || \ 94 ![lcontain $presence(user_jids,$xlib,$user) $from]} { 95 lappend presence(user_jids,$xlib,$user) $from 96 } 97 98 cache_preferred_jid_on_available $xlib $from $user 99 cache_user_status $xlib $user 100 } 101 } 102 103 eval {hook::run client_presence_hook $xlib $from $type $x} $args 104} 105 106############################################################################### 107 108proc get_jids_of_user {xlib user} { 109 global presence 110 111 if {[info exists presence(user_jids,$xlib,$user)]} { 112 return $presence(user_jids,$xlib,$user) 113 } elseif {![cequal [::xmpp::jid::resource $user] ""]} { 114 if {[info exists presence(type,$xlib,$user)]} { 115 return [list $user] 116 } 117 } 118 return {} 119} 120 121proc get_jid_of_user {xlib user} { 122 global presence 123 124 if {[info exists presence(preferred_jid,$xlib,$user)]} { 125 return $presence(preferred_jid,$xlib,$user) 126 } else { 127 return $user 128 } 129} 130 131proc cache_preferred_jid_on_available {xlib jid user} { 132 global presence 133 134 set pri $presence(priority,$xlib,$jid) 135 136 if {![info exists presence(maxpriority,$xlib,$user)]} { 137 cache_preferred_jid $xlib $user 138 return 139 } elseif {$presence(preferred_jid,$xlib,$user) == $jid && \ 140 $pri < $presence(maxpriority,$xlib,$user)} { 141 unset presence(preferred_jid,$xlib,$user) 142 unset presence(maxpriority,$xlib,$user) 143 cache_preferred_jid $xlib $user 144 return 145 } else { 146 set maxpri $presence(maxpriority,$xlib,$user) 147 if {$pri > $maxpri} { 148 set presence(maxpriority,$xlib,$user) $pri 149 set presence(preferred_jid,$xlib,$user) $jid 150 } 151 } 152} 153 154proc cache_preferred_jid_on_unavailable {xlib jid user} { 155 global presence 156 157 if {![info exists presence(maxpriority,$xlib,$user)]} { 158 cache_preferred_jid $xlib $user 159 return 160 } 161 162 if {$presence(preferred_jid,$xlib,$user) == $jid} { 163 unset presence(preferred_jid,$xlib,$user) 164 unset presence(maxpriority,$xlib,$user) 165 cache_preferred_jid $xlib $user 166 } 167} 168 169proc cache_preferred_jid {xlib user} { 170 global presence 171 172 set jids [get_jids_of_user $xlib $user] 173 174 if {$jids != {}} { 175 set rjid [lindex $jids 0] 176 set pri $presence(priority,$xlib,$rjid) 177 178 foreach jid $jids { 179 if {$presence(priority,$xlib,$jid) > $pri} { 180 set pri $presence(priority,$xlib,$jid) 181 set rjid $jid 182 } 183 } 184 185 set presence(maxpriority,$xlib,$user) $pri 186 set presence(preferred_jid,$xlib,$user) $rjid 187 } 188} 189 190 191proc get_jid_status {xlib jid} { 192 global presence 193 194 set j $jid 195 if {[info exists presence(show,$xlib,$j)]} { 196 return $presence(show,$xlib,$j) 197 } else { 198 return unavailable 199 } 200} 201 202proc get_jid_presence_info {param xlib jid} { 203 global presence 204 205 if {[info exists presence($param,$xlib,$jid)]} { 206 return $presence($param,$xlib,$jid) 207 } else { 208 return "" 209 } 210} 211 212proc get_user_status {xlib user} { 213 global presence 214 215 if {[info exists presence(cachedstatus,$xlib,$user)]} { 216 return $presence(cachedstatus,$xlib,$user) 217 } elseif {[info exists presence(show,$xlib,$user)]} { 218 return $presence(show,$xlib,$user) 219 } else { 220 return unavailable 221 } 222} 223 224proc cache_user_status {xlib user} { 225 global presence 226 227 set jid [get_jid_of_user $xlib $user] 228 if {[info exists presence(show,$xlib,$jid)]} { 229 set presence(cachedstatus,$xlib,$user) $presence(show,$xlib,$jid) 230 } else { 231 set presence(cachedstatus,$xlib,$user) unavailable 232 } 233} 234 235proc get_user_status_desc {xlib user} { 236 global presence 237 238 set jid [get_jid_of_user $xlib $user] 239 if {[info exists presence(error,$xlib,$jid)]} { 240 return [::xmpp::stanzaerror::message $presence(error,$xlib,$jid)] 241 } elseif {[info exists presence(status,$xlib,$jid)]} { 242 return $presence(status,$xlib,$jid) 243 } else { 244 return "" 245 } 246} 247 248array set status_priority { 249 unavailable 1 250 xa 2 251 away 3 252 dnd 4 253 available 5 254 chat 6 255} 256 257proc compare_status {s1 s2} { 258 global status_priority 259 set p1 $status_priority($s1) 260 set p2 $status_priority($s2) 261 if {$p1 > $p2} { 262 return 1 263 } elseif {$p1 == $p2} { 264 return 0 265 } else { 266 return -1 267 } 268} 269 270proc max_status {s1 s2} { 271 global status_priority 272 set p1 $status_priority($s1) 273 set p2 $status_priority($s2) 274 if {$p1 >= $p2} { 275 return $s1 276 } else { 277 return $s2 278 } 279} 280 281############################################################################### 282 283set curpriority 0 284set curuserstatus unavailable 285set curtextstatus "" 286 287custom::defvar userpriority 0 [::msgcat::mc "Stored user priority."] \ 288 -type integer -group Hidden 289custom::defvar userstatus available [::msgcat::mc "Stored user status."] \ 290 -type string -group Hidden 291custom::defvar textstatus "" [::msgcat::mc "Stored user text status."] \ 292 -type string -group Hidden 293 294set userstatusdesc [::msgcat::mc "Not logged in"] 295 296set statusdesc(available) [::msgcat::mc "Available"] 297set statusdesc(chat) [::msgcat::mc "Free to chat"] 298set statusdesc(away) [::msgcat::mc "Away"] 299set statusdesc(xa) [::msgcat::mc "Extended away"] 300set statusdesc(dnd) [::msgcat::mc "Do not disturb"] 301set statusdesc(invisible) [::msgcat::mc "Invisible"] 302set statusdesc(unavailable) [::msgcat::mc "Unavailable"] 303 304############################################################################### 305 306proc change_priority_dialog {} { 307 global tmppriority 308 global userpriority 309 310 set tmppriority $userpriority 311 312 set w .change_priority 313 if {[winfo exists $w]} { 314 focus -force $w 315 return 316 } 317 318 Dialog $w -title [::msgcat::mc "Change Presence Priority"] \ 319 -modal none -separator 1 -anchor e -default 0 -cancel 1 320 321 $w add -text [::msgcat::mc "OK"] \ 322 -command [list do_change_priority $w] 323 $w add -text [::msgcat::mc "Cancel"] -command [list destroy $w] 324 325 set f [$w getframe] 326 label $f.lpriority -text [::msgcat::mc "Priority:"] 327 Spinbox $f.priority -1000 1000 1 tmppriority 328 329 grid $f.lpriority -row 0 -column 0 -sticky e 330 grid $f.priority -row 0 -column 1 -sticky ew 331 332 grid columnconfigure $f 0 -weight 1 333 grid columnconfigure $f 1 -weight 1 334 335 $w draw 336} 337 338############################################################################### 339 340proc do_change_priority {w} { 341 global userstatus 342 global tmppriority 343 global userpriority 344 345 destroy $w 346 if {![cequal $userpriority $tmppriority]} { 347 set userpriority $tmppriority 348 set userstatus $userstatus 349 } 350} 351 352############################################################################### 353 354trace variable userstatus w change_our_presence 355trace variable logoutuserstatus w change_our_presence 356 357############################################################################### 358 359proc change_our_presence {name1 name2 op} { 360 global userstatus logoutuserstatus curuserstatus 361 global textstatus logouttextstatus curtextstatus 362 global userpriority logoutpriority curpriority 363 global statusdesc userstatusdesc 364 365 switch -- $name1 { 366 logoutuserstatus { 367 set newstatus $logoutuserstatus 368 set newtextstatus $logouttextstatus 369 set newpriority $logoutpriority 370 } 371 default { 372 if {[lempty [connections]]} return 373 set newstatus $userstatus 374 set newtextstatus $textstatus 375 set newpriority $userpriority 376 } 377 } 378 379 if {[cequal $newstatus $curuserstatus] \ 380 && [cequal $newtextstatus $curtextstatus] \ 381 && [cequal $newpriority $curpriority]} { 382 return 383 } 384 385 if {[lsearch -exact [array names statusdesc] $newstatus] < 0} { 386 error [::msgcat::mc "Invalid userstatus value %s" $newstatus] 387 } 388 389 set userstatusdesc $statusdesc($newstatus) 390 set status $newtextstatus 391 392 foreach xlib [connections] { 393 send_presence $xlib $newstatus \ 394 -status $status \ 395 -priority $newpriority 396 } 397 398 foreach chatid [lfilter chat::is_groupchat [chat::opened]] { 399 set xlib [chat::get_xlib $chatid] 400 set group [chat::get_jid $chatid] 401 set nick [get_our_groupchat_nick $chatid] 402 403 if {$newstatus == "invisible"} { 404 set newst available 405 } else { 406 set newst $newstatus 407 } 408 409 send_presence $xlib $newst \ 410 -to $group/$nick \ 411 -status $status \ 412 -priority $userpriority 413 } 414 415 set curuserstatus $newstatus 416 set curtextstatus $newtextstatus 417 set curpriority $newpriority 418 419 hook::run change_our_presence_post_hook $newstatus 420} 421 422############################################################################### 423 424proc send_first_presence {xlib} { 425 global userstatus curuserstatus statusdesc userstatusdesc 426 global textstatus curtextstatus 427 global userpriority curpriority 428 global loginconf 429 430 if {[lsearch -exact [array names statusdesc] $userstatus] < 0} { 431 error [::msgcat::mc "Invalid userstatus value %s" $userstatus] 432 } 433 434 set userstatusdesc $statusdesc($userstatus) 435 set status $textstatus 436 437 set curuserstatus $userstatus 438 set curtextstatus $textstatus 439 set curpriority [set userpriority $loginconf(priority)] 440 441 send_presence $xlib $userstatus \ 442 -status $status \ 443 -priority $userpriority 444 445 hook::run change_our_presence_post_hook $userstatus 446} 447 448hook::add connected_hook [namespace current]::send_first_presence 15 449 450############################################################################### 451 452proc send_custom_presence {xlib jid status args} { 453 global userpriority 454 global statusdesc 455 456 set type jid 457 set stat "" 458 foreach {key val} $args { 459 switch -- $key { 460 -type { set type $val } 461 -status { set stat $val } 462 } 463 } 464 465 switch -- $type { 466 group { 467 set to $jid/[get_our_groupchat_nick [chat::chatid $xlib $jid]] 468 } 469 default { 470 set to $jid 471 } 472 } 473 474 eval {send_presence $xlib $status} $args \ 475 {-to $to -status $stat -priority $userpriority} 476} 477 478############################################################################### 479 480proc send_presence {xlib status args} { 481 set newargs [eval [list presence_args $xlib $status] $args] 482 eval [list ::xmpp::sendPresence $xlib] $newargs 483} 484 485############################################################################### 486 487proc presence_args {xlib status args} { 488 switch -- $status { 489 available { 490 set newargs {} 491 } 492 unavailable { 493 set newargs [list -type $status] 494 } 495 default { 496 set newargs [list -show $status] 497 } 498 } 499 500 set xlist {} 501 set to "" 502 set stat "" 503 foreach {opt val} $args { 504 switch -- $opt { 505 -id { lappend newargs -id $val } 506 -to { 507 set to $val 508 lappend newargs -to $val 509 } 510 -priority { lappend newargs -priority $val } 511 -xlist { set xlist $val } 512 -status { set stat $val } 513 } 514 } 515 516 hook::run rewrite_presence_status_hook stat $xlib 517 518 if {$stat != ""} { 519 lappend newargs -status $stat 520 } 521 522 hook::run presence_xlist_hook xlist $xlib $stat 523 lappend newargs -xlist $xlist 524 525 debugmsg presence "$newargs" 526 return $newargs 527} 528 529############################################################################### 530 531proc normalize_show {show} { 532 set res $show 533 534 switch -- $show { 535 away {} 536 chat {} 537 dnd {} 538 xa {} 539 unavailable {} 540 default {set res available} 541 } 542 return $res 543} 544 545############################################################################### 546 547proc add_presence_to_popup_info {infovar xlib jid} { 548 upvar 0 $infovar info 549 550 set bjid [::xmpp::jid::stripResource $jid] 551 if {[chat::is_groupchat [chat::chatid $xlib $bjid]]} return 552 553 set priority [get_jid_presence_info priority $xlib $jid] 554 if {$priority != ""} { 555 append info [format "\n\t[::msgcat::mc {Priority:}] %s" $priority] 556 } 557} 558 559hook::add roster_user_popup_info_hook add_presence_to_popup_info 20 560 561############################################################################### 562 563proc clear_presence_info {xlib} { 564 global curuserstatus 565 global userstatusdesc 566 global presence 567 568 array unset presence type,$xlib,* 569 array unset presence status,$xlib,* 570 array unset presence priority,$xlib,* 571 array unset presence show,$xlib,* 572 array unset presence error,$xlib,* 573 array unset presence x,$xlib,* 574 array unset presence user_jids,$xlib,* 575 array unset presence preferred_jid,$xlib,* 576 array unset presence cachedstatus,$xlib,* 577 array unset presence maxpriority,$xlib,* 578 579 if {[connections] == {}} { 580 set_status "Disconnected" 581 582 set curuserstatus unavailable 583 set userstatusdesc [::msgcat::mc "Not logged in"] 584 hook::run change_our_presence_post_hook unavailable 585 } 586} 587 588hook::add disconnected_hook clear_presence_info 589 590############################################################################### 591 592proc custom_presence_menu {m xlib jid} { 593 set chatid [chat::chatid $xlib $jid] 594 set chatid1 [chat::chatid $xlib [::xmpp::jid::removeResource $jid]] 595 596 if {![chat::is_groupchat $chatid] && [chat::is_groupchat $chatid1]} { 597 return 598 } 599 600 if {[chat::is_groupchat $chatid]} { 601 set jid [::xmpp::jid::replaceResource $jid [get_our_groupchat_nick $chatid]] 602 } 603 604 set mm [menu $m.custom_presence -tearoff 0] 605 606 $mm add command -label [::msgcat::mc "Available"] \ 607 -command [list send_custom_presence $xlib $jid available] 608 $mm add command -label [::msgcat::mc "Free to chat"] \ 609 -command [list send_custom_presence $xlib $jid chat] 610 $mm add command -label [::msgcat::mc "Away"] \ 611 -command [list send_custom_presence $xlib $jid away] 612 $mm add command -label [::msgcat::mc "Extended away"] \ 613 -command [list send_custom_presence $xlib $jid xa] 614 $mm add command -label [::msgcat::mc "Do not disturb"] \ 615 -command [list send_custom_presence $xlib $jid dnd] 616 $mm add command -label [::msgcat::mc "Unavailable"] \ 617 -command [list send_custom_presence $xlib $jid unavailable] 618 619 $m add cascade -label [::msgcat::mc "Send custom presence"] \ 620 -menu $mm 621} 622 623hook::add chat_create_user_menu_hook custom_presence_menu 43 624hook::add roster_jid_popup_menu_hook custom_presence_menu 43 625hook::add roster_service_popup_menu_hook custom_presence_menu 43 626hook::add chat_create_conference_menu_hook custom_presence_menu 43 627 628############################################################################### 629 630proc service_login {xlib jid} { 631 global userstatus curtextstatus 632 633 set newargs {} 634 635 if {$curtextstatus != ""} { 636 lappend newargs -status $curtextstatus 637 } 638 639 switch -- $userstatus { 640 available { 641 set command [list ::xmpp::sendPresence $xlib -to $jid] 642 } 643 invisible { 644 set command [list ::xmpp::sendPresence $xlib -to $jid -type $userstatus] 645 } 646 default { 647 set command [list ::xmpp::sendPresence $xlib -to $jid -show $userstatus] 648 } 649 } 650 651 eval $command $newargs 652} 653 654proc service_logout {xlib jid} { 655 global curtextstatus 656 657 set newargs {} 658 659 if {$curtextstatus != ""} { 660 lappend newargs -status $curtextstatus 661 } 662 663 set command [list ::xmpp::sendPresence $xlib -to $jid -type unavailable] 664 665 eval $command $newargs 666} 667 668proc service_login_logout_menu_item {m xlib jid} { 669 # TODO 670 $m add command -label [::msgcat::mc "Log in"] \ 671 -command [list service_login $xlib $jid] 672 $m add command -label [::msgcat::mc "Log out"] \ 673 -command [list service_logout $xlib $jid] 674} 675 676hook::add roster_service_popup_menu_hook service_login_logout_menu_item 20 677 678############################################################################### 679 680proc systray_presence_menu_item {m} { 681 set mp [menu $m.presence -title [::msgcat::mc "Presence"] \ 682 -tearoff $ifacetk::options(show_tearoffs)] 683 684 $mp add command -label [::msgcat::mc "Available"] \ 685 -command {set userstatus available} 686 $mp add command -label [::msgcat::mc "Free to chat"] \ 687 -command {set userstatus chat} 688 $mp add command -label [::msgcat::mc "Away"] \ 689 -command {set userstatus away} 690 $mp add command -label [::msgcat::mc "Extended away"] \ 691 -command {set userstatus xa} 692 $mp add command -label [::msgcat::mc "Do not disturb"] \ 693 -command {set userstatus dnd} 694 $mp add separator 695 $mp add command -label [::msgcat::mc "Change priority..."] \ 696 -command change_priority_dialog 697 698 $m add cascade -label [::msgcat::mc "Presence"] -menu $mp 699 700} 701 702hook::add systray_menu_hook systray_presence_menu_item 40 703 704# vim:ts=8:sw=4:sts=4:noet 705