Hello to all libmsn and kopete developers,

I was trying to add support for webcam conversations to libmsn and have some 
questions regarding this topic.

At first I wanted to ask if somebody knows the current state of the telepathy 
plugin for kopete (as seen in the private conversation to Tiago below). 
The msn connection manager telepathy-butterfly is based upon papyon, a python 
library for the MSN Messenger network. Papyon would already support webcam 
conversations using the "webcam protocol" and SIP (which currently seems to be 
broken, maybe due to the same issues as amsn [1]). 

Libmsn had already initial support for the handshake and I tried to implement 
the other parts, but I'm currently stuck.
I got the XML description for the webcam transfer from the other client but 
don't know what to respond exactly. I tried to follow the payon code as close 
as possible, but couldn't manage to get it work. I also couldn't find any 
useful information in the internet and sniffing data from amsn also didn't 
help. 
Could anybody explain the handshake to me?

I attached my current diff file, but it needs a major refactoring before the 
release.  The biggest changes are some debug lines that I used to understand 
the protocol. I think the best way to implement the webcam P2P protocol would 
be to create P2P as base class and FileTransfer and Webcam as subclasses. Does 
anybody have a issue with the proposed method?

Greetings,
Lukas

[1]  http://kakaroto.homelinux.net/2010/03/amsn-0-98-2-to-be-released-without-
audiovideo-support/

Am Freitag 14 Mai 2010 20:17:19 schrieb Tiago Salem Herrmann:
> Hi, thanks for the contact.
> 
> On Thu, May 13, 2010 at 5:31 PM, Lukas Hetzenecker <l...@gmx.at> wrote:
> > Hello Tiago and Will,
> > 
> > This project would certainly have been interesting, it's a pity that it
> > wasn't accepted.
> > Webcam support in WLM is requested by many people [see bug 70538 for
> > example] and would be the killer feature in kopete, because many other
> > clients can't handle it.
> 
> Indeed.
> 
> > At the beginng I have some questions about the future developement of
> > kopete: - What is the current state of the telepathy plugin?
> > I've read that the Telepathy framework uses farsight to handle media
> > streams [1], so would it be better to use the Telepathy-Qt4 library [2]
> > directly?
> > 
> > There are many different protocols for webcam / video conversations as
> > described here [3].
> > The most important seem to be the "The Computer Call" and "The Webcam":
> > 
> > The Webcam uses the ML20 codec for encoding and seems to be rather easy
> > to implement. I've used wireshark to sniff for some traffic generated by
> > amsn. If you want I can send you some tcpdump capture files.
> > libmimic could be used to decode the video stream (I don't think farsight
> > would be a good idea to use directly, because it needs Glib and uses
> > GObjects).
> > There are already implementations in amsn [4][5] and papyon [6].
> > 
> > The official Live Messenger seems to use the "Computer Call" whenever it
> > is possible. This protocol is based on SIP and RTP. It also requires
> > MSNP2PV2 and therefore MSNP18. Porting to this version is the ideal
> > long-term goal, because if also offers many other features like
> > "Multiple Points of Presence" and Group sessions [7]. Also amsn will go
> > this way soon [8].
> > 
> > I would be glad to help in the developement of libmsn and kopete.
> 
> Nice, so do you know exactly what was changed from MSNp15 to MSNp18?
> It's been a while since I stopped following the msn protocol updates.
> I believe that updating libmsn to a new protocol would be a very good idea.
> 
> > Greetings from Austria,
> > Lukas Hetzenecker
> > 
> > p.s. Would you mind if i CC kopete-devel and Libmsn-discuss too?
> 
> Not at all. Please do it.
> 
> > [1] http://farsight.freedesktop.org/wiki/
> > [2] http://telepathy.freedesktop.org/wiki/Components
> > [3] http://imfreedom.org/wiki/MSN:AV
> > [4]
> > http://amsn.svn.sourceforge.net/viewvc/amsn/trunk/amsn/msncam.tcl?view=ma
> > rkup [5]
> > http://amsn.svn.sourceforge.net/viewvc/amsn/trunk/amsn/msnp2p.tcl?view=ma
> > rkup [6]
> > http://git.collabora.co.uk/?p=papyon.git;a=blob;f=papyon/msnp2p/webcam.p
> > y [7] http://en.wikipedia.org/wiki/Microsoft_Notification_Protocol#MSNP17
> > [8]
> > http://kakaroto.homelinux.net/2010/03/amsn-0-98-2-to-be-released-without
> > - audiovideo-support/
> > 
> > Am Mittwoch 12 Mai 2010 17:32:16 schrieb Will Stephenson:
> >> Tiago:  LuHe was asking in Kopete about MSN webcam so I am cc'ing him
> >> your proposal outline - perhaps you could join forces.
> >> 
> >> Will
> >> 
> >> 
> >> "Since KDE 4.2, kopete is using WLM (based on libmsn [2]) as the main
> >> MSN plugin. WLM is working fine so far, but there is a big feature
> >> missing: Webcam support. This feature will require some work both on
> >> WLM and libmsn. Libmsn still does not recognize the webcam protocol,
> >> only the initial handshake. The webcam communication is based on a P2P
> >> protocol and unfortunately it is not well documented, not even in
> >> msnpiki [5]. Currently there is an opensource library called libmimic
> >> that helps on encoding/decoding the stream sent/received by the
> >> official client. Part of the work will be link libmimic to libmsn (or
> >> WLM, as it will be decided during the development).
> >> 
> >> WLM will also need a new UI to manage the webcam calls. This UI will be
> >> created with qtdesigner and (if possible) fully integrated with the chat
> >> window. The current implementation shows the webcam calls out of the
> >> chatwindow, and this way it is not easy to track which webcam call
> >> belong to a certain chatwindow. Moving the webcam window to inside the
> >> chatwindow will improve the user experience."

Index: msn/p2p.cpp
===================================================================
--- msn/p2p.cpp	(Revision 120)
+++ msn/p2p.cpp	(Arbeitskopie)
@@ -20,6 +20,8 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#include <msn/debug.h>
+
 #include <msn/notificationserver.h>
 #include <msn/errorcodes.h>
 #include <msn/externals.h>
@@ -32,13 +34,14 @@
 #include <fstream>
 #include <vector>
 
-#include <string.h>
+#include <cstring>
 
 namespace MSN {
 
     P2P::P2P()
     {
         rand_helper = 1;
+
     }
     P2P::~P2P()
     {
@@ -51,7 +54,11 @@
         p2pPacket packet;
         // not for me, ignore it
         if(headers["P2P-Dest"] != conn.myNotificationServer()->myPassport)
+        {
+            std::cout << "not my dest!" << std::endl;
+            std::flush(std::cout);
             return;
+        }
 
         std::istringstream header(body,std::ios::binary);
 
@@ -73,10 +80,29 @@
         
         if(packet.p2pHeader.flag==FLAG_ACK)
         {
+            debug_start();
+            debug_line("Handle ack to P2P package " << packet.p2pHeader.ackID);
+            debug_end();
             handle_p2pACK(conn, packet);
             return;
         }
 
+        debug_start();
+        debug_line("Got P2P message!");
+        debug_line("Session ID:      " << packet.p2pHeader.sessionID);
+        debug_line("Identifier:      " << packet.p2pHeader.identifier);
+        debug_line("Data offset:     " << packet.p2pHeader.dataOffset);
+        debug_line("Total data size: " << packet.p2pHeader.totalDataSize);
+        debug_line("Message Length:  " << packet.p2pHeader.messageLength);
+        debug_line("Flag:            " << packet.p2pHeader.flag);
+        debug_line("Ack ID:          " << packet.p2pHeader.ackID);
+        debug_line("Ack UID:         " << packet.p2pHeader.ackUID);
+        debug_line("Ack Data size:   " << packet.p2pHeader.ackDataSize);
+        debug_line("Body:");
+        debug_line(packet.body);
+        debug_line("Footer:          " << packet.p2pFooter.appID);
+        debug_end();
+
         if(packet.p2pHeader.sessionID == 0x40 &&
                 little2big_endian(packet.p2pFooter.appID)==3) // INK, oh god!
         {
@@ -134,17 +160,24 @@
         }
 
         // in these conditions, the packet is data, receive it to a file
-        if(packet.p2pHeader.sessionID &&
-               (packet.p2pHeader.flag == FLAG_FILE_DATA ||
-                packet.p2pHeader.flag == FLAG_FILE_DATA2 ||
-                packet.p2pHeader.flag == FLAG_DATA_EMOTICONS)) 
+        if(packet.p2pHeader.sessionID)
         {
             // we need to ensure we have a started session
             if(!startedSessions.count(packet.p2pHeader.sessionID))
                 return;
+            startedSessions[packet.p2pHeader.sessionID].step = STEP_RECEIVING;
 
-            startedSessions[packet.p2pHeader.sessionID].step = STEP_RECEIVING;
-            receiveP2PData(conn,packet);
+            if (packet.p2pHeader.flag == FLAG_FILE_DATA ||
+                packet.p2pHeader.flag == FLAG_FILE_DATA2 ||
+                packet.p2pHeader.flag == FLAG_DATA_EMOTICONS)
+            {
+                receiveP2PData(conn,packet);
+            }
+            else if (packet.p2pHeader.flag == FLAG_WEBCAM)
+            {
+                receiveP2PWebcamData(conn,packet);
+            }
+
             return;
         }
 
@@ -215,7 +248,8 @@
         {
             handle_603Decline(conn,packet);
         }
-/*        std::cout << "session id: " << packet.p2pHeader.sessionID << std::endl;
+        /*std::cout << "------------------" << std::endl;
+        std::cout << "session id: " << packet.p2pHeader.sessionID << std::endl;
         std::cout << "identifier: " << packet.p2pHeader.identifier << std::endl;
         std::cout << "dataOffset: " << packet.p2pHeader.dataOffset << std::endl;
         std::cout << "totalDataSize: " << packet.p2pHeader.totalDataSize << std::endl;
@@ -225,7 +259,9 @@
         std::cout << "ackUID: " << packet.p2pHeader.ackUID << std::endl;
         std::cout << "ackDataSize: " << packet.p2pHeader.ackDataSize << std::endl;
         std::cout << "footer: " << packet.p2pFooter.appID << std::endl << std::endl;
-*/
+        std::cout << "------------------" << std::endl;
+        std::flush(std::cout);*/
+
     }
 
     void P2P::sendACK(MSN::SwitchboardServerConnection &conn, p2pPacket &packet, p2pSession &session)
@@ -276,6 +312,26 @@
         buf_ << "MSG " << conn.trID++ << " D " << full_msg.str().size() << "\r\n";
         buf_ << full_msg.str();
 
+        debug_start();
+        debug_line("Send ACK to package id " << ack_pkt.p2pHeader.ackID);
+        debug_end();
+
+        /*debug_start();
+        debug_line("Send ACK!");
+        debug_line("Session ID:      " << ack_pkt.p2pHeader.sessionID);
+        debug_line("Identifier:      " << ack_pkt.p2pHeader.identifier);
+        debug_line("Data offset:     " << ack_pkt.p2pHeader.dataOffset);
+        debug_line("Total data size: " << ack_pkt.p2pHeader.totalDataSize);
+        debug_line("Message Length:  " << ack_pkt.p2pHeader.messageLength);
+        debug_line("Flag:            " << ack_pkt.p2pHeader.flag);
+        debug_line("Ack ID:          " << ack_pkt.p2pHeader.ackID);
+        debug_line("Ack UID:         " << ack_pkt.p2pHeader.ackUID);
+        debug_line("Ack Data size:   " << ack_pkt.p2pHeader.ackDataSize);
+        debug_line("Body:");
+        debug_line(packet.body);
+        debug_line("Footer:          " << ack_pkt.p2pFooter.appID);
+        debug_end();*/
+
         if (conn.write(buf_) != buf_.str().size())
             return;
 /*            std::cout << "session id: " << ack_pkt.p2pHeader.sessionID << std::endl;
@@ -294,6 +350,9 @@
 
     void P2P::receiveP2PData(MSN::SwitchboardServerConnection &conn, p2pPacket &packet)
     {
+        std::cout << "got data from session " << packet.p2pHeader.sessionID << std::endl;
+        std::flush(std::cout);
+
         // check if there is no session
         if(!startedSessions.count(packet.p2pHeader.sessionID))
             return;
@@ -356,6 +415,147 @@
         }
     }
 
+    void P2P::receiveP2PWebcamData(MSN::SwitchboardServerConnection &conn, p2pPacket &packet)
+    {
+        static std::string data = "";
+        std::cout << "got webcam data from session " << packet.p2pHeader.sessionID << std::endl;
+        std::flush(std::cout);
+
+        // check if there is no session
+        if(!startedSessions.count(packet.p2pHeader.sessionID))
+            return;
+
+        p2pSession session = startedSessions[packet.p2pHeader.sessionID];
+
+        // we have to wait until the message is complete...
+        data.insert(packet.p2pHeader.dataOffset, packet.body);
+        if (packet.p2pHeader.totalDataSize != data.length())
+            return;
+
+        // use _ucs2_utf8
+        std::string content = "";
+        for(int i = 5; i <= data.length() / 2; i++) // skip the first 10 bytes and return every second
+        {
+            if (data.at(i*2) == '\0')
+                break;
+            content.append(&data.at(i*2));
+        }
+
+        std::cout << "content is: " << content << " (" << content.length() << ")" << std::endl;
+        std::cout << "producer: " << content.find("<producer>") << "; npos: " << std::string::npos << std::endl;
+        std::flush(std::cout);
+
+        if (content == "syn")
+        {
+            std::cout << "send ack" << std::endl;
+            std::flush(std::cout);
+
+            session.currentIdentifier++;
+            if(session.currentIdentifier == session.baseIdentifier) // skip the original identifier
+                session.currentIdentifier++;
+
+            p2pPacket pkt;
+            pkt.p2pHeader.sessionID = packet.p2pHeader.sessionID;
+            pkt.p2pHeader.flag = FLAG_NOP;
+            pkt.p2pHeader.identifier = session.currentIdentifier;
+            pkt.p2pHeader.ackID = rand()%0x8FFFFFF0 + rand_helper++;
+            pkt.p2pHeader.ackUID = 0;
+            pkt.p2pHeader.ackDataSize = 0;
+            // big endian
+            pkt.p2pFooter.appID = little2big_endian(session.appID);
+
+            std::ostringstream content;
+            //content.write("a\00c\00k\00\00\00",8);
+            content.write("\x80\xEA\x00\x00\x08\x00\x08\x00\x00\x00\x61\x00\x63\x00\x6b\x00\x00\x00",18);
+
+            pkt.body = content.str();
+
+            sendP2PPacket(conn, pkt, session);
+        }
+        else if (content.find("<producer>") != std::string::npos)
+        {
+            std::cout << "start parsing" << std::endl;
+            std::flush(std::cout);
+
+            XMLNode producer = XMLNode::parseString(content.c_str(), "producer");
+            //std::string version = producer.getChildNode("version").getText();
+            //std::string rid = producer.getChildNode("rid").getText();
+
+            XMLNode viewer = XMLNode::createXMLTopNode("viewer");
+            viewer.addChild("version").addText("2.0");
+            viewer.addChild("rid").addText(producer.getChildNode("rid").getText());
+            viewer.addChild("session").addText(producer.getChildNode("session").getText());
+            viewer.addChild("ctypes").addText("0");
+            viewer.addChild("cpu").addText("2010");
+            XMLNode tcp = viewer.addChild("tcp");
+            tcp.addChild("tcpport").addText(producer.getChildNode("tcp").getChildNode("tcpport").getText());
+            tcp.addChild("tcplocalport").addText(producer.getChildNode("tcp").getChildNode("tcpport").getText());
+            tcp.addChild("tcpexternalport").addText("0");
+
+            int u = 0;
+            for(int i = 0; i <= producer.getChildNode("tcp").nChildNode(); i++)
+            {
+                std::string name = producer.getChildNode(i).getName();
+                if (name.find("tcpipaddress") != std::string::npos)
+                {
+                    tcp.addChild("tcpipaddress" + u).addText(producer.getChildNode(i).getText());
+                    u++;
+                }
+            }
+
+            viewer.addChild("codec").addText("");
+            viewer.addChild("channelmode").addText("2");
+
+            std::cout << "send: " << viewer.createXMLString() << std::endl;
+            std::flush(std::cout);
+
+            std::string xml = viewer.createXMLString();
+
+            std::string temp;
+            for (unsigned int i = 0; i < xml.length(); i++)
+                if (xml[i] != ' ' && xml[i] != '\n' && xml[i] != '\t')
+                    temp += xml[i];
+            xml = temp;
+
+            // TODO - convert filename to ucs2
+            U8 *filenameutf8 = new U8[520];
+            U8 *filenameutf16 = new U8[521];
+            memset(filenameutf8, 0, 520);
+            memset(filenameutf16, 0, 521);
+            memcpy(filenameutf8, xml.c_str(), xml.size());
+            _utf8_ucs2(filenameutf16, filenameutf8);
+            filenameutf16++;
+
+            session.currentIdentifier++;
+            if(session.currentIdentifier == session.baseIdentifier) // skip the original identifier
+                session.currentIdentifier++;
+
+            p2pPacket pkt;
+            pkt.p2pHeader.sessionID = packet.p2pHeader.sessionID;
+            pkt.p2pHeader.flag = FLAG_NOP;
+            pkt.p2pHeader.identifier = session.currentIdentifier;
+            pkt.p2pHeader.ackID = rand()%0x8FFFFFF0 + rand_helper++;
+            pkt.p2pHeader.ackUID = 0;
+            pkt.p2pHeader.ackDataSize = 0;
+            // big endian
+            pkt.p2pFooter.appID = little2big_endian(session.appID);
+
+            std::ostringstream content;
+            content.write("\x80\x00\x09\x00\x08\x00\xD8\x02\x00\x00", 10);
+            content.write((char*)filenameutf16, xml.size()*2-1);
+            content.write("\x00\x0d\x00\x0a\x00\x0d\x00\x0a\x00\x00\x00", 11);
+            pkt.body = content.str();
+
+            sendP2PPacket(conn, pkt, session);
+
+
+        }
+
+        data = "";
+
+    }
+
+
     void P2P::sendP2PData(MSN::SwitchboardServerConnection &conn, p2pSession &session, p2pPacket &packet)
     {
         p2pPacket pkt_part = session.tempPacket;
@@ -441,6 +641,23 @@
         buf_ << "MSG " << conn.trID << " D " << full_msg.str().size() << "\r\n";
         buf_ << full_msg.str();
 
+        debug_start();
+        debug_line("Send P2P Data!");
+        debug_line("Session ID:      " << pkt_part.p2pHeader.sessionID);
+        debug_line("Identifier:      " << pkt_part.p2pHeader.identifier);
+        debug_line("Data offset:     " << pkt_part.p2pHeader.dataOffset);
+        debug_line("Total data size: " << pkt_part.p2pHeader.totalDataSize);
+        debug_line("Message Length:  " << pkt_part.p2pHeader.messageLength);
+        debug_line("Flag:            " << pkt_part.p2pHeader.flag);
+        debug_line("Ack ID:          " << pkt_part.p2pHeader.ackID);
+        debug_line("Ack UID:         " << pkt_part.p2pHeader.ackUID);
+        debug_line("Ack Data size:   " << pkt_part.p2pHeader.ackDataSize);
+        debug_line("Body:");
+        debug_line(packet.body);
+        debug_line("Footer:          " << pkt_part.p2pFooter.appID);
+        debug_end();
+
+
         if (conn.write(buf_) != buf_.str().size())
             return;
 
@@ -506,6 +723,23 @@
             buf_ << "MSG " << conn.trID++ << " D " << full_msg.str().size() << "\r\n";
             buf_ << full_msg.str();
 
+            debug_start();
+            debug_line("Send P2P Packet!");
+            debug_line("Session ID:      " << packet.p2pHeader.sessionID);
+            debug_line("Identifier:      " << packet.p2pHeader.identifier);
+            debug_line("Data offset:     " << packet.p2pHeader.dataOffset);
+            debug_line("Total data size: " << packet.p2pHeader.totalDataSize);
+            debug_line("Message Length:  " << packet.p2pHeader.messageLength);
+            debug_line("Flag:            " << packet.p2pHeader.flag);
+            debug_line("Ack ID:          " << packet.p2pHeader.ackID);
+            debug_line("Ack UID:         " << packet.p2pHeader.ackUID);
+            debug_line("Ack Data size:   " << packet.p2pHeader.ackDataSize);
+            debug_line("Body:");
+            debug_line(packet.body);
+            debug_line("Footer:          " << packet.p2pFooter.appID);
+            debug_end();
+
+
             if (conn.write(buf_) != buf_.str().size())
                 return;
         }
@@ -520,7 +754,7 @@
         Message::Headers header_app = Message::Headers(msg[1]);
         switch(session.appID)
         {
-            case APP_FILE_TRANSFER:
+            case APP_FILE_TRANSFER or APP_WEBCAM:
             {
                 session.CSeq = decimalFromString(header_slp["CSeq"]);
                 session.Bridges = header_app["Bridges"];
@@ -592,6 +826,9 @@
 
     void P2P::handle_INVITE(MSN::SwitchboardServerConnection &conn, p2pPacket &packet)
     {
+        std::cout << "handle invite" << std::endl;
+        std::flush(std::cout);
+
         p2pSession session;
         std::vector<std::string> msg = splitString(packet.body, "\r\n\r\n");
         msg[1]+="\r\n"; // this is really needed :(
@@ -610,9 +847,15 @@
         session.CallID = header_slp["Call-ID"];
         session.ContentType = header_slp["Content-Type"];
 
+        std::cout << "look for: " << session.CallID << std::endl;
+        std::flush(std::cout);
+
         std::map<unsigned int, p2pSession>::iterator i = startedSessions.begin();
         for(; i != startedSessions.end(); i++)
         {
+            std::cout << (*i).first << ": " << (*i).second.CallID << std::endl;
+            std::flush(std::cout);
+
             if((*i).second.CallID == session.CallID)
             {
                 // this isn't a new session, since I already have this callid
@@ -622,6 +865,8 @@
                 old_session.CSeq = session.CSeq;
                 old_session.ContentType = session.ContentType;
                 // handle the changes received in this invitation packet
+                std::cout << "handle session changes!!!" << std::endl;
+                std::flush(std::cout);
                 handle_session_changes(conn, packet, old_session);
                 return;
             }
@@ -641,19 +886,34 @@
         session.baseIdentifier++;
         session.step = STEP_ACK_INVITATION_SENT;
 
+        std::cout << "Session appID is: " << session.appID << std::endl;
+        std::flush(std::cout);
+
         switch(session.appID)
         {
             case APP_WEBCAM:
             {
-                if(header_app["EUF-GUID"] == "{4BD96FC0-AB17-4425-A14A-439185962DC8}")
+                std::cout << "GUID is: " << header_app["EUF-GUID"] << std::endl;
+                std::flush(std::cout);
+                if(header_app["EUF-GUID"] == "{4BD96FC0-AB17-4425-A14A-439185962DC8}") // Media session
                 {
                     //conn.myNotificationServer()->externalCallbacks.askWebCam(&conn, session.sessionID);
+                    std::cout << "want media session!" << std::endl;
+                    std::flush(std::cout);
+                    
                     std::string body("SessionID: "+ toStr(session.sessionID) +"\r\n");
                     send_200OK(conn, session, body);
+                    //handle_session_changes(conn, packet, session);
                 }
-                if(header_app["EUF-GUID"] == "{1C9AA97E-9C05-4583-A3BD-908A196F1E92}")
+                if(header_app["EUF-GUID"] == "{1C9AA97E-9C05-4583-A3BD-908A196F1E92}") // Media receive only
                 {
-                    //conn.myNotificationServer()->externalCallbacks.askWebCam(&conn, session.sessionID);
+                    startedSessions[session.sessionID]=session;
+
+                    std::cout << "want webcam receive!\n";
+                    std::flush(std::cout);
+
+                    conn.myNotificationServer()->externalCallbacks.askWebCam(&conn, session.sessionID);
+
                 }
                 break;
             }
@@ -938,6 +1198,9 @@
 
     void P2P::send_603Decline(MSN::SwitchboardServerConnection &conn, p2pSession &session)
     {
+        std::cout << "sending decline!" << std::endl;
+        std::flush(std::cout);
+
         p2pPacket packet;
 
         std::ostringstream body2;
@@ -948,6 +1211,7 @@
                 "From: <msnmsgr:"+session.from+">\r\n"
                 "Via: "+session.Via+"\r\n"
                 "CSeq: "+ toStr(++session.CSeq) + "\r\n"
+                //"CSeq: "+ toStr(1) + "\r\n"
                 "Call-ID: "+session.CallID+"\r\n"
                 "Max-Forwards: 0\r\n"
                 "Content-Type: application/x-msnmsgr-sessionreqbody\r\n"
@@ -1050,21 +1314,46 @@
         this->removeCallback(packet.p2pHeader.ackUID);
         p2pSession session = startedSessions[sessionID];
         session.step = STEP_SENDING;
-        std::string filepath;
-        filepath += b64_decode(session.Context.c_str()); // prevents empty context
-        if(filepath.length())
+
+        switch(session.appID)
         {
-            if(!conn.myNotificationServer()->msnobj.getMSNObjectRealPath(b64_decode(session.Context.c_str()), session.filename))
+            case APP_FILE_TRANSFER:
             {
-                send_603Decline(conn,session);
+                std::string filepath;
+                filepath += b64_decode(session.Context.c_str()); // prevents empty context
+                if(filepath.length())
+                {
+                    if(!conn.myNotificationServer()->msnobj.getMSNObjectRealPath(b64_decode(session.Context.c_str()), session.filename))
+                    {
+                        std::cout << "DECLINE! " <<  __FUNCTION__ << " " << __FILE__ << ":" << __LINE__ << std::endl;
+                        std::flush(std::cout);
+
+                        send_603Decline(conn,session);
+                        return;
+                    }
+                }
+                else
+                {
+                    std::cout << "DECLINE! " <<  __FUNCTION__ << " " << __FILE__ << ":" << __LINE__ << std::endl;
+                    std::flush(std::cout);
+                    send_603Decline(conn,session);
+                    return;
+                }
+                break;
+            }
+
+            /*
+            case APP_WEBCAM:
+            {
+                std::string body("SessionID: "+ toStr(session.sessionID) +"\r\n");
+                send_200OK(conn,session,body);
                 return;
             }
+            */
+
+
         }
-        else
-        {
-            send_603Decline(conn,session);
-            return;
-        }
+
         sendP2PData(conn, session, packet);
     }
 
@@ -1091,10 +1380,35 @@
         else // user rejected
         {
             // I dont want to receive your file, blergh
+            std::cout << "DECLINE! " <<  __FUNCTION__ << " " << __FILE__ << ":" << __LINE__ << std::endl;
+            std::flush(std::cout);
             send_603Decline(conn,session);
         }
     }
 
+    void P2P::handle_webcamResponse(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, bool response)
+    {
+        p2pSession session = startedSessions[sessionID];
+        std::cout << "Session ID:" << sessionID << std::endl;
+        std::cout << session.sessionID << ";" << session.to << ";" << session.from << ";" << std::endl;
+        std::flush(std::cout);
+        if(response) // user accepted
+        {
+            std::cout << "send accept" << std::endl;
+            std::flush(std::cout);
+            session.in_stream = new std::ofstream;
+            std::string body("SessionID: "+ toStr(session.sessionID) +"\r\n");
+            send_200OK(conn, session, body);
+        }
+        else // user rejected
+        {
+            // I dont want to receive your webcam, blergh
+            std::cout << "DECLINE! " <<  __FUNCTION__ << " " << __FILE__ << ":" << __LINE__ << std::endl;
+            std::flush(std::cout);
+            send_603Decline(conn,session);
+        }
+    }
+
     void P2P::handle_DataACK(MSN::SwitchboardServerConnection &conn, unsigned int sessionID, p2pPacket &packet)
     {
         this->removeCallback(packet.p2pHeader.ackUID);
Index: msn/switchboardserver.h
===================================================================
--- msn/switchboardserver.h	(Revision 120)
+++ msn/switchboardserver.h	(Arbeitskopie)
@@ -148,6 +148,7 @@
         /** Response to a file transfer invitation
          */
         void fileTransferResponse(unsigned int sessionID, std::string filename, bool response);
+        void webcamResponse(unsigned int sessionID, bool response);
 
         /** Cancel a file transfer in progress
          */
Index: msn/externals.h
===================================================================
--- msn/externals.h	(Revision 120)
+++ msn/externals.h	(Arbeitskopie)
@@ -316,6 +316,10 @@
 
         virtual int getSocketFileDescriptor (void *sock) = 0;
 
+        /** Notifies your application that someone is trying to make a webcam session.
+          */
+        virtual void askWebCam(MSN::SwitchboardServerConnection * /*conn*/, unsigned int sessionID) = 0;
+
         /** Asks your application to get @c size bytes of data available in @p sock
          * and store them in @p data.
          * It must return the real size written to @p data
Index: msn/debug.h
===================================================================
--- msn/debug.h	(Revision 0)
+++ msn/debug.h	(Revision 0)
@@ -0,0 +1,36 @@
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#define DEBUG
+
+#ifdef DEBUG
+#include <ctime>
+#include <iostream>
+
+std::string GetTime()
+{
+    time_t curtime = time(0);
+    tm now=*localtime(&curtime);
+
+    char dest[256]={0};
+    const char format[]="%X";
+    strftime(dest, sizeof(dest)-1, format, &now);
+    return dest;
+}
+
+
+#define debug_start() std::cout << "*** " << GetTime() << " - " << __FUNCTION__ << ": " << "in " << __FILE__ << ":" << __LINE__ << std::endl;
+#define debug_line(a)  std::cout << "* " << a << std::endl;
+#define debug_end() std::cout << "***" << std::endl << std::endl; std::flush(std::cout);
+
+
+#else
+
+void debug_start() { }
+void debug_line() { }
+void debug_end() { }
+
+#endif /* DEBUG */
+
+
+#endif // DEBUG_H
Index: msn/switchboardserver.cpp
===================================================================
--- msn/switchboardserver.cpp	(Revision 120)
+++ msn/switchboardserver.cpp	(Arbeitskopie)
@@ -688,6 +688,13 @@
         p2p.handle_fileTransferResponse(*this,sessionID, filename, response);
     }
 
+    void SwitchboardServerConnection::webcamResponse(unsigned int sessionID, bool response)
+    {
+        std::cout << "in SwitchboardServerConnection::webcamResponse\n";
+        std::flush(std::cout);
+        p2p.handle_webcamResponse(*this,sessionID, response);
+    }
+
     void SwitchboardServerConnection::callback_AnsweredCall(std::vector<std::string> & args, int trid, void * data)
     {
         this->assertConnectionStateIs(SB_WAITING_FOR_USERS);        
Index: msn/p2p.h
===================================================================
--- msn/p2p.h	(Revision 120)
+++ msn/p2p.h	(Arbeitskopie)
@@ -91,6 +91,7 @@
                 FLAG_ERROR = 0x8,
                 FLAG_DATA_EMOTICONS = 0x20,
                 FLAG_DATA_PICTURE = 0x20,
+                FLAG_WEBCAM = 0x1000000,
                 FLAG_FILE_DATA = 0x01000030,
                 FLAG_FILE_DATA2 = 0x01000020
             };
@@ -213,6 +214,9 @@
             void receiveP2PData(MSN::SwitchboardServerConnection &conn, 
                     p2pPacket &packet);
 
+            void receiveP2PWebcamData(MSN::SwitchboardServerConnection &conn,
+                    p2pPacket &packet);
+
             void handle_negotiation(MSN::SwitchboardServerConnection &conn, 
                     p2pPacket &packet);
 
@@ -275,6 +279,10 @@
                     std::string filename,
                     bool response);
 
+            void handle_webcamResponse(MSN::SwitchboardServerConnection &conn,
+                    unsigned int sessionID,
+                    bool response);
+
             void handle_session_changes(MSN::SwitchboardServerConnection &conn, 
                     p2pPacket &packet, 
                     p2pSession &session);
Index: msntest/msntest.cpp
===================================================================
--- msntest/msntest.cpp	(Revision 120)
+++ msntest/msntest.cpp	(Arbeitskopie)
@@ -37,6 +37,7 @@
 #include <openssl/ssl.h>
 
 #include <msn/msn.h>
+
 #include <string>
 #include <iostream>
 
@@ -156,6 +157,8 @@
 
     virtual void askFileTransfer(MSN::SwitchboardServerConnection *conn, MSN::fileTransferInvite ft);
 
+    virtual void askWebCam(MSN::SwitchboardServerConnection *conn, unsigned int sessionID);
+
     virtual int listenOnPort(int port);
     
     virtual std::string getOurIP();
@@ -477,6 +480,7 @@
         clientid += MSN::InkGifSupport;
         clientid += MSN::SIPInvitations;
         clientid += MSN::SupportMultiPacketMessaging;
+        clientid += MSN::SupportWebcam;
 
         mainConnection.setState(MSN::buddyStatusFromString(state), clientid);
     } else if (!strcmp(command, "friendlyname")) {
@@ -1180,6 +1184,12 @@
     conn->fileTransferResponse(ft.sessionId, filename2, true);
 }
 
+void Callbacks::askWebCam(MSN::SwitchboardServerConnection *conn, unsigned int sessionID)
+{
+    std::cout << "Somebody wants to make a webcam session..." << std::endl;
+    conn->webcamResponse(sessionID, true);
+}
+
 void Callbacks::addedContactToGroup(MSN::NotificationServerConnection * conn, bool added, std::string groupId, std::string contactId)
 {
     if(added)
_______________________________________________
kopete-devel mailing list
kopete-devel@kde.org
https://mail.kde.org/mailman/listinfo/kopete-devel

Reply via email to