diff options
author | Slávek Banko <slavek.banko@axis.cz> | 2019-12-11 01:41:26 +0100 |
---|---|---|
committer | Slávek Banko <slavek.banko@axis.cz> | 2019-12-11 02:23:35 +0100 |
commit | 73f00336178a9f312bac2992649120d462e0ac2d (patch) | |
tree | 8672519cb50cb0e642af0817188ad28f810338ee | |
parent | 914254104c50dec222fb31ce7a25a21870e90f16 (diff) | |
download | tdenetwork-73f00336178a9f312bac2992649120d462e0ac2d.tar.gz tdenetwork-73f00336178a9f312bac2992649120d462e0ac2d.zip |
kopete: Restore the MSN protocol because a replacement MSN server was created.
This reverts commits 0486034738 - 2d5f9c55da and f6fd4ab6c0.
Signed-off-by: Slávek Banko <slavek.banko@axis.cz>
158 files changed, 23315 insertions, 63 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 337df7c9..589cc6f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,7 @@ option( WITH_SLP "Enable OpenSLP support (krdc, krfb)" OFF ) option( BUILD_KOPETE_PROTOCOL_ALL "Build all kopete protocols" OFF ) option( BUILD_KOPETE_PROTOCOL_TESTBED "Build kopete protocol testbed" ${BUILD_KOPETE_PROTOCOL_ALL} ) option( BUILD_KOPETE_PROTOCOL_GROUPWISE "Build kopete protocol groupwise" ${BUILD_KOPETE_PROTOCOL_ALL} ) +option( BUILD_KOPETE_PROTOCOL_MSN "Build kopete protocol msn" ${BUILD_KOPETE_PROTOCOL_ALL} ) option( BUILD_KOPETE_PROTOCOL_IRC "Build kopete protocol irc" ${BUILD_KOPETE_PROTOCOL_ALL} ) option( BUILD_KOPETE_PROTOCOL_OSCAR "Build kopete protocol oscar" ${BUILD_KOPETE_PROTOCOL_ALL} ) option( BUILD_KOPETE_PROTOCOL_YAHOO "Build kopete protocol yahoo" ${BUILD_KOPETE_PROTOCOL_ALL} ) diff --git a/doc/kopete/chatstyle.docbook b/doc/kopete/chatstyle.docbook index 93bee59e..991bee25 100644 --- a/doc/kopete/chatstyle.docbook +++ b/doc/kopete/chatstyle.docbook @@ -224,7 +224,7 @@ This is the name of the contact associated with the message. It use MetaContact <varlistentry><term><filename>%service%</filename></term> <listitem> <para> -Display the name of the service associated with the message. Examples: Jabber, Yahoo. +Display the name of the service associated with the message. Examples: Jabber, Yahoo, MSN. </para> </listitem> </varlistentry> diff --git a/doc/kopete/index.docbook b/doc/kopete/index.docbook index acbaaf3e..ce6741ee 100644 --- a/doc/kopete/index.docbook +++ b/doc/kopete/index.docbook @@ -106,6 +106,7 @@ Appendix: Chat Window Style Guide (1st draft, Michaël) <keyword>Messaging</keyword> <keyword>Jabber</keyword> <keyword>IRC</keyword> +<keyword>MSN</keyword> <keyword>ICQ</keyword> <keyword>AIM</keyword> <keyword>Yahoo</keyword> @@ -167,7 +168,7 @@ Appendix: Chat Window Style Guide (1st draft, Michaël) <para>To use &kopete; you need to set up one or more accounts for the instant messaging services you wish to use.</para> <para>You've probably already chosen a messaging service, either because you already use &im;, or you need to use the same service as your friends. If you don't fit into either of these categories, please consider using a messaging service based on open standards, because these are designed for use by Free Software. Other messaging services are prone to changing the underlying technology without making the details freely available, making them harder for Free Software developers to support.</para> <para>The messaging services that &kopete; supports that are based on open standards are Jabber and IRC.</para> -<para>The following section assumes you are registered with an &im; service already. If not, you can register with Gadu-Gadu and Jabber from inside &kopete;; for other services, you'll have to register using their respective web site before creating an account in &kopete;.</para> +<para>The following section assumes you are registered with an &im; service already. If not, you can register with Gadu-Gadu, Jabber, and <trademark>MSN</trademark> from inside &kopete;; for other services, you'll have to register using their respective web site before creating an account in &kopete;.</para> <sect1 id="creating-accounts"> <title>Creating Accounts</title> <para>To create an account, use <menuchoice><guimenu>Settings</guimenu> <guimenuitem>Configure &kopete;...</guimenuitem> </menuchoice> to display the Configure window.</para> @@ -698,6 +699,11 @@ Shortcuts...</guimenuitem></menuchoice>.</para></tip> <title>ICQ</title> <para>ICQ has an Invisibility feature which allows you to hide from selected contacts. You may also search the ICQ user directory when adding a contact. A wide range of contact details can be set using the <guilabel>Properties</guilabel> option.</para> </sect2> + <sect2 id="protocols-msn"> + <title>MSN</title> + <para>MSN supports the sending and receiving of webcams, if your camera is supported by the Video4Linux 2 (v4l2) standard. To view someone's webcam, right click on their MSN buttefly icon and select <menuchoice><guimenuitem>View Contact's Webcam</guimenuitem></menuchoice>. File transfer and multi user chats work. To transfer a file, drag the file from Konqueror or the desktop into the chat window. To invite someone else into a chat, drag them from the Contact List into the chat window. The <menuchoice><guimenu>File</guimenu></menuchoice> menu also contains these commands. In addition, MSN supports custom emoticons.</para> + <para>To use file transfer or a webcam, make sure port 6891 is forwarded to your computer.</para> + </sect2> <sect2 id="protocols-yahoo"> <title>Yahoo</title> <para>Yahoo can send and receive webcam video. It also supports Yahoo mail and the Yahoo addressbook from the account menu. Conferencing is also possible.</para> @@ -895,7 +901,7 @@ Shortcuts...</guimenuitem></menuchoice>.</para></tip> <question><para>I need to connect via a SOCKS proxy, but I can't find any proxy configuration options in &kopete;. How do I set up &kopete; to use SOCKS?</para> </question> <answer><itemizedlist> -<listitem><para><trademark>ICQ</trademark>, <trademark>AIM</trademark>, Jabber, and <trademark>Yahoo</trademark> use the &kde; network infrastructure. Their SOCKS proxy details are configured with the rest of &kde;, in <application>Control Center</application>, <menuchoice><guimenu>Internet & Network</guimenu><guimenuitem>Proxy</guimenuitem></menuchoice>.</para></listitem> +<listitem><para><trademark>MSN</trademark>, <trademark>ICQ</trademark>, <trademark>AIM</trademark>, Jabber, and <trademark>Yahoo</trademark> use the &kde; network infrastructure. Their SOCKS proxy details are configured with the rest of &kde;, in <application>Control Center</application>, <menuchoice><guimenu>Internet & Network</guimenu><guimenuitem>Proxy</guimenuitem></menuchoice>.</para></listitem> </itemizedlist> </answer> </qandaentry> diff --git a/kopete/ConfigureChecks.cmake b/kopete/ConfigureChecks.cmake index 44939818..d4c97769 100644 --- a/kopete/ConfigureChecks.cmake +++ b/kopete/ConfigureChecks.cmake @@ -9,8 +9,8 @@ # ################################################# -# glib-2.0 (jabber) -if( BUILD_KOPETE_PROTOCOL_JABBER AND WITH_JINGLE ) +# glib-2.0 (jabber, msn) +if( (BUILD_KOPETE_PROTOCOL_JABBER AND WITH_JINGLE) OR (BUILD_KOPETE_PROTOCOL_MSN AND WITH_WEBCAM) ) pkg_search_module( GLIB2 glib-2.0 ) if( GLIB2_FOUND ) set( HAVE_GLIB 1 CACHE INTERNAL "" FORCE ) diff --git a/kopete/KABC_INTEG_NOTES b/kopete/KABC_INTEG_NOTES index 87d22977..ea053dbe 100644 --- a/kopete/KABC_INTEG_NOTES +++ b/kopete/KABC_INTEG_NOTES @@ -38,7 +38,7 @@ E.g. we know someones telephone nr. but we don't want to put this information ba brunes goals the only points I wanted to make were -1. I think the OSCAR picture support should somehow integrate with / use the picture in kaddressbook, and +1. I think the MSN / OSCAR picture support should somehow integrate with / use the picture in kaddressbook, and 2. we need to get the KAB guys to add >1 field for IM account in KAB, and all the contacts for a MC should have their accounts there Syncing policies diff --git a/kopete/TODO b/kopete/TODO index eab40274..e9a7a594 100644 --- a/kopete/TODO +++ b/kopete/TODO @@ -76,3 +76,15 @@ OSCAR ICQ/AIM TODO ITEMS ================================================================================ + + + MSN TODO (for the Kopete 1.0 release) +--------------------------------------- + + - Handle the MSN PLUS! color codes + - Show internals messages in chat window when filetransfers (go with + the new interface for invitation in libkopete) + + - Search for an MSN User (not for Kopete 1.0) + +================================================================================ diff --git a/kopete/kopete/eventsrc b/kopete/kopete/eventsrc index 8e3216cc..9d3cf2f7 100644 --- a/kopete/kopete/eventsrc +++ b/kopete/kopete/eventsrc @@ -997,6 +997,181 @@ Comment[zh_HK]=您的 Yahoo 收件匣有新郵件 Comment[zh_TW]=新郵件送達您的 Yahoo 收件匣 default_presentation=16 +[msn_alert] +Name=MSN Alert +Name[bg]=MSN съобщение +Name[ca]=Alerta del MSN +Name[cs]=MSN upozornění +Name[da]=MSN-alarm +Name[de]=MSN-Warnung +Name[el]=Ειδοποίηση του MSN +Name[es]=Alerta MSN +Name[et]=MSN teade +Name[fa]=هشدار اماسان +Name[fi]=MSN-varoitus +Name[fr]=Alerte MSN +Name[he]=אזהרה של MSN +Name[hu]=MSN értesítő +Name[is]=MSN skeyti +Name[it]=Avviso MSN +Name[km]=សេចក្ដីជូនដំណឹង MSN +Name[lt]=MSN: „dėmesio!“ +Name[nb]=MSN-varsling +Name[nds]=MSN-Alarm +Name[ne]=एमएसएन सावधानी +Name[nl]=MSN-melding +Name[pa]=MSN ਚੇਤਾਵਨੀ +Name[pl]=Alarm MSN +Name[pt]=Alerta MSN +Name[pt_BR]=Alerta do MSN +Name[ru]=Предупреждение MSN +Name[sk]=MSN Upozornenie +Name[sl]=Alarm MSN +Name[sr]=MSN аларм +Name[sr@Latn]=MSN alarm +Name[sv]=MSN-larm +Name[tr]=MSN Uyarısı +Name[uk]=Сигнал MSN +Name[zh_CN]=MSN 提醒 +Name[zh_TW]=MSN 警告 +Comment=A new alert has been sent to you +Comment[bg]=Изпратено ви е ново съобщение +Comment[ca]=Se us ha enviat una nova alerta +Comment[cs]=Bylo vám doručeno nové upozornění +Comment[da]=En ny alarm er sendt til dig +Comment[de]=Sie haben eine neue Warnung erhalten +Comment[el]=Σας στάλθηκε μια νέα ειδοποίηση +Comment[es]=Se le ha enviado una nueva alerta +Comment[et]=Sulle saadeti uus teade +Comment[fa]=هشدار جدیدی برای شما ارسال شده است +Comment[fi]=Sinulle on lähetetty uusi varoitus +Comment[fr]=Une nouvelle alerte vous a été envoyée +Comment[he]=אזהרה חדשה נשלחה אליך +Comment[hu]=Új értesítőt küldtek Önnek +Comment[is]=Þér hefur verið sent nýtt skeyti +Comment[it]=Ti è stato inviato un avviso +Comment[ja]=新しいアラートを受信しました +Comment[km]=បានផ្ញើសេចក្ដីជូនដំណឹងទៅឲ្យអ្នក +Comment[lt]=Jums pasiųstas naujas „dėmesio!“ signalas +Comment[nb]=En ny varsling er sendt til deg +Comment[nds]=Een hett Di en niegen Alarm sendt +Comment[ne]=तपाईँलाई एउटा नयाँ सावधानी सन्देश पठाएको छ +Comment[nl]=U hebt een nieuwe melding ontvangen +Comment[pl]=Nowy alarm został wysłany do Ciebie +Comment[pt]=Foi enviada um novo alerta +Comment[pt_BR]=Um novo alerta foi enviado para você +Comment[ru]=Вам отправлено предупреждение +Comment[sk]=Bolo vám poslané nové upozornenie +Comment[sl]=Poslan vam je bil alarm +Comment[sr]=Послат вам је нови аларм +Comment[sr@Latn]=Poslat vam je novi alarm +Comment[sv]=Ett nytt larm har skickats till dig +Comment[tr]=Size yeni bir uyarı gönderildi +Comment[uk]=Вам було відіслано новий сигнал +Comment[zh_CN]=您收到了新提醒 +Comment[zh_TW]=一個新的警告已送達給您 +default_presentation=16 + +[msn_mail] +Name=MSN Mail +Name[ar]=بريد MSN +Name[be]=Пошта MSN +Name[bg]=Пристигна нова поща в MSN +Name[bn]=এমএসএন মেইল +Name[br]=Postel MSN +Name[bs]=MSN mail +Name[ca]=Correu de MSN +Name[cs]=MSN pošta +Name[da]=MSN-Mail +Name[de]=MSN-Mail +Name[es]=Correo MSN +Name[fa]=نامۀ اماسان +Name[fi]=MSN-sähköposti +Name[fr]=Courriel MSN +Name[gl]=Correo MSN +Name[he]=דוא"ל של MSN +Name[hi]=एमएसएन मेल +Name[hr]=MSN pošta +Name[hu]=MSN e-mail +Name[is]=MSN póstur +Name[it]=Posta MSN +Name[ja]=MSN メール +Name[ka]=MSN ფოსტა +Name[kk]=MSN поштасы +Name[km]=សំបុត្រ MSN +Name[lt]=MSN Paštas +Name[mk]=MSN-пошта +Name[nb]=MSN e-post +Name[nds]=MSN-Nettpost +Name[ne]=एमएसएन मेल +Name[nn]=MSN-e-post +Name[pa]=MSN ਮੇਲ +Name[pl]=Poczta MSN +Name[pt]=E-mail MSN +Name[ru]=Почта MSN +Name[sl]=E-pošta MSN +Name[sr]=MSN пошта +Name[sr@Latn]=MSN pošta +Name[sv]=MSN e-post +Name[ta]=MSN மின்னஞ்சல் +Name[tg]=Пости MSN +Name[tr]=MSN Posta +Name[uk]=Пошта MSN +Name[zh_CN]=MSN 邮件 +Comment=New email has arrived in your MSN inbox +Comment[be]=У вашай электроннай скрыні MSN новая пошта +Comment[bg]=Пристигна нова поща в MSN +Comment[bn]=আপনার এমএসএন ইনবক্সে নতুন ই-মেইল উপস্থিত হয়েছে +Comment[br]=Deuet eo ur postel nevez d'em voest degemer MSN +Comment[bs]=Stigla je nova pošta u vaš MSN sandučić +Comment[ca]=Ha arribat un nou correu a la vostra bústia de MSN +Comment[cs]=Přišla nová pošta do vaší MSN schránky +Comment[da]=Ny e-mail ankom til din MSN-indbakke +Comment[de]=Eine neue Nachricht befindet sich im MSN-Eingangsordner +Comment[el]=Μόλις έφτασε νέο e-mail στα εισερχόμενα του MSN σας +Comment[eo]=Nova poŝto ricevita +Comment[es]=Tiene correo nuevo en la cuenta de MSN +Comment[et]=Saabus uus kiri sinu MSN Inboxi +Comment[eu]=E-posta berri bat jaso da zure MSN-ko sarrerako ontzian +Comment[fa]=یک رایانامۀ جدید در دریافتی اماسان شما رسیده است +Comment[fi]=Uutta postia saapunut MSN-sähköpostilaatikkoon +Comment[fr]=Un nouveau message est arrivé dans votre boîte aux lettres MSN +Comment[gl]=Unha nova mensaxe chegou ao teu cartafol de entrada de MSN +Comment[he]=התקבל עבורך דואר חדש בתיבת הדוא"ל של MSN +Comment[hr]=Nova pošta je stigla u vaš MSN sandučić +Comment[hu]=Új levél érkezett az MSN postaládába +Comment[is]=Það er nýr póstur í MSN innhólfinu þínu +Comment[it]=È arrivata nuova posta nella tua casella MSN +Comment[ja]=MSN の受信箱に新しいメールが届きました +Comment[ka]=ახალი ელფოსტა მოვიდა MSN საფოსტო ყუთში +Comment[kk]=MSN пошта жәшігне хабарлама келді +Comment[km]=អ៊ីមែលថ្មីបានមកដល់ក្នុងប្រអប់ទទួល MSN របស់អ្នកហើយ +Comment[lt]=Į MSN pašto dėžutę gautas naujas laiškas +Comment[mk]=Пристигна нова пошта во вашето MSN-сандаче +Comment[nb]=Ny e-post er ankommet i MSN-innboksen +Comment[nds]=Du hest niege Nettpost in Dien MSN-Postingang +Comment[ne]=तपाईँको एमएसएन पत्रमञ्जूषामा नयाँ इमेल छ +Comment[nl]=Er is een nieuwe e-mail aangekomen in uw MSN-inbox +Comment[nn]=Ny e-post er komen til MSN-innboksen +Comment[pl]=Nadeszła nowa wiadomość do Twojej skrzynki odbiorczej w MSN +Comment[pt]=Chegou uma mensagem nova à sua caixa de correio do MSN +Comment[pt_BR]=chegou um novo e-mail em sua caixa de entrada MSN +Comment[ru]=Пришли новые письма в MSN +Comment[se]=Ođđa e-boasta lea boahtán du MSN-boastaboksii +Comment[sk]=Do vašej schránky MSN prišla nová správa +Comment[sl]=Nova e-pošta je prispela v vaš nabiralnik MSN +Comment[sr]=Нова порука је стигла у ваше MSN сандуче +Comment[sr@Latn]=Nova poruka je stigla u vaše MSN sanduče +Comment[sv]=Ett nytt brev har anlänt i din MSN-inkorg +Comment[ta]=உங்கள் எம்எஸ்என் அஞ்சல் பெட்டிக்கு புதிய மின்னஞ்சல் வந்துள்ளது +Comment[tg]=Ба қутии пости MSN-и шумо пайёми электронии нав омад +Comment[tr]=MSN gelen kutunuza yeni bir e-posta geldi +Comment[uk]=Прийшли нові листи у вашу скриньку MSN +Comment[zh_CN]=您的 MSN 收件箱中有新邮件到达 +Comment[zh_HK]=您的 MSN 收件匣有新郵件 +Comment[zh_TW]=新郵件送達您的 MSN 收件匣 +default_presentation=16 + [icq_authorization] Name=ICQ Authorization Name[be]=Спраўджванне асобы ICQ diff --git a/kopete/kopete/kimiface.h b/kopete/kopete/kimiface.h index 970d9698..b6413f29 100644 --- a/kopete/kopete/kimiface.h +++ b/kopete/kopete/kimiface.h @@ -107,7 +107,7 @@ k_dcop: * Get the KABC uid corresponding to the supplied IM address * Protocols should be * @param contactId the protocol specific identifier for the contact, eg UIN for ICQ, screenname for AIM, nick for IRC. - * @param protocol the protocol, eg one of "AIMProtocol", "ICQProtocol", + * @param protocol the protocol, eg one of "AIMProtocol", "MSNProtocol", "ICQProtocol", * @return a KABC uid or null if none found/ */ virtual TQString locate( const TQString & contactId, const TQString & protocol ) = 0; @@ -167,7 +167,7 @@ k_dcop: /** * Add a contact to the contact list * @param contactId the protocol specific identifier for the contact, eg UIN for ICQ, screenname for AIM, nick for IRC. - * @param protocol the protocol, eg one of "AIMProtocol", "ICQProtocol", ... + * @param protocol the protocol, eg one of "AIMProtocol", "MSNProtocol", "ICQProtocol", ... * @return whether the add succeeded. False may signal already present, protocol not supported, or add operation not supported. */ virtual bool addContact( const TQString &contactId, const TQString &protocol ) = 0; diff --git a/kopete/kopete/kopeteiface.h b/kopete/kopete/kopeteiface.h index d715018e..f68c3c57 100644 --- a/kopete/kopete/kopeteiface.h +++ b/kopete/kopete/kopeteiface.h @@ -105,13 +105,13 @@ k_dcop: /** * load a plugin - * the name is the name of the library: example: kopete_icq + * the name is the name of the library: example: kopete_msn * but you can ommit the kopete_ prefix */ bool loadPlugin( const TQString& name ); /** * unload a plugin - * the name is the name of the library: example: kopete_icq + * the name is the name of the library: example: kopete_msn * but you can ommit the kopete_ prefix */ bool unloadPlugin( const TQString& name ); diff --git a/kopete/kopete/tdeconf_update/kopete-account-tdeconf_update.cpp b/kopete/kopete/tdeconf_update/kopete-account-tdeconf_update.cpp index 67b39ad8..42ba47f1 100644 --- a/kopete/kopete/tdeconf_update/kopete-account-tdeconf_update.cpp +++ b/kopete/kopete/tdeconf_update/kopete-account-tdeconf_update.cpp @@ -55,7 +55,7 @@ void parseGroup( const TQString &group, const TQString &rawLine ) { // Groups that are converted can almost certainly be removed entirely - if ( group == "ICQ" || group == "Oscar" || group == "Gadu" || group == "Jabber" || group == "IRC" ) + if ( group == "MSN" || group == "ICQ" || group == "Oscar" || group == "Gadu" || group == "Jabber" || group == "IRC" ) { accountId = "EMPTY"; autoConnect = "true"; @@ -82,7 +82,21 @@ void parseGroup( const TQString &group, const TQString &rawLine ) void parseKey( const TQString &group, const TQString &key, const TQString &value, const TQString &rawLine ) { //qcerr << "*** group='" << group << "'" << endl; - if ( group == "ICQ" ) + if ( group == "MSN" ) + { + if ( key == "UserID" ) + accountId = value; + else if ( key == "Password" ) + password = value; + else if ( key == "AutoConnect" ) + autoConnect = value; + else if ( key == "Nick" ) + pluginData[ "displayName" ] = value; + + // All other keys are ignored for MSN, as these apply to stuff that's + // now in libkopete (and the main app) instead. + } + else if ( group == "ICQ" ) { if ( key == "UIN" ) accountId = value; diff --git a/kopete/libkopete/API-TODO b/kopete/libkopete/API-TODO index c9684b8a..4914b00c 100644 --- a/kopete/libkopete/API-TODO +++ b/kopete/libkopete/API-TODO @@ -85,7 +85,8 @@ KCL::selectedGroups can be removed outright. Allow emoticons and emoticon sets to be flagged as being for only a specific protocol. Allow the user to have more than one emoticon set enabled at once, and to set priorities. -This way, the user will be able to have a base theme, a set of Gadu-Gadu-specific emoticons and so on. +This way, the user will be able to have a base theme, a set of MSN-specific emoticons, a +set of Gadu-Gadu-specific emoticons and so on. Possibly move emoticon support into a plugin? diff --git a/kopete/libkopete/kopeteawaydialog.h b/kopete/libkopete/kopeteawaydialog.h index 0f4aaa29..b9470af9 100644 --- a/kopete/libkopete/kopeteawaydialog.h +++ b/kopete/libkopete/kopeteawaydialog.h @@ -128,7 +128,7 @@ public slots: * Shows the away dialog, but maintains a "state" * so you can specify if you're setting away, * do not disturb, gone, etc for protocols that - * support this like ICQ. + * support this like ICQ and MSN. * * This string does not have any special internal * meaning, but rather will get passed to setAway() diff --git a/kopete/libkopete/kopetecontact.h b/kopete/libkopete/kopetecontact.h index b92322f5..f8a6052d 100644 --- a/kopete/libkopete/kopetecontact.h +++ b/kopete/libkopete/kopetecontact.h @@ -79,7 +79,7 @@ public: * Across those boundaries ids may occur multiple times. * The id is solely for comparing items safely (using pointers is * more crash-prone). DO NOT assume anything regarding the id's - * value! Even if it may look like an ICQ UIN, + * value! Even if it may look like an ICQ UIN or an MSN passport, * this is undefined and may change at any time! * * @param account is the parent account. this constructor automatically register the contact to the account @@ -106,7 +106,7 @@ public: * Across those boundaries ids may occur multiple times. * The id is solely for comparing items safely (using pointers is * more crash-prone). DO NOT assume anything regarding the id's - * value! Even if it may look like an ICQ UIN, + * value! Even if it may look like an ICQ UIN or an MSN passport, * this is undefined and may change at any time! * * @return The unique id of the contact @@ -274,7 +274,7 @@ public: /** * Returns the primary message manager affiliated with this contact * Although a contact can have more than one active message manager - * only one message manager will + * (as is the case with MSN at least), only one message manager will * ever be the contacts "primary" message manager.. aka the 1 on 1 chat. * This function should always return that instance. * diff --git a/kopete/libkopete/kopetecontactlist.cpp b/kopete/libkopete/kopetecontactlist.cpp index dc8cdeb1..f8aecb32 100644 --- a/kopete/libkopete/kopetecontactlist.cpp +++ b/kopete/libkopete/kopetecontactlist.cpp @@ -358,6 +358,11 @@ void ContactList::slotPhotoChanged() emit globalIdentityChanged(Kopete::Global::Properties::self()->photo().key(), myself()->picture().path()); mutex=false; + /* The mutex is usefull to don't have such as stack overflow + Kopete::ContactList::slotPhotoChanged -> Kopete::ContactList::globalIdentityChanged + MSNAccount::slotGlobalIdentityChanged -> Kopete::Contact::propertyChanged + Kopete::MetaContact::slotPropertyChanged -> Kopete::MetaContact::photoChanged -> Kopete::ContactList::slotPhotoChanged + */ } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -517,7 +522,7 @@ void ContactList::convertContactList( const TQString &fileName, uint /* fromVers { // Convert address book fields. // Jabber will be called "xmpp", Aim/Toc and Aim/Oscar both will - // be called "aim". AIM, IRC, Oscar and SMS don't use address + // be called "aim". MSN, AIM, IRC, Oscar and SMS don't use address // book fields yet; Gadu and ICQ can be converted as-is. // As Yahoo is unfinished we won't try to convert at all. TQString id = oldContactElement.attribute( TQString::fromLatin1( "id" ), TQString() ); @@ -607,7 +612,12 @@ void ContactList::convertContactList( const TQString &fileName, uint /* fromVers bool convertOldAim = false; uint fieldCount = 1; TQString addressBookLabel; - if( id == TQString::fromLatin1("IRCProtocol") ) + if( id == TQString::fromLatin1("MSNProtocol") ) + { + fieldCount = 3; + addressBookLabel = TQString::fromLatin1("msn"); + } + else if( id == TQString::fromLatin1("IRCProtocol") ) { fieldCount = 3; addressBookLabel = TQString::fromLatin1("irc"); @@ -647,7 +657,7 @@ void ContactList::convertContactList( const TQString &fileName, uint /* fromVers } // Do the actual conversion - if ( id == TQString::fromLatin1( "OscarProtocol" ) || + if( id == TQString::fromLatin1( "MSNProtocol" ) || id == TQString::fromLatin1( "OscarProtocol" ) || id == TQString::fromLatin1( "AIMProtocol" ) || id == TQString::fromLatin1( "IRCProtocol" ) || id == TQString::fromLatin1( "ICQProtocol" ) || id == TQString::fromLatin1( "JabberProtocol" ) || id == TQString::fromLatin1( "SMSProtocol" ) || id == TQString::fromLatin1( "WPProtocol" ) || @@ -689,7 +699,14 @@ void ContactList::convertContactList( const TQString &fileName, uint /* fromVers else dataField.appendChild( newList.createTextNode( strList[ idx + 1 ] ) ); - if( id == TQString::fromLatin1("IRCProtocol") ) + if( id == TQString::fromLatin1("MSNProtocol") ) + { + dataField = newList.createElement( TQString::fromLatin1( "plugin-data-field" ) ); + pluginData[ id ].appendChild( dataField ); + dataField.setAttribute( TQString::fromLatin1( "key" ), TQString::fromLatin1( "groups" ) ); + dataField.appendChild( newList.createTextNode( strList[ idx + 2 ] ) ); + } + else if( id == TQString::fromLatin1("IRCProtocol") ) { dataField = newList.createElement( TQString::fromLatin1( "plugin-data-field" ) ); pluginData[ id ].appendChild( dataField ); @@ -725,7 +742,7 @@ void ContactList::convertContactList( const TQString &fileName, uint /* fromVers idx += 2; } - // AIM, IRC, Oscar and SMS didn't store address book fields up + // MSN, AIM, IRC, Oscar and SMS didn't store address book fields up // to now, so create one if( id != TQString::fromLatin1("ICQProtocol") && id != TQString::fromLatin1("JabberProtocol") && id != TQString::fromLatin1("WPProtocol") && id != TQString::fromLatin1("GaduProtocol") ) { diff --git a/kopete/libkopete/kopetemetacontact.h b/kopete/libkopete/kopetemetacontact.h index b7f9fbc7..15d82228 100644 --- a/kopete/libkopete/kopetemetacontact.h +++ b/kopete/libkopete/kopetemetacontact.h @@ -483,7 +483,7 @@ public slots: * * Like sendMessage, but this time a full-blown chat will be opened. * Most protocols can't distinguish between the two and are either - * completely session based like ICQ or completely message based like + * completely session based like MSN or completely message based like * ICQ the only true difference is the GUI shown to the user. * * returns the Contact that was chosen as the preferred diff --git a/kopete/libkopete/kopeteonlinestatus.h b/kopete/libkopete/kopeteonlinestatus.h index dbdae1ff..6ad34c55 100644 --- a/kopete/libkopete/kopeteonlinestatus.h +++ b/kopete/libkopete/kopeteonlinestatus.h @@ -74,7 +74,10 @@ public: /** * Refers to protocols where state cannot be determined. This * applies to SMS contacts (text messages via mobile phones), - * since there's no presence information over SMS. Lastly, libkopete + * since there's no presence information over SMS, but also + * to e.g. MSN contacts that are not on your contact list, + * since MSN only allows a user to query online state for + * users that are formally on the contact list. Lastly, libkopete * itself uses the Unknown state in @ref MetaContact for * meta contacts that have no child contacts at all. */ diff --git a/kopete/plugins/CMakeLists.txt b/kopete/plugins/CMakeLists.txt index f8eb63fc..a5c6b08c 100644 --- a/kopete/plugins/CMakeLists.txt +++ b/kopete/plugins/CMakeLists.txt @@ -22,6 +22,7 @@ tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_TEXTEFFECT texteffect ) tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_HIGHLIGHT highlight ) tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_ALIAS alias ) tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_MOTIONAUTOAWAY motionautoaway ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_NETMEETING netmeeting ) tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_ADDBOOKMARKS addbookmarks ) tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_STATISTICS statistics ) tde_conditional_add_subdirectory( BUILD_KOPETE_PLUGIN_SMPPPDCS smpppdcs ) diff --git a/kopete/plugins/Makefile.am b/kopete/plugins/Makefile.am index ca0d0188..8b817465 100644 --- a/kopete/plugins/Makefile.am +++ b/kopete/plugins/Makefile.am @@ -8,5 +8,5 @@ endif SUBDIRS = latex autoreplace history contactnotes cryptography\ connectionstatus translator nowlistening webpresence texteffect\ - highlight alias $(MOTIONAUTOAWAY_SUBDIR) addbookmarks\ + highlight alias $(MOTIONAUTOAWAY_SUBDIR) netmeeting addbookmarks\ statistics $(SMPPPDCS_SUBDIR) diff --git a/kopete/plugins/history/converter.cpp b/kopete/plugins/history/converter.cpp index a3c6580a..c63833e8 100644 --- a/kopete/plugins/history/converter.cpp +++ b/kopete/plugins/history/converter.cpp @@ -68,7 +68,13 @@ void HistoryPlugin::convertOldHistory() if(accountId.isNull() || protocolId.isNull()) { - if(fi->fileName() == "ICQProtocol" || fi->fileName() == "icq_logs" ) + if(fi->fileName() == "MSNProtocol" || fi->fileName() == "msn_logs" ) + { + protocolId="MSNProtocol"; + TDEGlobal::config()->setGroup("MSN"); + accountId=TDEGlobal::config()->readEntry( "UserID" ); + } + else if(fi->fileName() == "ICQProtocol" || fi->fileName() == "icq_logs" ) { protocolId="ICQProtocol"; TDEGlobal::config()->setGroup("ICQ"); @@ -319,7 +325,9 @@ bool HistoryPlugin::detectOldHistory() if( dynamic_cast<Kopete::Protocol *>( Kopete::PluginManager::self()->plugin( fi->fileName() ) ) ) return true; - if(fi->fileName() == "ICQProtocol" || fi->fileName() == "icq_logs" ) + if(fi->fileName() == "MSNProtocol" || fi->fileName() == "msn_logs" ) + return true; + else if(fi->fileName() == "ICQProtocol" || fi->fileName() == "icq_logs" ) return true; else if(fi->fileName() == "AIMProtocol" || fi->fileName() == "aim_logs" ) return true; diff --git a/kopete/plugins/latex/latexplugin.cpp b/kopete/plugins/latex/latexplugin.cpp index 8c6e0ad8..2773d21f 100644 --- a/kopete/plugins/latex/latexplugin.cpp +++ b/kopete/plugins/latex/latexplugin.cpp @@ -178,6 +178,7 @@ void LatexPlugin::slotMessageAboutToShow( Kopete::Message& msg ) void LatexPlugin::slotMessageAboutToSend( Kopete::Message& msg) { Q_UNUSED(msg) + //disabled because to work correctly, we need to find what special has the gif we can send over MSN #if 0 TDEConfig *config = TDEGlobal::config(); config->setGroup("Latex Plugin"); @@ -188,6 +189,8 @@ void LatexPlugin::slotMessageAboutToSend( Kopete::Message& msg) TQString messageText = msg.plainBody(); if( !messageText.contains("$$")) return; +/* if( msg.from()->protocol()->pluginId()!="MSNProtocol" ) + return;*/ // this searches for $$formula$$ TQRegExp rg("^\\s*\\$\\$([^$]+)\\$\\$\\s*$"); @@ -223,6 +226,7 @@ TQString LatexPlugin::handleLatex(const TQString &latexFormula) TQString argumentRes = "-r %1x%2"; TQString argumentOut = "-o %1"; + //TQString argumentFormat = "-fgif"; //we uses gif format because MSN only handle gif int hDPI, vDPI; hDPI = LatexConfig::self()->horizontalDPI(); vDPI = LatexConfig::self()->verticalDPI(); diff --git a/kopete/plugins/netmeeting/CMakeLists.txt b/kopete/plugins/netmeeting/CMakeLists.txt new file mode 100644 index 00000000..dfda84e3 --- /dev/null +++ b/kopete/plugins/netmeeting/CMakeLists.txt @@ -0,0 +1,56 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +if( NOT BUILD_KOPETE_PROTOCOL_MSN ) + tde_message_fatal( "netmeeting plugin needs msn protocol.\n Add -DBUILD_KOPETE_PROTOCOL_MSN=ON to cmake flags." ) +endif( ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${CMAKE_SOURCE_DIR}/kopete/protocols/msn + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_netmeeting.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES kopete_netmeeting_config.desktop DESTINATION ${SERVICES_INSTALL_DIR}/tdeconfiguredialog ) +install( FILES netmeetingchatui.rc DESTINATION ${DATA_INSTALL_DIR}/kopete_netmeeting ) + + +##### kopete_netmeeting (module) ################ + +tde_add_kpart( kopete_netmeeting AUTOMOC + SOURCES + netmeetingplugin.cpp netmeetinginvitation.cpp + netmeetingguiclient.cpp + LINK kopete_msn_shared-shared kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kcm_kopete_netmeeting (module) ############ + +tde_add_kpart( kcm_kopete_netmeeting AUTOMOC + SOURCES + netmeetingprefs_ui.ui netmeetingpreferences.cpp + LINK tdeutils-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/plugins/netmeeting/Makefile.am b/kopete/plugins/netmeeting/Makefile.am new file mode 100644 index 00000000..54b08a5b --- /dev/null +++ b/kopete/plugins/netmeeting/Makefile.am @@ -0,0 +1,23 @@ +METASOURCES = AUTO + +AM_CPPFLAGS = $(KOPETE_INCLUDES) -I$(top_srcdir)/kopete/protocols/msn $(all_includes) + +kde_module_LTLIBRARIES = kopete_netmeeting.la kcm_kopete_netmeeting.la + +kopete_netmeeting_la_SOURCES = netmeetingplugin.cpp netmeetinginvitation.cpp netmeetingguiclient.cpp +kopete_netmeeting_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries) $(LIB_QT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor +kopete_netmeeting_la_LIBADD = $(top_builddir)/kopete/libkopete/libkopete.la $(top_builddir)/kopete/protocols/msn/libkopete_msn_shared.la + +service_DATA = kopete_netmeeting.desktop +servicedir = $(kde_servicesdir) + +mydatadir = $(kde_datadir)/kopete_netmeeting +mydata_DATA = netmeetingchatui.rc + +kcm_kopete_netmeeting_la_SOURCES = netmeetingprefs_ui.ui netmeetingpreferences.cpp +kcm_kopete_netmeeting_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries) $(LIB_QT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor +kcm_kopete_netmeeting_la_LIBADD = $(LIB_KOPETECOMPAT) $(LIB_TDEUTILS) + + +kcm_DATA = kopete_netmeeting_config.desktop +kcmdir = $(kde_servicesdir)/tdeconfiguredialog diff --git a/kopete/plugins/netmeeting/kopete_netmeeting.desktop b/kopete/plugins/netmeeting/kopete_netmeeting.desktop new file mode 100644 index 00000000..5c697fbe --- /dev/null +++ b/kopete/plugins/netmeeting/kopete_netmeeting.desktop @@ -0,0 +1,81 @@ +[Desktop Entry] +Type=Service +X-Kopete-Version=1000900 +Icon=phone +ServiceTypes=Kopete/Plugin +X-TDE-Library=kopete_netmeeting +X-TDE-PluginInfo-Author=Olivier Goffart +X-TDE-PluginInfo-Email=ogoffart@tiscalinet.be +X-TDE-PluginInfo-Name=kopete_netmeeting +X-TDE-PluginInfo-Version=0.8.0 +X-TDE-PluginInfo-Website=http://kopete.kde.org +X-TDE-PluginInfo-Category=Plugins +X-TDE-PluginInfo-Depends=kopete_msn +X-TDE-PluginInfo-License=GPL +X-TDE-PluginInfo-EnabledByDefault=false +Name=Netmeeting +Name[ar]=الاجتماع على الشبكة +Name[bg]=Видео чат +Name[bn]=নেট মিটিং +Name[da]=Netmøde +Name[eo]=Reta renkontiĝo +Name[fa]=نت میتینگ +Name[fr]=Vidéo-conférence +Name[hi]=नेटमीटिंग +Name[ja]=ネットミーティング +Name[km]=ប្រជុំលើបណ្ដាញ +Name[lt]=Bendravimas tinkle +Name[nds]=Nettmööt +Name[ne]=नेट मिटिङ +Name[nl]=NetMeeting +Name[pa]=ਨੈੱਟ-ਮੀਟਿੰਗ +Name[sv]=Nätverksmöte +Name[ta]=இணைய சந்திப்பு +Name[tg]=Вохӯриҳои шабакавӣ +Comment=Voice and Video with MSN Messenger +Comment[be]=Гук і відэа праз MSN Messenger +Comment[bg]=Приставка за разговор с глас и видео с MSN Messenger +Comment[bn]=এমএসএন বার্তাবাহকের সঙ্গে স্বর এবং ভিডিও +Comment[bs]=Glas i video sa MSN Messengerom +Comment[ca]=Veu i vídeo amb MSN Messenger +Comment[cs]=Hlas a video pomocí MSN Messenger +Comment[da]=Stemme og video med MSN Messenger +Comment[de]=Sprache und Video mit dem MSN-Messenger verwenden +Comment[el]=Βίντεο και εικόνα με το MSN Messenger +Comment[es]=Voz y vídeo con MSN Messenger +Comment[et]=Audio ja video kasutamine MSN Messengeriga +Comment[eu]=Ahotsa eta bideoa MSN Messenger-ekin +Comment[fa]=ویدیو و صدا با پیامرسان اماسان +Comment[fi]=Ääni ja videokuva MSN Messengerin kanssa +Comment[fr]=Voix et vidéo avec MSN Messenger +Comment[gl]=Voz e video con MSN Messenger +Comment[he]=חוזי ושמע עם MSN Messenger +Comment[hu]=Hang és videó az MSN Messengerrel +Comment[is]=Hljóð og vídeó með MSN Messenger +Comment[it]=Voce e video con MSN Messenger +Comment[ja]=MSN メッセンジャーとボイス/ビデオチャット +Comment[ka]=ხმა და ვიდეო MSN მესინჯერთან +Comment[kk]=MSN Messenger дыбыс пен бейнемен +Comment[km]=សំឡេង និងវីដេអូដោយប្រើកម្មវិធីផ្ញើសារ MSN +Comment[lt]=Bendravimas balsu ir vaizdu per MSN Messenger +Comment[mk]=Глас и видео со Гласникот на MSN +Comment[nb]=Lyd og bilde med MSN Messenger +Comment[nds]=Spraak un Video mit dat MSN-Kortnarichtenprogramm +Comment[ne]=एमएसएन मेसेन्जरसँग आवाज र भिडियो +Comment[nl]=Beeld en geluid met MSN Messenger +Comment[nn]=Lyd og bilete med MSN Messenger +Comment[pl]=Głos i wideo za pomocą MSN Messenger +Comment[pt]=Voz e Vídeo com o MSN Messenger +Comment[pt_BR]=Voz e Vídeo com o MSN Messenger +Comment[ru]=Аудио и видео с MSN Messenger +Comment[sk]=Hlas a video pomocou MSN Messenger +Comment[sl]=Glas in video z MSN Messenger +Comment[sr]=Глас и видео са MSN Messenger-ом +Comment[sr@Latn]=Glas i video sa MSN Messenger-om +Comment[sv]=Ljud och video med MSN Messenger +Comment[ta]=எம்எஸ்என் செய்தியில் குரல் மற்றும் படக்காட்சி +Comment[tr]=MSN Messenger ile Video ve Ses +Comment[uk]=Аудіо і відео з MSN Messenger +Comment[zh_CN]=与 MSN Messenger 一起使用影音 +Comment[zh_HK]=和 MSN Messenger 一起使用語音和視像 +Comment[zh_TW]=MSN Messenger 影像與聲音 diff --git a/kopete/plugins/netmeeting/kopete_netmeeting_config.desktop b/kopete/plugins/netmeeting/kopete_netmeeting_config.desktop new file mode 100644 index 00000000..0d266c03 --- /dev/null +++ b/kopete/plugins/netmeeting/kopete_netmeeting_config.desktop @@ -0,0 +1,77 @@ +[Desktop Entry] +Icon=highlight +Type=Service +ServiceTypes=TDECModule + +X-TDE-ModuleType=Library +X-TDE-Library=kopete_netmeeting +X-TDE-FactoryName=NetmeetingConfigFactory +X-TDE-ParentApp=kopete_netmeeting +X-TDE-ParentComponents=kopete_netmeeting + +Name=Netmeeting +Name[ar]=الاجتماع على الشبكة +Name[bg]=Видео чат +Name[bn]=নেট মিটিং +Name[da]=Netmøde +Name[eo]=Reta renkontiĝo +Name[fa]=نت میتینگ +Name[fr]=Vidéo-conférence +Name[hi]=नेटमीटिंग +Name[ja]=ネットミーティング +Name[km]=ប្រជុំលើបណ្ដាញ +Name[lt]=Bendravimas tinkle +Name[nds]=Nettmööt +Name[ne]=नेट मिटिङ +Name[nl]=NetMeeting +Name[pa]=ਨੈੱਟ-ਮੀਟਿੰਗ +Name[sv]=Nätverksmöte +Name[ta]=இணைய சந்திப்பு +Name[tg]=Вохӯриҳои шабакавӣ +Comment=Voice and Video with MSN Messenger +Comment[be]=Гук і відэа праз MSN Messenger +Comment[bg]=Приставка за разговор с глас и видео с MSN Messenger +Comment[bn]=এমএসএন বার্তাবাহকের সঙ্গে স্বর এবং ভিডিও +Comment[bs]=Glas i video sa MSN Messengerom +Comment[ca]=Veu i vídeo amb MSN Messenger +Comment[cs]=Hlas a video pomocí MSN Messenger +Comment[da]=Stemme og video med MSN Messenger +Comment[de]=Sprache und Video mit dem MSN-Messenger verwenden +Comment[el]=Βίντεο και εικόνα με το MSN Messenger +Comment[es]=Voz y vídeo con MSN Messenger +Comment[et]=Audio ja video kasutamine MSN Messengeriga +Comment[eu]=Ahotsa eta bideoa MSN Messenger-ekin +Comment[fa]=ویدیو و صدا با پیامرسان اماسان +Comment[fi]=Ääni ja videokuva MSN Messengerin kanssa +Comment[fr]=Voix et vidéo avec MSN Messenger +Comment[gl]=Voz e video con MSN Messenger +Comment[he]=חוזי ושמע עם MSN Messenger +Comment[hu]=Hang és videó az MSN Messengerrel +Comment[is]=Hljóð og vídeó með MSN Messenger +Comment[it]=Voce e video con MSN Messenger +Comment[ja]=MSN メッセンジャーとボイス/ビデオチャット +Comment[ka]=ხმა და ვიდეო MSN მესინჯერთან +Comment[kk]=MSN Messenger дыбыс пен бейнемен +Comment[km]=សំឡេង និងវីដេអូដោយប្រើកម្មវិធីផ្ញើសារ MSN +Comment[lt]=Bendravimas balsu ir vaizdu per MSN Messenger +Comment[mk]=Глас и видео со Гласникот на MSN +Comment[nb]=Lyd og bilde med MSN Messenger +Comment[nds]=Spraak un Video mit dat MSN-Kortnarichtenprogramm +Comment[ne]=एमएसएन मेसेन्जरसँग आवाज र भिडियो +Comment[nl]=Beeld en geluid met MSN Messenger +Comment[nn]=Lyd og bilete med MSN Messenger +Comment[pl]=Głos i wideo za pomocą MSN Messenger +Comment[pt]=Voz e Vídeo com o MSN Messenger +Comment[pt_BR]=Voz e Vídeo com o MSN Messenger +Comment[ru]=Аудио и видео с MSN Messenger +Comment[sk]=Hlas a video pomocou MSN Messenger +Comment[sl]=Glas in video z MSN Messenger +Comment[sr]=Глас и видео са MSN Messenger-ом +Comment[sr@Latn]=Glas i video sa MSN Messenger-om +Comment[sv]=Ljud och video med MSN Messenger +Comment[ta]=எம்எஸ்என் செய்தியில் குரல் மற்றும் படக்காட்சி +Comment[tr]=MSN Messenger ile Video ve Ses +Comment[uk]=Аудіо і відео з MSN Messenger +Comment[zh_CN]=与 MSN Messenger 一起使用影音 +Comment[zh_HK]=和 MSN Messenger 一起使用語音和視像 +Comment[zh_TW]=MSN Messenger 影像與聲音 diff --git a/kopete/plugins/netmeeting/netmeetingchatui.rc b/kopete/plugins/netmeeting/netmeetingchatui.rc new file mode 100644 index 00000000..b0d139ae --- /dev/null +++ b/kopete/plugins/netmeeting/netmeetingchatui.rc @@ -0,0 +1,9 @@ +<!DOCTYPE kpartgui> +<kpartgui version="1" name="kopete_msn_netmeeting"> + <MenuBar> + <Menu name="tools"> + <text>&Tools</text> + <Action name="netmeeting" /> + </Menu> + </MenuBar> +</kpartgui> diff --git a/kopete/plugins/netmeeting/netmeetingguiclient.cpp b/kopete/plugins/netmeeting/netmeetingguiclient.cpp new file mode 100644 index 00000000..bf457b90 --- /dev/null +++ b/kopete/plugins/netmeeting/netmeetingguiclient.cpp @@ -0,0 +1,61 @@ +/* + netmeetingguiclient.cpp + + Kopete NetMeeting plugin + + Copyright (c) 2003-2004 by Olivier Goffart <ogoffart @ kde.org> + + Kopete (c) 2003-2004 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include <tqvariant.h> + +#include <kdebug.h> +#include <tdeaction.h> +#include <tdelocale.h> +#include <kgenericfactory.h> + +#include "msnchatsession.h" +#include "msncontact.h" + +#include "netmeetingguiclient.h" +#include "netmeetinginvitation.h" + +class NetMeetingPlugin; + +NetMeetingGUIClient::NetMeetingGUIClient( MSNChatSession *parent, const char *name ) +: TQObject( parent, name ) , KXMLGUIClient(parent) +{ + setInstance(KGenericFactory<NetMeetingPlugin>::instance()); + m_manager=parent; + + new TDEAction( i18n( "Invite to Use NetMeeting" ), 0, this, TQT_SLOT( slotStartInvitation() ), actionCollection() , "netmeeting" ) ; + + setXMLFile("netmeetingchatui.rc"); +} + +NetMeetingGUIClient::~NetMeetingGUIClient() +{ + +} + +void NetMeetingGUIClient::slotStartInvitation() +{ + TQPtrList<Kopete::Contact> c=m_manager->members(); + NetMeetingInvitation *i=new NetMeetingInvitation(false, static_cast<MSNContact*>(c.first()),m_manager); + m_manager->initInvitation(i); +} + +#include "netmeetingguiclient.moc" + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/plugins/netmeeting/netmeetingguiclient.h b/kopete/plugins/netmeeting/netmeetingguiclient.h new file mode 100644 index 00000000..882a7759 --- /dev/null +++ b/kopete/plugins/netmeeting/netmeetingguiclient.h @@ -0,0 +1,61 @@ +/* + netmeetingguiclient.h + + Kopete NetMeeting Plugin + + Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org> + + Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef TRANSLATORGUICLIENT_H +#define TRANSLATORGUICLIENT_H + +#include <tqobject.h> +#include <kxmlguiclient.h> + +namespace Kopete { class ChatSession; } +class MSNChatSession; +class NetMeetingPlugin; + +/** + * @author Olivier Goffart <ogoffart @ kde.org> + */ + +class NetMeetingGUIClient : public TQObject , public KXMLGUIClient +{ + Q_OBJECT + + +public: + NetMeetingGUIClient( MSNChatSession *parent, const char *name=0L); + ~NetMeetingGUIClient(); + +private slots: + void slotStartInvitation(); + +private: + MSNChatSession *m_manager; + NetMeetingPlugin *m_plugin; +}; + +#endif + +/* + * Local variables: + * c-indentation-style: k&r + * c-basic-offset: 8 + * indent-tabs-mode: t + * End: + */ +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/plugins/netmeeting/netmeetinginvitation.cpp b/kopete/plugins/netmeeting/netmeetinginvitation.cpp new file mode 100644 index 00000000..b42f9e93 --- /dev/null +++ b/kopete/plugins/netmeeting/netmeetinginvitation.cpp @@ -0,0 +1,183 @@ +/* + msninvitation.cpp + + Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include "netmeetinginvitation.h" + +#include "kopeteuiglobal.h" + +#include "msnchatsession.h" +#include "msnswitchboardsocket.h" +#include "msncontact.h" +#include "kopetemetacontact.h" + +#include <tdelocale.h> +#include <tdemessagebox.h> +#include <kdebug.h> +#include <tdeconfig.h> +#include <tdeglobal.h> + + +#include <tqtimer.h> +#include <kprocess.h> + +NetMeetingInvitation::NetMeetingInvitation(bool incoming, MSNContact *c, TQObject *parent) + : TQObject(parent) , MSNInvitation( incoming, NetMeetingInvitation::applicationID() , i18n("NetMeeting") ) +{ + m_contact=c; + oki=false; +} + + +NetMeetingInvitation::~NetMeetingInvitation() +{ +} + + +TQString NetMeetingInvitation::invitationHead() +{ + TQTimer::singleShot( 10*60000, this, TQT_SLOT( slotTimeout() ) ); //send TIMEOUT in 10 minute if the invitation has not been accepted/refused + return TQString( MSNInvitation::invitationHead()+ + "Session-Protocol: SM1\r\n" + "Session-ID: {6672F94C-45BF-11D7-B4AE-00010A1008DF}\r\n" //FIXME i don't know what is the session id + "\r\n").utf8(); +} + +void NetMeetingInvitation::parseInvitation(const TQString& msg) +{ + TQRegExp rx("Invitation-Command: ([A-Z]*)"); + rx.search(msg); + TQString command=rx.cap(1); + if( msg.contains("Invitation-Command: INVITE") ) + { + MSNInvitation::parseInvitation(msg); //for the cookie + + unsigned int result = KMessageBox::questionYesNo( Kopete::UI::Global::mainWidget(), + i18n("%1 wants to start a chat with NetMeeting; do you want to accept it? " ).arg(m_contact->metaContact()->displayName()), + i18n("MSN Plugin") , i18n("Accept"),i18n("Refuse")); + + MSNChatSession* manager=dynamic_cast<MSNChatSession*>(m_contact->manager()); + + if(manager && manager->service()) + { + if(result==3) // Yes == 3 + { + TQCString message=TQString( + "MIME-Version: 1.0\r\n" + "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n" + "\r\n" + "Invitation-Command: ACCEPT\r\n" + "Invitation-Cookie: " + TQString::number(cookie()) + "\r\n" + "Session-ID: {6672F94C-45BF-11D7-B4AE-00010A1008DF}\r\n" //FIXME + "Session-Protocol: SM1\r\n" + "Launch-Application: TRUE\r\n" + "Request-Data: IP-Address:\r\n" + "IP-Address: " + manager->service()->getLocalIP()+ "\r\n" + "\r\n" ).utf8(); + + + manager->service()->sendCommand( "MSG" , "N", true, message ); + oki=false; + TQTimer::singleShot( 10* 60000, this, TQT_SLOT( slotTimeout() ) ); //TIMOUT afte 10 min + } + else //No + { + manager->service()->sendCommand( "MSG" , "N", true, rejectMessage() ); + emit done(this); + } + } + } + else if( msg.contains("Invitation-Command: ACCEPT") ) + { + if( ! incoming() ) + { + MSNChatSession* manager=dynamic_cast<MSNChatSession*>(m_contact->manager()); + if(manager && manager->service()) + { + TQCString message=TQString( + "MIME-Version: 1.0\r\n" + "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n" + "\r\n" + "Invitation-Command: ACCEPT\r\n" + "Invitation-Cookie: " + TQString::number(cookie()) + "\r\n" + "Session-ID: {6672F94C-45BF-11D7-B4AE-00010A1008DF}\r\n" //FIXME: what is session id? + "Session-Protocol: SM1\r\n" + "Launch-Application: TRUE\r\n" + "Request-Data: IP-Address:\r\n" + "IP-Address: " + manager->service()->getLocalIP() + "\r\n" + "\r\n" ).utf8(); + manager->service()->sendCommand( "MSG" , "N", true, message ); + } + rx=TQRegExp("IP-Address: ([0-9\\:\\.]*)"); + rx.search(msg); + TQString ip_address = rx.cap(1); + startMeeting(ip_address); + kdDebug() << k_funcinfo << ip_address << endl; + } + else + { + rx=TQRegExp("IP-Address: ([0-9\\:\\.]*)"); + rx.search(msg); + TQString ip_address = rx.cap(1); + + startMeeting(ip_address); + } + } + else //CANCEL + { + emit done(this); + } +} + +void NetMeetingInvitation::slotTimeout() +{ + if(oki) + return; + + MSNChatSession* manager=dynamic_cast<MSNChatSession*>(m_contact->manager()); + + if(manager && manager->service()) + { + manager->service()->sendCommand( "MSG" , "N", true, rejectMessage("TIMEOUT") ); + } + emit done(this); + +} + + +void NetMeetingInvitation::startMeeting(const TQString & ip_address) +{ + //TODO: use TDEProcess + + TDEConfig *config=TDEGlobal::config(); + config->setGroup("Netmeeting Plugin"); + TQString app=config->readEntry("NetmeetingApplication","ekiga -c callto://%1").arg(ip_address); + + kdDebug() << k_funcinfo << app << endl ; + + TQStringList args=TQStringList::split(" ", app); + + TDEProcess p; + for(TQStringList::Iterator it=args.begin() ; it != args.end() ; ++it) + { + p << *it; + } + p.start(); +} + +#include "netmeetinginvitation.moc" + + + + diff --git a/kopete/plugins/netmeeting/netmeetinginvitation.h b/kopete/plugins/netmeeting/netmeetinginvitation.h new file mode 100644 index 00000000..8bf88e96 --- /dev/null +++ b/kopete/plugins/netmeeting/netmeetinginvitation.h @@ -0,0 +1,57 @@ +/* + netmeetinginvitation.cpp + + Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org> + + Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ +#ifndef MSNVOICEINVITATION_H +#define MSNVOICEINVITATION_H + +#include <tqobject.h> +#include "msninvitation.h" + +class MSNContact; + +/** + *@author Olivier Goffart + */ +class NetMeetingInvitation : public TQObject , public MSNInvitation +{ +Q_OBJECT + +public: + NetMeetingInvitation(bool incoming ,MSNContact*, TQObject *parent = 0); + ~NetMeetingInvitation(); + + static TQString applicationID() { return "44BBA842-CC51-11CF-AAFA-00AA00B6015C"; } + TQString invitationHead(); + + virtual void parseInvitation(const TQString& invitation); + + virtual TQObject* object() { return this; } + +signals: + void done( MSNInvitation * ); + +private slots: + void slotTimeout(); + +private: + MSNContact *m_contact; + bool oki; + void startMeeting(const TQString & ip_address); + +}; + + +#endif diff --git a/kopete/plugins/netmeeting/netmeetingplugin.cpp b/kopete/plugins/netmeeting/netmeetingplugin.cpp new file mode 100644 index 00000000..17dfcc0b --- /dev/null +++ b/kopete/plugins/netmeeting/netmeetingplugin.cpp @@ -0,0 +1,91 @@ +/* + netmeetingplugin.cpp + + Copyright (c) 2003-2004 by Olivier Goffart <ogoffart @ kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include "netmeetingplugin.h" + +#include <kdebug.h> +#include <kgenericfactory.h> +#include <tdeaction.h> +#include <tdeversion.h> +#include <tdeaboutdata.h> + +#include "kopetepluginmanager.h" +#include "kopetechatsessionmanager.h" + +#include "msnchatsession.h" +#include "msnprotocol.h" +#include "msncontact.h" + +#include "netmeetinginvitation.h" +#include "netmeetingguiclient.h" + + +static const TDEAboutData aboutdata("kopete_netmeeting", I18N_NOOP("NetMeeting") , "1.0" ); +K_EXPORT_COMPONENT_FACTORY( kopete_netmeeting, KGenericFactory<NetMeetingPlugin>( &aboutdata ) ) + +NetMeetingPlugin::NetMeetingPlugin( TQObject *parent, const char *name, const TQStringList &/*args*/ ) +: Kopete::Plugin( TDEGlobal::instance(), parent, name ) +{ + if(MSNProtocol::protocol()) + slotPluginLoaded(MSNProtocol::protocol()); + else + connect(Kopete::PluginManager::self() , TQT_SIGNAL(pluginLoaded(Kopete::Plugin*) ), this, TQT_SLOT(slotPluginLoaded(Kopete::Plugin*))); + + + connect( Kopete::ChatSessionManager::self(), TQT_SIGNAL( chatSessionCreated( Kopete::ChatSession * )) , TQT_SLOT( slotNewKMM( Kopete::ChatSession * ) ) ); + //Add GUI action to all already existing kmm (if the plugin is launched when kopete already rining) + TQValueList<Kopete::ChatSession*> sessions = Kopete::ChatSessionManager::self()->sessions(); + for (TQValueListIterator<Kopete::ChatSession*> it= sessions.begin(); it!=sessions.end() ; ++it) + { + slotNewKMM(*it); + } +} + +NetMeetingPlugin::~NetMeetingPlugin() +{ + +} + +void NetMeetingPlugin::slotPluginLoaded(Kopete::Plugin *p) +{ + if(p->pluginId()=="MSNProtocol") + { + connect( p , TQT_SIGNAL(invitation(MSNInvitation*& , const TQString & , long unsigned int , MSNChatSession* , MSNContact* )) , + this, TQT_SLOT( slotInvitation(MSNInvitation*& , const TQString & , long unsigned int , MSNChatSession* , MSNContact* ))); + } +} + +void NetMeetingPlugin::slotNewKMM(Kopete::ChatSession *KMM) +{ + MSNChatSession *msnMM=dynamic_cast<MSNChatSession*>(KMM); + if(msnMM) + { + connect(this , TQT_SIGNAL( destroyed(TQObject*)) , + new NetMeetingGUIClient(msnMM) + , TQT_SLOT(deleteLater())); + } +} + + +void NetMeetingPlugin::slotInvitation(MSNInvitation*& invitation, const TQString &bodyMSG , long unsigned int /*cookie*/ , MSNChatSession* msnMM , MSNContact* c ) +{ + if(!invitation && bodyMSG.contains(NetMeetingInvitation::applicationID())) + { + invitation=new NetMeetingInvitation(true,c,msnMM); + invitation->parseInvitation(bodyMSG); + } +} + +#include "netmeetingplugin.moc" diff --git a/kopete/plugins/netmeeting/netmeetingplugin.h b/kopete/plugins/netmeeting/netmeetingplugin.h new file mode 100644 index 00000000..0456dadd --- /dev/null +++ b/kopete/plugins/netmeeting/netmeetingplugin.h @@ -0,0 +1,47 @@ +/* + netmeetingplugin.h + + Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + + + +#ifndef NetMeetingPLUGIN_H +#define NetMeetingPLUGIN_H + +#include "kopeteplugin.h" + +namespace Kopete { class ChatSession; } +class MSNChatSession; +class MSNContact; +class MSNInvitation; + + +class NetMeetingPlugin : public Kopete::Plugin +{ + Q_OBJECT + + +public: + NetMeetingPlugin( TQObject *parent, const char *name, const TQStringList &args ); + ~NetMeetingPlugin(); + +private slots: + void slotNewKMM(Kopete::ChatSession *); + void slotPluginLoaded(Kopete::Plugin*); + void slotInvitation(MSNInvitation*& invitation, const TQString &bodyMSG , long unsigned int cookie , MSNChatSession* msnMM , MSNContact* c ); + + +}; + +#endif + diff --git a/kopete/plugins/netmeeting/netmeetingpreferences.cpp b/kopete/plugins/netmeeting/netmeetingpreferences.cpp new file mode 100644 index 00000000..309d4d15 --- /dev/null +++ b/kopete/plugins/netmeeting/netmeetingpreferences.cpp @@ -0,0 +1,81 @@ +/*************************************************************************** + Netmeetingpreferences.cpp - description + ------------------- + copyright : (C) 2004 by Olivier Goffart + email : ogoffart @ kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include <tqlayout.h> +#include <tqcheckbox.h> + +#include <kcombobox.h> +#include <klineedit.h> +#include <tdeparts/componentfactory.h> +#include <tdelocale.h> +#include <tdeconfig.h> +#include <tdeglobal.h> +#include <kcombobox.h> +#include <tdelistview.h> +#include <kgenericfactory.h> +#include <kcolorbutton.h> +#include <kinputdialog.h> +#include <kurlrequester.h> +#include <kregexpeditorinterface.h> +#include <kdebug.h> + +#include "netmeetingplugin.h" +#include "netmeetingprefs_ui.h" +#include "netmeetingpreferences.h" + +typedef KGenericFactory<NetmeetingPreferences> NetmeetingPreferencesFactory; +K_EXPORT_COMPONENT_FACTORY( kcm_kopete_netmeeting, NetmeetingPreferencesFactory( "kcm_kopete_netmeeting" ) ) + +NetmeetingPreferences::NetmeetingPreferences(TQWidget *parent, const char* /*name*/, const TQStringList &args) + : TDECModule(NetmeetingPreferencesFactory::instance(), parent, args) +{ + ( new TQVBoxLayout( this ) )->setAutoAdd( true ); + preferencesDialog = new NetmeetingPrefsUI(this); + + connect(preferencesDialog->m_app , TQT_SIGNAL(textChanged(const TQString &)) , this , TQT_SLOT(slotChanged())); + + load(); +} + +NetmeetingPreferences::~NetmeetingPreferences() +{ +} + +void NetmeetingPreferences::load() +{ + TDEConfig *config=TDEGlobal::config(); + config->setGroup("Netmeeting Plugin"); + preferencesDialog->m_app->setCurrentText(config->readEntry("NetmeetingApplication","ekiga -c callto://%1")); + emit TDECModule::changed(false); +} + +void NetmeetingPreferences::save() +{ + TDEConfig *config=TDEGlobal::config(); + config->setGroup("Netmeeting Plugin"); + config->writeEntry("NetmeetingApplication",preferencesDialog->m_app->currentText()); + emit TDECModule::changed(false); +} + + +void NetmeetingPreferences::slotChanged() +{ + emit TDECModule::changed(true); +} + +#include "netmeetingpreferences.moc" + +// vim: set noet ts=4 sts=4 sw=4: diff --git a/kopete/plugins/netmeeting/netmeetingpreferences.h b/kopete/plugins/netmeeting/netmeetingpreferences.h new file mode 100644 index 00000000..958860cf --- /dev/null +++ b/kopete/plugins/netmeeting/netmeetingpreferences.h @@ -0,0 +1,47 @@ +/*************************************************************************** + netmeetingpreferences.h - description + ------------------- + copyright : (C) 2004 by Olivier Goffart + email : ogoffart @ kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef NetmeetingPREFERENCES_H +#define NetmeetingPREFERENCES_H + +#include <tdecmodule.h> +#include <tqstring.h> + +class NetmeetingPrefsUI; + +/** + *@author Olivier Goffart + */ + +class NetmeetingPreferences : public TDECModule { + Q_OBJECT + +public: + + NetmeetingPreferences(TQWidget *parent = 0, const char* name = 0, const TQStringList &args = TQStringList()); + ~NetmeetingPreferences(); + + virtual void save(); + virtual void load(); + +private: + NetmeetingPrefsUI *preferencesDialog; + +private slots: + void slotChanged(); +}; + +#endif diff --git a/kopete/plugins/netmeeting/netmeetingprefs_ui.ui b/kopete/plugins/netmeeting/netmeetingprefs_ui.ui new file mode 100644 index 00000000..0c0838fb --- /dev/null +++ b/kopete/plugins/netmeeting/netmeetingprefs_ui.ui @@ -0,0 +1,148 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>NetmeetingPrefsUI</class> +<author>Olivier Goffart</author> +<widget class="TQWidget"> + <property name="name"> + <cstring>Form1</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>424</width> + <height>297</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>The NetMeeting Plugin allows you to start a video or voice chat with your MSN Messenger contacts. + +This is not the same as webcam chat you can find in the newer Windows Messenger®, but uses the older NetMeeting chat you can find in old versions.</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + <widget class="Line"> + <property name="name"> + <cstring>line1</cstring> + </property> + <property name="frameShape"> + <enum>HLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + </widget> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout1</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Application to launch:</string> + </property> + </widget> + <widget class="KComboBox"> + <item> + <property name="text"> + <string>ekiga -c callto://%1</string> + </property> + </item> + <item> + <property name="text"> + <string>konference callto://%1</string> + </property> + </item> + <property name="name"> + <cstring>m_app</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="editable"> + <bool>true</bool> + </property> + <property name="autoCompletion"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + <widget class="TQLabel"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string><b>%1</b> will be replaced by the ip to call</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>21</width> + <height>60</height> + </size> + </property> + </spacer> + <widget class="KActiveLabel"> + <property name="name"> + <cstring>kActiveLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>You can download Konference here: <a href="http://www.kde-apps.org/content/show.php?content=10395">http://www.kde-apps.org/content/show.php?content=10395</a></string> + </property> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kcombobox.h</includehint> + <includehint>klineedit.h</includehint> + <includehint>kactivelabel.h</includehint> +</includehints> +</UI> diff --git a/kopete/plugins/nowlistening/nowlisteningplugin.cpp b/kopete/plugins/nowlistening/nowlisteningplugin.cpp index 31284446..6fcc21cd 100644 --- a/kopete/plugins/nowlistening/nowlisteningplugin.cpp +++ b/kopete/plugins/nowlistening/nowlisteningplugin.cpp @@ -258,36 +258,84 @@ void NowListeningPlugin::slotAdvertCurrentMusic() TQPtrList<Kopete::Account> accountsList = Kopete::AccountManager::self()->accounts(); for( Kopete::Account* a = accountsList.first(); a; a = accountsList.next() ) { - if( NowListeningConfig::self()->appendStatusAdvertising() ) - { - // Check for the now listening message in parenthesis, - // include the header to not override other messages in parenthesis. - TQRegExp statusSong( TQString(" \\(%1.*\\)$").arg( NowListeningConfig::header()) ); - - // HACK: Don't keep appending the now listened song. Replace it in the status message. - advert = a->myself()->property( Kopete::Global::Properties::self()->awayMessage() ).value().toString(); - // Remove the braces when they are no listened song. - TQString mediaAdvert = mediaPlayerAdvert(false); - if(!mediaAdvert.isEmpty()) - { - if(statusSong.search(advert) != -1) - { - advert = advert.replace(statusSong, TQString(" (%1)").arg(mediaPlayerAdvert(false)) ); - } - else - { - advert += TQString(" (%1)").arg( mediaPlayerAdvert(false) ); - } - } - else - { - advert = advert.replace(statusSong, ""); - } - } - else - { - advert = mediaPlayerAdvert(false); // newTrackPlaying has done the update. - } + /* + NOTE: + MSN status message(personal message) use a special tag to advert the current music playing. + So, we don't send the all formatted string, send a special string seperated by ";". + + Also, do not use MSN hack in appending mode. + */ + if( a->protocol()->pluginId() == "MSNProtocol" && !NowListeningConfig::self()->appendStatusAdvertising() ) + { + TQString track, artist, album, mediaList; + bool isPlaying=false; + + if( NowListeningConfig::self()->useSpecifiedMediaPlayer() && d->m_currentMediaPlayer ) + { + if( d->m_currentMediaPlayer->playing() ) + { + track = d->m_currentMediaPlayer->track(); + artist = d->m_currentMediaPlayer->artist(); + album = d->m_currentMediaPlayer->album(); + mediaList = track + ";" + artist + ";" + album; + isPlaying = true; + } + } + else + { + for ( NLMediaPlayer* i = d->m_mediaPlayerList.first(); i; i = d->m_mediaPlayerList.next() ) + { + if( i->playing() ) + { + track = i->track(); + artist = i->artist(); + album = i->album(); + mediaList = track + ";" + artist + ";" + album; + isPlaying = true; + } + } + } + + // KDE4 TODO: Use the new status message framework, and remove this "hack". + if( isPlaying ) + { + advert = TQString("[Music]%1").arg(mediaList); + } + + } + else + { + if( NowListeningConfig::self()->appendStatusAdvertising() ) + { + // Check for the now listening message in parenthesis, + // include the header to not override other messages in parenthesis. + TQRegExp statusSong( TQString(" \\(%1.*\\)$").arg( NowListeningConfig::header()) ); + + // HACK: Don't keep appending the now listened song. Replace it in the status message. + advert = a->myself()->property( Kopete::Global::Properties::self()->awayMessage() ).value().toString(); + // Remove the braces when they are no listened song. + TQString mediaAdvert = mediaPlayerAdvert(false); + if(!mediaAdvert.isEmpty()) + { + if(statusSong.search(advert) != -1) + { + advert = advert.replace(statusSong, TQString(" (%1)").arg(mediaPlayerAdvert(false)) ); + } + else + { + advert += TQString(" (%1)").arg( mediaPlayerAdvert(false) ); + } + } + else + { + advert = advert.replace(statusSong, ""); + } + } + else + { + advert = mediaPlayerAdvert(false); // newTrackPlaying has done the update. + } + } a->setOnlineStatus(a->myself()->onlineStatus(), advert); } diff --git a/kopete/plugins/webpresence/webpresence_html.xsl b/kopete/plugins/webpresence/webpresence_html.xsl index e0e55589..f768ccf5 100644 --- a/kopete/plugins/webpresence/webpresence_html.xsl +++ b/kopete/plugins/webpresence/webpresence_html.xsl @@ -57,6 +57,9 @@ <xsl:when test=".='AIMProtocol'"> <xsl:text>AIM</xsl:text> </xsl:when> + <xsl:when test=".='MSNProtocol'"> + <xsl:text>MSN</xsl:text> + </xsl:when> <xsl:when test=".='ICQProtocol'"> <xsl:text>ICQ</xsl:text> </xsl:when> diff --git a/kopete/plugins/webpresence/webpresence_html_images.xsl b/kopete/plugins/webpresence/webpresence_html_images.xsl index bd97657b..5b707046 100644 --- a/kopete/plugins/webpresence/webpresence_html_images.xsl +++ b/kopete/plugins/webpresence/webpresence_html_images.xsl @@ -24,6 +24,9 @@ <xsl:template match="protocol"> <xsl:choose> + <xsl:when test=".='MSNProtocol'"> + <img src="{$images}/msn_protocol.png" alt="MSN" title="MSN"/> + </xsl:when> <xsl:when test=".='ICQProtocol'"> <img src="{$images}/icq_protocol.png" alt="ICQ" title="ICQ"/> </xsl:when> diff --git a/kopete/plugins/webpresence/webpresence_xhtml.xsl b/kopete/plugins/webpresence/webpresence_xhtml.xsl index acec55ee..9d749c14 100644 --- a/kopete/plugins/webpresence/webpresence_xhtml.xsl +++ b/kopete/plugins/webpresence/webpresence_xhtml.xsl @@ -56,6 +56,9 @@ <xsl:when test=".='AIMProtocol'"> <xsl:text>AIM</xsl:text> </xsl:when> + <xsl:when test=".='MSNProtocol'"> + <xsl:text>MSN</xsl:text> + </xsl:when> <xsl:when test=".='ICQProtocol'"> <xsl:text>ICQ</xsl:text> </xsl:when> diff --git a/kopete/plugins/webpresence/webpresence_xhtml_images.xsl b/kopete/plugins/webpresence/webpresence_xhtml_images.xsl index cf8d0219..8254a674 100644 --- a/kopete/plugins/webpresence/webpresence_xhtml_images.xsl +++ b/kopete/plugins/webpresence/webpresence_xhtml_images.xsl @@ -25,6 +25,9 @@ <xsl:template match="protocol"> <xsl:choose> + <xsl:when test=".='MSNProtocol'"> + <img src="{$images}/msn_protocol.png" alt="MSN" title="MSN"/> + </xsl:when> <xsl:when test=".='ICQProtocol'"> <img src="{$images}/icq_protocol.png" alt="ICQ" title="ICQ"/> </xsl:when> diff --git a/kopete/plugins/webpresence/webpresenceprefs.ui b/kopete/plugins/webpresence/webpresenceprefs.ui index 479f128e..dfe781f1 100644 --- a/kopete/plugins/webpresence/webpresenceprefs.ui +++ b/kopete/plugins/webpresence/webpresenceprefs.ui @@ -200,15 +200,16 @@ Note that some web browsers do not support XHTML. You should also make sure your <string>Repla&ce protocol text with images in (X)HTML</string> </property> <property name="toolTip" stdset="0"> - <string>Replaces the protocol names, such as IRC with images.</string> + <string>Replaces the protocol names, such as MSN and IRC with images.</string> </property> <property name="whatsThis" stdset="0"> - <string>Replaces the protocol names, such as IRC with images. + <string>Replaces the protocol names, such as MSN and IRC with images. Note that you have to manually copy the PNG files into place. The following files are used by default: +images/msn_protocol.png images/icq_protocol.png images/jabber_protocol.png images/yahoo_protocol.png diff --git a/kopete/protocols/CMakeLists.txt b/kopete/protocols/CMakeLists.txt index 9ed1a7bf..14127078 100644 --- a/kopete/protocols/CMakeLists.txt +++ b/kopete/protocols/CMakeLists.txt @@ -11,6 +11,7 @@ tde_conditional_add_subdirectory( BUILD_KOPETE_PROTOCOL_TESTBED testbed ) tde_conditional_add_subdirectory( BUILD_KOPETE_PROTOCOL_GROUPWISE groupwise ) +tde_conditional_add_subdirectory( BUILD_KOPETE_PROTOCOL_MSN msn ) tde_conditional_add_subdirectory( BUILD_KOPETE_PROTOCOL_IRC irc ) tde_conditional_add_subdirectory( BUILD_KOPETE_PROTOCOL_OSCAR oscar ) tde_conditional_add_subdirectory( BUILD_KOPETE_PROTOCOL_YAHOO yahoo ) diff --git a/kopete/protocols/Makefile.am b/kopete/protocols/Makefile.am index 86ff1356..6612e8a9 100644 --- a/kopete/protocols/Makefile.am +++ b/kopete/protocols/Makefile.am @@ -18,4 +18,4 @@ if include_smsgsm SMS=sms endif -SUBDIRS = $(TESTBED) groupwise irc oscar yahoo winpopup $(SMS) $(JABBER) $(GADU) $(MEANWHILE) +SUBDIRS = $(TESTBED) groupwise msn irc oscar yahoo winpopup $(SMS) $(JABBER) $(GADU) $(MEANWHILE) diff --git a/kopete/protocols/configure.in.in b/kopete/protocols/configure.in.in index 398ebfd0..7a96cc9a 100644 --- a/kopete/protocols/configure.in.in +++ b/kopete/protocols/configure.in.in @@ -104,13 +104,27 @@ AM_CONDITIONAL(include_testbed, test "$with_testbed" = "yes") PKG_CHECK_MODULES(GLIB, glib-2.0 gmodule-2.0, have_glib=yes, have_glib=no) if test x$have_glib = xno; then - AC_MSG_WARN([GLib 2.0 is required for Jabber Jingle. You can get it from http://www.gtk.org/]) + AC_MSG_WARN([GLib 2.0 is required for MSN webcam and Jabber Jingle. You can get it from http://www.gtk.org/]) else AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_LIBS) AC_DEFINE(HAVE_GLIB, 1, [Glib is required for oRTP code and libmimic code]) fi +if test "x$have_glib" != "xyes"; then + compile_msn_webcam=no + msn_webcam_val=0 +else + compile_msn_webcam=yes + msn_webcam_val=1 +fi + +AC_MSG_CHECKING([if MSN webcam support should be enabled]) +AC_MSG_RESULT($compile_msn_webcam) +AC_DEFINE_UNQUOTED(MSN_WEBCAM, $msn_webcam_val, [Define if MSN webcam support can be enabled]) + +AM_CONDITIONAL(include_msn_webcam, test "x$compile_msn_webcam" = "xyes") + # Check for sms protocol AC_ARG_ENABLE(smsgsm, AC_HELP_STRING([--disable-smsgsm], [disable the GSM SMS protocol]), diff --git a/kopete/protocols/jabber/jabbertransport.cpp b/kopete/protocols/jabber/jabbertransport.cpp index be962fad..d0c59e3a 100644 --- a/kopete/protocols/jabber/jabbertransport.cpp +++ b/kopete/protocols/jabber/jabbertransport.cpp @@ -54,7 +54,9 @@ JabberTransport::JabberTransport (JabberAccount * parentAccount, const XMPP::Ros #if KOPETE_IS_VERSION(0,11,51) //setCustomIcon is new in kopete 0.12 TQString cIcon; - if(gateway_type=="icq") + if(gateway_type=="msn") + cIcon="jabber_gateway_msn"; + else if(gateway_type=="icq") cIcon="jabber_gateway_icq"; else if(gateway_type=="aim") cIcon="jabber_gateway_aim"; diff --git a/kopete/protocols/jabber/jabbertransport.h b/kopete/protocols/jabber/jabbertransport.h index 21fa208d..83f5e502 100644 --- a/kopete/protocols/jabber/jabbertransport.h +++ b/kopete/protocols/jabber/jabbertransport.h @@ -47,7 +47,7 @@ public: * constructor called when the transport is created by info from server (i.e not when loading kopete) * @param parentAccount is the parent jabber account. * @param item is the roster item of the gateway - * @param gateway_type eg: "icq" only used when the account is not loaded from config file for determining the icon + * @param gateway_type eg: "msn" or "icq" only used when the account is not loaded from config file for determining the icon */ JabberTransport (JabberAccount * parentAccount, const XMPP::RosterItem &item, const TQString& gateway_type=TQString()); diff --git a/kopete/protocols/msn/CMakeLists.txt b/kopete/protocols/msn/CMakeLists.txt new file mode 100644 index 00000000..f38683d8 --- /dev/null +++ b/kopete/protocols/msn/CMakeLists.txt @@ -0,0 +1,72 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( ui ) +add_subdirectory( icons ) +add_subdirectory( config ) + +if( WITH_WEBCAM ) + add_subdirectory( webcam ) + add_definitions( -DMSN_WEBCAM ) + set( WEBCAM_LIBRARIES mimicwrapper-static kopete_videodevice-shared ${GLIB2_LIBRARIES} ) +endif( ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/ui + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/ui + ${CMAKE_CURRENT_SOURCE_DIR}/webcam + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${CMAKE_SOURCE_DIR}/kopete/libkopete/private + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES kopete_msn.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES msnchatui.rc DESTINATION ${DATA_INSTALL_DIR}/kopete_msn ) + + +##### kopete_msn (module) ####################### + +tde_add_kpart( kopete_msn AUTOMOC + SOURCES + dummy.cpp webcam.cpp + LINK kopete_msn_shared-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kopete_msn_shared (shared) ################ + +tde_add_library( kopete_msn_shared SHARED AUTOMOC + SOURCES + msnprotocol.cpp msnaccount.cpp msnaddcontactpage.cpp msncontact.cpp + msnsocket.cpp msnchatsession.cpp msndebugrawcmddlg.cpp + msnnotifysocket.cpp msnswitchboardsocket.cpp + msnfiletransfersocket.cpp msninvitation.cpp sha1.cpp + msnsecureloginhandler.cpp msnchallengehandler.cpp + dispatcher.cpp p2p.cpp messageformatter.cpp incomingtransfer.cpp + outgoingtransfer.cpp webcam.cpp + VERSION 0.0.0 + LINK + kopetemsnui-static ${WEBCAM_LIBRARIES} kopete-shared + DESTINATION ${LIB_INSTALL_DIR} +) diff --git a/kopete/protocols/msn/Changelog b/kopete/protocols/msn/Changelog new file mode 100644 index 00000000..80f52264 --- /dev/null +++ b/kopete/protocols/msn/Changelog @@ -0,0 +1,106 @@ +Have fun using this all-improved plugin and feel free to contribute patches +and other improvements to our mailing list! Although we all like to boast +about our great work, we're sure there are still bugs remaining, which is +why we don't call this release 1.0, but only 0.5. + +Nevertheless, we feel this new MSN plugin is an enormous step forward from +the last 0.4.1 release and we recommend anyone to try out this all-improved +plugin. Please read the release notes first before reporting bugs, but please +do report anything not listed there! + +Thanks for your interest in Kopete! + + October 2002, the Kopete team <kopete-devel@kde.org> + + +CHANGES IN THE MSN PLUGIN SINCE KOPETE 0.4.1 + +- Ported the plugin to the new MetaContact API, allowing a locally cached + copy of the contact list to be always available (even when offline) and + to combine your MSN contacts with other messaging systems in one entry + in the contact list. + +- Added additional online states ('be right back', 'out to lunch', 'busy', + 'invisible') and the possibility to connect directly with a particular + status (especially useful with 'invisible') + +- Fix multi-user chat now the API finally supports it properly + +- Fix a grave bug in Kopete 0.4.1 where Kopete would popup the 'new user' + dialog for every user in your block list, asking whether you want to + allow or block the user, often crashing Kopete completely + +- Fix support for Unicode messages + +- Fix the 'unhandled error 219' problem that caused Kopete to disconnect + unexpectedly for some people + +- Added possibility to talk with users who aren't in the contact list + +- Incoming filetransfers + +- As usual, several other bugfixes + +CHANGES IN THE MSN PLUGIN SINCE KOPETE 0.4 + +- Added block/unblock user + +- Don't show contacts from the allow list if they are not also in the + friend list (like deleted contacts). Small problem: there already was + a need to have a gui for manipulating blocked/allowed contacts, with + this change this is even a bit more urgent... + +- Hopefully fix a problem with an empty reverse list on a fresh MSN account. + can't test, because by the time the recompile was done the reverse list + was no longer empty... + +- Fix a problem with MSN users no longer receiving messages. Apparently + Microsoft changed the server so messages without an explicit font name are + no longer passed on. + +- Fixed UTF8 handling not really being UTF8. MSN should work fine now with + all unicode characters + +- Moved the plugin to use KGenericFactory as preparation for more KDE-style + plugin handling (as opposed to the current custom code) + +- Fixed crash when disconnecting while an earlier connect was still running + +- Made the connect code asynchronous, so connecting doesn't hang kopete + while processing + +- Fixed minor memory leak in the connect code + +CHANGES IN THE MSN PLUGIN SINCE KOPETE 0.3 + +Many things changed since 0.3. I won't mention them all, because so much of +the internal code changed that the individual commits often fix more than I +was even aware of at that time. Below are the bigger changes and fixes: + +- Ported the plugin to the new KopeteMessageManager. This move unifies the + handling of various resources like chat windows, balloons, system tray + flashing, and more. In Kopete 0.3 this was the exclusive domain of the + ICQ plugin, in this release all plugins except IRC already use the shared + code. + +- Rewrote almost all of the internal protocol handling, fixing an awful lot + of bugs during the process. The main goal was to make the code more + maintainable and extensible, but the gratuitous bug fixes are of course + much more useful for most people. The most important fix of all is a + grave bug that caused the plugin to read a fixed-size 1kb buffer in Kopete + 0.3 without checking for additional data, often causing the plugin to + seemingly 'hang'. + +- Added the ability to change the display name while connected. This can + currently only be done from the context menu. The option in the + preferences never worked, and still does not do what you'd expect it to + do. Sorry :) + +- Added much more useful debug code for developers, testers and other + interested people. It is also a lot *more* debug output, so if you're + scared of console output, better not start Kopete from it... + +- All those tiny bugfixes of which I don't even know whether they fix + regressions introduced during the development of version 0.4, or whether + they fix long-standing bugs. + diff --git a/kopete/protocols/msn/Makefile.am b/kopete/protocols/msn/Makefile.am new file mode 100644 index 00000000..5248d295 --- /dev/null +++ b/kopete/protocols/msn/Makefile.am @@ -0,0 +1,47 @@ +if include_msn_webcam +WEBCAM = webcam +WEBCAM_LIBMINICWRAPPER = webcam/libmimicwrapper.la +endif + +KDE_OPTIONS = nofinal +METASOURCES = AUTO +SUBDIRS = ui $(WEBCAM) . icons config +AM_CPPFLAGS = -Iui \ + -I$(srcdir)/webcam \ + -I$(srcdir)/ui \ + $(KOPETE_INCLUDES) \ + -I$(top_srcdir)/kopete/libkopete/private \ + $(all_includes) + +kde_module_LTLIBRARIES = kopete_msn.la +lib_LTLIBRARIES = libkopete_msn_shared.la + +CLEANFILES = dummy.cpp + +libkopete_msn_shared_la_SOURCES = msnprotocol.cpp msnaccount.cpp \ + msnaddcontactpage.cpp msncontact.cpp msnsocket.cpp msnchatsession.cpp msndebugrawcmddlg.cpp \ + msnnotifysocket.cpp msnswitchboardsocket.cpp msnfiletransfersocket.cpp msninvitation.cpp \ + sha1.cpp msnsecureloginhandler.cpp msnchallengehandler.cpp dispatcher.cpp \ + p2p.cpp messageformatter.cpp incomingtransfer.cpp outgoingtransfer.cpp \ + webcam.cpp + +libkopete_msn_shared_la_LIBADD = ./ui/libkopetemsnui.la ../../libkopete/libkopete.la $(WEBCAM_LIBMINICWRAPPER) ../../libkopete/avdevice/libkopete_videodevice.la $(LIB_TDEIO) +libkopete_msn_shared_la_LDFLAGS = -version-info 0:0:0 -no-undefined $(all_libraries) $(LIB_QT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor -lkopete_videodevice -L../../libkopete/avdevice/ + +kopete_msn_la_SOURCES = dummy.cpp webcam.cpp +kopete_msn_la_LIBADD = libkopete_msn_shared.la + +kopete_msn_la_LDFLAGS = -no-undefined -module $(KDE_PLUGIN) $(all_libraries) $(LIB_QT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor -lkopete_videodevice -L../../libkopete/avdevice/ + +dummy.cpp: $(srcdir)/Makefile.am + echo '#include "kdemacros.h"' > $@ + echo 'extern "C" KDE_EXPORT void *init_libkopete_msn_shared();' >> $@ + echo 'extern "C" KDE_EXPORT void *init_kopete_msn() { return init_libkopete_msn_shared(); }' >> $@ + +service_DATA = kopete_msn.desktop +servicedir = $(kde_servicesdir) + +mydatadir = $(kde_datadir)/kopete_msn +mydata_DATA = msnchatui.rc +noinst_HEADERS = p2p.h dispatcher.h messageformatter.h incomingtransfer.h \ + outgoingtransfer.h webcam.h diff --git a/kopete/protocols/msn/ReleaseNotes b/kopete/protocols/msn/ReleaseNotes new file mode 100644 index 00000000..1a36724a --- /dev/null +++ b/kopete/protocols/msn/ReleaseNotes @@ -0,0 +1,31 @@ +If you can bear with beta-quality code, we welcome you to try out this +highly improved plugin and use it as your primary instant messaging system! +Patches are always welcome on kopete-devel@kde.org. Trivial fixes can be +committed directly to KDE CVS if you have an account, but please coordinate +larger changes with us to avoid double work. + +Thanks for your interest in Kopete! + + October 2002, the Kopete team <kopete-devel@kde.org> + +RELEASE NOTES FOR THE MSN PLUGIN IN KOPETE 0.5 + +Most of the MSN plugin has seen an enormous improvement since the previous +release, with many bugs fixed in all aspects of the plugin, most notably +the contact list handling and group chats. See the Changelog for a more +detailed description of what was changed in this release. + +Some issues in MSN are still remaining though. We hope to fix them in the +next release, since we think they are not critical enough to delay this +release: + +- MSN allows you to have multiple groups with the same name, because + internally a group ID is used. Kopete currently uses the name as a unique + identifier, however, and will likely get a bit confused by this. If you + do experience problems, you could join both groups using another MSN + client, like the official client, Trillian or Pidgin as a workaround. + +- Kopete contacts can be at Top-Level and in no groups. MSN doesn't + support this freature. The kopete's contact list can differe from server + if you have top-level contact + diff --git a/kopete/protocols/msn/TODO b/kopete/protocols/msn/TODO new file mode 100644 index 00000000..be73e830 --- /dev/null +++ b/kopete/protocols/msn/TODO @@ -0,0 +1,5 @@ +Some things to think about as of 2003/04/15 +(according to Gof) +Adding 'search for user' +Adding MSN chatroom protocol +Add MSN alerts diff --git a/kopete/protocols/msn/config/CMakeLists.txt b/kopete/protocols/msn/config/CMakeLists.txt new file mode 100644 index 00000000..d9fd401b --- /dev/null +++ b/kopete/protocols/msn/config/CMakeLists.txt @@ -0,0 +1,38 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( FILES + kopete_msn_config.desktop + DESTINATION ${SERVICES_INSTALL_DIR}/tdeconfiguredialog ) + + +##### kcm_kopete_msn (module) ################### + +tde_add_kpart( kcm_kopete_msn AUTOMOC + SOURCES + msnprefs.ui msnpreferences.cpp + LINK kopete-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) diff --git a/kopete/protocols/msn/config/Makefile.am b/kopete/protocols/msn/config/Makefile.am new file mode 100644 index 00000000..2d84950e --- /dev/null +++ b/kopete/protocols/msn/config/Makefile.am @@ -0,0 +1,12 @@ +METASOURCES = AUTO +AM_CPPFLAGS = $(KOPETE_INCLUDES) $(all_includes) + +kde_module_LTLIBRARIES = kcm_kopete_msn.la + +kcm_kopete_msn_la_SOURCES = msnprefs.ui msnpreferences.cpp +kcm_kopete_msn_la_LDFLAGS = -no-undefined -module $(KDE_PLUGIN) $(all_libraries) $(LIB_QT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor +kcm_kopete_msn_la_LIBADD = ../../../libkopete/libkopete.la $(LIB_TDEUTILS) + +service_DATA = kopete_msn_config.desktop +servicedir = $(kde_servicesdir)/tdeconfiguredialog + diff --git a/kopete/protocols/msn/config/kopete_msn_config.desktop b/kopete/protocols/msn/config/kopete_msn_config.desktop new file mode 100644 index 00000000..4754ae1f --- /dev/null +++ b/kopete/protocols/msn/config/kopete_msn_config.desktop @@ -0,0 +1,123 @@ +[Desktop Entry] +Icon=msn_protocol +Type=Service +ServiceTypes=TDECModule + +X-TDE-ModuleType=Library +X-TDE-Library=kopete_msn +X-TDE-FactoryName=MSNProtocolConfigFactory +X-TDE-ParentApp=kopete_msn +X-TDE-ParentComponents=kopete_msn + +Name=MSN Plugin +Name[ar]=توصيلة MSN +Name[be]=Модуль MSN +Name[bg]=MSN +Name[bn]=এমএসএন প্লাগিন +Name[br]=Lugent MSN +Name[bs]=MSN dodatak +Name[ca]=Connector de MSN +Name[cs]=MSN modul +Name[cy]=Ategyn MSN +Name[da]=MSN-Plugin +Name[de]=MSN-Modul +Name[el]=Πρόσθετο MSN +Name[es]=Complemento de MSN +Name[et]=MSN plugin +Name[eu]=MSN Plugin-a +Name[fa]=وصلۀ اماسان +Name[fi]=MSN-liitännäinen +Name[fr]=Module MSN +Name[ga]=Breiseán MSN +Name[gl]=Plugin para MSN +Name[he]=תוסף MSN +Name[hi]=एमएसएन प्लगइन +Name[hr]=MSN umetak +Name[hu]=MSN modul +Name[is]=MSN íforrit +Name[it]=Plugin MSN +Name[ja]=MSN プラグイン +Name[ka]=MSN მოდული +Name[kk]=MSN плагин модулі +Name[km]=កម្មវិធីជំនួយ MSN +Name[lt]=MSN įskiepis +Name[mk]=MSN-приклучок +Name[nb]=MSN programtillegg +Name[nds]=MSN-Moduul +Name[ne]=एमएसएन प्लगइन +Name[nl]=MSN-plugin +Name[nn]=MSN-programtillegg +Name[pa]=MSN ਪਲੱਗਇਨ +Name[pl]=Wtyczka MSN +Name[pt]='Plugin' MSN +Name[pt_BR]=Plug-in MSN +Name[ro]=Modul MSN +Name[ru]=Модуль MSN +Name[se]=MSN-lassemoduvla +Name[sk]=Modul MSN +Name[sl]=Vstavek za MSN +Name[sr]=MSN прикључак +Name[sr@Latn]=MSN priključak +Name[sv]=MSN-insticksprogram +Name[ta]=MSN செருகல் +Name[tg]=Модули MSN +Name[tr]=MSN Eklentisi +Name[uk]=Втулок MSN +Name[uz]=MSN plagini +Name[uz@cyrillic]=MSN плагини +Name[wa]=Tchôke-divins MSN +Name[zh_CN]=MSN 插件 +Name[zh_HK]=MSN 插件 +Name[zh_TW]=MSN 外掛程式 +Comment=Microsoft Network Protocol +Comment[ar]=بروتوكول شبكة Microsoft +Comment[be]=Пратакол сеткі Microsoft Network +Comment[bg]=Протокол за връзка с Microsoft Network +Comment[bn]=মাইক্রোসফ্ট নেটওয়ার্ক প্রোটোকল +Comment[br]=Komenad rouedad Microsoft +Comment[bs]=Microsoft Network protokol +Comment[ca]=Protocol per a la xarxa de Microsoft +Comment[cs]=Protokol sítě Microsoft +Comment[cy]=Protocol Rhwydwaith Microsoft +Comment[de]=Microsoft Netzwerk-Protokoll +Comment[el]=Πρωτόκολλο Microsoft Network +Comment[es]=Protocolo de red de Microsoft +Comment[et]=Microsofti võrguprotokoll +Comment[fa]=قرارداد شبکۀ میکروسافت +Comment[fi]=Microsoft Network -yhteyskäytäntö +Comment[fr]=Protocole réseau Microsoft +Comment[ga]=Prótacal Gréasáin Mhicrosoft +Comment[gl]=Protocolo para a rede de Microsoft +Comment[he]=תוסף חיבור לרשת מיקרוסופט +Comment[hi]=माइक्रोसॉफ्ट नेटवर्क प्रोटोकॉल +Comment[hr]=Microsoft mrežni protokol +Comment[hu]=Microsoft hálózati protokoll +Comment[is]=Microsoft Network Protocol +Comment[it]=Protocollo di rete Microsoft +Comment[ja]=Microsoft ネットワークプロトコル +Comment[ka]=Microsoft ქსელის ოქმი +Comment[kk]=Microsoft Network желі протоколы +Comment[km]=ពិធីការបណ្ដាញម៉ៃក្រូសូហ្វ +Comment[lt]=Microsoft tinklo protokolas +Comment[mk]=Мрежен протокол на Microsoft +Comment[nds]=Microsoft-Nettwarkprotokoll +Comment[ne]=माइक्रोसफ्ट सञ्जाल प्रोटोकल +Comment[nl]=Protocol voor Microsoft Network +Comment[nn]=Microsoft Network-protokoll +Comment[pl]=Protokół Microsoft Network +Comment[pt]=Protocolo da Microsoft Network +Comment[pt_BR]=Protocolo de Rede Microsoft +Comment[ru]=Протокол сети Microsoft Network +Comment[sl]=Protokol za povezavo na MSN +Comment[sv]=Microsoft-nätverksprotokoll +Comment[ta]=மைக்ரோசாப்ட் இணைய விதிமுறை +Comment[tg]=Қарордоди Шабакаи Microsoft +Comment[tr]=Microsoft Ağ Protokolü +Comment[uk]=Мережний протокол Microsoft +Comment[uz]=Microsoft tarmogʻi bilan aloqa oʻrnatish uchun protokol +Comment[uz@cyrillic]=Microsoft тармоғи билан алоқа ўрнатиш учун протокол +Comment[wa]=Protocole pol rantoele da Microsoft +Comment[zh_CN]=Microsoft Network 协议 +Comment[zh_HK]=Microsoft 網絡通訊協定 +Comment[zh_TW]=Microsoft 網路協定 + diff --git a/kopete/protocols/msn/config/msnpreferences.cpp b/kopete/protocols/msn/config/msnpreferences.cpp new file mode 100644 index 00000000..a6f5371f --- /dev/null +++ b/kopete/protocols/msn/config/msnpreferences.cpp @@ -0,0 +1,33 @@ +/* + msnpreferences.cpp - MSN Preferences Widget + + Copyright (c) 2002-2003 by Olivier Goffart <ogoffart @ kde.org> + Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include <kgenericfactory.h> +#include "kcautoconfigmodule.h" +#include "msnprefs.h" + +class MSNPreferences; + +typedef KGenericFactory<MSNPreferences> MSNProtocolConfigFactory; +K_EXPORT_COMPONENT_FACTORY( kcm_kopete_msn, MSNProtocolConfigFactory( "kcm_kopete_msn" ) ) + +class MSNPreferences : public KCAutoConfigModule +{ +public: + MSNPreferences( TQWidget *parent = 0, const char * = 0, const TQStringList &args = TQStringList() ) : KCAutoConfigModule( MSNProtocolConfigFactory::instance(), parent, args ) + { + setMainWidget( new msnPrefsUI( this ) , "MSN"); + } +}; diff --git a/kopete/protocols/msn/config/msnprefs.ui b/kopete/protocols/msn/config/msnprefs.ui new file mode 100644 index 00000000..13817584 --- /dev/null +++ b/kopete/protocols/msn/config/msnprefs.ui @@ -0,0 +1,217 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>msnPrefsUI</class> +<author>Duncan Mac-Vicar P.</author> +<widget class="TQWidget"> + <property name="name"> + <cstring>msnPrefsUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>522</width> + <height>347</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLabel"> + <property name="name"> + <cstring>TextLabel3_2_2_2_3</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>General</string> + </property> + </widget> + <widget class="TQFrame"> + <property name="name"> + <cstring>Frame3_3_3_2_3</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>HLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + </widget> + <widget class="TQCheckBox"> + <property name="name"> + <cstring>NotifyNewChat</cstring> + </property> + <property name="text"> + <string>&Automatically open a chat window when someone starts a conversation</string> + </property> + </widget> + <widget class="TQCheckBox"> + <property name="name"> + <cstring>AutoDownloadPicture</cstring> + </property> + <property name="text"> + <string>&Automatically download the display picture if possible</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="TQCheckBox"> + <property name="name"> + <cstring>useCustomEmoticons</cstring> + </property> + <property name="text"> + <string>Download and show custom emoticons (experimental)</string> + </property> + </widget> + <widget class="TQLabel"> + <property name="name"> + <cstring>TextLabel1_3_3_3</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="TQLabel"> + <property name="name"> + <cstring>TextLabel3_2_2_2_2</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>Away Messages</string> + </property> + </widget> + <widget class="TQFrame"> + <property name="name"> + <cstring>Frame3_3_3_2_2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>HLine</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + </widget> + <widget class="TQCheckBox"> + <property name="name"> + <cstring>SendAwayMessages</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Send &away messages</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout18</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLabel"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Do not send more than one away message every</string> + </property> + </widget> + <widget class="KIntNumInput"> + <property name="name"> + <cstring>AwayMessageSeconds</cstring> + </property> + <property name="value"> + <number>90</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + </widget> + <widget class="TQLabel"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>seconds</string> + </property> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer7</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>21</width> + <height>70</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<connections> + <connection> + <sender>SendAwayMessages</sender> + <signal>toggled(bool)</signal> + <receiver>AwayMessageSeconds</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<tabstops> + <tabstop>NotifyNewChat</tabstop> + <tabstop>AutoDownloadPicture</tabstop> + <tabstop>useCustomEmoticons</tabstop> + <tabstop>SendAwayMessages</tabstop> + <tabstop>AwayMessageSeconds</tabstop> +</tabstops> +<includes> + <include location="global" impldecl="in implementation">knuminput.h</include> +</includes> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> +</includehints> +</UI> diff --git a/kopete/protocols/msn/dispatcher.cpp b/kopete/protocols/msn/dispatcher.cpp new file mode 100644 index 00000000..70d43a14 --- /dev/null +++ b/kopete/protocols/msn/dispatcher.cpp @@ -0,0 +1,647 @@ +/* + dispatcher.cpp - msn p2p protocol + + Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org> + Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include "dispatcher.h" +#include "incomingtransfer.h" +#include "outgoingtransfer.h" + +#if MSN_WEBCAM +#include "webcam.h" +#endif + +using P2P::Dispatcher; +using P2P::Message; +using P2P::TransferContext; +using P2P::IncomingTransfer; +using P2P::OutgoingTransfer; + +#include "msnswitchboardsocket.h" + +// Kde includes +#include <kdebug.h> +#include <kmdcodec.h> +#include <kstandarddirs.h> +#include <tdetempfile.h> + +// TQt includes +#include <tqdatastream.h> +#include <tqfile.h> +#include <tqregexp.h> +#include <tqtextcodec.h> +#include <tqtextstream.h> + +// Kopete includes +#include <kopetechatsession.h> // Just for getting the contact +#include <kopeteaccount.h> +#include <kopetetransfermanager.h> + +#include <stdlib.h> + +Dispatcher::Dispatcher(TQObject *parent, const TQString& contact, const TQStringList &ip) + : TQObject(parent) , m_contact(contact) , m_callbackChannel(0l) , m_ip(ip) +{} + +Dispatcher::~Dispatcher() +{ + kdDebug(14140) << k_funcinfo << endl; + + if(m_callbackChannel) + { + delete m_callbackChannel; + m_callbackChannel = 0l; + } +} + +void Dispatcher::detach(TransferContext* transfer) +{ + m_sessions.remove(transfer->m_sessionId); + transfer->deleteLater(); +} + +TQString Dispatcher::localContact() +{ + return m_contact; +} + +void Dispatcher::requestDisplayIcon(const TQString& from, const TQString& msnObject) +{ + TQ_UINT32 sessionId = rand()%0xFFFFFF00 + 4; + TransferContext* current = + new IncomingTransfer(from, this, sessionId); + + current->m_branch = P2P::Uid::createUid(); + current->m_callId = P2P::Uid::createUid(); + current->setType(P2P::UserDisplayIcon); + current->m_object = msnObject; + // Add the transfer to the list. + m_sessions.insert(sessionId, current); + + kdDebug(14140) << k_funcinfo << "Requesting, " << msnObject << endl; + + TQString context = TQString::fromUtf8(KCodecs::base64Encode(msnObject.utf8())); + // NOTE remove the \0 character automatically + // appended to a TQCString. + context.replace("=", TQString()); + TQString content = + "EUF-GUID: {A4268EEC-FEC5-49E5-95C3-F126696BDBF6}\r\n" + "SessionID: " + TQString::number(sessionId) + "\r\n" + "AppID: 1\r\n" + "Context: " + context + "\r\n" + "\r\n"; + // Send the sending client an invitation message. + current->sendMessage(INVITE, content); +} + +void Dispatcher::sendFile(const TQString& path, TQ_INT64 fileSize, const TQString& to) +{ + // Create a new transfer context that will handle + // the file transfer. + TQ_UINT32 sessionId = rand()%0xFFFFFF00 + 4; + TransferContext *current = + new OutgoingTransfer(to, this, sessionId); + current->m_branch = P2P::Uid::createUid(); + current->m_callId = P2P::Uid::createUid(); + current->setType(P2P::File); + // Add the transfer to the list. + m_sessions.insert(sessionId, current); + + // Set the transfer context file. + current->m_file = new TQFile(path); + // Create the file context data. + TQString context; + + TQByteArray header(638); + header.fill('\0'); + TQDataStream writer(header, IO_WriteOnly); + writer.setByteOrder(TQDataStream::LittleEndian); + + // Write the header length to the stream. + writer << (TQ_INT32)638; + // Write client version to the stream. + writer << (TQ_INT32)3; + // Write the file size to the stream. + writer << fileSize; + // Write the file transfer flag to the stream. + // TODO support file preview. For now disable file preview. + writer << (TQ_INT32)1; + // Write the file name in utf-16 to the stream. + TQTextStream ts(header, IO_WriteOnly); + ts.setEncoding(TQTextStream::RawUnicode); + ts.device()->at(20); + ts << path.section('/', -1); + // NOTE Background Sharing base64 [540..569] + // TODO add support for background sharing. + // Write file exchange type to the stream. + // NOTE File - 0xFFFFFFFF + // NOTE Background Sharing - 0xFFFFFFFE + writer.device()->at(570); + writer << (TQ_UINT32)0xFFFFFFFF; + + // Encode the file context header to base64 encoding. + context = TQString::fromUtf8(KCodecs::base64Encode(header)); + + // Send an INVITE message to the recipient. + TQString content = "EUF-GUID: {5D3E02AB-6190-11D3-BBBB-00C04F795683}\r\n" + "SessionID: " + TQString::number(sessionId) + "\r\n" + "AppID: 2\r\n" + "Context: " + context + "\r\n" + "\r\n"; + current->sendMessage(INVITE, content); +} + +void Dispatcher::sendImage(const TQString& /*fileName*/, const TQString& /*to*/) +{ +// TODO kdDebug(14140) << k_funcinfo << endl; +// TQFile imageFile(fileName); +// if(!imageFile.open(IO_ReadOnly)) +// { +// kdDebug(14140) << k_funcinfo << "Error opening image file." +// << endl; +// return; +// } +// +// OutgoingTransfer *outbound = +// new OutgoingTransfer(to, this, 64); +// +// outbound->sendImage(imageFile.readAll()); +} + +#if MSN_WEBCAM +void Dispatcher::startWebcam(const TQString &/*myHandle*/, const TQString &msgHandle, bool wantToReceive) +{ + TQ_UINT32 sessionId = rand()%0xFFFFFF00 + 4; + Webcam::Who who= wantToReceive ? Webcam::wViewer : Webcam::wProducer; + TransferContext* current = + new Webcam(who, msgHandle, this, sessionId); + + current->m_branch = P2P::Uid::createUid(); + current->m_callId = P2P::Uid::createUid(); + current->setType(P2P::WebcamType); + // Add the transfer to the list. + m_sessions.insert(sessionId, current); + + // {4BD96FC0-AB17-4425-A14A-439185962DC8} <- i want to show you my webcam + // {1C9AA97E-9C05-4583-A3BD-908A196F1E92} <- i want to see your webcam + TQString GUID= (who==Webcam::wProducer) ? "4BD96FC0-AB17-4425-A14A-439185962DC8" : "1C9AA97E-9C05-4583-A3BD-908A196F1E92" ; + + TQString content="EUF-GUID: {"+GUID+"}\r\n" + "SessionID: "+ TQString::number(sessionId)+"\r\n" + "AppID: 4\r\n" + "Context: ewBCADgAQgBFADcAMABEAEUALTQBFADIAQwBBAC0ANAA0ADAAMAAtAEEARTQAwADMALQA4ADgARgBGADgANTQBCADkARgA0AEUAOAB9AA==\r\n\r\n"; + + // context is the base64 of the utf16 of {B8BE70DE-E2CA-4400-AE03-88FF85B9F4E8} + + current->sendMessage( INVITE , content ); +} +#endif + + + +void Dispatcher::slotReadMessage(const TQString &from, const TQByteArray& stream) +{ + P2P::Message receivedMessage = + m_messageFormatter.readMessage(stream); + + receivedMessage.source = from; + + if(receivedMessage.contentType == "application/x-msnmsgrp2p") + { + if((receivedMessage.header.dataSize == 0)/* && ((receivedMessage.header.flag & 0x02) == 0x02)*/) + { + TransferContext *current = 0l; + TQMap<TQ_UINT32, TransferContext*>::Iterator it = m_sessions.begin(); + for(; it != m_sessions.end(); it++) + { + if(receivedMessage.header.ackSessionIdentifier == it.data()->m_identifier){ + current = it.data(); + break; + } + } + + if(current){ + // Inform the transfer object of the acknowledge. + current->m_ackSessionIdentifier = receivedMessage.header.identifier; + current->m_ackUniqueIdentifier = receivedMessage.header.ackSessionIdentifier; + current->acknowledged(); + } + else + { + kdDebug(14140) << k_funcinfo + << "no transfer context with identifier, " + << receivedMessage.header.ackSessionIdentifier + << endl; + } + return; + } + + if(m_messageBuffer.contains(receivedMessage.header.identifier)) + { + kdDebug(14140) << k_funcinfo + << TQString("retrieving buffered messsage, %1").arg(receivedMessage.header.identifier) + << endl; + + // The message was split, try to reconstruct the message + // with this received piece. + Message bufferedMessage = m_messageBuffer[receivedMessage.header.identifier]; + // Remove the buffered message. + m_messageBuffer.remove(receivedMessage.header.identifier); + + bufferedMessage.body.resize(bufferedMessage.body.size() + receivedMessage.header.dataSize); + for(TQ_UINT32 i=0; i < receivedMessage.header.dataSize; i++){ + // Add the remaining message data to the buffered message. + bufferedMessage.body[receivedMessage.header.dataOffset + i] = receivedMessage.body[i]; + } + bufferedMessage.header.dataSize += receivedMessage.header.dataSize; + bufferedMessage.header.dataOffset = 0; + + receivedMessage = bufferedMessage; + } + + // Dispatch the received message. + dispatch(receivedMessage); + } +} + +void Dispatcher::dispatch(const P2P::Message& message) + +{ + TransferContext *messageHandler = 0l; + + if(message.header.sessionId > 0) + { + if(m_sessions.contains(message.header.sessionId)){ + messageHandler = m_sessions[message.header.sessionId]; + } + } + else + { + TQString body = + TQCString(message.body.data(), message.header.dataSize); + TQRegExp regex("SessionID: ([0-9]*)\r\n"); + if(regex.search(body) > 0) + { + TQ_UINT32 sessionId = regex.cap(1).toUInt(); + if(m_sessions.contains(sessionId)){ + // Retrieve the message handler associated with the specified session Id. + messageHandler = m_sessions[sessionId]; + } + } + else + { + // Otherwise, try to retrieve the message handler + // based on the acknowlegded unique identifier. + if(m_sessions.contains(message.header.ackUniqueIdentifier)){ + messageHandler = + m_sessions[message.header.ackUniqueIdentifier]; + } + + if(!messageHandler) + { + // If the message handler still has not been found, + // try to retrieve the handler based on the call id. + regex = TQRegExp("Call-ID: \\{([0-9A-F\\-]*)\\}\r\n"); + regex.search(body); + TQString callId = regex.cap(1); + + TransferContext *current = 0l; + TQMap<TQ_UINT32, TransferContext*>::Iterator it = m_sessions.begin(); + for(; it != m_sessions.end(); it++) + { + current = it.data(); + if(current->m_callId == callId){ + messageHandler = current; + break; + } + } + } + } + } + + if(messageHandler){ + // Process the received message using the + // retrieved registered handler. + messageHandler->m_ackSessionIdentifier = message.header.identifier; + messageHandler->m_ackUniqueIdentifier = message.header.ackSessionIdentifier; + messageHandler->processMessage(message); + } + else + { + // There are no objects registered, with the retrieved session Id, + // to handle the received message; default to this dispatcher. + + if(message.header.totalDataSize > message.header.dataOffset + message.header.dataSize) + { + // The entire message has not been received; + // buffer the recevied portion of the original message. + kdDebug(14140) << k_funcinfo + << TQString("Buffering messsage, %1").arg(message.header.identifier) + << endl; + m_messageBuffer.insert(message.header.identifier, message); + return; + } + + TQString body = + TQCString(message.body.data(), message.header.dataSize); + kdDebug(14140) << k_funcinfo << "received, " << body << endl; + + if(body.startsWith("INVITE")) + { + // Retrieve the branch, call id, and session id. + // These fields will be used later on in the p2p + // transaction. + TQRegExp regex(";branch=\\{([0-9A-F\\-]*)\\}\r\n"); + regex.search(body); + TQString branch = regex.cap(1); + regex = TQRegExp("Call-ID: \\{([0-9A-F\\-]*)\\}\r\n"); + regex.search(body); + TQString callId = regex.cap(1); + regex = TQRegExp("SessionID: ([0-9]*)\r\n"); + regex.search(body); + TQString sessionId = regex.cap(1); + // Retrieve the contact that requested the session. + regex = TQRegExp("From: <msnmsgr:([^>]*)>"); + regex.search(body); + TQString from = regex.cap(1); + // Retrieve the application identifier which + // is used to determine what type of session + // is being requested. + regex = TQRegExp("AppID: ([0-9]*)\r\n"); + regex.search(body); + TQ_UINT32 applicationId = regex.cap(1).toUInt(); + + if(applicationId == 1 || applicationId == 11 || applicationId == 12 ) + { //the AppID is 12 since Messenger 7.5 + // A contact has requested a session to download + // a display icon (User Display Icon or CustomEmotion). + + regex = TQRegExp("Context: ([0-9a-zA-Z+/=]*)"); + regex.search(body); + TQCString msnobj; + + // Decode the msn object from base64 encoding. + KCodecs::base64Decode(regex.cap(1).utf8() , msnobj); + kdDebug(14140) << k_funcinfo << "Contact requested, " + << msnobj << endl; + + // Create a new transfer context that will handle + // the user display icon transfer. + TransferContext *current = + new OutgoingTransfer(from, this, sessionId.toUInt()); + current->m_branch = branch; + current->m_callId = callId; + current->setType(P2P::UserDisplayIcon); + // Add the transfer to the list. + m_sessions.insert(sessionId.toUInt(), current); + + // Determine the display icon being requested. + TQString fileName = objectList.contains(msnobj) + ? objectList[msnobj] + : m_pictureUrl; + TQFile *source = new TQFile(fileName); + // Try to open the source file for reading. + // If an error occurs, send an internal + // error message to the recipient. + if(!source->open(IO_ReadOnly)) + { + current->error(); + return; + } + + current->m_file = source; + // Acknowledge the session request. + current->acknowledge(message); + + current->m_ackSessionIdentifier = message.header.identifier; + current->m_ackUniqueIdentifier = message.header.ackSessionIdentifier; + // Send a 200 OK message to the recipient. + TQString content = TQString("SessionID: %1\r\n\r\n").arg(sessionId); + current->sendMessage(OK, content); + } + else if(applicationId == 2) + { + // A contact has requested a session to + // send a file. + + kdDebug(14140) << k_funcinfo << "File transfer invitation." << endl; + + // Create a new transfer context that will handle + // the file transfer. + TransferContext *transfer = + new IncomingTransfer(from, this, sessionId.toUInt()); + transfer->m_branch = branch; + transfer->m_callId = callId; + transfer->setType(P2P::File); + // Add the transfer to the list. + m_sessions.insert(sessionId.toUInt(), transfer); + + regex = TQRegExp("Context: ([0-9a-zA-Z+/=]*)"); + regex.search(body); + TQByteArray context; + + // Decode the file context from base64 encoding. + KCodecs::base64Decode(regex.cap(1).utf8(), context); + TQDataStream reader(context, IO_ReadOnly); + reader.setByteOrder(TQDataStream::LittleEndian); + //Retrieve the file info from the context field. + // File Size [8..15] Int64 + reader.device()->at(8); + TQ_INT64 fileSize; + reader >> fileSize; + // Flag [15..18] Int32 + // 0x00 File transfer with preview data. + // 0x01 File transfer without preview data. + // 0x02 Background sharing. + TQ_INT32 flag; + reader >> flag; + kdDebug(14140) << flag << endl; + // FileName UTF16 (Unicode) [19..539] + TQByteArray bytes(520); + reader.readRawBytes(bytes.data(), bytes.size()); + TQTextStream ts(bytes, IO_ReadOnly); + ts.setEncoding(TQTextStream::Unicode); + TQString fileName; + fileName = ts.readLine().utf8(); + + emit incomingTransfer(from, fileName, fileSize); + + kdDebug(14140) << + TQString("%1, %2 bytes.").arg(fileName, TQString::number(fileSize)) + << endl + << endl; + + // Get the contact that is sending the file. + Kopete::Contact *contact = getContactByAccountId(from); + + if(contact) + { + // Acknowledge the file invitation message. + transfer->acknowledge(message); + + transfer->m_ackSessionIdentifier = message.header.identifier; + transfer->m_ackUniqueIdentifier = message.header.ackSessionIdentifier; + + TQObject::connect(Kopete::TransferManager::transferManager(), TQT_SIGNAL(accepted(Kopete::Transfer*, const TQString&)), transfer, TQT_SLOT(slotTransferAccepted(Kopete::Transfer*, const TQString&))); + TQObject::connect(Kopete::TransferManager::transferManager(), TQT_SIGNAL(refused(const Kopete::FileTransferInfo&)), transfer, TQT_SLOT(slotTransferRefused(const Kopete::FileTransferInfo&))); + + // Show the file transfer accept/decline dialog. + Kopete::TransferManager::transferManager()->askIncomingTransfer(contact, fileName, fileSize, TQString(), sessionId); + } + else + { + kdWarning(14140) << fileName << " from " << from + << " has failed; could not retrieve contact from contact list." + << endl; + transfer->m_ackSessionIdentifier = message.header.identifier; + transfer->m_ackUniqueIdentifier = message.header.ackSessionIdentifier; + transfer->sendMessage(ERROR); + } + } + else if(applicationId == 4) + { +#if MSN_WEBCAM + regex = TQRegExp("EUF-GUID: \\{([0-9a-zA-Z\\-]*)\\}"); + regex.search(body); + TQString GUID=regex.cap(1); + + kdDebug(14140) << k_funcinfo << "webcam " << GUID << endl; + + Webcam::Who who; + if(GUID=="4BD96FC0-AB17-4425-A14A-439185962DC8") + { //that mean "I want to send MY webcam" + who=Webcam::wViewer; + } + else if(GUID=="1C9AA97E-9C05-4583-A3BD-908A196F1E92") + { //that mean "I want YOU to send YOUR webcam" + who=Webcam::wProducer; + } + else + { //unknown GUID + //current->error(); + kdWarning(14140) << k_funcinfo << "Unknown GUID " << GUID << endl; + return; + } + + TransferContext *current = new P2P::Webcam(who, from, this, sessionId.toUInt()); + current->m_branch = branch; + current->m_callId = callId; + + // Add the transfer to the list. + m_sessions.insert(sessionId.toUInt(), current); + // Acknowledge the session request. + current->acknowledge(message); + TQTimer::singleShot(0,current, TQT_SLOT(askIncommingInvitation()) ); +#endif + } + } + else if(message.header.sessionId == 64) + { + // A contact has sent an inkformat (handwriting) gif. + // NOTE The entire message body is UTF16 encoded. + TQString body = ""; + for (TQ_UINT32 i=0; i < message.header.totalDataSize; i++){ + if (message.body[i] != TQChar('\0')){ + body += TQChar(message.body[i]); + } + } + + TQRegExp regex("Content-Type: ([A-Za-z0-9$!*/\\-]*)"); + regex.search(body); + TQString contentType = regex.cap(1); + + if(contentType == "image/gif") + { + IncomingTransfer transfer(message.source, this, message.header.sessionId); + transfer.acknowledge(message); + + regex = TQRegExp("base64:([0-9a-zA-Z+/=]*)"); + regex.search(body); + TQString base64 = regex.cap(1); + TQByteArray image; +// Convert from base64 encoding to byte array. + KCodecs::base64Decode(base64.utf8(), image); +// Create a temporary file to store the image data. + KTempFile *ink = new KTempFile(locateLocal("tmp", "inkformatgif-" ), ".gif"); + ink->setAutoDelete(true); +// Save the image data to disk. + ink->file()->writeBlock(image); + ink->file()->close(); + displayIconReceived(ink, "inkformatgif"); + ink = 0l; + } + } + } +} + +void Dispatcher::messageAcknowledged(unsigned int correlationId, bool fullReceive) +{ + if(fullReceive) + { + TransferContext *current = 0l; + TQMap<TQ_UINT32, TransferContext*>::Iterator it = m_sessions.begin(); + for(; it != m_sessions.end(); it++) + { + current = it.data(); + if(current->m_transactionId == correlationId) + { + // Inform the transfer object of the acknowledge. + current->readyWrite(); + break; + } + } + } +} + +Kopete::Contact* Dispatcher::getContactByAccountId(const TQString& accountId) +{ + Kopete::Contact *contact = 0l; + if(parent()) + { + // Retrieve the contact from the current chat session context. + Kopete::ChatSession *session = dynamic_cast<Kopete::ChatSession*>(parent()->parent()); + if(session) + { + contact = session->account()->contacts()[accountId]; + session->setCanBeDeleted(false); + } + } + return contact; +} + +Dispatcher::CallbackChannel::CallbackChannel(MSNSwitchBoardSocket *switchboard) +{ + m_switchboard = switchboard; +} + +Dispatcher::CallbackChannel::~CallbackChannel() +{} + +TQ_UINT32 Dispatcher::CallbackChannel::send(const TQByteArray& stream) +{ + return m_switchboard->sendCommand("MSG", "D", true, stream, true); +} + +Dispatcher::CallbackChannel* Dispatcher::callbackChannel() +{ + if(m_callbackChannel == 0l){ + MSNSwitchBoardSocket *callback = dynamic_cast<MSNSwitchBoardSocket *>(parent()); + if(callback == 0l) return 0l; + m_callbackChannel = new Dispatcher::CallbackChannel(callback); + } + + return m_callbackChannel; +} + +#include "dispatcher.moc" diff --git a/kopete/protocols/msn/dispatcher.h b/kopete/protocols/msn/dispatcher.h new file mode 100644 index 00000000..65b8dd3d --- /dev/null +++ b/kopete/protocols/msn/dispatcher.h @@ -0,0 +1,108 @@ +/* + dispatcher.h - msn p2p protocol + + Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org> + Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef DISPATCHER_H +#define DISPATCHER_H + +#include <tqobject.h> +#include <tqstringlist.h> + +#include "kopete_export.h" + +#include "p2p.h" +#include "messageformatter.h" +#include "incomingtransfer.h" +#include "outgoingtransfer.h" + + +namespace Kopete { class Contact; } +class MSNSwitchBoardSocket; + +/** +@author Kopete Developers +*/ +namespace P2P{ + class IncomingTransfer; + class OutgoingTransfer; + + class KOPETE_EXPORT Dispatcher : public TQObject + { Q_OBJECT + + public: + Dispatcher(TQObject *parent, const TQString& contact, const TQStringList &ip); + ~Dispatcher(); + + void detach(TransferContext* transfer); + TQString localContact(); + void requestDisplayIcon(const TQString& from, const TQString& msnObject); + void sendFile(const TQString& path, TQ_INT64 fileSize, const TQString& to); + void sendImage(const TQString& fileName, const TQString& to); + TQString m_pictureUrl; + TQMap<TQString, TQString> objectList; + +#if MSN_WEBCAM + void startWebcam(const TQString &myHandle, const TQString &msgHandle, bool wantToReceive); +#endif + + + public slots: + void slotReadMessage(const TQString &from, const TQByteArray& stream); + void messageAcknowledged(unsigned int correlationId, bool fullReceive); + + signals: + void sendCommand(const TQString &cmd, const TQString &args = TQString(), bool addId = true, const TQByteArray &body = TQByteArray(), bool binary=false); + void displayIconReceived(KTempFile* file, const TQString& msnObject); + void incomingTransfer(const TQString& from, const TQString& fileName, TQ_INT64 fileSize); + + private: + class CallbackChannel + { + public: + CallbackChannel(MSNSwitchBoardSocket *switchboard); + ~CallbackChannel(); + + TQ_UINT32 send(const TQByteArray& stream); + + private: + MSNSwitchBoardSocket *m_switchboard; + }; + + public: + CallbackChannel* callbackChannel(); + /** + * IP's of this compiter, the first one is the one seen by the server. + */ + TQStringList localIp() { return m_ip; } + + + private: + void dispatch(const P2P::Message& message); + Kopete::Contact* getContactByAccountId(const TQString& accountId); + + P2P::MessageFormatter m_messageFormatter; + TQMap<TQ_UINT32, P2P::TransferContext*> m_sessions; + TQMap<TQ_UINT32, P2P::Message> m_messageBuffer; + TQString m_contact; + CallbackChannel *m_callbackChannel; + TQStringList m_ip; + + friend class P2P::TransferContext; + friend class P2P::IncomingTransfer; + friend class P2P::OutgoingTransfer; + }; +} + +#endif diff --git a/kopete/protocols/msn/dummy.cpp b/kopete/protocols/msn/dummy.cpp new file mode 100644 index 00000000..6edd39fa --- /dev/null +++ b/kopete/protocols/msn/dummy.cpp @@ -0,0 +1,3 @@ +#include "kdemacros.h" +extern "C" KDE_EXPORT void *init_libkopete_msn_shared(); +extern "C" KDE_EXPORT void *init_kopete_msn() { return init_libkopete_msn_shared(); } diff --git a/kopete/protocols/msn/icons/CMakeLists.txt b/kopete/protocols/msn/icons/CMakeLists.txt new file mode 100644 index 00000000..ba51467b --- /dev/null +++ b/kopete/protocols/msn/icons/CMakeLists.txt @@ -0,0 +1,12 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +tde_install_icons( DESTINATION ${DATA_INSTALL_DIR}/kopete/icons ) diff --git a/kopete/protocols/msn/icons/Makefile.am b/kopete/protocols/msn/icons/Makefile.am new file mode 100644 index 00000000..9143c6b4 --- /dev/null +++ b/kopete/protocols/msn/icons/Makefile.am @@ -0,0 +1,2 @@ +kopeteicondir = $(kde_datadir)/kopete/icons +kopeteicon_ICON = AUTO diff --git a/kopete/protocols/msn/icons/cr128-app-msn_protocol.png b/kopete/protocols/msn/icons/cr128-app-msn_protocol.png Binary files differnew file mode 100644 index 00000000..dc94a4e9 --- /dev/null +++ b/kopete/protocols/msn/icons/cr128-app-msn_protocol.png diff --git a/kopete/protocols/msn/icons/cr16-action-msn_away.png b/kopete/protocols/msn/icons/cr16-action-msn_away.png Binary files differnew file mode 100644 index 00000000..cbbd45fc --- /dev/null +++ b/kopete/protocols/msn/icons/cr16-action-msn_away.png diff --git a/kopete/protocols/msn/icons/cr16-action-msn_blocked.png b/kopete/protocols/msn/icons/cr16-action-msn_blocked.png Binary files differnew file mode 100644 index 00000000..80efc4c7 --- /dev/null +++ b/kopete/protocols/msn/icons/cr16-action-msn_blocked.png diff --git a/kopete/protocols/msn/icons/cr16-action-msn_brb.png b/kopete/protocols/msn/icons/cr16-action-msn_brb.png Binary files differnew file mode 100644 index 00000000..3f1a0d30 --- /dev/null +++ b/kopete/protocols/msn/icons/cr16-action-msn_brb.png diff --git a/kopete/protocols/msn/icons/cr16-action-msn_busy.png b/kopete/protocols/msn/icons/cr16-action-msn_busy.png Binary files differnew file mode 100644 index 00000000..b3dcac08 --- /dev/null +++ b/kopete/protocols/msn/icons/cr16-action-msn_busy.png diff --git a/kopete/protocols/msn/icons/cr16-action-msn_connecting.mng b/kopete/protocols/msn/icons/cr16-action-msn_connecting.mng Binary files differnew file mode 100644 index 00000000..38629273 --- /dev/null +++ b/kopete/protocols/msn/icons/cr16-action-msn_connecting.mng diff --git a/kopete/protocols/msn/icons/cr16-action-msn_invisible.png b/kopete/protocols/msn/icons/cr16-action-msn_invisible.png Binary files differnew file mode 100644 index 00000000..ce42bef0 --- /dev/null +++ b/kopete/protocols/msn/icons/cr16-action-msn_invisible.png diff --git a/kopete/protocols/msn/icons/cr16-action-msn_lunch.png b/kopete/protocols/msn/icons/cr16-action-msn_lunch.png Binary files differnew file mode 100644 index 00000000..abf42e3f --- /dev/null +++ b/kopete/protocols/msn/icons/cr16-action-msn_lunch.png diff --git a/kopete/protocols/msn/icons/cr16-action-msn_na.png b/kopete/protocols/msn/icons/cr16-action-msn_na.png Binary files differnew file mode 100644 index 00000000..b1aa91af --- /dev/null +++ b/kopete/protocols/msn/icons/cr16-action-msn_na.png diff --git a/kopete/protocols/msn/icons/cr16-action-msn_newmsg.png b/kopete/protocols/msn/icons/cr16-action-msn_newmsg.png Binary files differnew file mode 100644 index 00000000..d42bb0ae --- /dev/null +++ b/kopete/protocols/msn/icons/cr16-action-msn_newmsg.png diff --git a/kopete/protocols/msn/icons/cr16-action-msn_offline.png b/kopete/protocols/msn/icons/cr16-action-msn_offline.png Binary files differnew file mode 100644 index 00000000..5cf9ffd5 --- /dev/null +++ b/kopete/protocols/msn/icons/cr16-action-msn_offline.png diff --git a/kopete/protocols/msn/icons/cr16-action-msn_online.png b/kopete/protocols/msn/icons/cr16-action-msn_online.png Binary files differnew file mode 100644 index 00000000..71169ad2 --- /dev/null +++ b/kopete/protocols/msn/icons/cr16-action-msn_online.png diff --git a/kopete/protocols/msn/icons/cr16-action-msn_phone.png b/kopete/protocols/msn/icons/cr16-action-msn_phone.png Binary files differnew file mode 100644 index 00000000..857ec14a --- /dev/null +++ b/kopete/protocols/msn/icons/cr16-action-msn_phone.png diff --git a/kopete/protocols/msn/icons/cr16-app-msn_protocol.png b/kopete/protocols/msn/icons/cr16-app-msn_protocol.png Binary files differnew file mode 100644 index 00000000..a18ff5d4 --- /dev/null +++ b/kopete/protocols/msn/icons/cr16-app-msn_protocol.png diff --git a/kopete/protocols/msn/icons/cr32-app-msn_protocol.png b/kopete/protocols/msn/icons/cr32-app-msn_protocol.png Binary files differnew file mode 100644 index 00000000..2c9b130b --- /dev/null +++ b/kopete/protocols/msn/icons/cr32-app-msn_protocol.png diff --git a/kopete/protocols/msn/icons/cr48-app-msn_protocol.png b/kopete/protocols/msn/icons/cr48-app-msn_protocol.png Binary files differnew file mode 100644 index 00000000..ad495100 --- /dev/null +++ b/kopete/protocols/msn/icons/cr48-app-msn_protocol.png diff --git a/kopete/protocols/msn/icons/cr64-app-msn_protocol.png b/kopete/protocols/msn/icons/cr64-app-msn_protocol.png Binary files differnew file mode 100644 index 00000000..338f81bf --- /dev/null +++ b/kopete/protocols/msn/icons/cr64-app-msn_protocol.png diff --git a/kopete/protocols/msn/incomingtransfer.cpp b/kopete/protocols/msn/incomingtransfer.cpp new file mode 100644 index 00000000..0da4a04c --- /dev/null +++ b/kopete/protocols/msn/incomingtransfer.cpp @@ -0,0 +1,384 @@ +/* + incomingtransfer.cpp - msn p2p protocol + + Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org> + Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include "incomingtransfer.h" +using P2P::TransferContext; +using P2P::IncomingTransfer; +using P2P::Message; + +// Kde includes +#include <kbufferedsocket.h> +#include <kdebug.h> +#include <tdelocale.h> +#include <kserversocket.h> +#include <kstandarddirs.h> +#include <tdetempfile.h> +using namespace KNetwork; + +// TQt includes +#include <tqfile.h> +#include <tqregexp.h> + +// Kopete includes +#include <kopetetransfermanager.h> + +IncomingTransfer::IncomingTransfer(const TQString& from, P2P::Dispatcher *dispatcher, TQ_UINT32 sessionId) +: TransferContext(from,dispatcher,sessionId) +{ + m_direction = P2P::Incoming; + m_listener = 0l; +} + +IncomingTransfer::~IncomingTransfer() +{ + kdDebug(14140) << k_funcinfo << endl; + if(m_listener) + { + delete m_listener; + m_listener = 0l; + } + + if(m_socket) + { + delete m_socket; + m_socket = 0l; + } +} + + +void IncomingTransfer::slotTransferAccepted(Kopete::Transfer* transfer, const TQString& /*fileName*/) +{ + TQ_UINT32 sessionId = transfer->info().internalId().toUInt(); + if(sessionId!=m_sessionId) + return; + + TQObject::connect(transfer , TQT_SIGNAL(transferCanceled()), this, TQT_SLOT(abort())); + m_transfer = transfer; + + TQString content = TQString("SessionID: %1\r\n\r\n").arg(sessionId); + sendMessage(OK, content); + + TQObject::disconnect(Kopete::TransferManager::transferManager(), 0l, this, 0l); +} + +void IncomingTransfer::slotTransferRefused(const Kopete::FileTransferInfo& info) +{ + TQ_UINT32 sessionId = info.internalId().toUInt(); + if(sessionId!=m_sessionId) + return; + + TQString content = TQString("SessionID: %1\r\n\r\n").arg(sessionId); + // Send the sending client a cancelation message. + sendMessage(DECLINE, content); + m_state=Finished; + + TQObject::disconnect(Kopete::TransferManager::transferManager(), 0l, this, 0l); +} + + + +void IncomingTransfer::acknowledged() +{ + kdDebug(14140) << k_funcinfo << endl; + + switch(m_state) + { + case Invitation: + // NOTE UDI: base identifier acknowledge message, ignore. + // UDI: 200 OK message should follow. + if(m_type == File) + { + // FT: 200 OK acknowledged message. + // If this is the first connection between the two clients, a direct connection invitation + // should follow. Otherwise, the file transfer may start right away. + if(m_transfer) + { + TQFile *destination = new TQFile(m_transfer->destinationURL().path()); + if(!destination->open(IO_WriteOnly)) + { + m_transfer->slotError(TDEIO::ERR_CANNOT_OPEN_FOR_WRITING, i18n("Cannot open file for writing")); + m_transfer = 0l; + + error(); + return; + } + m_file = destination; + } + m_state = Negotiation; + } + break; + + case Negotiation: + // 200 OK acknowledge message. + break; + + case DataTransfer: + break; + + case Finished: + // UDI: Bye acknowledge message. + m_dispatcher->detach(this); + break; + } +} + +void IncomingTransfer::processMessage(const Message& message) +{ + if(m_file && (message.header.flag == 0x20 || message.header.flag == 0x01000030)) + { + // UserDisplayIcon data or File data is in this message. + // Write the recieved data to the file. + kdDebug(14140) << k_funcinfo << TQString("Received, %1 bytes").arg(message.header.dataSize) << endl; + + m_file->writeBlock(message.body.data(), message.header.dataSize); + if(m_transfer){ + m_transfer->slotProcessed(message.header.dataOffset + message.header.dataSize); + } + + if((message.header.dataOffset + message.header.dataSize) == message.header.totalDataSize) + { + // Transfer is complete. + if(m_type == UserDisplayIcon){ + m_tempFile->close(); + m_dispatcher->displayIconReceived(m_tempFile, m_object); + m_tempFile = 0l; + m_file = 0l; + } + else + { + m_file->close(); + } + + m_isComplete = true; + // Send data acknowledge message. + acknowledge(message); + + if(m_type == UserDisplayIcon) + { + m_state = Finished; + // Send BYE message. + sendMessage(BYE, "\r\n"); + } + } + } + else if(message.header.dataSize == 4 && message.applicationIdentifier == 1) + { + // Data preparation message. + //if (m_tempFile->name().isEmpty() == false) { + // TQFile::remove(m_tempFile->name()); + //} + m_tempFile = new KTempFile(locateLocal("tmp", "msnpicture--"), ".png"); + m_tempFile->setAutoDelete(true); + m_file = m_tempFile->file(); + m_state = DataTransfer; + // Send data preparation acknowledge message. + acknowledge(message); + } + else + { + TQString body = + TQCString(message.body.data(), message.header.dataSize); +// kdDebug(14140) << k_funcinfo << "received, " << body << endl; + + if(body.startsWith("INVITE")) + { + // Retrieve some MSNSLP headers used when + // replying to this INVITE message. + TQRegExp regex(";branch=\\{([0-9A-F\\-]*)\\}\r\n"); + regex.search(body); + m_branch = regex.cap(1); + // NOTE Call-ID never changes. + regex = TQRegExp("Call-ID: \\{([0-9A-F\\-]*)\\}\r\n"); + regex.search(body); + m_callId = regex.cap(1); + regex = TQRegExp("Bridges: ([^\r\n]*)\r\n"); + regex.search(body); + TQString bridges = regex.cap(1); + // The NetID field is 0 if the Conn-Type is + // Direct-Connect or Firewall, otherwise, it is + // a randomly generated number. + regex = TQRegExp("NetID: (\\-?\\d+)\r\n"); + regex.search(body); + TQString netId = regex.cap(1); + kdDebug(14140) << "net id, " << netId << endl; + // Connection Types + // - Direct-Connect + // - Port-Restrict-NAT + // - IP-Restrict-NAT + // - Symmetric-NAT + // - Firewall + regex = TQRegExp("Conn-Type: ([^\r\n]+)\r\n"); + regex.search(body); + TQString connType = regex.cap(1); + + bool wouldListen = false; + if(netId.toUInt() == 0 && connType == "Direct-Connect"){ + wouldListen = true; + + } + else if(connType == "IP-Restrict-NAT"){ + wouldListen = true; + } +#if 1 + wouldListen = false; // TODO Direct connection support +#endif + TQString content; + + if(wouldListen) + { + // Create a listening socket for direct file transfer. + m_listener = new TDEServerSocket("", ""); + m_listener->setResolutionEnabled(true); + // Create the callback that will try to accept incoming connections. + TQObject::connect(m_listener, TQT_SIGNAL(readyAccept()), TQT_SLOT(slotAccept())); + TQObject::connect(m_listener, TQT_SIGNAL(gotError(int)), this, TQT_SLOT(slotListenError(int))); + // Listen for incoming connections. + bool isListening = m_listener->listen(1); + kdDebug(14140) << k_funcinfo << (isListening ? "listening" : "not listening") << endl; + kdDebug(14140) << k_funcinfo + << "local endpoint, " << m_listener->localAddress().nodeName() + << endl; + + content = "Bridge: TCPv1\r\n" + "Listening: true\r\n" + + TQString("Hashed-Nonce: {%1}\r\n").arg(P2P::Uid::createUid()) + + TQString("IPv4Internal-Addrs: %1\r\n").arg(m_listener->localAddress().nodeName()) + + TQString("IPv4Internal-Port: %1\r\n").arg(m_listener->localAddress().serviceName()) + + "\r\n"; + } + else + { + content = + "Bridge: TCPv1\r\n" + "Listening: false\r\n" + "Hashed-Nonce: {00000000-0000-0000-0000-000000000000}\r\n" + "\r\n"; + } + + m_state = DataTransfer; + + if (m_type != File) + { + // NOTE For file transfers, the connection invite *must not* be acknowledged in any way + // as this trips MSN 7.5 + + acknowledge(message); + // Send 200 OK message to the sending client. + sendMessage(OK, content); + } + } + else if(body.startsWith("BYE")) + { + m_state = Finished; + // Send the sending client an acknowledge message. + acknowledge(message); + + if(m_file && m_transfer) + { + if(m_isComplete){ + // The transfer is complete. + m_transfer->slotComplete(); + } + else + { + // The transfer has been canceled remotely. + if(m_transfer){ + // Inform the user of the file transfer cancelation. + m_transfer->slotError(TDEIO::ERR_ABORTED, i18n("File transfer canceled.")); + } + // Remove the partially received file. + m_file->remove(); + } + } + + // Dispose of this transfer context. + m_dispatcher->detach(this); + } + else if(body.startsWith("MSNSLP/1.0 200 OK")) + { + if(m_type == UserDisplayIcon){ + m_state = Negotiation; + // Acknowledge the 200 OK message. + acknowledge(message); + } + } + } +} + +void IncomingTransfer::slotListenError(int /*errorCode*/) +{ + kdDebug(14140) << k_funcinfo << m_listener->errorString() << endl; +} + +void IncomingTransfer::slotAccept() +{ + // Try to accept an incoming connection from the sending client. + m_socket = static_cast<TDEBufferedSocket*>(m_listener->accept()); + if(!m_socket) + { + // NOTE If direct connection fails, the sending + // client wil transfer the file data through the + // existing session. + kdDebug(14140) << k_funcinfo << "Direct connection failed." << endl; + // Close the listening endpoint. + m_listener->close(); + return; + } + + kdDebug(14140) << k_funcinfo << "Direct connection established." << endl; + + // Set the socket to non blocking, + // enable the ready read signal and disable + // ready write signal. + // NOTE readyWrite consumes too much cpu usage. + m_socket->setBlocking(false); + m_socket->enableRead(true); + m_socket->enableWrite(false); + + // Create the callback that will try to read bytes from the accepted socket. + TQObject::connect(m_socket, TQT_SIGNAL(readyRead()), this, TQT_SLOT(slotSocketRead())); + // Create the callback that will try to handle the socket close event. + TQObject::connect(m_socket, TQT_SIGNAL(closed()), this, TQT_SLOT(slotSocketClosed())); + // Create the callback that will try to handle the socket error event. + TQObject::connect(m_socket, TQT_SIGNAL(gotError(int)), this, TQT_SLOT(slotSocketError(int))); +} + +void IncomingTransfer::slotSocketRead() +{ + int available = m_socket->bytesAvailable(); + kdDebug(14140) << k_funcinfo << available << ", bytes available." << endl; + if(available > 0) + { + TQByteArray buffer(available); + m_socket->readBlock(buffer.data(), buffer.size()); + + if(TQString(buffer) == "foo"){ + kdDebug(14140) << "Connection Check." << endl; + } + } +} + +void IncomingTransfer::slotSocketClosed() +{ + kdDebug(14140) << k_funcinfo << endl; +} + +void IncomingTransfer::slotSocketError(int errorCode) +{ + kdDebug(14140) << k_funcinfo << errorCode << endl; +} + +#include "incomingtransfer.moc" diff --git a/kopete/protocols/msn/incomingtransfer.h b/kopete/protocols/msn/incomingtransfer.h new file mode 100644 index 00000000..3ce3f391 --- /dev/null +++ b/kopete/protocols/msn/incomingtransfer.h @@ -0,0 +1,58 @@ +/* + incomingtransfer.h - msn p2p protocol + + Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org> + Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef INCOMINGTRANSFER_H +#define INCOMINGTRANSFER_H + +#include "p2p.h" +#include "dispatcher.h" + +namespace KNetwork{ + class TDEServerSocket; +} + +/** +@author Kopete Developers +*/ +namespace P2P{ + class IncomingTransfer : public P2P::TransferContext + { Q_OBJECT + + public: + IncomingTransfer(const TQString& from, P2P::Dispatcher *dispatcher, TQ_UINT32 sessionId); + virtual ~IncomingTransfer(); + + private slots: + void slotListenError(int errorCode); + void slotAccept(); + void slotSocketRead(); + void slotSocketClosed(); + void slotSocketError(int errorCode); + + void slotTransferAccepted(Kopete::Transfer* transfer, const TQString& fileName); + void slotTransferRefused(const Kopete::FileTransferInfo& info); + + + private: + virtual void acknowledged(); + virtual void processMessage(const Message& message); + + KTempFile *m_tempFile; + KNetwork::TDEServerSocket *m_listener; + }; +} + +#endif diff --git a/kopete/protocols/msn/kopete_msn.desktop b/kopete/protocols/msn/kopete_msn.desktop new file mode 100644 index 00000000..8f74c113 --- /dev/null +++ b/kopete/protocols/msn/kopete_msn.desktop @@ -0,0 +1,99 @@ +[Desktop Entry] +Type=Service +Icon=msn_protocol +ServiceTypes=Kopete/Protocol +X-TDE-Library=kopete_msn +X-Kopete-Version=1000900 +X-Kopete-Messaging-Protocol=messaging/msn +X-TDE-PluginInfo-Author=Kopete Developers +X-TDE-PluginInfo-Email=kopete-devel@kde.org +X-TDE-PluginInfo-Name=kopete_msn +X-TDE-PluginInfo-Version=0.8.0 +X-TDE-PluginInfo-Website=http://kopete.kde.org +X-TDE-PluginInfo-Category=Protocols +X-TDE-PluginInfo-Depends= +X-TDE-PluginInfo-License=GPL +X-TDE-PluginInfo-EnabledByDefault=false +Name=MSN Messenger +Name[ar]=مرسال MSN +Name[bn]=এমএসএন বার্তাবাহক +Name[cy]=Negesydd MSN +Name[da]=MSN-Messenger +Name[de]=MSN-Messenger +Name[eo]=MSN-mesaĝilo +Name[fa]=پیامرسان اماسان +Name[gl]=MSN Messanger +Name[hi]=एमएसएन मैसेंजर +Name[ja]=MSN メッセンジャー +Name[km]=កម្មវិធីផ្ញើសារ MSN +Name[lt]=MSN žinučių klientas +Name[mk]=Гласник за MSN +Name[nds]=MSN-Kortnarichtendeenst +Name[ne]=एमएसएन मेसेन्जर +Name[pa]=MSN ਸੁਨੇਹੇਦਾਰ +Name[pl]=Komunikator MSN Messenger +Name[pt_BR]=Mensageiro MSN +Name[ro]=Mesaje instantanee MSN +Name[tg]=MSN Пайёмбар +Name[uk]=Кур'єр MSN +Name[uz]=MSN mesenjer +Name[uz@cyrillic]=MSN месенжер +Comment=Protocol to connect to MSN Messenger +Comment[ar]=البرتوكول سيتصل بمرسال MSN +Comment[be]=Пратакол MSN Messenger +Comment[bg]=Протокол за връзка с MSN Messenger +Comment[bn]=এমএসএন বার্তাবাহকে সংযোগ করতে প্রোটোকল +Comment[br]=Komenad kevreañ ouzh MSN Messenger +Comment[bs]=MSN Messenger protokol +Comment[ca]=Protocol per a connectar-se a MSN Messenger +Comment[cs]=Protokol k připojení k MSN Messengeru +Comment[cy]=Protocol i gysylltu â Negesydd MSN +Comment[da]=Protokol til at forbinde til MSN-Messenger +Comment[de]=Protokoll zur Verbindung mit dem MSN-Messenger +Comment[el]=Πρωτόκολλο για σύνδεση στο MSN Messenger +Comment[es]=Protocolo para conectar con MSN Messenger +Comment[et]=Protokoll ühendumiseks MSN Messengeriga +Comment[eu]=MSN Messenger-era konektatzeko protokoloa +Comment[fa]=قرارداد برای اتصال به پیامرسان اماسان +Comment[fi]=Yhteyskäytäntö MSN Messanger -verkkoon kytkeytymiseen +Comment[fr]=Protocole pour se connecter sur MSN Messenger +Comment[ga]=Prótacal chun ceangal le MSN Messenger +Comment[gl]=Protocolo para se conectar ó MSN Messanger +Comment[he]=פרוטוקול התחברות ל- MSN Messenger +Comment[hi]=एमएसएन मैसेंजर से जुड़ने का प्रोटोकॉल +Comment[hr]=Protokol za povezivanje na MSN Messenger +Comment[hu]=Protokoll az MSN Messenger használatához +Comment[is]=Samskiptamáti til að tengjast MSN Messenger +Comment[it]=Protocollo per connessione a MSN Messenger +Comment[ja]=MSN メッセンジャーに接続するプロトコル +Comment[ka]=MSN Messenger დაკავშირების ოქმი +Comment[kk]=MSN Messenger-ге қосылу протоколы +Comment[km]=ពិធីការដើម្បីភ្ជាប់ទៅកម្មវិធីផ្ញើសារ MSN +Comment[lt]=Protokolas prisijungimui prie MSN žinučių kliento +Comment[mk]=Протокол за поврзување на Гласникот на MSN +Comment[nb]=Protokoll for å koble til MSN Messenger +Comment[nds]=Protokoll för't Tokoppeln na den MSN-Kortnarichtendeenst +Comment[ne]=एमएसएन मेसेन्जरमा जडान गर्नुपर्ने प्रोटोकल +Comment[nl]=Protocol voor MSN Messenger +Comment[nn]=Protokoll for å kopla til MSN Messenger +Comment[pl]=Protokół połączenia z serwerem MSN Messenger +Comment[pt]=Um protocolo para ser ligar ao MSN Messenger +Comment[pt_BR]=Protocolo para conexão ao MSN Messenger +Comment[ro]=Protocol de conectare la MSN Messenger +Comment[ru]=Протокол для подключения к MSN Messenger +Comment[sk]=Protokol pre pripojenie k MSN Messenger +Comment[sl]=Protokol za povezavo na MSN Messenger +Comment[sr]=Протокол за повезивање на MSN Messenger +Comment[sr@Latn]=Protokol za povezivanje na MSN Messenger +Comment[sv]=Protokoll för att ansluta till MSN-meddelandeklient +Comment[ta]= MSN Messenger யுடன் இணைக்க விதிமுறை +Comment[tg]=Қарордоди пайвастшавӣ ба MSN Пайёмбар +Comment[tr]=MSN Messenger'a bağlantı iletişim kuralı +Comment[uk]=Протокол для з'єднання з MSN Messenger +Comment[uz]=MSN mesenjer bilan aloqa oʻrnatish uchun protokol +Comment[uz@cyrillic]=MSN месенжер билан алоқа ўрнатиш учун протокол +Comment[wa]=Protocole po s' raloyî a MSN +Comment[zh_CN]=连接到 MSN Messenger 协议 +Comment[zh_HK]=用來連接至 MSN Messenger 的通訊協定 +Comment[zh_TW]=連線到 MSN 的協定 + diff --git a/kopete/protocols/msn/messageformatter.cpp b/kopete/protocols/msn/messageformatter.cpp new file mode 100644 index 00000000..0ca71789 --- /dev/null +++ b/kopete/protocols/msn/messageformatter.cpp @@ -0,0 +1,192 @@ +/* + messageformatter.cpp - msn p2p protocol + + Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include "messageformatter.h" +#include "p2p.h" + +// TQt includes +#include <tqdatastream.h> +#include <tqregexp.h> + +// Kde includes +#include <kdebug.h> + +using P2P::MessageFormatter; +using P2P::Message; + +MessageFormatter::MessageFormatter(TQObject *parent, const char *name) : TQObject(parent, name) +{} + +MessageFormatter::~MessageFormatter() +{} + +Message MessageFormatter::readMessage(const TQByteArray& stream, bool compact) +{ + Message inbound; + + TQ_UINT32 index = 0; + if(compact == false) + { + // Determine the end position of the message header. + while(index < stream.size()) + { + if(stream[index++] == '\n'){ + if(stream[index - 3] == '\n') + break; + } + } + + // Retrieve the message header. + TQString messageHeader = TQCString(stream.data(), index); + + // Retrieve the message mime version, content type, + // and p2p destination. + TQRegExp regex("Content-Type: ([A-Za-z0-9$!*/\\-]*)"); + regex.search(messageHeader); + TQString contentType = regex.cap(1); + + if(contentType != "application/x-msnmsgrp2p") + return inbound; + +// kdDebug(14140) << k_funcinfo << endl; + + regex = TQRegExp("MIME-Version: (\\d.\\d)"); + regex.search(messageHeader); + inbound.mimeVersion = regex.cap(1); + inbound.contentType = contentType; + regex = TQRegExp("P2P-Dest: ([^\r\n]*)"); + regex.search(messageHeader); + TQString destination = regex.cap(1); + } + + TQDataStream reader(stream, IO_ReadOnly); + reader.setByteOrder(TQDataStream::LittleEndian); + // Seek to the start position of the message + // transport header. + reader.device()->at(index); + + // Read the message transport headers from the stream. + reader >> inbound.header.sessionId; + reader >> inbound.header.identifier; + reader >> inbound.header.dataOffset; + reader >> inbound.header.totalDataSize; + reader >> inbound.header.dataSize; + reader >> inbound.header.flag; + reader >> inbound.header.ackSessionIdentifier; + reader >> inbound.header.ackUniqueIdentifier; + reader >> inbound.header.ackDataSize; + + /*kdDebug(14140) + << "session id, " << inbound.header.sessionId << endl + << "identifier, " << inbound.header.identifier << endl + << "data offset, " << inbound.header.dataOffset << endl + << "total size, " << inbound.header.totalDataSize << endl + << "data size, " << inbound.header.dataSize << endl + << "flag, " << inbound.header.flag << endl + << "ack session identifier, " << inbound.header.ackSessionIdentifier << endl + << "ack unique identifier, " << inbound.header.ackUniqueIdentifier << endl + << "ack data size, " << inbound.header.ackDataSize + << endl;*/ + + // Read the message body from the stream. + if(inbound.header.dataSize > 0){ + inbound.body.resize(inbound.header.dataSize); + reader.readRawBytes(inbound.body.data(), inbound.header.dataSize); + } + + if(compact == false) + { + reader.setByteOrder(TQDataStream::BigEndian); + // Read the message application identifier from the stream. + reader >> inbound.applicationIdentifier; + +/* kdDebug(14140) + << "application identifier, " << inbound.applicationIdentifier + << endl;*/ + } + + return inbound; +} + +void MessageFormatter::writeMessage(const Message& message, TQByteArray& stream, bool compact) +{ +// kdDebug(14140) << k_funcinfo << endl; + + TQDataStream writer(stream, IO_WriteOnly); + writer.setByteOrder(TQDataStream::LittleEndian); + + if(compact == false) + { + const TQCString messageHeader = TQString("MIME-Version: 1.0\r\n" + "Content-Type: application/x-msnmsgrp2p\r\n" + "P2P-Dest: " + message.destination + "\r\n" + "\r\n").utf8(); + // Set the capacity of the message buffer. + stream.resize(messageHeader.length() + 48 + message.body.size() + 4); + // Write the message header to the stream + writer.writeRawBytes(messageHeader.data(), messageHeader.length()); + } + else + { + // Set the capacity of the message buffer. + stream.resize(4 + 48 + message.body.size()); + // Write the message size to the stream. + writer << (TQ_INT32)(48+message.body.size()); + } + + + // Write the transport headers to the stream. + writer << message.header.sessionId; + writer << message.header.identifier; + writer << message.header.dataOffset; + writer << message.header.totalDataSize; + writer << message.header.dataSize; + writer << message.header.flag; + writer << message.header.ackSessionIdentifier; + writer << message.header.ackUniqueIdentifier; + writer << message.header.ackDataSize; + +/* kdDebug(14140) + << "session id, " << message.header.sessionId << endl + << "identifier, " << message.header.identifier << endl + << "data offset, " << message.header.dataOffset << endl + << "total size, " << message.header.totalDataSize << endl + << "data size, " << message.header.dataSize << endl + << "flag, " << message.header.flag << endl + << "ack session identifier, " << message.header.ackSessionIdentifier << endl + << "ack unique identifier, " << message.header.ackUniqueIdentifier << endl + << "ack data size, " << message.header.ackDataSize + << endl; +*/ + if(message.body.size() > 0){ + // Write the messge body to the stream. + writer.writeRawBytes(message.body.data(), message.body.size()); + } + + if(compact == false) + { + // Seek to the message application identifier section. + writer.setByteOrder(TQDataStream::BigEndian); + // Write the message application identifier to the stream. + writer << message.applicationIdentifier; + +/* kdDebug(14140) + << "application identifier, " << message.applicationIdentifier + << endl; + */ + } +} + +#include "messageformatter.moc" diff --git a/kopete/protocols/msn/messageformatter.h b/kopete/protocols/msn/messageformatter.h new file mode 100644 index 00000000..fb29c300 --- /dev/null +++ b/kopete/protocols/msn/messageformatter.h @@ -0,0 +1,41 @@ +/* + messageformatter.h - msn p2p protocol + + Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef MESSAGEFORMATTER_H +#define MESSAGEFORMATTER_H + +#include <tqobject.h> + +namespace P2P{ + class Message; +} + +/** +@author Kopete Developers +*/ +namespace P2P{ + class MessageFormatter : public TQObject + { Q_OBJECT + + public: + MessageFormatter(TQObject *parent = 0, const char *name = 0); + ~MessageFormatter(); + + Message readMessage(const TQByteArray& stream, bool compact=false); + void writeMessage(const Message& message, TQByteArray& stream, bool compact=false); + }; +} + +#endif diff --git a/kopete/protocols/msn/msnaccount.cpp b/kopete/protocols/msn/msnaccount.cpp new file mode 100644 index 00000000..c987bf1d --- /dev/null +++ b/kopete/protocols/msn/msnaccount.cpp @@ -0,0 +1,1499 @@ +/* + msnaccount.h - Manages a single MSN account + + Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org> + Copyright (c) 2003 by Martijn Klingens <klingens@kde.org> + Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include "msnaccount.h" + +#include <config.h> + +#include <tdeaction.h> +#include <tdeconfig.h> +#include <kdebug.h> +#include <kinputdialog.h> +#include <tdemessagebox.h> +#include <tdepopupmenu.h> +#include <kstandarddirs.h> +#include <kmdcodec.h> +#include <tdelocale.h> + +#include <tqfile.h> +#include <tqregexp.h> +#include <tqvalidator.h> +#include <tqimage.h> + +#include "msncontact.h" +#include "msnnotifysocket.h" +#include "msnchatsession.h" +#include "kopetecontactlist.h" +#include "kopetegroup.h" +#include "kopetemetacontact.h" +#include "kopetepassword.h" +#include "kopeteuiglobal.h" +#include "kopeteglobal.h" +#include "kopetechatsessionmanager.h" +#include "contactaddednotifydialog.h" +#include "kopeteutils.h" + +#include "sha1.h" + + +#if !defined NDEBUG +#include "msndebugrawcmddlg.h" +#include <tdeglobal.h> +#endif + +#if MSN_WEBCAM +#include "avdevice/videodevicepool.h" +#endif + +MSNAccount::MSNAccount( MSNProtocol *parent, const TQString& AccountID, const char *name ) + : Kopete::PasswordedAccount ( parent, AccountID.lower(), 0, name ) +{ + m_notifySocket = 0L; + m_connectstatus = MSNProtocol::protocol()->NLN; + m_addWizard_metaContact = 0L; + m_newContactList=false; + + // Init the myself contact + setMyself( new MSNContact( this, accountId(), Kopete::ContactList::self()->myself() ) ); + //myself()->setOnlineStatus( MSNProtocol::protocol()->FLN ); + + TQObject::connect( Kopete::ContactList::self(), TQT_SIGNAL( groupRenamed( Kopete::Group *, const TQString & ) ), + TQT_SLOT( slotKopeteGroupRenamed( Kopete::Group * ) ) ); + TQObject::connect( Kopete::ContactList::self(), TQT_SIGNAL( groupRemoved( Kopete::Group * ) ), + TQT_SLOT( slotKopeteGroupRemoved( Kopete::Group * ) ) ); + + TQObject::connect( Kopete::ContactList::self(), TQT_SIGNAL( globalIdentityChanged(const TQString&, const TQVariant& ) ), TQT_SLOT( slotGlobalIdentityChanged(const TQString&, const TQVariant& ) )); + + m_openInboxAction = new TDEAction( i18n( "Open Inbo&x..." ), "mail_generic", 0, this, TQT_SLOT( slotOpenInbox() ), this, "m_openInboxAction" ); + m_changeDNAction = new TDEAction( i18n( "&Change Display Name..." ), TQString(), 0, this, TQT_SLOT( slotChangePublicName() ), this, "renameAction" ); + m_startChatAction = new TDEAction( i18n( "&Start Chat..." ), "mail_generic", 0, this, TQT_SLOT( slotStartChat() ), this, "startChatAction" ); + + + TDEConfigGroup *config=configGroup(); + + m_blockList = config->readListEntry( "blockList" ) ; + m_allowList = config->readListEntry( "allowList" ) ; + m_reverseList = config->readListEntry( "reverseList" ) ; + + // Load the avatar + m_pictureFilename = locateLocal( "appdata", "msnpicture-"+ accountId().lower().replace(TQRegExp("[./~]"),"-") +".png" ); + resetPictureObject(true); + + static_cast<MSNContact *>( myself() )->setInfo( "PHH", config->readEntry("PHH") ); + static_cast<MSNContact *>( myself() )->setInfo( "PHM", config->readEntry("PHM") ); + static_cast<MSNContact *>( myself() )->setInfo( "PHW", config->readEntry("PHW") ); + //this is the display name + static_cast<MSNContact *>( myself() )->setInfo( "MFN", config->readEntry("MFN") ); + + //construct the group list + //Before 2003-11-14 the MSN server allowed us to download the group list without downloading the whole contactlist, but it's not possible anymore + TQPtrList<Kopete::Group> groupList = Kopete::ContactList::self()->groups(); + for ( Kopete::Group *g = groupList.first(); g; g = groupList.next() ) + { + TQString groupGuid=g->pluginData( protocol(), accountId() + " id" ); + if ( !groupGuid.isEmpty() ) + m_groupList.insert( groupGuid , g ); + } + + // Set the client Id for the myself contact. It sets what MSN feature we support. + m_clientId = MSNProtocol::MSNC4 | MSNProtocol::InkFormatGIF | MSNProtocol::SupportMultiPacketMessaging; + +#if MSN_WEBCAM + Kopete::AV::VideoDevicePool::self()->scanDevices(); + if( Kopete::AV::VideoDevicePool::self()->hasDevices() ) + { + m_clientId |= MSNProtocol::SupportWebcam; + } +#endif +} + + +TQString MSNAccount::serverName() +{ + return configGroup()->readEntry( "serverName" , "messenger.hotmail.com" ); +} + +uint MSNAccount::serverPort() +{ + return configGroup()->readNumEntry( "serverPort" , 1863 ); +} + +bool MSNAccount::useHttpMethod() const +{ + return configGroup()->readBoolEntry( "useHttpMethod" , false ); +} + +TQString MSNAccount::myselfClientId() const +{ + return TQString::number(m_clientId, 10); +} + +void MSNAccount::connectWithPassword( const TQString &passwd ) +{ + m_newContactList=false; + if ( isConnected() ) + { + kdDebug( 14140 ) << k_funcinfo <<"Ignoring Connect request " + << "(Already Connected)" << endl; + return; + } + + if ( m_notifySocket ) + { + kdDebug( 14140 ) << k_funcinfo <<"Ignoring Connect request (Already connecting)" << endl; + return; + } + + m_password = passwd; + + if ( m_password.isNull() ) + { + kdDebug( 14140 ) << k_funcinfo <<"Abort connection (null password)" << endl; + return; + } + + + if ( contacts().count() <= 1 ) + { + // Maybe the contactlist.xml has been removed, and the serial number not updated + // ( the 1 is for the myself contact ) + configGroup()->writeEntry( "serial", 0 ); + } + + m_openInboxAction->setEnabled( false ); + + createNotificationServer(serverName(), serverPort()); +} + +void MSNAccount::createNotificationServer( const TQString &host, uint port ) +{ + if(m_notifySocket) //we are switching from one to another notifysocket. + { + //remove every slots to that socket, so we won't delete receive signals + // from the old socket thinking they are from the new one + TQObject::disconnect( m_notifySocket , 0, this, 0 ); + m_notifySocket->deleteLater(); //be sure it will be deleted + m_notifySocket=0L; + } + + m_msgHandle.clear(); + + myself()->setOnlineStatus( MSNProtocol::protocol()->CNT ); + + + m_notifySocket = new MSNNotifySocket( this, accountId() , m_password); + m_notifySocket->setUseHttpMethod( useHttpMethod() ); + + TQObject::connect( m_notifySocket, TQT_SIGNAL( groupAdded( const TQString&, const TQString& ) ), + TQT_SLOT( slotGroupAdded( const TQString&, const TQString& ) ) ); + TQObject::connect( m_notifySocket, TQT_SIGNAL( groupRenamed( const TQString&, const TQString& ) ), + TQT_SLOT( slotGroupRenamed( const TQString&, const TQString& ) ) ); + TQObject::connect( m_notifySocket, TQT_SIGNAL( groupListed( const TQString&, const TQString& ) ), + TQT_SLOT( slotGroupAdded( const TQString&, const TQString& ) ) ); + TQObject::connect( m_notifySocket, TQT_SIGNAL( groupRemoved( const TQString& ) ), + TQT_SLOT( slotGroupRemoved( const TQString& ) ) ); + TQObject::connect( m_notifySocket, TQT_SIGNAL( contactList(const TQString&, const TQString&, const TQString&, uint, const TQString& ) ), + TQT_SLOT( slotContactListed(const TQString&, const TQString&, const TQString&, uint, const TQString& ) ) ); + TQObject::connect( m_notifySocket, TQT_SIGNAL(contactAdded(const TQString&, const TQString&, const TQString&, const TQString&, const TQString& ) ), + TQT_SLOT( slotContactAdded(const TQString&, const TQString&, const TQString&, const TQString&, const TQString& ) ) ); + TQObject::connect( m_notifySocket, TQT_SIGNAL( contactRemoved(const TQString&, const TQString&, const TQString&, const TQString& ) ), + TQT_SLOT( slotContactRemoved(const TQString&, const TQString&, const TQString&, const TQString& ) ) ); + TQObject::connect( m_notifySocket, TQT_SIGNAL( statusChanged( const Kopete::OnlineStatus & ) ), + TQT_SLOT( slotStatusChanged( const Kopete::OnlineStatus & ) ) ); + TQObject::connect( m_notifySocket, TQT_SIGNAL( invitedToChat( const TQString&, const TQString&, const TQString&, const TQString&, const TQString& ) ), + TQT_SLOT( slotCreateChat( const TQString&, const TQString&, const TQString&, const TQString&, const TQString& ) ) ); + TQObject::connect( m_notifySocket, TQT_SIGNAL( startChat( const TQString&, const TQString& ) ), + TQT_SLOT( slotCreateChat( const TQString&, const TQString& ) ) ); + TQObject::connect( m_notifySocket, TQT_SIGNAL( socketClosed() ), + TQT_SLOT( slotNotifySocketClosed() ) ); + TQObject::connect( m_notifySocket, TQT_SIGNAL( newContactList() ), + TQT_SLOT( slotNewContactList() ) ); + TQObject::connect( m_notifySocket, TQT_SIGNAL( receivedNotificationServer(const TQString&, uint ) ), + TQT_SLOT(createNotificationServer(const TQString&, uint ) ) ); + TQObject::connect( m_notifySocket, TQT_SIGNAL( hotmailSeted( bool ) ), + m_openInboxAction, TQT_SLOT( setEnabled( bool ) ) ); + TQObject::connect( m_notifySocket, TQT_SIGNAL( errorMessage(int, const TQString& ) ), + TQT_SLOT( slotErrorMessageReceived(int, const TQString& ) ) ); + + m_notifySocket->setStatus( m_connectstatus ); + m_notifySocket->connect(host, port); +} + +void MSNAccount::disconnect() +{ + if ( m_notifySocket ) + m_notifySocket->disconnect(); +} + +TDEActionMenu * MSNAccount::actionMenu() +{ + TDEActionMenu *m_actionMenu=Kopete::Account::actionMenu(); + if ( isConnected() ) + { + m_openInboxAction->setEnabled( true ); + m_startChatAction->setEnabled( true ); + m_changeDNAction->setEnabled( true ); + } + else + { + m_openInboxAction->setEnabled( false ); + m_startChatAction->setEnabled( false ); + m_changeDNAction->setEnabled( false ); + } + + m_actionMenu->popupMenu()->insertSeparator(); + + m_actionMenu->insert( m_changeDNAction ); + m_actionMenu->insert( m_startChatAction ); + +// m_actionMenu->popupMenu()->insertSeparator(); + + m_actionMenu->insert( m_openInboxAction ); + +#if !defined NDEBUG + TDEActionMenu *debugMenu = new TDEActionMenu( "Debug", m_actionMenu ); + debugMenu->insert( new TDEAction( i18n( "Send Raw C&ommand..." ), 0, + this, TQT_SLOT( slotDebugRawCommand() ), debugMenu, "m_debugRawCommand" ) ); + m_actionMenu->popupMenu()->insertSeparator(); + m_actionMenu->insert( debugMenu ); +#endif + + return m_actionMenu; +} + +MSNNotifySocket *MSNAccount::notifySocket() +{ + return m_notifySocket; +} + + +void MSNAccount::setOnlineStatus( const Kopete::OnlineStatus &status , const TQString &reason) +{ + kdDebug( 14140 ) << k_funcinfo << status.description() << endl; + + // HACK: When changing song, do don't anything while connected + if( reason.contains("[Music]") && ( status == MSNProtocol::protocol()->UNK || status == MSNProtocol::protocol()->CNT ) ) + return; + + // Only send personal message when logged. + if( m_notifySocket && m_notifySocket->isLogged() ) + { + // Only update the personal/status message, don't change the online status + // since it's the same. + if( reason.contains("[Music]") ) + { + TQString personalMessage = reason.section("[Music]", 1); + setPersonalMessage( MSNProtocol::PersonalMessageMusic, personalMessage ); + + // Don't send un-needed status change. + return; + } + else + { + setPersonalMessage( MSNProtocol::PersonalMessageNormal, reason ); + } + } + + if(status.status()== Kopete::OnlineStatus::Offline) + disconnect(); + else if ( m_notifySocket ) + { + m_notifySocket->setStatus( status ); + } + else + { + m_connectstatus = status; + connect(); + } + + +} + +void MSNAccount::slotStartChat() +{ + + bool ok; + TQString handle = KInputDialog::getText( i18n( "Start Chat - MSN Plugin" ), + i18n( "Please enter the email address of the person with whom you want to chat:" ), TQString(), &ok ).lower(); + if ( ok ) + { + if ( MSNProtocol::validContactId( handle ) ) + { + if ( !contacts()[ handle ] ) + addContact( handle, handle, 0L, Kopete::Account::Temporary ); + + contacts()[ handle ]->execute(); + } + else + { + KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry, + i18n( "<qt>You must enter a valid email address.</qt>" ), i18n( "MSN Plugin" ) ); + } + } +} + +void MSNAccount::slotDebugRawCommand() +{ +#if !defined NDEBUG + if ( !isConnected() ) + return; + + MSNDebugRawCmdDlg *dlg = new MSNDebugRawCmdDlg( 0L ); + int result = dlg->exec(); + if ( result == TQDialog::Accepted && m_notifySocket ) + { + m_notifySocket->sendCommand( dlg->command(), dlg->params(), + dlg->addId(), dlg->msg().replace( "\n", "\r\n" ).utf8() ); + } + delete dlg; +#endif +} + +void MSNAccount::slotChangePublicName() +{ + if ( !isConnected() ) + { + return; + //TODO: change it anyway, and sync at the next connection + } + + bool ok; + TQString name = KInputDialog::getText( i18n( "Change Display Name - MSN Plugin" ), + i18n( "Enter the new display name by which you want to be visible to your friends on MSN:" ), + myself()->property( Kopete::Global::Properties::self()->nickName()).value().toString(), &ok ); + + if ( ok ) + { + if ( name.length() > 387 ) + { + KMessageBox::error( Kopete::UI::Global::mainWidget(), + i18n( "<qt>The display name you entered is too long. Please use a shorter name.\n" + "Your display name has <b>not</b> been changed.</qt>" ), + i18n( "Change Display Name - MSN Plugin" ) ); + return; + } + + setPublicName( name ); + } +} + + +void MSNAccount::slotOpenInbox() +{ + if ( m_notifySocket ) + m_notifySocket->slotOpenInbox(); +} + + +void MSNAccount::slotNotifySocketClosed() +{ + kdDebug( 14140 ) << k_funcinfo << endl; + + Kopete::Account::DisconnectReason reason=(Kopete::Account::DisconnectReason)(m_notifySocket->disconnectReason()); + m_notifySocket->deleteLater(); + m_notifySocket = 0l; + myself()->setOnlineStatus( MSNProtocol::protocol()->FLN ); + setAllContactsStatus( MSNProtocol::protocol()->FLN ); + disconnected(reason); + + + if(reason == Kopete::Account::OtherClient) + { //close all chat sessions, so new message will arive to the other client. + + TQValueList<Kopete::ChatSession*> sessions = Kopete::ChatSessionManager::self()->sessions(); + TQValueList<Kopete::ChatSession*>::Iterator it; + for (it=sessions.begin() ; it != sessions.end() ; it++ ) + { + MSNChatSession *msnCS = dynamic_cast<MSNChatSession *>( *it ); + if ( msnCS && msnCS->account() == this ) + { + msnCS->slotCloseSession(); + } + } + } + +#if 0 + else if ( state == 0x10 ) // connection died unexpectedly + { + KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Error , i18n( "The connection with the MSN server was lost unexpectedly.\n" + "If you cannot reconnect now, the server might be down. In that case, please try again later." ), + i18n( "Connection Lost - MSN Plugin" ), KMessageBox::Notify ); + } +#endif + m_msgHandle.clear(); + // kdDebug( 14140 ) << "MSNAccount::slotNotifySocketClosed - done" << endl; +} + +void MSNAccount::slotStatusChanged( const Kopete::OnlineStatus &status ) +{ +// kdDebug( 14140 ) << k_funcinfo << status.internalStatus() << endl; + myself()->setOnlineStatus( status ); + + if(m_newContactList) + { + m_newContactList=false; + + TQDictIterator<Kopete::Contact> it( contacts() ); + for ( ; it.current(); ++it ) + { + MSNContact *c = static_cast<MSNContact *>( *it ); + if(c && c->isDeleted() && c->metaContact() && !c->metaContact()->isTemporary() && c!=myself()) + { + if(c->serverGroups().isEmpty()) + { //the contact is new, add it on the server + c->setOnlineStatus( MSNProtocol::protocol()->FLN ); + addContactServerside( c->contactId() , c->metaContact()->groups() ); + } + else //the contact had been deleted, remove it. + { + c->deleteLater(); + } + } + } + } +} + + +void MSNAccount::slotPersonalMessageChanged( const TQString& personalMessage ) +{ + TQString oldPersonalMessage=myself()->property(MSNProtocol::protocol()->propPersonalMessage).value().toString() ; + if ( personalMessage != oldPersonalMessage ) + { + myself()->setProperty( MSNProtocol::protocol()->propPersonalMessage, personalMessage ); + configGroup()->writeEntry( "personalMessage" , personalMessage ); + } +} + +void MSNAccount::setPublicName( const TQString &publicName ) +{ + if ( m_notifySocket ) + { + m_notifySocket->changePublicName( publicName, TQString() ); + } +} + +void MSNAccount::setPersonalMessage( MSNProtocol::PersonalMessageType type, const TQString &personalMessage ) +{ + if ( m_notifySocket ) + { + m_notifySocket->changePersonalMessage( type, personalMessage ); + } + /* Eh, if we can't change the display name, don't let make the user think it has changed + else if(type == MSNProtocol::PersonalMessageNormal) // Normal personalMessage, not a dynamic one that need formatting. + { + slotPersonalMessageChanged( personalMessage ); + }*/ +} + +void MSNAccount::slotGroupAdded( const TQString& groupName, const TQString &groupGuid ) +{ + if ( m_groupList.contains( groupGuid ) ) + { + // Group can already be in the list since the idle timer does a 'List Groups' + // command. Simply return, don't issue a warning. + // kdDebug( 14140 ) << k_funcinfo << "Group " << groupName << " already in list, skipped." << endl; + return; + } + + //--------- Find the appropriate Kopete::Group, or create one ---------// + TQPtrList<Kopete::Group> groupList = Kopete::ContactList::self()->groups(); + Kopete::Group *fallBack = 0L; + + //check if we have one in the old group list. if yes, update the id translate map. + for(TQMap<TQString, Kopete::Group*>::Iterator it=m_oldGroupList.begin() ; it != m_oldGroupList.end() ; ++it ) + { + Kopete::Group *g=it.data(); + if (g && g->pluginData( protocol(), accountId() + " displayName" ) == groupName && + g->pluginData( protocol(), accountId() + " id" ).isEmpty() ) + { //it has the same name! we got it. (and it is not yet an msn group) + fallBack=g; + /*if ( g->displayName() != groupName ) + { + // The displayName was changed in Kopete while we were offline + // FIXME: update the server right now + }*/ + break; + } + } + + if(!fallBack) + { + //it's certenly a new group ! search if one already exist with the same displayname. + for ( Kopete::Group *g = groupList.first(); g; g = groupList.next() ) + { + /* --This has been replaced by the loop right before. + if ( !g->pluginData( protocol(), accountId() + " id" ).isEmpty() ) + { + if ( g->pluginData( protocol(), accountId() + " id" ).toUInt() == groupNumber ) + { + m_groupList.insert( groupNumber, g ); + TQString oldGroupName; + if ( g->pluginData( protocol(), accountId() + " displayName" ) != groupName ) + { + // The displayName of the group has been modified by another client + slotGroupRenamed( groupName, groupNumber ); + } + return; + } + } + else {*/ + if ( g->displayName() == groupName && (groupGuid.isEmpty()|| g->type()==Kopete::Group::Normal) && + g->pluginData( protocol(), accountId() + " id" ).isEmpty() ) + { + fallBack = g; + kdDebug( 14140 ) << k_funcinfo << "We didn't found the group " << groupName <<" in the old MSN group. But kopete has already one with the same name." << endl; + break; + } + } + } + + if ( !fallBack ) + { + if( groupGuid.isEmpty() ) + { // The group #0 is an unremovable group. his default name is "~" , + // but the official client rename it i18n("others contact") at the first + // connection. + // In many case, the users don't use that group as a real group, or just as + // a group to put all contact that are not sorted. + fallBack = Kopete::Group::topLevel(); + } + else + { + fallBack = new Kopete::Group( groupName ); + Kopete::ContactList::self()->addGroup( fallBack ); + kdDebug( 14140 ) << k_funcinfo << "We didn't found the group " << groupName <<" So we're creating a new one." << endl; + + } + } + + fallBack->setPluginData( protocol(), accountId() + " id", groupGuid ); + fallBack->setPluginData( protocol(), accountId() + " displayName", groupName ); + m_groupList.insert( groupGuid, fallBack ); + + // We have pending groups that we need add a contact to + if ( tmp_addToNewGroup.contains(groupName) ) + { + TQStringList list=tmp_addToNewGroup[groupName]; + for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it ) + { + TQString contactId = *it; + kdDebug( 14140 ) << k_funcinfo << "Adding to new group: " << contactId << endl; + MSNContact *c = static_cast<MSNContact *>(contacts()[contactId]); + if(c && c->hasProperty(MSNProtocol::protocol()->propGuid.key()) ) + notifySocket()->addContact( contactId, MSNProtocol::FL, TQString(), c->guid(), groupGuid ); + else + { + // If we get to here, we're currently adding a new contact, add the groupGUID to the groupList + // to add when contact will be added to contactlist. + if( tmp_addNewContactToGroup.contains( contactId ) ) + tmp_addNewContactToGroup[contactId].append(groupGuid); + else + tmp_addNewContactToGroup.insert(contactId, TQStringList(groupGuid) ); + } + } + tmp_addToNewGroup.remove(groupName); + } +} + +void MSNAccount::slotGroupRenamed( const TQString &groupGuid, const TQString& groupName ) +{ + if ( m_groupList.contains( groupGuid ) ) + { + m_groupList[ groupGuid ]->setPluginData( protocol(), accountId() + " id", groupGuid ); + m_groupList[ groupGuid ]->setPluginData( protocol(), accountId() + " displayName", groupName ); + m_groupList[ groupGuid ]->setDisplayName( groupName ); + } + else + { + slotGroupAdded( groupName, groupGuid ); + } +} + +void MSNAccount::slotGroupRemoved( const TQString& groupGuid ) +{ + if ( m_groupList.contains( groupGuid ) ) + { + m_groupList[ groupGuid ]->setPluginData( protocol(), TQMap<TQString,TQString>() ); + m_groupList.remove( groupGuid ); + } +} + +void MSNAccount::addGroup( const TQString &groupName, const TQString& contactToAdd ) +{ + if ( !contactToAdd.isNull() ) + { + if( tmp_addToNewGroup.contains(groupName) ) + { + tmp_addToNewGroup[groupName].append(contactToAdd); + //A group with the same name is about to be added, + // we don't need to add a second group with the same name + kdDebug( 14140 ) << k_funcinfo << "no need to re-add " << groupName << " for " << contactToAdd << endl; + return; + } + else + { + tmp_addToNewGroup.insert(groupName,TQStringList(contactToAdd)); + kdDebug( 14140 ) << k_funcinfo << "preparing to add " << groupName << " for " << contactToAdd << endl; + } + } + + if ( m_notifySocket ) + m_notifySocket->addGroup( groupName ); + +} + +void MSNAccount::slotKopeteGroupRenamed( Kopete::Group *g ) +{ + if ( notifySocket() && g->type() == Kopete::Group::Normal ) + { + if ( !g->pluginData( protocol(), accountId() + " id" ).isEmpty() && + g->displayName() != g->pluginData( protocol(), accountId() + " displayName" ) && + m_groupList.contains( g->pluginData( protocol(), accountId() + " id" ) ) ) + { + notifySocket()->renameGroup( g->displayName(), g->pluginData( protocol(), accountId() + " id" ) ); + } + } +} + +void MSNAccount::slotKopeteGroupRemoved( Kopete::Group *g ) +{ + //The old gorup list is only used whe syncing the contactlist. + //We can assume the contactlist is already fully synced at this time. + //The group g is maybe in the oldGroupList. We remove everithing since + //we don't need it anymore, no need to search it + m_oldGroupList.clear(); + + + if ( !g->pluginData( protocol(), accountId() + " id" ).isEmpty() ) + { + TQString groupGuid = g->pluginData( protocol(), accountId() + " id" ); + if ( !m_groupList.contains( groupGuid ) ) + { + // the group is maybe already removed in the server + slotGroupRemoved( groupGuid ); + return; + } + + //this is also done later, but he have to do it now! + // (in slotGroupRemoved) + m_groupList.remove(groupGuid); + + if ( groupGuid.isEmpty() ) + { + // the group #0 can't be deleted + // then we set it as the top-level group + if ( g->type() == Kopete::Group::TopLevel ) + return; + + Kopete::Group::topLevel()->setPluginData( protocol(), accountId() + " id", "" ); + Kopete::Group::topLevel()->setPluginData( protocol(), accountId() + " displayName", g->pluginData( protocol(), accountId() + " displayName" ) ); + g->setPluginData( protocol(), accountId() + " id", TQString() ); // the group should be soon deleted, but make sure + + return; + } + + if ( m_notifySocket ) + { + bool still_have_contact=false; + // if contact are contains only in the group we are removing, abort the + TQDictIterator<Kopete::Contact> it( contacts() ); + for ( ; it.current(); ++it ) + { + MSNContact *c = static_cast<MSNContact *>( it.current() ); + if ( c && c->serverGroups().contains( groupGuid ) ) + { + /** don't do that becasue theses may already have been sent + m_notifySocket->removeContact( c->contactId(), groupNumber, MSNProtocol::FL ); + */ + still_have_contact=true; + break; + } + } + if(!still_have_contact) + m_notifySocket->removeGroup( groupGuid ); + } + } +} + +void MSNAccount::slotNewContactList() +{ + m_oldGroupList=m_groupList; + for(TQMap<TQString, Kopete::Group*>::Iterator it=m_oldGroupList.begin() ; it != m_oldGroupList.end() ; ++it ) + { //they are about to be changed + if(it.data()) + it.data()->setPluginData( protocol(), accountId() + " id", TQString() ); + } + + m_allowList.clear(); + m_blockList.clear(); + m_reverseList.clear(); + m_groupList.clear(); + TDEConfigGroup *config=configGroup(); + config->writeEntry( "blockList" , TQString() ) ; + config->writeEntry( "allowList" , TQString() ); + config->writeEntry( "reverseList" , TQString() ); + + // clear all date information which will be received. + // if the information is not anymore on the server, it will not be received + TQDictIterator<Kopete::Contact> it( contacts() ); + for ( ; it.current(); ++it ) + { + MSNContact *c = static_cast<MSNContact *>( *it ); + c->setBlocked( false ); + c->setAllowed( false ); + c->setReversed( false ); + c->setDeleted( true ); + c->setInfo( "PHH", TQString() ); + c->setInfo( "PHW", TQString() ); + c->setInfo( "PHM", TQString() ); + c->removeProperty( MSNProtocol::protocol()->propGuid ); + } + m_newContactList=true; +} + +void MSNAccount::slotContactListed( const TQString& handle, const TQString& publicName, const TQString &contactGuid, uint lists, const TQString& groups ) +{ + // On empty lists handle might be empty, ignore that + // ignore also the myself contact. + if ( handle.isEmpty() || handle==accountId()) + return; + + MSNContact *c = static_cast<MSNContact *>( contacts()[ handle ] ); + + if ( lists & 1 ) // FL + { + TQStringList contactGroups = TQStringList::split( ",", groups, false ); + if ( c ) + { + if( !c->metaContact() ) + { + kdWarning( 14140 ) << k_funcinfo << "the contact " << c->contactId() << " has no meta contact" <<endl; + Kopete::MetaContact *metaContact = new Kopete::MetaContact(); + + c->setMetaContact(metaContact); + Kopete::ContactList::self()->addMetaContact( metaContact ); + } + + // Contact exists, update data. + // Merging difference between server contact list and Kopete::Contact's contact list into MetaContact's contact-list + c->setOnlineStatus( MSNProtocol::protocol()->FLN ); + if(!publicName.isEmpty() && publicName!=handle) + c->setProperty( Kopete::Global::Properties::self()->nickName() , publicName ); + else + c->removeProperty( Kopete::Global::Properties::self()->nickName() ); + c->setProperty( MSNProtocol::protocol()->propGuid, contactGuid); + + const TQMap<TQString, Kopete::Group *> oldServerGroups = c->serverGroups(); + c->clearServerGroups(); + for ( TQStringList::ConstIterator it = contactGroups.begin(); it != contactGroups.end(); ++it ) + { + TQString newServerGroupID = *it; + if(m_groupList.contains(newServerGroupID)) + { + Kopete::Group *newServerGroup=m_groupList[ newServerGroupID ] ; + c->contactAddedToGroup( newServerGroupID, newServerGroup ); + if( !c->metaContact()->groups().contains(newServerGroup) ) + { + // The contact has been added in a group by another client + c->metaContact()->addToGroup( newServerGroup ); + } + } + } + + for ( TQMap<TQString, Kopete::Group *>::ConstIterator it = oldServerGroups.begin(); it != oldServerGroups.end(); ++it ) + { + Kopete::Group *old_group=m_oldGroupList[it.key()]; + if(old_group) + { + TQString oldnewID=old_group->pluginData(protocol() , accountId() +" id"); + if ( !oldnewID.isEmpty() && contactGroups.contains( oldnewID ) ) + continue; //ok, it's correctn no need to do anything. + + c->metaContact()->removeFromGroup( old_group ); + } + } + + c->setDeleted(false); + + // Update server if the contact has been moved to another group while MSN was offline + c->sync(); + } + else + { + Kopete::MetaContact *metaContact = new Kopete::MetaContact(); + + c = new MSNContact( this, handle, metaContact ); + c->setDeleted(true); //we don't want to sync + c->setOnlineStatus( MSNProtocol::protocol()->FLN ); + if(!publicName.isEmpty() && publicName!=handle) + c->setProperty( Kopete::Global::Properties::self()->nickName() , publicName ); + else + c->removeProperty( Kopete::Global::Properties::self()->nickName() ); + c->setProperty( MSNProtocol::protocol()->propGuid, contactGuid ); + + for ( TQStringList::Iterator it = contactGroups.begin(); + it != contactGroups.end(); ++it ) + { + TQString groupGuid = *it; + if(m_groupList.contains(groupGuid)) + { + c->contactAddedToGroup( groupGuid, m_groupList[ groupGuid ] ); + metaContact->addToGroup( m_groupList[ groupGuid ] ); + } + } + Kopete::ContactList::self()->addMetaContact( metaContact ); + + c->setDeleted(false); + } + } + else //the contact is _not_ in the FL, it has been removed + { + if(c) + { + c->setOnlineStatus( static_cast<MSNProtocol*>(protocol())->UNK ); + c->clearServerGroups(); + //TODO: display a message and suggest to remove the contact. + // but i fear a simple messageBox QuestionYesNo here gives a nice crash. + //delete ct; + } + } + if ( lists & 2 ) + slotContactAdded( handle, "AL", publicName, TQString(), TQString() ); + else if(c) + c->setAllowed(false); + if ( lists & 4 ) + slotContactAdded( handle, "BL", publicName, TQString(), TQString() ); + else if(c) + c->setBlocked(false); + if ( lists & 8 ) + slotContactAdded( handle, "RL", publicName, TQString(), TQString() ); + else if(c) + c->setReversed(false); + if ( lists & 16 ) // This contact is on the pending list. Add to the reverse list and delete from the pending list + { + notifySocket()->addContact( handle, MSNProtocol::RL, TQString(), TQString(), TQString() ); + notifySocket()->removeContact( handle, MSNProtocol::PL, TQString(), TQString() ); + } +} + +void MSNAccount::slotContactAdded( const TQString& handle, const TQString& list, const TQString& publicName, const TQString& contactGuid, const TQString &groupGuid ) +{ + if ( list == "FL" ) + { + bool new_contact = false; + if ( !contacts()[ handle ] ) + { + new_contact = true; + + Kopete::MetaContact *m= m_addWizard_metaContact ? m_addWizard_metaContact : new Kopete::MetaContact(); + + MSNContact *c = new MSNContact( this, handle, m ); + if(!publicName.isEmpty() && publicName!=handle) + c->setProperty( Kopete::Global::Properties::self()->nickName() , publicName ); + else + c->removeProperty( Kopete::Global::Properties::self()->nickName() ); + c->setProperty( MSNProtocol::protocol()->propGuid, contactGuid ); + // Add the new contact to the group he belongs. + if ( tmp_addNewContactToGroup.contains(handle) ) + { + TQStringList list = tmp_addNewContactToGroup[handle]; + for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it ) + { + TQString groupGuid = *it; + + // If the group didn't exist yet (yay for async operations), don't add the contact to the group + // Let slotGroupAdded do it. + if( m_groupList.contains(groupGuid) ) + { + kdDebug( 14140 ) << k_funcinfo << "Adding " << handle << " to group: " << groupGuid << endl; + notifySocket()->addContact( handle, MSNProtocol::FL, TQString(), contactGuid, groupGuid ); + + c->contactAddedToGroup( groupGuid, m_groupList[ groupGuid ] ); + + m->addToGroup( m_groupList[ groupGuid ] ); + + } + if ( !m_addWizard_metaContact ) + { + Kopete::ContactList::self()->addMetaContact( m ); + } + } + tmp_addNewContactToGroup.remove(handle); + } + + c->setOnlineStatus( MSNProtocol::protocol()->FLN ); + + m_addWizard_metaContact = 0L; + } + if ( !new_contact ) + { + // Contact has been added to a group + MSNContact *c = findContactByGuid(contactGuid); + if(c != 0L) + { + // Make sure that the contact has always his contactGUID. + if( !c->hasProperty(MSNProtocol::protocol()->propGuid.key()) ) + c->setProperty( MSNProtocol::protocol()->propGuid, contactGuid ); + + if ( c->onlineStatus() == MSNProtocol::protocol()->UNK ) + c->setOnlineStatus( MSNProtocol::protocol()->FLN ); + + if ( c->metaContact() && c->metaContact()->isTemporary() ) + c->metaContact()->setTemporary( false, m_groupList.contains( groupGuid ) ? m_groupList[ groupGuid ] : 0L ); + else + { + if(m_groupList.contains(groupGuid)) + { + if( c->metaContact() ) + c->metaContact()->addToGroup( m_groupList[groupGuid] ); + c->contactAddedToGroup( groupGuid, m_groupList[ groupGuid ] ); + } + } + } + } + + if ( !handle.isEmpty() && !m_allowList.contains( handle ) && !m_blockList.contains( handle ) ) + { + kdDebug(14140) << k_funcinfo << "Trying to add contact to AL. " << endl; + notifySocket()->addContact(handle, MSNProtocol::AL, TQString(), TQString(), TQString() ); + } + } + else if ( list == "BL" ) + { + if ( contacts()[ handle ] ) + static_cast<MSNContact *>( contacts()[ handle ] )->setBlocked( true ); + if ( !m_blockList.contains( handle ) ) + { + m_blockList.append( handle ); + configGroup()->writeEntry( "blockList" , m_blockList ) ; + } + } + else if ( list == "AL" ) + { + if ( contacts()[ handle ] ) + static_cast<MSNContact *>( contacts()[ handle ] )->setAllowed( true ); + if ( !m_allowList.contains( handle ) ) + { + m_allowList.append( handle ); + configGroup()->writeEntry( "allowList" , m_allowList ) ; + } + } + else if ( list == "RL" ) + { + // search for new Contacts + Kopete::Contact *ct=contacts()[ handle ]; + if ( !ct || !ct->metaContact() || ct->metaContact()->isTemporary() ) + { + // Users in the allow list or block list now never trigger the + // 'new user' dialog, which makes it impossible to add those here. + // Not necessarily bad, but the usability effects need more thought + // before I declare it good :- ) + if ( !m_allowList.contains( handle ) && !m_blockList.contains( handle ) ) + { + TQString nick; //in most case, the public name is not know + if(publicName!=handle) // so we don't whos it if it is not know + nick=publicName; + Kopete::UI::ContactAddedNotifyDialog *dialog= + new Kopete::UI::ContactAddedNotifyDialog( handle,nick,this, + Kopete::UI::ContactAddedNotifyDialog::InfoButton ); + TQObject::connect(dialog,TQT_SIGNAL(applyClicked(const TQString&)), + this,TQT_SLOT(slotContactAddedNotifyDialogClosed(const TQString& ))); + dialog->show(); + } + } + else + { + static_cast<MSNContact *>( ct )->setReversed( true ); + } + m_reverseList.append( handle ); + configGroup()->writeEntry( "reverseList" , m_reverseList ) ; + } +} + +void MSNAccount::slotContactRemoved( const TQString& handle, const TQString& list, const TQString& contactGuid, const TQString& groupGuid ) +{ + kdDebug( 14140 ) << k_funcinfo << "handle: " << handle << " list: " << list << " contact-uid: " << contactGuid << endl; + MSNContact *c=static_cast<MSNContact *>( contacts()[ handle ] ); + if ( list == "BL" ) + { + m_blockList.remove( handle ); + configGroup()->writeEntry( "blockList" , m_blockList ) ; + if ( !m_allowList.contains( handle ) ) + notifySocket()->addContact( handle, MSNProtocol::AL, TQString(), TQString(), TQString() ); + + if(c) + c->setBlocked( false ); + } + else if ( list == "AL" ) + { + m_allowList.remove( handle ); + configGroup()->writeEntry( "allowList" , m_allowList ) ; + if ( !m_blockList.contains( handle ) ) + notifySocket()->addContact( handle, MSNProtocol::BL, TQString(), TQString(), TQString() ); + + if(c) + c->setAllowed( false ); + } + else if ( list == "RL" ) + { + m_reverseList.remove( handle ); + configGroup()->writeEntry( "reverseList" , m_reverseList ) ; + + if ( c ) + { + // Contact is removed from the reverse list + // only MSN can do this, so this is currently not supported + c->setReversed( false ); + /* + InfoWidget *info = new InfoWidget( 0 ); + info->title->setText( "<b>" + i18n( "Contact removed!" ) +"</b>" ); + TQString dummy; + dummy = "<center><b>" + imContact->getPublicName() + "( " +imContact->getHandle() +" )</b></center><br>"; + dummy += i18n( "has removed you from his contact list!" ) + "<br>"; + dummy += i18n( "This contact is now removed from your contact list" ); + info->infoText->setText( dummy ); + info->setCaption( "KMerlin - Info" ); + info->show(); + */ + } + } + else if ( list == "FL" ) + { + // The FL list only use the contact GUID, use the contact referenced by the GUID. + MSNContact *contactRemoved = findContactByGuid(contactGuid); + TQStringList groupGuidList; + bool deleteContact = groupGuid.isEmpty() ? true : false; // Delete the contact when the group GUID is empty. + // Remove the contact from the contact list for all the group he is a member. + if( groupGuid.isEmpty() ) + { + if(contactRemoved) + { + TQPtrList<Kopete::Group> groupList = contactRemoved->metaContact()->groups(); + for( TQPtrList<Kopete::Group>::Iterator it = groupList.begin(); it != groupList.end(); ++it ) + { + Kopete::Group *group = *it; + if ( !group->pluginData( protocol(), accountId() + " id" ).isEmpty() ) + { + groupGuidList.append( group->pluginData( protocol(), accountId() + " id" ) ); + } + } + } + } + else + { + groupGuidList.append( groupGuid ); + } + + if( !groupGuidList.isEmpty() ) + { + TQStringList::const_iterator stringIt; + for( stringIt = groupGuidList.begin(); stringIt != groupGuidList.end(); ++stringIt ) + { + // Contact is removed from the FL list, remove it from the group + if(contactRemoved != 0L) + contactRemoved->contactRemovedFromGroup( *stringIt ); + + //check if the group is now empty to remove it + if ( m_notifySocket ) + { + bool still_have_contact=false; + // if contact are contains only in the group we are removing, abort the + TQDictIterator<Kopete::Contact> it( contacts() ); + for ( ; it.current(); ++it ) + { + MSNContact *c2 = static_cast<MSNContact *>( it.current() ); + if ( c2->serverGroups().contains( *stringIt ) ) + { + still_have_contact=true; + break; + } + } + if(!still_have_contact) + m_notifySocket->removeGroup( *stringIt ); + } + } + } + if(deleteContact && contactRemoved) + { + kdDebug(14140) << k_funcinfo << "Deleting the MSNContact " << contactRemoved->contactId() << endl; + contactRemoved->deleteLater(); + } + } +} + +void MSNAccount::slotCreateChat( const TQString& address, const TQString& auth ) +{ + slotCreateChat( 0L, address, auth, m_msgHandle.first(), m_msgHandle.first() ); +} + +void MSNAccount::slotCreateChat( const TQString& ID, const TQString& address, const TQString& auth, + const TQString& handle_, const TQString& publicName ) +{ + TQString handle = handle_.lower(); + + if ( handle.isEmpty() ) + { + // we have lost the handle? + kdDebug(14140) << k_funcinfo << "Impossible to open a chat session, I forgot the contact to invite" <<endl; + // forget it + return; + } + +// kdDebug( 14140 ) << k_funcinfo <<"Creating chat for " << handle << endl; + + if ( !contacts()[ handle ] ) + addContact( handle, publicName, 0L, Kopete::Account::Temporary ); + + MSNContact *c = static_cast<MSNContact *>( contacts()[ handle ] ); + + if ( c && myself() ) + { + // we can't use simply c->manager(true) here to get the manager, because this will re-open + // another chat session, and then, close this new one. We have to re-create the manager manualy. + MSNChatSession *manager = dynamic_cast<MSNChatSession*>( c->manager( Kopete::Contact::CannotCreate ) ); + if(!manager) + { + Kopete::ContactPtrList chatmembers; + chatmembers.append(c); + manager = new MSNChatSession( protocol(), myself(), chatmembers ); + } + + manager->createChat( handle, address, auth, ID ); + + /** + * This code should open a chatwindow when a socket is open + * It has been disabled because pidgin open switchboeard too often + * + * the solution is to open the window only when the contact start typing + * see MSNChatSession::receivedTypingMsg + * + + TDEGlobal::config()->setGroup( "MSN" ); + bool notifyNewChat = TDEGlobal::config()->readBoolEntry( "NotifyNewChat", false ); + if ( !ID.isEmpty() && notifyNewChat ) + { + // this temporary message should open the window if they not exist + TQString body = i18n( "%1 has started a chat with you" ).arg( c->metaContact()->displayName() ); + Kopete::Message tmpMsg = Kopete::Message( c, manager->members(), body, Kopete::Message::Internal, Kopete::Message::PlainText ); + manager->appendMessage( tmpMsg ); + } + */ + } + + if(!m_msgHandle.isEmpty()) + m_msgHandle.pop_front(); +} + +void MSNAccount::slotStartChatSession( const TQString& handle ) +{ + // First create a message manager, because we might get an existing + // manager back, in which case we likely also have an active switchboard + // connection to reuse... + + MSNContact *c = static_cast<MSNContact *>( contacts()[ handle ] ); + // if ( isConnected() && c && myself() && handle != m_msnId ) + if ( m_notifySocket && c && myself() && handle != accountId() ) + { + if ( !c->manager(Kopete::Contact::CannotCreate) || !static_cast<MSNChatSession *>( c->manager( Kopete::Contact::CanCreate ) )->service() ) + { + m_msgHandle.prepend(handle); + m_notifySocket->createChatSession(); + } + } +} + +void MSNAccount::slotContactAddedNotifyDialogClosed(const TQString& handle) +{ + const Kopete::UI::ContactAddedNotifyDialog *dialog = + dynamic_cast<const Kopete::UI::ContactAddedNotifyDialog *>(sender()); + if(!dialog || !m_notifySocket) + return; + + if(dialog->added()) + { + Kopete::MetaContact *mc=dialog->addContact(); + if(mc) + { //if the contact has been added this way, it's because the other user added us. + // don't forgot to set the reversed flag (Bug 114400) + MSNContact *c=dynamic_cast<MSNContact*>(mc->contacts().first()); + if(c && c->contactId() == handle ) + { + c->setReversed( true ); + } + } + } + + if ( !dialog->authorized() ) + { + if ( m_allowList.contains( handle ) ) + m_notifySocket->removeContact( handle, MSNProtocol::AL, TQString(), TQString() ); + else if ( !m_blockList.contains( handle ) ) + m_notifySocket->addContact( handle, MSNProtocol::BL, TQString(), TQString(), TQString() ); + } + else + { + if ( m_blockList.contains( handle ) ) + m_notifySocket->removeContact( handle, MSNProtocol::BL, TQString(), TQString() ); + else if ( !m_allowList.contains( handle ) ) + m_notifySocket->addContact( handle, MSNProtocol::AL, TQString(), TQString(), TQString() ); + } + + +} + +void MSNAccount::slotGlobalIdentityChanged( const TQString &key, const TQVariant &value ) +{ + if( !configGroup()->readBoolEntry("ExcludeGlobalIdentity", false) ) + { + if(key == Kopete::Global::Properties::self()->nickName().key()) + { + TQString oldNick = myself()->property( Kopete::Global::Properties::self()->nickName()).value().toString(); + TQString newNick = value.toString(); + + if(newNick != oldNick) + { + setPublicName( value.toString() ); + } + } + else if(key == Kopete::Global::Properties::self()->photo().key()) + { + m_pictureFilename = value.toString(); + kdDebug( 14140 ) << k_funcinfo << m_pictureFilename << endl; + resetPictureObject(false, true); + } + } +} + +void MSNAccount::slotErrorMessageReceived( int type, const TQString &msg ) +{ + TQString caption = i18n( "MSN Plugin" ); + + // Use different notification type based on the error context. + switch(type) + { + case MSNSocket::ErrorConnectionLost: + { + Kopete::Utils::notifyConnectionLost( this, caption, msg ); + break; + } + case MSNSocket::ErrorConnectionError: + { + Kopete::Utils::notifyConnectionError( this, caption, msg ); + break; + } + case MSNSocket::ErrorCannotConnect: + { + Kopete::Utils::notifyCannotConnect( this ); + break; + } + case MSNSocket::ErrorInformation: + { + KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Information, msg, caption ); + break; + } + case MSNSocket::ErrorServerError: + default: + { + Kopete::Utils::notifyServerError( this, caption, msg ); + break; + } + } +} + +bool MSNAccount::createContact( const TQString &contactId, Kopete::MetaContact *metaContact ) +{ + if ( !metaContact->isTemporary() && m_notifySocket) + { + m_addWizard_metaContact = metaContact; + + addContactServerside(contactId, metaContact->groups()); + + // FIXME: Find out if this contact was really added or not! + return true; + } + else + { + // This is a temporary contact. ( a person who messaged us but is not on our conntact list. + // We don't want to create it on the server.Just create the local contact object and add it + // Or we are diconnected, and in that case, the contact will be added when connecting + MSNContact *newContact = new MSNContact( this, contactId, metaContact ); + newContact->setDeleted(true); + return true; + } + +} + +void MSNAccount::addContactServerside(const TQString &contactId, TQPtrList<Kopete::Group> groupList) +{ + // First of all, fill the temporary group list. The contact will be moved to his group(s). + // When we receive back his contact GUID(required to move a contact between groups) + for( Kopete::Group *group = groupList.first(); group; group = groupList.next() ) + { + // TODO: It it time that libkopete generate a unique ID that contains protocols, account and contact id. + TQString groupId = group->pluginData( protocol(), accountId() + " id" ); + // If the groupId is empty, that's mean the Kopete group is not on the MSN server. + if( !groupId.isEmpty() ) + { + // Something got corrupted on contactlist.xml + if( !m_groupList.contains(groupId) ) + { + // Clear the group plugin data. + group->setPluginData( protocol() , accountId() + " id" , TQString()); + group->setPluginData( protocol() , accountId() + " displayName" , TQString()); + kdDebug( 14140 ) << k_funcinfo << " Group " << group->displayName() << " marked with id #" << groupId << " does not seems to be anymore on the server" << endl; + + // Add the group on MSN server, will fix the corruption. + kdDebug(14140) << k_funcinfo << "Fixing group corruption, re-adding " << group->displayName() << "to the server." << endl; + addGroup( group->displayName(), contactId); + } + else + { + // Add the group that the contact belong to add it when we will receive the contact GUID. + if( tmp_addNewContactToGroup.contains( contactId ) ) + tmp_addNewContactToGroup[contactId].append(groupId); + else + tmp_addNewContactToGroup.insert(contactId, TQStringList(groupId) ); + } + } + else + { + if( !group->displayName().isEmpty() && group->type() == Kopete::Group::Normal ) + { + kdDebug(14140) << k_funcinfo << "Group not on MSN server, add it" << endl; + addGroup( group->displayName(), contactId ); + } + } + } + + // After add the contact to the top-level, it will be moved to required groups later. + kdDebug( 14140 ) << k_funcinfo << "Add the contact on the server " << endl; + m_notifySocket->addContact( contactId, MSNProtocol::FL, contactId, TQString(), TQString() ); +} + +MSNContact *MSNAccount::findContactByGuid(const TQString &contactGuid) +{ + kdDebug(14140) << k_funcinfo << "Looking for " << contactGuid << endl; + TQDictIterator<Kopete::Contact> it( contacts() ); + for ( ; it.current(); ++it ) + { + MSNContact *c = dynamic_cast<MSNContact *>( it.current() ); + + if(c && c->guid() == contactGuid ) + { + kdDebug(14140) << k_funcinfo << "OK found a contact. " << endl; + // Found the contact GUID + return c; + } + } + + return 0L; +} + +bool MSNAccount::isHotmail() const +{ + if ( !m_openInboxAction ) + return false; + return m_openInboxAction->isEnabled(); +} + +TQString MSNAccount::pictureUrl() +{ + return m_pictureFilename; +} + +void MSNAccount::setPictureUrl(const TQString &url) +{ + m_pictureFilename = url; +} + +TQString MSNAccount::pictureObject() +{ + if(m_pictureObj.isNull()) + resetPictureObject(true); //silent=true to keep infinite loop away + return m_pictureObj; +} + +void MSNAccount::resetPictureObject(bool silent, bool force) +{ + TQString old=m_pictureObj; + + if(!configGroup()->readBoolEntry("exportCustomPicture") && !force) + { + m_pictureObj=""; + myself()->removeProperty( Kopete::Global::Properties::self()->photo() ); + } + else + { + // Check if the picture is a 96x96 image, if not scale, crop and save. + TQImage picture(m_pictureFilename); + if(picture.isNull()) + { + m_pictureObj=""; + myself()->removeProperty( Kopete::Global::Properties::self()->photo() ); + } + else + { + if(picture.width() != 96 || picture.height() != 96) + { + // Save to a new location in msnpictures. + TQString newLocation( locateLocal( "appdata", "msnpictures/"+ KURL(m_pictureFilename).fileName().lower() ) ); + + // Scale and crop the picture. + picture = MSNProtocol::protocol()->scalePicture(picture); + + // Use the cropped/scaled image now. + if(!picture.save(newLocation, "PNG")) + { + m_pictureObj=""; + myself()->removeProperty( Kopete::Global::Properties::self()->photo() ); + } + m_pictureFilename = newLocation; + } + } + + TQFile pictFile( m_pictureFilename ); + if(!pictFile.open(IO_ReadOnly)) + { + m_pictureObj=""; + myself()->removeProperty( Kopete::Global::Properties::self()->photo() ); + } + else + { + TQByteArray ar=pictFile.readAll(); + TQString sha1d= TQString((KCodecs::base64Encode(SHA1::hash(ar)))); + + TQString size=TQString::number( pictFile.size() ); + TQString all= "Creator"+accountId()+"Size"+size+"Type3Locationkopete.tmpFriendlyAAA=SHA1D"+ sha1d; + m_pictureObj="<msnobj Creator=\"" + accountId() + "\" Size=\"" + size + "\" Type=\"3\" Location=\"kopete.tmp\" Friendly=\"AAA=\" SHA1D=\""+sha1d+"\" SHA1C=\""+ TQString(KCodecs::base64Encode(SHA1::hashString(all.utf8()))) +"\"/>"; + myself()->setProperty( Kopete::Global::Properties::self()->photo() , m_pictureFilename ); + } + } + + if(old!=m_pictureObj && isConnected() && m_notifySocket && !silent) + { + //update the msn pict + m_notifySocket->setStatus( myself()->onlineStatus() ); + } +} + +#include "msnaccount.moc" + +// vim: set noet ts=4 sts=4 sw=4: + + diff --git a/kopete/protocols/msn/msnaccount.h b/kopete/protocols/msn/msnaccount.h new file mode 100644 index 00000000..e7a80f4a --- /dev/null +++ b/kopete/protocols/msn/msnaccount.h @@ -0,0 +1,271 @@ +/* + msnaccount.h - Manages a single MSN account + + Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org> + Copyright (c) 2005 by Michaêl Larouche <michael.larouche@kdemail.net> + + Kopete (c) 2003-2005 by The Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef MSNACCOUNT_H +#define MSNACCOUNT_H + +#include <tqobject.h> + +#include "kopetepasswordedaccount.h" + +#include "msnprotocol.h" + +class TDEAction; +class TDEActionMenu; + +class MSNNotifySocket; +class MSNContact; + +/** + * MSNAccount encapsulates everything that is account-based, as opposed to + * protocol based. This basically means sockets, current status, and account + * info are stored in the account, whereas the protocol is only the + * 'manager' class that creates and manages accounts. + */ +class MSNAccount : public Kopete::PasswordedAccount +{ + Q_OBJECT + + +public: + MSNAccount( MSNProtocol *parent, const TQString &accountID, const char *name = 0L ); + + /* + * return the menu for this account + */ + virtual TDEActionMenu* actionMenu(); + + //------ internal functions + /** + * change the publicName to this new name + */ + void setPublicName( const TQString &name ); + void setPersonalMessage(MSNProtocol::PersonalMessageType type, const TQString &personalMessage ); + + /** + * Returns the address of the MSN server + */ + TQString serverName(); + + /** + * Returns the address of the MSN server port + */ + uint serverPort(); + + MSNNotifySocket *notifySocket(); + + /** + * return true if we are able to send mail, or to open hotmail inbox + */ + bool isHotmail() const; + + + /** + * Return the picture url. + */ + TQString pictureUrl(); + + /** + * Set the picture url. + */ + void setPictureUrl(const TQString &url); + + /** + * return the <msnobj> tag of the display picture + */ + TQString pictureObject(); + + /** + * reset the <msnobj>. This method should be called if the displayimage has changed + * If we are actualy connected, it will imediatly update the <msgobj> on the server, exepted + * if @param silent is set to true + * @param force Force the application of MSN picture + */ + void resetPictureObject(bool silent=false, bool force=false); + + //BEGIN Http + + bool useHttpMethod() const; + + //END + + /** + * Return the client ID for the myself contact of this account. + * It is dynamic to see if we really have a webcam or not. + */ + TQString myselfClientId() const; + +public slots: + virtual void connectWithPassword( const TQString &password ) ; + virtual void disconnect() ; + virtual void setOnlineStatus( const Kopete::OnlineStatus &status , const TQString &reason = TQString()); + + /** + * Ask to the account to create a new chat session + */ + void slotStartChatSession( const TQString& handle ); + + /** + * Single slot to display error message. + */ + void slotErrorMessageReceived( int type, const TQString &msg ); + +protected: + virtual bool createContact( const TQString &contactId, Kopete::MetaContact *parentContact ); + + +private slots: + // Actions related + void slotStartChat(); + void slotOpenInbox(); + void slotChangePublicName(); + +//#if !defined NDEBUG //(Stupid moc which don't see when he don't need to slot this slot) + /** + * Show simple debugging aid + */ + void slotDebugRawCommand(); +//#endif + + // notifySocket related + void slotStatusChanged( const Kopete::OnlineStatus &status ); + void slotNotifySocketClosed(); + void slotPersonalMessageChanged(const TQString& personalMessage); + void slotContactRemoved(const TQString& handle, const TQString& list, const TQString& contactGuid, const TQString& groupGuid ); + void slotContactAdded(const TQString& handle, const TQString& list, const TQString& publicName, const TQString& contactGuid, const TQString &groupGuid ); + void slotContactListed( const TQString& handle, const TQString& publicName, const TQString &contactGuid, uint lists, const TQString& groups ); + void slotNewContactList(); + /** + * The group has successful renamed in the server + * groupName: is new new group name + */ + void slotGroupRenamed(const TQString &groupGuid, const TQString& groupName ); + /** + * A new group was created on the server (or received durring an LSG command) + */ + void slotGroupAdded( const TQString& groupName, const TQString &groupGuid ); + /** + * Group was removed from the server + */ + void slotGroupRemoved( const TQString &groupGuid ); + /** + * Incoming RING command: connect to the Switchboard server and send + * the startChat signal + */ + void slotCreateChat( const TQString& sessionID, const TQString& address, const TQString& auth, + const TQString& handle, const TQString& publicName ); + /** + * Incoming XFR command: this is an result from + * slotStartChatSession(handle) + * connect to the switchboard server and sen startChat signal + */ + void slotCreateChat( const TQString& address, const TQString& auth); + + + // ui related + /** + * A kopetegroup is renamed, rename group on the server + */ + void slotKopeteGroupRenamed( Kopete::Group *g ); + + /** + * A kopetegroup is removed, remove the group in the server + **/ + void slotKopeteGroupRemoved( Kopete::Group* ); + + /** + * add contact ui + */ + void slotContactAddedNotifyDialogClosed( const TQString &handle); + + /** + * When the dispatch server sends us the notification server to use. + */ + void createNotificationServer( const TQString &host, uint port ); + + /** + * When a global identity key get changed. + */ + void slotGlobalIdentityChanged( const TQString &key, const TQVariant &value ); + +private: + MSNNotifySocket *m_notifySocket; + TDEAction *m_openInboxAction; + TDEAction *m_startChatAction; + TDEAction *m_changeDNAction; + + // status which will be using for connecting + Kopete::OnlineStatus m_connectstatus; + + TQStringList m_msgHandle; + + bool m_newContactList; + + + /** + * Add the contact on the server in the given groups. + * this is a helper function called bu createContact and slotStatusChanged + */ + void addContactServerside(const TQString &contactId, TQPtrList<Kopete::Group> groupList); + + + +public: //FIXME: should be private + TQMap<TQString, Kopete::Group*> m_groupList; + + void addGroup( const TQString &groupName, const TQString &contactToAdd = TQString() ); + + /** + * Find and retrive a MSNContact by its contactGuid. (Helper function) + */ + MSNContact *findContactByGuid(const TQString &contactGuid); +private: + + // server data + TQStringList m_allowList; + TQStringList m_blockList; + TQStringList m_reverseList; + + Kopete::MetaContact *m_addWizard_metaContact; + TQMap< TQString, TQStringList > tmp_addToNewGroup; + TQMap< TQString, TQStringList > tmp_addNewContactToGroup; + + TQString m_pictureObj; //a cache of the <msnobj> + TQString m_pictureFilename; // the picture filename. + + //this is the translation between old to new groups id when syncing from server. + TQMap<TQString, Kopete::Group*> m_oldGroupList; + + /** + * I need the password in createNotificationServer. + * but i can't ask it there with password() because a nested loop will provoque crash + * at this place. so i'm forced to keep it here. + * I would like an API to request the password WITHOUT askling it. + */ + TQString m_password; + + /** + * Cliend ID is a bitfield that contains supported features for a MSN contact. + */ + uint m_clientId; +}; + +#endif + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/msn/msnaddcontactpage.cpp b/kopete/protocols/msn/msnaddcontactpage.cpp new file mode 100644 index 00000000..74102e1d --- /dev/null +++ b/kopete/protocols/msn/msnaddcontactpage.cpp @@ -0,0 +1,85 @@ +/* + Copyright (c) 2002-2005 by Olivier Goffart <ogoffart@ kde.org> + Kopete (c) 2002-2005 by The Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + + +#include <tqlayout.h> +#include <tqlineedit.h> + +#include <tdelocale.h> +#include <tdemessagebox.h> + +#include "msnadd.h" +#include "msnaddcontactpage.h" +#include "msnprotocol.h" +#include "kopeteaccount.h" +#include "kopeteuiglobal.h" + +MSNAddContactPage::MSNAddContactPage(bool connected, TQWidget *parent, const char *name ) + : AddContactPage(parent,name) +{ + (new TQVBoxLayout(this))->setAutoAdd(true); +/* if ( connected ) + {*/ + msndata = new msnAddUI(this); + /* + msndata->cmbGroup->insertStringList(owner->getGroups()); + msndata->cmbGroup->setCurrentItem(0); + */ + canadd = true; + +/* } + else + { + noaddMsg1 = new TQLabel( i18n( "You need to be connected to be able to add contacts." ), this ); + noaddMsg2 = new TQLabel( i18n( "Please connect to the MSN network and try again." ), this ); + canadd = false; +}*/ + +} +MSNAddContactPage::~MSNAddContactPage() +{ +} + +bool MSNAddContactPage::apply( Kopete::Account* i, Kopete::MetaContact*m ) +{ + if ( validateData() ) + { + TQString userid = msndata->addID->text(); + return i->addContact( userid , m, Kopete::Account::ChangeKABC ); + } + return false; +} + + +bool MSNAddContactPage::validateData() +{ + if(!canadd) + return false; + + TQString userid = msndata->addID->text(); + + if(MSNProtocol::validContactId(userid)) + return true; + + KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry, + i18n( "<qt>You must enter a valid email address.</qt>" ), i18n( "MSN Plugin" ) ); + + return false; + +} + +#include "msnaddcontactpage.moc" + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/msn/msnaddcontactpage.h b/kopete/protocols/msn/msnaddcontactpage.h new file mode 100644 index 00000000..dc81dc44 --- /dev/null +++ b/kopete/protocols/msn/msnaddcontactpage.h @@ -0,0 +1,34 @@ + +#ifndef MSNADDCONTACTPAGE_H +#define MSNADDCONTACTPAGE_H + +#include <tqwidget.h> +#include <addcontactpage.h> +#include <tqlabel.h> + +/** + *@author duncan + */ +class msnAddUI; +class MSNProtocol; + +class MSNAddContactPage : public AddContactPage +{ + Q_OBJECT + +public: + MSNAddContactPage(bool connected, TQWidget *parent=0, const char *name=0); + ~MSNAddContactPage(); + msnAddUI *msndata; + TQLabel *noaddMsg1; + TQLabel *noaddMsg2; + bool canadd; + virtual bool validateData(); + virtual bool apply( Kopete::Account*, Kopete::MetaContact* ); + +}; + +#endif + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/msn/msnchallengehandler.cpp b/kopete/protocols/msn/msnchallengehandler.cpp new file mode 100644 index 00000000..5492b9e7 --- /dev/null +++ b/kopete/protocols/msn/msnchallengehandler.cpp @@ -0,0 +1,151 @@ +/* + msnchallengehandler.h - Computes a msn challenge response hash key. + + Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com> + Kopete (c) 2003-2005 by The Kopete developers <kopete-devel@kde.org> + + Portions taken from + http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include "msnchallengehandler.h" + +#include <tqdatastream.h> + +#include <kdebug.h> +#include <kmdcodec.h> + +MSNChallengeHandler::MSNChallengeHandler(const TQString& productKey, const TQString& productId) +{ + m_productKey = productKey; + m_productId = productId; +} + + +MSNChallengeHandler::~MSNChallengeHandler() +{ + kdDebug(14140) << k_funcinfo << endl; +} + +TQString MSNChallengeHandler::computeHash(const TQString& challengeString) +{ + // Step One: THe MD5 Hash. + + // Combine the received challenge string with the product key. + KMD5 md5((challengeString + m_productKey).utf8()); + TQCString digest = md5.hexDigest(); + + kdDebug(14140) << k_funcinfo << "md5: " << digest << endl; + + TQValueVector<TQ_INT32> md5Integers(4); + for(TQ_UINT32 i=0; i < md5Integers.count(); i++) + { + md5Integers[i] = hexSwap(digest.mid(i*8, 8)).toUInt(0, 16) & 0x7FFFFFFF; + kdDebug(14140) << k_funcinfo << ("0x" + hexSwap(digest.mid(i*8, 8))) << " " << md5Integers[i] << endl; + } + + // Step Two: Create the challenge string key + + TQString challengeKey = challengeString + m_productId; + // Pad to multiple of 8. + challengeKey = challengeKey.leftJustify(challengeKey.length() + (8 - challengeKey.length() % 8), '0'); + + kdDebug(14140) << k_funcinfo << "challenge key: " << challengeKey << endl; + + TQValueVector<TQ_INT32> challengeIntegers(challengeKey.length() / 4); + for(TQ_UINT32 i=0; i < challengeIntegers.count(); i++) + { + TQString sNum = challengeKey.mid(i*4, 4), sNumHex; + + // Go through the number string, determining the hex equivalent of each value + // and add that to our new hex string for this number. + for(uint j=0; j < sNum.length(); j++) { + sNumHex += TQString::number((int)sNum[j].latin1(), 16); + } + + // swap because of the byte ordering issue. + sNumHex = hexSwap(sNumHex); + // Assign the converted number. + challengeIntegers[i] = sNumHex.toInt(0, 16); + kdDebug(14140) << k_funcinfo << sNum << (": 0x"+sNumHex) << " " << challengeIntegers[i] << endl; + } + + // Step Three: Create the 64-bit hash key. + + // Get the hash key using the specified arrays. + TQ_INT64 key = createHashKey(md5Integers, challengeIntegers); + kdDebug(14140) << k_funcinfo << "key: " << key << endl; + + // Step Four: Create the final hash key. + + TQString upper = TQString::number(TQString(digest.mid(0, 16)).toULongLong(0, 16)^key, 16); + if(upper.length() % 16 != 0) + upper = upper.rightJustify(upper.length() + (16 - upper.length() % 16), '0'); + + TQString lower = TQString::number(TQString(digest.mid(16, 16)).toULongLong(0, 16)^key, 16); + if(lower.length() % 16 != 0) + lower = lower.rightJustify(lower.length() + (16 - lower.length() % 16), '0'); + + return (upper + lower); +} + +TQ_INT64 MSNChallengeHandler::createHashKey(const TQValueVector<TQ_INT32>& md5Integers, + const TQValueVector<TQ_INT32>& challengeIntegers) +{ + kdDebug(14140) << k_funcinfo << "Creating 64-bit key." << endl; + + TQ_INT64 magicNumber = 0x0E79A9C1L, high = 0L, low = 0L; + + for(uint i=0; i < challengeIntegers.count(); i += 2) + { + TQ_INT64 temp = ((challengeIntegers[i] * magicNumber) % 0x7FFFFFFF) + high; + temp = ((temp * md5Integers[0]) + md5Integers[1]) % 0x7FFFFFFF; + + high = (challengeIntegers[i + 1] + temp) % 0x7FFFFFFF; + high = ((high * md5Integers[2]) + md5Integers[3]) % 0x7FFFFFFF; + + low += high + temp; + } + + high = (high + md5Integers[1]) % 0x7FFFFFFF; + low = (low + md5Integers[3]) % 0x7FFFFFFF; + + TQDataStream buffer(TQByteArray(8), IO_ReadWrite); + buffer.setByteOrder(TQDataStream::LittleEndian); + buffer << (TQ_INT32)high; + buffer << (TQ_INT32)low; + + buffer.device()->reset(); + buffer.setByteOrder(TQDataStream::BigEndian); + TQ_INT64 key; + buffer >> key; + + return key; +} + +TQString MSNChallengeHandler::hexSwap(const TQString& in) +{ + TQString sHex = in, swapped; + while(sHex.length() > 0) + { + swapped = swapped + sHex.mid(sHex.length() - 2, 2); + sHex = sHex.remove(sHex.length() - 2, 2); + } + return swapped; +} + +TQString MSNChallengeHandler::productId() +{ + return m_productId; +} + +#include "msnchallengehandler.moc" diff --git a/kopete/protocols/msn/msnchallengehandler.h b/kopete/protocols/msn/msnchallengehandler.h new file mode 100644 index 00000000..22024aee --- /dev/null +++ b/kopete/protocols/msn/msnchallengehandler.h @@ -0,0 +1,65 @@ +/* + msnchallengehandler.h - Computes a msn challenge response hash key. + + Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com> + Kopete (c) 2003-2005 by The Kopete developers <kopete-devel@kde.org> + + Portions taken from + http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef MSNCHALLENGEHANDLER_H +#define MSNCHALLENGEHANDLER_H + +#include <tqobject.h> +#include <tqvaluevector.h> + +/** + * Provides a simple way to compute a msn challenge response hash key. + * + * @author Gregg Edghill + */ +class MSNChallengeHandler : public TQObject +{ +Q_OBJECT + +public: + MSNChallengeHandler(const TQString& productKey, const TQString& productId); + ~MSNChallengeHandler(); + + /** + * Computes the response hash string for the specified challenge string. + */ + TQString computeHash(const TQString& challengeString); + + /** + * Returns the product id used by the challenge handler. + */ + TQString productId(); + +private: + + /** + * Creates a 64-bit hash key. + */ + TQ_INT64 createHashKey(const TQValueVector<TQ_INT32>& md5Integers, const TQValueVector<TQ_INT32>& challengeIntegers); + + /** + * Swaps the bytes in a hex string. + */ + TQString hexSwap(const TQString& in); + + TQString m_productKey; + TQString m_productId; +}; + +#endif diff --git a/kopete/protocols/msn/msnchatsession.cpp b/kopete/protocols/msn/msnchatsession.cpp new file mode 100644 index 00000000..fabd62ab --- /dev/null +++ b/kopete/protocols/msn/msnchatsession.cpp @@ -0,0 +1,775 @@ +/* + msnchatsession.cpp - MSN Message Manager + + Copyright (c) 2002-2005 by Olivier Goffart <ogoffart at kde.org> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include "msnchatsession.h" + +#include <tqlabel.h> +#include <tqimage.h> +#include <tqtooltip.h> +#include <tqfile.h> +#include <tqiconset.h> + + +#include <tdeconfig.h> +#include <kdebug.h> +#include <kinputdialog.h> +#include <tdelocale.h> +#include <tdemessagebox.h> +#include <tdepopupmenu.h> +#include <tdetempfile.h> +#include <tdemainwindow.h> +#include <tdetoolbar.h> +#include <krun.h> + +#include "kopetecontactaction.h" +#include "kopetemetacontact.h" +#include "kopetecontactlist.h" +#include "kopetechatsessionmanager.h" +#include "kopeteuiglobal.h" +#include "kopeteglobal.h" +#include "kopeteview.h" + +#include "msncontact.h" +#include "msnfiletransfersocket.h" +#include "msnaccount.h" +#include "msnswitchboardsocket.h" + +#include "config.h" + +#if !defined NDEBUG +#include "msndebugrawcmddlg.h" +#endif + +MSNChatSession::MSNChatSession( Kopete::Protocol *protocol, const Kopete::Contact *user, + Kopete::ContactPtrList others, const char *name ) +: Kopete::ChatSession( user, others, protocol, name ) +{ + Kopete::ChatSessionManager::self()->registerChatSession( this ); + m_chatService = 0l; + m_timeoutTimer =0L; + m_newSession = true; + m_connectionTry=0; + + setInstance(protocol->instance()); + + connect( this, TQT_SIGNAL( messageSent( Kopete::Message&, + Kopete::ChatSession* ) ), + this, TQT_SLOT( slotMessageSent( Kopete::Message&, + Kopete::ChatSession* ) ) ); + + connect( this, TQT_SIGNAL( invitation(MSNInvitation*& , const TQString & , long unsigned int , MSNChatSession* , MSNContact* ) ) , + protocol, TQT_SIGNAL( invitation(MSNInvitation*& , const TQString & , long unsigned int , MSNChatSession* , MSNContact* ) ) ); + + + m_actionInvite = new TDEActionMenu( i18n( "&Invite" ), "kontact_contacts", actionCollection(), "msnInvite" ); + connect ( m_actionInvite->popupMenu() , TQT_SIGNAL( aboutToShow() ) , this , TQT_SLOT(slotActionInviteAboutToShow() ) ) ; + + #if !defined NDEBUG + new TDEAction( i18n( "Send Raw C&ommand..." ), 0, this, TQT_SLOT( slotDebugRawCommand() ), actionCollection(), "msnDebugRawCommand" ) ; + #endif + + + m_actionNudge=new TDEAction( i18n( "Send Nudge" ), "bell", 0, this, TQT_SLOT(slotSendNudge() ), actionCollection(), "msnSendNudge" ) ; +#if MSN_WEBCAM + // Invite to receive webcam action + m_actionWebcamReceive=new TDEAction( i18n( "View Contact's Webcam" ), "webcamreceive", 0, this, TQT_SLOT(slotWebcamReceive()), actionCollection(), "msnWebcamReceive" ) ; + + //Send webcam action + m_actionWebcamSend=new TDEAction( i18n( "Send Webcam" ), "webcamsend", 0, this, TQT_SLOT(slotWebcamSend()), actionCollection(), "msnWebcamSend" ) ; +#endif + + new TDEAction( i18n( "Send File" ),"attach", 0, this, TQT_SLOT( slotSendFile() ), actionCollection(), "msnSendFile" ); + + MSNContact *c = static_cast<MSNContact*>( others.first() ); + (new TDEAction( i18n( "Request Display Picture" ), "image", 0, this, TQT_SLOT( slotRequestPicture() ), actionCollection(), "msnRequestDisplayPicture" ))->setEnabled(!c->object().isEmpty()); + + if ( !c->object().isEmpty() ) + { + + connect( c, TQT_SIGNAL( displayPictureChanged() ), this, TQT_SLOT( slotDisplayPictureChanged() ) ); + m_image = new TQLabel( 0L, "kde toolbar widget" ); + new KWidgetAction( m_image, i18n( "MSN Display Picture" ), 0, this, TQT_SLOT( slotRequestPicture() ), actionCollection(), "msnDisplayPicture" ); + if(c->hasProperty(Kopete::Global::Properties::self()->photo().key()) ) + { + //if the view doesn't exist yet, we will be unable to get the size of the toolbar + // so when the view will exist, we will show the displaypicture. + //How to know when a our view is created? We can't. + // but chances are the next created view will be for this KMM + // And if it is not? never mind. the icon will just be sized 22x22 + connect( Kopete::ChatSessionManager::self() , TQT_SIGNAL(viewActivated(KopeteView* )) , this, TQT_SLOT(slotDisplayPictureChanged()) ); + //it's viewActivated and not viewCreated because the view get his mainwindow only when it is shown. + } + } + else + { + m_image = 0L; + } + + setXMLFile("msnchatui.rc"); + + setMayInvite( true ); +} + +MSNChatSession::~MSNChatSession() +{ + delete m_image; + //force to disconnect the switchboard + //not needed since the m_chatService has us as parent + // if(m_chatService) + // delete m_chatService; + + TQMap<unsigned long int, MSNInvitation*>::Iterator it; + for( it = m_invitations.begin(); it != m_invitations.end() ; it = m_invitations.begin()) + { + delete *it; + m_invitations.remove( it ); + } +} + +void MSNChatSession::createChat( const TQString &handle, + const TQString &address, const TQString &auth, const TQString &ID ) +{ + /* disabled because i don't want to reopen a chatwindow if we just closed it + * and the contact take much time to type his message + m_newSession= !(ID.isEmpty()); + */ + + if( m_chatService ) + { + kdDebug(14140) << k_funcinfo << "Service already exists, disconnect them." << endl; + delete m_chatService; + } + +// uncomment this line if you don't want to the peer know when you close the window +// setCanBeDeleted( false ); + + m_chatService = new MSNSwitchBoardSocket( static_cast<MSNAccount*>( myself()->account() ) , this); + m_chatService->setUseHttpMethod( static_cast<MSNAccount*>( myself()->account() )->useHttpMethod() ); + m_chatService->setHandle( myself()->account()->accountId() ); + m_chatService->setMsgHandle( handle ); + m_chatService->connectToSwitchBoard( ID, address, auth ); + + connect( m_chatService, TQT_SIGNAL( userJoined(const TQString&,const TQString&,bool)), + this, TQT_SLOT( slotUserJoined(const TQString&,const TQString&,bool) ) ); + connect( m_chatService, TQT_SIGNAL( userLeft(const TQString&,const TQString&)), + this, TQT_SLOT( slotUserLeft(const TQString&,const TQString&) ) ); + connect( m_chatService, TQT_SIGNAL( msgReceived( Kopete::Message & ) ), + this, TQT_SLOT( slotMessageReceived( Kopete::Message & ) ) ); + connect( m_chatService, TQT_SIGNAL( switchBoardClosed() ), + this, TQT_SLOT( slotSwitchBoardClosed() ) ); + connect( m_chatService, TQT_SIGNAL( receivedTypingMsg( const TQString &, bool ) ), + this, TQT_SLOT( receivedTypingMsg( const TQString &, bool ) ) ); + TDEConfig *config = TDEGlobal::config(); + config->setGroup( "MSN" ); + if(config->readBoolEntry( "SendTypingNotification" , true) ) + { + connect( this, TQT_SIGNAL( myselfTyping( bool ) ), + m_chatService, TQT_SLOT( sendTypingMsg( bool ) ) ); + } + connect( m_chatService, TQT_SIGNAL( msgAcknowledgement(unsigned int, bool) ), + this, TQT_SLOT( slotAcknowledgement(unsigned int, bool) ) ); + connect( m_chatService, TQT_SIGNAL( invitation( const TQString&, const TQString& ) ), + this, TQT_SLOT( slotInvitation( const TQString&, const TQString& ) ) ); + connect( m_chatService, TQT_SIGNAL( nudgeReceived(const TQString&) ), + this, TQT_SLOT( slotNudgeReceived(const TQString&) ) ); + connect( m_chatService, TQT_SIGNAL( errorMessage(int, const TQString& ) ), static_cast<MSNAccount *>(myself()->account()), TQT_SLOT( slotErrorMessageReceived(int, const TQString& ) ) ); + + if(!m_timeoutTimer) + { + m_timeoutTimer=new TQTimer(this); + connect( m_timeoutTimer , TQT_SIGNAL(timeout()), this , TQT_SLOT(slotConnectionTimeout() ) ); + } + m_timeoutTimer->start(20000,true); +} + +void MSNChatSession::slotUserJoined( const TQString &handle, const TQString &publicName, bool IRO ) +{ + delete m_timeoutTimer; + m_timeoutTimer=0L; + + if( !account()->contacts()[ handle ] ) + account()->addContact( handle, TQString(), 0L, Kopete::Account::Temporary); + + MSNContact *c = static_cast<MSNContact*>( account()->contacts()[ handle ] ); + + c->setProperty( Kopete::Global::Properties::self()->nickName() , publicName); + + if(c->clientFlags() & MSNProtocol::MSNC4 ) + { + m_actionNudge->setEnabled(true); + } +#if MSN_WEBCAM + if(c->clientFlags() & MSNProtocol::SupportWebcam ) + { + m_actionWebcamReceive->setEnabled(true); + } +#endif + + addContact(c , IRO); // don't show notificaions when we join wesalef + if(!m_messagesQueue.empty() || !m_invitations.isEmpty()) + sendMessageQueue(); + + TDEConfig *config = TDEGlobal::config(); + config->setGroup( "MSN" ); + if ( members().count()==1 && config->readNumEntry( "DownloadPicture", 1 ) >= 1 && !c->object().isEmpty() && !c->hasProperty(Kopete::Global::Properties::self()->photo().key())) + slotRequestPicture(); +} + +void MSNChatSession::slotUserLeft( const TQString &handle, const TQString& reason ) +{ + MSNContact *c = static_cast<MSNContact*>( myself()->account()->contacts()[ handle ] ); + if(c) + removeContact(c, reason ); +} + + + +void MSNChatSession::slotSwitchBoardClosed() +{ + //kdDebug(14140) << "MSNChatSession::slotSwitchBoardClosed" << endl; + m_chatService->deleteLater(); + m_chatService=0l; + + cleanMessageQueue( i18n("Connection closed") ); + + if(m_invitations.isEmpty()) + setCanBeDeleted( true ); +} + +void MSNChatSession::slotMessageSent(Kopete::Message &message,Kopete::ChatSession *) +{ + m_newSession=false; + if(m_chatService) + { + int id = m_chatService->sendMsg(message); + if(id == -1) + { + m_messagesQueue.append(message); + kdDebug(14140) << k_funcinfo << "Message added to the queue" <<endl; + } + else if( id== -2 ) //the message has not been sent + { + //FIXME: tell the what window the message has been processed. but we havent't sent it + messageSucceeded(); //that should stop the blonking icon. + } + else if( id == -3) //the message has been sent as an immge + { + appendMessage(message); + messageSucceeded(); + } + else + { + m_messagesSent.insert( id, message ); + message.setBg(TQColor()); // clear the bgColor + message.setBody(message.plainBody() , Kopete::Message::PlainText ); //clear every custom tag which are not sent + appendMessage(message); // send the own msg to chat window + } + } + else // There's no switchboard available, so we must create a new one! + { + startChatSession(); + m_messagesQueue.append(message); +// sendMessageQueue(); + //m_msgQueued=new Kopete::Message(message); + } +} + +void MSNChatSession::slotMessageReceived( Kopete::Message &msg ) +{ + m_newSession=false; + if( msg.plainBody().startsWith( "AutoMessage: " ) ) + { + //FIXME: HardCodded color are not so good + msg.setFg( TQColor( "SlateGray3" ) ); + TQFont f; + f.setItalic( true ); + msg.setFont( f ); + } + appendMessage( msg ); +} + +void MSNChatSession::slotActionInviteAboutToShow() +{ + // We can't simply insert TDEAction in this menu bebause we don't know when to delete them. + // items inserted with insert items are automatically deleted when we call clear + + m_inviteactions.setAutoDelete(true); + m_inviteactions.clear(); + + m_actionInvite->popupMenu()->clear(); + + + TQDictIterator<Kopete::Contact> it( account()->contacts() ); + for( ; it.current(); ++it ) + { + if( !members().contains( it.current() ) && it.current()->isOnline() && it.current() != myself() ) + { + TDEAction *a=new KopeteContactAction( it.current(), this, + TQT_SLOT( slotInviteContact( Kopete::Contact * ) ), m_actionInvite ); + m_actionInvite->insert( a ); + m_inviteactions.append( a ) ; + } + } + TDEAction *b=new TDEAction( i18n ("Other..."), 0, this, TQT_SLOT( slotInviteOtherContact() ), m_actionInvite, "actionOther" ); + m_actionInvite->insert( b ); + m_inviteactions.append( b ) ; +} + +void MSNChatSession::slotCloseSession() +{ + kdDebug(14140) << k_funcinfo << m_chatService <<endl; + if(m_chatService) + m_chatService->slotCloseSession(); +} + +void MSNChatSession::slotInviteContact( Kopete::Contact *contact ) +{ + if(contact) + inviteContact( contact->contactId() ); +} + +void MSNChatSession::inviteContact(const TQString &contactId) +{ + if( m_chatService ) + m_chatService->slotInviteContact( contactId ); + else + startChatSession(); +} + +void MSNChatSession::slotInviteOtherContact() +{ + bool ok; + TQString handle = KInputDialog::getText(i18n( "MSN Plugin" ), + i18n( "Please enter the email address of the person you want to invite:" ), + TQString(), &ok ); + if( !ok ) + return; + + if( handle.contains('@') != 1 || handle.contains('.') <1) + { + KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry, + i18n("<qt>You must enter a valid email address.</qt>"), i18n("MSN Plugin")); + return; + } + + inviteContact(handle); +} + + +void MSNChatSession::sendMessageQueue() +{ + if(!m_chatService) + { + kdDebug(14140) <<k_funcinfo << "Service doesn't exist" <<endl; + return; + } +// kdDebug(14140) << "MSNChatSession::sendMessageQueue: " << m_messagesQueue.count() <<endl; + for ( TQValueList<Kopete::Message>::iterator it = m_messagesQueue.begin(); it!=m_messagesQueue.end(); it = m_messagesQueue.begin() ) + { + //m_chatService->sendMsg( *it) ; + slotMessageSent(*it , this); + m_messagesQueue.remove(it); + } + + + TQMap<unsigned long int, MSNInvitation*>::Iterator it; + for( it = m_invitations.begin(); it != m_invitations.end() ; ++it) + { + if(! (*it)->incoming() && (*it)->state()<MSNInvitation::Invited) + { + m_chatService->sendCommand( "MSG" , "N", true, (*it)->invitationHead().utf8() ); + (*it)->setState(MSNInvitation::Invited); + } + } +} + +void MSNChatSession::slotAcknowledgement(unsigned int id, bool ack) +{ + if ( !m_messagesSent.contains( id ) ) + { + // This is maybe a ACK/NAK for a non-messaging message + return; + } + + if ( !ack ) + { + Kopete::Message m = m_messagesSent[ id ]; + TQString body = i18n( "The following message has not been sent correctly:\n%1" ).arg( m.plainBody() ); + Kopete::Message msg = Kopete::Message( m.to().first(), members(), body, Kopete::Message::Internal, Kopete::Message::PlainText ); + appendMessage( msg ); + //stop the stupid animation + messageSucceeded(); + } + else + { + messageSucceeded(); + } + + m_messagesSent.remove( id ); +} + +void MSNChatSession::slotInvitation(const TQString &handle, const TQString &msg) +{ + //FIXME! a contact from another account can send a file + MSNContact *c = static_cast<MSNContact*>( myself()->account()->contacts()[ handle ] ); + if(!c) + return; + + TQRegExp rx("Invitation-Cookie: ([0-9]*)"); + rx.search(msg); + long unsigned int cookie=rx.cap(1).toUInt(); + + if(m_invitations.contains(cookie)) + { + MSNInvitation *msnI=m_invitations[cookie]; + msnI->parseInvitation(msg); + } + else if( msg.contains("Invitation-Command: INVITE") ) + { + if( msg.contains(MSNFileTransferSocket::applicationID()) ) + { + MSNFileTransferSocket *MFTS=new MSNFileTransferSocket(myself()->account()->accountId(),c,true,this); + connect(MFTS, TQT_SIGNAL( done(MSNInvitation*) ) , this , TQT_SLOT( invitationDone(MSNInvitation*) )); + m_invitations.insert( cookie , MFTS); + MFTS->parseInvitation(msg); + setCanBeDeleted(false); + } + else + { + MSNInvitation *i=0l; + emit invitation( i , msg, cookie, this, c ); + if(i) + { + m_invitations.insert( cookie , i ); + //don't delete this if all invitation are not done + setCanBeDeleted(false); + } + else + { + rx=TQRegExp("Application-Name: ([^\\r\\n]*)"); + rx.search(msg); + TQString inviteName = rx.cap( 1 ); + + TQString body = i18n( + "%1 has sent an unimplemented invitation, the invitation was rejected.\n" + "The invitation was: %2" ) + .arg( c->property( Kopete::Global::Properties::self()->nickName()).value().toString(), inviteName ); + Kopete::Message tmpMsg = Kopete::Message( c , members() , body , Kopete::Message::Internal, Kopete::Message::PlainText); + appendMessage(tmpMsg); + + m_chatService->sendCommand( "MSG" , "N", true, MSNInvitation::unimplemented(cookie) ); + } + } + } +} + +void MSNChatSession::invitationDone(MSNInvitation* MFTS) +{ + kdDebug(14140) << k_funcinfo <<endl; + m_invitations.remove(MFTS->cookie()); +// MFTS->deleteLater(); + delete MFTS; + if(!m_chatService && m_invitations.isEmpty()) + setCanBeDeleted(true); +} + +void MSNChatSession::sendFile(const TQString &fileLocation, const TQString &/*fileName*/, + long unsigned int fileSize) +{ + // TODO create a switchboard to send the file is one is not available. + if(m_chatService && members().getFirst()) + { + m_chatService->PeerDispatcher()->sendFile(fileLocation, (TQ_INT64)fileSize, members().getFirst()->contactId()); + } +} + +void MSNChatSession::initInvitation(MSNInvitation* invitation) +{ + connect(invitation->object(), TQT_SIGNAL( done(MSNInvitation*) ) , this , TQT_SLOT( invitationDone(MSNInvitation*) )); + m_invitations.insert( invitation->cookie() , invitation); + + if(m_chatService) + { + m_chatService->sendCommand( "MSG" , "N", true, invitation->invitationHead().utf8() ); + invitation->setState(MSNInvitation::Invited); + } + else + { + startChatSession(); + } +} + +void MSNChatSession::slotRequestPicture() +{ + TQPtrList<Kopete::Contact> mb=members(); + MSNContact *c = static_cast<MSNContact*>( mb.first() ); + if(!c) + return; + + if( !c->hasProperty(Kopete::Global::Properties::self()->photo().key())) + { + if(m_chatService) + { + if( !c->object().isEmpty() ) + m_chatService->requestDisplayPicture(); + } + else if(myself()->onlineStatus().isDefinitelyOnline() && myself()->onlineStatus().status() != Kopete::OnlineStatus::Invisible ) + startChatSession(); + } + else + { //we already have the picture, just show it. + KRun::runURL( KURL::fromPathOrURL( c->property(Kopete::Global::Properties::self()->photo()).value().toString() ), "image/png" ); + } + +} + +void MSNChatSession::slotDisplayPictureChanged() +{ + TQPtrList<Kopete::Contact> mb=members(); + MSNContact *c = static_cast<MSNContact *>( mb.first() ); + if ( c && m_image ) + { + if(c->hasProperty(Kopete::Global::Properties::self()->photo().key())) + { + int sz=22; + // get the size of the toolbar were the aciton is plugged. + // if you know a better way to get the toolbar, let me know + TDEMainWindow *w= view(false) ? dynamic_cast<TDEMainWindow*>( view(false)->mainWidget()->topLevelWidget() ) : 0L; + if(w) + { + //We connected that in the constructor. we don't need to keep this slot active. + disconnect( Kopete::ChatSessionManager::self() , TQT_SIGNAL(viewActivated(KopeteView* )) , this, TQT_SLOT(slotDisplayPictureChanged()) ); + + TQPtrListIterator<TDEToolBar> it=w->toolBarIterator() ; + TDEAction *imgAction=actionCollection()->action("msnDisplayPicture"); + if(imgAction) while(it) + { + TDEToolBar *tb=*it; + if(imgAction->isPlugged(tb)) + { + sz=tb->iconSize(); + //ipdate if the size of the toolbar change. + disconnect(tb, TQT_SIGNAL(modechange()), this, TQT_SLOT(slotDisplayPictureChanged())); + connect(tb, TQT_SIGNAL(modechange()), this, TQT_SLOT(slotDisplayPictureChanged())); + break; + } + ++it; + } + } + TQString imgURL=c->property(Kopete::Global::Properties::self()->photo()).value().toString(); + TQImage scaledImg = TQPixmap( imgURL ).convertToImage().smoothScale( sz, sz ); + if(!scaledImg.isNull()) + m_image->setPixmap( scaledImg ); + else + { //the image has maybe not been transfered correctly.. force to download again + c->removeProperty(Kopete::Global::Properties::self()->photo()); + //slotDisplayPictureChanged(); //don't do that or we might end in a infinite loop + } + TQToolTip::add( m_image, "<qt><img src=\"" + imgURL + "\"></qt>" ); + + } + else + { + TDEConfig *config = TDEGlobal::config(); + config->setGroup( "MSN" ); + if ( config->readNumEntry( "DownloadPicture", 1 ) >= 1 && !c->object().isEmpty() ) + slotRequestPicture(); + } + } +} + +void MSNChatSession::slotDebugRawCommand() +{ +#if !defined NDEBUG + if ( !m_chatService ) + return; + + MSNDebugRawCmdDlg *dlg = new MSNDebugRawCmdDlg( 0L ); + int result = dlg->exec(); + if( result == TQDialog::Accepted && m_chatService ) + { + m_chatService->sendCommand( dlg->command(), dlg->params(), + dlg->addId(), dlg->msg().replace("\n","\r\n").utf8() ); + } + delete dlg; +#endif +} + + +void MSNChatSession::receivedTypingMsg( const TQString &contactId, bool b ) +{ + MSNContact *c = dynamic_cast<MSNContact *>( account()->contacts()[ contactId ] ); + if(c && m_newSession && !view(false)) + { + //this was originaly in MSNAccount::slotCreateChat + TDEGlobal::config()->setGroup( "MSN" ); + bool notifyNewChat = TDEGlobal::config()->readBoolEntry( "NotifyNewChat", false ); + if ( notifyNewChat ) + { + // this internal message should open the window if they not exist + TQString body = i18n( "%1 has started a chat with you" ).arg( c->metaContact()->displayName() ); + Kopete::Message tmpMsg = Kopete::Message( c, members(), body, Kopete::Message::Internal, Kopete::Message::PlainText ); + appendMessage( tmpMsg ); + } + } + m_newSession=false; + if(c) + Kopete::ChatSession::receivedTypingMsg(c,b); +} + +void MSNChatSession::slotSendNudge() +{ + if(m_chatService) + { + m_chatService->sendNudge(); + Kopete::Message msg = Kopete::Message( myself(), members() , i18n ( "has sent a nudge" ), Kopete::Message::Outbound, + Kopete::Message::PlainText, TQString(), Kopete::Message::TypeAction ); + appendMessage( msg ); + + } +} + + +void MSNChatSession::slotNudgeReceived(const TQString& handle) +{ + Kopete::Contact *c = account()->contacts()[ handle ] ; + if(!c) + c=members().getFirst(); + Kopete::Message msg = Kopete::Message(c, myself(), i18n ( "has sent you a nudge" ), Kopete::Message::Inbound, + Kopete::Message::PlainText, TQString(), Kopete::Message::TypeAction ); + appendMessage( msg ); + // Emit the nudge/buzz notification (configured by user). + emitNudgeNotification(); +} + + +void MSNChatSession::slotWebcamReceive() +{ +#if MSN_WEBCAM + if(m_chatService && members().getFirst()) + { + m_chatService->PeerDispatcher()->startWebcam(myself()->contactId() , members().getFirst()->contactId() , true); + } +#endif +} + +void MSNChatSession::slotWebcamSend() +{ +#if MSN_WEBCAM + kdDebug(14140) << k_funcinfo << endl; + if(m_chatService && members().getFirst()) + { + m_chatService->PeerDispatcher()->startWebcam(myself()->contactId() , members().getFirst()->contactId() , false); + } +#endif +} + + +void MSNChatSession::slotSendFile() + { + TQPtrList<Kopete::Contact>contacts = members(); + static_cast<MSNContact *>(contacts.first())->sendFile(); + } + +void MSNChatSession::startChatSession() +{ + TQPtrList<Kopete::Contact> mb=members(); + static_cast<MSNAccount*>( account() )->slotStartChatSession( mb.first()->contactId() ); + + if(!m_timeoutTimer) + { + m_timeoutTimer=new TQTimer(this); + connect( m_timeoutTimer , TQT_SIGNAL(timeout()), this , TQT_SLOT(slotConnectionTimeout() ) ); + } + m_timeoutTimer->start(20000, true); +} + + +void MSNChatSession::cleanMessageQueue( const TQString & reason ) +{ + delete m_timeoutTimer; + m_timeoutTimer=0L; + + uint nb=m_messagesQueue.count()+m_messagesSent.count(); + if(nb==0) + return; + else if(nb==1) + { + Kopete::Message m; + if(m_messagesQueue.count() == 1) + m=m_messagesQueue.first(); + else + m=m_messagesSent.begin().data(); + + TQString body=i18n("The following message has not been sent correctly (%1): \n%2").arg(reason, m.plainBody()); + Kopete::Message msg = Kopete::Message(m.to().first() , members() , body , Kopete::Message::Internal, Kopete::Message::PlainText); + appendMessage(msg); + } + else + { + Kopete::Message m; + TQString body=i18n("These messages have not been sent correctly (%1): <br /><ul>").arg(reason); + for ( TQMap<unsigned int , Kopete::Message>::iterator it = m_messagesSent.begin(); it!=m_messagesSent.end(); it = m_messagesSent.begin() ) + { + m=it.data(); + body+= "<li>"+m.escapedBody()+"</li>"; + m_messagesSent.remove(it); + } + for ( TQValueList<Kopete::Message>::iterator it = m_messagesQueue.begin(); it!=m_messagesQueue.end(); it = m_messagesQueue.begin() ) + { + m=(*it); + body+= "<li>"+m.escapedBody()+"</li>"; + m_messagesQueue.remove(it); + } + body+="</ul>"; + Kopete::Message msg = Kopete::Message(m.to().first() , members() , body , Kopete::Message::Internal, Kopete::Message::RichText); + appendMessage(msg); + + } + m_messagesQueue.clear(); + m_messagesSent.clear(); + messageSucceeded(); //stop stupid animation +} + +void MSNChatSession::slotConnectionTimeout() +{ + m_connectionTry++; + if(m_chatService) + { + disconnect(m_chatService , 0 , this , 0 ); + m_chatService->deleteLater(); + m_chatService=0L; + } + + if( m_connectionTry > 3 ) + { + cleanMessageQueue( i18n("Impossible to establish the connection") ); + delete m_timeoutTimer; + m_timeoutTimer=0L; + return; + } + startChatSession(); + +} + + + + +#include "msnchatsession.moc" + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/msn/msnchatsession.h b/kopete/protocols/msn/msnchatsession.h new file mode 100644 index 00000000..2fb92d2a --- /dev/null +++ b/kopete/protocols/msn/msnchatsession.h @@ -0,0 +1,141 @@ +/* + msnchatsession.h - MSN Message Manager + + Copyright (c) 2002-2005 by Olivier Goffart <ogoffart @ kde.org> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef MSNMESSAGEMANAGER_H +#define MSNMESSAGEMANAGER_H + +#include "kopetechatsession.h" + +class MSNSwitchBoardSocket; +class TDEActionCollection; +class MSNInvitation; +class MSNContact; +class TDEActionMenu; +class TQLabel; + + +/** + * @author Olivier Goffart + */ +class KOPETE_EXPORT MSNChatSession : public Kopete::ChatSession +{ + Q_OBJECT + + +public: + MSNChatSession( Kopete::Protocol *protocol, const Kopete::Contact *user, Kopete::ContactPtrList others, const char *name = 0 ); + ~MSNChatSession(); + + void createChat( const TQString &handle, const TQString &address, const TQString &auth, const TQString &ID = TQString() ); + + MSNSwitchBoardSocket *service() { return m_chatService; }; + + void sendFile( const TQString &fileLocation, const TQString &fileName, + long unsigned int fileSize ); + + /** + * append an invitation in the invitation map, and send the first invitation message + */ + void initInvitation(MSNInvitation* invitation); + + virtual void inviteContact(const TQString& ); + +public slots: + void slotCloseSession(); + void slotInviteOtherContact(); + + void invitationDone( MSNInvitation* ); + + void slotRequestPicture(); + + /** + * this is a reimplementation of ChatSesstion slot. + * the original slot is not virtual, but that's not a problem because it's a slot. + */ + virtual void receivedTypingMsg( const TQString &, bool ); + + void slotConnectionTimeout(); + +private slots: + void slotMessageSent( Kopete::Message &message, Kopete::ChatSession *kmm ); + void slotMessageReceived( Kopete::Message &message ); + + void slotUserJoined( const TQString &handle, const TQString &publicName, bool IRO ); + void slotUserLeft( const TQString &handle, const TQString &reason ); + void slotSwitchBoardClosed(); + void slotInviteContact( Kopete::Contact *contact ); + void slotAcknowledgement( unsigned int id, bool ack ); + void slotInvitation( const TQString &handle, const TQString &msg ); + + void slotActionInviteAboutToShow(); + + void slotDisplayPictureChanged(); + + /** + * (debug) + */ + void slotDebugRawCommand(); + + void slotSendNudge(); + void slotWebcamReceive(); + void slotWebcamSend(); + void slotSendFile(); + + void slotNudgeReceived(const TQString& handle); + +private: + + MSNSwitchBoardSocket *m_chatService; + TQString otherString; + TDEActionMenu *m_actionInvite; + TQPtrList<TDEAction> m_inviteactions; + TDEAction *m_actionNudge; + TDEAction *m_actionWebcamReceive; + TDEAction *m_actionWebcamSend; + + //Messages sent before the ending of the connection are queued + TQValueList<Kopete::Message> m_messagesQueue; + void sendMessageQueue(); + void cleanMessageQueue( const TQString &reason); + void startChatSession(); + + TQMap<unsigned int, Kopete::Message> m_messagesSent; + + TQMap<long unsigned int, MSNInvitation*> m_invitations; + + + /** + * weither or not the "has opened a new chat" message need to be sent if the user is typing + */ + bool m_newSession; + + TQLabel *m_image; + TQTimer *m_timeoutTimer; + uint m_connectionTry; + + +signals: + /* + * This signal is relayed to the protocol and after, to plugins + */ + void invitation(MSNInvitation*& invitation, const TQString &bodyMSG , long unsigned int cookie , MSNChatSession* msnMM , MSNContact* c ); +}; + +#endif + +// vim: set noet ts=4 sts=4 tw=4: + diff --git a/kopete/protocols/msn/msnchatui.rc b/kopete/protocols/msn/msnchatui.rc new file mode 100644 index 00000000..6dbc94ca --- /dev/null +++ b/kopete/protocols/msn/msnchatui.rc @@ -0,0 +1,27 @@ +<!DOCTYPE kpartgui> +<kpartgui version="12" name="kopete_msn_chat"> + <MenuBar> + <Menu noMerge="1" name="file"> + <text>&Chat</text> + <Action name="msnWebcamReceive" /> + <Action name="msnWebcamSend" /> + <Action name="msnSendFile" /> + <Action name="msnSendNudge" /> + <Action name="msnRequestDisplayPicture" /> + <Action name="msnInvite" /> +<!-- <Menu name="debug"> + <text>&Debug</text> + <Action name="msnDebugRawCommand" /> + </Menu> --> + </Menu> + </MenuBar> + + + <ToolBar name="statusToolBar"> + <Action name="msnDisplayPicture" /> + <Action name="msnWebcamReceive" /> + <Action name="msnWebcamSend" /> + <Action name="msnSendFile" /> + <Action name="msnSendNudge" /> + </ToolBar> +</kpartgui> diff --git a/kopete/protocols/msn/msncontact.cpp b/kopete/protocols/msn/msncontact.cpp new file mode 100644 index 00000000..e58c3800 --- /dev/null +++ b/kopete/protocols/msn/msncontact.cpp @@ -0,0 +1,713 @@ +/* + msncontact.cpp - MSN Contact + + Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org> + Copyright (c) 2002 by Ryan Cumming <bodnar42@phalynx.dhs.org> + Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org> + Copyright (c) 2002-2005 by Olivier Goffart <ogoffart at kde.org> + Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + ************************************************************************* +*/ + +#include "msncontact.h" + +#include <tqcheckbox.h> + +#undef KDE_NO_COMPAT +#include <tdeaction.h> +#include <kdebug.h> +#include <tdefiledialog.h> +#include <klineedit.h> +#include <tdelocale.h> +#include <kstandarddirs.h> +#include <tdemessagebox.h> +#include <krun.h> +#include <tdetempfile.h> +#include <tdeconfig.h> +#include <tdeglobal.h> +#include <tqregexp.h> +#include <tdeio/job.h> + +#include "kopetecontactlist.h" +#include "kopetechatsessionmanager.h" +#include "kopetemetacontact.h" +#include "kopetegroup.h" +#include "kopeteuiglobal.h" +#include "kopeteglobal.h" + +#include "msninfo.h" +#include "msnchatsession.h" +#include "msnnotifysocket.h" +#include "msnaccount.h" + +MSNContact::MSNContact( Kopete::Account *account, const TQString &id, Kopete::MetaContact *parent ) +: Kopete::Contact( account, id, parent ) +{ + m_deleted = false; + m_allowed = false; + m_blocked = false; + m_reversed = false; + m_moving = false; + + m_clientFlags=0; + + setFileCapable( true ); + + // When we are not connected, it's because we are loading the contactlist. + // so we set the initial status to offline. + // We set offline directly because modifying the status after is too slow. + // (notification, contactlist updating,....) + // + // FIXME: Hacks like these shouldn't happen in the protocols, but should be + // covered properly at the libkopete level instead - Martijn + // + // When we are connected, it can be because the user added a contact with the + // wizard, and it can be because we are creating a temporary contact. + // if it's added by the wizard, the status will be set immediately after. + // if it's a temporary contact, better to set the unknown status. + setOnlineStatus( ( parent && parent->isTemporary() ) ? MSNProtocol::protocol()->UNK : MSNProtocol::protocol()->FLN ); + + actionBlock = 0L; + + setProperty(MSNProtocol::protocol()->propEmail, id); +} + +MSNContact::~MSNContact() +{ + kdDebug(14140) << k_funcinfo << endl; +} + +bool MSNContact::isReachable() +{ + if ( account()->isConnected() && isOnline() && account()->myself()->onlineStatus() != MSNProtocol::protocol()->HDN ) + return true; + + MSNChatSession *kmm=dynamic_cast<MSNChatSession*>(manager(Kopete::Contact::CannotCreate)); + if( kmm && kmm->service() ) //the chat socket is open. than mean message will be sent + return true; + + // When we are invisible we can't start a chat with others, make isReachable return false + // (This is an MSN limitation, not a problem in Kopete) + if ( !account()->isConnected() || account()->myself()->onlineStatus() == MSNProtocol::protocol()->HDN ) + return false; + + //if the contact is offline, it is impossible to send it a message. but it is impossible + //to be sure the contact is realy offline. For example, if the contact is not on the contactlist for + //some reason. + if( onlineStatus() == MSNProtocol::protocol()->FLN && ( isAllowed() || isBlocked() ) && !serverGroups().isEmpty() ) + return false; + + return true; +} + +Kopete::ChatSession *MSNContact::manager( Kopete::Contact::CanCreateFlags canCreate ) +{ + Kopete::ContactPtrList chatmembers; + chatmembers.append(this); + + Kopete::ChatSession *_manager = Kopete::ChatSessionManager::self()->findChatSession( account()->myself(), chatmembers, protocol() ); + MSNChatSession *manager = dynamic_cast<MSNChatSession*>( _manager ); + if(!manager && canCreate==Kopete::Contact::CanCreate) + { + manager = new MSNChatSession( protocol(), account()->myself(), chatmembers ); + static_cast<MSNAccount*>( account() )->slotStartChatSession( contactId() ); + } + return manager; +} + +TQPtrList<TDEAction> *MSNContact::customContextMenuActions() +{ + TQPtrList<TDEAction> *m_actionCollection = new TQPtrList<TDEAction>; + + // Block/unblock Contact + TQString label = isBlocked() ? i18n( "Unblock User" ) : i18n( "Block User" ); + if( !actionBlock ) + { + actionBlock = new TDEAction( label, "msn_blocked",0, this, TQT_SLOT( slotBlockUser() ), + this, "actionBlock" ); + + //show profile + actionShowProfile = new TDEAction( i18n("Show Profile") , 0, this, TQT_SLOT( slotShowProfile() ), + this, "actionShowProfile" ); + + // Send mail (only available if it is an hotmail account) + actionSendMail = new TDEAction( i18n("Send Email...") , "mail_generic",0, this, TQT_SLOT( slotSendMail() ), + this, "actionSendMail" ); + + // Invite to receive webcam + actionWebcamReceive = new TDEAction( i18n( "View Contact's Webcam" ), "webcamreceive", 0, this, TQT_SLOT(slotWebcamReceive() ), this, "msnWebcamReceive" ) ; + + //Send webcam action + actionWebcamSend = new TDEAction( i18n( "Send Webcam" ), "webcamsend", 0, this, TQT_SLOT(slotWebcamSend() ), this, "msnWebcamSend" ) ; + } + else + actionBlock->setText( label ); + + actionSendMail->setEnabled( static_cast<MSNAccount*>(account())->isHotmail()); + + m_actionCollection->append( actionBlock ); + m_actionCollection->append( actionShowProfile ); + m_actionCollection->append( actionSendMail ); + m_actionCollection->append( actionWebcamReceive ); + m_actionCollection->append( actionWebcamSend ); + + + return m_actionCollection; +} + +void MSNContact::slotBlockUser() +{ + MSNNotifySocket *notify = static_cast<MSNAccount*>( account() )->notifySocket(); + if( !notify ) + { + KMessageBox::error( Kopete::UI::Global::mainWidget(), + i18n( "<qt>Please go online to block or unblock a contact.</qt>" ), + i18n( "MSN Plugin" )); + return; + } + + if( m_blocked ) + { + notify->removeContact( contactId(), MSNProtocol::BL, TQString(), TQString() ); + } + else + { + if(m_allowed) + notify->removeContact( contactId(), MSNProtocol::AL, TQString(), TQString() ); + else + notify->addContact( contactId(), MSNProtocol::BL, TQString(), TQString(), TQString() ); + } +} + +void MSNContact::slotUserInfo() +{ + KDialogBase *infoDialog=new KDialogBase( 0l, "infoDialog", /*modal = */false, TQString(), KDialogBase::Close , KDialogBase::Close, false ); + TQString nick=property( Kopete::Global::Properties::self()->nickName()).value().toString(); + TQString personalMessage=property( MSNProtocol::protocol()->propPersonalMessage).value().toString(); + MSNInfo *info=new MSNInfo ( infoDialog,"info"); + info->m_id->setText( contactId() ); + info->m_displayName->setText(nick); + info->m_personalMessage->setText(personalMessage); + info->m_phh->setText(m_phoneHome); + info->m_phw->setText(m_phoneWork); + info->m_phm->setText(m_phoneMobile); + info->m_reversed->setChecked(m_reversed); + + connect( info->m_reversed, TQT_SIGNAL(toggled(bool)) , this, TQT_SLOT(slotUserInfoDialogReversedToggled())); + + infoDialog->setMainWidget(info); + infoDialog->setCaption(nick); + infoDialog->show(); +} + +void MSNContact::slotUserInfoDialogReversedToggled() +{ + //workaround to make this checkboxe readonly + const TQCheckBox *cb=dynamic_cast<const TQCheckBox*>(sender()); + if(cb && cb->isChecked()!=m_reversed) + const_cast<TQCheckBox*>(cb)->setChecked(m_reversed); +} + +void MSNContact::deleteContact() +{ + kdDebug( 14140 ) << k_funcinfo << endl; + + MSNNotifySocket *notify = static_cast<MSNAccount*>( account() )->notifySocket(); + if( notify ) + { + if( hasProperty(MSNProtocol::protocol()->propGuid.key()) ) + { + // Remove from all groups he belongs (if applicable) + for( TQMap<TQString, Kopete::Group*>::Iterator it = m_serverGroups.begin(); it != m_serverGroups.end(); ++it ) + { + kdDebug(14140) << k_funcinfo << "Removing contact from group \"" << it.key() << "\"" << endl; + notify->removeContact( contactId(), MSNProtocol::FL, guid(), it.key() ); + } + + // Then trully remove it from server contact list, + // because only removing the contact from his groups isn't sufficent from MSNP11. + kdDebug( 14140 ) << k_funcinfo << "Removing contact from top-level." << endl; + notify->removeContact( contactId(), MSNProtocol::FL, guid(), TQString()); + } + else + { + kdDebug( 14140 ) << k_funcinfo << "The contact is already removed from server, just delete it" << endl; + deleteLater(); + } + } + else + { + // FIXME: This case should be handled by Kopete, not by the plugins :( - Martijn + // FIXME: We should be able to delete contacts offline, and remove it from server next time we go online - Olivier + KMessageBox::error( Kopete::UI::Global::mainWidget(), i18n( "<qt>Please go online to remove a contact from your contact list.</qt>" ), i18n( "MSN Plugin" )); + } +} + +bool MSNContact::isBlocked() const +{ + return m_blocked; +} + +void MSNContact::setBlocked( bool blocked ) +{ + if( m_blocked != blocked ) + { + m_blocked = blocked; + //update the status + setOnlineStatus(m_currentStatus); + //m_currentStatus is used here. previously it was onlineStatus() but this may cause problem when + // the account is offline because of the Kopete::Contact::OnlineStatus() account offline hack. + } +} + +bool MSNContact::isAllowed() const +{ + return m_allowed; +} + +void MSNContact::setAllowed( bool allowed ) +{ + m_allowed = allowed; +} + +bool MSNContact::isReversed() const +{ + return m_reversed; +} + +void MSNContact::setReversed( bool reversed ) +{ + m_reversed= reversed; +} + +bool MSNContact::isDeleted() const +{ + return m_deleted; +} + +void MSNContact::setDeleted( bool deleted ) +{ + m_deleted= deleted; +} + +uint MSNContact::clientFlags() const +{ + return m_clientFlags; +} + +void MSNContact::setClientFlags( uint flags ) +{ + if(m_clientFlags != flags) + { + if(hasProperty( MSNProtocol::protocol()->propClient.key() )) + { + if( flags & MSNProtocol::WebMessenger) + setProperty( MSNProtocol::protocol()->propClient , i18n("Web Messenger") ); + else if( flags & MSNProtocol::WindowsMobile) + setProperty( MSNProtocol::protocol()->propClient , i18n("Windows Mobile") ); + else if( flags & MSNProtocol::MSNMobileDevice) + setProperty( MSNProtocol::protocol()->propClient , i18n("MSN Mobile") ); + else if( m_obj.contains("kopete") ) + setProperty( MSNProtocol::protocol()->propClient , i18n("Kopete") ); + } + + } + m_clientFlags=flags; +} + +void MSNContact::setInfo(const TQString &type,const TQString &data ) +{ + if( type == "PHH" ) + { + m_phoneHome = data; + setProperty(MSNProtocol::protocol()->propPhoneHome, data); + } + else if( type == "PHW" ) + { + m_phoneWork=data; + setProperty(MSNProtocol::protocol()->propPhoneWork, data); + } + else if( type == "PHM" ) + { + m_phoneMobile = data; + setProperty(MSNProtocol::protocol()->propPhoneMobile, data); + } + else if( type == "MOB" ) + { + if( data == "Y" ) + m_phone_mob = true; + else if( data == "N" ) + m_phone_mob = false; + else + kdDebug( 14140 ) << k_funcinfo << "Unknown MOB " << data << endl; + } + else if( type == "MFN" ) + { + setProperty(Kopete::Global::Properties::self()->nickName(), data ); + } + else + { + kdDebug( 14140 ) << k_funcinfo << "Unknow info " << type << " " << data << endl; + } +} + + +void MSNContact::serialize( TQMap<TQString, TQString> &serializedData, TQMap<TQString, TQString> & /* addressBookData */ ) +{ + // Contact id and display name are already set for us, only add the rest + TQString groups; + bool firstEntry = true; + for( TQMap<TQString, Kopete::Group *>::ConstIterator it = m_serverGroups.begin(); it != m_serverGroups.end(); ++it ) + { + if( !firstEntry ) + { + groups += ","; + firstEntry = true; + } + groups += it.key(); + } + + TQString lists="C"; + if(m_blocked) + lists +="B"; + if(m_allowed) + lists +="A"; + if(m_reversed) + lists +="R"; + + serializedData[ "groups" ] = groups; + serializedData[ "PHH" ] = m_phoneHome; + serializedData[ "PHW" ] = m_phoneWork; + serializedData[ "PHM" ] = m_phoneMobile; + serializedData[ "lists" ] = lists; + serializedData[ "obj" ] = m_obj; + serializedData[ "contactGuid" ] = guid(); +} + + +TQString MSNContact::guid(){ return property(MSNProtocol::protocol()->propGuid).value().toString(); } + +TQString MSNContact::phoneHome(){ return m_phoneHome ;} +TQString MSNContact::phoneWork(){ return m_phoneWork ;} +TQString MSNContact::phoneMobile(){ return m_phoneMobile ;} + + +const TQMap<TQString, Kopete::Group*> MSNContact::serverGroups() const +{ + return m_serverGroups; +} +void MSNContact::clearServerGroups() +{ + m_serverGroups.clear(); +} + + +void MSNContact::sync( unsigned int changed ) +{ + if( ! (changed & Kopete::Contact::MovedBetweenGroup) ) + return; //we are only interested by a change in groups + + if(!metaContact() || metaContact()->isTemporary() ) + return; + + if(m_moving) + { + //We need to make sure that syncGroups is not called twice successively + // because m_serverGroups will be only updated with the reply of the server + // and then, the same command can be sent twice. + // FIXME: if this method is called a seconds times, that mean change can be + // done in the contactlist. we should found a way to recall this + // method later. (a TQTimer?) + kdDebug( 14140 ) << k_funcinfo << " This contact is already moving. Abort sync id: " << contactId() << endl; + return; + } + + MSNNotifySocket *notify = static_cast<MSNAccount*>( account() )->notifySocket(); + if( !notify ) + { + //We are not connected, we will doing it next connection. + //Force to reload the whole contactlist from server to suync groups when connecting + account()->configGroup()->writeEntry("serial", 0 ); + return; + } + + if(m_deleted) //the contact hasn't been synced from server yet. + return; + + unsigned int count=m_serverGroups.count(); + + //Don't add the contact if it's myself. + if(count==0 && contactId() == account()->accountId()) + return; + + //STEP ONE : add the contact to every kopetegroups where the MC is + TQPtrList<Kopete::Group> groupList = metaContact()->groups(); + for ( Kopete::Group *group = groupList.first(); group; group = groupList.next() ) + { + //For each group, ensure it is on the MSN server + if( !group->pluginData( protocol() , account()->accountId() + " id" ).isEmpty() ) + { + TQString Gid=group->pluginData( protocol(), account()->accountId() + " id" ); + if( !static_cast<MSNAccount*>( account() )->m_groupList.contains(Gid) ) + { // ohoh! something is corrupted on the contactlist.xml + // anyway, we never should add a contact to an unexisting group on the server. + // This shouln't be possible anymore 2004-06-10 -Olivier + + //repair the problem + group->setPluginData( protocol() , account()->accountId() + " id" , TQString()); + group->setPluginData( protocol() , account()->accountId() + " displayName" , TQString()); + kdWarning( 14140 ) << k_funcinfo << " Group " << group->displayName() << " marked with id #" <<Gid << " does not seems to be anymore on the server" << endl; + + if(!group->displayName().isEmpty() && group->type() == Kopete::Group::Normal) //not the top-level + { + //Create the group and add the contact + static_cast<MSNAccount*>( account() )->addGroup( group->displayName(),contactId() ); + count++; + m_moving=true; + } + } + else if( !m_serverGroups.contains(Gid) ) + { + //Add the contact to the group on the server + notify->addContact( contactId(), MSNProtocol::FL, TQString(), guid(), Gid ); + count++; + m_moving=true; + } + } + else + { + if(!group->displayName().isEmpty() && group->type() == Kopete::Group::Normal) //not the top-level + { + //Create the group and add the contact + static_cast<MSNAccount*>( account() )->addGroup( group->displayName(),contactId() ); + + //WARNING: if contact is not correctly added (because the group was not aded corrdctly for hinstance), + // if we increment the count, the contact can be deleted from the old group, and be lost :-( + count++; + m_moving=true; + } + } + } + + //STEP TWO : remove the contact from groups where the MC is not, but let it at least in one group + + //contact is not in that group. on the server. we will remove them dirrectly after the loop + TQValueList<TQString> removinglist; + + for( TQMap<TQString, Kopete::Group*>::Iterator it = m_serverGroups.begin();(count > 1 && it != m_serverGroups.end()); ++it ) + { + if( !static_cast<MSNAccount*>( account() )->m_groupList.contains(it.key()) ) + { // ohoh! something is corrupted on the contactlist.xml + // anyway, we never should add a contact to an unexisting group on the server. + + //repair the problem ... //contactRemovedFromGroup( it.key() ); + // ... later (we can't remove it from the map now ) + removinglist.append(it.key()); + count--; + + kdDebug( 14140 ) << k_funcinfo << "the group marked with id #" << it.key() << " does not seems to be anymore on the server" << endl; + + continue; + } + + Kopete::Group *group=it.data(); + if(!group) //we can't trust the data of it() see in MSNProtocol::deserializeContact why + group=static_cast<MSNAccount*>( account() )->m_groupList[it.key()]; + if( !metaContact()->groups().contains(group) ) + { + m_moving=true; + notify->removeContact( contactId(), MSNProtocol::FL, guid(), it.key() ); + count--; + } + } + + for(TQValueList<TQString>::Iterator it= removinglist.begin() ; it != removinglist.end() ; ++it ) + contactRemovedFromGroup(*it); + + //FINAL TEST: is the contact at least in a group.. + // this may happens if we just added a temporary contact to top-level + // we add the contact to the group #0 (the default one) + /*if(count==0) + { +// notify->addContact( contactId(), MSNProtocol::FL, TQString(), guid(), "0"); + }*/ +} + +void MSNContact::contactAddedToGroup( const TQString& groupId, Kopete::Group *group ) +{ + m_serverGroups.insert( groupId, group ); + m_moving=false; +} + +void MSNContact::contactRemovedFromGroup( const TQString& groupId ) +{ + m_serverGroups.remove( groupId ); + if(m_serverGroups.isEmpty() && !m_moving) + { + deleteLater(); + } + m_moving=false; +} + + +void MSNContact::rename( const TQString &newName ) +{ + //kdDebug( 14140 ) << k_funcinfo << "From: " << displayName() << ", to: " << newName << endl; + +/* if( newName == displayName() ) + return;*/ + + // FIXME: This should be called anymore. + MSNNotifySocket *notify = static_cast<MSNAccount*>( account() )->notifySocket(); + if( notify ) + { + notify->changePublicName( newName, contactId() ); + } +} + +void MSNContact::slotShowProfile() +{ + KRun::runURL( KURL( TQString::fromLatin1("http://members.msn.com/?pgmarket=it-it&mem=") + contactId()) , "text/html" ); +} + + +/** + * FIXME: Make this a standard KMM API call + */ +void MSNContact::sendFile( const KURL &sourceURL, const TQString &altFileName, uint /*fileSize*/ ) +{ + TQString filePath; + + //If the file location is null, then get it from a file open dialog + if( !sourceURL.isValid() ) + filePath = KFileDialog::getOpenFileName( TQString() ,"*", 0l , i18n( "Kopete File Transfer" )); + else + filePath = sourceURL.path(-1); + + //kdDebug(14140) << "MSNContact::sendFile: File chosen to send:" << fileName << endl; + + if ( !filePath.isEmpty() ) + { + TQ_UINT32 fileSize = TQFileInfo(filePath).size(); + //Send the file + static_cast<MSNChatSession*>( manager(Kopete::Contact::CanCreate) )->sendFile( filePath, altFileName, fileSize ); + + } +} + +void MSNContact::setOnlineStatus(const Kopete::OnlineStatus& status) +{ + if(isBlocked() && status.internalStatus() < 15) + { + Kopete::Contact::setOnlineStatus( + Kopete::OnlineStatus(status.status() , + (status.weight()==0) ? 0 : (status.weight() -1) , + protocol() , + status.internalStatus()+15 , + status.overlayIcons() + TQStringList("msn_blocked") , + i18n("%1|Blocked").arg( status.description() ) ) ); + } + else if(!isBlocked() && status.internalStatus() >= 15) + { //the user is not blocked, but the status is blocked + switch(status.internalStatus()-15) + { + case 1: + Kopete::Contact::setOnlineStatus(MSNProtocol::protocol()->NLN); + break; + case 2: + Kopete::Contact::setOnlineStatus(MSNProtocol::protocol()->BSY); + break; + case 3: + Kopete::Contact::setOnlineStatus(MSNProtocol::protocol()->BRB); + break; + case 4: + Kopete::Contact::setOnlineStatus(MSNProtocol::protocol()->AWY); + break; + case 5: + Kopete::Contact::setOnlineStatus(MSNProtocol::protocol()->PHN); + break; + case 6: + Kopete::Contact::setOnlineStatus(MSNProtocol::protocol()->LUN); + break; + case 7: + Kopete::Contact::setOnlineStatus(MSNProtocol::protocol()->FLN); + break; + case 8: + Kopete::Contact::setOnlineStatus(MSNProtocol::protocol()->HDN); + break; + case 9: + Kopete::Contact::setOnlineStatus(MSNProtocol::protocol()->IDL); + break; + default: + Kopete::Contact::setOnlineStatus(MSNProtocol::protocol()->UNK); + break; + } + } + else + Kopete::Contact::setOnlineStatus(status); + m_currentStatus=status; +} + +void MSNContact::slotSendMail() +{ + MSNNotifySocket *notify = static_cast<MSNAccount*>( account() )->notifySocket(); + if( notify ) + { + notify->sendMail( contactId() ); + } +} + +void MSNContact::setDisplayPicture(KTempFile *f) +{ + //copy the temp file somewere else. + // in a better world, the file could be dirrectly wrote at the correct location. + // but the custom emoticon code is to deeply merged in the display picture code while it could be separated. + TQString newlocation=locateLocal( "appdata", "msnpictures/"+ contactId().lower().replace(TQRegExp("[./~]"),"-") +".png" ) ; + + TDEIO::Job *j=TDEIO::file_move( KURL::fromPathOrURL( f->name() ) , KURL::fromPathOrURL( newlocation ) , -1, true /*overwrite*/ , false /*resume*/ , false /*showProgressInfo*/ ); + + f->setAutoDelete(false); + delete f; + + //let the time to TDEIO to copy the file + connect(j, TQT_SIGNAL(result(TDEIO::Job *)) , this, TQT_SLOT(slotEmitDisplayPictureChanged() )); +} + +void MSNContact::slotEmitDisplayPictureChanged() +{ + TQString newlocation=locateLocal( "appdata", "msnpictures/"+ contactId().lower().replace(TQRegExp("[./~]"),"-") +".png" ) ; + setProperty( Kopete::Global::Properties::self()->photo() , newlocation ); + emit displayPictureChanged(); +} + +void MSNContact::setObject(const TQString &obj) +{ + if(m_obj==obj && (obj.isEmpty() || hasProperty(Kopete::Global::Properties::self()->photo().key()))) + return; + + m_obj=obj; + + removeProperty( Kopete::Global::Properties::self()->photo() ) ; + emit displayPictureChanged(); + + TDEConfig *config = TDEGlobal::config(); + config->setGroup( "MSN" ); + if ( config->readNumEntry( "DownloadPicture", 2 ) >= 2 && !obj.isEmpty() + && account()->myself()->onlineStatus().status() != Kopete::OnlineStatus::Invisible ) + manager(Kopete::Contact::CanCreate); //create the manager which will download the photo automatically. +} + +#include "msncontact.moc" + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/msn/msncontact.h b/kopete/protocols/msn/msncontact.h new file mode 100644 index 00000000..759be403 --- /dev/null +++ b/kopete/protocols/msn/msncontact.h @@ -0,0 +1,200 @@ +/* + msncontact.h - MSN Contact + + Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org> + Copyright (c) 2002 by Ryan Cumming <bodnar42@phalynx.dhs.org> + Copyright (c) 2002 by Martijn Klingens <klingens@kde.org> + Copyright (c) 2002-2005 by Olivier Goffart <ogoffart at kde.org> + Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef MSNCONTACT_H +#define MSNCONTACT_H + +#include "kopetecontact.h" +#include "kopeteonlinestatus.h" + +#include <kurl.h> + +class TQListView; +class TQListViewItem; +class TQPixmap; +class TQTimer; + +class MSNChatSession; +class TDEAction; +class TDEActionCollection; +class KTempFile; + +namespace Kopete { class Protocol; } +namespace Kopete { class OnlineStatus; } + +class MSNContact : public Kopete::Contact +{ + Q_OBJECT + + +public: + MSNContact( Kopete::Account *account, const TQString &id, Kopete::MetaContact *parent ); + ~MSNContact(); + + /** + * Indicate whether this contact is blocked + */ + bool isBlocked() const; + void setBlocked( bool b ); + + /** + * Indicate whether this contact is deleted + * (not on the serverside list) + */ + bool isDeleted() const; + void setDeleted( bool d ); + + /** + * Indicate whether this contact is allowed + */ + bool isAllowed() const; + void setAllowed( bool d ); + + /** + * Indicate whether this contact is on the reversed list + */ + bool isReversed() const; + void setReversed( bool d ); + + /** + * set one phone number + */ + void setInfo(const TQString &type, const TQString &data); + + /** + * The groups in which the user is located on the server. + */ + const TQMap<TQString, Kopete::Group *> serverGroups() const; + /** + * clear that map + */ + void clearServerGroups(); + + /** + * client flags (say what version of msn messenger the contact is using) + */ + uint clientFlags() const; + void setClientFlags( uint ); + + virtual bool isReachable(); + + virtual TQPtrList<TDEAction> *customContextMenuActions(); + + /** + * update the server group map + */ + void contactRemovedFromGroup( const TQString& groupId ); + void contactAddedToGroup(const TQString& groupId, Kopete::Group *group ); + + virtual void serialize( TQMap<TQString, TQString> &serializedData, TQMap<TQString, TQString> &addressBookData ); + + /** + * Rename contact on server + */ + virtual void rename( const TQString &newName ) KDE_DEPRECATED; + + /** + * Returns the MSN Message Manager associated with this contact + */ + virtual Kopete::ChatSession *manager( Kopete::Contact::CanCreateFlags = Kopete::Contact::CannotCreate ); + + + /** + * Because blocked contact have a small auto-modified status + */ + void setOnlineStatus(const Kopete::OnlineStatus&); + + TQString guid(); + TQString phoneHome(); + TQString phoneWork(); + TQString phoneMobile(); + + void setObject(const TQString &obj); + TQString object() const { return m_obj; } + +public slots: + virtual void slotUserInfo(); + virtual void deleteContact(); + virtual void sendFile( const KURL &sourceURL = KURL(), + const TQString &fileName = TQString(), uint fileSize = 0L ); + + /** + * Every time the kopete's contactlist is modified, we sync the serverlist with it + */ + virtual void sync( unsigned int cvhanged= 0xff); + + + void setDisplayPicture(KTempFile *f) ; + +signals: + void displayPictureChanged(); + +private slots: + void slotBlockUser(); + void slotShowProfile(); + void slotSendMail(); + void slotEmitDisplayPictureChanged(); + + /** + * Workaround to make this checkboxe readonly + */ + void slotUserInfoDialogReversedToggled(); + +private: + TQMap<TQString, Kopete::Group *> m_serverGroups; + + bool m_blocked; + bool m_allowed; + bool m_deleted; + bool m_reversed; + bool m_moving; + bool m_phone_mob; + + uint m_clientFlags; + + TQString m_phoneHome; + TQString m_phoneWork; + TQString m_phoneMobile; + + + TDEAction *actionBlock; + TDEAction *actionShowProfile; + TDEAction *actionSendMail; + TDEAction *actionWebcamReceive; + TDEAction *actionWebcamSend; + + TQString m_obj; //the MSNObject + + /** + * keep the current status here. (it's normally already in Kopete::Contact::d->onlineStatus) + * This is a workaround to prevent problems with the account offline status. + */ + Kopete::OnlineStatus m_currentStatus; + + //MSNProtocol::deserializeContact need to acess some contact insternals + friend class MSNProtocol; +}; + +#endif + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/msn/msndebugrawcmddlg.cpp b/kopete/protocols/msn/msndebugrawcmddlg.cpp new file mode 100644 index 00000000..b2a4ebbd --- /dev/null +++ b/kopete/protocols/msn/msndebugrawcmddlg.cpp @@ -0,0 +1,73 @@ +/* + msndebugrawcmddlg.cpp - Send a raw MSN command for debugging + + Copyright (c) 2002 by Martijn Klingens <klingens@kde.org> + Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org> + + Portions of this code are taken from KMerlin, + (c) 2001 by Olaf Lueg <olueg@olsd.de> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include "msndebugrawcmddlg.h" + +#include "ui/msndebugrawcommand_base.h" + +#include <tqcheckbox.h> +#include <tqlineedit.h> +#include <ktextedit.h> + +#include <tdelocale.h> + +MSNDebugRawCmdDlg::MSNDebugRawCmdDlg( TQWidget *parent ) +: KDialogBase( parent, 0L, true, + i18n( "DEBUG: Send Raw Command - MSN Plugin" ), Ok | Cancel, + Ok, true ) +{ + setInitialSize( TQSize( 350, 200 ) ); + + m_main = new MSNDebugRawCommand_base( this ); + setMainWidget( m_main ); +} + +MSNDebugRawCmdDlg::~MSNDebugRawCmdDlg() +{ +} + +TQString MSNDebugRawCmdDlg::command() +{ + return m_main->m_command->text(); +} + +TQString MSNDebugRawCmdDlg::params() +{ + return m_main->m_params->text(); +} + +bool MSNDebugRawCmdDlg::addNewline() +{ + return m_main->m_addNewline->isChecked(); +} + +bool MSNDebugRawCmdDlg::addId() +{ + return m_main->m_addId->isChecked(); +} + +TQString MSNDebugRawCmdDlg::msg() +{ + return m_main->m_msg->text(); +} + +#include "msndebugrawcmddlg.moc" + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/msn/msndebugrawcmddlg.h b/kopete/protocols/msn/msndebugrawcmddlg.h new file mode 100644 index 00000000..443b8c05 --- /dev/null +++ b/kopete/protocols/msn/msndebugrawcmddlg.h @@ -0,0 +1,54 @@ +/* + msndebugrawcmddlg.h - Send a raw MSN command for debugging + + Copyright (c) 2002 by Martijn Klingens <klingens@kde.org> + Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org> + + Portions of this code are taken from KMerlin, + (c) 2001 by Olaf Lueg <olueg@olsd.de> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef MSNDEBUGRAWCMDDLG_H +#define MSNDEBUGRAWCMDDLG_H + +#include <kdialogbase.h> + +class MSNDebugRawCommand_base; + +/** + * @author Martijn Klingens <klingens@kde.org> + * + * Simple debugging help + */ +class MSNDebugRawCmdDlg : public KDialogBase +{ + Q_OBJECT + + +public: + MSNDebugRawCmdDlg( TQWidget *parent ); + ~MSNDebugRawCmdDlg(); + + TQString command(); + TQString params(); + bool addNewline(); + bool addId(); + TQString msg(); + +private: + MSNDebugRawCommand_base *m_main; +}; + +#endif + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/msn/msnfiletransfersocket.cpp b/kopete/protocols/msn/msnfiletransfersocket.cpp new file mode 100644 index 00000000..b7121c30 --- /dev/null +++ b/kopete/protocols/msn/msnfiletransfersocket.cpp @@ -0,0 +1,481 @@ +/*************************************************************************** + msnfiletransfersocket.cpp - description + ------------------- + begin : mer jui 31 2002 + copyright : (C) 2002 by Olivier Goffart + email : ogoffart @ kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#include "msnfiletransfersocket.h" + +#include <stdlib.h> +#include <math.h> + +//qt +#include <tqtimer.h> + +// kde +#include <kdebug.h> +#include <kserversocket.h> +#include <kbufferedsocket.h> +#include <tdefiledialog.h> +#include <tdelocale.h> + +#include "kopetetransfermanager.h" +#include "kopetecontact.h" +#include "kopetemetacontact.h" +#include "msnchatsession.h" +#include "msnswitchboardsocket.h" +#include "msnnotifysocket.h" +#include "msnaccount.h" + +using namespace KNetwork; + +MSNFileTransferSocket::MSNFileTransferSocket(const TQString &handle, Kopete::Contact *c,bool incoming, TQObject* parent) + : MSNSocket(parent) , MSNInvitation(incoming, MSNFileTransferSocket::applicationID() , i18n("File Transfer - MSN Plugin")) +{ + m_handle=handle; + m_kopeteTransfer=0l; + m_file=0L; + m_server=0L; + m_contact=c; + ready=true; + + TQObject::connect( this, TQT_SIGNAL( socketClosed() ), this, TQT_SLOT( slotSocketClosed() ) ); + TQObject::connect( this, TQT_SIGNAL( blockRead( const TQByteArray & ) ), this, TQT_SLOT(slotReadBlock( const TQByteArray & ) ) ); +} + +MSNFileTransferSocket::~MSNFileTransferSocket() +{ + delete m_file; + delete m_server; + kdDebug(14140) << "MSNFileTransferSocket::~MSNFileTransferSocket" <<endl; +} + +void MSNFileTransferSocket::parseCommand(const TQString & cmd, uint id, const TQString & data) +{ + if( cmd == "VER" ) + { + if(data.section( ' ', 0, 0 ) != "MSNFTP") + { + kdDebug(14140) << "MSNFileTransferSocket::parseCommand (VER): bad version: disconnect" <<endl; + disconnect(); + } + else + { + if( m_incoming ) + sendCommand( "USR", m_handle + " " + m_authcook, false ); + else + sendCommand( "VER", "MSNFTP" , false ); + } + } + else if( cmd == "FIL" ) + { + m_size=id; //data.toUInt(); //BUG: the size is take as id bye MSNSocket because it is a number + + m_downsize=0; + m_file=new TQFile(m_fileName); + + if( m_file->open( IO_WriteOnly )) + sendCommand( "TFR" ,NULL,false); + else + { + kdDebug(14140) << "MSNFileTransferSocket::parseCommand: ERROR: unable to open file - disconnect " <<endl; + disconnect(); + } + } + else if( cmd == "BYE" ) + { + kdDebug(14140) << "MSNFileTransferSocket::parseCommand : end of transfer " <<endl; + disconnect(); + } + else if( cmd == "USR" ) + { + if(data.section( ' ', 1, 1 )!= m_authcook) + { + kdDebug(14140) << "MSNFileTransferSocket::parseCommand (USR): bad auth" <<endl; + disconnect(); + } + else + sendCommand("FIL" , TQString::number(size()) , false); + } + else if( cmd == "TFR" ) + { + m_downsize=0; + ready=true; + TQTimer::singleShot( 0, this, TQT_SLOT(slotSendFile()) ); + } + else if( cmd == "CCL" ) + { + disconnect(); + } + else + kdDebug(14140) << "MSNFileTransferSocket::parseCommand : unknown command " <<cmd <<endl; + +// kdDebug(14140) << "MSNFileTransferSocket::parseCommand : done " <<cmd <<endl; +} + +void MSNFileTransferSocket::doneConnect() +{ + if(m_incoming) + sendCommand( "VER", "MSNFTP", false ); + MSNSocket::doneConnect(); +} + +void MSNFileTransferSocket::bytesReceived(const TQByteArray & head) +{ + if(head[0]!='\0') + { + kdDebug(14140) << "MSNFileTransferSocket::bytesReceived: transfer aborted" <<endl; + TQTimer::singleShot(0,this,TQT_SLOT(disconnect())); + } + unsigned int sz=(int)((unsigned char)head.data()[2])*256+(int)((unsigned char)head.data()[1]); +// kdDebug(14140) << "MSNFileTransferSocket::bytesReceived: " << sz <<endl; + readBlock(sz); +} + +void MSNFileTransferSocket::slotSocketClosed() +{ + kdDebug(14140) << "MSNFileTransferSocket::slotSocketClose "<< endl; + if(m_file) + m_file->close(); + delete m_file; + m_file=0L; + delete m_server; + m_server=0L; + if(m_kopeteTransfer) + { + if( (m_downsize!=m_size || m_downsize==0 ) ) + m_kopeteTransfer->slotError( TDEIO::ERR_UNKNOWN , i18n( "An unknown error occurred" ) ); + else + m_kopeteTransfer->slotComplete(); + } + emit done(this); +} + +void MSNFileTransferSocket::slotReadBlock(const TQByteArray &block) +{ + m_file->writeBlock( block.data(), block.size() ); // write to file + + m_downsize+=block.size(); + if(m_kopeteTransfer) m_kopeteTransfer->slotProcessed(m_downsize); + kdDebug(14140) << "MSNFileTransferSocket - " << m_downsize << " of " << m_size <<" done"<<endl; + + if(m_downsize==m_size) + { + //the transfer seems to be finished. + sendCommand( "BYE" ,"16777989",false); + // if we are not already disconected in 30 seconds, do it. + TQTimer::singleShot( 30000 , this, TQT_SLOT(disconnect() ) ); + + } +} + +void MSNFileTransferSocket::setKopeteTransfer(Kopete::Transfer *kt) +{ + m_kopeteTransfer=kt; + if(kt) + { + TQObject::connect(kt , TQT_SIGNAL(transferCanceled()), this, TQT_SLOT(abort())); + TQObject::connect(kt, TQT_SIGNAL(destroyed()) , this , TQT_SLOT(slotKopeteTransferDestroyed())); + } +} + +void MSNFileTransferSocket::listen(int port) +{ + m_server = new TDEServerSocket(); + + TQObject::connect( m_server, TQT_SIGNAL(readyAccept()), this, TQT_SLOT(slotAcceptConnection())); + m_server->setAddress(TQString::number(port)); + + kdDebug(14140) << "MSNFileTransferSocket::listen: about to listen"<<endl; + bool listenResult = m_server->listen(1); + kdDebug(14140) << "MSNFileTransferSocket::listen: result: "<< listenResult <<endl; + TQTimer::singleShot( 60000, this, TQT_SLOT(slotTimer()) ); + kdDebug(14140) << "MSNFileTransferSocket::listen done" <<endl; +} + +void MSNFileTransferSocket::slotAcceptConnection() +{ + kdDebug(14140) << "MSNFileTransferSocket::slotAcceptConnection" <<endl; + if(!accept(m_server)) + { + if( m_kopeteTransfer) + m_kopeteTransfer->slotError( TDEIO::ERR_UNKNOWN , i18n( "An unknown error occurred" ) ); + emit done(this); + } +} + +void MSNFileTransferSocket::slotTimer() +{ + if(onlineStatus() != Disconnected) + return; + kdDebug(14140) << "MSNFileTransferSocket::slotTimer: timeout "<< endl; + if( m_kopeteTransfer) + { + m_kopeteTransfer->slotError( TDEIO::ERR_CONNECTION_BROKEN , i18n( "Connection timed out" ) ); + } + + MSNChatSession* manager=dynamic_cast<MSNChatSession*>(m_contact->manager()); + if(manager && manager->service()) + manager->service()->sendCommand( "MSG" , "N", true, rejectMessage("TIMEOUT") ); + + emit done(this); +} + +void MSNFileTransferSocket::abort() +{ + if(m_incoming) + { + sendCommand( "CCL" , NULL ,false); + } + else + { + TQByteArray bytes(3); + bytes[0]='\1'; + bytes[1]='\0'; + bytes[2]='\0'; + sendBytes( bytes ); + m_downsize=m_size; //we don't want to send data anymore; + } + //the timer wait one second, the time to send the CCL or the binary header + //retarding the disconnection keep away from a crash. (in TDEIO::Job::emitResult when `delete this`) + TQTimer::singleShot( 1000, this, TQT_SLOT(disconnect()) ); + ready=false; +} + +void MSNFileTransferSocket::setFile( const TQString &fn, long unsigned int fileSize ) +{ + m_fileName=fn; + if(!m_incoming) + { + if(m_file) + { + kdDebug(14140) << "MSNFileTransferSocket::setFileName: WARNING m_file already exists" << endl; + delete m_file; + } + m_file = new TQFile( fn ); + if(!m_file->open(IO_ReadOnly)) + { + //FIXME: abort transfer here + kdDebug(14140) << "MSNFileTransferSocket::setFileName: WARNING unable to open the file" << endl; + } + + //If the fileSize is 0 it was not given, we are to get it from the file + if(fileSize == 0L) + m_size = m_file->size(); + else + m_size = fileSize; + } +} + + +void MSNFileTransferSocket::slotSendFile() +{ +// kdDebug(14140) <<"MSNFileTransferSocket::slotSendFile()" <<endl; + if( m_downsize >= m_size) + { + //the transfer seems to be finished. + // if we are not already disconected in 30 seconds, do it. + TQTimer::singleShot( 30000 , this, TQT_SLOT(disconnect() ) ); + return; + } + + if(ready) + { + char data[2046]; + int bytesRead = m_file->readBlock( data, 2045 ); + + TQByteArray block(bytesRead+3); +// char i1= (char)fmod( bytesRead, 256 ) ; +// char i2= (char)floor( bytesRead / 256 ) ; +// kdDebug(14140) << "MSNFileTransferSocket::slotSendFile: " << (int)i1 <<" + 256* "<< (int)i2 <<" = " << bytesRead <<endl; + block[0]='\0'; + block[1]= (char)fmod( bytesRead, 256 ); + block[2]= (char)floor( bytesRead / 256 ); + + for ( int f = 0; f < bytesRead; f++ ) + { + block[f+3] = data[f]; + } + + sendBytes(block); + + m_downsize+=bytesRead; + if(m_kopeteTransfer) + m_kopeteTransfer->slotProcessed(m_downsize); + kdDebug(14140) << "MSNFileTransferSocket::slotSendFile: " << m_downsize << " of " << m_size <<" done"<<endl; + } + ready=false; + + TQTimer::singleShot( 10, this, TQT_SLOT(slotSendFile()) ); +} + +void MSNFileTransferSocket::slotReadyWrite() +{ + ready=true; + MSNSocket::slotReadyWrite(); +} + +TQString MSNFileTransferSocket::invitationHead() +{ + TQTimer::singleShot( 10 * 60000, this, TQT_SLOT(slotTimer()) ); //the user has 10 mins to accept or refuse or initiate the transfer + + return TQString( MSNInvitation::invitationHead()+ + "Application-File: "+ m_fileName.right( m_fileName.length() - m_fileName.findRev( '/' ) - 1 ) +"\r\n" + "Application-FileSize: "+ TQString::number(size()) +"\r\n\r\n").utf8(); +} + +void MSNFileTransferSocket::parseInvitation(const TQString& msg) +{ + TQRegExp rx("Invitation-Command: ([A-Z]*)"); + rx.search(msg); + TQString command=rx.cap(1); + if( msg.contains("Invitation-Command: INVITE") ) + { + rx=TQRegExp("Application-File: ([^\\r\\n]*)"); + rx.search(msg); + TQString filename = rx.cap(1); + rx=TQRegExp("Application-FileSize: ([0-9]*)"); + rx.search(msg); + unsigned long int filesize= rx.cap(1).toUInt(); + + MSNInvitation::parseInvitation(msg); //for the cookie + + Kopete::TransferManager::transferManager()->askIncomingTransfer( m_contact , filename, filesize, TQString(), TQString::number( cookie() ) ); + + TQObject::connect( Kopete::TransferManager::transferManager(), TQT_SIGNAL( accepted( Kopete::Transfer *, const TQString& ) ),this, TQT_SLOT( slotFileTransferAccepted( Kopete::Transfer *, const TQString& ) ) ); + TQObject::connect( Kopete::TransferManager::transferManager(), TQT_SIGNAL( refused( const Kopete::FileTransferInfo & ) ), this, TQT_SLOT( slotFileTransferRefused( const Kopete::FileTransferInfo & ) ) ); + + } + else if( msg.contains("Invitation-Command: ACCEPT") ) + { + if(incoming()) + { + rx=TQRegExp("IP-Address: ([0-9\\.]*)"); + rx.search(msg); + TQString ip_address = rx.cap(1); + rx=TQRegExp("AuthCookie: ([0-9]*)"); + rx.search(msg); + TQString authcook = rx.cap(1); + rx=TQRegExp("Port: ([0-9]*)"); + rx.search(msg); + TQString port = rx.cap(1); + + setAuthCookie(authcook); + connect(ip_address, port.toUInt()); + } + else + { + unsigned long int auth = (rand()%(999999))+1; + setAuthCookie(TQString::number(auth)); + + setKopeteTransfer(Kopete::TransferManager::transferManager()->addTransfer(m_contact, fileName(), size(), m_contact->metaContact() ? m_contact->metaContact()->displayName() : m_contact->contactId() , Kopete::FileTransferInfo::Outgoing)); + + MSNChatSession* manager=dynamic_cast<MSNChatSession*>(m_contact->manager()); + if(manager && manager->service()) + { + MSNNotifySocket *notify=static_cast<MSNAccount*>(manager->account())->notifySocket(); + if(notify){ + + TQCString message=TQString( + "MIME-Version: 1.0\r\n" + "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n" + "\r\n" + "Invitation-Command: ACCEPT\r\n" + "Invitation-Cookie: " + TQString::number(cookie()) + "\r\n" + "IP-Address: " + notify->localIP() + "\r\n" + "Port: 6891\r\n" + "AuthCookie: "+TQString::number(auth)+"\r\n" + "Launch-Application: FALSE\r\n" + "Request-Data: IP-Address:\r\n\r\n").utf8(); + + manager->service()->sendCommand( "MSG" , "N", true, message ); + } + } + + listen(6891); + } + } + else //CANCEL + { + MSNInvitation::parseInvitation(msg); + if( m_kopeteTransfer) + m_kopeteTransfer->slotError( TDEIO::ERR_ABORTED , i18n( "The remote user aborted" ) ); + emit done(this); + + } +} + +void MSNFileTransferSocket::slotFileTransferAccepted(Kopete::Transfer *trans, const TQString& fileName) +{ + if(trans->info().internalId().toULong() != cookie()) + return; + + if(!trans->info().contact()) + return; + + setKopeteTransfer(trans); + + MSNChatSession* manager=dynamic_cast<MSNChatSession*>(m_contact->manager()); + + if(manager && manager->service()) + { + setFile(fileName); + + TQCString message=TQString( + "MIME-Version: 1.0\r\n" + "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n" + "\r\n" + "Invitation-Command: ACCEPT\r\n" + "Invitation-Cookie: " + TQString::number(cookie()) + "\r\n" + "Launch-Application: FALSE\r\n" + "Request-Data: IP-Address:\r\n" ).utf8(); + manager->service()->sendCommand( "MSG" , "N", true, message ); + + TQTimer::singleShot( 3 * 60000, this, TQT_SLOT(slotTimer()) ); //if after 3 minutes the transfer has not begin, delete this + } + else + { + if( m_kopeteTransfer) + m_kopeteTransfer->slotError( TDEIO::ERR_UNKNOWN , i18n( "An unknown error occurred" ) ); + emit done(this); + + } +} + +void MSNFileTransferSocket::slotFileTransferRefused(const Kopete::FileTransferInfo &info) +{ + if(info.internalId().toULong() != cookie()) + return; + + if(!info.contact()) + return; + + MSNChatSession* manager=dynamic_cast<MSNChatSession*>(m_contact->manager()); + + if(manager && manager->service()) + { + manager->service()->sendCommand( "MSG" , "N", true, rejectMessage() ); + } + + emit done(this); +} + +void MSNFileTransferSocket::slotKopeteTransferDestroyed() +{ + m_kopeteTransfer=0L; +} + + +#include "msnfiletransfersocket.moc" + diff --git a/kopete/protocols/msn/msnfiletransfersocket.h b/kopete/protocols/msn/msnfiletransfersocket.h new file mode 100644 index 00000000..5803cd01 --- /dev/null +++ b/kopete/protocols/msn/msnfiletransfersocket.h @@ -0,0 +1,120 @@ +/*************************************************************************** + msnfiletransfersocket.h - description + ------------------- + begin : mer jui 31 2002 + copyright : (C) 2002 by Olivier Goffart + email : ogoffart @ kde.org + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef MSNFILETRANSFERSOCKET_H +#define MSNFILETRANSFERSOCKET_H + +#include <tqwidget.h> + +#include "msnsocket.h" +#include "msninvitation.h" + +class TQFile; + +namespace KNetwork { + class TDEServerSocket; +} + +namespace Kopete { class Transfer; } +namespace Kopete { class FileTransferInfo; } +namespace Kopete { class Protocol; } +namespace Kopete { class Contact; } + +/** + * @author Olivier Goffart + */ +class MSNFileTransferSocket : public MSNSocket , public MSNInvitation +{ + Q_OBJECT + + +public: + MSNFileTransferSocket(const TQString &myID,Kopete::Contact* c, bool incoming, TQObject* parent = 0L ); + ~MSNFileTransferSocket(); + + static TQString applicationID() { return "5D3E02AB-6190-11d3-BBBB-00C04F795683"; } + TQString invitationHead(); + + + void setKopeteTransfer( Kopete::Transfer *kt ); + Kopete::Transfer* kopeteTransfer() { return m_kopeteTransfer; } + void setFile( const TQString &fn, long unsigned int fileSize = 0L ); + void setAuthCookie( const TQString &c ) { m_authcook = c; } + TQString fileName() { return m_fileName;} + long unsigned int size() { return m_size;} + void listen( int port ); + + virtual void parseInvitation(const TQString& invitation); + + virtual TQObject* object() { return this; } + +public slots: + void abort(); + +signals: + void done( MSNInvitation * ); + +protected: + /** + * This reimplementation sets up the negotiating with the server and + * suppresses the change of the status to online until the handshake + * is complete. + */ + virtual void doneConnect(); + + /** + * Handle an MSN command response line. + */ + virtual void parseCommand(const TQString & cmd, uint id, const TQString & data); + virtual void bytesReceived(const TQByteArray & data); + +protected slots: + virtual void slotReadyWrite(); + +private slots: + void slotSocketClosed(); + void slotReadBlock(const TQByteArray &); + void slotAcceptConnection(); + void slotTimer(); + void slotSendFile(); + + void slotFileTransferRefused( const Kopete::FileTransferInfo &info ); + void slotFileTransferAccepted( Kopete::Transfer *trans, const TQString& fileName ); + /* the Kopete::Transfer has been deleted */ + void slotKopeteTransferDestroyed(); + + +private: + TQString m_handle; + Kopete::Contact *m_contact; + + long unsigned int m_size; + long unsigned int m_downsize; + TQString m_authcook; + TQString m_fileName; + Kopete::Transfer* m_kopeteTransfer; + TQFile *m_file ; + KNetwork::TDEServerSocket *m_server; + + bool ready; + +}; + +#endif + +// vim: set noet ts=4 sts=4 tw=4: + diff --git a/kopete/protocols/msn/msninvitation.cpp b/kopete/protocols/msn/msninvitation.cpp new file mode 100644 index 00000000..8a64aa93 --- /dev/null +++ b/kopete/protocols/msn/msninvitation.cpp @@ -0,0 +1,100 @@ +/* + msninvitation.cpp + + Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org> + + Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ +#include "msninvitation.h" +#include <stdlib.h> +#include <tqregexp.h> + +MSNInvitation::MSNInvitation(bool incoming, const TQString &applicationID , const TQString &applicationName) +{ + m_incoming=incoming; + m_applicationId=applicationID; + m_applicationName=applicationName; + m_cookie= (rand()%(999999))+1; + m_state = Nothing; +} + + +MSNInvitation::~MSNInvitation() +{ +} + +TQCString MSNInvitation::unimplemented(long unsigned int cookie) +{ + return TQString( "MIME-Version: 1.0\r\n" + "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n" + "\r\n" + "Invitation-Command: CANCEL\r\n" + "Cancel-Code: REJECT_NOT_INSTALLED\r\n" + "Invitation-Cookie: " + TQString::number(cookie) + "\r\n" + "Session-ID: {120019D9-C3F5-4F94-978D-CB33534C3309}\r\n\r\n").utf8(); + //FIXME: i don't know at all what Seession-ID is +} + +TQString MSNInvitation::invitationHead() +{ + setState(Invited); + return TQString( "MIME-Version: 1.0\r\n" + "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n" + "\r\n" + "Application-Name: " + m_applicationName + "\r\n" + "Application-GUID: {" + m_applicationId + "}\r\n" + "Invitation-Command: INVITE\r\n" + "Invitation-Cookie: " +TQString::number(m_cookie) +"\r\n"); +} + +TQCString MSNInvitation::rejectMessage(const TQString & rejectcode) +{ + return TQString( "MIME-Version: 1.0\r\n" + "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n" + "\r\n" + "Invitation-Command: CANCEL\r\n" + "Invitation-Cookie: " + TQString::number(cookie()) + "\r\n" + "Cancel-Code: "+ rejectcode +"\r\n").utf8(); +} + +void MSNInvitation::parseInvitation(const TQString& msg) +{ + TQRegExp rx("Invitation-Command: ([A-Z]*)"); + rx.search(msg); + TQString command=rx.cap(1); + + if(command=="INVITE") + { + rx=TQRegExp("Invitation-Cookie: ([0-9]*)"); + rx.search(msg); + m_cookie=rx.cap(1).toUInt(); + } + else if(command=="CANCEL") + { + /*rx=TQRegExp("Cancel-Code: ([0-9]*)"); + rx.search(msg); + TQString code=rx.cap(1).toUInt(); + //TODO: parse the code*/ + } +// else if(command=="ACCEPT") +} + +MSNInvitation::State MSNInvitation::state() +{ + return m_state; +} + +void MSNInvitation::setState(MSNInvitation::State s) +{ + m_state=s; +} + diff --git a/kopete/protocols/msn/msninvitation.h b/kopete/protocols/msn/msninvitation.h new file mode 100644 index 00000000..88d617b4 --- /dev/null +++ b/kopete/protocols/msn/msninvitation.h @@ -0,0 +1,126 @@ +/* + msninvitation.cpp + + Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org> + + Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ +#ifndef MSNINVITATION_H +#define MSNINVITATION_H + +#include <tqstring.h> + +#include "kopete_export.h" + +class TQObject; + +/** + * @author Olivier Goffart + * + * The invitation is the base class which handle an MSN invitation. + * The implemented class must to herits from TQObject too. + * You can accept the invitation by catching @ref MSNProtocol::invitation() signals + * or create one and insert it to a kmm with @ref MSNChatSession::initInvitation() + * you can add action with @ref Kopete::Plugin::customChatActions() + */ +class KOPETE_EXPORT MSNInvitation +{ +public: + /** + * Constructor + * @param incoming say if it is an incoming invitation + * @param applicationID is the exadecimal id of the invitation + * @param applicationName is a i18n'ed string of the name of the application + */ + MSNInvitation(bool incoming,const TQString &applicationID , const TQString &applicationName); + virtual ~MSNInvitation(); + + /** + * @internal + * it is a reject invitation because the invitation is not implemented + */ + static TQCString unimplemented(long unsigned int cookie); + + /** + * you can set manualy the cookie. note that a cookie is automatically generated when a new + * invitation is created, or in @ref parseInvitation + */ + void setCookie( long unsigned int c ) { m_cookie = c; } + /** + * @return the cookie + */ + long unsigned int cookie() { return m_cookie; } + + /** + * @return true if it is an incommijng invitation + */ + bool incoming() { return m_incoming; } + + + /** + * reimplement this. this is the invitation string used in @ref MSNChatSession::initInvitation() + * the default implementation return the common begin. + * You can also set the state to Invited (the default implementation do that) + */ + virtual TQString invitationHead(); + + /** + * This is the reject invitation string + * @param rejectcode is the code, it can be "REJECT" or "TIMEOUT" + */ + TQCString rejectMessage(const TQString & rejectcode = "REJECT"); + + /** + * reimplement this method. it is called when an invitation message with the invitation's cookie is received + * the default implementation parse the cookie, or the reject message + */ + virtual void parseInvitation(const TQString& invitation); + + /** + * return the qobject (this) + */ + virtual TQObject* object()=0; +//signals: + /** + * reimplement this as a signal, and emit it when the invitation has to be destroyed. + * don't delete the invitation yourself + */ + virtual void done(MSNInvitation*)=0; + + /** + * This indiquate the state. it is going to be completed later + * - Nothing means than nothing has been done in the invitaiton (nothing has been sent/received) + * - Invited means than the invitaiton has been sent + */ + enum State { Nothing=0 , Invited=1 }; + /** + * retrun the current state + */ + State state(); + /** + * set the current State + */ + void setState(State); + + + +protected: + bool m_incoming; + long unsigned int m_cookie; + TQString m_applicationId; + TQString m_applicationName; + State m_state; + + +}; + +#endif diff --git a/kopete/protocols/msn/msnnotifysocket.cpp b/kopete/protocols/msn/msnnotifysocket.cpp new file mode 100644 index 00000000..ec9b27c8 --- /dev/null +++ b/kopete/protocols/msn/msnnotifysocket.cpp @@ -0,0 +1,1309 @@ +/* + msnnotifysocket.cpp - Notify Socket for the MSN Protocol + + Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org> + Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org> + Copyright (c) 2002-2005 by Olivier Goffart <ogoffart at kde.org> + Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net> + Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + Portions taken from + KMerlin (c) 2001 by Olaf Lueg <olueg@olsd.de> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include "msnnotifysocket.h" +#include "msncontact.h" +#include "msnaccount.h" +#include "msnsecureloginhandler.h" +#include "msnchallengehandler.h" + +#include <tqdatetime.h> +#include <tqregexp.h> +#include <tqdom.h> + +#include <kdebug.h> +#include <tdeversion.h> +#include <tdelocale.h> +#include <kmdcodec.h> +#include <tdemessagebox.h> +#include <kstandarddirs.h> +#include <tdetempfile.h> +#include <krun.h> +#include <tdeio/job.h> +#include <tqfile.h> +#include <tdeconfig.h> +#include <knotification.h> + +#include "kopeteuiglobal.h" +#include "kopeteglobal.h" + +#include <ctime> + + +MSNNotifySocket::MSNNotifySocket( MSNAccount *account, const TQString& /*msnId*/, const TQString &password ) +: MSNSocket( account ) +{ + m_newstatus = MSNProtocol::protocol()->NLN; + m_secureLoginHandler=0L; + m_challengeHandler = 0L; + + m_isHotmailAccount=false; + m_ping=false; + m_disconnectReason=Kopete::Account::Unknown; + + m_account = account; + m_password=password; + TQObject::connect( this, TQT_SIGNAL( blockRead( const TQByteArray & ) ), + this, TQT_SLOT( slotReadMessage( const TQByteArray & ) ) ); + m_keepaliveTimer = 0L; + m_isLogged = false; +} + +MSNNotifySocket::~MSNNotifySocket() +{ + delete m_secureLoginHandler; + delete m_challengeHandler; + + kdDebug(14140) << k_funcinfo << endl; +} + +void MSNNotifySocket::doneConnect() +{ +// kdDebug( 14140 ) << k_funcinfo << "Negotiating server protocol version" << endl; + sendCommand( "VER", "MSNP11 MSNP10 CVR0" ); +} + + +void MSNNotifySocket::disconnect() +{ + m_isLogged = false; + if( m_disconnectReason==Kopete::Account::Unknown ) + m_disconnectReason=Kopete::Account::Manual; + if( onlineStatus() == Connected ) + sendCommand( "OUT", TQString(), false ); + + if( m_keepaliveTimer ) + m_keepaliveTimer->stop(); + + // the socket is not connected yet, so I should force the signals + if ( onlineStatus() == Disconnected || onlineStatus() == Connecting ) + emit socketClosed(); + else + MSNSocket::disconnect(); +} + +void MSNNotifySocket::handleError( uint code, uint id ) +{ + kdDebug(14140) << k_funcinfo << endl; + + TQString handle; + if(m_tmpHandles.contains(id)) + handle=m_tmpHandles[id]; + + TQString msg; + MSNSocket::ErrorType type; + // See http://www.hypothetic.org/docs/msn/basics.php for a + // description of all possible error codes. + // TODO: Add support for all of these! + switch( code ) + { + case 201: + case 205: + case 208: + { + msg = i18n( "<qt>The MSN user '%1' does not exist.<br>Please check the MSN ID.</qt>" ).arg( handle ); + type = MSNSocket::ErrorServerError; + break; + } + case 207: + case 218: + case 540: + { + msg = i18n( "<qt>An internal error occurred in the MSN plugin.<br>" + "MSN Error: %1<br>" + "please send us a detailed bug report " + "at kopete-devel@kde.org containing the raw debug output on the " + "console (in gzipped format, as it is probably a lot of output.)" ).arg(code); + type = MSNSocket::ErrorServerError; + break; + } + case 209: + { + if(handle==m_account->accountId()) + { + msg = i18n( "Unable to change your display name.\n" + "Please ensure your display is not too long and does not contains censored words." ); + type = MSNSocket::ErrorServerError; + } + /*else + { + TQString msg = i18n( "You are trying to change the display name of a user who has not " + "confirmed his or her email address;\n" + "the contact was not renamed on the server." ); + KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Error, msg, i18n( "MSN Plugin" ) ); + }*/ + break; + } + case 210: + { + msg = i18n("Your contact list is full; you cannot add any new contacts."); + type = MSNSocket::ErrorServerError; + break; + } + case 215: + { + msg = i18n( "<qt>The user '%1' already exists in this group on the MSN server;<br>" + "if Kopete does not show the user, please send us a detailed bug report " + "at kopete-devel@kde.org containing the raw debug output on the " + "console (in gzipped format, as it is probably a lot of output.)</qt>" ).arg(handle); + type = MSNSocket::ErrorInformation; + break; + } + case 216: + { + //This might happen is you rename an user if he is not in the contactlist + //currently, we just iniore; + //TODO: try to don't rename user not in the list + //actualy, the bug is in MSNChatSession::slotUserJoined() + break; + } + case 219: + { + msg = i18n( "The user '%1' seems to already be blocked or allowed on the server." ).arg(handle); + type = MSNSocket::ErrorServerError; + break; + } + case 223: + { + msg = i18n( "You have reached the maximum number of groups:\n" + "MSN does not support more than 30 groups." ); + type = MSNSocket::ErrorServerError; + break; + } + case 224: + case 225: + case 230: + { + msg = i18n("Kopete is trying to perform an operation on a group or a contact that does not exists on the server.\n" + "This might happen if the Kopete contact list and the MSN-server contact list are not correctly synchronized; if this is the case, you probably should send a bug report."); + type = MSNSocket::ErrorServerError; + break; + } + + case 229: + { + msg = i18n("The group name is too long; it has not been changed on the MSN server."); + type = MSNSocket::ErrorServerError; + break; + } + case 710: + { + msg = i18n( "You cannot open a Hotmail inbox because you do not have an MSN account with a valid " + "Hotmail or MSN mailbox." ); + type = MSNSocket::ErrorServerError; + break; + } + case 715: + { + /* + //if(handlev==m_account->accountId()) + TQString msg = i18n( "Your email address has not been verified with the MSN server.\n" + "You should have received a mail with a link to confirm your email address.\n" + "Some functions will be restricted if you do not confirm your email address." ); + KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry, msg, i18n( "MSN Plugin" ) );//TODO don't show again + */ + break; + } + case 800: + { + //This happen when too much commends are sent to the server. + //the command will not be executed, too bad. + // ignore it for now, as we don't really know what command it was. + /* TQString msg = i18#n( "You are trying to change your status, or your display name too rapidly.\n" + "This might happen if you added yourself to your own contact list." ); + KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry, msg, i18n( "MSN Plugin" ) ); + //FIXME: try to fix this problem*/ + break; + } + case 911: + m_disconnectReason=Kopete::Account::BadPassword; + disconnect(); + break; + case 913: + { + msg = i18n( "You can not send messages when you are offline or when you are invisible." ); + type = MSNSocket::ErrorServerError; + break; + } + case 923: + { + msg = i18n( "You are trying to perform an action you are not allowed to perform in 'kid mode'." ); + type = MSNSocket::ErrorServerError; + break; + } + + default: + MSNSocket::handleError( code, id ); + break; + } + + if( !msg.isEmpty() ) + emit errorMessage( type, msg ); +} + +void MSNNotifySocket::parseCommand( const TQString &cmd, uint id, const TQString &data ) +{ + //kdDebug(14140) << "MSNNotifySocket::parseCommand: Command: " << cmd << endl; + + if ( cmd == "VER" ) + { + sendCommand( "CVR", "0x0409 winnt 5.1 i386 MSNMSGR 7.0.0816 MSMSGS " + m_account->accountId() ); +/* + struct utsname utsBuf; + uname ( &utsBuf ); + + sendCommand( "CVR", i18n( "MS Local code, see http://www.microsoft.com/globaldev/reference/oslocversion.mspx", "0x0409" ) + + " " + escape( utsBuf.sysname ) + " " + escape( utsBuf.release ) + " " + escape( utsBuf.machine ) + " Kopete " + + escape( kapp->aboutData()->version() ) + " Kopete " + m_msnId ); +*/ + } + else if ( cmd == "CVR" ) //else if ( cmd == "INF" ) + { + sendCommand( "USR", "TWN I " + m_account->accountId() ); + } + else if( cmd == "USR" ) //// here follow the auth processus + { + if( data.section( ' ', 1, 1 ) == "S" ) + { + m_secureLoginHandler = new MSNSecureLoginHandler(m_account->accountId(), m_password, data.section( ' ' , 2 , 2 )); + + TQObject::connect(m_secureLoginHandler, TQT_SIGNAL(loginFailed()), this, TQT_SLOT(sslLoginFailed())); + TQObject::connect(m_secureLoginHandler, TQT_SIGNAL(loginBadPassword()), this, TQT_SLOT(sslLoginIncorrect())); + TQObject::connect(m_secureLoginHandler, TQT_SIGNAL(loginSuccesful(TQString )), this, TQT_SLOT(sslLoginSucceeded(TQString ))); + + m_secureLoginHandler->login(); + } + else + { + // Successful authentication. + m_disconnectReason=Kopete::Account::Unknown; + + // Synchronize with the server. + TQString lastSyncTime, lastChange; + + if(m_account->contacts().count() > 1) + { + // Retrieve the last synchronization timestamp, and last change timestamp. + lastSyncTime = m_account->configGroup()->readEntry("lastsynctime", "0"); + lastChange = m_account->configGroup()->readEntry("lastchange", "0"); + } + else + { + //the contactliust has maybe being removed, force to sync + //(the only contact is myself) + lastSyncTime="0"; + lastChange="0"; + } + + sendCommand( "SYN", lastChange + " " + lastSyncTime); + // Get client features. + if(!useHttpMethod()) { + sendCommand( "GCF", "Shields.xml"); + // We are connected start to ping + slotSendKeepAlive(); + } + } + } + else if( cmd == "LST" ) + { + // MSNP11 changed command. Now it's: + // LST N=passport@hotmail.com F=Display%20Name C=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 13 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + // But can be + // LST N=passport@hotmail.com 10 + TQString publicName, contactGuid, groups; + uint lists; + + TQRegExp regex("N=([^ ]+)(?: F=([^ ]+))?(?: C=([0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12}))? (\\d+)\\s?((?:[0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12},?)*)$"); + regex.search(data); + + // Capture passport email. + m_tmpLastHandle = regex.cap(1); + // Capture public name. + publicName = unescape( regex.cap(2) ); + // Capture contact guid. + contactGuid = regex.cap(3); + // Capture list enum type. + lists = regex.cap(4).toUInt(); + // Capture contact group(s) guid(s) + groups = regex.cap(5); + +// kdDebug(14140) << k_funcinfo << " msnId: " << m_tmpLastHandle << " publicName: " << publicName << " contactGuid: " << contactGuid << " list: " << lists << " groupGuid: " << groups << endl; + + // handle, publicName, Contact GUID, lists, Group GUID + emit contactList( m_tmpLastHandle , publicName, contactGuid, lists, groups ); + } + else if( cmd == "GCF" ) + { + m_configFile = data.section(' ', 0, 0); + readBlock( data.section( ' ', 1, 1 ).toUInt() ); + } + else if( cmd == "MSG" ) + { + readBlock( data.section( ' ', 2, 2 ).toUInt() ); + } + else if( cmd == "ILN" || cmd == "NLN" ) + { + // status handle publicName strangeNumber MSNOBJECT + MSNContact *c = static_cast<MSNContact*>( m_account->contacts()[ data.section( ' ', 1, 1 ) ] ); + if( c && c->contactId() != m_account->accountId() ) + { + TQString publicName=unescape( data.section( ' ', 2, 2 ) ); + if ( (publicName!=c->contactId() || c->hasProperty(Kopete::Global::Properties::self()->nickName().key()) ) && + publicName!=c->property( Kopete::Global::Properties::self()->nickName()).value().toString() ) + + changePublicName(publicName,c->contactId()); + TQString obj=unescape(data.section( ' ', 4, 4 )); + c->setObject( obj ); + c->setOnlineStatus( convertOnlineStatus( data.section( ' ', 0, 0 ) ) ); + c->setClientFlags(data.section( ' ', 3, 3 ).toUInt()); + } + } + else if( cmd == "UBX" ) + { + m_tmpLastHandle = data.section(' ', 0, 0); + uint length = data.section( ' ', 1, 1 ).toUInt(); + if(length > 0) { + readBlock( length ); + } + } + else if( cmd == "UUX" ) + { + // UUX is sended to acknowledge that the server has received and processed the personal Message. + // if the result is 0, set the myself() contact personalMessage. + if( data.section(' ', 0, 0) == TQString::fromUtf8("0") ) + m_account->myself()->setProperty(MSNProtocol::protocol()->propPersonalMessage, m_propertyPersonalMessage); + } + else if( cmd == "FLN" ) + { + MSNContact *c = static_cast<MSNContact*>( m_account->contacts()[ data.section( ' ', 0, 0 ) ] ); + if( c && c->contactId() != m_account->accountId() ) + { + c->setOnlineStatus( MSNProtocol::protocol()->FLN ); + c->removeProperty( MSNProtocol::protocol()->propClient ); + } + } + else if( cmd == "XFR" ) + { + TQString stype=data.section( ' ', 0, 0 ); + if( stype=="SB" ) //switchboard connection (chat) + { + // Address, AuthInfo + emit startChat( data.section( ' ', 1, 1 ), data.section( ' ', 3, 3 ) ); + } + else if( stype=="NS" ) //notifysocket ; Got our notification server + { //we are connecting and we receive the initial NS, or the msn server encounter a problem, and we are switching to another switchboard + TQString host = data.section( ' ', 1, 1 ); + TQString server = host.section( ':', 0, 0 ); + uint port = host.section( ':', 1, 1 ).toUInt(); + setOnlineStatus( Connected ); + emit receivedNotificationServer( server, port ); + disconnect(); + } + + } + else if( cmd == "RNG" ) + { + // SessionID, Address, AuthInfo, handle, publicName + emit invitedToChat( TQString::number( id ), data.section( ' ', 0, 0 ), data.section( ' ', 2, 2 ), + data.section( ' ', 3, 3 ), unescape( data.section( ' ', 4, 4 ) ) ); + } + else if( cmd == "ADC" ) + { + TQString msnId, list, publicName, contactGuid, groupGuid; + + // Retrieve the list parameter (FL/AL/BL/RL) + list = data.section( ' ', 0, 0 ); + + // Examples of received data + // ADC TrID xL N=example@passport.com + // ADC TrID FL C=contactGuid groupdGuid + // ADC TrID RL N=example@passport.com F=friednly%20name + // ADC TrID FL N=ex@pas.com F=My%20Name C=contactGuid + // Thanks Gregg for that complex RegExp. + TQRegExp regex("(?:N=([^ ]+))?(?: F=([^ ]+))?(?: C=([0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12}))?\\s?((?:[0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12},?)*)$"); + regex.search( data.section( ' ', 1 ) ); + + // Capture passport email. + msnId = regex.cap(1); + // Capture public name. + publicName = unescape( regex.cap(2) ); + // Capture contact guid. + contactGuid = regex.cap(3); + // Capture contact group(s) guid(s) + groupGuid = regex.cap(4); + +// kdDebug(14140) << k_funcinfo << list << " msnId: " << msnId << " publicName: " << publicName << " contactGuid: " << contactGuid << " groupGuid: " << groupGuid << endl; + + // handle, list, publicName, contactGuid, groupGuid + emit contactAdded( msnId, list, publicName, contactGuid, groupGuid ); + } + else if( cmd == "REM" ) // someone is removed from a list + { + TQString handle, list, contactGuid, groupGuid; + list = data.section( ' ', 0, 0 ); + if( list == "FL" ) + { + // Removing a contact + if( data.contains( ' ' ) < 2 ) + { + contactGuid = data.section( ' ', 1, 1 ); + } + // Removing a contact from a group + else if( data.contains( ' ' ) < 3 ) + { + contactGuid = data.section( ' ', 1, 1 ); + groupGuid = data.section( ' ', 2, 2 ); + } + } + else + { + handle = data.section( ' ', 1, 1); + } + + // handle, list, contactGuid, groupGuid + emit contactRemoved( handle, list, contactGuid, groupGuid ); + + } + else if( cmd == "OUT" ) + { + if( data.section( ' ', 0, 0 ) == "OTH" ) + { + m_disconnectReason=Kopete::Account::OtherClient; + } + disconnect(); + } + else if( cmd == "CHG" ) + { + TQString status = data.section( ' ', 0, 0 ); + setOnlineStatus( Connected ); + emit statusChanged( convertOnlineStatus( status ) ); + } + else if( cmd == "SBP" ) + { + TQString contactGuid, type, publicName; + contactGuid = data.section( ' ', 0, 0 ); + type = data.section( ' ', 1, 1 ); + if(type == "MFN" ) + { + publicName = unescape( data.section( ' ', 2, 2 ) ); + MSNContact *c = m_account->findContactByGuid( contactGuid ); + if(c != 0L) + { + c->setProperty( Kopete::Global::Properties::self()->nickName(), publicName ); + } + } + } + else if( cmd == "LSG" ) + { + // New Format: LSG Friends xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + // groupDisplayName, groupGuid + emit groupListed( unescape( data.section( ' ', 0, 0 ) ), data.section( ' ', 1, 1) ); + } + else if( cmd == "ADG" ) + { + // groupName, groupGuid + emit groupAdded( unescape( data.section( ' ', 0, 0 ) ), + data.section( ' ', 1, 1 ) ); + } + else if( cmd == "REG" ) + { + // groupGuid, groupName + emit groupRenamed( data.section( ' ', 0, 0 ), unescape( data.section( ' ', 1, 1 ) ) ); + } + else if( cmd == "RMG" ) + { + // groupGuid + emit groupRemoved( data.section( ' ', 1, 1 ) ); + } + else if( cmd == "CHL" ) + { + m_challengeHandler = new MSNChallengeHandler("CFHUR$52U_{VIX5T", "PROD0101{0RM?UBW"); + // Compute the challenge response hash, and send the response. + TQString chlResponse = m_challengeHandler->computeHash(data.section(' ', 0, 0)); + sendCommand("QRY", m_challengeHandler->productId(), true, chlResponse.utf8()); + // Dispose of the challenge handler. + m_challengeHandler->deleteLater(); + m_challengeHandler = 0L; + } + else if( cmd == "SYN" ) + { + // Retrieve the last synchronization timestamp known to the server. + TQString lastSyncTime = data.section( ' ', 1, 1 ); + TQString lastChange = data.section( ' ', 0, 0 ); + if( lastSyncTime != m_account->configGroup()->readEntry("lastsynctime") || + lastChange != m_account->configGroup()->readEntry("lastchange") ) + { + // If the server timestamp and the local timestamp are different, + // prepare to receive the contact list. + emit newContactList(); // remove all contacts datas, msn sends a new contact list + m_account->configGroup()->writeEntry( "lastsynctime" , lastSyncTime); + m_account->configGroup()->writeEntry( "lastchange", lastChange); + }else + kdDebug(14140) << k_funcinfo << "Contact list up-to-date." << endl; + + // set the status + setStatus( m_newstatus ); + } + else if( cmd == "BPR" ) + { + MSNContact *c = static_cast<MSNContact*>( m_account->contacts()[ m_tmpLastHandle ] ); + if( c ) + c->setInfo(data.section( ' ', 0, 0 ),unescape(data.section( ' ', 1, 1 ))); + } + else if( cmd == "PRP" ) + { + MSNContact *c = static_cast<MSNContact*>( m_account->myself() ); + if( c ) + { + TQString type = data.section( ' ', 0, 0 ); + TQString prpData = unescape( data.section( ' ', 1, 1 ) ); //SECURITY???????? + c->setInfo( type, prpData ); + m_account->configGroup()->writeEntry( type, prpData ); + } + } + else if( cmd == "BLP" ) + { + if( id > 0 ) //FROM BLP + { + m_account->configGroup()->writeEntry( "serial" , data.section( ' ', 0, 0 ) ); + m_account->configGroup()->writeEntry( "BLP" , data.section( ' ', 1, 1 ) ); + } + else //FROM SYN + m_account->configGroup()->writeEntry( "BLP" , data.section( ' ', 0, 0 ) ); + } + else if( cmd == "QRY" ) + { + // Do nothing + } + else if( cmd == "QNG" ) + { + //this is a reply from a ping + m_ping=false; + + // id is the timeout in fact, and we remove 5% of it + if( m_keepaliveTimer ) + m_keepaliveTimer->start( id * 950, true ); + kdDebug( 14140 ) << k_funcinfo << "timerTimeout=" << id << "sec"<< endl; + } + else if( cmd == "URL" ) + { + // URL 6 /cgi-bin/HoTMaiL https://loginnet.passport.com/ppsecure/md5auth.srf?lc=1033 2 + //example of reply: URL 10 /cgi-bin/HoTMaiL https://msnialogin.passport.com/ppsecure/md5auth.srf?lc=1036 3 + TQString from_action_url = data.section( ' ', 1, 1 ); + TQString rru = data.section( ' ', 0, 0 ); + TQString id = data.section( ' ', 2, 2 ); + + //write the tmp file + TQString UserID=m_account->accountId(); + + time_t actualTime; + time(&actualTime); + TQString sl = TQString::number( ( unsigned long ) actualTime - m_loginTime.toULong() ); + + TQString md5this( m_MSPAuth + sl + m_password ); + KMD5 md5( md5this.utf8() ); + + TQString hotmailRequest = "<html>\n" + "<head>\n" + "<noscript>\n" + "<meta http-equiv=Refresh content=\"0; url=http://www.hotmail.com\">\n" + "</noscript>\n" + "</head>\n" + "<body onload=\"document.pform.submit(); \">\n" + "<form name=\"pform\" action=\"" + from_action_url + "\" method=\"POST\">\n" + "<input type=\"hidden\" name=\"mode\" value=\"ttl\">\n" + "<input type=\"hidden\" name=\"login\" value=\"" + UserID.left( UserID.find('@') ) + "\">\n" + "<input type=\"hidden\" name=\"username\" value=\"" + UserID + "\">\n" + "<input type=\"hidden\" name=\"sid\" value=\"" + m_sid + "\">\n" + "<input type=\"hidden\" name=\"kv\" value=\"" + m_kv + "\">\n" + "<input type=\"hidden\" name=\"id\" value=\""+ id +"\">\n" + "<input type=\"hidden\" name=\"sl\" value=\"" + sl +"\">\n" + "<input type=\"hidden\" name=\"rru\" value=\"" + rru + "\">\n" + "<input type=\"hidden\" name=\"auth\" value=\"" + m_MSPAuth + "\">\n" + "<input type=\"hidden\" name=\"creds\" value=\"" + TQString::fromLatin1( md5.hexDigest() ) + "\">\n" + "<input type=\"hidden\" name=\"svc\" value=\"mail\">\n" + "<input type=\"hidden\" name=\"js\" value=\"yes\">\n" + "</form></body>\n</html>\n"; + + KTempFile tmpMailFile( locateLocal( "tmp", "kopetehotmail-" ), ".html" ); + *tmpMailFile.textStream() << hotmailRequest; + tmpMailFile.file()->flush(); + + KRun::runURL( KURL::fromPathOrURL( tmpMailFile.name() ), "text/html" , true ); + + } + else if ( cmd == "NOT" ) + { + kdDebug( 14140 ) << k_funcinfo << "Received NOT command, issueing read block for '" << id << " more bytes" << endl; + readBlock( id ); + } + else + { + // Let the base class handle the rest + //MSNSocket::parseCommand( cmd, id, data ); + kdDebug( 14140 ) << k_funcinfo << "Unimplemented command '" << cmd << " " << id << " " << data << "' from server!" << endl; + } +} + + +void MSNNotifySocket::sslLoginFailed() +{ + m_disconnectReason=Kopete::Account::InvalidHost; + disconnect(); +} + +void MSNNotifySocket::sslLoginIncorrect() +{ + m_disconnectReason=Kopete::Account::BadPassword; + disconnect(); +} + +void MSNNotifySocket::sslLoginSucceeded(TQString ticket) +{ + sendCommand("USR" , "TWN S " + ticket); + + m_secureLoginHandler->deleteLater(); + m_secureLoginHandler = 0L; +} + +void MSNNotifySocket::slotMSNAlertUnwanted() +{ + // user not interested .. clean up the list of actions + m_msnAlertURLs.clear(); +} + +void MSNNotifySocket::slotMSNAlertLink(unsigned int action) +{ + // index into our action list and pull out the URL that was clicked .. + KURL tempURLForLaunch(m_msnAlertURLs[action-1]); + + KRun* urlToRun = new KRun(tempURLForLaunch); +} + +void MSNNotifySocket::slotOpenInbox() +{ + sendCommand("URL", "INBOX" ); +} + +void MSNNotifySocket::sendMail(const TQString &email) +{ + sendCommand("URL", TQString("COMPOSE " + email).utf8() ); +} + +bool MSNNotifySocket::setUseHttpMethod(bool useHttp) +{ + bool ret = MSNSocket::setUseHttpMethod( useHttp ); + + if( useHttpMethod() ) { + if( m_keepaliveTimer ) { + delete m_keepaliveTimer; + m_keepaliveTimer = 0L; + } + } + else { + if( !m_keepaliveTimer ) { + m_keepaliveTimer = new TQTimer( this, "m_keepaliveTimer" ); + TQObject::connect( m_keepaliveTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( slotSendKeepAlive() ) ); + } + } + + return ret; +} + +void MSNNotifySocket::slotReadMessage( const TQByteArray &bytes ) +{ + TQString msg = TQString::fromUtf8(bytes, bytes.size()); + + if(msg.contains("text/x-msmsgsinitialmdatanotification")) + { + //Mail-Data: <MD><E><I>301</I><IU>1</IU><O>4</O><OU>2</OU></E><Q><TQTM>409600</TQTM><TQNM>204800</TQNM></Q></MD> + // MD - Mail Data + // E - email + // I - initial mail + // IU - initial unread + // O - other mail + // OU - other unread. + TQRegExp regex("<MD><E><I>(\\d+)?</I>(?:<IU>(\\d+)?</IU>)<O>(\\d+)?</O><OU>(\\d+)?</OU></E><Q>.*</Q></MD>"); + regex.search(msg); + + bool unread; + // Retrieve the number of unread email messages. + mailCount = regex.cap(2).toUInt(&unread); + if(unread && mailCount > 0) + { + // If there are new email message available, raise the unread email event. + TQObject::connect(KNotification::event( "msn_mail", i18n( "You have one unread message in your MSN inbox.", + "You have %n unread messages in your MSN inbox.", mailCount ), 0 , 0 , i18n( "Open Inbox..." ) ), + TQT_SIGNAL(activated(unsigned int ) ) , this, TQT_SLOT( slotOpenInbox() ) ); + } + } + else if(msg.contains("text/x-msmsgsactivemailnotification")) + { + //this sends the server if mails are deleted + TQString m = msg.right(msg.length() - msg.find("Message-Delta:") ); + m = m.left(msg.find("\r\n")); + mailCount = mailCount - m.right(m.length() -m.find(" ")-1).toUInt(); + } + else if(msg.contains("text/x-msmsgsemailnotification")) + { + //this sends the server if a new mail has arrived + TQRegExp rx("From-Addr: ([A-Za-z0-9@._\\-]*)"); + rx.search(msg); + TQString m=rx.cap(1); + + mailCount++; + + //TODO: it is also possible to get the subject (but warning about the encoding) + TQObject::connect(KNotification::event( "msn_mail",i18n( "You have one new email from %1 in your MSN inbox." ).arg(m), + 0 , 0 , i18n( "Open Inbox..." ) ), + TQT_SIGNAL(activated(unsigned int ) ) , this, TQT_SLOT( slotOpenInbox() ) ); + } + else if(msg.contains("text/x-msmsgsprofile")) + { + //Hotmail profile + if(msg.contains("MSPAuth:")) + { + TQRegExp rx("MSPAuth: ([A-Za-z0-9$!*]*)"); + rx.search(msg); + m_MSPAuth=rx.cap(1); + } + if(msg.contains("sid:")) + { + TQRegExp rx("sid: ([0-9]*)"); + rx.search(msg); + m_sid=rx.cap(1); + } + if(msg.contains("kv:")) + { + TQRegExp rx("kv: ([0-9]*)"); + rx.search(msg); + m_kv=rx.cap(1); + } + if(msg.contains("LoginTime:")) + { + TQRegExp rx("LoginTime: ([0-9]*)"); + rx.search(msg); + m_loginTime=rx.cap(1); + } + else //IN MSNP9 there are no logintime it seems, so set it manualy + { + time_t actualTime; + time(&actualTime); + m_loginTime=TQString::number((unsigned long)actualTime); + } + if(msg.contains("EmailEnabled:")) + { + TQRegExp rx("EmailEnabled: ([0-9]*)"); + rx.search(msg); + m_isHotmailAccount = (rx.cap(1).toUInt() == 1); + emit hotmailSeted(m_isHotmailAccount); + } + if(msg.contains("ClientIP:")) + { + TQRegExp rx("ClientIP: ([0-9.]*)"); + rx.search(msg); + m_localIP = rx.cap(1); + } + + // We are logged when we receive the initial profile from Hotmail. + m_isLogged = true; + } + else if (msg.contains("NOTIFICATION")) + { + // MSN alert (i.e. NOTIFICATION) [for docs see http://www.hypothetic.org/docs/msn/client/notification.php] + // format of msg is as follows: + // + // <NOTIFICATION ver="2" id="1342902633" siteid="199999999" siteurl="http://alerts.msn.com"> + // <TO pid="0x0006BFFD:0x8582C0FB" name="example@passport.com"/> + // <MSG pri="1" id="1342902633"> + // <SUBSCR url="http://g.msn.com/3ALMSNTRACKING/199999999ToastChange?http://alerts.msn.com/Alerts/MyAlerts.aspx?strela=1"/> + // <ACTION url="http://g.msn.com/3ALMSNTRACKING/199999999ToastAction?http://alerts.msn.com/Alerts/MyAlerts.aspx?strela=1"/> + // <BODY lang="3076" icon=""> + // <TEXT>utf8-encoded text</TEXT> + // </BODY> + // </MSG> + // </NOTIFICATION> + + // MSN sends out badly formed XML .. fix it for them (thanks MS!) + TQString notificationDOMAsString(msg); + + TQRegExp rx( "&(?!amp;)" ); // match ampersands but not & + notificationDOMAsString.replace(rx, "&"); + TQDomDocument alertDOM; + alertDOM.setContent(notificationDOMAsString); + + TQDomNodeList msgElements = alertDOM.elementsByTagName("MSG"); + for (uint i = 0 ; i < msgElements.count() ; i++) + { + TQString subscString; + TQString actionString; + TQString textString; + + TQDomNode msgDOM = msgElements.item(i); + + TQDomNodeList msgChildren = msgDOM.childNodes(); + for (uint i = 0 ; i < msgChildren.length() ; i++) { + TQDomNode child = msgChildren.item(i); + TQDomElement element = child.toElement(); + if (element.tagName() == "SUBSCR") + { + TQDomAttr subscElementURLAttribute; + if (element.hasAttribute("url")) + { + subscElementURLAttribute = element.attributeNode("url"); + subscString = subscElementURLAttribute.value(); + } + } + else if (element.tagName() == "ACTION") + { + // process ACTION node to pull out URL the alert is tied to + TQDomAttr actionElementURLAttribute; + if (element.hasAttribute("url")) + { + actionElementURLAttribute = element.attributeNode("url"); + actionString = actionElementURLAttribute.value(); + } + } + else if (element.tagName() == "BODY") + { + // process BODY node to get the text of the alert + TQDomNodeList textElements = element.elementsByTagName("TEXT"); + if (textElements.count() >= 1) + { + TQDomElement textElement = textElements.item(0).toElement(); + textString = textElement.text(); + } + } + + + } + +// kdDebug( 14140 ) << "subscString " << subscString << " actionString " << actionString << " textString " << textString << endl; + // build an internal list of actions ... we'll need to index into this list when we receive an event + TQStringList actions; + actions.append(i18n("More Information")); + m_msnAlertURLs.append(actionString); + + actions.append(i18n("Manage Subscription")); + m_msnAlertURLs.append(subscString); + + // Don't do any MSN alerts notification for new blog updates + if( subscString != TQString::fromLatin1("s.htm") && actionString != TQString::fromLatin1("a.htm") ) + { + KNotification* notification = KNotification::event("msn_alert", textString, 0L, 0L, actions); + TQObject::connect(notification, TQT_SIGNAL(activated(unsigned int)), this, TQT_SLOT(slotMSNAlertLink(unsigned int))); + TQObject::connect(notification, TQT_SIGNAL(closed()), this, TQT_SLOT(slotMSNAlertUnwanted())); + } + } // end for each MSG tag + } + + if(!m_configFile.isNull()) + { + // TODO Get client features. + } + + if(!m_tmpLastHandle.isNull()) + { + TQString personalMessage, currentMedia; + TQDomDocument psm; + if( psm.setContent(msg) ) + { + // Get the first child of the xml "document"; + TQDomElement psmElement = psm.documentElement().firstChild().toElement(); + + while( !psmElement.isNull() ) + { + if(psmElement.tagName() == TQString::fromUtf8("PSM")) + { + personalMessage = psmElement.text(); + kdDebug(14140) << k_funcinfo << "Personnal Message received: " << personalMessage << endl; + } + else if(psmElement.tagName() == TQString::fromUtf8("CurrentMedia")) + { + if( !psmElement.text().isEmpty() ) + { + kdDebug(14140) << k_funcinfo << "XML CurrentMedia: " << psmElement.text() << endl; + currentMedia = processCurrentMedia( psmElement.text() ); + } + } + psmElement = psmElement.nextSibling().toElement(); + } + + MSNContact *contact = static_cast<MSNContact*>(m_account->contacts()[ m_tmpLastHandle ]); + if(contact) + { + contact->setProperty(MSNProtocol::protocol()->propPersonalMessage, currentMedia.isEmpty() ? personalMessage : currentMedia); + } + } + m_tmpLastHandle = TQString(); + } +} + +TQString MSNNotifySocket::processCurrentMedia( const TQString &mediaXmlElement ) +{ + /* + The value of the CurrentMedia tag you can think of like an array + seperated by "\0" characters (literal backslash followed by zero, not NULL). + + The elements of this "array" are as follows: + + * Application - This is the app you are using. Usually empty + * Type - This is the type of PSM, either “Music”, “Games” or “Office” + * Enabled - This is a boolean value (0/1) to enable/disable + * Format - A formatter string ala .Net; For example, “{0} - {1}” + * First line - The first line (Matches {0} in the Format) + * Second line - The second line (Matches {1} in the Format) + * Third line - The third line (Matches {2} in the Format) + + There is probably no limit to the number of lines unless you go over the maximum length of the tag. + + Example of currentMedia xml tag: + <CurrentMedia>\0Music\01\0{0} - {1}\0 Song Title\0Song Artist\0Song Album\0\0</CurrentMedia> + <CurrentMedia>\0Games\01\0Playing {0}\0Game Name\0</CurrentMedia> + <CurrentMedia>\0Office\01\0Office Message\0Office App Name\0</CurrentMedia> + + From http://msnpiki.msnfanatic.com/index.php/MSNP11:Changes + */ + TQString application, type, format, currentMedia; + bool enabled=false, test; + // \0 is textual, it's the "array" separator. + TQStringList argumentLists = TQStringList::split(TQString::fromUtf8("\\0"), mediaXmlElement, true); + + // Retrive the "stable" array elements. + application = argumentLists[0]; + type = argumentLists[1]; + enabled = argumentLists[2].toInt(&test); + format = argumentLists[3]; + + // Get the formatter strings + TQStringList formatterStrings; + TQStringList::ConstIterator it; + for( it = argumentLists.at(4); it != argumentLists.end(); ++it ) + { + formatterStrings.append( *it ); + } + + // Replace the formatter in the format string. + currentMedia = format; + for(uint i=0; i<formatterStrings.size(); i++) + { + currentMedia = currentMedia.replace(TQString("{%1}").arg(i), formatterStrings[i]); + } + + if( type == TQString::fromUtf8("Music") ) + { + // the "♫" is encoded in utf8 (and should be in utf8) + currentMedia = i18n("Now Listening: ♫ %1 ♫").arg(currentMedia); + } + + kdDebug(1414) << "Current Media received: " << currentMedia << endl; + + return currentMedia; +} + +void MSNNotifySocket::addGroup(const TQString& groupName) +{ + // escape spaces + sendCommand( "ADG", escape( groupName ) ); +} + +void MSNNotifySocket::renameGroup( const TQString& groupName, const TQString& groupGuid ) +{ + // escape spaces + sendCommand( "REG", groupGuid + " " + escape( groupName ) ); +} + +void MSNNotifySocket::removeGroup( const TQString& groupGuid ) +{ + sendCommand( "RMG", groupGuid ); +} + +void MSNNotifySocket::addContact( const TQString &handle, int list, const TQString& publicName, const TQString& contactGuid, const TQString& groupGuid ) +{ + TQString args; + switch( list ) + { + case MSNProtocol::FL: + { + // Adding the contact to a group + if( !contactGuid.isEmpty() ) + { + args = TQString("FL C=%1 %2").arg( contactGuid ).arg( groupGuid ); + kdDebug(14140) << k_funcinfo << "In adding contact to a group" << endl; + } + // Adding a new contact + else + { + args = TQString("FL N=%1 F=%2").arg( handle ).arg( escape( publicName ) ); + kdDebug(14140) << k_funcinfo << "In adding contact to a new contact" << endl; + } + break; + } + case MSNProtocol::AL: + args = TQString("AL N=%1").arg( handle ); + break; + case MSNProtocol::BL: + args = TQString("BL N=%1").arg( handle ); + break; + case MSNProtocol::RL: + args = TQString("RL N=%1").arg( handle ); + break; + default: + kdDebug(14140) << k_funcinfo <<"WARNING! Unknown list " << list << "!" << endl; + return; + } + unsigned int id=sendCommand( "ADC", args ); + m_tmpHandles[id]=handle; +} + +void MSNNotifySocket::removeContact( const TQString &handle, int list, const TQString& contactGuid, const TQString& groupGuid ) +{ + TQString args; + switch( list ) + { + case MSNProtocol::FL: + args = "FL " + contactGuid; + // Removing a contact from a group + if( !groupGuid.isEmpty() ) + args += " " + groupGuid; + break; + case MSNProtocol::AL: + args = "AL " + handle; + break; + case MSNProtocol::BL: + args = "BL " + handle; + break; + case MSNProtocol::PL: + args = "PL " + handle; + break; + default: + kdDebug(14140) <<k_funcinfo << "WARNING! Unknown list " << list << "!" << endl; + return; + } + unsigned int id=sendCommand( "REM", args ); + m_tmpHandles[id]=handle; +} + +void MSNNotifySocket::setStatus( const Kopete::OnlineStatus &status ) +{ +// kdDebug( 14140 ) << k_funcinfo << statusToString( status ) << endl; + + if( onlineStatus() == Disconnected ) + m_newstatus = status; + else + sendCommand( "CHG", statusToString( status ) + " " + m_account->myselfClientId() + " " + escape(m_account->pictureObject()) ); +} + +void MSNNotifySocket::changePublicName( const TQString &publicName, const TQString &handle ) +{ + TQString tempPublicName = publicName; + + //The maximum length is 387. but with utf8 or encodage, each character may be triple + // 387/3 = 129 so we make sure the length is not longer than 129 char, even if + // it's possible to have longer nicks. + if( escape(publicName).length() > 129 ) + { + tempPublicName = publicName.left(129); + } + + if( handle.isNull() ) + { + unsigned int id = sendCommand( "PRP", "MFN " + escape( tempPublicName ) ); + m_tmpHandles[id] = m_account->accountId(); + } + else + { + MSNContact *currentContact = static_cast<MSNContact *>(m_account->contacts()[handle]); + if(currentContact && !currentContact->guid().isEmpty() ) + { + // FIXME if there is not guid server disconnects. + unsigned int id = sendCommand( "SBP", currentContact->guid() + " MFN " + escape( tempPublicName ) ); + m_tmpHandles[id] = handle; + } + } +} + +void MSNNotifySocket::changePersonalMessage( MSNProtocol::PersonalMessageType type, const TQString &personalMessage ) +{ + TQString tempPersonalMessage; + TQString xmlCurrentMedia; + + // Only espace and cut the personalMessage is the type is normal. + if(type == MSNProtocol::PersonalMessageNormal) + { + tempPersonalMessage = personalMessage; + //Magic number : 129 characters + if( escape(personalMessage).length() > 129 ) + { + // We cut. for now. + tempPersonalMessage = personalMessage.left(129); + } + } + + TQDomDocument xmlMessage; + xmlMessage.appendChild( xmlMessage.createElement( "Data" ) ); + + TQDomElement psm = xmlMessage.createElement("PSM"); + psm.appendChild( xmlMessage.createTextNode( tempPersonalMessage ) ); + xmlMessage.documentElement().appendChild( psm ); + + TQDomElement currentMedia = xmlMessage.createElement("CurrentMedia"); + + /* Example of currentMedia xml tag: + <CurrentMedia>\0Music\01\0{0} - {1}\0 Song Title\0Song Artist\0Song Album\0\0</CurrentMedia> + <CurrentMedia>\0Games\01\0Playing {0}\0Game Name\0</CurrentMedia> + <CurrentMedia>\0Office\01\0Office Message\0Office App Name\0</CurrentMedia> + */ + switch(type) + { + case MSNProtocol::PersonalMessageMusic: + { + xmlCurrentMedia = "\\0Music\\01\\0"; + TQStringList mediaList = TQStringList::split(";", personalMessage); + TQString formatterArguments; + if( !mediaList[0].isEmpty() ) // Current Track + { + xmlCurrentMedia += "{0}"; + formatterArguments += TQString("%1\\0").arg(mediaList[0]); + } + if( !mediaList[1].isEmpty() ) // Current Artist + { + xmlCurrentMedia += " - {1}"; + formatterArguments += TQString("%1\\0").arg(mediaList[1]); + } + if( !mediaList[2].isEmpty() ) // Current Album + { + xmlCurrentMedia += " ({2})"; + formatterArguments += TQString("%1\\0").arg(mediaList[2]); + } + xmlCurrentMedia += "\\0" + formatterArguments + "\\0"; + break; + } + default: + break; + } + + currentMedia.appendChild( xmlMessage.createTextNode( xmlCurrentMedia ) ); + + // Set the status message for myself, check if currentMedia is empty, for either using the normal or Music personal + m_propertyPersonalMessage = xmlCurrentMedia.isEmpty() ? tempPersonalMessage : processCurrentMedia( currentMedia.text() ); + + xmlMessage.documentElement().appendChild( currentMedia ); + + unsigned int id = sendCommand("UUX","",true, xmlMessage.toString().utf8(), false); + m_tmpHandles[id] = m_account->accountId(); + +} + +void MSNNotifySocket::changePhoneNumber( const TQString &key, const TQString &data ) +{ + sendCommand( "PRP", key + " " + escape ( data ) ); +} + + +void MSNNotifySocket::createChatSession() +{ + sendCommand( "XFR", "SB" ); +} + +TQString MSNNotifySocket::statusToString( const Kopete::OnlineStatus &status ) const +{ + if( status == MSNProtocol::protocol()->NLN ) + return "NLN"; + else if( status == MSNProtocol::protocol()->BSY ) + return "BSY"; + else if( status == MSNProtocol::protocol()->BRB ) + return "BRB"; + else if( status == MSNProtocol::protocol()->AWY ) + return "AWY"; + else if( status == MSNProtocol::protocol()->PHN ) + return "PHN"; + else if( status == MSNProtocol::protocol()->LUN ) + return "LUN"; + else if( status == MSNProtocol::protocol()->FLN ) + return "FLN"; + else if( status == MSNProtocol::protocol()->HDN ) + return "HDN"; + else if( status == MSNProtocol::protocol()->IDL ) + return "IDL"; + else + { + kdWarning( 14140 ) << k_funcinfo << "Unknown status " << status.internalStatus() << "!" << endl; + return "UNK"; + } +} + +void MSNNotifySocket::slotSendKeepAlive() +{ + //we did not received the previous TQNG + if(m_ping) + { + m_disconnectReason=Kopete::Account::ConnectionReset; + disconnect(); + /*KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Information, + i18n( "The connection with the MSN network has been lost." ) , i18n ("MSN Plugin") );*/ + return; + } + else + { + // Send a dummy command to fake activity. This makes sure MSN doesn't + // disconnect you when the notify socket is idle. + sendCommand( "PNG" , TQString() , false ); + m_ping=true; + } + + //at least 90 second has been ellapsed since the last messages + // we shouldn't receive error from theses command anymore + m_tmpHandles.clear(); +} + +Kopete::OnlineStatus MSNNotifySocket::convertOnlineStatus( const TQString &status ) +{ + if( status == "NLN" ) + return MSNProtocol::protocol()->NLN; + else if( status == "FLN" ) + return MSNProtocol::protocol()->FLN; + else if( status == "HDN" ) + return MSNProtocol::protocol()->HDN; + else if( status == "PHN" ) + return MSNProtocol::protocol()->PHN; + else if( status == "LUN" ) + return MSNProtocol::protocol()->LUN; + else if( status == "BRB" ) + return MSNProtocol::protocol()->BRB; + else if( status == "AWY" ) + return MSNProtocol::protocol()->AWY; + else if( status == "BSY" ) + return MSNProtocol::protocol()->BSY; + else if( status == "IDL" ) + return MSNProtocol::protocol()->IDL; + else + return MSNProtocol::protocol()->UNK; +} + + +#include "msnnotifysocket.moc" + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/msn/msnnotifysocket.h b/kopete/protocols/msn/msnnotifysocket.h new file mode 100644 index 00000000..838aee94 --- /dev/null +++ b/kopete/protocols/msn/msnnotifysocket.h @@ -0,0 +1,217 @@ +/* + msnnotifysocket.h - Notify Socket for the MSN Protocol + + Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org> + Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org> + Copyright (c) 2002-2005 by Olivier Goffart <ogoffart at kde.org> + Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net> + Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + Portions taken from + KMerlin (c) 2001 by Olaf Lueg <olueg@olsd.de> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef MSNNOTIFYSOCKET_H +#define MSNNOTIFYSOCKET_H + +#include "msnsocket.h" +#include "msnprotocol.h" + + +class MSNDispatchSocket; +class MSNAccount; +class KTempFile; +class MSNSecureLoginHandler; +class MSNChallengeHandler; + +/** + * @author Olaf Lueg + * @author Olivier Goffart + */ +class MSNNotifySocket : public MSNSocket +{ + Q_OBJECT + + +public: + MSNNotifySocket( MSNAccount* account, const TQString &msnId, const TQString &password ); + ~MSNNotifySocket(); + + virtual void disconnect(); + + void setStatus( const Kopete::OnlineStatus &status ); + void addContact( const TQString &handle, int list, const TQString& publicName, const TQString& contactGuid, const TQString& groupGuid ); + void removeContact( const TQString &handle, int list, const TQString &contactGuid, const TQString &groupGuid ); + + void addGroup( const TQString& groupName ); + void removeGroup( const TQString& group ); + void renameGroup( const TQString& groupName, const TQString& groupGuid ); + + void changePublicName( const TQString& publicName , const TQString &handle=TQString() ); + void changePersonalMessage( MSNProtocol::PersonalMessageType type , const TQString& personalMessage ); + + void changePhoneNumber( const TQString &key, const TQString &data ); + + void createChatSession(); + + void sendMail(const TQString &email); + + /** + * this should return a Kopete::Account::DisconnectReason value + */ + int disconnectReason() { return m_disconnectReason; } + + TQString localIP() { return m_localIP; } + + bool setUseHttpMethod( bool useHttpMethod ); + + bool isLogged() const { return m_isLogged; } + +public slots: + void slotOpenInbox(); + void slotMSNAlertLink(unsigned int action); + void slotMSNAlertUnwanted(); + +signals: + void newContactList(); + void contactList(const TQString& handle, const TQString& publicName, const TQString &contactGuid, uint lists, const TQString& groups); + void contactStatus(const TQString&, const TQString&, const TQString& ); + void contactAdded(const TQString& handle, const TQString& list, const TQString& publicName, const TQString& contactGuid, const TQString& groupGuid); + //void contactRemoved(const TQString&, const TQString&, uint); + void contactRemoved(const TQString& handle, const TQString& list, const TQString& contactGuid, const TQString& groupGuid); + + void groupListed(const TQString&, const TQString&); + void groupAdded( const TQString&, const TQString&); + void groupRenamed( const TQString&, const TQString& ); + void groupRemoved( const TQString& ); + + void invitedToChat(const TQString&, const TQString&, const TQString&, const TQString&, const TQString& ); + void startChat( const TQString&, const TQString& ); + + void statusChanged( const Kopete::OnlineStatus &newStatus ); + + void hotmailSeted(bool) ; + + + /** + * When the dispatch server sends us the notification server to use, this + * signal is emitted. After this the socket is automatically closed. + */ + void receivedNotificationServer( const TQString &host, uint port ); + + +protected: + /** + * Handle an MSN command response line. + */ + virtual void parseCommand( const TQString &cmd, uint id, + const TQString &data ); + + /** + * Handle an MSN error condition. + * This reimplementation handles most of the other MSN error codes. + */ + virtual void handleError( uint code, uint id ); + + /** + * This reimplementation sets up the negotiating with the server and + * suppresses the change of the status to online until the handshake + * is complete. + */ + virtual void doneConnect(); + + +private slots: + /** + * We received a message from the server, which is sent as raw data, + * instead of cr/lf line-based text. + */ + void slotReadMessage( const TQByteArray &bytes ); + + /** + * Send a keepalive to the server to avoid idle connections to cause + * MSN closing the connection + */ + void slotSendKeepAlive(); + + void sslLoginFailed(); + void sslLoginIncorrect(); + void sslLoginSucceeded(TQString ticket); + + +private: + /** + * Convert the MSN status strings to a Kopete::OnlineStatus + */ + Kopete::OnlineStatus convertOnlineStatus( const TQString &statusString ); + + MSNAccount *m_account; + TQString m_password; + TQStringList m_msnAlertURLs; + + unsigned int mailCount; + + Kopete::OnlineStatus m_newstatus; + + /** + * Convert an entry of the Status enum back to a string + */ + TQString statusToString( const Kopete::OnlineStatus &status ) const; + + /** + * Process the CurrentMedia XML element. + * @param mediaXmlElement the source XML element as text. + */ + TQString processCurrentMedia( const TQString &mediaXmlElement ); + + //know the last handle used + TQString m_tmpLastHandle; + TQMap <unsigned int,TQString> m_tmpHandles; + TQString m_configFile; + + //for hotmail inbox opening + bool m_isHotmailAccount; + TQString m_MSPAuth; + TQString m_kv; + TQString m_sid; + TQString m_loginTime; + TQString m_localIP; + MSNSecureLoginHandler *m_secureLoginHandler; + + MSNChallengeHandler *m_challengeHandler; + TQTimer *m_keepaliveTimer; + + bool m_ping; + + int m_disconnectReason; + + /** + * Used to set the myself() personalMessage when the acknowledge(UUX) command is received. + * The personalMessage is built into @ref changePersonalMessage + */ + TQString m_propertyPersonalMessage; + + /** + * Used to tell when we are logged in to MSN Messeger service. + * Logged when we receive the initial profile message from Hotmail. + * + * Some commands only make sense to be done when logged. + */ + bool m_isLogged; +}; + +#endif + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/msn/msnprotocol.cpp b/kopete/protocols/msn/msnprotocol.cpp new file mode 100644 index 00000000..2670d2a0 --- /dev/null +++ b/kopete/protocols/msn/msnprotocol.cpp @@ -0,0 +1,179 @@ +/* + msnprotocol.cpp - Kopete MSN Protocol Plugin + + Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org> + Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org> + Copyright (c) 2002-2003 by Olivier Goffart <ogoffart @ kde.org> + Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net> + + Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include <tqimage.h> + +#include <kdebug.h> +#include <kgenericfactory.h> +#include <tdeconfig.h> +#include <tdeversion.h> +#include <tdeaboutdata.h> + +#include "kopeteaccountmanager.h" +#include "kopeteglobal.h" +#include "kopeteonlinestatusmanager.h" + +#include "msnaddcontactpage.h" +#include "msneditaccountwidget.h" +#include "msncontact.h" +#include "msnaccount.h" +#include "msnprotocol.h" +#include "msnchatsession.h" + +typedef KGenericFactory<MSNProtocol> MSNProtocolFactory; +#if KDE_IS_VERSION(3,2,90) +static const TDEAboutData aboutdata("kopete_msn", I18N_NOOP("MSN Messenger") , "1.0" ); +K_EXPORT_COMPONENT_FACTORY( libkopete_msn_shared, MSNProtocolFactory( &aboutdata ) ) +#else +K_EXPORT_COMPONENT_FACTORY( libkopete_msn_shared, MSNProtocolFactory( "kopete_msn" ) ) +#endif + +MSNProtocol *MSNProtocol::s_protocol = 0L; + +MSNProtocol::MSNProtocol( TQObject *parent, const char *name, const TQStringList & /* args */ ) +: Kopete::Protocol( MSNProtocolFactory::instance(), parent, name ), + NLN( Kopete::OnlineStatus::Online, 25, this, 1, TQString(), i18n( "Online" ) , i18n( "O&nline" ), Kopete::OnlineStatusManager::Online,Kopete::OnlineStatusManager::HasAwayMessage ), + BSY( Kopete::OnlineStatus::Away, 20, this, 2, "msn_busy", i18n( "Busy" ) , i18n( "&Busy" ), Kopete::OnlineStatusManager::Busy, Kopete::OnlineStatusManager::HasAwayMessage ), + BRB( Kopete::OnlineStatus::Away, 22, this, 3, "msn_brb", i18n( "Be Right Back" ), i18n( "Be &Right Back" ) , 0 , Kopete::OnlineStatusManager::HasAwayMessage ), + AWY( Kopete::OnlineStatus::Away, 18, this, 4, "contact_away_overlay", i18n( "Away From Computer" ),i18n( "&Away" ), Kopete::OnlineStatusManager::Away, Kopete::OnlineStatusManager::HasAwayMessage ), + PHN( Kopete::OnlineStatus::Away, 12, this, 5, "contact_phone_overlay", i18n( "On the Phone" ) , i18n( "On The &Phone" ) , 0 , Kopete::OnlineStatusManager::HasAwayMessage ), + LUN( Kopete::OnlineStatus::Away, 15, this, 6, "contact_food_overlay", i18n( "Out to Lunch" ) , i18n( "Out To &Lunch" ) , 0 , Kopete::OnlineStatusManager::HasAwayMessage ), + FLN( Kopete::OnlineStatus::Offline, 0, this, 7, TQString(), i18n( "Offline" ) , i18n( "&Offline" ), Kopete::OnlineStatusManager::Offline,Kopete::OnlineStatusManager::DisabledIfOffline ), + HDN( Kopete::OnlineStatus::Invisible, 3, this, 8, "contact_invisible_overlay", i18n( "Invisible" ) , i18n( "&Invisible" ), Kopete::OnlineStatusManager::Invisible ), + IDL( Kopete::OnlineStatus::Away, 10, this, 9, "contact_away_overlay", i18n( "Idle" ) , i18n( "&Idle" ), Kopete::OnlineStatusManager::Idle , Kopete::OnlineStatusManager::HideFromMenu ), + UNK( Kopete::OnlineStatus::Unknown, 25, this, 0, "status_unknown", i18n( "Status not available" ) ), + CNT( Kopete::OnlineStatus::Connecting, 2, this, 10,"msn_connecting", i18n( "Connecting" ) ), + propEmail(Kopete::Global::Properties::self()->emailAddress()), + propPhoneHome(Kopete::Global::Properties::self()->privatePhone()), + propPhoneWork(Kopete::Global::Properties::self()->workPhone()), + propPhoneMobile(Kopete::Global::Properties::self()->privateMobilePhone()), + propClient("client", i18n("Remote Client"), 0, false), + propGuid("guid", i18n("Contact GUID"), 0, true), + propPersonalMessage(Kopete::Global::Properties::self()->awayMessage()) +{ + s_protocol = this; + + addAddressBookField( "messaging/msn", Kopete::Plugin::MakeIndexField ); + + setCapabilities( Kopete::Protocol::BaseFgColor | Kopete::Protocol::BaseFont | Kopete::Protocol::BaseFormatting ); + + // m_status = m_unknownStatus = UNK; +} + +Kopete::Contact *MSNProtocol::deserializeContact( Kopete::MetaContact *metaContact, const TQMap<TQString, TQString> &serializedData, + const TQMap<TQString, TQString> & /* addressBookData */ ) +{ + TQString contactId = serializedData[ "contactId" ] ; + TQString accountId = serializedData[ "accountId" ] ; + TQString lists = serializedData[ "lists" ]; + TQStringList groups = TQStringList::split( ",", serializedData[ "groups" ] ); + TQString contactGuid = serializedData[ "contactGuid" ] ; + + TQDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts( this ); + + Kopete::Account *account = accounts[ accountId ]; + if( !account ) + account = createNewAccount( accountId ); + + // Create MSN contact + MSNContact *c = new MSNContact( account, contactId, metaContact ); + + for( TQStringList::Iterator it = groups.begin() ; it != groups.end(); ++it ) + c->contactAddedToGroup( *it, 0L /* FIXME - m_groupList[ ( *it ).toUInt() ]*/ ); + + c->m_obj= serializedData[ "obj" ]; + c->setInfo( "PHH" , serializedData[ "PHH" ] ); + c->setInfo( "PHW" , serializedData[ "PHW" ] ); + c->setInfo( "PHM" , serializedData[ "PHM" ] ); + c->setProperty( propGuid, contactGuid ); + + c->setBlocked( (bool)(lists.contains('B')) ); + c->setAllowed( (bool)(lists.contains('A')) ); + c->setReversed( (bool)(lists.contains('R')) ); + + return c; +} + +AddContactPage *MSNProtocol::createAddContactWidget(TQWidget *parent , Kopete::Account *i) +{ + return (new MSNAddContactPage(i->isConnected(),parent)); +} + +KopeteEditAccountWidget *MSNProtocol::createEditAccountWidget(Kopete::Account *account, TQWidget *parent) +{ + return new MSNEditAccountWidget(this,account,parent); +} + +Kopete::Account *MSNProtocol::createNewAccount(const TQString &accountId) +{ + return new MSNAccount(this, accountId); +} + + +// NOTE: CALL THIS ONLY BEING CONNECTED +void MSNProtocol::slotSyncContactList() +{ +/* if ( ! mIsConnected ) + { + return; + } + // First, delete D marked contacts + TQStringList localcontacts; + + contactsFile->setGroup("Default"); + + contactsFile->readListEntry("Contacts",localcontacts); + TQString tmpUin; + tmpUin.sprintf("%d",uin); + tmp.append(tmpUin); + cnt=contactsFile->readNumEntry("Count",0); +*/ +} + +MSNProtocol* MSNProtocol::protocol() +{ + return s_protocol; +} + +bool MSNProtocol::validContactId(const TQString& userid) +{ + return ( userid.contains('@') ==1 && userid.contains('.') >=1 && userid.contains(' ') == 0); +} + +TQImage MSNProtocol::scalePicture(const TQImage &picture) +{ + TQImage img(picture); + img = img.smoothScale( 96, 96, TQ_ScaleMin ); + // crop image if not square + if(img.width() < img.height()) + { + img = img.copy((img.width()-img.height())/2, 0, 96, 96); + } + else if(img.width() > img.height()) + { + img = img.copy(0, (img.height()-img.width())/2, 96, 96); + } + + return img; +} +#include "msnprotocol.moc" + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/msn/msnprotocol.h b/kopete/protocols/msn/msnprotocol.h new file mode 100644 index 00000000..52fb2800 --- /dev/null +++ b/kopete/protocols/msn/msnprotocol.h @@ -0,0 +1,188 @@ +/* + msnprotocol.h - Kopete MSN Protocol Plugin + + Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org> + Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org> + Copyright (c) 2002-2003 by Olivier Goffart <ogoffart @ kde.org> + Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net> + + Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef __msnprotocol_h__ +#define __msnprotocol_h__ + +#include <tqmap.h> +#include <tqstringlist.h> + +#include "kopeteprotocol.h" +#include "kopeteonlinestatus.h" +#include "kopetecontactproperty.h" + +#include "msnsocket.h" + + +class TQImage; + +class TDEAction; +class TDEActionMenu; + +class MSNContact; +class MSNAccount; +class MSNNotifySocket; +class MSNSwitchBoardSocket; +class MSNChatSession; +class MSNInvitation; +namespace Kopete { class ChatSession; } +namespace Kopete { class MetaContact; } +namespace Kopete { class Contact; } +namespace Kopete { class Message; } +namespace Kopete { class Group; } + +/** + * @author duncan + * @author Martijn Klingens <klingens@kde.org> + * @author Olivier Goffart <ogoffart @ kde.org> + */ +class KOPETE_EXPORT MSNProtocol : public Kopete::Protocol +{ + Q_OBJECT + + +public: + MSNProtocol( TQObject *parent, const char *name, const TQStringList &args ); + + /** + * SyncMode indicates whether settings differing between client and + * server should be propagated to keep them in sync. + * SyncToServer - Ignore the server setting when sent. Instead, push + * the local setting to the server. Used when changing + * settings offline. + * SyncFromServer - Update locally stored settings with the value sent + * by the server. Used when connecting to the server if + * no offline changes are pending to force a sync. + * SyncBoth - Changes are updated both ways. This is truly a + * 'first come, first serve' scenario, which breaks if + * the 'old' value is sent by one peer before the other + * end is able to push the new value. An example of this + * is changing the MSN nickname offline - the server can + * only be updated after it has sent the old value to + * the client during connect, destroying the new setting. + * Once connected this is often the most useful setting. + * DontSync - Do not sync values at all. This is used if settings + * are overridden locally, but should not be sent to the + * server, nor should the client update server-pushed + * values. This can be useful for e.g. contact lists. + */ + enum SyncMode + { + DontSync = 0x00, + SyncToServer = 0x01, + SyncFromServer = 0x02, + SyncBoth = 0x03 + }; + + /** + * The possible MSN online statuses + */ + const Kopete::OnlineStatus NLN; //online + const Kopete::OnlineStatus BSY; //busy + const Kopete::OnlineStatus BRB; //be right back + const Kopete::OnlineStatus AWY; //away + const Kopete::OnlineStatus PHN; //on the phone + const Kopete::OnlineStatus LUN; //out to lunch + const Kopete::OnlineStatus FLN; //offline + const Kopete::OnlineStatus HDN; //invisible + const Kopete::OnlineStatus IDL; //idle + const Kopete::OnlineStatus UNK; //inknown (internal) + const Kopete::OnlineStatus CNT; //connecting (internal) + + const Kopete::ContactPropertyTmpl propEmail; + const Kopete::ContactPropertyTmpl propPhoneHome; + const Kopete::ContactPropertyTmpl propPhoneWork; + const Kopete::ContactPropertyTmpl propPhoneMobile; + const Kopete::ContactPropertyTmpl propClient; + const Kopete::ContactPropertyTmpl propGuid; + const Kopete::ContactPropertyTmpl propPersonalMessage; // it's the equivalent of away message. + + enum List + { + FL, // forward + AL, // allow + BL, // blocked + RL, // reverse + PL // pending + }; + + // Enums used to build the Kopete's MSN ClientId. + enum MSNClientInformationFields + { + WindowsMobile = 0x1, + InkFormatGIF = 0x04, + InkFormatISF = 0x08, + SupportWebcam = 0x10, + SupportMultiPacketMessaging = 0x20, + MSNMobileDevice = 0x40, + MSNDirectDevice = 0x80, + WebMessenger = 0x100, + SupportDirectIM = 0x4000, + SupportWinks = 0x8000, + MSNC1 = 0x10000000, + MSNC2 = 0x20000000, + MSNC3 = 0x30000000, + MSNC4 = 0x40000000 + }; + + enum PersonalMessageType + { + PersonalMessageNormal, + PersonalMessageMusic, + PersonalMessageGame, + PersonalMessageOffice + }; + + virtual Kopete::Contact *deserializeContact( Kopete::MetaContact *metaContact, + const TQMap<TQString, TQString> &serializedData, const TQMap<TQString, TQString> &addressBookData ); + + virtual AddContactPage *createAddContactWidget( TQWidget *parent , Kopete::Account *i); + virtual KopeteEditAccountWidget *createEditAccountWidget(Kopete::Account *account, TQWidget *parent); + virtual Kopete::Account *createNewAccount(const TQString &accountId); + + static MSNProtocol* protocol(); + static bool validContactId(const TQString&); + TQImage scalePicture(const TQImage &picture); + +private slots: + void slotSyncContactList(); + +private: + + static MSNProtocol *s_protocol; + +signals: + /** + * A new msn invitation has been arrived. plugins can connect this signal to handle invitations. + * if the invitationID match to their internal id. they can create a new MSNInvitation and pass it via invitation + * + * @param invitation should be set by the plugin to the new invitaiton. plugin should check it is equal to 0L before + * @param bodyMSG is the whole invitation message + * @param cookie is the invitation cookie + * @param msnMM is the message manager + * @param c is the contact + */ + void invitation(MSNInvitation*& invitation, const TQString &bodyMSG , long unsigned int cookie , MSNChatSession* msnMM , MSNContact* c ); +}; + +#endif + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/msn/msnsecureloginhandler.cpp b/kopete/protocols/msn/msnsecureloginhandler.cpp new file mode 100644 index 00000000..f8431f9f --- /dev/null +++ b/kopete/protocols/msn/msnsecureloginhandler.cpp @@ -0,0 +1,131 @@ +/* + msnsecureloginhandler.cpp - SSL login for MSN protocol + + Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ +#include "msnsecureloginhandler.h" + +// TQt includes +#include <tqregexp.h> + +// KDE includes +#include <tdeio/job.h> +#include <kurl.h> +#include <kdebug.h> + +MSNSecureLoginHandler::MSNSecureLoginHandler(const TQString &accountId, const TQString &password, const TQString &authParameters) + : m_password(password), m_accountId(accountId), m_authentification(authParameters) +{ + +} + +MSNSecureLoginHandler::~MSNSecureLoginHandler() +{ +// kdDebug(14140) << k_funcinfo << endl; +} + +void MSNSecureLoginHandler::login() +{ + // Retrive the login server. + // Do a reload and don't show the progress. + TDEIO::Job *getLoginServer = TDEIO::get(KURL("https://nexus.passport.com/rdr/pprdr.asp"), true, false); + + getLoginServer->addMetaData("cookies", "manual"); + getLoginServer->addMetaData("cache", "reload"); + getLoginServer->addMetaData("PropagateHttpHeader", "true"); + + connect(getLoginServer, TQT_SIGNAL(result(TDEIO::Job *)), this, TQT_SLOT(slotLoginServerReceived(TDEIO::Job* ))); +} + +void MSNSecureLoginHandler::slotLoginServerReceived(TDEIO::Job *loginJob) +{ + if(!loginJob->error()) + { + // Retrive the HTTP header + TQString httpHeaders = loginJob->queryMetaData("HTTP-Headers"); + + // Get the login URL using TQRegExp + TQRegExp rx("PassportURLs: DARealm=(.*),DALogin=(.*),DAReg="); + rx.search(httpHeaders); + + // Set the loginUrl and loginServer + TQString loginUrl = rx.cap(2); + TQString loginServer = loginUrl.section('/', 0, 0); + + kdDebug(14140) << k_funcinfo << loginServer << endl; + + TQString authURL = "https://" + loginUrl; + + TDEIO::Job *authJob = TDEIO::get(KURL(authURL), true, false); + authJob->addMetaData("cookies", "manual"); + + TQString authRequest = "Authorization: Passport1.4 " + "OrgVerb=GET," + "OrgURL=http%3A%2F%2Fmessenger%2Emsn%2Ecom," + "sign-in=" + KURL::encode_string(m_accountId) + + ",pwd=" + KURL::encode_string( m_password ).replace(',',"%2C") + + "," + m_authentification + "\r\n"; + +// warning, this debug contains the password +// kdDebug(14140) << k_funcinfo << "Auth request: " << authRequest << endl; + + authJob->addMetaData("customHTTPHeader", authRequest); + authJob->addMetaData("SendLanguageSettings", "false"); + authJob->addMetaData("PropagateHttpHeader", "true"); + authJob->addMetaData("cookies", "manual"); + authJob->addMetaData("cache", "reload"); + + connect(authJob, TQT_SIGNAL(result(TDEIO::Job *)), this, TQT_SLOT(slotTweenerReceived(TDEIO::Job* ))); + } + else + { + kdDebug(14140) << k_funcinfo << loginJob->errorString() << endl; + + emit loginFailed(); + } +} + +void MSNSecureLoginHandler::slotTweenerReceived(TDEIO::Job *authJob) +{ + if(!authJob->error()) + { + TQString httpHeaders = authJob->queryMetaData("HTTP-Headers"); + +// kdDebug(14140) << k_funcinfo << "HTTP headers: " << httpHeaders << endl; + + // Check if we get "401 Unauthorized", thats means it's a bad password. + if(httpHeaders.contains(TQString::fromUtf8("401 Unauthorized"))) + { +// kdDebug(14140) << k_funcinfo << "MSN Login Bad password." << endl; + emit loginBadPassword(); + } + else + { + TQRegExp rx("from-PP='(.*)'"); + rx.search(httpHeaders); + TQString ticket = rx.cap(1); + + // kdDebug(14140) << k_funcinfo << "Received ticket: " << ticket << endl; + + emit loginSuccesful(ticket); + } + } + else + { + kdDebug(14140) << k_funcinfo << authJob->errorString() << endl; + + emit loginFailed(); + } +} +#include "msnsecureloginhandler.moc" diff --git a/kopete/protocols/msn/msnsecureloginhandler.h b/kopete/protocols/msn/msnsecureloginhandler.h new file mode 100644 index 00000000..5dc4a277 --- /dev/null +++ b/kopete/protocols/msn/msnsecureloginhandler.h @@ -0,0 +1,77 @@ +/* + msnsecureloginhandler.h - SSL login for MSN protocol + + Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ +#ifndef MSNSECURELOGINHANDLER_H +#define MSNSECURELOGINHANDLER_H + +#include <tqobject.h> + +namespace TDEIO +{ + class Job; + class MetaData; +} + +/** + * This class handle the login process. It connect to the .NET Password service and retrive the ticket(tweener) to login. + * Use TDEIO. + * + * @author Michaël Larouche <michael.larouche@kdemail.net> +*/ +class MSNSecureLoginHandler : public TQObject +{ +Q_OBJECT + +public: + MSNSecureLoginHandler(const TQString &accountId, const TQString &password, const TQString &authParameters); + + ~MSNSecureLoginHandler(); + + void login(); + +signals: + /** + * TODO: return to const TQString & + */ + void loginSuccesful(TQString ticket); + void loginBadPassword(); + void loginFailed(); + +private slots: + void slotLoginServerReceived(TDEIO::Job *); + /** + * We have received our ticket to login. + */ + void slotTweenerReceived(TDEIO::Job *); + +private: + /** + * Store the password. + */ + TQString m_password; + /** + * Store the accountId. + */ + TQString m_accountId; + /** + * Store the authentification parameters + */ + TQString m_authentification; + + void displayMetaData(TDEIO::MetaData data); +}; + +#endif diff --git a/kopete/protocols/msn/msnsocket.cpp b/kopete/protocols/msn/msnsocket.cpp new file mode 100644 index 00000000..2bb33c97 --- /dev/null +++ b/kopete/protocols/msn/msnsocket.cpp @@ -0,0 +1,1099 @@ +/* + msnsocket.cpp - Base class for the sockets used in MSN + + Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org> + Copyright (c) 2002-2005 by Olivier Goffart <ogoffart at kde.org> + Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + Portions of this code are taken from KMerlin, + (c) 2001 by Olaf Lueg <olueg@olsd.de> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include "msnsocket.h" +//#include "msnprotocol.h" + +#include <tqregexp.h> +#include <tqtimer.h> + +#include <kdebug.h> +#include <tdeconfig.h> +#include <kbufferedsocket.h> +#include <kserversocket.h> +#include <kresolver.h> +#include <tdelocale.h> +#include <tdemessagebox.h> +#include <kurl.h> + +#include "kopeteuiglobal.h" + +using namespace KNetwork; + +class MimeMessage +{ + public: + MimeMessage(const TQString &msg) : message(msg) {} + + TQString getValue(const TQString &key) + { + TQRegExp rx(key+": ([^\r\n]+)"); + rx.search(message); + return rx.cap(1); + } + private: + TQString message; +}; + +MSNSocket::MSNSocket(TQObject* parent) : TQObject (parent) +{ + m_onlineStatus = Disconnected; + m_socket = 0L; + m_useHttp = false; + m_timer = 0L; +} + +MSNSocket::~MSNSocket() +{ + //if ( m_onlineStatus != Disconnected ) + // disconnect(); + delete m_timer; + m_timer = 0L; + doneDisconnect(); + if ( m_socket ) + m_socket->deleteLater(); +} + +void MSNSocket::connect( const TQString &server, uint port ) +{ + if ( m_onlineStatus == Connected || m_onlineStatus == Connecting ) + { + kdWarning( 14140 ) << k_funcinfo << "Already connected or connecting! Not connecting again." << endl; + return; + } + + if( m_onlineStatus == Disconnecting ) + { + // Cleanup first. + // FIXME: More generic!!! + kdWarning( 14140 ) << k_funcinfo << "We're still disconnecting! Deleting socket the hard way first." << endl; + delete m_socket; + } + + setOnlineStatus( Connecting ); + m_id = 0; + //m_lastId = 0; + m_waitBlockSize = 0; + m_buffer = Buffer( 0 ); + + //m_sendQueue.clear(); + + m_server = server; + m_port = port; + + if(!m_useHttp) + m_socket = new TDEBufferedSocket( server, TQString::number(port) ); + else { + m_socket = new TDEBufferedSocket( m_gateway, "80" ); + } + + m_socket->enableRead( true ); + + // enableWrite eats the CPU, and we only need it when the queue is + // non-empty, so disable it until we have actual data in the queue + m_socket->enableWrite( false ); + + TQObject::connect( m_socket, TQT_SIGNAL( readyRead() ), this, TQT_SLOT( slotDataReceived() ) ); + TQObject::connect( m_socket, TQT_SIGNAL( readyWrite() ), this, TQT_SLOT( slotReadyWrite() ) ); + TQObject::connect( m_socket, TQT_SIGNAL( hostFound() ), this, TQT_SLOT( slotHostFound() ) ); + TQObject::connect( m_socket, TQT_SIGNAL( connected( const KResolverEntry&) ), this, TQT_SLOT( slotConnectionSuccess() ) ); + TQObject::connect( m_socket, TQT_SIGNAL( gotError( int ) ), this, TQT_SLOT( slotSocketError( int ) ) ); + TQObject::connect( m_socket, TQT_SIGNAL( closed( ) ), this, TQT_SLOT( slotSocketClosed( ) ) ); + + if(m_useHttp) + { + if(m_timer == 0L) + { + m_timer = new TQTimer(this, "Http poll timer"); + // Connect the slot HttpPoll with the timer timeout signal. + TQObject::connect(m_timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotHttpPoll())); + } + } + + aboutToConnect(); + + // start the asynchronous connection + m_socket->connect(); +} + +void MSNSocket::disconnect() +{ + if(m_useHttp) + if(m_timer->isActive()) { + // If the timer is still active, stop the timer. + m_timer->stop(); + } + + if ( m_socket ) + m_socket->closeNow(); + else + slotSocketClosed(); +} + +void MSNSocket::aboutToConnect() +{ + /* Empty default implementation */ +} + +void MSNSocket::doneConnect() +{ + setOnlineStatus( Connected ); +} + +void MSNSocket::doneDisconnect() +{ + setOnlineStatus( Disconnected ); +} + +void MSNSocket::setOnlineStatus( MSNSocket::OnlineStatus status ) +{ + if ( m_onlineStatus == status ) + return; + + m_onlineStatus = status; + emit onlineStatusChanged( status ); +} + +void MSNSocket::slotSocketError( int error ) +{ + kdWarning( 14140 ) << k_funcinfo << "Error: " << error << " (" << m_socket->TDESocketBase::errorString() << ")" << endl; + + if(!TDESocketBase::isFatalError(error)) + return; + //we only care about fatal error + + TQString errormsg = i18n( "There was an error while connecting to the MSN server.\nError message:\n" ); + if ( error == TDESocketBase::LookupFailure ) + errormsg += i18n( "Unable to lookup %1" ).arg( m_socket->peerResolver().nodeName() ); + else + errormsg += m_socket->TDESocketBase::errorString() ; + + //delete m_socket; + m_socket->deleteLater(); + m_socket = 0L; + + setOnlineStatus( Disconnected ); + emit connectionFailed(); + //like if the socket is closed + emit socketClosed(); + + emit errorMessage( ErrorConnectionError, errormsg ); +} + +void MSNSocket::slotDataReceived() +{ + int avail = m_socket->bytesAvailable(); + if ( avail < 0 ) + { + // error! + kdWarning( 14140 ) << k_funcinfo << "bytesAvailable() returned " << avail + << ". This should not happen!" << endl + << "Are we disconnected? Backtrace:" << endl << kdBacktrace() << endl; + return; + } + + // incoming data, plus an extra char where we pretend a NUL is so the conversion + // to TQCString doesn't go over the end of the allocated memory. + char *buffer = new char[ avail + 1 ]; + int ret = m_socket->readBlock( buffer, avail ); + + if ( ret < 0 ) + { + kdWarning( 14140 ) << k_funcinfo << "readBlock() returned " << ret << "!" <<endl; + } + else if ( ret == 0 ) + { + kdWarning( 14140 ) << k_funcinfo << "readBlock() returned no data!" <<endl; + } + else + { + if ( avail ) + { + if ( ret != avail) + { + kdWarning( 14140 ) << k_funcinfo << avail << " bytes were reported available, " + << "but readBlock() returned only " << ret << " bytes! Proceeding anyway." << endl; + } + } + else + { + kdDebug( 14140 ) << k_funcinfo << "Read " << ret << " bytes into 4kb block." << endl; + } + + + TQString rawData; + + if(m_useHttp) + { + bool error = false; + TQByteArray bytes; + + // Check if all data has arrived. + rawData = TQString(TQCString(buffer, avail + 1)); + bool headers = (rawData.find(TQRegExp("HTTP/\\d\\.\\d (\\d+) ([^\r\n]+)")) != -1); + + if(headers) + { + // The http header packet arrived. + int endOfHeaders = rawData.find("\r\n\r\n"); + if((endOfHeaders + 4) == avail) + { + // Only the response headers data is included. + TQRegExp re("Content-Length: ([^\r\n]+)"); + if(re.search(rawData) != -1) + { + bool valid; + int l = re.cap(1).toInt(&valid); + if(valid && l > 0) + { + // The packet contains the headers but does not contain the content data; + // buffer the data received and read again. + m_buffer.add(buffer, avail); + + delete[] buffer; + // Update how much data remains. + m_remaining = l; + return; + } + } + } + } + else + { + // Write the received data to the buffer. + m_buffer.add(buffer, avail); + + m_remaining -= avail; + if(m_remaining != 0) + { + // We have not received all the content data, read again. + delete[] buffer; + return; + } + + // At this point, we have all the bytes returned from the web request. + bytes = m_buffer.take(m_buffer.size()); + } + + if(bytes.size() == 0) + { + // The response headers and the content came in one packet. + bytes.assign(buffer, avail); + } + + + // Create the web response object from the response bytes. + WebResponse response(bytes); + + if(response.getStatusCode() == 100) { + return; + } + + if(response.getStatusCode() == 200) + { + // If we received a valid response, read the required headers. + // Retrieve the X-MSN-Messenger header. + TQString header = response.getHeaders()->getValue("X-MSN-Messenger"); + + TQStringList parts = TQStringList::split(";", header.replace(" ", "")); + if(!header.isNull() && (parts.count() >= 2)) + { + if(parts[0].find("SessionID", 0) != -1) + { + // Assign the session id. + m_sessionId = parts[0].section("=", 1, 1); + }else + error = true; + + if(parts[1].find("GW-IP", 0) != -1) + { + // Assign the gateway IP address. + m_gwip = parts[1].section("=", 1, 1); + }else + error = true; + + + if(parts.count() > 2) + if((parts[2].find("Session", 0) != -1) && (parts[2].section("=", 1, 1) == "close")) + { + // The http session has been closed by the server, disconnect. + kdDebug(14140) << k_funcinfo << "Session closed." << endl; + m_bCanPoll = false; + disconnect(); + return; + } + }else + error = true; + + // Retrieve the content length header. + header = response.getHeaders()->getValue("Content-Length"); + + if(!header.isNull()) + { + bool valid; + int length = header.toInt(&valid); + if(valid && (length == 0)) + { + // If the response content length is zero, there is nothing to do. + m_pending = false; + return; + } + + if(valid && (length > 0)) + { + // Otherwise, if the content length is greater than zero, get the web response stream. + TQDataStream *stream = response.getResponseStream(); + buffer = new char[length]; + // Read the web response content. + stream->readRawBytes(buffer, length); + ret = length; + }else + error = true; + }else + error = true; + }else + error = true; + + if(error) + { + kdDebug(14140) << k_funcinfo << "Http error: " << response.getStatusCode() << " " + << response.getStatusDescription() << endl; + + // If we encountered an error, disconnect and return. + m_bCanPoll = false; + // Disconnect from the service. + disconnect(); + return; + } + } + + // Simple check to avoid dumping the binary data from the icons and emoticons to kdDebug: + // all MSN commands start with one or more uppercase characters. + // For now just check the first three chars, let's see how accurate it is. + // Additionally, if we receive an MSN-P2P packet, strip off anything after the P2P header. + rawData = TQString( TQCString( buffer, ((!m_useHttp)? avail : ret) + 1 ) ).stripWhiteSpace().replace( + TQRegExp( "(P2P-Dest:.[a-zA-Z@.]*).*" ), "\\1\n\n(Stripped binary data)" ); + + bool isBinary = false; + for ( uint i = 0; i < 3 ; ++i ) + { + if ( (rawData[ i ] < 'A' || rawData[ i ] > 'Z') && (rawData[ i ] < '0' || rawData[ i ] > '9') ) + isBinary = true; + } + + if ( isBinary ) + kdDebug( 14141 ) << k_funcinfo << "(Stripped binary data)" << endl; + else + kdDebug( 14141 ) << k_funcinfo << rawData << endl; + + // fill the buffer with the received data + m_buffer.add( buffer, ret ); + + slotReadLine(); + + if(m_useHttp) { + // Set data pending to false. + m_pending = false; + } + } + + // Cleanup. + delete[] buffer; +} + +void MSNSocket::slotReadLine() +{ + // We have data, first check if it's meant for a block read, otherwise + // parse the first line (which will recursively parse the other lines) + if ( !pollReadBlock() ) + { + if ( m_buffer.size() >= 3 && ( m_buffer.data()[ 0 ] == '\0' || m_buffer.data()[ 0 ]== '\1' ) ) + { + bytesReceived( m_buffer.take( 3 ) ); + TQTimer::singleShot( 0, this, TQT_SLOT( slotReadLine() ) ); + return; + } + + int index = -1; + for ( uint x = 0; m_buffer.size() > x + 1; ++x ) + { + if ( ( m_buffer[ x ] == '\r' ) && ( m_buffer[ x + 1 ] == '\n' ) ) + { + index = x; + break; + } + } + + if ( index != -1 ) + { + TQString command = TQString::fromUtf8( m_buffer.take( index + 2 ), index ); + command.replace( "\r\n", "" ); + //kdDebug( 14141 ) << k_funcinfo << command << endl; + + // Don't block the GUI while parsing data, only do a single line! + // (Done before parseLine() to prevent a potential crash) + TQTimer::singleShot( 0, this, TQT_SLOT( slotReadLine() ) ); + + parseLine( command ); + // WARNING: At this point 'this' can be deleted (when disconnecting) + } + } +} + +void MSNSocket::readBlock( uint len ) +{ + if ( m_waitBlockSize ) + { + kdWarning( 14140 ) << k_funcinfo << "Cannot wait for data block: still waiting for other block of size " + << m_waitBlockSize << "! Data will not be returned." << endl; + return; + } + + m_waitBlockSize = len; + + //kdDebug( 14140 ) << k_funcinfo << "Preparing for block read of size " << len << endl; + + // Try to return the data now, if available. Otherwise slotDataReady + // will do this whenever all data is there. + pollReadBlock(); +} + +bool MSNSocket::pollReadBlock() +{ + if ( !m_waitBlockSize ) + { + return false; + } + else if ( m_buffer.size() < m_waitBlockSize ) + { + kdDebug( 14140 ) << k_funcinfo << "Waiting for data. Received: " << m_buffer.size() << ", required: " << m_waitBlockSize << endl; + return true; + } + + TQByteArray block = m_buffer.take( m_waitBlockSize ); + + //kdDebug( 14140 ) << k_funcinfo << "Successfully read block of size " << m_waitBlockSize << endl; + + m_waitBlockSize = 0; + emit blockRead( block); + + return false; +} + +void MSNSocket::parseLine( const TQString &str ) +{ + TQString cmd = str.section( ' ', 0, 0 ); + TQString data = str.section( ' ', 2 ).replace( "\r\n" , "" ); + + bool isNum; + uint id = str.section( ' ', 1, 1 ).toUInt( &isNum ); + + // In some rare cases, like the 'NLN' / 'FLN' commands no id at all + // is sent. Here it's actually a real parameter... + if ( !isNum ) + data = str.section( ' ', 1, 1 ) + " " + data; + + //if ( isNum && id ) + // m_lastId = id; + + //kdDebug( 14140 ) << k_funcinfo << "Parsing command " << cmd << " (ID " << id << "): '" << data << "'" << endl; + + data.replace( "\r\n", "" ); + bool isError; + uint errorCode = cmd.toUInt( &isError ); + if ( isError ) + handleError( errorCode, id ); + else + parseCommand( cmd, id, data ); +} + +void MSNSocket::handleError( uint code, uint /* id */ ) +{ + kdDebug(14140) << k_funcinfo << endl; + TQString msg; + ErrorType type = ErrorServerError; + switch ( code ) + { +/* + // We cant show message for error we don't know what they are or not related to the correct socket + // Theses following messages are not so instructive + case 205: + msg = i18n ( "An invalid username has been specified.\nPlease correct it, and try to reconnect.\n" ); + break; + case 201: + msg = i18n ( "Fully Qualified domain name missing.\n" ); + break; + case 207: + msg = i18n ( "You are already logged in!\n" ); + break; + case 208: + msg = i18n ( "You specified an invalid username.\nPlease correct it, and try to reconnect.\n"); + break; + case 209: + msg = i18n ( "Your nickname is invalid. Please check it, correct it,\nand try to reconnect.\n" ); + break; + case 210: + msg = i18n ( "Your list has reached its maximum capacity.\nNo more contacts can be added, unless you remove some first.\n" ); + break; + case 216: + msg = i18n ( "This user is not in your contact list.\n " ); + break; + case 300: + msg = i18n ( "Some required fields are missing. Please fill them in and try again.\n" ); + break; + case 302: + msg = i18n ( "You are not logged in.\n" ); + break; +*/ + case 500: + msg = i18n ( "An internal server error occurred. Please try again later." ); + type = MSNSocket::ErrorCannotConnect; + break; + case 502: + msg = i18n ( "It is no longer possible to perform this operation. The MSN server does not allow it anymore." ); + type = MSNSocket::ErrorServerError; + break; + case 600: + case 910: + case 912: + case 921: + case 922: + msg = i18n ( "The MSN server is busy. Please try again later." ); + type = MSNSocket::ErrorConnectionError; + break; + case 601: + case 604: + case 605: + case 914: + case 915: + case 916: + case 917: + msg = i18n ( "The server is not available at the moment. Please try again later." ); + type = MSNSocket::ErrorCannotConnect; + break; + // Server error + default: + // FIXME: if the error causes a disconnect, it will crash, but we can't disconnect every time + msg = i18n( "Unhandled MSN error code %1 \n" + "Please fill a bug report with a detailed description and if possible the last console debug output." ).arg( code ); + // "See http://www.hypothetic.org/docs/msn/basics.php for a description of all error codes." + break; + } + + if ( !msg.isEmpty() ) + emit errorMessage( type, msg ); + + return; +} + +int MSNSocket::sendCommand( const TQString &cmd, const TQString &args, bool addId, const TQByteArray &body, bool binary ) +{ + if ( !m_socket ) + { + kdWarning( 14140 ) << k_funcinfo << "m_socket == NULL!" << endl; + return -1; + } + + TQCString data = cmd.utf8(); + if ( addId ) + data += " " + TQString::number( m_id ).utf8(); + + if ( !args.isEmpty() ) + data += " " + args.utf8(); + + // Add length in bytes, not characters + if ( !body.isEmpty() ) + data += " " + TQString::number( body.size() - (binary ? 0 : 1 ) ).utf8(); + + data += "\r\n"; + + + // the command will be sent in slotReadyWrite + TQByteArray bytes; + const uint length = data.length(); + bytes.duplicate(data.data(), length); + if(!body.isEmpty()) + { + uint l = body.size() - (binary ? 0 : 1); + bytes.resize(length + l); + for(uint i=0; i < l; i++) + bytes[length + i] = body[i]; + } + + // Add the request to the queue. + m_sendQueue.append(bytes); + m_socket->enableWrite(true); + + if ( addId ) + { + ++m_id; + return m_id - 1; + } + + return 0; +} + +void MSNSocket::slotReadyWrite() +{ + if ( !m_sendQueue.isEmpty() ) + { + // If the command queue is not empty, retrieve the first command. + TQValueList<TQByteArray>::Iterator it = m_sendQueue.begin(); + + if(m_useHttp) + { + // If web response data is not pending, send the http request. + if(!m_pending) + { + m_pending = true; + // Temporarily disable http polling. + m_bCanPoll = false; + // Set the host to the msn gateway by default. + TQString host = m_gateway; + TQString query; // Web request query string. + + if(m_bIsFirstInTransaction) + { + query.append("Action=open&Server="); + query.append(m_type); + + query += "&IP=" + m_server; + + m_bIsFirstInTransaction = false; + } + else + { + // If this is not the first request sent in the transaction, + // only add the session Id. + host = m_gwip; + query += "SessionID=" + m_sessionId; + } + + // Create the web request headers. + TQString s = makeHttpRequestString(host, query, (*it).size()); + + uint length = s.length(); + // Create the web request bytes. + TQByteArray bytes(length + (*it).size()); + + // Copy the request headers into the request bytes. + for(uint i=0; i < length; i++) + bytes[i] = s.ascii()[i]; + // Copy the request body into the request bytes. + for(uint i=0; i < (*it).size(); i++) + bytes[length + i] = (*it)[i]; + + kdDebug( 14141 ) << k_funcinfo << "Sending http command: " << TQString(*it).stripWhiteSpace() << endl; + + // Write the request bytes to the socket. + m_socket->writeBlock(bytes.data(), bytes.size()); + + // Remove the request from the request queue. + m_sendQueue.remove(it); + + if(m_sendQueue.isEmpty()) + { + // Disable sending requests. + m_socket->enableWrite(false); + // If the request queue is empty, poll the server. + m_bCanPoll = true; + } + } + } + else + { + // Otherwise, send the command normally. + + // Simple check to avoid dumping the binary data from the icons and emoticons to kdDebug: + // When sending an MSN-P2P packet, strip off anything after the P2P header. + TQString debugData = TQString( *it ).stripWhiteSpace().replace( + TQRegExp( "(P2P-Dest:.[a-zA-Z@.]*).*" ), "\\1\n\n(Stripped binary data)" ); + kdDebug( 14141 ) << k_funcinfo << "Sending command: " << debugData << endl; + + m_socket->writeBlock( *it, ( *it ).size() ); + m_sendQueue.remove( it ); + + // If the queue is empty agalin stop waiting for readyWrite signals + // because of the CPU usage + if ( m_sendQueue.isEmpty() ) + m_socket->enableWrite( false ); + } + } + else + { + m_socket->enableWrite( false ); + + if(m_useHttp) + { + // If the request queue is empty, poll the server. + m_bCanPoll = true; + } + } +} + +TQString MSNSocket::escape( const TQString &str ) +{ + //return ( KURL::encode_string( str, 106 ) ); + //It's not needed to encode everything. The official msn client only encode spaces and % + //If we encode more, the size can be longer than excepted. + + int old_length= str.length(); + TQChar *new_segment = new TQChar[ old_length * 3 + 1 ]; + int new_length = 0; + + for ( int i = 0; i < old_length; i++ ) + { + unsigned short character = str[i].unicode(); + + if( character <= 32 || character == '%' ) + { + new_segment[ new_length++ ] = '%'; + + unsigned int c = character / 16; + c += (c > 9) ? ('A' - 10) : '0'; + new_segment[ new_length++ ] = c; + + c = character % 16; + c += (c > 9) ? ('A' - 10) : '0'; + new_segment[ new_length++ ] = c; + } + else + new_segment[ new_length++ ] = str[i]; + } + + TQString result = TQString(new_segment, new_length); + delete [] new_segment; + return result; + +} + +TQString MSNSocket::unescape( const TQString &str ) +{ + TQString str2 = KURL::decode_string( str, 106 ); + //remove msn+ colors code + str2 = str2.replace( TQRegExp("[\\x1-\\x8]"), "" ); // old msn+ colors + // added by kaoul <erwin.kwolek at gmail.com> + str2 = str2.replace( TQRegExp("\\xB7[&@\'#0]"),""); // dot ... + str2 = str2.replace( TQRegExp("\\xB7\\$,?\\d{1,2}"),""); // dot dollar (comma)? 0-99 + + return str2; +} + +void MSNSocket::slotConnectionSuccess() +{ + if(m_useHttp) + { + // If we are connected, set the data pending flag to false, + // and disable http polling. + m_pending = false; + m_bCanPoll = false; + // If we are connected, start the timer. + m_timer->start(2000, false); + } + + //kdDebug( 14140 ) << k_funcinfo << endl; + doneConnect(); +} + +void MSNSocket::slotHostFound() +{ + // nothing to do +} + +void MSNSocket::slotSocketClosed() +{ + kdDebug( 14140 ) << k_funcinfo << "Socket closed. " << endl; + + if ( !m_socket || m_onlineStatus == Disconnected ) + { + kdDebug( 14140 ) << k_funcinfo << "Socket already deleted or already disconnected" << endl; + return; + } + + doneDisconnect(); + + m_buffer = Buffer( 0 ); + //delete m_socket; + m_socket->deleteLater(); + m_socket = 0L; + + emit socketClosed(); +} + +void MSNSocket::slotHttpPoll() +{ + if(m_pending || !m_bCanPoll){ + // If data is pending or poll has been temporary disabled, return. + return; + } + + // Create the http request headers. + const TQCString headers = makeHttpRequestString(m_gwip, "Action=poll&SessionID=" + m_sessionId, 0).utf8(); + m_socket->writeBlock(headers, headers.length()); + // Wait for the response. + m_pending = true; + m_socket->enableWrite(true); +} + +// Used in MSNFileTransferSocket +// FIXME: Why is this here if it's only used for file transfer? - Martijn +void MSNSocket::bytesReceived( const TQByteArray & /* data */ ) +{ + kdWarning( 14140 ) << k_funcinfo << "Unknown bytes were received" << endl; +} + +void MSNSocket::sendBytes( const TQByteArray &data ) +{ + if ( !m_socket ) + { + kdWarning( 14140 ) << k_funcinfo << "Not yet connected" << endl; + return; + } + + m_socket->writeBlock( data, data.size() ); + m_socket->enableWrite( true ); +} + +bool MSNSocket::setUseHttpMethod( bool useHttp ) +{ + if( m_useHttp == useHttp ) + return true; + + if( useHttp ) { + TQString s = TQString( this->className() ).lower(); + if( s == "msnnotifysocket" ) + m_type = "NS"; + else if( s == "msnswitchboardsocket" ) + m_type = "SB"; + else + m_type = TQString(); + + if( m_type.isNull() ) + return false; + + m_bCanPoll = false; + m_bIsFirstInTransaction = true; + m_pending = false; + m_remaining = 0; + m_gateway = "gateway.messenger.hotmail.com"; + } + + if ( m_onlineStatus != Disconnected ) + disconnect(); + + m_useHttp = useHttp; + + return true; +} + +bool MSNSocket::useHttpMethod() const +{ + return m_useHttp; +} + +bool MSNSocket::accept( TDEServerSocket *server ) +{ + if ( m_socket ) + { + kdWarning( 14140 ) << k_funcinfo << "Socket already exists!" << endl; + return false; + } + + m_socket = static_cast<TDEBufferedSocket*>(server->accept()); + + if ( !m_socket ) + { +// kdWarning( 14140 ) << k_funcinfo << "Socket not created. Error nb" << server->error() << " : " << server->errorString() << endl; + return false; + } + + kdDebug( 14140 ) << k_funcinfo << "incoming connection accepted" << endl; + + setOnlineStatus( Connecting ); + + m_id = 0; + //m_lastId = 0; + m_waitBlockSize = 0; + + m_socket->setBlocking( false ); + m_socket->enableRead( true ); + m_socket->enableWrite( true ); + + TQObject::connect( m_socket, TQT_SIGNAL( readyRead() ), this, TQT_SLOT( slotDataReceived() ) ); + TQObject::connect( m_socket, TQT_SIGNAL( readyWrite() ), this, TQT_SLOT( slotReadyWrite() ) ); + TQObject::connect( m_socket, TQT_SIGNAL( closed() ), this, TQT_SLOT( slotSocketClosed() ) ); + TQObject::connect( m_socket, TQT_SIGNAL( gotError( int ) ), this, TQT_SLOT( slotSocketError( int ) ) ); + + doneConnect(); + return true; +} + +TQString MSNSocket::getLocalIP() +{ + if ( !m_socket ) + return TQString(); + + const TDESocketAddress address = m_socket->localAddress(); + + TQString ip = address.nodeName(); + + kdDebug( 14140 ) << k_funcinfo << "IP: " << ip <<endl; + //delete address; + return ip; +} + +MSNSocket::Buffer::Buffer( unsigned int sz ) +: TQByteArray( sz ) +{ +} + +MSNSocket::Buffer::~Buffer() +{ +} + +void MSNSocket::Buffer::add( char *str, unsigned int sz ) +{ + char *b = new char[ size() + sz ]; + for ( uint f = 0; f < size(); f++ ) + b[ f ] = data()[ f ]; + for ( uint f = 0; f < sz; f++ ) + b[ size() + f ] = str[ f ]; + + duplicate( b, size() + sz ); + delete[] b; +} + +TQByteArray MSNSocket::Buffer::take( unsigned blockSize ) +{ + if ( size() < blockSize ) + { + kdWarning( 14140 ) << k_funcinfo << "Buffer size " << size() << " < asked size " << blockSize << "!" << endl; + return TQByteArray(); + } + + TQByteArray rep( blockSize ); + for( uint i = 0; i < blockSize; i++ ) + rep[ i ] = data()[ i ]; + + char *str = new char[ size() - blockSize ]; + for ( uint i = 0; i < size() - blockSize; i++ ) + str[ i ] = data()[ blockSize + i ]; + duplicate( str, size() - blockSize ); + delete[] str; + + return rep; +} + +TQString MSNSocket::makeHttpRequestString(const TQString& host, const TQString& query, uint contentLength) +{ + TQString s( + "POST http://" + host + "/gateway/gateway.dll?" + query + " HTTP/1.1\r\n" + + "Accept: */*\r\n" + + "Accept-Language: en-us\r\n" + + "User-Agent: MSMSGS\r\n" + + "Host: " + host + "\r\n" + + "Proxy-Connection: Keep-Alive\r\n" + + "Connection: Keep-Alive\r\n" + + "Pragma: no-cache\r\n" + + "Content-Type: application/x-msn-messenger\r\n" + + "Content-Length: " + TQString::number(contentLength) + "\r\n" + + "\r\n"); + return s; +} + +MSNSocket::WebResponse::WebResponse(const TQByteArray& bytes) +{ + m_statusCode = 0; + m_stream = 0; + + int headerEnd; + TQString header; + TQString data(TQCString(bytes, bytes.size() + 1)); + + // Parse the HTTP status header + TQRegExp re("HTTP/\\d\\.\\d (\\d+) ([^\r\n]+)"); + headerEnd = data.find("\r\n"); + header = data.left( (headerEnd == -1) ? 20 : headerEnd ); + + re.search(header); + m_statusCode = re.cap(1).toInt(); + m_statusDescription = re.cap(2); + + // Remove the web response status header. + data = data.mid(headerEnd + 2, (data.find("\r\n\r\n") + 2) - (headerEnd + 2)); + // Create a MimeMessage, removing the HTTP status header + m_headers = new MimeMessage(data); + + // Retrieve the contentlength header. + header = m_headers->getValue("Content-Length"); + if(!header.isNull()) + { + bool valid; + int length = header.toInt(&valid); + if(valid && length > 0) + { + // If the content length is valid, and not zero, + // copy the web response content bytes. + int offset = bytes.size() - length; + + TQByteArray content(length); + for(int i=0; i < length; i++) + content[i] = bytes[offset + i]; + // Create the web response stream from the response content bytes. + m_stream = new TQDataStream(content, IO_ReadOnly); + } + } +} + +MSNSocket::WebResponse::~WebResponse() +{ + delete m_headers; + m_headers = 0; + delete m_stream; + m_stream = 0; +} + +MimeMessage* MSNSocket::WebResponse::getHeaders() +{ + return m_headers; +} + +TQDataStream* MSNSocket::WebResponse::getResponseStream() +{ + return m_stream; +} + +int MSNSocket::WebResponse::getStatusCode() +{ + return m_statusCode; +} + +TQString MSNSocket::WebResponse::getStatusDescription() +{ + return m_statusDescription; +} + + +#include "msnsocket.moc" + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/msn/msnsocket.h b/kopete/protocols/msn/msnsocket.h new file mode 100644 index 00000000..5661ee41 --- /dev/null +++ b/kopete/protocols/msn/msnsocket.h @@ -0,0 +1,363 @@ +/* + msnsocket.h - Base class for the sockets used in MSN + + Copyright (c) 2002 by Martijn Klingens <klingens@kde.org> + Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org> + Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com> + Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org> + + Portions of this code are taken from KMerlin, + (c) 2001 by Olaf Lueg <olueg@olsd.de> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef MSNSOCKET_H +#define MSNSOCKET_H + +#include <tqobject.h> +#include <tqdatastream.h> +#include <tqstringlist.h> +#include <tqtimer.h> +#include <tqvaluelist.h> + +#include "kopete_export.h" + +namespace KNetwork { + class TDEBufferedSocket; + class TDEServerSocket; +} + +class MimeMessage; + +/** + * @author Martijn Klingens <klingens@kde.org> + * + * MSNSocket encapsulates the common functionality shared by the Dispatch + * Server, the Notification Server and the Switchboard Server. It is + * inherited by the various specialized classes. + */ +class KOPETE_EXPORT MSNSocket : public TQObject +{ + Q_OBJECT + + +public: + MSNSocket(TQObject* parent=0l); + ~MSNSocket(); + + /** + * Asynchronously read a block of data of the specified size. When the + * data is available, the blockRead() signal will be emitted with the + * data as parameter. + * + * NOTE: As the block queue takes precedence over the line-based + * command-processing this method can effectively block all + * communications when passed a wrong length! + */ + void readBlock( uint len ); + + /** + * OnlineStatus encapsulates the 4 states a connection can be in, + * Connecting, Connected, Disconnecting, Disconnected. Connecting + * and Disconnecting are in the default implementation not used, + * because the socket connect is an atomic operation and not yet + * performed asynchronously. + * In derived classes, like the Notification Server, this state is + * actively used, because merely having a socket connection established + * by no means indicates we're actually online - the rest of the + * handshake likely has to follow first. + */ + enum OnlineStatus { Connecting, Connected, Disconnecting, Disconnected }; + enum LookupStatus { Processing, Success, Failed }; + enum Transport { TcpTransport, HttpTransport }; + enum ErrorType { ErrorConnectionLost, ErrorConnectionError, ErrorCannotConnect, ErrorServerError, ErrorInformation}; + + OnlineStatus onlineStatus() { return m_onlineStatus; } + + /* + * return the local ip. + * Used for filetransfer + */ + TQString getLocalIP(); + + //BEGIN Http + + virtual bool setUseHttpMethod( bool useHttp ); + bool useHttpMethod() const; + + //END + +public slots: + void connect( const TQString &server, uint port ); + virtual void disconnect(); + + + /** + * Send an MSN command to the socket + * + * For debugging it's convenient to have this method public, but using + * it outside this class is deprecated for any other use! + * + * The size of the body (if any) is automatically added to the argument + * list and shouldn't be explicitly specified! This size is in bytes + * instead of characters to reflect what actually goes over the wire. + * + * if the param binary is set to true, then, the body is send as a binary message + * + * return the id + */ + int sendCommand( const TQString &cmd, const TQString &args = TQString(), + bool addId = true, const TQByteArray &body = TQByteArray() , bool binary=false ); + +signals: + /** + * A block read is ready. + * After this the normal line-based reads go on again + */ + void blockRead( const TQByteArray &block ); + + /** + * The online status has changed + */ + void onlineStatusChanged( MSNSocket::OnlineStatus status ); + + /** + * The connection failed + */ + void connectionFailed(); + + /** + * The connection was closed + */ + void socketClosed(); + + /** + * A error has occured. Handle the display of the message. + */ + void errorMessage( int type, const TQString &msg ); + +protected: + /** + * Convenience method: escape spaces with '%20' for use in the protocol. + * Doesn't escape any other sequence. + */ + TQString escape( const TQString &str ); + + /** + * And the other way round... + */ + TQString unescape( const TQString &str ); + + /** + * Set the online status. Emits onlineStatusChanged. + */ + void setOnlineStatus( OnlineStatus status ); + + /** + * This method is called directly before the socket will actually connect. + * Override in derived classes to setup whatever is needed before connect. + */ + virtual void aboutToConnect(); + + /** + * Directly after the connect, this method is called. The default + * implementation sets the OnlineStatus to Connected, be sure to override + * this if a handshake is required. + */ + virtual void doneConnect(); + + /** + * Directly after the disconnect, this method is called before the actual + * cleanup takes place. The socket is close here. Cleanup internal + * variables here. + */ + virtual void doneDisconnect(); + + /** + * Handle an MSN error condition. + * The default implementation displays a generic error message and + * closes the connection. Override to allow more graceful handling and + * possibly recovery. + */ + virtual void handleError( uint code, uint id ); + + /** + * Handle an MSN command response line. + * This method is pure virtual and *must* be overridden in derived + * classes. + */ + virtual void parseCommand( const TQString &cmd, uint id, + const TQString &data ) = 0; + + /** + * Used in MSNFileTransferSocket + */ + virtual void bytesReceived( const TQByteArray & ); + bool accept( KNetwork::TDEServerSocket * ); + void sendBytes( const TQByteArray &data ); + + const TQString &server() { return m_server; } + uint port() { return m_port; } + + /** + * The last confirmed ID by the server + */ + //uint m_lastId; + +private slots: + void slotDataReceived(); + /** + * If the socket emits a connectionFailed() then this slot is called + * to handle the error. + */ + void slotSocketError( int error ); + + /* + * Calls connectDone() when connection is successfully established. + */ + void slotConnectionSuccess(); + + /** + * Sets m_lookupProgress to 'Finished' if count > 0 or 'Failed' if count = 0. + */ + void slotHostFound( ); + + /** + * Check if new lines of data are available and process the first line + */ + void slotReadLine(); + + void slotSocketClosed(); + + //BEGIN Http + + /** + * Sends a poll request to the msn gateway when using HttpTransport. + * equivalent to sending a PNG command over TcpTransport. + */ + void slotHttpPoll(); + + //END + +protected slots: + virtual void slotReadyWrite(); + +private: + /** + * Check if we're waiting for a block of raw data. Emits blockRead() + * when the data is available. + * Returns true when still waiting and false when there is no pending + * read, or when the read is successfully handled. + */ + bool pollReadBlock(); + + /** + * The id of the message sent to the MSN server. This ID will increment + * for each subsequent message sent. + */ + uint m_id; + + /** + * Queue of pending commands (should be mostly empty, but is needed to + * send more than one command to the server) + */ + TQValueList<TQByteArray> m_sendQueue; + + /** + * Parse a single line of data. + * Will call either parseCommand or handleError depending on the type of + * data received. + */ + void parseLine( const TQString &str ); + + KNetwork::TDEBufferedSocket *m_socket; + OnlineStatus m_onlineStatus; + + TQString m_server; + uint m_port; + + /** + * The size of the requested block for block-based reads + */ + uint m_waitBlockSize; + + class Buffer : public TQByteArray + { + public: + Buffer( unsigned size = 0 ); + ~Buffer(); + void add( char *str, unsigned size ); + TQByteArray take( unsigned size ); + }; + + Buffer m_buffer; + + //BEGIN Http + + /** + * Makes a http request headers string using the specified, host, query, and content length. + * return: The string containing the http request headers. + */ + TQString makeHttpRequestString(const TQString& host, const TQString& query, uint contentLength); + + bool m_useHttp; // Indicates whether to use the msn http gateway to connect to the msn service. + bool m_bCanPoll; // Indicates whether polling of the http server is allowed. + bool m_bIsFirstInTransaction; // Indicates whether pending message to be sent is the first in the transaction. + // If so, the gateway is used. + // Use the gateway only for initial connected state; Otherwise, use the host. + TQString m_gateway; // Msn http gateway domain name. + TQString m_gwip; // The ip address of the msn gateway. + TQString m_sessionId; // session id. + TQTimer *m_timer; // Msn http poll timer. + TQString m_type; // Indicates the type of socket being used. NS or SB + bool m_pending; // Indicates whether a http response is pending. + int m_remaining; // Indicates how many bytes of content data remain + // to be received if the content bytes are sent in + // a seperate packet(s). + + /** + * Provides access to information returned from a URI request. + */ + class WebResponse + { + public: + WebResponse(const TQByteArray& bytes); + ~WebResponse(); + + /** + * Gets the headers associated with this response from the server. + */ + MimeMessage* getHeaders(); + /** + * Gets the data stream used to read the body of the response from the server. + */ + TQDataStream* getResponseStream(); + /** + * Gets the status code of the response. + */ + int getStatusCode(); + /** + * Gets the status description returned with the response. + */ + TQString getStatusDescription(); + + private: + MimeMessage *m_headers; + TQDataStream *m_stream; + int m_statusCode; + TQString m_statusDescription; + }; + + //END +}; + +#endif + diff --git a/kopete/protocols/msn/msnswitchboardsocket.cpp b/kopete/protocols/msn/msnswitchboardsocket.cpp new file mode 100644 index 00000000..77b7b550 --- /dev/null +++ b/kopete/protocols/msn/msnswitchboardsocket.cpp @@ -0,0 +1,1142 @@ +/* + msnswitchboardsocket.cpp - switch board connection socket + + Copyright (c) 2002 by Martijn Klingens <klingens@kde.org> + Copyright (c) 2002-2006 by Olivier Goffart <ogoffart@ kde.org> + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + Portions of this code are taken from KMerlin, + (c) 2001 by Olaf Lueg <olueg@olsd.de> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + + +#include "msnswitchboardsocket.h" + +#include <stdlib.h> +#include <time.h> +#include <cmath> + +// qt +#include <tqstylesheet.h> +#include <tqregexp.h> +#include <tqimage.h> +#include <tqtimer.h> +#include <tqfile.h> +#include <tqfileinfo.h> + +// kde +#include <kdebug.h> +#include <tdemessagebox.h> +#include <tdeapplication.h> +#include <tdeaboutdata.h> +#include <tdetempfile.h> +#include <tdeconfig.h> +#include <kmdcodec.h> +#include <kstandarddirs.h> +#include <tdetempfile.h> + +// for the display picture +#include <msncontact.h> +#include "msnnotifysocket.h" + +//kopete +#include "msnaccount.h" +#include "msnprotocol.h" +#include "kopetemessage.h" +#include "kopetecontact.h" +#include "kopeteuiglobal.h" +#include "kopeteemoticons.h" +//#include "kopeteaccountmanager.h" +//#include "kopeteprotocol.h" + +#include "sha1.h" + +#include "dispatcher.h" +using P2P::Dispatcher; + +MSNSwitchBoardSocket::MSNSwitchBoardSocket( MSNAccount *account , TQObject *parent ) +: MSNSocket( parent ) +{ + m_account = account; + m_recvIcons=0; + m_emoticonTimer=0L; + m_chunks=0; + m_clientcapsSent=false; + m_dispatcher = 0l; + m_keepAlive = 0l; + m_keepAliveNb=0; +} + +MSNSwitchBoardSocket::~MSNSwitchBoardSocket() +{ + kdDebug(14140) << k_funcinfo << endl; + + TQMap<TQString , TQPair<TQString , KTempFile*> >::Iterator it; + for ( it = m_emoticons.begin(); it != m_emoticons.end(); ++it ) + { + delete it.data().second; + } +} + +void MSNSwitchBoardSocket::connectToSwitchBoard(TQString ID, TQString address, TQString auth) +{ + // we need these for the handshake later on (when we're connected) + m_ID = ID; + m_auth = auth; + + TQString server = address.left( address.find( ":" ) ); + uint port = address.right( address.length() - address.findRev( ":" ) - 1 ).toUInt(); + + TQObject::connect( this, TQT_SIGNAL( blockRead( const TQByteArray & ) ), + this, TQT_SLOT(slotReadMessage( const TQByteArray & ) ) ); + + TQObject::connect( this, TQT_SIGNAL( onlineStatusChanged( MSNSocket::OnlineStatus ) ), + this, TQT_SLOT( slotOnlineStatusChanged( MSNSocket::OnlineStatus ) ) ); + + TQObject::connect( this, TQT_SIGNAL( socketClosed( ) ), + this, TQT_SLOT( slotSocketClosed( ) ) ); + + connect( server, port ); +} + +void MSNSwitchBoardSocket::handleError( uint code, uint id ) +{ + kdDebug(14140) << k_funcinfo << endl; + + TQString msg; + MSNSocket::ErrorType type; + + switch( code ) + { + case 208: + { + msg = i18n( "Invalid user:\n" + "this MSN user does not exist; please check the MSN ID." ); + type = MSNSocket::ErrorServerError; + + userLeftChat(m_msgHandle , i18n("user never joined")); + break; + } + case 215: + { + msg = i18n( "The user %1 is already in this chat." ).arg( m_msgHandle ); + type = MSNSocket::ErrorServerError; + + //userLeftChat(m_msgHandle , i18n("user was twice in this chat") ); //(the user shouln't join there + break; + } + case 216: + { + msg = i18n( "The user %1 is online but has blocked you:\nyou can not talk to this user." ).arg( m_msgHandle ); + type = MSNSocket::ErrorInformation; + + userLeftChat(m_msgHandle, i18n("user blocked you")); + break; + } + case 217: + { + // TODO: we need to know the nickname instead of the handle. + msg = i18n( "The user %1 is currently not signed in.\n" "Messages will not be delivered." ).arg( m_msgHandle ); + type = MSNSocket::ErrorServerError; + + userLeftChat(m_msgHandle, i18n("user disconnected")); + break; + } + case 713: + { + TQString msg = i18n( "You are trying to invite too many contacts to this chat at the same time" ).arg( m_msgHandle ); + type = MSNSocket::ErrorInformation; + + userLeftChat(m_msgHandle, i18n("user blocked you")); + break; + } + case 911: + { + msg = i18n("Kopete MSN plugin has trouble authenticating with switchboard server."); + type = MSNSocket::ErrorServerError; + + break; + } + default: + MSNSocket::handleError( code, id ); + break; + } + + if( !msg.isEmpty() ) + emit errorMessage( type, msg ); +} + +void MSNSwitchBoardSocket::parseCommand( const TQString &cmd, uint id , + const TQString &data ) +{ + if( cmd == "NAK" ) + { + emit msgAcknowledgement(id, false); // msg was not accepted + } + else if( cmd == "ACK" ) + { + emit msgAcknowledgement(id, true); // msg has received + } + else if( cmd == "JOI" ) + { + // new user joins the chat, update user in chat list + TQString handle = data.section( ' ', 0, 0 ); + TQString screenname = unescape(data.section( ' ', 1, 1 )); + if( !m_chatMembers.contains( handle ) ) + m_chatMembers.append( handle ); + emit userJoined( handle, screenname, false ); + } + else if( cmd == "IRO" ) + { + // we have joined a multi chat session- this are the users in this chat + TQString handle = data.section( ' ', 2, 2 ); + if( !m_chatMembers.contains( handle ) ) + m_chatMembers.append( handle ); + + TQString screenname = unescape(data.section( ' ', 3, 3)); + emit userJoined( handle, screenname, true ); + } + else if( cmd == "USR" ) + { + slotInviteContact(m_msgHandle); + } + else if( cmd == "BYE" ) + { + // some has disconnect from chat, update user in chat list + cleanQueue(); //in case some message are waiting their emoticons, never mind, send them + + TQString handle = data.section( ' ', 0, 0 ).replace( "\r\n" , "" ); + userLeftChat( handle, (data.section( ' ', 1, 1 ) == "1" ) ? i18n("timeout") : TQString() ); + } + else if( cmd == "MSG" ) + { + TQString len = data.section( ' ', 2, 2 ); + + // we need to know who's sending is the block... + m_msgHandle = data.section( ' ', 0, 0 ); + + /*//This is WRONG! the displayName is never updated on the switchboeardsocket + //so we can't trust it. + //that's why the official client does not uptade alaws the nickname immediately. + if(m_account->contacts()[ m_msgHandle ]) + { + TQString displayName=data.section( ' ', 1, 1 ); + if(m_account->contacts()[ m_msgHandle ]->displayName() != displayName) + m_account->contacts()[ m_msgHandle ]->rename(displayName); + }*/ + + readBlock(len.toUInt()); + } +} + +void MSNSwitchBoardSocket::slotReadMessage( const TQByteArray &bytes ) +{ + TQString msg = TQString::fromUtf8(bytes, bytes.size()); + + TQRegExp rx("Content-Type: ([A-Za-z0-9/\\-]*)"); + rx.search(msg); + TQString type=rx.cap(1); + + rx=TQRegExp("User-Agent: ([A-Za-z0-9./\\-]*)"); + rx.search(msg); + TQString clientStr=rx.cap(1); + + if( !clientStr.isNull() && !m_msgHandle.isNull()) + { + Kopete::Contact *c=m_account->contacts()[m_msgHandle]; + if(c) + c->setProperty( MSNProtocol::protocol()->propClient , clientStr ); + } + + // incoming message for File-transfer + if( type== "text/x-msmsgsinvite" ) + { + emit invitation(m_msgHandle,msg); + } + else if( type== "text/x-msmsgscontrol" ) + { + TQString message; + message = msg.right( msg.length() - msg.findRev( " " ) - 1 ); + message = message.replace( "\r\n" ,"" ); + emit receivedTypingMsg( message.lower(), true ); + } + else if(type == "text/x-msnmsgr-datacast") + { + if(msg.contains("ID:")) + { + TQRegExp rx("ID: ([0-9]*)"); + rx.search(msg); + uint dataCastId = rx.cap(1).toUInt(); + if( dataCastId == 1 ) + { + kdDebug(14140) << k_funcinfo << "Received a nudge !" << endl; + emit nudgeReceived(m_msgHandle); + } + } + } + else if(type=="text/plain" /* || type.isEmpty()*/ ) + { + // Some MSN Clients (like CCMSN) don't like to stick to the rules. + // In case of CCMSN, it doesn't send what the content type is when + // sending a text message. So if it's not supplied, we'll just + // assume its that. + + TQColor fontColor; + TQFont font; + + if ( msg.contains( "X-MMS-IM-Format" ) ) + { + TQString fontName; + TQString fontInfo; + TQString color; + + rx=TQRegExp("X-MMS-IM-Format: ([^\r\n]*)"); + rx.search(msg); + fontInfo =rx.cap(1); + + color = parseFontAttr(fontInfo, "CO"); + + // FIXME: this is so BAAAAAAAAAAAAD :( + if (!color.isEmpty() && color.toInt(0,16)!=0) + { + if ( color.length() == 2) // only #RR (red color) given + fontColor.setRgb( + color.mid(0,2).toInt(0,16), + 0, + 0); + else if ( color.length() == 4) // #GGRR (green, red) given. + { + fontColor.setRgb( + color.mid(2,2).toInt(0,16), + color.mid(0,2).toInt(0,16), + 0); + } + else if ( color.length() == 6) // full #BBGGRR given + { + fontColor.setRgb( + color.mid(4,2).toInt(0, 16), + color.mid(2,2).toInt(0,16), + color.mid(0,2).toInt(0,16)); + } + } + + fontName = parseFontAttr(fontInfo, "FN").replace( "%20" , " " ); + + // Some clients like Trillian and Kopete itself send a font + // name of 'MS Serif' since MS changed the server to + // _require_ a font name specified in june 2002. + // MSN's own client defaults to 'MS Sans Serif', which also + // has issues. + // Handle 'MS Serif' and 'MS Sans Serif' as an empty font name + if( !fontName.isEmpty() && fontName != "MS Serif" && fontName != "MS Sans Serif" ) + { + TQString ef=parseFontAttr( fontInfo, "EF" ); + + font = TQFont( fontName, + parseFontAttr( fontInfo, "PF" ).toInt(), // font size + ef.contains( 'B' ) ? TQFont::Bold : TQFont::Normal, + ef.contains( 'I' ) ); + font.setUnderline(ef.contains( 'U' )); + font.setStrikeOut(ef.contains( 'S' )); + } + } + + TQPtrList<Kopete::Contact> others; + others.append( m_account->myself() ); + TQStringList::iterator it2; + for( it2 = m_chatMembers.begin(); it2 != m_chatMembers.end(); ++it2 ) + { + if( *it2 != m_msgHandle ) + others.append( m_account->contacts()[ *it2 ] ); + } + + TQString message=msg.right( msg.length() - msg.find("\r\n\r\n") - 4 ); + + //Stupid MSN PLUS colors code. message with incorrect charactere are not showed correctly in the chatwindow. + //TODO: parse theses one to show the color too in Kopete + message.replace("\3","").replace("\4","").replace("\2","").replace("\5","").replace("\6","").replace("\7",""); + + if(!m_account->contacts()[m_msgHandle]) + { + //this may happens if the contact has been deleted. + kdDebug(14140) << k_funcinfo <<"WARNING: contact is null, adding it" <<endl; + if( !m_chatMembers.contains( m_msgHandle ) ) + m_chatMembers.append( m_msgHandle ); + emit userJoined( m_msgHandle , m_msgHandle , false); + } + + Kopete::Message kmsg( m_account->contacts()[ m_msgHandle ], others, + message, + Kopete::Message::Inbound , Kopete::Message::PlainText ); + + kmsg.setFg( fontColor ); + kmsg.setFont( font ); + + rx=TQRegExp("Chunks: ([0-9]*)"); + rx.search(msg); + unsigned int chunks=rx.cap(1).toUInt(); + rx=TQRegExp("Chunk: ([0-9]*)"); + rx.search(msg); + unsigned int chunk=rx.cap(1).toUInt(); + + if(chunk != 0 && !m_msgQueue.isEmpty()) + { + TQString msg=m_msgQueue.last().plainBody(); + m_msgQueue.pop_back(); //removes the last item + kmsg.setBody( msg+ message, Kopete::Message::PlainText ); + } + + if(chunk == 0 ) + m_chunks=chunks; + else if(chunk+1 >= m_chunks) + m_chunks=0; + + if ( m_recvIcons > 0 || m_chunks > 0) + { //Some custom emoticons are waiting to be received. so append the message to the queue + //Or the message has not been fully received, so same thing + kdDebug(14140) << k_funcinfo << "Message not fully received => append to queue. Emoticon left: " << m_recvIcons << " chunks: " << chunk+1 << " of " << m_chunks <<endl; + m_msgQueue.append( kmsg ); + if(!m_emoticonTimer) //to be sure no message will be lost, we will appends message to + { // the queue in 15 secondes even if we have not received emoticons + m_emoticonTimer=new TQTimer(this); + TQObject::connect(m_emoticonTimer , TQT_SIGNAL(timeout()) , this, TQT_SLOT(cleanQueue())); + m_emoticonTimer->start( 15000 , true ); + } + } + else + emit msgReceived( parseCustomEmoticons( kmsg ) ); + + } + else if( type== "text/x-mms-emoticon" || type== "text/x-mms-animemoticon") + { + // TODO remove Displatcher. + TDEConfig *config = TDEGlobal::config(); + config->setGroup( "MSN" ); + if ( config->readBoolEntry( "useCustomEmoticons", true ) ) + { + TQRegExp rx("([^\\s]*)[\\s]*(<msnobj [^>]*>)"); + rx.setMinimal(true); + int pos = rx.search(msg); + while( pos != -1) + { + TQString msnobj=rx.cap(2); + TQString txt=rx.cap(1); + kdDebug(14140) << k_funcinfo << "emoticon: " << txt << " msnobj: " << msnobj<< endl; + + if( !m_emoticons.contains(msnobj) || !m_emoticons[msnobj].second ) + { + m_emoticons.insert(msnobj, tqMakePair(txt,(KTempFile*)0L)); + MSNContact *c=static_cast<MSNContact*>(m_account->contacts()[m_msgHandle]); + if(!c) + return; + + // we are receiving emoticons, so delay message display until received signal + m_recvIcons++; + PeerDispatcher()->requestDisplayIcon(m_msgHandle, msnobj); + } + pos=rx.search(msg, pos+rx.matchedLength()); + } + } + } + else if( type== "application/x-msnmsgrp2p" ) + { + PeerDispatcher()->slotReadMessage(m_msgHandle, bytes); + } + else if( type == "text/x-clientcaps" ) + { + rx=TQRegExp("Client-Name: ([A-Za-z0-9.$!*/% \\-]*)"); + rx.search(msg); + clientStr=unescape( rx.cap(1) ); + + if( !clientStr.isNull() && !m_msgHandle.isNull()) + { + Kopete::Contact *c=m_account->contacts()[m_msgHandle]; + if(c) + c->setProperty( MSNProtocol::protocol()->propClient , clientStr ); + } + + if(!m_clientcapsSent) + { + TDEConfig *config = TDEGlobal::config(); + config->setGroup( "MSN" ); + + TQString JabberID; + if(config->readBoolEntry("SendJabber", true)) + JabberID=config->readEntry("JabberAccount"); + + if(!JabberID.isEmpty()) + JabberID="JabberID: "+JabberID +"\r\n"; + + if( config->readBoolEntry("SendClientInfo", true) || !JabberID.isEmpty()) + { + + TQCString message = TQString( "MIME-Version: 1.0\r\n" + "Content-Type: text/x-clientcaps\r\n" + "Client-Name: Kopete/"+escape(kapp->aboutData()->version())+"\r\n" + +JabberID+ + "\r\n" ).utf8(); + + TQString args = "U"; + sendCommand( "MSG", args, true, message ); + } + m_clientcapsSent=true; + } + + + } + else if(type == "image/gif" || msg.contains("Message-ID:")) + { + // Incoming inkformatgif. + TQRegExp regex("Message-ID: \\{([0-9A-F\\-]*)\\}"); + regex.search(msg); + TQString messageId = regex.cap(1); + regex = TQRegExp("Chunks: (\\d+)"); + regex.search(msg); + TQString chunks = regex.cap(1); + regex = TQRegExp("Chunk: (\\d+)"); + regex.search(msg); + TQString chunk = regex.cap(1); + + if(!messageId.isNull()) + { + bool valid = true; + // Retrieve the nmber of data chunks. + TQ_UINT32 numberOfChunks = chunks.toUInt(&valid); + if(valid && (numberOfChunks > 1)) + { + regex = TQRegExp("base64:([0-9a-zA-Z+/=]+)"); + regex.search(msg); + // Retrieve the first chunk of the ink format gif. + TQString base64 = regex.cap(1); + // More chunks are expected, buffer the chunk received. + InkMessage inkMessage; + inkMessage.chunks = numberOfChunks; + inkMessage.data += base64; + m_inkMessageBuffer.insert(messageId, inkMessage); + } + } + else + { + // There is only one chunk of data. + regex = TQRegExp("base64:([0-9a-zA-Z+/=]*)"); + regex.search(msg); + // Retrieve the base64 encoded ink data. + TQString data = regex.cap(1); + DispatchInkMessage(data); + } + + if(!messageId.isNull()) + { + if(m_inkMessageBuffer.contains(messageId)) + { + if(chunks.isNull()) + { + InkMessage inkMessage = m_inkMessageBuffer[messageId]; + inkMessage.data += msg.section("\r\n\r\n", -1); + if(inkMessage.chunks == chunk.toUInt() + 1) + { + DispatchInkMessage(inkMessage.data); + // Remove the ink message from the buffer. + m_inkMessageBuffer.remove(messageId); + } + } + } + } + } + else + { + kdDebug(14140) << k_funcinfo <<" Unknown type '" << type << "' message: \n"<< msg <<endl; + } +} + +void MSNSwitchBoardSocket::DispatchInkMessage(const TQString& base64String) +{ + TQByteArray image; + // Convert from base64 encoded string to byte array. + KCodecs::base64Decode(base64String.utf8() , image); + KTempFile *inkImage = new KTempFile(locateLocal( "tmp", "inkformatgif-" ), ".gif"); + inkImage->setAutoDelete(true); + inkImage->file()->writeBlock(image.data(), image.size()); + inkImage->file()->close(); + + slotEmoticonReceived(inkImage , "inkformatgif"); + inkImage = 0l; +} + +void MSNSwitchBoardSocket::sendTypingMsg( bool isTyping ) +{ + if( !isTyping ) + return; + + if ( onlineStatus() != Connected || m_chatMembers.empty()) + { + //we are not yet in a chat. + //if we send that command now, we may get disconnected. + return; + } + + + TQCString message = TQString( "MIME-Version: 1.0\r\n" + "Content-Type: text/x-msmsgscontrol\r\n" + "TypingUser: " + m_myHandle + "\r\n" + "\r\n" ).utf8(); + + // Length is appended by sendCommand() + TQString args = "U"; + sendCommand( "MSG", args, true, message ); +} + +// this Invites an Contact +void MSNSwitchBoardSocket::slotInviteContact(const TQString &handle) +{ + m_msgHandle=handle; + sendCommand( "CAL", handle ); +} +// +// Send a custum emoticon +// +int MSNSwitchBoardSocket::sendCustomEmoticon(const TQString &name, const TQString &filename) +{ + TQString picObj; + + //try to find it in the cache. + const TQMap<TQString, TQString> objectList = PeerDispatcher()->objectList; + for (TQMap<TQString,TQString>::ConstIterator it = objectList.begin(); it != objectList.end(); ++it ) + { + if(it.data() == filename) + { + picObj=it.key(); + break; + } + } + + if(picObj.isNull()) + { //if not found in the cache, generate the picture object + TQFileInfo fi(filename); + // open the icon file + TQFile pictFile(fi.filePath()); + if (pictFile.open(IO_ReadOnly)) { + + TQByteArray ar = pictFile.readAll(); + pictFile.close(); + + TQString sha1d = TQString(KCodecs::base64Encode(SHA1::hash(ar))); + TQString size = TQString::number( pictFile.size() ); + TQString all = "Creator" + m_account->accountId() + "Size" + size + "Type2Location" + fi.fileName() + "FriendlyAAA=SHA1D" + sha1d; + TQString sha1c = TQString(KCodecs::base64Encode(SHA1::hashString(all.utf8()))); + picObj = "<msnobj Creator=\"" + m_account->accountId() + "\" Size=\"" + size + "\" Type=\"2\" Location=\""+ fi.fileName() + "\" Friendly=\"AAA=\" SHA1D=\""+sha1d+ "\" SHA1C=\""+sha1c+"\"/>"; + + PeerDispatcher()->objectList.insert(picObj, filename); + } + else + return 0; + } + + TQString msg = "MIME-Version: 1.0\r\n" + "Content-Type: text/x-mms-emoticon\r\n" + "\r\n" + + name + "\t" + picObj + "\t\r\n"; + + return sendCommand("MSG", "A", true, msg.utf8()); + +} + +// this sends a short message to the server +int MSNSwitchBoardSocket::sendMsg( const Kopete::Message &msg ) +{ + if ( onlineStatus() != Connected || m_chatMembers.empty()) + { +// m_messagesQueue.append(msg); + return -1; + } + +#if 0 //this is to test webcam + if(msg.plainBody().contains("/webcam")) + { + PeerDispatcher()->startWebcam( m_myHandle , m_msgHandle); + return -3; + } +#endif + + TDEConfig *config = TDEGlobal::config(); + config->setGroup( "MSN" ); + if ( config->readBoolEntry( "exportEmoticons", false ) ) + { + TQMap<TQString, TQStringList> emap = Kopete::Emoticons::self()->emoticonAndPicList(); + + // Check the list for any custom emoticons + for (TQMap<TQString, TQStringList>::const_iterator itr = emap.begin(); itr != emap.end(); itr++) + { + for ( TQStringList::const_iterator itr2 = itr.data().constBegin(); itr2 != itr.data().constEnd(); ++itr2 ) + { + if ( msg.plainBody().contains( *itr2 ) ) + sendCustomEmoticon( *itr2, itr.key() ); + } + } + } + + if( msg.format() & Kopete::Message::RichText ) + { + TQRegExp regex("^\\s*<img src=\"([^>\"]+)\"[^>]*>\\s*$"); + if(regex.search(msg.escapedBody()) != -1) + { + // FIXME why are we sending the images.. the contact should request them. + PeerDispatcher()->sendImage(regex.cap(1), m_msgHandle); + return -3; + } + } + + // User-Agent is not a official flag, but PIDGIN has it + TQString UA; + if( config->readBoolEntry("SendClientInfo", true) ) + { + UA="User-Agent: Kopete/"+escape(kapp->aboutData()->version())+"\r\n"; + } + + TQString head = + "MIME-Version: 1.0\r\n" + "Content-Type: text/plain; charset=UTF-8\r\n" + +UA+ + "X-MMS-IM-Format: "; + + if(msg.font() != TQFont() ) + { + //It's verry strange that if the font name is bigger than 31 char, the _server_ close the socket and don't deliver the message. + // the real question is why ? my guess is that MS patched the server because a bug in their client, but that's just a guess. + // - Olivier 06-2005 + head += "FN=" + escape( msg.font().family().left(31)); + head += "; EF="; + if(msg.font().bold()) + head += "B"; + if(msg.font().italic()) + head += "I"; + if(msg.font().strikeOut()) + head += "S"; + if(msg.font().underline()) + head += "U"; + head += "; "; + } + else head+="FN=; EF=; "; + /* + * I don't know what to set by default, so i decided to set nothing. CF Bug 82734 + * (but don't forgeto to add an empty FN= and EF= , or webmessenger will break. (CF Bug 102371) ) + else head+="FN=MS%20Serif; EF=; "; + */ + + // Color support + if (msg.fg().isValid()) + { + TQString colorCode = TQColor(msg.fg().blue(),msg.fg().green(),msg.fg().red()).name().remove(0,1); //colors aren't sent in RGB but in BGR (O.G.) + head += "CO=" + colorCode; + } + else + { + head += "CO=0"; + } + + head += "; CS=0; PF=0"; + if (msg.plainBody().isRightToLeft()) + head += "; RL=1"; + head += "\r\n"; + + TQString message= msg.plainBody().replace( "\n" , "\r\n" ); + + //-- Check if the message isn't too big, TODO: do that at the libkopete level. + int len_H=head.utf8().length(); // != head.length() because i need the size in butes and + int len_M=message.utf8().length(); // some utf8 char may be longer than one byte + if( len_H+len_M >= 1660 ) //1664 is the maximum size of messages allowed by the server + { + //We will certenly split the message in several ones. + //It's possible to made the opposite client join them, as explained in this MS Word document + //http://www.bot-depot.com/forums/index.php?act=Attach&type=post&id=35110 + + head+="Message-ID: {7B7B34E6-7A8D-44FF-926C-1799156B58"+TQString::number( rand()%10)+TQString::number( rand()%10)+"}\r\n"; + int len_H=head.utf8().length()+ 14; //14 is the size of "Chunks: x" + //this is the size of each part of the message (excluding the header) + int futurmessages_size=1400; //1400 is a common good size + //int futurmessages_size=1664-len_H; + + int nb=(int)ceil((float)(len_M)/(float)(futurmessages_size)); + + if(KMessageBox::warningContinueCancel(0L /* FIXME: we should try to find a parent somewere*/ , + i18n("The message you are trying to send is too long; it will be split into %1 messages.").arg(nb) , + i18n("Message too big - MSN Plugin" ), KStdGuiItem::cont() , "SendLongMessages" ) + == KMessageBox::Continue ) + { + int place=0; + int result; + int chunk=0; + do + { + TQString m=message.mid(place, futurmessages_size); + place += futurmessages_size; + + //make sure the size is not too big because of utf8 + int d=m.utf8().length() + len_H -1664; + if( d > 0 ) + {//it contains some utf8 chars, so we strip the string a bit. + m=m.left( futurmessages_size - d ); + place -= d; + } + + //try to snip on space if possible + int len=m.length(); + d=0; + while(d<200 && !m[len-d].isSpace() ) + d++; + if(d<200) + { + m=m.left(len-d); + place -= d; + } + TQString chunk_str; + if(chunk==0) + chunk_str="Chunks: "+TQString::number(nb)+"\r\n"; + else if(chunk<nb) + chunk_str="Chunk: "+TQString::number(chunk)+"\r\n"; + else + { + kdDebug(14140) << k_funcinfo <<"The message is slit in more than initially estimated" <<endl; + } + result=sendCommand( "MSG", "A", true, (head+chunk_str+"\r\n"+m).utf8() ); + chunk++; + } + while(place < len_M) ; + + while(chunk<nb) + { + kdDebug(14140) << k_funcinfo <<"The message is plit in less than initially estimated. Sending empty message to complete" <<endl; + TQString chunk_str="Chunk: "+TQString::number(chunk); + sendCommand( "MSG", "A", true, (head+chunk_str+"\r\n").utf8() ); + chunk++; + } + return result; + } + return -2; //the message hasn't been sent. + } + + if(!m_keepAlive) + { + m_keepAliveNb=20; + m_keepAlive=new TQTimer(this); + TQObject::connect(m_keepAlive, TQT_SIGNAL(timeout()) , this , TQT_SLOT(slotKeepAliveTimer())); + m_keepAlive->start(50*1000); + } + + + return sendCommand( "MSG", "A", true, (head+"\r\n"+message).utf8() ); +} + +void MSNSwitchBoardSocket::slotSocketClosed( ) +{ + for( TQStringList::Iterator it = m_chatMembers.begin(); it != m_chatMembers.end(); ++it ) + { + emit userLeft( (*it), i18n("connection closed")); + } + + // we have lost the connection, send a message to chatwindow (this will not displayed) +// emit switchBoardIsActive(false); + emit switchBoardClosed( ); +} + +void MSNSwitchBoardSocket::slotCloseSession() +{ + sendCommand( "OUT", TQString(), false ); + disconnect(); +} + +// Check if we are connected. If so, then send the handshake. +void MSNSwitchBoardSocket::slotOnlineStatusChanged( MSNSocket::OnlineStatus status ) +{ + if (status == Connected) + { + TQCString command; + TQString args; + + if( !m_ID ) // we're inviting + { + command = "USR"; + args = m_myHandle + " " + m_auth; + } + else // we're invited + { + command = "ANS"; + args = m_myHandle + " " + m_auth + " " + m_ID; + } + sendCommand( command, args ); + + if(!m_keepAlive) + { + m_keepAliveNb=20; + m_keepAlive=new TQTimer(this); + TQObject::connect(m_keepAlive, TQT_SIGNAL(timeout()) , this , TQT_SLOT(slotKeepAliveTimer())); + m_keepAlive->start(50*1000); + } + } +} + +void MSNSwitchBoardSocket::userLeftChat(const TQString& handle , const TQString &reason) +{ + emit userLeft( handle, reason ); + + if( m_chatMembers.contains( handle ) ) + m_chatMembers.remove( handle ); + + if(m_chatMembers.isEmpty()) + disconnect(); +} + +void MSNSwitchBoardSocket::requestDisplayPicture() +{ + MSNContact *contact = static_cast<MSNContact*>(m_account->contacts()[m_msgHandle]); + if(!contact) return; + + PeerDispatcher()->requestDisplayIcon(m_msgHandle, contact->object()); +} + +void MSNSwitchBoardSocket::slotEmoticonReceived( KTempFile *file, const TQString &msnObj ) +{ + kdDebug(14141) << k_funcinfo << msnObj << endl; + + if(m_emoticons.contains(msnObj)) + { //it's an emoticon + m_emoticons[msnObj].second=file; + + if( m_recvIcons > 0 ) + m_recvIcons--; + kdDebug(14140) << k_funcinfo << "emoticons received queue is now: " << m_recvIcons << endl; + + if ( m_recvIcons <= 0 ) + cleanQueue(); + } + else if(msnObj == "inkformatgif") + { + TQString msg=i18n("<img src=\"%1\" alt=\"Typewrited message\" />" ).arg( file->name() ); + + kdDebug(14140) << k_funcinfo << file->name() <<endl; + + m_typewrited.append(file); + m_typewrited.setAutoDelete(true); + + TQPtrList<Kopete::Contact> others; + others.append( m_account->myself() ); + + TQStringList::iterator it2; + for( it2 = m_chatMembers.begin(); it2 != m_chatMembers.end(); ++it2 ) + { + if( *it2 != m_msgHandle ) + others.append( m_account->contacts()[ *it2 ] ); + } + + if(!m_account->contacts()[m_msgHandle]) + { + //this may happens if the contact has been deleted. + kdDebug(14140) << k_funcinfo <<"WARNING: contact is null, adding it" <<endl; + if( !m_chatMembers.contains( m_msgHandle ) ) + m_chatMembers.append( m_msgHandle ); + emit userJoined( m_msgHandle , m_msgHandle , false); + } + + Kopete::Message kmsg( m_account->contacts()[ m_msgHandle ], others, + msg, Kopete::Message::Inbound , Kopete::Message::RichText ); + + emit msgReceived( kmsg ); + } + else //if it is not an emoticon, + { // it's certenly the displaypicture. + MSNContact *c=static_cast<MSNContact*>(m_account->contacts()[m_msgHandle]); + if(c && c->object()==msnObj) + c->setDisplayPicture(file); + else + delete file; + } +} + +void MSNSwitchBoardSocket::slotIncomingFileTransfer(const TQString& from, const TQString& /*fileName*/, TQ_INT64 /*fileSize*/) +{ + TQPtrList<Kopete::Contact> others; + others.append( m_account->myself() ); + TQStringList::iterator it2; + for( it2 = m_chatMembers.begin(); it2 != m_chatMembers.end(); ++it2 ) + { + if( *it2 != m_msgHandle ) + others.append( m_account->contacts()[ *it2 ] ); + } + + if(!m_account->contacts()[m_msgHandle]) + { + //this may happens if the contact has been deleted. + kdDebug(14140) << k_funcinfo <<"WARNING: contact is null, adding it" <<endl; + if( !m_chatMembers.contains( m_msgHandle ) ) + m_chatMembers.append( m_msgHandle ); + emit userJoined( m_msgHandle , m_msgHandle , false); + } + TQString invite = "Incoming file transfer."; + Kopete::Message msg = + Kopete::Message(m_account->contacts()[from], others, invite, Kopete::Message::Internal, Kopete::Message::PlainText); + emit msgReceived(msg); +} + +void MSNSwitchBoardSocket::cleanQueue() +{ + if(m_emoticonTimer) + { + m_emoticonTimer->stop(); + m_emoticonTimer->deleteLater(); + m_emoticonTimer=0L; + } + kdDebug(14141) << k_funcinfo << m_msgQueue.count() << endl; + + TQValueList<const Kopete::Message>::Iterator it_msg; + for ( it_msg = m_msgQueue.begin(); it_msg != m_msgQueue.end(); ++it_msg ) + { + Kopete::Message kmsg = (*it_msg); + emit msgReceived( parseCustomEmoticons( kmsg ) ); + } + m_msgQueue.clear(); +} + +Kopete::Message &MSNSwitchBoardSocket::parseCustomEmoticons(Kopete::Message &kmsg) +{ + TQString message=kmsg.escapedBody(); + TQMap<TQString , TQPair<TQString , KTempFile*> >::Iterator it; + for ( it = m_emoticons.begin(); it != m_emoticons.end(); ++it ) + { + TQString es=TQStyleSheet::escape(it.data().first); + KTempFile *f=it.data().second; + if(message.contains(es) && f) + { + TQString imgPath = f->name(); + TQImage iconImage(imgPath); + /* We don't use a comple algoritm (like the one in the #if) because the msn client shows + * emoticons like that. So, in that case, we show like the MSN client */ + #if 0 + TQString em = TQRegExp::escape( es ); + message.replace( TQRegExp(TQString::fromLatin1( "(^|[\\W\\s]|%1)(%2)(?!\\w)" ).arg(em).arg(em)), + TQString::fromLatin1("\\1<img align=\"center\" width=\"") + + #endif + //match any occurence which is not in a html tag. + message.replace( TQRegExp(TQString::fromLatin1("%1(?![^><]*>)").arg(TQRegExp::escape(es))), + TQString::fromLatin1("<img align=\"center\" width=\"") + + TQString::number(iconImage.width()) + + TQString::fromLatin1("\" height=\"") + + TQString::number(iconImage.height()) + + TQString::fromLatin1("\" src=\"") + imgPath + + TQString::fromLatin1("\" title=\"") + es + + TQString::fromLatin1("\" alt=\"") + es + + TQString::fromLatin1( "\"/>" ) ); + kmsg.setBody(message, Kopete::Message::RichText); + } + } + return kmsg; +} + +int MSNSwitchBoardSocket::sendNudge() +{ + TQCString message = TQString( "MIME-Version: 1.0\r\n" + "Content-Type: text/x-msnmsgr-datacast\r\n" + "\r\n" + "ID: 1\r\n" + "\r\n\r\n" ).utf8(); + + TQString args = "U"; + return sendCommand( "MSG", args, true, message ); +} + + + +// FIXME: This is nasty... replace with a regexp or so. +TQString MSNSwitchBoardSocket::parseFontAttr(TQString str, TQString attr) +{ + TQString tmp; + int pos1=0, pos2=0; + + pos1 = str.find(attr + "="); + + if (pos1 == -1) + return ""; + + pos2 = str.find(";", pos1+3); + + if (pos2 == -1) + tmp = str.mid(pos1+3, str.length() - pos1 - 3); + else + tmp = str.mid(pos1+3, pos2 - pos1 - 3); + + return tmp; +} + +Dispatcher* MSNSwitchBoardSocket::PeerDispatcher() +{ + if(!m_dispatcher) + { + // Create a new msnslp dispatcher to handle + // all peer to peer requests. + TQStringList ip; + if(m_account->notifySocket()) + { + ip << m_account->notifySocket()->localIP(); + if(m_account->notifySocket()->localIP() != m_account->notifySocket()->getLocalIP()) + ip << m_account->notifySocket()->getLocalIP(); + } + m_dispatcher = new Dispatcher(this, m_account->accountId(),ip ); + +// TQObject::connect(this, TQT_SIGNAL(blockRead(const TQByteArray&)), m_dispatcher, TQT_SLOT(slotReadMessage(const TQByteArray&))); +// TQObject::connect(m_dispatcher, TQT_SIGNAL(sendCommand(const TQString&, const TQString&, bool, const TQByteArray&, bool)), this, TQT_SLOT(sendCommand(const TQString&, const TQString&, bool, const TQByteArray&, bool))); + TQObject::connect(m_dispatcher, TQT_SIGNAL(incomingTransfer(const TQString&, const TQString&, TQ_INT64)), this, TQT_SLOT(slotIncomingFileTransfer(const TQString&, const TQString&, TQ_INT64))); + TQObject::connect(m_dispatcher, TQT_SIGNAL(displayIconReceived(KTempFile *, const TQString&)), this, TQT_SLOT(slotEmoticonReceived( KTempFile *, const TQString&))); + TQObject::connect(this, TQT_SIGNAL(msgAcknowledgement(unsigned int, bool)), m_dispatcher, TQT_SLOT(messageAcknowledged(unsigned int, bool))); + m_dispatcher->m_pictureUrl = m_account->pictureUrl(); + } + return m_dispatcher; +} + +void MSNSwitchBoardSocket::slotKeepAliveTimer( ) +{ + /* + This is a workaround against the bug 113425 + The problem: the P2P::Webcam class is parent of us, and when we get deleted, it get deleted. + the correct solution would be to change that. + The second problem: after one minute of inactivity, the official client close the chat socket. + the workaround: we simulate the activity by sending small packet each 50 seconds + the nice side effect: the "xxx has closed the chat" is now meaningfull + the bad side effect: some switchboard connection may be maintained for really long time! + */ + + if ( onlineStatus() != Connected || m_chatMembers.empty()) + { + //we are not yet in a chat. + //if we send that command now, we may get disconnected. + return; + } + + + TQCString message = TQString( "MIME-Version: 1.0\r\n" + "Content-Type: text/x-keepalive\r\n" + "\r\n" ).utf8(); + + // Length is appended by sendCommand() + TQString args = "U"; + sendCommand( "MSG", args, true, message ); + + m_keepAliveNb--; + if(m_keepAliveNb <= 0) + { + m_keepAlive->deleteLater(); + m_keepAlive=0L; + } +} + +#include "msnswitchboardsocket.moc" + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/msn/msnswitchboardsocket.h b/kopete/protocols/msn/msnswitchboardsocket.h new file mode 100644 index 00000000..e47ebdfe --- /dev/null +++ b/kopete/protocols/msn/msnswitchboardsocket.h @@ -0,0 +1,167 @@ +/* + msnswitchboardsocket.h - switch board connection socket + + Copyright (c) 2002 by Martijn Klingens <klingens@kde.org> + Copyright (c) 2002-2006 by Olivier Goffart <ogoffart@ kde.org> + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + Portions of this code are taken from KMerlin, + (c) 2001 by Olaf Lueg <olueg@olsd.de> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef MSNSWITCHBOARDSOCKET_H +#define MSNSWITCHBOARDSOCKET_H + +#include <tqobject.h> +#include <tqstrlist.h> +#include <tqvaluevector.h> + +#include <kstringhandler.h> + +#include "msnsocket.h" + +namespace Kopete { class Message; } +class MSNAccount; +class TQTimer; + +class MSNP2PDisplatcher; +class KTempFile; + +namespace P2P { class Dispatcher; } + +#include "dispatcher.h" + +class KOPETE_EXPORT MSNSwitchBoardSocket : public MSNSocket +{ + Q_OBJECT + + +public: + /** + * Contructor: id is the KopeteMessageMangager's id + */ + MSNSwitchBoardSocket( MSNAccount * account , TQObject *parent); + ~MSNSwitchBoardSocket(); + +private: + P2P::Dispatcher *m_dispatcher; + MSNAccount *m_account; + + TQString m_myHandle; // our handle + + // contains the handle of the last person that msg'ed us. + // since we receive the actual message by readBlock(), we need + // to remember what the handle was of the person sending us the message. + TQString m_msgHandle; + + TQString m_ID; + TQString m_auth; + TQStringList m_chatMembers; + + //used for emoticons + TQValueList<const Kopete::Message> m_msgQueue; + unsigned m_recvIcons; + TQMap<TQString , TQPair<TQString , KTempFile*> > m_emoticons; + Kopete::Message &parseCustomEmoticons(Kopete::Message &msg); + TQTimer *m_emoticonTimer; + TQPtrList<KTempFile> m_typewrited; + + struct InkMessage{ + TQ_UINT32 chunks; + TQString data; + }; + TQMap<TQString, InkMessage> m_inkMessageBuffer; + + /** the number of chunk for currents messages */ + unsigned int m_chunks; + + /** true is we already sent the x-clientcaps message */ + bool m_clientcapsSent; + +private: + void DispatchInkMessage(const TQString &base64String); + +protected: + /** + * Handle an MSN command response line. + */ + virtual void parseCommand( const TQString &cmd, uint id, + const TQString &data ); + + /** + * Handle exceptions that might occur during a chat. + */ + virtual void handleError( uint code, uint id ); + + TQString parseFontAttr( TQString str, TQString attr ); + + +public: + void connectToSwitchBoard( TQString ID, TQString address, TQString auth ); + void setHandle( TQString handle ) { m_myHandle = handle; } + void setMsgHandle( TQString handle ) { m_msgHandle = handle; } + + const TQStringList &chatMembers() { return m_chatMembers; } + + void userLeftChat( const TQString &handle , const TQString &reason ); + int sendMsg( const Kopete::Message &msg ); + int sendCustomEmoticon(const TQString &name, const TQString &filename); + + int sendNudge(); + + P2P::Dispatcher* PeerDispatcher(); + +public slots: + void slotCloseSession(); + void slotInviteContact(const TQString &handle); + + /** + * Notify the server that the user is typing a message + */ + void sendTypingMsg( bool isTyping ); + + void requestDisplayPicture(); + + /** workaround Bug 113425 . see slotKeepAliveTimer() **/ + TQTimer *m_keepAlive; + int m_keepAliveNb; + + + +private slots: + void slotOnlineStatusChanged( MSNSocket::OnlineStatus status ); + void slotSocketClosed( ); + void slotReadMessage( const TQByteArray &bytes ); + void slotEmoticonReceived( KTempFile *, const TQString& ); + void slotIncomingFileTransfer(const TQString& from, const TQString& fileName, TQ_INT64 fileSize); + void cleanQueue(); + + /** workaround Bug 113425 . see comment inside the function **/ + void slotKeepAliveTimer(); + +signals: + void msgReceived( Kopete::Message &msg ); + void receivedTypingMsg( const TQString &contactId, bool isTyping ); + void msgAcknowledgement(unsigned int, bool); + void userJoined(const TQString& handle , const TQString &publicName , bool IRO); + void userLeft(const TQString& handle , const TQString &reason); + void nudgeReceived(const TQString &handle); + + void switchBoardClosed( ); + void invitation(const TQString& handle, const TQString& msg); + +}; + +#endif + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/msn/outgoingtransfer.cpp b/kopete/protocols/msn/outgoingtransfer.cpp new file mode 100644 index 00000000..5d66c495 --- /dev/null +++ b/kopete/protocols/msn/outgoingtransfer.cpp @@ -0,0 +1,432 @@ +/* + outgoingtransfer.cpp - msn p2p protocol + + Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org> + Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include "outgoingtransfer.h" + +#include <stdlib.h> + +// Kde includes +#include <kbufferedsocket.h> +#include <kdebug.h> +#include <tdelocale.h> +#include <kmdcodec.h> +using namespace KNetwork; + +// TQt includes +#include <tqfile.h> +#include <tqregexp.h> +#include <tqtimer.h> + +// Kopete includes +#include <kopetetransfermanager.h> + +#include <netinet/in.h> // For htonl +using P2P::TransferContext; +using P2P::Dispatcher; +using P2P::OutgoingTransfer; +using P2P::Message; + +OutgoingTransfer::OutgoingTransfer(const TQString& to, P2P::Dispatcher *dispatcher, TQ_UINT32 sessionId) +: TransferContext(to,dispatcher,sessionId) +{ + m_direction = Outgoing; + m_handshake = 0x01; +} + +OutgoingTransfer::~OutgoingTransfer() +{ + kdDebug(14140) << k_funcinfo << endl; +} + +void OutgoingTransfer::sendImage(const TQByteArray& image) +{ + +// TODO TQByteArray base64 = KCodecs::base64Encode(image); +// +// TQCString body = "MIME-Version: 1.0\r\n" +// "Content-Type: image/gif\r\n" +// "\r\n" +// "base64:" + +// +// Message outbound; +// outbound.header.sessionId = m_sessionId; +// outbound.header.identifier = m_baseIdentifier; +// outbound.header.dataOffset = 0; +// outbound.header.totalDataSize = 4; +// outbound.header.dataSize = 4; +// outbound.header.flag = 0; +// outbound.header.ackSessionIdentifier = rand()%0x8FFFFFF0 + 4; +// outbound.header.ackUniqueIdentifier = 0; +// outbound.header.ackDataSize = 0l; +// TQByteArray bytes(4); +// bytes.fill('\0'); +// outbound.body = bytes; +// outbound.applicationIdentifier = 0; +// outbound.attachApplicationId = false; +// outbound.destination = m_recipient; +// +// sendMessage(outbound, body); +} + +void OutgoingTransfer::slotSendData() +{ + TQ_INT32 bytesRead = 0; + TQByteArray buffer(1202); + if(!m_file) + return; + + // Read a chunk from the source file. + bytesRead = m_file->readBlock(buffer.data(), buffer.size()); + + if (bytesRead < 0) { + m_file->close(); + // ### error handling + } + else { + + if(bytesRead < 1202){ + buffer.resize(bytesRead); + } + + kdDebug(14140) << k_funcinfo << TQString("Sending, %1 bytes").arg(bytesRead) << endl; + + if((m_offset + bytesRead) < m_file->size()) + { + sendData(buffer); + m_offset += bytesRead; + } + else + { + m_isComplete = true; + // Send the last chunk of the file. + sendData(buffer); + m_offset += buffer.size(); + // Close the file. + m_file->close(); + } + } + + if(m_transfer){ + m_transfer->slotProcessed(m_offset); + if(m_isComplete){ + // The transfer is complete. + m_transfer->slotComplete(); + } + } +} + +void OutgoingTransfer::acknowledged() +{ + kdDebug(14140) << k_funcinfo << endl; + + switch(m_state) + { + case Invitation: + { + if(m_type == UserDisplayIcon) + { + m_state = Negotiation; + // Send data preparation message. + sendDataPreparation(); + } + break; + } + + case Negotiation: + { + if(m_type == UserDisplayIcon) + { + // <<< Data preparation acknowledge message. + m_state = DataTransfer; + m_identifier++; + // Start sending data. + slotSendData(); + } + break; + } + + case DataTransfer: + // NOTE <<< Data acknowledged message. + // <<< Bye message should follow. + if(m_type == File) + { + if(m_handshake == 0x01) + { + // Data handshake acknowledge message. + // Start sending data. + slotSendData(); + } + else if(m_handshake == 0x02) + { + // Data acknowledge message. + // Send the recipient a BYE message. + m_state = Finished; + sendMessage(BYE, "\r\n"); + } + } + + break; + + case Finished: + if(m_type == File) + { + // BYE acknowledge message. + m_dispatcher->detach(this); + } + + break; + } +} + +void OutgoingTransfer::processMessage(const Message& message) +{ + TQString body = + TQCString(message.body.data(), message.header.dataSize); + kdDebug(14140) << k_funcinfo << "received, " << body << endl; + + if(body.startsWith("BYE")) + { + m_state = Finished; + // Send the recipient an acknowledge message. + acknowledge(message); + if(!m_isComplete) + { + // The peer cancelled the transfer. + if(m_transfer) + { + // Inform the user of the file transfer cancelation. + m_transfer->slotError(TDEIO::ERR_ABORTED, i18n("File transfer canceled.")); + } + } + // Dispose of this transfer context. + m_dispatcher->detach(this); + } + else if(body.startsWith("MSNSLP/1.0 200 OK")) + { + // Retrieve the message content type. + TQRegExp regex("Content-Type: ([A-Za-z0-9$!*/\\-]*)"); + regex.search(body); + TQString contentType = regex.cap(1); + + if(contentType == "application/x-msnmsgr-sessionreqbody") + { + // Recipient has accepted the file transfer. + // Acknowledge the recipient. + acknowledge(message); + + // Try to open the file for reading. + // If an error occurs, send an internal + // error message to the recipient. + if(!m_file->open(IO_ReadOnly)){ + error(); + return; + } + + // Retrieve the receiving client's contact. + Kopete::Contact *contact = m_dispatcher->getContactByAccountId(m_recipient); + if(contact == 0l) + { + error(); + return; + } + + m_transfer = + Kopete::TransferManager::transferManager()->addTransfer(contact, m_file->name(), m_file->size(), m_recipient, Kopete::FileTransferInfo::Outgoing); + + TQObject::connect(m_transfer , TQT_SIGNAL(transferCanceled()), this, TQT_SLOT(abort())); + + m_state = Negotiation; + + m_branch = P2P::Uid::createUid(); + + // Send the direct connection invitation message. + TQString content = "Bridges: TRUDPv1 TCPv1\r\n" + + TQString("NetID: %1\r\n").arg("-123657987") + + TQString("Conn-Type: %1\r\n").arg("Restrict-NAT") + + "UPnPNat: false\r\n" + "ICF: false\r\n" + + TQString("Hashed-Nonce: {%1}\r\n").arg(P2P::Uid::createUid()) + + "\r\n"; + sendMessage(INVITE, content); + } + else if(contentType == "application/x-msnmsgr-transrespbody") + { + // Determine whether the recipient created + // a listening endpoint. + regex = TQRegExp("Listening: ([^\r\n]+)\r\n"); + regex.search(body); + bool isListening = (regex.cap(1) == "true"); + + // Send the recipient an acknowledge message. + acknowledge(message); + + m_state = DataTransfer; + +#if 1 + isListening = false; // TODO complete direct connection. +#endif + if(isListening) + { + // Retrieve the hashed nonce for this direct connection instance. + regex = TQRegExp("Hashed-Nonce: \\{([0-9A-F\\-]*)\\}\r\n"); + regex.search(body); + m_nonce = regex.cap(1); + // Retrieve the listening endpoints of the receiving client. + regex = TQRegExp("IPv4Internal-Addrs: ([^\r\n]+)\r\n"); + regex.search(body); + m_peerEndpoints = TQStringList::split(" ", regex.cap(1)); + m_endpointIterator = m_peerEndpoints.begin(); + // Retrieve the listening port of the receiving client. + regex = TQRegExp("IPv4Internal-Port: ([^\r\n]+)\r\n"); + regex.search(body); + m_remotePort = regex.cap(1); + + // Try to connect to the receiving client's + // listening endpoint. + connectToEndpoint(*m_endpointIterator); + } + else + { + m_handshake = 0x02; + // Otherwise, send data through the already + // existing session. + slotSendData(); + } + } + } + else if(body.startsWith("MSNSLP/1.0 603 Decline")) + { + // File transfer has been cancelled remotely. + // Send an acknowledge message + acknowledge(message); + if(m_transfer) + { + // Inform the user of the file transfer cancelation. + m_transfer->slotError(TDEIO::ERR_ABORTED, i18n("File transfer canceled.")); + } + + if(m_file && m_file->isOpen()){ + // Close the file. + m_file->close(); + } + m_dispatcher->detach(this); + } +} + +void OutgoingTransfer::readyToSend() +{ + if(m_isComplete){ + // Ignore, do nothing. + return; + } + + slotSendData(); +} + +void OutgoingTransfer::connectToEndpoint(const TQString& hostName) +{ + m_socket = new TDEBufferedSocket(hostName, m_remotePort); + m_socket->setBlocking(false); + m_socket->enableRead(true); + // Disable write signal for now. Only enable + // when we are ready to sent data. + // NOTE readyWrite consumes too much cpu usage. + m_socket->enableWrite(false); + TQObject::connect(m_socket, TQT_SIGNAL(readyRead()), this, TQT_SLOT(slotRead())); + TQObject::connect(m_socket, TQT_SIGNAL(connected(const KResolverEntry&)), this, TQT_SLOT(slotConnected())); + TQObject::connect(m_socket, TQT_SIGNAL(gotError(int)), this, TQT_SLOT(slotSocketError(int))); + TQObject::connect(m_socket, TQT_SIGNAL(closed()), this, TQT_SLOT(slotSocketClosed())); + // Try to connect to the endpoint. + m_socket->connect(); +} + +void OutgoingTransfer::slotConnected() +{ + kdDebug(14140) << k_funcinfo << endl; + // Check if connection is ok. + TQ_UINT32 bytesWritten = m_socket->writeBlock(TQCString("foo").data(), 4); + if(bytesWritten != 4) + { + // Not all data was written, close the socket. + m_socket->closeNow(); + // Schedule the data to be sent through the existing session. + TQTimer::singleShot(2000, this, TQT_SLOT(slotSendData())); + return; + } + + // Send data handshake message. + P2P::Message handshake; + handshake.header.sessionId = 0; + handshake.header.identifier = ++m_identifier; + handshake.header.dataOffset = 0l; + handshake.header.totalDataSize = 0l; + handshake.header.dataSize = 0; + // Set the flag to indicate that this is + // a direct connection handshake message. + handshake.header.flag = 0x100; + TQString nonce = m_nonce.remove('-'); + handshake.header.ackSessionIdentifier = nonce.mid(0, 8).toUInt(0, 16); + handshake.header.ackUniqueIdentifier = + nonce.mid(8, 4).toUInt(0, 16) | (nonce.mid(12, 4).toUInt(0, 16) << 16); + const TQ_UINT32 lo = nonce.mid(16, 8).toUInt(0, 16); + const TQ_UINT32 hi = nonce.mid(24, 8).toUInt(0, 16); + handshake.header.ackDataSize = + ((TQ_INT64)htonl(lo)) | (((TQ_INT64)htonl(hi)) << 32); + + TQByteArray stream; + // Write the message to the memory stream. + m_messageFormatter.writeMessage(handshake, stream, true); + // Send the byte stream over the wire. + m_socket->writeBlock(stream.data(), stream.size()); +} + +void OutgoingTransfer::slotRead() +{ + TQ_INT32 bytesAvailable = m_socket->bytesAvailable(); + kdDebug(14140) << k_funcinfo << bytesAvailable << ", bytes available." << endl; +} + +void OutgoingTransfer::slotSocketError(int) +{ + kdDebug(14140) << k_funcinfo << m_socket->TDESocketBase::errorString() << endl; + // If an error has occurred, try to connect + // to another available peer endpoint. + // If there are no more available endpoints, + // send the data through the session. + m_socket->closeNow(); + + // Move to the next available endpoint. + m_endpointIterator++; + if(m_endpointIterator != m_peerEndpoints.end()){ + // Try to connect to the endpoint. + connectToEndpoint(*m_endpointIterator); + } + else + { + // Otherwise, send the data through the session. + m_identifier -= 1; + TQTimer::singleShot(2000, this, TQT_SLOT(slotSendData())); + } +} + +void OutgoingTransfer::slotSocketClosed() +{ + kdDebug(14140) << k_funcinfo << endl; + m_socket->deleteLater(); + m_socket = 0l; +} + +#include "outgoingtransfer.moc" diff --git a/kopete/protocols/msn/outgoingtransfer.h b/kopete/protocols/msn/outgoingtransfer.h new file mode 100644 index 00000000..0ec5846c --- /dev/null +++ b/kopete/protocols/msn/outgoingtransfer.h @@ -0,0 +1,60 @@ +/* + outgoingtransfer.h - msn p2p protocol + + Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org> + Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef OUTGOINGTRANSFER_H +#define OUTGOINGTRANSFER_H + +#include "p2p.h" +#include "dispatcher.h" +#include <tqstringlist.h> + +/** +@author Kopete Developers +*/ +namespace P2P{ + class OutgoingTransfer : public TransferContext + { Q_OBJECT + + public: + OutgoingTransfer(const TQString& to, P2P::Dispatcher *dispatcher, TQ_UINT32 sessionId); + virtual ~OutgoingTransfer(); + + void sendImage(const TQByteArray& image); + + private slots: + void slotConnected(); + void slotRead(); + void slotSendData(); + void slotSocketError(int); + void slotSocketClosed(); + + private: + virtual void acknowledged(); + void connectToEndpoint(const TQString& hostName); + virtual void processMessage(const Message& message); + + TQStringList m_peerEndpoints; + TQStringList::Iterator m_endpointIterator; + TQString m_remotePort; + TQString m_nonce; + char m_handshake; + + protected: + virtual void readyToSend(); + }; +} + +#endif diff --git a/kopete/protocols/msn/p2p.cpp b/kopete/protocols/msn/p2p.cpp new file mode 100644 index 00000000..11f308fb --- /dev/null +++ b/kopete/protocols/msn/p2p.cpp @@ -0,0 +1,412 @@ +/* + p2p.cpp - msn p2p protocol + + Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org> + Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include "p2p.h" +#include "dispatcher.h" +using P2P::TransferContext; +using P2P::Message; +using P2P::MessageType; +using P2P::TransferType; + +#include <stdlib.h> + +// Kde includes +#include <kbufferedsocket.h> +#include <kdebug.h> +// TQt includes +#include <tqfile.h> + +// Kopete includes +#include <kopetetransfermanager.h> + +TQString P2P::Uid::createUid() +{ + return (TQString::number((unsigned long int)rand()%0xAAFF+0x1111, 16) + + TQString::number((unsigned long int)rand()%0xAAFF+0x1111, 16) + "-" + + TQString::number((unsigned long int)rand()%0xAAFF+0x1111, 16) + "-" + + TQString::number((unsigned long int)rand()%0xAAFF+0x1111, 16) + "-" + + TQString::number(rand()%0xAAFF+0x1111, 16) + "-" + + TQString::number((unsigned long int)rand()%0xAAFF+0x1111, 16) + + TQString::number((unsigned long int)rand()%0xAAFF+0x1111, 16) + + TQString::number((unsigned long int)rand()%0xAAFF+0x1111, 16)).upper(); +} + +TransferContext::TransferContext(const TQString &contact, P2P::Dispatcher *dispatcher, TQ_UINT32 sessionId) + : TQObject(dispatcher) , + m_sessionId(sessionId) , + m_identifier(0) , + m_file(0) , + m_transactionId (0), + m_ackSessionIdentifier (0) , + m_ackUniqueIdentifier ( 0 ), + m_transfer ( 0l) , + + m_baseIdentifier(rand()%0x0FFFFFF0 + 4), + m_dispatcher (dispatcher), + m_isComplete (false) , + m_offset(0), + m_totalDataSize(0), + m_recipient(contact), + m_sender(dispatcher->localContact()), + m_socket(0), + m_state ( Invitation) +{ + m_type = File ; //uh, why??? -Olivier +} + +TransferContext::~TransferContext() +{ + m_transfer = 0l; + + if(m_file){ + delete m_file; + m_file = 0l; + } +} + +void TransferContext::acknowledge(const Message& message) +{ + kdDebug(14140) << k_funcinfo << m_dispatcher<< endl; + + Message outbound; + outbound.header.sessionId = message.header.sessionId; + + if(m_identifier == 0){ + m_identifier = m_baseIdentifier; + } +// else if(m_state == Finished && m_direction == Incoming){ +// m_identifier = m_baseIdentifier - 1; +// } + else if(m_state == Finished && m_direction == Outgoing){ + m_identifier = m_baseIdentifier + 1; + } + else + ++m_identifier; + + outbound.header.identifier = m_identifier; + outbound.header.dataOffset = 0l; + outbound.header.totalDataSize = message.header.totalDataSize; + outbound.header.dataSize = 0; +// if(m_type == UserDisplayIcon && m_state == Finished){ +// if(m_direction == Outgoing){ +// outbound.header.flag = 0x40; +// } +// } +// else + outbound.header.flag = 2; + + outbound.header.ackSessionIdentifier = message.header.identifier; + outbound.header.ackUniqueIdentifier = message.header.ackSessionIdentifier; + outbound.header.ackDataSize = message.header.totalDataSize; + // NOTE outbound.body is null by default + outbound.applicationIdentifier = 0l; + outbound.destination = m_recipient; + + TQByteArray stream; + // Write the acknowledge message to the stream. + m_messageFormatter.writeMessage(outbound, stream, (m_socket != 0l)); + if(!m_socket) + { + // Send the acknowledge message. + m_dispatcher->callbackChannel()->send(stream); + } + else + { + // Send acknowledge message directly. + m_socket->writeBlock(stream.data(), stream.size()); + } +} + +void TransferContext::error() +{ + kdDebug(14140) << k_funcinfo << endl; + sendMessage(ERROR); + m_dispatcher->detach(this); +} + +void TransferContext::sendData(const TQByteArray& bytes) +{ + Message outbound; + outbound.header.sessionId = m_sessionId; + outbound.header.identifier = m_identifier; + outbound.header.dataOffset = m_offset; + if(m_file){ + outbound.header.totalDataSize = m_file->size(); + } + else + outbound.header.totalDataSize = m_totalDataSize; + + outbound.header.dataSize = bytes.size(); + if(m_type == UserDisplayIcon){ + outbound.header.flag = 0x20; + } + else if(m_type == P2P::File){ + outbound.header.flag = 0x01000030; + } + else outbound.header.flag = 0; + + outbound.header.ackSessionIdentifier = rand()%0x8FFFFFF0 + 4; + outbound.header.ackUniqueIdentifier = 0; + outbound.header.ackDataSize = 0l; + outbound.body = bytes; + outbound.applicationIdentifier = (uint)m_type; + + outbound.destination = m_recipient; + + TQByteArray stream; + m_messageFormatter.writeMessage(outbound, stream, (m_socket != 0l)); + if(!m_socket) + { + // Send the data message. + m_transactionId = m_dispatcher->callbackChannel()->send(stream); + } + else + { + // Send data directly. + m_socket->writeBlock(stream.data(), stream.size()); + } +} + +void TransferContext::sendDataPreparation() +{ + kdDebug(14140) << k_funcinfo << endl; + + Message outbound; + outbound.header.sessionId = m_sessionId; + outbound.header.identifier = ++m_identifier; + outbound.header.dataOffset = 0; + outbound.header.totalDataSize = 4; + outbound.header.dataSize = 4; + outbound.header.flag = 0; + outbound.header.ackSessionIdentifier = rand()%0x8FFFFFF0 + 4; + outbound.header.ackUniqueIdentifier = 0; + outbound.header.ackDataSize = 0l; + TQByteArray bytes(4); + bytes.fill('\0'); + outbound.body = bytes; + outbound.applicationIdentifier = 1; + outbound.destination = m_recipient; + + TQByteArray stream; + m_messageFormatter.writeMessage(outbound, stream); + // Send the receiving client the data prepartion message. + m_dispatcher->callbackChannel()->send(stream); +} + +void TransferContext::sendMessage(MessageType type, const TQString& content, TQ_INT32 flag, TQ_INT32 appId) +{ + Message outbound; + if(appId != 0){ + outbound.header.sessionId = m_sessionId; + } + else + outbound.header.sessionId = 0; + + if(m_identifier == 0){ + m_identifier = m_baseIdentifier; + } + else if(m_state == Invitation && m_direction == P2P::Outgoing && m_type == UserDisplayIcon) + { + m_identifier -= 3; + } + else if(m_state == Invitation && m_direction == P2P::Incoming && m_type == File) + { + m_identifier -= 3; + } + else + ++m_identifier; + + outbound.header.identifier = m_identifier; + outbound.header.flag = flag; + outbound.header.ackSessionIdentifier = m_ackSessionIdentifier; + outbound.header.ackUniqueIdentifier = m_ackUniqueIdentifier; + outbound.header.ackDataSize = 0l; + outbound.applicationIdentifier = appId; + outbound.destination = m_recipient; + + TQString contentType, cSeq, method; + + switch(m_state) + { + case DataTransfer: + contentType = "application/x-msnmsgr-transreqbody"; + if(m_type == File && m_direction == Incoming) + { + contentType = "application/x-msnmsgr-transrespbody"; + } + break; + case Finished: + contentType = "application/x-msnmsgr-sessionclosebody"; + break; + default: + contentType = "application/x-msnmsgr-sessionreqbody"; + if(m_type == File && m_direction == Outgoing) + { + if(m_state == Negotiation){ + contentType = "application/x-msnmsgr-transreqbody"; + } + } + if(m_type == P2P::WebcamType && type==P2P::INVITE && m_state == Negotiation) + { + contentType = "application/x-msnmsgr-transreqbody"; + } + break; + } + + switch(type) + { + case BYE: + method = "BYE MSNMSGR:" + m_recipient + " MSNSLP/1.0"; + cSeq = "0"; + break; + + case DECLINE: + method = "MSNSLP/1.0 603 DECLINE"; + cSeq = "1"; + break; + + case ERROR: + contentType = "null"; + method = "MSNSLP/1.0 500 Internal Error"; + cSeq = "1"; + break; + + case INVITE: + method = "INVITE MSNMSGR:" + m_recipient + " MSNSLP/1.0"; + cSeq = "0"; + break; + + case OK: + method = "MSNSLP/1.0 200 OK"; + cSeq = "1"; + break; + } + + TQCString body = TQString(method + "\r\n" + "To: <msnmsgr:" + m_recipient + ">\r\n" + "From: <msnmsgr:" + m_sender + ">\r\n" + "Via: MSNSLP/1.0/TLP ;branch={" + m_branch.upper() + "}\r\n" + "CSeq: "+ cSeq +"\r\n" + "Call-ID: {" + m_callId.upper() + "}\r\n" + "Max-Forwards: 0\r\n" + "Content-Type: " + contentType + "\r\n" + "Content-Length: "+ TQString::number(content.length() + 1) + "\r\n" + "\r\n" + + content).utf8(); + + // NOTE The body must have a null character at the end. + // TQCString by chance automatically adds a \0 to the + // end of the string. + + outbound.header.totalDataSize = body.size(); + // Send the outbound message. + sendMessage(outbound, body); +} + +void TransferContext::sendMessage(Message& outbound, const TQByteArray& body) +{ + TQ_INT64 offset = 0L, bytesLeft = outbound.header.totalDataSize; + TQ_INT16 chunkLength = 1202; + + // Split the outbound message if necessary. + while(bytesLeft > 0L) + { + if(bytesLeft < chunkLength) + { + // Copy the last chunk of the multipart message. + outbound.body.duplicate(body.data() + offset, bytesLeft); + outbound.header.dataSize = bytesLeft; + outbound.header.dataOffset = offset; + bytesLeft = 0L; + } + else + { + // Copy the next chunk of the multipart message in the sequence. + outbound.body.duplicate(body.data() + offset, chunkLength); + outbound.header.dataSize = chunkLength; + outbound.header.dataOffset = offset; + offset += chunkLength; + bytesLeft -= offset; + } + + kdDebug(14140) << k_funcinfo << + TQCString(outbound.body.data(), outbound.body.size()) + << endl; + + TQByteArray stream; + // Write the outbound message to the stream. + m_messageFormatter.writeMessage(outbound, stream, (m_socket != 0l)); + if(!m_socket) + { + // Send the outbound message. + m_dispatcher->callbackChannel()->send(stream); + } + else + { + // Send outbound message directly. + m_socket->writeBlock(stream.data(), stream.size()); + } + } +} + +void TransferContext::setType(TransferType type) +{ + m_type = type; +} + +void TransferContext::abort() +{ + kdDebug(14140) << k_funcinfo << endl; + if(m_transfer) + { + if(m_transfer->error() == TDEIO::ERR_ABORTED) + { + switch(m_direction) + { + case P2P::Outgoing: + if(m_type == File) + { + // Do nothing. + } + break; + + case P2P::Incoming: + if(m_type == File) + { + // Do nothing. + } + break; + } + } + else + { + m_state = Finished; + sendMessage(BYE, "\r\n"); + } + } +} + +void TransferContext::readyWrite() +{ + if(m_direction == Outgoing && m_state == DataTransfer){ + readyToSend(); + } +} + +void TransferContext::readyToSend() +{} + +#include "p2p.moc" diff --git a/kopete/protocols/msn/p2p.h b/kopete/protocols/msn/p2p.h new file mode 100644 index 00000000..55a7866a --- /dev/null +++ b/kopete/protocols/msn/p2p.h @@ -0,0 +1,148 @@ +/* + p2p.h - msn p2p protocol + + Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org> + Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef P2P_H +#define P2P_H + +// TQt includes +#include <tqobject.h> +#include "messageformatter.h" + +#include "kopete_export.h" + +#include "config.h" + +namespace Kopete { class Transfer; } +namespace Kopete { struct FileTransferInfo; } +namespace P2P { class Dispatcher; } +namespace KNetwork { class TDEBufferedSocket; } +class TQFile; +class KTempFile; + +/** +@author Kopete Developers +*/ +namespace System{ + class Guid + { + public: + ~Guid(){} + static Guid newGuid(); + TQString toString(); + + private: + Guid(){} + }; +} + +namespace P2P{ + + enum TransferType { UserDisplayIcon = 1, File = 2, WebcamType=4}; + enum TransferDirection { Incoming = 1, Outgoing = 8}; + enum MessageType { BYE, OK, DECLINE, ERROR, INVITE }; + + enum CommunicationState + { + Invitation = 1, + Negotiation = 2, + DataTransfer = 8, + Finished = 16 + }; + + struct TransportHeader + { + TQ_UINT32 sessionId; + TQ_UINT32 identifier; + TQ_INT64 dataOffset; + TQ_INT64 totalDataSize; + TQ_UINT32 dataSize; + TQ_UINT32 flag; + TQ_UINT32 ackSessionIdentifier; + TQ_UINT32 ackUniqueIdentifier; + TQ_INT64 ackDataSize; + }; + + struct Message + { + public: + TQString mimeVersion; + TQString contentType; + TQString destination; + TQString source; + TransportHeader header; + TQByteArray body; + TQ_INT32 applicationIdentifier; + bool attachApplicationIdentifier; + }; + + class KOPETE_EXPORT Uid + { + public: static TQString createUid(); + }; + + class KOPETE_EXPORT TransferContext : public TQObject + { Q_OBJECT + + public: + virtual ~TransferContext(); + + void acknowledge(const Message& message); + virtual void acknowledged() = 0; + void error(); + virtual void processMessage(const P2P::Message& message) = 0; + void sendDataPreparation(); + void sendMessage(MessageType type, const TQString& content=TQString(), TQ_INT32 flag=0, TQ_INT32 appId=0); + void setType(TransferType type); + + public: + TQ_UINT32 m_sessionId; + TQ_UINT32 m_identifier; + TQFile *m_file; + TQ_UINT32 m_transactionId; + TQ_UINT32 m_ackSessionIdentifier; + TQ_UINT32 m_ackUniqueIdentifier; + Kopete::Transfer *m_transfer; + TQString m_branch; + TQString m_callId; + TQString m_object; + + + public slots: + void abort(); + void readyWrite(); + + protected: + TransferContext(const TQString& contact, P2P::Dispatcher *dispatcher,TQ_UINT32 sessionId); + void sendData(const TQByteArray& bytes); + void sendMessage(P2P::Message& outbound, const TQByteArray& body); + virtual void readyToSend(); + + TQ_UINT32 m_baseIdentifier; + TransferDirection m_direction; + P2P::Dispatcher *m_dispatcher; + bool m_isComplete; + TQ_INT64 m_offset; + TQ_INT64 m_totalDataSize; + P2P::MessageFormatter m_messageFormatter; + TQString m_recipient; + TQString m_sender; + KNetwork::TDEBufferedSocket *m_socket; + CommunicationState m_state; + TransferType m_type; + }; +} + +#endif diff --git a/kopete/protocols/msn/sha1.cpp b/kopete/protocols/msn/sha1.cpp new file mode 100644 index 00000000..d0a13135 --- /dev/null +++ b/kopete/protocols/msn/sha1.cpp @@ -0,0 +1,192 @@ +/* + * sha1.cpp - Secure Hash Algorithm 1 + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "sha1.h" + +/**************************************************************************** + SHA1 - from a public domain implementation by Steve Reid (steve@edmweb.com) +****************************************************************************/ + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15]^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +SHA1::SHA1() +{ + int wordSize; + + tqSysInfo(&wordSize, &bigEndian); +} + +unsigned long SHA1::blk0(TQ_UINT32 i) +{ + if(bigEndian) + return block->l[i]; + else + return (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) | (rol(block->l[i],8)&0x00FF00FF)); +} + +// Hash a single 512-bit block. This is the core of the algorithm. +void SHA1::transform(TQ_UINT32 state[5], unsigned char buffer[64]) +{ + TQ_UINT32 a, b, c, d, e; + + block = (CHAR64LONG16*)buffer; + + // Copy context->state[] to working vars + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + + // 4 rounds of 20 operations each. Loop unrolled. + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + + // Add the working vars back into context.state[] + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + + // Wipe variables + a = b = c = d = e = 0; +} + +// SHA1Init - Initialize new context +void SHA1::init(SHA1_CONTEXT* context) +{ + // SHA1 initialization constants + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + +// Run your data through this +void SHA1::update(SHA1_CONTEXT* context, unsigned char* data, TQ_UINT32 len) +{ + TQ_UINT32 i, j; + + j = (context->count[0] >> 3) & 63; + if((context->count[0] += len << 3) < (len << 3)) + context->count[1]++; + + context->count[1] += (len >> 29); + + if((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +} + +// Add padding and return the message digest +void SHA1::final(unsigned char digest[20], SHA1_CONTEXT* context) +{ + TQ_UINT32 i, j; + unsigned char finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); // Endian independent + } + update(context, (unsigned char *)"\200", 1); + while ((context->count[0] & 504) != 448) { + update(context, (unsigned char *)"\0", 1); + } + update(context, finalcount, 8); // Should cause a transform() + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + + // Wipe variables + i = j = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(&finalcount, 0, 8); +} + +TQByteArray SHA1::hash(const TQByteArray &a) +{ + SHA1_CONTEXT context; + TQByteArray b(20); + + SHA1 s; + s.init(&context); + s.update(&context, (unsigned char *)a.data(), (unsigned int)a.size()); + s.final((unsigned char *)b.data(), &context); + return b; +} + +TQByteArray SHA1::hashString(const TQCString &cs) +{ + TQByteArray a(cs.length()); + memcpy(a.data(), cs.data(), a.size()); + return SHA1::hash(a); +} + +TQString SHA1::digest(const TQString &in) +{ + TQByteArray a = SHA1::hashString(in.utf8()); + TQString out; + for(int n = 0; n < (int)a.size(); ++n) { + TQString str; + str.sprintf("%02x", (uchar)a[n]); + out.append(str); + } + + return out; +} diff --git a/kopete/protocols/msn/sha1.h b/kopete/protocols/msn/sha1.h new file mode 100644 index 00000000..9138fd32 --- /dev/null +++ b/kopete/protocols/msn/sha1.h @@ -0,0 +1,59 @@ +/* + * sha1.h - Secure Hash Algorithm 1 + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef CS_SHA1_H +#define CS_SHA1_H + +#include <tqstring.h> + +class SHA1 +{ +public: + static TQByteArray hash(const TQByteArray &); + static TQByteArray hashString(const TQCString &); + static TQString digest(const TQString &); + +private: + SHA1(); + + struct SHA1_CONTEXT + { + TQ_UINT32 state[5]; + TQ_UINT32 count[2]; + unsigned char buffer[64]; + }; + + typedef union { + unsigned char c[64]; + TQ_UINT32 l[16]; + } CHAR64LONG16; + + void transform(TQ_UINT32 state[5], unsigned char buffer[64]); + void init(SHA1_CONTEXT* context); + void update(SHA1_CONTEXT* context, unsigned char* data, TQ_UINT32 len); + void final(unsigned char digest[20], SHA1_CONTEXT* context); + + unsigned long blk0(TQ_UINT32 i); + bool bigEndian; + + CHAR64LONG16* block; +}; + +#endif diff --git a/kopete/protocols/msn/transport.cpp b/kopete/protocols/msn/transport.cpp new file mode 100644 index 00000000..57b1f356 --- /dev/null +++ b/kopete/protocols/msn/transport.cpp @@ -0,0 +1,356 @@ +/* + transport.cpp - Peer to peer transport + + Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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; version 2 of the License. * + * * + ************************************************************************* +*/ + +#include "transport.h" +#include "messageformatter.h" + +//BEGIN QT Includes +//END + +//BEGIN KDE Includes +#include <kclientsocketbase.h> +#include <kdebug.h> +#include <kstreamsocket.h> +//END + +//BEGIN Using Directives +using namespace KNetwork; +//END + +#include "msnswitchboardsocket.h" + +namespace PeerToPeer { + +Transport::Transport(TQObject* parent, const char* name) + : TQObject(parent, name) +{ + mFormatter = new PeerToPeer::MessageFormatter(this); +} + + +Transport::~Transport() +{ +} + +//BEGIN Public Methods + +TransportBridge* Transport::getBridge (const TQString& to, TQ_UINT16 port, TransportBridgeType type, const TQString& identifier) +{ + TransportBridge *bridge = 0l; + KInetSocketAddress address; + if (mAddresses.contains(to)) + { + address = mAddresses[to]; + } + else + { + address = KInetSocketAddress(KIpAddress(to), port); + mAddresses[to] = address; + } + + if (PeerToPeer::Tcp == type){ + bridge = new TcpTransportBridge(address, mFormatter, this, identifier.ascii()); + } + + if (PeerToPeer::Udp == type){ +// TODO Add class UdpTransportBridge +// bridge = new UdpTransportBridge(address, this, mFormatter, identifier.ascii()); + } + + if (bridge != 0l) + { + TQObject::connect(bridge, TQT_SIGNAL(readyRead(const TQByteArray&)), TQT_SLOT(slotOnReceive(const TQByteArray&))); + } + + return 0l; +} + +void Transport::setDefaultBridge(MSNSwitchBoardSocket* mss) +{ + mDefaultBridge = mss; + TQObject::connect((MSNSwitchBoardSocket*)mDefaultBridge, TQT_SIGNAL(messageReceived(const TQString&, const TQByteArray&)), TQT_SLOT(slotOnReceive(const TQString&, const TQByteArray&))); +} + +//END + +//BEGIN Private Slot Methods + +// void Transport::slotOnReceive(Message& message) +// { +// } + +void Transport::slotOnReceive(const TQString& contact, const TQByteArray& bytes) +{ + kdDebug (14140) << k_funcinfo << " >> RECEIVED " << bytes.size() << " bytes." << endl; +// Message message = mFormatter->readMessage(bytes); +} + +//END + + + + +TransportBridge::TransportBridge(const KNetwork::KInetSocketAddress& to, MessageFormatter* formatter, TQObject* parent, const char* name) +: TQObject(parent, name) +{ + mAddress = to; + mFormatter = formatter; +} + +TransportBridge::TransportBridge(KNetwork::KClientSocketBase* socket, MessageFormatter* formatter, TQObject* parent, const char* name) +: TQObject(parent, name) +{ + mSocket = socket; + mAddress = mSocket->peerAddress(); +} + +TransportBridge::~TransportBridge() +{ +} + +//BEGIN Public Methods + +void TransportBridge::connect() +{ + slotOnConnect(); +} + +void TransportBridge::disconnect() +{ + slotOnDisconnect(); +} + +//END + +//BEGIN Protected Slot Methods + +void TransportBridge::slotOnConnect() +{ +} + +void TransportBridge::slotOnDisconnect() +{ +} + +void TransportBridge::slotOnError(int) +{ +} + +void TransportBridge::slotOnSocketClose() +{ +} + +void TransportBridge::slotOnSocketConnect() +{ +} + +void TransportBridge::slotOnSocketReceive() +{ +} + + +//END + + + +TcpTransportBridge::TcpTransportBridge(const KNetwork::KInetSocketAddress& to, MessageFormatter* formatter, TQObject* parent, const char* name) +: TransportBridge(to, formatter, parent, name) +{ + mSocket = new KStreamSocket(mAddress.ipAddress().toString(), TQString::number(mAddress.port()), this); + mSocket->setBlocking(false); + TQObject::connect(mSocket, TQT_SIGNAL(connected(const KResolverEntry&)), TQT_SLOT(slotOnSocketConnect())); + TQObject::connect(mSocket, TQT_SIGNAL(gotError(int)), TQT_SLOT(slotOnError(int))); + mConnected = false; +} + +TcpTransportBridge::TcpTransportBridge(KNetwork::KClientSocketBase* socket, MessageFormatter* formatter, TQObject* parent, const char* name) +: TransportBridge(socket, formatter, parent, name) +{ + mConnected = (mSocket->state() == KStreamSocket::Open) ? true : false; + mSocket->setBlocking(false); +} + +TcpTransportBridge::~TcpTransportBridge() +{ +} + +//BEGIN Protected Slot Methods + +void TcpTransportBridge::slotOnConnect() +{ + if (mConnected) + { + kdDebug(14140) << k_funcinfo << "Bridge (" << name() << ") ALREADY CONNECTED " << mSocket->peerAddress().toString() << " <-> " << mSocket->localAddress().toString() << endl; + return; + } + + KStreamSocket *socket = static_cast<KStreamSocket*>(mSocket); + socket->setTimeout(5000); + TQObject::connect(socket, TQT_SIGNAL(timeOut()), TQT_SLOT(slotOnSocketConnectTimeout())); + mSocket->connect(); +} + +void TcpTransportBridge::slotOnDisconnect() +{ + if (mConnected){ + mSocket->close(); + } +} + +void TcpTransportBridge::slotOnError(int errorCode) +{ + kdDebug(14140) << k_funcinfo << "Bridge (" << name() << ") ERROR occurred on {" << mSocket->localAddress().toString() << " <-> " << mSocket->peerAddress().toString() << "} - " << mSocket->errorString() << endl; + emit bridgeError(TQString("Bridge ERROR %1: %2").arg(errorCode).arg(mSocket->errorString())); + if (mConnected){ + mSocket->disconnect(); + mConnected = false; + } + mSocket->deleteLater(); + mSocket = 0l; +} + +void TcpTransportBridge::slotOnSocketClose() +{ + mSocket->disconnect(); + kdDebug(14140) << k_funcinfo << "Bridge (" << name() << ") DISCONNECTED {" << mSocket->peerAddress().toString() << " <-> " << mSocket->localAddress().toString() << "}" << endl; + mConnected = false; + mSocket->deleteLater(); + mSocket = 0l; + + emit bridgeDisconnect(); +} + +void TcpTransportBridge::slotOnSocketConnect() +{ + kdDebug(14140) << k_funcinfo << "Bridge (" << name() << ") CONNECTED to " << mSocket->peerAddress().toString() << " from " + << mSocket->localAddress().toString() << endl; + mConnected = true; + + TQObject::connect(mSocket, TQT_SIGNAL(readyRead()), TQT_SLOT(slotOnSocketReceive())); + TQObject::connect(mSocket, TQT_SIGNAL(closed()), TQT_SLOT(slotOnSocketClose())); + + mVerified = true; + TQString foo = "foo\0"; + mSocket->writeBlock(foo.ascii(), foo.length()); + foo = TQString(); + + emit bridgeConnect(); +} + +void TcpTransportBridge::slotOnSocketReceive() +{ + kdDebug (14140) << k_funcinfo << "Bridge (" << name() << ") RECEIVED " << mSocket->bytesAvailable() << " bytes." << endl; + + TQByteArray bytes(mSocket->bytesAvailable()); + mSocket->readBlock(bytes.data(), bytes.size()); + // Write the data to the buffer. + mBuffer.write(bytes); + + if (mVerified == false && mBuffer.size() >= 4) + { + TQByteArray foo = mBuffer.read(4); + if (TQString(foo) == "foo"){ + kdDebug (14140) << k_funcinfo << "Bridge (" << name() << ") CONNECTION verified." << endl; + mVerified = true; + } + } + + while(mBuffer.size() > 0) + { + if (mBuffer.size() >= 4 && mLength == 0) + { + TQByteArray array = mBuffer.read(4); + for (int i=0; i < 4; i++){ + ((char*)mLength)[i] = array[i]; + } + } + + if (mLength > 0 && mBuffer.size() >= mLength) + { + kdDebug (14140) << k_funcinfo << "Bridge (" << name() << ") read " << mLength << " bytes." << endl; + bytes = mBuffer.read(mLength); + mLength = 0; +// Message message = mFormatter->readMessage(bytes, true); +// emit messageReceived(message); + } + else + { + kdDebug (14140) << k_funcinfo << "Bridge (" << name() << ") waiting for " << mLength << " bytes." << endl; + break; + } + } +} + +//END + +//BEGIN Private Slot Methods + +void TcpTransportBridge::slotOnSocketConnectTimeout() +{ + kdDebug (14140) << k_funcinfo << "Bridge (" << name() << ") CONNECT timeout." << endl; + emit bridgeConnectTimeout(); + mSocket->deleteLater(); + mSocket = 0l; +} + +//END + + + + +TcpTransportBridge::Buffer::Buffer(TQ_UINT32 length) +: TQByteArray(length) +{ +} + +TcpTransportBridge::Buffer::~Buffer() +{ +} + +//BEGIN Public Methods + +void TcpTransportBridge::Buffer::write(const TQByteArray& bytes) +{ + resize(size() + bytes.size()); + for (uint i=0; i < bytes.size(); i++){ + (*this)[size() + i] = bytes[i]; + } +} + +TQByteArray TcpTransportBridge::Buffer::read(TQ_UINT32 length) +{ + if (length >= size()) return TQByteArray(); + + TQByteArray buffer; + buffer.duplicate(data(), length); + + char *bytes = new char[size() - length]; + for(uint i=0; i < size() - length; i++){ + bytes[i] = data()[length + i]; + } + + duplicate(bytes, size() - length); + delete[] bytes; + + return buffer; +} + +//END + +} + +#include "transport.moc" + diff --git a/kopete/protocols/msn/transport.h b/kopete/protocols/msn/transport.h new file mode 100644 index 00000000..c5554b54 --- /dev/null +++ b/kopete/protocols/msn/transport.h @@ -0,0 +1,170 @@ +/* + transport.h - Peer to peer transport + + Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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; version 2 of the License. * + * * + ************************************************************************* +*/ + +#ifndef PEERTOPEERTRANSPORT_H +#define PEERTOPEERTRANSPORT_H + +//BEGIN QT Includes +#include <tqobject.h> +#include <tqguardedptr.h> +#include <tqvaluelist.h> +//END + +//BEGIN KDE Includes +#include <tdesocketaddress.h> +//END + +namespace KNetwork { +class KClientSocketBase; +} + +class MSNSwitchBoardSocket; + +namespace PeerToPeer { + +class MessageFormatter; +class TransportBridge; + +enum TransportBridgeType +{ + Tcp = 1, + Udp = 2 +}; + +/** + @author Gregg Edghill <gregg.edghill@gmail.com> */ +/** @brief Represents the protocol used to send and receive message between peers. */ +class Transport : public TQObject +{ + Q_OBJECT + +public: + /** @brief Creates a new instance of the class Transport. */ + Transport(TQObject* parent, const char* name = 0l); + ~Transport(); + /** @brief Get a transport bridge with the specified address, port, type and identifier. */ + TransportBridge* getBridge(const TQString& address, TQ_UINT16 port, TransportBridgeType type, const TQString& identifier); + /** @brief Sets the default transport bridge. */ + void setDefaultBridge(MSNSwitchBoardSocket* mss); + +private slots: + /** @brief Invokes when a message is received on a transport bridge. */ +// void slotOnReceive(Message& message); + /** @brief Invokes when a message is received on the default transport bridge (relay). */ + void slotOnReceive(const TQString& contact, const TQByteArray& bytes); + +private: + /** @brief Known SocketAddresses of peers. */ + TQMap<TQString, KNetwork::KInetSocketAddress> mAddresses; + /** @brief The list the connected transport bridges. */ + TQValueList<TransportBridge*> mBridges; + /** @brief The default transport bridge (relay). */ + TQGuardedPtr<MSNSwitchBoardSocket> mDefaultBridge; + /** @brief Message formatter used to ser/deser message. */ + MessageFormatter *mFormatter; +}; + +/** @brief Represents the channel connecting two peers. */ +class TransportBridge : public TQObject +{ + Q_OBJECT + +public: + virtual ~TransportBridge(); + +protected: + /** @brief Creates a new instance of the class TransportBridge with the specified address and formatter. */ + TransportBridge(const KNetwork::KInetSocketAddress& to, MessageFormatter* formatter, TQObject* parent, const char* name = 0l); + /** @brief Creates a new instance of the class TransportBridge with the specified socket and formatter. */ + TransportBridge(KNetwork::KClientSocketBase* socket, MessageFormatter* formatter, TQObject* parent, const char* name = 0l); + +public: + /** @brief Creates a connection between two peers. */ + void connect(); + /** @brief Disconnects the connection between two peers. */ + void disconnect(); + +protected slots: + virtual void slotOnConnect(); + virtual void slotOnDisconnect(); + virtual void slotOnError(int); + virtual void slotOnSocketClose(); + virtual void slotOnSocketConnect(); + virtual void slotOnSocketReceive(); + +signals: + void bridgeConnect(); + void bridgeDisconnect(); + void bridgeError(const TQString& e); + void bytesReceived(const TQByteArray&); + +protected: + + KNetwork::KInetSocketAddress mAddress; + bool mConnected; + MessageFormatter *mFormatter; + TQ_UINT32 mLength; + KNetwork::KClientSocketBase *mSocket; + bool mVerified; +}; + +class TcpTransportBridge : public TransportBridge +{ + Q_OBJECT + + friend class Transport; + +public: + virtual ~TcpTransportBridge(); + +private: + TcpTransportBridge(const KNetwork::KInetSocketAddress& to, MessageFormatter* formatter, TQObject* parent, const char* name = 0l); + TcpTransportBridge(KNetwork::KClientSocketBase* socket, MessageFormatter* formatter, TQObject* parent, const char* name = 0l); + +protected slots: + virtual void slotOnConnect(); + virtual void slotOnDisconnect(); + virtual void slotOnError(int); + virtual void slotOnSocketClose(); + virtual void slotOnSocketConnect(); + virtual void slotOnSocketReceive(); + +private slots: + void slotOnSocketConnectTimeout(); + +signals: + void bridgeConnectTimeout(); + +private: + class Buffer : public TQByteArray + { + public: + Buffer(TQ_UINT32 length = 0); + ~Buffer(); + + public: + void write(const TQByteArray& bytes); + TQByteArray read(TQ_UINT32 length); + }; + + Buffer mBuffer; + TQ_UINT32 mLength; +}; + + +} + +#endif diff --git a/kopete/protocols/msn/ui/CMakeLists.txt b/kopete/protocols/msn/ui/CMakeLists.txt new file mode 100644 index 00000000..bd70ce80 --- /dev/null +++ b/kopete/protocols/msn/ui/CMakeLists.txt @@ -0,0 +1,29 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_BINARY_DIR}/kopete/libkopete/ui + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${CMAKE_SOURCE_DIR}/kopete/libkopete/ui + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### kopetemsnui (static) ###################### + +tde_add_library( kopetemsnui STATIC_PIC AUTOMOC + SOURCES + msnadd.ui msndebugrawcommand_base.ui msninfo.ui msneditaccountui.ui + msneditaccountwidget.cpp +) diff --git a/kopete/protocols/msn/ui/Makefile.am b/kopete/protocols/msn/ui/Makefile.am new file mode 100644 index 00000000..08a7cbac --- /dev/null +++ b/kopete/protocols/msn/ui/Makefile.am @@ -0,0 +1,12 @@ +METASOURCES = AUTO +AM_CPPFLAGS = $(KOPETE_INCLUDES) \ + -I$(srcdir)/.. \ + $(all_includes) + +noinst_LTLIBRARIES = libkopetemsnui.la + +libkopetemsnui_la_SOURCES = msnadd.ui msndebugrawcommand_base.ui msninfo.ui \ + msneditaccountui.ui msneditaccountwidget.cpp + +EXTRA_DIST = msnadd.ui msninfo.ui + diff --git a/kopete/protocols/msn/ui/msnadd.ui b/kopete/protocols/msn/ui/msnadd.ui new file mode 100644 index 00000000..fab496d3 --- /dev/null +++ b/kopete/protocols/msn/ui/msnadd.ui @@ -0,0 +1,97 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>msnAddUI</class> +<widget class="TQWidget"> + <property name="name"> + <cstring>msnAddUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>397</width> + <height>347</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout21</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLabel"> + <property name="name"> + <cstring>TextLabel1</cstring> + </property> + <property name="text"> + <string>&MSN Passport ID:</string> + </property> + <property name="alignment"> + <set>AlignTop</set> + </property> + <property name="buddy" stdset="0"> + <cstring>addID</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The user ID of the MSN contact you would like to add.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The user ID of the MSN contact you would like to add. This should be in the form of a valid E-mail address.</string> + </property> + </widget> + <widget class="TQLineEdit"> + <property name="name"> + <cstring>addID</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The user ID of the MSN contact you would like to add.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The user ID of the MSN contact you would like to add. This should be in the form of a valid E-mail address.</string> + </property> + </widget> + </hbox> + </widget> + <widget class="TQLabel"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string><i>(for example: joe@hotmail.com)</i></string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer13</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>160</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kopete/protocols/msn/ui/msndebugrawcommand_base.ui b/kopete/protocols/msn/ui/msndebugrawcommand_base.ui new file mode 100644 index 00000000..b5699dd0 --- /dev/null +++ b/kopete/protocols/msn/ui/msndebugrawcommand_base.ui @@ -0,0 +1,107 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>MSNDebugRawCommand_base</class> +<widget class="TQWidget"> + <property name="name"> + <cstring>MSNDebugRawCommand_base</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>320</width> + <height>201</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="TQLabel" row="1" column="0"> + <property name="name"> + <cstring>TextLabel2</cstring> + </property> + <property name="text"> + <string>&Parameters:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_params</cstring> + </property> + </widget> + <widget class="TQLineEdit" row="0" column="1"> + <property name="name"> + <cstring>m_command</cstring> + </property> + </widget> + <widget class="TQLabel" row="0" column="0"> + <property name="name"> + <cstring>TextLabel1</cstring> + </property> + <property name="text"> + <string>Co&mmand:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_command</cstring> + </property> + </widget> + <widget class="TQLineEdit" row="1" column="1"> + <property name="name"> + <cstring>m_params</cstring> + </property> + </widget> + <widget class="TQCheckBox" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>m_addId</cstring> + </property> + <property name="text"> + <string>Add &ID</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="TQCheckBox" row="3" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>m_addNewline</cstring> + </property> + <property name="text"> + <string>Add &new line</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="KTextEdit" row="5" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>m_msg</cstring> + </property> + <property name="textFormat"> + <enum>PlainText</enum> + </property> + </widget> + <widget class="TQLabel" row="4" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>TextLabel3</cstring> + </property> + <property name="text"> + <string>Message:</string> + </property> + </widget> + </grid> +</widget> +<tabstops> + <tabstop>m_command</tabstop> + <tabstop>m_params</tabstop> + <tabstop>m_addId</tabstop> + <tabstop>m_addNewline</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>ktextedit.h</includehint> +</includehints> +</UI> diff --git a/kopete/protocols/msn/ui/msneditaccountui.ui b/kopete/protocols/msn/ui/msneditaccountui.ui new file mode 100644 index 00000000..0a764c57 --- /dev/null +++ b/kopete/protocols/msn/ui/msneditaccountui.ui @@ -0,0 +1,1421 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>MSNEditAccountUI</class> +<author>Olivier Goffart</author> +<widget class="TQWidget"> + <property name="name"> + <cstring>Form1</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>604</width> + <height>437</height> + </rect> + </property> + <property name="caption"> + <string>Account Preferences - MSN</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="TQTabWidget"> + <property name="name"> + <cstring>tabWidget3</cstring> + </property> + <property name="tabShape"> + <enum>Rounded</enum> + </property> + <widget class="TQWidget"> + <property name="name"> + <cstring>tab_connection</cstring> + </property> + <attribute name="title"> + <string>&Basic Setup</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer row="2" column="0"> + <property name="name"> + <cstring>spacer41</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>146</height> + </size> + </property> + </spacer> + <widget class="TQGroupBox" row="1" column="0"> + <property name="name"> + <cstring>groupBox5</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Registration</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLabel"> + <property name="name"> + <cstring>textLabel6</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + <property name="text"> + <string>To connect to the Microsoft network, you will need a Microsoft Passport.<br><br>If you do not currently have a Passport, please click the button to create one.</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + <widget class="TQPushButton"> + <property name="name"> + <cstring>buttonRegister</cstring> + </property> + <property name="text"> + <string>Re&gister New Account</string> + </property> + </widget> + </hbox> + </widget> + <widget class="TQGroupBox" row="0" column="0"> + <property name="name"> + <cstring>m_accountInfo</cstring> + </property> + <property name="title"> + <string>Account Information</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout14</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLabel"> + <property name="name"> + <cstring>TextLabel1_3</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&MSN Passport ID:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_login</cstring> + </property> + <property name="toolTip" stdset="0"> + <string>The user ID of the MSN contact you would like to use.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The user ID of the MSN contact you would like to use. This should be in the form of a valid E-mail address.</string> + </property> + </widget> + <widget class="TQLineEdit"> + <property name="name"> + <cstring>m_login</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>The user ID of the MSN contact you would like to use.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The user ID of the MSN contact you would like to use. This should be in the form of a valid E-mail address.</string> + </property> + </widget> + </hbox> + </widget> + <widget class="Kopete::UI::PasswordWidget" row="1" column="0"> + <property name="name"> + <cstring>m_password</cstring> + </property> + </widget> + <widget class="TQCheckBox" row="2" column="0"> + <property name="name"> + <cstring>m_autologin</cstring> + </property> + <property name="text"> + <string>E&xclude from connect all</string> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <property name="whatsThis" stdset="0"> + <string>If you check this checkbox, the account will not be connected when you press the "Connect All" button, or at startup when automatic connection at startup is enabled.</string> + </property> + </widget> + <widget class="TQCheckBox" row="3" column="0"> + <property name="name"> + <cstring>m_globalIdentity</cstring> + </property> + <property name="text"> + <string>Exclu&de from Global Identity</string> + </property> + </widget> + </grid> + </widget> + </grid> + </widget> + <widget class="TQWidget"> + <property name="name"> + <cstring>TabPage</cstring> + </property> + <attribute name="title"> + <string>MSN &Settings</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLabel"> + <property name="name"> + <cstring>textLabel1_3</cstring> + </property> + <property name="font"> + <font> + <italic>1</italic> + </font> + </property> + <property name="text"> + <string><qt><b>Note:</b> These settings are applicable to all MSN accounts</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignCenter</set> + </property> + </widget> + <widget class="TQGroupBox"> + <property name="name"> + <cstring>global_settings_page</cstring> + </property> + <property name="title"> + <string>Global MSN Options</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQCheckBox"> + <property name="name"> + <cstring>NotifyNewChat</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>1</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Au&tomatically open a chat window when someone starts a conversation</string> + </property> + <property name="whatsThis" stdset="0"> + <string>This option will notify you when a contact starts typing their message, before the message is sent or finished.</string> + </property> + </widget> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout13_2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLabel"> + <property name="name"> + <cstring>textLabel1_4</cstring> + </property> + <property name="text"> + <string>Download the msn picture:</string> + </property> + <property name="whatsThis" stdset="0"> + <string><qt><p>Indicate when Kopete will download the display pictures of contacts</p> +<dl><dt>Only manually</dt><dd>The picture is not downloaded automatically. It is only downloaded when the user requests it</dd> +<dt>When a chat is open</dt><dd>The picture is downloaded when a conversation socket is opened, i.e. when you open a chat window</dd> +<dt>Automatically</dt><dd>Always try to download the picture if the contact has one. <b>Note:</b> this will open a socket, and let the user know you are downloading their picture.</dd></dl></string> + </property> + </widget> + <widget class="TQComboBox"> + <item> + <property name="text"> + <string>Only Manually</string> + </property> + </item> + <item> + <property name="text"> + <string>When a Chat is Open</string> + </property> + </item> + <item> + <property name="text"> + <string>Automatically</string> + </property> + </item> + <property name="name"> + <cstring>DownloadPicture</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="currentItem"> + <number>2</number> + </property> + <property name="whatsThis" stdset="0"> + <string><qt><p>Indicate when Kopete will download the pictures of contacts</p> +<dl><dt>Only manually</dt><dd>The picture is not downloaded automatically. It is only downloaded when the user requests it</dd> +<dt>When a chat is open</dt><dd>The picture is downloaded when a conversation socket is opened, i.e. when you open a chat window</dd> +<dt>Automatically</dt><dd>Always try to download the picture if the contact has one. <b>Note:</b> this will open a socket, and let the user know you are downloading their picture.</dd></dl></string> + </property> + </widget> + </hbox> + </widget> + <widget class="TQCheckBox"> + <property name="name"> + <cstring>useCustomEmoticons</cstring> + </property> + <property name="text"> + <string>&Download and show custom emoticons</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="whatsThis" stdset="0"> + <string>MSN Messenger allows users to download and use custom emoticons. If this option is enabled, Kopete will download these emoticons and show them.</string> + </property> + </widget> + <widget class="TQCheckBox"> + <property name="name"> + <cstring>exportEmoticons</cstring> + </property> + <property name="text"> + <string>E&xport the current emoticon theme to users</string> + </property> + <property name="toolTip" stdset="0"> + <string>Only work with emoticons in the PNG format</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Export all the emoticon themes as custom emoticons. +Only works for emoticons in the PNG format.</string> + </property> + </widget> + </vbox> + </widget> + <widget class="TQGroupBox"> + <property name="name"> + <cstring>privacy_page</cstring> + </property> + <property name="title"> + <string>Privacy</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQCheckBox"> + <property name="name"> + <cstring>SendClientInfo</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Send client information</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string><qt>Make it possible for your contacts to detect if you are using Kopete.<br>We recommend leaving this checked.</qt></string> + </property> + <property name="whatsThis" stdset="0"> + <string>Third party MSN clients, such as Kopete, give users the ability to let other third party clients guess which client they are using. We recommend leaving this checkbox checked.</string> + </property> + </widget> + <widget class="TQCheckBox"> + <property name="name"> + <cstring>SendTypingNotification</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Send &typing notifications</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="whatsThis" stdset="0"> + <string><qt>Check this box to send <b>Typing notifications</b> to your contacts. When you are composing a message, you might want your contact to know that you are typing so that he knows you are answering.</qt></string> + </property> + </widget> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout28</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQCheckBox"> + <property name="name"> + <cstring>SendJabber</cstring> + </property> + <property name="text"> + <string>Expose my Jabber account to Jabber users</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="whatsThis" stdset="0"> + <string>If you have a Jabber account, you may let Jabber users on an MSN gateway know that you are also using Jabber.</string> + </property> + </widget> + <widget class="KComboBox"> + <property name="name"> + <cstring>JabberAccount</cstring> + </property> + <property name="editable"> + <bool>true</bool> + </property> + <property name="whatsThis" stdset="0"> + <string>If you have a Jabber account, you may let Jabber users on an MSN gateway know that you are also using Jabber.</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer26</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>61</width> + <height>21</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer25</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Minimum</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="TQLabel"> + <property name="name"> + <cstring>textLabel1_5</cstring> + </property> + <property name="text"> + <string>There are also privacy options in the "Contacts" tab</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignCenter</set> + </property> + </widget> + </vbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer20</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>31</width> + <height>40</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="TQWidget"> + <property name="name"> + <cstring>tab_info</cstring> + </property> + <attribute name="title"> + <string>User &Info</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>Layout22_2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="TQLabel"> + <property name="name"> + <cstring>TextLabel2_2_2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>4</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&Nickname:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_displayName</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>The alias you would like to use on MSN. You may change this at any time you wish.</string> + </property> + </widget> + <widget class="TQLineEdit"> + <property name="name"> + <cstring>m_displayName</cstring> + </property> + <property name="readOnly"> + <bool>false</bool> + </property> + <property name="whatsThis" stdset="0"> + <string>The alias you would like to use on MSN. You may change this at any time you wish.</string> + </property> + </widget> + </hbox> + </widget> + <widget class="TQGroupBox"> + <property name="name"> + <cstring>m_phones</cstring> + </property> + <property name="title"> + <string>Phone Numbers</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLabel" row="1" column="0"> + <property name="name"> + <cstring>TextLabel5</cstring> + </property> + <property name="text"> + <string>Hom&e:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_phh</cstring> + </property> + </widget> + <widget class="TQLabel" row="0" column="0"> + <property name="name"> + <cstring>TextLabel6</cstring> + </property> + <property name="text"> + <string>&Work:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_phw</cstring> + </property> + </widget> + <widget class="TQLineEdit" row="0" column="1"> + <property name="name"> + <cstring>m_phw</cstring> + </property> + </widget> + <widget class="TQLineEdit" row="1" column="1"> + <property name="name"> + <cstring>m_phh</cstring> + </property> + </widget> + <widget class="TQLabel" row="2" column="0"> + <property name="name"> + <cstring>TextLabel7</cstring> + </property> + <property name="text"> + <string>&Mobile:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_phm</cstring> + </property> + </widget> + <widget class="TQLineEdit" row="2" column="1"> + <property name="name"> + <cstring>m_phm</cstring> + </property> + </widget> + </grid> + </widget> + <widget class="TQGroupBox"> + <property name="name"> + <cstring>groupBox2</cstring> + </property> + <property name="title"> + <string>Display Picture</string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout17</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQCheckBox"> + <property name="name"> + <cstring>m_useDisplayPicture</cstring> + </property> + <property name="text"> + <string>E&xport a display picture</string> + </property> + </widget> + <widget class="TQLabel"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Please select a square image. The image will be scaled to 96x96.</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout13</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQPushButton"> + <property name="name"> + <cstring>m_selectImage</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>&Select Image...</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer5</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>61</width> + <height>21</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer7_2_2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Minimum</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>1</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout16</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="TQLabel"> + <property name="name"> + <cstring>m_displayPicture</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>96</width> + <height>96</height> + </size> + </property> + <property name="maximumSize"> + <size> + <width>96</width> + <height>96</height> + </size> + </property> + <property name="frameShape"> + <enum>GroupBoxPanel</enum> + </property> + <property name="scaledContents"> + <bool>true</bool> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer7_2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Minimum</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>1</height> + </size> + </property> + </spacer> + </vbox> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer16</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="TQLabel"> + <property name="name"> + <cstring>m_warning_1</cstring> + </property> + <property name="paletteForegroundColor"> + <color> + <red>255</red> + <green>0</green> + <blue>0</blue> + </color> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>WARNING: You need to be connected to modify this page.</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + </vbox> + </widget> + <widget class="TQWidget"> + <property name="name"> + <cstring>tab_contacts</cstring> + </property> + <attribute name="title"> + <string>Con&tacts</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLabel"> + <property name="name"> + <cstring>label_font</cstring> + </property> + <property name="text"> + <string><i>Italics</i> contacts are not on your contact list.<br> +<br> +<b>Bold</b> contacts are in your contact list but you are not in their contact list.</string> + </property> + </widget> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLabel" row="0" column="2"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Bloc&ked contacts:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_BL</cstring> + </property> + </widget> + <widget class="TQListBox" row="1" column="0"> + <property name="name"> + <cstring>m_AL</cstring> + </property> + </widget> + <widget class="TQLayoutWidget" row="1" column="1"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQPushButton"> + <property name="name"> + <cstring>m_blockButton</cstring> + </property> + <property name="text"> + <string>&></string> + </property> + </widget> + <widget class="TQPushButton"> + <property name="name"> + <cstring>m_allowButton</cstring> + </property> + <property name="text"> + <string>&<</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer13</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </vbox> + </widget> + <widget class="TQLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Allo&wed contacts:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_AL</cstring> + </property> + </widget> + <widget class="TQListBox" row="1" column="2"> + <property name="name"> + <cstring>m_BL</cstring> + </property> + </widget> + </grid> + </widget> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout58</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer47</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>41</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="TQCheckBox"> + <property name="name"> + <cstring>m_blp</cstring> + </property> + <property name="text"> + <string>Block all users not in 'Allowed' &list</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Checking this box will block all users not explicitly shown in the allowed list here, including any contacts not on your contact list.</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer50</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>41</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout59</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer48</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>81</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="TQPushButton"> + <property name="name"> + <cstring>m_RLButton</cstring> + </property> + <property name="maximumSize"> + <size> + <width>200</width> + <height>32767</height> + </size> + </property> + <property name="text"> + <string>View &Reverse List</string> + </property> + <property name="toolTip" stdset="0"> + <string>The reverse list is the list of contacts who added you to their own contact list.</string> + </property> + <property name="whatsThis" stdset="0"> + <string>The reverse list is the list of contacts who added you to their own contact list.</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer49</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>111</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="TQLabel"> + <property name="name"> + <cstring>m_warning_2</cstring> + </property> + <property name="paletteForegroundColor"> + <color> + <red>255</red> + <green>0</green> + <blue>0</blue> + </color> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>WARNING: You need to be connected to modify this page</string> + </property> + <property name="alignment"> + <set>WordBreak|AlignVCenter</set> + </property> + </widget> + </vbox> + </widget> + <widget class="TQWidget"> + <property name="name"> + <cstring>TabPage</cstring> + </property> + <attribute name="title"> + <string>Co&nnection</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQGroupBox"> + <property name="name"> + <cstring>groupBox66</cstring> + </property> + <property name="title"> + <string>Connection Preferences (for advanced users)</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQCheckBox"> + <property name="name"> + <cstring>optionOverrideServer</cstring> + </property> + <property name="text"> + <string>&Override default server information</string> + </property> + </widget> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout20</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout19</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="spacing"> + <number>3</number> + </property> + <widget class="TQLabel"> + <property name="name"> + <cstring>labelServer</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Ser&ver /</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_serverName</cstring> + </property> + </widget> + <widget class="TQLabel"> + <property name="name"> + <cstring>labelPort</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>po&rt:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>m_serverPort</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="TQLineEdit"> + <property name="name"> + <cstring>m_serverName</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>messenger.hotmail.com</string> + </property> + <property name="toolTip" stdset="0"> + <string>Only modify these values if you want to use a special IM proxy server, like SIMP</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Only modify these values if you want to use a special IM proxy server, like SIMP</string> + </property> + </widget> + <widget class="TQSpinBox"> + <property name="name"> + <cstring>m_serverPort</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="maxValue"> + <number>65535</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="value"> + <number>1863</number> + </property> + <property name="toolTip" stdset="0"> + <string>Only modify these values if you want to use a special IM proxy server, like SIMP</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Only modify these values if you want to use a special IM proxy server, like SIMP</string> + </property> + </widget> + </hbox> + </widget> + <widget class="TQCheckBox"> + <property name="name"> + <cstring>optionUseHttpMethod</cstring> + </property> + <property name="text"> + <string>Use &HTTP method</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Connect to MSN Messenger using an HTTP-like protocol on port 80. +This may be used to connect on a network with a restrictive firewall. +Only check this option if the normal connection doesn't work.</string> + </property> + </widget> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout22</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQCheckBox"> + <property name="name"> + <cstring>m_useWebcamPort</cstring> + </property> + <property name="text"> + <string>S&pecify a base port for incoming webcam connections:</string> + </property> + <property name="whatsThis" stdset="0"> + <string>If you are behind a firewall, you may specify a base port to use for the incoming connection, and configure your firewall to accept connections on a range of 10 ports, starting at this one. Incoming connections are used for the webcam. If you don't specify a port yourself, the operating system will choose an available port for you. It is recommended to leave the checkbox unchecked.</string> + </property> + </widget> + <widget class="TQSpinBox"> + <property name="name"> + <cstring>m_webcamPort</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="maxValue"> + <number>65535</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="value"> + <number>6891</number> + </property> + <property name="whatsThis" stdset="0"> + <string>If you are behind a firewall, you may specify a base port to use for the incoming connection, and configure your firewall to accept connections on a range of 10 ports, starting at this one. Incoming connections are used for the webcam. If you don't specify a port yourself, the operating system will choose an available port for you. It is recommended to leave the checkbox unchecked.</string> + </property> + </widget> + </hbox> + </widget> + </vbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer7</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>21</width> + <height>70</height> + </size> + </property> + </spacer> + </vbox> + </widget> + </widget> + <widget class="TQLabel"> + <property name="name"> + <cstring>labelStatusMessage</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + </vbox> +</widget> +<customwidgets> + <customwidget> + <class>Kopete::UI::PasswordWidget</class> + <header location="local">kopetepasswordwidget.h</header> + <sizehint> + <width>50</width> + <height>50</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>1</hordata> + <verdata>0</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + <signal>changed()</signal> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="PNG" length="868">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000032b49444154388db59531681b6714c77f32373c8186ef0305eea0050932f8201d944d493bc4d0a1a21e4427bb533a74299dbc25905288a7d0b9836932d58116eac1411932388ba421a5a7a17005174e83e00e2cb80f6ab83708d2e18bec8ada26d0f4c1c1ddbbf7fdeeff3efeefbbda70346419b76fdd7ecd3b88e16858ab2dc183c3c1ebee7a97a99b521515d969f65610e71cd971c6f8d7312ccef3c152e9b39f9e11351d36164acdb819d4a9b4c4362ce5a2c48a45162588253ff5cfe5a2c406862405d9138e5eea2a18609a4fb12d212d7ea42c334089ac92e6423113cab902826d4227568a002480a942780dead16a2767e0ca55949a81668023b2c2e8952139748c270e58aa115aebc2675b86b80b6143710aa1b9049ccd336e064a5979e8e039ec7f5f78544368af1b24807ca64cff50befba6a0b765d8be2b67f00bc1562c95e6441646afe40d54b9f36948af2fb4df078722440c0e2af6f70a064f0be2568beea6c5885b01af2d6f4a2db10dc8ff128e0edc19f4f32f8576dbe1707022fcf2b4647babce175f8780f0c31307a7e0162bdc55c5e52247e742fabbc31843af2f9886c32d40d4b0fb4849278ef20476ee59c62f7ced3831848d55f0aa62816ca6de11ad37ed2fa10f1ce9c4619ac2c647824a45dc1100f2a9e2542e067b9f82155f108adf539c61f781924efc0745c0be57273240b08409e62ac508d0f085c94c112c83e778a54608434331733cbc9f331a5bf2636f85a855bfda15f9694e27565ad785e99fcae0a062fb6e4479a2f43e16eacd3a0fef433175ec7e95a1aa98a6d0e95454f355f2bff65803e8f5bddbf7f70a0687393bf72ced2e74ba253bdfb631a1c139872e948d7e487c83ab15979a2301dcba033a373c7e52f0f851c1f885d0ed080ec88f7374ae672b7f3b72249b115143389fce7f4e5e91d11398cefd986e6c099816839fbd1bd2c9b91ad3147afd16a32387534580ac58957c0e3ece485230d77c5ba6a1f4fa42ef9398719253153e1f5f8f687f9013df80f16684c1e0161969b20aae0d47437fc007d0f950882210c19fad81bf24f04e399701a04820380769a2e485e28a0b14b380e4a5927059e85be67cac5dfae63fc61af87fd4ff027ed7f0e16858fb1ba5cd86c64770b2e90000000049454e44ae426082</data> + </image> +</images> +<connections> + <connection> + <sender>m_useDisplayPicture</sender> + <signal>toggled(bool)</signal> + <receiver>m_selectImage</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_useDisplayPicture</sender> + <signal>toggled(bool)</signal> + <receiver>m_selectImage</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_useDisplayPicture</sender> + <signal>toggled(bool)</signal> + <receiver>textLabel1_2</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>optionOverrideServer</sender> + <signal>toggled(bool)</signal> + <receiver>labelServer</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>optionOverrideServer</sender> + <signal>toggled(bool)</signal> + <receiver>m_serverName</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>optionOverrideServer</sender> + <signal>toggled(bool)</signal> + <receiver>labelPort</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>optionOverrideServer</sender> + <signal>toggled(bool)</signal> + <receiver>m_serverPort</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_useDisplayPicture</sender> + <signal>toggled(bool)</signal> + <receiver>m_displayPicture</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>m_useWebcamPort</sender> + <signal>toggled(bool)</signal> + <receiver>m_webcamPort</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<tabstops> + <tabstop>tabWidget3</tabstop> + <tabstop>optionOverrideServer</tabstop> + <tabstop>m_serverName</tabstop> + <tabstop>m_serverPort</tabstop> + <tabstop>optionUseHttpMethod</tabstop> + <tabstop>m_login</tabstop> + <tabstop>m_autologin</tabstop> + <tabstop>buttonRegister</tabstop> + <tabstop>m_displayName</tabstop> + <tabstop>m_phw</tabstop> + <tabstop>m_phh</tabstop> + <tabstop>m_phm</tabstop> + <tabstop>m_useDisplayPicture</tabstop> + <tabstop>m_selectImage</tabstop> + <tabstop>m_AL</tabstop> + <tabstop>m_blockButton</tabstop> + <tabstop>m_allowButton</tabstop> + <tabstop>m_BL</tabstop> + <tabstop>m_blp</tabstop> + <tabstop>m_RLButton</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kopetepasswordwidget.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>klineedit.h</includehint> +</includehints> +</UI> diff --git a/kopete/protocols/msn/ui/msneditaccountwidget.cpp b/kopete/protocols/msn/ui/msneditaccountwidget.cpp new file mode 100644 index 00000000..41a007f9 --- /dev/null +++ b/kopete/protocols/msn/ui/msneditaccountwidget.cpp @@ -0,0 +1,369 @@ +/* + msneditaccountwidget.cpp - MSN Account Widget + + Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org> + Copyright (c) 2003 by Martijn Klingens <klingens@kde.org> + + Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include "msneditaccountwidget.h" + +#include <tqcheckbox.h> +#include <tqgroupbox.h> +#include <tqimage.h> +#include <tqlabel.h> +#include <tqlayout.h> +#include <tqlineedit.h> +#include <tqlistbox.h> +#include <tqpushbutton.h> +#include <tqregexp.h> +#include <tqspinbox.h> +#include <kcombobox.h> + +#include <kautoconfig.h> +#include <tdefiledialog.h> +#include <tdelocale.h> +#include <tdemessagebox.h> +#include <kstandarddirs.h> +#include <tdeio/netaccess.h> +#include <kdebug.h> +#include <kpassdlg.h> +#include <krun.h> +#include <tdeconfig.h> +#include <kpixmapregionselectordialog.h> + +#include "kopeteuiglobal.h" +#include "kopeteglobal.h" + +#include "kopetepasswordwidget.h" +#include "kopeteaccountmanager.h" + +#include "msnaccount.h" +#include "msncontact.h" +#include "msneditaccountui.h" +#include "msnnotifysocket.h" +#include "msnprotocol.h" + +class MSNEditAccountWidgetPrivate +{ +public: + MSNProtocol *protocol; + KAutoConfig *autoConfig; + MSNEditAccountUI *ui; + + TQString pictureUrl; + TQImage pictureData; +}; + +MSNEditAccountWidget::MSNEditAccountWidget( MSNProtocol *proto, Kopete::Account *account, TQWidget *parent, const char * /* name */ ) +: TQWidget( parent ), KopeteEditAccountWidget( account ) +{ + d = new MSNEditAccountWidgetPrivate; + + d->protocol=proto; + + ( new TQVBoxLayout( this, 0, 0 ) )->setAutoAdd( true ); + + d->ui = new MSNEditAccountUI( this ); + + d->autoConfig = new KAutoConfig( TQT_TQOBJECT(d->ui) ); + d->autoConfig->addWidget( d->ui->global_settings_page, "MSN" ); + d->autoConfig->addWidget( d->ui->privacy_page, "MSN" ); + //the JabberAccount need to be saved as text, and can't be handled by kautoconfig + d->autoConfig->ignoreSubWidget( d->ui->JabberAccount ); + d->autoConfig->retrieveSettings( true ); + + //Get a list of all jabber accounts + TDEGlobal::config()->setGroup("MSN"); + TQString jab_account=TDEGlobal::config()->readEntry("JabberAccount"); + + TQPtrList<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts(); + for(Kopete::Account *a=accounts.first() ; a; a=accounts.next() ) + { + if(a->protocol()->pluginId()=="JabberProtocol") + { + d->ui->JabberAccount->insertItem(a->accountId()); + if( jab_account.isEmpty() ) + jab_account=a->accountId(); + } + } + d->ui->JabberAccount->setCurrentText(jab_account); + + // FIXME: actually, I don't know how to set fonts for qlistboxitem - Olivier + d->ui->label_font->hide(); + + // default fields + if ( account ) + { + TDEConfigGroup * config=account->configGroup(); + + d->ui->m_login->setText( account->accountId() ); + d->ui->m_password->load( &static_cast<MSNAccount *>(account)->password() ); + + //remove me after we can change account ids (Matt) + d->ui->m_login->setDisabled( true ); + d->ui->m_autologin->setChecked( account->excludeConnect() ); + if ( ( static_cast<MSNAccount*>(account)->serverName() != "messenger.hotmail.com" ) || ( static_cast<MSNAccount*>(account)->serverPort() != 1863) ) { + d->ui->optionOverrideServer->setChecked( true ); + } + + d->ui->m_webcamPort->setDisabled(true); + uint port=config->readNumEntry("WebcamPort" ,0); + d->ui->m_useWebcamPort->setChecked( port != 0); + d->ui->m_webcamPort->setValue( port != 0 ? port : 6891 ); + + d->ui->optionUseHttpMethod->setChecked( static_cast<MSNAccount*>(account)->useHttpMethod() ); + + MSNContact *myself = static_cast<MSNContact *>( account->myself() ); + + d->ui->m_displayName->setText( myself->property( Kopete::Global::Properties::self()->nickName()).value().toString() ); + d->ui->m_phw->setText( config->readEntry("PHW") ); + d->ui->m_phm->setText( config->readEntry("PHM") ); + d->ui->m_phh->setText( config->readEntry("PHH") ); + + bool connected = account->isConnected(); + if ( connected ) + { + d->ui->m_warning_1->hide(); + d->ui->m_warning_2->hide(); + } + d->ui->m_phones->setEnabled( connected ); + d->ui->m_displayName->setEnabled( connected ); + d->ui->m_allowButton->setEnabled( connected ); + d->ui->m_blockButton->setEnabled( connected ); + + MSNAccount *m_account = static_cast<MSNAccount*>( account ); + d->ui->m_serverName->setText( m_account->serverName() ); + d->ui->m_serverPort->setValue( m_account->serverPort() ); + + TQStringList blockList = config->readListEntry( "blockList" ); + TQStringList allowList = config->readListEntry( "allowList" ); + //TQStringList reverseList = config->readListEntry("reverseList" ); + + for ( TQStringList::Iterator it = blockList.begin(); it != blockList.end(); ++it ) + d->ui->m_BL->insertItem( *it ); + + for ( TQStringList::Iterator it = allowList.begin(); it != allowList.end(); ++it ) + d->ui->m_AL->insertItem( *it ); + + d->ui->m_blp->setChecked( config->readEntry( "BLP" ) == "BL" ); + + d->pictureUrl = locateLocal( "appdata", "msnpicture-" + + account->accountId().lower().replace( TQRegExp("[./~]" ), "-" ) + ".png" ); + d->ui->m_displayPicture->setPixmap( d->pictureUrl ); + + d->ui->m_useDisplayPicture->setChecked( config->readBoolEntry( "exportCustomPicture" )); + + // Global Identity + d->ui->m_globalIdentity->setChecked( config->readBoolEntry("ExcludeGlobalIdentity", false) ); + } + else + { + d->ui->tab_contacts->setDisabled( true ); + d->ui->m_displayName->setDisabled( true ); + d->ui->m_phones->setDisabled( true ); + } + + connect( d->ui->m_allowButton, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotAllow() ) ); + connect( d->ui->m_blockButton, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotBlock() ) ); + connect( d->ui->m_selectImage, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotSelectImage() ) ); + connect( d->ui->m_RLButton, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotShowReverseList() ) ); + connect( d->ui->buttonRegister, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotOpenRegister())); + TQWidget::setTabOrder( d->ui->m_login, d->ui->m_password->mRemembered ); + TQWidget::setTabOrder( d->ui->m_password->mRemembered, d->ui->m_password->mPassword ); + TQWidget::setTabOrder( d->ui->m_password->mPassword, d->ui->m_autologin ); +} + +MSNEditAccountWidget::~MSNEditAccountWidget() +{ + delete d; +} + +Kopete::Account * MSNEditAccountWidget::apply() +{ + d->autoConfig->saveSettings(); + TDEGlobal::config()->setGroup("MSN"); + TDEGlobal::config()->writeEntry("JabberAccount", d->ui->JabberAccount->currentText()); + + if ( !account() ) + setAccount( new MSNAccount( d->protocol, d->ui->m_login->text() ) ); + + TDEConfigGroup *config=account()->configGroup(); + + account()->setExcludeConnect( d->ui->m_autologin->isChecked() ); + d->ui->m_password->save( &static_cast<MSNAccount *>(account())->password() ); + + config->writeEntry( "exportCustomPicture", d->ui->m_useDisplayPicture->isChecked() ); + if (d->ui->optionOverrideServer->isChecked() ) { + config->writeEntry( "serverName", d->ui->m_serverName->text() ); + config->writeEntry( "serverPort", d->ui->m_serverPort->value() ); + } + else { + config->writeEntry( "serverName", "messenger.hotmail.com" ); + config->writeEntry( "serverPort", "1863" ); + } + + config->writeEntry( "useHttpMethod", d->ui->optionUseHttpMethod->isChecked() ); + + if(d->ui->m_useWebcamPort->isChecked()) + config->writeEntry( "WebcamPort" , d->ui->m_webcamPort->value() ); + else + config->writeEntry( "WebcamPort" , 0 ); + + // Global Identity + config->writeEntry( "ExcludeGlobalIdentity", d->ui->m_globalIdentity->isChecked() ); + + // Save the avatar image + if( d->ui->m_useDisplayPicture->isChecked() && !d->pictureData.isNull() ) + { + d->pictureUrl = locateLocal( "appdata", "msnpicture-" + + account()->accountId().lower().replace( TQRegExp("[./~]" ), "-" ) + ".png" ); + if ( d->pictureData.save( d->pictureUrl, "PNG" ) ) + { + static_cast<MSNAccount *>( account() )->setPictureUrl( d->pictureUrl ); + } + else + { + KMessageBox::sorry( this, i18n( "<qt>An error occurred when trying to change the display picture.<br>" + "Make sure that you have selected a correct image file</qt>" ), i18n( "MSN Plugin" ) ); + } + } + + static_cast<MSNAccount *>( account() )->resetPictureObject(); + + if ( account()->isConnected() ) + { + MSNContact *myself = static_cast<MSNContact *>( account()->myself() ); + MSNNotifySocket *notify = static_cast<MSNAccount *>( account() )->notifySocket(); + if ( d->ui->m_displayName->text() != myself->property( Kopete::Global::Properties::self()->nickName()).value().toString() ) + static_cast<MSNAccount *>( account() )->setPublicName( d->ui->m_displayName->text() ); + + if ( notify ) + { + if ( d->ui->m_phw->text() != myself->phoneWork() && ( !d->ui->m_phw->text().isEmpty() || !myself->phoneWork().isEmpty() ) ) + notify->changePhoneNumber( "PHW", d->ui->m_phw->text() ); + if( d->ui->m_phh->text() != myself->phoneHome() && ( !d->ui->m_phh->text().isEmpty() || !myself->phoneHome().isEmpty() ) ) + notify->changePhoneNumber( "PHH", d->ui->m_phh->text() ); + if( d->ui->m_phm->text() != myself->phoneMobile() && ( !d->ui->m_phm->text().isEmpty() || !myself->phoneMobile().isEmpty() ) ) + notify->changePhoneNumber( "PHM", d->ui->m_phm->text() ); + // (the && .isEmpty is because one can be null and the other empty) + + if ( ( config->readEntry("BLP") == "BL" ) != d->ui->m_blp->isChecked() ) + { + // Yes, I know, calling sendCommand here is not very clean - Olivier + notify->sendCommand( "BLP", d->ui->m_blp->isChecked() ? "BL" : "AL" ); + } + } + } + return account(); +} + +bool MSNEditAccountWidget::validateData() +{ + TQString userid = d->ui->m_login->text(); + if ( MSNProtocol::validContactId( userid ) ) + return true; + + KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry, + i18n( "<qt>You must enter a valid email address.</qt>" ), i18n( "MSN Plugin" ) ); + return false; +} + +void MSNEditAccountWidget::slotAllow() +{ + //TODO: play with multiple selection + TQListBoxItem *item = d->ui->m_BL->selectedItem(); + if ( !item ) + return; + + TQString handle = item->text(); + + MSNNotifySocket *notify = static_cast<MSNAccount *>( account() )->notifySocket(); + if ( !notify ) + return; + notify->removeContact( handle, MSNProtocol::BL, TQString(), TQString() ); + + d->ui->m_BL->takeItem( item ); + d->ui->m_AL->insertItem( item ); +} + +void MSNEditAccountWidget::slotBlock() +{ + //TODO: play with multiple selection + TQListBoxItem *item = d->ui->m_AL->selectedItem(); + if ( !item ) + return; + + TQString handle = item->text(); + + MSNNotifySocket *notify = static_cast<MSNAccount *>( account() )->notifySocket(); + if ( !notify ) + return; + + notify->removeContact( handle, MSNProtocol::AL, TQString(), TQString() ); + + d->ui->m_AL->takeItem( item ); + d->ui->m_BL->insertItem( item ); +} + +void MSNEditAccountWidget::slotShowReverseList() +{ + TQStringList reverseList = account()->configGroup()->readListEntry( "reverseList" ); + KMessageBox::informationList( this, i18n( "Here you can see a list of contacts who added you to their contact list" ), reverseList, + i18n( "Reverse List - MSN Plugin" ) ); +} + +void MSNEditAccountWidget::slotSelectImage() +{ + TQString path = 0; + bool remoteFile = false; + KURL filePath = KFileDialog::getImageOpenURL( TQString(), this, i18n( "MSN Display Picture" ) ); + if( filePath.isEmpty() ) + return; + + if( !filePath.isLocalFile() ) { + if(!TDEIO::NetAccess::download( filePath, path, this )) { + KMessageBox::sorry( this, i18n( "Downloading of display image failed" ), i18n( "MSN Plugin" ) ); + return; + } + remoteFile = true; + } + else path = filePath.path(); + + TQImage img( path ); + img = KPixmapRegionSelectorDialog::getSelectedImage( TQPixmap(img), 96, 96, this ); + + if(!img.isNull()) + { + img = MSNProtocol::protocol()->scalePicture(img); + + d->ui->m_displayPicture->setPixmap( TQPixmap(img) ); + d->pictureData = img; + } + else + { + KMessageBox::sorry( this, i18n( "<qt>An error occurred when trying to change the display picture.<br>" + "Make sure that you have selected a correct image file</qt>" ), i18n( "MSN Plugin" ) ); + } + if( remoteFile ) TDEIO::NetAccess::removeTempFile( path ); +} + +void MSNEditAccountWidget::slotOpenRegister() +{ + KRun::runURL( "http://register.passport.net/", "text/html" ); +} + +#include "msneditaccountwidget.moc" + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/msn/ui/msneditaccountwidget.h b/kopete/protocols/msn/ui/msneditaccountwidget.h new file mode 100644 index 00000000..730eea3f --- /dev/null +++ b/kopete/protocols/msn/ui/msneditaccountwidget.h @@ -0,0 +1,60 @@ +/* + msneditaccountwidget.h - MSN Account Widget + + Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org> + Copyright (c) 2003 by Martijn Klingens <klingens@kde.org> + + Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef MSNEDITACCOUNTWIDEGET_H +#define MSNEDITACCOUNTWIDEGET_H + +#include <tqwidget.h> + +#include "editaccountwidget.h" + +namespace Kopete { class Account; } + +class MSNProtocol; + +class MSNEditAccountWidgetPrivate; + +/** + * @author Olivier Goffart <ogoffart @ kde.org> + */ +class MSNEditAccountWidget : public TQWidget, public KopeteEditAccountWidget +{ + Q_OBJECT + + +public: + MSNEditAccountWidget( MSNProtocol *proto, Kopete::Account *account, TQWidget *parent = 0, const char *name = 0 ); + ~MSNEditAccountWidget(); + virtual bool validateData(); + virtual Kopete::Account * apply(); + +private slots: + void slotAllow(); + void slotBlock(); + void slotShowReverseList(); + void slotSelectImage(); + void slotOpenRegister(); + +private: + MSNEditAccountWidgetPrivate *d; +}; + +#endif + +// vim: set noet ts=4 sts=4 sw=4: + diff --git a/kopete/protocols/msn/ui/msninfo.ui b/kopete/protocols/msn/ui/msninfo.ui new file mode 100644 index 00000000..59d50be6 --- /dev/null +++ b/kopete/protocols/msn/ui/msninfo.ui @@ -0,0 +1,221 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>MSNInfo</class> +<widget class="TQWidget"> + <property name="name"> + <cstring>MSNInfo</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>457</width> + <height>360</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>Layout22</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="TQLabel"> + <property name="name"> + <cstring>TextLabel2_2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>4</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Email address:</string> + </property> + </widget> + <widget class="TQLineEdit"> + <property name="name"> + <cstring>m_id</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>Layout22_2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <widget class="TQLabel"> + <property name="name"> + <cstring>TextLabel2_2_2</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>4</hsizetype> + <vsizetype>4</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Display name:</string> + </property> + </widget> + <widget class="TQLineEdit"> + <property name="name"> + <cstring>m_displayName</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + <widget class="TQLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Personal message:</string> + </property> + </widget> + <widget class="TQLineEdit"> + <property name="name"> + <cstring>m_personalMessage</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </hbox> + </widget> + <widget class="TQGroupBox"> + <property name="name"> + <cstring>GroupBox2</cstring> + </property> + <property name="title"> + <string>Phones</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="TQLabel" row="1" column="0"> + <property name="name"> + <cstring>TextLabel5</cstring> + </property> + <property name="text"> + <string>Home:</string> + </property> + </widget> + <widget class="TQLabel" row="0" column="0"> + <property name="name"> + <cstring>TextLabel6</cstring> + </property> + <property name="text"> + <string>Work:</string> + </property> + </widget> + <widget class="TQLineEdit" row="0" column="1"> + <property name="name"> + <cstring>m_phw</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="TQLineEdit" row="1" column="1"> + <property name="name"> + <cstring>m_phh</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="TQLabel" row="2" column="0"> + <property name="name"> + <cstring>TextLabel7</cstring> + </property> + <property name="text"> + <string>Mobile:</string> + </property> + </widget> + <widget class="TQLineEdit" row="2" column="1"> + <property name="name"> + <cstring>m_phm</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </grid> + </widget> + <widget class="TQCheckBox"> + <property name="name"> + <cstring>m_reversed</cstring> + </property> + <property name="text"> + <string>I am on &the contact list of this contact</string> + </property> + <property name="toolTip" stdset="0"> + <string>Show whether you are on the contact list of this user</string> + </property> + <property name="whatsThis" stdset="0"> + <string>If this box is checked, you are on this user's contact list. +If not, the user has not added you to their list, or has removed you.</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>Spacer10</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kopete/protocols/msn/webcam.cpp b/kopete/protocols/msn/webcam.cpp new file mode 100644 index 00000000..60046d17 --- /dev/null +++ b/kopete/protocols/msn/webcam.cpp @@ -0,0 +1,891 @@ +/* + Copyright (c) 2005 by Olivier Goffart <ogoffart@ kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + + +#include "webcam.h" + +#if MSN_WEBCAM + +#include <stdlib.h> +#include <kdebug.h> +#include <tqregexp.h> +#include <kbufferedsocket.h> +#include <tdelocale.h> +#include <kserversocket.h> +#include <tdemessagebox.h> +#include <tqlabel.h> +#include <tqguardedptr.h> +#include <tqtimer.h> +#include <tqevent.h> +#include <tqdatetime.h> +#include <tdeconfig.h> + +#include "dispatcher.h" + +#include "mimicwrapper.h" +#include "msnwebcamdialog.h" + + +#include "avdevice/videodevicepool.h" + +using namespace KNetwork; + +namespace P2P { + +Webcam::Webcam(Who who, const TQString& to, Dispatcher *parent, TQ_UINT32 sessionId) + : TransferContext(to,parent,sessionId) , m_who(who) , m_timerId(0) +{ + setType(P2P::WebcamType); + m_direction = Incoming; + m_listener = 0l; + m_webcamSocket=0L; +// m_webcamState=wsNegotiating; + + m_mimic=0L; + m_widget=0L; + + TDEConfig *config = TDEGlobal::config(); + config->setGroup( "MSN" ); + + // Read the configuration to get the number of frame per second to send + int webCamFps=config->readNumEntry("WebcamFPS", 25); + m_timerFps = 1000 / webCamFps; +} + +Webcam::~Webcam() +{ + kdDebug(14140) << k_funcinfo<< "################################################" << endl; + m_dispatcher=0l; + delete m_mimic; + delete m_webcamSocket; + delete m_widget; + + if(m_timerId != 0) //if we were sending + { + Kopete::AV::VideoDevicePool *videoDevice = Kopete::AV::VideoDevicePool::self(); + videoDevice->stopCapturing(); + videoDevice->close(); + } + +} + +void Webcam::askIncommingInvitation() +{ + m_direction = Incoming; + //protect, in case this is deleted when the messagebox is active + TQGuardedPtr<Webcam> _this = this; + TQString message= (m_who==wProducer) ? + i18n("<qt>The contact %1 wants to see <b>your</b> webcam, do you want them to see it?</qt>") : + i18n("The contact %1 wants to show you his/her webcam, do you want to see it?") ; + int result=KMessageBox::questionYesNo( 0L , message.arg(m_recipient), + i18n("Webcam invitation - Kopete MSN Plugin") , i18n("Accept") , i18n("Decline")); + if(!_this) + return; + + TQString content = TQString("SessionID: %1\r\n\r\n").arg(m_sessionId); + if(result==KMessageBox::Yes) + { + //Send two message, an OK, and an invite. + //Normaly, the user should decline the invite (i hope) + + // Send a 200 OK message to the recipient. + sendMessage(OK, content); + + + //send an INVITE message we want the user decline + //need to change the branch of the second message + m_branch=Uid::createUid(); + m_state = Negotiation; //set type to application/x-msnmsgr-transreqbody + + content=TQString("Bridges: TRUDPv1 TCPv1\r\n" + "NetID: -1280904111\r\n" + "Conn-Type: Firewall\r\n" + "UPnPNat: false\r\n" + "ICF: false\r\n\r\n"); + + sendMessage(INVITE, content); + + } + else + { + //Decline the invitation + sendMessage(DECLINE, content); + m_state=Finished; + } +} + +void Webcam::sendBYEMessage() +{ + m_state=Finished; + TQString content="Context: dAMAgQ==\r\n"; + sendMessage(BYE,content); + + //If ever the opposite client was dead or something, we'll ack anyway, so everything get cleaned + TQTimer::singleShot(60*1000 , this, TQT_SLOT(acknowledged())); +} + + + +void Webcam::acknowledged() +{ + kdDebug(14140) << k_funcinfo << endl; + + switch(m_state) + { + case Invitation: + { +// m_state=Negotiation; + break; + } + + /* + case Negotiation: + { + if(m_type == UserDisplayIcon) + { + <<< Data preparation acknowledge message. + m_state = DataTransfer; + m_identifier++; + Start sending data. + slotSendData(); + } + break; + } + + case DataTransfer: + NOTE <<< Data acknowledged message. + <<< Bye message should follow. + if(m_type == File) + { + if(m_handshake == 0x01) + { + Data handshake acknowledge message. + Start sending data. + slotSendData(); + } + else if(m_handshake == 0x02) + { + Data acknowledge message. + Send the recipient a BYE message. + m_state = Finished; + sendMessage(BYE, "\r\n"); + } + } + + break; + */ + case Finished: + //BYE or DECLINE acknowledge message. + m_dispatcher->detach(this); + break; + default: + break; + } +} + + + + +void Webcam::processMessage(const Message& message) +{ + if(message.header.dataOffset+message.header.dataSize >= message.header.totalDataSize) + acknowledge( message ); //aknowledge if needed + + if(message.applicationIdentifier != 4l) + { + TQString body = TQCString(message.body.data(), message.header.dataSize); + kdDebug(14141) << k_funcinfo << "received, " << body << endl; + + if(body.startsWith("MSNSLP/1.0 200 OK")) + { + m_direction = Outgoing; + } + if(body.startsWith("INVITE")) + { + if(m_direction == Outgoing) + { + TQRegExp regex(";branch=\\{([0-9A-F\\-]*)\\}\r\n"); + regex.search(body); + m_branch=regex.cap(1); + //decline + sendMessage(DECLINE); + makeSIPMessage("syn",0x17,0x2a,0x01); + } + } + else if(body.startsWith("MSNSLP/1.0 603 DECLINE")) + { + //if it is the declinaison of the second invite message, we have to don't care + //TODO anyway, if it's the declinaison of our invitation, we have to something + } + else if(body.startsWith("BYE")) + { + m_state = Finished; + + // Dispose of this transfer context. + m_dispatcher->detach(this); + } + return; + } + + + + //Let's take the fun, we entering into the delicious webcam negotiation binary protocol + + //well, there is maybe better to take utf16, but it's ascii, so no problem. + TQByteArray dataMessage=message.body; + +#if 0 + TQString echoS=""; + unsigned int f=0; + while(f<dataMessage.size()) + { + echoS+="\n"; + for(unsigned int q=0; q<16 ; q++) + { + if(q+f<dataMessage.size()) + { + unsigned int N=(unsigned int) (dataMessage[q+f]); + if(N<16) + echoS+="0"; + echoS+=TQString::number( N ,16)+" "; + } + else + echoS+=" "; + } + echoS+=" "; + + for(unsigned int q=0; (q<16 && (q+f)<dataMessage.size()) ; q++) + { + unsigned char X=dataMessage[q+f]; + char C=((char)(( X<128 && X>31 ) ? X : '.')); + echoS+=TQString::fromLatin1(&C,1); + } + f+=16; + } + kdDebug(14141) << k_funcinfo << dataMessage.size() << echoS << endl; +#endif + + + + + + for(uint pos=m_content.isNull() ? 10 : 0; pos<dataMessage.size(); pos+=2) + { + if(dataMessage[pos] !=0 ) + m_content+=dataMessage[pos]; + } + + if(message.header.dataOffset+message.header.dataSize < message.header.totalDataSize) + return; + + kdDebug(14141) << k_funcinfo << "Message contents: " << m_content << "\n" << endl; + if(m_content.startsWith("syn")) + { + if(m_direction == Incoming) + makeSIPMessage("syn",0x17,0x2a,0x01); + else + makeSIPMessage("ack",0xea,0x00,0x00); + } + else if(m_content.startsWith("ack")) + { + if(m_direction == Incoming) + makeSIPMessage("ack",0xea,0x00,0x00); + + if(m_who==wProducer) + { + uint sess=rand()%1000+5000; + uint rid=rand()%100+50; + m_myAuth=TQString("recipientid=%1&sessionid=%2\r\n\r\n").arg(rid).arg(sess); + kdDebug(14140) << k_funcinfo << "m_myAuth= " << m_myAuth << endl; + TQString producerxml=xml(sess , rid); + kdDebug(14140) << k_funcinfo << "producerxml= " << producerxml << endl; + makeSIPMessage(producerxml); + } + } + else if(m_content.contains("<producer>") || m_content.contains("<viewer>")) + { + TQRegExp rx("<rid>([0-9]*)</rid>.*<session>([0-9]*)</session>"); + rx.search(m_content); + TQString rid=rx.cap(1); + TQString sess=rx.cap(2); + if(m_content.contains("<producer>")) + { + + TQString viewerxml=xml(sess.toUInt() , rid.toUInt()); + kdDebug(14140) << k_funcinfo << "vewerxml= " << viewerxml << endl; + makeSIPMessage( viewerxml ,0x00,0x09,0x00 ); + m_peerAuth=m_myAuth=TQString("recipientid=%1&sessionid=%2\r\n\r\n").arg(rid,sess); + kdDebug(14140) << k_funcinfo << "m_auth= " << m_myAuth << endl; + } + else + { + m_peerAuth=TQString("recipientid=%1&sessionid=%2\r\n\r\n").arg(rid,sess); + + makeSIPMessage("receivedViewerData", 0xec , 0xda , 0x03); + } + + if(!m_listener) + { + //it should have been creed in xml + sendBYEMessage(); + return; + } + //m_listener->setResolutionEnabled(true); + // Create the callback that will try to accept incoming connections. + TQObject::connect(m_listener, TQT_SIGNAL(readyAccept()), this, TQT_SLOT(slotAccept())); + TQObject::connect(m_listener, TQT_SIGNAL(gotError(int)), this, TQT_SLOT(slotListenError(int))); + // Listen for incoming connections. + bool isListening = m_listener->listen(); + kdDebug(14140) << k_funcinfo << (isListening ? TQString("listening %1").arg(m_listener->localAddress().toString()) : TQString("not listening")) << endl; + + rx=TQRegExp("<tcpport>([^<]*)</tcpport>"); + rx.search(m_content); + TQString port1=rx.cap(1); + if(port1=="0") + port1=TQString(); + + rx=TQRegExp("<tcplocalport>([^<]*)</tcplocalport>"); + rx.search(m_content); + TQString port2=rx.cap(1); + if(port2==port1 || port2=="0") + port2=TQString(); + + rx=TQRegExp("<tcpexternalport>([^<]*)</tcpexternalport>"); + rx.search(m_content); + TQString port3=rx.cap(1); + if(port3==port1 || port3==port2 || port3=="0") + port3=TQString(); + + int an=0; + while(true) + { + an++; + if(!m_content.contains( TQString("<tcpipaddress%1>").arg(an) )) + break; + rx=TQRegExp(TQString("<tcpipaddress%1>([^<]*)</tcpipaddress%2>").arg(an).arg(an)); + rx.search(m_content); + TQString ip=rx.cap(1); + if(ip.isNull()) + continue; + + if(!port1.isNull()) + { + kdDebug(14140) << k_funcinfo << "trying to connect on " << ip <<":" << port1 << endl; + TDEBufferedSocket *sock=new TDEBufferedSocket( ip, port1, this ); + m_allSockets.append(sock); + TQObject::connect( sock, TQT_SIGNAL( connected( const KResolverEntry&) ), this, TQT_SLOT( slotSocketConnected() ) ); + TQObject::connect( sock, TQT_SIGNAL( gotError(int)), this, TQT_SLOT(slotSocketError(int))); + sock->connect(ip, port1); + kdDebug(14140) << k_funcinfo << "okok " << sock << " - " << sock->peerAddress().toString() << " ; " << sock->localAddress().toString() << endl; + } + if(!port2.isNull()) + { + kdDebug(14140) << k_funcinfo << "trying to connect on " << ip <<":" << port2 << endl; + TDEBufferedSocket *sock=new TDEBufferedSocket( ip, port2, this ); + m_allSockets.append(sock); + TQObject::connect( sock, TQT_SIGNAL( connected( const KResolverEntry&) ), this, TQT_SLOT( slotSocketConnected() ) ); + TQObject::connect( sock, TQT_SIGNAL( gotError(int)), this, TQT_SLOT(slotSocketError(int))); + sock->connect(ip, port2); + } + if(!port3.isNull()) + { + kdDebug(14140) << k_funcinfo << "trying to connect on " << ip <<":" << port3 << endl; + TDEBufferedSocket *sock=new TDEBufferedSocket( ip, port3, this ); + m_allSockets.append(sock); + TQObject::connect( sock, TQT_SIGNAL( connected( const KResolverEntry&) ), this, TQT_SLOT( slotSocketConnected() ) ); + TQObject::connect( sock, TQT_SIGNAL( gotError(int)), this, TQT_SLOT(slotSocketError(int))); + sock->connect(ip, port3); + } + } + TQValueList<TDEBufferedSocket*>::iterator it; + for ( it = m_allSockets.begin(); it != m_allSockets.end(); ++it ) + { + TDEBufferedSocket *sock=(*it); + + //sock->enableRead( false ); + kdDebug(14140) << k_funcinfo << "connect to " << sock << " - "<< sock->peerAddress().toString() << " ; " << sock->localAddress().toString() << endl; + } + } + else if(m_content.contains("receivedViewerData")) + { + //I'm happy you received the xml i sent, really. + } + else + error(); + m_content=TQString(); +} + +void Webcam::makeSIPMessage(const TQString &message, TQ_UINT8 XX, TQ_UINT8 YY , TQ_UINT8 ZZ) +{ + TQByteArray dataMessage; //(12+message.length()*2); + TQDataStream writer(dataMessage, IO_WriteOnly); + writer.setByteOrder(TQDataStream::LittleEndian); + writer << (TQ_UINT8)0x80; + writer << (TQ_UINT8)XX; + writer << (TQ_UINT8)YY; + writer << (TQ_UINT8)ZZ; + writer << (TQ_UINT8)0x08; + writer << (TQ_UINT8)0x00; + writer << message+'\0'; + //writer << (TQ_UINT16)0x0000; + + /*TQString echoS=""; + unsigned int f=0; + while(f<dataMessage.size()) + { + echoS+="\n"; + for(unsigned int q=0; q<16 ; q++) + { + if(q+f<dataMessage.size()) + { + unsigned int N=(unsigned int) (dataMessage[q+f]); + if(N<16) + echoS+="0"; + echoS+=TQString::number( N ,16)+" "; + } + else + echoS+=" "; + } + echoS+=" "; + + for(unsigned int q=0; (q<16 && (q+f)<dataMessage.size()) ; q++) + { + unsigned char X=dataMessage[q+f]; + char C=((char)(( X<128 && X>31 ) ? X : '.')); + echoS+=TQString::fromLatin1(&C,1); + } + f+=16; + } + kdDebug(14141) << k_funcinfo << dataMessage.size() << echoS << endl;*/ + + + sendBigP2PMessage(dataMessage); +} + +void Webcam::sendBigP2PMessage( const TQByteArray & dataMessage) +{ + unsigned int size=m_totalDataSize=dataMessage.size(); + m_offset=0; + ++m_identifier; + + for(unsigned int f=0;f<size;f+=1200) + { + m_offset=f; + TQByteArray dm2; + dm2.duplicate(dataMessage.data()+m_offset, TQMIN(1200,m_totalDataSize-m_offset)); + sendData( dm2 ); + m_offset+=dm2.size(); + } + m_offset=0; + m_totalDataSize=0; +} + + + +TQString Webcam::xml(uint session , uint rid) +{ + TQString who= ( m_who == wProducer ) ? TQString("producer") : TQString("viewer"); + + TQString ip; + + uint ip_number=1; + TQStringList::iterator it; + TQStringList ips=m_dispatcher->localIp(); + for ( it = ips.begin(); it != ips.end(); ++it ) + { + ip+=TQString("<tcpipaddress%1>%2</tcpipaddress%3>").arg(ip_number).arg(*it).arg(ip_number); + ++ip_number; + } + + TQString port = TQString::number(getAvailablePort()); + + m_listener = new TDEServerSocket(port, this) ; + + return "<" + who + "><version>2.0</version><rid>"+TQString::number(rid)+"</rid><udprid>"+TQString::number(rid+1)+"</udprid><session>"+TQString::number(session)+"</session><ctypes>0</ctypes><cpu>2931</cpu>" + + "<tcp><tcpport>"+port+"</tcpport>\t\t\t\t\t\t\t\t <tcplocalport>"+port+"</tcplocalport>\t\t\t\t\t\t\t\t <tcpexternalport>"+port+"</tcpexternalport>"+ip+"</tcp>"+ + "<udp><udplocalport>7786</udplocalport><udpexternalport>31863</udpexternalport><udpexternalip>"+ ip +"</udpexternalip><a1_port>31859</a1_port><b1_port>31860</b1_port><b2_port>31861</b2_port><b3_port>31862</b3_port><symmetricallocation>1</symmetricallocation><symmetricallocationincrement>1</symmetricallocationincrement><udpversion>1</udpversion><udpinternalipaddress1>127.0.0.1</udpinternalipaddress1></udp>"+ + "<codec></codec><channelmode>1</channelmode></"+who+">\r\n\r\n"; +} + +int Webcam::getAvailablePort() +{ + TDEConfig *config = TDEGlobal::config(); + config->setGroup( "MSN" ); + TQString basePort=config->readEntry("WebcamPort"); + if(basePort.isEmpty() || basePort == "0" ) + basePort="6891"; + + uint firstport = basePort.toInt(); + uint maxOffset=config->readUnsignedNumEntry("WebcamMaxPortOffset", 10); + uint lastport = firstport + maxOffset; + + // try to find an available port + // + TDEServerSocket *ss = new TDEServerSocket(); + ss->setFamily(KResolver::InetFamily); + bool found = false; + unsigned int port = firstport; + for( ; port <= lastport; ++port) { + ss->setAddress( TQString::number( port ) ); + bool success = ss->listen(); + if( found = ( success && ss->error() == TDESocketBase::NoError ) ) + break; + ss->close(); + } + delete ss; + + + kdDebug(14140) << k_funcinfo<< "found available port : " << port << endl; + + return port; +} + + +/* ---------- Now functions about the dirrect connection --------- */ + +void Webcam::slotSocketConnected() +{ + kdDebug(14140) << k_funcinfo <<"##########################" << endl; + + m_webcamSocket=const_cast<TDEBufferedSocket*>(static_cast<const TDEBufferedSocket*>(sender())); + if(!m_webcamSocket) + return; + + kdDebug(14140) << k_funcinfo << "Connection established on " << m_webcamSocket->peerAddress().toString() << " ; " << m_webcamSocket->localAddress().toString() << endl; + + m_webcamSocket->setBlocking(false); + m_webcamSocket->enableRead(true); + m_webcamSocket->enableWrite(false); + + // Create the callback that will try to read bytes from the accepted socket. + TQObject::connect(m_webcamSocket, TQT_SIGNAL(readyRead()), this, TQT_SLOT(slotSocketRead())); + // Create the callback that will try to handle the socket close event. + TQObject::connect(m_webcamSocket, TQT_SIGNAL(closed()), this, TQT_SLOT(slotSocketClosed())); + // Create the callback that will try to handle the socket error event. +// TQObject::connect(m_webcamSocket, TQT_SIGNAL(gotError(int)), this, TQT_SLOT(slotSocketError(int))); + + m_webcamStates[m_webcamSocket]=wsConnected; + TQCString to_send=m_peerAuth.utf8(); + m_webcamSocket->writeBlock(to_send.data(), to_send.length()); + kdDebug(14140) << k_funcinfo << "sending "<< m_peerAuth << endl; + +} + + +void Webcam::slotAccept() +{ + // Try to accept an incoming connection from the sending client. + m_webcamSocket = static_cast<TDEBufferedSocket*>(m_listener->accept()); + if(!m_webcamSocket) + { + // NOTE If direct connection fails, the sending + // client wil transfer the file data through the + // existing session. + kdDebug(14140) << k_funcinfo << "Direct connection failed." << endl; + // Close the listening endpoint. +// m_listener->close(); + return; + } + + kdDebug(14140) << k_funcinfo << "################################ Direct connection established." << endl; + + // Set the socket to non blocking, + // enable the ready read signal and disable + // ready write signal. + // NOTE readyWrite consumes too much cpu usage. + m_webcamSocket->setBlocking(false); + m_webcamSocket->enableRead(true); + m_webcamSocket->enableWrite(false); + + // Create the callback that will try to read bytes from the accepted socket. + TQObject::connect(m_webcamSocket, TQT_SIGNAL(readyRead()), this, TQT_SLOT(slotSocketRead())); + // Create the callback that will try to handle the socket close event. + TQObject::connect(m_webcamSocket, TQT_SIGNAL(closed()), this, TQT_SLOT(slotSocketClosed())); + // Create the callback that will try to handle the socket error event. + TQObject::connect(m_webcamSocket, TQT_SIGNAL(gotError(int)), this, TQT_SLOT(slotSocketError(int))); + + m_allSockets.append(m_webcamSocket); + m_webcamStates[m_webcamSocket]=wsNegotiating; +} + +void Webcam::slotSocketRead() +{ + m_webcamSocket=const_cast<TDEBufferedSocket*>(static_cast<const TDEBufferedSocket*>(sender())); + + uint available = m_webcamSocket->bytesAvailable(); + kdDebug(14140) << k_funcinfo << m_webcamSocket << "############# " << available << " bytes available." << endl; + + TQByteArray avail_buff(available); + m_webcamSocket->peekBlock(avail_buff.data(), avail_buff.size()); + + kdDebug(14140) << k_funcinfo << m_webcamSocket << avail_buff << endl; + + + + const TQString connected_str("connected\r\n\r\n"); + switch(m_webcamStates[m_webcamSocket]) + { + case wsNegotiating: + { + if(available < m_myAuth.length()) + { + kdDebug(14140) << k_funcinfo << "waiting more data ( " << available << " of " <<m_myAuth.length()<< " )"<< endl; + break; + } + TQByteArray buffer(available); + m_webcamSocket->readBlock(buffer.data(), buffer.size()); + + kdDebug(14140) << k_funcinfo << buffer.data() << endl; + + if(TQString(buffer) == m_myAuth ) + { + closeAllOtherSockets(); + kdDebug(14140) << k_funcinfo << "Sending " << connected_str << endl; + TQCString conne=connected_str.utf8(); + m_webcamSocket->writeBlock(conne.data(), conne.length()); + m_webcamStates[m_webcamSocket]=wsConnecting; + + //SHOULD NOT BE THERE + m_mimic=new MimicWrapper(); + if(m_who==wProducer) + { + Kopete::AV::VideoDevicePool *videoDevice = Kopete::AV::VideoDevicePool::self(); + videoDevice->open(); + videoDevice->setSize(320, 240); + videoDevice->startCapturing(); + + m_timerId=startTimer(m_timerFps); + kdDebug(14140) << k_funcinfo << "new timer" << m_timerId << endl; + } + m_widget=new MSNWebcamDialog(m_recipient); + connect(m_widget, TQT_SIGNAL( closingWebcamDialog() ) , this , TQT_SLOT(sendBYEMessage())); + + } + else + { + kdWarning(14140) << k_funcinfo << "Auth failed" << endl; + m_webcamSocket->disconnect(); + m_webcamSocket->deleteLater(); + m_allSockets.remove(m_webcamSocket); + m_webcamSocket=0l; + //sendBYEMessage(); + } + break; + } + case wsConnecting: + case wsConnected: + { + if(available < connected_str.length()) + { + kdDebug(14140) << k_funcinfo << "waiting more data ( " << available << " of " <<connected_str.length()<< " )"<< endl; + break; + } + TQByteArray buffer(connected_str.length()); + m_webcamSocket->readBlock(buffer.data(), buffer.size()); + +// kdDebug(14140) << k_funcinfo << "state " << m_webcamState << " received :" << TQCString(buffer) << endl; + + + if(TQString(buffer) == connected_str) + { + if(m_webcamStates[m_webcamSocket]==wsConnected) + { + closeAllOtherSockets(); + kdDebug(14140) << k_funcinfo << "Sending " << connected_str << endl; + TQCString conne=connected_str.utf8(); + m_webcamSocket->writeBlock(conne.data(), conne.length()); + + //SHOULD BE DONE IN ALL CASE + m_mimic=new MimicWrapper(); + if(m_who==wProducer) + { + Kopete::AV::VideoDevicePool *videoDevice = Kopete::AV::VideoDevicePool::self(); + videoDevice->open(); + videoDevice->setSize(320, 240); + videoDevice->startCapturing(); + + m_timerId=startTimer(m_timerFps); + kdDebug(14140) << k_funcinfo << "new timer" << m_timerId << endl; + } + m_widget=new MSNWebcamDialog(m_recipient); + connect(m_widget, TQT_SIGNAL( closingWebcamDialog() ) , this , TQT_SLOT(sendBYEMessage())); + + } + m_webcamStates[m_webcamSocket]=wsTransfer; + + } + else + { + kdWarning(14140) << k_funcinfo << "Connecting failed" << endl; + m_webcamSocket->disconnect(); + m_webcamSocket->deleteLater(); + m_allSockets.remove(m_webcamSocket); + m_webcamSocket=0l; + } + break; + } + case wsTransfer: + { + if(m_who==wProducer) + { + kdWarning(14140) << k_funcinfo << "data received when we are producer"<< endl; + break; + } + if(available < 24) + { + kdDebug(14140) << k_funcinfo << "waiting more data ( " << available << " of " <<24<< " )"<< endl; + break; + } + TQByteArray buffer(24); + m_webcamSocket->peekBlock(buffer.data(), buffer.size()); + + TQ_UINT32 paysize=(uchar)buffer[8] + ((uchar)buffer[9]<<8) + ((uchar)buffer[10]<<16) + ((uchar)buffer[11]<<24); + + if(available < (paysize+24)) + { + kdDebug(14140) << k_funcinfo << "waiting more data ( " << available << " of " <<paysize<< " )"<< endl; + break; + } + m_webcamSocket->readBlock(buffer.data(), 24); //flush + buffer.resize(paysize); + m_webcamSocket->readBlock(buffer.data(), buffer.size()); + + TQPixmap pix=m_mimic->decode(buffer); + if(pix.isNull()) + { + kdWarning(14140) << k_funcinfo << "incorrect pixmap returned, better to stop everything"<< endl; + m_webcamSocket->disconnect(); + sendBYEMessage(); + } + m_widget->newImage(pix); + break; + } + default: + break; + } + +} + +void Webcam::slotListenError(int errorCode) +{ + kdWarning(14140) << k_funcinfo << "Error " << errorCode << " : " << m_listener->errorString() << endl; +} + +void Webcam::slotSocketClosed() +{ + if(!m_dispatcher) //we are in this destructor + return; + + TDEBufferedSocket *m_webcamSocket=const_cast<TDEBufferedSocket*>(static_cast<const TDEBufferedSocket*>(sender())); + + kdDebug(14140) << k_funcinfo << m_webcamSocket << endl; + + if(m_listener) + { //if we are still waiting for other socket to connect, just remove this socket from the socket list + m_webcamSocket->disconnect(); + m_webcamSocket->deleteLater(); + m_allSockets.remove(m_webcamSocket); + m_webcamSocket=0l; + } + else // else, close the session + sendBYEMessage(); + +} + +void Webcam::slotSocketError(int errorCode) +{ + TDEBufferedSocket *socket=const_cast<TDEBufferedSocket*>(static_cast<const TDEBufferedSocket*>(sender())); + kdDebug(14140) << k_funcinfo << socket << " - " << errorCode << " : " << socket->TDESocketBase::errorString() << endl; + //sendBYEMessage(); +} + +void Webcam::closeAllOtherSockets() +{ + //m_lisener->close(); + delete m_listener; + m_listener=0l; + + TQValueList<TDEBufferedSocket*>::iterator it; + for ( it = m_allSockets.begin(); it != m_allSockets.end(); ++it ) + { + TDEBufferedSocket *sock=(*it); + if(sock != m_webcamSocket) + delete sock; + } + m_allSockets.clear(); +} + + +void Webcam::timerEvent( TQTimerEvent *e ) +{ + if(e->timerId() != m_timerId) + return TransferContext::timerEvent(e); + +// kdDebug(14140) << k_funcinfo << endl; + + Kopete::AV::VideoDevicePool *videoDevice = Kopete::AV::VideoDevicePool::self(); + videoDevice->getFrame(); + TQImage img; + videoDevice->getImage(&img); + + if(m_widget) + m_widget->newImage(img); + + if(img.width()!=320 || img.height()!=240) + { + kdWarning(14140) << k_funcinfo << "Bad image size " <<img.width() << "x" << img.height() << endl; + return; + } + + uchar *bits=img.bits(); + TQByteArray image_data(img.width()*img.height()*3); + uint b2=0; + uint imgsize=img.width()*img.height()*4; + for(uint f=0; f< imgsize; f+=4) + { + image_data[b2+0]=bits[f+2]; + image_data[b2+1]=bits[f+1]; + image_data[b2+2]=bits[f+0]; + b2+=3; + } + + TQByteArray frame=m_mimic->encode(image_data); + + + kdDebug(14140) << k_funcinfo << "Sendinf frame of size " << frame.size() << endl; + //build the header. + TQByteArray header; + + TQDataStream writer(header, IO_WriteOnly); + writer.setByteOrder(TQDataStream::LittleEndian); + writer << (TQ_UINT16)24; // header size + writer << (TQ_UINT16)img.width(); + writer << (TQ_UINT16)img.height(); + writer << (TQ_UINT16)0x0000; //wtf .? + writer << (TQ_UINT32)frame.size(); + writer << (TQ_UINT8)('M') << (TQ_UINT8)('L') << (TQ_UINT8)('2') << (TQ_UINT8)('0'); + writer << (TQ_UINT32)0x00000000; //wtf .? + writer << TQTime::currentTime(); //FIXME: possible midnight bug ? + + m_webcamSocket->writeBlock(header.data(), header.size()); + m_webcamSocket->writeBlock(frame.data(), frame.size()); +} + + +} + + +#include "webcam.moc" + +#endif + diff --git a/kopete/protocols/msn/webcam.h b/kopete/protocols/msn/webcam.h new file mode 100644 index 00000000..a056fe24 --- /dev/null +++ b/kopete/protocols/msn/webcam.h @@ -0,0 +1,92 @@ +/* + Copyright (c) 2005 by Olivier Goffart <ogoffart@ kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef P2PWEBCAM_H +#define P2PWEBCAM_H + +#include "p2p.h" + +#if MSN_WEBCAM + +namespace KNetwork{ class TDEServerSocket; class TDEBufferedSocket; } + +class MimicWrapper; +class TQLabel; +class MSNWebcamDialog; +class TQTimerEvent; + +namespace P2P { + + +class Webcam : public TransferContext +{ Q_OBJECT + + public: + enum Who { wProducer , wViewer }; + + Webcam( Who who , const TQString& to, Dispatcher *parent, TQ_UINT32 sessionID); + ~Webcam( ); + + virtual void processMessage(const Message& message); + + public slots: + void askIncommingInvitation(); + virtual void acknowledged(); + void sendBYEMessage(); + + private: + void makeSIPMessage(const TQString &message, TQ_UINT8 XX=0, TQ_UINT8 YY=9 , TQ_UINT8 ZZ=0); + void sendBigP2PMessage( const TQByteArray& dataMessage ); + void closeAllOtherSockets(); + TQString m_content; + + TQString xml(uint session , uint rid); + int getAvailablePort(); + + + KNetwork::TDEServerSocket *m_listener; + KNetwork::TDEBufferedSocket *m_webcamSocket; + + enum WebcamStatus { wsNegotiating , wsConnecting, wsConnected, wsTransfer } ; + + Who m_who; + + TQString m_myAuth; + TQString m_peerAuth; + + MimicWrapper *m_mimic; + MSNWebcamDialog *m_widget; + + TQValueList<KNetwork::TDEBufferedSocket* > m_allSockets; + TQMap<KNetwork::TDEBufferedSocket*, WebcamStatus> m_webcamStates; + + int m_timerId; + int m_timerFps; + + private slots: + void slotListenError(int errorCode); + void slotAccept(); + void slotSocketRead(); + void slotSocketClosed(); + void slotSocketError(int errorCode); + void slotSocketConnected(); +// void slotReadyWrite(); + protected: + virtual void timerEvent( TQTimerEvent * ); +}; + +} + +#endif + +#endif diff --git a/kopete/protocols/msn/webcam/CMakeLists.txt b/kopete/protocols/msn/webcam/CMakeLists.txt new file mode 100644 index 00000000..aafa2d94 --- /dev/null +++ b/kopete/protocols/msn/webcam/CMakeLists.txt @@ -0,0 +1,28 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( libmimic ) + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/kopete/libkopete + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} + ${GLIB2_INCLUDE_DIRS} +) + + +##### mimicwrapper (static) ##################### + +tde_add_library( mimicwrapper STATIC_PIC AUTOMOC + SOURCES mimicwrapper.cpp msnwebcamdialog.cpp + LINK mimic-static +) diff --git a/kopete/protocols/msn/webcam/Makefile.am b/kopete/protocols/msn/webcam/Makefile.am new file mode 100644 index 00000000..27d37fe0 --- /dev/null +++ b/kopete/protocols/msn/webcam/Makefile.am @@ -0,0 +1,14 @@ +METASOURCES = AUTO +SUBDIRS = libmimic +AM_CPPFLAGS = -I$(srcdir)/libmimic \ + $(KOPETE_INCLUDES) \ + $(all_includes) \ + $(GLIB_CFLAGS) + +noinst_LTLIBRARIES = libmimicwrapper.la + +libmimicwrapper_la_SOURCES = mimicwrapper.cpp msnwebcamdialog.cpp +libmimicwrapper_la_LIBADD = ./libmimic/libmimic.la +libmimicwrapper_la_LDFLAGS = -no-undefined $(GLIB_LIBS) $(all_libraries) + + diff --git a/kopete/protocols/msn/webcam/libmimic/AUTHORS b/kopete/protocols/msn/webcam/libmimic/AUTHORS new file mode 100644 index 00000000..8dd0671d --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/AUTHORS @@ -0,0 +1,2 @@ +Ole André Vadla Ravnås <oleavr@gmail.com> + diff --git a/kopete/protocols/msn/webcam/libmimic/CMakeLists.txt b/kopete/protocols/msn/webcam/libmimic/CMakeLists.txt new file mode 100644 index 00000000..5a340661 --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/CMakeLists.txt @@ -0,0 +1,24 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${GLIB2_INCLUDE_DIRS} +) + + +##### mimic (static) ############################ + +tde_add_library( mimic STATIC_PIC + SOURCES + mimic.c encode.c decode.c bitstring.c vlc_common.c vlc_encode.c + vlc_decode.c fdct_quant.c idct_dequant.c colorspace.c deblock.c + LINK ${GLIB2_LIBRARIES} +) diff --git a/kopete/protocols/msn/webcam/libmimic/COPYING b/kopete/protocols/msn/webcam/libmimic/COPYING new file mode 100644 index 00000000..ae23fcfd --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/kopete/protocols/msn/webcam/libmimic/Makefile.am b/kopete/protocols/msn/webcam/libmimic/Makefile.am new file mode 100644 index 00000000..1a2c99d3 --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/Makefile.am @@ -0,0 +1,24 @@ +# INCLUDES = @GLIB_CFLAGS@ +AM_CPPFLAGS = $(all_includes) $(GLIB_CFLAGS) + +# libmimicincludedir = $(includedir) +# libmimicinclude_HEADERS = mimic.h + +noinst_LTLIBRARIES = libmimic.la +libmimic_la_SOURCES = \ + mimic.c \ + encode.c \ + decode.c \ + bitstring.c \ + vlc_common.c \ + vlc_encode.c \ + vlc_decode.c \ + fdct_quant.c \ + idct_dequant.c \ + colorspace.c \ + deblock.c \ + mimic-private.h +# libmimic_la_LDFLAGS = \ +# -version-info $(MIMIC_CURRENT):$(MIMIC_REVISION):$(MIMIC_AGE) \ +# -export-symbols-regex "^[^_].*" + diff --git a/kopete/protocols/msn/webcam/libmimic/README b/kopete/protocols/msn/webcam/libmimic/README new file mode 100644 index 00000000..c60336ec --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/README @@ -0,0 +1,40 @@ +ABOUT +----- + +libmimic is an open source video encoding/decoding library for Mimic V2.x- +encoded content (fourCC: ML20), which is the encoding used by MSN Messenger +for webcam conversations. + +It was written because there was no third-party MSN-client that supported +this feature due to this proprietary/unknown codec involved. I didn't like +this lack of interoperability, so I decided to do something about it. After +studying the official MSN-client a little closer, it became clear that the +codec involved was statically linked into the executable, so there was no +easy way to use the codec code through Wine. So for fun, and challenge, I +reverse-engineered the original implementation by studying the massive +amount of assembly code involved, and after a lot of hard work I ended +up with this implementation in C. + +It should be noted that reverse-engineering for interoperability is 100% +legal here in Norway (and in most European countries). + + +THANKS +------ + +Special thanks to Rob Taylor and the rest of the Farsight-team for all +the feedback and inspiration during development, you guys rock! :-) + + +BOTTOM LINE +----------- + +If you like my work and decide to use it in your project, please feel free +to credit me. I put a lot of time and hard work into this, so I hope others +will find it useful. + +Well, enough chit chat, enjoy! :-) + +Ole André Vadla Ravnås +oleavr at gmail dot com + diff --git a/kopete/protocols/msn/webcam/libmimic/bitstring.c b/kopete/protocols/msn/webcam/libmimic/bitstring.c new file mode 100644 index 00000000..7caf9fd8 --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/bitstring.c @@ -0,0 +1,88 @@ +/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mimic-private.h" + +/* + * _read_bits + * + * Internal helper-function used to read num_bits + * from stream. + */ +guint32 _read_bits(MimCtx *ctx, gint num_bits) +{ + guint32 bits; + + if (ctx->cur_chunk_len >= 16) { + guchar *input_buf = (guchar *) ctx->data_buffer + ctx->data_index; + + if (!ctx->read_odd) { + ctx->read_odd = TRUE; + + ctx->cur_chunk = (input_buf[3] << 24) | + (input_buf[2] << 16) | + (input_buf[1] << 8) | + input_buf[0]; + + } else { + ctx->read_odd = FALSE; + + ctx->cur_chunk = (input_buf[1] << 24) | + (input_buf[0] << 16) | + (input_buf[7] << 8) | + input_buf[6]; + + ctx->data_index += 4; + } + + ctx->cur_chunk_len -= 16; + } + + bits = (ctx->cur_chunk << ctx->cur_chunk_len) >> (32 - num_bits); + ctx->cur_chunk_len += num_bits; + + return bits; +} + +/* + * _write_bits + * + * Internal helper-function used to write "length" + * bits of "bits" to stream. + */ +void _write_bits(MimCtx *ctx, guint32 bits, gint length) +{ + /* Left-align the bit string within its 32-bit container. */ + bits <<= (32 - length); + + /* Append the bit string (one or more of the trailing bits might not fit, but that's ok). */ + ctx->cur_chunk |= bits >> ctx->cur_chunk_len; + ctx->cur_chunk_len += length; + + /* Is it full? */ + if (ctx->cur_chunk_len >= 32) { + + /* Add the full 32-bit chunk to the stream and update counter. */ + ctx->chunk_ptr[0] = GUINT32_TO_LE(ctx->cur_chunk); + ctx->chunk_ptr++; + ctx->cur_chunk_len -= 32; + + /* Add any trailing bits that didn't fit. */ + ctx->cur_chunk = bits << (length - ctx->cur_chunk_len); + } +} + diff --git a/kopete/protocols/msn/webcam/libmimic/colorspace.c b/kopete/protocols/msn/webcam/libmimic/colorspace.c new file mode 100644 index 00000000..27de4a09 --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/colorspace.c @@ -0,0 +1,161 @@ +/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mimic-private.h" + +#define RED_INDEX_1 0 +#define GREEN_INDEX_1 1 +#define BLUE_INDEX_1 2 + +#define RED_INDEX_2 3 +#define GREEN_INDEX_2 4 +#define BLUE_INDEX_2 5 + +/* + * _rgb_to_yuv + * + * Internal helper-function used to convert an image + * from RGB 24-bpp packed-pixel to YUV420 planar. + */ +void _rgb_to_yuv(const guchar *input_rgb, + guchar *output_y, + guchar *output_cb, + guchar *output_cr, + gint width, + gint height) +{ + gint y, x; + + for (y = 0; y < height; y += 2) { + + const guchar *src1, *src2; + guchar *dst1, *dst2, *dst3, *dst4; + gint num_cols; + + src1 = input_rgb + ((height - 1 - y) * width * 3); + src2 = input_rgb + ((height - 2 - y) * width * 3); + + dst1 = output_y + (y * width); + dst2 = output_y + ((y + 1) * width); + dst3 = output_cb + ((y / 2) * (width / 2)); + dst4 = output_cr + ((y / 2) * (width / 2)); + + num_cols = width / 2; + + for (x = 0; x < num_cols; x++) { + + gint expr1, expr2, expr3, expr4, expr5, v; + + expr1 = (src1[BLUE_INDEX_1] * 19595) + (src1[GREEN_INDEX_1] * 38470) + (src1[RED_INDEX_1] * 7471); + expr2 = (src1[BLUE_INDEX_2] * 19595) + (src1[GREEN_INDEX_2] * 38470) + (src1[RED_INDEX_2] * 7471); + expr3 = (src2[BLUE_INDEX_1] * 19595) + (src2[GREEN_INDEX_1] * 38470) + (src2[RED_INDEX_1] * 7471); + expr4 = (src2[BLUE_INDEX_2] * 19595) + (src2[GREEN_INDEX_2] * 38470) + (src2[RED_INDEX_2] * 7471); + + expr5 = expr1 + expr2 + expr3 + expr4; + + dst1[0] = expr1 >> 16; + dst1[1] = expr2 >> 16; + dst2[0] = expr3 >> 16; + dst2[1] = expr4 >> 16; + + v = (((src1[BLUE_INDEX_1] + src1[BLUE_INDEX_2] + src2[BLUE_INDEX_1] + src2[BLUE_INDEX_2]) << 16) - expr5 + 131071) >> 16; + dst3[0] = _clamp_value(((v * 57475) >> 18) + 128); + + v = (((src1[RED_INDEX_1] + src1[RED_INDEX_2] + src2[RED_INDEX_1] + src2[RED_INDEX_2]) << 16) - expr5 + 131071) >> 16; + dst4[0] = ((v * 32244) >> 18) + 128; + + src1 += 6; + src2 += 6; + + dst1 += 2; + dst2 += 2; + dst3++; + dst4++; + + } + + } + +} + +/* + * _yuv_to_rgb + * + * Internal helper-function used to convert an image + * from YUV420 planar to RGB 24-bpp packed-pixel. + */ +void _yuv_to_rgb(const guchar *input_y, + const guchar *input_cb, + const guchar *input_cr, + guchar *output_rgb, + guint width, + guint height) +{ + const guchar *src_y, *src_cb, *src_cr; + guchar *dst_rgb; + guint i, j, rgb_stride; + + src_y = input_y; + src_cb = input_cb; + src_cr = input_cr; + + rgb_stride = width * 3; + dst_rgb = output_rgb + (rgb_stride * (height - 1)); + + for (i = 0; i < height; i++) { + const guchar *p_y, *p_cb, *p_cr; + guchar *p_rgb; + + p_y = src_y; + p_cb = src_cb; + p_cr = src_cr; + + p_rgb = dst_rgb; + + for (j = 0; j < width; j++) { + gint v; + + v = ((p_y[0] * 65536) + ((p_cr[0] - 128) * 133169)) / 65536; + p_rgb[0] = _clamp_value(v); + + v = ((p_y[0] * 65536) - ((p_cr[0] - 128) * 25821) - ((p_cb[0] - 128) * 38076)) / 65536; + p_rgb[1] = _clamp_value(v); + + v = ((p_y[0] * 65536) + ((p_cb[0] - 128) * 74711)) / 65536; + p_rgb[2] = _clamp_value(v); + + p_y++; + if ((j + 1) % 2 == 0) { + p_cb++; + p_cr++; + } + + p_rgb += 3; + } + + src_y += width; + if ((i + 1) % 2 == 0) { + src_cb += (width + 1) / 2; + src_cr += (width + 1) / 2; + } + + dst_rgb -= rgb_stride; + + } + +} + diff --git a/kopete/protocols/msn/webcam/libmimic/deblock.c b/kopete/protocols/msn/webcam/libmimic/deblock.c new file mode 100644 index 00000000..02218d36 --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/deblock.c @@ -0,0 +1,450 @@ +/* Copyright (C) 2005 Ole Andr Vadla Ravns <oleavr@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdlib.h> +#include <string.h> +#include "mimic-private.h" + +static void deblock_horizontal(guchar *blocks, guint stride, guint row_count); +static void deblock_vertical(guchar *blocks, guint stride, guint row_count); + +static gboolean deblock_h_consider_entire(guchar *blocks, guint stride); +static void deblock_h_do_entire(guchar *blocks, guint stride); +static void deblock_h_do_boundaries(guchar *blocks, guint stride); + +static gboolean deblock_v_consider_entire(guchar *blocks, guint stride); +static void deblock_v_do_entire(guchar *blocks, guint stride); +static void deblock_v_do_boundaries(guchar *blocks, guint stride); + +/* + * _deblock + * + * Internal helper-function used for de-blocking. + */ +void _deblock(guchar *blocks, guint stride, guint row_count) +{ + deblock_horizontal(blocks, stride, row_count); + deblock_vertical(blocks, stride, row_count); +} + +static void deblock_horizontal(guchar *blocks, guint stride, guint row_count) +{ + guchar *p1; + gint i, j, n1, n2; + + if (stride <= 8 || row_count == 0) + return; + + p1 = blocks + 4; + n1 = ((row_count - 1) >> 2) + 1; + n2 = ((stride - 9) >> 3) + 1; + + for (i = 0; i < n1; i++) { + guchar *p; + + p = p1; + + for (j = 0; j < n2; j++) { + + if (deblock_h_consider_entire(p - 1, stride) == TRUE) { + + gint v1, v2, v; + + v1 = p[0]; + v2 = p[7]; + + v = v1 - v2; + if (v <= 0) + v = v2 - v1; + + if (v < 20) + deblock_h_do_entire(p - 1, stride); + + } else { + deblock_h_do_boundaries(p - 1, stride); + } + + p += 8; + } + + p1 += stride * 4; + } +} + +static void deblock_vertical(guchar *blocks, guint stride, guint row_count) +{ + gint i, j, k, n1, n2; + guchar *p1, *p2; + + if (stride == 0 || row_count <= 8) + return; + + p1 = blocks + (stride * 3); + p2 = blocks + (stride * 4); + + n1 = ((row_count - 9) >> 3) + 1; + n2 = ((stride - 1) >> 3) + 1; + + for (i = 0; i < n1; i++) { + guchar *p3, *p4; + + p3 = p1; + p4 = p2; + + for (j = 0; j < n2; j++) { + + if (deblock_v_consider_entire(p3, stride) == TRUE) { + guchar *p5; + gboolean do_entire; + + p5 = p3 + (stride * 8); + do_entire = TRUE; + + for (k = 0; k < 8; k++) { + gint v1, v2, v; + + v1 = p4[k]; + v2 = p5[k]; + + v = v1 - v2; + if (v <= 0) + v = v2 - v1; + + if (v > 20) { + do_entire = FALSE; + break; + } + } + + if (do_entire) + deblock_v_do_entire(p3, stride); + } else { + deblock_v_do_boundaries(p3, stride); + } + + p3 += 8; + p4 += 8; + } + + p1 += stride * 8; + p2 += stride * 8; + } +} + +static gboolean deblock_h_consider_entire(guchar *blocks, guint stride) +{ + guchar *p; + gint i, j, count; + + count = 0; + p = blocks; + + for (i = 0; i < 4; i++) { + + for (j = 1; j <= 7; j++) { + gint v1, v2, v; + + v1 = p[j]; + v2 = p[j+1]; + + v = v1 - v2; + if (v <= 0) + v = v2 - v1; + + if (v <= 1) + count--; + } + + p += stride; + } + + return (count <= -20); +} + +static void deblock_h_do_entire(guchar *blocks, guint stride) +{ + guchar buf[8], *p; + gint i; + + p = blocks; + + for (i = 0; i < 4; i++) { + gint v, low, high; + + v = p[0] - p[1]; + if (v <= 0) + v = p[1] - p[0]; + + if (v < 10) + low = p[0]; + else + low = p[1]; + + v = p[8] - p[9]; + if (v <= 0) + v = p[9] - p[8]; + + if (v >= 10) + high = p[8]; + else + high = p[9]; + + v = (low * 3) + p[1] + p[2] + p[3] + p[4] + 4; + buf[0] = (((p[1] + v) << 1) - p[4] + p[5]) >> 4; + + v += p[5] - low; + buf[1] = (((p[2] + v) << 1) - p[5] + p[6]) >> 4; + + v += p[6] - low; + buf[2] = (((p[3] + v) << 1) - p[6] + p[7]) >> 4; + + v += p[7] - low; + buf[3] = (((p[4] + v) << 1) - p[1] - p[7] + p[8] + low) >> 4; + + v += p[8] - p[1]; + buf[4] = (((p[5] + v) << 1) + p[1] - p[2] - p[8] + high) >> 4; + + v += high - p[2]; + buf[5] = (((p[6] + v) << 1) + p[2] - p[3]) >> 4; + + v += high - p[3]; + buf[6] = (((p[7] + v) << 1) + p[3] - p[4]) >> 4; + + v += high; + buf[7] = (((p[8] + v) << 1) - p[4] - p[5]) >> 4; + + memcpy(p + 1, buf, 8); + + p += stride; + } +} + +static void deblock_h_do_boundaries(guchar *blocks, guint stride) +{ + guchar *p; + gint i; + + p = blocks; + + for (i = 0; i < 4; i++) { + gint v, v1, v2, v3; + + v = p[4] - p[5]; + + if ((v / 2) != 0) { + + v1 = ((p[3] - p[6]) * 2) - (v * 5); + + if (abs(v1) < 80) { + + v2 = ((p[3] - p[2]) * 5) + ((p[1] - p[4]) * 2); + v3 = (p[5] * 2) + (p[7] * 5) - (p[8] * 7); + + v = abs(v1) - MIN(abs(v2), abs(v3)); + + if (v > 0) { + + v = ((v * 5) + 32) >> 6; + if (v > 0) { + + v2 = (p[4] - p[5]) / 2; + v3 = (((v1 < 0) * 2) - 1) * v; + + if (v2 > 0) + v = MIN(v2, ((v3 < 0) - 1) & v3); + else + v = MAX(v2, ((v3 > 0) - 1) & v3); + + p[4] -= v; + p[5] += v; + } + } + } + } + + p += stride; + } +} + +static gboolean deblock_v_consider_entire(guchar *blocks, guint stride) +{ + gint count, i, j; + guchar *p1, *p2; + + count = 0; + + p1 = blocks + stride; + p2 = blocks + (stride * 2); + + for (i = 0; i < 7; i++) { + + for (j = 0; j < 8; j++) { + gint v1, v2, v; + + v1 = p1[j]; + v2 = p2[j]; + + v = v1 - v2; + if (v <= 0) + v = v2 - v1; + + if (v <= 1) + count++; + } + + p1 += stride; + p2 += stride; + } + + return (count > 40); +} + +static void deblock_v_do_entire(guchar *blocks, guint stride) +{ + gint offset0, offset1, offset2, offset3; + gint offset4, offset5, offset6, offset7; + gint offset8, i; + guchar *p, buf[8]; + + offset0 = stride - (stride * 6); + offset1 = (stride * 2) - (stride * 6); + offset2 = (stride * 3) - (stride * 6); + offset3 = (stride * 4) - (stride * 6); + offset4 = (stride * 5) - (stride * 6); + offset5 = 0; + offset6 = (stride * 7) - (stride * 6); + offset7 = (stride * 8) - (stride * 6); + offset8 = (stride * 9) - (stride * 6); + + p = blocks + (stride * 6); + + for (i = 0; i < 8; i++) { + gint v, low, high; + + v = blocks[i] - p[offset0]; + if (v <= 0) + v = p[offset0] - blocks[i]; + + if (v < 10) + low = blocks[i]; + else + low = p[offset0]; + + v = p[offset7] - p[offset8]; + if (v <= 0) + v = p[offset8] - p[offset7]; + + if (v < 10) + high = p[offset8]; + else + high = p[offset7]; + + v = p[offset0] + (low * 3) + p[offset1] + p[offset2] + p[offset3] + 4; + + buf[0] = (((p[offset0] + v) << 1) - p[offset3] + p[offset4]) >> 4; + + v += p[offset4] - low; + + buf[1] = (((p[offset1] + v) << 1) - p[offset4] + p[0]) >> 4; + + v += p[0] - low; + + buf[2] = (((p[offset2] + v) << 1) - p[0] + p[offset6]) >> 4; + + v += p[offset6] - low; + + buf[3] = (((p[offset3] + v) << 1) - p[offset0] - p[offset6] + p[offset7] + low) >> 4; + + v += p[offset7] - p[offset0]; + + buf[4] = (((p[offset4] + v) << 1) - p[offset7] - p[offset1] + p[offset0] + high) >> 4; + + v += high - p[offset1]; + + buf[5] = (((p[0] + v) << 1) - p[offset2] + p[offset1]) >> 4; + + v += high - p[offset2]; + + buf[6] = (((p[offset6] + v) << 1) - p[offset3] + p[offset2]) >> 4; + + v += high; + + buf[7] = (((p[offset7] + v) << 1) - p[offset4] - p[offset3]) >> 4; + + p[offset0] = buf[0]; + p[offset1] = buf[1]; + p[offset2] = buf[2]; + p[offset3] = buf[3]; + p[offset4] = buf[4]; + p[offset5] = buf[5]; + p[offset6] = buf[6]; + p[offset7] = buf[7]; + + p++; + } +} + +static void deblock_v_do_boundaries(guchar *blocks, guint stride) +{ + guchar *p; + gint offset0, offset1, offset2, offset3; + gint offset4, offset5, offset6, offset7; + gint i; + + p = blocks + (stride * 3); + + offset0 = stride - (stride * 3); + offset1 = (stride * 2) - (stride * 3); + offset2 = 0; + offset3 = (stride * 4) - (stride * 3); + offset4 = (stride * 5) - (stride * 3); + offset5 = (stride * 6) - (stride * 3); + offset6 = (stride * 7) - (stride * 3); + offset7 = (stride * 8) - (stride * 3); + + for (i = 0; i < 8; i++) { + gint v1, v2, v3, v; + + v1 = ((p[offset4] - p[offset3]) * 5) + ((p[offset2] - p[offset5]) * 2); + + if (abs(v1) < 80) { + + v2 = ((p[offset2] - p[offset1]) * 5) + ((p[offset0] - p[offset3]) * 2); + v3 = ((p[offset6] - p[offset5]) * 5) + ((p[offset4] - p[offset7]) * 2); + + v = abs(v1) - MIN(abs(v2), abs(v3)); + if (v < 0) + v = 0; + + v2 = (p[offset3] - p[offset4]) / 2; + v3 = (((v * 5) + 32) >> 6) * (((v1 < 0) * 2) - 1); + + if (v2 > 0) + v = MIN(v2, ((v3 < 0) - 1) & v3); + else + v = MAX(v2, ((v3 > 0) - 1) & v3); + } else { + v = 0; + } + + p[offset3] -= v; + p[offset4] += v; + + p++; + } +} + diff --git a/kopete/protocols/msn/webcam/libmimic/decode.c b/kopete/protocols/msn/webcam/libmimic/decode.c new file mode 100644 index 00000000..83067562 --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/decode.c @@ -0,0 +1,311 @@ +/* Copyright (C) 2005 Ole Andr Vadla Ravns <oleavr@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> +#include "mimic-private.h" + + +static gboolean decode(MimCtx *ctx, gboolean is_pframe); + +/** + * Decode a MIMIC-encoded frame into RGB data. + * + * @param ctx the mimic context + * @param input_buffer buffer containing the MIMIC-encoded frame to decode + * @param output_buffer buffer that will receive the decoded frame in RGB 24-bpp packed pixel top-down format + * (use #mimic_get_property to determine the required buffer size, as well as frame width and height) + * @returns #TRUE on success + */ +gboolean mimic_decode_frame(MimCtx *ctx, + const guchar *input_buffer, + guchar *output_buffer) +{ + gboolean result, is_pframe; + guchar *input_y, *input_cr, *input_cb; + gint width, height; + + /* + * Some sanity checks. + */ + if (ctx == NULL || input_buffer == NULL || output_buffer == NULL) + { + return FALSE; + } + + if (!ctx->decoder_initialized) + { + return FALSE; + } + + /* + * Get frame dimensions. + */ + width = GUINT16_FROM_LE(*((guint16 *) (input_buffer + 4))); + height = GUINT16_FROM_LE(*((guint16 *) (input_buffer + 6))); + + /* + * Resolution changing is not supported. + */ + if (width != ctx->frame_width || + height != ctx->frame_height) + { + return FALSE; + } + + /* + * Increment frame counter. + */ + ctx->frame_num++; + + /* + * Initialize state. + */ + ctx->quality = GUINT16_FROM_LE(*((guint16 *) (input_buffer + 2))); + is_pframe = GUINT32_FROM_LE(*((guint32 *) (input_buffer + 12))); + ctx->num_coeffs = input_buffer[16]; + + ctx->data_buffer = (gchar *) (input_buffer + 20); + ctx->data_index = 0; + ctx->cur_chunk_len = 16; + ctx->read_odd = FALSE; + + /* + * Decode frame. + */ + if (!(is_pframe && ctx->prev_frame_buf == NULL)) + result = decode(ctx, is_pframe); + else + { + result = FALSE; + } + + /* + * Perform YUV 420 to RGB conversion. + */ + input_y = ctx->cur_frame_buf; + input_cr = ctx->cur_frame_buf + ctx->y_size; + input_cb = ctx->cur_frame_buf + ctx->y_size + ctx->crcb_size; + + _yuv_to_rgb(input_y, + input_cb, + input_cr, + output_buffer, + ctx->frame_width, + ctx->frame_height); + + return result; +} + +/* + * decode_main + * + * Main decoding loop. + */ +static gboolean decode(MimCtx *ctx, gboolean is_pframe) +{ + gint y, x, i, j, chrom_ch, *bptr, base_offset, offset; + gint dct_block[64]; + guchar *src, *dst, *p; + guint32 bit; + + /* + * Clear Cr and Cb planes. + */ + p = ctx->cur_frame_buf + ctx->y_size; + memset(p, 128, 2 * ctx->crcb_size); + + /* + * Decode Y plane. + */ + for (y = 0; y < ctx->num_vblocks_y; y++) { + + base_offset = ctx->y_stride * 8 * y; + + src = ctx->prev_frame_buf + base_offset; + dst = ctx->cur_frame_buf + base_offset; + + for (x = 0; x < ctx->num_hblocks_y; x++) { + + /* Check for a change condition in the current block. */ + + if (is_pframe) + bit = _read_bits(ctx, 1); + else + bit = 0; + + if (bit == 0) { + + /* Yes: Is the new content the same as it was in one of + * the 15 last frames preceding the previous? */ + + if (is_pframe) + bit = _read_bits(ctx, 1); + + if (bit == 0) { + + /* No: decode it. */ + + if (_vlc_decode_block(ctx, dct_block, ctx->num_coeffs) == FALSE) { + + return FALSE; + } + + _idct_dequant_block(ctx, dct_block, 0); + + bptr = dct_block; + for (i = 0; i < 8; i++) { + offset = ctx->y_stride * i; + + for (j = 0; j < 8; j++) { + guint v; + + if (bptr[j] <= 255) + v = (bptr[j] >= 0) ? bptr[j] : 0; + else + v = 255; + + *(dst + offset + j) = v; + } + + bptr += 8; + } + } else { + guint32 backref; + + /* Yes: read the backreference (4 bits) and copy. */ + + backref = _read_bits(ctx, 4); + + p = ctx->buf_ptrs[(ctx->ptr_index + backref) % 16]; + p += base_offset + (x * 8); + + for (i = 0; i < 8; i++) { + offset = ctx->y_stride * i; + + memcpy(dst + offset, p + offset, 8); + } + } + } else { + + /* No change no worries: just copy from the previous frame. */ + + for (i = 0; i < 8; i++) { + offset = ctx->y_stride * i; + + memcpy(dst + offset, src + offset, 8); + } + } + + src += 8; + dst += 8; + } + } + + /* + * Decode Cr and Cb planes. + */ + for (chrom_ch = 0; chrom_ch < 2; chrom_ch++) { + + base_offset = ctx->y_size + (ctx->crcb_size * chrom_ch); + + for (y = 0; y < ctx->num_vblocks_cbcr; y++) { + guint num_rows = 8; + + /* The last row of blocks in chrominance for 160x120 resolution + * is half the normal height and must be accounted for. */ + if (y + 1 == ctx->num_vblocks_cbcr && ctx->frame_height % 16 != 0) + num_rows = 4; + + offset = base_offset + (ctx->crcb_stride * 8 * y); + + src = ctx->prev_frame_buf + offset; + dst = ctx->cur_frame_buf + offset; + + for (x = 0; x < ctx->num_hblocks_cbcr; x++) { + + /* Check for a change condition in the current block. */ + + if (is_pframe) + bit = _read_bits(ctx, 1); + else + bit = 1; + + if (bit == 1) { + + /* Yes: decode it. */ + + if (_vlc_decode_block(ctx, dct_block, ctx->num_coeffs) == FALSE) { + + /* Corrupted frame: clear Cr and Cb planes and return. */ + p = ctx->cur_frame_buf + ctx->y_size; + memset(p, 128, ctx->crcb_size * 2); + + return FALSE; + } + + _idct_dequant_block(ctx, dct_block, 1); + + for (i = 0; i < num_rows; i++) { + p = dst + (ctx->crcb_stride * i); + + for (j = 0; j < 8; j++) + p[j] = dct_block[(i * 8) + j]; + } + + } else { + + /* No change no worries: just copy from the previous frame. */ + + for (i = 0; i < num_rows; i++) { + offset = ctx->crcb_stride * i; + + memcpy(dst + offset, src + offset, 8); + } + } + + src += 8; + dst += 8; + } + } + } + + /* + * Make a copy of the current frame and store in + * the circular pointer list of 16 entries. + */ + ctx->prev_frame_buf = ctx->buf_ptrs[ctx->ptr_index]; + memcpy(ctx->prev_frame_buf, ctx->cur_frame_buf, + ctx->y_size + (ctx->crcb_size * 2)); + + if (--ctx->ptr_index < 0) + ctx->ptr_index = 15; + + /* + * Perform deblocking on all planes. + */ + _deblock(ctx->cur_frame_buf, + ctx->y_stride, ctx->y_row_count); + + _deblock(ctx->cur_frame_buf + ctx->y_size, + ctx->crcb_stride, ctx->crcb_row_count); + + _deblock(ctx->cur_frame_buf + ctx->y_size + ctx->crcb_size, + ctx->crcb_stride, ctx->crcb_row_count); + + return TRUE; +} + diff --git a/kopete/protocols/msn/webcam/libmimic/encode.c b/kopete/protocols/msn/webcam/libmimic/encode.c new file mode 100644 index 00000000..6c8a9de8 --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/encode.c @@ -0,0 +1,419 @@ +/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include "mimic-private.h" + +#define LUMINANCE_THRESHOLD 32.0f +#define CHROMINANCE_THRESHOLD 36.0f + +static void encode_main(MimCtx *ctx, guchar *out_buf, gboolean is_pframe); + +/** + * Encode a MIMIC-encoded frame from RGB data. + * + * @param ctx the mimic context + * @param input_buffer buffer containing pixeldata in RGB 24-bpp packed pixel top-down format + * @param output_buffer buffer that will receive the MIMIC-encoded frame + * (use #mimic_get_property to determine the required buffer size) + * @param output_length pointer to an integer that receives the length of the encoded data + * written to output_buffer + * @param make_keyframe whether the encoder should make this frame a keyframe + * @returns #TRUE on success + */ +gboolean mimic_encode_frame(MimCtx *ctx, + const guchar *input_buffer, + guchar *output_buffer, + gint *output_length, + gboolean make_keyframe) +{ + guchar *output_y, *output_cb, *output_cr; + + /* + * Some sanity checks. + */ + if (ctx == NULL || input_buffer == NULL || + output_buffer == NULL || output_length == NULL) + { + return FALSE; + } + + if (!ctx->encoder_initialized) + return FALSE; + + /* + * Initialize state. + */ + ctx->chunk_ptr = (guint32 *) (output_buffer + 20); + ctx->cur_chunk = 0; + ctx->cur_chunk_len = 0; + + if (ctx->frame_num == 0) + make_keyframe = TRUE; + + /* + * Write header. + */ + memset(output_buffer, 0, 20); + *((guint16 *) (output_buffer + 0)) = GUINT16_TO_LE(256); + *((guint16 *) (output_buffer + 2)) = GUINT16_TO_LE(ctx->quality); + *((guint16 *) (output_buffer + 4)) = GUINT16_TO_LE(ctx->frame_width); + *((guint16 *) (output_buffer + 6)) = GUINT16_TO_LE(ctx->frame_height); + *((guint32 *) (output_buffer + 12)) = GUINT32_TO_LE((make_keyframe == 0)); + *(output_buffer + 16) = ctx->num_coeffs; + *(output_buffer + 17) = 0; + + /* + * Perform RGB to YUV 420 conversion. + */ + output_y = ctx->cur_frame_buf; + output_cr = ctx->cur_frame_buf + ctx->y_size; + output_cb = ctx->cur_frame_buf + ctx->y_size + ctx->crcb_size; + + _rgb_to_yuv(input_buffer, + output_y, + output_cb, + output_cr, + ctx->frame_width, + ctx->frame_height); + + /* + * Encode frame. + */ + encode_main(ctx, output_buffer, (make_keyframe == FALSE)); + + /* + * Write out any pending bits to stream by zero-padding with 32 bits. + */ + _write_bits(ctx, 0, 32); + + /* + * Calculate bytes written. + */ + *output_length = (guchar *) ctx->chunk_ptr - output_buffer; + + /* + * Increment frame counter. + */ + ctx->frame_num++; + + return TRUE; +} + +static gdouble compare_blocks(const guchar *p1, + const guchar *p2, + gint stride, + gint row_count, + gboolean is_chrom); + +/* + * encode_main + * + * Main encoding loop. + */ +static void encode_main(MimCtx *ctx, guchar *out_buf, gboolean is_pframe) +{ + gint x, y, i, offset, chrom_ch; + gint dct_block[64]; + guchar *src, *dst, *p1, *p2; + gdouble match; + gboolean encoded; + + /* + * Round down small differences in luminance channel. + */ + if (is_pframe) { + + p1 = ctx->cur_frame_buf; + p2 = ctx->prev_frame_buf; + + for (i = 0; i < ctx->y_size; i++) { + + if (abs(p2[0] - p1[0]) < 7) + p1[0] = p2[0]; + + p1++; + p2++; + } + } + + /* + * Encode Y plane. + */ + for (y = 0; y < ctx->num_vblocks_y; y++) { + + for (x = 0; x < ctx->num_hblocks_y; x++) { + + /* Calculate final offset into buffer. */ + offset = (ctx->y_stride * 8 * y) + (x * 8); + + src = NULL; + encoded = FALSE; + + if (is_pframe) { + + /* Is the current block similar enough to what it was in the previous frame? */ + + match = compare_blocks(ctx->cur_frame_buf + offset, + ctx->prev_frame_buf + offset, + ctx->y_stride, 8, + FALSE); + + if (match > LUMINANCE_THRESHOLD) { + + /* Yes: write out '1' to indicate a no-change condition. */ + + _write_bits(ctx, 1, 1); + + src = ctx->prev_frame_buf + offset; + encoded = TRUE; + + } else { + + /* No: Is the current block similar enough to what it was in one + * of the (up to) 15 last frames preceding the previous? */ + + gint best_index = 0; + gdouble best_match = 0.0; + + gint num_backrefs = ctx->frame_num - 1; + if (num_backrefs > 15) + num_backrefs = 15; + + for (i = 1; i <= num_backrefs; i++) { + + match = compare_blocks(ctx->buf_ptrs[(ctx->ptr_index + i) % 16] + offset, + ctx->cur_frame_buf + offset, + ctx->y_stride, 8, + FALSE); + + if (match > LUMINANCE_THRESHOLD && match > best_match) { + best_index = i; + best_match = match; + } + + } + + if (best_index != 0) { + + /* Yes: write out '01' to indicate a "change but like previous"-condition, + * followed by 4 bits containing the back-reference. */ + _write_bits(ctx, 0, 1); + _write_bits(ctx, 1, 1); + _write_bits(ctx, best_index, 4); + + src = ctx->buf_ptrs[(ctx->ptr_index + best_index) % 16] + offset; + encoded = TRUE; + + } + } + } + + if (!encoded) { + + /* Keyframe or in any case no? ;-) Well, encode it then. */ + + if (is_pframe) { + _write_bits(ctx, 0, 1); + _write_bits(ctx, 0, 1); + } + + _fdct_quant_block(ctx, + dct_block, + ctx->cur_frame_buf + offset, + ctx->y_stride, + FALSE, + ctx->num_coeffs); + + _vlc_encode_block(ctx, + dct_block, + ctx->num_coeffs); + + } + + /* And if there was some kind of no-change condition, + * we want to copy the previous block. */ + if (src != NULL) { + + dst = ctx->cur_frame_buf + offset; + for (i = 0; i < 8; i++) { + + memcpy(dst, src, 8); + + src += ctx->y_stride; + dst += ctx->y_stride; + } + + } + + } + + } + + /* + * Encode Cr and Cb planes. + */ + for (chrom_ch = 0; chrom_ch < 2; chrom_ch++) { + + /* Calculate base offset into buffer. */ + gint base_offset = ctx->y_size + (ctx->crcb_size * chrom_ch); + + for (y = 0; y < ctx->num_vblocks_cbcr; y++) { + guchar tmp_block[64]; + guint num_rows = 8; + + /* The last row of blocks in chrominance for 160x120 resolution + * is half the normal height and must be accounted for. */ + if (y + 1 == ctx->num_vblocks_cbcr && ctx->frame_height % 16 != 0) + num_rows = 4; + + for (x = 0; x < ctx->num_hblocks_cbcr; x++) { + + /* Calculate final offset into buffer. */ + offset = base_offset + (ctx->crcb_stride * 8 * y) + (x * 8); + + src = NULL; + encoded = FALSE; + + if (is_pframe) { + + /* Is the current block similar enough to what it was in the previous frame? */ + + match = compare_blocks(ctx->prev_frame_buf + offset, + ctx->cur_frame_buf + offset, + ctx->crcb_stride, num_rows, + TRUE); + + if (match > CHROMINANCE_THRESHOLD) { + + /* Yes: write out '0' to indicate a no-change condition. */ + + _write_bits(ctx, 0, 1); + + encoded = TRUE; + + src = ctx->prev_frame_buf + offset; + dst = ctx->cur_frame_buf + offset; + for (i = 0; i < num_rows; i++) { + + memcpy(dst, src, 8); + + src += ctx->crcb_stride; + dst += ctx->crcb_stride; + } + } + + } + + if (!encoded) { + + /* Keyframe or just not similar enough? ;-) Well, encode it then. */ + + if (is_pframe) + _write_bits(ctx, 1, 1); + + /* Use a temporary array to handle cases where the + * current block is not of normal height (see above). */ + src = ctx->cur_frame_buf + offset; + dst = tmp_block; + for (i = 0; i < 8; i++) { + + memcpy(dst, src, 8); + + if (i < (num_rows - 1)) + src += ctx->crcb_stride; + dst += 8; + } + + _fdct_quant_block(ctx, + dct_block, + tmp_block, + 8, + TRUE, + ctx->num_coeffs); + + _vlc_encode_block(ctx, + dct_block, + ctx->num_coeffs); + + } + + } + + } + + } + + /* + * Make a copy of the current frame and store in + * the circular pointer list of 16 entries. + */ + ctx->prev_frame_buf = ctx->buf_ptrs[ctx->ptr_index]; + memcpy(ctx->prev_frame_buf, ctx->cur_frame_buf, + ctx->y_size + (ctx->crcb_size * 2)); + + if (--ctx->ptr_index < 0) + ctx->ptr_index = 15; +} + +/* + * compare_blocks + * + * Helper-function used to compare two blocks and + * determine how similar they are. + */ +static gdouble compare_blocks(const guchar *p1, + const guchar *p2, + gint stride, + gint row_count, + gboolean is_chrom) +{ + gint i, j, sum; + gdouble d; + + sum = 0; + + for (i = 0; i < row_count; i++) { + + for (j = 0; j < 8; j++) { + + gint d = p2[j] - p1[j]; + + sum += d * d; + } + + p1 += stride; + p2 += stride; + } + + if (is_chrom) { + if (row_count == 8) + d = sum * 0.015625; + else + d = sum * 0.03125; + } else { + d = sum / 64; + } + + if (d == 0.0f) + return 100.0f; + else + return (10.0f * log(65025.0f / d)) / G_LN10; +} + diff --git a/kopete/protocols/msn/webcam/libmimic/fdct_quant.c b/kopete/protocols/msn/webcam/libmimic/fdct_quant.c new file mode 100644 index 00000000..f32adc80 --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/fdct_quant.c @@ -0,0 +1,181 @@ +/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mimic-private.h" + +extern guchar _col_zag[64]; + +void _fdct_quant_block(MimCtx *ctx, gint *block, const guchar *src, + gint stride, gboolean is_chrom, gint num_coeffs) +{ + gint sum1, sum2, sum3, sum4; + gint diff1, diff2, diff3, diff4; + gint ex1, ex2, ex3, ex4, ex5; + gint i, j; + const guchar *p1; + gint *iptr; + + /* + * Forward DCT, first pass (horizontal). + */ + p1 = src; + iptr = block; + + for (i = 0; i < 8; i++) { + sum1 = p1[0] + p1[7]; + sum2 = p1[1] + p1[6]; + sum3 = p1[2] + p1[5]; + sum4 = p1[3] + p1[4]; + + diff1 = p1[0] - p1[7]; + diff2 = p1[1] - p1[6]; + diff3 = p1[2] - p1[5]; + diff4 = p1[3] - p1[4]; + + ex1 = ((diff1 + diff4) * 851) - (diff1 * 282); + ex2 = ((diff2 + diff3) * 1004) - (diff2 * 804); + ex3 = ((diff2 + diff3) * 1004) - (diff3 * 1204); + ex4 = ((diff1 + diff4) * 851) - (diff4 * 1420); + + iptr[0] = sum1 + sum2 + sum3 + sum4; + iptr[2] = (((sum1 - sum4) * 1337) + ((sum2 - sum3) * 554)) >> 10; + iptr[4] = sum1 - sum2 - sum3 + sum4; + + iptr[1] = (ex1 + ex2 + ex3 + ex4) >> 10; + iptr[3] = ((ex4 - ex2) * 181) >> 17; + iptr[5] = ((ex1 - ex3) * 181) >> 17; + + p1 += stride; + iptr += 8; + } + + p1 = src; + iptr = block; + + /* + * Forward DCT, first pass (vertical). + * + * This is only known to be correct for i == 0, though it seems to be ... + */ + for (i = 0; i < 6; i++) { + sum1 = iptr[ 0 + i] + iptr[56 + i]; + sum2 = iptr[ 8 + i] + iptr[48 + i]; + sum3 = iptr[16 + i] + iptr[40 + i]; + sum4 = iptr[24 + i] + iptr[32 + i]; + + diff1 = iptr[ 0 + i] - iptr[56 + i]; + diff2 = iptr[ 8 + i] - iptr[48 + i]; + diff3 = iptr[16 + i] - iptr[40 + i]; + diff4 = iptr[24 + i] - iptr[32 + i]; + + ex1 = ((diff1 + diff4) * 851) - (diff1 * 282); + ex2 = ((diff2 + diff3) * 1004) - (diff2 * 804); + ex3 = ((diff2 + diff3) * 1004) - (diff3 * 1204); + ex4 = ((diff1 + diff4) * 851) - (diff4 * 1420); + + ex5 = (sum1 + sum2 - sum3 - sum4) * 554; + + for (j = 0; j < 7 - i; j++) { + switch (j) { + + case 0: + iptr[ 0 + i] = (16 + sum1 + sum2 + sum3 + sum4) >> 5; + break; + + case 1: + iptr[ 8 + i] = (16384 + ex1 + ex2 + ex3 + ex4) >> 15; + break; + + case 2: + iptr[16 + i] = (16384 + ((sum1 - sum4) * 783) + ex5) >> 15; + break; + + case 3: + iptr[24 + i] = (8192 + (((ex4 - ex2) >> 8) * 181)) >> 14; + break; + + case 4: + iptr[32 + i] = (16 + sum1 - sum2 - sum3 + sum4) >> 5; + break; + + case 5: + iptr[40 + i] = (8192 + (((ex1 - ex3) >> 8) * 181)) >> 14; + break; + + case 6: + iptr[48 + i] = (16384 - ((sum2 - sum3) * 1891) + ex5) >> 15; + break; + } + } + } + + /* + * Quantize. + */ + block[0] /= 2; + block[8] /= 4; + block[1] /= 4; + block[6] = 0; + + if (num_coeffs > 3) { + + gdouble s = (10000 - ctx->quality) * 10.0 * (gfloat) 9.9999997e-5; + + if (s > 10.0) + s = 10.0; + else if (is_chrom != 0 && s < 1.0) + s = 1.0; + else if (s < 2.0) + s = 2.0; + + s = 1.0 / s; + + for (i = 3; i < num_coeffs; i++) { + + gdouble coeff, r; + + coeff = block[_col_zag[i]] * s; + r = coeff - (gint) coeff; + + if (r >= 0.6) + block[_col_zag[i]] = (gint) (coeff + 1.0); + else if (r <= -0.6) + block[_col_zag[i]] = (gint) (coeff - 1.0); + else + block[_col_zag[i]] = (gint) coeff; + + if (block[_col_zag[i]] > 120) + block[_col_zag[i]] = 120; + else if (block[_col_zag[i]] < -120) + block[_col_zag[i]] = -120; + } + } + + if (block[8] > 120) + block[8] = 120; + else if (block[8] < -120) + block[8] = -120; + + if (block[1] > 120) + block[1] = 120; + else if (block[1] < -120) + block[1] = -120; + + for (i = num_coeffs; i < 64; i++) + block[_col_zag[i]] = 0; +} + diff --git a/kopete/protocols/msn/webcam/libmimic/idct_dequant.c b/kopete/protocols/msn/webcam/libmimic/idct_dequant.c new file mode 100644 index 00000000..3512247e --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/idct_dequant.c @@ -0,0 +1,134 @@ +/* Copyright (C) 2005 Ole Andr Vadla Ravns <oleavr@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mimic-private.h" + +void _idct_dequant_block(MimCtx *ctx, gint *block, gboolean is_chrom) +{ + gdouble f; + gint i, *p; + + /* + * De-quantize. + */ + f = (10000 - ctx->quality) * 10.0 * (gfloat) 9.9999997e-5; + + if (f > 10.0) + f = 10.0; + + if (!is_chrom) { + if (f < 2.0) + f = 2.0; + } else { + if (f < 1.0) + f = 1.0; + } + + block[0] <<= 1; + block[1] <<= 2; + block[8] <<= 2; + + for (i = 2; i < 64; i++) { + if (i == 8) + continue; + + block[i] *= f; + } + + /* + * Inverse DCT, first pass (horizontal). + */ + p = block; + + for (i = 0; i < 8; i++) { + gint v1, v2, v3, v4, v5, v6, v7, v8; + gint va, vb; + + va = (p[0] << 11) + (p[4] << 11); + vb = ((p[2] << 2) * 392) + (((p[2] << 2) + (p[6] << 2)) * 277); + v1 = va + vb + 512; + v2 = va - vb + 512; + + va = (p[0] << 11) - (p[4] << 11); + vb = (((p[2] << 2) + (p[6] << 2)) * 277) - ((p[6] << 2) * 946); + v3 = va + vb + 512; + v4 = va - vb + 512; + + va = (p[1] << 9) + (p[3] * 724) + (p[7] << 9); + vb = (p[1] << 9) + (p[5] * 724) - (p[7] << 9); + v5 = (((va + vb) * 213) - (vb * 71)) >> 6; + v6 = (((va + vb) * 213) - (va * 355)) >> 6; + + va = (p[1] << 9) - (p[3] * 724) + (p[7] << 9); + vb = (p[1] << 9) - (p[5] * 724) - (p[7] << 9); + v7 = (((va + vb) * 251) - (va * 201)) >> 6; + v8 = (((va + vb) * 251) - (vb * 301)) >> 6; + + p[0] = (v1 + v5) >> 10; + p[1] = (v3 + v7) >> 10; + p[2] = (v4 + v8) >> 10; + p[3] = (v2 + v6) >> 10; + p[4] = (v2 - v6) >> 10; + p[5] = (v4 - v8) >> 10; + p[6] = (v3 - v7) >> 10; + p[7] = (v1 - v5) >> 10; + + p += 8; + } + + /* + * Inverse dct, second pass (vertical). + */ + p = block; + + for (i = 0; i < 8; i++) { + gint v1, v2, v3, v4, v5, v6, v7, v8; + gint va, vb; + + va = (p[0] << 9) + (p[32] << 9); + vb = ((p[16] + p[48]) * 277) + (p[16] * 392); + v1 = va + vb + 1024; + v2 = va - vb + 1024; + + va = (p[0] << 9) - (p[32] << 9); + vb = ((p[16] + p[48]) * 277) - (p[48] * 946); + v3 = va + vb + 1024; + v4 = va - vb + 1024; + + va = ((p[8] << 7) + (p[24] * 181) + (p[56] << 7)) >> 6; + vb = ((p[8] << 7) + (p[40] * 181) - (p[56] << 7)) >> 6; + v5 = ((va + vb) * 213) - (vb * 71); + v6 = ((va + vb) * 213) - (va * 355); + + va = ((p[8] << 7) - (p[24] * 181) + (p[56] << 7)) >> 6; + vb = ((p[8] << 7) - (p[40] * 181) - (p[56] << 7)) >> 6; + v7 = ((va + vb) * 251) - (va * 201); + v8 = ((va + vb) * 251) - (vb * 301); + + p[0] = (v1 + v5) >> 11; + p[8] = (v3 + v7) >> 11; + p[16] = (v4 + v8) >> 11; + p[24] = (v2 + v6) >> 11; + p[32] = (v2 - v6) >> 11; + p[40] = (v4 - v8) >> 11; + p[48] = (v3 - v7) >> 11; + p[56] = (v1 - v5) >> 11; + + p++; + } +} + diff --git a/kopete/protocols/msn/webcam/libmimic/mimic-private.h b/kopete/protocols/msn/webcam/libmimic/mimic-private.h new file mode 100644 index 00000000..adf59291 --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/mimic-private.h @@ -0,0 +1,117 @@ +/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MIMIC_PRIVATE_H +#define MIMIC_PRIVATE_H + +#include "mimic.h" + +#define ENCODER_BUFFER_SIZE 16384 +#define ENCODER_QUALITY_DEFAULT 0 +#define ENCODER_QUALITY_MIN 0 +#define ENCODER_QUALITY_MAX 10000 + +struct _MimCtx { + gboolean encoder_initialized; + gboolean decoder_initialized; + + gint frame_width; + gint frame_height; + gint quality; + gint num_coeffs; + + gint y_stride; + gint y_row_count; + gint y_size; + + gint crcb_stride; + gint crcb_row_count; + gint crcb_size; + + gint num_vblocks_y; + gint num_hblocks_y; + + gint num_vblocks_cbcr; + gint num_hblocks_cbcr; + + guchar *cur_frame_buf; + guchar *prev_frame_buf; + + gint8 vlcdec_lookup[2296]; + + gchar *data_buffer; + guint data_index; + + guint32 cur_chunk; + gint cur_chunk_len; + + guint32 *chunk_ptr; + gboolean read_odd; + + gint frame_num; + + gint ptr_index; + guchar *buf_ptrs[16]; +}; + +typedef struct { + guchar length1; + guint32 part1; + + guchar length2; + guint32 part2; +} VlcSymbol; + +typedef struct { + guint32 magic; + guchar pos_add; + guchar num_bits; +} VlcMagic; + +void _mimic_init(MimCtx *ctx, gint width, gint height); +guchar _clamp_value(gint value); + +guint32 _read_bits(MimCtx *ctx, gint num_bits); +void _write_bits(MimCtx *ctx, guint32 bits, gint length); + +void _vlc_encode_block(MimCtx *ctx, const gint *block, gint num_coeffs); +gboolean _vlc_decode_block(MimCtx *ctx, gint *block, gint num_coeffs); + +void _fdct_quant_block(MimCtx *ctx, gint *block, const guchar *src, + gint stride, gboolean is_chrom, gint num_coeffs); +void _idct_dequant_block(MimCtx *ctx, gint *block, gboolean is_chrom); + +VlcMagic *_find_magic(guint magic); +void _initialize_vlcdec_lookup(gint8 *lookup_tbl); + +void _rgb_to_yuv(const guchar *input_rgb, + guchar *output_y, + guchar *output_cb, + guchar *output_cr, + gint width, + gint height); +void _yuv_to_rgb(const guchar *input_y, + const guchar *input_cb, + const guchar *input_cr, + guchar *output_rgb, + guint width, + guint height); + +void _deblock(guchar *blocks, guint stride, guint row_count); + +#endif + diff --git a/kopete/protocols/msn/webcam/libmimic/mimic.c b/kopete/protocols/msn/webcam/libmimic/mimic.c new file mode 100644 index 00000000..61abf9e8 --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/mimic.c @@ -0,0 +1,334 @@ +/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> +#include "mimic-private.h" + +/** + * Creates a new instance and returns a pointer to the new context + * that can be used for either encoding or decoding by calling + * #mimic_encoder_init or #mimic_decoder_init. + * + * #mimic_close is called to free any resources associated with + * the context once done. + * + * @returns a new mimic context + */ +MimCtx *mimic_open() +{ + MimCtx *ctx; + + ctx = g_new0(MimCtx, 1); + + ctx->encoder_initialized = FALSE; + ctx->decoder_initialized = FALSE; + + return ctx; +} + +/** + * Frees any resources associated with the given context. + * + * @param ctx the mimic context to free + */ +void mimic_close(MimCtx *ctx) +{ + if (ctx->encoder_initialized || ctx->decoder_initialized) { + gint i; + + g_free(ctx->cur_frame_buf); + + for (i = 0; i < 16; i++) + g_free(ctx->buf_ptrs[i]); + } + + g_free(ctx); +} + +/* + * mimic_init + * + * Internal helper-function used to initialize + * a given context. + */ +static void mimic_init(MimCtx *ctx, gint width, gint height) +{ + gint bufsize, i; + + /* + * Dimensions-related. + */ + ctx->frame_width = width; + ctx->frame_height = height; + + ctx->y_stride = ctx->frame_width; + ctx->y_row_count = ctx->frame_height; + ctx->y_size = ctx->y_stride * ctx->y_row_count; + + ctx->crcb_stride = ctx->y_stride / 2; + ctx->crcb_row_count = ctx->y_row_count / 2; + ctx->crcb_size = ctx->crcb_stride * ctx->crcb_row_count; + + ctx->num_vblocks_y = ctx->frame_height / 8; + ctx->num_hblocks_y = ctx->frame_width / 8; + + ctx->num_vblocks_cbcr = ctx->frame_height / 16; + ctx->num_hblocks_cbcr = ctx->frame_width / 16; + + if (ctx->frame_height % 16 != 0) + ctx->num_vblocks_cbcr++; + + /* + * Initialize state. + */ + ctx->frame_num = 0; + ctx->ptr_index = 15; + ctx->num_coeffs = 28; + + /* + * Allocate memory for buffers. + */ + ctx->cur_frame_buf = g_new(guchar, (320 * 240 * 3) / 2); + + bufsize = ctx->y_size + (ctx->crcb_size * 2); + for (i = 0; i < 16; i++) + ctx->buf_ptrs[i] = g_new(guchar, bufsize); + + /* + * Initialize vlc lookup used by decoder. + */ + _initialize_vlcdec_lookup(ctx->vlcdec_lookup); +} + +/** + * Initialize the mimic encoder and prepare for encoding by + * initializing internal state and allocating resources as + * needed. + * + * After initializing use #mimic_get_property to determine + * the size of the output buffer needed for calls to + * #mimic_encode_frame. Use #mimic_set_property to set + * encoding quality. + * + * Note that once a given context has been initialized + * for either encoding or decoding it is not possible + * to initialize it again. + * + * @param ctx the mimic context to initialize + * @param resolution a #MimicResEnum used to specify the resolution + * @returns #TRUE on success + */ +gboolean mimic_encoder_init(MimCtx *ctx, const MimicResEnum resolution) +{ + gint width, height; + + /* Check if we've been initialized before. */ + if (ctx->encoder_initialized || ctx->decoder_initialized) + return FALSE; + + /* Check resolution. */ + if (resolution == MIMIC_RES_LOW) { + width = 160; + height = 120; + } else if (resolution == MIMIC_RES_HIGH) { + width = 320; + height = 240; + } else { + return FALSE; + } + + /* Initialize! */ + mimic_init(ctx, width, height); + + /* Set a default quality setting. */ + ctx->quality = ENCODER_QUALITY_DEFAULT; + + ctx->encoder_initialized = TRUE; + + return TRUE; +} + +/** + * Initialize the mimic decoder. The frame passed in frame_buffer + * is used to determine the resolution so that the internal state + * can be prepared and resources allocated accordingly. Note that + * the frame passed has to be a keyframe. + * + * After initializing use #mimic_get_property to determine required + * buffer-size, resolution, quality, etc. + * + * Note that once a given context has been initialized + * for either encoding or decoding it is not possible + * to initialize it again. + * + * @param ctx the mimic context to initialize + * @param frame_buffer buffer containing the first frame to decode + * @returns #TRUE on success + */ +gboolean mimic_decoder_init(MimCtx *ctx, const guchar *frame_buffer) +{ + gint width, height; + gboolean is_keyframe; + + /* Check if we've been initialized before and that + * frame_buffer is not NULL. */ + if (ctx->encoder_initialized || ctx->decoder_initialized || + frame_buffer == NULL) + { + return FALSE; + } + + /* Check resolution. */ + width = GUINT16_FROM_LE(*((guint16 *) (frame_buffer + 4))); + height = GUINT16_FROM_LE(*((guint16 *) (frame_buffer + 6))); + + if (!(width == 160 && height == 120) && !(width == 320 && height == 240)) + return FALSE; + + /* Check that we're initialized with a keyframe. */ + is_keyframe = (GUINT32_FROM_LE(*((guint32 *) (frame_buffer + 12))) == 0); + + if (!is_keyframe) + return FALSE; + + /* Get quality setting (in case we get queried for it before decoding). */ + ctx->quality = GUINT16_FROM_LE(*((guint16 *) (frame_buffer + 2))); + + /* Initialize! */ + mimic_init(ctx, width, height); + + ctx->decoder_initialized = TRUE; + + return TRUE; +} + +/** + * Get a property from a given mimic context. The context + * has to be initialized. + * + * Currently the following properties are defined: + * - "buffer_size" + * - Required output buffer size + * - "width" + * - Frame width + * - "height" + * - Frame height + * - "quality" + * - Encoder: Encoding quality used + * - Decoder: Decoding quality of the last known frame + * + * @param ctx the mimic context to retrieve the property from + * @param name of the property to retrieve the current value of + * @param data pointer to the data that will receive the retrieved value + * @returns #TRUE on success + */ +gboolean mimic_get_property(MimCtx *ctx, const gchar *name, gpointer data) +{ + /* Either the encoder or the decoder has to be initialized. */ + if (!ctx->encoder_initialized && !ctx->decoder_initialized) + return FALSE; + + if (ctx->encoder_initialized) { + + if (strcmp(name, "buffer_size") == 0) { + *((gint *) data) = ENCODER_BUFFER_SIZE; + + return TRUE; + } + + } else { /* decoder_initialized */ + + if (strcmp(name, "buffer_size") == 0) { + *((gint *) data) = ctx->frame_width * ctx->frame_height * 3; + + return TRUE; + } + } + + if (strcmp(name, "width") == 0) { + *((gint *) data) = ctx->frame_width; + + return TRUE; + } else if (strcmp(name, "height") == 0) { + *((gint *) data) = ctx->frame_height; + + return TRUE; + } else if (strcmp(name, "quality") == 0) { + *((gint *) data) = ctx->quality; + + return TRUE; + } + + return FALSE; +} + +/** + * Set a property in a given mimic context. The context + * has to be initialized. + * + * Currently the following properties are defined: + * - "quality" + * - Encoding quality used by encoder. + * + * @param ctx the mimic context to set a property in + * @param name of the property to set to a new value + * @param data pointer to the data that contains the new value + * @returns #TRUE on success + */ +gboolean mimic_set_property(MimCtx *ctx, const gchar *name, gpointer data) +{ + /* Either the encoder or the decoder has to be initialized. */ + if (!ctx->encoder_initialized && !ctx->decoder_initialized) + return FALSE; + + if (ctx->encoder_initialized) { + + if (strcmp(name, "quality") == 0) { + gint new_quality = *((gint *) data); + + if (new_quality < ENCODER_QUALITY_MIN || + new_quality > ENCODER_QUALITY_MAX) + { + return FALSE; + } + + ctx->quality = new_quality; + + return TRUE; + } + + } else { /* decoder_initialized */ } + + return FALSE; +} + +/* + * _clamp_value + * + * Internal helper-function used to clamp a given + * value to the range [ 0, 255 ]. + */ +guchar _clamp_value(gint value) +{ + if (value < 0) + return 0; + else if (value > 255) + return 255; + else + return value; +} + diff --git a/kopete/protocols/msn/webcam/libmimic/mimic.h b/kopete/protocols/msn/webcam/libmimic/mimic.h new file mode 100644 index 00000000..9660e674 --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/mimic.h @@ -0,0 +1,73 @@ +/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MIMIC_H +#define MIMIC_H + +#include <glib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup libmimic libmimic public API + * @brief The public API of the libmimic library + * + * libmimic provides the API required for encoding and decoding + * MIMIC v2.x-encoded content. + * + * @{ + */ + +/** + * The mimic encoding/decoding context returned by #mimic_open + * and used for all further API calls until #mimic_close. + */ +typedef struct _MimCtx MimCtx; + +typedef enum { + MIMIC_RES_LOW, /**< 160x120 resolution */ + MIMIC_RES_HIGH /**< 320x240 resolution */ +} MimicResEnum; + +MimCtx *mimic_open(); +void mimic_close(MimCtx *ctx); + +gboolean mimic_encoder_init(MimCtx *ctx, const MimicResEnum resolution); +gboolean mimic_decoder_init(MimCtx *ctx, const guchar *frame_buffer); + +gboolean mimic_get_property(MimCtx *ctx, const gchar *name, gpointer data); +gboolean mimic_set_property(MimCtx *ctx, const gchar *name, gpointer data); + +gboolean mimic_encode_frame(MimCtx *ctx, + const guchar *input_buffer, + guchar *output_buffer, + gint *output_length, + gboolean make_keyframe); +gboolean mimic_decode_frame(MimCtx *ctx, + const guchar *input_buffer, + guchar *output_buffer); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/kopete/protocols/msn/webcam/libmimic/query.c b/kopete/protocols/msn/webcam/libmimic/query.c new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/query.c @@ -0,0 +1 @@ + diff --git a/kopete/protocols/msn/webcam/libmimic/vlc_common.c b/kopete/protocols/msn/webcam/libmimic/vlc_common.c new file mode 100644 index 00000000..16a4859f --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/vlc_common.c @@ -0,0 +1,1364 @@ +/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdlib.h> +#include "mimic-private.h" + +guchar _col_zag[64] = { + 0, 8, 1, 2, 9, 16, 24, 17, + 10, 3, 4, 11, 18, 25, 32, 40, + 33, 26, 19, 12, 5, 6, 13, 20, + 27, 34, 41, 48, 56, 49, 42, 35, + 28, 21, 14, 7, 15, 22, 29, 36, + 43, 50, 57, 58, 51, 44, 37, 30, + 23, 31, 38, 45, 52, 59, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +VlcSymbol _vlc_alphabet[16][128] = { + + /* + * base alphabet - no zeroes prefixed + */ + { + { 3, 0x1, 0, 0 }, { 4, 0x7, 0, 0 }, + { 4, 0x5, 0, 0 }, { 6, 0x27, 0, 0 }, + { 6, 0x25, 0, 0 }, { 6, 0x23, 0, 0 }, + { 6, 0x21, 0, 0 }, { 8, 0xcf, 0, 0 }, + { 8, 0xcd, 0, 0 }, { 8, 0xcb, 0, 0 }, + { 8, 0xc9, 0, 0 }, { 8, 0xc7, 0, 0 }, + { 8, 0xc5, 0, 0 }, { 8, 0xc3, 0, 0 }, + { 8, 0xc1, 0, 0 }, { 10, 0x35f, 0, 0 }, + { 10, 0x35d, 0, 0 }, { 10, 0x35b, 0, 0 }, + { 10, 0x359, 0, 0 }, { 10, 0x357, 0, 0 }, + { 10, 0x355, 0, 0 }, { 10, 0x353, 0, 0 }, + { 10, 0x351, 0, 0 }, { 10, 0x34f, 0, 0 }, + { 10, 0x34d, 0, 0 }, { 10, 0x34b, 0, 0 }, + { 10, 0x349, 0, 0 }, { 10, 0x347, 0, 0 }, + { 10, 0x345, 0, 0 }, { 10, 0x343, 0, 0 }, + { 10, 0x341, 0, 0 }, { 12, 0xeff, 0, 0 }, + { 12, 0xefd, 0, 0 }, { 12, 0xefb, 0, 0 }, + { 12, 0xef9, 0, 0 }, { 12, 0xef7, 0, 0 }, + { 12, 0xef5, 0, 0 }, { 12, 0xef3, 0, 0 }, + { 12, 0xef1, 0, 0 }, { 12, 0xeef, 0, 0 }, + { 12, 0xeed, 0, 0 }, { 12, 0xeeb, 0, 0 }, + { 12, 0xee9, 0, 0 }, { 12, 0xee7, 0, 0 }, + { 12, 0xee5, 0, 0 }, { 12, 0xee3, 0, 0 }, + { 12, 0xee1, 0, 0 }, { 12, 0xedf, 0, 0 }, + { 12, 0xedd, 0, 0 }, { 12, 0xedb, 0, 0 }, + { 12, 0xed9, 0, 0 }, { 12, 0xed7, 0, 0 }, + { 12, 0xed5, 0, 0 }, { 12, 0xed3, 0, 0 }, + { 12, 0xed1, 0, 0 }, { 12, 0xecf, 0, 0 }, + { 12, 0xecd, 0, 0 }, { 12, 0xecb, 0, 0 }, + { 12, 0xec9, 0, 0 }, { 12, 0xec7, 0, 0 }, + { 12, 0xec5, 0, 0 }, { 12, 0xec3, 0, 0 }, + { 12, 0xec1, 0, 0 }, { 17, 0x1fd7f, 0, 0 }, + { 17, 0x1fd7d, 0, 0 }, { 17, 0x1fd7b, 0, 0 }, + { 17, 0x1fd79, 0, 0 }, { 17, 0x1fd77, 0, 0 }, + { 17, 0x1fd75, 0, 0 }, { 17, 0x1fd73, 0, 0 }, + { 17, 0x1fd71, 0, 0 }, { 17, 0x1fd6f, 0, 0 }, + { 17, 0x1fd6d, 0, 0 }, { 17, 0x1fd6b, 0, 0 }, + { 17, 0x1fd69, 0, 0 }, { 17, 0x1fd67, 0, 0 }, + { 17, 0x1fd65, 0, 0 }, { 17, 0x1fd63, 0, 0 }, + { 17, 0x1fd61, 0, 0 }, { 17, 0x1fd5f, 0, 0 }, + { 17, 0x1fd5d, 0, 0 }, { 17, 0x1fd5b, 0, 0 }, + { 17, 0x1fd59, 0, 0 }, { 17, 0x1fd57, 0, 0 }, + { 17, 0x1fd55, 0, 0 }, { 17, 0x1fd53, 0, 0 }, + { 17, 0x1fd51, 0, 0 }, { 17, 0x1fd4f, 0, 0 }, + { 17, 0x1fd4d, 0, 0 }, { 17, 0x1fd4b, 0, 0 }, + { 17, 0x1fd49, 0, 0 }, { 17, 0x1fd47, 0, 0 }, + { 17, 0x1fd45, 0, 0 }, { 17, 0x1fd43, 0, 0 }, + { 17, 0x1fd41, 0, 0 }, { 17, 0x1fd3f, 0, 0 }, + { 17, 0x1fd3d, 0, 0 }, { 17, 0x1fd3b, 0, 0 }, + { 17, 0x1fd39, 0, 0 }, { 17, 0x1fd37, 0, 0 }, + { 17, 0x1fd35, 0, 0 }, { 17, 0x1fd33, 0, 0 }, + { 17, 0x1fd31, 0, 0 }, { 17, 0x1fd2f, 0, 0 }, + { 17, 0x1fd2d, 0, 0 }, { 17, 0x1fd2b, 0, 0 }, + { 17, 0x1fd29, 0, 0 }, { 17, 0x1fd27, 0, 0 }, + { 17, 0x1fd25, 0, 0 }, { 17, 0x1fd23, 0, 0 }, + { 17, 0x1fd21, 0, 0 }, { 17, 0x1fd1f, 0, 0 }, + { 17, 0x1fd1d, 0, 0 }, { 17, 0x1fd1b, 0, 0 }, + { 17, 0x1fd19, 0, 0 }, { 17, 0x1fd17, 0, 0 }, + { 17, 0x1fd15, 0, 0 }, { 17, 0x1fd13, 0, 0 }, + { 17, 0x1fd11, 0, 0 }, { 17, 0x1fd0f, 0, 0 }, + { 17, 0x1fd0d, 0, 0 }, { 17, 0x1fd0b, 0, 0 }, + { 17, 0x1fd09, 0, 0 }, { 17, 0x1fd07, 0, 0 }, + { 17, 0x1fd05, 0, 0 }, { 17, 0x1fd03, 0, 0 }, + { 17, 0x1fd01, 0, 0 }, { 17, 0x1fd01, 0, 0 } + }, + + /* + * prefixed with 1 zero + */ + { + { 5, 0x17, 0, 0 }, { 8, 0xe7, 0, 0 }, + { 8, 0xe5, 0, 0 }, { 9, 0x1d7, 0, 0 }, + { 9, 0x1d5, 0, 0 }, { 9, 0x1d3, 0, 0 }, + { 9, 0x1d1, 0, 0 }, { 12, 0xf8f, 0, 0 }, + { 12, 0xf8d, 0, 0 }, { 12, 0xf8b, 0, 0 }, + { 12, 0xf89, 0, 0 }, { 12, 0xf87, 0, 0 }, + { 12, 0xf85, 0, 0 }, { 12, 0xf83, 0, 0 }, + { 12, 0xf81, 0, 0 }, { 15, 0x7f1f, 0, 0 }, + { 15, 0x7f1d, 0, 0 }, { 15, 0x7f1b, 0, 0 }, + { 15, 0x7f19, 0, 0 }, { 15, 0x7f17, 0, 0 }, + { 15, 0x7f15, 0, 0 }, { 15, 0x7f13, 0, 0 }, + { 15, 0x7f11, 0, 0 }, { 15, 0x7f0f, 0, 0 }, + { 15, 0x7f0d, 0, 0 }, { 15, 0x7f0b, 0, 0 }, + { 15, 0x7f09, 0, 0 }, { 15, 0x7f07, 0, 0 }, + { 15, 0x7f05, 0, 0 }, { 15, 0x7f03, 0, 0 }, + { 15, 0x7f01, 0, 0 }, { 16, 0xfe7f, 0, 0 }, + { 16, 0xfe7d, 0, 0 }, { 16, 0xfe7b, 0, 0 }, + { 16, 0xfe79, 0, 0 }, { 16, 0xfe77, 0, 0 }, + { 16, 0xfe75, 0, 0 }, { 16, 0xfe73, 0, 0 }, + { 16, 0xfe71, 0, 0 }, { 16, 0xfe6f, 0, 0 }, + { 16, 0xfe6d, 0, 0 }, { 16, 0xfe6b, 0, 0 }, + { 16, 0xfe69, 0, 0 }, { 16, 0xfe67, 0, 0 }, + { 16, 0xfe65, 0, 0 }, { 16, 0xfe63, 0, 0 }, + { 16, 0xfe61, 0, 0 }, { 16, 0xfe5f, 0, 0 }, + { 16, 0xfe5d, 0, 0 }, { 16, 0xfe5b, 0, 0 }, + { 16, 0xfe59, 0, 0 }, { 16, 0xfe57, 0, 0 }, + { 16, 0xfe55, 0, 0 }, { 16, 0xfe53, 0, 0 }, + { 16, 0xfe51, 0, 0 }, { 16, 0xfe4f, 0, 0 }, + { 16, 0xfe4d, 0, 0 }, { 16, 0xfe4b, 0, 0 }, + { 16, 0xfe49, 0, 0 }, { 16, 0xfe47, 0, 0 }, + { 16, 0xfe45, 0, 0 }, { 16, 0xfe43, 0, 0 }, + { 16, 0xfe41, 0, 0 }, { 27, 0x7fffff9, 7, 0x7f }, + { 27, 0x7fffff9, 7, 0x7d }, { 27, 0x7fffff9, 7, 0x7b }, + { 27, 0x7fffff9, 7, 0x79 }, { 27, 0x7fffff9, 7, 0x77 }, + { 27, 0x7fffff9, 7, 0x75 }, { 27, 0x7fffff9, 7, 0x73 }, + { 27, 0x7fffff9, 7, 0x71 }, { 27, 0x7fffff9, 7, 0x6f }, + { 27, 0x7fffff9, 7, 0x6d }, { 27, 0x7fffff9, 7, 0x6b }, + { 27, 0x7fffff9, 7, 0x69 }, { 27, 0x7fffff9, 7, 0x67 }, + { 27, 0x7fffff9, 7, 0x65 }, { 27, 0x7fffff9, 7, 0x63 }, + { 27, 0x7fffff9, 7, 0x61 }, { 27, 0x7fffff9, 7, 0x5f }, + { 27, 0x7fffff9, 7, 0x5d }, { 27, 0x7fffff9, 7, 0x5b }, + { 27, 0x7fffff9, 7, 0x59 }, { 27, 0x7fffff9, 7, 0x57 }, + { 27, 0x7fffff9, 7, 0x55 }, { 27, 0x7fffff9, 7, 0x53 }, + { 27, 0x7fffff9, 7, 0x51 }, { 27, 0x7fffff9, 7, 0x4f }, + { 27, 0x7fffff9, 7, 0x4d }, { 27, 0x7fffff9, 7, 0x4b }, + { 27, 0x7fffff9, 7, 0x49 }, { 27, 0x7fffff9, 7, 0x47 }, + { 27, 0x7fffff9, 7, 0x45 }, { 27, 0x7fffff9, 7, 0x43 }, + { 27, 0x7fffff9, 7, 0x41 }, { 27, 0x7fffff9, 7, 0x3f }, + { 27, 0x7fffff9, 7, 0x3d }, { 27, 0x7fffff9, 7, 0x3b }, + { 27, 0x7fffff9, 7, 0x39 }, { 27, 0x7fffff9, 7, 0x37 }, + { 27, 0x7fffff9, 7, 0x35 }, { 27, 0x7fffff9, 7, 0x33 }, + { 27, 0x7fffff9, 7, 0x31 }, { 27, 0x7fffff9, 7, 0x2f }, + { 27, 0x7fffff9, 7, 0x2d }, { 27, 0x7fffff9, 7, 0x2b }, + { 27, 0x7fffff9, 7, 0x29 }, { 27, 0x7fffff9, 7, 0x27 }, + { 27, 0x7fffff9, 7, 0x25 }, { 27, 0x7fffff9, 7, 0x23 }, + { 27, 0x7fffff9, 7, 0x21 }, { 27, 0x7fffff9, 7, 0x1f }, + { 27, 0x7fffff9, 7, 0x1d }, { 27, 0x7fffff9, 7, 0x1b }, + { 27, 0x7fffff9, 7, 0x19 }, { 27, 0x7fffff9, 7, 0x17 }, + { 27, 0x7fffff9, 7, 0x15 }, { 27, 0x7fffff9, 7, 0x13 }, + { 27, 0x7fffff9, 7, 0x11 }, { 27, 0x7fffff9, 7, 0xf }, + { 27, 0x7fffff9, 7, 0xd }, { 27, 0x7fffff9, 7, 0xb }, + { 27, 0x7fffff9, 7, 0x9 }, { 27, 0x7fffff9, 7, 0x7 }, + { 27, 0x7fffff9, 7, 0x5 }, { 27, 0x7fffff9, 7, 0x3 }, + { 27, 0x7fffff9, 7, 0x1 }, { 27, 0x7fffff9, 7, 0x1 } + }, + + /* + * prefixed with 2 zeroes + */ + { + { 6, 0x37, 0, 0 }, { 9, 0x1ef, 0, 0 }, + { 9, 0x1ed, 0, 0 }, { 12, 0xfd7, 0, 0 }, + { 12, 0xfd5, 0, 0 }, { 12, 0xfd3, 0, 0 }, + { 12, 0xfd1, 0, 0 }, { 13, 0x1fbf, 0, 0 }, + { 13, 0x1fbd, 0, 0 }, { 13, 0x1fbb, 0, 0 }, + { 13, 0x1fb9, 0, 0 }, { 13, 0x1fb7, 0, 0 }, + { 13, 0x1fb5, 0, 0 }, { 13, 0x1fb3, 0, 0 }, + { 13, 0x1fb1, 0, 0 }, { 25, 0x1ffff7f, 0, 0 }, + { 25, 0x1ffff7d, 0, 0 }, { 25, 0x1ffff7b, 0, 0 }, + { 25, 0x1ffff79, 0, 0 }, { 25, 0x1ffff77, 0, 0 }, + { 25, 0x1ffff75, 0, 0 }, { 25, 0x1ffff73, 0, 0 }, + { 25, 0x1ffff71, 0, 0 }, { 25, 0x1ffff6f, 0, 0 }, + { 25, 0x1ffff6d, 0, 0 }, { 25, 0x1ffff6b, 0, 0 }, + { 25, 0x1ffff69, 0, 0 }, { 25, 0x1ffff67, 0, 0 }, + { 25, 0x1ffff65, 0, 0 }, { 25, 0x1ffff63, 0, 0 }, + { 25, 0x1ffff61, 0, 0 }, { 30, 0x3ffffe3f, 0, 0 }, + { 30, 0x3ffffe3d, 0, 0 }, { 30, 0x3ffffe3b, 0, 0 }, + { 30, 0x3ffffe39, 0, 0 }, { 30, 0x3ffffe37, 0, 0 }, + { 30, 0x3ffffe35, 0, 0 }, { 30, 0x3ffffe33, 0, 0 }, + { 30, 0x3ffffe31, 0, 0 }, { 30, 0x3ffffe2f, 0, 0 }, + { 30, 0x3ffffe2d, 0, 0 }, { 30, 0x3ffffe2b, 0, 0 }, + { 30, 0x3ffffe29, 0, 0 }, { 30, 0x3ffffe27, 0, 0 }, + { 30, 0x3ffffe25, 0, 0 }, { 30, 0x3ffffe23, 0, 0 }, + { 30, 0x3ffffe21, 0, 0 }, { 30, 0x3ffffe1f, 0, 0 }, + { 30, 0x3ffffe1d, 0, 0 }, { 30, 0x3ffffe1b, 0, 0 }, + { 30, 0x3ffffe19, 0, 0 }, { 30, 0x3ffffe17, 0, 0 }, + { 30, 0x3ffffe15, 0, 0 }, { 30, 0x3ffffe13, 0, 0 }, + { 30, 0x3ffffe11, 0, 0 }, { 30, 0x3ffffe0f, 0, 0 }, + { 30, 0x3ffffe0d, 0, 0 }, { 30, 0x3ffffe0b, 0, 0 }, + { 30, 0x3ffffe09, 0, 0 }, { 30, 0x3ffffe07, 0, 0 }, + { 30, 0x3ffffe05, 0, 0 }, { 30, 0x3ffffe03, 0, 0 }, + { 30, 0x3ffffe01, 0, 0 }, { 27, 0x7fffffa, 7, 0x7f }, + { 27, 0x7fffffa, 7, 0x7d }, { 27, 0x7fffffa, 7, 0x7b }, + { 27, 0x7fffffa, 7, 0x79 }, { 27, 0x7fffffa, 7, 0x77 }, + { 27, 0x7fffffa, 7, 0x75 }, { 27, 0x7fffffa, 7, 0x73 }, + { 27, 0x7fffffa, 7, 0x71 }, { 27, 0x7fffffa, 7, 0x6f }, + { 27, 0x7fffffa, 7, 0x6d }, { 27, 0x7fffffa, 7, 0x6b }, + { 27, 0x7fffffa, 7, 0x69 }, { 27, 0x7fffffa, 7, 0x67 }, + { 27, 0x7fffffa, 7, 0x65 }, { 27, 0x7fffffa, 7, 0x63 }, + { 27, 0x7fffffa, 7, 0x61 }, { 27, 0x7fffffa, 7, 0x5f }, + { 27, 0x7fffffa, 7, 0x5d }, { 27, 0x7fffffa, 7, 0x5b }, + { 27, 0x7fffffa, 7, 0x59 }, { 27, 0x7fffffa, 7, 0x57 }, + { 27, 0x7fffffa, 7, 0x55 }, { 27, 0x7fffffa, 7, 0x53 }, + { 27, 0x7fffffa, 7, 0x51 }, { 27, 0x7fffffa, 7, 0x4f }, + { 27, 0x7fffffa, 7, 0x4d }, { 27, 0x7fffffa, 7, 0x4b }, + { 27, 0x7fffffa, 7, 0x49 }, { 27, 0x7fffffa, 7, 0x47 }, + { 27, 0x7fffffa, 7, 0x45 }, { 27, 0x7fffffa, 7, 0x43 }, + { 27, 0x7fffffa, 7, 0x41 }, { 27, 0x7fffffa, 7, 0x3f }, + { 27, 0x7fffffa, 7, 0x3d }, { 27, 0x7fffffa, 7, 0x3b }, + { 27, 0x7fffffa, 7, 0x39 }, { 27, 0x7fffffa, 7, 0x37 }, + { 27, 0x7fffffa, 7, 0x35 }, { 27, 0x7fffffa, 7, 0x33 }, + { 27, 0x7fffffa, 7, 0x31 }, { 27, 0x7fffffa, 7, 0x2f }, + { 27, 0x7fffffa, 7, 0x2d }, { 27, 0x7fffffa, 7, 0x2b }, + { 27, 0x7fffffa, 7, 0x29 }, { 27, 0x7fffffa, 7, 0x27 }, + { 27, 0x7fffffa, 7, 0x25 }, { 27, 0x7fffffa, 7, 0x23 }, + { 27, 0x7fffffa, 7, 0x21 }, { 27, 0x7fffffa, 7, 0x1f }, + { 27, 0x7fffffa, 7, 0x1d }, { 27, 0x7fffffa, 7, 0x1b }, + { 27, 0x7fffffa, 7, 0x19 }, { 27, 0x7fffffa, 7, 0x17 }, + { 27, 0x7fffffa, 7, 0x15 }, { 27, 0x7fffffa, 7, 0x13 }, + { 27, 0x7fffffa, 7, 0x11 }, { 27, 0x7fffffa, 7, 0xf }, + { 27, 0x7fffffa, 7, 0xd }, { 27, 0x7fffffa, 7, 0xb }, + { 27, 0x7fffffa, 7, 0x9 }, { 27, 0x7fffffa, 7, 0x7 }, + { 27, 0x7fffffa, 7, 0x5 }, { 27, 0x7fffffa, 7, 0x3 }, + { 27, 0x7fffffa, 7, 0x1 }, { 27, 0x7fffffa, 7, 0x1 } + }, + + /* + * prefixed with 3 zeroes + */ + { + { 7, 0x71, 0, 0 }, { 10, 0x3ef, 0, 0 }, + { 10, 0x3ed, 0, 0 }, { 17, 0x1ffdf, 0, 0 }, + { 17, 0x1ffdd, 0, 0 }, { 17, 0x1ffdb, 0, 0 }, + { 17, 0x1ffd9, 0, 0 }, { 21, 0x1fffbf, 0, 0 }, + { 21, 0x1fffbd, 0, 0 }, { 21, 0x1fffbb, 0, 0 }, + { 21, 0x1fffb9, 0, 0 }, { 21, 0x1fffb7, 0, 0 }, + { 21, 0x1fffb5, 0, 0 }, { 21, 0x1fffb3, 0, 0 }, + { 21, 0x1fffb1, 0, 0 }, { 26, 0x3ffff1f, 0, 0 }, + { 26, 0x3ffff1d, 0, 0 }, { 26, 0x3ffff1b, 0, 0 }, + { 26, 0x3ffff19, 0, 0 }, { 26, 0x3ffff17, 0, 0 }, + { 26, 0x3ffff15, 0, 0 }, { 26, 0x3ffff13, 0, 0 }, + { 26, 0x3ffff11, 0, 0 }, { 26, 0x3ffff0f, 0, 0 }, + { 26, 0x3ffff0d, 0, 0 }, { 26, 0x3ffff0b, 0, 0 }, + { 26, 0x3ffff09, 0, 0 }, { 26, 0x3ffff07, 0, 0 }, + { 26, 0x3ffff05, 0, 0 }, { 26, 0x3ffff03, 0, 0 }, + { 26, 0x3ffff01, 0, 0 }, { 30, 0x3ffffe7f, 0, 0 }, + { 30, 0x3ffffe7d, 0, 0 }, { 30, 0x3ffffe7b, 0, 0 }, + { 30, 0x3ffffe79, 0, 0 }, { 30, 0x3ffffe77, 0, 0 }, + { 30, 0x3ffffe75, 0, 0 }, { 30, 0x3ffffe73, 0, 0 }, + { 30, 0x3ffffe71, 0, 0 }, { 30, 0x3ffffe6f, 0, 0 }, + { 30, 0x3ffffe6d, 0, 0 }, { 30, 0x3ffffe6b, 0, 0 }, + { 30, 0x3ffffe69, 0, 0 }, { 30, 0x3ffffe67, 0, 0 }, + { 30, 0x3ffffe65, 0, 0 }, { 30, 0x3ffffe63, 0, 0 }, + { 30, 0x3ffffe61, 0, 0 }, { 30, 0x3ffffe5f, 0, 0 }, + { 30, 0x3ffffe5d, 0, 0 }, { 30, 0x3ffffe5b, 0, 0 }, + { 30, 0x3ffffe59, 0, 0 }, { 30, 0x3ffffe57, 0, 0 }, + { 30, 0x3ffffe55, 0, 0 }, { 30, 0x3ffffe53, 0, 0 }, + { 30, 0x3ffffe51, 0, 0 }, { 30, 0x3ffffe4f, 0, 0 }, + { 30, 0x3ffffe4d, 0, 0 }, { 30, 0x3ffffe4b, 0, 0 }, + { 30, 0x3ffffe49, 0, 0 }, { 30, 0x3ffffe47, 0, 0 }, + { 30, 0x3ffffe45, 0, 0 }, { 30, 0x3ffffe43, 0, 0 }, + { 30, 0x3ffffe41, 0, 0 }, { 27, 0x7fffffb, 7, 0x7f }, + { 27, 0x7fffffb, 7, 0x7d }, { 27, 0x7fffffb, 7, 0x7b }, + { 27, 0x7fffffb, 7, 0x79 }, { 27, 0x7fffffb, 7, 0x77 }, + { 27, 0x7fffffb, 7, 0x75 }, { 27, 0x7fffffb, 7, 0x73 }, + { 27, 0x7fffffb, 7, 0x71 }, { 27, 0x7fffffb, 7, 0x6f }, + { 27, 0x7fffffb, 7, 0x6d }, { 27, 0x7fffffb, 7, 0x6b }, + { 27, 0x7fffffb, 7, 0x69 }, { 27, 0x7fffffb, 7, 0x67 }, + { 27, 0x7fffffb, 7, 0x65 }, { 27, 0x7fffffb, 7, 0x63 }, + { 27, 0x7fffffb, 7, 0x61 }, { 27, 0x7fffffb, 7, 0x5f }, + { 27, 0x7fffffb, 7, 0x5d }, { 27, 0x7fffffb, 7, 0x5b }, + { 27, 0x7fffffb, 7, 0x59 }, { 27, 0x7fffffb, 7, 0x57 }, + { 27, 0x7fffffb, 7, 0x55 }, { 27, 0x7fffffb, 7, 0x53 }, + { 27, 0x7fffffb, 7, 0x51 }, { 27, 0x7fffffb, 7, 0x4f }, + { 27, 0x7fffffb, 7, 0x4d }, { 27, 0x7fffffb, 7, 0x4b }, + { 27, 0x7fffffb, 7, 0x49 }, { 27, 0x7fffffb, 7, 0x47 }, + { 27, 0x7fffffb, 7, 0x45 }, { 27, 0x7fffffb, 7, 0x43 }, + { 27, 0x7fffffb, 7, 0x41 }, { 27, 0x7fffffb, 7, 0x3f }, + { 27, 0x7fffffb, 7, 0x3d }, { 27, 0x7fffffb, 7, 0x3b }, + { 27, 0x7fffffb, 7, 0x39 }, { 27, 0x7fffffb, 7, 0x37 }, + { 27, 0x7fffffb, 7, 0x35 }, { 27, 0x7fffffb, 7, 0x33 }, + { 27, 0x7fffffb, 7, 0x31 }, { 27, 0x7fffffb, 7, 0x2f }, + { 27, 0x7fffffb, 7, 0x2d }, { 27, 0x7fffffb, 7, 0x2b }, + { 27, 0x7fffffb, 7, 0x29 }, { 27, 0x7fffffb, 7, 0x27 }, + { 27, 0x7fffffb, 7, 0x25 }, { 27, 0x7fffffb, 7, 0x23 }, + { 27, 0x7fffffb, 7, 0x21 }, { 27, 0x7fffffb, 7, 0x1f }, + { 27, 0x7fffffb, 7, 0x1d }, { 27, 0x7fffffb, 7, 0x1b }, + { 27, 0x7fffffb, 7, 0x19 }, { 27, 0x7fffffb, 7, 0x17 }, + { 27, 0x7fffffb, 7, 0x15 }, { 27, 0x7fffffb, 7, 0x13 }, + { 27, 0x7fffffb, 7, 0x11 }, { 27, 0x7fffffb, 7, 0xf }, + { 27, 0x7fffffb, 7, 0xd }, { 27, 0x7fffffb, 7, 0xb }, + { 27, 0x7fffffb, 7, 0x9 }, { 27, 0x7fffffb, 7, 0x7 }, + { 27, 0x7fffffb, 7, 0x5 }, { 27, 0x7fffffb, 7, 0x3 }, + { 27, 0x7fffffb, 7, 0x1 }, { 27, 0x7fffffb, 7, 0x1 } + }, + + /* + * prefixed with 4 zeroes + */ + { + { 8, 0xf1, 0, 0 }, { 11, 0x7e3, 0, 0 }, + { 11, 0x7e1, 0, 0 }, { 18, 0x3ffc7, 0, 0 }, + { 18, 0x3ffc5, 0, 0 }, { 18, 0x3ffc3, 0, 0 }, + { 18, 0x3ffc1, 0, 0 }, { 22, 0x3fff8f, 0, 0 }, + { 22, 0x3fff8d, 0, 0 }, { 22, 0x3fff8b, 0, 0 }, + { 22, 0x3fff89, 0, 0 }, { 22, 0x3fff87, 0, 0 }, + { 22, 0x3fff85, 0, 0 }, { 22, 0x3fff83, 0, 0 }, + { 22, 0x3fff81, 0, 0 }, { 26, 0x3ffff3f, 0, 0 }, + { 26, 0x3ffff3d, 0, 0 }, { 26, 0x3ffff3b, 0, 0 }, + { 26, 0x3ffff39, 0, 0 }, { 26, 0x3ffff37, 0, 0 }, + { 26, 0x3ffff35, 0, 0 }, { 26, 0x3ffff33, 0, 0 }, + { 26, 0x3ffff31, 0, 0 }, { 26, 0x3ffff2f, 0, 0 }, + { 26, 0x3ffff2d, 0, 0 }, { 26, 0x3ffff2b, 0, 0 }, + { 26, 0x3ffff29, 0, 0 }, { 26, 0x3ffff27, 0, 0 }, + { 26, 0x3ffff25, 0, 0 }, { 26, 0x3ffff23, 0, 0 }, + { 26, 0x3ffff21, 0, 0 }, { 30, 0x3ffffebf, 0, 0 }, + { 30, 0x3ffffebd, 0, 0 }, { 30, 0x3ffffebb, 0, 0 }, + { 30, 0x3ffffeb9, 0, 0 }, { 30, 0x3ffffeb7, 0, 0 }, + { 30, 0x3ffffeb5, 0, 0 }, { 30, 0x3ffffeb3, 0, 0 }, + { 30, 0x3ffffeb1, 0, 0 }, { 30, 0x3ffffeaf, 0, 0 }, + { 30, 0x3ffffead, 0, 0 }, { 30, 0x3ffffeab, 0, 0 }, + { 30, 0x3ffffea9, 0, 0 }, { 30, 0x3ffffea7, 0, 0 }, + { 30, 0x3ffffea5, 0, 0 }, { 30, 0x3ffffea3, 0, 0 }, + { 30, 0x3ffffea1, 0, 0 }, { 30, 0x3ffffe9f, 0, 0 }, + { 30, 0x3ffffe9d, 0, 0 }, { 30, 0x3ffffe9b, 0, 0 }, + { 30, 0x3ffffe99, 0, 0 }, { 30, 0x3ffffe97, 0, 0 }, + { 30, 0x3ffffe95, 0, 0 }, { 30, 0x3ffffe93, 0, 0 }, + { 30, 0x3ffffe91, 0, 0 }, { 30, 0x3ffffe8f, 0, 0 }, + { 30, 0x3ffffe8d, 0, 0 }, { 30, 0x3ffffe8b, 0, 0 }, + { 30, 0x3ffffe89, 0, 0 }, { 30, 0x3ffffe87, 0, 0 }, + { 30, 0x3ffffe85, 0, 0 }, { 30, 0x3ffffe83, 0, 0 }, + { 30, 0x3ffffe81, 0, 0 }, { 28, 0xffffff8, 7, 0x7f }, + { 28, 0xffffff8, 7, 0x7d }, { 28, 0xffffff8, 7, 0x7b }, + { 28, 0xffffff8, 7, 0x79 }, { 28, 0xffffff8, 7, 0x77 }, + { 28, 0xffffff8, 7, 0x75 }, { 28, 0xffffff8, 7, 0x73 }, + { 28, 0xffffff8, 7, 0x71 }, { 28, 0xffffff8, 7, 0x6f }, + { 28, 0xffffff8, 7, 0x6d }, { 28, 0xffffff8, 7, 0x6b }, + { 28, 0xffffff8, 7, 0x69 }, { 28, 0xffffff8, 7, 0x67 }, + { 28, 0xffffff8, 7, 0x65 }, { 28, 0xffffff8, 7, 0x63 }, + { 28, 0xffffff8, 7, 0x61 }, { 28, 0xffffff8, 7, 0x5f }, + { 28, 0xffffff8, 7, 0x5d }, { 28, 0xffffff8, 7, 0x5b }, + { 28, 0xffffff8, 7, 0x59 }, { 28, 0xffffff8, 7, 0x57 }, + { 28, 0xffffff8, 7, 0x55 }, { 28, 0xffffff8, 7, 0x53 }, + { 28, 0xffffff8, 7, 0x51 }, { 28, 0xffffff8, 7, 0x4f }, + { 28, 0xffffff8, 7, 0x4d }, { 28, 0xffffff8, 7, 0x4b }, + { 28, 0xffffff8, 7, 0x49 }, { 28, 0xffffff8, 7, 0x47 }, + { 28, 0xffffff8, 7, 0x45 }, { 28, 0xffffff8, 7, 0x43 }, + { 28, 0xffffff8, 7, 0x41 }, { 28, 0xffffff8, 7, 0x3f }, + { 28, 0xffffff8, 7, 0x3d }, { 28, 0xffffff8, 7, 0x3b }, + { 28, 0xffffff8, 7, 0x39 }, { 28, 0xffffff8, 7, 0x37 }, + { 28, 0xffffff8, 7, 0x35 }, { 28, 0xffffff8, 7, 0x33 }, + { 28, 0xffffff8, 7, 0x31 }, { 28, 0xffffff8, 7, 0x2f }, + { 28, 0xffffff8, 7, 0x2d }, { 28, 0xffffff8, 7, 0x2b }, + { 28, 0xffffff8, 7, 0x29 }, { 28, 0xffffff8, 7, 0x27 }, + { 28, 0xffffff8, 7, 0x25 }, { 28, 0xffffff8, 7, 0x23 }, + { 28, 0xffffff8, 7, 0x21 }, { 28, 0xffffff8, 7, 0x1f }, + { 28, 0xffffff8, 7, 0x1d }, { 28, 0xffffff8, 7, 0x1b }, + { 28, 0xffffff8, 7, 0x19 }, { 28, 0xffffff8, 7, 0x17 }, + { 28, 0xffffff8, 7, 0x15 }, { 28, 0xffffff8, 7, 0x13 }, + { 28, 0xffffff8, 7, 0x11 }, { 28, 0xffffff8, 7, 0xf }, + { 28, 0xffffff8, 7, 0xd }, { 28, 0xffffff8, 7, 0xb }, + { 28, 0xffffff8, 7, 0x9 }, { 28, 0xffffff8, 7, 0x7 }, + { 28, 0xffffff8, 7, 0x5 }, { 28, 0xffffff8, 7, 0x3 }, + { 28, 0xffffff8, 7, 0x1 }, { 0, 0, 0, 0 } + }, + + /* + * prefixed with 5 zeroes + */ + { + { 8, 0xf3, 0, 0 }, { 11, 0x7e7, 0, 0 }, + { 11, 0x7e5, 0, 0 }, { 18, 0x3ffcf, 0, 0 }, + { 18, 0x3ffcd, 0, 0 }, { 18, 0x3ffcb, 0, 0 }, + { 18, 0x3ffc9, 0, 0 }, { 22, 0x3fff9f, 0, 0 }, + { 22, 0x3fff9d, 0, 0 }, { 22, 0x3fff9b, 0, 0 }, + { 22, 0x3fff99, 0, 0 }, { 22, 0x3fff97, 0, 0 }, + { 22, 0x3fff95, 0, 0 }, { 22, 0x3fff93, 0, 0 }, + { 22, 0x3fff91, 0, 0 }, { 26, 0x3ffff5f, 0, 0 }, + { 26, 0x3ffff5d, 0, 0 }, { 26, 0x3ffff5b, 0, 0 }, + { 26, 0x3ffff59, 0, 0 }, { 26, 0x3ffff57, 0, 0 }, + { 26, 0x3ffff55, 0, 0 }, { 26, 0x3ffff53, 0, 0 }, + { 26, 0x3ffff51, 0, 0 }, { 26, 0x3ffff4f, 0, 0 }, + { 26, 0x3ffff4d, 0, 0 }, { 26, 0x3ffff4b, 0, 0 }, + { 26, 0x3ffff49, 0, 0 }, { 26, 0x3ffff47, 0, 0 }, + { 26, 0x3ffff45, 0, 0 }, { 26, 0x3ffff43, 0, 0 }, + { 26, 0x3ffff41, 0, 0 }, { 30, 0x3ffffeff, 0, 0 }, + { 30, 0x3ffffefd, 0, 0 }, { 30, 0x3ffffefb, 0, 0 }, + { 30, 0x3ffffef9, 0, 0 }, { 30, 0x3ffffef7, 0, 0 }, + { 30, 0x3ffffef5, 0, 0 }, { 30, 0x3ffffef3, 0, 0 }, + { 30, 0x3ffffef1, 0, 0 }, { 30, 0x3ffffeef, 0, 0 }, + { 30, 0x3ffffeed, 0, 0 }, { 30, 0x3ffffeeb, 0, 0 }, + { 30, 0x3ffffee9, 0, 0 }, { 30, 0x3ffffee7, 0, 0 }, + { 30, 0x3ffffee5, 0, 0 }, { 30, 0x3ffffee3, 0, 0 }, + { 30, 0x3ffffee1, 0, 0 }, { 30, 0x3ffffedf, 0, 0 }, + { 30, 0x3ffffedd, 0, 0 }, { 30, 0x3ffffedb, 0, 0 }, + { 30, 0x3ffffed9, 0, 0 }, { 30, 0x3ffffed7, 0, 0 }, + { 30, 0x3ffffed5, 0, 0 }, { 30, 0x3ffffed3, 0, 0 }, + { 30, 0x3ffffed1, 0, 0 }, { 30, 0x3ffffecf, 0, 0 }, + { 30, 0x3ffffecd, 0, 0 }, { 30, 0x3ffffecb, 0, 0 }, + { 30, 0x3ffffec9, 0, 0 }, { 30, 0x3ffffec7, 0, 0 }, + { 30, 0x3ffffec5, 0, 0 }, { 30, 0x3ffffec3, 0, 0 }, + { 30, 0x3ffffec1, 0, 0 }, { 28, 0xffffff9, 7, 0x7f }, + { 28, 0xffffff9, 7, 0x7d }, { 28, 0xffffff9, 7, 0x7b }, + { 28, 0xffffff9, 7, 0x79 }, { 28, 0xffffff9, 7, 0x77 }, + { 28, 0xffffff9, 7, 0x75 }, { 28, 0xffffff9, 7, 0x73 }, + { 28, 0xffffff9, 7, 0x71 }, { 28, 0xffffff9, 7, 0x6f }, + { 28, 0xffffff9, 7, 0x6d }, { 28, 0xffffff9, 7, 0x6b }, + { 28, 0xffffff9, 7, 0x69 }, { 28, 0xffffff9, 7, 0x67 }, + { 28, 0xffffff9, 7, 0x65 }, { 28, 0xffffff9, 7, 0x63 }, + { 28, 0xffffff9, 7, 0x61 }, { 28, 0xffffff9, 7, 0x5f }, + { 28, 0xffffff9, 7, 0x5d }, { 28, 0xffffff9, 7, 0x5b }, + { 28, 0xffffff9, 7, 0x59 }, { 28, 0xffffff9, 7, 0x57 }, + { 28, 0xffffff9, 7, 0x55 }, { 28, 0xffffff9, 7, 0x53 }, + { 28, 0xffffff9, 7, 0x51 }, { 28, 0xffffff9, 7, 0x4f }, + { 28, 0xffffff9, 7, 0x4d }, { 28, 0xffffff9, 7, 0x4b }, + { 28, 0xffffff9, 7, 0x49 }, { 28, 0xffffff9, 7, 0x47 }, + { 28, 0xffffff9, 7, 0x45 }, { 28, 0xffffff9, 7, 0x43 }, + { 28, 0xffffff9, 7, 0x41 }, { 28, 0xffffff9, 7, 0x3f }, + { 28, 0xffffff9, 7, 0x3d }, { 28, 0xffffff9, 7, 0x3b }, + { 28, 0xffffff9, 7, 0x39 }, { 28, 0xffffff9, 7, 0x37 }, + { 28, 0xffffff9, 7, 0x35 }, { 28, 0xffffff9, 7, 0x33 }, + { 28, 0xffffff9, 7, 0x31 }, { 28, 0xffffff9, 7, 0x2f }, + { 28, 0xffffff9, 7, 0x2d }, { 28, 0xffffff9, 7, 0x2b }, + { 28, 0xffffff9, 7, 0x29 }, { 28, 0xffffff9, 7, 0x27 }, + { 28, 0xffffff9, 7, 0x25 }, { 28, 0xffffff9, 7, 0x23 }, + { 28, 0xffffff9, 7, 0x21 }, { 28, 0xffffff9, 7, 0x1f }, + { 28, 0xffffff9, 7, 0x1d }, { 28, 0xffffff9, 7, 0x1b }, + { 28, 0xffffff9, 7, 0x19 }, { 28, 0xffffff9, 7, 0x17 }, + { 28, 0xffffff9, 7, 0x15 }, { 28, 0xffffff9, 7, 0x13 }, + { 28, 0xffffff9, 7, 0x11 }, { 28, 0xffffff9, 7, 0xf }, + { 28, 0xffffff9, 7, 0xd }, { 28, 0xffffff9, 7, 0xb }, + { 28, 0xffffff9, 7, 0x9 }, { 28, 0xffffff9, 7, 0x7 }, + { 28, 0xffffff9, 7, 0x5 }, { 28, 0xffffff9, 7, 0x3 }, + { 28, 0xffffff9, 7, 0x1 }, { 0, 0, 0, 0 } + }, + + /* + * prefixed with 6 zeroes + */ + { + { 8, 0xf5, 0, 0 }, { 14, 0x3feb, 0, 0 }, + { 14, 0x3fe9, 0, 0 }, { 18, 0x3ffd7, 0, 0 }, + { 18, 0x3ffd5, 0, 0 }, { 18, 0x3ffd3, 0, 0 }, + { 18, 0x3ffd1, 0, 0 }, { 22, 0x3fffaf, 0, 0 }, + { 22, 0x3fffad, 0, 0 }, { 22, 0x3fffab, 0, 0 }, + { 22, 0x3fffa9, 0, 0 }, { 22, 0x3fffa7, 0, 0 }, + { 22, 0x3fffa5, 0, 0 }, { 22, 0x3fffa3, 0, 0 }, + { 22, 0x3fffa1, 0, 0 }, { 26, 0x3ffff7f, 0, 0 }, + { 26, 0x3ffff7d, 0, 0 }, { 26, 0x3ffff7b, 0, 0 }, + { 26, 0x3ffff79, 0, 0 }, { 26, 0x3ffff77, 0, 0 }, + { 26, 0x3ffff75, 0, 0 }, { 26, 0x3ffff73, 0, 0 }, + { 26, 0x3ffff71, 0, 0 }, { 26, 0x3ffff6f, 0, 0 }, + { 26, 0x3ffff6d, 0, 0 }, { 26, 0x3ffff6b, 0, 0 }, + { 26, 0x3ffff69, 0, 0 }, { 26, 0x3ffff67, 0, 0 }, + { 26, 0x3ffff65, 0, 0 }, { 26, 0x3ffff63, 0, 0 }, + { 26, 0x3ffff61, 0, 0 }, { 31, 0x7ffffe3f, 0, 0 }, + { 31, 0x7ffffe3d, 0, 0 }, { 31, 0x7ffffe3b, 0, 0 }, + { 31, 0x7ffffe39, 0, 0 }, { 31, 0x7ffffe37, 0, 0 }, + { 31, 0x7ffffe35, 0, 0 }, { 31, 0x7ffffe33, 0, 0 }, + { 31, 0x7ffffe31, 0, 0 }, { 31, 0x7ffffe2f, 0, 0 }, + { 31, 0x7ffffe2d, 0, 0 }, { 31, 0x7ffffe2b, 0, 0 }, + { 31, 0x7ffffe29, 0, 0 }, { 31, 0x7ffffe27, 0, 0 }, + { 31, 0x7ffffe25, 0, 0 }, { 31, 0x7ffffe23, 0, 0 }, + { 31, 0x7ffffe21, 0, 0 }, { 31, 0x7ffffe1f, 0, 0 }, + { 31, 0x7ffffe1d, 0, 0 }, { 31, 0x7ffffe1b, 0, 0 }, + { 31, 0x7ffffe19, 0, 0 }, { 31, 0x7ffffe17, 0, 0 }, + { 31, 0x7ffffe15, 0, 0 }, { 31, 0x7ffffe13, 0, 0 }, + { 31, 0x7ffffe11, 0, 0 }, { 31, 0x7ffffe0f, 0, 0 }, + { 31, 0x7ffffe0d, 0, 0 }, { 31, 0x7ffffe0b, 0, 0 }, + { 31, 0x7ffffe09, 0, 0 }, { 31, 0x7ffffe07, 0, 0 }, + { 31, 0x7ffffe05, 0, 0 }, { 31, 0x7ffffe03, 0, 0 }, + { 31, 0x7ffffe01, 0, 0 }, { 28, 0xffffffa, 7, 0x7f }, + { 28, 0xffffffa, 7, 0x7d }, { 28, 0xffffffa, 7, 0x7b }, + { 28, 0xffffffa, 7, 0x79 }, { 28, 0xffffffa, 7, 0x77 }, + { 28, 0xffffffa, 7, 0x75 }, { 28, 0xffffffa, 7, 0x73 }, + { 28, 0xffffffa, 7, 0x71 }, { 28, 0xffffffa, 7, 0x6f }, + { 28, 0xffffffa, 7, 0x6d }, { 28, 0xffffffa, 7, 0x6b }, + { 28, 0xffffffa, 7, 0x69 }, { 28, 0xffffffa, 7, 0x67 }, + { 28, 0xffffffa, 7, 0x65 }, { 28, 0xffffffa, 7, 0x63 }, + { 28, 0xffffffa, 7, 0x61 }, { 28, 0xffffffa, 7, 0x5f }, + { 28, 0xffffffa, 7, 0x5d }, { 28, 0xffffffa, 7, 0x5b }, + { 28, 0xffffffa, 7, 0x59 }, { 28, 0xffffffa, 7, 0x57 }, + { 28, 0xffffffa, 7, 0x55 }, { 28, 0xffffffa, 7, 0x53 }, + { 28, 0xffffffa, 7, 0x51 }, { 28, 0xffffffa, 7, 0x4f }, + { 28, 0xffffffa, 7, 0x4d }, { 28, 0xffffffa, 7, 0x4b }, + { 28, 0xffffffa, 7, 0x49 }, { 28, 0xffffffa, 7, 0x47 }, + { 28, 0xffffffa, 7, 0x45 }, { 28, 0xffffffa, 7, 0x43 }, + { 28, 0xffffffa, 7, 0x41 }, { 28, 0xffffffa, 7, 0x3f }, + { 28, 0xffffffa, 7, 0x3d }, { 28, 0xffffffa, 7, 0x3b }, + { 28, 0xffffffa, 7, 0x39 }, { 28, 0xffffffa, 7, 0x37 }, + { 28, 0xffffffa, 7, 0x35 }, { 28, 0xffffffa, 7, 0x33 }, + { 28, 0xffffffa, 7, 0x31 }, { 28, 0xffffffa, 7, 0x2f }, + { 28, 0xffffffa, 7, 0x2d }, { 28, 0xffffffa, 7, 0x2b }, + { 28, 0xffffffa, 7, 0x29 }, { 28, 0xffffffa, 7, 0x27 }, + { 28, 0xffffffa, 7, 0x25 }, { 28, 0xffffffa, 7, 0x23 }, + { 28, 0xffffffa, 7, 0x21 }, { 28, 0xffffffa, 7, 0x1f }, + { 28, 0xffffffa, 7, 0x1d }, { 28, 0xffffffa, 7, 0x1b }, + { 28, 0xffffffa, 7, 0x19 }, { 28, 0xffffffa, 7, 0x17 }, + { 28, 0xffffffa, 7, 0x15 }, { 28, 0xffffffa, 7, 0x13 }, + { 28, 0xffffffa, 7, 0x11 }, { 28, 0xffffffa, 7, 0xf }, + { 28, 0xffffffa, 7, 0xd }, { 28, 0xffffffa, 7, 0xb }, + { 28, 0xffffffa, 7, 0x9 }, { 28, 0xffffffa, 7, 0x7 }, + { 28, 0xffffffa, 7, 0x5 }, { 28, 0xffffffa, 7, 0x3 }, + { 28, 0xffffffa, 7, 0x1 }, { 0, 0, 0, 0 } + }, + + /* + * prefixed with 7 zeroes + */ + { + { 9, 0x1f3, 0, 0 }, { 14, 0x3fef, 0, 0 }, + { 14, 0x3fed, 0, 0 }, { 18, 0x3ffdf, 0, 0 }, + { 18, 0x3ffdd, 0, 0 }, { 18, 0x3ffdb, 0, 0 }, + { 18, 0x3ffd9, 0, 0 }, { 22, 0x3fffbf, 0, 0 }, + { 22, 0x3fffbd, 0, 0 }, { 22, 0x3fffbb, 0, 0 }, + { 22, 0x3fffb9, 0, 0 }, { 22, 0x3fffb7, 0, 0 }, + { 22, 0x3fffb5, 0, 0 }, { 22, 0x3fffb3, 0, 0 }, + { 22, 0x3fffb1, 0, 0 }, { 27, 0x7ffff1f, 0, 0 }, + { 27, 0x7ffff1d, 0, 0 }, { 27, 0x7ffff1b, 0, 0 }, + { 27, 0x7ffff19, 0, 0 }, { 27, 0x7ffff17, 0, 0 }, + { 27, 0x7ffff15, 0, 0 }, { 27, 0x7ffff13, 0, 0 }, + { 27, 0x7ffff11, 0, 0 }, { 27, 0x7ffff0f, 0, 0 }, + { 27, 0x7ffff0d, 0, 0 }, { 27, 0x7ffff0b, 0, 0 }, + { 27, 0x7ffff09, 0, 0 }, { 27, 0x7ffff07, 0, 0 }, + { 27, 0x7ffff05, 0, 0 }, { 27, 0x7ffff03, 0, 0 }, + { 27, 0x7ffff01, 0, 0 }, { 31, 0x7ffffe7f, 0, 0 }, + { 31, 0x7ffffe7d, 0, 0 }, { 31, 0x7ffffe7b, 0, 0 }, + { 31, 0x7ffffe79, 0, 0 }, { 31, 0x7ffffe77, 0, 0 }, + { 31, 0x7ffffe75, 0, 0 }, { 31, 0x7ffffe73, 0, 0 }, + { 31, 0x7ffffe71, 0, 0 }, { 31, 0x7ffffe6f, 0, 0 }, + { 31, 0x7ffffe6d, 0, 0 }, { 31, 0x7ffffe6b, 0, 0 }, + { 31, 0x7ffffe69, 0, 0 }, { 31, 0x7ffffe67, 0, 0 }, + { 31, 0x7ffffe65, 0, 0 }, { 31, 0x7ffffe63, 0, 0 }, + { 31, 0x7ffffe61, 0, 0 }, { 31, 0x7ffffe5f, 0, 0 }, + { 31, 0x7ffffe5d, 0, 0 }, { 31, 0x7ffffe5b, 0, 0 }, + { 31, 0x7ffffe59, 0, 0 }, { 31, 0x7ffffe57, 0, 0 }, + { 31, 0x7ffffe55, 0, 0 }, { 31, 0x7ffffe53, 0, 0 }, + { 31, 0x7ffffe51, 0, 0 }, { 31, 0x7ffffe4f, 0, 0 }, + { 31, 0x7ffffe4d, 0, 0 }, { 31, 0x7ffffe4b, 0, 0 }, + { 31, 0x7ffffe49, 0, 0 }, { 31, 0x7ffffe47, 0, 0 }, + { 31, 0x7ffffe45, 0, 0 }, { 31, 0x7ffffe43, 0, 0 }, + { 31, 0x7ffffe41, 0, 0 }, { 28, 0xffffffb, 7, 0x7f }, + { 28, 0xffffffb, 7, 0x7d }, { 28, 0xffffffb, 7, 0x7b }, + { 28, 0xffffffb, 7, 0x79 }, { 28, 0xffffffb, 7, 0x77 }, + { 28, 0xffffffb, 7, 0x75 }, { 28, 0xffffffb, 7, 0x73 }, + { 28, 0xffffffb, 7, 0x71 }, { 28, 0xffffffb, 7, 0x6f }, + { 28, 0xffffffb, 7, 0x6d }, { 28, 0xffffffb, 7, 0x6b }, + { 28, 0xffffffb, 7, 0x69 }, { 28, 0xffffffb, 7, 0x67 }, + { 28, 0xffffffb, 7, 0x65 }, { 28, 0xffffffb, 7, 0x63 }, + { 28, 0xffffffb, 7, 0x61 }, { 28, 0xffffffb, 7, 0x5f }, + { 28, 0xffffffb, 7, 0x5d }, { 28, 0xffffffb, 7, 0x5b }, + { 28, 0xffffffb, 7, 0x59 }, { 28, 0xffffffb, 7, 0x57 }, + { 28, 0xffffffb, 7, 0x55 }, { 28, 0xffffffb, 7, 0x53 }, + { 28, 0xffffffb, 7, 0x51 }, { 28, 0xffffffb, 7, 0x4f }, + { 28, 0xffffffb, 7, 0x4d }, { 28, 0xffffffb, 7, 0x4b }, + { 28, 0xffffffb, 7, 0x49 }, { 28, 0xffffffb, 7, 0x47 }, + { 28, 0xffffffb, 7, 0x45 }, { 28, 0xffffffb, 7, 0x43 }, + { 28, 0xffffffb, 7, 0x41 }, { 28, 0xffffffb, 7, 0x3f }, + { 28, 0xffffffb, 7, 0x3d }, { 28, 0xffffffb, 7, 0x3b }, + { 28, 0xffffffb, 7, 0x39 }, { 28, 0xffffffb, 7, 0x37 }, + { 28, 0xffffffb, 7, 0x35 }, { 28, 0xffffffb, 7, 0x33 }, + { 28, 0xffffffb, 7, 0x31 }, { 28, 0xffffffb, 7, 0x2f }, + { 28, 0xffffffb, 7, 0x2d }, { 28, 0xffffffb, 7, 0x2b }, + { 28, 0xffffffb, 7, 0x29 }, { 28, 0xffffffb, 7, 0x27 }, + { 28, 0xffffffb, 7, 0x25 }, { 28, 0xffffffb, 7, 0x23 }, + { 28, 0xffffffb, 7, 0x21 }, { 28, 0xffffffb, 7, 0x1f }, + { 28, 0xffffffb, 7, 0x1d }, { 28, 0xffffffb, 7, 0x1b }, + { 28, 0xffffffb, 7, 0x19 }, { 28, 0xffffffb, 7, 0x17 }, + { 28, 0xffffffb, 7, 0x15 }, { 28, 0xffffffb, 7, 0x13 }, + { 28, 0xffffffb, 7, 0x11 }, { 28, 0xffffffb, 7, 0xf }, + { 28, 0xffffffb, 7, 0xd }, { 28, 0xffffffb, 7, 0xb }, + { 28, 0xffffffb, 7, 0x9 }, { 28, 0xffffffb, 7, 0x7 }, + { 28, 0xffffffb, 7, 0x5 }, { 28, 0xffffffb, 7, 0x3 }, + { 28, 0xffffffb, 7, 0x1 }, { 0, 0, 0, 0 } + }, + + /* + * prefixed with 8 zeroes + */ + { + { 9, 0x1f5, 0, 0 }, { 15, 0x7fe3, 0, 0 }, + { 15, 0x7fe1, 0, 0 }, { 19, 0x7ffc7, 0, 0 }, + { 19, 0x7ffc5, 0, 0 }, { 19, 0x7ffc3, 0, 0 }, + { 19, 0x7ffc1, 0, 0 }, { 23, 0x7fff8f, 0, 0 }, + { 23, 0x7fff8d, 0, 0 }, { 23, 0x7fff8b, 0, 0 }, + { 23, 0x7fff89, 0, 0 }, { 23, 0x7fff87, 0, 0 }, + { 23, 0x7fff85, 0, 0 }, { 23, 0x7fff83, 0, 0 }, + { 23, 0x7fff81, 0, 0 }, { 27, 0x7ffff3f, 0, 0 }, + { 27, 0x7ffff3d, 0, 0 }, { 27, 0x7ffff3b, 0, 0 }, + { 27, 0x7ffff39, 0, 0 }, { 27, 0x7ffff37, 0, 0 }, + { 27, 0x7ffff35, 0, 0 }, { 27, 0x7ffff33, 0, 0 }, + { 27, 0x7ffff31, 0, 0 }, { 27, 0x7ffff2f, 0, 0 }, + { 27, 0x7ffff2d, 0, 0 }, { 27, 0x7ffff2b, 0, 0 }, + { 27, 0x7ffff29, 0, 0 }, { 27, 0x7ffff27, 0, 0 }, + { 27, 0x7ffff25, 0, 0 }, { 27, 0x7ffff23, 0, 0 }, + { 27, 0x7ffff21, 0, 0 }, { 31, 0x7ffffebf, 0, 0 }, + { 31, 0x7ffffebd, 0, 0 }, { 31, 0x7ffffebb, 0, 0 }, + { 31, 0x7ffffeb9, 0, 0 }, { 31, 0x7ffffeb7, 0, 0 }, + { 31, 0x7ffffeb5, 0, 0 }, { 31, 0x7ffffeb3, 0, 0 }, + { 31, 0x7ffffeb1, 0, 0 }, { 31, 0x7ffffeaf, 0, 0 }, + { 31, 0x7ffffead, 0, 0 }, { 31, 0x7ffffeab, 0, 0 }, + { 31, 0x7ffffea9, 0, 0 }, { 31, 0x7ffffea7, 0, 0 }, + { 31, 0x7ffffea5, 0, 0 }, { 31, 0x7ffffea3, 0, 0 }, + { 31, 0x7ffffea1, 0, 0 }, { 31, 0x7ffffe9f, 0, 0 }, + { 31, 0x7ffffe9d, 0, 0 }, { 31, 0x7ffffe9b, 0, 0 }, + { 31, 0x7ffffe99, 0, 0 }, { 31, 0x7ffffe97, 0, 0 }, + { 31, 0x7ffffe95, 0, 0 }, { 31, 0x7ffffe93, 0, 0 }, + { 31, 0x7ffffe91, 0, 0 }, { 31, 0x7ffffe8f, 0, 0 }, + { 31, 0x7ffffe8d, 0, 0 }, { 31, 0x7ffffe8b, 0, 0 }, + { 31, 0x7ffffe89, 0, 0 }, { 31, 0x7ffffe87, 0, 0 }, + { 31, 0x7ffffe85, 0, 0 }, { 31, 0x7ffffe83, 0, 0 }, + { 31, 0x7ffffe81, 0, 0 }, { 29, 0x1ffffff8, 7, 0x7f }, + { 29, 0x1ffffff8, 7, 0x7d }, { 29, 0x1ffffff8, 7, 0x7b }, + { 29, 0x1ffffff8, 7, 0x79 }, { 29, 0x1ffffff8, 7, 0x77 }, + { 29, 0x1ffffff8, 7, 0x75 }, { 29, 0x1ffffff8, 7, 0x73 }, + { 29, 0x1ffffff8, 7, 0x71 }, { 29, 0x1ffffff8, 7, 0x6f }, + { 29, 0x1ffffff8, 7, 0x6d }, { 29, 0x1ffffff8, 7, 0x6b }, + { 29, 0x1ffffff8, 7, 0x69 }, { 29, 0x1ffffff8, 7, 0x67 }, + { 29, 0x1ffffff8, 7, 0x65 }, { 29, 0x1ffffff8, 7, 0x63 }, + { 29, 0x1ffffff8, 7, 0x61 }, { 29, 0x1ffffff8, 7, 0x5f }, + { 29, 0x1ffffff8, 7, 0x5d }, { 29, 0x1ffffff8, 7, 0x5b }, + { 29, 0x1ffffff8, 7, 0x59 }, { 29, 0x1ffffff8, 7, 0x57 }, + { 29, 0x1ffffff8, 7, 0x55 }, { 29, 0x1ffffff8, 7, 0x53 }, + { 29, 0x1ffffff8, 7, 0x51 }, { 29, 0x1ffffff8, 7, 0x4f }, + { 29, 0x1ffffff8, 7, 0x4d }, { 29, 0x1ffffff8, 7, 0x4b }, + { 29, 0x1ffffff8, 7, 0x49 }, { 29, 0x1ffffff8, 7, 0x47 }, + { 29, 0x1ffffff8, 7, 0x45 }, { 29, 0x1ffffff8, 7, 0x43 }, + { 29, 0x1ffffff8, 7, 0x41 }, { 29, 0x1ffffff8, 7, 0x3f }, + { 29, 0x1ffffff8, 7, 0x3d }, { 29, 0x1ffffff8, 7, 0x3b }, + { 29, 0x1ffffff8, 7, 0x39 }, { 29, 0x1ffffff8, 7, 0x37 }, + { 29, 0x1ffffff8, 7, 0x35 }, { 29, 0x1ffffff8, 7, 0x33 }, + { 29, 0x1ffffff8, 7, 0x31 }, { 29, 0x1ffffff8, 7, 0x2f }, + { 29, 0x1ffffff8, 7, 0x2d }, { 29, 0x1ffffff8, 7, 0x2b }, + { 29, 0x1ffffff8, 7, 0x29 }, { 29, 0x1ffffff8, 7, 0x27 }, + { 29, 0x1ffffff8, 7, 0x25 }, { 29, 0x1ffffff8, 7, 0x23 }, + { 29, 0x1ffffff8, 7, 0x21 }, { 29, 0x1ffffff8, 7, 0x1f }, + { 29, 0x1ffffff8, 7, 0x1d }, { 29, 0x1ffffff8, 7, 0x1b }, + { 29, 0x1ffffff8, 7, 0x19 }, { 29, 0x1ffffff8, 7, 0x17 }, + { 29, 0x1ffffff8, 7, 0x15 }, { 29, 0x1ffffff8, 7, 0x13 }, + { 29, 0x1ffffff8, 7, 0x11 }, { 29, 0x1ffffff8, 7, 0xf }, + { 29, 0x1ffffff8, 7, 0xd }, { 29, 0x1ffffff8, 7, 0xb }, + { 29, 0x1ffffff8, 7, 0x9 }, { 29, 0x1ffffff8, 7, 0x7 }, + { 29, 0x1ffffff8, 7, 0x5 }, { 29, 0x1ffffff8, 7, 0x3 }, + { 29, 0x1ffffff8, 7, 0x1 }, { 0, 0, 0, 0 } + }, + + /* + * prefixed with 9 zeroes + */ + { + { 11, 0x7f7, 0, 0 }, { 15, 0x7fe7, 0, 0 }, + { 15, 0x7fe5, 0, 0 }, { 19, 0x7ffcf, 0, 0 }, + { 19, 0x7ffcd, 0, 0 }, { 19, 0x7ffcb, 0, 0 }, + { 19, 0x7ffc9, 0, 0 }, { 23, 0x7fff9f, 0, 0 }, + { 23, 0x7fff9d, 0, 0 }, { 23, 0x7fff9b, 0, 0 }, + { 23, 0x7fff99, 0, 0 }, { 23, 0x7fff97, 0, 0 }, + { 23, 0x7fff95, 0, 0 }, { 23, 0x7fff93, 0, 0 }, + { 23, 0x7fff91, 0, 0 }, { 27, 0x7ffff5f, 0, 0 }, + { 27, 0x7ffff5d, 0, 0 }, { 27, 0x7ffff5b, 0, 0 }, + { 27, 0x7ffff59, 0, 0 }, { 27, 0x7ffff57, 0, 0 }, + { 27, 0x7ffff55, 0, 0 }, { 27, 0x7ffff53, 0, 0 }, + { 27, 0x7ffff51, 0, 0 }, { 27, 0x7ffff4f, 0, 0 }, + { 27, 0x7ffff4d, 0, 0 }, { 27, 0x7ffff4b, 0, 0 }, + { 27, 0x7ffff49, 0, 0 }, { 27, 0x7ffff47, 0, 0 }, + { 27, 0x7ffff45, 0, 0 }, { 27, 0x7ffff43, 0, 0 }, + { 27, 0x7ffff41, 0, 0 }, { 31, 0x7ffffeff, 0, 0 }, + { 31, 0x7ffffefd, 0, 0 }, { 31, 0x7ffffefb, 0, 0 }, + { 31, 0x7ffffef9, 0, 0 }, { 31, 0x7ffffef7, 0, 0 }, + { 31, 0x7ffffef5, 0, 0 }, { 31, 0x7ffffef3, 0, 0 }, + { 31, 0x7ffffef1, 0, 0 }, { 31, 0x7ffffeef, 0, 0 }, + { 31, 0x7ffffeed, 0, 0 }, { 31, 0x7ffffeeb, 0, 0 }, + { 31, 0x7ffffee9, 0, 0 }, { 31, 0x7ffffee7, 0, 0 }, + { 31, 0x7ffffee5, 0, 0 }, { 31, 0x7ffffee3, 0, 0 }, + { 31, 0x7ffffee1, 0, 0 }, { 31, 0x7ffffedf, 0, 0 }, + { 31, 0x7ffffedd, 0, 0 }, { 31, 0x7ffffedb, 0, 0 }, + { 31, 0x7ffffed9, 0, 0 }, { 31, 0x7ffffed7, 0, 0 }, + { 31, 0x7ffffed5, 0, 0 }, { 31, 0x7ffffed3, 0, 0 }, + { 31, 0x7ffffed1, 0, 0 }, { 31, 0x7ffffecf, 0, 0 }, + { 31, 0x7ffffecd, 0, 0 }, { 31, 0x7ffffecb, 0, 0 }, + { 31, 0x7ffffec9, 0, 0 }, { 31, 0x7ffffec7, 0, 0 }, + { 31, 0x7ffffec5, 0, 0 }, { 31, 0x7ffffec3, 0, 0 }, + { 31, 0x7ffffec1, 0, 0 }, { 29, 0x1ffffff9, 7, 0x7f }, + { 29, 0x1ffffff9, 7, 0x7d }, { 29, 0x1ffffff9, 7, 0x7b }, + { 29, 0x1ffffff9, 7, 0x79 }, { 29, 0x1ffffff9, 7, 0x77 }, + { 29, 0x1ffffff9, 7, 0x75 }, { 29, 0x1ffffff9, 7, 0x73 }, + { 29, 0x1ffffff9, 7, 0x71 }, { 29, 0x1ffffff9, 7, 0x6f }, + { 29, 0x1ffffff9, 7, 0x6d }, { 29, 0x1ffffff9, 7, 0x6b }, + { 29, 0x1ffffff9, 7, 0x69 }, { 29, 0x1ffffff9, 7, 0x67 }, + { 29, 0x1ffffff9, 7, 0x65 }, { 29, 0x1ffffff9, 7, 0x63 }, + { 29, 0x1ffffff9, 7, 0x61 }, { 29, 0x1ffffff9, 7, 0x5f }, + { 29, 0x1ffffff9, 7, 0x5d }, { 29, 0x1ffffff9, 7, 0x5b }, + { 29, 0x1ffffff9, 7, 0x59 }, { 29, 0x1ffffff9, 7, 0x57 }, + { 29, 0x1ffffff9, 7, 0x55 }, { 29, 0x1ffffff9, 7, 0x53 }, + { 29, 0x1ffffff9, 7, 0x51 }, { 29, 0x1ffffff9, 7, 0x4f }, + { 29, 0x1ffffff9, 7, 0x4d }, { 29, 0x1ffffff9, 7, 0x4b }, + { 29, 0x1ffffff9, 7, 0x49 }, { 29, 0x1ffffff9, 7, 0x47 }, + { 29, 0x1ffffff9, 7, 0x45 }, { 29, 0x1ffffff9, 7, 0x43 }, + { 29, 0x1ffffff9, 7, 0x41 }, { 29, 0x1ffffff9, 7, 0x3f }, + { 29, 0x1ffffff9, 7, 0x3d }, { 29, 0x1ffffff9, 7, 0x3b }, + { 29, 0x1ffffff9, 7, 0x39 }, { 29, 0x1ffffff9, 7, 0x37 }, + { 29, 0x1ffffff9, 7, 0x35 }, { 29, 0x1ffffff9, 7, 0x33 }, + { 29, 0x1ffffff9, 7, 0x31 }, { 29, 0x1ffffff9, 7, 0x2f }, + { 29, 0x1ffffff9, 7, 0x2d }, { 29, 0x1ffffff9, 7, 0x2b }, + { 29, 0x1ffffff9, 7, 0x29 }, { 29, 0x1ffffff9, 7, 0x27 }, + { 29, 0x1ffffff9, 7, 0x25 }, { 29, 0x1ffffff9, 7, 0x23 }, + { 29, 0x1ffffff9, 7, 0x21 }, { 29, 0x1ffffff9, 7, 0x1f }, + { 29, 0x1ffffff9, 7, 0x1d }, { 29, 0x1ffffff9, 7, 0x1b }, + { 29, 0x1ffffff9, 7, 0x19 }, { 29, 0x1ffffff9, 7, 0x17 }, + { 29, 0x1ffffff9, 7, 0x15 }, { 29, 0x1ffffff9, 7, 0x13 }, + { 29, 0x1ffffff9, 7, 0x11 }, { 29, 0x1ffffff9, 7, 0xf }, + { 29, 0x1ffffff9, 7, 0xd }, { 29, 0x1ffffff9, 7, 0xb }, + { 29, 0x1ffffff9, 7, 0x9 }, { 29, 0x1ffffff9, 7, 0x7 }, + { 29, 0x1ffffff9, 7, 0x5 }, { 29, 0x1ffffff9, 7, 0x3 }, + { 29, 0x1ffffff9, 7, 0x1 }, { 0, 0, 0, 0 } + }, + + /* + * prefixed with 10 zeroes + */ + { + { 12, 0xff1, 0, 0 }, { 15, 0x7feb, 0, 0 }, + { 15, 0x7fe9, 0, 0 }, { 19, 0x7ffd7, 0, 0 }, + { 19, 0x7ffd5, 0, 0 }, { 19, 0x7ffd3, 0, 0 }, + { 19, 0x7ffd1, 0, 0 }, { 23, 0x7fffaf, 0, 0 }, + { 23, 0x7fffad, 0, 0 }, { 23, 0x7fffab, 0, 0 }, + { 23, 0x7fffa9, 0, 0 }, { 23, 0x7fffa7, 0, 0 }, + { 23, 0x7fffa5, 0, 0 }, { 23, 0x7fffa3, 0, 0 }, + { 23, 0x7fffa1, 0, 0 }, { 27, 0x7ffff7f, 0, 0 }, + { 27, 0x7ffff7d, 0, 0 }, { 27, 0x7ffff7b, 0, 0 }, + { 27, 0x7ffff79, 0, 0 }, { 27, 0x7ffff77, 0, 0 }, + { 27, 0x7ffff75, 0, 0 }, { 27, 0x7ffff73, 0, 0 }, + { 27, 0x7ffff71, 0, 0 }, { 27, 0x7ffff6f, 0, 0 }, + { 27, 0x7ffff6d, 0, 0 }, { 27, 0x7ffff6b, 0, 0 }, + { 27, 0x7ffff69, 0, 0 }, { 27, 0x7ffff67, 0, 0 }, + { 27, 0x7ffff65, 0, 0 }, { 27, 0x7ffff63, 0, 0 }, + { 27, 0x7ffff61, 0, 0 }, { 32, 0xfffffe3f, 0, 0 }, + { 32, 0xfffffe3d, 0, 0 }, { 32, 0xfffffe3b, 0, 0 }, + { 32, 0xfffffe39, 0, 0 }, { 32, 0xfffffe37, 0, 0 }, + { 32, 0xfffffe35, 0, 0 }, { 32, 0xfffffe33, 0, 0 }, + { 32, 0xfffffe31, 0, 0 }, { 32, 0xfffffe2f, 0, 0 }, + { 32, 0xfffffe2d, 0, 0 }, { 32, 0xfffffe2b, 0, 0 }, + { 32, 0xfffffe29, 0, 0 }, { 32, 0xfffffe27, 0, 0 }, + { 32, 0xfffffe25, 0, 0 }, { 32, 0xfffffe23, 0, 0 }, + { 32, 0xfffffe21, 0, 0 }, { 32, 0xfffffe1f, 0, 0 }, + { 32, 0xfffffe1d, 0, 0 }, { 32, 0xfffffe1b, 0, 0 }, + { 32, 0xfffffe19, 0, 0 }, { 32, 0xfffffe17, 0, 0 }, + { 32, 0xfffffe15, 0, 0 }, { 32, 0xfffffe13, 0, 0 }, + { 32, 0xfffffe11, 0, 0 }, { 32, 0xfffffe0f, 0, 0 }, + { 32, 0xfffffe0d, 0, 0 }, { 32, 0xfffffe0b, 0, 0 }, + { 32, 0xfffffe09, 0, 0 }, { 32, 0xfffffe07, 0, 0 }, + { 32, 0xfffffe05, 0, 0 }, { 32, 0xfffffe03, 0, 0 }, + { 32, 0xfffffe01, 0, 0 }, { 29, 0x1ffffffa, 7, 0x7f }, + { 29, 0x1ffffffa, 7, 0x7d }, { 29, 0x1ffffffa, 7, 0x7b }, + { 29, 0x1ffffffa, 7, 0x79 }, { 29, 0x1ffffffa, 7, 0x77 }, + { 29, 0x1ffffffa, 7, 0x75 }, { 29, 0x1ffffffa, 7, 0x73 }, + { 29, 0x1ffffffa, 7, 0x71 }, { 29, 0x1ffffffa, 7, 0x6f }, + { 29, 0x1ffffffa, 7, 0x6d }, { 29, 0x1ffffffa, 7, 0x6b }, + { 29, 0x1ffffffa, 7, 0x69 }, { 29, 0x1ffffffa, 7, 0x67 }, + { 29, 0x1ffffffa, 7, 0x65 }, { 29, 0x1ffffffa, 7, 0x63 }, + { 29, 0x1ffffffa, 7, 0x61 }, { 29, 0x1ffffffa, 7, 0x5f }, + { 29, 0x1ffffffa, 7, 0x5d }, { 29, 0x1ffffffa, 7, 0x5b }, + { 29, 0x1ffffffa, 7, 0x59 }, { 29, 0x1ffffffa, 7, 0x57 }, + { 29, 0x1ffffffa, 7, 0x55 }, { 29, 0x1ffffffa, 7, 0x53 }, + { 29, 0x1ffffffa, 7, 0x51 }, { 29, 0x1ffffffa, 7, 0x4f }, + { 29, 0x1ffffffa, 7, 0x4d }, { 29, 0x1ffffffa, 7, 0x4b }, + { 29, 0x1ffffffa, 7, 0x49 }, { 29, 0x1ffffffa, 7, 0x47 }, + { 29, 0x1ffffffa, 7, 0x45 }, { 29, 0x1ffffffa, 7, 0x43 }, + { 29, 0x1ffffffa, 7, 0x41 }, { 29, 0x1ffffffa, 7, 0x3f }, + { 29, 0x1ffffffa, 7, 0x3d }, { 29, 0x1ffffffa, 7, 0x3b }, + { 29, 0x1ffffffa, 7, 0x39 }, { 29, 0x1ffffffa, 7, 0x37 }, + { 29, 0x1ffffffa, 7, 0x35 }, { 29, 0x1ffffffa, 7, 0x33 }, + { 29, 0x1ffffffa, 7, 0x31 }, { 29, 0x1ffffffa, 7, 0x2f }, + { 29, 0x1ffffffa, 7, 0x2d }, { 29, 0x1ffffffa, 7, 0x2b }, + { 29, 0x1ffffffa, 7, 0x29 }, { 29, 0x1ffffffa, 7, 0x27 }, + { 29, 0x1ffffffa, 7, 0x25 }, { 29, 0x1ffffffa, 7, 0x23 }, + { 29, 0x1ffffffa, 7, 0x21 }, { 29, 0x1ffffffa, 7, 0x1f }, + { 29, 0x1ffffffa, 7, 0x1d }, { 29, 0x1ffffffa, 7, 0x1b }, + { 29, 0x1ffffffa, 7, 0x19 }, { 29, 0x1ffffffa, 7, 0x17 }, + { 29, 0x1ffffffa, 7, 0x15 }, { 29, 0x1ffffffa, 7, 0x13 }, + { 29, 0x1ffffffa, 7, 0x11 }, { 29, 0x1ffffffa, 7, 0xf }, + { 29, 0x1ffffffa, 7, 0xd }, { 29, 0x1ffffffa, 7, 0xb }, + { 29, 0x1ffffffa, 7, 0x9 }, { 29, 0x1ffffffa, 7, 0x7 }, + { 29, 0x1ffffffa, 7, 0x5 }, { 29, 0x1ffffffa, 7, 0x3 }, + { 29, 0x1ffffffa, 7, 0x1 }, { 0, 0, 0, 0 } + }, + + /* + * prefixed with 11 zeroes + */ + { + { 12, 0xff3, 0, 0 }, { 15, 0x7fef, 0, 0 }, + { 15, 0x7fed, 0, 0 }, { 19, 0x7ffdf, 0, 0 }, + { 19, 0x7ffdd, 0, 0 }, { 19, 0x7ffdb, 0, 0 }, + { 19, 0x7ffd9, 0, 0 }, { 23, 0x7fffbf, 0, 0 }, + { 23, 0x7fffbd, 0, 0 }, { 23, 0x7fffbb, 0, 0 }, + { 23, 0x7fffb9, 0, 0 }, { 23, 0x7fffb7, 0, 0 }, + { 23, 0x7fffb5, 0, 0 }, { 23, 0x7fffb3, 0, 0 }, + { 23, 0x7fffb1, 0, 0 }, { 28, 0xfffff1f, 0, 0 }, + { 28, 0xfffff1d, 0, 0 }, { 28, 0xfffff1b, 0, 0 }, + { 28, 0xfffff19, 0, 0 }, { 28, 0xfffff17, 0, 0 }, + { 28, 0xfffff15, 0, 0 }, { 28, 0xfffff13, 0, 0 }, + { 28, 0xfffff11, 0, 0 }, { 28, 0xfffff0f, 0, 0 }, + { 28, 0xfffff0d, 0, 0 }, { 28, 0xfffff0b, 0, 0 }, + { 28, 0xfffff09, 0, 0 }, { 28, 0xfffff07, 0, 0 }, + { 28, 0xfffff05, 0, 0 }, { 28, 0xfffff03, 0, 0 }, + { 28, 0xfffff01, 0, 0 }, { 32, 0xfffffe7f, 0, 0 }, + { 32, 0xfffffe7d, 0, 0 }, { 32, 0xfffffe7b, 0, 0 }, + { 32, 0xfffffe79, 0, 0 }, { 32, 0xfffffe77, 0, 0 }, + { 32, 0xfffffe75, 0, 0 }, { 32, 0xfffffe73, 0, 0 }, + { 32, 0xfffffe71, 0, 0 }, { 32, 0xfffffe6f, 0, 0 }, + { 32, 0xfffffe6d, 0, 0 }, { 32, 0xfffffe6b, 0, 0 }, + { 32, 0xfffffe69, 0, 0 }, { 32, 0xfffffe67, 0, 0 }, + { 32, 0xfffffe65, 0, 0 }, { 32, 0xfffffe63, 0, 0 }, + { 32, 0xfffffe61, 0, 0 }, { 32, 0xfffffe5f, 0, 0 }, + { 32, 0xfffffe5d, 0, 0 }, { 32, 0xfffffe5b, 0, 0 }, + { 32, 0xfffffe59, 0, 0 }, { 32, 0xfffffe57, 0, 0 }, + { 32, 0xfffffe55, 0, 0 }, { 32, 0xfffffe53, 0, 0 }, + { 32, 0xfffffe51, 0, 0 }, { 32, 0xfffffe4f, 0, 0 }, + { 32, 0xfffffe4d, 0, 0 }, { 32, 0xfffffe4b, 0, 0 }, + { 32, 0xfffffe49, 0, 0 }, { 32, 0xfffffe47, 0, 0 }, + { 32, 0xfffffe45, 0, 0 }, { 32, 0xfffffe43, 0, 0 }, + { 32, 0xfffffe41, 0, 0 }, { 29, 0x1ffffffb, 7, 0x7f }, + { 29, 0x1ffffffb, 7, 0x7d }, { 29, 0x1ffffffb, 7, 0x7b }, + { 29, 0x1ffffffb, 7, 0x79 }, { 29, 0x1ffffffb, 7, 0x77 }, + { 29, 0x1ffffffb, 7, 0x75 }, { 29, 0x1ffffffb, 7, 0x73 }, + { 29, 0x1ffffffb, 7, 0x71 }, { 29, 0x1ffffffb, 7, 0x6f }, + { 29, 0x1ffffffb, 7, 0x6d }, { 29, 0x1ffffffb, 7, 0x6b }, + { 29, 0x1ffffffb, 7, 0x69 }, { 29, 0x1ffffffb, 7, 0x67 }, + { 29, 0x1ffffffb, 7, 0x65 }, { 29, 0x1ffffffb, 7, 0x63 }, + { 29, 0x1ffffffb, 7, 0x61 }, { 29, 0x1ffffffb, 7, 0x5f }, + { 29, 0x1ffffffb, 7, 0x5d }, { 29, 0x1ffffffb, 7, 0x5b }, + { 29, 0x1ffffffb, 7, 0x59 }, { 29, 0x1ffffffb, 7, 0x57 }, + { 29, 0x1ffffffb, 7, 0x55 }, { 29, 0x1ffffffb, 7, 0x53 }, + { 29, 0x1ffffffb, 7, 0x51 }, { 29, 0x1ffffffb, 7, 0x4f }, + { 29, 0x1ffffffb, 7, 0x4d }, { 29, 0x1ffffffb, 7, 0x4b }, + { 29, 0x1ffffffb, 7, 0x49 }, { 29, 0x1ffffffb, 7, 0x47 }, + { 29, 0x1ffffffb, 7, 0x45 }, { 29, 0x1ffffffb, 7, 0x43 }, + { 29, 0x1ffffffb, 7, 0x41 }, { 29, 0x1ffffffb, 7, 0x3f }, + { 29, 0x1ffffffb, 7, 0x3d }, { 29, 0x1ffffffb, 7, 0x3b }, + { 29, 0x1ffffffb, 7, 0x39 }, { 29, 0x1ffffffb, 7, 0x37 }, + { 29, 0x1ffffffb, 7, 0x35 }, { 29, 0x1ffffffb, 7, 0x33 }, + { 29, 0x1ffffffb, 7, 0x31 }, { 29, 0x1ffffffb, 7, 0x2f }, + { 29, 0x1ffffffb, 7, 0x2d }, { 29, 0x1ffffffb, 7, 0x2b }, + { 29, 0x1ffffffb, 7, 0x29 }, { 29, 0x1ffffffb, 7, 0x27 }, + { 29, 0x1ffffffb, 7, 0x25 }, { 29, 0x1ffffffb, 7, 0x23 }, + { 29, 0x1ffffffb, 7, 0x21 }, { 29, 0x1ffffffb, 7, 0x1f }, + { 29, 0x1ffffffb, 7, 0x1d }, { 29, 0x1ffffffb, 7, 0x1b }, + { 29, 0x1ffffffb, 7, 0x19 }, { 29, 0x1ffffffb, 7, 0x17 }, + { 29, 0x1ffffffb, 7, 0x15 }, { 29, 0x1ffffffb, 7, 0x13 }, + { 29, 0x1ffffffb, 7, 0x11 }, { 29, 0x1ffffffb, 7, 0xf }, + { 29, 0x1ffffffb, 7, 0xd }, { 29, 0x1ffffffb, 7, 0xb }, + { 29, 0x1ffffffb, 7, 0x9 }, { 29, 0x1ffffffb, 7, 0x7 }, + { 29, 0x1ffffffb, 7, 0x5 }, { 29, 0x1ffffffb, 7, 0x3 }, + { 29, 0x1ffffffb, 7, 0x1 }, { 0, 0, 0, 0 } + }, + + /* + * prefixed with 12 zeroes + */ + { + { 12, 0xff5, 0, 0 }, { 16, 0xffe3, 0, 0 }, + { 16, 0xffe1, 0, 0 }, { 20, 0xfffc7, 0, 0 }, + { 20, 0xfffc5, 0, 0 }, { 20, 0xfffc3, 0, 0 }, + { 20, 0xfffc1, 0, 0 }, { 24, 0xffff8f, 0, 0 }, + { 24, 0xffff8d, 0, 0 }, { 24, 0xffff8b, 0, 0 }, + { 24, 0xffff89, 0, 0 }, { 24, 0xffff87, 0, 0 }, + { 24, 0xffff85, 0, 0 }, { 24, 0xffff83, 0, 0 }, + { 24, 0xffff81, 0, 0 }, { 28, 0xfffff3f, 0, 0 }, + { 28, 0xfffff3d, 0, 0 }, { 28, 0xfffff3b, 0, 0 }, + { 28, 0xfffff39, 0, 0 }, { 28, 0xfffff37, 0, 0 }, + { 28, 0xfffff35, 0, 0 }, { 28, 0xfffff33, 0, 0 }, + { 28, 0xfffff31, 0, 0 }, { 28, 0xfffff2f, 0, 0 }, + { 28, 0xfffff2d, 0, 0 }, { 28, 0xfffff2b, 0, 0 }, + { 28, 0xfffff29, 0, 0 }, { 28, 0xfffff27, 0, 0 }, + { 28, 0xfffff25, 0, 0 }, { 28, 0xfffff23, 0, 0 }, + { 28, 0xfffff21, 0, 0 }, { 32, 0xfffffebf, 0, 0 }, + { 32, 0xfffffebd, 0, 0 }, { 32, 0xfffffebb, 0, 0 }, + { 32, 0xfffffeb9, 0, 0 }, { 32, 0xfffffeb7, 0, 0 }, + { 32, 0xfffffeb5, 0, 0 }, { 32, 0xfffffeb3, 0, 0 }, + { 32, 0xfffffeb1, 0, 0 }, { 32, 0xfffffeaf, 0, 0 }, + { 32, 0xfffffead, 0, 0 }, { 32, 0xfffffeab, 0, 0 }, + { 32, 0xfffffea9, 0, 0 }, { 32, 0xfffffea7, 0, 0 }, + { 32, 0xfffffea5, 0, 0 }, { 32, 0xfffffea3, 0, 0 }, + { 32, 0xfffffea1, 0, 0 }, { 32, 0xfffffe9f, 0, 0 }, + { 32, 0xfffffe9d, 0, 0 }, { 32, 0xfffffe9b, 0, 0 }, + { 32, 0xfffffe99, 0, 0 }, { 32, 0xfffffe97, 0, 0 }, + { 32, 0xfffffe95, 0, 0 }, { 32, 0xfffffe93, 0, 0 }, + { 32, 0xfffffe91, 0, 0 }, { 32, 0xfffffe8f, 0, 0 }, + { 32, 0xfffffe8d, 0, 0 }, { 32, 0xfffffe8b, 0, 0 }, + { 32, 0xfffffe89, 0, 0 }, { 32, 0xfffffe87, 0, 0 }, + { 32, 0xfffffe85, 0, 0 }, { 32, 0xfffffe83, 0, 0 }, + { 32, 0xfffffe81, 0, 0 }, { 30, 0x1fff7400, 7, 0x7f }, + { 30, 0x1fff7400, 7, 0x7d }, { 30, 0x1fff7400, 7, 0x7b }, + { 30, 0x1fff7400, 7, 0x79 }, { 30, 0x1fff7400, 7, 0x77 }, + { 30, 0x1fff7400, 7, 0x75 }, { 30, 0x1fff7400, 7, 0x73 }, + { 30, 0x1fff7400, 7, 0x71 }, { 30, 0x1fff7400, 7, 0x6f }, + { 30, 0x1fff7400, 7, 0x6d }, { 30, 0x1fff7400, 7, 0x6b }, + { 30, 0x1fff7400, 7, 0x69 }, { 30, 0x1fff7400, 7, 0x67 }, + { 30, 0x1fff7400, 7, 0x65 }, { 30, 0x1fff7400, 7, 0x63 }, + { 30, 0x1fff7400, 7, 0x61 }, { 30, 0x1fff7400, 7, 0x5f }, + { 30, 0x1fff7400, 7, 0x5d }, { 30, 0x1fff7400, 7, 0x5b }, + { 30, 0x1fff7400, 7, 0x59 }, { 30, 0x1fff7400, 7, 0x57 }, + { 30, 0x1fff7400, 7, 0x55 }, { 30, 0x1fff7400, 7, 0x53 }, + { 30, 0x1fff7400, 7, 0x51 }, { 30, 0x1fff7400, 7, 0x4f }, + { 30, 0x1fff7400, 7, 0x4d }, { 30, 0x1fff7400, 7, 0x4b }, + { 30, 0x1fff7400, 7, 0x49 }, { 30, 0x1fff7400, 7, 0x47 }, + { 30, 0x1fff7400, 7, 0x45 }, { 30, 0x1fff7400, 7, 0x43 }, + { 30, 0x1fff7400, 7, 0x41 }, { 30, 0x1fff7400, 7, 0x3f }, + { 30, 0x1fff7400, 7, 0x3d }, { 30, 0x1fff7400, 7, 0x3b }, + { 30, 0x1fff7400, 7, 0x39 }, { 30, 0x1fff7400, 7, 0x37 }, + { 30, 0x1fff7400, 7, 0x35 }, { 30, 0x1fff7400, 7, 0x33 }, + { 30, 0x1fff7400, 7, 0x31 }, { 30, 0x1fff7400, 7, 0x2f }, + { 30, 0x1fff7400, 7, 0x2d }, { 30, 0x1fff7400, 7, 0x2b }, + { 30, 0x1fff7400, 7, 0x29 }, { 30, 0x1fff7400, 7, 0x27 }, + { 30, 0x1fff7400, 7, 0x25 }, { 30, 0x1fff7400, 7, 0x23 }, + { 30, 0x1fff7400, 7, 0x21 }, { 30, 0x1fff7400, 7, 0x1f }, + { 30, 0x1fff7400, 7, 0x1d }, { 30, 0x1fff7400, 7, 0x1b }, + { 30, 0x1fff7400, 7, 0x19 }, { 30, 0x1fff7400, 7, 0x17 }, + { 30, 0x1fff7400, 7, 0x15 }, { 30, 0x1fff7400, 7, 0x13 }, + { 30, 0x1fff7400, 7, 0x11 }, { 30, 0x1fff7400, 7, 0xf }, + { 30, 0x1fff7400, 7, 0xd }, { 30, 0x1fff7400, 7, 0xb }, + { 30, 0x1fff7400, 7, 0x9 }, { 30, 0x1fff7400, 7, 0x7 }, + { 30, 0x1fff7400, 7, 0x5 }, { 30, 0x1fff7400, 7, 0x3 }, + { 30, 0x1fff7400, 7, 0x1 }, { 0, 0, 0, 0 } + }, + + /* + * prefixed with 13 zeroes + */ + { + { 12, 0xff7, 0, 0 }, { 16, 0xffe7, 0, 0 }, + { 16, 0xffe5, 0, 0 }, { 20, 0xfffcf, 0, 0 }, + { 20, 0xfffcd, 0, 0 }, { 20, 0xfffcb, 0, 0 }, + { 20, 0xfffc9, 0, 0 }, { 24, 0xffff9f, 0, 0 }, + { 24, 0xffff9d, 0, 0 }, { 24, 0xffff9b, 0, 0 }, + { 24, 0xffff99, 0, 0 }, { 24, 0xffff97, 0, 0 }, + { 24, 0xffff95, 0, 0 }, { 24, 0xffff93, 0, 0 }, + { 24, 0xffff91, 0, 0 }, { 28, 0xfffff5f, 0, 0 }, + { 28, 0xfffff5d, 0, 0 }, { 28, 0xfffff5b, 0, 0 }, + { 28, 0xfffff59, 0, 0 }, { 28, 0xfffff57, 0, 0 }, + { 28, 0xfffff55, 0, 0 }, { 28, 0xfffff53, 0, 0 }, + { 28, 0xfffff51, 0, 0 }, { 28, 0xfffff4f, 0, 0 }, + { 28, 0xfffff4d, 0, 0 }, { 28, 0xfffff4b, 0, 0 }, + { 28, 0xfffff49, 0, 0 }, { 28, 0xfffff47, 0, 0 }, + { 28, 0xfffff45, 0, 0 }, { 28, 0xfffff43, 0, 0 }, + { 28, 0xfffff41, 0, 0 }, { 32, 0xfffffeff, 0, 0 }, + { 32, 0xfffffefd, 0, 0 }, { 32, 0xfffffefb, 0, 0 }, + { 32, 0xfffffef9, 0, 0 }, { 32, 0xfffffef7, 0, 0 }, + { 32, 0xfffffef5, 0, 0 }, { 32, 0xfffffef3, 0, 0 }, + { 32, 0xfffffef1, 0, 0 }, { 32, 0xfffffeef, 0, 0 }, + { 32, 0xfffffeed, 0, 0 }, { 32, 0xfffffeeb, 0, 0 }, + { 32, 0xfffffee9, 0, 0 }, { 32, 0xfffffee7, 0, 0 }, + { 32, 0xfffffee5, 0, 0 }, { 32, 0xfffffee3, 0, 0 }, + { 32, 0xfffffee1, 0, 0 }, { 32, 0xfffffedf, 0, 0 }, + { 32, 0xfffffedd, 0, 0 }, { 32, 0xfffffedb, 0, 0 }, + { 32, 0xfffffed9, 0, 0 }, { 32, 0xfffffed7, 0, 0 }, + { 32, 0xfffffed5, 0, 0 }, { 32, 0xfffffed3, 0, 0 }, + { 32, 0xfffffed1, 0, 0 }, { 32, 0xfffffecf, 0, 0 }, + { 32, 0xfffffecd, 0, 0 }, { 32, 0xfffffecb, 0, 0 }, + { 32, 0xfffffec9, 0, 0 }, { 32, 0xfffffec7, 0, 0 }, + { 32, 0xfffffec5, 0, 0 }, { 32, 0xfffffec3, 0, 0 }, + { 32, 0xfffffec1, 0, 0 }, { 30, 0x3ffffff9, 7, 0x7f }, + { 30, 0x3ffffff9, 7, 0x7d }, { 30, 0x3ffffff9, 7, 0x7b }, + { 30, 0x3ffffff9, 7, 0x79 }, { 30, 0x3ffffff9, 7, 0x77 }, + { 30, 0x3ffffff9, 7, 0x75 }, { 30, 0x3ffffff9, 7, 0x73 }, + { 30, 0x3ffffff9, 7, 0x71 }, { 30, 0x3ffffff9, 7, 0x6f }, + { 30, 0x3ffffff9, 7, 0x6d }, { 30, 0x3ffffff9, 7, 0x6b }, + { 30, 0x3ffffff9, 7, 0x69 }, { 30, 0x3ffffff9, 7, 0x67 }, + { 30, 0x3ffffff9, 7, 0x65 }, { 30, 0x3ffffff9, 7, 0x63 }, + { 30, 0x3ffffff9, 7, 0x61 }, { 30, 0x3ffffff9, 7, 0x5f }, + { 30, 0x3ffffff9, 7, 0x5d }, { 30, 0x3ffffff9, 7, 0x5b }, + { 30, 0x3ffffff9, 7, 0x59 }, { 30, 0x3ffffff9, 7, 0x57 }, + { 30, 0x3ffffff9, 7, 0x55 }, { 30, 0x3ffffff9, 7, 0x53 }, + { 30, 0x3ffffff9, 7, 0x51 }, { 30, 0x3ffffff9, 7, 0x4f }, + { 30, 0x3ffffff9, 7, 0x4d }, { 30, 0x3ffffff9, 7, 0x4b }, + { 30, 0x3ffffff9, 7, 0x49 }, { 30, 0x3ffffff9, 7, 0x47 }, + { 30, 0x3ffffff9, 7, 0x45 }, { 30, 0x3ffffff9, 7, 0x43 }, + { 30, 0x3ffffff9, 7, 0x41 }, { 30, 0x3ffffff9, 7, 0x3f }, + { 30, 0x3ffffff9, 7, 0x3d }, { 30, 0x3ffffff9, 7, 0x3b }, + { 30, 0x3ffffff9, 7, 0x39 }, { 30, 0x3ffffff9, 7, 0x37 }, + { 30, 0x3ffffff9, 7, 0x35 }, { 30, 0x3ffffff9, 7, 0x33 }, + { 30, 0x3ffffff9, 7, 0x31 }, { 30, 0x3ffffff9, 7, 0x2f }, + { 30, 0x3ffffff9, 7, 0x2d }, { 30, 0x3ffffff9, 7, 0x2b }, + { 30, 0x3ffffff9, 7, 0x29 }, { 30, 0x3ffffff9, 7, 0x27 }, + { 30, 0x3ffffff9, 7, 0x25 }, { 30, 0x3ffffff9, 7, 0x23 }, + { 30, 0x3ffffff9, 7, 0x21 }, { 30, 0x3ffffff9, 7, 0x1f }, + { 30, 0x3ffffff9, 7, 0x1d }, { 30, 0x3ffffff9, 7, 0x1b }, + { 30, 0x3ffffff9, 7, 0x19 }, { 30, 0x3ffffff9, 7, 0x17 }, + { 30, 0x3ffffff9, 7, 0x15 }, { 30, 0x3ffffff9, 7, 0x13 }, + { 30, 0x3ffffff9, 7, 0x11 }, { 30, 0x3ffffff9, 7, 0xf }, + { 30, 0x3ffffff9, 7, 0xd }, { 30, 0x3ffffff9, 7, 0xb }, + { 30, 0x3ffffff9, 7, 0x9 }, { 30, 0x3ffffff9, 7, 0x7 }, + { 30, 0x3ffffff9, 7, 0x5 }, { 30, 0x3ffffff9, 7, 0x3 }, + { 30, 0x3ffffff9, 7, 0x1 }, { 0, 0, 0, 0 } + }, + + /* + * prefixed with 14 zeroes + */ + { + { 13, 0x1ff1, 0, 0 }, { 16, 0xffeb, 0, 0 }, + { 16, 0xffe9, 0, 0 }, { 20, 0xfffd7, 0, 0 }, + { 20, 0xfffd5, 0, 0 }, { 20, 0xfffd3, 0, 0 }, + { 20, 0xfffd1, 0, 0 }, { 24, 0xffffaf, 0, 0 }, + { 24, 0xffffad, 0, 0 }, { 24, 0xffffab, 0, 0 }, + { 24, 0xffffa9, 0, 0 }, { 24, 0xffffa7, 0, 0 }, + { 24, 0xffffa5, 0, 0 }, { 24, 0xffffa3, 0, 0 }, + { 24, 0xffffa1, 0, 0 }, { 28, 0xfffff7f, 0, 0 }, + { 28, 0xfffff7d, 0, 0 }, { 28, 0xfffff7b, 0, 0 }, + { 28, 0xfffff79, 0, 0 }, { 28, 0xfffff77, 0, 0 }, + { 28, 0xfffff75, 0, 0 }, { 28, 0xfffff73, 0, 0 }, + { 28, 0xfffff71, 0, 0 }, { 28, 0xfffff6f, 0, 0 }, + { 28, 0xfffff6d, 0, 0 }, { 28, 0xfffff6b, 0, 0 }, + { 28, 0xfffff69, 0, 0 }, { 28, 0xfffff67, 0, 0 }, + { 28, 0xfffff65, 0, 0 }, { 28, 0xfffff63, 0, 0 }, + { 28, 0xfffff61, 0, 0 }, { 27, 0x7fffff8, 6, 0x3f }, + { 27, 0x7fffff8, 6, 0x3d }, { 27, 0x7fffff8, 6, 0x3b }, + { 27, 0x7fffff8, 6, 0x39 }, { 27, 0x7fffff8, 6, 0x37 }, + { 27, 0x7fffff8, 6, 0x35 }, { 27, 0x7fffff8, 6, 0x33 }, + { 27, 0x7fffff8, 6, 0x31 }, { 27, 0x7fffff8, 6, 0x2f }, + { 27, 0x7fffff8, 6, 0x2d }, { 27, 0x7fffff8, 6, 0x2b }, + { 27, 0x7fffff8, 6, 0x29 }, { 27, 0x7fffff8, 6, 0x27 }, + { 27, 0x7fffff8, 6, 0x25 }, { 27, 0x7fffff8, 6, 0x23 }, + { 27, 0x7fffff8, 6, 0x21 }, { 27, 0x7fffff8, 6, 0x1f }, + { 27, 0x7fffff8, 6, 0x1d }, { 27, 0x7fffff8, 6, 0x1b }, + { 27, 0x7fffff8, 6, 0x19 }, { 27, 0x7fffff8, 6, 0x17 }, + { 27, 0x7fffff8, 6, 0x15 }, { 27, 0x7fffff8, 6, 0x13 }, + { 27, 0x7fffff8, 6, 0x11 }, { 27, 0x7fffff8, 6, 0xf }, + { 27, 0x7fffff8, 6, 0xd }, { 27, 0x7fffff8, 6, 0xb }, + { 27, 0x7fffff8, 6, 0x9 }, { 27, 0x7fffff8, 6, 0x7 }, + { 27, 0x7fffff8, 6, 0x5 }, { 27, 0x7fffff8, 6, 0x3 }, + { 27, 0x7fffff8, 6, 0x1 }, { 30, 0x3ffffffa, 7, 0x7f }, + { 30, 0x3ffffffa, 7, 0x7d }, { 30, 0x3ffffffa, 7, 0x7b }, + { 30, 0x3ffffffa, 7, 0x79 }, { 30, 0x3ffffffa, 7, 0x77 }, + { 30, 0x3ffffffa, 7, 0x75 }, { 30, 0x3ffffffa, 7, 0x73 }, + { 30, 0x3ffffffa, 7, 0x71 }, { 30, 0x3ffffffa, 7, 0x6f }, + { 30, 0x3ffffffa, 7, 0x6d }, { 30, 0x3ffffffa, 7, 0x6b }, + { 30, 0x3ffffffa, 7, 0x69 }, { 30, 0x3ffffffa, 7, 0x67 }, + { 30, 0x3ffffffa, 7, 0x65 }, { 30, 0x3ffffffa, 7, 0x63 }, + { 30, 0x3ffffffa, 7, 0x61 }, { 30, 0x3ffffffa, 7, 0x5f }, + { 30, 0x3ffffffa, 7, 0x5d }, { 30, 0x3ffffffa, 7, 0x5b }, + { 30, 0x3ffffffa, 7, 0x59 }, { 30, 0x3ffffffa, 7, 0x57 }, + { 30, 0x3ffffffa, 7, 0x55 }, { 30, 0x3ffffffa, 7, 0x53 }, + { 30, 0x3ffffffa, 7, 0x51 }, { 30, 0x3ffffffa, 7, 0x4f }, + { 30, 0x3ffffffa, 7, 0x4d }, { 30, 0x3ffffffa, 7, 0x4b }, + { 30, 0x3ffffffa, 7, 0x49 }, { 30, 0x3ffffffa, 7, 0x47 }, + { 30, 0x3ffffffa, 7, 0x45 }, { 30, 0x3ffffffa, 7, 0x43 }, + { 30, 0x3ffffffa, 7, 0x41 }, { 30, 0x3ffffffa, 7, 0x3f }, + { 30, 0x3ffffffa, 7, 0x3d }, { 30, 0x3ffffffa, 7, 0x3b }, + { 30, 0x3ffffffa, 7, 0x39 }, { 30, 0x3ffffffa, 7, 0x37 }, + { 30, 0x3ffffffa, 7, 0x35 }, { 30, 0x3ffffffa, 7, 0x33 }, + { 30, 0x3ffffffa, 7, 0x31 }, { 30, 0x3ffffffa, 7, 0x2f }, + { 30, 0x3ffffffa, 7, 0x2d }, { 30, 0x3ffffffa, 7, 0x2b }, + { 30, 0x3ffffffa, 7, 0x29 }, { 30, 0x3ffffffa, 7, 0x27 }, + { 30, 0x3ffffffa, 7, 0x25 }, { 30, 0x3ffffffa, 7, 0x23 }, + { 30, 0x3ffffffa, 7, 0x21 }, { 30, 0x3ffffffa, 7, 0x1f }, + { 30, 0x3ffffffa, 7, 0x1d }, { 30, 0x3ffffffa, 7, 0x1b }, + { 30, 0x3ffffffa, 7, 0x19 }, { 30, 0x3ffffffa, 7, 0x17 }, + { 30, 0x3ffffffa, 7, 0x15 }, { 30, 0x3ffffffa, 7, 0x13 }, + { 30, 0x3ffffffa, 7, 0x11 }, { 30, 0x3ffffffa, 7, 0xf }, + { 30, 0x3ffffffa, 7, 0xd }, { 30, 0x3ffffffa, 7, 0xb }, + { 30, 0x3ffffffa, 7, 0x9 }, { 30, 0x3ffffffa, 7, 0x7 }, + { 30, 0x3ffffffa, 7, 0x5 }, { 30, 0x3ffffffa, 7, 0x3 }, + { 30, 0x3ffffffa, 7, 0x1 }, { 0, 0, 0, 0 } + }, + + /* + * prefixed with 15 zeroes + */ + { + { 13, 0x1ff3, 0, 0 }, { 2, 0x3, 0, 0 }, + { 2, 0x1, 0, 0 }, { 3, 0x7, 0, 0 }, + { 3, 0x5, 0, 0 }, { 3, 0x3, 0, 0 }, + { 3, 0x1, 0, 0 }, { 31, 0x7ffffffb, 4, 0xf }, + { 31, 0x7ffffffb, 4, 0xd }, { 31, 0x7ffffffb, 4, 0xb }, + { 31, 0x7ffffffb, 4, 0x9 }, { 31, 0x7ffffffb, 4, 0x7 }, + { 31, 0x7ffffffb, 4, 0x5 }, { 31, 0x7ffffffb, 4, 0x3 }, + { 31, 0x7ffffffb, 4, 0x1 }, { 5, 0x1f, 0, 0 }, + { 5, 0x1d, 0, 0 }, { 5, 0x1b, 0, 0 }, + { 5, 0x19, 0, 0 }, { 5, 0x17, 0, 0 }, + { 5, 0x15, 0, 0 }, { 5, 0x13, 0, 0 }, + { 5, 0x11, 0, 0 }, { 5, 0xf, 0, 0 }, + { 5, 0xd, 0, 0 }, { 5, 0xb, 0, 0 }, + { 5, 0x9, 0, 0 }, { 5, 0x7, 0, 0 }, + { 5, 0x5, 0, 0 }, { 5, 0x3, 0, 0 }, + { 5, 0x1, 0, 0 }, { 6, 0x3f, 0, 0 }, + { 6, 0x3d, 0, 0 }, { 6, 0x3b, 0, 0 }, + { 6, 0x39, 0, 0 }, { 6, 0x37, 0, 0 }, + { 6, 0x35, 0, 0 }, { 6, 0x33, 0, 0 }, + { 6, 0x31, 0, 0 }, { 6, 0x2f, 0, 0 }, + { 6, 0x2d, 0, 0 }, { 6, 0x2b, 0, 0 }, + { 6, 0x29, 0, 0 }, { 6, 0x27, 0, 0 }, + { 6, 0x25, 0, 0 }, { 6, 0x23, 0, 0 }, + { 6, 0x21, 0, 0 }, { 6, 0x1f, 0, 0 }, + { 6, 0x1d, 0, 0 }, { 6, 0x1b, 0, 0 }, + { 6, 0x19, 0, 0 }, { 6, 0x17, 0, 0 }, + { 6, 0x15, 0, 0 }, { 6, 0x13, 0, 0 }, + { 6, 0x11, 0, 0 }, { 6, 0xf, 0, 0 }, + { 6, 0xd, 0, 0 }, { 6, 0xb, 0, 0 }, + { 6, 0x9, 0, 0 }, { 6, 0x7, 0, 0 }, + { 6, 0x5, 0, 0 }, { 6, 0x3, 0, 0 }, + { 6, 0x1, 0, 0 }, { 7, 0x7f, 0, 0 }, + { 7, 0x7d, 0, 0 }, { 7, 0x7b, 0, 0 }, + { 7, 0x79, 0, 0 }, { 7, 0x77, 0, 0 }, + { 7, 0x75, 0, 0 }, { 7, 0x73, 0, 0 }, + { 7, 0x71, 0, 0 }, { 7, 0x6f, 0, 0 }, + { 7, 0x6d, 0, 0 }, { 7, 0x6b, 0, 0 }, + { 7, 0x69, 0, 0 }, { 7, 0x67, 0, 0 }, + { 7, 0x65, 0, 0 }, { 7, 0x63, 0, 0 }, + { 7, 0x61, 0, 0 }, { 7, 0x5f, 0, 0 }, + { 7, 0x5d, 0, 0 }, { 7, 0x5b, 0, 0 }, + { 7, 0x59, 0, 0 }, { 7, 0x57, 0, 0 }, + { 7, 0x55, 0, 0 }, { 7, 0x53, 0, 0 }, + { 7, 0x51, 0, 0 }, { 7, 0x4f, 0, 0 }, + { 7, 0x4d, 0, 0 }, { 7, 0x4b, 0, 0 }, + { 7, 0x49, 0, 0 }, { 7, 0x47, 0, 0 }, + { 7, 0x45, 0, 0 }, { 7, 0x43, 0, 0 }, + { 7, 0x41, 0, 0 }, { 7, 0x3f, 0, 0 }, + { 7, 0x3d, 0, 0 }, { 7, 0x3b, 0, 0 }, + { 7, 0x39, 0, 0 }, { 7, 0x37, 0, 0 }, + { 7, 0x35, 0, 0 }, { 7, 0x33, 0, 0 }, + { 7, 0x31, 0, 0 }, { 7, 0x2f, 0, 0 }, + { 7, 0x2d, 0, 0 }, { 7, 0x2b, 0, 0 }, + { 7, 0x29, 0, 0 }, { 7, 0x27, 0, 0 }, + { 7, 0x25, 0, 0 }, { 7, 0x23, 0, 0 }, + { 7, 0x21, 0, 0 }, { 7, 0x1f, 0, 0 }, + { 7, 0x1d, 0, 0 }, { 7, 0x1b, 0, 0 }, + { 7, 0x19, 0, 0 }, { 7, 0x17, 0, 0 }, + { 7, 0x15, 0, 0 }, { 7, 0x13, 0, 0 }, + { 7, 0x11, 0, 0 }, { 7, 0xf, 0, 0 }, + { 7, 0xd, 0, 0 }, { 7, 0xb, 0, 0 }, + { 7, 0x9, 0, 0 }, { 7, 0x7, 0, 0 }, + { 7, 0x5, 0, 0 }, { 7, 0x3, 0, 0 }, + { 7, 0x1, 0, 0 }, { 0, 0, 0, 0 } + } +}; + +VlcMagic _magic_values[] = { + { 0x0, 0, 1 }, + { 0x1, 0, 2 }, + { 0x4, 0, 3 }, + { 0xB, 1, 1 }, + { 0xC, 0, 4 }, + { 0x1A, 0, 5 }, + { 0x1B, 2, 1 }, + { 0x38, 3, 1 }, + { 0x39, 1, 2 }, + { 0x3A, 1, 3 }, + { 0x3B, 0, 6 }, + { 0x78, 4, 1 }, + { 0x79, 5, 1 }, + { 0x7A, 6, 1 }, + { 0x7B, 2, 2 }, + { 0xF8, 1, 4 }, + { 0xF9, 7, 1 }, + { 0xFA, 8, 1 }, + { 0xFB, 3, 2 }, + { 0x1F8, 4, 2 }, + { 0x1F9, 5, 2 }, + { 0x1FA, 2, 3 }, + { 0x1FB, 2, 4 }, + { 0x3F8, 1, 5 }, + { 0x3F9, 1, 6 }, + { 0x3FA, 0, 7 }, + { 0x3FB, 9, 1 }, + { 0x7F8, 10, 1 }, + { 0x7F9, 11, 1 }, + { 0x7FA, 12, 1 }, + { 0x7FB, 13, 1 }, + { 0xFF8, 14, 1 }, + { 0xFF9, 15, 1 }, + { 0xFFA, 6, 2 }, + { 0xFFB, 7, 2 }, + { 0x1FF8, 8, 2 }, + { 0x1FF9, 9, 2 }, + { 0x1FFA, 10, 2 }, + { 0x1FFB, 11, 2 }, + { 0x3FF8, 12, 2 }, + { 0x3FF9, 13, 2 }, + { 0x3FFA, 14, 2 }, + { 0x3FFB, 3, 3 }, + { 0x7FF8, 4, 3 }, + { 0x7FF9, 5, 3 }, + { 0x7FFA, 6, 3 }, + { 0x7FFB, 7, 3 }, + { 0xFFF8, 8, 3 }, + { 0xFFF9, 9, 3 }, + { 0xFFFA, 10, 3 }, + { 0xFFFB, 11, 3 }, + { 0x1FFF8, 12, 3 }, + { 0x1FFF9, 13, 3 }, + { 0x1FFFA, 14, 3 }, + { 0x1FFFB, 3, 4 }, + { 0x3FFF8, 4, 4 }, + { 0x3FFF9, 5, 4 }, + { 0x3FFFA, 6, 4 }, + { 0x3FFFB, 7, 4 }, + { 0x7FFF8, 8, 4 }, + { 0x7FFF9, 9, 4 }, + { 0x7FFFA, 10, 4 }, + { 0x7FFFB, 11, 4 }, + { 0xFFFF8, 12, 4 }, + { 0xFFFF9, 13, 4 }, + { 0xFFFFA, 14, 4 }, + { 0xFFFFB, 2, 5 }, + { 0x1FFFF8, 3, 5 }, + { 0x1FFFF9, 4, 5 }, + { 0x1FFFFA, 5, 5 }, + { 0x1FFFFB, 6, 5 }, + { 0x3FFFF8, 7, 5 }, + { 0x3FFFF9, 8, 5 }, + { 0x3FFFFA, 9, 5 }, + { 0x3FFFFB, 10, 5 }, + { 0x7FFFF8, 11, 5 }, + { 0x7FFFF9, 12, 5 }, + { 0x7FFFFA, 13, 5 }, + { 0x7FFFFB, 14, 5 }, + { 0xFFFFF8, 2, 6 }, + { 0xFFFFF9, 3, 6 }, + { 0xFFFFFA, 4, 6 }, + { 0xFFFFFB, 5, 6 }, + { 0x1FFFFF8, 6, 6 }, + { 0x1FFFFF9, 7, 6 }, + { 0x1FFFFFA, 8, 6 }, + { 0x1FFFFFB, 9, 6 }, + { 0x3FFFFF8, 10, 6 }, + { 0x3FFFFF9, 11, 6 }, + { 0x3FFFFFA, 12, 6 }, + { 0x3FFFFFB, 13, 6 }, + { 0x7FFFFF8, 14, 6 }, + { 0x7FFFFF9, 1, 7 }, + { 0x7FFFFFA, 2, 7 }, + { 0x7FFFFFB, 3, 7 }, + { 0xFFFFFF8, 4, 7 }, + { 0xFFFFFF9, 5, 7 }, + { 0xFFFFFFA, 6, 7 }, + { 0xFFFFFFB, 7, 7 }, + { 0x1FFFFFF8, 8, 7 }, + { 0x1FFFFFF9, 9, 7 }, + { 0x1FFFFFFA, 10, 7 }, + { 0x1FFFFFFB, 11, 7 }, + { 0x3FFFFFF8, 12, 7 }, + { 0x3FFFFFF9, 13, 7 }, + { 0x3FFFFFFA, 14, 7 } +}; + +/* + * _find_magic + * + * Internal helper-function used to locate a given + * VlcMagic entry. + */ +VlcMagic *_find_magic(guint magic) +{ + gint low = 0; + gint high = sizeof(_magic_values) / sizeof(VlcMagic) - 1; + gint mid; + + while (low <= high) { + mid = (low + high) / 2; + + if (_magic_values[mid].magic < magic) + low = mid + 1; + else if (_magic_values[mid].magic > magic) + high = mid - 1; + else + return &_magic_values[mid]; + } + + return NULL; +} + +/* + * _initialize_vlcdec_lookup + * + * Internal helper-function used to initialize + * the lookup-table used by the VLC-decoder. + */ +void _initialize_vlcdec_lookup(gint8 *lookup_tbl) +{ + gint8 util_buf[3072]; + gint v1_start, v1_end, v1_dec, util_buf_offset; + gint util_buf_offset_inc, buf1_val, samples_offset; + gint v1, v2; + gint8 *p, *p1, *p2, *p3; + + util_buf[0] = 0; + util_buf[1] = 0; + util_buf[2] = 0; + util_buf[3] = 1; + util_buf[4] = 1; + util_buf[5] = 1; + util_buf[765] = 1; + util_buf[766] = 0; + util_buf[767] = 1; + lookup_tbl[255] = 255; + lookup_tbl[256] = 1; + + v1_start = -3; + v1_dec = 4; + + util_buf_offset = 11; + util_buf_offset_inc = 12; + buf1_val = 2; + + samples_offset = 509; + + do { + v1 = v1_start; + v1_end = -(abs(v1_start) + 1) / 2; + v2 = 0; + + p2 = util_buf + util_buf_offset - 3; + + do { + p1 = util_buf + ((v1 & 0xff) * 3); + p1[0] = buf1_val; + p1[1] = v2; + p1[2] = buf1_val; + + p2[1] = buf1_val; + p2[2] = v2 + 1; + p2[3] = buf1_val; + + p3 = lookup_tbl + samples_offset + v2 + 1; + p3[0] = v1 & 0xff; + p3[1] = -(v1 & 0xff); + + v1++; + v2 += 2; + p2 -= 3; + } while (v1 <= v1_end); + + v1_start -= v1_dec; + v1_dec *= 2; + + util_buf_offset += util_buf_offset_inc; + util_buf_offset_inc *= 2; + buf1_val++; + + samples_offset += 255; + } while (buf1_val <= 7); + + p = lookup_tbl + 1785 + util_buf[388]; + p[0] = 129; +} + diff --git a/kopete/protocols/msn/webcam/libmimic/vlc_decode.c b/kopete/protocols/msn/webcam/libmimic/vlc_decode.c new file mode 100644 index 00000000..fed26743 --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/vlc_decode.c @@ -0,0 +1,119 @@ +/* Copyright (C) 2005 Ole Andr Vadla Ravns <oleavr@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> +#include "mimic-private.h" + +extern guchar _col_zag[64]; + +/* + * _vlc_decode_block + * + * De-serialize (reconstruct) a variable length coded 8x8 block. + */ +gboolean _vlc_decode_block(MimCtx *ctx, gint *block, gint num_coeffs) +{ + guint pos; + + memset(block, 0, 64 * sizeof(gint)); + + /* The DC-value is read in as is. */ + block[0] = _read_bits(ctx, 8); + + for (pos = 1; pos < num_coeffs; pos++) { + + guint prev_data_index, prev_cur_chunk_len, prev_chunk; + guint value, num_bits; + gboolean prev_read_odd, found_magic; + + /* Save context. */ + prev_data_index = ctx->data_index; + prev_cur_chunk_len = ctx->cur_chunk_len; + prev_chunk = ctx->cur_chunk; + prev_read_odd = ctx->read_odd; + + /* Grab 16 bits. */ + value = _read_bits(ctx, 16) << 16; + + /* Restore context. */ + ctx->data_index = prev_data_index; + ctx->cur_chunk_len = prev_cur_chunk_len; + ctx->cur_chunk = prev_chunk; + ctx->read_odd = prev_read_odd; + + /* Analyze and determine number of bits to read initially. */ + num_bits = 3; + if ((value >> 30) == 0 || (value >> 30) == 1) { + num_bits = 2; + } else if ((value & 0xE0000000) != 0x80000000) { + guint nibble = value >> 28; + + if (nibble == 11 || nibble == 12) { + num_bits = 4; + } else if (nibble == 10) { + _read_bits(ctx, 4); + + return TRUE; + } else { + if (((value << 2) & 0x8000000) == 0) + num_bits = 2; + + num_bits += 2; + } + } + + /* Read that number of bits. */ + value = _read_bits(ctx, num_bits); + + /* + * Look up the current value against the magic ones, + * and continue extending it bit by bit from the input + * stream until the magic value is found or we have + * read 32 bits (in which case we give up). + */ + found_magic = FALSE; + while (!found_magic) { + VlcMagic *magic; + + if (num_bits > 32) + return FALSE; + + magic = _find_magic(value); + + if (magic != NULL) { + pos += magic->pos_add; + num_bits = magic->num_bits; + + found_magic = TRUE; + } else { + value <<= 1; + value |= _read_bits(ctx, 1); + + num_bits++; + } + } + + /* Read the number of bits given by magic value entry. */ + value = _read_bits(ctx, num_bits); + + /* Gotcha! :-) */ + block[_col_zag[pos]] = ctx->vlcdec_lookup[(num_bits * 255) + value]; + } + + return TRUE; +} + diff --git a/kopete/protocols/msn/webcam/libmimic/vlc_encode.c b/kopete/protocols/msn/webcam/libmimic/vlc_encode.c new file mode 100644 index 00000000..6aeebe1c --- /dev/null +++ b/kopete/protocols/msn/webcam/libmimic/vlc_encode.c @@ -0,0 +1,84 @@ +/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdlib.h> +#include "mimic-private.h" + +extern guchar _col_zag[64]; +extern VlcSymbol _vlc_alphabet[16][128]; + +/* + * _vlc_encode_block + * + * Serialize an 8x8 block using variable length coding. + */ +void _vlc_encode_block(MimCtx *ctx, const gint *block, gint num_coeffs) +{ + gint i, num_zeroes; + + /* The DC value is written out as is. */ + _write_bits(ctx, block[0], 8); + + /* Number of zeroes prefixing the next non-zero value. */ + num_zeroes = 0; + + for (i = 1; i < num_coeffs && num_zeroes <= 14; i++) { + + /* Fetch AC coefficients from block in zig-zag order. */ + gint value = block[_col_zag[i]]; + + if (value != 0) { + VlcSymbol sym; + + /* Clip input values to [-128, +128]. */ + if (value < -128) + value = -128; + else if (value > 128) + value = 128; + + /* Look up symbol for the current non-zero value. */ + sym = _vlc_alphabet[num_zeroes][abs(value) - 1]; + + /* No symbol? very rare... */ + if (sym.length1 <= 0) + break; + + /* The symbols for negative values are the same as for positives, minus one. */ + if (value < 0) { + if (sym.length2 > 0) + sym.part2 -= 1; + else + sym.part1 -= 1; + } + + /* Write out the full symbol. */ + _write_bits(ctx, sym.part1, sym.length1); + if (sym.length2 > 0) + _write_bits(ctx, sym.part2, sym.length2); + + /* Start counting zeroes again. */ + num_zeroes = 0; + } else { + num_zeroes++; + } + } + + /* Write out EOB if necessary. */ + if (num_zeroes > 0) + _write_bits(ctx, 0xA, 4); +} + diff --git a/kopete/protocols/msn/webcam/mimicwrapper.cpp b/kopete/protocols/msn/webcam/mimicwrapper.cpp new file mode 100644 index 00000000..75eb52cf --- /dev/null +++ b/kopete/protocols/msn/webcam/mimicwrapper.cpp @@ -0,0 +1,105 @@ +/* + Copyright (c) 2005 by Olivier Goffart <ogoffart@ kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + + +#include "mimicwrapper.h" + +#include "libmimic/mimic.h" + +//#include <tqbytearray.h> +#include <kdebug.h> +#include <tqimage.h> + +MimicWrapper::MimicWrapper() : m_init(false) +{ + m_mimctx=mimic_open(); +} + +MimicWrapper::~MimicWrapper() +{ + mimic_close(m_mimctx); +} + + +TQPixmap MimicWrapper::decode(const TQByteArray& data) +{ + if(!m_init) + { + if(!mimic_decoder_init(m_mimctx, (guchar*)(data.data()))) + { + kdWarning(14140) << k_funcinfo << "Impossible to init decoder" << endl; + return TQPixmap(); + } + if (!mimic_get_property( m_mimctx, "buffer_size", &m_bufferSize) ) + { + kdWarning(14140) << k_funcinfo << "Impossible to get buffer size" << endl; + return TQPixmap(); + } + m_init=true; + } + + TQByteArray buff(m_bufferSize); + if(!mimic_decode_frame(m_mimctx, (guchar*)(data.data()) , (guchar*)(buff.data()) ) ) + { + kdWarning(14140) << k_funcinfo << "Impossible to decode frame" << endl; + return TQPixmap(); + } + int width,height; + mimic_get_property(m_mimctx, "width", &width); + mimic_get_property(m_mimctx, "height", &height); + + + TQByteArray buff2(m_bufferSize*4/3); + uint b2=0; + for(uint f=0;f<m_bufferSize;f+=3) + { + buff2[b2+0]=buff[f+2]; + buff2[b2+1]=buff[f+1]; + buff2[b2+2]=buff[f+0]; + buff2[b2+3]=0x00; + b2+=4; + } + + TQImage img( (uchar*)(buff2.data()) , width , height , 32 , 0L , 0, TQImage::BigEndian ); + return TQPixmap(img); +} + +TQByteArray MimicWrapper::encode(const TQByteArray& data) +{ + if(!m_init) + { + if(!mimic_encoder_init(m_mimctx, MIMIC_RES_HIGH)) + { + kdWarning(14140) << k_funcinfo << "Impossible to init encoder" << endl; + return TQByteArray(); + } + if (!mimic_get_property( m_mimctx, "buffer_size", &m_bufferSize) ) + { + kdWarning(14140) << k_funcinfo << "Impossible to get buffer size" << endl; + return TQByteArray(); + } + m_init=true; + m_numFrames=0; + } + + TQByteArray buff(m_bufferSize); + int buff_new_size; + if(!mimic_encode_frame(m_mimctx, (guchar*)(data.data()) , (guchar*)(buff.data()) , (gint*)(&buff_new_size) , m_numFrames%15==0 ) ) + { + kdWarning(14140) << k_funcinfo << "Impossible to decode frame" << endl; + return TQByteArray(); + } + buff.resize(buff_new_size); + ++m_numFrames; + return buff; +} diff --git a/kopete/protocols/msn/webcam/mimicwrapper.h b/kopete/protocols/msn/webcam/mimicwrapper.h new file mode 100644 index 00000000..1ed048a2 --- /dev/null +++ b/kopete/protocols/msn/webcam/mimicwrapper.h @@ -0,0 +1,40 @@ +/* + Copyright (c) 2005 by Olivier Goffart <ogoffart@ kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef MIMICWRAPPER_H +#define MIMICWREPPER_H + +#include <tqpixmap.h> + +#include "kopete_export.h" + +typedef struct _MimCtx MimCtx; + +class KOPETE_EXPORT MimicWrapper +{ + public: + MimicWrapper(); + ~MimicWrapper(); + + TQPixmap decode(const TQByteArray &data); + TQByteArray encode(const TQByteArray &data); + + private: + MimCtx *m_mimctx; + bool m_init; + uint m_bufferSize; + uint m_numFrames; +}; + +#endif + diff --git a/kopete/protocols/msn/webcam/msnwebcamdialog.cpp b/kopete/protocols/msn/webcam/msnwebcamdialog.cpp new file mode 100644 index 00000000..f10d6abe --- /dev/null +++ b/kopete/protocols/msn/webcam/msnwebcamdialog.cpp @@ -0,0 +1,82 @@ +/* + Kopete MSN Protocol + Copyright (c) 2005 by Olivier Goffart <ogoffart @kde.org> + + Note: this is just YahooWebcamDialog with s/Yahoo/MSN/g + + Copyright (c) 2005 by Matt Rogers <mattr@kde.org> + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#include "msnwebcamdialog.h" + +#include <tqframe.h> +#include <tqobject.h> +#include <tqwidget.h> +#include <kdebug.h> +#include <tdelocale.h> + + + +MSNWebcamDialog::MSNWebcamDialog( const TQString& contact, TQWidget * parent, const char * name ) + : KDialogBase( KDialogBase::Plain, i18n( "Webcam for %1" ).arg( contact ), + KDialogBase::Close, KDialogBase::Close, parent, name, false, true /*seperator*/ ), + m_imageContainer( this ) +{ + setInitialSize( TQSize(320,290), true ); + + setEscapeButton( KDialogBase::Close ); + /* + TQObject::connect( contact, TQT_SIGNAL( signalReceivedWebcamImage( const TQPixmap& ) ), + this, TQT_SLOT( newImage( const TQPixmap& ) ) ); + */ + TQObject::connect( this, TQT_SIGNAL( closeClicked() ), this, TQT_SIGNAL( closingWebcamDialog() ) ); + /* + TQObject::connect( contact, TQT_SIGNAL( webcamClosed( int ) ), this, TQT_SLOT( webcamClosed( int ) ) ); + */ + TQFrame* page = plainPage(); + if ( page ) + { + kdDebug(14180) << k_funcinfo << "Adding webcam image container" << endl; + //m_imageContainer.setText( i18n( "No webcam image received" ) ); + //m_imageContainer.setAlignment( TQt::AlignCenter ); + m_imageContainer.setMinimumSize(320,240); + } + show(); +} + +MSNWebcamDialog::~ MSNWebcamDialog( ) +{ + +} + +void MSNWebcamDialog::newImage( const TQPixmap & image ) +{ + kdDebug(14180) << k_funcinfo << "New image received" << endl; + // kdDebug(14180) << image << endl; + //m_imageContainer.clear(); + m_imageContainer.updatePixmap( image ); + //show(); +} + +void MSNWebcamDialog::webcamClosed( int reason ) +{ + kdDebug(14180) << k_funcinfo << "webcam closed with reason?? " << reason <<endl; + //m_imageContainer.clear(); + //m_imageContainer.setText( i18n( "Webcam closed with reason %1" ).arg( TQString::number( reason ) ) ); + //m_imageContainer.setAlignment( TQt::AlignCenter ); + //show(); +} + +// kate: indent-mode csands; tab-width 4; + +#include "msnwebcamdialog.moc" diff --git a/kopete/protocols/msn/webcam/msnwebcamdialog.h b/kopete/protocols/msn/webcam/msnwebcamdialog.h new file mode 100644 index 00000000..a740e8ab --- /dev/null +++ b/kopete/protocols/msn/webcam/msnwebcamdialog.h @@ -0,0 +1,56 @@ +/* + Kopete MSN Protocol + + Copyright (c) 2005 by Olivier Goffart <ogoffart @kde.org> + + Note: this is just YahooWebcamDialog with s/Yahoo/MSN/g + + Copyright (c) 2005 by Matt Rogers <mattr@kde.org> + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + ************************************************************************* + * * + * 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. * + * * + ************************************************************************* +*/ + +#ifndef YAHOOWEBCAMDIALOG_H_ +#define YAHOOWEBCAMDIALOG_H_ + +//#include <tqlabel.h> +#include <webcamwidget.h> +#include <kdialogbase.h> + +#include "kopete_export.h" + + +class TQPixmap; +class TQWidget; +class MSNContact; + +class KOPETE_EXPORT MSNWebcamDialog : public KDialogBase +{ +Q_OBJECT + +public: + MSNWebcamDialog( const TQString& contact, TQWidget* parent = 0, const char* name = 0 ); + ~MSNWebcamDialog(); + +public slots: + void newImage( const TQPixmap& image ); + void webcamClosed( int ); + +signals: + void closingWebcamDialog(); + +private: + Kopete::WebcamWidget m_imageContainer; + +}; + +#endif +//kate: indent-mode csands; auto-insert-doxygen on; diff --git a/kopete/protocols/yahoo/libkyahoo/yabentry.cpp b/kopete/protocols/yahoo/libkyahoo/yabentry.cpp index 4b197b28..9ca9af3d 100644 --- a/kopete/protocols/yahoo/libkyahoo/yabentry.cpp +++ b/kopete/protocols/yahoo/libkyahoo/yabentry.cpp @@ -60,6 +60,7 @@ void YABEntry::fromTQDomElement( const TQDomElement &e ) imGoogleTalk = e.attribute("img"); imICQ = e.attribute("imq"); imIRC = e.attribute("imc"); + imMSN = e.attribute("imm"); imQQ = e.attribute("imqq"); imSkype = e.attribute("imk"); } @@ -109,6 +110,7 @@ void YABEntry::fromTQDomDocument( const TQDomDocument &d ) imGoogleTalk = d.elementsByTagName("img").item(0).toElement().text(); imICQ = d.elementsByTagName("imq").item(0).toElement().text(); imIRC = d.elementsByTagName("imc").item(0).toElement().text(); + imMSN = d.elementsByTagName("imm").item(0).toElement().text(); imQQ = d.elementsByTagName("imqq").item(0).toElement().text(); imSkype = d.elementsByTagName("imk").item(0).toElement().text(); } @@ -155,6 +157,7 @@ void YABEntry::fillTQDomElement( TQDomElement &e ) const e.setAttribute( "img", imGoogleTalk ); e.setAttribute( "imq", imICQ ); e.setAttribute( "imc", imIRC ); + e.setAttribute( "imm", imMSN ); e.setAttribute( "imqq", imQQ ); e.setAttribute( "imk", imSkype ); } diff --git a/kopete/protocols/yahoo/libkyahoo/yabentry.h b/kopete/protocols/yahoo/libkyahoo/yabentry.h index bcb2bce9..14cacef8 100644 --- a/kopete/protocols/yahoo/libkyahoo/yabentry.h +++ b/kopete/protocols/yahoo/libkyahoo/yabentry.h @@ -46,6 +46,7 @@ struct YABEntry TQString altEmail2; TQString imAIM; TQString imICQ; + TQString imMSN; TQString imGoogleTalk; TQString imSkype; TQString imIRC; diff --git a/kopete/protocols/yahoo/ui/yahoouserinfodialog.cpp b/kopete/protocols/yahoo/ui/yahoouserinfodialog.cpp index 8164bae3..f98b80a4 100644 --- a/kopete/protocols/yahoo/ui/yahoouserinfodialog.cpp +++ b/kopete/protocols/yahoo/ui/yahoouserinfodialog.cpp @@ -120,6 +120,7 @@ void YahooUserInfoDialog::slotSaveAndCloseClicked() // entry.imGoogleTalk = m_genInfoWidget->firstNameEdit->text(); // entry.imICQ = m_genInfoWidget->firstNameEdit->text(); // entry.imIRC = m_genInfoWidget->firstNameEdit->text(); +// entry.imMSN = m_genInfoWidget->firstNameEdit->text(); // entry.imQQ = m_genInfoWidget->firstNameEdit->text(); // entry.imSkype = m_genInfoWidget->firstNameEdit->text(); @@ -190,6 +191,7 @@ void YahooUserInfoDialog::slotUser2() // entry.imGoogleTalk = m_genInfoWidget->firstNameEdit->text().isEmpty() ? oldEntry->notes : m_otherInfoWidget->commentsEdit->text(); // entry.imICQ = m_genInfoWidget->firstNameEdit->text().isEmpty() ? oldEntry->notes : m_otherInfoWidget->commentsEdit->text(); // entry.imIRC = m_genInfoWidget->firstNameEdit->text().isEmpty() ? oldEntry->notes : m_otherInfoWidget->commentsEdit->text(); + // entry.imMSN = m_genInfoWidget->firstNameEdit->text().isEmpty() ? oldEntry->notes : m_otherInfoWidget->commentsEdit->text(); // entry.imQQ = m_genInfoWidget->firstNameEdit->text().isEmpty() ? oldEntry->notes : m_otherInfoWidget->commentsEdit->text(); // entry.imSkype = m_genInfoWidget->firstNameEdit->text().isEmpty() ? oldEntry->notes : m_otherInfoWidget->commentsEdit->text(); diff --git a/kopete/protocols/yahoo/yahoocontact.cpp b/kopete/protocols/yahoo/yahoocontact.cpp index 8d9c5b00..86f89076 100644 --- a/kopete/protocols/yahoo/yahoocontact.cpp +++ b/kopete/protocols/yahoo/yahoocontact.cpp @@ -737,6 +737,7 @@ void YahooContact::writeYABEntry() setProperty( YahooProtocol::protocol()->propAltEmail2, m_YABEntry->altEmail2 ); setProperty( YahooProtocol::protocol()->propImAIM, m_YABEntry->imAIM ); setProperty( YahooProtocol::protocol()->propImICQ, m_YABEntry->imICQ ); + setProperty( YahooProtocol::protocol()->propImMSN, m_YABEntry->imMSN ); setProperty( YahooProtocol::protocol()->propImGoogleTalk, m_YABEntry->imGoogleTalk ); setProperty( YahooProtocol::protocol()->propImSkype, m_YABEntry->imSkype ); setProperty( YahooProtocol::protocol()->propImIRC, m_YABEntry->imIRC ); @@ -799,6 +800,7 @@ void YahooContact::readYABEntry() m_YABEntry->altEmail2 = property( YahooProtocol::protocol()->propAltEmail2 ).value().toString(); m_YABEntry->imAIM = property( YahooProtocol::protocol()->propImAIM ).value().toString(); m_YABEntry->imICQ = property( YahooProtocol::protocol()->propImICQ ).value().toString(); + m_YABEntry->imMSN = property( YahooProtocol::protocol()->propImMSN ).value().toString(); m_YABEntry->imGoogleTalk = property( YahooProtocol::protocol()->propImGoogleTalk ).value().toString(); m_YABEntry->imSkype = property( YahooProtocol::protocol()->propImSkype ).value().toString(); m_YABEntry->imIRC = property( YahooProtocol::protocol()->propImIRC ).value().toString(); diff --git a/kopete/protocols/yahoo/yahooprotocol.cpp b/kopete/protocols/yahoo/yahooprotocol.cpp index 6b0328d6..6197aa86 100644 --- a/kopete/protocols/yahoo/yahooprotocol.cpp +++ b/kopete/protocols/yahoo/yahooprotocol.cpp @@ -73,6 +73,7 @@ YahooProtocol::YahooProtocol( TQObject *parent, const char *name, const TQString propAltEmail2("YABAlternativeEmail2", i18n("Alternative email 1"), TQString(), true, false), propImAIM("YABIMAIM", i18n("AIM"), TQString(), true, false), propImICQ("YABIMICQ", i18n("ICQ"), TQString(), true, false), + propImMSN("YABIMMSN", i18n("MSN"), TQString(), true, false), propImGoogleTalk("YABIMGoogleTalk", i18n("GoogleTalk"), TQString(), true, false), propImSkype("YABIMSkype", i18n("Skype"), TQString(), true, false), propImIRC("YABIMIRC", i18n("IRC"), TQString(), true, false), diff --git a/kopete/protocols/yahoo/yahooprotocol.h b/kopete/protocols/yahoo/yahooprotocol.h index 4370e69f..075b84db 100644 --- a/kopete/protocols/yahoo/yahooprotocol.h +++ b/kopete/protocols/yahoo/yahooprotocol.h @@ -89,6 +89,7 @@ public: const Kopete::ContactPropertyTmpl propAltEmail2; const Kopete::ContactPropertyTmpl propImAIM; const Kopete::ContactPropertyTmpl propImICQ; + const Kopete::ContactPropertyTmpl propImMSN; const Kopete::ContactPropertyTmpl propImGoogleTalk; const Kopete::ContactPropertyTmpl propImSkype; const Kopete::ContactPropertyTmpl propImIRC; |