Code Search for Developers
 
 
  

msnp2p.tcl from aMSN at Krugle


Show msnp2p.tcl syntax highlighted


::Version::setSubversionId {$Id: msnp2p.tcl 8902 2007-07-10 16:03:36Z kakaroto $}

::snit::type P2PMessage {

	option -sessionid
	option -identifier
	option -offset
	option -totalsize
	option -datalength
	option -flag
	option -ackid
	option -ackuid
	option -acksize
	variable headers
	variable body ""

	constructor {args} {
		#TODO: remove me when object is destroyed in the right place
		#DONE (hopefully)
		#after 30000 $self destroy
	}

	#creates a P2PMessage object from a normal Message object
	method createFromMessage { message } {
		#		array set headers [$message getHeaders]
		#		set data [$message getBody]
		#		set idx [string first "\r\n\r\n" $data]
		#		set head [string range $data 0 [expr $idx -1]]
		##		set body [string range $data [expr $idx +4] end]
		set body [$message getBody]
		#		set head [string map {"\r" ""} $head]
		#		set heads [split $head "\n"]
		#		foreach header $heads {
		#			set idx [string first ": " $header]
		#			array set headers [list [string range $header 0 [expr $idx -1]] \
		    #					  [string range $header [expr $idx +2] end]]
		#		}
		binary scan [string range $body 0 48] iiiiiiiiiiii cSid cId cOffset1 cOffset2 cTotalDataSize1 cTotalDataSize2 cMsgSize cFlags cAckId cAckUID cAckSize1 cAckSize2
		set body [string range $body 48 end]
		set options(-sessionid) $cSid
		set options(-identifier) $cId
		set options(-offset) [int2word $cOffset1 $cOffset2]
		set options(-totalsize) [int2word $cTotalDataSize1 $cTotalDataSize2]
		set options(-datalength) $cMsgSize
		set options(-flag) $cFlags
		set options(-ackid) $cAckId
		set options(-ackuid) $cAckUID
		set options(-acksize) [int2word $cAckSize1 $cAckSize2]
	}

	method toString { {humanReadable 0} } {
		set str ""
		foreach { header info } [array get headers] {
			set str "$str$header: $info\r\n"
		}
		set str "$str\r\n\r\n"
		if { $humanReadable } {
			set str "${str}sessionid: $options(-sessionid)\n"
			set str "${str}identifier: $options(-identifier)\n"
			set str "${str}offset: $options(-offset)\n"
			set str "${str}totalsize: $options(-totalsize)\n"
			set str "${str}datalength: $options(-datalength)\n"
			set str "${str}flag: $options(-flag)\n"
			set str "${str}ackid: $options(-ackid)\n"
			set str "${str}ackuid: $options(-ackuid)\n"
			set str "${str}acksize: $options(-acksize)\n"
		} else {
			#TODO
		}
		set str "$str$body"
		return $str
	}


	#	proc ReadData { message chatid } {
	#		variable chunkedData
	#		# Get values from the header
	##		set idx [expr [string first "\r\n\r\n" $data] + 4]
	##		set headend [expr $idx + 48]
	#		set data [$message getBody]
	#
	#	        binary scan [string range $data 0 48] iiiiiiiiiiii cSid cId cOffset1 cOffset2 cTotalDataSize1 cTotalDataSize2 cMsgSize cFlags cAckId cAckUID cAckSize1 cAckSize2
	#
	#	        set cOffset [int2word $cOffset1 $cOffset2]
	#	        set cTotalDataSize [int2word $cTotalDataSize1 $cTotalDataSize2]
	#   	        set cAckSize [int2word $cAckSize1 $cAckSize2]
	#
	#		#status_log "Read header : $cSid $cId $cOffset $cTotalDataSize $cMsgSize $cFlags $cAckId $cAckUID $cAckSize\n" red
	#		#status_log "Sid : $cSid -> " red
	#
	#		if {$cSid == "0" && $cMsgSize != "0" && $cMsgSize != $cTotalDataSize } {
	#
	#			if { ![info exists chunkedData($cId)] } {
	#				set chunkedData($cId) "[string range $data 48 end-4]"
	#			} else {
	#				set chunkedData($cId) "$chunkedData($cId)[string range $data 48 end-4]"
	#			}
	#			#status_log "Data is now : $chunkedData($cId)\n\n";
	#
	#			if { $cTotalDataSize != [string length $chunkedData($cId)] } {
	#				return
	#			} else {
	#				set data $chunkedData($cId)
	#				set headend 0
	#				set cMsgSize $cTotalDataSize
	#			}
	#
	#		}
	#	}








	method getBody { } {
		return $body
	}

	method getHeader { name } {
		return [lindex [array get headers $name] 1]
	}
}

namespace eval ::MSNP2P {
	namespace export loadUserPic SessionList ReadData MakePacket MakeACK MakeSLP


	#Get picture from $user, if cached, or sets image as "loading", and request it
	#using MSNP2P
	proc loadUserPic { chatid user {reload "0"} } {
		global HOME

		#Line below changed from != -1 to == 0 because -1 means 
		#"enabled but imagemagick unavailable"
		if { [::config::getKey getdisppic] == 0 } {
			status_log "Display Pics disabled, exiting loadUserPic\n" red
			return
		} 

		#status_log "::MSNP2P::GetUser: Checking if picture for user $user exists\n" blue

		set msnobj [::abook::getVolatileData $user msnobj]

		#status_log "::MSNP2P::GetUser: MSNOBJ is $msnobj\n" blue

		#set filename [::MSNP2P::GetFilenameFromMSNOBJ $msnobj]
		set filename [::abook::getContactData $user displaypicfile ""]
		status_log "::MSNP2P::GetUser: filename is $filename\n" white

		if { $filename == "" } {
			return
		}

		#Reload 1 means that we force aMSN to reload a new display pic
		#Destroy it before to avoid TkCxImage to redraw animated gif above the good display pic
		#TODO: FIX: I think the next line is incorrect, did you want image delete? (be careful if there are images on the screen)
		destroy user_pic_$user
		if { ![file readable "[file join $HOME displaypic cache ${filename}].png"] || $reload == "1" } {
			status_log "::MSNP2P::GetUser: FILE [file join $HOME displaypic cache ${filename}] doesn't exist!!\n" white
			image create photo user_pic_$user -file [::skin::GetSkinFile "displaypic" "loading.gif"] -format cximage


			#if the small picture (for notifications e.g.) already exists, change it
			if { [ImageExists displaypicture_not_$user] } {
			
				status_log "User DP Changed, recreating small image as it already exist"
				
				#clear it first before overwriting
				displaypicture_not_$user blank
				#if there is no problem copying, it's OK, we resize it if bigger then 50x50
				if {![catch {displaypicture_not_$user copy user_pic_$user}]} {
					if {[image width displaypicture_not_$user] > 50 || [image height displaypicture_not_$user] > 50} {
						::picture::ResizeWithRatio displaypicture_not_$user 50 50
					}
				} else {
					image delete displaypicture_not_$user
				}
			}				
			

			create_dir [file join $HOME displaypic]
			create_dir [file join $HOME displaypic cache]
			::MSNP2P::RequestObject $chatid $user $msnobj
		} else {
			::skin::getDisplayPicture $user 1
		}
	}

	proc loadUserSmiley { chatid user msnobj } {
		global HOME

		set filename [::MSNP2P::GetFilenameFromMSNOBJ $msnobj]

		status_log "Got filename $filename for $chatid with $user and $msnobj\n" red

		if { $filename == "" } {
			return
		}

		image create photo emoticonCustom_std_$filename -width 19 -height 19

		status_log "::MSNP2P::GetUserPic: filename is $filename\n" white

		if { [catch {image create photo emoticonCustom_std_$filename -file "[file join $HOME smileys cache ${filename}].png" -format cximage}] } {
			#We didn't manage to load the smiley (either we haven't it either it's bad) so we ask it
			status_log "::MSNP2P::GetUser: FILE [file join $HOME smileys cache ${filename}] doesn't exist!!\n" white
			image create photo emoticonCustom_std_$filename -width 19 -height 19

			create_dir [file join $HOME smileys]
			create_dir [file join $HOME smileys cache]
			if { [::abook::getContactData $user showcustomsmileys] != 0 } { ::MSNP2P::RequestObject $chatid $user $msnobj }
		} else {
			# Make sure the smiley is max 50x50
			::smiley::resizeCustomSmiley emoticonCustom_std_${filename}
		}
	}


	proc GetFilenameFromMSNOBJ { msnobj } {
		set sha1d [split $msnobj " "]
		set idx [lsearch $sha1d "SHA1D=*"]
		set sha1d [lindex $sha1d $idx]
		set sha1d [string range $sha1d 7 end-1]
		if { $sha1d == "" } {
			return ""
		}
		#return [::md5::md5 $sha1d]
		binary scan $sha1d h* filename
		return $filename
	}

	proc GetFilenameFromContext { context } {
		global msnobjcontext

		set old_msnobj [::base64::decode $context]
		set msnobj [string range $old_msnobj [string first "<" $old_msnobj] [expr {[string first "/>" $old_msnobj] + 1}]]

		status_log "GetFilenameFromContext : $context == $old_msnobj == $msnobj\n[string first "<" $old_msnobj] - [string first "/>"  $old_msnobj]\n\n" red
		if { [info exists msnobjcontext($msnobj)] } {
			status_log "Found filename\n" red
			return $msnobjcontext($msnobj)
		} else {
			status_log "Couln't find filename for context \n$context\n = $msnobj --- [array get msnobjcontext] --[info exists msnobjcontext($msnobj)] \n" red
			return ""
		}


	}

	#//////////////////////////////////////////////////////////////////////////////
	# SessionList (action sid [varlist])
	# Data Structure for MSNP2P Sessions, contains :
	# 0 - Message Identifier	(msgid)
	# 1 - TotalDataSize		(totalsize) This variable is only used if sending data in split packets
	# 2 - Offset			(offset)
	# 3 - Destination		(dest)
	# 4 - Step to run after ack     (AferAck)   For now can be DATAPREP, SENDDATA
	# 5 - CallID (MSNSLP)		(callid)
	# 6 - File Descriptor		(fd)
	# 7 - Session Type		(type) bicon, emoticon, filetransfer
        # 8 - Filename for transfer     (Filename)
	# 9 - branchid                  (branchid)
	#
	# action can be :
	#	get : This method returns a list with all the array info, 0 if non existent
	#	set : This method sets the variables for the given sessionid, takes a list as argument.
	#	unset : This method removes the given sessionid variables
	#	findid : This method searches all Sessions for one that has the given Identifier, returns session ID or -1 if not found
	#	findcallid : This method searches all Sessions for one that has the given Call-ID, returns session ID or -1 if not found
	proc SessionList { action sid { varlist "" } } {

		switch $action {
			get {
				set ret [getObjOption $sid MsgId] 
				#status_log "getting $sid : [getObjOption $sid MsgId] - $ret" green

				if { $ret != "" } {
					# Session found, return values
					lappend ret [getObjOption $sid TotalSize] 
					lappend ret [getObjOption $sid Offset] 
					lappend ret [getObjOption $sid Destination] 
					lappend ret [getObjOption $sid AfterAck]
					lappend ret [getObjOption $sid CallId] 
					lappend ret [getObjOption $sid Fd]
					lappend ret [getObjOption $sid Type] 
					lappend ret [getObjOption $sid Filename]
					lappend ret [getObjOption $sid branchid]

					#status_log "returning $ret" green
					return $ret
				} else {
					#status_log "Not found" green
					# Session not found, return 0
					return 0
				}
			}

			set {
				#status_log "setting $sid with $varlist" green
				# This overwrites previous vars if they are set to something else than -1
				if { [lindex $varlist 0] != -1 } {
					setObjOption $sid MsgId [lindex $varlist 0]
					setObjOption [lindex $varlist 0] sid $sid
				}
				if { [lindex $varlist 1] != -1 } {
					setObjOption $sid TotalSize [lindex $varlist 1]
				}
				if { [lindex $varlist 2] != -1 } {
					setObjOption $sid Offset [lindex $varlist 2]
				}
				if { [lindex $varlist 3] != -1 } {
					setObjOption $sid Destination [lindex $varlist 3]
				}
				if { [lindex $varlist 4] != -1 } {
					setObjOption $sid AfterAck [lindex $varlist 4]
				}
				if { [lindex $varlist 5] != -1 } {
					setObjOption $sid CallId [lindex $varlist 5]
					setObjOption [lindex $varlist 5] sid $sid
				}
				if { [lindex $varlist 6] != -1 } {
					setObjOption $sid Fd [lindex $varlist 6]
				}
				if { [lindex $varlist 7] != -1 } {
					setObjOption $sid Type [lindex $varlist 7]
				}
				if { [lindex $varlist 8] != -1 } {
				        setObjOption $sid Filename [lindex $varlist 8]
				}
				if { [lindex $varlist 9] != -1 } {
				        setObjOption $sid branchid [lindex $varlist 9]
				}
			}

			unset {
				#status_log "unsetting..." green
				return
			}
			findcallid -
			findid {
				#status_log "Finding $action of $sid, found : [getObjOption $sid sid]" green
				if { [getObjOption $sid sid] != "" } {
					return [getObjOption $sid sid]
				}
			}
		}
	}

	proc SendVoiceClip {chatid file } {
		set sbn [::MSN::SBFor $chatid]
		set msnobj [create_msnobj [::config::getKey login] 11 $file]
		
		set msg "MIME-Version: 1.0\r\nContent-Type: text/x-msnmsgr-datacast\r\n\r\nID: 3\r\nData: $msnobj\r\n\r\n"
		set msg_len [string length $msg]
	
		#Send the packet
		::MSN::WriteSBNoNL $sbn "MSG" "U $msg_len\r\n$msg"

		# Let the magic do the rest... (the INVITE will be sent by the other client requesting the msnobj 
		# created with [create_msnobj] which also links the msnobj to the filename which will be automatically sent)
	}


	#//////////////////////////////////////////////////////////////////////////////
	# ReadData ( data chatid )
	# This is the handler for all received MSNP2P packets
	# data is the MSNP2P packet
	# chatid will be used to get the SB ?? Ack alvaro if it's better to use chatid or some way to use the dest email
	# For now only manages buddy and emoticon transfer
	# TODO : Error checking on fields (to, from, sizes, etc)
	proc ReadData { message chatid } {
		global HOME
		variable chunkedData

		#		set message [P2PMessage create %AUTO%]
		#		$message createFromMessage $msg

		#status_log "called ReadData with $data\n" red

		# Get values from the header
		#		set idx [expr [string first "\r\n\r\n" $data] + 4]
		#		set headend [expr $idx + 48]
		#		set data [$message getBody]

		#	        binary scan [string range $data 0 48] iiiiiiiiiiii cSid cId cOffset1 cOffset2 cTotalDataSize1 cTotalDataSize2 cMsgSize cFlags cAckId cAckUID cAckSize1 cAckSize2

		#	        set cOffset [int2word $cOffset1 $cOffset2]
		#	        set cTotalDataSize [int2word $cTotalDataSize1 $cTotalDataSize2]
		#   	        set cAckSize [int2word $cAckSize1 $cAckSize2]


		set cSid [$message cget -sessionid]
		set cId [$message cget -identifier]
		set cOffset [$message cget -offset]
		set cTotalDataSize [$message cget -totalsize]
		set cMsgSize [$message cget -datalength]
		set cFlags [$message cget -flag]
		set cAckId [$message cget -ackid]
		set cAckUID [$message cget -ackuid]
		set cAckSize [$message cget -acksize]
		set data [$message getBody]

		#status_log "Read header : $cSid $cId $cOffset $cTotalDataSize $cMsgSize $cFlags $cAckId $cAckUID $cAckSize\n$data" red
		

		if {$cSid == "0" && $cMsgSize != "0" && $cMsgSize != $cTotalDataSize } {

			if { ![info exists chunkedData($cId)] } {
				set chunkedData($cId) "[string range $data 0 [expr { $cMsgSize - 1}]]"
			} else {
				set chunkedData($cId) "$chunkedData($cId)[string range $data 0 [expr { $cMsgSize - 1}]]"
			}
			#			status_log "Data is now : $chunkedData($cId)\n\n";
			status_log "chunked data :  $cTotalDataSize - $cMsgSize - $cOffset - [string length $chunkedData($cId)]"

			if { $cTotalDataSize != [expr {$cMsgSize + $cOffset}] } {
				#	status_log "not enough data to complete chunk...$cTotalDataSize - $cOffset - $cMsgSize - [string length $chunkedData($cId)]" 
				return
			} else {
				#status_log "data completed... $cTotalDataSize - $cOffset - [string length $chunkedData($cId)]"
				set data $chunkedData($cId)
				unset chunkedData($cId)
				#				set headend 0
				set cMsgSize $cTotalDataSize
			}

		}

		if { [lindex [SessionList get $cSid] 7] == "ignore" } {
			status_log "MSNP2P | $cSid -> Ignoring packet! not for us!\n"
			return
		}


		# Check if this is an ACK Message
		# TODO : Actually check if the ACK is good ? check size and all that crap...
		if { $cMsgSize == 0 } {
			# Let us check if any of our sessions is waiting for an ACK
			set sid [SessionList findid $cAckId]
			status_log "GOT SID : $sid for Ackid : $cAckId\n"
			if { $sid != -1 } {
			 	#status_log "MSNP2P | $sid -> Got MSNP2P ACK " red

			 	# We found a session id that is waiting for an ACK
			 	set step [lindex [SessionList get $sid] 4]

				# Just these 2 for now, will probably need more with file transfers
				switch $step {
					DATAPREP {
						# Set the right variables, prepare to send data after next ack
						SessionList set $sid [list -1 4 0 -1 "SENDDATA" -1 -1 -1 -1 -1]

						# We need to send a data preparation message
						SendPacket [::MSN::SBFor $chatid] [MakePacket $sid [binary format i 0]]
						status_log "MSNP2P | $sid -> Sent DATA Preparation\n" red
					}
					SENDDATA {
						#status_log "MSNP2P | $sid -> Sending DATA now\n" red
						set file [lindex [SessionList get $sid] 8]
						if { $file != "" } {
							SendData $sid $chatid "[lindex [SessionList get $sid] 8]"
						} else {
							SendData $sid $chatid "[::skin::GetSkinFile displaypic [::config::getKey displaypic]]"
						}
					}
					DATASENT {
						SessionList set $sid [list -1 -1 0 -1 0 -1 -1 -1 -1 -1]
						#status_log "MSNP2P | $sid -> Got ACK for sending data, now sending BYE\n" red
						set branchid "[format %X [myRand 4369 65450]][format %X [myRand 4369 65450]]-[format %X [myRand 4369 65450]]-[format %X [myRand 4369 65450]]-[format %X [myRand 4369 65450]]-[format %X [myRand 4369 65450]][format %X [myRand 4369 65450]][format %X [myRand 4369 65450]]"
						SendPacket [::MSN::SBFor $chatid] [MakePacket $sid [MakeMSNSLP "BYE" [lindex [SessionList get $sid] 3] [::config::getKey login] "$branchid" "0" [lindex [SessionList get $sid] 5] 0 0] 1]
					}
				}
			}
			return
		}

		#status_log "ReadData : data : $data" green
		# Check if this is an INVITE message

		if { [string first "INVITE MSNMSGR" $data] != -1 } {
			#status_log "Got an invitation!\n" red

			# Let's get the session ID, destination email, branchUID, UID, AppID, Cseq
			set idx [expr {[string first "SessionID:" $data] + 11}]
			set idx2 [expr {[string first "\r\n" $data $idx] - 1}]
			set sid [string range $data $idx $idx2]

			set idx [expr {[string first "From: <msnmsgr:" $data] + 15}]
			set idx2 [expr {[string first "\r\n" $data $idx] - 2}]
			set dest [string range $data $idx $idx2]

			set idx [expr {[string first "branch=\{" $data] + 8}]
			set idx2 [expr {[string first "\}" $data $idx] - 1}]
			set branchuid [string range $data $idx $idx2]

			set idx [expr {[string first "Call-ID: \{" $data] + 10}]
			set idx2 [expr {[string first "\}" $data $idx] - 1}]
			set uid [string range $data $idx $idx2]

			set idx [expr {[string first "CSeq:" $data] + 6}]
			set idx2 [expr {[string first "\r\n" $data $idx] - 1}]
			set cseq [string range $data $idx $idx2]

			set idx [expr {[string first "Content-Type: " $data $idx] + 14}]
			set idx2 [expr {[string first "\r\n" $data $idx] - 1}]
			set ctype [string range $data $idx $idx2]

			status_log "Got INVITE with content-type : $ctype\n" red

			if { $ctype == "application/x-msnmsgr-transreqbody"} {

				set sid [SessionList findcallid $uid]
				set type [lindex [SessionList get $sid] 7]

				#this catches an error with MSN7, still need to find out why sid = -1
				if {$sid == -1} {return}
				set idx [expr {[string first "Conn-Type: " $data] + 11}]
				set idx2 [expr {[string first "\r\n" $data $idx] - 1}]
				set conntype [string range $data $idx $idx2]

				set idx [expr {[string first "UPnPNat: " $data] + 9}]
				set idx2 [expr {[string first "\r\n" $data $idx] - 1}]
				set upnp [string range $data $idx $idx2]

				# Let's send an ACK
				SendPacket [::MSN::SBFor $chatid] [MakeACK $sid 0 $cTotalDataSize $cId $cAckId]

				if { $type == "filetransfer" } {
					# We received an invite for a FT, send 200 OK
					::MSN6FT::answerFTInvite $sid $chatid $branchuid $conntype
				} elseif { $type == "webcam" } {
					::MSNCAM::answerCamInvite $sid $chatid $branchuid
				}

			} elseif { $ctype == "application/x-msnmsgr-sessionreqbody" } {

				# Let's check if it's an invitation for buddy icon or emoticon
				set idx [expr {[string first "EUF-GUID:" $data] + 11}]
				set idx2 [expr {[string first "\}" $data $idx] - 1}]
				set eufguid [string range $data $idx $idx2]

				set idx [expr {[string first "Context:" $data] + 9}]
				set idx2 [expr {[string first "\r\n" $data $idx] - 1}]

				if { $idx == 8 || $idx2 == -2 } {
					# Let's send an ACK
					SendPacket [::MSN::SBFor $chatid] [MakeACK $sid 0 $cTotalDataSize $cId $cAckId]
					status_log "MSNP2P | $sid $dest -> Sent ACK for INVITE\n" red

					# Send a 500 Internal Error in case tehre is no context field sent...
					set slpdata [MakeMSNSLP "ERROR" $dest [::config::getKey login] $branchuid [expr {$cseq + 1}] $uid 0 0 $sid]
					SendPacket [::MSN::SBFor $chatid] [MakePacket $sid $slpdata 1]
					status_log "MSNP2P | $sid $dest -> Sent 500 Internal error.. No Context field in request\n" red

					# Avoid it entering the next if processing...
					set eufguid ""
				} else {
					set context [string range $data $idx $idx2]
				}

				status_log "$idx $idx2"

				if { $eufguid == "A4268EEC-FEC5-49E5-95C3-F126696BDBF6" ||
				     $eufguid == "5D3E02AB-6190-11D3-BBBB-00C04F795683" ||
				     $eufguid == "E073B06B-636E-45B7-ACA4-6D4B5978C93C" ||
				     $eufguid == "4BD96FC0-AB17-4425-A14A-439185962DC8" ||
				     $eufguid == "1C9AA97E-9C05-4583-A3BD-908A196F1E92"} {
					status_log "MSNP2P | $sid $dest -> Got INVITE for buddy icon, emoticon, or file transfer, or Wink(MSN 7)\n" red
					# Make new data structure for this session id
					if { $eufguid == "A4268EEC-FEC5-49E5-95C3-F126696BDBF6" } {
						# Buddyicon or emoticon
						set filenameFromContext [GetFilenameFromContext $context]
						if { $filenameFromContext != "" } {
							#status_log "dest = $dest , uid = $uid , sid = $sid"
							if { ! ( $filenameFromContext == [::config::getKey displaypic] && [::abook::getContactData $dest dontshowdp] == 1 ) } {
								SessionList set $sid [list 0 0 0 $dest 0 $uid 0 "bicon" $filenameFromContext ""]
							} else {
								return ""
							}
						} else {
							status_log "MSNP2P | $sid -> This is not an invitation for us, don't reply.\n" red
							# Now we tell this procedure to ignore all packets with this sid
							SessionList set $sid [list 0 0 0 0 0 0 0 "ignore" 0 ""]
							return
						}
					} elseif { $eufguid == "5D3E02AB-6190-11D3-BBBB-00C04F795683" } {
						# File transfer
						#check if a conversation is open with that contact
						#no need to test either a chatwindow has been created or not, because MakeFor is going to do it ! 
						::ChatWindow::MakeFor $chatid
						SessionList set $sid [list 0 0 0 $dest 0 $uid 0 "filetransfer" "" "$branchuid"]
						# Let's send an ACK
						SendPacket [::MSN::SBFor $chatid] [MakeACK $sid 0 $cTotalDataSize $cId $cAckId]
						status_log "MSNP2P | $sid $dest -> Sent ACK for INVITE\n" red

						# Let's get filename and filesize from context
						set idx [expr {[string first "Context:" $data] + 9}]
						set context [base64::decode [string range $data $idx end]]
						::MSN6FT::GotFileTransferRequest $chatid $dest $branchuid $cseq $uid $sid $context
						return

					} elseif { $eufguid =="E073B06B-636E-45B7-ACA4-6D4B5978C93C"} {
						#We received Winks
						status_log "####WINKS RECEIVED####\n" blue
						set decoding [base64::decode $context]
						status_log "$decoding\n" blue
						status_log "######################\n" blue

						# Let's notify the user that he/she has received a Wink
						SendMessageFIFO [list ::amsn::WinWrite $chatid "\n [trans winkreceived [::abook::getDisplayNick $chatid]]\n" black "" 0] "::amsn::messages_stack($chatid)" "::amsn::messages_flushing($chatid)"
					} elseif { $eufguid == "4BD96FC0-AB17-4425-A14A-439185962DC8" ||
						   $eufguid == "1C9AA97E-9C05-4583-A3BD-908A196F1E92" }	{
						#check if a conversation is open with that contact
						#no need to test either a chatwindow has been created or not, because MakeFor is going to do it ! 
						::ChatWindow::MakeFor $chatid
						if { $eufguid == "4BD96FC0-AB17-4425-A14A-439185962DC8" } {
							set producer 0
						} else {
							set producer 1
						}

						status_log "we got an webcam invitation" red

						set context [base64::decode $context]
						set context [FromUnicode $context]

						#answerFtInvite $sid $chatid $branchuid $conntype
						# Let's send an ACK
						SendPacket [::MSN::SBFor $chatid] [MakeACK $sid 0 $cTotalDataSize $cId $cAckId]

						SessionList set $sid [list 0 0 0 $dest 0 $uid 0 "webcam" "" "$branchuid"]
						
						if { $context != "\{B8BE70DE-E2CA-4400-AE03-88FF85B9F4E8\}" } {
							status_log "Received a video conferenced invitation.. we do not support this"
							::CAMGUI::InvitationRejected $chatid $sid $branchuid $uid
							#::MSNCAM::RejectFT $chatid $sid $branchuid $uid
							::CAMGUI::GotVideoConferenceInvitation $chatid
							return
						}

						

						::CAMGUI::AcceptOrRefuse $chatid $dest $branchuid $cseq $uid $sid $producer

						status_log "MSNP2P | $sid $dest -> Sent ACK for INVITE\n" red
						return
					}

					# Let's send an ACK
					SendPacket [::MSN::SBFor $chatid] [MakeACK $sid 0 $cTotalDataSize $cId $cAckId]
					status_log "MSNP2P | $sid $dest -> Sent ACK for INVITE\n" red

					# Let's make and send a 200 OK Message
					set slpdata [MakeMSNSLP "OK" $dest [::config::getKey login] $branchuid [expr {$cseq + 1}] $uid 0 0 $sid]
					SendPacket [::MSN::SBFor $chatid] [MakePacket $sid $slpdata 1]
					status_log "MSNP2P | $sid $dest -> Sent 200 OK Message\n" red
					
					# Send Data Prep AFTER ACK received (set AfterAck)
					SessionList set $sid [list -1 -1 -1 -1 "DATAPREP" -1 -1 -1 -1 -1]
					
					return
				}
			} elseif { $ctype == "application/x-msnmsgr-transrespbody" } {
				set idx [expr {[string first "Call-ID: \{" $data] + 10}]
				set idx2 [expr {[string first "\}" $data $idx] -1}]
				set uid [string range $data $idx $idx2]
				set sid [SessionList findcallid $uid]
				set idx [expr {[string first "Listening: " $data] + 11}]
				set idx2 [expr {[string first "\r\n" $data $idx] -1}]
				set listening [string range $data $idx $idx2]
				
				#status_log "MSNP2P | $sid -> Got 200 OK for File transfer, parsing result\n"
				#status_log "MSNP2P | $sid -> Found uid = $uid , lestening = $listening\n"

				if { $sid != -1 }  {
					SendPacket [::MSN::SBFor $chatid] [MakeACK $sid 0 $cTotalDataSize $cId $cAckId]
					set type [lindex [SessionList get $sid] 7]

					if { $listening == "true" } {
						set idx [expr {[string first "Nonce: \{" $data] + 8}]
						set idx2 [expr {[string first "\r\n" $data $idx] -2}]
						set nonce [string range $data $idx $idx2]

						if {[string first "IPv4External-Addrs: " $data] != -1 } {
							set idx [expr {[string first "IPv4External-Addrs: " $data] + 20}]
							set idx2 [expr {[string first "\r\n" $data $idx] -1}]
							set addr [string range $data $idx $idx2]

							set idx [expr {[string first "IPv4External-Port: " $data] + 19}]
							set idx2 [expr {[string first "\r\n" $data $idx] -1}]
							set port [string range $data $idx $idx2]

							status_log "MSNP2P | $sid -> Receiver is listening EXTERNAL with $addr : $port\n" red
							if {$type == "filetransfer" } {
								::MSN6FT::ConnectSockets $sid $nonce $addr $port 0
							} elseif { $type == "webcam" } {
								#::MSNCAM::connectMsnCam2 $sid $nonce $addr $port 0
							}
						} 
						if {[string first "IPv4Internal-Addrs: " $data] != -1 } {
							set idx [expr {[string first "IPv4Internal-Addrs: " $data] + 20}]
							set idx2 [expr {[string first "\r\n" $data $idx] -1}]
							set addr [string range $data $idx $idx2]

							set idx [expr {[string first "IPv4Internal-Port: " $data] + 19}]
							set idx2 [expr {[string first "\r\n" $data $idx] -1}]
							set port [string range $data $idx $idx2]

							status_log "MSNP2P | $sid -> Receiver is listening INTERNAL with $addr : $port\n" red
							if {$type == "filetransfer" } {
								::MSN6FT::ConnectSockets $sid $nonce $addr $port 0
							} elseif { $type == "webcam" } {
								#::MSNCAM::connectMsnCam2 $sid $nonce $addr $port 0
							}
						}


					}
				}
			}
		}
		# Check if it is a 200 OK message
		if { [string first "MSNSLP/1.0 200 OK" $data] != -1 } {
			# Send a 200 OK ACK
			set first [string first "SessionID:" $data]
			#msn8 introduce a SessionId in the Direct connection invitation message, that's why we check for listening
			set listening_idx [string first "Listening:" $data]
			if { $first != -1 && $listening_idx == -1} {
				set idx [expr {[string first "SessionID:" $data] + 11}]
				set idx2 [expr {[string first "\r\n" $data $idx] -1}]
				set sid [string range $data $idx $idx2]
				set type [lindex [SessionList get $sid] 7]

				if { $type == "ignore" } {
					#status_log "MSNP2P | $sid -> Ignoring packet! not for us!\n"
					return
				}

				#status_log "MSNP2P | $sid -> Got 200 OK message, sending an ACK for it\n" red
				SendPacket [::MSN::SBFor $chatid] [MakeACK $sid 0 $cTotalDataSize $cId $cAckId]
				if { $type == "filetransfer" } {
					::MSN6FT::SendFTInvite $sid $chatid
				}
			} else {
				set idx [expr {[string first "Call-ID: \{" $data] + 10}]
				set idx2 [expr {[string first "\}" $data $idx] -1}]
				set uid [string range $data $idx $idx2]
				set sid [SessionList findcallid $uid]
				set idx [expr {[string first "Listening: " $data] + 11}]
				set idx2 [expr {[string first "\r\n" $data $idx] -1}]
				set listening [string range $data $idx $idx2]

				#status_log "MSNP2P | $sid -> Got 200 OK for File transfer, parsing result\n"
				#status_log "MSNP2P | $sid -> Found uid = $uid , lestening = $listening\n"


				set type [lindex [SessionList get $sid] 7]

				if { $sid != -1 }  {
					SendPacket [::MSN::SBFor $chatid] [MakeACK $sid 0 $cTotalDataSize $cId $cAckId]

					if { $listening == "true" } {
						set idx [expr {[string first "Nonce: \{" $data] + 8}]
						set idx2 [expr {[string first "\r\n" $data $idx] -2}]
						set nonce [string range $data $idx $idx2]

						if {[string first "IPv4External-Addrs: " $data] != -1 } {
							set idx [expr {[string first "IPv4External-Addrs: " $data] + 20}]
							set idx2 [expr {[string first "\r\n" $data $idx] -1}]
							set addr [string range $data $idx $idx2]

							set idx [expr {[string first "IPv4External-Port: " $data] + 19}]
							set idx2 [expr {[string first "\r\n" $data $idx] -1}]
							set port [string range $data $idx $idx2]

							status_log "MSNP2P | $sid -> Receiver is listening EXTERNAL with $addr : $port\n" red
							#after 5500 "::MSNP2P::SendData $sid $chatid [lindex [SessionList get $sid] 8]"
							if { $type == "filetransfer" } {
								::MSNP2P::SessionList set $sid [list -1 -1 -1 -1 "INVITE2" -1 -1 -1 -1 -1]
								::MSN6FT::ConnectSockets $sid $nonce $addr $port 1
							} elseif { $type == "webcam" } {
								::MSNCAM::SendSyn $sid $chatid
							}
						} 
						if { [string first "IPv4Internal-Addrs: " $data] != -1 } {
							set idx [expr {[string first "IPv4Internal-Addrs: " $data] + 20}]
							set idx2 [expr {[string first "\r\n" $data $idx] -1}]
							set addr [string range $data $idx $idx2]

							set idx [expr {[string first "IPv4Internal-Port: " $data] + 19}]
							set idx2 [expr {[string first "\r\n" $data $idx] -1}]
							set port [string range $data $idx $idx2]

							status_log "MSNP2P | $sid -> Receiver is listening INTERNAL with $addr : $port\n" red
							#after 5500 "::MSNP2P::SendData $sid $chatid [lindex [SessionList get $sid] 8]"
							if { $type == "filetransfer" } {
								::MSNP2P::SessionList set $sid [list -1 -1 -1 -1 "INVITE2" -1 -1 -1 -1 -1]
								::MSN6FT::ConnectSockets $sid $nonce $addr $port 1
							} elseif { $type == "webcam" } {
								::MSNCAM::SendSyn $sid $chatid
							}
						}

					} elseif { $listening == "false" } {
						status_log "MSNP2P | $sid -> Receiver is not listening, sending INVITE\n" red
						if { $type == "filetransfer" } {
							::MSN6FT::SendFTInvite2 $sid $chatid
						} elseif { $type == "webcam" } {
							::MSNCAM::SendSyn $sid $chatid
							#::MSNCAM::SendAcceptInvite $sid $chatid
						}
					} else {
						status_log "Error sending file, got answer to invite :\n$data\n\n" red
					}
				}
			}


			return


		}

		# Check if we got BYE message
		if { [string first "BYE MSNMSGR:" $data] != -1 } {
			# Lets get the call ID and find our SessionID
			set idx [expr {[string first "Call-ID: \{" $data] + 10}]
			set idx2 [expr {[string first "\}" $data $idx] - 1}]
			set uid [string range $data $idx $idx2]
			set sid [SessionList findcallid $uid]
			status_log "MSNP2P | $sid -> Got BYE for UID : $uid\n" red

			if { $sid != -1 } {
				# Send a BYE ACK
				SendPacket [::MSN::SBFor $chatid] [MakeACK $sid 0 $cTotalDataSize $cId $cAckId]
				status_log "MSNP2P | $sid -> Sending BYE ACK\n" red

				# If it's a file transfer, advise the user it has been canceled
				if { [lindex [SessionList get $sid] 7] == "filetransfer" } {
					status_log "File transfer canceled\n"
					::MSNP2P::SessionList set $sid [list -1 -1 -1 -1 -1 -1 -1 "ftcanceled" -1 -1]
					if { [::amsn::FTProgress ca $sid [lindex [SessionList get $sid] 6]] == -1 } {
						::amsn::RejectFT $chatid "-2" $sid
					}
				}
				if { [lindex [SessionList get $sid] 7] == "webcam" } {
					status_log "Webcam canceled\n"
					::MSNCAM::CamCanceled $chatid $sid
				}

				# Delete SessionID Data
				SessionList unset $sid

			} else {
				status_log "MSNP2P | $sid -> Got a BYE for unexisting SessionID\n" red
			}
			return
		}
		# Check if we got DECLINE message
		if { [string first "603 Decline" $data] != -1 } {
			# Lets get the call ID and find our SessionID
			set idx [expr {[string first "Call-ID: \{" $data] + 10}]
			set idx2 [expr {[string first "\}" $data $idx] - 1}]
			set uid [string range $data $idx $idx2]
			set sid [SessionList findcallid $uid]
			status_log "MSNP2P | $sid -> Got DECLINE for UID : $uid\n" red

			if { $sid != -1 } {
				# Send a BYE ACK
				SendPacket [::MSN::SBFor $chatid] [MakeACK $sid 0 $cTotalDataSize $cId $cAckId]
				status_log "MSNP2P | $sid -> Sending DECLINE ACK\n" red

				# If it's a file transfer, advise the user it has been canceled
				if { [lindex [SessionList get $sid] 7] == "webcam" } {
				    set idx [expr {[string first "Content-Type: " $data] + 14}]
				    set idx2 [expr {[string first "\r\n" $data $idx] - 1}]
				    set content_type [string range $data $idx $idx2]
				    if { $content_type != "null" } {
					::CAMGUI::InvitationDeclined $chatid $sid
				    } else {
					::MSNCAM::SendSyn $sid $chatid
				    }
				}


			} else {
				status_log "MSNP2P | $sid -> Got a DECLINE for unexisting SessionID\n" red
			}
		}


		# Let's check for data preparation messages and data messages
		if { $cSid != 0 } {
			# Make sure this isn't a canceled FT
			if { [lindex [SessionList get $cSid] 7] == "ftcanceled" } { return }
			set sid $cSid
			set fd [lindex [SessionList get $cSid] 6]
			set type [lindex [SessionList get $cSid] 7]

			#If it's a file transfer, display Progress bar
			if { $type == "filetransfer" } {
				#::amsn::FTProgress w $cSid "" [trans throughserver]
				::amsn::FTProgress r $cSid [lindex [SessionList get $cSid] 6] $cOffset $cTotalDataSize
			}
			if { $type != "webcam" && $fd != "" && $fd != 0 && $fd != -1 } {
				# File already open and being written to (fd exists)
				# Lets write data to file
				seek $fd $cOffset
				set written [getObjOption $sid written]
				if { $written == "" } {
					set written $cMsgSize
				} else {
					incr written $cMsgSize
				}
				setObjOption $sid written $written

				puts -nonewline $fd [string range $data 0 [expr {$cMsgSize - 1}]]
				#status_log "MSNP2P | $sid -> FD EXISTS, file already open... with fd = $fd --- $cOffset + $cMsgSize + $cTotalDataSize . Writing DATA to file\n" red
				# Check if this is last part if splitted
				if { $written >= $cTotalDataSize } {
					close $fd

					set session_data [SessionList get $cSid]
					set user_login [lindex $session_data 3]
					set filename [lindex $session_data 8]
					#We have closed the file so we set its fd to 0
					SessionList set $cSid [list -1 -1 -1 -1 -1 -1 0 -1 -1 -1]

					# Lets send an ACK followed by a BYE if it's a buddy icon or emoticon
					#status_log "MSNP2P | $sid -> Sending an ACK for file received and sending a BYE\n" red
					SendPacket [::MSN::SBFor $chatid] [MakeACK $sid $cSid $cTotalDataSize $cId $cAckId]

					if { [lindex [SessionList get $cSid] 7] == "bicon" } {
						SendPacket [::MSN::SBFor $chatid] [MakePacket $sid [MakeMSNSLP "BYE" $user_login [::config::getKey login] "19A50529-4196-4DE9-A561-D68B0BF1E83F" 0 [lindex $session_data 5] 0 0] 1]

						#set filename2 [::MSNP2P::GetFilenameFromMSNOBJ [::abook::getVolatileData $user_login msnobj]]
						set filename2 [::abook::getContactData $user_login displaypicfile ""]
						status_log "MSNP2P | $sid -> Got picture with file : $filename and $filename2\n" blue
						if {$filename == $filename2 } {

							status_log "MSNP2P | $sid -> Closed file $filename.. finished writing\n" red
							
							::skin::getDisplayPicture $user_login 1
							::amsn::UpdateAllPictures

							set desc_file "[file join $HOME displaypic cache ${filename}.dat]"
							create_dir [file join $HOME displaypic]
							set fd [open [file join $HOME displaypic $desc_file] w]
							status_log "Writing description to $desc_file\n"
#							puts $fd "[clock format [clock seconds] -format %x]\n$user_login"
							puts $fd "[clock seconds]\n$user_login"
							close $fd

						} else {
							#set file [png_to_gif [file join $HOME smileys cache ${filename}.png]]
							set file [file join $HOME smileys cache ${filename}.png]
							if { $file != "" } {
								set tw [::ChatWindow::GetOutText [::ChatWindow::For $chatid]]
								#set file [filenoext $file].gif
								set scrolling [::ChatWindow::getScrolling $tw]
								catch {image create photo emoticonCustom_std_${filename} -file "[file join $HOME smileys cache ${filename}.png]" -format cximage}
								
								# Make sure the smiley is max 50x50
								::smiley::resizeCustomSmiley emoticonCustom_std_${filename}
								if { $scrolling } { ::ChatWindow::Scroll $tw }
							}
						}

					} elseif { [lindex [SessionList get $cSid] 7] == "voice" } {
						set file [file join $HOME voiceclips cache ${filename}.wav]
						if { $file != "" && [file exists $file]} {
							
							::ChatWindow::ReceivedVoiceClip $chatid $file
							status_log "VOICE: file <$file> should be decoded and played..." red
						} else {
							status_log "VOICE: file <$file> does not exist" red
						}
						
					} elseif { [lindex [SessionList get $cSid] 7] == "wink" } { 
						# Winks support is not in the core, all we can do is support it in the msnp2p code
						# and launch an event for an appropriate plugin to manage it.
						set evPar(chatid) chatid
						set evPar(filename) [file join $HOME winks cache ${filename}.cab]
						::plugins::PostEvent WinkReceived evPar
					} elseif { [lindex [SessionList get $cSid] 7] == "filetransfer" } {
						# Display message that file transfer is finished...
						status_log "MSNP2P | $cSid -> File transfer finished!\n"
						set filename [file join [::config::getKey receiveddir] [lindex [SessionList get $cSid] 8]]
						::amsn::FTProgress fr $cSid [lindex [SessionList get $cSid] 6] $cOffset $cTotalDataSize
						set finishedname [filenoext $filename]
						if { [ string range $filename [expr {[string length $filename] - 11}] [string length $filename]] == ".incomplete" } {
							if { [catch { file rename $filename $finishedname } ] } {
								::amsn::infoMsg [trans couldnotrename $filename] warning
								status_log "Could not rename file $filename to $finishedname!"
							}
						}		
				SessionList set $cSid [list -1 -1 -1 -1 -1 -1 -1 "filetransfersuccessfull" -1 -1]
					}
				}
			} elseif { $cMsgSize == 4 } {
				# We got ourselves a DATA PREPARATION message, lets open file and send ACK
				set session_data [SessionList get $sid]
				set user_login [lindex $session_data 3]
				#status_log "MSNP2P | $sid $user_login -> Got data preparation message, opening file for writing\n" red
				set filename [lindex $session_data 8]
				#set filename2 [::MSNP2P::GetFilenameFromMSNOBJ [::abook::getVolatileData $user_login msnobj]]
				set filename2 [::abook::getContactData $user_login displaypicfile ""]
				#status_log "MSNP2P | $sid $user_login -> opening file $filename for writing with $filename2 as user msnobj\n\n" blue
				set type [lindex [SessionList get $cSid] 7]
				if {$type == "bicon" } {
					if { $filename == $filename2 } {
						create_dir [file join $HOME displaypic cache]
						set fd [open "[file join $HOME displaypic cache ${filename}.png]" w]
					} else {
						create_dir [file join $HOME smileys cache]
						set fd [open "[file join $HOME smileys cache ${filename}.png]" w]
					}
				} elseif {$type == "voice" } {
					create_dir [file join $HOME voiceclips]
					create_dir [file join $HOME voiceclips cache]
					set fd [open "[file join $HOME voiceclips cache ${filename}.wav]" w]
				} elseif {$type == "wink" }  {
					set fd [open "[file join $HOME winks cache ${filename}.cab]" w]
				}

				fconfigure $fd -translation {binary binary}
				SendPacket [::MSN::SBFor $chatid] [MakeACK $sid $cSid $cTotalDataSize $cId $cAckId]
				#status_log "MSNP2P | $sid $user_login -> Sent an ACK for DATA PREP Message\n" red
				SessionList set $sid [list -1 -1 -1 -1 -1 -1 $fd -1 -1 -1]
			} elseif { $type == "webcam" } {
				# WEBCAM TO COMPLETE

				set h1 [string range $data 0 3]
				set h2 [string range $data 4 9]
				set msg [string range $data 10 [expr { $cMsgSize - 1}]]

				set msg [FromUnicode $msg]

				status_log "Received data for webcam $sid : $data\n$msg\n" red

				if {[expr {$cOffset + $cMsgSize}] >= $cTotalDataSize} {
					SendPacket [::MSN::SBFor $chatid] [MakeACK $sid $cSid $cTotalDataSize $cId $cAckId]
				}

				if { $msg == "syn\x00" } {

					::MSNCAM::SendSyn $sid $chatid

					::MSNCAM::SendAck $sid $chatid

				} elseif { $msg == "ack\x00" } {
					set producer [getObjOption $sid producer]
					status_log "Received the ack for webcam\n" red

					if {$producer} {
						status_log "We should send the XML\n" red
						::MSNCAM::SendXML $chatid $sid
					}

				} elseif { $msg == "receivedViewerData\x00" } {
					status_log "ReceivedViewData received\n" red
					::MSNCAM::ConnectSockets $sid
				} elseif {[string first "<producer>" $msg] == 0 || [string first "<viewer>" $msg] == 0 || $cOffset != 0} {
					set xml [getObjOption $sid xml]
					set xml "${xml}[string range $data 0 [expr { $cMsgSize - 1}]]"

					setObjOption $sid xml $xml

					if { [expr {$cOffset + $cMsgSize}] >= $cTotalDataSize } {
						set xml [string range $xml 10 end]
						setObjOption $sid xml $xml

						::MSNCAM::ReceivedXML $chatid $sid
					}

				} elseif { [string first "ReflData:" $msg] == 0 } {
					set refldata [string range $msg 9 end-1]
					set refldata [binary format H* $refldata]

					::MSNCAM::ConnectToReflector $sid $refldata

				} else {
					status_log "UNKNOWN" red
				}


			} elseif {$sid == 64} {
				set msg [FromUnicode $data]
				set ink_message [getObjOption $sid ink_message_$cId]
				set ink_message "${ink_message}[string range $data 0 [expr { $cMsgSize - 1}]]"
				setObjOption $sid ink_message_$cId $ink_message

				if {[expr {$cOffset + $cMsgSize}] >= $cTotalDataSize} {
					setObjOption $sid ink_message_$cId ""
					SendPacket [::MSN::SBFor $chatid] [MakeACK $sid $cSid $cTotalDataSize $cId $cAckId]
					set ink_message [FromUnicode $ink_message]
					set idx [string first "\r\n\r\n" $ink_message]
					incr idx 4
					if {[string first "\x00" $ink_message $idx] == $idx } {
						incr idx
					}
					set body [string range $ink_message $idx end]

					if { [string first "base64:" $body] != -1 } {
						set data [::base64::decode [string range $body 7 end]]
					} else {
						set data $body
					}
					set img [image create photo [TmpImgName] -data $data]
					set user [lindex [::MSN::usersInChat $chatid] 0]
					set nick [::abook::getDisplayNick $user]
					set p4c_enabled 0
					status_log "got ink from $user - $nick with image $img"
					SendMessageFIFO [list ::amsn::ShowInk $chatid $user $nick $img ink $p4c_enabled] "::amsn::messages_stack($chatid)" "::amsn::messages_flushing($chatid)"
				}

			} else {
				status_log "Received data for unknown type : $sid\n" red
			}
		}
	}

	#//////////////////////////////////////////////////////////////////////////////
	# RequestObject ( chatid msnobject filename)
	# This function creates the invitation packet in order to receive an MSNObject (custom emoticon and display buddy for now)
	# chatid : Chatid from which we will request the object
	# dest : The email of the user that will receive our request
	# msnobject : The object we want to request (has to be url decoded)
	proc RequestObject { chatid dest msnobject} {
		RequestObjectEx $chatid $dest $msnobject "bicon"
	}

	proc RequestObjectEx { chatid dest msnobject type} {
		# Let's create a new session
		set sid [expr {int([expr rand() * 1000000000])%125000000 + 4 } ]
		# Generate BranchID and CallID
		set branchid "[format %X [myRand 4369 65450]][format %X [myRand 4369 65450]]-[format %X [myRand 4369 65450]]-[format %X [myRand 4369 65450]]-[format %X [myRand 4369 65450]]-[format %X [myRand 4369 65450]][format %X [myRand 4369 65450]][format %X [myRand 4369 65450]]"
		set callid "[format %X [myRand 4369 65450]][format %X [myRand 4369 65450]]-[format %X [myRand 4369 65450]]-[format %X [myRand 4369 65450]]-[format %X [myRand 4369 65450]]-[format %X [myRand 4369 65450]][format %X [myRand 4369 65450]][format %X [myRand 4369 65450]]"

		SessionList set $sid [list 0 0 0 $dest 0 $callid 0 "$type" [::MSNP2P::GetFilenameFromMSNOBJ $msnobject] ""]

		# Create and send our packet
		set slpdata [MakeMSNSLP "INVITE" $dest [::config::getKey login] $branchid 0 $callid 0 0 "A4268EEC-FEC5-49E5-95C3-F126696BDBF6" $sid 1 [string map { "\n" "" } [::base64::encode "$msnobject\x00"]]]
		SendPacket [::MSN::SBFor $chatid] [MakePacket $sid $slpdata 1]
		status_log "Sent an INVITE to $dest on chatid $chatid of object $msnobject\n" red
	}

	#//////////////////////////////////////////////////////////////////////////////
	# MakePacket ( sid slpdata [nullsid] [MsgId] [TotalSize] [Offset] [Destination] )
	# This function creates the appropriate MSNP2P packet with the given SLP info
	# This will be used for everything except for ACK's
	# slpdata	: the SLP info created by MakeMSNSLP that will be included in the packet
	#		  it could also be binary data that will be sent in the P2P packet
	# sid		: the session id
	# nullsid 	: 0 to add sid to header, 1 to put 0 instead of sid in header (usefull for negot + bye)
	# Returns the MSNP2P packet (half text half binary)
	proc MakePacket { sid slpdata {nullsid "0"} {MsgId "0"} {TotalSize "0"} {Offset "0"} {Destination "0"} {AfterAck "0"} {flags "0"}} {

		# Let's get our session id variables and put them in a list
		# If sessionid is 0, means we want to initiate a new session
		if { $sid != 0 } {
			set SessionInfo [SessionList get $sid]
			set MsgId [lindex $SessionInfo 0]
			set TotalSize [lindex $SessionInfo 1]
			set Offset [lindex $SessionInfo 2]
			set Destination [lindex $SessionInfo 3]
		}

		# Here is our text header
		set theader "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $Destination\r\n\r\n"

		# We start by creating the 48 byte binary header
		if { $nullsid != 0 } {
			# session id field set to 0 during negotiation and bye
			set bheader [binary format i 0]
		} else {
			# normal message
		    # This is a workaround to prevent a bug with Telepathy client which sends a 64-bit SID..
		    if {[catch {set bheader [binary format i $sid]}] } {
			set bheader [binary format i 0]
		    }
		}

		if { $MsgId == 0 } {
			# No MsgId let's generate one and add to our list
			set MsgId [expr {int([expr rand() * 1000000]) + 4}]
		} elseif { $Offset == 0 } {
			# Offset is different than 0, we need to incr the MsgId to prepare our next message
			incr MsgId
		}

		if {[string is digit $Offset] == 0 || $Offset == ""} {
			set Offset 0
		}

		append bheader [binary format i $MsgId]
		append bheader [binword $Offset]

		set CurrentSize [string length $slpdata]
		# We must set TotalSize to the size of data if it is > 1202 bytes otherwise we set to 0
		if { $TotalSize == 0 } {
			# This isn't a split message
			append bheader "[binword $CurrentSize][binary format i $CurrentSize]"
		} else {
			# This is a split message
			append bheader "[binword $TotalSize][binary format i $CurrentSize]"
			incr Offset $CurrentSize
			if { $Offset >= $TotalSize } {
				# We have finished sending the last part of the message
				set Offset 0
				set TotalSize 0
			}
		}

		# Set flags to 0
		append bheader [binary format i $flags]

		# Just give the Ack Session ID some dumbo random number
		append bheader [binary format i [myRand 4369 6545000]]
		#append bheader [binary format i 67152542]

		# Set last 2 ack fields to 0
		append bheader [binary format i 0][binword 0]

		# Now the footer
		if { $nullsid == 1 } {
			# Negotiating Session so set to 0
			set bfooter [binary format I 0]
		} else {
			# Either sending a display pic, or an emoticon so set to 1
			set bfooter [binary format I 1]
		}

		# Combine it all
		set packet "${theader}${bheader}${slpdata}${bfooter}"
		#status_log "Sent a packet with header $theader\n" red

		unset bheader
		unset bfooter
		unset slpdata

		# Save new Session Variables into SessionList
		SessionList set $sid [list $MsgId $TotalSize $Offset -1 -1 -1 -1 -1 -1 -1]

		return $packet
	}


	#//////////////////////////////////////////////////////////////////////////////
	# MakeACK (sid originalsid originalsize originalid originaluid)
	# This function creates an ack packet for msnp2p
	# original* arguments are all arguments of the message we want to ack
	# sid has to be != 0
	# Returns the ACK packet
	proc MakeACK { sid originalsid originalsize originalid originaluid } {
		set new 0
		if { $sid != 0 } {
			set SessionInfo [SessionList get $sid]
			set MsgId [lindex $SessionInfo 0]
			set Destination [lindex $SessionInfo 3]
		} else {
			return
		}

		if { $MsgId == 0 } {
			# No MsgId let's generate one and add to our list
			set MsgId [expr {int([expr rand() * 1000000]) + 10 } ]
			set new 1
		} else {
			incr MsgId
		}

		# The text header
		set theader "MIME-Version: 1.0\r\nContent-Type: application/x-msnmsgrp2p\r\nP2P-Dest: $Destination\r\n\r\n"

		# Set the binary header and footer
		set b [binary format ii $originalsid $MsgId][binword 0][binword $originalsize][binary format iiii 0 2 $originalid $originaluid][binword $originalsize][binary format I 0]

		# Save new Session Variables into SessionList
		if { $new == 1 } {
			incr MsgId -4
		}
		SessionList set $sid [list $MsgId -1 -1 -1 -1 -1 -1 -1 -1 -1]

		return "${theader}${b}"
	}


	#//////////////////////////////////////////////////////////////////////////////
	#	# MakeMSNSLP ( method to from branchuid cseq maxfwds contenttentype [A] [B] [C] [D] [E] [F] [G])
	# This function creates the appropriate MSNSLP packets
	# method :		INVITE, BYE, OK, DECLINE, ERROR
	# contenttype : 	0 for application/x-msnmsgr-sessionreqbody (Starting a session) or sessionclosebody for a BYE
	#			1 for application/x-msnmsgr-transreqbody (Starting transfer) or sessionclosebody for a BYE and use A
	#                                                                                                                 as context
	#			2 for application/x-msnmsgr-transrespbody (Starting transfer)
	#                       3 for null (starting webcam)
	#
	#
	# If INVITE method is chosen then A, B, C, D and/or E are used dependinf on contenttype
	# for 0 we got : "EUF-GUID", "SessionID", "AppID" and "Context" (E not used)
	# for 1 we got : "Bridges", "NetID", "Conn-Type", "UPnPNat" and "ICF"
	# for 2 we got : "Bridge", "Listening", "Nonce", "IPv4External-Addrs","IPv4External-Port"
	#		 "IPv4Internal-Addrs" and "IPv4Internal-Port"
	#
	# If OK method is chosen then A to G are used depending on contenttype
	# for 0 we got : "SessionID"
	# for 1 we got : "Bridge", "Listening", "Nonce", "IPv4External-Addrs","IPv4External-Port"
	#		 "IPv4Internal-Addrs" and "IPv4Internal-Port"
	# Returns the formated MSNSLP data
	proc MakeMSNSLP { method to from branchuid cseq uid maxfwds contenttype {A ""} {B ""} {C ""} {D ""} {E ""} {F ""} {G ""} } {

		# Generate start line
		if { $method == "INVITE" } {
			set data "INVITE MSNMSGR:${to} MSNSLP/1.0\r\n"
		} elseif { $method == "BYE" } {
			set data "BYE MSNMSGR:${to} MSNSLP/1.0\r\n"
		} elseif { $method == "OK" } {
			set data "MSNSLP/1.0 200 OK\r\n"
		} elseif { $method == "DECLINE" } {
			set data "MSNSLP/1.0 603 Decline\r\n"
		} elseif { $method == "ERROR" } {
			set data "MSNSLP/1.0 500 Internal Error\r\n"
		}

		# Lets create our message body first (so we can calc it's length for the header)
		set body ""
		if { $method == "INVITE" } {
			if { $contenttype == 0 } {
			    append body "EUF-GUID: {${A}}\r\nSessionID: ${B}\r\nAppID: ${C}\r\nContext: ${D}\r\n"
			} elseif { $contenttype == 1 } {
				append body "Bridges: ${A}\r\nNetID: ${B}\r\nConn-Type: ${C}\r\nUPnPNat: ${D}\r\nICF: ${E}\r\n"
			} else {
				append body "Bridge: ${A}\r\nListening: ${B}\r\nNonce: \{${C}\}\r\n"
				if {${B} == "true" } {
					if { [::abook::getDemographicField conntype] == "IP-Restrict-NAT" } {
						append body "IPv4External-Addrs: ${D}\r\nIPv4External-Port: ${E}\r\nIPv4Internal-Addrs: ${F}\r\nIPv4Internal-Port: ${G}\r\n"
					} else {
						append body "IPv4Internal-Addrs: ${D}\r\nIPv4Internal-Port: ${E}\r\n"
					}
				}

			}
		} elseif { $method == "OK" } {
			if { $contenttype == 0 } {
				append body "SessionID: ${A}\r\n"
			} else {
				append body "Bridge: ${A}\r\nListening: ${B}\r\nNonce: \{${C}\}\r\n"
				if {${B} == "true" } {
					if { [::abook::getDemographicField conntype] == "IP-Restrict-NAT" } {
						append body "IPv4External-Addrs: ${D}\r\nIPv4External-Port: ${E}\r\nIPv4Internal-Addrs: ${F}\r\nIPv4Internal-Port: ${G}\r\n"
					} else {
						append body "IPv4Internal-Addrs: ${D}\r\nIPv4Internal-Port: ${E}\r\n"
					}
				}

			}
		} elseif { $method == "DECLINE" || $method == "ERROR" } {
			append body "SessionID: ${A}\r\n"
		} elseif { $method == "BYE" && $contenttype == 1} {
			append body "Context: ${A}"
		} 

		append body "\r\n\x00"

		# Here comes the message header
		append data "To: <msnmsgr:${to}>\r\nFrom: <msnmsgr:${from}>\r\nVia: MSNSLP/1.0/TLP ;branch={${branchuid}}\r\nCSeq: ${cseq}\r\nCall-ID: {${uid}}\r\nMax-Forwards: ${maxfwds}\r\n"
		if { $method == "BYE" } {
			append data "Content-Type: application/x-msnmsgr-sessionclosebody\r\n"
		} else {
			if { $contenttype == 0 } {
				append data "Content-Type: application/x-msnmsgr-sessionreqbody\r\n"
			} elseif { $contenttype == 1 } {
				append data "Content-Type: application/x-msnmsgr-transreqbody\r\n"
			} elseif { $contenttype == 2 } {
				append data "Content-Type: application/x-msnmsgr-transrespbody\r\n"
			} elseif { $contenttype == 3 } {
				append data "Content-Type: null\r\n"
				set body ""
			}
		}
		append data "Content-Length: [expr [string length $body]]\r\n\r\n"

		append data $body
		unset body

		#status_log $data
		return $data
	}

	#//////////////////////////////////////////////////////////////////////////////
	# SendData ( sid chatid )
	# This procedure sends the data given by the filename in the Session vars given by SessionID
	proc SendData { sid chatid filename } {


		SessionList set $sid [list -1 [file size "${filename}"] -1 -1 -1 -1 -1 -1 -1 -1]
		set fd [lindex [SessionList get $sid] 6]
		if { $fd == 0 } {
			set fd [open "${filename}"]
			SessionList set $sid [list -1 -1 -1 -1 -1 -1 $fd -1 -1 -1]
			fconfigure $fd -translation binary
		}
		if { $fd == "" } {
			# 			set sock [sb get [MSN::SBFor $chatid] sock]

			# 			if { $sock != "" } {
			# 				fileevent $sock writable ""
			# 			}
			return
		}
		set chunk [read $fd 1200]
		SendPacket [::MSN::SBFor $chatid] [MakePacket $sid $chunk 0 0 0 0 0 0 32]
		unset chunk

		#status_log "[SessionList get $sid]\n"
		if { [lindex [SessionList get $sid] 1] == 0 } {
			# All file has been sent
			close $fd
			unset fd
			# We finished sending the data, set appropriate Afterack and Fd
			SessionList set $sid [list -1 -1 -1 -1 DATASENT -1 0 -1 -1]
		} else {
			#	set sock [sb get [MSN::SBFor $chatid] sock]
			# Still need to send
			#			if { $sock != "" } {
			after 100 "[list ::MSNP2P::SendData $sid $chatid ${filename}]"
			# }
		}


	}


	proc SendDataFile { sid chatid filename match } {

		if { [lindex [::MSNP2P::SessionList get $sid] 7] == "ftcanceled" } {
			return
		}

		if { [lindex [::MSNP2P::SessionList get $sid] 4] != "$match"} {
			return
		}

		status_log "state is [lindex [::MSNP2P::SessionList get $sid] 4] => sending through SB\n\n" red;

		#return

		SessionList set $sid [list -1 [file size "${filename}"] -1 -1 -1 -1 -1 -1 -1 -1]
		set fd [lindex [SessionList get $sid] 6]
		if { $fd == 0 || $fd == "" } {
			set fd [open "${filename}"]
			SessionList set $sid [list -1 -1 -1 -1 -1 -1 $fd -1 -1 -1]
			fconfigure $fd -translation {binary binary}
		}
		if { $fd == "" } {
			return
		}
		::amsn::FTProgress w $sid "" [trans throughserver]

		# 		SendPacketExt [::MSN::SBFor $chatid] $sid [read $fd] 0 0 0 0 0 0 16777264
		# 		close $fd


		set sbn [::MSN::SBFor $chatid]
		set sock [$sbn cget -sock]

		set offset 0
		SessionList set $sid [list -1 -1 $offset -1 -1 -1 -1 -1 -1 -1]

		if { [fileevent $sock writable] == "" } {
			status_log "assining new fileevent proc\n"
			fileevent $sock writable "::MSNP2P::SendDataEvent $sbn $sid $fd $match"
		}



	}

	proc SendDataEvent { sbn sid fd match} {


		set sock [$sbn cget -sock]
		fileevent $sock writable ""
		#return

		set offset [lindex [SessionList get $sid] 2]
		set filesize [lindex [SessionList get $sid] 1]

		if { $offset == "" } {
			close $fd
			return
		}

		if { [lindex [::MSNP2P::SessionList get $sid] 7] == "ftcanceled" } {
			close $fd
			return
		}

		if { [lindex [::MSNP2P::SessionList get $sid] 4] != "$match"} {
			return
		}

		set data [read $fd 1202]
		#		status_log "Reading 1202 bytes of data : got [string length $data]"
		if { [string length $data] >= 1202 } {
			set msg [MakePacket $sid $data 0 0 0 0 0 0 16777264]
			set msg_len [string length $msg]
			puts -nonewline $sock "MSG [incr ::MSN::trid] D $msg_len\r\n$msg"
			set offset [expr {$offset + 1202}]
			SessionList set $sid [list -1 -1 $offset -1 -1 -1 -1 -1 -1 -1]
			::amsn::FTProgress s $sid "" $offset $filesize
			#catch {after 200 [list catch {fileevent $sock writable "::MSNP2P::SendDataEvent $sbn $sid $fd"}]}
			after 200 [list ::MSNP2P::SetSendDataFileEvent $sock $sbn $sid $fd $match]
			
		} else {

			set msg [MakePacket $sid $data 0 0 0 0 0 0 16777264]
			set msg_len [string length $msg]
			puts -nonewline $sock "MSG [incr ::MSN::trid] D $msg_len\r\n$msg"
			set offset [expr {$offset + 1202}]
			SessionList set $sid [list -1 -1 0 -1 -1 -1 -1 -1 -1 -1 ]

			set msgId [expr {[lindex [SessionList get $sid] 0] + 1}]
			SessionList set $sid [list $msgId -1 0 -1 DATASENT -1 0 -1 -1 -1]
			close $fd
			unset fd

			::amsn::FTProgress fs $sid ""
		}
	}
	
	proc SetSendDataFileEvent { sock sbn sid fd match} { 
		catch { 
			fileevent $sock writable "::MSNP2P::SendDataEvent $sbn $sid $fd $match"
		}
	}



	#//////////////////////////////////////////////////////////////////////////////
	# SendPacket ( sbn msg )
	# This function sends the packet given by (msg) into the given (sbn)
	proc SendPacket { sbn msg } {
		#	if { [string length $msg] > 1202 }
		set msg_len [string length $msg]
		catch {::MSN::WriteSBNoNL $sbn "MSG" "D $msg_len\r\n$msg"}
	}

	proc SendPacketExt { sbn sid slpdata {nullsid "0"} {MsgId "0"} {TotalSize "0"} {Offset "0"} {Destination "0"} {AfterAck "0"} {flags "0"}} {
		set offset 0
		SessionList set $sid [list -1 [string length $slpdata] $offset -1 -1 -1 -1 -1 -1 -1]
		set fd [$sbn cget -sock]
		status_log "Got socket $fd\n"
		if { [fileevent $fd writable] == "" } {
			status_log "assining new fileevent proc\n"
			fileevent $fd writable "::MSNP2P::SendPacketExtEvent $sbn $sid [list $slpdata] $nullsid $MsgId $TotalSize $Offset $Destination $AfterAck $flags"
		}
	}

	proc SendPacketExtEvent { sbn sid slpdata {nullsid "0"} {MsgId "0"} {TotalSize "0"} {Offset "0"} {Destination "0"} {AfterAck "0"} {flags "0"} } {
		set fd [$sbn cget -sock]
		fileevent $fd writable ""

		#status_log "got sbn : $sbn, $fd\nsid : $sid\nslpdata: $slpdata \n$nullsid $MsgId $TotalSize $Offset $Destination $AfterAck $flags\n\n"  red

		set offset [lindex [SessionList get $sid] 2]

		if { $offset == "" } {
			return
		}

		if { [expr {$offset + 1202}] < [string length $slpdata] } {
			set msg [MakePacket $sid [string range $slpdata $offset [expr {$offset + 1201}]] $nullsid $MsgId $TotalSize $Offset $Destination $AfterAck $flags]
			set msg_len [string length $msg]
			::MSN::WriteSBNoNL $sbn "MSG" "D $msg_len\r\n$msg"
			set offset [expr {$offset + 1202}]
			SessionList set $sid [list -1 -1 $offset -1 -1 -1 -1 -1 -1 -1]
			after 200 [list fileevent $fd writable "::MSNP2P::SendPacketExtEvent $sbn $sid [list $slpdata] $nullsid $MsgId $TotalSize $Offset $Destination $AfterAck $flags" ]
		} else {

			set msg [MakePacket $sid [string range $slpdata $offset [expr {$offset + 1201}]] $nullsid $MsgId $TotalSize $Offset $Destination $AfterAck $flags]
			set msg_len [string length $msg]
			::MSN::WriteSBNoNL $sbn "MSG" "D $msg_len\r\n$msg"
			set offset [expr {$offset + 1202}]
			SessionList set $sid [list -1 -1 0 -1 -1 -1 -1 -1 -1 -1 ]
		}
	}

}




See more files for this project here

aMSN

A very nice MSN compatible messenger application, aMSN Messenger is a multiplatform MSN messenger clone. Works pretty much like its Windows based counterpart. Perfect for keeping in touch with those friends who have not yet seen the light. Works on linux

Project homepage: http://sourceforge.net/projects/amsn
Programming language(s): C,C++,PHP,Tcl,XML
License: other

  autopackage/
    @tcl.sourceforge.net/
      tcl/
        skeleton.1
      tk/
        skeleton.1
    default.apspec
  debian/
    changelog.in
    compat
    control
    copyright
    dirs
    package.postinst
    package.postrm
    rules
  desktop-icons/
    128x128/
    16x16/
    22x22/
    32x32/
    48x48/
    64x64/
    72x72/
    96x96/
  docs/
    DOCS-HOWTO
    FAQbs
    FAQca
    FAQca_VC
    FAQde
    FAQdu
    FAQee
    FAQel
    FAQes
    FAQfr
    FAQfr.html
    FAQfr_CA
    FAQfur
    FAQhu
    FAQit
    FAQmn
    FAQnl
    FAQno
    FAQpt
    FAQpt_BR
    FAQsl
    FAQsv
    FAQtr
    HELPca
    HELPca_VC
    HELPcs
    HELPda
    HELPde
    HELPee
    HELPel
    HELPes
    HELPfr
    HELPfr_CA
    HELPfur
    HELPhu
    HELPit
    HELPmn
    HELPnl
    HELPno
    HELPpt
    HELPpt_BR
    HELPro
    HELPru
    HELPtr
    HELPzh_TW
    READMEca
    READMEca_VC
    READMEcs
    READMEda
    READMEde
    READMEes
    READMEfr
    READMEfr_CA
    READMEfur
    READMEhu
    READMEit
    READMEmn
    READMEnl
    READMEpt
    READMEpt_BR
    READMEro
    READMEru
    READMEsv
    READMEtr
    READMEzh_TW
  lang/
    LANG-HOWTO
    addkey.tcl
    complete.pl
    convert.tcl
    genlangfiles.c
    genpage.c
    lang1.tmpl
    lang2.tmpl
    lang3.tmpl
    langal
    langast
    langca
    langca_VC
    langchk.sh
    langcs
    langcy
    langda
    langde
    langee
    langel
    langen
    langes
    langeu
    langfi
    langfr
    langfr_CA
    langfri
    langglg
    langgr2
    langhu
    langid
    langis
    langit
    langko
    langlt
    langmk
    langnl
    langno
    langoc
    langpl
    langpt
    langpt_BR
    langro
    langru
    langsk
    langsl
    langsr
    langsv
    langtr
    langzh-CN
    langzh-TW
    missing.py
    sortlang
  plugins/
    Nudge/
    PowerTool/
    WebcamShooter/
    inkdraw/
    remind/
    winks/
  skins/
    default/
  utils/
    TkCximage/
    base64/
    bwidget1.8.0/
    combobox/
    contentmanager/
    dpbrowser/
    drawboard/
    framec/
    http2.4/
    linux/
    log/
    macosx/
    pixmapbutton/
    pixmapmenu/
    pixmapoption/
    pixmapprogbar/
    pixmapscroll/
    scalable-bg/
    sexytile/
    sha1/
  AGREEMENT
  AppMain.tcl
  BWidget_mods.tcl
  CREDITS
  Compile.mk
  FAQ
  GNUGPL
  HELP
  INSTALL
  Makefile.in
  README
  README.macosx
  TODO
  abook.tcl
  alarm.tcl
  amsn
  amsn-remote
  amsn-remote-CLI
  amsn.debianmenu
  amsn.desktop
  amsn.spec
  amsn_des.tcl
  amsncore.tcl
  assistant.tcl
  audio.tcl
  automsg.tcl
  autoupdate.tcl
  balloon.tcl
  bugs.tcl
  chatwindow.tcl
  clgui.tcl
  config.tcl
  configure
  configure.ac
  console.tcl
  contactlist.tcl
  ctthemes.tcl
  debug.tcl
  dock.tcl
  groups.tcl
  gui.tcl
  guicontactlist.tcl
  hotmail.tcl
  hotmlog.htm
  lang.tcl
  langlist
  login_screen.tcl
  loging.tcl
  migmd5.tcl
  msncam.tcl
  msnp2p.tcl
  mutex.tcl
  notes.tcl
  picture.tcl
  plugins.tcl
  pluginslog.tcl
  preferences.tcl
  progressbar.tcl
  protocol.tcl
  proxy.tcl
  remote.help
  remote.tcl
  searchdialog.tcl
  skins.tcl
  smileys.tcl
  soap.tcl
  socks.tcl
  spaces.tcl
  sxml.tcl
  trayicon.tcl