[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kopete-devel
Subject:    Re: Webcam sessions
From:       Lukas Hetzenecker <LuHe () gmx ! at>
Date:       2010-07-22 1:21:51
Message-ID: 201007220321.51949.LuHe () gmx ! at
[Download RAW message or body]

Hello,

I attached a new version of the patch for libmsn

The webcam session is now managed in an own class (in msn/media/session.cpp), 
so it is possible to have more than one webcam session and the connection is 
now asynchronous.

Greetings,
Lukas


On Tuesday 25 May 2010 03:40:15 Lukas Hetzenecker wrote:
> I just managed to save the frames in files and wrote a small python script
> to display them (webcamdata.py).
> Please note that this is my first C++ project so it is really buggy and
> will likely need a complete rewrite.
> 
> The current features are:
>  - Webcam inventations can be accepted
>  - Webcam frames are decoded using libmimic
>  - The decoded frames are saved in the folder /tmp/webcamdata
>  - There is a small script to display these frames
> 
> The drawbacks are:
>  - Only receiving is implemented
>  - I think it will only work if both clients are in the same network
>    (There is currently no possibilty to get the external IP address)
>  - There can be only one session
>  - The webcam socket in msntest blocks the whole application
>    (if linked to QtNetwork it would be possible to use a QTcpServer)
> 
> Lukas
> 
> Am Freitag 21 Mai 2010 21:47:38 schrieb Lukas Hetzenecker:
> > 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 <LuHe@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?vie
> > > > w= ma rkup [5]
> > > > http://amsn.svn.sourceforge.net/viewvc/amsn/trunk/amsn/msnp2p.tcl?vie
> > > > w= ma rkup [6]
> > > > http://git.collabora.co.uk/?p=papyon.git;a=blob;f=papyon/msnp2p/webca
> > > > m. 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-with
> > > > ou t - 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."

["libmsn-webcam-WIP-3.diff" (text/x-patch)]

Index: msn/p2p.cpp
===================================================================
--- msn/p2p.cpp	(revision 120)
+++ msn/p2p.cpp	(working copy)
@@ -20,19 +20,22 @@
  * 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>
 #include <msn/util.h>
 #include <msn/p2p.h>
 #include <msn/xmlParser.h>
+#include <msn/media/session.h>
 
 #include <cctype>
 #include <iostream>
 #include <fstream>
 #include <vector>
 
-#include <string.h>
+#include <cstring>
 
 namespace MSN {
 
@@ -73,10 +76,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 +156,25 @@
         }
 
         // 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;
-            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 +245,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 +256,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 +309,10 @@
         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();
+
         if (conn.write(buf_) != buf_.str().size())
             return;
 /*            std::cout << "session id: " << ack_pkt.p2pHeader.sessionID << \
std::endl; @@ -356,6 +393,190 @@
         }
     }
 
+    void P2P::receiveP2PWebcamData(MSN::SwitchboardServerConnection &conn, p2pPacket \
&packet) +    {
+        static std::string data = "";
+        static void* s = 0;
+
+        // 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));
+        }
+
+        if (content == "syn")
+        {
+            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)
+        {
+            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");
+            */
+
+            /*
+            XMLNode viewer = XMLNode::createXMLTopNode("viewer");
+            viewer.addChild("version").addText("2.0");
+            viewer.addChild("rid").addText("108");
+            viewer.addChild("session").addText(producer.getChildNode("session").getText());
 +            viewer.addChild("ctypes").addText("0");
+            viewer.addChild("cpu").addText("730");
+            XMLNode tcp = viewer.addChild("tcp");
+            tcp.addChild("tcpport").addText("6891");
+            tcp.addChild("tcplocalport").addText("6891");
+            tcp.addChild("tcpexternalport").addText("6891");
+            tcp.addChild("tcpipaddress1").addText(producer.getChildNode("tcp").getChildNode("tcpipaddress2").getText());
 +            tcp.addChild("tcpipaddress2").addText("192.168.1.101");
+
+            viewer.addChild("codec").addText("");
+            viewer.addChild("channelmode").addText("1");
+            */
+
+            //std::string xml = viewer.createXMLString();
+            std::list<std::string> ips = \
conn.myNotificationServer()->externalCallbacks.getLocalIPs(); +
+            std::stringstream xml;
+            xml << "<viewer>";
+            xml << "<version>2.0</version>";
+            xml << "<rid>143</rid>";
+            xml << "<session>" + \
std::string(producer.getChildNode("session").getText()) + "</session>"; +            \
xml << "<ctypes>0</ctypes>"; +            xml << "<cpu>730</cpu>";
+            xml << "<tcp>";
+            xml << "<tcpport>6891</tcpport>";
+            xml << "        <tcplocalport>6891</tcplocalport>";
+            xml << "        <tcpexternalport>6891</tcpexternalport>";
+            xml << "<tcpipaddress1>" + \
conn.myNotificationServer()->server_reported_ip + "</tcpipaddress1>"; +            \
int i = 2; +            while (!ips.empty()) {
+                std::string ip = ips.front();
+                xml << "<tcpipaddress" << i << ">" << ip << "</tcpipaddress" << i << \
">"; +                i++;
+                ips.pop_front();
+            }
+            xml << "</tcp>";
+            xml << "<codec></codec>";
+            xml << "<channelmode>1</channelmode>";
+            xml << "</viewer>";
+
+            std::string temp;
+            for (unsigned int i = 0; i < xml.str().length(); i++)
+            {
+                //if (xml[i] != ' ' && xml[i] != '\n' && xml[i] != '\t')
+                if (xml.str()[i] == ' ')
+                    temp += '\t';
+                if (xml.str()[i] != '\n')
+                    temp += xml.str()[i];
+            }
+
+
+            // TODO - convert filename to ucs2
+            U8 *filenameutf8 = new U8[800];
+            U8 *filenameutf16 = new U8[801];
+            memset(filenameutf8, 0, 800);
+            memset(filenameutf16, 0, 801);
+            memcpy(filenameutf8, temp.c_str(), temp.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, (temp.length()*2)-1);
+            //content.write(temp.c_str(), temp.length());
+            content.write("\x00\x0d\x00\x0a\x00\x0d\x00\x0a\x00\x00\x00", 11);
+            pkt.body = content.str();
+
+            sendP2PPacket(conn, pkt, session);
+
+            MediaSession *newMediaSession = new \
MediaSession(*conn.myNotificationServer(), 6891); +            \
conn.myNotificationServer()->addMediaSession(newMediaSession); +        }
+
+        else if (content == "receivedViewerData")
+        {
+
+        }
+
+        data = "";
+
+    }
+
     void P2P::sendP2PData(MSN::SwitchboardServerConnection &conn, p2pSession \
&session, p2pPacket &packet)  {
         p2pPacket pkt_part = session.tempPacket;
@@ -441,6 +662,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 +744,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 +775,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"];
@@ -645,15 +900,16 @@
         {
             case APP_WEBCAM:
             {
-                if(header_app["EUF-GUID"] == \
"{4BD96FC0-AB17-4425-A14A-439185962DC8}") +                if(header_app["EUF-GUID"] \
== "{4BD96FC0-AB17-4425-A14A-439185962DC8}") // Media session  {
-                    \
                //conn.myNotificationServer()->externalCallbacks.askWebCam(&conn, \
                session.sessionID);
-                    std::string body("SessionID: "+ toStr(session.sessionID) \
                +"\r\n");
-                    send_200OK(conn, session, body);
+                    conn.myNotificationServer()->externalCallbacks.askWebCam(&conn, \
session.sessionID);  }
-                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; +
+                    conn.myNotificationServer()->externalCallbacks.askWebCam(&conn, \
session.sessionID); +
                 }
                 break;
             }
@@ -1050,21 +1306,31 @@
         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);
-                return;
+                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)) +                    {
+                        send_603Decline(conn,session);
+                        return;
+                    }
+                }
+                else
+                {
+                    send_603Decline(conn,session);
+                    return;
+                }
+                break;
             }
+
         }
-        else
-        {
-            send_603Decline(conn,session);
-            return;
-        }
+
         sendP2PData(conn, session, packet);
     }
 
@@ -1095,6 +1361,22 @@
         }
     }
 
+    void P2P::handle_webcamResponse(MSN::SwitchboardServerConnection &conn, unsigned \
int sessionID, bool response) +    {
+        p2pSession session = startedSessions[sessionID];
+        if(response) // user accepted
+        {
+            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
+            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	(working copy)
@@ -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
          */
@@ -208,6 +209,8 @@
         /** Request a display picture
          */
         void requestDisplayPicture(unsigned int id, std::string filename, \
std::string msnobject); +
+        void receivedWebcamData(MSN::SwitchboardServerConnection *conn, char* data, \
int len);  protected:
         virtual void handleIncomingData();
         SwitchboardServerState _connectionState;
Index: msn/externals.h
===================================================================
--- msn/externals.h	(revision 120)
+++ msn/externals.h	(working copy)
@@ -308,14 +308,20 @@
          */
         virtual void askFileTransfer(MSN::SwitchboardServerConnection *conn, \
MSN::fileTransferInvite ft) = 0;  
-        virtual int listenOnPort(int port) = 0;
+        virtual void * listenOnPort(int port) = 0;
 
-        virtual std::string getOurIP() = 0;
+        virtual void decodedWebcamFrame(MSN::SwitchboardServerConnection *conn, \
const char *data) = 0;  
+        virtual std::list<std::string> getLocalIPs() = 0;
+
         virtual std::string getSecureHTTPProxy() = 0;
 
         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/notificationserver.cpp
===================================================================
--- msn/notificationserver.cpp	(revision 120)
+++ msn/notificationserver.cpp	(working copy)
@@ -29,6 +29,7 @@
 #include <msn/md5.h>
 #include <msn/util.h>
 #include <msn/soap.h>
+#include <msn/media/session.h>
 #include <algorithm>
 #include <cctype>
 #include <cassert>
@@ -91,7 +92,16 @@
                 return (*d);
         }
 
+        std::vector<MediaSession *> & list3 = _mediaSessions;
+        std::vector<MediaSession *>::iterator j = list3.begin();
 
+        for (; j != list3.end(); j++)
+        {
+            Connection *c = (*j)->connectionWithSocket(sock);
+            if (c)
+                return c;
+        }
+
         return NULL;
     }
     
@@ -130,6 +140,26 @@
         _SoapConnections.push_back(s);
     }
 
+    void NotificationServerConnection::addMediaSession(MediaSession *s)
+    {
+        this->assertConnectionStateIsAtLeast(NS_CONNECTED);
+        _mediaSessions.push_back(s);
+    }
+
+    void NotificationServerConnection::acceptedConnection(void *sock, void *client)
+    {
+        std::vector<MediaSession *> & list3 = _mediaSessions;
+        std::vector<MediaSession *>::iterator j = list3.begin();
+
+        for (; j != list3.end(); j++)
+        {
+            if((*j)->sock == sock )
+            {
+                return (*j)->acceptedConnection(client);
+            }
+        }
+    }
+
     void NotificationServerConnection::removeSwitchboardConnection(SwitchboardServerConnection \
*c)  {
         this->assertConnectionStateIsAtLeast(NS_CONNECTED);        
@@ -1218,7 +1248,15 @@
         {
              delete *d;
         }
+        std::vector<MediaSession *> list3 = _mediaSessions;
+        std::vector<MediaSession *>::iterator j = list3.begin();
 
+        for (; j != list3.end(); ++j)
+        {
+             delete *j;
+        }
+
+
         this->callbacks.clear();
         this->sitesToAuthList.erase(sitesToAuthList.begin(), sitesToAuthList.end());
         SentQueuedOIMs.erase(SentQueuedOIMs.begin(), SentQueuedOIMs.end());
Index: msn/CMakeLists.txt
===================================================================
--- msn/CMakeLists.txt	(revision 120)
+++ msn/CMakeLists.txt	(working copy)
@@ -16,6 +16,8 @@
     msnobject.cpp
     buddy.cpp
     passport.cpp
+    p2p/webcam.cpp
+    media/session.cpp
 )
 
 if(WIN32)
@@ -40,7 +42,10 @@
     soap.h
     p2p.h
     msnobject.h
-    libmsn_export.h)
+    libmsn_export.h
+    p2p/webcam.h
+    media/session.h
+)
 
 set(siren_STAT_SRCS
     libsiren/common.cpp
@@ -61,9 +66,21 @@
     libsiren/rmlt.h
     libsiren/siren7.h
 )
+
+INCLUDE( ${CMAKE_ROOT}/Modules/FindPkgConfig.cmake )
+pkg_search_module(MIMIC REQUIRED libmimic)
+
+INCLUDE_DIRECTORIES(${MIMIC_INCLUDE_DIRS})
+INCLUDE_DIRECTORIES("/usr/include/glib-2.0")
+
 add_library(msn SHARED ${msn_STAT_SRCS} ${siren_STAT_SRCS})
+
+TARGET_LINK_LIBRARIES(msn ${MIMIC_LIBRARIES})
+
+
 set_target_properties(msn PROPERTIES VERSION 0.3.0 
                                      SOVERSION 0.3
+                                     COMPILE_FLAGS "-I/usr/include/glib-2.0 \
-I/usr/include/glib-2.0/include -lmimic -lglib-2.0"  )
 
 if(NOT WIN32)
Index: msn/switchboardserver.cpp
===================================================================
--- msn/switchboardserver.cpp	(revision 120)
+++ msn/switchboardserver.cpp	(working copy)
@@ -688,6 +688,11 @@
         p2p.handle_fileTransferResponse(*this,sessionID, filename, response);
     }
 
+    void SwitchboardServerConnection::webcamResponse(unsigned int sessionID, bool \
response) +    {
+        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	(working copy)
@@ -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: msn/notificationserver.h
===================================================================
--- msn/notificationserver.h	(revision 120)
+++ msn/notificationserver.h	(working copy)
@@ -34,6 +34,7 @@
 #include <msn/externals.h>
 #include <msn/msnobject.h>
 #include <msn/soap.h>
+#include <msn/media/session.h>
 #include <cassert>
 #include <sys/types.h>
 
@@ -181,6 +182,10 @@
          */
         void addSoapConnection(Soap *);
 
+        void addMediaSession(MediaSession *);
+
+        void acceptedConnection(void *sock, void *client);
+
         /* Remove a SwitchboardServerConnection from the list of connections that \
                have
          *  been started from this connection.
          */
@@ -484,6 +489,7 @@
 private:
         std::vector<SwitchboardServerConnection *> _switchboardConnections;
         std::vector<Soap *> _SoapConnections;
+        std::vector<MediaSession *> _mediaSessions;
         std::map<int, std::pair<NotificationServerCallback, void *> > callbacks;
 
         ListSyncInfo *listInfo;
Index: msntest/CMakeLists.txt
===================================================================
--- msntest/CMakeLists.txt	(revision 120)
+++ msntest/CMakeLists.txt	(working copy)
@@ -4,6 +4,11 @@
 
 include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR})
 
+INCLUDE( ${CMAKE_ROOT}/Modules/FindPkgConfig.cmake )
+pkg_search_module(MIMIC REQUIRED libmimic)
+INCLUDE_DIRECTORIES(${MIMIC_INCLUDE_DIRS})
+
+
 add_executable (msntest ${msntest_SRCS} ) 
 target_link_libraries(msntest crypto msn ssl)
 
Index: msntest/msntest.cpp
===================================================================
--- msntest/msntest.cpp	(revision 120)
+++ msntest/msntest.cpp	(working copy)
@@ -23,20 +23,24 @@
  */
 
 #include <errno.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ifaddrs.h>
 #include <sys/types.h>
 #include <sys/socket.h>
-#include <unistd.h>
+#include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/poll.h>
 #include <arpa/inet.h>
-#include <fcntl.h>
+#include <net/if.h>
 #include <netinet/in.h>
-#include <netdb.h>
-#include <stdlib.h>
-#include <string.h>
 #include <openssl/ssl.h>
 
 #include <msn/msn.h>
+
 #include <string>
 #include <iostream>
 
@@ -156,10 +160,14 @@
 
     virtual void askFileTransfer(MSN::SwitchboardServerConnection *conn, \
MSN::fileTransferInvite ft);  
-    virtual int listenOnPort(int port);
+    virtual void askWebCam(MSN::SwitchboardServerConnection *conn, unsigned int \
sessionID); +
+    virtual void * listenOnPort(int port);
     
-    virtual std::string getOurIP();
+    virtual void decodedWebcamFrame(MSN::SwitchboardServerConnection *conn, const \
char *data);  
+    virtual std::list<std::string> getLocalIPs();
+
     virtual int getSocketFileDescriptor (void *sock);
 
     virtual size_t getDataFromSocket (void *sock, char *data, size_t size);
@@ -189,6 +197,9 @@
 
 int main()
 {
+    int optval;
+    socklen_t optlen;
+
     for (int i = 1; i < 20; i++)
     {
         mySockets[i].fd = -1;
@@ -224,6 +235,7 @@
     }
     
     pass = getpass("Enter your password: ");
+
     fprintf(stderr, "Connecting to the MSN Messenger service...\n");
     
     MSN::NotificationServerConnection mainConnection(uname, pass, cb);
@@ -236,8 +248,9 @@
         for (int i = 1; i < 20; i++)
         {
             if (mySockets[i].fd == -1)
-                break; 
-            if (mySockets[i].revents & POLLHUP) 
+                break;
+
+            if (mySockets[i].revents & POLLHUP)
             {
                 mySockets[i].revents = 0; 
                 continue; 
@@ -254,6 +267,21 @@
                 // if this is a libmsn socket
                 if (c != NULL)
                 {
+
+                    if (getsockopt(mySockets[i].fd, SOL_SOCKET, SO_ACCEPTCONN, \
(void*)&optval, &optlen) >= 0) { +                        if (optval == 1)
+                        {
+                            int sock;
+                            struct sockaddr_in addr;
+                            socklen_t len = sizeof(addr);
+
+                            sock = accept(mySockets[i].fd, (struct sockaddr \
*)(&addr), &len); +
+                            \
mainConnection.acceptedConnection((void*)mySockets[i].fd, (void*)sock); +             \
} +                    }
+
+
                     // If we aren't connected yet, a socket event means that
                     // our connection attempt has completed.
                     if(mySocketsSsl[i].isSSL && !mySocketsSsl[i].isConnected)
@@ -301,6 +329,9 @@
                     // If this event is due to the socket becoming writable
                     if (mySockets[i].revents & POLLOUT)
                     {
+                        //std::cout << "socket " << mySockets[i].fd << " is becoming \
writeable!" << std::endl; +                        std::flush(std::cout);
+                        mySockets[i].revents |= POLLOUT;
                         c->socketIsWritable();
                     }
                 }
@@ -477,6 +508,7 @@
         clientid += MSN::InkGifSupport;
         clientid += MSN::SIPInvitations;
         clientid += MSN::SupportMultiPacketMessaging;
+        clientid += MSN::SupportWebcam;
 
         mainConnection.setState(MSN::buddyStatusFromString(state), clientid);
     } else if (!strcmp(command, "friendlyname")) {
@@ -1106,14 +1138,14 @@
     return (void*)s;
 }
 
-int Callbacks::listenOnPort(int port)
+void * Callbacks::listenOnPort(int port)
 {
     int s;
     struct sockaddr_in addr;
     
     if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
     {
-        return -1;
+        return (void*)-1;
     }
     
     memset(&addr, 0, sizeof(addr));
@@ -1123,23 +1155,59 @@
     if (bind(s, (sockaddr *)(&addr), sizeof(addr)) < 0 || listen(s, 1) < 0)
     {
         close(s);
-        return -1;
+        return (void*)-1;
     }
-    
-    return s;
+
+    return (void*)s;
 }
 
-std::string Callbacks::getOurIP(void)
+void Callbacks::decodedWebcamFrame(MSN::SwitchboardServerConnection *conn, const \
char *data)  {
-    struct hostent * hn;
-    char buf2[1024];
-    
-    gethostname(buf2,1024);
-    hn = gethostbyname(buf2);
-    
-    return inet_ntoa( *((struct in_addr*)hn->h_addr));
+    static int i = 0;
+    i++;
+
+    int status;
+    status = mkdir("/tmp/webcamdata", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+
+    std::stringstream filename;
+    filename << "/tmp/webcamdata/frame";
+    filename << i;
+
+    std::ofstream webcamFile(filename.str().c_str(), std::ios::out);
+    webcamFile << data;
 }
 
+std::list<std::string> Callbacks::getLocalIPs()
+{
+    std::list<std::string> ips = std::list<std::string>();
+
+    struct ifaddrs *ifaddr, *ifa;
+    char host[NI_MAXHOST];
+
+    if (getifaddrs(&ifaddr) == -1) {
+       exit(EXIT_FAILURE);
+    }
+
+    for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+       if ( ifa->ifa_addr->sa_family == AF_INET &&
+            ifa->ifa_flags & IFF_UP &&
+            ifa->ifa_flags & IFF_RUNNING &&
+            ifa->ifa_flags | IFF_LOOPBACK) {
+
+          if (getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, \
NI_MAXHOST, NULL, 0, NI_NUMERICHOST) != 0) { +              continue;
+          }
+          if (strcmp(host, "127.0.0.1")) {
+             ips.push_back(host);
+          }
+       }
+    }
+
+    freeifaddrs(ifaddr);
+    return ips;
+
+}
+
 void Callbacks::log(int i, const char *s)
 {
     
@@ -1180,6 +1248,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... accept it" << \
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


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic