From 220b6befab0c63303381524d7d23df0835b2ac8d Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 14 May 2015 13:51:21 -0700 Subject: [PATCH 01/66] Used a thread to send clipboard data #4601 --- src/lib/server/ClientProxy1_0.cpp | 2 +- src/lib/server/ClientProxy1_0.h | 9 ++++++--- src/lib/server/ClientProxy1_5.cpp | 28 ++++++++++++++++++++++++++++ src/lib/server/ClientProxy1_5.h | 8 ++++++++ src/lib/synergy/protocol_types.h | 2 +- 5 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/lib/server/ClientProxy1_0.cpp b/src/lib/server/ClientProxy1_0.cpp index 9cca86d6..6cc8057c 100644 --- a/src/lib/server/ClientProxy1_0.cpp +++ b/src/lib/server/ClientProxy1_0.cpp @@ -282,6 +282,7 @@ ClientProxy1_0::setClipboard(ClipboardID id, const IClipboard* clipboard) Clipboard::copy(&m_clipboard[id].m_clipboard, clipboard); String data = m_clipboard[id].m_clipboard.marshall(); + LOG((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getName().c_str(), data.size())); ProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, &data); } @@ -505,7 +506,6 @@ ClientProxy1_0::recvGrabClipboard() return true; } - // // ClientProxy1_0::ClientClipboard // diff --git a/src/lib/server/ClientProxy1_0.h b/src/lib/server/ClientProxy1_0.h index 57f4a1fe..d8dc48f6 100644 --- a/src/lib/server/ClientProxy1_0.h +++ b/src/lib/server/ClientProxy1_0.h @@ -84,8 +84,7 @@ private: bool recvClipboard(); bool recvGrabClipboard(); -private: - typedef bool (ClientProxy1_0::*MessageParser)(const UInt8*); +protected: struct ClientClipboard { public: ClientClipboard(); @@ -96,8 +95,12 @@ private: bool m_dirty; }; - ClientInfo m_info; ClientClipboard m_clipboard[kClipboardEnd]; + +private: + typedef bool (ClientProxy1_0::*MessageParser)(const UInt8*); + + ClientInfo m_info; double m_heartbeatAlarm; EventQueueTimer* m_heartbeatTimer; MessageParser m_parser; diff --git a/src/lib/server/ClientProxy1_5.cpp b/src/lib/server/ClientProxy1_5.cpp index 43016db3..c011c588 100644 --- a/src/lib/server/ClientProxy1_5.cpp +++ b/src/lib/server/ClientProxy1_5.cpp @@ -19,7 +19,9 @@ #include "server/Server.h" #include "synergy/ProtocolUtil.h" +#include "mt/Thread.h" #include "io/IStream.h" +#include "base/TMethodJob.h" #include "base/Log.h" // @@ -71,6 +73,24 @@ ClientProxy1_5::fileChunkSending(UInt8 mark, char* data, size_t dataSize) ProtocolUtil::writef(getStream(), kMsgDFileTransfer, mark, &chunk); } +void +ClientProxy1_5::setClipboard(ClipboardID id, const IClipboard* clipboard) +{ + // ignore if this clipboard is already clean + if (m_clipboard[id].m_dirty) { + // this clipboard is now clean + m_clipboard[id].m_dirty = false; + Clipboard::copy(&m_clipboard[id].m_clipboard, clipboard); + + m_clipboardData = m_clipboard[id].m_clipboard.marshall(); + + m_sendFileThread = new Thread( + new TMethodJob( + this, &ClientProxy1_5::sendClipboardThread, + reinterpret_cast(id))); + } +} + bool ClientProxy1_5::parseMessage(const UInt8* code) { @@ -148,3 +168,11 @@ ClientProxy1_5::dragInfoReceived() m_server->dragInfoReceived(fileNum, content); } + +void +ClientProxy1_5::sendClipboardThread(void* data) +{ + ClipboardID id = reinterpret_cast(data);\ + LOG((CLOG_DEBUG "sending clipboard %d to \"%s\" size=%d", id, getName().c_str(), m_clipboardData.size())); + ProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, &m_clipboardData); +} diff --git a/src/lib/server/ClientProxy1_5.h b/src/lib/server/ClientProxy1_5.h index d3016eed..03ddbac6 100644 --- a/src/lib/server/ClientProxy1_5.h +++ b/src/lib/server/ClientProxy1_5.h @@ -22,6 +22,7 @@ class Server; class IEventQueue; +class Thread; //! Proxy for client implementing protocol version 1.5 class ClientProxy1_5 : public ClientProxy1_4 { @@ -31,10 +32,15 @@ public: virtual void sendDragInfo(UInt32 fileCount, const char* info, size_t size); virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize); + virtual void setClipboard(ClipboardID id, const IClipboard* clipboard); virtual bool parseMessage(const UInt8* code); void fileChunkReceived(); void dragInfoReceived(); +private: + // thread funciton for sending clipboard + void sendClipboardThread(void*); + private: IEventQueue* m_events; @@ -42,4 +48,6 @@ private: double m_elapsedTime; size_t m_receivedDataSize; static const UInt16 m_intervalThreshold; + Thread* m_sendFileThread; + String m_clipboardData; }; diff --git a/src/lib/synergy/protocol_types.h b/src/lib/synergy/protocol_types.h index 69f18f11..4391605c 100644 --- a/src/lib/synergy/protocol_types.h +++ b/src/lib/synergy/protocol_types.h @@ -27,7 +27,7 @@ // 1.3: adds keep alive and deprecates heartbeats, // adds horizontal mouse scrolling // 1.4: adds crypto support -// 1.5: adds file transfer and removes home brew crypto +// 1.5: adds file/clipboard transfer and removes home brew crypto // NOTE: with new version, synergy minor version should increment static const SInt16 kProtocolMajorVersion = 1; static const SInt16 kProtocolMinorVersion = 5; From 08aee6cba7cf8f2151849cd9667af36e82317cca Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 14 May 2015 14:39:03 -0700 Subject: [PATCH 02/66] Fixed cast error from pointer to small type on Unix #4601 --- src/lib/server/ClientProxy1_5.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/server/ClientProxy1_5.cpp b/src/lib/server/ClientProxy1_5.cpp index c011c588..34c30e83 100644 --- a/src/lib/server/ClientProxy1_5.cpp +++ b/src/lib/server/ClientProxy1_5.cpp @@ -172,7 +172,7 @@ ClientProxy1_5::dragInfoReceived() void ClientProxy1_5::sendClipboardThread(void* data) { - ClipboardID id = reinterpret_cast(data);\ + size_t id = reinterpret_cast(data); LOG((CLOG_DEBUG "sending clipboard %d to \"%s\" size=%d", id, getName().c_str(), m_clipboardData.size())); ProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, &m_clipboardData); } From 44089d55e83b6fcaea196b1eb83635f2de86865d Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 15 May 2015 14:26:57 -0700 Subject: [PATCH 03/66] Send clipboard data in small chunks without using thread #4601 --- src/lib/client/ServerProxy.cpp | 43 +++++---- src/lib/server/ClientProxy1_5.cpp | 110 ++++++++++++++++++++--- src/lib/server/ClientProxy1_5.h | 4 +- src/lib/synergy/FileChunker.cpp | 6 +- src/lib/synergy/protocol_types.cpp | 2 +- src/lib/synergy/protocol_types.h | 11 ++- src/test/integtests/net/NetworkTests.cpp | 6 +- 7 files changed, 137 insertions(+), 45 deletions(-) diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp index 914dd4ee..c50e44d9 100644 --- a/src/lib/client/ServerProxy.cpp +++ b/src/lib/client/ServerProxy.cpp @@ -545,21 +545,32 @@ void ServerProxy::setClipboard() { // parse + static String dataCached; ClipboardID id; UInt32 seqNum; + size_t mark = 0; String data; - ProtocolUtil::readf(m_stream, kMsgDClipboard + 4, &id, &seqNum, &data); - LOG((CLOG_DEBUG "recv clipboard %d size=%d", id, data.size())); + ProtocolUtil::readf(m_stream, kMsgDClipboard + 4, &id, &seqNum, &mark, &data); - // validate - if (id >= kClipboardEnd) { - return; + if (mark == kDataStart) { + //TODO: validate size + LOG((CLOG_DEBUG "start receiving clipboard data")); + dataCached.clear(); + } + else if (mark == kDataChunk) { + dataCached.append(data); + } + else if (mark == kDataEnd) { + LOG((CLOG_DEBUG "received clipboard %d size=%d", id, dataCached.size())); + // validate + if (id >= kClipboardEnd) { + return; + } + // forward + Clipboard clipboard; + clipboard.unmarshall(dataCached, 0); + m_client->setClipboard(id, &clipboard); } - - // forward - Clipboard clipboard; - clipboard.unmarshall(data, 0); - m_client->setClipboard(id, &clipboard); } void @@ -857,7 +868,7 @@ ServerProxy::fileChunkReceived() ProtocolUtil::readf(m_stream, kMsgDFileTransfer + 4, &mark, &content); switch (mark) { - case kFileStart: + case kDataStart: m_client->clearReceivedFileData(); m_client->setExpectedFileSize(content); if (CLOG->getFilter() >= kDEBUG2) { @@ -866,7 +877,7 @@ ServerProxy::fileChunkReceived() } break; - case kFileChunk: + case kDataChunk: m_client->fileChunkReceived(content); if (CLOG->getFilter() >= kDEBUG2) { LOG((CLOG_DEBUG2 "recv file data from server: size=%i", content.size())); @@ -884,7 +895,7 @@ ServerProxy::fileChunkReceived() } break; - case kFileEnd: + case kDataEnd: m_events->addEvent(Event(m_events->forIScreen().fileRecieveCompleted(), m_client)); if (CLOG->getFilter() >= kDEBUG2) { LOG((CLOG_DEBUG2 "file data transfer finished")); @@ -915,15 +926,15 @@ ServerProxy::fileChunkSending(UInt8 mark, char* data, size_t dataSize) String chunk(data, dataSize); switch (mark) { - case kFileStart: + case kDataStart: LOG((CLOG_DEBUG2 "file sending start: size=%s", data)); break; - case kFileChunk: + case kDataChunk: LOG((CLOG_DEBUG2 "file chunk sending: size=%i", chunk.size())); break; - case kFileEnd: + case kDataEnd: LOG((CLOG_DEBUG2 "file sending finished")); break; } diff --git a/src/lib/server/ClientProxy1_5.cpp b/src/lib/server/ClientProxy1_5.cpp index 34c30e83..a0d082eb 100644 --- a/src/lib/server/ClientProxy1_5.cpp +++ b/src/lib/server/ClientProxy1_5.cpp @@ -18,12 +18,15 @@ #include "server/ClientProxy1_5.h" #include "server/Server.h" +#include "synergy/FileChunker.h" #include "synergy/ProtocolUtil.h" #include "mt/Thread.h" #include "io/IStream.h" #include "base/TMethodJob.h" #include "base/Log.h" +#include + // // ClientProxy1_5 // @@ -57,15 +60,15 @@ ClientProxy1_5::fileChunkSending(UInt8 mark, char* data, size_t dataSize) String chunk(data, dataSize); switch (mark) { - case kFileStart: + case kDataStart: LOG((CLOG_DEBUG2 "file sending start: size=%s", data)); break; - case kFileChunk: + case kDataChunk: LOG((CLOG_DEBUG2 "file chunk sending: size=%i", chunk.size())); break; - case kFileEnd: + case kDataEnd: LOG((CLOG_DEBUG2 "file sending finished")); break; } @@ -82,12 +85,50 @@ ClientProxy1_5::setClipboard(ClipboardID id, const IClipboard* clipboard) m_clipboard[id].m_dirty = false; Clipboard::copy(&m_clipboard[id].m_clipboard, clipboard); - m_clipboardData = m_clipboard[id].m_clipboard.marshall(); + String data = m_clipboard[id].m_clipboard.marshall(); - m_sendFileThread = new Thread( - new TMethodJob( - this, &ClientProxy1_5::sendClipboardThread, - reinterpret_cast(id))); + size_t size = data.size(); + LOG((CLOG_DEBUG "sending clipboard %d to \"%s\" size=%d", id, getName().c_str(), size)); + + //TODO: refactor FileChunker and use thread + // send first message (file size) + std::stringstream ss; + ss << size; + String dataSize = ss.str(); + ProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, kDataStart, &dataSize); + + // send chunk messages with a fixed chunk size + size_t sentLength = 0; + size_t chunkSize = 2048; + Stopwatch stopwatch; + stopwatch.start(); + while (true) { + if (stopwatch.getTime() > 0.1f) { + // make sure we don't read too much from the mock data. + if (sentLength + chunkSize > size) { + chunkSize = size - sentLength; + } + + String chunk(data.substr(sentLength, chunkSize).c_str(), chunkSize); + ProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, kDataChunk, &chunk); + + sentLength += chunkSize; + + if (sentLength == size) { + break; + } + + stopwatch.reset(); + } + } + + // send last message + ProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, kDataEnd, "\0"); + +// m_sendFileThread = new Thread( +// new TMethodJob( +// this, &ClientProxy1_5::sendClipboardThread, +// reinterpret_cast(id))); } } @@ -117,7 +158,7 @@ ClientProxy1_5::fileChunkReceived() Server* server = getServer(); switch (mark) { - case kFileStart: + case kDataStart: server->clearReceivedFileData(); server->setExpectedFileSize(content); if (CLOG->getFilter() >= kDEBUG2) { @@ -126,7 +167,7 @@ ClientProxy1_5::fileChunkReceived() } break; - case kFileChunk: + case kDataChunk: server->fileChunkReceived(content); if (CLOG->getFilter() >= kDEBUG2) { LOG((CLOG_DEBUG2 "recv file data from client: chunck size=%i", content.size())); @@ -144,7 +185,7 @@ ClientProxy1_5::fileChunkReceived() } break; - case kFileEnd: + case kDataEnd: m_events->addEvent(Event(m_events->forIScreen().fileRecieveCompleted(), server)); if (CLOG->getFilter() >= kDEBUG2) { LOG((CLOG_DEBUG2 "file data transfer finished")); @@ -172,7 +213,48 @@ ClientProxy1_5::dragInfoReceived() void ClientProxy1_5::sendClipboardThread(void* data) { - size_t id = reinterpret_cast(data); - LOG((CLOG_DEBUG "sending clipboard %d to \"%s\" size=%d", id, getName().c_str(), m_clipboardData.size())); - ProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, &m_clipboardData); + //size_t id = reinterpret_cast(data); + //String clipboardData = m_clipboardData.at(id); + //int size = clipboardData.size(); + //LOG((CLOG_DEBUG "sending clipboard %d to \"%s\" size=%d", id, getName().c_str(), size)); + + ////TODO: refactor FileChunker + //// send first message (file size) + //std::stringstream ss; + //ss << size; + //String dataSize = ss.str(); + //ProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, kDataStart, &dataSize); + + //// send chunk messages with a fixed chunk size + //size_t sentLength = 0; + //size_t chunkSize = 2048; + //Stopwatch stopwatch; + //stopwatch.start(); + //while (true) { + // if (stopwatch.getTime() > 0.1f) { + // // make sure we don't read too much from the mock data. + // if (sentLength + chunkSize > size) { + // chunkSize = size - sentLength; + // } + + // char* chunk = new char[chunkSize]; + // memcpy(chunk, clipboardData.substr(sentLength, chunkSize).c_str(), chunkSize); + // //String chunk(clipboardData.substr(sentLength, chunkSize).c_str(), chunkSize); + // //int sizetest = chunk.size(); + // //sizetest++; + // //sizetest--; + // ProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, kDataChunk, chunk); + + // sentLength += chunkSize; + + // if (sentLength == size) { + // break; + // } + + // stopwatch.reset(); + // } + //} + + //// send last message + //ProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, kDataEnd, "\0"); } diff --git a/src/lib/server/ClientProxy1_5.h b/src/lib/server/ClientProxy1_5.h index 03ddbac6..8301d3be 100644 --- a/src/lib/server/ClientProxy1_5.h +++ b/src/lib/server/ClientProxy1_5.h @@ -19,6 +19,7 @@ #include "server/ClientProxy1_4.h" #include "base/Stopwatch.h" +#include "common/stdvector.h" class Server; class IEventQueue; @@ -48,6 +49,5 @@ private: double m_elapsedTime; size_t m_receivedDataSize; static const UInt16 m_intervalThreshold; - Thread* m_sendFileThread; - String m_clipboardData; + //Thread* m_sendFileThread; }; diff --git a/src/lib/synergy/FileChunker.cpp b/src/lib/synergy/FileChunker.cpp index 7936ecc6..d0289eca 100644 --- a/src/lib/synergy/FileChunker.cpp +++ b/src/lib/synergy/FileChunker.cpp @@ -54,7 +54,7 @@ FileChunker::sendFileChunks(char* filename, IEventQueue* events, void* eventTarg FileChunk* sizeMessage = new FileChunk(sizeLength + 2); char* chunkData = sizeMessage->m_chunk; - chunkData[0] = kFileStart; + chunkData[0] = kDataStart; memcpy(&chunkData[1], fileSize.c_str(), sizeLength); chunkData[sizeLength + 1] = '\0'; events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, sizeMessage)); @@ -76,7 +76,7 @@ FileChunker::sendFileChunks(char* filename, IEventQueue* events, void* eventTarg FileChunk* fileChunk = new FileChunk(chunkSize + 2); char* chunkData = fileChunk->m_chunk; - chunkData[0] = kFileChunk; + chunkData[0] = kDataChunk; file.read(&chunkData[1], chunkSize); chunkData[chunkSize + 1] = '\0'; events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, fileChunk)); @@ -96,7 +96,7 @@ FileChunker::sendFileChunks(char* filename, IEventQueue* events, void* eventTarg FileChunk* transferFinished = new FileChunk(2); chunkData = transferFinished->m_chunk; - chunkData[0] = kFileEnd; + chunkData[0] = kDataEnd; chunkData[1] = '\0'; events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, transferFinished)); diff --git a/src/lib/synergy/protocol_types.cpp b/src/lib/synergy/protocol_types.cpp index 9f2eef9d..1e274b06 100644 --- a/src/lib/synergy/protocol_types.cpp +++ b/src/lib/synergy/protocol_types.cpp @@ -41,7 +41,7 @@ const char* kMsgDMouseMove = "DMMV%2i%2i"; const char* kMsgDMouseRelMove = "DMRM%2i%2i"; const char* kMsgDMouseWheel = "DMWM%2i%2i"; const char* kMsgDMouseWheel1_0 = "DMWM%2i"; -const char* kMsgDClipboard = "DCLP%1i%4i%s"; +const char* kMsgDClipboard = "DCLP%1i%4i%1i%s"; const char* kMsgDInfo = "DINF%2i%2i%2i%2i%2i%2i%2i"; const char* kMsgDSetOptions = "DSOP%4I"; const char* kMsgDFileTransfer = "DFTR%1i%s"; diff --git a/src/lib/synergy/protocol_types.h b/src/lib/synergy/protocol_types.h index 4391605c..956f4f49 100644 --- a/src/lib/synergy/protocol_types.h +++ b/src/lib/synergy/protocol_types.h @@ -70,13 +70,12 @@ enum EDirectionMask { }; // file transfer constants -enum EFileTransfer { - kFileStart = 1, - kFileChunk = 2, - kFileEnd = 3 +enum EDataTransfer { + kDataStart = 1, + kDataChunk = 2, + kDataEnd = 3 }; - // // message codes (trailing NUL is not part of code). in comments, $n // refers to the n'th argument (counting from one). message codes are @@ -225,7 +224,7 @@ extern const char* kMsgDMouseWheel; extern const char* kMsgDMouseWheel1_0; // clipboard data: primary <-> secondary -// $2 = sequence number, $3 = clipboard data. the sequence number +// $2 = sequence number, $3 = mark $4 = clipboard data. the sequence number // is 0 when sent by the primary. secondary screens should use the // sequence number from the most recent kMsgCEnter. $1 = clipboard // identifier. diff --git a/src/test/integtests/net/NetworkTests.cpp b/src/test/integtests/net/NetworkTests.cpp index 94d9cac0..fb358bde 100644 --- a/src/test/integtests/net/NetworkTests.cpp +++ b/src/test/integtests/net/NetworkTests.cpp @@ -420,7 +420,7 @@ NetworkTests::sendMockData(void* eventTarget) FileChunker::FileChunk* sizeMessage = new FileChunker::FileChunk(sizeLength + 2); char* chunkData = sizeMessage->m_chunk; - chunkData[0] = kFileStart; + chunkData[0] = kDataStart; memcpy(&chunkData[1], size.c_str(), sizeLength); chunkData[sizeLength + 1] = '\0'; m_events.addEvent(Event(m_events.forIScreen().fileChunkSending(), eventTarget, sizeMessage)); @@ -440,7 +440,7 @@ NetworkTests::sendMockData(void* eventTarget) FileChunker::FileChunk* fileChunk = new FileChunker::FileChunk(chunkSize + 2); char* chunkData = fileChunk->m_chunk; - chunkData[0] = kFileChunk; + chunkData[0] = kDataChunk; memcpy(&chunkData[1], &m_mockData[sentLength], chunkSize); chunkData[chunkSize + 1] = '\0'; m_events.addEvent(Event(m_events.forIScreen().fileChunkSending(), eventTarget, fileChunk)); @@ -458,7 +458,7 @@ NetworkTests::sendMockData(void* eventTarget) FileChunker::FileChunk* transferFinished = new FileChunker::FileChunk(2); chunkData = transferFinished->m_chunk; - chunkData[0] = kFileEnd; + chunkData[0] = kDataEnd; chunkData[1] = '\0'; m_events.addEvent(Event(m_events.forIScreen().fileChunkSending(), eventTarget, transferFinished)); } From 4c36c08099e4169650df01777ec5b2010891d9e0 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 15 May 2015 16:05:20 -0700 Subject: [PATCH 04/66] Send clipboard data in a thread #4601 --- src/lib/server/ClientProxy1_5.cpp | 56 ------------------------------- src/lib/server/ClientProxy1_5.h | 6 ---- src/lib/server/Server.cpp | 18 +++++++--- src/lib/server/Server.h | 5 +++ 4 files changed, 19 insertions(+), 66 deletions(-) diff --git a/src/lib/server/ClientProxy1_5.cpp b/src/lib/server/ClientProxy1_5.cpp index a0d082eb..5c01b5d5 100644 --- a/src/lib/server/ClientProxy1_5.cpp +++ b/src/lib/server/ClientProxy1_5.cpp @@ -20,9 +20,7 @@ #include "server/Server.h" #include "synergy/FileChunker.h" #include "synergy/ProtocolUtil.h" -#include "mt/Thread.h" #include "io/IStream.h" -#include "base/TMethodJob.h" #include "base/Log.h" #include @@ -124,11 +122,6 @@ ClientProxy1_5::setClipboard(ClipboardID id, const IClipboard* clipboard) // send last message ProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, kDataEnd, "\0"); - -// m_sendFileThread = new Thread( -// new TMethodJob( -// this, &ClientProxy1_5::sendClipboardThread, -// reinterpret_cast(id))); } } @@ -209,52 +202,3 @@ ClientProxy1_5::dragInfoReceived() m_server->dragInfoReceived(fileNum, content); } - -void -ClientProxy1_5::sendClipboardThread(void* data) -{ - //size_t id = reinterpret_cast(data); - //String clipboardData = m_clipboardData.at(id); - //int size = clipboardData.size(); - //LOG((CLOG_DEBUG "sending clipboard %d to \"%s\" size=%d", id, getName().c_str(), size)); - - ////TODO: refactor FileChunker - //// send first message (file size) - //std::stringstream ss; - //ss << size; - //String dataSize = ss.str(); - //ProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, kDataStart, &dataSize); - - //// send chunk messages with a fixed chunk size - //size_t sentLength = 0; - //size_t chunkSize = 2048; - //Stopwatch stopwatch; - //stopwatch.start(); - //while (true) { - // if (stopwatch.getTime() > 0.1f) { - // // make sure we don't read too much from the mock data. - // if (sentLength + chunkSize > size) { - // chunkSize = size - sentLength; - // } - - // char* chunk = new char[chunkSize]; - // memcpy(chunk, clipboardData.substr(sentLength, chunkSize).c_str(), chunkSize); - // //String chunk(clipboardData.substr(sentLength, chunkSize).c_str(), chunkSize); - // //int sizetest = chunk.size(); - // //sizetest++; - // //sizetest--; - // ProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, kDataChunk, chunk); - - // sentLength += chunkSize; - - // if (sentLength == size) { - // break; - // } - - // stopwatch.reset(); - // } - //} - - //// send last message - //ProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, kDataEnd, "\0"); -} diff --git a/src/lib/server/ClientProxy1_5.h b/src/lib/server/ClientProxy1_5.h index 8301d3be..3705b85c 100644 --- a/src/lib/server/ClientProxy1_5.h +++ b/src/lib/server/ClientProxy1_5.h @@ -23,7 +23,6 @@ class Server; class IEventQueue; -class Thread; //! Proxy for client implementing protocol version 1.5 class ClientProxy1_5 : public ClientProxy1_4 { @@ -38,10 +37,6 @@ public: void fileChunkReceived(); void dragInfoReceived(); -private: - // thread funciton for sending clipboard - void sendClipboardThread(void*); - private: IEventQueue* m_events; @@ -49,5 +44,4 @@ private: double m_elapsedTime; size_t m_receivedDataSize; static const UInt16 m_intervalThreshold; - //Thread* m_sendFileThread; }; diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 0bea6aa9..999a97b4 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -91,7 +91,8 @@ Server::Server( m_ignoreFileTransfer(false), m_enableDragDrop(enableDragDrop), m_getDragInfoThread(NULL), - m_waitDragInfoThread(true) + m_waitDragInfoThread(true), + m_dataTransmissionThread(NULL) { // must have a primary client and it must have a canonical name assert(m_primaryClient != NULL); @@ -505,9 +506,10 @@ Server::switchScreen(BaseClientProxy* dst, forScreensaver); // send the clipboard data to new active screen - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - m_active->setClipboard(id, &m_clipboards[id].m_clipboard); - } + m_dataTransmissionThread = new Thread( + new TMethodJob( + this, &Server::clipboardTransmissionThread, + NULL)); Server::SwitchToScreenInfo* info = Server::SwitchToScreenInfo::alloc(m_active->getName()); @@ -1849,6 +1851,14 @@ Server::sendDragInfo(BaseClientProxy* newScreen) } } +void +Server::clipboardTransmissionThread(void*) +{ + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_active->setClipboard(id, &m_clipboards[id].m_clipboard); + } +} + void Server::onMouseMoveSecondary(SInt32 dx, SInt32 dy) { diff --git a/src/lib/server/Server.h b/src/lib/server/Server.h index 742a0a9c..8cc700b4 100644 --- a/src/lib/server/Server.h +++ b/src/lib/server/Server.h @@ -370,6 +370,9 @@ private: // send drag info to new client screen void sendDragInfo(BaseClientProxy* newScreen); + // thread funciton for sending clipboard + void clipboardTransmissionThread(void*); + public: bool m_mock; @@ -480,4 +483,6 @@ private: bool m_waitDragInfoThread; ClientListener* m_clientListener; + + Thread* m_dataTransmissionThread; }; From 30f96b9fbb76f30bd89aa5b871bc30cc20989afe Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 18 May 2015 10:17:22 -0700 Subject: [PATCH 05/66] Renamed FileChunker to StreamChunker #4601 --- src/lib/client/Client.cpp | 10 +++++----- src/lib/server/ClientProxy1_5.cpp | 2 +- src/lib/server/Server.cpp | 8 ++++---- .../{FileChunker.cpp => StreamChunker.cpp} | 15 ++++++++------- .../synergy/{FileChunker.h => StreamChunker.h} | 12 ++++++------ src/test/integtests/net/NetworkTests.cpp | 8 ++++---- 6 files changed, 28 insertions(+), 27 deletions(-) rename src/lib/synergy/{FileChunker.cpp => StreamChunker.cpp} (87%) rename src/lib/synergy/{FileChunker.h => StreamChunker.h} (83%) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 849dd102..944c06aa 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -27,7 +27,7 @@ #include "synergy/ProtocolUtil.h" #include "synergy/protocol_types.h" #include "synergy/XSynergy.h" -#include "synergy/FileChunker.h" +#include "synergy/StreamChunker.h" #include "synergy/IPlatformScreen.h" #include "mt/Thread.h" #include "net/TCPSocket.h" @@ -422,12 +422,12 @@ Client::sendConnectionFailedEvent(const char* msg) void Client::sendFileChunk(const void* data) { - FileChunker::FileChunk* fileChunk = reinterpret_cast(const_cast(data)); - LOG((CLOG_DEBUG1 "sendFileChunk")); + StreamChunker::Chunk* chunk = reinterpret_cast(const_cast(data)); + LOG((CLOG_DEBUG1 "send file chunk")); assert(m_server != NULL); // relay - m_server->fileChunkSending(fileChunk->m_chunk[0], &(fileChunk->m_chunk[1]), fileChunk->m_dataSize); + m_server->fileChunkSending(chunk->m_chunk[0], &(chunk->m_chunk[1]), chunk->m_size); } void @@ -821,7 +821,7 @@ Client::sendFileThread(void* filename) { try { char* name = reinterpret_cast(filename); - FileChunker::sendFileChunks(name, m_events, this); + StreamChunker::sendFileChunks(name, m_events, this); } catch (std::runtime_error error) { LOG((CLOG_ERR "failed sending file chunks: %s", error.what())); diff --git a/src/lib/server/ClientProxy1_5.cpp b/src/lib/server/ClientProxy1_5.cpp index 5c01b5d5..4135375b 100644 --- a/src/lib/server/ClientProxy1_5.cpp +++ b/src/lib/server/ClientProxy1_5.cpp @@ -18,7 +18,7 @@ #include "server/ClientProxy1_5.h" #include "server/Server.h" -#include "synergy/FileChunker.h" +#include "synergy/StreamChunker.h" #include "synergy/ProtocolUtil.h" #include "io/IStream.h" #include "base/Log.h" diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 999a97b4..c3c98f4b 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -2033,13 +2033,13 @@ Server::onMouseWheel(SInt32 xDelta, SInt32 yDelta) void Server::onFileChunkSending(const void* data) { - FileChunker::FileChunk* fileChunk = reinterpret_cast(const_cast(data)); + StreamChunker::Chunk* chunk = reinterpret_cast(const_cast(data)); - LOG((CLOG_DEBUG1 "onFileChunkSending")); + LOG((CLOG_DEBUG1 "sending file chunk")); assert(m_active != NULL); // relay - m_active->fileChunkSending(fileChunk->m_chunk[0], &(fileChunk->m_chunk[1]), fileChunk->m_dataSize); + m_active->fileChunkSending(chunk->m_chunk[0], &(chunk->m_chunk[1]), chunk->m_size); } void @@ -2376,7 +2376,7 @@ Server::sendFileThread(void* data) try { char* filename = reinterpret_cast(data); LOG((CLOG_DEBUG "sending file to client, filename=%s", filename)); - FileChunker::sendFileChunks(filename, m_events, this); + StreamChunker::sendFileChunks(filename, m_events, this); } catch (std::runtime_error error) { LOG((CLOG_ERR "failed sending file chunks, error: %s", error.what())); diff --git a/src/lib/synergy/FileChunker.cpp b/src/lib/synergy/StreamChunker.cpp similarity index 87% rename from src/lib/synergy/FileChunker.cpp rename to src/lib/synergy/StreamChunker.cpp index d0289eca..ab105053 100644 --- a/src/lib/synergy/FileChunker.cpp +++ b/src/lib/synergy/StreamChunker.cpp @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -#include "synergy/FileChunker.h" +#include "synergy/StreamChunker.h" #include "synergy/protocol_types.h" #include "base/EventTypes.h" @@ -33,10 +33,10 @@ using namespace std; -const size_t FileChunker::m_chunkSize = 512 * 1024; // 512kb +const size_t StreamChunker::m_chunkSize = 512 * 1024; // 512kb void -FileChunker::sendFileChunks(char* filename, IEventQueue* events, void* eventTarget) +StreamChunker::sendFileChunks(char* filename, IEventQueue* events, void* eventTarget) { std::fstream file(reinterpret_cast(filename), std::ios::in | std::ios::binary); @@ -51,7 +51,7 @@ FileChunker::sendFileChunks(char* filename, IEventQueue* events, void* eventTarg // send first message (file size) String fileSize = intToString(size); size_t sizeLength = fileSize.size(); - FileChunk* sizeMessage = new FileChunk(sizeLength + 2); + Chunk* sizeMessage = new Chunk(sizeLength + 2); char* chunkData = sizeMessage->m_chunk; chunkData[0] = kDataStart; @@ -73,7 +73,7 @@ FileChunker::sendFileChunks(char* filename, IEventQueue* events, void* eventTarg } // for fileChunk->m_chunk, the first byte is the chunk mark, last is \0 - FileChunk* fileChunk = new FileChunk(chunkSize + 2); + Chunk* fileChunk = new Chunk(chunkSize + 2); char* chunkData = fileChunk->m_chunk; chunkData[0] = kDataChunk; @@ -93,7 +93,7 @@ FileChunker::sendFileChunks(char* filename, IEventQueue* events, void* eventTarg } // send last message - FileChunk* transferFinished = new FileChunk(2); + Chunk* transferFinished = new Chunk(2); chunkData = transferFinished->m_chunk; chunkData[0] = kDataEnd; @@ -104,8 +104,9 @@ FileChunker::sendFileChunks(char* filename, IEventQueue* events, void* eventTarg } String -FileChunker::intToString(size_t i) +StreamChunker::intToString(size_t i) { + //TODO: this should be in string stringstream ss; ss << i; return ss.str(); diff --git a/src/lib/synergy/FileChunker.h b/src/lib/synergy/StreamChunker.h similarity index 83% rename from src/lib/synergy/FileChunker.h rename to src/lib/synergy/StreamChunker.h index 38db8166..0560347f 100644 --- a/src/lib/synergy/FileChunker.h +++ b/src/lib/synergy/StreamChunker.h @@ -21,20 +21,20 @@ class IEventQueue; -class FileChunker { +class StreamChunker { public: //! FileChunk data - class FileChunk { + class Chunk { public: - FileChunk(size_t chunkSize) : m_dataSize(chunkSize - 2) + Chunk(size_t size) : m_size(size - 2) { - m_chunk = new char[chunkSize]; + m_chunk = new char[size]; } - ~FileChunk() { delete[] m_chunk; } + ~Chunk() { delete[] m_chunk; } public: - const size_t m_dataSize; + const size_t m_size; char* m_chunk; }; diff --git a/src/test/integtests/net/NetworkTests.cpp b/src/test/integtests/net/NetworkTests.cpp index fb358bde..21097163 100644 --- a/src/test/integtests/net/NetworkTests.cpp +++ b/src/test/integtests/net/NetworkTests.cpp @@ -28,7 +28,7 @@ #include "server/Server.h" #include "server/ClientListener.h" #include "client/Client.h" -#include "synergy/FileChunker.h" +#include "synergy/StreamChunker.h" #include "net/SocketMultiplexer.h" #include "net/NetworkAddress.h" #include "net/TCPSocketFactory.h" @@ -417,7 +417,7 @@ NetworkTests::sendMockData(void* eventTarget) // send first message (file size) String size = intToString(kMockDataSize); size_t sizeLength = size.size(); - FileChunker::FileChunk* sizeMessage = new FileChunker::FileChunk(sizeLength + 2); + StreamChunker::Chunk* sizeMessage = new StreamChunker::Chunk(sizeLength + 2); char* chunkData = sizeMessage->m_chunk; chunkData[0] = kDataStart; @@ -437,7 +437,7 @@ NetworkTests::sendMockData(void* eventTarget) } // first byte is the chunk mark, last is \0 - FileChunker::FileChunk* fileChunk = new FileChunker::FileChunk(chunkSize + 2); + StreamChunker::Chunk* fileChunk = new StreamChunker::Chunk(chunkSize + 2); char* chunkData = fileChunk->m_chunk; chunkData[0] = kDataChunk; @@ -455,7 +455,7 @@ NetworkTests::sendMockData(void* eventTarget) } // send last message - FileChunker::FileChunk* transferFinished = new FileChunker::FileChunk(2); + StreamChunker::Chunk* transferFinished = new StreamChunker::Chunk(2); chunkData = transferFinished->m_chunk; chunkData[0] = kDataEnd; From af73e8280daa17cad28e5af27438b04ebf2acb94 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 18 May 2015 11:25:58 -0700 Subject: [PATCH 06/66] Increased protocol version number to 1.6 #4601 --- src/lib/server/ClientProxy1_5.cpp | 51 ---------------------------- src/lib/server/ClientProxy1_5.h | 1 - src/lib/server/ClientProxy1_6.cpp | 56 +++++++++++++++++++++++++++++++ src/lib/server/ClientProxy1_6.h | 35 +++++++++++++++++++ src/lib/synergy/protocol_types.h | 5 +-- 5 files changed, 94 insertions(+), 54 deletions(-) create mode 100644 src/lib/server/ClientProxy1_6.cpp create mode 100644 src/lib/server/ClientProxy1_6.h diff --git a/src/lib/server/ClientProxy1_5.cpp b/src/lib/server/ClientProxy1_5.cpp index 4135375b..fee91a4f 100644 --- a/src/lib/server/ClientProxy1_5.cpp +++ b/src/lib/server/ClientProxy1_5.cpp @@ -74,57 +74,6 @@ ClientProxy1_5::fileChunkSending(UInt8 mark, char* data, size_t dataSize) ProtocolUtil::writef(getStream(), kMsgDFileTransfer, mark, &chunk); } -void -ClientProxy1_5::setClipboard(ClipboardID id, const IClipboard* clipboard) -{ - // ignore if this clipboard is already clean - if (m_clipboard[id].m_dirty) { - // this clipboard is now clean - m_clipboard[id].m_dirty = false; - Clipboard::copy(&m_clipboard[id].m_clipboard, clipboard); - - String data = m_clipboard[id].m_clipboard.marshall(); - - size_t size = data.size(); - LOG((CLOG_DEBUG "sending clipboard %d to \"%s\" size=%d", id, getName().c_str(), size)); - - //TODO: refactor FileChunker and use thread - // send first message (file size) - std::stringstream ss; - ss << size; - String dataSize = ss.str(); - ProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, kDataStart, &dataSize); - - // send chunk messages with a fixed chunk size - size_t sentLength = 0; - size_t chunkSize = 2048; - Stopwatch stopwatch; - stopwatch.start(); - while (true) { - if (stopwatch.getTime() > 0.1f) { - // make sure we don't read too much from the mock data. - if (sentLength + chunkSize > size) { - chunkSize = size - sentLength; - } - - String chunk(data.substr(sentLength, chunkSize).c_str(), chunkSize); - ProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, kDataChunk, &chunk); - - sentLength += chunkSize; - - if (sentLength == size) { - break; - } - - stopwatch.reset(); - } - } - - // send last message - ProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, kDataEnd, "\0"); - } -} - bool ClientProxy1_5::parseMessage(const UInt8* code) { diff --git a/src/lib/server/ClientProxy1_5.h b/src/lib/server/ClientProxy1_5.h index 3705b85c..8229739f 100644 --- a/src/lib/server/ClientProxy1_5.h +++ b/src/lib/server/ClientProxy1_5.h @@ -32,7 +32,6 @@ public: virtual void sendDragInfo(UInt32 fileCount, const char* info, size_t size); virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize); - virtual void setClipboard(ClipboardID id, const IClipboard* clipboard); virtual bool parseMessage(const UInt8* code); void fileChunkReceived(); void dragInfoReceived(); diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp new file mode 100644 index 00000000..3361d242 --- /dev/null +++ b/src/lib/server/ClientProxy1_6.cpp @@ -0,0 +1,56 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015 Synergy Si Inc. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "server/ClientProxy1_6.h" + +#include "server/Server.h" +#include "synergy/StreamChunker.h" +#include "synergy/ProtocolUtil.h" +#include "io/IStream.h" +#include "base/Log.h" + +// +// ClientProxy1_6 +// + +ClientProxy1_6::ClientProxy1_6(const String& name, synergy::IStream* stream, Server* server, IEventQueue* events) : + ClientProxy1_5(name, stream, server, events), + m_events(events) +{ +} + +ClientProxy1_6::~ClientProxy1_6() +{ +} + +void +ClientProxy1_6::setClipboard(ClipboardID id, const IClipboard* clipboard) +{ + // ignore if this clipboard is already clean + if (m_clipboard[id].m_dirty) { + // this clipboard is now clean + m_clipboard[id].m_dirty = false; + Clipboard::copy(&m_clipboard[id].m_clipboard, clipboard); + + String data = m_clipboard[id].m_clipboard.marshall(); + + size_t size = data.size(); + LOG((CLOG_DEBUG "sending clipboard %d to \"%s\" size=%d", id, getName().c_str(), size)); + + StreamChunker::sendData(data, size, id, m_events, this); + } +} diff --git a/src/lib/server/ClientProxy1_6.h b/src/lib/server/ClientProxy1_6.h new file mode 100644 index 00000000..7077c9b8 --- /dev/null +++ b/src/lib/server/ClientProxy1_6.h @@ -0,0 +1,35 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015 Synergy Si Inc. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "server/ClientProxy1_5.h" + +class Server; +class IEventQueue; + +//! Proxy for client implementing protocol version 1.6 +class ClientProxy1_6 : public ClientProxy1_5 { +public: + ClientProxy1_6(const String& name, synergy::IStream* adoptedStream, Server* server, IEventQueue* events); + ~ClientProxy1_6(); + + virtual void setClipboard(ClipboardID id, const IClipboard* clipboard); + +private: + IEventQueue* m_events; +}; diff --git a/src/lib/synergy/protocol_types.h b/src/lib/synergy/protocol_types.h index 956f4f49..b34213f4 100644 --- a/src/lib/synergy/protocol_types.h +++ b/src/lib/synergy/protocol_types.h @@ -27,10 +27,11 @@ // 1.3: adds keep alive and deprecates heartbeats, // adds horizontal mouse scrolling // 1.4: adds crypto support -// 1.5: adds file/clipboard transfer and removes home brew crypto +// 1.5: adds file transfer and removes home brew crypto +// 1.6: adds clipboard streaming // NOTE: with new version, synergy minor version should increment static const SInt16 kProtocolMajorVersion = 1; -static const SInt16 kProtocolMinorVersion = 5; +static const SInt16 kProtocolMinorVersion = 6; // default contact port number static const UInt16 kDefaultPort = 24800; From 70be9cd97f3cc9b175a9f5a94567798eb1b6eafe Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 18 May 2015 11:27:54 -0700 Subject: [PATCH 07/66] Refactored intToString #4601 --- src/lib/base/String.cpp | 8 ++++++++ src/lib/base/String.h | 7 ++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/lib/base/String.cpp b/src/lib/base/String.cpp index 5760e4d4..b873663e 100644 --- a/src/lib/base/String.cpp +++ b/src/lib/base/String.cpp @@ -207,6 +207,14 @@ removeChar(String& subject, const char c) subject.erase(std::remove(subject.begin(), subject.end(), c), subject.end()); } +String +intToString(size_t integer) +{ + std::stringstream ss; + ss << integer; + return ss.str(); +} + // // CaselessCmp // diff --git a/src/lib/base/String.h b/src/lib/base/String.h index 33e4f3d8..81d98a9b 100644 --- a/src/lib/base/String.h +++ b/src/lib/base/String.h @@ -84,10 +84,15 @@ void uppercase(String& subject); //! Remove all specific char in suject /*! -Remove all specific \c char in \c suject +Remove all specific \c c in \c suject */ void removeChar(String& subject, const char c); +//! Convert an integer to a string +/*! +Convert an \c integer to a string +*/ +String intToString(size_t integer); //! Case-insensitive comparisons /*! From c67464218135096a9b37845d0280eb749dc6cdc7 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 19 May 2015 09:30:35 -0700 Subject: [PATCH 08/66] Used StreamChunker for clipboard and file transfer #4601 Clipboard is not handled at this point --- src/lib/client/Client.cpp | 7 +- src/lib/server/ClientProxy1_6.cpp | 17 +++- src/lib/server/ClientProxy1_6.h | 1 + src/lib/server/ClientProxyUnknown.cpp | 5 ++ src/lib/server/Server.cpp | 15 ++-- src/lib/server/Server.h | 2 +- src/lib/synergy/Chunk.cpp | 30 +++++++ src/lib/synergy/Chunk.h | 30 +++++++ src/lib/synergy/ClipboardChunk.cpp | 81 +++++++++++++++++++ src/lib/synergy/ClipboardChunk.h | 40 +++++++++ src/lib/synergy/FileChunk.cpp | 62 ++++++++++++++ src/lib/synergy/FileChunk.h | 33 ++++++++ src/lib/synergy/StreamChunker.h | 29 +++---- src/test/integtests/net/NetworkTests.cpp | 46 +++-------- .../unittests/synergy/ClipboardChunkTests.cpp | 76 +++++++++++++++++ 15 files changed, 410 insertions(+), 64 deletions(-) create mode 100644 src/lib/synergy/Chunk.cpp create mode 100644 src/lib/synergy/Chunk.h create mode 100644 src/lib/synergy/ClipboardChunk.cpp create mode 100644 src/lib/synergy/ClipboardChunk.h create mode 100644 src/lib/synergy/FileChunk.cpp create mode 100644 src/lib/synergy/FileChunk.h create mode 100644 src/test/unittests/synergy/ClipboardChunkTests.cpp diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 944c06aa..06ea45d5 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -22,6 +22,7 @@ #include "client/ServerProxy.h" #include "synergy/Screen.h" #include "synergy/Clipboard.h" +#include "synergy/FileChunk.h" #include "synergy/DropHelper.h" #include "synergy/PacketStreamFilter.h" #include "synergy/ProtocolUtil.h" @@ -422,12 +423,12 @@ Client::sendConnectionFailedEvent(const char* msg) void Client::sendFileChunk(const void* data) { - StreamChunker::Chunk* chunk = reinterpret_cast(const_cast(data)); + FileChunk* chunk = reinterpret_cast(const_cast(data)); LOG((CLOG_DEBUG1 "send file chunk")); assert(m_server != NULL); // relay - m_server->fileChunkSending(chunk->m_chunk[0], &(chunk->m_chunk[1]), chunk->m_size); + m_server->fileChunkSending(chunk->m_chunk[0], &chunk->m_chunk[1], chunk->m_dataSize); } void @@ -821,7 +822,7 @@ Client::sendFileThread(void* filename) { try { char* name = reinterpret_cast(filename); - StreamChunker::sendFileChunks(name, m_events, this); + StreamChunker::sendFile(name, m_events, this); } catch (std::runtime_error error) { LOG((CLOG_ERR "failed sending file chunks: %s", error.what())); diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp index 3361d242..acdb7dfd 100644 --- a/src/lib/server/ClientProxy1_6.cpp +++ b/src/lib/server/ClientProxy1_6.cpp @@ -19,7 +19,6 @@ #include "server/Server.h" #include "synergy/StreamChunker.h" -#include "synergy/ProtocolUtil.h" #include "io/IStream.h" #include "base/Log.h" @@ -37,6 +36,20 @@ ClientProxy1_6::~ClientProxy1_6() { } +bool +ClientProxy1_6::parseMessage(const UInt8* code) +{ + //TODO:: parse data tansfer + if (memcmp(code, kMsgDFileTransfer, 4) == 0) { + fileChunkReceived(); + } + else { + return ClientProxy1_5::parseMessage(code); + } + + return true; +} + void ClientProxy1_6::setClipboard(ClipboardID id, const IClipboard* clipboard) { @@ -51,6 +64,6 @@ ClientProxy1_6::setClipboard(ClipboardID id, const IClipboard* clipboard) size_t size = data.size(); LOG((CLOG_DEBUG "sending clipboard %d to \"%s\" size=%d", id, getName().c_str(), size)); - StreamChunker::sendData(data, size, id, m_events, this); + StreamChunker::sendClipboard(data, size, id, 0, m_events, this); } } diff --git a/src/lib/server/ClientProxy1_6.h b/src/lib/server/ClientProxy1_6.h index 7077c9b8..3290ffe6 100644 --- a/src/lib/server/ClientProxy1_6.h +++ b/src/lib/server/ClientProxy1_6.h @@ -28,6 +28,7 @@ public: ClientProxy1_6(const String& name, synergy::IStream* adoptedStream, Server* server, IEventQueue* events); ~ClientProxy1_6(); + virtual bool parseMessage(const UInt8* code); virtual void setClipboard(ClipboardID id, const IClipboard* clipboard); private: diff --git a/src/lib/server/ClientProxyUnknown.cpp b/src/lib/server/ClientProxyUnknown.cpp index 28191792..a328d213 100644 --- a/src/lib/server/ClientProxyUnknown.cpp +++ b/src/lib/server/ClientProxyUnknown.cpp @@ -25,6 +25,7 @@ #include "server/ClientProxy1_3.h" #include "server/ClientProxy1_4.h" #include "server/ClientProxy1_5.h" +#include "server/ClientProxy1_6.h" #include "synergy/protocol_types.h" #include "synergy/ProtocolUtil.h" #include "synergy/XSynergy.h" @@ -227,6 +228,10 @@ ClientProxyUnknown::handleData(const Event&, void*) case 5: m_proxy = new ClientProxy1_5(name, m_stream, m_server, m_events); break; + + case 6: + m_proxy = new ClientProxy1_6(name, m_stream, m_server, m_events); + break; } } diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index c3c98f4b..607a0078 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -22,13 +22,14 @@ #include "server/ClientProxyUnknown.h" #include "server/PrimaryClient.h" #include "server/ClientListener.h" +#include "synergy/FileChunk.h" #include "synergy/IPlatformScreen.h" #include "synergy/DropHelper.h" #include "synergy/option_types.h" #include "synergy/protocol_types.h" #include "synergy/XScreen.h" #include "synergy/XSynergy.h" -#include "synergy/FileChunker.h" +#include "synergy/StreamChunker.h" #include "synergy/KeyState.h" #include "synergy/Screen.h" #include "synergy/PacketStreamFilter.h" @@ -508,7 +509,7 @@ Server::switchScreen(BaseClientProxy* dst, // send the clipboard data to new active screen m_dataTransmissionThread = new Thread( new TMethodJob( - this, &Server::clipboardTransmissionThread, + this, &Server::sendClipboardThread, NULL)); Server::SwitchToScreenInfo* info = @@ -1852,10 +1853,10 @@ Server::sendDragInfo(BaseClientProxy* newScreen) } void -Server::clipboardTransmissionThread(void*) +Server::sendClipboardThread(void*) { for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - m_active->setClipboard(id, &m_clipboards[id].m_clipboard); + m_active->setClipboard(id, &m_clipboards[id].m_clipboard); } } @@ -2033,13 +2034,13 @@ Server::onMouseWheel(SInt32 xDelta, SInt32 yDelta) void Server::onFileChunkSending(const void* data) { - StreamChunker::Chunk* chunk = reinterpret_cast(const_cast(data)); + FileChunk* chunk = reinterpret_cast(const_cast(data)); LOG((CLOG_DEBUG1 "sending file chunk")); assert(m_active != NULL); // relay - m_active->fileChunkSending(chunk->m_chunk[0], &(chunk->m_chunk[1]), chunk->m_size); + m_active->fileChunkSending(chunk->m_chunk[0], &chunk->m_chunk[1], chunk->m_dataSize); } void @@ -2376,7 +2377,7 @@ Server::sendFileThread(void* data) try { char* filename = reinterpret_cast(data); LOG((CLOG_DEBUG "sending file to client, filename=%s", filename)); - StreamChunker::sendFileChunks(filename, m_events, this); + StreamChunker::sendFile(filename, m_events, this); } catch (std::runtime_error error) { LOG((CLOG_ERR "failed sending file chunks, error: %s", error.what())); diff --git a/src/lib/server/Server.h b/src/lib/server/Server.h index 8cc700b4..02b9cd47 100644 --- a/src/lib/server/Server.h +++ b/src/lib/server/Server.h @@ -371,7 +371,7 @@ private: void sendDragInfo(BaseClientProxy* newScreen); // thread funciton for sending clipboard - void clipboardTransmissionThread(void*); + void sendClipboardThread(void*); public: bool m_mock; diff --git a/src/lib/synergy/Chunk.cpp b/src/lib/synergy/Chunk.cpp new file mode 100644 index 00000000..fe4aeae7 --- /dev/null +++ b/src/lib/synergy/Chunk.cpp @@ -0,0 +1,30 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015 Synergy Si Inc. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "synergy/Chunk.h" +#include "base/String.h" + +Chunk::Chunk(size_t size) +{ + m_chunk = new char[size]; + memset(m_chunk, 0, size); +} + +Chunk::~Chunk() +{ + delete[] m_chunk; +} diff --git a/src/lib/synergy/Chunk.h b/src/lib/synergy/Chunk.h new file mode 100644 index 00000000..13431950 --- /dev/null +++ b/src/lib/synergy/Chunk.h @@ -0,0 +1,30 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015 Synergy Si Inc. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "common/basic_types.h" + +class Chunk { +public: + Chunk(size_t size); + ~Chunk(); + +public: + size_t m_dataSize; + char* m_chunk; +}; diff --git a/src/lib/synergy/ClipboardChunk.cpp b/src/lib/synergy/ClipboardChunk.cpp new file mode 100644 index 00000000..9d43953c --- /dev/null +++ b/src/lib/synergy/ClipboardChunk.cpp @@ -0,0 +1,81 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015 Synergy Si Inc. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "synergy/ClipboardChunk.h" + +#include "synergy/protocol_types.h" + +ClipboardChunk::ClipboardChunk(size_t size) : + Chunk(size) +{ + m_dataSize = size - CLIPBOARD_CHUNK_META_SIZE; +} + +ClipboardChunk* +ClipboardChunk::start( + ClipboardID id, + UInt32 sequence, + const String& size) +{ + size_t sizeLength = size.size(); + ClipboardChunk* start = new ClipboardChunk(sizeLength + CLIPBOARD_CHUNK_META_SIZE); + char* chunk = start->m_chunk; + + chunk[0] = id; + UInt32* seq = reinterpret_cast(&chunk[1]); + *seq = sequence; + chunk[5] = kDataStart; + memcpy(&chunk[6], size.c_str(), sizeLength); + chunk[sizeLength + CLIPBOARD_CHUNK_META_SIZE - 1] = '\0'; + + return start; +} + +ClipboardChunk* +ClipboardChunk::data( + ClipboardID id, + UInt32 sequence, + const String& data) +{ + size_t dataSize = data.size(); + ClipboardChunk* chunk = new ClipboardChunk(dataSize + CLIPBOARD_CHUNK_META_SIZE); + char* chunkData = chunk->m_chunk; + + chunkData[0] = id; + UInt32* seq = reinterpret_cast(&chunkData[1]); + *seq = sequence; + chunkData[5] = kDataChunk; + memcpy(&chunkData[6], data.c_str(), dataSize); + chunkData[dataSize + CLIPBOARD_CHUNK_META_SIZE - 1] = '\0'; + + return chunk; +} + +ClipboardChunk* +ClipboardChunk::end(ClipboardID id, UInt32 sequence) +{ + ClipboardChunk* end = new ClipboardChunk(CLIPBOARD_CHUNK_META_SIZE); + char* chunk = end->m_chunk; + + chunk[0] = id; + UInt32* seq = reinterpret_cast(&chunk[1]); + *seq = sequence; + chunk[5] = kDataEnd; + chunk[CLIPBOARD_CHUNK_META_SIZE - 1] = '\0'; + + return end; +} diff --git a/src/lib/synergy/ClipboardChunk.h b/src/lib/synergy/ClipboardChunk.h new file mode 100644 index 00000000..df78cb08 --- /dev/null +++ b/src/lib/synergy/ClipboardChunk.h @@ -0,0 +1,40 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015 Synergy Si Inc. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "synergy/Chunk.h" +#include "synergy/clipboard_types.h" +#include "base/String.h" +#include "common/basic_types.h" + +#define CLIPBOARD_CHUNK_META_SIZE 7 + +class ClipboardChunk : public Chunk { +public: + ClipboardChunk(size_t size); + + static ClipboardChunk* start( + ClipboardID id, + UInt32 sequence, + const String& size); + static ClipboardChunk* data( + ClipboardID id, + UInt32 sequence, + const String& data); + static ClipboardChunk* end(ClipboardID id, UInt32 sequence); +}; diff --git a/src/lib/synergy/FileChunk.cpp b/src/lib/synergy/FileChunk.cpp new file mode 100644 index 00000000..82c103cc --- /dev/null +++ b/src/lib/synergy/FileChunk.cpp @@ -0,0 +1,62 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015 Synergy Si Inc. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "synergy/FileChunk.h" + +#include "synergy/protocol_types.h" + +FileChunk::FileChunk(size_t size) : + Chunk(size) +{ + m_dataSize = size - FILE_CHUNK_META_SIZE; +} + +FileChunk* +FileChunk::start(const String& size) +{ + size_t sizeLength = size.size(); + FileChunk* start = new FileChunk(sizeLength + FILE_CHUNK_META_SIZE); + char* chunk = start->m_chunk; + chunk[0] = kDataStart; + memcpy(&chunk[1], size.c_str(), sizeLength); + chunk[sizeLength + 1] = '\0'; + + return start; +} + +FileChunk* +FileChunk::data(UInt8* data, size_t dataSize) +{ + FileChunk* chunk = new FileChunk(dataSize + FILE_CHUNK_META_SIZE); + char* chunkData = chunk->m_chunk; + chunkData[0] = kDataChunk; + memcpy(&chunkData[1], data, dataSize); + chunkData[dataSize + 1] = '\0'; + + return chunk; +} + +FileChunk* +FileChunk::end() +{ + FileChunk* end = new FileChunk(FILE_CHUNK_META_SIZE); + char* chunk = end->m_chunk; + chunk[0] = kDataEnd; + chunk[1] = '\0'; + + return end; +} diff --git a/src/lib/synergy/FileChunk.h b/src/lib/synergy/FileChunk.h new file mode 100644 index 00000000..baf8faa8 --- /dev/null +++ b/src/lib/synergy/FileChunk.h @@ -0,0 +1,33 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015 Synergy Si Inc. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "synergy/Chunk.h" +#include "base/String.h" +#include "common/basic_types.h" + +#define FILE_CHUNK_META_SIZE 2 + +class FileChunk : public Chunk { +public: + FileChunk(size_t size); + + static FileChunk* start(const String& size); + static FileChunk* data(UInt8* data, size_t dataSize); + static FileChunk* end(); +}; diff --git a/src/lib/synergy/StreamChunker.h b/src/lib/synergy/StreamChunker.h index 0560347f..e1dd4605 100644 --- a/src/lib/synergy/StreamChunker.h +++ b/src/lib/synergy/StreamChunker.h @@ -17,29 +17,24 @@ #pragma once +#include "synergy/clipboard_types.h" #include "base/String.h" class IEventQueue; class StreamChunker { public: - //! FileChunk data - class Chunk { - public: - Chunk(size_t size) : m_size(size - 2) - { - m_chunk = new char[size]; - } - - ~Chunk() { delete[] m_chunk; } - - public: - const size_t m_size; - char* m_chunk; - }; - - static void sendFileChunks(char* filename, IEventQueue* events, void* eventTarget); - static String intToString(size_t i); + static void sendFile( + char* filename, + IEventQueue* events, + void* eventTarget); + static void sendClipboard( + String& data, + size_t size, + ClipboardID id, + UInt32 sequence, + IEventQueue* events, + void* eventTarget); private: static const size_t m_chunkSize; diff --git a/src/test/integtests/net/NetworkTests.cpp b/src/test/integtests/net/NetworkTests.cpp index 21097163..f4f9e4c5 100644 --- a/src/test/integtests/net/NetworkTests.cpp +++ b/src/test/integtests/net/NetworkTests.cpp @@ -28,6 +28,7 @@ #include "server/Server.h" #include "server/ClientListener.h" #include "client/Client.h" +#include "synergy/FileChunk.h" #include "synergy/StreamChunker.h" #include "net/SocketMultiplexer.h" #include "net/NetworkAddress.h" @@ -60,7 +61,6 @@ const size_t kMockFileSize = 1024 * 1024 * 10; // 10MB void getScreenShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h); void getCursorPos(SInt32& x, SInt32& y); -String intToString(size_t i); UInt8* newMockData(size_t size); void createFile(fstream& file, const char* filename, size_t size); @@ -415,38 +415,28 @@ void NetworkTests::sendMockData(void* eventTarget) { // send first message (file size) - String size = intToString(kMockDataSize); - size_t sizeLength = size.size(); - StreamChunker::Chunk* sizeMessage = new StreamChunker::Chunk(sizeLength + 2); - char* chunkData = sizeMessage->m_chunk; - - chunkData[0] = kDataStart; - memcpy(&chunkData[1], size.c_str(), sizeLength); - chunkData[sizeLength + 1] = '\0'; + String size = synergy::string::intToString(kMockDataSize); + FileChunk* sizeMessage = FileChunk::start(size); + m_events.addEvent(Event(m_events.forIScreen().fileChunkSending(), eventTarget, sizeMessage)); // send chunk messages with incrementing chunk size size_t lastSize = 0; size_t sentLength = 0; while (true) { - size_t chunkSize = lastSize + kMockDataChunkIncrement; + size_t dataSize = lastSize + kMockDataChunkIncrement; // make sure we don't read too much from the mock data. - if (sentLength + chunkSize > kMockDataSize) { - chunkSize = kMockDataSize - sentLength; + if (sentLength + dataSize > kMockDataSize) { + dataSize = kMockDataSize - sentLength; } // first byte is the chunk mark, last is \0 - StreamChunker::Chunk* fileChunk = new StreamChunker::Chunk(chunkSize + 2); - char* chunkData = fileChunk->m_chunk; + FileChunk* chunk = FileChunk::data(m_mockData, dataSize); + m_events.addEvent(Event(m_events.forIScreen().fileChunkSending(), eventTarget, chunk)); - chunkData[0] = kDataChunk; - memcpy(&chunkData[1], &m_mockData[sentLength], chunkSize); - chunkData[chunkSize + 1] = '\0'; - m_events.addEvent(Event(m_events.forIScreen().fileChunkSending(), eventTarget, fileChunk)); - - sentLength += chunkSize; - lastSize = chunkSize; + sentLength += dataSize; + lastSize = dataSize; if (sentLength == kMockDataSize) { break; @@ -455,11 +445,7 @@ NetworkTests::sendMockData(void* eventTarget) } // send last message - StreamChunker::Chunk* transferFinished = new StreamChunker::Chunk(2); - chunkData = transferFinished->m_chunk; - - chunkData[0] = kDataEnd; - chunkData[1] = '\0'; + FileChunk* transferFinished = FileChunk::end(); m_events.addEvent(Event(m_events.forIScreen().fileChunkSending(), eventTarget, transferFinished)); } @@ -527,12 +513,4 @@ getCursorPos(SInt32& x, SInt32& y) y = 0; } -String -intToString(size_t i) -{ - stringstream ss; - ss << i; - return ss.str(); -} - #endif // WINAPI_CARBON diff --git a/src/test/unittests/synergy/ClipboardChunkTests.cpp b/src/test/unittests/synergy/ClipboardChunkTests.cpp new file mode 100644 index 00000000..3666fe45 --- /dev/null +++ b/src/test/unittests/synergy/ClipboardChunkTests.cpp @@ -0,0 +1,76 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015 Synergy Si Inc. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "synergy/ClipboardChunk.h" +#include "synergy/protocol_types.h" + +#include "test/global/gtest.h" + +TEST(ClipboardChunkTests, start_formatStartChunk) +{ + ClipboardID id = 0; + UInt32 sequence = 0; + String mockDataSize("10"); + ClipboardChunk* chunk = ClipboardChunk::start(id, sequence, mockDataSize); + + EXPECT_EQ(id, chunk->m_chunk[0]); + EXPECT_EQ(sequence, (UInt32)chunk->m_chunk[1]); + EXPECT_EQ(kDataStart, chunk->m_chunk[5]); + EXPECT_EQ('1', chunk->m_chunk[6]); + EXPECT_EQ('0', chunk->m_chunk[7]); + EXPECT_EQ('\0', chunk->m_chunk[8]); + + delete chunk; +} + +TEST(ClipboardChunkTests, data_formatDataChunk) +{ + ClipboardID id = 0; + UInt32 sequence = 1; + String mockData("mock data"); + ClipboardChunk* chunk = ClipboardChunk::data(id, sequence, mockData); + + EXPECT_EQ(id, chunk->m_chunk[0]); + EXPECT_EQ(sequence, (UInt32)chunk->m_chunk[1]); + EXPECT_EQ(kDataChunk, chunk->m_chunk[5]); + EXPECT_EQ('m', chunk->m_chunk[6]); + EXPECT_EQ('o', chunk->m_chunk[7]); + EXPECT_EQ('c', chunk->m_chunk[8]); + EXPECT_EQ('k', chunk->m_chunk[9]); + EXPECT_EQ(' ', chunk->m_chunk[10]); + EXPECT_EQ('d', chunk->m_chunk[11]); + EXPECT_EQ('a', chunk->m_chunk[12]); + EXPECT_EQ('t', chunk->m_chunk[13]); + EXPECT_EQ('a', chunk->m_chunk[14]); + EXPECT_EQ('\0', chunk->m_chunk[15]); + + delete chunk; +} + +TEST(ClipboardChunkTests, end_formatDataChunk) +{ + ClipboardID id = 1; + UInt32 sequence = 1; + ClipboardChunk* chunk = ClipboardChunk::end(id, sequence); + + EXPECT_EQ(id, chunk->m_chunk[0]); + EXPECT_EQ(sequence, (UInt32)chunk->m_chunk[1]); + EXPECT_EQ(kDataEnd, chunk->m_chunk[5]); + EXPECT_EQ('\0', chunk->m_chunk[6]); + + delete chunk; +} From 6e7b3d87c5338c25df977771e6d4639d05e6bccd Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 19 May 2015 09:37:15 -0700 Subject: [PATCH 09/66] Added changes for StreamChunker that is left from last commit #4601 --- src/lib/synergy/StreamChunker.cpp | 88 ++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 26 deletions(-) diff --git a/src/lib/synergy/StreamChunker.cpp b/src/lib/synergy/StreamChunker.cpp index ab105053..46d19e3c 100644 --- a/src/lib/synergy/StreamChunker.cpp +++ b/src/lib/synergy/StreamChunker.cpp @@ -17,6 +17,8 @@ #include "synergy/StreamChunker.h" +#include "synergy/FileChunk.h" +#include "synergy/ClipboardChunk.h" #include "synergy/protocol_types.h" #include "base/EventTypes.h" #include "base/Event.h" @@ -24,10 +26,10 @@ #include "base/EventTypes.h" #include "base/Log.h" #include "base/Stopwatch.h" +#include "base/String.h" #include "common/stdexcept.h" #include -#include #define PAUSE_TIME_HACK 0.1 @@ -36,7 +38,10 @@ using namespace std; const size_t StreamChunker::m_chunkSize = 512 * 1024; // 512kb void -StreamChunker::sendFileChunks(char* filename, IEventQueue* events, void* eventTarget) +StreamChunker::sendFile( + char* filename, + IEventQueue* events, + void* eventTarget) { std::fstream file(reinterpret_cast(filename), std::ios::in | std::ios::binary); @@ -49,14 +54,9 @@ StreamChunker::sendFileChunks(char* filename, IEventQueue* events, void* eventTa size_t size = (size_t)file.tellg(); // send first message (file size) - String fileSize = intToString(size); - size_t sizeLength = fileSize.size(); - Chunk* sizeMessage = new Chunk(sizeLength + 2); - char* chunkData = sizeMessage->m_chunk; + String fileSize = synergy::string::intToString(size); + FileChunk* sizeMessage = FileChunk::start(fileSize); - chunkData[0] = kDataStart; - memcpy(&chunkData[1], fileSize.c_str(), sizeLength); - chunkData[sizeLength + 1] = '\0'; events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, sizeMessage)); // send chunk messages with a fixed chunk size @@ -72,13 +72,12 @@ StreamChunker::sendFileChunks(char* filename, IEventQueue* events, void* eventTa chunkSize = size - sentLength; } - // for fileChunk->m_chunk, the first byte is the chunk mark, last is \0 - Chunk* fileChunk = new Chunk(chunkSize + 2); - char* chunkData = fileChunk->m_chunk; + char* chunkData = new char[chunkSize]; + file.read(chunkData, chunkSize); + UInt8* data = reinterpret_cast(chunkData); + FileChunk* fileChunk = FileChunk::data(data, chunkSize); + delete[] chunkData; - chunkData[0] = kDataChunk; - file.read(&chunkData[1], chunkSize); - chunkData[chunkSize + 1] = '\0'; events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, fileChunk)); sentLength += chunkSize; @@ -93,21 +92,58 @@ StreamChunker::sendFileChunks(char* filename, IEventQueue* events, void* eventTa } // send last message - Chunk* transferFinished = new Chunk(2); - chunkData = transferFinished->m_chunk; + FileChunk* end = FileChunk::end(); - chunkData[0] = kDataEnd; - chunkData[1] = '\0'; - events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, transferFinished)); + events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, end)); file.close(); } -String -StreamChunker::intToString(size_t i) +void +StreamChunker::sendClipboard( + String& data, + size_t size, + ClipboardID id, + UInt32 sequence, + IEventQueue* events, + void* eventTarget) { - //TODO: this should be in string - stringstream ss; - ss << i; - return ss.str(); + // send first message (data size) + String dataSize = synergy::string::intToString(size); + ClipboardChunk* sizeMessage = ClipboardChunk::start(id, sequence, dataSize); + + events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, sizeMessage)); + + // send clipboard chunk with a fixed size + // TODO: 4096 fails and this shouldn't a magic number + size_t sentLength = 0; + size_t chunkSize = 2048; + Stopwatch stopwatch; + stopwatch.start(); + while (true) { + if (stopwatch.getTime() > 0.1f) { + // make sure we don't read too much from the mock data. + if (sentLength + chunkSize > size) { + chunkSize = size - sentLength; + } + + String chunk(data.substr(sentLength, chunkSize).c_str(), chunkSize); + ClipboardChunk* dataChunk = ClipboardChunk::data(id, sequence, chunk); + + events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, dataChunk)); + + sentLength += chunkSize; + + if (sentLength == size) { + break; + } + + stopwatch.reset(); + } + } + + // send last message + ClipboardChunk* end = ClipboardChunk::end(id, sequence); + + events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, end)); } From eaff6f50f8d6674d3241e4f6be2a78603349af62 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 19 May 2015 10:54:02 -0700 Subject: [PATCH 10/66] Added sending clipboard chunks handling #4601 --- src/lib/base/EventTypes.cpp | 1 + src/lib/base/EventTypes.h | 11 ++++++++- src/lib/server/ClientProxy1_6.cpp | 39 +++++++++++++++++++++++++++++++ src/lib/server/ClientProxy1_6.h | 3 +++ src/lib/synergy/StreamChunker.cpp | 6 ++--- 5 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/lib/base/EventTypes.cpp b/src/lib/base/EventTypes.cpp index 0629cef3..d1617d45 100644 --- a/src/lib/base/EventTypes.cpp +++ b/src/lib/base/EventTypes.cpp @@ -116,6 +116,7 @@ REGISTER_EVENT(ClientListener, connected) REGISTER_EVENT(ClientProxy, ready) REGISTER_EVENT(ClientProxy, disconnected) REGISTER_EVENT(ClientProxy, clipboardChanged) +REGISTER_EVENT(ClientProxy, clipboardSending) // // ClientProxyUnknown diff --git a/src/lib/base/EventTypes.h b/src/lib/base/EventTypes.h index 75a0800c..7eea4eea 100644 --- a/src/lib/base/EventTypes.h +++ b/src/lib/base/EventTypes.h @@ -351,7 +351,8 @@ public: ClientProxyEvents() : m_ready(Event::kUnknown), m_disconnected(Event::kUnknown), - m_clipboardChanged(Event::kUnknown) { } + m_clipboardChanged(Event::kUnknown), + m_clipboardSending(Event::kUnknown) { } //! @name accessors //@{ @@ -379,12 +380,20 @@ public: */ Event::Type clipboardChanged(); + //! Clipboard sending event type + /*! + Returns the clipboard sending event type. This is used to send + clipboard chunks. + */ + Event::Type clipboardSending(); + //@} private: Event::Type m_ready; Event::Type m_disconnected; Event::Type m_clipboardChanged; + Event::Type m_clipboardSending; }; class ClientProxyUnknownEvents : public EventTypes { diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp index acdb7dfd..7c778ad1 100644 --- a/src/lib/server/ClientProxy1_6.cpp +++ b/src/lib/server/ClientProxy1_6.cpp @@ -18,8 +18,11 @@ #include "server/ClientProxy1_6.h" #include "server/Server.h" +#include "synergy/ProtocolUtil.h" #include "synergy/StreamChunker.h" +#include "synergy/ClipboardChunk.h" #include "io/IStream.h" +#include "base/TMethodEventJob.h" #include "base/Log.h" // @@ -30,6 +33,10 @@ ClientProxy1_6::ClientProxy1_6(const String& name, synergy::IStream* stream, Ser ClientProxy1_5(name, stream, server, events), m_events(events) { + m_events->adoptHandler(m_events->forClientProxy().clipboardSending(), + this, + new TMethodEventJob(this, + &ClientProxy1_6::handleClipboardSendingEvent)); } ClientProxy1_6::~ClientProxy1_6() @@ -67,3 +74,35 @@ ClientProxy1_6::setClipboard(ClipboardID id, const IClipboard* clipboard) StreamChunker::sendClipboard(data, size, id, 0, m_events, this); } } + +void +ClientProxy1_6::handleClipboardSendingEvent(const Event& event, void*) +{ + void* data = event.getData(); + ClipboardChunk* clipboardData = reinterpret_cast(data); + + LOG((CLOG_DEBUG1 "sending clipboard chunk")); + + char* chunk = clipboardData->m_chunk; + ClipboardID id = chunk[0]; + UInt32* seq = reinterpret_cast(&chunk[1]); + UInt32 sequence = *seq; + UInt8 mark = chunk[5]; + String dataChunk(&chunk[6], clipboardData->m_dataSize); + + switch (mark) { + case kDataStart: + LOG((CLOG_DEBUG2 "file sending start: size=%s", dataChunk.c_str())); + break; + + case kDataChunk: + LOG((CLOG_DEBUG2 "file chunk sending: size=%i", dataChunk.size())); + break; + + case kDataEnd: + LOG((CLOG_DEBUG2 "file sending finished")); + break; + } + + ProtocolUtil::writef(getStream(), kMsgDClipboard, id, sequence, mark, &dataChunk); +} diff --git a/src/lib/server/ClientProxy1_6.h b/src/lib/server/ClientProxy1_6.h index 3290ffe6..4e5fba0f 100644 --- a/src/lib/server/ClientProxy1_6.h +++ b/src/lib/server/ClientProxy1_6.h @@ -31,6 +31,9 @@ public: virtual bool parseMessage(const UInt8* code); virtual void setClipboard(ClipboardID id, const IClipboard* clipboard); +private: + void handleClipboardSendingEvent(const Event&, void*); + private: IEventQueue* m_events; }; diff --git a/src/lib/synergy/StreamChunker.cpp b/src/lib/synergy/StreamChunker.cpp index 46d19e3c..e471622c 100644 --- a/src/lib/synergy/StreamChunker.cpp +++ b/src/lib/synergy/StreamChunker.cpp @@ -112,7 +112,7 @@ StreamChunker::sendClipboard( String dataSize = synergy::string::intToString(size); ClipboardChunk* sizeMessage = ClipboardChunk::start(id, sequence, dataSize); - events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, sizeMessage)); + events->addEvent(Event(events->forClientProxy().clipboardSending(), eventTarget, sizeMessage)); // send clipboard chunk with a fixed size // TODO: 4096 fails and this shouldn't a magic number @@ -130,7 +130,7 @@ StreamChunker::sendClipboard( String chunk(data.substr(sentLength, chunkSize).c_str(), chunkSize); ClipboardChunk* dataChunk = ClipboardChunk::data(id, sequence, chunk); - events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, dataChunk)); + events->addEvent(Event(events->forClientProxy().clipboardSending(), eventTarget, dataChunk)); sentLength += chunkSize; @@ -145,5 +145,5 @@ StreamChunker::sendClipboard( // send last message ClipboardChunk* end = ClipboardChunk::end(id, sequence); - events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, end)); + events->addEvent(Event(events->forClientProxy().clipboardSending(), eventTarget, end)); } From c032091dd50ede35cd22527dce3d6059876ba2fc Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 19 May 2015 13:51:13 -0700 Subject: [PATCH 11/66] Added send clipboard finish log #4601 --- src/lib/server/ClientProxy1_6.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp index 7c778ad1..e1438aa4 100644 --- a/src/lib/server/ClientProxy1_6.cpp +++ b/src/lib/server/ClientProxy1_6.cpp @@ -69,9 +69,11 @@ ClientProxy1_6::setClipboard(ClipboardID id, const IClipboard* clipboard) String data = m_clipboard[id].m_clipboard.marshall(); size_t size = data.size(); - LOG((CLOG_DEBUG "sending clipboard %d to \"%s\" size=%d", id, getName().c_str(), size)); + LOG((CLOG_DEBUG "sending clipboard %d to \"%s\"", id, getName().c_str())); StreamChunker::sendClipboard(data, size, id, 0, m_events, this); + + LOG((CLOG_DEBUG "sent clipboard size=%d", size)); } } From 2c74a7f5c1fb7e3d09ee0e9b22adf3dff39d81ee Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 19 May 2015 14:23:43 -0700 Subject: [PATCH 12/66] Added stringToSizeType and its unittest #4601 --- src/lib/base/String.cpp | 13 +++++++++++-- src/lib/base/String.h | 12 +++++++++--- src/lib/synergy/StreamChunker.cpp | 4 ++-- src/test/integtests/net/NetworkTests.cpp | 2 +- src/test/unittests/base/StringTests.cpp | 18 ++++++++++++++++++ 5 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/lib/base/String.cpp b/src/lib/base/String.cpp index b873663e..99a2bb43 100644 --- a/src/lib/base/String.cpp +++ b/src/lib/base/String.cpp @@ -208,13 +208,22 @@ removeChar(String& subject, const char c) } String -intToString(size_t integer) +sizeTypeToString(size_t n) { std::stringstream ss; - ss << integer; + ss << n; return ss.str(); } +size_t +stringToSizeType(String string) +{ + std::istringstream iss(string); + size_t value; + iss >> value; + return value; +} + // // CaselessCmp // diff --git a/src/lib/base/String.h b/src/lib/base/String.h index 81d98a9b..de34ad0a 100644 --- a/src/lib/base/String.h +++ b/src/lib/base/String.h @@ -88,11 +88,17 @@ Remove all specific \c c in \c suject */ void removeChar(String& subject, const char c); -//! Convert an integer to a string +//! Convert a size type to a string /*! -Convert an \c integer to a string +Convert an size type to a string */ -String intToString(size_t integer); +String sizeTypeToString(size_t n); + +//! Convert a string to a size type +/*! +Convert an a \c string to an size type +*/ +size_t stringToSizeType(String string); //! Case-insensitive comparisons /*! diff --git a/src/lib/synergy/StreamChunker.cpp b/src/lib/synergy/StreamChunker.cpp index e471622c..b7393d94 100644 --- a/src/lib/synergy/StreamChunker.cpp +++ b/src/lib/synergy/StreamChunker.cpp @@ -54,7 +54,7 @@ StreamChunker::sendFile( size_t size = (size_t)file.tellg(); // send first message (file size) - String fileSize = synergy::string::intToString(size); + String fileSize = synergy::string::sizeTypeToString(size); FileChunk* sizeMessage = FileChunk::start(fileSize); events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, sizeMessage)); @@ -109,7 +109,7 @@ StreamChunker::sendClipboard( void* eventTarget) { // send first message (data size) - String dataSize = synergy::string::intToString(size); + String dataSize = synergy::string::sizeTypeToString(size); ClipboardChunk* sizeMessage = ClipboardChunk::start(id, sequence, dataSize); events->addEvent(Event(events->forClientProxy().clipboardSending(), eventTarget, sizeMessage)); diff --git a/src/test/integtests/net/NetworkTests.cpp b/src/test/integtests/net/NetworkTests.cpp index f4f9e4c5..cd9be3c2 100644 --- a/src/test/integtests/net/NetworkTests.cpp +++ b/src/test/integtests/net/NetworkTests.cpp @@ -415,7 +415,7 @@ void NetworkTests::sendMockData(void* eventTarget) { // send first message (file size) - String size = synergy::string::intToString(kMockDataSize); + String size = synergy::string::sizeTypeToString(kMockDataSize); FileChunk* sizeMessage = FileChunk::start(size); m_events.addEvent(Event(m_events.forIScreen().fileChunkSending(), eventTarget, sizeMessage)); diff --git a/src/test/unittests/base/StringTests.cpp b/src/test/unittests/base/StringTests.cpp index 18694302..6381cac5 100644 --- a/src/test/unittests/base/StringTests.cpp +++ b/src/test/unittests/base/StringTests.cpp @@ -82,3 +82,21 @@ TEST(StringTests, removeChar) EXPECT_EQ("fbar", subject); } + +TEST(StringTests, intToString) +{ + size_t value = 123; + + String number = string::sizeTypeToString(value); + + EXPECT_EQ("123", number); +} + +TEST(StringTests, stringToUint) +{ + String number = "123"; + + size_t value = string::stringToSizeType(number); + + EXPECT_EQ(123, value); +} From a6dfbbe02139dc6e77cdd57d964dc157f8492043 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 19 May 2015 14:28:38 -0700 Subject: [PATCH 13/66] Validated received clipboard data size #4601 --- src/lib/client/ServerProxy.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp index c50e44d9..0a995846 100644 --- a/src/lib/client/ServerProxy.cpp +++ b/src/lib/client/ServerProxy.cpp @@ -546,6 +546,7 @@ ServerProxy::setClipboard() { // parse static String dataCached; + static size_t expectedSize; ClipboardID id; UInt32 seqNum; size_t mark = 0; @@ -553,7 +554,7 @@ ServerProxy::setClipboard() ProtocolUtil::readf(m_stream, kMsgDClipboard + 4, &id, &seqNum, &mark, &data); if (mark == kDataStart) { - //TODO: validate size + expectedSize = synergy::string::stringToSizeType(data); LOG((CLOG_DEBUG "start receiving clipboard data")); dataCached.clear(); } @@ -562,14 +563,21 @@ ServerProxy::setClipboard() } else if (mark == kDataEnd) { LOG((CLOG_DEBUG "received clipboard %d size=%d", id, dataCached.size())); + // validate if (id >= kClipboardEnd) { return; } + else if (expectedSize != dataCached.size()) { + LOG((CLOG_ERR "corrupted clipboard data, expected size=%d actual size=%d", expectedSize, dataCached.size())); + return; + } + // forward Clipboard clipboard; clipboard.unmarshall(dataCached, 0); m_client->setClipboard(id, &clipboard); + expectedSize = 0; } } From 49ac320f97a6c9de4e8e98ab3c94d0d19d3cb53a Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Wed, 20 May 2015 19:09:54 +0100 Subject: [PATCH 14/66] Refactored clipboard event into ClipboardEvent #4601 --- src/lib/base/EventQueue.cpp | 2 + src/lib/base/EventQueue.h | 2 + src/lib/base/EventTypes.cpp | 11 ++-- src/lib/base/EventTypes.h | 72 +++++++++++++++----------- src/lib/base/IEventQueue.h | 2 + src/lib/client/Client.cpp | 4 +- src/lib/platform/MSWindowsScreen.cpp | 8 +-- src/lib/platform/OSXScreen.cpp | 4 +- src/lib/server/ClientProxy1_0.cpp | 4 +- src/lib/server/ClientProxy1_6.cpp | 2 +- src/lib/server/Server.cpp | 17 ++---- src/lib/synergy/StreamChunker.cpp | 6 +-- src/test/mock/synergy/MockEventQueue.h | 1 + 13 files changed, 76 insertions(+), 59 deletions(-) diff --git a/src/lib/base/EventQueue.cpp b/src/lib/base/EventQueue.cpp index 0aab4277..71208661 100644 --- a/src/lib/base/EventQueue.cpp +++ b/src/lib/base/EventQueue.cpp @@ -46,6 +46,7 @@ EVENT_TYPE_ACCESSOR(ServerApp) EVENT_TYPE_ACCESSOR(IKeyState) EVENT_TYPE_ACCESSOR(IPrimaryScreen) EVENT_TYPE_ACCESSOR(IScreen) +EVENT_TYPE_ACCESSOR(Clipboard) // interrupt handler. this just adds a quit event to the queue. static @@ -82,6 +83,7 @@ EventQueue::EventQueue() : m_typesForIKeyState(NULL), m_typesForIPrimaryScreen(NULL), m_typesForIScreen(NULL), + m_typesForClipboard(NULL), m_readyMutex(new Mutex), m_readyCondVar(new CondVar(m_readyMutex, false)) { diff --git a/src/lib/base/EventQueue.h b/src/lib/base/EventQueue.h index ed47c2b8..cf6dd01d 100644 --- a/src/lib/base/EventQueue.h +++ b/src/lib/base/EventQueue.h @@ -157,6 +157,7 @@ public: IKeyStateEvents& forIKeyState(); IPrimaryScreenEvents& forIPrimaryScreen(); IScreenEvents& forIScreen(); + ClipboardEvents& forClipboard(); private: ClientEvents* m_typesForClient; @@ -177,6 +178,7 @@ private: IKeyStateEvents* m_typesForIKeyState; IPrimaryScreenEvents* m_typesForIPrimaryScreen; IScreenEvents* m_typesForIScreen; + ClipboardEvents* m_typesForClipboard; Mutex* m_readyMutex; CondVar* m_readyCondVar; std::queue m_pending; diff --git a/src/lib/base/EventTypes.cpp b/src/lib/base/EventTypes.cpp index d1617d45..0fdb87d7 100644 --- a/src/lib/base/EventTypes.cpp +++ b/src/lib/base/EventTypes.cpp @@ -115,8 +115,6 @@ REGISTER_EVENT(ClientListener, connected) REGISTER_EVENT(ClientProxy, ready) REGISTER_EVENT(ClientProxy, disconnected) -REGISTER_EVENT(ClientProxy, clipboardChanged) -REGISTER_EVENT(ClientProxy, clipboardSending) // // ClientProxyUnknown @@ -176,7 +174,6 @@ REGISTER_EVENT(IPrimaryScreen, fakeInputEnd) REGISTER_EVENT(IScreen, error) REGISTER_EVENT(IScreen, shapeChanged) -REGISTER_EVENT(IScreen, clipboardGrabbed) REGISTER_EVENT(IScreen, suspend) REGISTER_EVENT(IScreen, resume) REGISTER_EVENT(IScreen, fileChunkSending) @@ -188,3 +185,11 @@ REGISTER_EVENT(IScreen, fileRecieveCompleted) REGISTER_EVENT(IpcServer, clientConnected) REGISTER_EVENT(IpcServer, messageReceived) + +// +// Clipboard +// + +REGISTER_EVENT(Clipboard, clipboardGrabbed) +REGISTER_EVENT(Clipboard, clipboardChanged) +REGISTER_EVENT(Clipboard, clipboardSending) \ No newline at end of file diff --git a/src/lib/base/EventTypes.h b/src/lib/base/EventTypes.h index 7eea4eea..33ee28cc 100644 --- a/src/lib/base/EventTypes.h +++ b/src/lib/base/EventTypes.h @@ -350,9 +350,7 @@ class ClientProxyEvents : public EventTypes { public: ClientProxyEvents() : m_ready(Event::kUnknown), - m_disconnected(Event::kUnknown), - m_clipboardChanged(Event::kUnknown), - m_clipboardSending(Event::kUnknown) { } + m_disconnected(Event::kUnknown) { } //! @name accessors //@{ @@ -372,28 +370,11 @@ public: */ Event::Type disconnected(); - //! Get clipboard changed event type - /*! - Returns the clipboard changed event type. This is sent whenever the - contents of the clipboard has changed. The data is a pointer to a - IScreen::ClipboardInfo. - */ - Event::Type clipboardChanged(); - - //! Clipboard sending event type - /*! - Returns the clipboard sending event type. This is used to send - clipboard chunks. - */ - Event::Type clipboardSending(); - //@} private: Event::Type m_ready; Event::Type m_disconnected; - Event::Type m_clipboardChanged; - Event::Type m_clipboardSending; }; class ClientProxyUnknownEvents : public EventTypes { @@ -643,7 +624,6 @@ public: IScreenEvents() : m_error(Event::kUnknown), m_shapeChanged(Event::kUnknown), - m_clipboardGrabbed(Event::kUnknown), m_suspend(Event::kUnknown), m_resume(Event::kUnknown), m_fileChunkSending(Event::kUnknown), @@ -666,14 +646,6 @@ public: */ Event::Type shapeChanged(); - //! Get clipboard grabbed event type - /*! - Returns the clipboard grabbed event type. This is sent whenever the - clipboard is grabbed by some other application so we don't own it - anymore. The data is a pointer to a ClipboardInfo. - */ - Event::Type clipboardGrabbed(); - //! Get suspend event type /*! Returns the suspend event type. This is sent whenever the system goes @@ -699,9 +671,49 @@ public: private: Event::Type m_error; Event::Type m_shapeChanged; - Event::Type m_clipboardGrabbed; Event::Type m_suspend; Event::Type m_resume; Event::Type m_fileChunkSending; Event::Type m_fileRecieveCompleted; }; + +class ClipboardEvents : public EventTypes { +public: + ClipboardEvents() : + m_clipboardGrabbed(Event::kUnknown), + m_clipboardChanged(Event::kUnknown), + m_clipboardSending(Event::kUnknown) { } + + //! @name accessors + //@{ + + //! Get clipboard grabbed event type + /*! + Returns the clipboard grabbed event type. This is sent whenever the + clipboard is grabbed by some other application so we don't own it + anymore. The data is a pointer to a ClipboardInfo. + */ + Event::Type clipboardGrabbed(); + + //! Get clipboard changed event type + /*! + Returns the clipboard changed event type. This is sent whenever the + contents of the clipboard has changed. The data is a pointer to a + IScreen::ClipboardInfo. + */ + Event::Type clipboardChanged(); + + //! Clipboard sending event type + /*! + Returns the clipboard sending event type. This is used to send + clipboard chunks. + */ + Event::Type clipboardSending(); + + //@} + +private: + Event::Type m_clipboardGrabbed; + Event::Type m_clipboardChanged; + Event::Type m_clipboardSending; +}; diff --git a/src/lib/base/IEventQueue.h b/src/lib/base/IEventQueue.h index 25c7bbd5..0dcc088a 100644 --- a/src/lib/base/IEventQueue.h +++ b/src/lib/base/IEventQueue.h @@ -48,6 +48,7 @@ class ServerAppEvents; class IKeyStateEvents; class IPrimaryScreenEvents; class IScreenEvents; +class ClipboardEvents; //! Event queue interface /*! @@ -244,4 +245,5 @@ public: virtual IKeyStateEvents& forIKeyState() = 0; virtual IPrimaryScreenEvents& forIPrimaryScreen() = 0; virtual IScreenEvents& forIScreen() = 0; + virtual ClipboardEvents& forClipboard() = 0; }; diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 06ea45d5..a309cf9e 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -488,7 +488,7 @@ Client::setupScreen() getEventTarget(), new TMethodEventJob(this, &Client::handleShapeChanged)); - m_events->adoptHandler(m_events->forIScreen().clipboardGrabbed(), + m_events->adoptHandler(m_events->forClipboard().clipboardGrabbed(), getEventTarget(), new TMethodEventJob(this, &Client::handleClipboardGrabbed)); @@ -546,7 +546,7 @@ Client::cleanupScreen() } m_events->removeHandler(m_events->forIScreen().shapeChanged(), getEventTarget()); - m_events->removeHandler(m_events->forIScreen().clipboardGrabbed(), + m_events->removeHandler(m_events->forClipboard().clipboardGrabbed(), getEventTarget()); delete m_server; m_server = NULL; diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp index 9252466c..efb92266 100644 --- a/src/lib/platform/MSWindowsScreen.cpp +++ b/src/lib/platform/MSWindowsScreen.cpp @@ -433,8 +433,8 @@ MSWindowsScreen::checkClipboards() if (m_ownClipboard && !MSWindowsClipboard::isOwnedBySynergy()) { LOG((CLOG_DEBUG "clipboard changed: lost ownership and no notification received")); m_ownClipboard = false; - sendClipboardEvent(m_events->forIScreen().clipboardGrabbed(), kClipboardClipboard); - sendClipboardEvent(m_events->forIScreen().clipboardGrabbed(), kClipboardSelection); + sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardClipboard); + sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardSelection); } } @@ -1501,8 +1501,8 @@ MSWindowsScreen::onClipboardChange() if (m_ownClipboard) { LOG((CLOG_DEBUG "clipboard changed: lost ownership")); m_ownClipboard = false; - sendClipboardEvent(m_events->forIScreen().clipboardGrabbed(), kClipboardClipboard); - sendClipboardEvent(m_events->forIScreen().clipboardGrabbed(), kClipboardSelection); + sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardClipboard); + sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardSelection); } } else if (!m_ownClipboard) { diff --git a/src/lib/platform/OSXScreen.cpp b/src/lib/platform/OSXScreen.cpp index 2a92d51f..acdeb4d5 100644 --- a/src/lib/platform/OSXScreen.cpp +++ b/src/lib/platform/OSXScreen.cpp @@ -954,8 +954,8 @@ OSXScreen::checkClipboards() LOG((CLOG_DEBUG2 "checking clipboard")); if (m_pasteboard.synchronize()) { LOG((CLOG_DEBUG "clipboard changed")); - sendClipboardEvent(m_events->forIScreen().clipboardGrabbed(), kClipboardClipboard); - sendClipboardEvent(m_events->forIScreen().clipboardGrabbed(), kClipboardSelection); + sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardClipboard); + sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardSelection); } } diff --git a/src/lib/server/ClientProxy1_0.cpp b/src/lib/server/ClientProxy1_0.cpp index 6cc8057c..cb2f6307 100644 --- a/src/lib/server/ClientProxy1_0.cpp +++ b/src/lib/server/ClientProxy1_0.cpp @@ -474,7 +474,7 @@ ClientProxy1_0::recvClipboard() ClipboardInfo* info = new ClipboardInfo; info->m_id = id; info->m_sequenceNumber = seqNum; - m_events->addEvent(Event(m_events->forClientProxy().clipboardChanged(), + m_events->addEvent(Event(m_events->forClipboard().clipboardChanged(), getEventTarget(), info)); return true; @@ -500,7 +500,7 @@ ClientProxy1_0::recvGrabClipboard() ClipboardInfo* info = new ClipboardInfo; info->m_id = id; info->m_sequenceNumber = seqNum; - m_events->addEvent(Event(m_events->forIScreen().clipboardGrabbed(), + m_events->addEvent(Event(m_events->forClipboard().clipboardGrabbed(), getEventTarget(), info)); return true; diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp index e1438aa4..3319866c 100644 --- a/src/lib/server/ClientProxy1_6.cpp +++ b/src/lib/server/ClientProxy1_6.cpp @@ -33,7 +33,7 @@ ClientProxy1_6::ClientProxy1_6(const String& name, synergy::IStream* stream, Ser ClientProxy1_5(name, stream, server, events), m_events(events) { - m_events->adoptHandler(m_events->forClientProxy().clipboardSending(), + m_events->adoptHandler(m_events->forClipboard().clipboardSending(), this, new TMethodEventJob(this, &ClientProxy1_6::handleClipboardSendingEvent)); diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 607a0078..60f75e66 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -92,8 +92,7 @@ Server::Server( m_ignoreFileTransfer(false), m_enableDragDrop(enableDragDrop), m_getDragInfoThread(NULL), - m_waitDragInfoThread(true), - m_dataTransmissionThread(NULL) + m_waitDragInfoThread(true) { // must have a primary client and it must have a canonical name assert(m_primaryClient != NULL); @@ -506,12 +505,6 @@ Server::switchScreen(BaseClientProxy* dst, m_primaryClient->getToggleMask(), forScreensaver); - // send the clipboard data to new active screen - m_dataTransmissionThread = new Thread( - new TMethodJob( - this, &Server::sendClipboardThread, - NULL)); - Server::SwitchToScreenInfo* info = Server::SwitchToScreenInfo::alloc(m_active->getName()); m_events->addEvent(Event(m_events->forServer().screenSwitched(), this, info)); @@ -2079,11 +2072,11 @@ Server::addClient(BaseClientProxy* client) client->getEventTarget(), new TMethodEventJob(this, &Server::handleShapeChanged, client)); - m_events->adoptHandler(m_events->forIScreen().clipboardGrabbed(), + m_events->adoptHandler(m_events->forClipboard().clipboardGrabbed(), client->getEventTarget(), new TMethodEventJob(this, &Server::handleClipboardGrabbed, client)); - m_events->adoptHandler(m_events->forClientProxy().clipboardChanged(), + m_events->adoptHandler(m_events->forClipboard().clipboardChanged(), client->getEventTarget(), new TMethodEventJob(this, &Server::handleClipboardChanged, client)); @@ -2115,9 +2108,9 @@ Server::removeClient(BaseClientProxy* client) // remove event handlers m_events->removeHandler(m_events->forIScreen().shapeChanged(), client->getEventTarget()); - m_events->removeHandler(m_events->forIScreen().clipboardGrabbed(), + m_events->removeHandler(m_events->forClipboard().clipboardGrabbed(), client->getEventTarget()); - m_events->removeHandler(m_events->forClientProxy().clipboardChanged(), + m_events->removeHandler(m_events->forClipboard().clipboardChanged(), client->getEventTarget()); // remove from list diff --git a/src/lib/synergy/StreamChunker.cpp b/src/lib/synergy/StreamChunker.cpp index b7393d94..60700407 100644 --- a/src/lib/synergy/StreamChunker.cpp +++ b/src/lib/synergy/StreamChunker.cpp @@ -112,7 +112,7 @@ StreamChunker::sendClipboard( String dataSize = synergy::string::sizeTypeToString(size); ClipboardChunk* sizeMessage = ClipboardChunk::start(id, sequence, dataSize); - events->addEvent(Event(events->forClientProxy().clipboardSending(), eventTarget, sizeMessage)); + events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, sizeMessage)); // send clipboard chunk with a fixed size // TODO: 4096 fails and this shouldn't a magic number @@ -130,7 +130,7 @@ StreamChunker::sendClipboard( String chunk(data.substr(sentLength, chunkSize).c_str(), chunkSize); ClipboardChunk* dataChunk = ClipboardChunk::data(id, sequence, chunk); - events->addEvent(Event(events->forClientProxy().clipboardSending(), eventTarget, dataChunk)); + events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, dataChunk)); sentLength += chunkSize; @@ -145,5 +145,5 @@ StreamChunker::sendClipboard( // send last message ClipboardChunk* end = ClipboardChunk::end(id, sequence); - events->addEvent(Event(events->forClientProxy().clipboardSending(), eventTarget, end)); + events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, end)); } diff --git a/src/test/mock/synergy/MockEventQueue.h b/src/test/mock/synergy/MockEventQueue.h index 7977b41d..d6256790 100644 --- a/src/test/mock/synergy/MockEventQueue.h +++ b/src/test/mock/synergy/MockEventQueue.h @@ -61,5 +61,6 @@ public: MOCK_METHOD0(forIKeyState, IKeyStateEvents&()); MOCK_METHOD0(forIPrimaryScreen, IPrimaryScreenEvents&()); MOCK_METHOD0(forIScreen, IScreenEvents&()); + MOCK_METHOD0(forClipboard, ClipboardEvents&()); MOCK_CONST_METHOD0(waitForReady, void()); }; From cf5347c8f6cae16559678838d280433f97941908 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Wed, 20 May 2015 19:53:30 +0100 Subject: [PATCH 15/66] Sent clipboard on a thread #4601 --- src/lib/client/Client.cpp | 25 ++++++++++++++++++------- src/lib/client/Client.h | 2 ++ src/lib/server/Server.cpp | 10 +++++++++- src/lib/server/Server.h | 2 +- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index a309cf9e..a99d3695 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -79,7 +79,8 @@ Client::Client( m_writeToDropDirThread(NULL), m_socket(NULL), m_useSecureNetwork(false), - m_args(args) + m_args(args), + m_sendClipboardThread(NULL) { assert(m_socketFactory != NULL); assert(m_screen != NULL); @@ -265,12 +266,11 @@ Client::leave() m_active = false; - // send clipboards that we own and that have changed - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - if (m_ownClipboard[id]) { - sendClipboard(id); - } - } + m_sendClipboardThread = new Thread( + new TMethodJob( + this, + &Client::sendClipboardThread, + NULL)); return true; } @@ -750,6 +750,17 @@ Client::onFileRecieveCompleted() } } +void +Client::sendClipboardThread(void*) +{ + // send clipboards that we own and that have changed + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (m_ownClipboard[id]) { + sendClipboard(id); + } + } +} + void Client::handleStopRetry(const Event&, void*) { diff --git a/src/lib/client/Client.h b/src/lib/client/Client.h index 62bc2473..7ab25738 100644 --- a/src/lib/client/Client.h +++ b/src/lib/client/Client.h @@ -194,6 +194,7 @@ private: void handleFileRecieveCompleted(const Event&, void*); void handleStopRetry(const Event&, void*); void onFileRecieveCompleted(); + void sendClipboardThread(void*); public: bool m_mock; @@ -224,4 +225,5 @@ private: TCPSocket* m_socket; bool m_useSecureNetwork; ClientArgs& m_args; + Thread* m_sendClipboardThread; }; diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 60f75e66..44f68de5 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -92,7 +92,8 @@ Server::Server( m_ignoreFileTransfer(false), m_enableDragDrop(enableDragDrop), m_getDragInfoThread(NULL), - m_waitDragInfoThread(true) + m_waitDragInfoThread(true), + m_sendClipboardThread(NULL) { // must have a primary client and it must have a canonical name assert(m_primaryClient != NULL); @@ -505,6 +506,13 @@ Server::switchScreen(BaseClientProxy* dst, m_primaryClient->getToggleMask(), forScreensaver); + // send the clipboard data to new active screen + m_sendClipboardThread = new Thread( + new TMethodJob( + this, + &Server::sendClipboardThread, + NULL)); + Server::SwitchToScreenInfo* info = Server::SwitchToScreenInfo::alloc(m_active->getName()); m_events->addEvent(Event(m_events->forServer().screenSwitched(), this, info)); diff --git a/src/lib/server/Server.h b/src/lib/server/Server.h index 02b9cd47..631ca762 100644 --- a/src/lib/server/Server.h +++ b/src/lib/server/Server.h @@ -484,5 +484,5 @@ private: ClientListener* m_clientListener; - Thread* m_dataTransmissionThread; + Thread* m_sendClipboardThread; }; From 490667e21b46cb925e4c418bf22239c059240aae Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Wed, 20 May 2015 19:54:17 +0100 Subject: [PATCH 16/66] Used StreamChunker to send clipboard in chunks #4601 --- src/lib/client/ServerProxy.cpp | 46 ++++++++++++++++++++++++++++++++-- src/lib/client/ServerProxy.h | 1 + 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp index 0a995846..f970ad59 100644 --- a/src/lib/client/ServerProxy.cpp +++ b/src/lib/client/ServerProxy.cpp @@ -19,6 +19,8 @@ #include "client/ServerProxy.h" #include "client/Client.h" +#include "synergy/ClipboardChunk.h" +#include "synergy/StreamChunker.h" #include "synergy/Clipboard.h" #include "synergy/ProtocolUtil.h" #include "synergy/option_types.h" @@ -69,6 +71,11 @@ ServerProxy::ServerProxy(Client* client, synergy::IStream* stream, IEventQueue* new TMethodEventJob(this, &ServerProxy::handleData)); + m_events->adoptHandler(m_events->forClipboard().clipboardSending(), + this, + new TMethodEventJob(this, + &ServerProxy::handleClipboardSendingEvent)); + // send heartbeat setKeepAliveRate(kKeepAliveRate); } @@ -358,8 +365,11 @@ void ServerProxy::onClipboardChanged(ClipboardID id, const IClipboard* clipboard) { String data = IClipboard::marshall(clipboard); - LOG((CLOG_DEBUG1 "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size())); - ProtocolUtil::writef(m_stream, kMsgDClipboard, id, m_seqNum, &data); + LOG((CLOG_DEBUG "sending clipboard %d seqnum=%d", id, m_seqNum)); + + StreamChunker::sendClipboard(data, data.size(), id, m_seqNum, m_events, this); + + LOG((CLOG_DEBUG "sent clipboard size=%d", data.size())); } void @@ -928,6 +938,38 @@ ServerProxy::dragInfoReceived() m_client->dragInfoReceived(fileNum, content); } +void +ServerProxy::handleClipboardSendingEvent(const Event& event, void*) +{ + void* data = event.getData(); + ClipboardChunk* clipboardData = reinterpret_cast(data); + + LOG((CLOG_DEBUG1 "sending clipboard chunk")); + + char* chunk = clipboardData->m_chunk; + ClipboardID id = chunk[0]; + UInt32* seq = reinterpret_cast(&chunk[1]); + UInt32 sequence = *seq; + UInt8 mark = chunk[5]; + String dataChunk(&chunk[6], clipboardData->m_dataSize); + + switch (mark) { + case kDataStart: + LOG((CLOG_DEBUG2 "file sending start: size=%s", dataChunk.c_str())); + break; + + case kDataChunk: + LOG((CLOG_DEBUG2 "file chunk sending: size=%i", dataChunk.size())); + break; + + case kDataEnd: + LOG((CLOG_DEBUG2 "file sending finished")); + break; + } + + ProtocolUtil::writef(m_stream, kMsgDClipboard, id, sequence, mark, &dataChunk); +} + void ServerProxy::fileChunkSending(UInt8 mark, char* data, size_t dataSize) { diff --git a/src/lib/client/ServerProxy.h b/src/lib/client/ServerProxy.h index 336c640b..7f498cef 100644 --- a/src/lib/client/ServerProxy.h +++ b/src/lib/client/ServerProxy.h @@ -106,6 +106,7 @@ private: void infoAcknowledgment(); void fileChunkReceived(); void dragInfoReceived(); + void handleClipboardSendingEvent(const Event&, void*); private: typedef EResult (ServerProxy::*MessageParser)(const UInt8*); From 3e9fc89d96a2758f8a9b0dbb15237ea0923744f9 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Wed, 20 May 2015 19:54:56 +0100 Subject: [PATCH 17/66] Modified server to use correct clipboard format #4601 --- src/lib/server/ClientProxy1_0.cpp | 54 ++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/src/lib/server/ClientProxy1_0.cpp b/src/lib/server/ClientProxy1_0.cpp index cb2f6307..a95fe2d9 100644 --- a/src/lib/server/ClientProxy1_0.cpp +++ b/src/lib/server/ClientProxy1_0.cpp @@ -452,31 +452,49 @@ bool ClientProxy1_0::recvClipboard() { // parse message + static String dataCached; + static size_t expectedSize; ClipboardID id; + UInt8 mark; UInt32 seqNum; String data; if (!ProtocolUtil::readf(getStream(), - kMsgDClipboard + 4, &id, &seqNum, &data)) { + kMsgDClipboard + 4, &id, &seqNum, &mark, &data)) { return false; } - LOG((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", getName().c_str(), id, seqNum, data.size())); - - // validate - if (id >= kClipboardEnd) { - return false; + + if (mark == kDataStart) { + expectedSize = synergy::string::stringToSizeType(data); + LOG((CLOG_DEBUG "start receiving clipboard data")); + dataCached.clear(); } - - // save clipboard - m_clipboard[id].m_clipboard.unmarshall(data, 0); - m_clipboard[id].m_sequenceNumber = seqNum; - - // notify - ClipboardInfo* info = new ClipboardInfo; - info->m_id = id; - info->m_sequenceNumber = seqNum; - m_events->addEvent(Event(m_events->forClipboard().clipboardChanged(), - getEventTarget(), info)); - + else if (mark == kDataChunk) { + dataCached.append(data); + } + else if (mark == kDataEnd) { + LOG((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", getName().c_str(), id, seqNum, dataCached.size())); + + // validate + if (id >= kClipboardEnd) { + return false; + } + else if (expectedSize != dataCached.size()) { + LOG((CLOG_ERR "corrupted clipboard data, expected size=%d actual size=%d", expectedSize, dataCached.size())); + return false; + } + + // save clipboard + m_clipboard[id].m_clipboard.unmarshall(dataCached, 0); + m_clipboard[id].m_sequenceNumber = seqNum; + + // notify + ClipboardInfo* info = new ClipboardInfo; + info->m_id = id; + info->m_sequenceNumber = seqNum; + m_events->addEvent(Event(m_events->forClipboard().clipboardChanged(), + getEventTarget(), info)); + } + return true; } From 7dbe30cb6185e4ddfb38ef9384b278fb03836225 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 20 May 2015 14:43:58 -0700 Subject: [PATCH 18/66] Fixed Linux uses wrong clipboard event #4601 --- src/lib/platform/XWindowsScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/platform/XWindowsScreen.cpp b/src/lib/platform/XWindowsScreen.cpp index 14486de9..067c4f3e 100644 --- a/src/lib/platform/XWindowsScreen.cpp +++ b/src/lib/platform/XWindowsScreen.cpp @@ -1325,7 +1325,7 @@ XWindowsScreen::handleSystemEvent(const Event& event, void*) if (id != kClipboardEnd) { LOG((CLOG_DEBUG "lost clipboard %d ownership at time %d", id, xevent->xselectionclear.time)); m_clipboard[id]->lost(xevent->xselectionclear.time); - sendClipboardEvent(m_events->forIScreen().clipboardGrabbed(), id); + sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), id); return; } } From 8b49eb659524b118039caa0f22d52ea43511fc2c Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 21 May 2015 15:19:00 -0700 Subject: [PATCH 19/66] Removed unused function #4601 --- src/lib/server/ClientProxy1_6.cpp | 14 -------------- src/lib/server/ClientProxy1_6.h | 1 - 2 files changed, 15 deletions(-) diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp index 3319866c..17e19319 100644 --- a/src/lib/server/ClientProxy1_6.cpp +++ b/src/lib/server/ClientProxy1_6.cpp @@ -43,20 +43,6 @@ ClientProxy1_6::~ClientProxy1_6() { } -bool -ClientProxy1_6::parseMessage(const UInt8* code) -{ - //TODO:: parse data tansfer - if (memcmp(code, kMsgDFileTransfer, 4) == 0) { - fileChunkReceived(); - } - else { - return ClientProxy1_5::parseMessage(code); - } - - return true; -} - void ClientProxy1_6::setClipboard(ClipboardID id, const IClipboard* clipboard) { diff --git a/src/lib/server/ClientProxy1_6.h b/src/lib/server/ClientProxy1_6.h index 4e5fba0f..4963e277 100644 --- a/src/lib/server/ClientProxy1_6.h +++ b/src/lib/server/ClientProxy1_6.h @@ -28,7 +28,6 @@ public: ClientProxy1_6(const String& name, synergy::IStream* adoptedStream, Server* server, IEventQueue* events); ~ClientProxy1_6(); - virtual bool parseMessage(const UInt8* code); virtual void setClipboard(ClipboardID id, const IClipboard* clipboard); private: From 257c19ecc4c2a23f034b3b976058b6353604c27a Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 21 May 2015 15:34:28 -0700 Subject: [PATCH 20/66] Deprecated and moved clipboard functionality into new protocol #4601 --- src/lib/server/ClientProxy1_0.cpp | 59 ++----------------------------- src/lib/server/ClientProxy1_0.h | 3 +- src/lib/server/ClientProxy1_6.cpp | 50 ++++++++++++++++++++++++++ src/lib/server/ClientProxy1_6.h | 1 + 4 files changed, 55 insertions(+), 58 deletions(-) diff --git a/src/lib/server/ClientProxy1_0.cpp b/src/lib/server/ClientProxy1_0.cpp index a95fe2d9..c124624f 100644 --- a/src/lib/server/ClientProxy1_0.cpp +++ b/src/lib/server/ClientProxy1_0.cpp @@ -275,17 +275,7 @@ ClientProxy1_0::leave() void ClientProxy1_0::setClipboard(ClipboardID id, const IClipboard* clipboard) { - // ignore if this clipboard is already clean - if (m_clipboard[id].m_dirty) { - // this clipboard is now clean - m_clipboard[id].m_dirty = false; - Clipboard::copy(&m_clipboard[id].m_clipboard, clipboard); - - String data = m_clipboard[id].m_clipboard.marshall(); - - LOG((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getName().c_str(), data.size())); - ProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, &data); - } + // ignore -- deprecated in protocol 1.0 } void @@ -451,51 +441,8 @@ ClientProxy1_0::recvInfo() bool ClientProxy1_0::recvClipboard() { - // parse message - static String dataCached; - static size_t expectedSize; - ClipboardID id; - UInt8 mark; - UInt32 seqNum; - String data; - if (!ProtocolUtil::readf(getStream(), - kMsgDClipboard + 4, &id, &seqNum, &mark, &data)) { - return false; - } - - if (mark == kDataStart) { - expectedSize = synergy::string::stringToSizeType(data); - LOG((CLOG_DEBUG "start receiving clipboard data")); - dataCached.clear(); - } - else if (mark == kDataChunk) { - dataCached.append(data); - } - else if (mark == kDataEnd) { - LOG((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", getName().c_str(), id, seqNum, dataCached.size())); - - // validate - if (id >= kClipboardEnd) { - return false; - } - else if (expectedSize != dataCached.size()) { - LOG((CLOG_ERR "corrupted clipboard data, expected size=%d actual size=%d", expectedSize, dataCached.size())); - return false; - } - - // save clipboard - m_clipboard[id].m_clipboard.unmarshall(dataCached, 0); - m_clipboard[id].m_sequenceNumber = seqNum; - - // notify - ClipboardInfo* info = new ClipboardInfo; - info->m_id = id; - info->m_sequenceNumber = seqNum; - m_events->addEvent(Event(m_events->forClipboard().clipboardChanged(), - getEventTarget(), info)); - } - - return true; + // deprecated in protocol 1.0 + return false; } bool diff --git a/src/lib/server/ClientProxy1_0.h b/src/lib/server/ClientProxy1_0.h index d8dc48f6..4c7f020b 100644 --- a/src/lib/server/ClientProxy1_0.h +++ b/src/lib/server/ClientProxy1_0.h @@ -70,7 +70,7 @@ protected: virtual void resetHeartbeatTimer(); virtual void addHeartbeatTimer(); virtual void removeHeartbeatTimer(); - + virtual bool recvClipboard(); private: void disconnect(); void removeHandlers(); @@ -81,7 +81,6 @@ private: void handleFlatline(const Event&, void*); bool recvInfo(); - bool recvClipboard(); bool recvGrabClipboard(); protected: diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp index 17e19319..eb2c86af 100644 --- a/src/lib/server/ClientProxy1_6.cpp +++ b/src/lib/server/ClientProxy1_6.cpp @@ -94,3 +94,53 @@ ClientProxy1_6::handleClipboardSendingEvent(const Event& event, void*) ProtocolUtil::writef(getStream(), kMsgDClipboard, id, sequence, mark, &dataChunk); } + +bool +ClientProxy1_6::recvClipboard() +{ + // parse message + static String dataCached; + static size_t expectedSize; + ClipboardID id; + UInt8 mark; + UInt32 seqNum; + String data; + if (!ProtocolUtil::readf(getStream(), + kMsgDClipboard + 4, &id, &seqNum, &mark, &data)) { + return false; + } + + if (mark == kDataStart) { + expectedSize = synergy::string::stringToSizeType(data); + LOG((CLOG_DEBUG "start receiving clipboard data")); + dataCached.clear(); + } + else if (mark == kDataChunk) { + dataCached.append(data); + } + else if (mark == kDataEnd) { + LOG((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", getName().c_str(), id, seqNum, dataCached.size())); + + // validate + if (id >= kClipboardEnd) { + return false; + } + else if (expectedSize != dataCached.size()) { + LOG((CLOG_ERR "corrupted clipboard data, expected size=%d actual size=%d", expectedSize, dataCached.size())); + return false; + } + + // save clipboard + m_clipboard[id].m_clipboard.unmarshall(dataCached, 0); + m_clipboard[id].m_sequenceNumber = seqNum; + + // notify + ClipboardInfo* info = new ClipboardInfo; + info->m_id = id; + info->m_sequenceNumber = seqNum; + m_events->addEvent(Event(m_events->forClipboard().clipboardChanged(), + getEventTarget(), info)); + } + + return true; +} diff --git a/src/lib/server/ClientProxy1_6.h b/src/lib/server/ClientProxy1_6.h index 4963e277..d8c69b11 100644 --- a/src/lib/server/ClientProxy1_6.h +++ b/src/lib/server/ClientProxy1_6.h @@ -29,6 +29,7 @@ public: ~ClientProxy1_6(); virtual void setClipboard(ClipboardID id, const IClipboard* clipboard); + virtual bool recvClipboard(); private: void handleClipboardSendingEvent(const Event&, void*); From 342a345d1f6ee8d0f2100650ea8b9e000e39498f Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 21 May 2015 15:35:36 -0700 Subject: [PATCH 21/66] Fixed code style --- src/lib/synergy/ClipboardChunk.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/lib/synergy/ClipboardChunk.h b/src/lib/synergy/ClipboardChunk.h index df78cb08..b3f199fd 100644 --- a/src/lib/synergy/ClipboardChunk.h +++ b/src/lib/synergy/ClipboardChunk.h @@ -28,13 +28,16 @@ class ClipboardChunk : public Chunk { public: ClipboardChunk(size_t size); - static ClipboardChunk* start( + static ClipboardChunk* + start( ClipboardID id, UInt32 sequence, const String& size); - static ClipboardChunk* data( + static ClipboardChunk* + data( ClipboardID id, UInt32 sequence, const String& data); - static ClipboardChunk* end(ClipboardID id, UInt32 sequence); + static ClipboardChunk* + end(ClipboardID id, UInt32 sequence); }; From 1c28444cf657a4343f5ad2e86ffb915af7917518 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 21 May 2015 16:04:50 -0700 Subject: [PATCH 22/66] Refactored duplicated code #4601 --- src/lib/client/ServerProxy.cpp | 30 ++++----------------- src/lib/server/ClientProxy1_6.cpp | 41 +++++++---------------------- src/lib/synergy/ClipboardChunk.cpp | 42 ++++++++++++++++++++++++++++++ src/lib/synergy/ClipboardChunk.h | 16 ++++++++++++ 4 files changed, 72 insertions(+), 57 deletions(-) diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp index f970ad59..1576ca8f 100644 --- a/src/lib/client/ServerProxy.cpp +++ b/src/lib/client/ServerProxy.cpp @@ -556,38 +556,18 @@ ServerProxy::setClipboard() { // parse static String dataCached; - static size_t expectedSize; ClipboardID id; - UInt32 seqNum; - size_t mark = 0; - String data; - ProtocolUtil::readf(m_stream, kMsgDClipboard + 4, &id, &seqNum, &mark, &data); + UInt32 seq; + + int r = ClipboardChunk::assemble(m_stream, dataCached, id, seq); - if (mark == kDataStart) { - expectedSize = synergy::string::stringToSizeType(data); - LOG((CLOG_DEBUG "start receiving clipboard data")); - dataCached.clear(); - } - else if (mark == kDataChunk) { - dataCached.append(data); - } - else if (mark == kDataEnd) { + if (r == kFinish) { LOG((CLOG_DEBUG "received clipboard %d size=%d", id, dataCached.size())); - - // validate - if (id >= kClipboardEnd) { - return; - } - else if (expectedSize != dataCached.size()) { - LOG((CLOG_ERR "corrupted clipboard data, expected size=%d actual size=%d", expectedSize, dataCached.size())); - return; - } - + // forward Clipboard clipboard; clipboard.unmarshall(dataCached, 0); m_client->setClipboard(id, &clipboard); - expectedSize = 0; } } diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp index eb2c86af..2e359e8e 100644 --- a/src/lib/server/ClientProxy1_6.cpp +++ b/src/lib/server/ClientProxy1_6.cpp @@ -100,47 +100,24 @@ ClientProxy1_6::recvClipboard() { // parse message static String dataCached; - static size_t expectedSize; ClipboardID id; - UInt8 mark; - UInt32 seqNum; - String data; - if (!ProtocolUtil::readf(getStream(), - kMsgDClipboard + 4, &id, &seqNum, &mark, &data)) { - return false; - } - - if (mark == kDataStart) { - expectedSize = synergy::string::stringToSizeType(data); - LOG((CLOG_DEBUG "start receiving clipboard data")); - dataCached.clear(); - } - else if (mark == kDataChunk) { - dataCached.append(data); - } - else if (mark == kDataEnd) { - LOG((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", getName().c_str(), id, seqNum, dataCached.size())); - - // validate - if (id >= kClipboardEnd) { - return false; - } - else if (expectedSize != dataCached.size()) { - LOG((CLOG_ERR "corrupted clipboard data, expected size=%d actual size=%d", expectedSize, dataCached.size())); - return false; - } - + UInt32 seq; + + int r = ClipboardChunk::assemble(getStream(), dataCached, id, seq); + + if (r == kFinish) { + LOG((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", getName().c_str(), id, seq, dataCached.size())); // save clipboard m_clipboard[id].m_clipboard.unmarshall(dataCached, 0); - m_clipboard[id].m_sequenceNumber = seqNum; + m_clipboard[id].m_sequenceNumber = seq; // notify ClipboardInfo* info = new ClipboardInfo; info->m_id = id; - info->m_sequenceNumber = seqNum; + info->m_sequenceNumber = seq; m_events->addEvent(Event(m_events->forClipboard().clipboardChanged(), getEventTarget(), info)); } - + return true; } diff --git a/src/lib/synergy/ClipboardChunk.cpp b/src/lib/synergy/ClipboardChunk.cpp index 9d43953c..53b97203 100644 --- a/src/lib/synergy/ClipboardChunk.cpp +++ b/src/lib/synergy/ClipboardChunk.cpp @@ -17,7 +17,10 @@ #include "synergy/ClipboardChunk.h" +#include "synergy/ProtocolUtil.h" #include "synergy/protocol_types.h" +#include "io/IStream.h" +#include "base/Log.h" ClipboardChunk::ClipboardChunk(size_t size) : Chunk(size) @@ -79,3 +82,42 @@ ClipboardChunk::end(ClipboardID id, UInt32 sequence) return end; } + +int +ClipboardChunk::assemble(synergy::IStream* stream, + String& dataCached, + ClipboardID& id, + UInt32& sequence) +{ + static size_t expectedSize; + UInt8 mark; + String data; + + if (!ProtocolUtil::readf(stream, kMsgDClipboard + 4, &id, &sequence, &mark, &data)) { + return kError; + } + + if (mark == kDataStart) { + expectedSize = synergy::string::stringToSizeType(data); + LOG((CLOG_DEBUG "start receiving clipboard data")); + dataCached.clear(); + return kNotFinish; + } + else if (mark == kDataChunk) { + dataCached.append(data); + return kNotFinish; + } + else if (mark == kDataEnd) { + // validate + if (id >= kClipboardEnd) { + return kError; + } + else if (expectedSize != dataCached.size()) { + LOG((CLOG_ERR "corrupted clipboard data, expected size=%d actual size=%d", expectedSize, dataCached.size())); + return kError; + } + return kFinish; + } + + return kError; +} diff --git a/src/lib/synergy/ClipboardChunk.h b/src/lib/synergy/ClipboardChunk.h index b3f199fd..f534f4c6 100644 --- a/src/lib/synergy/ClipboardChunk.h +++ b/src/lib/synergy/ClipboardChunk.h @@ -24,6 +24,16 @@ #define CLIPBOARD_CHUNK_META_SIZE 7 +enum EAssembleResult { + kNotFinish, + kFinish, + kError +}; + +namespace synergy { +class IStream; +}; + class ClipboardChunk : public Chunk { public: ClipboardChunk(size_t size); @@ -40,4 +50,10 @@ public: const String& data); static ClipboardChunk* end(ClipboardID id, UInt32 sequence); + + static int assemble( + synergy::IStream* stream, + String& dataCached, + ClipboardID& id, + UInt32& sequence); }; From 1df566d241e65d99ab7c73f5d2d51a61963ab438 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 21 May 2015 17:50:55 -0700 Subject: [PATCH 23/66] Refactored duplicated code #4601 --- src/lib/client/ServerProxy.cpp | 28 +-------------------------- src/lib/server/ClientProxy1_6.cpp | 28 +-------------------------- src/lib/synergy/ClipboardChunk.cpp | 31 ++++++++++++++++++++++++++++++ src/lib/synergy/ClipboardChunk.h | 2 ++ 4 files changed, 35 insertions(+), 54 deletions(-) diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp index 1576ca8f..05b94fd9 100644 --- a/src/lib/client/ServerProxy.cpp +++ b/src/lib/client/ServerProxy.cpp @@ -921,33 +921,7 @@ ServerProxy::dragInfoReceived() void ServerProxy::handleClipboardSendingEvent(const Event& event, void*) { - void* data = event.getData(); - ClipboardChunk* clipboardData = reinterpret_cast(data); - - LOG((CLOG_DEBUG1 "sending clipboard chunk")); - - char* chunk = clipboardData->m_chunk; - ClipboardID id = chunk[0]; - UInt32* seq = reinterpret_cast(&chunk[1]); - UInt32 sequence = *seq; - UInt8 mark = chunk[5]; - String dataChunk(&chunk[6], clipboardData->m_dataSize); - - switch (mark) { - case kDataStart: - LOG((CLOG_DEBUG2 "file sending start: size=%s", dataChunk.c_str())); - break; - - case kDataChunk: - LOG((CLOG_DEBUG2 "file chunk sending: size=%i", dataChunk.size())); - break; - - case kDataEnd: - LOG((CLOG_DEBUG2 "file sending finished")); - break; - } - - ProtocolUtil::writef(m_stream, kMsgDClipboard, id, sequence, mark, &dataChunk); + ClipboardChunk::send(m_stream, event.getData()); } void diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp index 2e359e8e..8b429cca 100644 --- a/src/lib/server/ClientProxy1_6.cpp +++ b/src/lib/server/ClientProxy1_6.cpp @@ -66,33 +66,7 @@ ClientProxy1_6::setClipboard(ClipboardID id, const IClipboard* clipboard) void ClientProxy1_6::handleClipboardSendingEvent(const Event& event, void*) { - void* data = event.getData(); - ClipboardChunk* clipboardData = reinterpret_cast(data); - - LOG((CLOG_DEBUG1 "sending clipboard chunk")); - - char* chunk = clipboardData->m_chunk; - ClipboardID id = chunk[0]; - UInt32* seq = reinterpret_cast(&chunk[1]); - UInt32 sequence = *seq; - UInt8 mark = chunk[5]; - String dataChunk(&chunk[6], clipboardData->m_dataSize); - - switch (mark) { - case kDataStart: - LOG((CLOG_DEBUG2 "file sending start: size=%s", dataChunk.c_str())); - break; - - case kDataChunk: - LOG((CLOG_DEBUG2 "file chunk sending: size=%i", dataChunk.size())); - break; - - case kDataEnd: - LOG((CLOG_DEBUG2 "file sending finished")); - break; - } - - ProtocolUtil::writef(getStream(), kMsgDClipboard, id, sequence, mark, &dataChunk); + ClipboardChunk::send(getStream(), event.getData()); } bool diff --git a/src/lib/synergy/ClipboardChunk.cpp b/src/lib/synergy/ClipboardChunk.cpp index 53b97203..0a0cc5c6 100644 --- a/src/lib/synergy/ClipboardChunk.cpp +++ b/src/lib/synergy/ClipboardChunk.cpp @@ -121,3 +121,34 @@ ClipboardChunk::assemble(synergy::IStream* stream, return kError; } + +void +ClipboardChunk::send(synergy::IStream* stream, void* data) +{ + ClipboardChunk* clipboardData = reinterpret_cast(data); + + LOG((CLOG_DEBUG1 "sending clipboard chunk")); + + char* chunk = clipboardData->m_chunk; + ClipboardID id = chunk[0]; + UInt32* seq = reinterpret_cast(&chunk[1]); + UInt32 sequence = *seq; + UInt8 mark = chunk[5]; + String dataChunk(&chunk[6], clipboardData->m_dataSize); + + switch (mark) { + case kDataStart: + LOG((CLOG_DEBUG2 "sending clipboard chunk start: size=%s", dataChunk.c_str())); + break; + + case kDataChunk: + LOG((CLOG_DEBUG2 "sending clipboard chunk data: size=%i", dataChunk.size())); + break; + + case kDataEnd: + LOG((CLOG_DEBUG2 "sending clipboard finished")); + break; + } + + ProtocolUtil::writef(stream, kMsgDClipboard, id, sequence, mark, &dataChunk); +} diff --git a/src/lib/synergy/ClipboardChunk.h b/src/lib/synergy/ClipboardChunk.h index f534f4c6..c77ad781 100644 --- a/src/lib/synergy/ClipboardChunk.h +++ b/src/lib/synergy/ClipboardChunk.h @@ -56,4 +56,6 @@ public: String& dataCached, ClipboardID& id, UInt32& sequence); + + static void send(synergy::IStream* stream, void* data); }; From a81b88c730024a689e8f571d67d5a6dc2ba62097 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 22 May 2015 11:27:57 -0700 Subject: [PATCH 24/66] Refactored file assemble and send code #4601 --- src/lib/client/Client.cpp | 19 ------- src/lib/client/Client.h | 14 ++--- src/lib/client/ServerProxy.cpp | 73 +++---------------------- src/lib/client/ServerProxy.h | 5 -- src/lib/server/ClientProxy1_5.cpp | 74 ++++--------------------- src/lib/server/ClientProxy1_5.h | 5 -- src/lib/server/Server.cpp | 19 ------- src/lib/server/Server.h | 16 ++---- src/lib/synergy/ClipboardChunk.h | 6 --- src/lib/synergy/FileChunk.cpp | 90 +++++++++++++++++++++++++++++++ src/lib/synergy/FileChunk.h | 19 +++++-- src/lib/synergy/protocol_types.h | 9 +++- 12 files changed, 140 insertions(+), 209 deletions(-) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index a99d3695..5919dab7 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -780,25 +780,6 @@ Client::writeToDropDirThread(void*) m_receivedFileData); } -void -Client::clearReceivedFileData() -{ - m_receivedFileData.clear(); -} - -void -Client::setExpectedFileSize(String data) -{ - std::istringstream iss(data); - iss >> m_expectedFileSize; -} - -void -Client::fileChunkReceived(String data) -{ - m_receivedFileData += data; -} - void Client::dragInfoReceived(UInt32 fileNum, String data) { diff --git a/src/lib/client/Client.h b/src/lib/client/Client.h index 7ab25738..3a19d853 100644 --- a/src/lib/client/Client.h +++ b/src/lib/client/Client.h @@ -85,15 +85,6 @@ public: */ virtual void handshakeComplete(); - //! Clears the file buffer - void clearReceivedFileData(); - - //! Set the expected size of receiving file - void setExpectedFileSize(String data); - - //! Received a chunk of file data - void fileChunkReceived(String data); - //! Received drag information void dragInfoReceived(UInt32 fileNum, String data); @@ -131,7 +122,10 @@ public: bool isReceivedFileSizeValid(); //! Return expected file size - size_t getExpectedFileSize() { return m_expectedFileSize; } + size_t& getExpectedFileSize() { return m_expectedFileSize; } + + //! Return received file data + String& getReceivedFileData() { return m_receivedFileData; } //@} diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp index 05b94fd9..935c9a31 100644 --- a/src/lib/client/ServerProxy.cpp +++ b/src/lib/client/ServerProxy.cpp @@ -19,6 +19,7 @@ #include "client/ServerProxy.h" #include "client/Client.h" +#include "synergy/FileChunk.h" #include "synergy/ClipboardChunk.h" #include "synergy/StreamChunker.h" #include "synergy/Clipboard.h" @@ -37,8 +38,6 @@ // ServerProxy // -const UInt16 ServerProxy::m_intervalThreshold = 1; - ServerProxy::ServerProxy(Client* client, synergy::IStream* stream, IEventQueue* events) : m_client(client), m_stream(stream), @@ -53,10 +52,7 @@ ServerProxy::ServerProxy(Client* client, synergy::IStream* stream, IEventQueue* m_keepAliveAlarm(0.0), m_keepAliveAlarmTimer(NULL), m_parser(&ServerProxy::parseHandshakeMessage), - m_events(events), - m_stopwatch(true), - m_elapsedTime(0), - m_receivedDataSize(0) + m_events(events) { assert(m_client != NULL); assert(m_stream != NULL); @@ -860,50 +856,13 @@ ServerProxy::infoAcknowledgment() void ServerProxy::fileChunkReceived() { - // parse - UInt8 mark = 0; - String content; - ProtocolUtil::readf(m_stream, kMsgDFileTransfer + 4, &mark, &content); + int result = FileChunk::assemble( + m_stream, + m_client->getReceivedFileData(), + m_client->getExpectedFileSize()); - switch (mark) { - case kDataStart: - m_client->clearReceivedFileData(); - m_client->setExpectedFileSize(content); - if (CLOG->getFilter() >= kDEBUG2) { - LOG((CLOG_DEBUG2 "recv file data from server: size=%s", content.c_str())); - m_stopwatch.start(); - } - break; - - case kDataChunk: - m_client->fileChunkReceived(content); - if (CLOG->getFilter() >= kDEBUG2) { - LOG((CLOG_DEBUG2 "recv file data from server: size=%i", content.size())); - double interval = m_stopwatch.getTime(); - LOG((CLOG_DEBUG2 "recv file data from server: interval=%f s", interval)); - m_receivedDataSize += content.size(); - if (interval >= m_intervalThreshold) { - double averageSpeed = m_receivedDataSize / interval / 1000; - LOG((CLOG_DEBUG2 "recv file data from server: average speed=%f kb/s", averageSpeed)); - - m_receivedDataSize = 0; - m_elapsedTime += interval; - m_stopwatch.reset(); - } - } - break; - - case kDataEnd: + if (result == kFinish) { m_events->addEvent(Event(m_events->forIScreen().fileRecieveCompleted(), m_client)); - if (CLOG->getFilter() >= kDEBUG2) { - LOG((CLOG_DEBUG2 "file data transfer finished")); - m_elapsedTime += m_stopwatch.getTime(); - double averageSpeed = m_client->getExpectedFileSize() / m_elapsedTime / 1000; - LOG((CLOG_DEBUG2 "file data transfer finished: total time consumed=%f s", m_elapsedTime)); - LOG((CLOG_DEBUG2 "file data transfer finished: total data received=%i kb", m_client->getExpectedFileSize() / 1000)); - LOG((CLOG_DEBUG2 "file data transfer finished: total average speed=%f kb/s", averageSpeed)); - } - break; } } @@ -927,23 +886,7 @@ ServerProxy::handleClipboardSendingEvent(const Event& event, void*) void ServerProxy::fileChunkSending(UInt8 mark, char* data, size_t dataSize) { - String chunk(data, dataSize); - - switch (mark) { - case kDataStart: - LOG((CLOG_DEBUG2 "file sending start: size=%s", data)); - break; - - case kDataChunk: - LOG((CLOG_DEBUG2 "file chunk sending: size=%i", chunk.size())); - break; - - case kDataEnd: - LOG((CLOG_DEBUG2 "file sending finished")); - break; - } - - ProtocolUtil::writef(m_stream, kMsgDFileTransfer, mark, &chunk); + FileChunk::send(m_stream, mark, data, dataSize); } void diff --git a/src/lib/client/ServerProxy.h b/src/lib/client/ServerProxy.h index 7f498cef..144fd000 100644 --- a/src/lib/client/ServerProxy.h +++ b/src/lib/client/ServerProxy.h @@ -130,9 +130,4 @@ private: MessageParser m_parser; IEventQueue* m_events; - - Stopwatch m_stopwatch; - double m_elapsedTime; - size_t m_receivedDataSize; - static const UInt16 m_intervalThreshold; }; diff --git a/src/lib/server/ClientProxy1_5.cpp b/src/lib/server/ClientProxy1_5.cpp index fee91a4f..bc8ddde6 100644 --- a/src/lib/server/ClientProxy1_5.cpp +++ b/src/lib/server/ClientProxy1_5.cpp @@ -18,6 +18,7 @@ #include "server/ClientProxy1_5.h" #include "server/Server.h" +#include "synergy/FileChunk.h" #include "synergy/StreamChunker.h" #include "synergy/ProtocolUtil.h" #include "io/IStream.h" @@ -29,14 +30,9 @@ // ClientProxy1_5 // -const UInt16 ClientProxy1_5::m_intervalThreshold = 1; - ClientProxy1_5::ClientProxy1_5(const String& name, synergy::IStream* stream, Server* server, IEventQueue* events) : ClientProxy1_4(name, stream, server, events), - m_events(events), - m_stopwatch(true), - m_elapsedTime(0), - m_receivedDataSize(0) + m_events(events) { } @@ -55,23 +51,7 @@ ClientProxy1_5::sendDragInfo(UInt32 fileCount, const char* info, size_t size) void ClientProxy1_5::fileChunkSending(UInt8 mark, char* data, size_t dataSize) { - String chunk(data, dataSize); - - switch (mark) { - case kDataStart: - LOG((CLOG_DEBUG2 "file sending start: size=%s", data)); - break; - - case kDataChunk: - LOG((CLOG_DEBUG2 "file chunk sending: size=%i", chunk.size())); - break; - - case kDataEnd: - LOG((CLOG_DEBUG2 "file sending finished")); - break; - } - - ProtocolUtil::writef(getStream(), kMsgDFileTransfer, mark, &chunk); + FileChunk::send(getStream(), mark, data, dataSize); } bool @@ -93,51 +73,15 @@ ClientProxy1_5::parseMessage(const UInt8* code) void ClientProxy1_5::fileChunkReceived() { - // parse - UInt8 mark = 0; - String content; - ProtocolUtil::readf(getStream(), kMsgDFileTransfer + 4, &mark, &content); - Server* server = getServer(); - switch (mark) { - case kDataStart: - server->clearReceivedFileData(); - server->setExpectedFileSize(content); - if (CLOG->getFilter() >= kDEBUG2) { - LOG((CLOG_DEBUG2 "recv file data from client: file size=%s", content.c_str())); - m_stopwatch.start(); - } - break; + int result = FileChunk::assemble( + getStream(), + server->getReceivedFileData(), + server->getExpectedFileSize()); + - case kDataChunk: - server->fileChunkReceived(content); - if (CLOG->getFilter() >= kDEBUG2) { - LOG((CLOG_DEBUG2 "recv file data from client: chunck size=%i", content.size())); - double interval = m_stopwatch.getTime(); - m_receivedDataSize += content.size(); - LOG((CLOG_DEBUG2 "recv file data from client: interval=%f s", interval)); - if (interval >= m_intervalThreshold) { - double averageSpeed = m_receivedDataSize / interval / 1000; - LOG((CLOG_DEBUG2 "recv file data from client: average speed=%f kb/s", averageSpeed)); - - m_receivedDataSize = 0; - m_elapsedTime += interval; - m_stopwatch.reset(); - } - } - break; - - case kDataEnd: + if (result == kFinish) { m_events->addEvent(Event(m_events->forIScreen().fileRecieveCompleted(), server)); - if (CLOG->getFilter() >= kDEBUG2) { - LOG((CLOG_DEBUG2 "file data transfer finished")); - m_elapsedTime += m_stopwatch.getTime(); - double averageSpeed = getServer()->getExpectedFileSize() / m_elapsedTime / 1000; - LOG((CLOG_DEBUG2 "file data transfer finished: total time consumed=%f s", m_elapsedTime)); - LOG((CLOG_DEBUG2 "file data transfer finished: total data received=%i kb", getServer()->getExpectedFileSize() / 1000)); - LOG((CLOG_DEBUG2 "file data transfer finished: total average speed=%f kb/s", averageSpeed)); - } - break; } } diff --git a/src/lib/server/ClientProxy1_5.h b/src/lib/server/ClientProxy1_5.h index 8229739f..3347f170 100644 --- a/src/lib/server/ClientProxy1_5.h +++ b/src/lib/server/ClientProxy1_5.h @@ -38,9 +38,4 @@ public: private: IEventQueue* m_events; - - Stopwatch m_stopwatch; - double m_elapsedTime; - size_t m_receivedDataSize; - static const UInt16 m_intervalThreshold; }; diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 44f68de5..8c0574ef 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -2338,25 +2338,6 @@ Server::KeyboardBroadcastInfo::alloc(State state, const String& screens) return info; } -void -Server::clearReceivedFileData() -{ - m_receivedFileData.clear(); -} - -void -Server::setExpectedFileSize(String data) -{ - std::istringstream iss(data); - iss >> m_expectedFileSize; -} - -void -Server::fileChunkReceived(String data) -{ - m_receivedFileData += data; -} - bool Server::isReceivedFileSizeValid() { diff --git a/src/lib/server/Server.h b/src/lib/server/Server.h index 631ca762..d85a113d 100644 --- a/src/lib/server/Server.h +++ b/src/lib/server/Server.h @@ -141,15 +141,6 @@ public: */ void disconnect(); - //! Clears the file buffer - void clearReceivedFileData(); - - //! Set the expected size of receiving file - void setExpectedFileSize(String data); - - //! Received a chunk of file data - void fileChunkReceived(String data); - //! Create a new thread and use it to send file to client void sendFileToClient(const char* filename); @@ -178,8 +169,11 @@ public: //! Return true if recieved file size is valid bool isReceivedFileSizeValid(); - //! Return expected file size - size_t getExpectedFileSize() { return m_expectedFileSize; } + //! Return expected file data size + size_t& getExpectedFileSize() { return m_expectedFileSize; } + + //! Return received file data + String& getReceivedFileData() { return m_receivedFileData; } //@} diff --git a/src/lib/synergy/ClipboardChunk.h b/src/lib/synergy/ClipboardChunk.h index c77ad781..3715b941 100644 --- a/src/lib/synergy/ClipboardChunk.h +++ b/src/lib/synergy/ClipboardChunk.h @@ -24,12 +24,6 @@ #define CLIPBOARD_CHUNK_META_SIZE 7 -enum EAssembleResult { - kNotFinish, - kFinish, - kError -}; - namespace synergy { class IStream; }; diff --git a/src/lib/synergy/FileChunk.cpp b/src/lib/synergy/FileChunk.cpp index 82c103cc..7a843cb4 100644 --- a/src/lib/synergy/FileChunk.cpp +++ b/src/lib/synergy/FileChunk.cpp @@ -17,7 +17,13 @@ #include "synergy/FileChunk.h" +#include "synergy/ProtocolUtil.h" #include "synergy/protocol_types.h" +#include "io/IStream.h" +#include "base/Stopwatch.h" +#include "base/Log.h" + +static const UInt16 kIntervalThreshold = 1; FileChunk::FileChunk(size_t size) : Chunk(size) @@ -60,3 +66,87 @@ FileChunk::end() return end; } + +int +FileChunk::assemble(synergy::IStream* stream, String& dataReceived, size_t& expectedSize) +{ + // parse + UInt8 mark = 0; + String content; + static size_t receivedDataSize; + static double elapsedTime; + static Stopwatch stopwatch; + + if (!ProtocolUtil::readf(stream, kMsgDFileTransfer + 4, &mark, &content)) { + return kError; + } + + switch (mark) { + case kDataStart: + dataReceived.clear(); + expectedSize = synergy::string::stringToSizeType(content); + receivedDataSize = 0; + elapsedTime = 0; + stopwatch.reset(); + + if (CLOG->getFilter() >= kDEBUG2) { + LOG((CLOG_DEBUG2 "recv file data from client: file size=%s", content.c_str())); + stopwatch.start(); + } + return kNotFinish; + + case kDataChunk: + dataReceived.append(content); + if (CLOG->getFilter() >= kDEBUG2) { + LOG((CLOG_DEBUG2 "recv file data from client: chunck size=%i", content.size())); + double interval = stopwatch.getTime(); + receivedDataSize += content.size(); + LOG((CLOG_DEBUG2 "recv file data from client: interval=%f s", interval)); + if (interval >= kIntervalThreshold) { + double averageSpeed = receivedDataSize / interval / 1000; + LOG((CLOG_DEBUG2 "recv file data from client: average speed=%f kb/s", averageSpeed)); + + receivedDataSize = 0; + elapsedTime += interval; + stopwatch.reset(); + } + } + return kNotFinish; + + case kDataEnd: + //m_events->addEvent(Event(m_events->forIScreen().fileRecieveCompleted(), server)); + if (CLOG->getFilter() >= kDEBUG2) { + LOG((CLOG_DEBUG2 "file data transfer finished")); + elapsedTime += stopwatch.getTime(); + double averageSpeed = expectedSize / elapsedTime / 1000; + LOG((CLOG_DEBUG2 "file data transfer finished: total time consumed=%f s", elapsedTime)); + LOG((CLOG_DEBUG2 "file data transfer finished: total data received=%i kb", expectedSize / 1000)); + LOG((CLOG_DEBUG2 "file data transfer finished: total average speed=%f kb/s", averageSpeed)); + } + return kFinish; + } + + return kError; +} + +void +FileChunk::send(synergy::IStream* stream, UInt8 mark, char* data, size_t dataSize) +{ + String chunk(data, dataSize); + + switch (mark) { + case kDataStart: + LOG((CLOG_DEBUG2 "sending file chunk start: size=%s", data)); + break; + + case kDataChunk: + LOG((CLOG_DEBUG2 "sending file chunk: size=%i", chunk.size())); + break; + + case kDataEnd: + LOG((CLOG_DEBUG2 "sending file finished")); + break; + } + + ProtocolUtil::writef(stream, kMsgDFileTransfer, mark, &chunk); +} diff --git a/src/lib/synergy/FileChunk.h b/src/lib/synergy/FileChunk.h index baf8faa8..2c2e0ee5 100644 --- a/src/lib/synergy/FileChunk.h +++ b/src/lib/synergy/FileChunk.h @@ -23,11 +23,24 @@ #define FILE_CHUNK_META_SIZE 2 +namespace synergy { +class IStream; +}; + class FileChunk : public Chunk { public: FileChunk(size_t size); - static FileChunk* start(const String& size); - static FileChunk* data(UInt8* data, size_t dataSize); - static FileChunk* end(); + static FileChunk* start(const String& size); + static FileChunk* data(UInt8* data, size_t dataSize); + static FileChunk* end(); + static int assemble( + synergy::IStream* stream, + String& dataCached, + size_t& expectedSize); + static void send( + synergy::IStream* stream, + UInt8 mark, + char* data, + size_t dataSize); }; diff --git a/src/lib/synergy/protocol_types.h b/src/lib/synergy/protocol_types.h index b34213f4..0fc0c265 100644 --- a/src/lib/synergy/protocol_types.h +++ b/src/lib/synergy/protocol_types.h @@ -70,13 +70,20 @@ enum EDirectionMask { kBottomMask = 1 << kBottom }; -// file transfer constants +// Data transfer constants enum EDataTransfer { kDataStart = 1, kDataChunk = 2, kDataEnd = 3 }; +// Data received constants +enum EDataReceived { + kNotFinish, + kFinish, + kError +}; + // // message codes (trailing NUL is not part of code). in comments, $n // refers to the n'th argument (counting from one). message codes are From 36ddc4f1c1bf0cd7514c181ce4eabba46b796937 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 22 May 2015 13:10:22 -0700 Subject: [PATCH 25/66] Fixed indentation --- src/lib/synergy/StreamChunker.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/synergy/StreamChunker.h b/src/lib/synergy/StreamChunker.h index e1dd4605..e0b2e1fa 100644 --- a/src/lib/synergy/StreamChunker.h +++ b/src/lib/synergy/StreamChunker.h @@ -37,5 +37,5 @@ public: void* eventTarget); private: - static const size_t m_chunkSize; + static const size_t m_chunkSize; }; From ff9ad5554a854c329b52221e65dda27326a52b3b Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 22 May 2015 13:30:50 -0700 Subject: [PATCH 26/66] Used different chunk size for SSL and non-SSL socket #4601 --- src/lib/synergy/ArgParser.cpp | 2 ++ src/lib/synergy/StreamChunker.cpp | 21 +++++++++++++++++---- src/lib/synergy/StreamChunker.h | 3 ++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index bcc731ab..a2021481 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -17,6 +17,7 @@ #include "synergy/ArgParser.h" +#include "synergy/StreamChunker.h" #include "synergy/App.h" #include "synergy/ServerArgs.h" #include "synergy/ClientArgs.h" @@ -288,6 +289,7 @@ ArgParser::parseGenericArgs(int argc, const char* const* argv, int& i) } else if (isArg(i, argc, argv, NULL, "--enable-crypto")) { argsBase().m_enableCrypto = true; + StreamChunker::updateChunkSize(true); } else if (isArg(i, argc, argv, NULL, "--profile-dir", 1)) { argsBase().m_profileDirectory = argv[++i]; diff --git a/src/lib/synergy/StreamChunker.cpp b/src/lib/synergy/StreamChunker.cpp index 60700407..4058385e 100644 --- a/src/lib/synergy/StreamChunker.cpp +++ b/src/lib/synergy/StreamChunker.cpp @@ -35,7 +35,10 @@ using namespace std; -const size_t StreamChunker::m_chunkSize = 512 * 1024; // 512kb +#define SOCKET_CHUNK_SIZE 512 * 1024; // 512kb +#define SECURE_SOCKET_CHUNK_SIZE 2 * 1024; // 2kb + +size_t StreamChunker::s_chunkSize = SOCKET_CHUNK_SIZE; void StreamChunker::sendFile( @@ -61,7 +64,7 @@ StreamChunker::sendFile( // send chunk messages with a fixed chunk size size_t sentLength = 0; - size_t chunkSize = m_chunkSize; + size_t chunkSize = s_chunkSize; Stopwatch stopwatch; stopwatch.start(); file.seekg (0, std::ios::beg); @@ -115,9 +118,8 @@ StreamChunker::sendClipboard( events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, sizeMessage)); // send clipboard chunk with a fixed size - // TODO: 4096 fails and this shouldn't a magic number size_t sentLength = 0; - size_t chunkSize = 2048; + size_t chunkSize = s_chunkSize; Stopwatch stopwatch; stopwatch.start(); while (true) { @@ -147,3 +149,14 @@ StreamChunker::sendClipboard( events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, end)); } + +void +StreamChunker::updateChunkSize(bool useSecureSocket) +{ + if (useSecureSocket) { + s_chunkSize = SECURE_SOCKET_CHUNK_SIZE; + } + else { + s_chunkSize = SOCKET_CHUNK_SIZE; + } +} diff --git a/src/lib/synergy/StreamChunker.h b/src/lib/synergy/StreamChunker.h index e0b2e1fa..a4be2714 100644 --- a/src/lib/synergy/StreamChunker.h +++ b/src/lib/synergy/StreamChunker.h @@ -35,7 +35,8 @@ public: UInt32 sequence, IEventQueue* events, void* eventTarget); + static void updateChunkSize(bool useSecureSocket); private: - static const size_t m_chunkSize; + static size_t s_chunkSize; }; From 905dbfee90bac192a74d1cec3bd5c80b5e56d277 Mon Sep 17 00:00:00 2001 From: Adam Potolsky Date: Fri, 22 May 2015 16:26:40 -0700 Subject: [PATCH 27/66] Fixed order of initializers for mac build #4697 --- src/lib/plugin/ns/SecureSocket.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 91f90bce..cb65bd85 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -52,8 +52,8 @@ SecureSocket::SecureSocket( SocketMultiplexer* socketMultiplexer) : TCPSocket(events, socketMultiplexer), m_secureReady(false), - m_maxRetry(MAX_RETRY_COUNT), - m_fatal(false) + m_fatal(false), + m_maxRetry(MAX_RETRY_COUNT) { } @@ -63,8 +63,8 @@ SecureSocket::SecureSocket( ArchSocket socket) : TCPSocket(events, socketMultiplexer, socket), m_secureReady(false), - m_maxRetry(MAX_RETRY_COUNT), - m_fatal(false) + m_fatal(false), + m_maxRetry(MAX_RETRY_COUNT) { } From fb3252efef64d2272e9c7afcd1818e6c7a42cb51 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Sat, 23 May 2015 00:34:00 +0100 Subject: [PATCH 28/66] Added a way to interrupt sending clipboard/file #4601 --- src/lib/client/Client.cpp | 10 +++++++- src/lib/server/Server.cpp | 11 +++++++- src/lib/synergy/StreamChunker.cpp | 42 +++++++++++++++++++++++++++++++ src/lib/synergy/StreamChunker.h | 10 ++++++-- 4 files changed, 69 insertions(+), 4 deletions(-) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 5919dab7..ff57d540 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -265,7 +265,11 @@ Client::leave() m_screen->leave(); m_active = false; - + + if (m_sendClipboardThread != NULL) { + StreamChunker::interruptClipboard(); + } + m_sendClipboardThread = new Thread( new TMethodJob( this, @@ -803,6 +807,10 @@ Client::isReceivedFileSizeValid() void Client::sendFileToServer(const char* filename) { + if (m_sendFileThread != NULL) { + StreamChunker::interruptFile(); + } + m_sendFileThread = new Thread( new TMethodJob( this, &Client::sendFileThread, diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 8c0574ef..832a03b0 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -505,7 +505,12 @@ Server::switchScreen(BaseClientProxy* dst, m_active->enter(x, y, m_seqNum, m_primaryClient->getToggleMask(), forScreensaver); - + // if already sending clipboard, we need to interupt it, otherwise + // clipboard data could be corrupted on the other side + if (m_sendClipboardThread != NULL) { + StreamChunker::interruptClipboard(); + } + // send the clipboard data to new active screen m_sendClipboardThread = new Thread( new TMethodJob( @@ -2347,6 +2352,10 @@ Server::isReceivedFileSizeValid() void Server::sendFileToClient(const char* filename) { + if (m_sendFileThread != NULL) { + StreamChunker::interruptFile(); + } + m_sendFileThread = new Thread( new TMethodJob( this, &Server::sendFileThread, diff --git a/src/lib/synergy/StreamChunker.cpp b/src/lib/synergy/StreamChunker.cpp index 4058385e..46230ccd 100644 --- a/src/lib/synergy/StreamChunker.cpp +++ b/src/lib/synergy/StreamChunker.cpp @@ -39,6 +39,11 @@ using namespace std; #define SECURE_SOCKET_CHUNK_SIZE 2 * 1024; // 2kb size_t StreamChunker::s_chunkSize = SOCKET_CHUNK_SIZE; +bool StreamChunker::s_isChunkingClipboard = false; +bool StreamChunker::s_interruptClipboard = false; +bool StreamChunker::s_isChunkingFile = false; +bool StreamChunker::s_interruptFile = false; + void StreamChunker::sendFile( @@ -46,6 +51,8 @@ StreamChunker::sendFile( IEventQueue* events, void* eventTarget) { + s_isChunkingFile = true; + std::fstream file(reinterpret_cast(filename), std::ios::in | std::ios::binary); if (!file.is_open()) { @@ -69,6 +76,11 @@ StreamChunker::sendFile( stopwatch.start(); file.seekg (0, std::ios::beg); while (true) { + if (s_interruptFile) { + s_interruptFile = false; + break; + } + if (stopwatch.getTime() > PAUSE_TIME_HACK) { // make sure we don't read too much from the mock data. if (sentLength + chunkSize > size) { @@ -100,6 +112,8 @@ StreamChunker::sendFile( events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, end)); file.close(); + + s_isChunkingFile = false; } void @@ -111,6 +125,8 @@ StreamChunker::sendClipboard( IEventQueue* events, void* eventTarget) { + s_isChunkingClipboard = true; + // send first message (data size) String dataSize = synergy::string::sizeTypeToString(size); ClipboardChunk* sizeMessage = ClipboardChunk::start(id, sequence, dataSize); @@ -122,7 +138,13 @@ StreamChunker::sendClipboard( size_t chunkSize = s_chunkSize; Stopwatch stopwatch; stopwatch.start(); + while (true) { + if (s_interruptClipboard) { + s_interruptClipboard = false; + break; + } + if (stopwatch.getTime() > 0.1f) { // make sure we don't read too much from the mock data. if (sentLength + chunkSize > size) { @@ -148,6 +170,8 @@ StreamChunker::sendClipboard( ClipboardChunk* end = ClipboardChunk::end(id, sequence); events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, end)); + + s_isChunkingClipboard = false; } void @@ -160,3 +184,21 @@ StreamChunker::updateChunkSize(bool useSecureSocket) s_chunkSize = SOCKET_CHUNK_SIZE; } } + +void +StreamChunker::interruptFile() +{ + if (s_isChunkingFile) { + s_interruptFile = true; + LOG((CLOG_INFO "previous dragged file has become invalid")); + } +} + +void +StreamChunker::interruptClipboard() +{ + if (s_isChunkingClipboard) { + s_interruptClipboard = true; + LOG((CLOG_INFO "previous clipboard data has become invalid")); + } +} diff --git a/src/lib/synergy/StreamChunker.h b/src/lib/synergy/StreamChunker.h index a4be2714..b0bfb341 100644 --- a/src/lib/synergy/StreamChunker.h +++ b/src/lib/synergy/StreamChunker.h @@ -36,7 +36,13 @@ public: IEventQueue* events, void* eventTarget); static void updateChunkSize(bool useSecureSocket); - + static void interruptFile(); + static void interruptClipboard(); + private: - static size_t s_chunkSize; + static size_t s_chunkSize; + static bool s_isChunkingClipboard; + static bool s_interruptClipboard; + static bool s_isChunkingFile; + static bool s_interruptFile; }; From f322a79760d025704e96840d7826713d8d4b3b7a Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 13:07:36 +0100 Subject: [PATCH 29/66] Copy plugins to config dir on Mac #4703 --- src/lib/plugin/ns/CMakeLists.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lib/plugin/ns/CMakeLists.txt b/src/lib/plugin/ns/CMakeLists.txt index 765ea795..31aa814b 100644 --- a/src/lib/plugin/ns/CMakeLists.txt +++ b/src/lib/plugin/ns/CMakeLists.txt @@ -85,18 +85,20 @@ if (WIN32) ..\\..\\..\\..\\..\\ext\\${OPENSSL_PLAT_DIR}\\out32dll\\ssleay32.* ..\\..\\..\\..\\..\\bin\\${CMAKE_CFG_INTDIR} ) -else() +endif() + +if (UNIX) if (APPLE) add_custom_command( TARGET ns POST_BUILD COMMAND mkdir -p - ${CMAKE_SOURCE_DIR}/bin/plugins + ${CMAKE_SOURCE_DIR}/bin/${CMAKE_CFG_INTDIR}/plugins && cp ${CMAKE_SOURCE_DIR}/lib/${CMAKE_CFG_INTDIR}/libns.* - ${CMAKE_SOURCE_DIR}/bin/plugins/ + ${CMAKE_SOURCE_DIR}/bin/${CMAKE_CFG_INTDIR}/plugins/ ) else() if (CMAKE_BUILD_TYPE STREQUAL Debug) From 18344e6b527bf605442a6526bc73abdf424cdb21 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 13:08:47 +0100 Subject: [PATCH 30/66] Copy ns plugin to debug dir when in debug mode #4704 --- src/lib/plugin/ns/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/plugin/ns/CMakeLists.txt b/src/lib/plugin/ns/CMakeLists.txt index 31aa814b..3e40a472 100644 --- a/src/lib/plugin/ns/CMakeLists.txt +++ b/src/lib/plugin/ns/CMakeLists.txt @@ -106,11 +106,11 @@ if (UNIX) TARGET ns POST_BUILD COMMAND mkdir -p - ${CMAKE_SOURCE_DIR}/bin/plugins + ${CMAKE_SOURCE_DIR}/bin/debug/plugins && cp ${CMAKE_SOURCE_DIR}/lib/debug/libns.* - ${CMAKE_SOURCE_DIR}/bin/plugins/ + ${CMAKE_SOURCE_DIR}/bin/debug/plugins/ ) else() add_custom_command( From 6788f3db3954ce0d177418d1018663b9ace1891f Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 14:21:09 +0100 Subject: [PATCH 31/66] Moved installer output to Release build config dir #4706 --- ext/toolchain/generators.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ext/toolchain/generators.py b/ext/toolchain/generators.py index f07beda4..3a754759 100644 --- a/ext/toolchain/generators.py +++ b/ext/toolchain/generators.py @@ -30,6 +30,13 @@ class Generator(object): def getSourceDir(self): return self.sourceDir +class VisualStudioGenerator(Generator): + def __init__(self, version): + super(VisualStudioGenerator, self).__init__('Visual Studio ' + version) + + def getBinDir(self, target=''): + return super(VisualStudioGenerator, self).getBinDir(target) + '/' + target + class MakefilesGenerator(Generator): def __init__(self): super(MakefilesGenerator, self).__init__('Unix Makefiles') From d175ad5c70f4a62c90e00fcdb9509330aa32571e Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 14:55:03 +0100 Subject: [PATCH 32/66] Moved installer output to Release build config dir #4706 Missing change from last commit --- ext/toolchain/commands1.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index d06b29ee..754c50c4 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -17,7 +17,7 @@ # TODO: split this file up, it's too long! import sys, os, ConfigParser, shutil, re, ftputil, zipfile, glob, commands -from generators import Generator, EclipseGenerator, XcodeGenerator, MakefilesGenerator +from generators import VisualStudioGenerator, EclipseGenerator, XcodeGenerator, MakefilesGenerator from getopt import gnu_getopt if sys.version_info >= (2, 4): @@ -254,12 +254,12 @@ class InternalCommands: gmockDir = 'gmock-1.6.0' win32_generators = { - 1 : Generator('Visual Studio 10'), - 2 : Generator('Visual Studio 10 Win64'), - 3 : Generator('Visual Studio 9 2008'), - 4 : Generator('Visual Studio 9 2008 Win64'), - 5 : Generator('Visual Studio 8 2005'), - 6 : Generator('Visual Studio 8 2005 Win64') + 1 : VisualStudioGenerator('10'), + 2 : VisualStudioGenerator('10 Win64'), + 3 : VisualStudioGenerator('9 2008'), + 4 : VisualStudioGenerator('9 2008 Win64'), + 5 : VisualStudioGenerator('8 2005'), + 6 : VisualStudioGenerator('8 2005 Win64') } unix_generators = { From 6ba2ddeb7d81b8f64bf61063328546d4269b719a Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 15:07:32 +0100 Subject: [PATCH 33/66] Added ns upload support to distftp step #4695 Needed to refactor the 'figure out platform and ext' code. --- ext/toolchain/commands1.py | 115 ++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 54 deletions(-) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index 754c50c4..f8f14854 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -840,7 +840,7 @@ class InternalCommands: pwd = lines[0] if (dist): - self.signFile(pfx, pwd, 'bin', self.dist_name('win')) + self.signFile(pfx, pwd, 'bin/Release', self.dist_name('win')) else: self.signFile(pfx, pwd, 'bin/Release', 'synergy.exe') self.signFile(pfx, pwd, 'bin/Release', 'synergyc.exe') @@ -1281,7 +1281,7 @@ class InternalCommands: arch) old = "bin/Release/synergy.msi" - new = "bin/%s" % (filename) + new = "bin/Release/%s" % (filename) try: os.remove(new) @@ -1344,56 +1344,48 @@ class InternalCommands: return major + '.' + minor + '.' + rev + def ftpUpload(self, ftp, source, target): + print "Uploading '%s' as '%s' to FTP server '%s'..." % ( + source, target, ftp.host) + ftp.run(source, target) + print 'Done' + def distftp(self, type, ftp): if not type: - raise Exception('Type not specified.') - - if not ftp: - raise Exception('FTP info not defined.') + raise Exception('Platform type not specified.') self.loadConfig() - src = self.dist_name(type) - dest = self.dist_name_rev(type) - print 'Uploading %s to FTP server %s...' % (dest, ftp.host) binDir = self.getGenerator().getBinDir('Release') - ftp.run(binDir + '/' + src, dest) - print 'Done' - - def getDebianArch(self): - if os.uname()[4][:3] == 'arm': - return 'armhf' + + packageSource = binDir + '/' + self.dist_name(type) + packageTarget = self.dist_name_rev(type) + self.ftpUpload(ftp, packageSource, packageTarget) + + if (type != 'src'): + pluginsDir = binDir + '/plugins' + nsPluginSource = self.findLibraryFile(type, pluginsDir, 'ns') + nsPluginTarget = self.getLibraryDistFilename(type, pluginsDir, 'ns') + self.ftpUpload(ftp, nsPluginSource, nsPluginTarget) # os_bits should be loaded with '32bit' or '64bit' import platform (os_bits, other) = platform.architecture() + def findLibraryFile(self, type, dir, name): + (platform, packageExt, libraryExt) = self.getDistributePlatformInfo(type) + ext = libraryExt + + pattern = name + '\.' + ext + + for filename in os.listdir(dir): + if re.search(pattern, filename): + return dir + '/' + filename - # get platform based on current platform - if os_bits == '32bit': - return 'i386' - elif os_bits == '64bit': - return 'amd64' - else: - raise Exception("unknown os bits: " + os_bits) + raise Exception('Could not find library name with pattern: ' + pattern) - def getLinuxPlatform(self): - if os.uname()[4][:3] == 'arm': - return 'Linux-armv6l' - - # os_bits should be loaded with '32bit' or '64bit' - import platform - (os_bits, other) = platform.architecture() - - # get platform based on current platform - if os_bits == '32bit': - return 'Linux-i686' - elif os_bits == '64bit': - return 'Linux-x86_64' - else: - raise Exception("unknown os bits: " + os_bits) - - def dist_name(self, type): + def getDistributePlatformInfo(self, type): ext = None + libraryExt = None platform = None if type == 'src': @@ -1401,14 +1393,14 @@ class InternalCommands: platform = 'Source' elif type == 'rpm' or type == 'deb': - ext = type - platform = self.getLinuxPlatform() + libraryExt = 'so' + platform = self.getLinuxPlatform() elif type == 'win': - # get platform based on last generator used ext = 'msi' + libraryExt = 'dll' generator = self.getGeneratorFromConfig().cmakeName if generator.find('Win64') != -1: platform = 'Windows-x64' @@ -1417,18 +1409,23 @@ class InternalCommands: elif type == 'mac': ext = "dmg" + libraryExt = 'dylib' platform = self.getMacPackageName() if not platform: - raise Exception('Unable to detect package platform.') - - pattern = re.escape(self.project + '-') + '\d+\.\d+\.\d+' + re.escape('-' + platform + '.' + ext) - - target = '' - if type == 'mac': - target = 'Release' + raise Exception('Unable to detect distributable platform.') - for filename in os.listdir(self.getBinDir(target)): + return (platform, ext, libraryExt) + + def dist_name(self, type): + (platform, packageExt, libraryExt) = self.getDistributePlatformInfo(type) + ext = packageExt + + pattern = ( + re.escape(self.project + '-') + '\d+\.\d+\.\d+' + + re.escape('-' + platform + '.' + ext)) + + for filename in os.listdir(self.getBinDir('Release')): if re.search(pattern, filename): return filename @@ -1441,6 +1438,15 @@ class InternalCommands: replace = "%s-%s" % ( self.getGitBranchName(), self.getGitRevision()) return re.sub(pattern, replace, self.dist_name(type)) + + def getDebianArch(self): + if os.uname()[4][:3] == 'arm': + return 'armhf' + + + if os.uname()[4][:3] == 'arm': + return 'Linux-armv6l' + def dist_usage(self): print ('Usage: %s package [package-type]\n' @@ -1913,10 +1919,11 @@ class CommandHandler: elif o == '--dir': dir = a - ftp = None - if host: - ftp = ftputil.FtpUploader( - host, user, password, dir) + if not host: + raise Exception('FTP host was not specified.') + + ftp = ftputil.FtpUploader( + host, user, password, dir) self.ic.distftp(type, ftp) From 74dda188dce4a19201fc8b45998967de6d771aea Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 15:17:18 +0100 Subject: [PATCH 34/66] Reintroduced accidentally removed functions #4695 --- ext/toolchain/commands1.py | 33 +- ext/toolchain/fuckfuck.py | 2000 +++++++++++++++++++++++++++++++++++ ext/toolchain/shitindent.py | 31 + 3 files changed, 2059 insertions(+), 5 deletions(-) create mode 100644 ext/toolchain/fuckfuck.py create mode 100644 ext/toolchain/shitindent.py diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index f8f14854..32716b12 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -1439,14 +1439,37 @@ class InternalCommands: self.getGitBranchName(), self.getGitRevision()) return re.sub(pattern, replace, self.dist_name(type)) - def getDebianArch(self): - if os.uname()[4][:3] == 'arm': - return 'armhf' + def getDebianArch(self): + if os.uname()[4][:3] == 'arm': + return 'armhf' + # os_bits should be loaded with '32bit' or '64bit' + import platform + (os_bits, other) = platform.architecture() + + # get platform based on current platform + if os_bits == '32bit': + return 'i386' + elif os_bits == '64bit': + return 'amd64' + else: + raise Exception("unknown os bits: " + os_bits) - if os.uname()[4][:3] == 'arm': - return 'Linux-armv6l' + def getLinuxPlatform(self): + if os.uname()[4][:3] == 'arm': + return 'Linux-armv6l' + # os_bits should be loaded with '32bit' or '64bit' + import platform + (os_bits, other) = platform.architecture() + + # get platform based on current platform + if os_bits == '32bit': + return 'Linux-i686' + elif os_bits == '64bit': + return 'Linux-x86_64' + else: + raise Exception("unknown os bits: " + os_bits) def dist_usage(self): print ('Usage: %s package [package-type]\n' diff --git a/ext/toolchain/fuckfuck.py b/ext/toolchain/fuckfuck.py new file mode 100644 index 00000000..4dcb6def --- /dev/null +++ b/ext/toolchain/fuckfuck.py @@ -0,0 +1,2000 @@ +# synergy -- mouse and keyboard sharing utility +# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2009 Nick Bolton +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file LICENSE that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# TODO: split this file up, it's too long! + +import sys, os, ConfigParser, shutil, re, ftputil, zipfile, glob, commands +from generators import VisualStudioGenerator, EclipseGenerator, XcodeGenerator, MakefilesGenerator +from getopt import gnu_getopt + +if sys.version_info >= (2, 4): + import subprocess + +class Toolchain: + + # minimum required version. + # 2.6 needed for ZipFile.extractall. + # do not change to 2.7, as the build machines are still at 2.6 + # and are a massive pain in the ass to upgrade. + requiredMajor = 2 + requiredMinor = 6 + + # options used by all commands + globalOptions = 'v' + globalOptionsLong = ['no-prompts', 'verbose', 'skip-gui', 'skip-core'] + + # list of valid commands as keys. the values are optarg strings, but most + # are None for now (this is mainly for extensibility) + cmd_opt_dict = { + 'about' : ['', []], + 'setup' : ['g:', ['generator=']], + 'configure' : ['g:dr', ['generator=', 'debug', 'release', 'mac-sdk=', 'mac-identity=']], + 'build' : ['dr', ['debug', 'release']], + 'clean' : ['dr', ['debug', 'release']], + 'update' : ['', []], + 'install' : ['', []], + 'doxygen' : ['', []], + 'dist' : ['', ['vcredist-dir=', 'qt-dir=']], + 'distftp' : ['', ['host=', 'user=', 'pass=', 'dir=']], + 'kill' : ['', []], + 'usage' : ['', []], + 'revision' : ['', []], + 'reformat' : ['', []], + 'open' : ['', []], + 'genlist' : ['', []], + 'reset' : ['', []], + 'signwin' : ['', ['pfx=', 'pwd=', 'dist']], + 'signmac' : ['', []] + } + + # aliases to valid commands + cmd_alias_dict = { + 'info' : 'about', + 'help' : 'usage', + 'package' : 'dist', + 'docs' : 'doxygen', + 'make' : 'build', + 'cmake' : 'configure', + } + + def complete_command(self, arg): + completions = [] + + for cmd, optarg in self.cmd_opt_dict.iteritems(): + # if command was matched fully, return only this, so that + # if `dist` is typed, it will return only `dist` and not + # `dist` and `distftp` for example. + if cmd == arg: + return [cmd,] + if cmd.startswith(arg): + completions.append(cmd) + + for alias, cmd in self.cmd_alias_dict.iteritems(): + # don't know if this will work just like above, but it's + # probably worth adding. + if alias == arg: + return [alias,] + if alias.startswith(arg): + completions.append(alias) + + return completions + + def start_cmd(self, argv): + + cmd_arg = '' + if len(argv) > 1: + cmd_arg = argv[1] + + # change common help args to help command + if cmd_arg in ('--help', '-h', '--usage', '-u', '/?'): + cmd_arg = 'usage' + + completions = self.complete_command(cmd_arg) + + if cmd_arg and len(completions) > 0: + + if len(completions) == 1: + + # get the only completion (since in this case we have 1) + cmd = completions[0] + + # build up the first part of the map (for illustrative purposes) + cmd_map = list() + if cmd_arg != cmd: + cmd_map.append(cmd_arg) + cmd_map.append(cmd) + + # map an alias to the command, and build up the map + if cmd in self.cmd_alias_dict.keys(): + alias = cmd + if cmd_arg == cmd: + cmd_map.append(alias) + cmd = self.cmd_alias_dict[cmd] + cmd_map.append(cmd) + + # show command map to avoid confusion + if len(cmd_map) != 0: + print 'Mapping command: %s' % ' -> '.join(cmd_map) + + self.run_cmd(cmd, argv[2:]) + + return 0 + + else: + print ( + 'Command `%s` too ambiguous, ' + 'could mean any of: %s' + ) % (cmd_arg, ', '.join(completions)) + else: + + if len(argv) == 1: + print 'No command specified, showing usage.\n' + else: + print 'Command not recognised: %s\n' % cmd_arg + + self.run_cmd('usage') + + # generic error code if not returned sooner + return 1 + + def run_cmd(self, cmd, argv = []): + + verbose = False + try: + options_pair = self.cmd_opt_dict[cmd] + + options = self.globalOptions + options_pair[0] + + options_long = [] + options_long.extend(self.globalOptionsLong) + options_long.extend(options_pair[1]) + + opts, args = gnu_getopt(argv, options, options_long) + + for o, a in opts: + if o in ('-v', '--verbose'): + verbose = True + + # pass args and optarg data to command handler, which figures out + # how to handle the arguments + handler = CommandHandler(argv, opts, args, verbose) + + # use reflection to get the function pointer + cmd_func = getattr(handler, cmd) + + cmd_func() + except: + if not verbose: + # print friendly error for users + sys.stderr.write('Error: ' + sys.exc_info()[1].__str__() + '\n') + sys.exit(1) + else: + # if user wants to be verbose let python do it's thing + raise + + def run(self, argv): + if sys.version_info < (self.requiredMajor, self.requiredMinor): + print ('Python version must be at least ' + + str(self.requiredMajor) + '.' + str(self.requiredMinor) + ', but is ' + + str(sys.version_info[0]) + '.' + str(sys.version_info[1])) + sys.exit(1) + + try: + self.start_cmd(argv) + except KeyboardInterrupt: + print '\n\nUser aborted, exiting.' + +class InternalCommands: + + project = 'synergy' + setup_version = 5 # increment to force setup/config + website_url = 'http://synergy-project.org/' + + this_cmd = 'hm' + cmake_cmd = 'cmake' + qmake_cmd = 'qmake' + make_cmd = 'make' + xcodebuild_cmd = 'xcodebuild' + w32_make_cmd = 'mingw32-make' + w32_qt_version = '4.6.2' + defaultTarget = 'release' + + cmake_dir = 'res' + gui_dir = 'src/gui' + doc_dir = 'doc' + extDir = 'ext' + + sln_filename = '%s.sln' % project + xcodeproj_filename = '%s.xcodeproj' % project + configDir = 'build' + configFilename = '%s/%s.cfg' % (configDir, this_cmd) + qtpro_filename = 'gui.pro' + doxygen_filename = 'doxygen.cfg' + + cmake_url = 'http://www.cmake.org/cmake/resources/software.html' + + # try_chdir(...) and restore_chdir() will use this + prevdir = '' + + # by default, no index specified as arg + generator_id = None + + # by default, prompt user for input + no_prompts = False + + # by default, compile the core + enableMakeCore = True + + # by default, compile the gui + enableMakeGui = True + + # by default, unknown + macSdk = None + + # by default, unknown + macIdentity = None + + # gtest dir with version number + gtestDir = 'gtest-1.6.0' + + # gmock dir with version number + gmockDir = 'gmock-1.6.0' + + win32_generators = { + 1 : VisualStudioGenerator('10'), + 2 : VisualStudioGenerator('10 Win64'), + 3 : VisualStudioGenerator('9 2008'), + 4 : VisualStudioGenerator('9 2008 Win64'), + 5 : VisualStudioGenerator('8 2005'), + 6 : VisualStudioGenerator('8 2005 Win64') + } + + unix_generators = { + 1 : MakefilesGenerator(), + 2 : EclipseGenerator(), + } + + darwin_generators = { + 1 : MakefilesGenerator(), + 2 : XcodeGenerator(), + 3 : EclipseGenerator(), + } + + def getBuildDir(self, target=''): + return self.getGenerator().getBuildDir(target) + + def getBinDir(self, target=''): + return self.getGenerator().getBinDir(target) + + def sln_filepath(self): + return '%s\%s' % (self.getBuildDir(), self.sln_filename) + + def xcodeproj_filepath(self, target=''): + return '%s/%s' % (self.getBuildDir(target), self.xcodeproj_filename) + + def usage(self): + app = sys.argv[0] + print ('Usage: %s [-g |-v|--no-prompts|]\n' + '\n' + 'Replace [command] with one of:\n' + ' about Show information about this script\n' + ' setup Runs the initial setup for this script\n' + ' conf Runs cmake (generates project files)\n' + ' open Attempts to open the generated project file\n' + ' build Builds using the platform build chain\n' + ' clean Cleans using the platform build chain\n' + ' kill Kills all synergy processes (run as admin)\n' + ' update Updates the source code from repository\n' + ' revision Display the current source code revision\n' + ' package Create a distribution package (e.g. tar.gz)\n' + ' install Installs the program\n' + ' doxygen Builds doxygen documentation\n' + ' reformat Reformat .cpp and .h files using AStyle\n' + ' genlist Shows the list of available platform generators\n' + ' usage Shows the help screen\n' + '\n' + 'Example: %s build -g 3' + ) % (app, app) + + def configureAll(self, targets, extraArgs=''): + + # if no mode specified, use default + if len(targets) == 0: + targets += [self.defaultTarget,] + + for target in targets: + self.configure(target) + + def checkGTest(self): + dir = self.extDir + '/' + self.gtestDir + if (os.path.isdir(dir)): + return + + zipFilename = dir + '.zip' + if (not os.path.exists(zipFilename)): + raise Exception('GTest zip not found at: ' + zipFilename) + + if not os.path.exists(dir): + os.mkdir(dir) + + zip = zipfile.ZipFile(zipFilename) + self.zipExtractAll(zip, dir) + + def checkGMock(self): + dir = self.extDir + '/' + self.gmockDir + if (os.path.isdir(dir)): + return + + zipFilename = dir + '.zip' + if (not os.path.exists(zipFilename)): + raise Exception('GMock zip not found at: ' + zipFilename) + + if not os.path.exists(dir): + os.mkdir(dir) + + zip = zipfile.ZipFile(zipFilename) + self.zipExtractAll(zip, dir) + + # ZipFile.extractall() is buggy in 2.6.1 + # http://bugs.python.org/issue4710 + def zipExtractAll(self, z, dir): + if not dir.endswith("/"): + dir += "/" + + for f in z.namelist(): + if f.endswith("/"): + os.makedirs(dir + f) + else: + z.extract(f, dir) + + def configure(self, target='', extraArgs=''): + + # ensure latest setup and do not ask config for generator (only fall + # back to prompt if not specified as arg) + self.ensure_setup_latest() + + if sys.platform == "darwin": + config = self.getConfig() + + if self.macSdk: + config.set('hm', 'macSdk', self.macSdk) + elif config.has_option("hm", "macSdk"): + self.macSdk = config.get('hm', 'macSdk') + + if self.macIdentity: + config.set('hm', 'macIdentity', self.macIdentity) + elif config.has_option("hm", "macIdentity"): + self.macIdentity = config.get('hm', 'macIdentity') + + self.write_config(config) + + if not self.macSdk: + raise Exception("Arg missing: --mac-sdk "); + + if not self.macIdentity: + raise Exception("Arg missing: --mac-identity "); + + sdkDir = self.getMacSdkDir() + if not os.path.exists(sdkDir): + raise Exception("Mac SDK not found at: " + sdkDir) + + os.environ["MACOSX_DEPLOYMENT_TARGET"] = self.macSdk + + # default is release + if target == '': + print 'Defaulting target to: ' + self.defaultTarget + target = self.defaultTarget + + # allow user to skip core compile + if self.enableMakeCore: + self.configureCore(target, extraArgs) + + # allow user to skip gui compile + if self.enableMakeGui: + self.configureGui(target, extraArgs) + + self.setConfRun(target) + + def configureCore(self, target="", extraArgs=""): + + # ensure that we have access to cmake + _cmake_cmd = self.persist_cmake() + + # now that we know we've got the latest setup, we can ask the config + # file for the generator (but again, we only fall back to this if not + # specified as arg). + generator = self.getGenerator() + + if generator != self.findGeneratorFromConfig(): + print('Generator changed, running setup.') + self.setup(target) + + cmake_args = '' + if generator.cmakeName != '': + cmake_args += ' -G "' + generator.cmakeName + '"' + + # for makefiles always specify a build type (debug, release, etc) + if generator.cmakeName.find('Unix Makefiles') != -1: + cmake_args += ' -DCMAKE_BUILD_TYPE=' + target.capitalize() + + elif sys.platform == "darwin": + macSdkMatch = re.match("(\d+)\.(\d+)", self.macSdk) + if not macSdkMatch: + raise Exception("unknown osx version: " + self.macSdk) + + sdkDir = self.getMacSdkDir() + cmake_args += " -DCMAKE_OSX_SYSROOT=" + sdkDir + cmake_args += " -DCMAKE_OSX_DEPLOYMENT_TARGET=" + self.macSdk + cmake_args += " -DOSX_TARGET_MAJOR=" + macSdkMatch.group(1) + cmake_args += " -DOSX_TARGET_MINOR=" + macSdkMatch.group(2) + + # if not visual studio, use parent dir + sourceDir = generator.getSourceDir() + + self.checkGTest() + self.checkGMock() + + if extraArgs != '': + cmake_args += ' ' + extraArgs + + cmake_cmd_string = _cmake_cmd + cmake_args + ' ' + sourceDir + + # Run from build dir so we have an out-of-source build. + self.try_chdir(self.getBuildDir(target)) + + print "CMake command: " + cmake_cmd_string + err = os.system(cmake_cmd_string) + + self.restore_chdir() + + if generator.cmakeName.find('Eclipse') != -1: + self.fixCmakeEclipseBug() + + if err != 0: + raise Exception('CMake encountered error: ' + str(err)) + + def configureGui(self, target="", extraArgs=""): + + # make sure we have qmake + self.persist_qmake() + + qmake_cmd_string = self.qmake_cmd + " " + self.qtpro_filename + " -r" + + if sys.platform == "darwin": + + # create makefiles on mac (not xcode). + qmake_cmd_string += " -spec macx-g++" + + (major, minor) = self.getMacVersion() + if major == 10 and minor <= 4: + # 10.4: universal (intel and power pc) + qmake_cmd_string += ' CONFIG+="ppc i386"' + + libs = ( + "-framework ApplicationServices " + "-framework Security " + "-framework cocoa") + + if major == 10 and minor >= 6: + libs += " -framework ServiceManagement" + + qmake_cmd_string += " \"MACX_LIBS=%s\" " % libs + + sdkDir = self.getMacSdkDir() + shortForm = "macosx" + self.macSdk + version = str(major) + "." + str(minor) + + qmake_cmd_string += " QMAKE_MACOSX_DEPLOYMENT_TARGET=" + version + + (qMajor, qMinor, qRev) = self.getQmakeVersion() + if qMajor <= 4: + # 4.6: qmake takes full sdk dir. + qmake_cmd_string += " QMAKE_MAC_SDK=" + sdkDir + else: + # 5.2: now we need to use the .path setting. + qmake_cmd_string += " QMAKE_MAC_SDK=" + shortForm + qmake_cmd_string += " QMAKE_MAC_SDK." + shortForm + ".path=" + sdkDir + + print "QMake command: " + qmake_cmd_string + + # run qmake from the gui dir + self.try_chdir(self.gui_dir) + err = os.system(qmake_cmd_string) + self.restore_chdir() + + if err != 0: + raise Exception('QMake encountered error: ' + str(err)) + + def getQmakeVersion(self): + version = commands.getoutput("qmake --version") + result = re.search('(\d+)\.(\d+)\.(\d)', version) + + if not result: + raise Exception("Could not get qmake version.") + + major = int(result.group(1)) + minor = int(result.group(2)) + rev = int(result.group(3)) + + return (major, minor, rev) + + def getMacSdkDir(self): + sdkName = "macosx" + self.macSdk + + # Ideally we'll use xcrun (which is influenced by $DEVELOPER_DIR), then try a couple + # fallbacks to known paths if xcrun is not available + status, sdkPath = commands.getstatusoutput("xcrun --show-sdk-path --sdk " + sdkName) + if status == 0 and sdkPath: + return sdkPath + + developerDir = os.getenv("DEVELOPER_DIR") + if not developerDir: + developerDir = "/Applications/Xcode.app/Contents/Developer" + + sdkDirName = sdkName.replace("macosx", "MacOSX") + sdkPath = developerDir + "/Platforms/MacOSX.platform/Developer/SDKs/" + sdkDirName + ".sdk" + if os.path.exists(sdkPath): + return sdkPath + + return "/Developer/SDKs/" + sdkDirName + ".sdk" + + # http://tinyurl.com/cs2rxxb + def fixCmakeEclipseBug(self): + print "Fixing CMake Eclipse bugs..." + + file = open('.project', 'r+') + content = file.read() + pattern = re.compile('\s+.+', re.S) + content = pattern.sub('', content) + file.seek(0) + file.write(content) + file.truncate() + file.close() + + def persist_cmake(self): + # even though we're running `cmake --version`, we're only doing this for the 0 return + # code; we don't care about the version, since CMakeLists worrys about this for us. + err = os.system('%s --version' % self.cmake_cmd) + + if err != 0: + # if return code from cmake is not 0, then either something has + # gone terribly wrong with --version, or it genuinely doesn't exist. + print ('Could not find `%s` in system path.\n' + 'Download the latest version from:\n %s') % ( + self.cmake_cmd, self.cmake_url) + raise Exception('Cannot continue without CMake.') + else: + return self.cmake_cmd + + def persist_qt(self): + self.persist_qmake() + + def persist_qmake(self): + # cannot use subprocess on < python 2.4 + if sys.version_info < (2, 4): + return + + try: + p = subprocess.Popen( + [self.qmake_cmd, '--version'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + except: + print >> sys.stderr, 'Error: Could not find qmake.' + if sys.platform == 'win32': # windows devs usually need hints ;) + print ( + 'Suggestions:\n' + '1. Ensure that qmake.exe exists in your system path.\n' + '2. Try to download Qt (check our dev FAQ for links):\n' + ' qt-sdk-win-opensource-2010.02.exe') + raise Exception('Cannot continue without qmake.') + + stdout, stderr = p.communicate() + if p.returncode != 0: + raise Exception('Could not test for cmake: %s' % stderr) + else: + m = re.search('.*Using Qt version (\d+\.\d+\.\d+).*', stdout) + if m: + if sys.platform == 'win32': + ver = m.group(1) + if ver != self.w32_qt_version: # TODO: test properly + print >> sys.stderr, ( + 'Warning: Not using supported Qt version %s' + ' (your version is %s).' + ) % (self.w32_qt_version, ver) + else: + pass # any version should be ok for other platforms + else: + raise Exception('Could not find qmake version.') + + def ensureConfHasRun(self, target, skipConfig): + if self.hasConfRun(target): + print 'Skipping config for target: ' + target + skipConfig = True + + if not skipConfig: + self.configure(target) + + def build(self, targets=[], skipConfig=False): + + # if no mode specified, use default + if len(targets) == 0: + targets += [self.defaultTarget,] + + self.ensure_setup_latest() + + self.loadConfig() + + # allow user to skip core compile + if self.enableMakeCore: + self.makeCore(targets) + + # allow user to skip gui compile + if self.enableMakeGui: + self.makeGui(targets) + + def loadConfig(self): + config = self.getConfig() + + if config.has_option("hm", "macSdk"): + self.macSdk = config.get("hm", "macSdk") + + if config.has_option("hm", "macIdentity"): + self.macIdentity = config.get("hm", "macIdentity") + + def makeCore(self, targets): + + generator = self.getGeneratorFromConfig().cmakeName + + if self.macSdk: + os.environ["MACOSX_DEPLOYMENT_TARGET"] = self.macSdk + + if generator.find('Unix Makefiles') != -1: + for target in targets: + self.runBuildCommand(self.make_cmd, target) + else: + for target in targets: + if generator.startswith('Visual Studio'): + self.run_vcbuild(generator, target, self.sln_filepath()) + elif generator == 'Xcode': + cmd = self.xcodebuild_cmd + ' -configuration ' + target.capitalize() + self.runBuildCommand(cmd, target) + else: + raise Exception('Build command not supported with generator: ' + generator) + + def makeGui(self, targets, args=""): + for target in targets: + + if sys.platform == 'win32': + + gui_make_cmd = self.w32_make_cmd + ' ' + target + args + print 'Make GUI command: ' + gui_make_cmd + + self.try_chdir(self.gui_dir) + err = os.system(gui_make_cmd) + self.restore_chdir() + + if err != 0: + raise Exception(gui_make_cmd + ' failed with error: ' + str(err)) + + elif sys.platform in ['linux2', 'sunos5', 'freebsd7', 'darwin']: + + gui_make_cmd = self.make_cmd + " -w" + args + print 'Make GUI command: ' + gui_make_cmd + + # start with a clean app bundle + targetDir = self.getGenerator().getBinDir(target) + bundleTargetDir = targetDir + '/Synergy.app' + if os.path.exists(bundleTargetDir): + shutil.rmtree(bundleTargetDir) + + binDir = self.getGenerator().binDir + bundleTempDir = binDir + '/Synergy.app' + if os.path.exists(bundleTempDir): + shutil.rmtree(bundleTempDir) + + self.try_chdir(self.gui_dir) + err = os.system(gui_make_cmd) + self.restore_chdir() + + if err != 0: + raise Exception(gui_make_cmd + ' failed with error: ' + str(err)) + + if sys.platform == 'darwin' and not "clean" in args: + self.macPostGuiMake(target) + + self.fixQtFrameworksLayout(target) + else: + raise Exception('Unsupported platform: ' + sys.platform) + + def macPostGuiMake(self, target): + bundle = 'Synergy.app' + binDir = self.getGenerator().binDir + targetDir = self.getGenerator().getBinDir(target) + bundleTempDir = binDir + '/' + bundle + bundleTargetDir = targetDir + '/' + bundle + + if os.path.exists(bundleTempDir): + shutil.move(bundleTempDir, bundleTargetDir) + + if self.enableMakeCore: + # copy core binaries into the bundle, since the gui + # now looks for the binaries in the current app dir. + + bundleBinDir = bundleTargetDir + "/Contents/MacOS/" + shutil.copy(targetDir + "/synergyc", bundleBinDir) + shutil.copy(targetDir + "/synergys", bundleBinDir) + shutil.copy(targetDir + "/syntool", bundleBinDir) + + self.loadConfig() + if not self.macIdentity: + raise Exception("run config with --mac-identity") + + if self.enableMakeGui: + # use qt to copy libs to bundle so no dependencies are needed. do not create a + # dmg at this point, since we need to sign it first, and then create our own + # after signing (so that qt does not affect the signed app bundle). + bin = "macdeployqt Synergy.app -verbose=2" + self.try_chdir(targetDir) + err = os.system(bin) + self.restore_chdir() + print bundleTargetDir + if err != 0: + raise Exception(bin + " failed with error: " + str(err)) + + (qMajor, qMinor, qRev) = self.getQmakeVersion() + if qMajor <= 4: + frameworkRootDir = "/Library/Frameworks" + else: + # TODO: auto-detect, qt can now be installed anywhere. + frameworkRootDir = "/Developer/Qt5.2.1/5.2.1/clang_64/lib" + + target = bundleTargetDir + "/Contents/Frameworks" + + # copy the missing Info.plist files for the frameworks. + for root, dirs, files in os.walk(target): + for dir in dirs: + if dir.startswith("Qt"): + shutil.copy( + frameworkRootDir + "/" + dir + "/Contents/Info.plist", + target + "/" + dir + "/Resources/") + + def symlink(self, source, target): + if not os.path.exists(target): + os.symlink(source, target) + + def move(self, source, target): + if os.path.exists(source): + shutil.move(source, target) + + def fixQtFrameworksLayout(self, target): + # reorganize Qt frameworks layout on Mac 10.9.5 or later + # http://goo.gl/BFnQ8l + # QtCore example: + # QtCore.framework/ + # QtCore -> Versions/Current/QtCore + # Resources -> Versions/Current/Resources + # Versions/ + # Current -> 5 + # 5/ + # QtCore + # Resources/ + # Info.plist + targetDir = self.getGenerator().getBinDir(target) + + target = targetDir + "/Synergy.app/Contents/Frameworks" + (major, minor) = self.getMacVersion() + if major == 10: + if minor >= 9: + for root, dirs, files in os.walk(target): + for dir in dirs: + if dir.startswith("Qt"): + self.try_chdir(target + "/" + dir +"/Versions") + self.symlink("5", "Current") + self.move("../Resources", "5") + self.restore_chdir() + + self.try_chdir(target + "/" + dir) + dot = dir.find('.') + frameworkName = dir[:dot] + self.symlink("Versions/Current/" + frameworkName, frameworkName) + self.symlink("Versions/Current/Resources", "Resources") + self.restore_chdir() + + def signmac(self): + self.loadConfig() + if not self.macIdentity: + raise Exception("run config with --mac-identity") + + self.try_chdir("bin/Release/") + err = os.system( + 'codesign --deep -fs "' + self.macIdentity + '" Synergy.app') + self.restore_chdir() + + if err != 0: + raise Exception("codesign failed with error: " + str(err)) + + def signwin(self, pfx, pwdFile, dist): + generator = self.getGeneratorFromConfig().cmakeName + if not generator.startswith('Visual Studio'): + raise Exception('only windows is supported') + + f = open(pwdFile) + lines = f.readlines() + f.close() + pwd = lines[0] + + if (dist): + self.signFile(pfx, pwd, 'bin/Release', self.dist_name('win')) + else: + self.signFile(pfx, pwd, 'bin/Release', 'synergy.exe') + self.signFile(pfx, pwd, 'bin/Release', 'synergyc.exe') + self.signFile(pfx, pwd, 'bin/Release', 'synergys.exe') + self.signFile(pfx, pwd, 'bin/Release', 'synergyd.exe') + self.signFile(pfx, pwd, 'bin/Release', 'syntool.exe') + self.signFile(pfx, pwd, 'bin/Release', 'synwinhk.dll') + + def signFile(self, pfx, pwd, dir, file): + self.try_chdir(dir) + err = os.system( + 'signtool sign' + ' /f ' + pfx + + ' /p ' + pwd + + ' /t http://timestamp.verisign.com/scripts/timstamp.dll ' + + file) + self.restore_chdir() + + if err != 0: + raise Exception("signtool failed with error: " + str(err)) + + def runBuildCommand(self, cmd, target): + + self.try_chdir(self.getBuildDir(target)) + err = os.system(cmd) + self.restore_chdir() + + if err != 0: + raise Exception(cmd + ' failed: ' + str(err)) + + def clean(self, targets=[]): + + # if no mode specified, use default + if len(targets) == 0: + targets += [self.defaultTarget,] + + # allow user to skip core clean + if self.enableMakeCore: + self.cleanCore(targets) + + # allow user to skip qui clean + if self.enableMakeGui: + self.cleanGui(targets) + + def cleanCore(self, targets): + generator = self.getGeneratorFromConfig().cmakeName + + if generator.startswith('Visual Studio'): + # special case for version 10, use new /target:clean + if generator.startswith('Visual Studio 10'): + for target in targets: + self.run_vcbuild(generator, target, self.sln_filepath(), '/target:clean') + + # any other version of visual studio, use /clean + elif generator.startswith('Visual Studio'): + for target in targets: + self.run_vcbuild(generator, target, self.sln_filepath(), '/clean') + + else: + cmd = '' + if generator == "Unix Makefiles": + print 'Cleaning with GNU Make...' + cmd = self.make_cmd + elif generator == 'Xcode': + print 'Cleaning with Xcode...' + cmd = self.xcodebuild_cmd + else: + raise Exception('Not supported with generator: ' + generator) + + for target in targets: + self.try_chdir(self.getBuildDir(target)) + err = os.system(cmd + ' clean') + self.restore_chdir() + + if err != 0: + raise Exception('Clean failed: ' + str(err)) + + def cleanGui(self, targets): + self.makeGui(targets, " clean") + + def open(self): + generator = self.getGeneratorFromConfig().cmakeName + if generator.startswith('Visual Studio'): + print 'Opening with %s...' % generator + self.open_internal(self.sln_filepath()) + + elif generator.startswith('Xcode'): + print 'Opening with %s...' % generator + self.open_internal(self.xcodeproj_filepath(), 'open') + + else: + raise Exception('Not supported with generator: ' + generator) + + def update(self): + print "Running Subversion update..." + err = os.system('svn update') + if err != 0: + raise Exception('Could not update from repository with error code code: ' + str(err)) + + def revision(self): + print self.find_revision() + + def find_revision(self): + return self.getGitRevision() + + def getGitRevision(self): + if sys.version_info < (2, 4): + raise Exception("Python 2.4 or greater required.") + + p = subprocess.Popen( + ["git", "log", "--pretty=format:%h", "-n", "1"], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + stdout, stderr = p.communicate() + + if p.returncode != 0: + raise Exception('Could not get revision, git error: ' + str(p.returncode)) + + return stdout.strip() + + def getGitBranchName(self): + if sys.version_info < (2, 4): + raise Exception("Python 2.4 or greater required.") + + p = subprocess.Popen( + ["git", "rev-parse", "--abbrev-ref", "HEAD"], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + stdout, stderr = p.communicate() + + if p.returncode != 0: + raise Exception('Could not get branch name, git error: ' + str(p.returncode)) + + return stdout.strip() + + def find_revision_svn(self): + if sys.version_info < (2, 4): + stdout = commands.getoutput('svn info') + else: + p = subprocess.Popen(['svn', 'info'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + + if p.returncode != 0: + raise Exception('Could not get revision - svn info failed with code: ' + str(p.returncode)) + + m = re.search('.*Revision: (\d+).*', stdout) + if not m: + raise Exception('Could not find revision number in svn info output.') + + return m.group(1) + + def kill(self): + if sys.platform == 'win32': + return os.system('taskkill /F /FI "IMAGENAME eq synergy*"') + else: + raise Exception('Not implemented for platform: ' + sys.platform) + + def doxygen(self): + self.enableMakeGui = False + + # The conf generates doc/doxygen.cfg from cmake/doxygen.cfg.in + self.configure(self.defaultTarget, '-DCONF_DOXYGEN:BOOL=TRUE') + + err = os.system('doxygen %s/%s' % (self.doc_dir, self.doxygen_filename)) + + if err != 0: + raise Exception('doxygen failed with error code: ' + str(err)) + + def dist(self, type, vcRedistDir, qtDir): + + # Package is supported by default. + package_unsupported = False + unixTarget = self.defaultTarget + + if type == '' or type == None: + self.dist_usage() + return + + moveExt = '' + + if type == 'src': + self.distSrc() + + elif type == 'rpm': + if sys.platform == 'linux2': + self.distRpm() + else: + package_unsupported = True + + elif type == 'deb': + if sys.platform == 'linux2': + self.distDeb() + else: + package_unsupported = True + + elif type == 'win': + if sys.platform == 'win32': + #self.distNsis(vcRedistDir, qtDir) + self.distWix() + else: + package_unsupported = True + + elif type == 'mac': + if sys.platform == 'darwin': + self.distMac() + else: + package_unsupported = True + + else: + raise Exception('Package type not supported: ' + type) + + if moveExt != '': + self.unixMove( + self.getGenerator().buildDir + '/release/*.' + moveExt, + self.getGenerator().binDir) + + if package_unsupported: + raise Exception( + ("Package type, '%s' is not supported for platform, '%s'") + % (type, sys.platform)) + + def distRpm(self): + rpmDir = self.getGenerator().buildDir + '/rpm' + if os.path.exists(rpmDir): + shutil.rmtree(rpmDir) + + os.makedirs(rpmDir) + + templateFile = open(self.cmake_dir + '/synergy.spec.in') + template = templateFile.read() + + template = template.replace('${in:version}', self.getVersionFromCmake()) + + specPath = rpmDir + '/synergy.spec' + + specFile = open(specPath, 'w') + specFile.write(template) + specFile.close() + + version = self.getVersionFromCmake() + target = '../../bin/synergy-%s-%s.rpm' % ( + version, self.getLinuxPlatform()) + + + try: + self.try_chdir(rpmDir) + cmd = 'rpmbuild -bb --define "_topdir `pwd`" synergy.spec' + print "Command: " + cmd + err = os.system(cmd) + if err != 0: + raise Exception('rpmbuild failed: ' + str(err)) + + self.unixMove('RPMS/*/*.rpm', target) + + cmd = 'rpmlint ' + target + print "Command: " + cmd + err = os.system(cmd) + if err != 0: + raise Exception('rpmlint failed: ' + str(err)) + + finally: + self.restore_chdir() + + def distDeb(self): + buildDir = self.getGenerator().buildDir + binDir = self.getGenerator().binDir + resDir = self.cmake_dir + + version = self.getVersionFromCmake() + package = '%s-%s-%s' % ( + self.project, version, self.getLinuxPlatform()) + + debDir = '%s/deb' % buildDir + if os.path.exists(debDir): + shutil.rmtree(debDir) + + metaDir = '%s/%s/DEBIAN' % (debDir, package) + os.makedirs(metaDir) + + templateFile = open(resDir + '/deb/control.in') + template = templateFile.read() + + template = template.replace('${in:version}', + self.getVersionFromCmake()) + + template = template.replace('${in:arch}', + self.getDebianArch()) + + controlPath = '%s/control' % metaDir + + controlFile = open(controlPath, 'w') + controlFile.write(template) + controlFile.close() + + targetBin = '%s/%s/usr/bin' % (debDir, package) + targetShare = '%s/%s/usr/share' % (debDir, package) + targetApplications = "%s/applications" % targetShare + targetIcons = "%s/icons" % targetShare + targetDocs = "%s/doc/%s" % (targetShare, self.project) + + os.makedirs(targetBin) + os.makedirs(targetApplications) + os.makedirs(targetIcons) + os.makedirs(targetDocs) + + for root, dirs, files in os.walk(debDir): + for d in dirs: + os.chmod(os.path.join(root, d), 0o0755) + + binFiles = ['synergy', 'synergyc', 'synergys', 'synergyd', 'syntool'] + for f in binFiles: + shutil.copy("%s/%s" % (binDir, f), targetBin) + target = "%s/%s" % (targetBin, f) + os.chmod(target, 0o0755) + err = os.system("strip " + target) + if err != 0: + raise Exception('strip failed: ' + str(err)) + + shutil.copy("%s/synergy.desktop" % resDir, targetApplications) + shutil.copy("%s/synergy.ico" % resDir, targetIcons) + + docTarget = "%s/doc/%s" % (targetShare, self.project) + + copyrightPath = "%s/deb/copyright" % resDir + shutil.copy(copyrightPath, docTarget) + + shutil.copy("%s/deb/changelog" % resDir, docTarget) + os.system("gzip -9 %s/changelog" % docTarget) + if err != 0: + raise Exception('gzip failed: ' + str(err)) + + for root, dirs, files in os.walk(targetShare): + for f in files: + os.chmod(os.path.join(root, f), 0o0644) + + target = '../../bin/%s.deb' % package + + try: + self.try_chdir(debDir) + + # TODO: consider dpkg-buildpackage (higher level tool) + cmd = 'fakeroot dpkg-deb --build %s' % package + print "Command: " + cmd + err = os.system(cmd) + if err != 0: + raise Exception('dpkg-deb failed: ' + str(err)) + + cmd = 'lintian %s.deb' % package + print "Command: " + cmd + err = os.system(cmd) + if err != 0: + raise Exception('lintian failed: ' + str(err)) + + self.unixMove('*.deb', target) + finally: + self.restore_chdir() + + def distSrc(self): + version = self.getVersionFromCmake() + name = (self.project + '-' + version + '-Source') + exportPath = self.getGenerator().buildDir + '/' + name + + if os.path.exists(exportPath): + print "Removing existing export..." + shutil.rmtree(exportPath) + + os.mkdir(exportPath) + + cmd = "git archive %s | tar -x -C %s" % ( + self.getGitBranchName(), exportPath) + + print 'Exporting repository to: ' + exportPath + err = os.system(cmd) + if err != 0: + raise Exception('Repository export failed: ' + str(err)) + + packagePath = '../' + self.getGenerator().binDir + '/' + name + '.tar.gz' + + try: + self.try_chdir(self.getGenerator().buildDir) + print 'Packaging to: ' + packagePath + err = os.system('tar cfvz ' + packagePath + ' ' + name) + if err != 0: + raise Exception('Package failed: ' + str(err)) + finally: + self.restore_chdir() + + def unixMove(self, source, dest): + print 'Moving ' + source + ' to ' + dest + err = os.system('mv ' + source + ' ' + dest) + if err != 0: + raise Exception('Package failed: ' + str(err)) + + def distMac(self): + self.loadConfig() + binDir = self.getGenerator().getBinDir('Release') + name = "Synergy" + dist = binDir + "/" + name + + # ensure dist dir is clean + if os.path.exists(dist): + shutil.rmtree(dist) + + os.makedirs(dist) + shutil.move(binDir + "/" + name + ".app", dist + "/" + name + ".app") + + self.try_chdir(dist) + err = os.system("ln -s /Applications") + self.restore_chdir() + + fileName = "%s-%s-%s.dmg" % ( + self.project, + self.getVersionFromCmake(), + self.getMacPackageName()) + + cmd = "hdiutil create " + fileName + " -srcfolder ./" + name + "/ -ov" + + self.try_chdir(binDir) + err = os.system(cmd) + self.restore_chdir() + + def distWix(self): + generator = self.getGeneratorFromConfig().cmakeName + + arch = 'x86' + if generator.endswith('Win64'): + arch = 'x64' + + version = self.getVersionFromCmake() + args = "/p:DefineConstants=\"Version=%s\"" % version + + self.run_vcbuild( + generator, 'release', 'synergy.sln', args, + 'src/setup/win32/', 'x86') + + filename = "%s-%s-Windows-%s.msi" % ( + self.project, + version, + arch) + + old = "bin/Release/synergy.msi" + new = "bin/Release/%s" % (filename) + + try: + os.remove(new) + except OSError: + pass + + os.rename(old, new) + + def distNsis(self, vcRedistDir, qtDir): + + if vcRedistDir == '': + raise Exception( + 'VC++ redist dir path not specified (--vcredist-dir).') + + if qtDir == '': + raise Exception( + 'QT SDK dir path not specified (--qt-dir).') + + generator = self.getGeneratorFromConfig().cmakeName + + arch = 'x86' + installDirVar = '$PROGRAMFILES32' + + if generator.endswith('Win64'): + arch = 'x64' + installDirVar = '$PROGRAMFILES64' + + templateFile = open(self.cmake_dir + '\Installer.nsi.in') + template = templateFile.read() + + template = template.replace('${in:version}', self.getVersionFromCmake()) + template = template.replace('${in:arch}', arch) + template = template.replace('${in:vcRedistDir}', vcRedistDir) + template = template.replace('${in:qtDir}', qtDir) + template = template.replace('${in:installDirVar}', installDirVar) + + nsiPath = self.getGenerator().buildDir + '\Installer.nsi' + nsiFile = open(nsiPath, 'w') + nsiFile.write(template) + nsiFile.close() + + command = 'makensis ' + nsiPath + print 'NSIS command: ' + command + err = os.system(command) + if err != 0: + raise Exception('Package failed: ' + str(err)) + + def getVersionFromCmake(self): + cmakeFile = open('CMakeLists.txt') + cmake = cmakeFile.read() + + majorRe = re.search('VERSION_MAJOR (\d+)', cmake) + major = majorRe.group(1) + + minorRe = re.search('VERSION_MINOR (\d+)', cmake) + minor = minorRe.group(1) + + revRe = re.search('VERSION_REV (\d+)', cmake) + rev = revRe.group(1) + + return major + '.' + minor + '.' + rev + + def ftpUpload(self, ftp, source, target): + print "Uploading '%s' as '%s' to FTP server '%s'..." % ( + source, target, ftp.host) + ftp.run(source, target) + print 'Done' + + def distftp(self, type, ftp): + if not type: + raise Exception('Platform type not specified.') + + self.loadConfig() + + binDir = self.getGenerator().getBinDir('Release') + + packageSource = binDir + '/' + self.dist_name(type) + packageTarget = self.dist_name_rev(type) + self.ftpUpload(ftp, packageSource, packageTarget) + + if (type != 'src'): + pluginsDir = binDir + '/plugins' + nsPluginSource = self.findLibraryFile(type, pluginsDir, 'ns') + nsPluginTarget = self.getLibraryDistFilename(type, pluginsDir, 'ns') + self.ftpUpload(ftp, nsPluginSource, nsPluginTarget) + + def getLibraryDistFilename(self, type, dir, name): + (platform, packageExt, libraryExt) = self.getDistributePlatformInfo(type) + branch = self.getGitBranchName() + revision = self.getGitRevision() + firstPart = '%s-%s-%s-%s' % (name, branch, revision, platform) + + filename = '%s.%s' % (firstPart, libraryExt) + if type == 'rpm' or type == 'deb': + # linux is a bit special, include dist type (deb/rpm in filename) + filename = '%s-%s.%s' % (firstPart, packageExt, libraryExt) + + return filename + + def findLibraryFile(self, type, dir, name): + (platform, packageExt, libraryExt) = self.getDistributePlatformInfo(type) + ext = libraryExt + + pattern = name + '\.' + ext + + for filename in os.listdir(dir): + if re.search(pattern, filename): + return dir + '/' + filename + + raise Exception('Could not find library name with pattern: ' + pattern) + + def getDistributePlatformInfo(self, type): + ext = None + libraryExt = None + platform = None + + if type == 'src': + ext = 'tar.gz' + platform = 'Source' + + elif type == 'rpm' or type == 'deb': + ext = type + libraryExt = 'so' + platform = self.getLinuxPlatform() + + elif type == 'win': + # get platform based on last generator used + ext = 'msi' + libraryExt = 'dll' + generator = self.getGeneratorFromConfig().cmakeName + if generator.find('Win64') != -1: + platform = 'Windows-x64' + else: + platform = 'Windows-x86' + + elif type == 'mac': + ext = "dmg" + libraryExt = 'dylib' + platform = self.getMacPackageName() + + if not platform: + raise Exception('Unable to detect distributable platform.') + + return (platform, ext, libraryExt) + + def dist_name(self, type): + (platform, packageExt, libraryExt) = self.getDistributePlatformInfo(type) + ext = packageExt + + pattern = ( + re.escape(self.project + '-') + '\d+\.\d+\.\d+' + + re.escape('-' + platform + '.' + ext)) + + for filename in os.listdir(self.getBinDir('Release')): + if re.search(pattern, filename): + return filename + + # still here? package probably not created yet. + raise Exception('Could not find package name with pattern: ' + pattern) + + def dist_name_rev(self, type): + # find the version number (we're puting the rev in after this) + pattern = '(\d+\.\d+\.\d+)' + replace = "%s-%s" % ( + self.getGitBranchName(), self.getGitRevision()) + return re.sub(pattern, replace, self.dist_name(type)) + + def getDebianArch(self): + if os.uname()[4][:3] == 'arm': + return 'armhf' + + # os_bits should be loaded with '32bit' or '64bit' + import platform + (os_bits, other) = platform.architecture() + + # get platform based on current platform + if os_bits == '32bit': + return 'i386' + elif os_bits == '64bit': + return 'amd64' + else: + raise Exception("unknown os bits: " + os_bits) + + def getLinuxPlatform(self): + if os.uname()[4][:3] == 'arm': + return 'Linux-armv6l' + + # os_bits should be loaded with '32bit' or '64bit' + import platform + (os_bits, other) = platform.architecture() + + # get platform based on current platform + if os_bits == '32bit': + return 'Linux-i686' + elif os_bits == '64bit': + return 'Linux-x86_64' + else: + raise Exception("unknown os bits: " + os_bits) + + def dist_usage(self): + print ('Usage: %s package [package-type]\n' + '\n' + 'Replace [package-type] with one of:\n' + ' src .tar.gz source (Posix only)\n' + ' rpm .rpm package (Red Hat)\n' + ' deb .deb paclage (Debian)\n' + ' win .exe installer (Windows)\n' + ' mac .dmg package (Mac OS X)\n' + '\n' + 'Example: %s package src-tgz') % (self.this_cmd, self.this_cmd) + + def about(self): + print ('Help Me script, from the Synergy project.\n' + '%s\n' + '\n' + 'For help, run: %s help') % (self.website_url, self.this_cmd) + + def try_chdir(self, dir): + global prevdir + + if dir == '': + prevdir = '' + return + + # Ensure temp build dir exists. + if not os.path.exists(dir): + print 'Creating dir: ' + dir + os.makedirs(dir) + + prevdir = os.path.abspath(os.curdir) + + # It will exist by this point, so it's safe to chdir. + print 'Entering dir: ' + dir + os.chdir(dir) + + def restore_chdir(self): + global prevdir + if prevdir == '': + return + print 'Going back to: ' + prevdir + os.chdir(prevdir) + + def open_internal(self, project_filename, application = ''): + + if not os.path.exists(project_filename): + raise Exception('Project file (%s) not found, run hm conf first.' % project_filename) + else: + path = project_filename + + if application != '': + path = application + ' ' + path + + err = os.system(path) + if err != 0: + raise Exception('Could not open project with error code code: ' + str(err)) + + def setup(self, target=''): + print "Running setup..." + + oldGenerator = self.findGeneratorFromConfig() + if not oldGenerator == None: + for target in ['debug', 'release']: + buildDir = oldGenerator.getBuildDir(target) + + cmakeCacheFilename = 'CMakeCache.txt' + if buildDir != '': + cmakeCacheFilename = buildDir + '/' + cmakeCacheFilename + + if os.path.exists(cmakeCacheFilename): + print "Removing %s, since generator changed." % cmakeCacheFilename + os.remove(cmakeCacheFilename) + + # always either get generator from args, or prompt user when + # running setup + generator = self.get_generator_from_prompt() + + config = self.getConfig() + config.set('hm', 'setup_version', self.setup_version) + + # store the generator so we don't need to ask again + config.set('cmake', 'generator', generator) + + self.write_config(config) + + # for all targets, set conf not run + self.setConfRun('all', False) + self.setConfRun('debug', False) + self.setConfRun('release', False) + + print "Setup complete." + + def getConfig(self): + if os.path.exists(self.configFilename): + config = ConfigParser.ConfigParser() + config.read(self.configFilename) + else: + config = ConfigParser.ConfigParser() + + if not config.has_section('hm'): + config.add_section('hm') + + if not config.has_section('cmake'): + config.add_section('cmake') + + return config + + def write_config(self, config, target=''): + if not os.path.isdir(self.configDir): + os.mkdir(self.configDir) + configfile = open(self.configFilename, 'wb') + config.write(configfile) + + def getGeneratorFromConfig(self): + generator = self.findGeneratorFromConfig() + if generator: + return generator + + raise Exception("Could not find generator: " + name) + + def findGeneratorFromConfig(self): + config = ConfigParser.RawConfigParser() + config.read(self.configFilename) + + if not config.has_section('cmake'): + return None + + name = config.get('cmake', 'generator') + + generators = self.get_generators() + keys = generators.keys() + keys.sort() + for k in keys: + if generators[k].cmakeName == name: + return generators[k] + + return None + + def min_setup_version(self, version): + if os.path.exists(self.configFilename): + config = ConfigParser.RawConfigParser() + config.read(self.configFilename) + + try: + return config.getint('hm', 'setup_version') >= version + except: + return False + else: + return False + + def hasConfRun(self, target): + if self.min_setup_version(2): + config = ConfigParser.RawConfigParser() + config.read(self.configFilename) + try: + return config.getboolean('hm', 'conf_done_' + target) + except: + return False + else: + return False + + def setConfRun(self, target, hasRun=True): + if self.min_setup_version(3): + config = ConfigParser.RawConfigParser() + config.read(self.configFilename) + config.set('hm', 'conf_done_' + target, hasRun) + self.write_config(config) + else: + raise Exception("User does not have correct setup version.") + + def get_generators(self): + if sys.platform == 'win32': + return self.win32_generators + elif sys.platform in ['linux2', 'sunos5', 'freebsd7', 'aix5']: + return self.unix_generators + elif sys.platform == 'darwin': + return self.darwin_generators + else: + raise Exception('Unsupported platform: ' + sys.platform) + + def get_generator_from_prompt(self): + return self.getGenerator().cmakeName + + def getGenerator(self): + generators = self.get_generators() + if len(generators.keys()) == 1: + return generators[generators.keys()[0]] + + # if user has specified a generator as an argument + if self.generator_id: + return generators[int(self.generator_id)] + + conf = self.findGeneratorFromConfig() + if conf: + return conf + + raise Exception( + 'Generator not specified, use -g arg ' + + '(use `hm genlist` for a list of generators).') + + def setup_generator_prompt(self, generators): + + if self.no_prompts: + raise Exception('User prompting is disabled.') + + prompt = 'Enter a number:' + print prompt, + + generator_id = raw_input() + + if generator_id in generators: + print 'Selected generator:', generators[generator_id] + else: + print 'Invalid number, try again.' + self.setup_generator_prompt(generators) + + return generators[generator_id] + + def get_vcvarsall(self, generator): + import platform, _winreg + + # os_bits should be loaded with '32bit' or '64bit' + (os_bits, other) = platform.architecture() + + # visual studio is a 32-bit app, so when we're on 64-bit, we need to check the WoW dungeon + if os_bits == '64bit': + key_name = r'SOFTWARE\Wow6432Node\Microsoft\VisualStudio\SxS\VS7' + else: + key_name = r'SOFTWARE\Microsoft\VisualStudio\SxS\VC7' + + try: + key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, key_name) + except: + raise Exception('Unable to open Visual Studio registry key. Application may not be installed.') + + if generator.startswith('Visual Studio 8'): + value,type = _winreg.QueryValueEx(key, '8.0') + elif generator.startswith('Visual Studio 9'): + value,type = _winreg.QueryValueEx(key, '9.0') + elif generator.startswith('Visual Studio 10'): + value,type = _winreg.QueryValueEx(key, '10.0') + else: + raise Exception('Cannot determine vcvarsall.bat location for: ' + generator) + + # not sure why, but the value on 64-bit differs slightly to the original + if os_bits == '64bit': + path = value + r'vc\vcvarsall.bat' + else: + path = value + r'vcvarsall.bat' + + if not os.path.exists(path): + raise Exception("'%s' not found." % path) + + return path + + def run_vcbuild(self, generator, mode, solution, args='', dir='', config32='Win32'): + import platform + + # os_bits should be loaded with '32bit' or '64bit' + (os_bits, other) = platform.architecture() + # Now we choose the parameters bases on OS 32/64 and our target 32/64 + # http://msdn.microsoft.com/en-us/library/x4d2c09s%28VS.80%29.aspx + + # valid options are only: ia64 amd64 x86_amd64 x86_ia64 + # but calling vcvarsall.bat does not garantee that it will work + # ret code from vcvarsall.bat is always 0 so the only way of knowing that I worked is by analysing the text output + # ms bugg: install VS9, FeaturePack, VS9SP1 and you'll obtain a vcvarsall.bat that fails. + if generator.find('Win64') != -1: + # target = 64bit + if os_bits == '32bit': + vcvars_platform = 'x86_amd64' # 32bit OS building 64bit app + else: + vcvars_platform = 'amd64' # 64bit OS building 64bit app + config_platform = 'x64' + else: # target = 32bit + vcvars_platform = 'x86' # 32/64bit OS building 32bit app + config_platform = config32 + + if mode == 'release': + config = 'Release' + else: + config = 'Debug' + + if generator.startswith('Visual Studio 10'): + cmd = ('@echo off\n' + 'call "%s" %s \n' + 'cd "%s"\n' + 'msbuild /nologo %s /p:Configuration="%s" /p:Platform="%s" "%s"' + ) % (self.get_vcvarsall(generator), vcvars_platform, dir, args, config, config_platform, solution) + else: + config = config + '|' + config_platform + cmd = ('@echo off\n' + 'call "%s" %s \n' + 'cd "%s"\n' + 'vcbuild /nologo %s "%s" "%s"' + ) % (self.get_vcvarsall(generator), vcvars_platform, dir, args, solution, config) + + # Generate a batch file, since we can't use environment variables directly. + temp_bat = self.getBuildDir() + r'\vcbuild.bat' + file = open(temp_bat, 'w') + file.write(cmd) + file.close() + + err = os.system(temp_bat) + if err != 0: + raise Exception('Microsoft compiler failed with error code: ' + str(err)) + + def ensure_setup_latest(self): + if not self.min_setup_version(self.setup_version): + self.setup() + + def reformat(self): + err = os.system( + r'tool\astyle\AStyle.exe ' + '--quiet --suffix=none --style=java --indent=force-tab=4 --recursive ' + 'lib/*.cpp lib/*.h cmd/*.cpp cmd/*.h') + + if err != 0: + raise Exception('Reformat failed with error code: ' + str(err)) + + def printGeneratorList(self): + generators = self.get_generators() + keys = generators.keys() + keys.sort() + for k in keys: + print str(k) + ': ' + generators[k].cmakeName + + def getMacVersion(self): + if not self.macSdk: + raise Exception("Mac OS X SDK not set.") + + result = re.search('(\d+)\.(\d+)', self.macSdk) + if not result: + print versions + raise Exception("Could not find Mac OS X version.") + + major = int(result.group(1)) + minor = int(result.group(2)) + return (major, minor) + + def getMacPackageName(self): + + (major, minor) = self.getMacVersion() + + if major == 10: + if minor <= 4: + # 10.4: intel and power pc + arch = "Universal" + elif minor <= 6: + # 10.5: 32-bit intel + arch = "i386" + else: + # 10.7: 64-bit intel (gui only) + arch = "x86_64" + else: + raise Exception("Mac OS major version unknown: " + + str(major)) + + # version is major and minor with no dots (e.g. 106) + version = str(major) + str(minor) + + return "MacOSX%s-%s" % (version, arch) + + def reset(self): + if os.path.exists('build'): + shutil.rmtree('build') + + if os.path.exists('bin'): + shutil.rmtree('bin') + + if os.path.exists('lib'): + shutil.rmtree('lib') + + if os.path.exists('src/gui/tmp'): + shutil.rmtree('src/gui/tmp') + + # qt 4.3 generates ui_ files. + for filename in glob.glob("src/gui/ui_*"): + os.remove(filename) + +# the command handler should be called only from hm.py (i.e. directly +# from the command prompt). the purpose of this class is so that we +# don't need to do argument handling all over the place in the internal +# commands class. +class CommandHandler: + ic = InternalCommands() + build_targets = [] + vcRedistDir = '' + qtDir = '' + + def __init__(self, argv, opts, args, verbose): + + self.ic.verbose = verbose + + self.opts = opts + self.args = args + + for o, a in self.opts: + if o == '--no-prompts': + self.ic.no_prompts = True + elif o in ('-g', '--generator'): + self.ic.generator_id = a + elif o == '--skip-gui': + self.ic.enableMakeGui = False + elif o == '--skip-core': + self.ic.enableMakeCore = False + elif o in ('-d', '--debug'): + self.build_targets += ['debug',] + elif o in ('-r', '--release'): + self.build_targets += ['release',] + elif o == '--vcredist-dir': + self.vcRedistDir = a + elif o == '--qt-dir': + self.qtDir = a + elif o == '--mac-sdk': + self.ic.macSdk = a + elif o == '--mac-identity': + self.ic.macIdentity = a + + def about(self): + self.ic.about() + + def setup(self): + self.ic.setup() + + def configure(self): + self.ic.configureAll(self.build_targets) + + def build(self): + self.ic.build(self.build_targets) + + def clean(self): + self.ic.clean(self.build_targets) + + def update(self): + self.ic.update() + + def install(self): + print 'Not yet implemented: install' + + def doxygen(self): + self.ic.doxygen() + + def dist(self): + + type = None + if len(self.args) > 0: + type = self.args[0] + + self.ic.dist(type, self.vcRedistDir, self.qtDir) + + def distftp(self): + type = None + host = None + user = None + password = None + dir = None + + if len(self.args) > 0: + type = self.args[0] + + for o, a in self.opts: + if o == '--host': + host = a + elif o == '--user': + user = a + elif o == '--pass': + password = a + elif o == '--dir': + dir = a + + if not host: + raise Exception('FTP host was not specified.') + + ftp = ftputil.FtpUploader( + host, user, password, dir) + + self.ic.distftp(type, ftp) + + def destroy(self): + self.ic.destroy() + + def kill(self): + self.ic.kill() + + def usage(self): + self.ic.usage() + + def revision(self): + self.ic.revision() + + def reformat(self): + self.ic.reformat() + + def open(self): + self.ic.open() + + def genlist(self): + self.ic.printGeneratorList() + + def reset(self): + self.ic.reset() + + def signwin(self): + pfx = None + pwd = None + dist = False + for o, a in self.opts: + if o == '--pfx': + pfx = a + elif o == '--pwd': + pwd = a + elif o == '--dist': + dist = True + self.ic.signwin(pfx, pwd, dist) + + def signmac(self): + self.ic.signmac() diff --git a/ext/toolchain/shitindent.py b/ext/toolchain/shitindent.py new file mode 100644 index 00000000..a6df18d1 --- /dev/null +++ b/ext/toolchain/shitindent.py @@ -0,0 +1,31 @@ + def getDebianArch(self): + if os.uname()[4][:3] == 'arm': + return 'armhf' + + # os_bits should be loaded with '32bit' or '64bit' + import platform + (os_bits, other) = platform.architecture() + + # get platform based on current platform + if os_bits == '32bit': + return 'i386' + elif os_bits == '64bit': + return 'amd64' + else: + raise Exception("unknown os bits: " + os_bits) + + def getLinuxPlatform(self): + if os.uname()[4][:3] == 'arm': + return 'Linux-armv6l' + + # os_bits should be loaded with '32bit' or '64bit' + import platform + (os_bits, other) = platform.architecture() + + # get platform based on current platform + if os_bits == '32bit': + return 'Linux-i686' + elif os_bits == '64bit': + return 'Linux-x86_64' + else: + raise Exception("unknown os bits: " + os_bits) \ No newline at end of file From 16d9bd8f0d3f0be66c41f56ce925b3c54ea3e5b5 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 15:19:54 +0100 Subject: [PATCH 35/66] Fixed messed up indentation in commands1.py #4695 --- ext/toolchain/commands1.py | 179 +++++++++++++++++++------------------ 1 file changed, 94 insertions(+), 85 deletions(-) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index 32716b12..876b9efd 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -55,14 +55,14 @@ class Toolchain: 'reformat' : ['', []], 'open' : ['', []], 'genlist' : ['', []], - 'reset' : ['', []], - 'signwin' : ['', ['pfx=', 'pwd=', 'dist']], - 'signmac' : ['', []] + 'reset' : ['', []], + 'signwin' : ['', ['pfx=', 'pwd=', 'dist']], + 'signmac' : ['', []] } # aliases to valid commands cmd_alias_dict = { - 'info' : 'about', + 'info' : 'about', 'help' : 'usage', 'package' : 'dist', 'docs' : 'doxygen', @@ -319,7 +319,6 @@ class InternalCommands: self.configure(target) def checkGTest(self): - dir = self.extDir + '/' + self.gtestDir if (os.path.isdir(dir)): return @@ -335,7 +334,6 @@ class InternalCommands: self.zipExtractAll(zip, dir) def checkGMock(self): - dir = self.extDir + '/' + self.gmockDir if (os.path.isdir(dir)): return @@ -524,14 +522,14 @@ class InternalCommands: version = commands.getoutput("qmake --version") result = re.search('(\d+)\.(\d+)\.(\d)', version) - if not result: + if not result: raise Exception("Could not get qmake version.") - major = int(result.group(1)) - minor = int(result.group(2)) - rev = int(result.group(3)) - - return (major, minor, rev) + major = int(result.group(1)) + minor = int(result.group(2)) + rev = int(result.group(3)) + + return (major, minor, rev) def getMacSdkDir(self): sdkName = "macosx" + self.macSdk @@ -575,10 +573,10 @@ class InternalCommands: # if return code from cmake is not 0, then either something has # gone terribly wrong with --version, or it genuinely doesn't exist. print ('Could not find `%s` in system path.\n' - 'Download the latest version from:\n %s') % ( + 'Download the latest version from:\n %s') % ( self.cmake_cmd, self.cmake_url) raise Exception('Cannot continue without CMake.') - else: + else: return self.cmake_cmd def persist_qt(self): @@ -786,15 +784,15 @@ class InternalCommands: # reorganize Qt frameworks layout on Mac 10.9.5 or later # http://goo.gl/BFnQ8l # QtCore example: - # QtCore.framework/ - # QtCore -> Versions/Current/QtCore - # Resources -> Versions/Current/Resources - # Versions/ - # Current -> 5 - # 5/ - # QtCore - # Resources/ - # Info.plist + # QtCore.framework/ + # QtCore -> Versions/Current/QtCore + # Resources -> Versions/Current/Resources + # Versions/ + # Current -> 5 + # 5/ + # QtCore + # Resources/ + # Info.plist targetDir = self.getGenerator().getBinDir(target) target = targetDir + "/Synergy.app/Contents/Frameworks" @@ -1026,7 +1024,7 @@ class InternalCommands: elif type == 'rpm': if sys.platform == 'linux2': - self.distRpm() + self.distRpm() else: package_unsupported = True @@ -1062,9 +1060,9 @@ class InternalCommands: ("Package type, '%s' is not supported for platform, '%s'") % (type, sys.platform)) - def distRpm(self): - rpmDir = self.getGenerator().buildDir + '/rpm' - if os.path.exists(rpmDir): + def distRpm(self): + rpmDir = self.getGenerator().buildDir + '/rpm' + if os.path.exists(rpmDir): shutil.rmtree(rpmDir) os.makedirs(rpmDir) @@ -1072,51 +1070,52 @@ class InternalCommands: templateFile = open(self.cmake_dir + '/synergy.spec.in') template = templateFile.read() - template = template.replace('${in:version}', self.getVersionFromCmake()) - + template = template.replace('${in:version}', self.getVersionFromCmake()) + specPath = rpmDir + '/synergy.spec' specFile = open(specPath, 'w') specFile.write(template) specFile.close() - version = self.getVersionFromCmake() - target = '../../bin/synergy-%s-%s.rpm' % ( - version, self.getLinuxPlatform()) - + version = self.getVersionFromCmake() + target = '../../bin/synergy-%s-%s.rpm' % ( + version, self.getLinuxPlatform()) + try: self.try_chdir(rpmDir) - cmd = 'rpmbuild -bb --define "_topdir `pwd`" synergy.spec' - print "Command: " + cmd + cmd = 'rpmbuild -bb --define "_topdir `pwd`" synergy.spec' + print "Command: " + cmd err = os.system(cmd) if err != 0: raise Exception('rpmbuild failed: ' + str(err)) self.unixMove('RPMS/*/*.rpm', target) - cmd = 'rpmlint ' + target - print "Command: " + cmd + cmd = 'rpmlint ' + target + print "Command: " + cmd err = os.system(cmd) if err != 0: raise Exception('rpmlint failed: ' + str(err)) + finally: self.restore_chdir() - def distDeb(self): + def distDeb(self): buildDir = self.getGenerator().buildDir binDir = self.getGenerator().binDir resDir = self.cmake_dir - version = self.getVersionFromCmake() - package = '%s-%s-%s' % ( - self.project, version, self.getLinuxPlatform()) + version = self.getVersionFromCmake() + package = '%s-%s-%s' % ( + self.project, version, self.getLinuxPlatform()) - debDir = '%s/deb' % buildDir - if os.path.exists(debDir): + debDir = '%s/deb' % buildDir + if os.path.exists(debDir): shutil.rmtree(debDir) - metaDir = '%s/%s/DEBIAN' % (debDir, package) + metaDir = '%s/%s/DEBIAN' % (debDir, package) os.makedirs(metaDir) templateFile = open(resDir + '/deb/control.in') @@ -1181,14 +1180,14 @@ class InternalCommands: self.try_chdir(debDir) # TODO: consider dpkg-buildpackage (higher level tool) - cmd = 'fakeroot dpkg-deb --build %s' % package - print "Command: " + cmd + cmd = 'fakeroot dpkg-deb --build %s' % package + print "Command: " + cmd err = os.system(cmd) if err != 0: raise Exception('dpkg-deb failed: ' + str(err)) - cmd = 'lintian %s.deb' % package - print "Command: " + cmd + cmd = 'lintian %s.deb' % package + print "Command: " + cmd err = os.system(cmd) if err != 0: raise Exception('lintian failed: ' + str(err)) @@ -1307,7 +1306,7 @@ class InternalCommands: if generator.endswith('Win64'): arch = 'x64' - installDirVar = '$PROGRAMFILES64' + installDirVar = '$PROGRAMFILES64' templateFile = open(self.cmake_dir + '\Installer.nsi.in') template = templateFile.read() @@ -1368,9 +1367,19 @@ class InternalCommands: nsPluginTarget = self.getLibraryDistFilename(type, pluginsDir, 'ns') self.ftpUpload(ftp, nsPluginSource, nsPluginTarget) - # os_bits should be loaded with '32bit' or '64bit' - import platform - (os_bits, other) = platform.architecture() + def getLibraryDistFilename(self, type, dir, name): + (platform, packageExt, libraryExt) = self.getDistributePlatformInfo(type) + branch = self.getGitBranchName() + revision = self.getGitRevision() + firstPart = '%s-%s-%s-%s' % (name, branch, revision, platform) + + filename = '%s.%s' % (firstPart, libraryExt) + if type == 'rpm' or type == 'deb': + # linux is a bit special, include dist type (deb/rpm in filename) + filename = '%s-%s.%s' % (firstPart, packageExt, libraryExt) + + return filename + def findLibraryFile(self, type, dir, name): (platform, packageExt, libraryExt) = self.getDistributePlatformInfo(type) ext = libraryExt @@ -1439,37 +1448,37 @@ class InternalCommands: self.getGitBranchName(), self.getGitRevision()) return re.sub(pattern, replace, self.dist_name(type)) - def getDebianArch(self): - if os.uname()[4][:3] == 'arm': - return 'armhf' + def getDebianArch(self): + if os.uname()[4][:3] == 'arm': + return 'armhf' - # os_bits should be loaded with '32bit' or '64bit' - import platform - (os_bits, other) = platform.architecture() - - # get platform based on current platform - if os_bits == '32bit': - return 'i386' - elif os_bits == '64bit': - return 'amd64' - else: - raise Exception("unknown os bits: " + os_bits) + # os_bits should be loaded with '32bit' or '64bit' + import platform + (os_bits, other) = platform.architecture() - def getLinuxPlatform(self): - if os.uname()[4][:3] == 'arm': - return 'Linux-armv6l' + # get platform based on current platform + if os_bits == '32bit': + return 'i386' + elif os_bits == '64bit': + return 'amd64' + else: + raise Exception("unknown os bits: " + os_bits) - # os_bits should be loaded with '32bit' or '64bit' - import platform - (os_bits, other) = platform.architecture() - - # get platform based on current platform - if os_bits == '32bit': - return 'Linux-i686' - elif os_bits == '64bit': - return 'Linux-x86_64' - else: - raise Exception("unknown os bits: " + os_bits) + def getLinuxPlatform(self): + if os.uname()[4][:3] == 'arm': + return 'Linux-armv6l' + + # os_bits should be loaded with '32bit' or '64bit' + import platform + (os_bits, other) = platform.architecture() + + # get platform based on current platform + if os_bits == '32bit': + return 'Linux-i686' + elif os_bits == '64bit': + return 'Linux-x86_64' + else: + raise Exception("unknown os bits: " + os_bits) def dist_usage(self): print ('Usage: %s package [package-type]\n' @@ -1533,7 +1542,7 @@ class InternalCommands: oldGenerator = self.findGeneratorFromConfig() if not oldGenerator == None: - for target in ['debug', 'release']: + for target in ['debug', 'release']: buildDir = oldGenerator.getBuildDir(target) cmakeCacheFilename = 'CMakeCache.txt' @@ -1689,7 +1698,7 @@ class InternalCommands: return generators[generator_id] - def get_vcvarsall(self, generator): + def get_vcvarsall(self, generator): import platform, _winreg # os_bits should be loaded with '32bit' or '64bit' @@ -1719,7 +1728,7 @@ class InternalCommands: if os_bits == '64bit': path = value + r'vc\vcvarsall.bat' else: - path = value + r'vcvarsall.bat' + path = value + r'vcvarsall.bat' if not os.path.exists(path): raise Exception("'%s' not found." % path) @@ -1849,7 +1858,7 @@ class InternalCommands: # qt 4.3 generates ui_ files. for filename in glob.glob("src/gui/ui_*"): - os.remove(filename) + os.remove(filename) # the command handler should be called only from hm.py (i.e. directly # from the command prompt). the purpose of this class is so that we @@ -1918,7 +1927,7 @@ class CommandHandler: type = None if len(self.args) > 0: - type = self.args[0] + type = self.args[0] self.ic.dist(type, self.vcRedistDir, self.qtDir) From b71c2981637df47db7c497cccd690144da52cda8 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 15:25:14 +0100 Subject: [PATCH 36/66] Changed sleep time to 100ms for unit test (hack) #4651 --- src/test/unittests/ipc/IpcLogOutputterTests.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/unittests/ipc/IpcLogOutputterTests.cpp b/src/test/unittests/ipc/IpcLogOutputterTests.cpp index 254752c4..9233256b 100644 --- a/src/test/unittests/ipc/IpcLogOutputterTests.cpp +++ b/src/test/unittests/ipc/IpcLogOutputterTests.cpp @@ -120,7 +120,9 @@ TEST(IpcLogOutputterTests, write_overBufferRateLimit_lastLineTruncated) // after waiting the time limit send another to make sure // we can log after the time limit passes. - ARCH->sleep(0.01); // 10ms + // HACK: sleep causes the unit test to fail intermittently, + // so lets try 100ms (there must be a better way to solve this) + ARCH->sleep(0.1); // 100ms outputter.write(kNOTE, "mock 3"); outputter.write(kNOTE, "mock 4"); outputter.sendBuffer(); From cbb4cec8414975e6a5267b65ec5ac67dc28f2d15 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 15:28:32 +0100 Subject: [PATCH 37/66] Fixed bad indentation for getLinuxPlatform() #4695 Caused by 16d9bd8f0d3f0be66c41f56ce925b3c54ea3e5b5 --- ext/toolchain/commands1.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index 876b9efd..feaa9f06 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -1468,17 +1468,17 @@ class InternalCommands: if os.uname()[4][:3] == 'arm': return 'Linux-armv6l' - # os_bits should be loaded with '32bit' or '64bit' - import platform - (os_bits, other) = platform.architecture() + # os_bits should be loaded with '32bit' or '64bit' + import platform + (os_bits, other) = platform.architecture() - # get platform based on current platform - if os_bits == '32bit': - return 'Linux-i686' - elif os_bits == '64bit': - return 'Linux-x86_64' - else: - raise Exception("unknown os bits: " + os_bits) + # get platform based on current platform + if os_bits == '32bit': + return 'Linux-i686' + elif os_bits == '64bit': + return 'Linux-x86_64' + else: + raise Exception("unknown os bits: " + os_bits) def dist_usage(self): print ('Usage: %s package [package-type]\n' From e4959a76617ccf37da7b50623269da181ea886ee Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 15:44:18 +0100 Subject: [PATCH 38/66] Removed angry files accidentally uploaded I'm having a bad day --- ext/toolchain/fuckfuck.py | 2000 ----------------------------------- ext/toolchain/shitindent.py | 31 - 2 files changed, 2031 deletions(-) delete mode 100644 ext/toolchain/fuckfuck.py delete mode 100644 ext/toolchain/shitindent.py diff --git a/ext/toolchain/fuckfuck.py b/ext/toolchain/fuckfuck.py deleted file mode 100644 index 4dcb6def..00000000 --- a/ext/toolchain/fuckfuck.py +++ /dev/null @@ -1,2000 +0,0 @@ -# synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. -# Copyright (C) 2009 Nick Bolton -# -# This package is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# found in the file LICENSE that should have accompanied this file. -# -# This package is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -# TODO: split this file up, it's too long! - -import sys, os, ConfigParser, shutil, re, ftputil, zipfile, glob, commands -from generators import VisualStudioGenerator, EclipseGenerator, XcodeGenerator, MakefilesGenerator -from getopt import gnu_getopt - -if sys.version_info >= (2, 4): - import subprocess - -class Toolchain: - - # minimum required version. - # 2.6 needed for ZipFile.extractall. - # do not change to 2.7, as the build machines are still at 2.6 - # and are a massive pain in the ass to upgrade. - requiredMajor = 2 - requiredMinor = 6 - - # options used by all commands - globalOptions = 'v' - globalOptionsLong = ['no-prompts', 'verbose', 'skip-gui', 'skip-core'] - - # list of valid commands as keys. the values are optarg strings, but most - # are None for now (this is mainly for extensibility) - cmd_opt_dict = { - 'about' : ['', []], - 'setup' : ['g:', ['generator=']], - 'configure' : ['g:dr', ['generator=', 'debug', 'release', 'mac-sdk=', 'mac-identity=']], - 'build' : ['dr', ['debug', 'release']], - 'clean' : ['dr', ['debug', 'release']], - 'update' : ['', []], - 'install' : ['', []], - 'doxygen' : ['', []], - 'dist' : ['', ['vcredist-dir=', 'qt-dir=']], - 'distftp' : ['', ['host=', 'user=', 'pass=', 'dir=']], - 'kill' : ['', []], - 'usage' : ['', []], - 'revision' : ['', []], - 'reformat' : ['', []], - 'open' : ['', []], - 'genlist' : ['', []], - 'reset' : ['', []], - 'signwin' : ['', ['pfx=', 'pwd=', 'dist']], - 'signmac' : ['', []] - } - - # aliases to valid commands - cmd_alias_dict = { - 'info' : 'about', - 'help' : 'usage', - 'package' : 'dist', - 'docs' : 'doxygen', - 'make' : 'build', - 'cmake' : 'configure', - } - - def complete_command(self, arg): - completions = [] - - for cmd, optarg in self.cmd_opt_dict.iteritems(): - # if command was matched fully, return only this, so that - # if `dist` is typed, it will return only `dist` and not - # `dist` and `distftp` for example. - if cmd == arg: - return [cmd,] - if cmd.startswith(arg): - completions.append(cmd) - - for alias, cmd in self.cmd_alias_dict.iteritems(): - # don't know if this will work just like above, but it's - # probably worth adding. - if alias == arg: - return [alias,] - if alias.startswith(arg): - completions.append(alias) - - return completions - - def start_cmd(self, argv): - - cmd_arg = '' - if len(argv) > 1: - cmd_arg = argv[1] - - # change common help args to help command - if cmd_arg in ('--help', '-h', '--usage', '-u', '/?'): - cmd_arg = 'usage' - - completions = self.complete_command(cmd_arg) - - if cmd_arg and len(completions) > 0: - - if len(completions) == 1: - - # get the only completion (since in this case we have 1) - cmd = completions[0] - - # build up the first part of the map (for illustrative purposes) - cmd_map = list() - if cmd_arg != cmd: - cmd_map.append(cmd_arg) - cmd_map.append(cmd) - - # map an alias to the command, and build up the map - if cmd in self.cmd_alias_dict.keys(): - alias = cmd - if cmd_arg == cmd: - cmd_map.append(alias) - cmd = self.cmd_alias_dict[cmd] - cmd_map.append(cmd) - - # show command map to avoid confusion - if len(cmd_map) != 0: - print 'Mapping command: %s' % ' -> '.join(cmd_map) - - self.run_cmd(cmd, argv[2:]) - - return 0 - - else: - print ( - 'Command `%s` too ambiguous, ' - 'could mean any of: %s' - ) % (cmd_arg, ', '.join(completions)) - else: - - if len(argv) == 1: - print 'No command specified, showing usage.\n' - else: - print 'Command not recognised: %s\n' % cmd_arg - - self.run_cmd('usage') - - # generic error code if not returned sooner - return 1 - - def run_cmd(self, cmd, argv = []): - - verbose = False - try: - options_pair = self.cmd_opt_dict[cmd] - - options = self.globalOptions + options_pair[0] - - options_long = [] - options_long.extend(self.globalOptionsLong) - options_long.extend(options_pair[1]) - - opts, args = gnu_getopt(argv, options, options_long) - - for o, a in opts: - if o in ('-v', '--verbose'): - verbose = True - - # pass args and optarg data to command handler, which figures out - # how to handle the arguments - handler = CommandHandler(argv, opts, args, verbose) - - # use reflection to get the function pointer - cmd_func = getattr(handler, cmd) - - cmd_func() - except: - if not verbose: - # print friendly error for users - sys.stderr.write('Error: ' + sys.exc_info()[1].__str__() + '\n') - sys.exit(1) - else: - # if user wants to be verbose let python do it's thing - raise - - def run(self, argv): - if sys.version_info < (self.requiredMajor, self.requiredMinor): - print ('Python version must be at least ' + - str(self.requiredMajor) + '.' + str(self.requiredMinor) + ', but is ' + - str(sys.version_info[0]) + '.' + str(sys.version_info[1])) - sys.exit(1) - - try: - self.start_cmd(argv) - except KeyboardInterrupt: - print '\n\nUser aborted, exiting.' - -class InternalCommands: - - project = 'synergy' - setup_version = 5 # increment to force setup/config - website_url = 'http://synergy-project.org/' - - this_cmd = 'hm' - cmake_cmd = 'cmake' - qmake_cmd = 'qmake' - make_cmd = 'make' - xcodebuild_cmd = 'xcodebuild' - w32_make_cmd = 'mingw32-make' - w32_qt_version = '4.6.2' - defaultTarget = 'release' - - cmake_dir = 'res' - gui_dir = 'src/gui' - doc_dir = 'doc' - extDir = 'ext' - - sln_filename = '%s.sln' % project - xcodeproj_filename = '%s.xcodeproj' % project - configDir = 'build' - configFilename = '%s/%s.cfg' % (configDir, this_cmd) - qtpro_filename = 'gui.pro' - doxygen_filename = 'doxygen.cfg' - - cmake_url = 'http://www.cmake.org/cmake/resources/software.html' - - # try_chdir(...) and restore_chdir() will use this - prevdir = '' - - # by default, no index specified as arg - generator_id = None - - # by default, prompt user for input - no_prompts = False - - # by default, compile the core - enableMakeCore = True - - # by default, compile the gui - enableMakeGui = True - - # by default, unknown - macSdk = None - - # by default, unknown - macIdentity = None - - # gtest dir with version number - gtestDir = 'gtest-1.6.0' - - # gmock dir with version number - gmockDir = 'gmock-1.6.0' - - win32_generators = { - 1 : VisualStudioGenerator('10'), - 2 : VisualStudioGenerator('10 Win64'), - 3 : VisualStudioGenerator('9 2008'), - 4 : VisualStudioGenerator('9 2008 Win64'), - 5 : VisualStudioGenerator('8 2005'), - 6 : VisualStudioGenerator('8 2005 Win64') - } - - unix_generators = { - 1 : MakefilesGenerator(), - 2 : EclipseGenerator(), - } - - darwin_generators = { - 1 : MakefilesGenerator(), - 2 : XcodeGenerator(), - 3 : EclipseGenerator(), - } - - def getBuildDir(self, target=''): - return self.getGenerator().getBuildDir(target) - - def getBinDir(self, target=''): - return self.getGenerator().getBinDir(target) - - def sln_filepath(self): - return '%s\%s' % (self.getBuildDir(), self.sln_filename) - - def xcodeproj_filepath(self, target=''): - return '%s/%s' % (self.getBuildDir(target), self.xcodeproj_filename) - - def usage(self): - app = sys.argv[0] - print ('Usage: %s [-g |-v|--no-prompts|]\n' - '\n' - 'Replace [command] with one of:\n' - ' about Show information about this script\n' - ' setup Runs the initial setup for this script\n' - ' conf Runs cmake (generates project files)\n' - ' open Attempts to open the generated project file\n' - ' build Builds using the platform build chain\n' - ' clean Cleans using the platform build chain\n' - ' kill Kills all synergy processes (run as admin)\n' - ' update Updates the source code from repository\n' - ' revision Display the current source code revision\n' - ' package Create a distribution package (e.g. tar.gz)\n' - ' install Installs the program\n' - ' doxygen Builds doxygen documentation\n' - ' reformat Reformat .cpp and .h files using AStyle\n' - ' genlist Shows the list of available platform generators\n' - ' usage Shows the help screen\n' - '\n' - 'Example: %s build -g 3' - ) % (app, app) - - def configureAll(self, targets, extraArgs=''): - - # if no mode specified, use default - if len(targets) == 0: - targets += [self.defaultTarget,] - - for target in targets: - self.configure(target) - - def checkGTest(self): - dir = self.extDir + '/' + self.gtestDir - if (os.path.isdir(dir)): - return - - zipFilename = dir + '.zip' - if (not os.path.exists(zipFilename)): - raise Exception('GTest zip not found at: ' + zipFilename) - - if not os.path.exists(dir): - os.mkdir(dir) - - zip = zipfile.ZipFile(zipFilename) - self.zipExtractAll(zip, dir) - - def checkGMock(self): - dir = self.extDir + '/' + self.gmockDir - if (os.path.isdir(dir)): - return - - zipFilename = dir + '.zip' - if (not os.path.exists(zipFilename)): - raise Exception('GMock zip not found at: ' + zipFilename) - - if not os.path.exists(dir): - os.mkdir(dir) - - zip = zipfile.ZipFile(zipFilename) - self.zipExtractAll(zip, dir) - - # ZipFile.extractall() is buggy in 2.6.1 - # http://bugs.python.org/issue4710 - def zipExtractAll(self, z, dir): - if not dir.endswith("/"): - dir += "/" - - for f in z.namelist(): - if f.endswith("/"): - os.makedirs(dir + f) - else: - z.extract(f, dir) - - def configure(self, target='', extraArgs=''): - - # ensure latest setup and do not ask config for generator (only fall - # back to prompt if not specified as arg) - self.ensure_setup_latest() - - if sys.platform == "darwin": - config = self.getConfig() - - if self.macSdk: - config.set('hm', 'macSdk', self.macSdk) - elif config.has_option("hm", "macSdk"): - self.macSdk = config.get('hm', 'macSdk') - - if self.macIdentity: - config.set('hm', 'macIdentity', self.macIdentity) - elif config.has_option("hm", "macIdentity"): - self.macIdentity = config.get('hm', 'macIdentity') - - self.write_config(config) - - if not self.macSdk: - raise Exception("Arg missing: --mac-sdk "); - - if not self.macIdentity: - raise Exception("Arg missing: --mac-identity "); - - sdkDir = self.getMacSdkDir() - if not os.path.exists(sdkDir): - raise Exception("Mac SDK not found at: " + sdkDir) - - os.environ["MACOSX_DEPLOYMENT_TARGET"] = self.macSdk - - # default is release - if target == '': - print 'Defaulting target to: ' + self.defaultTarget - target = self.defaultTarget - - # allow user to skip core compile - if self.enableMakeCore: - self.configureCore(target, extraArgs) - - # allow user to skip gui compile - if self.enableMakeGui: - self.configureGui(target, extraArgs) - - self.setConfRun(target) - - def configureCore(self, target="", extraArgs=""): - - # ensure that we have access to cmake - _cmake_cmd = self.persist_cmake() - - # now that we know we've got the latest setup, we can ask the config - # file for the generator (but again, we only fall back to this if not - # specified as arg). - generator = self.getGenerator() - - if generator != self.findGeneratorFromConfig(): - print('Generator changed, running setup.') - self.setup(target) - - cmake_args = '' - if generator.cmakeName != '': - cmake_args += ' -G "' + generator.cmakeName + '"' - - # for makefiles always specify a build type (debug, release, etc) - if generator.cmakeName.find('Unix Makefiles') != -1: - cmake_args += ' -DCMAKE_BUILD_TYPE=' + target.capitalize() - - elif sys.platform == "darwin": - macSdkMatch = re.match("(\d+)\.(\d+)", self.macSdk) - if not macSdkMatch: - raise Exception("unknown osx version: " + self.macSdk) - - sdkDir = self.getMacSdkDir() - cmake_args += " -DCMAKE_OSX_SYSROOT=" + sdkDir - cmake_args += " -DCMAKE_OSX_DEPLOYMENT_TARGET=" + self.macSdk - cmake_args += " -DOSX_TARGET_MAJOR=" + macSdkMatch.group(1) - cmake_args += " -DOSX_TARGET_MINOR=" + macSdkMatch.group(2) - - # if not visual studio, use parent dir - sourceDir = generator.getSourceDir() - - self.checkGTest() - self.checkGMock() - - if extraArgs != '': - cmake_args += ' ' + extraArgs - - cmake_cmd_string = _cmake_cmd + cmake_args + ' ' + sourceDir - - # Run from build dir so we have an out-of-source build. - self.try_chdir(self.getBuildDir(target)) - - print "CMake command: " + cmake_cmd_string - err = os.system(cmake_cmd_string) - - self.restore_chdir() - - if generator.cmakeName.find('Eclipse') != -1: - self.fixCmakeEclipseBug() - - if err != 0: - raise Exception('CMake encountered error: ' + str(err)) - - def configureGui(self, target="", extraArgs=""): - - # make sure we have qmake - self.persist_qmake() - - qmake_cmd_string = self.qmake_cmd + " " + self.qtpro_filename + " -r" - - if sys.platform == "darwin": - - # create makefiles on mac (not xcode). - qmake_cmd_string += " -spec macx-g++" - - (major, minor) = self.getMacVersion() - if major == 10 and minor <= 4: - # 10.4: universal (intel and power pc) - qmake_cmd_string += ' CONFIG+="ppc i386"' - - libs = ( - "-framework ApplicationServices " - "-framework Security " - "-framework cocoa") - - if major == 10 and minor >= 6: - libs += " -framework ServiceManagement" - - qmake_cmd_string += " \"MACX_LIBS=%s\" " % libs - - sdkDir = self.getMacSdkDir() - shortForm = "macosx" + self.macSdk - version = str(major) + "." + str(minor) - - qmake_cmd_string += " QMAKE_MACOSX_DEPLOYMENT_TARGET=" + version - - (qMajor, qMinor, qRev) = self.getQmakeVersion() - if qMajor <= 4: - # 4.6: qmake takes full sdk dir. - qmake_cmd_string += " QMAKE_MAC_SDK=" + sdkDir - else: - # 5.2: now we need to use the .path setting. - qmake_cmd_string += " QMAKE_MAC_SDK=" + shortForm - qmake_cmd_string += " QMAKE_MAC_SDK." + shortForm + ".path=" + sdkDir - - print "QMake command: " + qmake_cmd_string - - # run qmake from the gui dir - self.try_chdir(self.gui_dir) - err = os.system(qmake_cmd_string) - self.restore_chdir() - - if err != 0: - raise Exception('QMake encountered error: ' + str(err)) - - def getQmakeVersion(self): - version = commands.getoutput("qmake --version") - result = re.search('(\d+)\.(\d+)\.(\d)', version) - - if not result: - raise Exception("Could not get qmake version.") - - major = int(result.group(1)) - minor = int(result.group(2)) - rev = int(result.group(3)) - - return (major, minor, rev) - - def getMacSdkDir(self): - sdkName = "macosx" + self.macSdk - - # Ideally we'll use xcrun (which is influenced by $DEVELOPER_DIR), then try a couple - # fallbacks to known paths if xcrun is not available - status, sdkPath = commands.getstatusoutput("xcrun --show-sdk-path --sdk " + sdkName) - if status == 0 and sdkPath: - return sdkPath - - developerDir = os.getenv("DEVELOPER_DIR") - if not developerDir: - developerDir = "/Applications/Xcode.app/Contents/Developer" - - sdkDirName = sdkName.replace("macosx", "MacOSX") - sdkPath = developerDir + "/Platforms/MacOSX.platform/Developer/SDKs/" + sdkDirName + ".sdk" - if os.path.exists(sdkPath): - return sdkPath - - return "/Developer/SDKs/" + sdkDirName + ".sdk" - - # http://tinyurl.com/cs2rxxb - def fixCmakeEclipseBug(self): - print "Fixing CMake Eclipse bugs..." - - file = open('.project', 'r+') - content = file.read() - pattern = re.compile('\s+.+', re.S) - content = pattern.sub('', content) - file.seek(0) - file.write(content) - file.truncate() - file.close() - - def persist_cmake(self): - # even though we're running `cmake --version`, we're only doing this for the 0 return - # code; we don't care about the version, since CMakeLists worrys about this for us. - err = os.system('%s --version' % self.cmake_cmd) - - if err != 0: - # if return code from cmake is not 0, then either something has - # gone terribly wrong with --version, or it genuinely doesn't exist. - print ('Could not find `%s` in system path.\n' - 'Download the latest version from:\n %s') % ( - self.cmake_cmd, self.cmake_url) - raise Exception('Cannot continue without CMake.') - else: - return self.cmake_cmd - - def persist_qt(self): - self.persist_qmake() - - def persist_qmake(self): - # cannot use subprocess on < python 2.4 - if sys.version_info < (2, 4): - return - - try: - p = subprocess.Popen( - [self.qmake_cmd, '--version'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - except: - print >> sys.stderr, 'Error: Could not find qmake.' - if sys.platform == 'win32': # windows devs usually need hints ;) - print ( - 'Suggestions:\n' - '1. Ensure that qmake.exe exists in your system path.\n' - '2. Try to download Qt (check our dev FAQ for links):\n' - ' qt-sdk-win-opensource-2010.02.exe') - raise Exception('Cannot continue without qmake.') - - stdout, stderr = p.communicate() - if p.returncode != 0: - raise Exception('Could not test for cmake: %s' % stderr) - else: - m = re.search('.*Using Qt version (\d+\.\d+\.\d+).*', stdout) - if m: - if sys.platform == 'win32': - ver = m.group(1) - if ver != self.w32_qt_version: # TODO: test properly - print >> sys.stderr, ( - 'Warning: Not using supported Qt version %s' - ' (your version is %s).' - ) % (self.w32_qt_version, ver) - else: - pass # any version should be ok for other platforms - else: - raise Exception('Could not find qmake version.') - - def ensureConfHasRun(self, target, skipConfig): - if self.hasConfRun(target): - print 'Skipping config for target: ' + target - skipConfig = True - - if not skipConfig: - self.configure(target) - - def build(self, targets=[], skipConfig=False): - - # if no mode specified, use default - if len(targets) == 0: - targets += [self.defaultTarget,] - - self.ensure_setup_latest() - - self.loadConfig() - - # allow user to skip core compile - if self.enableMakeCore: - self.makeCore(targets) - - # allow user to skip gui compile - if self.enableMakeGui: - self.makeGui(targets) - - def loadConfig(self): - config = self.getConfig() - - if config.has_option("hm", "macSdk"): - self.macSdk = config.get("hm", "macSdk") - - if config.has_option("hm", "macIdentity"): - self.macIdentity = config.get("hm", "macIdentity") - - def makeCore(self, targets): - - generator = self.getGeneratorFromConfig().cmakeName - - if self.macSdk: - os.environ["MACOSX_DEPLOYMENT_TARGET"] = self.macSdk - - if generator.find('Unix Makefiles') != -1: - for target in targets: - self.runBuildCommand(self.make_cmd, target) - else: - for target in targets: - if generator.startswith('Visual Studio'): - self.run_vcbuild(generator, target, self.sln_filepath()) - elif generator == 'Xcode': - cmd = self.xcodebuild_cmd + ' -configuration ' + target.capitalize() - self.runBuildCommand(cmd, target) - else: - raise Exception('Build command not supported with generator: ' + generator) - - def makeGui(self, targets, args=""): - for target in targets: - - if sys.platform == 'win32': - - gui_make_cmd = self.w32_make_cmd + ' ' + target + args - print 'Make GUI command: ' + gui_make_cmd - - self.try_chdir(self.gui_dir) - err = os.system(gui_make_cmd) - self.restore_chdir() - - if err != 0: - raise Exception(gui_make_cmd + ' failed with error: ' + str(err)) - - elif sys.platform in ['linux2', 'sunos5', 'freebsd7', 'darwin']: - - gui_make_cmd = self.make_cmd + " -w" + args - print 'Make GUI command: ' + gui_make_cmd - - # start with a clean app bundle - targetDir = self.getGenerator().getBinDir(target) - bundleTargetDir = targetDir + '/Synergy.app' - if os.path.exists(bundleTargetDir): - shutil.rmtree(bundleTargetDir) - - binDir = self.getGenerator().binDir - bundleTempDir = binDir + '/Synergy.app' - if os.path.exists(bundleTempDir): - shutil.rmtree(bundleTempDir) - - self.try_chdir(self.gui_dir) - err = os.system(gui_make_cmd) - self.restore_chdir() - - if err != 0: - raise Exception(gui_make_cmd + ' failed with error: ' + str(err)) - - if sys.platform == 'darwin' and not "clean" in args: - self.macPostGuiMake(target) - - self.fixQtFrameworksLayout(target) - else: - raise Exception('Unsupported platform: ' + sys.platform) - - def macPostGuiMake(self, target): - bundle = 'Synergy.app' - binDir = self.getGenerator().binDir - targetDir = self.getGenerator().getBinDir(target) - bundleTempDir = binDir + '/' + bundle - bundleTargetDir = targetDir + '/' + bundle - - if os.path.exists(bundleTempDir): - shutil.move(bundleTempDir, bundleTargetDir) - - if self.enableMakeCore: - # copy core binaries into the bundle, since the gui - # now looks for the binaries in the current app dir. - - bundleBinDir = bundleTargetDir + "/Contents/MacOS/" - shutil.copy(targetDir + "/synergyc", bundleBinDir) - shutil.copy(targetDir + "/synergys", bundleBinDir) - shutil.copy(targetDir + "/syntool", bundleBinDir) - - self.loadConfig() - if not self.macIdentity: - raise Exception("run config with --mac-identity") - - if self.enableMakeGui: - # use qt to copy libs to bundle so no dependencies are needed. do not create a - # dmg at this point, since we need to sign it first, and then create our own - # after signing (so that qt does not affect the signed app bundle). - bin = "macdeployqt Synergy.app -verbose=2" - self.try_chdir(targetDir) - err = os.system(bin) - self.restore_chdir() - print bundleTargetDir - if err != 0: - raise Exception(bin + " failed with error: " + str(err)) - - (qMajor, qMinor, qRev) = self.getQmakeVersion() - if qMajor <= 4: - frameworkRootDir = "/Library/Frameworks" - else: - # TODO: auto-detect, qt can now be installed anywhere. - frameworkRootDir = "/Developer/Qt5.2.1/5.2.1/clang_64/lib" - - target = bundleTargetDir + "/Contents/Frameworks" - - # copy the missing Info.plist files for the frameworks. - for root, dirs, files in os.walk(target): - for dir in dirs: - if dir.startswith("Qt"): - shutil.copy( - frameworkRootDir + "/" + dir + "/Contents/Info.plist", - target + "/" + dir + "/Resources/") - - def symlink(self, source, target): - if not os.path.exists(target): - os.symlink(source, target) - - def move(self, source, target): - if os.path.exists(source): - shutil.move(source, target) - - def fixQtFrameworksLayout(self, target): - # reorganize Qt frameworks layout on Mac 10.9.5 or later - # http://goo.gl/BFnQ8l - # QtCore example: - # QtCore.framework/ - # QtCore -> Versions/Current/QtCore - # Resources -> Versions/Current/Resources - # Versions/ - # Current -> 5 - # 5/ - # QtCore - # Resources/ - # Info.plist - targetDir = self.getGenerator().getBinDir(target) - - target = targetDir + "/Synergy.app/Contents/Frameworks" - (major, minor) = self.getMacVersion() - if major == 10: - if minor >= 9: - for root, dirs, files in os.walk(target): - for dir in dirs: - if dir.startswith("Qt"): - self.try_chdir(target + "/" + dir +"/Versions") - self.symlink("5", "Current") - self.move("../Resources", "5") - self.restore_chdir() - - self.try_chdir(target + "/" + dir) - dot = dir.find('.') - frameworkName = dir[:dot] - self.symlink("Versions/Current/" + frameworkName, frameworkName) - self.symlink("Versions/Current/Resources", "Resources") - self.restore_chdir() - - def signmac(self): - self.loadConfig() - if not self.macIdentity: - raise Exception("run config with --mac-identity") - - self.try_chdir("bin/Release/") - err = os.system( - 'codesign --deep -fs "' + self.macIdentity + '" Synergy.app') - self.restore_chdir() - - if err != 0: - raise Exception("codesign failed with error: " + str(err)) - - def signwin(self, pfx, pwdFile, dist): - generator = self.getGeneratorFromConfig().cmakeName - if not generator.startswith('Visual Studio'): - raise Exception('only windows is supported') - - f = open(pwdFile) - lines = f.readlines() - f.close() - pwd = lines[0] - - if (dist): - self.signFile(pfx, pwd, 'bin/Release', self.dist_name('win')) - else: - self.signFile(pfx, pwd, 'bin/Release', 'synergy.exe') - self.signFile(pfx, pwd, 'bin/Release', 'synergyc.exe') - self.signFile(pfx, pwd, 'bin/Release', 'synergys.exe') - self.signFile(pfx, pwd, 'bin/Release', 'synergyd.exe') - self.signFile(pfx, pwd, 'bin/Release', 'syntool.exe') - self.signFile(pfx, pwd, 'bin/Release', 'synwinhk.dll') - - def signFile(self, pfx, pwd, dir, file): - self.try_chdir(dir) - err = os.system( - 'signtool sign' - ' /f ' + pfx + - ' /p ' + pwd + - ' /t http://timestamp.verisign.com/scripts/timstamp.dll ' + - file) - self.restore_chdir() - - if err != 0: - raise Exception("signtool failed with error: " + str(err)) - - def runBuildCommand(self, cmd, target): - - self.try_chdir(self.getBuildDir(target)) - err = os.system(cmd) - self.restore_chdir() - - if err != 0: - raise Exception(cmd + ' failed: ' + str(err)) - - def clean(self, targets=[]): - - # if no mode specified, use default - if len(targets) == 0: - targets += [self.defaultTarget,] - - # allow user to skip core clean - if self.enableMakeCore: - self.cleanCore(targets) - - # allow user to skip qui clean - if self.enableMakeGui: - self.cleanGui(targets) - - def cleanCore(self, targets): - generator = self.getGeneratorFromConfig().cmakeName - - if generator.startswith('Visual Studio'): - # special case for version 10, use new /target:clean - if generator.startswith('Visual Studio 10'): - for target in targets: - self.run_vcbuild(generator, target, self.sln_filepath(), '/target:clean') - - # any other version of visual studio, use /clean - elif generator.startswith('Visual Studio'): - for target in targets: - self.run_vcbuild(generator, target, self.sln_filepath(), '/clean') - - else: - cmd = '' - if generator == "Unix Makefiles": - print 'Cleaning with GNU Make...' - cmd = self.make_cmd - elif generator == 'Xcode': - print 'Cleaning with Xcode...' - cmd = self.xcodebuild_cmd - else: - raise Exception('Not supported with generator: ' + generator) - - for target in targets: - self.try_chdir(self.getBuildDir(target)) - err = os.system(cmd + ' clean') - self.restore_chdir() - - if err != 0: - raise Exception('Clean failed: ' + str(err)) - - def cleanGui(self, targets): - self.makeGui(targets, " clean") - - def open(self): - generator = self.getGeneratorFromConfig().cmakeName - if generator.startswith('Visual Studio'): - print 'Opening with %s...' % generator - self.open_internal(self.sln_filepath()) - - elif generator.startswith('Xcode'): - print 'Opening with %s...' % generator - self.open_internal(self.xcodeproj_filepath(), 'open') - - else: - raise Exception('Not supported with generator: ' + generator) - - def update(self): - print "Running Subversion update..." - err = os.system('svn update') - if err != 0: - raise Exception('Could not update from repository with error code code: ' + str(err)) - - def revision(self): - print self.find_revision() - - def find_revision(self): - return self.getGitRevision() - - def getGitRevision(self): - if sys.version_info < (2, 4): - raise Exception("Python 2.4 or greater required.") - - p = subprocess.Popen( - ["git", "log", "--pretty=format:%h", "-n", "1"], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - - stdout, stderr = p.communicate() - - if p.returncode != 0: - raise Exception('Could not get revision, git error: ' + str(p.returncode)) - - return stdout.strip() - - def getGitBranchName(self): - if sys.version_info < (2, 4): - raise Exception("Python 2.4 or greater required.") - - p = subprocess.Popen( - ["git", "rev-parse", "--abbrev-ref", "HEAD"], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - - stdout, stderr = p.communicate() - - if p.returncode != 0: - raise Exception('Could not get branch name, git error: ' + str(p.returncode)) - - return stdout.strip() - - def find_revision_svn(self): - if sys.version_info < (2, 4): - stdout = commands.getoutput('svn info') - else: - p = subprocess.Popen(['svn', 'info'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = p.communicate() - - if p.returncode != 0: - raise Exception('Could not get revision - svn info failed with code: ' + str(p.returncode)) - - m = re.search('.*Revision: (\d+).*', stdout) - if not m: - raise Exception('Could not find revision number in svn info output.') - - return m.group(1) - - def kill(self): - if sys.platform == 'win32': - return os.system('taskkill /F /FI "IMAGENAME eq synergy*"') - else: - raise Exception('Not implemented for platform: ' + sys.platform) - - def doxygen(self): - self.enableMakeGui = False - - # The conf generates doc/doxygen.cfg from cmake/doxygen.cfg.in - self.configure(self.defaultTarget, '-DCONF_DOXYGEN:BOOL=TRUE') - - err = os.system('doxygen %s/%s' % (self.doc_dir, self.doxygen_filename)) - - if err != 0: - raise Exception('doxygen failed with error code: ' + str(err)) - - def dist(self, type, vcRedistDir, qtDir): - - # Package is supported by default. - package_unsupported = False - unixTarget = self.defaultTarget - - if type == '' or type == None: - self.dist_usage() - return - - moveExt = '' - - if type == 'src': - self.distSrc() - - elif type == 'rpm': - if sys.platform == 'linux2': - self.distRpm() - else: - package_unsupported = True - - elif type == 'deb': - if sys.platform == 'linux2': - self.distDeb() - else: - package_unsupported = True - - elif type == 'win': - if sys.platform == 'win32': - #self.distNsis(vcRedistDir, qtDir) - self.distWix() - else: - package_unsupported = True - - elif type == 'mac': - if sys.platform == 'darwin': - self.distMac() - else: - package_unsupported = True - - else: - raise Exception('Package type not supported: ' + type) - - if moveExt != '': - self.unixMove( - self.getGenerator().buildDir + '/release/*.' + moveExt, - self.getGenerator().binDir) - - if package_unsupported: - raise Exception( - ("Package type, '%s' is not supported for platform, '%s'") - % (type, sys.platform)) - - def distRpm(self): - rpmDir = self.getGenerator().buildDir + '/rpm' - if os.path.exists(rpmDir): - shutil.rmtree(rpmDir) - - os.makedirs(rpmDir) - - templateFile = open(self.cmake_dir + '/synergy.spec.in') - template = templateFile.read() - - template = template.replace('${in:version}', self.getVersionFromCmake()) - - specPath = rpmDir + '/synergy.spec' - - specFile = open(specPath, 'w') - specFile.write(template) - specFile.close() - - version = self.getVersionFromCmake() - target = '../../bin/synergy-%s-%s.rpm' % ( - version, self.getLinuxPlatform()) - - - try: - self.try_chdir(rpmDir) - cmd = 'rpmbuild -bb --define "_topdir `pwd`" synergy.spec' - print "Command: " + cmd - err = os.system(cmd) - if err != 0: - raise Exception('rpmbuild failed: ' + str(err)) - - self.unixMove('RPMS/*/*.rpm', target) - - cmd = 'rpmlint ' + target - print "Command: " + cmd - err = os.system(cmd) - if err != 0: - raise Exception('rpmlint failed: ' + str(err)) - - finally: - self.restore_chdir() - - def distDeb(self): - buildDir = self.getGenerator().buildDir - binDir = self.getGenerator().binDir - resDir = self.cmake_dir - - version = self.getVersionFromCmake() - package = '%s-%s-%s' % ( - self.project, version, self.getLinuxPlatform()) - - debDir = '%s/deb' % buildDir - if os.path.exists(debDir): - shutil.rmtree(debDir) - - metaDir = '%s/%s/DEBIAN' % (debDir, package) - os.makedirs(metaDir) - - templateFile = open(resDir + '/deb/control.in') - template = templateFile.read() - - template = template.replace('${in:version}', - self.getVersionFromCmake()) - - template = template.replace('${in:arch}', - self.getDebianArch()) - - controlPath = '%s/control' % metaDir - - controlFile = open(controlPath, 'w') - controlFile.write(template) - controlFile.close() - - targetBin = '%s/%s/usr/bin' % (debDir, package) - targetShare = '%s/%s/usr/share' % (debDir, package) - targetApplications = "%s/applications" % targetShare - targetIcons = "%s/icons" % targetShare - targetDocs = "%s/doc/%s" % (targetShare, self.project) - - os.makedirs(targetBin) - os.makedirs(targetApplications) - os.makedirs(targetIcons) - os.makedirs(targetDocs) - - for root, dirs, files in os.walk(debDir): - for d in dirs: - os.chmod(os.path.join(root, d), 0o0755) - - binFiles = ['synergy', 'synergyc', 'synergys', 'synergyd', 'syntool'] - for f in binFiles: - shutil.copy("%s/%s" % (binDir, f), targetBin) - target = "%s/%s" % (targetBin, f) - os.chmod(target, 0o0755) - err = os.system("strip " + target) - if err != 0: - raise Exception('strip failed: ' + str(err)) - - shutil.copy("%s/synergy.desktop" % resDir, targetApplications) - shutil.copy("%s/synergy.ico" % resDir, targetIcons) - - docTarget = "%s/doc/%s" % (targetShare, self.project) - - copyrightPath = "%s/deb/copyright" % resDir - shutil.copy(copyrightPath, docTarget) - - shutil.copy("%s/deb/changelog" % resDir, docTarget) - os.system("gzip -9 %s/changelog" % docTarget) - if err != 0: - raise Exception('gzip failed: ' + str(err)) - - for root, dirs, files in os.walk(targetShare): - for f in files: - os.chmod(os.path.join(root, f), 0o0644) - - target = '../../bin/%s.deb' % package - - try: - self.try_chdir(debDir) - - # TODO: consider dpkg-buildpackage (higher level tool) - cmd = 'fakeroot dpkg-deb --build %s' % package - print "Command: " + cmd - err = os.system(cmd) - if err != 0: - raise Exception('dpkg-deb failed: ' + str(err)) - - cmd = 'lintian %s.deb' % package - print "Command: " + cmd - err = os.system(cmd) - if err != 0: - raise Exception('lintian failed: ' + str(err)) - - self.unixMove('*.deb', target) - finally: - self.restore_chdir() - - def distSrc(self): - version = self.getVersionFromCmake() - name = (self.project + '-' + version + '-Source') - exportPath = self.getGenerator().buildDir + '/' + name - - if os.path.exists(exportPath): - print "Removing existing export..." - shutil.rmtree(exportPath) - - os.mkdir(exportPath) - - cmd = "git archive %s | tar -x -C %s" % ( - self.getGitBranchName(), exportPath) - - print 'Exporting repository to: ' + exportPath - err = os.system(cmd) - if err != 0: - raise Exception('Repository export failed: ' + str(err)) - - packagePath = '../' + self.getGenerator().binDir + '/' + name + '.tar.gz' - - try: - self.try_chdir(self.getGenerator().buildDir) - print 'Packaging to: ' + packagePath - err = os.system('tar cfvz ' + packagePath + ' ' + name) - if err != 0: - raise Exception('Package failed: ' + str(err)) - finally: - self.restore_chdir() - - def unixMove(self, source, dest): - print 'Moving ' + source + ' to ' + dest - err = os.system('mv ' + source + ' ' + dest) - if err != 0: - raise Exception('Package failed: ' + str(err)) - - def distMac(self): - self.loadConfig() - binDir = self.getGenerator().getBinDir('Release') - name = "Synergy" - dist = binDir + "/" + name - - # ensure dist dir is clean - if os.path.exists(dist): - shutil.rmtree(dist) - - os.makedirs(dist) - shutil.move(binDir + "/" + name + ".app", dist + "/" + name + ".app") - - self.try_chdir(dist) - err = os.system("ln -s /Applications") - self.restore_chdir() - - fileName = "%s-%s-%s.dmg" % ( - self.project, - self.getVersionFromCmake(), - self.getMacPackageName()) - - cmd = "hdiutil create " + fileName + " -srcfolder ./" + name + "/ -ov" - - self.try_chdir(binDir) - err = os.system(cmd) - self.restore_chdir() - - def distWix(self): - generator = self.getGeneratorFromConfig().cmakeName - - arch = 'x86' - if generator.endswith('Win64'): - arch = 'x64' - - version = self.getVersionFromCmake() - args = "/p:DefineConstants=\"Version=%s\"" % version - - self.run_vcbuild( - generator, 'release', 'synergy.sln', args, - 'src/setup/win32/', 'x86') - - filename = "%s-%s-Windows-%s.msi" % ( - self.project, - version, - arch) - - old = "bin/Release/synergy.msi" - new = "bin/Release/%s" % (filename) - - try: - os.remove(new) - except OSError: - pass - - os.rename(old, new) - - def distNsis(self, vcRedistDir, qtDir): - - if vcRedistDir == '': - raise Exception( - 'VC++ redist dir path not specified (--vcredist-dir).') - - if qtDir == '': - raise Exception( - 'QT SDK dir path not specified (--qt-dir).') - - generator = self.getGeneratorFromConfig().cmakeName - - arch = 'x86' - installDirVar = '$PROGRAMFILES32' - - if generator.endswith('Win64'): - arch = 'x64' - installDirVar = '$PROGRAMFILES64' - - templateFile = open(self.cmake_dir + '\Installer.nsi.in') - template = templateFile.read() - - template = template.replace('${in:version}', self.getVersionFromCmake()) - template = template.replace('${in:arch}', arch) - template = template.replace('${in:vcRedistDir}', vcRedistDir) - template = template.replace('${in:qtDir}', qtDir) - template = template.replace('${in:installDirVar}', installDirVar) - - nsiPath = self.getGenerator().buildDir + '\Installer.nsi' - nsiFile = open(nsiPath, 'w') - nsiFile.write(template) - nsiFile.close() - - command = 'makensis ' + nsiPath - print 'NSIS command: ' + command - err = os.system(command) - if err != 0: - raise Exception('Package failed: ' + str(err)) - - def getVersionFromCmake(self): - cmakeFile = open('CMakeLists.txt') - cmake = cmakeFile.read() - - majorRe = re.search('VERSION_MAJOR (\d+)', cmake) - major = majorRe.group(1) - - minorRe = re.search('VERSION_MINOR (\d+)', cmake) - minor = minorRe.group(1) - - revRe = re.search('VERSION_REV (\d+)', cmake) - rev = revRe.group(1) - - return major + '.' + minor + '.' + rev - - def ftpUpload(self, ftp, source, target): - print "Uploading '%s' as '%s' to FTP server '%s'..." % ( - source, target, ftp.host) - ftp.run(source, target) - print 'Done' - - def distftp(self, type, ftp): - if not type: - raise Exception('Platform type not specified.') - - self.loadConfig() - - binDir = self.getGenerator().getBinDir('Release') - - packageSource = binDir + '/' + self.dist_name(type) - packageTarget = self.dist_name_rev(type) - self.ftpUpload(ftp, packageSource, packageTarget) - - if (type != 'src'): - pluginsDir = binDir + '/plugins' - nsPluginSource = self.findLibraryFile(type, pluginsDir, 'ns') - nsPluginTarget = self.getLibraryDistFilename(type, pluginsDir, 'ns') - self.ftpUpload(ftp, nsPluginSource, nsPluginTarget) - - def getLibraryDistFilename(self, type, dir, name): - (platform, packageExt, libraryExt) = self.getDistributePlatformInfo(type) - branch = self.getGitBranchName() - revision = self.getGitRevision() - firstPart = '%s-%s-%s-%s' % (name, branch, revision, platform) - - filename = '%s.%s' % (firstPart, libraryExt) - if type == 'rpm' or type == 'deb': - # linux is a bit special, include dist type (deb/rpm in filename) - filename = '%s-%s.%s' % (firstPart, packageExt, libraryExt) - - return filename - - def findLibraryFile(self, type, dir, name): - (platform, packageExt, libraryExt) = self.getDistributePlatformInfo(type) - ext = libraryExt - - pattern = name + '\.' + ext - - for filename in os.listdir(dir): - if re.search(pattern, filename): - return dir + '/' + filename - - raise Exception('Could not find library name with pattern: ' + pattern) - - def getDistributePlatformInfo(self, type): - ext = None - libraryExt = None - platform = None - - if type == 'src': - ext = 'tar.gz' - platform = 'Source' - - elif type == 'rpm' or type == 'deb': - ext = type - libraryExt = 'so' - platform = self.getLinuxPlatform() - - elif type == 'win': - # get platform based on last generator used - ext = 'msi' - libraryExt = 'dll' - generator = self.getGeneratorFromConfig().cmakeName - if generator.find('Win64') != -1: - platform = 'Windows-x64' - else: - platform = 'Windows-x86' - - elif type == 'mac': - ext = "dmg" - libraryExt = 'dylib' - platform = self.getMacPackageName() - - if not platform: - raise Exception('Unable to detect distributable platform.') - - return (platform, ext, libraryExt) - - def dist_name(self, type): - (platform, packageExt, libraryExt) = self.getDistributePlatformInfo(type) - ext = packageExt - - pattern = ( - re.escape(self.project + '-') + '\d+\.\d+\.\d+' + - re.escape('-' + platform + '.' + ext)) - - for filename in os.listdir(self.getBinDir('Release')): - if re.search(pattern, filename): - return filename - - # still here? package probably not created yet. - raise Exception('Could not find package name with pattern: ' + pattern) - - def dist_name_rev(self, type): - # find the version number (we're puting the rev in after this) - pattern = '(\d+\.\d+\.\d+)' - replace = "%s-%s" % ( - self.getGitBranchName(), self.getGitRevision()) - return re.sub(pattern, replace, self.dist_name(type)) - - def getDebianArch(self): - if os.uname()[4][:3] == 'arm': - return 'armhf' - - # os_bits should be loaded with '32bit' or '64bit' - import platform - (os_bits, other) = platform.architecture() - - # get platform based on current platform - if os_bits == '32bit': - return 'i386' - elif os_bits == '64bit': - return 'amd64' - else: - raise Exception("unknown os bits: " + os_bits) - - def getLinuxPlatform(self): - if os.uname()[4][:3] == 'arm': - return 'Linux-armv6l' - - # os_bits should be loaded with '32bit' or '64bit' - import platform - (os_bits, other) = platform.architecture() - - # get platform based on current platform - if os_bits == '32bit': - return 'Linux-i686' - elif os_bits == '64bit': - return 'Linux-x86_64' - else: - raise Exception("unknown os bits: " + os_bits) - - def dist_usage(self): - print ('Usage: %s package [package-type]\n' - '\n' - 'Replace [package-type] with one of:\n' - ' src .tar.gz source (Posix only)\n' - ' rpm .rpm package (Red Hat)\n' - ' deb .deb paclage (Debian)\n' - ' win .exe installer (Windows)\n' - ' mac .dmg package (Mac OS X)\n' - '\n' - 'Example: %s package src-tgz') % (self.this_cmd, self.this_cmd) - - def about(self): - print ('Help Me script, from the Synergy project.\n' - '%s\n' - '\n' - 'For help, run: %s help') % (self.website_url, self.this_cmd) - - def try_chdir(self, dir): - global prevdir - - if dir == '': - prevdir = '' - return - - # Ensure temp build dir exists. - if not os.path.exists(dir): - print 'Creating dir: ' + dir - os.makedirs(dir) - - prevdir = os.path.abspath(os.curdir) - - # It will exist by this point, so it's safe to chdir. - print 'Entering dir: ' + dir - os.chdir(dir) - - def restore_chdir(self): - global prevdir - if prevdir == '': - return - print 'Going back to: ' + prevdir - os.chdir(prevdir) - - def open_internal(self, project_filename, application = ''): - - if not os.path.exists(project_filename): - raise Exception('Project file (%s) not found, run hm conf first.' % project_filename) - else: - path = project_filename - - if application != '': - path = application + ' ' + path - - err = os.system(path) - if err != 0: - raise Exception('Could not open project with error code code: ' + str(err)) - - def setup(self, target=''): - print "Running setup..." - - oldGenerator = self.findGeneratorFromConfig() - if not oldGenerator == None: - for target in ['debug', 'release']: - buildDir = oldGenerator.getBuildDir(target) - - cmakeCacheFilename = 'CMakeCache.txt' - if buildDir != '': - cmakeCacheFilename = buildDir + '/' + cmakeCacheFilename - - if os.path.exists(cmakeCacheFilename): - print "Removing %s, since generator changed." % cmakeCacheFilename - os.remove(cmakeCacheFilename) - - # always either get generator from args, or prompt user when - # running setup - generator = self.get_generator_from_prompt() - - config = self.getConfig() - config.set('hm', 'setup_version', self.setup_version) - - # store the generator so we don't need to ask again - config.set('cmake', 'generator', generator) - - self.write_config(config) - - # for all targets, set conf not run - self.setConfRun('all', False) - self.setConfRun('debug', False) - self.setConfRun('release', False) - - print "Setup complete." - - def getConfig(self): - if os.path.exists(self.configFilename): - config = ConfigParser.ConfigParser() - config.read(self.configFilename) - else: - config = ConfigParser.ConfigParser() - - if not config.has_section('hm'): - config.add_section('hm') - - if not config.has_section('cmake'): - config.add_section('cmake') - - return config - - def write_config(self, config, target=''): - if not os.path.isdir(self.configDir): - os.mkdir(self.configDir) - configfile = open(self.configFilename, 'wb') - config.write(configfile) - - def getGeneratorFromConfig(self): - generator = self.findGeneratorFromConfig() - if generator: - return generator - - raise Exception("Could not find generator: " + name) - - def findGeneratorFromConfig(self): - config = ConfigParser.RawConfigParser() - config.read(self.configFilename) - - if not config.has_section('cmake'): - return None - - name = config.get('cmake', 'generator') - - generators = self.get_generators() - keys = generators.keys() - keys.sort() - for k in keys: - if generators[k].cmakeName == name: - return generators[k] - - return None - - def min_setup_version(self, version): - if os.path.exists(self.configFilename): - config = ConfigParser.RawConfigParser() - config.read(self.configFilename) - - try: - return config.getint('hm', 'setup_version') >= version - except: - return False - else: - return False - - def hasConfRun(self, target): - if self.min_setup_version(2): - config = ConfigParser.RawConfigParser() - config.read(self.configFilename) - try: - return config.getboolean('hm', 'conf_done_' + target) - except: - return False - else: - return False - - def setConfRun(self, target, hasRun=True): - if self.min_setup_version(3): - config = ConfigParser.RawConfigParser() - config.read(self.configFilename) - config.set('hm', 'conf_done_' + target, hasRun) - self.write_config(config) - else: - raise Exception("User does not have correct setup version.") - - def get_generators(self): - if sys.platform == 'win32': - return self.win32_generators - elif sys.platform in ['linux2', 'sunos5', 'freebsd7', 'aix5']: - return self.unix_generators - elif sys.platform == 'darwin': - return self.darwin_generators - else: - raise Exception('Unsupported platform: ' + sys.platform) - - def get_generator_from_prompt(self): - return self.getGenerator().cmakeName - - def getGenerator(self): - generators = self.get_generators() - if len(generators.keys()) == 1: - return generators[generators.keys()[0]] - - # if user has specified a generator as an argument - if self.generator_id: - return generators[int(self.generator_id)] - - conf = self.findGeneratorFromConfig() - if conf: - return conf - - raise Exception( - 'Generator not specified, use -g arg ' + - '(use `hm genlist` for a list of generators).') - - def setup_generator_prompt(self, generators): - - if self.no_prompts: - raise Exception('User prompting is disabled.') - - prompt = 'Enter a number:' - print prompt, - - generator_id = raw_input() - - if generator_id in generators: - print 'Selected generator:', generators[generator_id] - else: - print 'Invalid number, try again.' - self.setup_generator_prompt(generators) - - return generators[generator_id] - - def get_vcvarsall(self, generator): - import platform, _winreg - - # os_bits should be loaded with '32bit' or '64bit' - (os_bits, other) = platform.architecture() - - # visual studio is a 32-bit app, so when we're on 64-bit, we need to check the WoW dungeon - if os_bits == '64bit': - key_name = r'SOFTWARE\Wow6432Node\Microsoft\VisualStudio\SxS\VS7' - else: - key_name = r'SOFTWARE\Microsoft\VisualStudio\SxS\VC7' - - try: - key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, key_name) - except: - raise Exception('Unable to open Visual Studio registry key. Application may not be installed.') - - if generator.startswith('Visual Studio 8'): - value,type = _winreg.QueryValueEx(key, '8.0') - elif generator.startswith('Visual Studio 9'): - value,type = _winreg.QueryValueEx(key, '9.0') - elif generator.startswith('Visual Studio 10'): - value,type = _winreg.QueryValueEx(key, '10.0') - else: - raise Exception('Cannot determine vcvarsall.bat location for: ' + generator) - - # not sure why, but the value on 64-bit differs slightly to the original - if os_bits == '64bit': - path = value + r'vc\vcvarsall.bat' - else: - path = value + r'vcvarsall.bat' - - if not os.path.exists(path): - raise Exception("'%s' not found." % path) - - return path - - def run_vcbuild(self, generator, mode, solution, args='', dir='', config32='Win32'): - import platform - - # os_bits should be loaded with '32bit' or '64bit' - (os_bits, other) = platform.architecture() - # Now we choose the parameters bases on OS 32/64 and our target 32/64 - # http://msdn.microsoft.com/en-us/library/x4d2c09s%28VS.80%29.aspx - - # valid options are only: ia64 amd64 x86_amd64 x86_ia64 - # but calling vcvarsall.bat does not garantee that it will work - # ret code from vcvarsall.bat is always 0 so the only way of knowing that I worked is by analysing the text output - # ms bugg: install VS9, FeaturePack, VS9SP1 and you'll obtain a vcvarsall.bat that fails. - if generator.find('Win64') != -1: - # target = 64bit - if os_bits == '32bit': - vcvars_platform = 'x86_amd64' # 32bit OS building 64bit app - else: - vcvars_platform = 'amd64' # 64bit OS building 64bit app - config_platform = 'x64' - else: # target = 32bit - vcvars_platform = 'x86' # 32/64bit OS building 32bit app - config_platform = config32 - - if mode == 'release': - config = 'Release' - else: - config = 'Debug' - - if generator.startswith('Visual Studio 10'): - cmd = ('@echo off\n' - 'call "%s" %s \n' - 'cd "%s"\n' - 'msbuild /nologo %s /p:Configuration="%s" /p:Platform="%s" "%s"' - ) % (self.get_vcvarsall(generator), vcvars_platform, dir, args, config, config_platform, solution) - else: - config = config + '|' + config_platform - cmd = ('@echo off\n' - 'call "%s" %s \n' - 'cd "%s"\n' - 'vcbuild /nologo %s "%s" "%s"' - ) % (self.get_vcvarsall(generator), vcvars_platform, dir, args, solution, config) - - # Generate a batch file, since we can't use environment variables directly. - temp_bat = self.getBuildDir() + r'\vcbuild.bat' - file = open(temp_bat, 'w') - file.write(cmd) - file.close() - - err = os.system(temp_bat) - if err != 0: - raise Exception('Microsoft compiler failed with error code: ' + str(err)) - - def ensure_setup_latest(self): - if not self.min_setup_version(self.setup_version): - self.setup() - - def reformat(self): - err = os.system( - r'tool\astyle\AStyle.exe ' - '--quiet --suffix=none --style=java --indent=force-tab=4 --recursive ' - 'lib/*.cpp lib/*.h cmd/*.cpp cmd/*.h') - - if err != 0: - raise Exception('Reformat failed with error code: ' + str(err)) - - def printGeneratorList(self): - generators = self.get_generators() - keys = generators.keys() - keys.sort() - for k in keys: - print str(k) + ': ' + generators[k].cmakeName - - def getMacVersion(self): - if not self.macSdk: - raise Exception("Mac OS X SDK not set.") - - result = re.search('(\d+)\.(\d+)', self.macSdk) - if not result: - print versions - raise Exception("Could not find Mac OS X version.") - - major = int(result.group(1)) - minor = int(result.group(2)) - return (major, minor) - - def getMacPackageName(self): - - (major, minor) = self.getMacVersion() - - if major == 10: - if minor <= 4: - # 10.4: intel and power pc - arch = "Universal" - elif minor <= 6: - # 10.5: 32-bit intel - arch = "i386" - else: - # 10.7: 64-bit intel (gui only) - arch = "x86_64" - else: - raise Exception("Mac OS major version unknown: " + - str(major)) - - # version is major and minor with no dots (e.g. 106) - version = str(major) + str(minor) - - return "MacOSX%s-%s" % (version, arch) - - def reset(self): - if os.path.exists('build'): - shutil.rmtree('build') - - if os.path.exists('bin'): - shutil.rmtree('bin') - - if os.path.exists('lib'): - shutil.rmtree('lib') - - if os.path.exists('src/gui/tmp'): - shutil.rmtree('src/gui/tmp') - - # qt 4.3 generates ui_ files. - for filename in glob.glob("src/gui/ui_*"): - os.remove(filename) - -# the command handler should be called only from hm.py (i.e. directly -# from the command prompt). the purpose of this class is so that we -# don't need to do argument handling all over the place in the internal -# commands class. -class CommandHandler: - ic = InternalCommands() - build_targets = [] - vcRedistDir = '' - qtDir = '' - - def __init__(self, argv, opts, args, verbose): - - self.ic.verbose = verbose - - self.opts = opts - self.args = args - - for o, a in self.opts: - if o == '--no-prompts': - self.ic.no_prompts = True - elif o in ('-g', '--generator'): - self.ic.generator_id = a - elif o == '--skip-gui': - self.ic.enableMakeGui = False - elif o == '--skip-core': - self.ic.enableMakeCore = False - elif o in ('-d', '--debug'): - self.build_targets += ['debug',] - elif o in ('-r', '--release'): - self.build_targets += ['release',] - elif o == '--vcredist-dir': - self.vcRedistDir = a - elif o == '--qt-dir': - self.qtDir = a - elif o == '--mac-sdk': - self.ic.macSdk = a - elif o == '--mac-identity': - self.ic.macIdentity = a - - def about(self): - self.ic.about() - - def setup(self): - self.ic.setup() - - def configure(self): - self.ic.configureAll(self.build_targets) - - def build(self): - self.ic.build(self.build_targets) - - def clean(self): - self.ic.clean(self.build_targets) - - def update(self): - self.ic.update() - - def install(self): - print 'Not yet implemented: install' - - def doxygen(self): - self.ic.doxygen() - - def dist(self): - - type = None - if len(self.args) > 0: - type = self.args[0] - - self.ic.dist(type, self.vcRedistDir, self.qtDir) - - def distftp(self): - type = None - host = None - user = None - password = None - dir = None - - if len(self.args) > 0: - type = self.args[0] - - for o, a in self.opts: - if o == '--host': - host = a - elif o == '--user': - user = a - elif o == '--pass': - password = a - elif o == '--dir': - dir = a - - if not host: - raise Exception('FTP host was not specified.') - - ftp = ftputil.FtpUploader( - host, user, password, dir) - - self.ic.distftp(type, ftp) - - def destroy(self): - self.ic.destroy() - - def kill(self): - self.ic.kill() - - def usage(self): - self.ic.usage() - - def revision(self): - self.ic.revision() - - def reformat(self): - self.ic.reformat() - - def open(self): - self.ic.open() - - def genlist(self): - self.ic.printGeneratorList() - - def reset(self): - self.ic.reset() - - def signwin(self): - pfx = None - pwd = None - dist = False - for o, a in self.opts: - if o == '--pfx': - pfx = a - elif o == '--pwd': - pwd = a - elif o == '--dist': - dist = True - self.ic.signwin(pfx, pwd, dist) - - def signmac(self): - self.ic.signmac() diff --git a/ext/toolchain/shitindent.py b/ext/toolchain/shitindent.py deleted file mode 100644 index a6df18d1..00000000 --- a/ext/toolchain/shitindent.py +++ /dev/null @@ -1,31 +0,0 @@ - def getDebianArch(self): - if os.uname()[4][:3] == 'arm': - return 'armhf' - - # os_bits should be loaded with '32bit' or '64bit' - import platform - (os_bits, other) = platform.architecture() - - # get platform based on current platform - if os_bits == '32bit': - return 'i386' - elif os_bits == '64bit': - return 'amd64' - else: - raise Exception("unknown os bits: " + os_bits) - - def getLinuxPlatform(self): - if os.uname()[4][:3] == 'arm': - return 'Linux-armv6l' - - # os_bits should be loaded with '32bit' or '64bit' - import platform - (os_bits, other) = platform.architecture() - - # get platform based on current platform - if os_bits == '32bit': - return 'Linux-i686' - elif os_bits == '64bit': - return 'Linux-x86_64' - else: - raise Exception("unknown os bits: " + os_bits) \ No newline at end of file From 91d05c29dbcaa120a63390b10909775be8a25c5d Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 16:05:16 +0100 Subject: [PATCH 39/66] Upload 'ns' to plugins dir (create if not exists) #4695 --- ext/toolchain/commands1.py | 10 ++-------- ext/toolchain/ftputil.py | 21 ++++++++++++++++++--- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index feaa9f06..601f04eb 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -1343,12 +1343,6 @@ class InternalCommands: return major + '.' + minor + '.' + rev - def ftpUpload(self, ftp, source, target): - print "Uploading '%s' as '%s' to FTP server '%s'..." % ( - source, target, ftp.host) - ftp.run(source, target) - print 'Done' - def distftp(self, type, ftp): if not type: raise Exception('Platform type not specified.') @@ -1359,13 +1353,13 @@ class InternalCommands: packageSource = binDir + '/' + self.dist_name(type) packageTarget = self.dist_name_rev(type) - self.ftpUpload(ftp, packageSource, packageTarget) + ftp.upload(packageSource, packageTarget) if (type != 'src'): pluginsDir = binDir + '/plugins' nsPluginSource = self.findLibraryFile(type, pluginsDir, 'ns') nsPluginTarget = self.getLibraryDistFilename(type, pluginsDir, 'ns') - self.ftpUpload(ftp, nsPluginSource, nsPluginTarget) + ftp.upload(nsPluginSource, nsPluginTarget, "plugins") def getLibraryDistFilename(self, type, dir, name): (platform, packageExt, libraryExt) = self.getDistributePlatformInfo(type) diff --git a/ext/toolchain/ftputil.py b/ext/toolchain/ftputil.py index 3cc8c9fe..aef8363e 100644 --- a/ext/toolchain/ftputil.py +++ b/ext/toolchain/ftputil.py @@ -23,13 +23,28 @@ class FtpUploader: self.password = password self.dir = dir - def run(self, src, dest, replace=False): - + def upload(self, src, dest, subDir=None): + print "Connecting to '%s'" % self.host ftp = FTP(self.host, self.user, self.password) - ftp.cwd(self.dir) + + self.changeDir(ftp, self.dir) + + if subDir: + self.changeDir(ftp, subDir) + print "Uploading '%s' as '%s'" % (src, dest) f = open(src, 'rb') ftp.storbinary('STOR ' + dest, f) f.close() ftp.close() + + print "Done" + + def changeDir(self, ftp, dir): + if dir not in ftp.nlst(): + print "Creating dir '%s'" % dir + ftp.mkd(dir) + + print "Changing to dir '%s'" % dir + ftp.cwd(dir) From d278c2d8d41e12cbfb2bee5e14861a2a9753780b Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 16:22:45 +0100 Subject: [PATCH 40/66] Swallow mkd fail in case nlst doesn't work #4695 --- ext/toolchain/ftputil.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/toolchain/ftputil.py b/ext/toolchain/ftputil.py index aef8363e..3351f6b4 100644 --- a/ext/toolchain/ftputil.py +++ b/ext/toolchain/ftputil.py @@ -44,7 +44,9 @@ class FtpUploader: def changeDir(self, ftp, dir): if dir not in ftp.nlst(): print "Creating dir '%s'" % dir - ftp.mkd(dir) + try: + ftp.mkd(dir) + except: print "Changing to dir '%s'" % dir ftp.cwd(dir) From b4b5ce64835d813d76fbbd052c6dc9a302a7a38c Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 16:23:21 +0100 Subject: [PATCH 41/66] Missing change from last commit, print error #4695 --- ext/toolchain/ftputil.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/toolchain/ftputil.py b/ext/toolchain/ftputil.py index 3351f6b4..b9a19306 100644 --- a/ext/toolchain/ftputil.py +++ b/ext/toolchain/ftputil.py @@ -47,6 +47,8 @@ class FtpUploader: try: ftp.mkd(dir) except: + # sometimes nlst may returns nothing, so mkd fails with 'File exists' + print "Failed to create dir '%s'" % dir print "Changing to dir '%s'" % dir ftp.cwd(dir) From a900543c1de733f30bacd68ee6703955ce940a1f Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 16:53:46 +0100 Subject: [PATCH 42/66] Allowed Mac OS X 10.7 to build ns plugin #4695 --- src/lib/plugin/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/plugin/CMakeLists.txt b/src/lib/plugin/CMakeLists.txt index 0989ab27..886802d0 100644 --- a/src/lib/plugin/CMakeLists.txt +++ b/src/lib/plugin/CMakeLists.txt @@ -19,7 +19,7 @@ if (WIN32) endif() if (APPLE) - if (OSX_TARGET_MINOR GREATER 7) + if (OSX_TARGET_MINOR GREATER 6) add_subdirectory(ns) endif() else() From 8ed3d79cc208754d5aa653023c7bfcb11855ac90 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 16:57:28 +0100 Subject: [PATCH 43/66] Only upload plugin if file exists #4695 Some platforms (e.g. Mac 10.6) don't build the plugin --- ext/toolchain/commands1.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index 601f04eb..a051ee14 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -1355,11 +1355,12 @@ class InternalCommands: packageTarget = self.dist_name_rev(type) ftp.upload(packageSource, packageTarget) - if (type != 'src'): + if type != 'src': pluginsDir = binDir + '/plugins' nsPluginSource = self.findLibraryFile(type, pluginsDir, 'ns') - nsPluginTarget = self.getLibraryDistFilename(type, pluginsDir, 'ns') - ftp.upload(nsPluginSource, nsPluginTarget, "plugins") + if nsPluginSource: + nsPluginTarget = self.getLibraryDistFilename(type, pluginsDir, 'ns') + ftp.upload(nsPluginSource, nsPluginTarget, "plugins") def getLibraryDistFilename(self, type, dir, name): (platform, packageExt, libraryExt) = self.getDistributePlatformInfo(type) @@ -1384,7 +1385,7 @@ class InternalCommands: if re.search(pattern, filename): return dir + '/' + filename - raise Exception('Could not find library name with pattern: ' + pattern) + return None def getDistributePlatformInfo(self, type): ext = None From 25237a14b70d997e9c73737237dcd36a26f0c2e5 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 17:40:36 +0100 Subject: [PATCH 44/66] Revert "Allowed Mac OS X 10.7 to build ns plugin #4695" This reverts commit a900543c1de733f30bacd68ee6703955ce940a1f. --- src/lib/plugin/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/plugin/CMakeLists.txt b/src/lib/plugin/CMakeLists.txt index 886802d0..0989ab27 100644 --- a/src/lib/plugin/CMakeLists.txt +++ b/src/lib/plugin/CMakeLists.txt @@ -19,7 +19,7 @@ if (WIN32) endif() if (APPLE) - if (OSX_TARGET_MINOR GREATER 6) + if (OSX_TARGET_MINOR GREATER 7) add_subdirectory(ns) endif() else() From 14bb44f539e4d224511821a2d98cf3bce65d6909 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 17:41:38 +0100 Subject: [PATCH 45/66] Comment explaining lack of 10.7 support #4695 --- src/lib/plugin/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/plugin/CMakeLists.txt b/src/lib/plugin/CMakeLists.txt index 0989ab27..81416002 100644 --- a/src/lib/plugin/CMakeLists.txt +++ b/src/lib/plugin/CMakeLists.txt @@ -19,6 +19,7 @@ if (WIN32) endif() if (APPLE) + # 10.7 should be supported, but gives is a _NXArgv linker error if (OSX_TARGET_MINOR GREATER 7) add_subdirectory(ns) endif() From 38bcea54d817a6e58db31022cea80c68d42b9fe5 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 18:17:09 +0100 Subject: [PATCH 46/66] Do nothing if plugins dir doesn't exist #4695 --- ext/toolchain/commands1.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index a051ee14..9f8149c2 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -1376,6 +1376,9 @@ class InternalCommands: return filename def findLibraryFile(self, type, dir, name): + if not os.path.exists(dir): + return None + (platform, packageExt, libraryExt) = self.getDistributePlatformInfo(type) ext = libraryExt From cae767735c1932a25fde9180b5307140fac3c3bb Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 18:59:38 +0100 Subject: [PATCH 47/66] Download specific plugin version on Mac #4708 --- src/gui/src/PluginManager.cpp | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/gui/src/PluginManager.cpp b/src/gui/src/PluginManager.cpp index 818a19c9..55e95191 100644 --- a/src/gui/src/PluginManager.cpp +++ b/src/gui/src/PluginManager.cpp @@ -29,10 +29,11 @@ #include #include -static QString kBaseUrl = "http://synergy-project.org/files"; +static const char kBaseUrl[] = "http://synergy-project.org/files"; +static const char kDefaultVersion[] = "1.0"; static const char kWinProcessorArch32[] = "Windows-x86"; static const char kWinProcessorArch64[] = "Windows-x64"; -static const char kMacProcessorArch[] = "MacOSX-i386"; +static const char kMacProcessorArch[] = "MacOSX%1-i386"; static const char kLinuxProcessorArchDeb32[] = "Linux-i686-deb"; static const char kLinuxProcessorArchDeb64[] = "Linux-x86_64-deb"; static const char kLinuxProcessorArchRpm32[] = "Linux-i686-rpm"; @@ -170,7 +171,17 @@ QString PluginManager::getPluginUrl(const QString& pluginName) #elif defined(Q_OS_MAC) - archName = kMacProcessorArch; + QString macVersion = "1010"; +#if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1090 // 10.9 + macVersion = "109"; +#elif __MAC_OS_X_VERSION_MIN_REQUIRED <= 1080 // 10.8 + macVersion = "108"; +#elif __MAC_OS_X_VERSION_MIN_REQUIRED <= 1070 // 10.7 + emit error(tr("Plugins not supported on this Mac OS X version.")); + return ""; +#endif + + archName = QString(kMacProcessorArch).arg(macVersion); #else @@ -194,13 +205,14 @@ QString PluginManager::getPluginUrl(const QString& pluginName) #endif - QString result = kBaseUrl; - result.append("/plugins/"); - result.append(pluginName).append("/1.0/"); - result.append(archName); - result.append("/"); - result.append(getPluginOsSpecificName(pluginName)); + QString result = QString("%1/plugins/%2/%3/%4/%5") + .arg(kBaseUrl) + .arg(pluginName) + .arg(kDefaultVersion) + .arg(archName) + .arg(getPluginOsSpecificName(pluginName)); + qDebug() << result; return result; } From e479f167051adeb099ed7b0109bf7db1b19d28bf Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 18:59:52 +0100 Subject: [PATCH 48/66] Incremented default plugin version to 1.1 --- src/gui/src/PluginManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/src/PluginManager.cpp b/src/gui/src/PluginManager.cpp index 55e95191..c55fdd12 100644 --- a/src/gui/src/PluginManager.cpp +++ b/src/gui/src/PluginManager.cpp @@ -30,7 +30,7 @@ #include static const char kBaseUrl[] = "http://synergy-project.org/files"; -static const char kDefaultVersion[] = "1.0"; +static const char kDefaultVersion[] = "1.1"; static const char kWinProcessorArch32[] = "Windows-x86"; static const char kWinProcessorArch64[] = "Windows-x64"; static const char kMacProcessorArch[] = "MacOSX%1-i386"; From e8a43dd0202d7570385ef47ab36ad349d4ae1ac3 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 19:41:53 +0100 Subject: [PATCH 49/66] Simplified logic to detect Linux 64/32 deb/rpm #4565 --- src/gui/src/MainWindow.cpp | 6 +- src/gui/src/PluginManager.cpp | 61 ++++++++++++------ src/gui/src/ProcessorArch.h | 20 +++--- src/gui/src/QUtility.cpp | 114 ++++++---------------------------- src/gui/src/QUtility.h | 4 +- 5 files changed, 74 insertions(+), 131 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index e3b8e2dd..323a1b6c 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -1132,12 +1132,12 @@ void MainWindow::downloadBonjour() { #if defined(Q_OS_WIN) QUrl url; - int arch = checkProcessorArch(); - if (arch == Win_x86) { + int arch = getProcessorArch(); + if (arch == kProcessorArchWin32) { url.setUrl(bonjourBaseUrl + bonjourFilename32); appendLogNote("downloading 32-bit Bonjour"); } - else if (arch == Win_x64) { + else if (arch == kProcessorArchWin64) { url.setUrl(bonjourBaseUrl + bonjourFilename64); appendLogNote("downloading 64-bit Bonjour"); } diff --git a/src/gui/src/PluginManager.cpp b/src/gui/src/PluginManager.cpp index c55fdd12..d37fc503 100644 --- a/src/gui/src/PluginManager.cpp +++ b/src/gui/src/PluginManager.cpp @@ -31,13 +31,13 @@ static const char kBaseUrl[] = "http://synergy-project.org/files"; static const char kDefaultVersion[] = "1.1"; -static const char kWinProcessorArch32[] = "Windows-x86"; -static const char kWinProcessorArch64[] = "Windows-x64"; -static const char kMacProcessorArch[] = "MacOSX%1-i386"; -static const char kLinuxProcessorArchDeb32[] = "Linux-i686-deb"; -static const char kLinuxProcessorArchDeb64[] = "Linux-x86_64-deb"; -static const char kLinuxProcessorArchRpm32[] = "Linux-i686-rpm"; -static const char kLinuxProcessorArchRpm64[] = "Linux-x86_64-rpm"; +static const char kWinPackagePlatform32[] = "Windows-x86"; +static const char kWinPackagePlatform64[] = "Windows-x64"; +static const char kMacPackagePlatform32[] = "MacOSX%1-i386"; +static const char kLinuxPackagePlatformDeb32[] = "Linux-i686-deb"; +static const char kLinuxPackagePlatformDeb64[] = "Linux-x86_64-deb"; +static const char kLinuxPackagePlatformRpm32[] = "Linux-i686-rpm"; +static const char kLinuxPackagePlatformRpm64[] = "Linux-x86_64-rpm"; #if defined(Q_OS_WIN) static const char kWinPluginExt[] = ".dll"; @@ -158,10 +158,10 @@ QString PluginManager::getPluginUrl(const QString& pluginName) try { QString coreArch = m_CoreInterface.getArch(); if (coreArch.startsWith("x86")) { - archName = kWinProcessorArch32; + archName = kWinPackagePlatform32; } else if (coreArch.startsWith("x64")) { - archName = kWinProcessorArch64; + archName = kWinPackagePlatform64; } } catch (...) { @@ -181,22 +181,43 @@ QString PluginManager::getPluginUrl(const QString& pluginName) return ""; #endif - archName = QString(kMacProcessorArch).arg(macVersion); + archName = QString(kMacPackagePlatform).arg(macVersion); #else - int arch = checkProcessorArch(); - if (arch == Linux_rpm_i686) { - archName = kLinuxProcessorArchRpm32; + QString program("dpkg"); + QStringList args; + args << "-s" << "synergy"; + + QProcess process; + process.setReadChannel(QProcess::StandardOutput); + process.start(program, args); + bool success = process.waitForStarted(); + + if (!success || !process.waitForFinished()) + { + emit error(tr("Could not get Linux package type.")); + return ""; } - else if (arch == Linux_rpm_x86_64) { - archName = kLinuxProcessorArchRpm64; + + bool isDeb = (process.exitCode() == 0); + + int arch = getProcessorArch(); + if (arch == kProcessorArchLinux32) { + if (isDeb) { + archName = kLinuxPackagePlatformDeb32; + } + else { + archName = kLinuxPackagePlatformRpm32; + } } - else if (arch == Linux_deb_i686) { - archName = kLinuxProcessorArchDeb32; - } - else if (arch == Linux_deb_x86_64) { - archName = kLinuxProcessorArchDeb64; + else if (arch == kProcessorArchLinux64) { + if (isDeb) { + archName = kLinuxPackagePlatformDeb64; + } + else { + archName = kLinuxPackagePlatformRpm64; + } } else { emit error(tr("Could not get Linux architecture type.")); diff --git a/src/gui/src/ProcessorArch.h b/src/gui/src/ProcessorArch.h index dacf6468..76f6e96a 100644 --- a/src/gui/src/ProcessorArch.h +++ b/src/gui/src/ProcessorArch.h @@ -15,18 +15,14 @@ * along with this program. If not, see . */ -#ifndef PROCESSORARCH_H -#define PROCESSORARCH_H +#pragma once enum qProcessorArch { - Win_x86, - Win_x64, - Mac_i386, - Linux_rpm_i686, - Linux_rpm_x86_64, - Linux_deb_i686, - Linux_deb_x86_64, - unknown + kProcessorArchWin32, + kProcessorArchWin64, + kProcessorArchMac32, + kProcessorArchMac64, + kProcessorArchLinux32, + kProcessorArchLinux64, + kProcessorArchUnknown }; - -#endif // PROCESSORARCH_H diff --git a/src/gui/src/QUtility.cpp b/src/gui/src/QUtility.cpp index ac1f8b32..ae72fdb4 100644 --- a/src/gui/src/QUtility.cpp +++ b/src/gui/src/QUtility.cpp @@ -29,12 +29,6 @@ #include #endif -#if defined(Q_OS_LINUX) -static const char kLinuxI686[] = "i686"; -static const char kLinuxX8664[] = "x86_64"; -static const char kUbuntu[] = "Ubuntu"; -#endif - void setIndexFromItemData(QComboBox* comboBox, const QVariant& itemData) { for (int i = 0; i < comboBox->count(); ++i) @@ -68,7 +62,7 @@ QString getFirstMacAddress() return mac; } -int checkProcessorArch() +qProcessorArch getProcessorArch() { #if defined(Q_OS_WIN) SYSTEM_INFO systemInfo; @@ -76,97 +70,27 @@ int checkProcessorArch() switch (systemInfo.wProcessorArchitecture) { case PROCESSOR_ARCHITECTURE_INTEL: - return Win_x86; + return kProcessorArchWin32; case PROCESSOR_ARCHITECTURE_IA64: - return Win_x64; + return kProcessorArchWin64; case PROCESSOR_ARCHITECTURE_AMD64: - return Win_x64; + return kProcessorArchWin64; default: - return unknown; - } -#elif defined(Q_OS_MAC) - return Mac_i386; -#else - bool version32 = false; - bool debPackaging = false; - - QString program1("uname"); - QStringList args1("-m"); - QProcess process1; - process1.setReadChannel(QProcess::StandardOutput); - process1.start(program1, args1); - bool success = process1.waitForStarted(); - - QString out, error; - if (success) - { - if (process1.waitForFinished()) { - out = process1.readAllStandardOutput(); - error = process1.readAllStandardError(); - } - } - - out = out.trimmed(); - error = error.trimmed(); - - if (out.isEmpty() || - !error.isEmpty() || - !success || - process1.exitCode() != 0) - { - return unknown; - } - - if (out == kLinuxI686) { - version32 = true; - } - - QString program2("python"); - QStringList args2("-mplatform"); - QProcess process2; - process2.setReadChannel(QProcess::StandardOutput); - process2.start(program2, args2); - success = process2.waitForStarted(); - - if (success) - { - if (process2.waitForFinished()) { - out = process2.readAllStandardOutput(); - error = process2.readAllStandardError(); - } - } - - out = out.trimmed(); - error = error.trimmed(); - - if (out.isEmpty() || - !error.isEmpty() || - !success || - process2.exitCode() != 0) - { - return unknown; - } - - if (out.contains(kUbuntu)) { - debPackaging = true; - } - - if (version32) { - if (debPackaging) { - return Linux_deb_i686; - } - else { - return Linux_rpm_i686; - } - } - else { - if (debPackaging) { - return Linux_deb_x86_64; - } - else { - return Linux_rpm_x86_64; - } + return kProcessorArchUnknown; } #endif - return unknown; + +#if defined(Q_OS_MAC) + return kProcessorArchMac; +#endif + +#if defined(Q_OS_LINUX) +#ifdef __i386__ + return kProcessorArchLinux32; +#else + return kProcessorArchLinux64; +#endif +#endif + + return kProcessorArchUnknown; } diff --git a/src/gui/src/QUtility.h b/src/gui/src/QUtility.h index 1b38409c..89861dda 100644 --- a/src/gui/src/QUtility.h +++ b/src/gui/src/QUtility.h @@ -17,6 +17,8 @@ #pragma once +#include "ProcessorArch.h" + #include #include #include @@ -25,4 +27,4 @@ void setIndexFromItemData(QComboBox* comboBox, const QVariant& itemData); QString hash(const QString& string); QString getFirstMacAddress(); -int checkProcessorArch(); +qProcessorArch getProcessorArch(); From 590d0482b30ed48335d72915cc8fe79ef0d0d715 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 19:54:21 +0100 Subject: [PATCH 50/66] Removed useless processor type return for Mac #4708 --- src/gui/src/QUtility.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/gui/src/QUtility.cpp b/src/gui/src/QUtility.cpp index ae72fdb4..14d1d1e1 100644 --- a/src/gui/src/QUtility.cpp +++ b/src/gui/src/QUtility.cpp @@ -80,10 +80,6 @@ qProcessorArch getProcessorArch() } #endif -#if defined(Q_OS_MAC) - return kProcessorArchMac; -#endif - #if defined(Q_OS_LINUX) #ifdef __i386__ return kProcessorArchLinux32; From 3563217c7b349cd59d3d02fedee1234c5492c82e Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 19:54:34 +0100 Subject: [PATCH 51/66] Fixed Mac package platform variable name #4708 --- src/gui/src/PluginManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/src/PluginManager.cpp b/src/gui/src/PluginManager.cpp index d37fc503..fd3a24e4 100644 --- a/src/gui/src/PluginManager.cpp +++ b/src/gui/src/PluginManager.cpp @@ -33,7 +33,7 @@ static const char kBaseUrl[] = "http://synergy-project.org/files"; static const char kDefaultVersion[] = "1.1"; static const char kWinPackagePlatform32[] = "Windows-x86"; static const char kWinPackagePlatform64[] = "Windows-x64"; -static const char kMacPackagePlatform32[] = "MacOSX%1-i386"; +static const char kMacPackagePlatform[] = "MacOSX%1-i386"; static const char kLinuxPackagePlatformDeb32[] = "Linux-i686-deb"; static const char kLinuxPackagePlatformDeb64[] = "Linux-x86_64-deb"; static const char kLinuxPackagePlatformRpm32[] = "Linux-i686-rpm"; From 0fdcfe9b95dff376f0ea1a6e25a38e4caf4ed0c2 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 20:19:37 +0100 Subject: [PATCH 52/66] Fixed code style, prefer enums over macros #4650 --- src/lib/plugin/ns/SecureSocket.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index cb65bd85..5942b523 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -35,7 +35,10 @@ // #define MAX_ERROR_SIZE 65535 -#define MAX_RETRY_COUNT 60 + +enum { + kMaxRetryCount = 60 +}; static const char kFingerprintDirName[] = "SSL/Fingerprints"; //static const char kFingerprintLocalFilename[] = "Local.txt"; @@ -53,7 +56,7 @@ SecureSocket::SecureSocket( TCPSocket(events, socketMultiplexer), m_secureReady(false), m_fatal(false), - m_maxRetry(MAX_RETRY_COUNT) + m_maxRetry(kMaxRetryCount) { } @@ -64,7 +67,7 @@ SecureSocket::SecureSocket( TCPSocket(events, socketMultiplexer, socket), m_secureReady(false), m_fatal(false), - m_maxRetry(MAX_RETRY_COUNT) + m_maxRetry(kMaxRetryCount) { } From 1cea4bb80ff5258fd89ce85e1e098989b09ac7dd Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 20:20:03 +0100 Subject: [PATCH 53/66] Fixed code style, use lower case for logging #4650 --- src/lib/plugin/ns/SecureSocket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 5942b523..8571d9a4 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -448,7 +448,7 @@ SecureSocket::checkResult(int status, int& retry) // If the retry max would exceed the allowed, treat it as a fatal error if (retry > maxRetry()) { - LOG((CLOG_ERR "Maximum retry count exceeded:%d",retry)); + LOG((CLOG_ERR "maximum retry count exceeded:%d",retry)); isFatal(true); } From 4aa57cfbdb47c2aa812755c8454e52ddadf2b069 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 25 May 2015 20:24:29 +0100 Subject: [PATCH 54/66] Fixed code style, more consistent logging #4650 --- src/lib/plugin/ns/SecureSocket.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 8571d9a4..367ef067 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -395,7 +395,7 @@ SecureSocket::checkResult(int status, int& retry) case SSL_ERROR_ZERO_RETURN: // connection closed isFatal(true); - LOG((CLOG_DEBUG "SSL connection has been closed")); + LOG((CLOG_DEBUG "ssl connection closed")); break; case SSL_ERROR_WANT_READ: @@ -405,21 +405,21 @@ SecureSocket::checkResult(int status, int& retry) retry += 1; // If there are a lot of retrys, it's worth warning about if ( retry % 5 == 0 ) { - LOG((CLOG_DEBUG "need to retry the same SSL function(%d) retry:%d", errorCode, retry)); + LOG((CLOG_DEBUG "ssl retry occurred, error=%s, attempt=%d", errorCode, retry)); } else if ( retry == (maxRetry() / 2) ) { - LOG((CLOG_WARN "need to retry the same SSL function(%d) retry:%d", errorCode, retry)); + LOG((CLOG_WARN "ssl retry occurred, error=%s, attempt=%d", errorCode, retry)); } else { - LOG((CLOG_DEBUG2 "need to retry the same SSL function(%d) retry:%d", errorCode, retry)); + LOG((CLOG_DEBUG2 "ssl retry occurred, error=%s, attempt=%d", errorCode, retry)); } break; case SSL_ERROR_SYSCALL: - LOG((CLOG_ERR "some secure socket I/O error occurred")); + LOG((CLOG_ERR "ssl error occurred")); if (ERR_peek_error() == 0) { if (status == 0) { - LOG((CLOG_ERR "an EOF violates the protocol")); + LOG((CLOG_ERR "eof violates ssl protocol")); } else if (status == -1) { // underlying socket I/O reproted an error @@ -436,19 +436,19 @@ SecureSocket::checkResult(int status, int& retry) break; case SSL_ERROR_SSL: - LOG((CLOG_ERR "a failure in the SSL library occurred")); + LOG((CLOG_ERR "generic ssl error")); isFatal(true); break; default: - LOG((CLOG_ERR "unknown secure socket error")); + LOG((CLOG_ERR "unknown ssl error")); isFatal(true); break; } // If the retry max would exceed the allowed, treat it as a fatal error if (retry > maxRetry()) { - LOG((CLOG_ERR "maximum retry count exceeded:%d",retry)); + LOG((CLOG_ERR "ssl retry limit exceeded: %d", retry)); isFatal(true); } From e96cead73267d0d5c17e52920f5ce121e51db4b6 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 26 May 2015 12:18:39 +0100 Subject: [PATCH 55/66] Fixed wrong sprintf specifier used (%s instead of %d) #4650 My bad, I was being carless in 4aa57cfbdb47c2aa812755c8454e52ddadf2b069 --- src/lib/plugin/ns/SecureSocket.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 367ef067..66c8a42f 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -405,13 +405,13 @@ SecureSocket::checkResult(int status, int& retry) retry += 1; // If there are a lot of retrys, it's worth warning about if ( retry % 5 == 0 ) { - LOG((CLOG_DEBUG "ssl retry occurred, error=%s, attempt=%d", errorCode, retry)); + LOG((CLOG_DEBUG "ssl retry occurred, error=%d, attempt=%d", errorCode, retry)); } else if ( retry == (maxRetry() / 2) ) { - LOG((CLOG_WARN "ssl retry occurred, error=%s, attempt=%d", errorCode, retry)); + LOG((CLOG_WARN "ssl retry occurred, error=%d, attempt=%d", errorCode, retry)); } else { - LOG((CLOG_DEBUG2 "ssl retry occurred, error=%s, attempt=%d", errorCode, retry)); + LOG((CLOG_DEBUG2 "ssl retry occurred, error=%d, attempt=%d", errorCode, retry)); } break; From 3dc3d5b30910f60f4b3f32509acd27a0746c14ef Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 26 May 2015 14:00:50 +0100 Subject: [PATCH 56/66] Increased ssl error rate limit from 60 to 50000 #4650 @speaker, a 50k limit seems a bit insane, but it seems to be the only way to get my Mac client to establish a connection :-/ --- src/lib/plugin/ns/SecureSocket.cpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 66c8a42f..4d892246 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -37,7 +37,9 @@ #define MAX_ERROR_SIZE 65535 enum { - kMaxRetryCount = 60 + // this limit seems extremely high, but mac client seem to generate around + // 20,000 errors before they establish a connection (wtf?) + kMaxRetryCount = 50000 }; static const char kFingerprintDirName[] = "SSL/Fingerprints"; @@ -402,17 +404,11 @@ SecureSocket::checkResult(int status, int& retry) case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: - retry += 1; - // If there are a lot of retrys, it's worth warning about - if ( retry % 5 == 0 ) { - LOG((CLOG_DEBUG "ssl retry occurred, error=%d, attempt=%d", errorCode, retry)); - } - else if ( retry == (maxRetry() / 2) ) { - LOG((CLOG_WARN "ssl retry occurred, error=%d, attempt=%d", errorCode, retry)); - } - else { - LOG((CLOG_DEBUG2 "ssl retry occurred, error=%d, attempt=%d", errorCode, retry)); - } + // it seems like these sort of errors are part of openssl's normal behavior, + // so we should expect a very high amount of these. sleeping doesn't seem to + // help... maybe you just have to swallow the errors (yuck). + retry++; + LOG((CLOG_DEBUG2 "passive ssl error, error=%d, attempt=%d", errorCode, retry)); break; case SSL_ERROR_SYSCALL: @@ -448,7 +444,7 @@ SecureSocket::checkResult(int status, int& retry) // If the retry max would exceed the allowed, treat it as a fatal error if (retry > maxRetry()) { - LOG((CLOG_ERR "ssl retry limit exceeded: %d", retry)); + LOG((CLOG_ERR "passive ssl error limit exceeded: %d", retry)); isFatal(true); } From 42ed1c2f270224205770779d158fef5f24f4324c Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 26 May 2015 14:14:24 +0100 Subject: [PATCH 57/66] Increased ssl error rate limit from 50k to 100k #4650 @speaker This is nuts... my mac client now errors 50k times before it eventually connects. --- src/lib/plugin/ns/SecureSocket.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 4d892246..04117742 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -38,8 +38,8 @@ enum { // this limit seems extremely high, but mac client seem to generate around - // 20,000 errors before they establish a connection (wtf?) - kMaxRetryCount = 50000 + // 50,000 errors before they establish a connection (wtf?) + kMaxRetryCount = 100000 }; static const char kFingerprintDirName[] = "SSL/Fingerprints"; From fd72bf17ce7096afcf55f3dc2bc396a8931b42fd Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 26 May 2015 14:17:30 +0100 Subject: [PATCH 58/66] Reworded SSL errors to be more consistent #4650 --- src/lib/plugin/ns/SecureSocket.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 04117742..9d1c10f5 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -412,7 +412,7 @@ SecureSocket::checkResult(int status, int& retry) break; case SSL_ERROR_SYSCALL: - LOG((CLOG_ERR "ssl error occurred")); + LOG((CLOG_ERR "ssl error occurred (system call failure)")); if (ERR_peek_error() == 0) { if (status == 0) { LOG((CLOG_ERR "eof violates ssl protocol")); @@ -432,12 +432,12 @@ SecureSocket::checkResult(int status, int& retry) break; case SSL_ERROR_SSL: - LOG((CLOG_ERR "generic ssl error")); + LOG((CLOG_ERR "ssl error occurred (generic failure)")); isFatal(true); break; default: - LOG((CLOG_ERR "unknown ssl error")); + LOG((CLOG_ERR "ssl error occurred (unknown failure)")); isFatal(true); break; } From 8b975878c2343a367d6b6dea322d747d1c9ef801 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 26 May 2015 15:04:04 +0100 Subject: [PATCH 59/66] Limited clipboard size to 1kb when SSL is enabled #4601 @XinyuHou I had no choice but to block clipboard data over 1kb in size... anything over that and you get an access violation. --- src/lib/server/ClientListener.h | 3 +++ src/lib/server/ClientProxy1_6.cpp | 17 +++++++++++++++-- src/lib/server/Server.cpp | 6 ++++++ src/lib/server/Server.h | 3 +++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/lib/server/ClientListener.h b/src/lib/server/ClientListener.h index c0b35ab3..70ecb2eb 100644 --- a/src/lib/server/ClientListener.h +++ b/src/lib/server/ClientListener.h @@ -64,6 +64,9 @@ public: //! Get server which owns this listener Server* getServer() { return m_server; } + //! Return true if using secure network connection + bool isSecure() { return m_useSecureNetwork; } + //@} private: diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp index 8b429cca..a27e8ce6 100644 --- a/src/lib/server/ClientProxy1_6.cpp +++ b/src/lib/server/ClientProxy1_6.cpp @@ -29,6 +29,11 @@ // ClientProxy1_6 // +enum +{ + kSslClipboardMaxSize = 1024 +}; + ClientProxy1_6::ClientProxy1_6(const String& name, synergy::IStream* stream, Server* server, IEventQueue* events) : ClientProxy1_5(name, stream, server, events), m_events(events) @@ -57,9 +62,17 @@ ClientProxy1_6::setClipboard(ClipboardID id, const IClipboard* clipboard) size_t size = data.size(); LOG((CLOG_DEBUG "sending clipboard %d to \"%s\"", id, getName().c_str())); - StreamChunker::sendClipboard(data, size, id, 0, m_events, this); + // HACK: if using SSL, don't send large clipboards (#4601) + bool send = true; + if (getServer()->isSecure() && (size > kSslClipboardMaxSize)) { + send = false; + LOG((CLOG_WARN "large clipboards not supported with ssl, size=%d", size)); + } - LOG((CLOG_DEBUG "sent clipboard size=%d", size)); + if (send) { + StreamChunker::sendClipboard(data, size, id, 0, m_events, this); + LOG((CLOG_DEBUG "sent clipboard size=%d", size)); + } } } diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 832a03b0..8b7c4afd 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -2389,3 +2389,9 @@ Server::dragInfoReceived(UInt32 fileNum, String content) m_screen->startDraggingFiles(m_dragFileList); } + +bool +Server::isSecure() const +{ + return m_clientListener->isSecure(); +} diff --git a/src/lib/server/Server.h b/src/lib/server/Server.h index d85a113d..63551efa 100644 --- a/src/lib/server/Server.h +++ b/src/lib/server/Server.h @@ -175,6 +175,9 @@ public: //! Return received file data String& getReceivedFileData() { return m_receivedFileData; } + //! Return true if using secure network connection + bool isSecure() const; + //@} private: From f5d303cab20a1cda0856b12fa1c63ffd8580d980 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 26 May 2015 15:57:53 +0100 Subject: [PATCH 60/66] ChangeLog for v1.7.3-stable --- ChangeLog | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/ChangeLog b/ChangeLog index 075c4f59..e46de303 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +Bug #4565 - Incorrect plugin downloads on Debian and Mint +Bug #4677 - Windows service log file grows to very large size +Bug #4651 - High logging rate causes Windows service to crash +Bug #4650 - SSL error log message repeats excessively and freezes cursor +Bug #4624 - Runaway logging causes GUI to freeze +Bug #4617 - Windows service randomly stops after 'ssl handshake failure' error +Bug #4601 - Large clipboard data with SSL causes 'protocol is shutdown' error +Bug #4593 - Locking Windows server causes SSL_ERROR_SSL to repeat +Bug #4577 - Memory leak in GUI on Windows caused by logging +Bug #4538 - Windows service crashes intermittently with no error +Bug #4341 - GUI freezes on first load when reading log +Bug #4566 - Client or server crashes with 'ssl handshake failure' error +Bug #4706 - Installer is not output to build config dir on Windows +Bug #4704 - Plugin 'ns' release build is overwritten with debug version on Linux +Bug #4703 - Plugins are not built to config directory on Mac +Bug #4697 - Timing can allow an SSL socket to be used after cleanup call +Enhancement #4661 - Log error but do not crash when failing to load plugins +Enhancement #4708 - Download ns plugin for specific Mac versions +Enhancement #4587 - Include OpenSSL binaries in source for easier building +Enhancement #4695 - Automatically upload plugins as Buildbot step + v1.7.2 ====== Bug #4564 - Modifier keys often stuck down on Mac client From ace8ffd6a88be762c642d761ae7d249ad906c249 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 26 May 2015 15:58:33 +0100 Subject: [PATCH 61/66] Fixed version names in ChangeLog --- ChangeLog | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index e46de303..72280b2c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,5 @@ +v1.7.3-stable +============= Bug #4565 - Incorrect plugin downloads on Debian and Mint Bug #4677 - Windows service log file grows to very large size Bug #4651 - High logging rate causes Windows service to crash @@ -19,8 +21,8 @@ Enhancement #4708 - Download ns plugin for specific Mac versions Enhancement #4587 - Include OpenSSL binaries in source for easier building Enhancement #4695 - Automatically upload plugins as Buildbot step -v1.7.2 -====== +v1.7.2-stable +============= Bug #4564 - Modifier keys often stuck down on Mac client Bug #4581 - Starting GUI on Mac crashes instantly on syntool segfault Bug #4520 - Laggy or sluggish cursor (ping spikes) on Mac when using WiFi @@ -32,8 +34,8 @@ Enhancement #4569 - Reintroduce GUI auto-hide setting (disabled by default) Enhancement #4570 - Make `--crypto-pass` show deprecated message Enhancement #4596 - Typo 'occurred' in WebClient.cpp -v1.7.1 -====== +v1.7.1-beta +=========== Bug #3784 - Double click & drag doesn't select words on client Bug #3052 - Triple-click (select line) does not work Bug #4367 - Duplicate Alt-S Keyboard Shortcuts on Gui @@ -59,8 +61,8 @@ Enhancement #4540 - Enable Network Security checkbox only when ns plugin exists Enhancement #4525 - Reorganize app data directory Enhancement #4390 - Disable GUI auto-hide by default -1.7.0 -===== +v1.7.0-beta +=========== Enhancement #4313 - SSL encrypted secure connection Enhancement #4168 - Plugin manager for GUI Enhancement #4307 - Always show client auto-detect dialog From 55c9f46156ef42a6acdb1fe2ca4871e3a623616e Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 26 May 2015 16:01:45 +0100 Subject: [PATCH 62/66] Fixed version name for 1.7.1 in ChangeLog --- ChangeLog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 72280b2c..b1d0c045 100644 --- a/ChangeLog +++ b/ChangeLog @@ -34,8 +34,8 @@ Enhancement #4569 - Reintroduce GUI auto-hide setting (disabled by default) Enhancement #4570 - Make `--crypto-pass` show deprecated message Enhancement #4596 - Typo 'occurred' in WebClient.cpp -v1.7.1-beta -=========== +v1.7.1-stable +============= Bug #3784 - Double click & drag doesn't select words on client Bug #3052 - Triple-click (select line) does not work Bug #4367 - Duplicate Alt-S Keyboard Shortcuts on Gui From 9b3d6d566532bdd507519c1af3dd8d067306986d Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 26 May 2015 16:31:56 +0100 Subject: [PATCH 63/66] Fake commit to force buildbot to build this branch --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index b1d0c045..ec6c108c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -287,3 +287,4 @@ Feature #3119: Mac OS X secondary screen Task #2905: Unit tests: Clipboard classes Task #3072: Downgrade Linux build machines Task #3090: CXWindowsKeyState integ test args wrong + \ No newline at end of file From 5faa5178b46d26f91c62f10f159ae5e64761aa43 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 26 May 2015 16:49:23 +0100 Subject: [PATCH 64/66] Remove 'heads/' string from git branch name #4695 --- ext/toolchain/commands1.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index 9f8149c2..1c750a1d 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -1440,10 +1440,16 @@ class InternalCommands: raise Exception('Could not find package name with pattern: ' + pattern) def dist_name_rev(self, type): + branch = "heads/" + self.getGitBranchName() + revision = self.getGitRevision() + + # sometimes, git will prepend "heads/" infront of the branch name, + # remove this as it's not useful to us and causes ftp issues. + branch = re.sub("heads/", "", branch) + # find the version number (we're puting the rev in after this) pattern = '(\d+\.\d+\.\d+)' - replace = "%s-%s" % ( - self.getGitBranchName(), self.getGitRevision()) + replace = "%s-%s" % (branch, revision) return re.sub(pattern, replace, self.dist_name(type)) def getDebianArch(self): From d6cefa73a00eed23a2a5978a1cc5ce32bfe7a6a8 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 26 May 2015 16:49:59 +0100 Subject: [PATCH 65/66] Removed test prepend string from previous commit #4695 --- ext/toolchain/commands1.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index 1c750a1d..777d7e98 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -1440,13 +1440,13 @@ class InternalCommands: raise Exception('Could not find package name with pattern: ' + pattern) def dist_name_rev(self, type): - branch = "heads/" + self.getGitBranchName() + branch = self.getGitBranchName() revision = self.getGitRevision() # sometimes, git will prepend "heads/" infront of the branch name, # remove this as it's not useful to us and causes ftp issues. branch = re.sub("heads/", "", branch) - + # find the version number (we're puting the rev in after this) pattern = '(\d+\.\d+\.\d+)' replace = "%s-%s" % (branch, revision) From efd01085974da90ee297f12e228719dfdce774b2 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 26 May 2015 16:56:21 +0100 Subject: [PATCH 66/66] Fixed "heads/" prepend problem for plugin upload #4695 --- ext/toolchain/commands1.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index 777d7e98..1cec58cd 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -972,7 +972,13 @@ class InternalCommands: if p.returncode != 0: raise Exception('Could not get branch name, git error: ' + str(p.returncode)) - return stdout.strip() + result = stdout.strip() + + # sometimes, git will prepend "heads/" infront of the branch name, + # remove this as it's not useful to us and causes ftp issues. + result = re.sub("heads/", "", result) + + return result def find_revision_svn(self): if sys.version_info < (2, 4): @@ -1443,10 +1449,6 @@ class InternalCommands: branch = self.getGitBranchName() revision = self.getGitRevision() - # sometimes, git will prepend "heads/" infront of the branch name, - # remove this as it's not useful to us and causes ftp issues. - branch = re.sub("heads/", "", branch) - # find the version number (we're puting the rev in after this) pattern = '(\d+\.\d+\.\d+)' replace = "%s-%s" % (branch, revision)