[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