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 <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?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."
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