[Tkabber-dev] r314 - trunk/plugins/avatarcache

tkabber-svn at jabber.ru tkabber-svn at jabber.ru
Fri Mar 5 21:55:49 MSK 2010


Author: Hermitifier
Date: 2010-03-05 21:55:49 +0300 (Fri, 05 Mar 2010)
New Revision: 314

Added:
   trunk/plugins/avatarcache/AUTHORS
   trunk/plugins/avatarcache/INSTALL
   trunk/plugins/avatarcache/README
   trunk/plugins/avatarcache/avatarcache.tcl
Log:
avatar cache initial commit

Added: trunk/plugins/avatarcache/AUTHORS
===================================================================
--- trunk/plugins/avatarcache/AUTHORS	                        (rev 0)
+++ trunk/plugins/avatarcache/AUTHORS	2010-03-05 18:55:49 UTC (rev 314)
@@ -0,0 +1,3 @@
+Jan Zachorowski <quantifier666 at gmail.com>
+
+Vcard photo saving proc was taken from plugin by Jet (megaxbit at xmpp.ru)
\ No newline at end of file

Added: trunk/plugins/avatarcache/INSTALL
===================================================================
--- trunk/plugins/avatarcache/INSTALL	                        (rev 0)
+++ trunk/plugins/avatarcache/INSTALL	2010-03-05 18:55:49 UTC (rev 314)
@@ -0,0 +1,10 @@
+As usually, copy this directory under the ~/.tkabber/plugins directory
+so that you get a hierarchy like this:
+  ~/.tkabber/plugins
+  ~/.tkabber/plugins/avatarcache/
+  ~/.tkabber/plugins/avatarcache/avatarcache.tcl
+
+Restart Tkabber, to get the plugin loaded.
+
+Consult the README file for the details about using this plugin.
+

Added: trunk/plugins/avatarcache/README
===================================================================
--- trunk/plugins/avatarcache/README	                        (rev 0)
+++ trunk/plugins/avatarcache/README	2010-03-05 18:55:49 UTC (rev 314)
@@ -0,0 +1,20 @@
+Avatar cache plugin for Tkabber.
+
+This plugin automatically detects avatars announced by PEP messages or
+vcard-temp update included in presence. Avatars are downloaded and
+stored in an offline cache.
+
+On it's own, the plugin doesn't do anything for the enduser. It is
+mostly targeted to other developers, who can write their own plugins
+which do actual drawing avatars.
+
+Whenever avatar announce is detected, hook is run:
+[hook::run user_avatar_notification_hook $jid $filename]
+
+$jid is a bare form, with stripped resource name
+$filename is name of actual file in the avatar cache. It's not warranted 
+that it contains any usable graphic data.
+
+Offline cache is restored on finload_hook. If it's needed at a later
+time, it can be invoked again by calling proc avatarcache::restore_cache
+

Added: trunk/plugins/avatarcache/avatarcache.tcl
===================================================================
--- trunk/plugins/avatarcache/avatarcache.tcl	                        (rev 0)
+++ trunk/plugins/avatarcache/avatarcache.tcl	2010-03-05 18:55:49 UTC (rev 314)
@@ -0,0 +1,327 @@
+# "Avatar cache" -- plugin for Tkabber.
+# Automatically downloads avatars announced by PEP or vcard-temp update
+# Written by Jan Zachorowski <quantifier666 at gmail.com>
+
+package require sha1
+
+namespace eval avatarcache {
+    variable vcard_photo_hashes
+    variable pep_avatar_hashes
+    variable assigned_hashes
+    
+    disco::register_feature "http://www.xmpp.org/extensions/xep-0084.html#ns-metadata+notify"
+    disco::register_feature "urn:xmpp:avatar:metadata+notify"
+
+    pubsub::register_event_notification_handler "urn:xmpp:avatar:metadata" \
+	    [list [namespace current]::process_avatar_metadata_notification "urn:xmpp:avatar:metadata"]
+    pubsub::register_event_notification_handler "http://www.xmpp.org/extensions/xep-0084.html#ns-metadata" \
+	    [list [namespace current]::process_avatar_metadata_notification "http://www.xmpp.org/extensions/xep-0084.html#ns-metadata"]
+    lappend ::debug_lvls avatarcache
+    
+    file mkdir [file join $::configdir avatars]
+
+    custom::defvar avatarcache {} "Mapping of JIDs to avatar hashes." \
+	    -group Hidden
+    custom::defvar avatarcache_custom {} "Mapping of JIDs to custom assigned avatar hashes." \
+	    -group Hidden
+
+    hook::add userinfo_hook [namespace current]::after_vCard_photo
+    
+    hook::add client_presence_hook [namespace current]::process_presence
+    
+    hook::add finload_hook [namespace current]::restore_cache
+
+    hook::add roster_jid_popup_menu_hook [namespace current]::add_assign_menu_item 73
+}
+
+proc avatarcache::process_presence {xlib from type x args} {
+    variable vcard_photo_hashes
+    variable assigned_hashes
+    set sjid [::xmpp::jid::stripResource $from]
+    if {[chat::is_groupchat [chat::chatid $xlib $sjid]]} {
+	return
+    }
+    switch -- $type {
+	available -
+	unavailable {
+	    foreach xs $x {
+		::xmpp::xml::split $xs tag xmlns attrs cdata subels
+		if {$xmlns == "vcard-temp:x:update"} {
+		    debugmsg avatarcache "got presence with vcard update: $from"
+		    foreach subel $subels {
+			::xmpp::xml::split $subel stag sxmlns sattrs scdata ssubels
+			if {$stag == "photo"} {
+			    debugmsg avatarcache "photo $from: '$scdata'"
+			    if {![info exists vcard_photo_hashes($sjid)] \
+				    || $vcard_photo_hashes($sjid) != $scdata} {
+				debugmsg avatarcache "saving unknown photo hash $from $scdata"
+				#very dirty hack ahead:
+				#if {$scdata == "9a9daf48824e2da088a085ae512a2e0377367e18"} return
+				if {$scdata != ""} {
+				    set vcard_photo_hashes($sjid) $scdata
+				    store_cache
+				    set filename [file join $::configdir avatars $scdata]
+				    if {![file exists $filename] && [roster::is_trusted $xlib $sjid]} {
+					after_vCard_photo "" $xlib $from 0
+					::xmpp::sendIQ $xlib get \
+					    -query [::xmpp::xml::create vCard -xmlns vcard-temp] \
+					    -to $sjid \
+					    -command [list userinfo::parse_vcard $from]
+				    } {
+					if {![info exists assigned_hashes($sjid)]} {
+					    hook::run user_avatar_notification_hook $sjid $filename
+					}
+				    }
+				} {
+				    catch {unset vcard_photo_hashes($sjid)}
+				    store_cache
+				    if {![info exists assigned_hashes($sjid)]} {
+					hook::run user_avatar_notification_hook $sjid ""
+				    }
+				}
+			    }
+			}
+		    }
+		}
+	    }
+	}
+    }
+}
+
+proc avatarcache::process_avatar_metadata_notification {node xlib jid items} {
+    variable pep_avatar_hashes
+    variable assigned_hashes
+    set stop true
+
+    foreach item $items {
+	::xmpp::xml::split $item tag xmlns attrs cdata subels
+
+	foreach imetadata $subels {
+	    ::xmpp::xml::split $imetadata stag sxmlns sattrs scdata ssubels
+
+	    if {![string equal $stag metadata]} continue
+	    if {![string equal $sxmlns $node]} continue
+
+	    foreach i $ssubels {
+		::xmpp::xml::split $i sstag ssxmlns ssattrs sscdata sssubels
+
+		switch -- $sstag {
+		    info {
+			set stop false
+			#TODO: check if has all needed data
+			
+			set id [::xmpp::xml::getAttr $ssattrs id]
+			
+			break
+		    }
+		}
+	    }
+	}
+    }
+    switch $node {
+	"http://www.xmpp.org/extensions/xep-0084.html#ns-metadata" {
+	    set node "http://www.xmpp.org/extensions/xep-0084.html#ns-data"
+	}
+	"urn:xmpp:avatar:metadata" {
+	    set node "urn:xmpp:avatar:data"
+	}
+    }	
+
+    if {$stop} {
+	catch {unset pep_avatar_hashes($jid)}
+	store_cache
+	if {![info exists assigned_hashes($jid)]} {
+	    hook::run user_avatar_notification_hook $jid ""
+	}
+    } {
+	set filename [file join $::configdir avatars $id]
+	if {[file exists $filename]} {
+	    set pep_avatar_hashes($jid) $id
+	    store_cache
+	    if {![info exists assigned_hashes($jid)]} {
+		hook::run user_avatar_notification_hook $jid $filename
+	    }
+	} {
+	    ::xmpp::pubsub::retrieveItems $xlib $jid $node -items $id \
+		    -command [list [namespace current]::process_avatar_data $jid $node $filename]
+	}
+    }
+}
+
+proc avatarcache::process_avatar_data {jid node filename status xml} {
+    if {$status != "ok"} return
+    
+    variable pep_avatar_hashes
+    variable assigned_hashes
+    
+    foreach item $xml {
+	::xmpp::xml::split $item tag xmlns attrs cdata subels
+	foreach idata $subels {
+	    ::xmpp::xml::split $idata stag sxmlns sattrs scdata ssubels
+
+	    if {![string equal $stag data]} continue
+	    if {![string equal $sxmlns $node]} continue
+
+	    set avatarb64 $scdata
+	    set hash [file tail $filename]
+
+	    #TODO: calculate real hash and check if matches ...
+	    if {!([file exists $filename] && [file attributes $filename -readonly])} {
+		set fileid [open $filename w]
+		fconfigure $fileid -translation binary
+		puts -nonewline $fileid [base64::decode $avatarb64]
+		close $fileid
+	    }
+	    set pep_avatar_hashes($jid) $hash
+	    store_cache
+	    if {![info exists assigned_hashes($jid)]} {
+		hook::run user_avatar_notification_hook $jid $filename
+	    }
+	    return
+	}
+    }
+}
+
+proc avatarcache::after_vCard_photo {tab xlib jid editable} {
+    debugmsg avatarcache [info level 0]
+    if {[roster::is_trusted $xlib $jid] && !$editable} {
+	set xjid [::xmpp::jid::stripResource $jid]
+	trace vdelete ::userinfo::userinfo(photo_binval,$jid) w [list [namespace current]::save_vCard_photo $jid $xjid]
+	trace variable ::userinfo::userinfo(photo_binval,$jid) w [list [namespace current]::save_vCard_photo $jid $xjid]
+    }
+}
+
+proc avatarcache::save_vCard_photo {jid xjid a b c} {
+    variable assigned_hashes
+    debugmsg avatarcache [info level 0]
+    trace vdelete "userinfo::$a\($b\)" $c [list [namespace current]::save_vCard_photo $jid $xjid]
+    variable vcard_photo_hashes
+    set bjid [::xmpp::jid::stripResource $jid]
+    set hash [sha1::sha1 $::userinfo::userinfo(photo_binval,$jid)]
+    if {[info exists vcard_photo_hashes($bjid)] && $vcard_photo_hashes($bjid) != ""} {
+	#TODO: check if hashes match ...
+	set filename [file join $::configdir avatars $vcard_photo_hashes($bjid)]
+    } {
+	#FIXME: calculate hash and add jid->hash mapping instead of this
+	#set filename [file join $::configdir avatars [::logger::jid_to_filename $xjid]]
+	set vcard_photo_hashes($bjid) $hash
+	set filename [file join $::configdir avatars $hash]
+    }
+    #very dirty hack ahead:
+    #if {$hash == "3afe88c89837563780697d2d30d5e9f47a7325dc" || $hash == "9a9daf48824e2da088a085ae512a2e0377367e18"} return
+	
+    if {!([file exists $filename] && [file attributes $filename -readonly])} {
+	set fileid [open $filename w]
+	fconfigure $fileid -translation binary
+	puts -nonewline $fileid $::userinfo::userinfo(photo_binval,$jid)
+	close $fileid
+    }
+    if {![info exists assigned_hashes($jid)]} {
+	hook::run user_avatar_notification_hook $xjid $filename
+    }
+    return
+}
+
+proc avatarcache::store_cache {} {
+    variable pep_avatar_hashes
+    variable vcard_photo_hashes
+    #variable assigned_hashes
+    variable avatarcache
+    array set tmp $avatarcache
+    array set tmp [array get vcard_photo_hashes]
+    array set tmp [array get pep_avatar_hashes]
+    #array set tmp [array get assigned_hashes]
+    set avatarcache [array get tmp]
+}
+
+proc avatarcache::restore_cache {} {
+    variable avatarcache
+    variable avatarcache_custom
+    
+    foreach filename [glob -nocomplain [file join $::configdir avatars *@*.*]] {
+	set jid_lookalike [file tail $filename]
+	if {[info exists avatarcache($jid_lookalike)]} continue
+
+	hook::run user_avatar_notification_hook $jid_lookalike $filename
+    }
+    
+    array set assigned_hashes $avatarcache_custom
+    foreach {jid hash} $avatarcache {
+	set filename [file join $::configdir avatars $hash]
+	if {[file exists $filename] && ![info exists assigned_hashes($jid)]} {
+	    hook::run user_avatar_notification_hook $jid $filename
+	}
+    }
+    
+    foreach {jid hash} $avatarcache_custom {
+	set filename [file join $::configdir avatars $hash]
+	if {[file exists $filename]} {
+	    hook::run user_avatar_notification_hook $jid $filename
+	}
+    }
+}
+
+proc avatarcache::assign_by_hand {jid} {
+    variable assigned_hashes
+    variable avatarcache_custom
+    set custom_filename [tk_getOpenFile -defaultextension .png \
+					-filetypes [list [list "Images" [list ".png" ".jpg" ".jpeg" ".gif" ".bmp"]]] \
+					-title "Select custom avatar"]
+    if {$custom_filename == ""} return
+    
+    set fd [open $custom_filename r]
+    fconfigure $fd -translation binary
+    set avatardata [read $fd [file size $custom_filename]]
+    close $fd
+    
+    set hash [sha1::sha1 $avatardata]
+    set filename [file join $::configdir avatars $hash]
+    if {![file exists $filename]} {
+	file copy $custom_filename $filename
+    }
+    
+    set assigned_hashes($jid) $hash
+    set avatarcache_custom [array get assigned_hashes]
+    hook::run user_avatar_notification_hook $jid $filename
+}
+
+proc avatarcache::unassign_by_hand {jid} {
+    variable assigned_hashes
+    variable avatarcache
+    variable avatarcache_custom
+
+    catch {unset assigned_hashes($jid)}
+    set avatarcache_custom [array get assigned_hashes]
+    foreach {cachedjid hash} $avatarcache {
+	if {$jid == $cachedjid} {
+	    set filename [file join $::configdir avatars $hash]
+	    if {[file exists $filename]} {
+		hook::run user_avatar_notification_hook $jid $filename
+	    }
+	    break
+	}
+    }
+}
+
+proc avatarcache::hide_avatar {jid} {
+    variable assigned_hashes
+    variable avatarcache_custom
+    set assigned_hashes($jid) ""
+    set avatarcache_custom [array get assigned_hashes]
+    hook::run user_avatar_notification_hook $jid ""
+}
+
+proc avatarcache::add_assign_menu_item {m xlib jid} {
+    variable assigned_hashes
+    set jid [::xmpp::jid::stripResource $jid]
+    $m add command -label [::msgcat::mc "Assign custom avatar"] \
+		   -command [namespace code [list assign_by_hand $jid]]
+    set state [expr {[info exists assigned_hashes($jid)] ? "normal" : "disabled"}]
+    $m add command -label [::msgcat::mc "Unassign custom avatar"] \
+		   -command [namespace code [list unassign_by_hand $jid]] \
+		   -state $state
+    set state [expr {([info exists assigned_hashes($jid)] && $assigned_hashes($jid) == "") ? "disabled" : "normal"}]
+    $m add command -label [::msgcat::mc "Ignore avatar"] \
+		   -command [namespace code [list hide_avatar $jid]] \
+		   -state $state
+}



More information about the Tkabber-dev mailing list