changeset 25718:4d6d7aeb3150

propagate from branch 'im.pidgin.pidgin' (head 3ffe6fd197f037185a0012875ef68c8f6d6c2f89) to branch 'im.pidgin.cpw.malu.xmpp.ibb_ft' (head f595301f7175800c4e04b36cace91a9061db5724)
author Marcus Lundblad <ml@update.uu.se>
date Tue, 16 Sep 2008 20:05:18 +0000
parents 5395b18f9f08 (current diff) 447f3c24cbc9 (diff)
children dc01f9b0aaa3
files pidgin/artwork/art-tools/clean-svg-definitions.sh pidgin/artwork/art-tools/render-pidgin-emotes.rb pidgin/artwork/hicolor/16x16/actions/pidgin-change-bgcolor.png pidgin/artwork/hicolor/16x16/actions/pidgin-change-fgcolor.png pidgin/artwork/hicolor/16x16/actions/pidgin-drag-down.png pidgin/artwork/hicolor/16x16/actions/pidgin-drag-left.png pidgin/artwork/hicolor/16x16/actions/pidgin-drag-right.png pidgin/artwork/hicolor/16x16/actions/pidgin-drag-up.png pidgin/artwork/hicolor/16x16/actions/pidgin-emote-select.png pidgin/artwork/hicolor/16x16/actions/pidgin-font-face.png pidgin/artwork/hicolor/16x16/actions/pidgin-font-size-down.png pidgin/artwork/hicolor/16x16/actions/pidgin-font-size-up.png pidgin/artwork/hicolor/16x16/actions/pidgin-get-attention.png pidgin/artwork/hicolor/16x16/actions/pidgin-insert-image.png pidgin/artwork/hicolor/16x16/actions/pidgin-insert-link.png pidgin/artwork/hicolor/16x16/actions/pidgin-insert.png pidgin/artwork/hicolor/16x16/actions/pidgin-message-new.png pidgin/artwork/hicolor/16x16/actions/pidgin-send-file.png pidgin/artwork/hicolor/16x16/actions/pidgin-unblock.png pidgin/artwork/hicolor/16x16/actions/pidgin-view-plugins.png pidgin/artwork/hicolor/16x16/actions/pidgin-view-transfers.png pidgin/artwork/hicolor/16x16/animations/process-working0.png pidgin/artwork/hicolor/16x16/animations/process-working1.png pidgin/artwork/hicolor/16x16/animations/process-working10.png pidgin/artwork/hicolor/16x16/animations/process-working11.png pidgin/artwork/hicolor/16x16/animations/process-working12.png pidgin/artwork/hicolor/16x16/animations/process-working13.png pidgin/artwork/hicolor/16x16/animations/process-working14.png pidgin/artwork/hicolor/16x16/animations/process-working15.png pidgin/artwork/hicolor/16x16/animations/process-working16.png pidgin/artwork/hicolor/16x16/animations/process-working17.png pidgin/artwork/hicolor/16x16/animations/process-working18.png pidgin/artwork/hicolor/16x16/animations/process-working19.png pidgin/artwork/hicolor/16x16/animations/process-working2.png pidgin/artwork/hicolor/16x16/animations/process-working20.png pidgin/artwork/hicolor/16x16/animations/process-working21.png pidgin/artwork/hicolor/16x16/animations/process-working22.png pidgin/artwork/hicolor/16x16/animations/process-working23.png pidgin/artwork/hicolor/16x16/animations/process-working24.png pidgin/artwork/hicolor/16x16/animations/process-working25.png pidgin/artwork/hicolor/16x16/animations/process-working26.png pidgin/artwork/hicolor/16x16/animations/process-working27.png pidgin/artwork/hicolor/16x16/animations/process-working28.png pidgin/artwork/hicolor/16x16/animations/process-working29.png pidgin/artwork/hicolor/16x16/animations/process-working3.png pidgin/artwork/hicolor/16x16/animations/process-working30.png pidgin/artwork/hicolor/16x16/animations/process-working4.png pidgin/artwork/hicolor/16x16/animations/process-working5.png pidgin/artwork/hicolor/16x16/animations/process-working6.png pidgin/artwork/hicolor/16x16/animations/process-working7.png pidgin/artwork/hicolor/16x16/animations/process-working8.png pidgin/artwork/hicolor/16x16/animations/process-working9.png pidgin/artwork/hicolor/16x16/animations/typing0.png pidgin/artwork/hicolor/16x16/animations/typing1.png pidgin/artwork/hicolor/16x16/animations/typing2.png pidgin/artwork/hicolor/16x16/animations/typing3.png pidgin/artwork/hicolor/16x16/animations/typing4.png pidgin/artwork/hicolor/16x16/animations/typing5.png pidgin/artwork/hicolor/16x16/apps/pidgin-aim.png pidgin/artwork/hicolor/16x16/apps/pidgin-bonjour.png pidgin/artwork/hicolor/16x16/apps/pidgin-facebook.png pidgin/artwork/hicolor/16x16/apps/pidgin-gadu-gadu.png pidgin/artwork/hicolor/16x16/apps/pidgin-google-talk.png pidgin/artwork/hicolor/16x16/apps/pidgin-icq.png pidgin/artwork/hicolor/16x16/apps/pidgin-irc.png pidgin/artwork/hicolor/16x16/apps/pidgin-jabber.png pidgin/artwork/hicolor/16x16/apps/pidgin-meanwhile.png pidgin/artwork/hicolor/16x16/apps/pidgin-msn.png pidgin/artwork/hicolor/16x16/apps/pidgin-myspace.png pidgin/artwork/hicolor/16x16/apps/pidgin-novell.png pidgin/artwork/hicolor/16x16/apps/pidgin-qq.png pidgin/artwork/hicolor/16x16/apps/pidgin-silc.png pidgin/artwork/hicolor/16x16/apps/pidgin-simple.png pidgin/artwork/hicolor/16x16/apps/pidgin-yahoo.png pidgin/artwork/hicolor/16x16/apps/pidgin-zephyr.png pidgin/artwork/hicolor/16x16/apps/pidgin.png pidgin/artwork/hicolor/16x16/devices/pidgin-hiptop.png pidgin/artwork/hicolor/16x16/devices/pidgin-mobile.png pidgin/artwork/hicolor/16x16/emblems/pidgin-aol-client.png pidgin/artwork/hicolor/16x16/emblems/pidgin-birthday.png pidgin/artwork/hicolor/16x16/emblems/pidgin-blocked.png pidgin/artwork/hicolor/16x16/emblems/pidgin-bot.png pidgin/artwork/hicolor/16x16/emblems/pidgin-external.png pidgin/artwork/hicolor/16x16/emblems/pidgin-female.png pidgin/artwork/hicolor/16x16/emblems/pidgin-founder.png pidgin/artwork/hicolor/16x16/emblems/pidgin-free-for-chat.png pidgin/artwork/hicolor/16x16/emblems/pidgin-game.png pidgin/artwork/hicolor/16x16/emblems/pidgin-half-operator.png pidgin/artwork/hicolor/16x16/emblems/pidgin-male.png pidgin/artwork/hicolor/16x16/emblems/pidgin-music.png pidgin/artwork/hicolor/16x16/emblems/pidgin-not-authorized.png pidgin/artwork/hicolor/16x16/emblems/pidgin-operator.png pidgin/artwork/hicolor/16x16/emblems/pidgin-qq-member.png pidgin/artwork/hicolor/16x16/emblems/pidgin-secure.png pidgin/artwork/hicolor/16x16/emblems/pidgin-unavailable.png pidgin/artwork/hicolor/16x16/emblems/pidgin-video.png pidgin/artwork/hicolor/16x16/emblems/pidgin-voice.png pidgin/artwork/hicolor/16x16/status/pidgin-available.png pidgin/artwork/hicolor/16x16/status/pidgin-away.png pidgin/artwork/hicolor/16x16/status/pidgin-busy.png pidgin/artwork/hicolor/16x16/status/pidgin-chatroom.png pidgin/artwork/hicolor/16x16/status/pidgin-contact.png pidgin/artwork/hicolor/16x16/status/pidgin-dialog-auth.png pidgin/artwork/hicolor/16x16/status/pidgin-dialog-error.png pidgin/artwork/hicolor/16x16/status/pidgin-dialog-info.png pidgin/artwork/hicolor/16x16/status/pidgin-dialog-mail.png pidgin/artwork/hicolor/16x16/status/pidgin-dialog-question.png pidgin/artwork/hicolor/16x16/status/pidgin-extended-away.png pidgin/artwork/hicolor/16x16/status/pidgin-invisible.png pidgin/artwork/hicolor/16x16/status/pidgin-offline.png pidgin/artwork/hicolor/16x16/status/pidgin-tray-available.png pidgin/artwork/hicolor/16x16/status/pidgin-tray-away.png pidgin/artwork/hicolor/16x16/status/pidgin-tray-busy.png pidgin/artwork/hicolor/16x16/status/pidgin-tray-extended-away.png pidgin/artwork/hicolor/16x16/status/pidgin-tray-invisible.png pidgin/artwork/hicolor/16x16/status/pidgin-tray-message-pending.png pidgin/artwork/hicolor/16x16/status/pidgin-tray-new-im.png pidgin/artwork/hicolor/16x16/status/pidgin-tray-offline.png pidgin/artwork/hicolor/22x22/apps/pidgin.png pidgin/artwork/hicolor/22x22/status/pidgin-available.png pidgin/artwork/hicolor/22x22/status/pidgin-away.png pidgin/artwork/hicolor/22x22/status/pidgin-busy.png pidgin/artwork/hicolor/22x22/status/pidgin-chatroom.png pidgin/artwork/hicolor/22x22/status/pidgin-contact.png pidgin/artwork/hicolor/22x22/status/pidgin-extended-away.png pidgin/artwork/hicolor/22x22/status/pidgin-invisible.png pidgin/artwork/hicolor/22x22/status/pidgin-offline.png pidgin/artwork/hicolor/22x22/status/pidgin-tray-available.png pidgin/artwork/hicolor/22x22/status/pidgin-tray-away.png pidgin/artwork/hicolor/22x22/status/pidgin-tray-busy.png pidgin/artwork/hicolor/22x22/status/pidgin-tray-extended-away.png pidgin/artwork/hicolor/22x22/status/pidgin-tray-invisible.png pidgin/artwork/hicolor/22x22/status/pidgin-tray-message-pending.png pidgin/artwork/hicolor/22x22/status/pidgin-tray-new-im.png pidgin/artwork/hicolor/22x22/status/pidgin-tray-offline.png pidgin/artwork/hicolor/24x24/apps/pidgin-aim.png pidgin/artwork/hicolor/24x24/apps/pidgin-bonjour.png pidgin/artwork/hicolor/24x24/apps/pidgin-facebook.png pidgin/artwork/hicolor/24x24/apps/pidgin-gadu-gadu.png pidgin/artwork/hicolor/24x24/apps/pidgin-google-talk.png pidgin/artwork/hicolor/24x24/apps/pidgin-icq.png pidgin/artwork/hicolor/24x24/apps/pidgin-irc.png pidgin/artwork/hicolor/24x24/apps/pidgin-jabber.png pidgin/artwork/hicolor/24x24/apps/pidgin-meanwhile.png pidgin/artwork/hicolor/24x24/apps/pidgin-msn.png pidgin/artwork/hicolor/24x24/apps/pidgin-myspace.png pidgin/artwork/hicolor/24x24/apps/pidgin-novell.png pidgin/artwork/hicolor/24x24/apps/pidgin-qq.png pidgin/artwork/hicolor/24x24/apps/pidgin-silc.png pidgin/artwork/hicolor/24x24/apps/pidgin-simple.png pidgin/artwork/hicolor/24x24/apps/pidgin-yahoo.png pidgin/artwork/hicolor/24x24/apps/pidgin-zephyr.png pidgin/artwork/hicolor/24x24/apps/pidgin.png pidgin/artwork/hicolor/24x24/emotes/Makefile.am pidgin/artwork/hicolor/24x24/emotes/Makefile.mingw pidgin/artwork/hicolor/24x24/emotes/default.theme.in pidgin/artwork/hicolor/24x24/emotes/none/Makefile.am pidgin/artwork/hicolor/24x24/emotes/none/Makefile.mingw pidgin/artwork/hicolor/24x24/emotes/none/none.theme.in pidgin/artwork/hicolor/24x24/emotes/none/theme pidgin/artwork/hicolor/24x24/emotes/pidgin-act-up.png pidgin/artwork/hicolor/24x24/emotes/pidgin-airplane.png pidgin/artwork/hicolor/24x24/emotes/pidgin-alien.png pidgin/artwork/hicolor/24x24/emotes/pidgin-angel.png pidgin/artwork/hicolor/24x24/emotes/pidgin-angry.png pidgin/artwork/hicolor/24x24/emotes/pidgin-arrogant.png pidgin/artwork/hicolor/24x24/emotes/pidgin-at-wits-end.png pidgin/artwork/hicolor/24x24/emotes/pidgin-bad.png pidgin/artwork/hicolor/24x24/emotes/pidgin-bashful.png pidgin/artwork/hicolor/24x24/emotes/pidgin-beat-up.png pidgin/artwork/hicolor/24x24/emotes/pidgin-beauty.png pidgin/artwork/hicolor/24x24/emotes/pidgin-beer.png pidgin/artwork/hicolor/24x24/emotes/pidgin-blowkiss.png pidgin/artwork/hicolor/24x24/emotes/pidgin-bomb.png pidgin/artwork/hicolor/24x24/emotes/pidgin-bowl.png pidgin/artwork/hicolor/24x24/emotes/pidgin-boy.png pidgin/artwork/hicolor/24x24/emotes/pidgin-brb.png pidgin/artwork/hicolor/24x24/emotes/pidgin-bulgy-eyes.png pidgin/artwork/hicolor/24x24/emotes/pidgin-bunny.png pidgin/artwork/hicolor/24x24/emotes/pidgin-bye.png pidgin/artwork/hicolor/24x24/emotes/pidgin-cake.png pidgin/artwork/hicolor/24x24/emotes/pidgin-call-me.png pidgin/artwork/hicolor/24x24/emotes/pidgin-camera.png pidgin/artwork/hicolor/24x24/emotes/pidgin-can.png pidgin/artwork/hicolor/24x24/emotes/pidgin-car.png pidgin/artwork/hicolor/24x24/emotes/pidgin-cat.png pidgin/artwork/hicolor/24x24/emotes/pidgin-chicken.png pidgin/artwork/hicolor/24x24/emotes/pidgin-cigarette.png pidgin/artwork/hicolor/24x24/emotes/pidgin-clap.png pidgin/artwork/hicolor/24x24/emotes/pidgin-clock.png pidgin/artwork/hicolor/24x24/emotes/pidgin-cloudy.png pidgin/artwork/hicolor/24x24/emotes/pidgin-clover.png pidgin/artwork/hicolor/24x24/emotes/pidgin-clown.png pidgin/artwork/hicolor/24x24/emotes/pidgin-coffee.png pidgin/artwork/hicolor/24x24/emotes/pidgin-coins.png pidgin/artwork/hicolor/24x24/emotes/pidgin-computer.png pidgin/artwork/hicolor/24x24/emotes/pidgin-confused.png pidgin/artwork/hicolor/24x24/emotes/pidgin-console.png pidgin/artwork/hicolor/24x24/emotes/pidgin-cow.png pidgin/artwork/hicolor/24x24/emotes/pidgin-cowboy.png pidgin/artwork/hicolor/24x24/emotes/pidgin-crying.png pidgin/artwork/hicolor/24x24/emotes/pidgin-curl-lip.png pidgin/artwork/hicolor/24x24/emotes/pidgin-curse.png pidgin/artwork/hicolor/24x24/emotes/pidgin-cute.png pidgin/artwork/hicolor/24x24/emotes/pidgin-cyclops.png pidgin/artwork/hicolor/24x24/emotes/pidgin-dance.png pidgin/artwork/hicolor/24x24/emotes/pidgin-dazed.png pidgin/artwork/hicolor/24x24/emotes/pidgin-desire.png pidgin/artwork/hicolor/24x24/emotes/pidgin-devil.png pidgin/artwork/hicolor/24x24/emotes/pidgin-disappointed.png pidgin/artwork/hicolor/24x24/emotes/pidgin-disdain.png pidgin/artwork/hicolor/24x24/emotes/pidgin-doctor.png pidgin/artwork/hicolor/24x24/emotes/pidgin-dog.png pidgin/artwork/hicolor/24x24/emotes/pidgin-doh.png pidgin/artwork/hicolor/24x24/emotes/pidgin-dont-know.png pidgin/artwork/hicolor/24x24/emotes/pidgin-drink.png pidgin/artwork/hicolor/24x24/emotes/pidgin-drool.png pidgin/artwork/hicolor/24x24/emotes/pidgin-eat.png pidgin/artwork/hicolor/24x24/emotes/pidgin-embarrassed.png pidgin/artwork/hicolor/24x24/emotes/pidgin-excruciating.png pidgin/artwork/hicolor/24x24/emotes/pidgin-eyeroll.png pidgin/artwork/hicolor/24x24/emotes/pidgin-female-fighter.png pidgin/artwork/hicolor/24x24/emotes/pidgin-film.png pidgin/artwork/hicolor/24x24/emotes/pidgin-fingers-crossed.png pidgin/artwork/hicolor/24x24/emotes/pidgin-flag.png pidgin/artwork/hicolor/24x24/emotes/pidgin-foot-in-mouth.png pidgin/artwork/hicolor/24x24/emotes/pidgin-freaked-out.png pidgin/artwork/hicolor/24x24/emotes/pidgin-ghost.png pidgin/artwork/hicolor/24x24/emotes/pidgin-giggle.png pidgin/artwork/hicolor/24x24/emotes/pidgin-girl.png pidgin/artwork/hicolor/24x24/emotes/pidgin-glasses-cool.png pidgin/artwork/hicolor/24x24/emotes/pidgin-glasses-nerdy.png pidgin/artwork/hicolor/24x24/emotes/pidgin-go-away.png pidgin/artwork/hicolor/24x24/emotes/pidgin-goat.png pidgin/artwork/hicolor/24x24/emotes/pidgin-good.png pidgin/artwork/hicolor/24x24/emotes/pidgin-hammer.png pidgin/artwork/hicolor/24x24/emotes/pidgin-handcuffs.png pidgin/artwork/hicolor/24x24/emotes/pidgin-handshake.png pidgin/artwork/hicolor/24x24/emotes/pidgin-highfive.png pidgin/artwork/hicolor/24x24/emotes/pidgin-hug-left.png pidgin/artwork/hicolor/24x24/emotes/pidgin-hug-right.png pidgin/artwork/hicolor/24x24/emotes/pidgin-hypnotized.png pidgin/artwork/hicolor/24x24/emotes/pidgin-in-love.png pidgin/artwork/hicolor/24x24/emotes/pidgin-island.png pidgin/artwork/hicolor/24x24/emotes/pidgin-jump.png pidgin/artwork/hicolor/24x24/emotes/pidgin-kiss.png pidgin/artwork/hicolor/24x24/emotes/pidgin-kissed.png pidgin/artwork/hicolor/24x24/emotes/pidgin-kissing.png pidgin/artwork/hicolor/24x24/emotes/pidgin-knife.png pidgin/artwork/hicolor/24x24/emotes/pidgin-lamp.png pidgin/artwork/hicolor/24x24/emotes/pidgin-lashes.png pidgin/artwork/hicolor/24x24/emotes/pidgin-laugh.png pidgin/artwork/hicolor/24x24/emotes/pidgin-liquor.png pidgin/artwork/hicolor/24x24/emotes/pidgin-loser.png pidgin/artwork/hicolor/24x24/emotes/pidgin-love-over.png pidgin/artwork/hicolor/24x24/emotes/pidgin-love.png pidgin/artwork/hicolor/24x24/emotes/pidgin-lying.png pidgin/artwork/hicolor/24x24/emotes/pidgin-mad-tongue.png pidgin/artwork/hicolor/24x24/emotes/pidgin-mail.png pidgin/artwork/hicolor/24x24/emotes/pidgin-male-fighter1.png pidgin/artwork/hicolor/24x24/emotes/pidgin-male-fighter2.png pidgin/artwork/hicolor/24x24/emotes/pidgin-mean.png pidgin/artwork/hicolor/24x24/emotes/pidgin-meeting.png pidgin/artwork/hicolor/24x24/emotes/pidgin-messed.png pidgin/artwork/hicolor/24x24/emotes/pidgin-mobile.png pidgin/artwork/hicolor/24x24/emotes/pidgin-mohawk.png pidgin/artwork/hicolor/24x24/emotes/pidgin-moneymouth.png pidgin/artwork/hicolor/24x24/emotes/pidgin-monkey.png pidgin/artwork/hicolor/24x24/emotes/pidgin-moon.png pidgin/artwork/hicolor/24x24/emotes/pidgin-msn-away.png pidgin/artwork/hicolor/24x24/emotes/pidgin-msn-busy.png pidgin/artwork/hicolor/24x24/emotes/pidgin-msn.png pidgin/artwork/hicolor/24x24/emotes/pidgin-msn_online.png pidgin/artwork/hicolor/24x24/emotes/pidgin-music.png pidgin/artwork/hicolor/24x24/emotes/pidgin-musical-note.png pidgin/artwork/hicolor/24x24/emotes/pidgin-nailbiting.png pidgin/artwork/hicolor/24x24/emotes/pidgin-neutral.png pidgin/artwork/hicolor/24x24/emotes/pidgin-on-the-phone.png pidgin/artwork/hicolor/24x24/emotes/pidgin-party.png pidgin/artwork/hicolor/24x24/emotes/pidgin-peace.png pidgin/artwork/hicolor/24x24/emotes/pidgin-phone.png pidgin/artwork/hicolor/24x24/emotes/pidgin-pig.png pidgin/artwork/hicolor/24x24/emotes/pidgin-pill.png pidgin/artwork/hicolor/24x24/emotes/pidgin-pirate.png pidgin/artwork/hicolor/24x24/emotes/pidgin-pissed-off.png pidgin/artwork/hicolor/24x24/emotes/pidgin-pizza.png pidgin/artwork/hicolor/24x24/emotes/pidgin-plate.png pidgin/artwork/hicolor/24x24/emotes/pidgin-poop.png pidgin/artwork/hicolor/24x24/emotes/pidgin-pray.png pidgin/artwork/hicolor/24x24/emotes/pidgin-present.png pidgin/artwork/hicolor/24x24/emotes/pidgin-pumpkin.png pidgin/artwork/hicolor/24x24/emotes/pidgin-qq.png pidgin/artwork/hicolor/24x24/emotes/pidgin-question.png pidgin/artwork/hicolor/24x24/emotes/pidgin-quiet.png pidgin/artwork/hicolor/24x24/emotes/pidgin-rain.png pidgin/artwork/hicolor/24x24/emotes/pidgin-rainbow.png pidgin/artwork/hicolor/24x24/emotes/pidgin-rose-dead.png pidgin/artwork/hicolor/24x24/emotes/pidgin-rose.png pidgin/artwork/hicolor/24x24/emotes/pidgin-rotfl.png pidgin/artwork/hicolor/24x24/emotes/pidgin-sad.png pidgin/artwork/hicolor/24x24/emotes/pidgin-sarcastic.png pidgin/artwork/hicolor/24x24/emotes/pidgin-search.png pidgin/artwork/hicolor/24x24/emotes/pidgin-secret.png pidgin/artwork/hicolor/24x24/emotes/pidgin-shame.png pidgin/artwork/hicolor/24x24/emotes/pidgin-sheep.png pidgin/artwork/hicolor/24x24/emotes/pidgin-shock.png pidgin/artwork/hicolor/24x24/emotes/pidgin-shout.png pidgin/artwork/hicolor/24x24/emotes/pidgin-shut-mouth.png pidgin/artwork/hicolor/24x24/emotes/pidgin-sick.png pidgin/artwork/hicolor/24x24/emotes/pidgin-sidefrown.png pidgin/artwork/hicolor/24x24/emotes/pidgin-silly.png pidgin/artwork/hicolor/24x24/emotes/pidgin-sinister.png pidgin/artwork/hicolor/24x24/emotes/pidgin-skeleton.png pidgin/artwork/hicolor/24x24/emotes/pidgin-skywalker.png pidgin/artwork/hicolor/24x24/emotes/pidgin-sleepy.png pidgin/artwork/hicolor/24x24/emotes/pidgin-smile-big.png pidgin/artwork/hicolor/24x24/emotes/pidgin-smile.png pidgin/artwork/hicolor/24x24/emotes/pidgin-smirk.png pidgin/artwork/hicolor/24x24/emotes/pidgin-snail.png pidgin/artwork/hicolor/24x24/emotes/pidgin-snicker.png pidgin/artwork/hicolor/24x24/emotes/pidgin-snowman.png pidgin/artwork/hicolor/24x24/emotes/pidgin-soccerball.png pidgin/artwork/hicolor/24x24/emotes/pidgin-soldier.png pidgin/artwork/hicolor/24x24/emotes/pidgin-star.png pidgin/artwork/hicolor/24x24/emotes/pidgin-starving.png pidgin/artwork/hicolor/24x24/emotes/pidgin-stop.png pidgin/artwork/hicolor/24x24/emotes/pidgin-struggle.png pidgin/artwork/hicolor/24x24/emotes/pidgin-sun.png pidgin/artwork/hicolor/24x24/emotes/pidgin-sweat.png pidgin/artwork/hicolor/24x24/emotes/pidgin-talktohand.png pidgin/artwork/hicolor/24x24/emotes/pidgin-teeth.png pidgin/artwork/hicolor/24x24/emotes/pidgin-terror.png pidgin/artwork/hicolor/24x24/emotes/pidgin-thinking.png pidgin/artwork/hicolor/24x24/emotes/pidgin-thunder.png pidgin/artwork/hicolor/24x24/emotes/pidgin-time-out.png pidgin/artwork/hicolor/24x24/emotes/pidgin-tongue.png pidgin/artwork/hicolor/24x24/emotes/pidgin-tremble.png pidgin/artwork/hicolor/24x24/emotes/pidgin-turtle.png pidgin/artwork/hicolor/24x24/emotes/pidgin-tv.png pidgin/artwork/hicolor/24x24/emotes/pidgin-umbrella.png pidgin/artwork/hicolor/24x24/emotes/pidgin-vampire.png pidgin/artwork/hicolor/24x24/emotes/pidgin-victory.png pidgin/artwork/hicolor/24x24/emotes/pidgin-waiting.png pidgin/artwork/hicolor/24x24/emotes/pidgin-watermelon.png pidgin/artwork/hicolor/24x24/emotes/pidgin-waving.png pidgin/artwork/hicolor/24x24/emotes/pidgin-weep.png pidgin/artwork/hicolor/24x24/emotes/pidgin-wilt.png pidgin/artwork/hicolor/24x24/emotes/pidgin-wink.png pidgin/artwork/hicolor/24x24/emotes/pidgin-worship.png pidgin/artwork/hicolor/24x24/emotes/pidgin-yawn.png pidgin/artwork/hicolor/24x24/emotes/pidgin-yin-yang.png pidgin/artwork/hicolor/24x24/emotes/theme pidgin/artwork/hicolor/24x24/status/pidgin-available.png pidgin/artwork/hicolor/24x24/status/pidgin-away.png pidgin/artwork/hicolor/24x24/status/pidgin-busy.png pidgin/artwork/hicolor/24x24/status/pidgin-chatroom.png pidgin/artwork/hicolor/24x24/status/pidgin-contact.png pidgin/artwork/hicolor/24x24/status/pidgin-extended-away.png pidgin/artwork/hicolor/24x24/status/pidgin-invisible.png pidgin/artwork/hicolor/24x24/status/pidgin-offline.png pidgin/artwork/hicolor/24x24/status/pidgin-tray-available.png pidgin/artwork/hicolor/24x24/status/pidgin-tray-away.png pidgin/artwork/hicolor/24x24/status/pidgin-tray-busy.png pidgin/artwork/hicolor/24x24/status/pidgin-tray-extended-away.png pidgin/artwork/hicolor/24x24/status/pidgin-tray-invisible.png pidgin/artwork/hicolor/24x24/status/pidgin-tray-message-pending.png pidgin/artwork/hicolor/24x24/status/pidgin-tray-new-im.png pidgin/artwork/hicolor/24x24/status/pidgin-tray-offline.png pidgin/artwork/hicolor/32x32/actions/pidgin-select-avatar.png pidgin/artwork/hicolor/32x32/apps/pidgin.png pidgin/artwork/hicolor/32x32/status/pidgin-available.png pidgin/artwork/hicolor/32x32/status/pidgin-away.png pidgin/artwork/hicolor/32x32/status/pidgin-busy.png pidgin/artwork/hicolor/32x32/status/pidgin-extended-away.png pidgin/artwork/hicolor/32x32/status/pidgin-invisible.png pidgin/artwork/hicolor/32x32/status/pidgin-offline.png pidgin/artwork/hicolor/32x32/status/pidgin-tray-available.png pidgin/artwork/hicolor/32x32/status/pidgin-tray-away.png pidgin/artwork/hicolor/32x32/status/pidgin-tray-busy.png pidgin/artwork/hicolor/32x32/status/pidgin-tray-extended-away.png pidgin/artwork/hicolor/32x32/status/pidgin-tray-invisible.png pidgin/artwork/hicolor/32x32/status/pidgin-tray-message-pending.png pidgin/artwork/hicolor/32x32/status/pidgin-tray-new-im.png pidgin/artwork/hicolor/32x32/status/pidgin-tray-offline.png pidgin/artwork/hicolor/48x48/apps/pidgin-aim.png pidgin/artwork/hicolor/48x48/apps/pidgin-bonjour.png pidgin/artwork/hicolor/48x48/apps/pidgin-facebook.png pidgin/artwork/hicolor/48x48/apps/pidgin-gadu-gadu.png pidgin/artwork/hicolor/48x48/apps/pidgin-icq.png pidgin/artwork/hicolor/48x48/apps/pidgin-irc.png pidgin/artwork/hicolor/48x48/apps/pidgin-jabber.png pidgin/artwork/hicolor/48x48/apps/pidgin-meanwhile.png pidgin/artwork/hicolor/48x48/apps/pidgin-msn.png pidgin/artwork/hicolor/48x48/apps/pidgin-myspace.png pidgin/artwork/hicolor/48x48/apps/pidgin-novell.png pidgin/artwork/hicolor/48x48/apps/pidgin-qq.png pidgin/artwork/hicolor/48x48/apps/pidgin-silc.png pidgin/artwork/hicolor/48x48/apps/pidgin-simple.png pidgin/artwork/hicolor/48x48/apps/pidgin-yahoo.png pidgin/artwork/hicolor/48x48/apps/pidgin-zephyr.png pidgin/artwork/hicolor/48x48/apps/pidgin.png pidgin/artwork/hicolor/48x48/status/pidgin-available.png pidgin/artwork/hicolor/48x48/status/pidgin-away.png pidgin/artwork/hicolor/48x48/status/pidgin-busy.png pidgin/artwork/hicolor/48x48/status/pidgin-dialog-auth.png pidgin/artwork/hicolor/48x48/status/pidgin-dialog-cool.png pidgin/artwork/hicolor/48x48/status/pidgin-dialog-dialog.png pidgin/artwork/hicolor/48x48/status/pidgin-dialog-error.png pidgin/artwork/hicolor/48x48/status/pidgin-dialog-info.png pidgin/artwork/hicolor/48x48/status/pidgin-dialog-mail.png pidgin/artwork/hicolor/48x48/status/pidgin-extended-away.png pidgin/artwork/hicolor/48x48/status/pidgin-invisible.png pidgin/artwork/hicolor/48x48/status/pidgin-offline.png pidgin/artwork/hicolor/48x48/status/pidgin-tray-available.png pidgin/artwork/hicolor/48x48/status/pidgin-tray-away.png pidgin/artwork/hicolor/48x48/status/pidgin-tray-busy.png pidgin/artwork/hicolor/48x48/status/pidgin-tray-extended-away.png pidgin/artwork/hicolor/48x48/status/pidgin-tray-invisible.png pidgin/artwork/hicolor/48x48/status/pidgin-tray-message-pending.png pidgin/artwork/hicolor/48x48/status/pidgin-tray-new-im.png pidgin/artwork/hicolor/48x48/status/pidgin-tray-offline.png pidgin/artwork/hicolor/48x48/status/question.png pidgin/artwork/hicolor/48x48/status/warning.png pidgin/artwork/hicolor/scalable/apps/pidgin.svg pidgin/artwork/hicolor/scalable/status/pidgin-auth.svg pidgin/artwork/hicolor/scalable/status/pidgin-cool.svg pidgin/artwork/hicolor/scalable/status/pidgin-dialog.svg pidgin/artwork/hicolor/scalable/status/pidgin-error.svg pidgin/artwork/hicolor/scalable/status/pidgin-info.svg pidgin/artwork/hicolor/scalable/status/pidgin-mail.svg pidgin/artwork/hicolor/scalable/status/pidgin-question.svg pidgin/artwork/hicolor/scalable/status/pidgin-warning.svg pidgin/artwork/pixmaps/logo.png pidgin/artwork/pixmaps/pidgin.ico
diffstat 9 files changed, 1002 insertions(+), 31 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/jabber/Makefile.am	Tue Sep 16 17:56:01 2008 +0000
+++ b/libpurple/protocols/jabber/Makefile.am	Tue Sep 16 20:05:18 2008 +0000
@@ -15,6 +15,8 @@
 			  disco.h \
 			  google.c \
 			  google.h \
+			  ibb.c \
+			  ibb.h \
 			  iq.c \
 			  iq.h \
 			  jabber.c \
--- a/libpurple/protocols/jabber/Makefile.mingw	Tue Sep 16 17:56:01 2008 +0000
+++ b/libpurple/protocols/jabber/Makefile.mingw	Tue Sep 16 20:05:18 2008 +0000
@@ -50,6 +50,7 @@
 			chat.c \
 			disco.c \
 			google.c \
+			ibb.c \
 			iq.c \
 			jabber.c \
 			jutil.c \
--- a/libpurple/protocols/jabber/disco.c	Tue Sep 16 17:56:01 2008 +0000
+++ b/libpurple/protocols/jabber/disco.c	Tue Sep 16 20:05:18 2008 +0000
@@ -132,9 +132,7 @@
 			SUPPORT_FEATURE("http://jabber.org/protocol/bytestreams")
 			SUPPORT_FEATURE("http://jabber.org/protocol/disco#info")
 			SUPPORT_FEATURE("http://jabber.org/protocol/disco#items")
-#if 0
-				SUPPORT_FEATURE("http://jabber.org/protocol/ibb")
-#endif
+			SUPPORT_FEATURE("http://jabber.org/protocol/ibb");
 			SUPPORT_FEATURE("http://jabber.org/protocol/muc")
 			SUPPORT_FEATURE("http://jabber.org/protocol/muc#user")
 			SUPPORT_FEATURE("http://jabber.org/protocol/si")
@@ -273,6 +271,10 @@
 				else if(!strcmp(var, "http://jabber.org/protocol/commands")) {
 					capabilities |= JABBER_CAP_ADHOC;
 				}
+				else if(!strcmp(var, "http://jabber.org/protocol/ibb")) {
+					purple_debug_info("jabber", "remote supports IBB\n");
+					capabilities |= JABBER_CAP_IBB;
+				}
 			}
 		}
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/jabber/ibb.c	Tue Sep 16 20:05:18 2008 +0000
@@ -0,0 +1,514 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
+ */
+ 
+#include "ibb.h"
+#include "debug.h"
+#include "xmlnode.h"
+
+#include <glib.h>
+#include <string.h>
+
+#define JABBER_IBB_SESSION_DEFAULT_BLOCK_SIZE 4096
+
+static GHashTable *jabber_ibb_sessions = NULL;
+static GList *open_handlers = NULL;
+
+JabberIBBSession *
+jabber_ibb_session_create(JabberStream *js, const gchar *sid, const gchar *who, 
+	gpointer user_data)
+{
+	JabberIBBSession *sess = g_new0(JabberIBBSession, 1);
+	
+	if (!sess) {
+		purple_debug_error("jabber", "Could not allocate IBB session object\n");
+		return NULL;
+	}
+	
+	sess->js = js;
+	if (sid) {
+		sess->sid = g_strdup(sid);
+	} else {
+		sess->sid = g_strdup(jabber_get_next_id(js));
+	}
+	sess->who = g_strdup(who);
+	sess->block_size = JABBER_IBB_SESSION_DEFAULT_BLOCK_SIZE;
+	sess->state = JABBER_IBB_SESSION_NOT_OPENED;
+	sess->user_data = user_data;
+	
+	g_hash_table_insert(jabber_ibb_sessions, sess->sid, sess);
+	
+	return sess;
+}
+
+JabberIBBSession *
+jabber_ibb_session_create_from_xmlnode(JabberStream *js, xmlnode *packet,
+	gpointer user_data)
+{
+	JabberIBBSession *sess = NULL;
+	xmlnode *open = xmlnode_get_child_with_namespace(packet, "open",
+		XEP_0047_NAMESPACE);
+	const gchar *sid = xmlnode_get_attrib(open, "sid");
+	const gchar *block_size = xmlnode_get_attrib(open, "block-size");
+	gsize block_size_int;
+	
+	if (!open) {
+		return NULL;
+	}
+	
+	sess = g_new0(JabberIBBSession, 1);
+	
+	if (!sess) {
+		purple_debug_error("jabber", "Could not allocate IBB session object\n");
+		return NULL;
+	}
+	
+	if (!sid || !block_size) {
+		purple_debug_error("jabber", 
+			"IBB session open tag requires sid and block-size attributes\n");
+		g_free(sess);
+		return NULL;
+	}
+	
+	block_size_int = atoi(block_size);
+	sess->js = js;
+	sess->sid = g_strdup(sid);
+	sess->id = g_strdup(xmlnode_get_attrib(packet, "id"));
+	sess->who = g_strdup(xmlnode_get_attrib(packet, "from"));
+	sess->block_size = block_size_int;
+	/* if we create a session from an incoming <open/> request, it means the
+	  session is immediatly open... */
+	sess->state = JABBER_IBB_SESSION_OPENED;
+	sess->user_data = user_data;
+	
+	g_hash_table_insert(jabber_ibb_sessions, sess->sid, sess);
+	
+	return sess;
+}
+
+void
+jabber_ibb_session_destroy(JabberIBBSession *sess)
+{
+	purple_debug_info("jabber", "IBB: destroying session %lx %s\n", sess, 
+		sess->sid);
+	
+	if (jabber_ibb_session_get_state(sess) == JABBER_IBB_SESSION_OPENED) {
+		jabber_ibb_session_close(sess);
+	}
+	
+	purple_debug_info("jabber", "IBB: last_iq_id: %lx\n", sess->last_iq_id);
+	if (sess->last_iq_id) {
+		purple_debug_info("jabber", "IBB: removing callback for <iq/> %s\n",
+			sess->last_iq_id);
+		jabber_iq_remove_callback_by_id(jabber_ibb_session_get_js(sess), 
+			sess->last_iq_id);
+		g_free(sess->last_iq_id);
+		sess->last_iq_id = NULL;
+	}
+	
+	g_hash_table_remove(jabber_ibb_sessions, sess->sid);
+	g_free(sess->sid);
+	g_free(sess->who);
+	g_free(sess);
+}
+
+const gchar *
+jabber_ibb_session_get_sid(const JabberIBBSession *sess)
+{
+	return sess->sid;
+}
+
+JabberStream *
+jabber_ibb_session_get_js(JabberIBBSession *sess)
+{
+	return sess->js;
+}
+
+const gchar *
+jabber_ibb_session_get_who(const JabberIBBSession *sess)
+{
+	return sess->who;
+}
+
+guint16
+jabber_ibb_session_get_send_seq(const JabberIBBSession *sess)
+{
+	return sess->send_seq;
+}
+
+guint16
+jabber_ibb_session_get_recv_seq(const JabberIBBSession *sess)
+{
+	return sess->recv_seq;
+}
+
+JabberIBBSessionState 
+jabber_ibb_session_get_state(const JabberIBBSession *sess)
+{
+	return sess->state;
+}
+
+gsize
+jabber_ibb_session_get_block_size(const JabberIBBSession *sess)
+{
+	return sess->block_size;
+}
+
+void
+jabber_ibb_session_set_block_size(JabberIBBSession *sess, gsize size)
+{
+	if (jabber_ibb_session_get_state(sess) == JABBER_IBB_SESSION_NOT_OPENED) {
+		sess->block_size = size;
+	} else {
+		purple_debug_error("jabber", 
+			"Can't set block size on an open IBB session\n");
+	}
+}
+
+gpointer
+jabber_ibb_session_get_user_data(JabberIBBSession *sess)
+{
+	return sess->user_data;
+}
+
+void
+jabber_ibb_session_set_opened_callback(JabberIBBSession *sess,
+	JabberIBBOpenedCallback *cb)
+{
+	sess->opened_cb = cb;
+}
+	
+void 
+jabber_ibb_session_set_data_sent_callback(JabberIBBSession *sess,
+	JabberIBBSentCallback *cb)
+{
+	sess->data_sent_cb = cb;
+}
+	
+void 
+jabber_ibb_session_set_closed_callback(JabberIBBSession *sess,
+	JabberIBBClosedCallback *cb)
+{
+	sess->closed_cb = cb;
+}
+	
+void 
+jabber_ibb_session_set_data_received_callback(JabberIBBSession *sess,
+	JabberIBBDataCallback *cb)
+{
+	sess->data_received_cb = cb;
+}
+
+void 
+jabber_ibb_session_set_error_callback(JabberIBBSession *sess, 
+	JabberIBBErrorCallback *cb)
+{
+	sess->error_cb = cb;
+}
+
+static void
+jabber_ibb_session_opened_cb(JabberStream *js, xmlnode *packet, gpointer data)
+{
+	JabberIBBSession *sess = (JabberIBBSession *) data;
+
+	sess->state = JABBER_IBB_SESSION_OPENED;
+	
+	if (sess->opened_cb) {
+		sess->opened_cb(sess);
+	}
+}
+
+void
+jabber_ibb_session_open(JabberIBBSession *sess)
+{
+	if (jabber_ibb_session_get_state(sess) != JABBER_IBB_SESSION_NOT_OPENED) {
+		purple_debug_error("jabber", 
+			"jabber_ibb_session called on an already open stream\n");
+	} else {
+		JabberIq *set = jabber_iq_new(sess->js, JABBER_IQ_SET);
+		xmlnode *open = xmlnode_new("open");
+		gchar block_size[10];
+		
+		xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess));
+		xmlnode_set_namespace(open, XEP_0047_NAMESPACE);
+		xmlnode_set_attrib(open, "sid", jabber_ibb_session_get_sid(sess));
+		g_snprintf(block_size, sizeof(block_size), "%ld", 
+			jabber_ibb_session_get_block_size(sess));
+		xmlnode_set_attrib(open, "block-size", block_size);
+		xmlnode_insert_child(set->node, open);
+		
+		jabber_iq_set_callback(set, jabber_ibb_session_opened_cb, sess);
+		
+		jabber_iq_send(set);
+	}
+}
+
+void
+jabber_ibb_session_close(JabberIBBSession *sess)
+{
+	JabberIBBSessionState state = jabber_ibb_session_get_state(sess);
+	
+	if (state != JABBER_IBB_SESSION_OPENED && state != JABBER_IBB_SESSION_ERROR) {
+		purple_debug_error("jabber",
+			"jabber_ibb_session_close called on a session that has not been"
+			"opened\n");
+	} else {
+		JabberIq *set = jabber_iq_new(jabber_ibb_session_get_js(sess),
+			JABBER_IQ_SET);
+		xmlnode *close = xmlnode_new("close");
+		
+		xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess));
+		xmlnode_set_namespace(close, XEP_0047_NAMESPACE);
+		xmlnode_set_attrib(close, "sid", jabber_ibb_session_get_sid(sess));
+		xmlnode_insert_child(set->node, close);
+		jabber_iq_send(set);
+		sess->state = JABBER_IBB_SESSION_CLOSED;
+	}
+}
+
+void
+jabber_ibb_session_accept(JabberIBBSession *sess)
+{
+	JabberIq *result = jabber_iq_new(jabber_ibb_session_get_js(sess),
+		JABBER_IQ_RESULT);
+	
+	xmlnode_set_attrib(result->node, "to", jabber_ibb_session_get_who(sess));
+	jabber_iq_set_id(result, sess->id);
+	jabber_iq_send(result);
+	sess->state = JABBER_IBB_SESSION_OPENED;
+}
+
+static void
+jabber_ibb_session_send_acknowledge_cb(JabberStream *js, xmlnode *packet, gpointer data)
+{
+	JabberIBBSession *sess = (JabberIBBSession *) data;
+	xmlnode *error = xmlnode_get_child(packet, "error");
+	
+	if (sess) {
+		/* reset callback */
+		if (sess->last_iq_id) {
+			g_free(sess->last_iq_id);
+			sess->last_iq_id = NULL;
+		}
+		
+		if (error) {
+			jabber_ibb_session_close(sess);
+			sess->state = JABBER_IBB_SESSION_ERROR;
+		
+			if (sess->error_cb) {
+				sess->error_cb(sess);
+			}
+		} else {
+			if (sess->data_sent_cb) {
+				sess->data_sent_cb(sess);
+			}
+		}
+	} else {
+		/* the session has gone away, it was probably cancelled */
+		purple_debug_info("jabber", 
+			"got response from send data, but IBB session is no longer active\n");
+	}
+}
+
+void
+jabber_ibb_session_send_data(JabberIBBSession *sess, gpointer data, gsize size)
+{
+	JabberIBBSessionState state = jabber_ibb_session_get_state(sess);
+	
+	if (state != JABBER_IBB_SESSION_OPENED) {
+		purple_debug_error("jabber", 
+			"trying to send data on a non-open IBB session\n");
+	} else if (size > jabber_ibb_session_get_block_size(sess)) {
+		purple_debug_error("jabber", 
+			"trying to send a too large packet in the IBB session\n");
+	} else {
+		JabberIq *set = jabber_iq_new(jabber_ibb_session_get_js(sess), 
+			JABBER_IQ_SET);
+		xmlnode *data_element = xmlnode_new("data");
+		char *base64 = purple_base64_encode(data, size);
+		char seq[10];
+		g_snprintf(seq, sizeof(seq), "%d", jabber_ibb_session_get_send_seq(sess));
+		
+		xmlnode_set_attrib(set->node, "to", jabber_ibb_session_get_who(sess));
+		xmlnode_set_namespace(data_element, XEP_0047_NAMESPACE);
+		xmlnode_set_attrib(data_element, "sid", jabber_ibb_session_get_sid(sess));
+		xmlnode_set_attrib(data_element, "seq", seq);
+		xmlnode_insert_data(data_element, base64, strlen(base64));
+		
+		xmlnode_insert_child(set->node, data_element);
+	
+		purple_debug_info("jabber", 
+			"IBB: setting send <iq/> callback for session %lx %s\n", sess,
+			sess->sid);
+		jabber_iq_set_callback(set, jabber_ibb_session_send_acknowledge_cb, sess);
+		sess->last_iq_id = g_strdup(xmlnode_get_attrib(set->node, "id"));
+		purple_debug_info("jabber", "IBB: set sess->last_iq_id: %lx %lx\n",
+			sess->last_iq_id, xmlnode_get_attrib(set->node, "id"));
+		jabber_iq_send(set);
+		
+		g_free(base64);
+		(sess->send_seq)++;
+	}
+}
+
+void
+jabber_ibb_parse(JabberStream *js, xmlnode *packet)
+{
+	xmlnode *data = xmlnode_get_child_with_namespace(packet, "data",
+		XEP_0047_NAMESPACE);
+	xmlnode *close = xmlnode_get_child_with_namespace(packet, "close", 
+		XEP_0047_NAMESPACE);
+	xmlnode *open = xmlnode_get_child_with_namespace(packet, "open",
+		XEP_0047_NAMESPACE);
+	const gchar *sid = 
+		data ? xmlnode_get_attrib(data, "sid") : 
+			close ? xmlnode_get_attrib(close, "sid") : NULL;
+	JabberIBBSession *sess = 
+		sid ? g_hash_table_lookup(jabber_ibb_sessions, sid) : NULL;
+	const gchar *who = xmlnode_get_attrib(packet, "from");
+	
+	if (sess) {
+		
+		if (strcmp(who, jabber_ibb_session_get_who(sess)) != 0) {
+			/* the iq comes from a different JID than the remote JID of the
+			  session, ignore it */
+			purple_debug_error("jabber", 
+				"Got IBB iq from wrong JID, ignoring\n");
+		} else if (data) {
+			guint16 seq = atoi(xmlnode_get_attrib(data, "seq"));
+			
+			/* reject the data, and set the session in error if we get an
+			  out-of-order packet */
+			if (seq == jabber_ibb_session_get_recv_seq(sess)) {
+				/* sequence # is the expected... */
+				JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT);
+				
+				jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
+				xmlnode_set_attrib(result->node, "to", 
+					xmlnode_get_attrib(packet, "from"));
+				
+				if (sess->data_received_cb) {
+					gchar *base64 = xmlnode_get_data(data);
+					gsize size;
+					gpointer rawdata = purple_base64_decode(base64, &size);
+					
+					g_free(base64);
+					
+					if (rawdata) {
+						if (size > jabber_ibb_session_get_block_size(sess)) {
+							purple_debug_error("jabber",
+								"IBB: received a too large packet\n");
+						} else {
+							sess->data_received_cb(sess, rawdata, size);
+						}
+						g_free(rawdata);
+					} else {
+						purple_debug_error("jabber", 
+							"IBB: invalid BASE64 data received\n");
+					}
+				}
+				
+				(sess->recv_seq)++;
+				jabber_iq_send(result);
+				
+			} else {
+				purple_debug_error("jabber", 
+					"Received an out-of-order IBB packet\n");
+				sess->state = JABBER_IBB_SESSION_ERROR;
+				
+				if (sess->error_cb) {
+					sess->error_cb(sess);
+				}
+			}
+		} else if (close) {
+			sess->state = JABBER_IBB_SESSION_CLOSED;
+			purple_debug_info("jabber", "IBB: received close\n");
+			
+			if (sess->closed_cb) {
+				purple_debug_info("jabber", "IBB: calling closed handler\n");
+				sess->closed_cb(sess);
+			}
+		
+		} else {
+			/* this should never happen */
+			purple_debug_error("jabber", "Received bogus iq for IBB session\n");
+		}
+	} else if (open) {
+		JabberIq *result;
+		const GList *iterator;
+		
+		/* run all open handlers registered until one returns true */
+		for (iterator = open_handlers ; iterator ; 
+			 iterator = g_list_next(iterator)) {
+			JabberIBBOpenHandler *handler = (JabberIBBOpenHandler *) iterator->data;
+
+			if (handler(js, packet)) {
+				result = jabber_iq_new(js, JABBER_IQ_RESULT);
+				xmlnode_set_attrib(result->node, "to", 
+					xmlnode_get_attrib(packet, "from"));
+				jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
+				jabber_iq_send(result);
+				return;
+			}
+		}
+	} else {
+		/* send error reply */
+		JabberIq *result = jabber_iq_new(js, JABBER_IQ_ERROR);
+		xmlnode *error = xmlnode_new("error");
+		xmlnode *item_not_found = xmlnode_new("item-not-found");
+		
+		xmlnode_set_namespace(item_not_found, 
+			"urn:ietf:params:xml:ns:xmpp-stanzas");
+		xmlnode_set_attrib(error, "code", "440");
+		xmlnode_set_attrib(error, "type", "cancel");
+		jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
+		xmlnode_set_attrib(result->node, "to", 
+			xmlnode_get_attrib(packet, "from"));
+		xmlnode_insert_child(error, item_not_found);
+		xmlnode_insert_child(result->node, error);
+		
+		jabber_iq_send(result);
+	}
+}
+
+void
+jabber_ibb_register_open_handler(JabberIBBOpenHandler *cb)
+{
+	open_handlers = g_list_append(open_handlers, cb);
+}
+
+void
+jabber_ibb_unregister_open_handler(JabberIBBOpenHandler *cb)
+{
+	open_handlers = g_list_remove(open_handlers, cb);
+}
+
+void
+jabber_ibb_init(void)
+{
+	jabber_ibb_sessions = g_hash_table_new(g_str_hash, g_str_equal);
+}
+
+void
+jabber_ibb_uninit(void)
+{
+	g_hash_table_destroy(jabber_ibb_sessions);
+	g_list_free(open_handlers);
+	jabber_ibb_sessions = NULL;
+	open_handlers = NULL;
+}
+
+
+		
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/jabber/ibb.h	Tue Sep 16 20:05:18 2008 +0000
@@ -0,0 +1,119 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
+ */
+ 
+#include "jabber.h"
+#include "iq.h"
+
+#ifndef JABBER_IBB_SESSION
+#define JABBER_IBB_SESSION
+
+#define XEP_0047_NAMESPACE "http://jabber.org/protocol/ibb"
+
+typedef struct _JabberIBBSession JabberIBBSession;
+
+typedef void 
+(JabberIBBDataCallback)(JabberIBBSession *, const gpointer data, gsize size);
+
+typedef void (JabberIBBOpenedCallback)(JabberIBBSession *);
+typedef void (JabberIBBClosedCallback)(JabberIBBSession *);
+typedef void (JabberIBBErrorCallback)(JabberIBBSession *);
+typedef void (JabberIBBSentCallback)(JabberIBBSession *);
+
+typedef gboolean (JabberIBBOpenHandler)(JabberStream *js, xmlnode *packet);
+
+typedef enum {
+	JABBER_IBB_SESSION_NOT_OPENED,
+	JABBER_IBB_SESSION_OPENED,
+	JABBER_IBB_SESSION_CLOSED,
+	JABBER_IBB_SESSION_ERROR
+} JabberIBBSessionState;
+
+struct _JabberIBBSession {
+	JabberStream *js;
+	gchar *who;
+	gchar *sid;
+	gchar *id;
+	guint16 send_seq;
+	guint16 recv_seq;
+	gsize block_size;
+	
+	/* session state */
+	JabberIBBSessionState state;
+	
+	/* user data (f.ex. a handle to a PurpleXfer) */
+	gpointer user_data;
+	
+	/* callbacks */
+	JabberIBBOpenedCallback *opened_cb;
+	JabberIBBSentCallback *data_sent_cb;
+	JabberIBBClosedCallback *closed_cb;
+	/* callback for receiving data */
+	JabberIBBDataCallback *data_received_cb;
+	JabberIBBErrorCallback *error_cb;
+	
+	/* store the last sent IQ (to permit cancel of callback) */
+	gchar *last_iq_id;
+};
+
+JabberIBBSession *jabber_ibb_session_create(JabberStream *js, const gchar *sid,
+	const gchar *who, gpointer user_data);
+JabberIBBSession *jabber_ibb_session_create_from_xmlnode(JabberStream *js,
+	xmlnode *packet, gpointer user_data);
+
+void jabber_ibb_session_destroy(JabberIBBSession *sess);
+
+void jabber_ibb_session_set_opened_callback(JabberIBBSession *sess,
+	JabberIBBOpenedCallback *cb);
+void jabber_ibb_session_set_data_sent_callback(JabberIBBSession *sess,
+	JabberIBBSentCallback *cb);
+void jabber_ibb_session_set_closed_callback(JabberIBBSession *sess,
+	JabberIBBClosedCallback *cb);
+void jabber_ibb_session_set_data_received_callback(JabberIBBSession *sess,
+	JabberIBBDataCallback *cb);
+void jabber_ibb_session_set_error_callback(JabberIBBSession *sess,
+	JabberIBBErrorCallback *cb);
+
+void jabber_ibb_session_open(JabberIBBSession *sess);
+void jabber_ibb_session_close(JabberIBBSession *sess);
+void jabber_ibb_session_accept(JabberIBBSession *sess);
+void jabber_ibb_session_send_data(JabberIBBSession *sess, gpointer data,
+	gsize size);
+
+const gchar *jabber_ibb_session_get_sid(const JabberIBBSession *sess);
+JabberStream *jabber_ibb_session_get_js(JabberIBBSession *sess);
+const gchar *jabber_ibb_session_get_who(const JabberIBBSession *sess);
+
+guint16 jabber_ibb_session_get_send_seq(const JabberIBBSession *sess);
+guint16 jabber_ibb_session_get_recv_seq(const JabberIBBSession *sess);
+
+JabberIBBSessionState jabber_ibb_session_get_state(const JabberIBBSession *sess);
+
+gsize jabber_ibb_session_get_block_size(const JabberIBBSession *sess);
+void jabber_ibb_session_set_block_size(JabberIBBSession *sess, gsize size);
+
+gpointer jabber_ibb_session_get_user_data(JabberIBBSession *sess);
+
+/* handle incoming packet */
+void jabber_ibb_parse(JabberStream *js, xmlnode *packet);
+
+/* add a handler for open session */
+void jabber_ibb_register_open_handler(JabberIBBOpenHandler *cb);
+void jabber_ibb_unregister_open_handler(JabberIBBOpenHandler *cb);
+
+void jabber_ibb_init(void);
+void jabber_ibb_uninit(void);
+
+#endif /* JABBER_IBB_SESSION */
--- a/libpurple/protocols/jabber/iq.c	Tue Sep 16 17:56:01 2008 +0000
+++ b/libpurple/protocols/jabber/iq.c	Tue Sep 16 20:05:18 2008 +0000
@@ -33,6 +33,7 @@
 #include "si.h"
 #include "ping.h"
 #include "adhoccommands.h"
+#include "ibb.h"
 
 #ifdef _WIN32
 #include "utsname.h"
@@ -355,6 +356,13 @@
 		return;
 	}
 
+	if (xmlnode_get_child_with_namespace(packet, "data", XEP_0047_NAMESPACE)
+		|| xmlnode_get_child_with_namespace(packet, "close", XEP_0047_NAMESPACE)
+		|| xmlnode_get_child_with_namespace(packet, "open", XEP_0047_NAMESPACE)) {
+		jabber_ibb_parse(js, packet);
+		return;
+	}
+	
 	/* If we get here, send the default error reply mandated by XMPP-CORE */
 	if(type && (!strcmp(type, "set") || !strcmp(type, "get"))) {
 		JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR);
--- a/libpurple/protocols/jabber/libxmpp.c	Tue Sep 16 17:56:01 2008 +0000
+++ b/libpurple/protocols/jabber/libxmpp.c	Tue Sep 16 20:05:18 2008 +0000
@@ -43,6 +43,7 @@
 #include "pep.h"
 #include "usertune.h"
 #include "caps.h"
+#include "ibb.h"
 
 static PurplePluginProtocolInfo prpl_info =
 {
@@ -148,6 +149,9 @@
 	purple_signal_unregister(plugin, "jabber-sending-xmlnode");
 	
 	purple_signal_unregister(plugin, "jabber-sending-text");
+
+	jabber_si_uninit();
+	jabber_ibb_uninit();
 	
 	return TRUE;
 }
@@ -269,11 +273,15 @@
 	
 	jabber_tune_init();
 	jabber_caps_init();
+	
+	jabber_ibb_init();
+	jabber_si_init();
 
 	jabber_add_feature("avatarmeta", AVATARNAMESPACEMETA, jabber_pep_namespace_only_when_pep_enabled_cb);
 	jabber_add_feature("avatardata", AVATARNAMESPACEDATA, jabber_pep_namespace_only_when_pep_enabled_cb);
 	jabber_add_feature("buzz", "http://www.xmpp.org/extensions/xep-0224.html#ns", jabber_buzz_isenabled);
-	
+	jabber_add_feature("ibb", XEP_0047_NAMESPACE, NULL);
+					   
 	jabber_pep_register_handler("avatar", AVATARNAMESPACEMETA, jabber_buddy_avatar_update_metadata);
 }
 
--- a/libpurple/protocols/jabber/si.c	Tue Sep 16 17:56:01 2008 +0000
+++ b/libpurple/protocols/jabber/si.c	Tue Sep 16 20:05:18 2008 +0000
@@ -35,6 +35,7 @@
 #include "jabber.h"
 #include "iq.h"
 #include "si.h"
+#include "ibb.h"
 
 #define STREAMHOST_CONNECT_TIMEOUT 15
 
@@ -64,8 +65,14 @@
 	size_t rxlen;
 	gsize rxmaxlen;
 	int local_streamhost_fd;
+	
+	JabberIBBSession *ibb_session;
+	FILE *fp;
 } JabberSIXfer;
 
+/* some forward declarations */
+static void jabber_si_xfer_ibb_send_init(JabberStream *js, PurpleXfer *xfer);
+
 static PurpleXfer*
 jabber_si_xfer_find(JabberStream *js, const char *sid, const char *from)
 {
@@ -204,8 +211,25 @@
 
 		jabber_iq_send(iq);
 
-		purple_xfer_cancel_local(xfer);
-
+		/* if IBB is available, revert to that before giving up... */
+		if (jsx->stream_method & STREAM_METHOD_IBB) {
+			/* if we are the initializer, init IBB */
+			purple_debug_info("jabber", 
+				"jabber_si_bytestreams_attempt_connect: "
+				"no streamhosts found, trying IBB\n");
+			/* if we are the sender, open an IBB session. But not if we already
+			  done it, since we could have received the error <iq/> from the
+			  receiver already... */
+			if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND
+				&& !jsx->ibb_session) {
+				jabber_si_xfer_ibb_send_init(jsx->js, xfer);
+			}
+			/* if we are the receiver, just wait for IBB open, callback is
+			  already set up... */
+		} else {
+			purple_xfer_cancel_local(xfer);
+		}
+		
 		return;
 	}
 
@@ -666,8 +690,29 @@
 	jsx = xfer->data;
 
 	if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result")) {
-		if (type && !strcmp(type, "error"))
-			purple_xfer_cancel_remote(xfer);
+	  purple_debug_info("jabber", 
+			    "jabber_si_xfer_connect_proxy_cb: type = %s\n",
+			    type);
+		if (type && !strcmp(type, "error")) {
+			/* if IBB is available, open IBB session */
+			purple_debug_info("jabber", 
+				"jabber_si_xfer_connect_proxy_cb: got error, method: %d\n",
+				jsx->stream_method);
+			if (jsx->stream_method & STREAM_METHOD_IBB) {
+				purple_debug_info("jabber", "IBB is possible, try it\n");
+				/* if we are the sender and haven't already opened an IBB
+				  session, do so now (we might already have failed to open
+				  the bytestream proxy ourselves when receiving this <iq/> */
+				if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND
+					&& !jsx->ibb_session) {
+					jabber_si_xfer_ibb_send_init(js, xfer);
+				}
+				/* if we are receiver, just wait for IBB open stanza, callback
+				  is already set up */
+			} else {
+				purple_xfer_cancel_remote(xfer);
+			}
+		}
 		return;
 	}
 
@@ -694,8 +739,19 @@
 			purple_debug_info("jabber", "Got local SOCKS5 streamhost-used.\n");
 			purple_xfer_start(xfer, xfer->fd, NULL, -1);
 		} else {
-			purple_debug_info("jabber", "streamhost-used does not match any proxy that was offered to target\n");
-			purple_xfer_cancel_local(xfer);
+			/* if available, try to revert to IBB... */
+			if (jsx->stream_method & STREAM_METHOD_IBB) {
+				purple_debug_info("jabber", 
+					"jabber_si_connect_proxy_cb: trying to revert to IBB\n");
+				if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
+					jabber_si_xfer_ibb_send_init(jsx->js, xfer);
+				}
+				/* if we are the receiver, we are already set up...*/
+			} else {
+				purple_debug_info("jabber", 
+					"streamhost-used does not match any proxy that was offered to target\n");
+				purple_xfer_cancel_local(xfer);
+			}
 		}
 		g_free(my_jid);
 		return;
@@ -822,8 +878,23 @@
 	/* We have no way of transferring, cancel the transfer */
 	if (streamhost_count == 0) {
 		jabber_iq_free(iq);
-		/* We should probably notify the target, but this really shouldn't ever happen */
-		purple_xfer_cancel_local(xfer);
+		
+		/* if available, revert to IBB */
+		if (jsx->stream_method & STREAM_METHOD_IBB) {
+			purple_debug_info("jabber",
+				"jabber_si_xfer_bytestreams_listen_cb: trying to revert to IBB\n");
+			if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
+				/* if we are the sender, init the IBB session... */
+				jabber_si_xfer_ibb_send_init(jsx->js, xfer);
+			}
+			/* if we are the receiver, we should just wait... the IBB open
+			  handler has already been set up... */
+		} else {
+			/* We should probably notify the target, 
+			  but this really shouldn't ever happen */
+			purple_xfer_cancel_local(xfer);
+		}
+		
 		return;
 	}
 
@@ -853,11 +924,200 @@
 
 }
 
+/* forward declare some functions here... */
+static void jabber_si_xfer_cancel_recv(PurpleXfer *xfer);
+static void jabber_si_xfer_cancel_send(PurpleXfer *xfer);
+static void jabber_si_xfer_free(PurpleXfer *xfer);
+
+static void
+jabber_si_xfer_ibb_error_cb(JabberIBBSession *sess)
+{
+	PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
+	JabberStream *js = jabber_ibb_session_get_js(sess);
+	PurpleConnection *gc = js->gc;
+	PurpleAccount *account = purple_connection_get_account(gc);
+	
+	purple_debug_error("jabber", "an error occured during IBB file transfer\n");
+	purple_xfer_error(purple_xfer_get_type(xfer), account,
+		jabber_ibb_session_get_who(sess), 
+			_("An error occured on the in-band bytestream transfer\n"));
+	purple_xfer_end(xfer);
+}
+
+static void
+jabber_si_xfer_ibb_closed_cb(JabberIBBSession *sess)
+{
+	PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
+	JabberStream *js = jabber_ibb_session_get_js(sess);
+	PurpleConnection *gc = js->gc;
+	PurpleAccount *account = purple_connection_get_account(gc);
+	
+	purple_debug_info("jabber", "the remote user closed the transfer\n");
+	if (purple_xfer_get_bytes_remaining(xfer) > 0) {
+		purple_xfer_error(purple_xfer_get_type(xfer), account,
+			jabber_ibb_session_get_who(sess), _("Transfer was closed."));
+		purple_xfer_end(xfer);
+	} else {
+		purple_xfer_set_completed(xfer, TRUE);
+	}
+}
+
+static void
+jabber_si_xfer_ibb_recv_data_cb(JabberIBBSession *sess, gpointer data,
+	gsize size)
+{
+	PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
+	JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+	
+	if (size <= purple_xfer_get_bytes_remaining(xfer)) {
+		fwrite(data, size, 1, jsx->fp);
+		purple_xfer_set_bytes_sent(xfer, purple_xfer_get_bytes_sent(xfer) + size);
+		purple_xfer_update_progress(xfer);
+		
+		if (purple_xfer_get_bytes_remaining(xfer) == 0) {
+			purple_xfer_set_completed(xfer, TRUE);
+		}
+	} else {
+		/* trying to write past size of file transfers negotiated size,
+		  reject transfer to protect against malicious behaviour */
+		purple_debug_error("jabber", 
+			"IBB file transfer, trying to write past end of file\n");
+		jabber_si_xfer_cancel_recv(xfer);
+	}
+	
+}
+
+static gboolean
+jabber_si_xfer_ibb_open_cb(JabberStream *js, xmlnode *packet)
+{
+	const gchar *who = xmlnode_get_attrib(packet, "from");
+	xmlnode *open = xmlnode_get_child(packet, "open");
+	const gchar *sid = xmlnode_get_attrib(open, "sid");
+	PurpleXfer *xfer = jabber_si_xfer_find(js, sid, who);
+	JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+	JabberIBBSession *sess = 
+		jabber_ibb_session_create_from_xmlnode(js, packet, xfer);
+	
+	if (sess) {
+		/* setup callbacks here...*/
+		jabber_ibb_session_set_data_received_callback(sess,
+			jabber_si_xfer_ibb_recv_data_cb);
+		jabber_ibb_session_set_closed_callback(sess, 
+			jabber_si_xfer_ibb_closed_cb);
+		jabber_ibb_session_set_error_callback(sess,
+			jabber_si_xfer_ibb_error_cb);
+		
+		/* open the file to write to */
+		jsx->fp = g_fopen(purple_xfer_get_local_filename(xfer), "w");
+		
+		jsx->ibb_session = sess;
+		
+		/* start the transfer */
+		purple_xfer_start(xfer, 0, NULL, 0);
+		return TRUE;
+	} else {
+		/* failed to create IBB session */
+		purple_debug_error("jabber", "failed to create IBB session\n");
+		jabber_si_xfer_cancel_recv(xfer);
+		return FALSE;
+	}
+}
+															
+static void
+jabber_si_xfer_ibb_send_data(JabberIBBSession *sess)
+{
+	PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
+	JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+	gsize remaining = purple_xfer_get_bytes_remaining(xfer);
+	gsize packet_size = remaining < jabber_ibb_session_get_block_size(sess) ?
+		remaining : jabber_ibb_session_get_block_size(sess);
+	gpointer data = g_malloc(packet_size);
+	int res;
+	
+	purple_debug_info("jabber", "IBB: about to read %d bytes from file %lx\n",
+		packet_size, jsx->fp);
+	res = fread(data, packet_size, 1, jsx->fp);
+	
+	if (res == 1) {
+		jabber_ibb_session_send_data(sess, data, packet_size);
+		purple_xfer_set_bytes_sent(xfer, 
+			purple_xfer_get_bytes_sent(xfer) + packet_size);
+		purple_xfer_update_progress(xfer);
+	} else {
+		purple_debug_error("jabber", 
+			"jabber_si_xfer_ibb_opened_cb: error reading from file\n");
+		jabber_si_xfer_cancel_send(xfer);
+	}
+}
+
+static void
+jabber_si_xfer_ibb_sent_cb(JabberIBBSession *sess)
+{
+	PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
+	gsize remaining = purple_xfer_get_bytes_remaining(xfer);
+	
+	if (remaining == 0) {
+		/* close the session */
+		jabber_ibb_session_close(sess);
+		purple_xfer_set_completed(xfer, TRUE);
+	} else {
+		/* send more... */
+		jabber_si_xfer_ibb_send_data(sess);
+	}
+}
+
+static void
+jabber_si_xfer_ibb_opened_cb(JabberIBBSession *sess)
+{
+	PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
+	JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+	
+	purple_xfer_start(xfer, 0, NULL, 0);
+	purple_xfer_set_bytes_sent(xfer, 0);
+	purple_xfer_update_progress(xfer);
+	jsx->fp = g_fopen(purple_xfer_get_local_filename(xfer), "r");
+	jabber_si_xfer_ibb_send_data(sess);
+}
+
+static void
+jabber_si_xfer_ibb_send_init(JabberStream *js, PurpleXfer *xfer)
+{
+	JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+	
+	purple_xfer_ref(xfer);
+	
+	jsx->ibb_session = jabber_ibb_session_create(js, jsx->stream_id,
+		purple_xfer_get_remote_user(xfer), xfer);
+	
+	if (jsx->ibb_session) {
+		/* should set callbacks here... */
+		jabber_ibb_session_set_opened_callback(jsx->ibb_session,
+			jabber_si_xfer_ibb_opened_cb);
+		jabber_ibb_session_set_data_sent_callback(jsx->ibb_session,
+			jabber_si_xfer_ibb_sent_cb);
+		jabber_ibb_session_set_closed_callback(jsx->ibb_session,
+			jabber_si_xfer_ibb_closed_cb);
+		jabber_ibb_session_set_error_callback(jsx->ibb_session,
+			jabber_si_xfer_ibb_error_cb);
+		
+		/* open the IBB session */
+		jabber_ibb_session_open(jsx->ibb_session);
+		
+	} else {
+		/* failed to create IBB session */
+		purple_xfer_unref(xfer);
+		purple_debug_error("jabber", 
+			"failed to initiate IBB session for file transfer\n");
+		jabber_si_xfer_cancel_send(xfer);
+	}
+}
+	
 static void jabber_si_xfer_send_method_cb(JabberStream *js, xmlnode *packet,
 		gpointer data)
 {
 	PurpleXfer *xfer = data;
 	xmlnode *si, *feature, *x, *field, *value;
+	gboolean found_method = FALSE;
 
 	if(!(si = xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si"))) {
 		purple_xfer_cancel_remote(xfer);
@@ -876,20 +1136,33 @@
 
 	for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
 		const char *var = xmlnode_get_attrib(field, "var");
-
+		JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+		
 		if(var && !strcmp(var, "stream-method")) {
 			if((value = xmlnode_get_child(field, "value"))) {
 				char *val = xmlnode_get_data(value);
 				if(val && !strcmp(val, "http://jabber.org/protocol/bytestreams")) {
 					jabber_si_xfer_bytestreams_send_init(xfer);
-					g_free(val);
-					return;
+					jsx->stream_method |= STREAM_METHOD_BYTESTREAMS;
+					found_method = TRUE;
+				} else if (val && !strcmp(val, XEP_0047_NAMESPACE)) {
+					jsx->stream_method |= STREAM_METHOD_IBB;
+					if (!found_method) {
+						/* we haven't tried to init a bytestream session, yet
+						  start IBB right away... */
+						jabber_si_xfer_ibb_send_init(js, xfer);
+						found_method = TRUE;
+					}
 				}
 				g_free(val);
 			}
 		}
 	}
-	purple_xfer_cancel_remote(xfer);
+	
+	if (!found_method) {
+		purple_xfer_cancel_remote(xfer);
+	}
+	
 }
 
 static void jabber_si_xfer_send_request(PurpleXfer *xfer)
@@ -929,11 +1202,9 @@
 	option = xmlnode_new_child(field, "option");
 	value = xmlnode_new_child(option, "value");
 	xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
-	/*
 	option = xmlnode_new_child(field, "option");
 	value = xmlnode_new_child(option, "value");
 	xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
-	*/
 
 	jabber_iq_set_callback(iq, jabber_si_xfer_send_method_cb, xfer);
 
@@ -967,6 +1238,16 @@
 		g_list_free(jsx->streamhosts);
 	}
 
+	if (jsx->ibb_session) {
+		purple_debug_info("jabber", 
+			"jabber_si_xfer_free: destroying IBB session\n");
+		jabber_ibb_session_destroy(jsx->ibb_session);
+	}
+	
+	if (jsx->fp) {
+		fclose(jsx->fp);
+	}
+	
 	g_free(jsx->stream_id);
 	g_free(jsx->iq_id);
 	/* XXX: free other stuff */
@@ -979,6 +1260,12 @@
 
 static void jabber_si_xfer_cancel_send(PurpleXfer *xfer)
 {
+	JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+	
+	/* if there is an IBB session active, send close on that */
+	if (jsx->ibb_session) {
+		jabber_ibb_session_close(jsx->ibb_session);
+	}
 	jabber_si_xfer_free(xfer);
 	purple_debug(PURPLE_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_send\n");
 }
@@ -993,6 +1280,11 @@
 
 static void jabber_si_xfer_cancel_recv(PurpleXfer *xfer)
 {
+	JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+	/* if there is an IBB session active, send close */
+	if (jsx->ibb_session) {
+		jabber_ibb_session_close(jsx->ibb_session);
+	}
 	jabber_si_xfer_free(xfer);
 	purple_debug(PURPLE_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_recv\n");
 }
@@ -1007,9 +1299,16 @@
 static void jabber_si_xfer_send_disco_cb(JabberStream *js, const char *who,
 		JabberCapabilities capabilities, gpointer data)
 {
-	PurpleXfer *xfer = data;
+	PurpleXfer *xfer = (PurpleXfer *) data;
+	JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
+	
+	if (capabilities & JABBER_CAP_IBB) {
+		purple_debug_info("jabber", 
+			"jabber_si_xfer_send_disco_cb: remote JID supports IBB\n");
+		jsx->stream_method |= STREAM_METHOD_IBB;
+	}
 
-	if(capabilities & JABBER_CAP_SI_FILE_XFER) {
+	if (capabilities & JABBER_CAP_SI_FILE_XFER) {
 		jabber_si_xfer_send_request(xfer);
 	} else {
 		char *msg = g_strdup_printf(_("Unable to send file to %s, user does not support file transfers"), who);
@@ -1136,18 +1435,20 @@
 		x = xmlnode_new_child(feature, "x");
 		xmlnode_set_namespace(x, "jabber:x:data");
 		xmlnode_set_attrib(x, "type", "submit");
-
 		field = xmlnode_new_child(x, "field");
 		xmlnode_set_attrib(field, "var", "stream-method");
-
-		value = xmlnode_new_child(field, "value");
-		if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS)
+					
+		/* we should maybe "remember" if bytestreams has failed before (in the
+			same session) with this JID, and only present IBB as an option to
+			avoid unnessesary timeout */
+		if (jsx->stream_method & STREAM_METHOD_BYTESTREAMS) {
+			value = xmlnode_new_child(field, "value");
 			xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
-		/*
-		else if(jsx->stream_method & STREAM_METHOD_IBB)
-		xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
-		*/
-
+		} else if(jsx->stream_method & STREAM_METHOD_IBB) {
+			value = xmlnode_new_child(field, "value");
+			xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
+		}
+							  
 		jabber_iq_send(iq);
 	}
 }
@@ -1167,6 +1468,9 @@
 		xfer->data = jsx = g_new0(JabberSIXfer, 1);
 		jsx->js = js;
 		jsx->local_streamhost_fd = -1;
+		
+		jsx->ibb_session = NULL;
+		jsx->fp = NULL;
 
 		purple_xfer_set_init_fnc(xfer, jabber_si_xfer_init);
 		purple_xfer_set_cancel_send_fnc(xfer, jabber_si_xfer_cancel_send);
@@ -1238,6 +1542,8 @@
 
 	jsx = g_new0(JabberSIXfer, 1);
 	jsx->local_streamhost_fd = -1;
+				   
+	jsx->ibb_session = NULL;
 
 	for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
 		const char *var = xmlnode_get_attrib(field, "var");
@@ -1249,10 +1555,8 @@
 					if((val = xmlnode_get_data(value))) {
 						if(!strcmp(val, "http://jabber.org/protocol/bytestreams")) {
 							jsx->stream_method |= STREAM_METHOD_BYTESTREAMS;
-							/*
 						} else if(!strcmp(val, "http://jabber.org/protocol/ibb")) {
 							jsx->stream_method |= STREAM_METHOD_IBB;
-							*/
 						}
 						g_free(val);
 					}
@@ -1290,4 +1594,15 @@
 	}
 }
 
+void
+jabber_si_init(void)
+{
+	jabber_ibb_register_open_handler(jabber_si_xfer_ibb_open_cb);
+}
 
+void
+jabber_si_uninit(void)
+{
+	jabber_ibb_unregister_open_handler(jabber_si_xfer_ibb_open_cb);
+}
+
--- a/libpurple/protocols/jabber/si.h	Tue Sep 16 17:56:01 2008 +0000
+++ b/libpurple/protocols/jabber/si.h	Tue Sep 16 20:05:18 2008 +0000
@@ -30,5 +30,7 @@
 void jabber_si_parse(JabberStream *js, xmlnode *packet);
 PurpleXfer *jabber_si_new_xfer(PurpleConnection *gc, const char *who);
 void jabber_si_xfer_send(PurpleConnection *gc, const char *who, const char *file);
+void jabber_si_init(void);
+void jabber_si_uninit(void);
 
 #endif /* _PURPLE_JABBER_SI_H_ */