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>
pull/19/head
Slávek Banko 5 years ago
parent 914254104c
commit 73f0033617
No known key found for this signature in database
GPG Key ID: 608F5293A04BE668

@ -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} )

@ -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>

@ -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 &amp; 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 &amp; Network</guimenu><guimenuitem>Proxy</guimenuitem></menuchoice>.</para></listitem>
</itemizedlist>
</answer>
</qandaentry>

@ -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 )

@ -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

@ -75,4 +75,16 @@ OSCAR ICQ/AIM TODO ITEMS
of AIM to take over just that part of the plugin.
================================================================================
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)
================================================================================

@ -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

@ -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;

@ -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 );

@ -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;

@ -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?

@ -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()

@ -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.
*

@ -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") )
{

@ -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

@ -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.
*/

@ -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 )

@ -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)

@ -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;

@ -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();

@ -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}
)

@ -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

@ -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 影像與聲音

@ -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 影像與聲音

@ -0,0 +1,9 @@
<!DOCTYPE kpartgui>
<kpartgui version="1" name="kopete_msn_netmeeting">
<MenuBar>
<Menu name="tools">
<text>&amp;Tools</text>
<Action name="netmeeting" />
</Menu>
</MenuBar>
</kpartgui>

@ -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:

@ -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:

@ -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"

@ -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

@ -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"

@ -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

@ -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:

@ -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

@ -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>&lt;b&gt;%1&lt;/b&gt; 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: &lt;a href="http://www.kde-apps.org/content/show.php?content=10395"&gt;http://www.kde-apps.org/content/show.php?content=10395&lt;/a&gt;</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>

@ -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);
}

@ -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>

@ -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>

@ -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>

@ -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>

@ -200,15 +200,16 @@ Note that some web browsers do not support XHTML. You should also make sure your
<string>Repla&amp;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

@ -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 )

@ -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)

@ -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]),

@ -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";

@ -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());

@ -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}
)

@ -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.

@ -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

@ -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

@ -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

@ -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}
)

@ -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

@ -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 網路協定

@ -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");
}
};

@ -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>&amp;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>&amp;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 &amp;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>

@ -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"

@ -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

@ -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(); }

@ -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 )

@ -0,0 +1,2 @@
kopeteicondir = $(kde_datadir)/kopete/icons
kopeteicon_ICON = AUTO

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 687 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 497 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 819 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 943 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 943 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

@ -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"

@ -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

@ -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 的協定

@ -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"

@ -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

File diff suppressed because it is too large Load Diff

@ -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:

@ -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:

@ -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:

@ -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"

@ -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

@ -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:

@ -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:

@ -0,0 +1,27 @@
<!DOCTYPE kpartgui>
<kpartgui version="12" name="kopete_msn_chat">
<MenuBar>
<Menu noMerge="1" name="file">
<text>&amp;Chat</text>
<Action name="msnWebcamReceive" />
<Action name="msnWebcamSend" />
<Action name="msnSendFile" />
<Action name="msnSendNudge" />
<Action name="msnRequestDisplayPicture" />
<Action name="msnInvite" />
<!-- <Menu name="debug">
<text>&amp;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>

@ -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:

@ -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:

@ -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:

@ -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:

@ -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"

@ -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:

@ -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;
}

@ -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

File diff suppressed because it is too large Load Diff

@ -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:

@ -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:

@ -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:

@ -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"

@ -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

File diff suppressed because it is too large Load Diff

@ -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

File diff suppressed because it is too large Load Diff

@ -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:

@ -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"

@ -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

@ -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"

@ -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

@ -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;
}

@ -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

@ -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"

@ -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

@ -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
)

@ -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

@ -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>&amp;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>&lt;i&gt;(for example: joe@hotmail.com)&lt;/i&gt;</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>

@ -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>&amp;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&amp;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 &amp;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 &amp;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>

File diff suppressed because it is too large Load Diff

@ -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:

@ -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:

@ -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 &amp;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>

@ -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

@ -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

@ -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
)

@ -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)

@ -0,0 +1,2 @@
Ole André Vadla Ravnås <oleavr@gmail.com>

@ -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}
)

@ -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!

@ -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 "^[^_].*"

@ -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

@ -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);
}
}

@ -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;
}
}

@ -0,0 +1,450 @@
/* 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 "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++;
}
}

@ -0,0 +1,311 @@
/* 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"
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;
}

@ -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;
}

@ -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;
}

@ -0,0 +1,134 @@
/* 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"
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++;
}
}

@ -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

@ -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;
}

@ -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

File diff suppressed because it is too large Load Diff

@ -0,0 +1,119 @@
/* 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"
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;
}

@ -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);
}

@ -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;
}

@ -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

@ -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"

@ -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;

@ -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 );
}

@ -46,6 +46,7 @@ struct YABEntry
TQString altEmail2;
TQString imAIM;
TQString imICQ;
TQString imMSN;
TQString imGoogleTalk;
TQString imSkype;
TQString imIRC;

@ -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();

@ -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();

@ -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),

@ -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;

Loading…
Cancel
Save