<commit_before>/*
 * SessionNamedPipeHttpConnectionListener.cpp
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include <session/SessionHttpConnectionListener.hpp>

#include <string>

#include <boost/utility.hpp>
#include <boost/format.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/asio/buffer.hpp>

#include <core/Log.hpp>
#include <core/Error.hpp>
#include <core/Thread.hpp>

#include <core/http/Request.hpp>
#include <core/http/Response.hpp>
#include <core/http/RequestParser.hpp>
#include <core/http/SocketUtils.hpp>

#include <core/json/JsonRpc.hpp>

#include <core/system/System.hpp>
#include <core/system/Environment.hpp>

#include <windows.h>
#include <sddl.h>

// Vista+
#ifndef PIPE_REJECT_REMOTE_CLIENTS
#define PIPE_REJECT_REMOTE_CLIENTS 0x00000008
#endif

// Mingw doesn't have this declaration
#define SDDL_REVISION_1 1
extern "C" BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorA(
                           LPCSTR, DWORD, LPVOID *, PULONG);

#include <session/SessionOptions.hpp>

#include "SessionHttpConnectionUtils.hpp"

// TODO: detailed review of named pipe connection listener code

using namespace core ;

namespace session {

namespace {

const int kReadBufferSize = 4096;

class NamedPipeHttpConnection : public HttpConnection,
                                boost::noncopyable
{
public:
   explicit NamedPipeHttpConnection(HANDLE hPipe)
      : hPipe_(hPipe)
   {
   }

   virtual ~NamedPipeHttpConnection()
   {
      try
      {
         close();
      }
      CATCH_UNEXPECTED_EXCEPTION
   }

   bool readRequest()
   {
      core::http::RequestParser parser;
      CHAR buff[kReadBufferSize];
      DWORD bytesRead;

      while(TRUE)
      {
         // read from pipe
         BOOL result = ::ReadFile(hPipe_, buff, kReadBufferSize, &bytesRead, NULL);

         // check for error
         if (!result)
         {
            Error error = systemError(::GetLastError(), ERROR_LOCATION);
            if (!core::http::isConnectionTerminatedError(error))
               LOG_ERROR(error);

            close();

            return false;
         }

         // end of file - we should never get this far (request parser
         // should signal that we have the full request bfore we get here)
         else if (bytesRead == 0)
         {
            LOG_WARNING_MESSAGE("ReadFile returned 0 bytes");

            core::http::Response response;
            response.setStatusCode(core::http::status::BadRequest);
            sendResponse(response);

            return false;
         }

         // got input
         else
         {
            // parse next chunk
            http::RequestParser::status status = parser.parse(
                                                   request_,
                                                   buff,
                                                   buff + bytesRead);

            // error - return bad request
            if (status == core::http::RequestParser::error)
            {
               core::http::Response response;
               response.setStatusCode(core::http::status::BadRequest);
               sendResponse(response);

               return false;
            }

            // incomplete -- keep reading
            else if (status == core::http::RequestParser::incomplete)
            {
               continue;
            }

            // got valid request -- handle it
            else
            {
               requestId_ = request_.headerValue("X-RS-RID");
               return true;
            }
         }
      }

      // keep compiler happy (we should never get here
      return false;
   }

   virtual const core::http::Request& request() { return request_; }

   virtual void sendResponse(const core::http::Response &response)
   {
      // get the buffers
      std::vector<boost::asio::const_buffer> buffers =response.toBuffers(
                                        core::http::Header::connectionClose());

      // write them
      DWORD bytesWritten;
      for (std::size_t i=0; i<buffers.size(); i++)
      {
         DWORD bytesToWrite = boost::asio::buffer_size(buffers[i]);
         BOOL success = ::WriteFile(
                  hPipe_,
                  boost::asio::buffer_cast<const unsigned char*>(buffers[i]),
                  bytesToWrite,
                  &bytesWritten,
                  NULL);

         if (!success || (bytesWritten != bytesToWrite))
         {
            // establish error
            Error error = systemError(::GetLastError(), ERROR_LOCATION);
            error.addProperty("request-uri", request_.uri());

            // log the error if it wasn't connection terminated
            if (!core::http::isConnectionTerminatedError(error))
               LOG_ERROR(error);

            // close and terminate
            close();
            break;
         }
      }
   }

   // close (occurs automatically after writeResponse, here in case it
   // need to be closed in other circumstances
   virtual void close()
   {
      if (hPipe_ != INVALID_HANDLE_VALUE)
      {
         if (!::FlushFileBuffers(hPipe_))
            LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));

         if (!::DisconnectNamedPipe(hPipe_))
            LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));

         if (!::CloseHandle(hPipe_))
            LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));

         hPipe_ = INVALID_HANDLE_VALUE;
     }
   }

   // other useful introspection methods
   virtual std::string requestId() const { return requestId_; }

private:
   HANDLE hPipe_;
   core::http::Request request_;
   std::string requestId_;
};

class NamedPipeHttpConnectionListener : public HttpConnectionListener,
                                        boost::noncopyable
{
public:
   explicit NamedPipeHttpConnectionListener(const std::string& pipeName,
                                            const std::string& secret)
      : pipeName_(pipeName), secret_(secret)
   {
   }

   virtual Error start()
   {
      core::thread::safeLaunchThread(
         boost::bind(&NamedPipeHttpConnectionListener::listenerThread,
                     this));

      return Success();
   }

   virtual void stop()
   {
      // we don't support stop because it is never called in desktop mode

   }

   // connection queues
   virtual HttpConnectionQueue& mainConnectionQueue()
   {
      return mainConnectionQueue_;
   }

   virtual HttpConnectionQueue& eventsConnectionQueue()
   {
      return eventsConnectionQueue_;
   }

private:
   void listenerThread()
   {
      try
      {
         while (true)
         {
            // create security attributes
            PSECURITY_ATTRIBUTES pSA = NULL;
            SECURITY_ATTRIBUTES sa;
            ZeroMemory(&sa, sizeof(sa));
            sa.nLength = sizeof(sa);
            sa.lpSecurityDescriptor = NULL;
            sa.bInheritHandle = FALSE;

            // get login session only descriptor -- proceed without one
            // if we fail since we don't have 100% assurance this will
            // work in all configurations and the world ends if we don't
            // proceed with creating the pipe
            sa.lpSecurityDescriptor = pipeServerSecurityDescriptor();
            if (sa.lpSecurityDescriptor)
               pSA = &sa;

            // create pipe
            HANDLE hPipe = ::CreateNamedPipeA(pipeName_.c_str(),
                                              PIPE_ACCESS_DUPLEX,
                                              PIPE_TYPE_BYTE |
                                              PIPE_READMODE_BYTE |
                                              PIPE_WAIT |
                                              PIPE_REJECT_REMOTE_CLIENTS,
                                              PIPE_UNLIMITED_INSTANCES,
                                              kReadBufferSize,
                                              kReadBufferSize,
                                              0,
                                              pSA);
            DWORD lastError = ::GetLastError(); // capture err before LocalFree

            // free security descriptor if we used one
            if (pSA)
               ::LocalFree(pSA->lpSecurityDescriptor);

            // check for error
            if (hPipe == INVALID_HANDLE_VALUE)
            {
               LOG_ERROR(systemError(lastError, ERROR_LOCATION));
               continue;
            }

            // attempt to connect
            BOOL connected = ::ConnectNamedPipe(hPipe, NULL) ?
                   TRUE : (::GetLastError() == ERROR_PIPE_CONNECTED);

            if (connected)
            {
               // create connection
               boost::shared_ptr<NamedPipeHttpConnection> ptrPipeConnection(
                                         new NamedPipeHttpConnection(hPipe));

               // if we can successfully read a request then enque it
               if (ptrPipeConnection->readRequest())
                  enqueConnection(ptrPipeConnection);
            }
            else
            {
               LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));
            }
         }
      }
      CATCH_UNEXPECTED_EXCEPTION
   }

   // NOTE: this logic is duplicated btw here and HttpConnectionListenerImpl

   void enqueConnection(
                  boost::shared_ptr<NamedPipeHttpConnection> ptrConnection)
   {
      // convert to cannonical HttpConnection
      boost::shared_ptr<HttpConnection> ptrHttpConnection =
            boost::shared_static_cast<HttpConnection>(ptrConnection);

      if (!authenticate(ptrHttpConnection))
      {
         core::http::Response response;
         response.setStatusCode(403);
         response.setStatusMessage("Forbidden");
         ptrConnection->sendResponse(response);
         return;
      }

      // check for the special rpc/abort endpoint and abort if requested
      // we do this in the background listener thread so it can always
      // be processed even if the foreground thread is deadlocked or otherwise
      // unresponsive
      if (connection::checkForAbort(
             ptrHttpConnection,
             boost::bind(&NamedPipeHttpConnectionListener::cleanup,
                         this)))
      {
         return;
      }

      // place the connection on the correct queue
      if (connection::isGetEvents(ptrHttpConnection))
         eventsConnectionQueue_.enqueConnection(ptrHttpConnection);
      else
         mainConnectionQueue_.enqueConnection(ptrHttpConnection);
   }

   virtual bool authenticate(boost::shared_ptr<HttpConnection> ptrConnection)
   {
      return connection::authenticate(ptrConnection, secret_);
   }

   core::Error cleanup()
   {
      return Success();
   }

   static LPVOID pipeServerSecurityDescriptor()
   {
      // NOTE: if this doesn't work for whatever reason we could consider
      // falling back to this: "D:(D;;GA;;;AN)(A;;GA;;;AU)"
      // (which would be deny access to anonymous users and grant access
      // to authenticated users)

      std::string securityDescriptor;
      Error error = logonSessionOnlyDescriptor(&securityDescriptor);
      if (error)
      {
         LOG_ERROR(error);
         return NULL;
      }

      ULONG sdSize;
      LPVOID pSA;
      if (::ConvertStringSecurityDescriptorToSecurityDescriptorA(
          securityDescriptor.c_str(),
          SDDL_REVISION_1,
          &pSA,
          &sdSize))
      {
         return pSA;
      }
      else
      {
         LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));
         return NULL;
      }
   }

   static core::Error logonSessionOnlyDescriptor(std::string* pDescriptor)
   {
      // token for current process
      HANDLE hToken = NULL;
      if (!OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken))
         return systemError(::GetLastError(), ERROR_LOCATION);
      core::system::CloseHandleOnExitScope tokenScope(&hToken, ERROR_LOCATION);

      // size of token groups structure (note that we exepct the error
      // since we pass NULL for the token information buffer)
      DWORD tgSize = 0;
      BOOL res = ::GetTokenInformation(hToken, TokenGroups, NULL, 0, &tgSize);
      if (res != FALSE && ::GetLastError() != ERROR_INSUFFICIENT_BUFFER)
         return systemError(::GetLastError(), ERROR_LOCATION);

      // get the token groups structure
      std::vector<char> tg(tgSize);
      TOKEN_GROUPS* pTG = reinterpret_cast<TOKEN_GROUPS*>(&tg[0]);
      if (!::GetTokenInformation(hToken, TokenGroups, pTG, tgSize, &tgSize))
         return systemError(::GetLastError(), ERROR_LOCATION);

      // find login sid
      SID* pSid = NULL;
      for (DWORD i = 0; i < pTG->GroupCount ; ++i)
      {
         if ((pTG->Groups[i].Attributes & SE_GROUP_LOGON_ID)
                                                   == SE_GROUP_LOGON_ID)
         {
           pSid = reinterpret_cast<SID*>(pTG->Groups[i].Sid);
           break;
         }
      }

      // ensure we found it
      if (pSid == NULL)
      {
         return systemError(boost::system::windows_error::file_not_found,
                            "Failed to find SE_GROUP_LOGON_ID",
                            ERROR_LOCATION);
      }

      // convert to a string
      char* pSidString = NULL;
      if (!::ConvertSidToStringSid(pSid, &pSidString))
         return systemError(::GetLastError(), ERROR_LOCATION);

      // format string for caller
      boost::format fmt("D:(A;OICI;GA;;;%1%)");
      *pDescriptor = boost::str(fmt % pSidString);

      // free sid string
      ::LocalFree(pSidString);

      // return success
      return Success();
   }

private:
   std::string pipeName_;
   std::string secret_;
   HttpConnectionQueue mainConnectionQueue_;
   HttpConnectionQueue eventsConnectionQueue_;
};

// pointer to global connection listener singleton
HttpConnectionListener* s_pHttpConnectionListener = NULL ;

}  // anonymouys namespace

void initializeHttpConnectionListener()
{
   std::string pipeName = core::system::getenv("RS_LOCAL_PEER");
   std::string secret = session::options().sharedSecret();
   s_pHttpConnectionListener = new NamedPipeHttpConnectionListener(pipeName,
                                                                   secret);
   Error error = s_pHttpConnectionListener->start();
   if (error)
      LOG_ERROR(error);
}

HttpConnectionListener& httpConnectionListener()
{
   return *s_pHttpConnectionListener;
}

} // namespace session
<commit_msg>update todo<commit_after>/*
 * SessionNamedPipeHttpConnectionListener.cpp
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include <session/SessionHttpConnectionListener.hpp>

#include <string>

#include <boost/utility.hpp>
#include <boost/format.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/asio/buffer.hpp>

#include <core/Log.hpp>
#include <core/Error.hpp>
#include <core/Thread.hpp>

#include <core/http/Request.hpp>
#include <core/http/Response.hpp>
#include <core/http/RequestParser.hpp>
#include <core/http/SocketUtils.hpp>

#include <core/json/JsonRpc.hpp>

#include <core/system/System.hpp>
#include <core/system/Environment.hpp>

#include <windows.h>
#include <sddl.h>

// Vista+
#ifndef PIPE_REJECT_REMOTE_CLIENTS
#define PIPE_REJECT_REMOTE_CLIENTS 0x00000008
#endif

// Mingw doesn't have this declaration
#define SDDL_REVISION_1 1
extern "C" BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorA(
                           LPCSTR, DWORD, LPVOID *, PULONG);

#include <session/SessionOptions.hpp>

#include "SessionHttpConnectionUtils.hpp"

using namespace core ;

namespace session {

namespace {

const int kReadBufferSize = 4096;

class NamedPipeHttpConnection : public HttpConnection,
                                boost::noncopyable
{
public:
   explicit NamedPipeHttpConnection(HANDLE hPipe)
      : hPipe_(hPipe)
   {
   }

   virtual ~NamedPipeHttpConnection()
   {
      try
      {
         close();
      }
      CATCH_UNEXPECTED_EXCEPTION
   }

   bool readRequest()
   {
      core::http::RequestParser parser;
      CHAR buff[kReadBufferSize];
      DWORD bytesRead;

      while(TRUE)
      {
         // read from pipe
         BOOL result = ::ReadFile(hPipe_, buff, kReadBufferSize, &bytesRead, NULL);

         // check for error
         if (!result)
         {
            Error error = systemError(::GetLastError(), ERROR_LOCATION);
            if (!core::http::isConnectionTerminatedError(error))
               LOG_ERROR(error);

            close();

            return false;
         }

         // end of file - we should never get this far (request parser
         // should signal that we have the full request bfore we get here)
         else if (bytesRead == 0)
         {
            LOG_WARNING_MESSAGE("ReadFile returned 0 bytes");

            core::http::Response response;
            response.setStatusCode(core::http::status::BadRequest);
            sendResponse(response);

            return false;
         }

         // got input
         else
         {
            // parse next chunk
            http::RequestParser::status status = parser.parse(
                                                   request_,
                                                   buff,
                                                   buff + bytesRead);

            // error - return bad request
            if (status == core::http::RequestParser::error)
            {
               core::http::Response response;
               response.setStatusCode(core::http::status::BadRequest);
               sendResponse(response);

               return false;
            }

            // incomplete -- keep reading
            else if (status == core::http::RequestParser::incomplete)
            {
               continue;
            }

            // got valid request -- handle it
            else
            {
               requestId_ = request_.headerValue("X-RS-RID");
               return true;
            }
         }
      }

      // keep compiler happy (we should never get here
      return false;
   }

   virtual const core::http::Request& request() { return request_; }

   virtual void sendResponse(const core::http::Response &response)
   {
      // get the buffers
      std::vector<boost::asio::const_buffer> buffers =response.toBuffers(
                                        core::http::Header::connectionClose());

      // write them
      DWORD bytesWritten;
      for (std::size_t i=0; i<buffers.size(); i++)
      {
         DWORD bytesToWrite = boost::asio::buffer_size(buffers[i]);
         BOOL success = ::WriteFile(
                  hPipe_,
                  boost::asio::buffer_cast<const unsigned char*>(buffers[i]),
                  bytesToWrite,
                  &bytesWritten,
                  NULL);

         if (!success || (bytesWritten != bytesToWrite))
         {
            // establish error
            Error error = systemError(::GetLastError(), ERROR_LOCATION);
            error.addProperty("request-uri", request_.uri());

            // log the error if it wasn't connection terminated
            if (!core::http::isConnectionTerminatedError(error))
               LOG_ERROR(error);

            // close and terminate
            close();
            break;
         }
      }
   }

   // close (occurs automatically after writeResponse, here in case it
   // need to be closed in other circumstances
   virtual void close()
   {
      if (hPipe_ != INVALID_HANDLE_VALUE)
      {
         if (!::FlushFileBuffers(hPipe_))
            LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));

         if (!::DisconnectNamedPipe(hPipe_))
            LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));

         if (!::CloseHandle(hPipe_))
            LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));

         hPipe_ = INVALID_HANDLE_VALUE;
     }
   }

   // other useful introspection methods
   virtual std::string requestId() const { return requestId_; }

private:
   HANDLE hPipe_;
   core::http::Request request_;
   std::string requestId_;
};

class NamedPipeHttpConnectionListener : public HttpConnectionListener,
                                        boost::noncopyable
{
public:
   explicit NamedPipeHttpConnectionListener(const std::string& pipeName,
                                            const std::string& secret)
      : pipeName_(pipeName), secret_(secret)
   {
   }

   virtual Error start()
   {
      core::thread::safeLaunchThread(
         boost::bind(&NamedPipeHttpConnectionListener::listenerThread,
                     this));

      return Success();
   }

   virtual void stop()
   {
      // we don't support stop because it is never called in desktop mode

   }

   // connection queues
   virtual HttpConnectionQueue& mainConnectionQueue()
   {
      return mainConnectionQueue_;
   }

   virtual HttpConnectionQueue& eventsConnectionQueue()
   {
      return eventsConnectionQueue_;
   }

private:
   void listenerThread()
   {
      try
      {
         while (true)
         {
            // create security attributes
            PSECURITY_ATTRIBUTES pSA = NULL;
            SECURITY_ATTRIBUTES sa;
            ZeroMemory(&sa, sizeof(sa));
            sa.nLength = sizeof(sa);
            sa.lpSecurityDescriptor = NULL;
            sa.bInheritHandle = FALSE;

            // get login session only descriptor -- proceed without one
            // if we fail since we don't have 100% assurance this will
            // work in all configurations and the world ends if we don't
            // proceed with creating the pipe
            sa.lpSecurityDescriptor = pipeServerSecurityDescriptor();
            if (sa.lpSecurityDescriptor)
               pSA = &sa;

            // create pipe
            HANDLE hPipe = ::CreateNamedPipeA(pipeName_.c_str(),
                                              PIPE_ACCESS_DUPLEX,
                                              PIPE_TYPE_BYTE |
                                              PIPE_READMODE_BYTE |
                                              PIPE_WAIT |
                                              PIPE_REJECT_REMOTE_CLIENTS,
                                              PIPE_UNLIMITED_INSTANCES,
                                              kReadBufferSize,
                                              kReadBufferSize,
                                              0,
                                              pSA);
            DWORD lastError = ::GetLastError(); // capture err before LocalFree

            // free security descriptor if we used one
            if (pSA)
               ::LocalFree(pSA->lpSecurityDescriptor);

            // check for error
            if (hPipe == INVALID_HANDLE_VALUE)
            {
               LOG_ERROR(systemError(lastError, ERROR_LOCATION));
               continue;
            }

            // attempt to connect
            BOOL connected = ::ConnectNamedPipe(hPipe, NULL) ?
                   TRUE : (::GetLastError() == ERROR_PIPE_CONNECTED);

            if (connected)
            {
               // create connection
               boost::shared_ptr<NamedPipeHttpConnection> ptrPipeConnection(
                                         new NamedPipeHttpConnection(hPipe));

               // if we can successfully read a request then enque it
               if (ptrPipeConnection->readRequest())
                  enqueConnection(ptrPipeConnection);
            }
            else
            {
               LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));
            }
         }
      }
      CATCH_UNEXPECTED_EXCEPTION
   }

   // NOTE: this logic is duplicated btw here and HttpConnectionListenerImpl

   void enqueConnection(
                  boost::shared_ptr<NamedPipeHttpConnection> ptrConnection)
   {
      // convert to cannonical HttpConnection
      boost::shared_ptr<HttpConnection> ptrHttpConnection =
            boost::shared_static_cast<HttpConnection>(ptrConnection);

      if (!authenticate(ptrHttpConnection))
      {
         core::http::Response response;
         response.setStatusCode(403);
         response.setStatusMessage("Forbidden");
         ptrConnection->sendResponse(response);
         return;
      }

      // check for the special rpc/abort endpoint and abort if requested
      // we do this in the background listener thread so it can always
      // be processed even if the foreground thread is deadlocked or otherwise
      // unresponsive
      if (connection::checkForAbort(
             ptrHttpConnection,
             boost::bind(&NamedPipeHttpConnectionListener::cleanup,
                         this)))
      {
         return;
      }

      // place the connection on the correct queue
      if (connection::isGetEvents(ptrHttpConnection))
         eventsConnectionQueue_.enqueConnection(ptrHttpConnection);
      else
         mainConnectionQueue_.enqueConnection(ptrHttpConnection);
   }

   virtual bool authenticate(boost::shared_ptr<HttpConnection> ptrConnection)
   {
      return connection::authenticate(ptrConnection, secret_);
   }

   core::Error cleanup()
   {
      return Success();
   }

   static LPVOID pipeServerSecurityDescriptor()
   {
      // NOTE: if this doesn't work for whatever reason we could consider
      // falling back to this: "D:(D;;GA;;;AN)(A;;GA;;;AU)"
      // (which would be deny access to anonymous users and grant access
      // to authenticated users)

      std::string securityDescriptor;
      Error error = logonSessionOnlyDescriptor(&securityDescriptor);
      if (error)
      {
         LOG_ERROR(error);
         return NULL;
      }

      ULONG sdSize;
      LPVOID pSA;
      if (::ConvertStringSecurityDescriptorToSecurityDescriptorA(
          securityDescriptor.c_str(),
          SDDL_REVISION_1,
          &pSA,
          &sdSize))
      {
         return pSA;
      }
      else
      {
         LOG_ERROR(systemError(::GetLastError(), ERROR_LOCATION));
         return NULL;
      }
   }

   static core::Error logonSessionOnlyDescriptor(std::string* pDescriptor)
   {
      // token for current process
      HANDLE hToken = NULL;
      if (!OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken))
         return systemError(::GetLastError(), ERROR_LOCATION);
      core::system::CloseHandleOnExitScope tokenScope(&hToken, ERROR_LOCATION);

      // size of token groups structure (note that we exepct the error
      // since we pass NULL for the token information buffer)
      DWORD tgSize = 0;
      BOOL res = ::GetTokenInformation(hToken, TokenGroups, NULL, 0, &tgSize);
      if (res != FALSE && ::GetLastError() != ERROR_INSUFFICIENT_BUFFER)
         return systemError(::GetLastError(), ERROR_LOCATION);

      // get the token groups structure
      std::vector<char> tg(tgSize);
      TOKEN_GROUPS* pTG = reinterpret_cast<TOKEN_GROUPS*>(&tg[0]);
      if (!::GetTokenInformation(hToken, TokenGroups, pTG, tgSize, &tgSize))
         return systemError(::GetLastError(), ERROR_LOCATION);

      // find login sid
      SID* pSid = NULL;
      for (DWORD i = 0; i < pTG->GroupCount ; ++i)
      {
         if ((pTG->Groups[i].Attributes & SE_GROUP_LOGON_ID)
                                                   == SE_GROUP_LOGON_ID)
         {
           pSid = reinterpret_cast<SID*>(pTG->Groups[i].Sid);
           break;
         }
      }

      // ensure we found it
      if (pSid == NULL)
      {
         return systemError(boost::system::windows_error::file_not_found,
                            "Failed to find SE_GROUP_LOGON_ID",
                            ERROR_LOCATION);
      }

      // convert to a string
      char* pSidString = NULL;
      if (!::ConvertSidToStringSid(pSid, &pSidString))
         return systemError(::GetLastError(), ERROR_LOCATION);

      // format string for caller
      boost::format fmt("D:(A;OICI;GA;;;%1%)");
      *pDescriptor = boost::str(fmt % pSidString);

      // free sid string
      ::LocalFree(pSidString);

      // return success
      return Success();
   }

private:
   std::string pipeName_;
   std::string secret_;
   HttpConnectionQueue mainConnectionQueue_;
   HttpConnectionQueue eventsConnectionQueue_;
};

// pointer to global connection listener singleton
HttpConnectionListener* s_pHttpConnectionListener = NULL ;

}  // anonymouys namespace

void initializeHttpConnectionListener()
{
   std::string pipeName = core::system::getenv("RS_LOCAL_PEER");
   std::string secret = session::options().sharedSecret();
   s_pHttpConnectionListener = new NamedPipeHttpConnectionListener(pipeName,
                                                                   secret);
   Error error = s_pHttpConnectionListener->start();
   if (error)
      LOG_ERROR(error);
}

HttpConnectionListener& httpConnectionListener()
{
   return *s_pHttpConnectionListener;
}

} // namespace session
<|endoftext|>


<commit_before><commit_msg>Check for duplicate indices in PBD constraint lists<commit_after><|endoftext|>


<commit_before>#include "../test.h"
#include <QUrl>
#include <QStateMachine>
#include <QCoreApplication>
#include <QtQuick/QQuickView>
#include <QKeyEvent>
#include <thread>
#include <chrono>
#include "../../src/input/scxml_importer.h"

class Test_ScxmlImporter : public ::testing::Test
{
 protected:
  virtual void SetUp()
  {
    int zero = 0;
    application = new QCoreApplication(zero, static_cast<char **>(nullptr));
    eventSender = new QObject();

    invokeManager = std::shared_ptr<InvokeManager>(new InvokeManager());
    ScxmlImporter importer(QUrl::fromLocalFile("../config/simple_state.xml"),
                           eventSender, invokeManager);
    stateMachine = importer.getStateMachine();

    stateMachine->start();

    application->processEvents(QEventLoop::ExcludeSocketNotifiers);
  }

  virtual void TearDown()
  {
    delete eventSender;
    delete application;
  }

  QCoreApplication *application;
  QObject *eventSender;
  ScxmlImporter *importer;
  std::shared_ptr<QStateMachine> stateMachine;
  std::shared_ptr<InvokeManager> invokeManager;
};

TEST_F(Test_ScxmlImporter, SwitchFromInitialToFinalStateWithKeyPress)
{
  EXPECT_TRUE(stateMachine->isRunning());

  auto configuration = stateMachine->configuration();
  EXPECT_EQ(1, configuration.size());
  EXPECT_EQ("idle", (*configuration.begin())->property("name").toString());

  application->sendEvent(
      eventSender, new QKeyEvent(QEvent::KeyPress, Qt::Key_B, Qt::NoModifier));
  application->processEvents();

  configuration = stateMachine->configuration();
  EXPECT_EQ(1, configuration.size());
  EXPECT_EQ("exit", (*configuration.begin())->property("name").toString());

  EXPECT_FALSE(stateMachine->isRunning());
}

class MockHandler : public QObject
{
  Q_OBJECT
 public slots:
  void printCurrentState()
  {
    wasPrintCurrentStateCalled = true;
  }

 public:
  bool wasPrintCurrentStateCalled = false;
};

TEST_F(Test_ScxmlImporter,
       SwitchFromInitialToAnotherStateAndInvokeWithStateChange)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);

  application->sendEvent(
      eventSender, new QKeyEvent(QEvent::KeyPress, Qt::Key_V, Qt::NoModifier));
  application->processEvents();

  auto configuration = stateMachine->configuration();
  EXPECT_EQ(1, configuration.size());
  EXPECT_EQ("base", (*configuration.begin())->property("name").toString());

  application->sendEvent(
      eventSender, new QKeyEvent(QEvent::KeyPress, Qt::Key_Y, Qt::NoModifier));
  application->processEvents();

  configuration = stateMachine->configuration();
  EXPECT_EQ(1, configuration.size());
  EXPECT_EQ("idle", (*configuration.begin())->property("name").toString());

  EXPECT_TRUE(handler.wasPrintCurrentStateCalled);
}

TEST_F(Test_ScxmlImporter,
       SwitchFromInitialToAnotherStateAndInvokeWithoutStateChange)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);

  application->sendEvent(
      eventSender, new QKeyEvent(QEvent::KeyPress, Qt::Key_V, Qt::NoModifier));
  application->processEvents();

  auto configuration = stateMachine->configuration();
  EXPECT_EQ(1, configuration.size());
  EXPECT_EQ("base", (*configuration.begin())->property("name").toString());

  application->sendEvent(
      eventSender, new QKeyEvent(QEvent::KeyPress, Qt::Key_N, Qt::NoModifier));
  application->processEvents();

  configuration = stateMachine->configuration();
  EXPECT_EQ(1, configuration.size());
  EXPECT_EQ("base", (*configuration.begin())->property("name").toString());

  EXPECT_TRUE(handler.wasPrintCurrentStateCalled);
}
#include "test_scxml_importer.moc"
<commit_msg>Extract expect function to make test more DRY.<commit_after>#include "../test.h"
#include <QUrl>
#include <QStateMachine>
#include <QCoreApplication>
#include <QtQuick/QQuickView>
#include <QKeyEvent>
#include <thread>
#include <chrono>
#include "../../src/input/scxml_importer.h"

class Test_ScxmlImporter : public ::testing::Test
{
 protected:
  virtual void SetUp()
  {
    int zero = 0;
    application = new QCoreApplication(zero, static_cast<char **>(nullptr));
    eventSender = new QObject();

    invokeManager = std::shared_ptr<InvokeManager>(new InvokeManager());
    ScxmlImporter importer(QUrl::fromLocalFile("../config/simple_state.xml"),
                           eventSender, invokeManager);
    stateMachine = importer.getStateMachine();

    stateMachine->start();

    application->processEvents(QEventLoop::ExcludeSocketNotifiers);
  }

  virtual void TearDown()
  {
    delete eventSender;
    delete application;
  }

  inline void expectSingleStateWithName(QString name)
  {
    auto configuration = stateMachine->configuration();
    EXPECT_EQ(1, configuration.size());
    EXPECT_EQ(name, (*configuration.begin())->property("name").toString());
  }

  QCoreApplication *application;
  QObject *eventSender;
  ScxmlImporter *importer;
  std::shared_ptr<QStateMachine> stateMachine;
  std::shared_ptr<InvokeManager> invokeManager;
};

TEST_F(Test_ScxmlImporter, SwitchFromInitialToFinalStateWithKeyPress)
{
  EXPECT_TRUE(stateMachine->isRunning());

  expectSingleStateWithName("idle");

  application->sendEvent(
      eventSender, new QKeyEvent(QEvent::KeyPress, Qt::Key_B, Qt::NoModifier));
  application->processEvents();

  expectSingleStateWithName("exit");

  EXPECT_FALSE(stateMachine->isRunning());
}

class MockHandler : public QObject
{
  Q_OBJECT
 public slots:
  void printCurrentState()
  {
    wasPrintCurrentStateCalled = true;
  }

 public:
  bool wasPrintCurrentStateCalled = false;
};

TEST_F(Test_ScxmlImporter,
       SwitchFromInitialToAnotherStateAndInvokeWithStateChange)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);

  application->sendEvent(
      eventSender, new QKeyEvent(QEvent::KeyPress, Qt::Key_V, Qt::NoModifier));
  application->processEvents();

  expectSingleStateWithName("base");

  application->sendEvent(
      eventSender, new QKeyEvent(QEvent::KeyPress, Qt::Key_Y, Qt::NoModifier));
  application->processEvents();

  expectSingleStateWithName("idle");

  EXPECT_TRUE(handler.wasPrintCurrentStateCalled);
}

TEST_F(Test_ScxmlImporter,
       SwitchFromInitialToAnotherStateAndInvokeWithoutStateChange)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);

  application->sendEvent(
      eventSender, new QKeyEvent(QEvent::KeyPress, Qt::Key_V, Qt::NoModifier));
  application->processEvents();

  expectSingleStateWithName("base");

  application->sendEvent(
      eventSender, new QKeyEvent(QEvent::KeyPress, Qt::Key_N, Qt::NoModifier));
  application->processEvents();

  expectSingleStateWithName("base");

  EXPECT_TRUE(handler.wasPrintCurrentStateCalled);
}

#include "test_scxml_importer.moc"
<|endoftext|>


<commit_before>// Copyright 2011 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#if defined(_MSC_VER)
#include <io.h>
#define access _access
#define F_OK 0
#else
#include <unistd.h>
#endif

#include <string>

#include "base/file.h"
#include "base/logging.h"
#include "base/scoped_ptr.h"

namespace operations_research {

File::File(FILE* const f_des, const std::string& name)
  : f_(f_des), name_(name) {}

bool File::Delete(char* const name) {
  return remove(name) == 0;
}

bool File::Exists(char* const name) {
  return access(name, F_OK) == 0;
}

size_t File::Size() {
  struct stat f_stat;
  stat(name_.c_str(), &f_stat);
  return f_stat.st_size;
}

bool File::Flush() {
  return fflush(f_) == 0;
}

bool File::Close() {
  if (fclose(f_) == 0) {
    f_ = NULL;
    return true;
  } else {
    return false;
  }
}

void File::ReadOrDie(void* const buf, size_t size) {
  CHECK_EQ(fread(buf, 1, size, f_), size);
}

size_t File::Read(void* const buf, size_t size) {
  return fread(buf, 1, size, f_);
}

void File::WriteOrDie(const void* const buf, size_t size) {
  CHECK_EQ(fwrite(buf, 1, size, f_), size);
}
size_t File::Write(const void* const buf, size_t size) {
  return fwrite(buf, 1, size, f_);
}

File* File::OpenOrDie(const char* const name, const char* const flag) {
  FILE* const f_des = fopen(name, flag);
  if (f_des == NULL) {
    std::cerr << "Cannot open " << name;
    exit(1);
  }
  File* const f = new File(f_des, name);
  return f;
}

File* File::Open(const char* const name, const char* const flag) {
  FILE* const f_des = fopen(name, flag);
  if (f_des == NULL) return NULL;
  File* const f = new File(f_des, name);
  return f;
}

char* File::ReadLine(char* const output, uint64 max_length) {
  return fgets(output, max_length, f_);
}

int64 File::ReadToString(std::string* const output, uint64 max_length) {
  CHECK_NOTNULL(output);
  output->clear();

  if (max_length == 0) return 0;
  if (max_length < 0) return -1;

  int64 needed = max_length;
  int bufsize = (needed < (2 << 20) ? needed : (2 << 20));

  scoped_array<char> buf(new char[bufsize]);

  int64 nread = 0;
  while (needed > 0) {
    nread = Read(buf.get(), (bufsize < needed ? bufsize : needed));
    if (nread > 0) {
      output->append(buf.get(), nread);
      needed -= nread;
    } else {
      break;
    }
  }
  return (nread >= 0 ? static_cast<int64>(output->size()) : -1);
}

size_t File::WriteString(const std::string& line) {
  return Write(line.c_str(), line.size());
}

bool File::WriteLine(const std::string& line) {
  if (Write(line.c_str(), line.size()) != line.size()) return false;
  return Write("\n", 1) == 1;
}

std::string File::CreateFileName() const {
  return name_;
}

bool File::Open() const {
  return f_ != NULL;
}

void File::Init() {}
}  // namespace operations_research
<commit_msg>Remove CR/LF when reading a line<commit_after>// Copyright 2011 Google
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#if defined(_MSC_VER)
#include <io.h>
#define access _access
#define F_OK 0
#else
#include <unistd.h>
#endif

#include <string>

#include "base/file.h"
#include "base/logging.h"
#include "base/scoped_ptr.h"

namespace operations_research {

File::File(FILE* const f_des, const std::string& name)
  : f_(f_des), name_(name) {}

bool File::Delete(char* const name) {
  return remove(name) == 0;
}

bool File::Exists(char* const name) {
  return access(name, F_OK) == 0;
}

size_t File::Size() {
  struct stat f_stat;
  stat(name_.c_str(), &f_stat);
  return f_stat.st_size;
}

bool File::Flush() {
  return fflush(f_) == 0;
}

bool File::Close() {
  if (fclose(f_) == 0) {
    f_ = NULL;
    return true;
  } else {
    return false;
  }
}

void File::ReadOrDie(void* const buf, size_t size) {
  CHECK_EQ(fread(buf, 1, size, f_), size);
}

size_t File::Read(void* const buf, size_t size) {
  return fread(buf, 1, size, f_);
}

void File::WriteOrDie(const void* const buf, size_t size) {
  CHECK_EQ(fwrite(buf, 1, size, f_), size);
}
size_t File::Write(const void* const buf, size_t size) {
  return fwrite(buf, 1, size, f_);
}

File* File::OpenOrDie(const char* const name, const char* const flag) {
  FILE* const f_des = fopen(name, flag);
  if (f_des == NULL) {
    std::cerr << "Cannot open " << name;
    exit(1);
  }
  File* const f = new File(f_des, name);
  return f;
}

File* File::Open(const char* const name, const char* const flag) {
  FILE* const f_des = fopen(name, flag);
  if (f_des == NULL) return NULL;
  File* const f = new File(f_des, name);
  return f;
}

char* File::ReadLine(char* const output, uint64 max_length) {
  char* const result = fgets(output, max_length, f_);
  // Chop the last linefeed if there.
  int len = strlen(result);
  if (result[len - 1] = 10) {  // Linefeed char.
    result[--len] = '\0';
  }
  if (result[len - 1] = 13) {  // Carriage return.
    result[--len] = '\0';
  }
}

int64 File::ReadToString(std::string* const output, uint64 max_length) {
  CHECK_NOTNULL(output);
  output->clear();

  if (max_length == 0) return 0;
  if (max_length < 0) return -1;

  int64 needed = max_length;
  int bufsize = (needed < (2 << 20) ? needed : (2 << 20));

  scoped_array<char> buf(new char[bufsize]);

  int64 nread = 0;
  while (needed > 0) {
    nread = Read(buf.get(), (bufsize < needed ? bufsize : needed));
    if (nread > 0) {
      output->append(buf.get(), nread);
      needed -= nread;
    } else {
      break;
    }
  }
  return (nread >= 0 ? static_cast<int64>(output->size()) : -1);
}

size_t File::WriteString(const std::string& line) {
  return Write(line.c_str(), line.size());
}

bool File::WriteLine(const std::string& line) {
  if (Write(line.c_str(), line.size()) != line.size()) return false;
  return Write("\n", 1) == 1;
}

std::string File::CreateFileName() const {
  return name_;
}

bool File::Open() const {
  return f_ != NULL;
}

void File::Init() {}
}  // namespace operations_research
<|endoftext|>


<commit_before>#include "Sweeping.h"
#include "../Util.h"
#include "SegmentIntersection.h"
#include <algorithm>
#include <map>
#include <utility>

// key为端点y坐标 value为端点
typedef std::multimap<double, Node, std::less<double>> T;
typedef std::multimap<double, Node, std::less<double>>::iterator TIter;
#define TKey(iter) ((iter)->first)
#define TValue(iter) ((iter)->second)

int is_left_node[MAX];

//端点排序
static bool Compare(const Node &a, const Node &b) {
  // x坐标不同 x坐标较小的优先
  if (!FloatEq(a.x, b.x))
    return a.x < b.x;

  // 若两点一个是左端点一个是右端点 左端点优先
  if (is_left_node[a.index] != is_left_node[b.index])
    return (is_left_node[a.index]) ? true : false;

  // y坐标不同 y坐标较小的优先
  if (!FloatEq(a.y, b.y))
    return a.y < b.y;

  return false;
}

static TIter Find(T &t, const Node &p) {
  std::pair<TIter, TIter> range = t.equal_range(p.y);
  for (TIter i = range.first; i != range.second; i++) {
    if (NodeEq(TValue(i), p)) {
      return i;
    }
  }
  return t.end();
}

static void Insert(T &t, const Node &p) { t.insert(std::make_pair(p.y, p)); }

static void Erase(T &t, const Node &p) {
  TIter i = Find(t, p);
  if (i != t.end()) {
    t.erase(i);
  }
}

static TIter Above(T &t, const Node &p) {
  TIter i = Find(t, p);
  if (i == t.end()) {
    return t.end();
  }
  if (i == t.begin()) {
    return t.end();
  }
  return --i;
}

static TIter Below(T &t, const Node &p) {
  TIter i = Find(t, p);
  if (i == t.end()) {
    return t.end();
  }
  if (i == t.begin()) {
    return t.end();
  }
  return ++i;
}

bool Sweeping(Segment *l, int n) {
  // 键值double指代点的y坐标
  // 映射Node指代y坐标所在点
  T t;
  std::memset(is_left_node, 0, MAX * sizeof(int));

  // s是线段集l的端点集
  Node s[MAX];
  for (int i = 0; i < n; i++) {
    s[l[i].left.index] = l[i].left;
    s[l[i].right.index] = l[i].right;
    // 标记某点为左端点/右端点
    is_left_node[l[i].left.index] = 1;
    is_left_node[l[i].right.index] = 0;
  }
  std::sort(s, s + 2 * n, Compare);

  // 从左至右
  for (int i = 0; i < 2 * n; i++) {
    Node p = s[i];
    // 若p为左端点
    if (is_left_node[p.index]) {
      Insert(t, p);
      TIter above_p = Above(t, p);
      TIter below_p = Below(t, p);

      if (above_p != t.end() &&
          SegmentIntersection(l[TValue(above_p).index], l[p.index]))
        return true;

      if (below_p != t.end() &&
          SegmentIntersection(l[TValue(below_p).index], l[p.index]))
        return true;

    } else {
      // 若p为右端点
      TIter above_p = Above(t, p);
      TIter below_p = Below(t, p);

      if (above_p != t.end() && below_p != t.end() &&
          SegmentIntersection(l[TValue(above_p).index],
                              l[TValue(below_p).index]))
        return true;

      Erase(t, p);
    }
  }
  return false;
}

<commit_msg>fix compiling bug<commit_after>#include "Sweeping.h"
#include "../Util.h"
#include "SegmentIntersection.h"
#include <algorithm>
#include <cstring>
#include <map>
#include <utility>

// key为端点y坐标 value为端点
typedef std::multimap<double, Node, std::less<double>> T;
typedef std::multimap<double, Node, std::less<double>>::iterator TIter;
#define TKey(iter) ((iter)->first)
#define TValue(iter) ((iter)->second)

int is_left_node[MAX];

//端点排序
static bool Compare(const Node &a, const Node &b) {
  // x坐标不同 x坐标较小的优先
  if (!FloatEq(a.x, b.x))
    return a.x < b.x;

  // 若两点一个是左端点一个是右端点 左端点优先
  if (is_left_node[a.index] != is_left_node[b.index])
    return (is_left_node[a.index]) ? true : false;

  // y坐标不同 y坐标较小的优先
  if (!FloatEq(a.y, b.y))
    return a.y < b.y;

  return false;
}

static TIter Find(T &t, const Node &p) {
  std::pair<TIter, TIter> range = t.equal_range(p.y);
  for (TIter i = range.first; i != range.second; i++) {
    if (NodeEq(TValue(i), p)) {
      return i;
    }
  }
  return t.end();
}

static void Insert(T &t, const Node &p) { t.insert(std::make_pair(p.y, p)); }

static void Erase(T &t, const Node &p) {
  TIter i = Find(t, p);
  if (i != t.end()) {
    t.erase(i);
  }
}

static TIter Above(T &t, const Node &p) {
  TIter i = Find(t, p);
  if (i == t.end()) {
    return t.end();
  }
  if (i == t.begin()) {
    return t.end();
  }
  return --i;
}

static TIter Below(T &t, const Node &p) {
  TIter i = Find(t, p);
  if (i == t.end()) {
    return t.end();
  }
  if (i == t.begin()) {
    return t.end();
  }
  return ++i;
}

bool Sweeping(Segment *l, int n) {
  // 键值double指代点的y坐标
  // 映射Node指代y坐标所在点
  T t;
  std::memset(is_left_node, 0, MAX * sizeof(int));

  // s是线段集l的端点集
  Node s[MAX];
  for (int i = 0; i < n; i++) {
    s[l[i].left.index] = l[i].left;
    s[l[i].right.index] = l[i].right;
    // 标记某点为左端点/右端点
    is_left_node[l[i].left.index] = 1;
    is_left_node[l[i].right.index] = 0;
  }
  std::sort(s, s + 2 * n, Compare);

  // 从左至右
  for (int i = 0; i < 2 * n; i++) {
    Node p = s[i];
    // 若p为左端点
    if (is_left_node[p.index]) {
      Insert(t, p);
      TIter above_p = Above(t, p);
      TIter below_p = Below(t, p);

      if (above_p != t.end() &&
          SegmentIntersection(l[TValue(above_p).index], l[p.index]))
        return true;

      if (below_p != t.end() &&
          SegmentIntersection(l[TValue(below_p).index], l[p.index]))
        return true;

    } else {
      // 若p为右端点
      TIter above_p = Above(t, p);
      TIter below_p = Below(t, p);

      if (above_p != t.end() && below_p != t.end() &&
          SegmentIntersection(l[TValue(above_p).index],
                              l[TValue(below_p).index]))
        return true;

      Erase(t, p);
    }
  }
  return false;
}
<|endoftext|>


<commit_before>// Copyright 2014-present Bill Fisher. All rights reserved.

#include "ofp/unittest.h"
#include "ofp/ipv4address.h"

using namespace ofp;

TEST(ipv4address, test) {
  IPv4Address addr{"192.168.1.1"};

  EXPECT_TRUE(addr.valid());
  EXPECT_EQ("192.168.1.1", addr.toString());
}

TEST(ipv4address, mask) {
  IPv4Address a = IPv4Address::mask(0);
  EXPECT_EQ("0.0.0.0", a.toString());

  IPv4Address b = IPv4Address::mask(1);
  EXPECT_EQ("128.0.0.0", b.toString());

  IPv4Address c = IPv4Address::mask(7);
  EXPECT_EQ("254.0.0.0", c.toString());

  IPv4Address d = IPv4Address::mask(8);
  EXPECT_EQ("255.0.0.0", d.toString());

  IPv4Address e = IPv4Address::mask(9);
  EXPECT_EQ("255.128.0.0", e.toString());

  IPv4Address f = IPv4Address::mask(23);
  EXPECT_EQ("255.255.254.0", f.toString());

  IPv4Address g = IPv4Address::mask(24);
  EXPECT_EQ("255.255.255.0", g.toString());

  IPv4Address h = IPv4Address::mask(25);
  EXPECT_EQ("255.255.255.128", h.toString());

  IPv4Address i = IPv4Address::mask(31);
  EXPECT_EQ("255.255.255.254", i.toString());

  IPv4Address j = IPv4Address::mask(32);
  EXPECT_EQ("255.255.255.255", j.toString());

  EXPECT_FALSE(i.isBroadcast());
  EXPECT_TRUE(j.isBroadcast());
}

TEST(ipv4address, prefix) {
  for (unsigned i = 0; i < 33; ++i) {
    IPv4Address addr = IPv4Address::mask(i);
    EXPECT_EQ(i, addr.prefix());
  }
}

TEST(ipv4address, valid) {
  IPv4Address addr;
  EXPECT_FALSE(addr.valid());
}

TEST(ipv4address, misaligned) {
  // IPv4Address should be "packed" when placed in a struct.

  struct TestBuf {
    UInt8 ignore;
    IPv4Address addr;
  };

  static_assert(sizeof(TestBuf) == 5, "Unexpected size");
  static_assert(offsetof(TestBuf, addr) == 1, "Unexpected offset");

  TestBuf buf;
  EXPECT_FALSE(buf.addr.valid());
  EXPECT_TRUE(buf.addr.parse("251.252.253.254"));
  EXPECT_TRUE(buf.addr.valid());
  EXPECT_EQ("251.252.253.254", buf.addr.toString());
}
<commit_msg>Add test that IPv4Address is initialized to zero.<commit_after>// Copyright 2014-present Bill Fisher. All rights reserved.

#include "ofp/unittest.h"
#include "ofp/ipv4address.h"

using namespace ofp;

TEST(ipv4address, test) {
  IPv4Address addr{"192.168.1.1"};

  EXPECT_TRUE(addr.valid());
  EXPECT_EQ("192.168.1.1", addr.toString());
}

TEST(ipv4address, mask) {
  IPv4Address a = IPv4Address::mask(0);
  EXPECT_EQ("0.0.0.0", a.toString());

  IPv4Address b = IPv4Address::mask(1);
  EXPECT_EQ("128.0.0.0", b.toString());

  IPv4Address c = IPv4Address::mask(7);
  EXPECT_EQ("254.0.0.0", c.toString());

  IPv4Address d = IPv4Address::mask(8);
  EXPECT_EQ("255.0.0.0", d.toString());

  IPv4Address e = IPv4Address::mask(9);
  EXPECT_EQ("255.128.0.0", e.toString());

  IPv4Address f = IPv4Address::mask(23);
  EXPECT_EQ("255.255.254.0", f.toString());

  IPv4Address g = IPv4Address::mask(24);
  EXPECT_EQ("255.255.255.0", g.toString());

  IPv4Address h = IPv4Address::mask(25);
  EXPECT_EQ("255.255.255.128", h.toString());

  IPv4Address i = IPv4Address::mask(31);
  EXPECT_EQ("255.255.255.254", i.toString());

  IPv4Address j = IPv4Address::mask(32);
  EXPECT_EQ("255.255.255.255", j.toString());

  EXPECT_FALSE(i.isBroadcast());
  EXPECT_TRUE(j.isBroadcast());
}

TEST(ipv4address, prefix) {
  for (unsigned i = 0; i < 33; ++i) {
    IPv4Address addr = IPv4Address::mask(i);
    EXPECT_EQ(i, addr.prefix());
  }
}

TEST(ipv4address, valid) {
  IPv4Address addr;
  EXPECT_FALSE(addr.valid());

  std::array<UInt8, 4> buf = {{ 1, 2, 3, 4 }};

  IPv4Address *x = new (&buf) IPv4Address;
  EXPECT_FALSE(x->valid());
}

TEST(ipv4address, misaligned) {
  // IPv4Address should be "packed" when placed in a struct.

  struct TestBuf {
    UInt8 ignore;
    IPv4Address addr;
  };

  static_assert(sizeof(TestBuf) == 5, "Unexpected size");
  static_assert(offsetof(TestBuf, addr) == 1, "Unexpected offset");

  TestBuf buf;
  EXPECT_FALSE(buf.addr.valid());
  EXPECT_TRUE(buf.addr.parse("251.252.253.254"));
  EXPECT_TRUE(buf.addr.valid());
  EXPECT_EQ("251.252.253.254", buf.addr.toString());
}
<|endoftext|>


<commit_before>#include "UIList.h"

#include <easycomplex.h>

namespace eui
{
namespace list
{

UIList::UIList()
{
	m_clipbox.xMin = m_clipbox.yMin = -200;
	m_clipbox.xMax = m_clipbox.yMax =  200;

	m_vertical = m_horizontal = false;
	m_hori_count = m_vert_count = 0;
	m_hori_space = m_vert_space = 0;
}

void UIList::StoreToFile(const char* filename) const
{
	std::string name = filename;
	name = name.substr(0, name.find_last_of('_'));

	std::string dir = d2d::FilenameTools::getFileDir(filename);

	// items complex
	ecomplex::Symbol items_complex;
	m_items.Traverse(d2d::FetchAllVisitor<d2d::ISprite>(items_complex.m_sprites), d2d::DT_ALL);
	std::sort(items_complex.m_sprites.begin(), items_complex.m_sprites.end(), ItemsCmp());
	for (int i = 0, n = items_complex.m_sprites.size(); i < n; ++i) {
		d2d::ISprite* spr = items_complex.m_sprites[i];
		spr->name = "item" + d2d::StringTools::ToString(i+1);
		spr->Retain();
	}
	std::string items_path = name + "_items_complex.json";
	items_complex.SetFilepath(items_path);
	ecomplex::FileStorer::Store(items_path.c_str(), &items_complex);

	// wrapper complex
	ecomplex::Sprite items_sprite(&items_complex);
	items_sprite.name = "list";
	ecomplex::Symbol wrapper_complex;
	wrapper_complex.m_clipbox = m_clipbox;
	wrapper_complex.m_sprites.push_back(&items_sprite);
	items_sprite.Retain();
	std::string top_path = name + "_wrapper_complex.json";
	wrapper_complex.SetFilepath(top_path);
	ecomplex::FileStorer::Store(top_path.c_str(), &wrapper_complex);

	// ui
	std::string ui_path = filename;
	Json::Value value;
	value["items filepath"] = d2d::FilenameTools::getRelativePath(dir, items_path).ToStdString();
	value["wrapper filepath"] = d2d::FilenameTools::getRelativePath(dir, top_path).ToStdString();
	value["type"] = "list";
	value["horizontal"] = m_horizontal;
	value["vertical"] = m_vertical;
	value["clipbox"]["w"] = m_clipbox.xLength();
	value["clipbox"]["h"] = m_clipbox.yLength();
	value["clipbox"]["x"] = m_clipbox.xMin;
	value["clipbox"]["y"] = m_clipbox.yMax;
	value["hori space"] = m_hori_space;
	value["vert space"] = m_vert_space;
	value["hori count"] = m_hori_count;
	value["vert count"] = m_vert_count;
	Json::StyledStreamWriter writer;
	std::locale::global(std::locale(""));
	std::ofstream fout(ui_path.c_str());
	std::locale::global(std::locale("C"));
	writer.write(fout, value);
	fout.close();
}

void UIList::LoadFromFile(const char* filename)
{
	Json::Value value;
	Json::Reader reader;
	std::locale::global(std::locale(""));
	std::ifstream fin(filename);
	std::locale::global(std::locale("C"));
	reader.parse(fin, value);
	fin.close();

	std::string type = value["type"].asString();
	if (type != "list") {
		return;
	}

	const Json::Value& cb_val = value["clipbox"];
	m_clipbox.xMin = cb_val["x"].asDouble();
	m_clipbox.yMax = cb_val["y"].asDouble();
	m_clipbox.xMax = m_clipbox.xMin + cb_val["w"].asDouble();
	m_clipbox.yMin = m_clipbox.yMax - cb_val["h"].asDouble();

	m_horizontal = value["horizontal"].asBool();
	m_vertical = value["vertical"].asBool();

	m_hori_count = value["hori count"].asInt();
	m_vert_count = value["vert count"].asInt();

	m_hori_space = value["hori space"].asDouble();
	m_vert_space = value["vert space"].asDouble();

	std::string items_filepath = value["items filepath"].asString();
	items_filepath = d2d::FilenameTools::getAbsolutePathFromFile(filename, items_filepath);
	ecomplex::Symbol items_complex;
	items_complex.LoadFromFile(items_filepath);
	for (int i = 0, n = items_complex.m_sprites.size(); i < n; ++i) {
		d2d::ISprite* spr = items_complex.m_sprites[i];
		spr->Retain();
		m_items.Insert(spr);
	}
}

bool UIList::Filling()
{
	// not support hori & vert filling
	if (m_items.Size() != 2) {
		return false;
	}

	std::vector<d2d::ISprite*> ori_sprites;
	m_items.Traverse(d2d::FetchAllVisitor<d2d::ISprite>(ori_sprites), d2d::DT_ALL, true);

	assert(ori_sprites.size() == 2);
	if (&ori_sprites[0]->GetSymbol() != &ori_sprites[1]->GetSymbol()) {
		return false;
	}
	if (ori_sprites[0]->GetPosition().x != ori_sprites[1]->GetPosition().x &&
		ori_sprites[0]->GetPosition().y != ori_sprites[1]->GetPosition().y) {
		return false;
	}
	if (ori_sprites[0]->GetPosition().x == ori_sprites[1]->GetPosition().x &&
		ori_sprites[0]->GetPosition().y == ori_sprites[1]->GetPosition().y) {
		return false;
	}

	std::vector<d2d::ISprite*> new_sprites;
	if (ori_sprites[0]->GetPosition().x == ori_sprites[1]->GetPosition().x) {
		VertFilling(ori_sprites, new_sprites);
	} else if (ori_sprites[0]->GetPosition().y == ori_sprites[1]->GetPosition().y) {
		HoriFilling(ori_sprites, new_sprites);
	} else {
		return false;
	}

	for (int i = 0; i < new_sprites.size(); ++i) {
		m_items.Insert(new_sprites[i]);
	}

	return !new_sprites.empty();
}

void UIList::HoriFilling(const std::vector<d2d::ISprite*>& ori_sprites,
						 std::vector<d2d::ISprite*>& new_sprites)
{
	m_horizontal = true;
	m_vertical = false;
	m_hori_space = fabs(ori_sprites[0]->GetPosition().x - ori_sprites[1]->GetPosition().x);
	m_vert_space = 1;

	float y = ori_sprites[0]->GetPosition().y;
	float x_min, x_max;
	if (ori_sprites[1]->GetPosition().x > ori_sprites[0]->GetPosition().x) {
		x_min = ori_sprites[0]->GetPosition().x;
		x_max = ori_sprites[1]->GetPosition().x;
	} else {
		x_min = ori_sprites[1]->GetPosition().x;
		x_max = ori_sprites[0]->GetPosition().x;
	}

	float dx = x_max - x_min;

	float _x = x_min - dx, _y = y;
	while (true) {
		d2d::ISprite* spr = ori_sprites[0]->Clone();
		spr->SetTransform(d2d::Vector(_x, _y), ori_sprites[0]->GetAngle());
		if (d2d::Math::isRectContainRect(m_clipbox, spr->GetRect())) {
			new_sprites.push_back(spr);
			_x -= dx;
		} else {
			delete spr;
			break;
		}
	}

	_x = x_max + dx;
	while (true) {
		d2d::ISprite* spr = ori_sprites[0]->Clone();
		spr->SetTransform(d2d::Vector(_x, _y), ori_sprites[0]->GetAngle());
		if (d2d::Math::isRectContainRect(m_clipbox, spr->GetRect())) {
			new_sprites.push_back(spr);
			_x += dx;
		} else {
			// add two more
			new_sprites.push_back(spr);
			spr = ori_sprites[0]->Clone();
			spr->SetTransform(d2d::Vector(_x + dx, _y), ori_sprites[0]->GetAngle());
			new_sprites.push_back(spr);
			break;
		}
	}

	m_hori_count = m_items.Size() + new_sprites.size();
	m_vert_count = 0;
}

void UIList::VertFilling(const std::vector<d2d::ISprite*>& ori_sprites, std::vector<d2d::ISprite*>& new_sprites)
{
	m_horizontal = false;
	m_vertical = true;
	m_hori_space = 1;
	m_vert_space = fabs(ori_sprites[0]->GetPosition().y - ori_sprites[1]->GetPosition().y);

	float x = ori_sprites[0]->GetPosition().x;
	float y_min, y_max;
	if (ori_sprites[1]->GetPosition().y > ori_sprites[0]->GetPosition().y) {
		y_min = ori_sprites[0]->GetPosition().y;
		y_max = ori_sprites[1]->GetPosition().y;
	} else {
		y_min = ori_sprites[1]->GetPosition().y;
		y_max = ori_sprites[0]->GetPosition().y;
	}

	float dy = y_max - y_min;

	float _x = x, _y = y_min - dy;
	while (true) {
		d2d::ISprite* spr = ori_sprites[0]->Clone();
		spr->SetTransform(d2d::Vector(_x, _y), ori_sprites[0]->GetAngle());
		if (d2d::Math::isRectContainRect(m_clipbox, spr->GetRect())) {
			new_sprites.push_back(spr);
			_y -= dy;
		} else {
			new_sprites.push_back(spr);
			spr = ori_sprites[0]->Clone();
			spr->SetTransform(d2d::Vector(_x, _y - dy), ori_sprites[0]->GetAngle());
			new_sprites.push_back(spr);
			break;
		}
	}

	_y = y_max + dy;
	while (true) {
		d2d::ISprite* spr = ori_sprites[0]->Clone();
		spr->SetTransform(d2d::Vector(_x, _y), ori_sprites[0]->GetAngle());
		if (d2d::Math::isRectContainRect(m_clipbox, spr->GetRect())) {
			new_sprites.push_back(spr);
			_y += dy;
		} else {
			delete spr;
			break;
		}
	}

	m_hori_count = 0;
	m_vert_count = m_items.Size() + new_sprites.size();
}

bool UIList::ItemsCmp::operator() (const d2d::ISprite* item0, const d2d::ISprite* item1) const
{
	if (item0->GetPosition().x != item1->GetPosition().x) {
		return item0->GetPosition().x < item1->GetPosition().x;
	} else {
		return item0->GetPosition().y >= item1->GetPosition().y;
	}
}

}
}<commit_msg>[FIXED] ui list打包规则<commit_after>#include "UIList.h"

#include <easycomplex.h>

namespace eui
{
namespace list
{

UIList::UIList()
{
	m_clipbox.xMin = m_clipbox.yMin = -200;
	m_clipbox.xMax = m_clipbox.yMax =  200;

	m_vertical = m_horizontal = false;
	m_hori_count = m_vert_count = 0;
	m_hori_space = m_vert_space = 0;
}

void UIList::StoreToFile(const char* filename) const
{
	std::string name = filename;
	name = name.substr(0, name.find_last_of('_'));

	std::string dir = d2d::FilenameTools::getFileDir(filename);

	// items complex
	ecomplex::Symbol items_complex;
	m_items.Traverse(d2d::FetchAllVisitor<d2d::ISprite>(items_complex.m_sprites), d2d::DT_ALL);
	std::sort(items_complex.m_sprites.begin(), items_complex.m_sprites.end(), ItemsCmp());
	for (int i = 0, n = items_complex.m_sprites.size(); i < n; ++i) {
		d2d::ISprite* spr = items_complex.m_sprites[i];
		spr->name = "item" + d2d::StringTools::ToString(i+1);
		spr->Retain();
	}
	std::string items_path = name + "_items_complex.json";
	items_complex.SetFilepath(items_path);
	ecomplex::FileStorer::Store(items_path.c_str(), &items_complex);

	// wrapper complex
	ecomplex::Sprite items_sprite(&items_complex);
	items_sprite.name = "anchor";
	ecomplex::Symbol wrapper_complex;
	wrapper_complex.m_clipbox = m_clipbox;
	wrapper_complex.m_sprites.push_back(&items_sprite);
	items_sprite.Retain();
	std::string top_path = name + "_wrapper_complex.json";
	wrapper_complex.SetFilepath(top_path);
	ecomplex::FileStorer::Store(top_path.c_str(), &wrapper_complex);

	// ui
	std::string ui_path = filename;
	Json::Value value;
	value["items filepath"] = d2d::FilenameTools::getRelativePath(dir, items_path).ToStdString();
	value["wrapper filepath"] = d2d::FilenameTools::getRelativePath(dir, top_path).ToStdString();
	value["type"] = "list";
	value["horizontal"] = m_horizontal;
	value["vertical"] = m_vertical;
	value["clipbox"]["w"] = m_clipbox.xLength();
	value["clipbox"]["h"] = m_clipbox.yLength();
	value["clipbox"]["x"] = m_clipbox.xMin;
	value["clipbox"]["y"] = m_clipbox.yMax;
	value["hori space"] = m_hori_space;
	value["vert space"] = m_vert_space;
	value["hori count"] = m_hori_count;
	value["vert count"] = m_vert_count;
	Json::StyledStreamWriter writer;
	std::locale::global(std::locale(""));
	std::ofstream fout(ui_path.c_str());
	std::locale::global(std::locale("C"));
	writer.write(fout, value);
	fout.close();
}

void UIList::LoadFromFile(const char* filename)
{
	Json::Value value;
	Json::Reader reader;
	std::locale::global(std::locale(""));
	std::ifstream fin(filename);
	std::locale::global(std::locale("C"));
	reader.parse(fin, value);
	fin.close();

	std::string type = value["type"].asString();
	if (type != "list") {
		return;
	}

	const Json::Value& cb_val = value["clipbox"];
	m_clipbox.xMin = cb_val["x"].asDouble();
	m_clipbox.yMax = cb_val["y"].asDouble();
	m_clipbox.xMax = m_clipbox.xMin + cb_val["w"].asDouble();
	m_clipbox.yMin = m_clipbox.yMax - cb_val["h"].asDouble();

	m_horizontal = value["horizontal"].asBool();
	m_vertical = value["vertical"].asBool();

	m_hori_count = value["hori count"].asInt();
	m_vert_count = value["vert count"].asInt();

	m_hori_space = value["hori space"].asDouble();
	m_vert_space = value["vert space"].asDouble();

	std::string items_filepath = value["items filepath"].asString();
	items_filepath = d2d::FilenameTools::getAbsolutePathFromFile(filename, items_filepath);
	ecomplex::Symbol items_complex;
	items_complex.LoadFromFile(items_filepath);
	for (int i = 0, n = items_complex.m_sprites.size(); i < n; ++i) {
		d2d::ISprite* spr = items_complex.m_sprites[i];
		spr->Retain();
		m_items.Insert(spr);
	}
}

bool UIList::Filling()
{
	// not support hori & vert filling
	if (m_items.Size() != 2) {
		return false;
	}

	std::vector<d2d::ISprite*> ori_sprites;
	m_items.Traverse(d2d::FetchAllVisitor<d2d::ISprite>(ori_sprites), d2d::DT_ALL, true);

	assert(ori_sprites.size() == 2);
	if (&ori_sprites[0]->GetSymbol() != &ori_sprites[1]->GetSymbol()) {
		return false;
	}
	if (ori_sprites[0]->GetPosition().x != ori_sprites[1]->GetPosition().x &&
		ori_sprites[0]->GetPosition().y != ori_sprites[1]->GetPosition().y) {
		return false;
	}
	if (ori_sprites[0]->GetPosition().x == ori_sprites[1]->GetPosition().x &&
		ori_sprites[0]->GetPosition().y == ori_sprites[1]->GetPosition().y) {
		return false;
	}

	std::vector<d2d::ISprite*> new_sprites;
	if (ori_sprites[0]->GetPosition().x == ori_sprites[1]->GetPosition().x) {
		VertFilling(ori_sprites, new_sprites);
	} else if (ori_sprites[0]->GetPosition().y == ori_sprites[1]->GetPosition().y) {
		HoriFilling(ori_sprites, new_sprites);
	} else {
		return false;
	}

	for (int i = 0; i < new_sprites.size(); ++i) {
		m_items.Insert(new_sprites[i]);
	}

	return !new_sprites.empty();
}

void UIList::HoriFilling(const std::vector<d2d::ISprite*>& ori_sprites,
						 std::vector<d2d::ISprite*>& new_sprites)
{
	m_horizontal = true;
	m_vertical = false;
	m_hori_space = fabs(ori_sprites[0]->GetPosition().x - ori_sprites[1]->GetPosition().x);
	m_vert_space = 1;

	float y = ori_sprites[0]->GetPosition().y;
	float x_min, x_max;
	if (ori_sprites[1]->GetPosition().x > ori_sprites[0]->GetPosition().x) {
		x_min = ori_sprites[0]->GetPosition().x;
		x_max = ori_sprites[1]->GetPosition().x;
	} else {
		x_min = ori_sprites[1]->GetPosition().x;
		x_max = ori_sprites[0]->GetPosition().x;
	}

	float dx = x_max - x_min;

	float _x = x_min - dx, _y = y;
	while (true) {
		d2d::ISprite* spr = ori_sprites[0]->Clone();
		spr->SetTransform(d2d::Vector(_x, _y), ori_sprites[0]->GetAngle());
		if (d2d::Math::isRectContainRect(m_clipbox, spr->GetRect())) {
			new_sprites.push_back(spr);
			_x -= dx;
		} else {
			delete spr;
			break;
		}
	}

	_x = x_max + dx;
	while (true) {
		d2d::ISprite* spr = ori_sprites[0]->Clone();
		spr->SetTransform(d2d::Vector(_x, _y), ori_sprites[0]->GetAngle());
		if (d2d::Math::isRectContainRect(m_clipbox, spr->GetRect())) {
			new_sprites.push_back(spr);
			_x += dx;
		} else {
			// add two more
			new_sprites.push_back(spr);
			spr = ori_sprites[0]->Clone();
			spr->SetTransform(d2d::Vector(_x + dx, _y), ori_sprites[0]->GetAngle());
			new_sprites.push_back(spr);
			break;
		}
	}

	m_hori_count = m_items.Size() + new_sprites.size();
	m_vert_count = 0;
}

void UIList::VertFilling(const std::vector<d2d::ISprite*>& ori_sprites, std::vector<d2d::ISprite*>& new_sprites)
{
	m_horizontal = false;
	m_vertical = true;
	m_hori_space = 1;
	m_vert_space = fabs(ori_sprites[0]->GetPosition().y - ori_sprites[1]->GetPosition().y);

	float x = ori_sprites[0]->GetPosition().x;
	float y_min, y_max;
	if (ori_sprites[1]->GetPosition().y > ori_sprites[0]->GetPosition().y) {
		y_min = ori_sprites[0]->GetPosition().y;
		y_max = ori_sprites[1]->GetPosition().y;
	} else {
		y_min = ori_sprites[1]->GetPosition().y;
		y_max = ori_sprites[0]->GetPosition().y;
	}

	float dy = y_max - y_min;

	float _x = x, _y = y_min - dy;
	while (true) {
		d2d::ISprite* spr = ori_sprites[0]->Clone();
		spr->SetTransform(d2d::Vector(_x, _y), ori_sprites[0]->GetAngle());
		if (d2d::Math::isRectContainRect(m_clipbox, spr->GetRect())) {
			new_sprites.push_back(spr);
			_y -= dy;
		} else {
			new_sprites.push_back(spr);
			spr = ori_sprites[0]->Clone();
			spr->SetTransform(d2d::Vector(_x, _y - dy), ori_sprites[0]->GetAngle());
			new_sprites.push_back(spr);
			break;
		}
	}

	_y = y_max + dy;
	while (true) {
		d2d::ISprite* spr = ori_sprites[0]->Clone();
		spr->SetTransform(d2d::Vector(_x, _y), ori_sprites[0]->GetAngle());
		if (d2d::Math::isRectContainRect(m_clipbox, spr->GetRect())) {
			new_sprites.push_back(spr);
			_y += dy;
		} else {
			delete spr;
			break;
		}
	}

	m_hori_count = 0;
	m_vert_count = m_items.Size() + new_sprites.size();
}

bool UIList::ItemsCmp::operator() (const d2d::ISprite* item0, const d2d::ISprite* item1) const
{
	if (item0->GetPosition().x != item1->GetPosition().x) {
		return item0->GetPosition().x < item1->GetPosition().x;
	} else {
		return item0->GetPosition().y >= item1->GetPosition().y;
	}
}

}
}<|endoftext|>


<commit_before>/*
    Copyright (c) 2006 Volker Krause <vkrause@kde.org>

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published by
    the Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    This library 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 Library General Public
    License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "akonadislave.h"

#include <akonadi/itemfetchjob.h>
#include <akonadi/itemdeletejob.h>
#include <akonadi/collection.h>
#include <akonadi/collectionfetchjob.h>
#include <akonadi/collectiondeletejob.h>

#include <kapplication.h>
#include <kcmdlineargs.h>
#include <kdebug.h>
#include <klocale.h>

extern "C" { int KDE_EXPORT kdemain(int argc, char **argv); }

int kdemain(int argc, char **argv) {

  KCmdLineArgs::init(argc, argv, "kio_akonadi", 0, KLocalizedString(), 0);

  KCmdLineOptions options;
  options.add("+protocol", ki18n( "Protocol name" ));
  options.add("+pool", ki18n( "Socket name" ));
  options.add("+app", ki18n( "Socket name" ));
  KCmdLineArgs::addCmdLineOptions( options );
  KApplication app( false );

  KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
  AkonadiSlave slave( args->arg(1).toLocal8Bit(), args->arg(2).toLocal8Bit() );
  slave.dispatchLoop();

  return 0;
}

using namespace Akonadi;

AkonadiSlave::AkonadiSlave(const QByteArray & pool_socket, const QByteArray & app_socket) :
    KIO::SlaveBase( "akonadi", pool_socket, app_socket )
{
  kDebug( 7129 ) << "kio_akonadi starting up";
}

AkonadiSlave::~ AkonadiSlave()
{
  kDebug( 7129 ) << "kio_akonadi shutting down";
}

void AkonadiSlave::get(const KUrl & url)
{
  const Item item = Item::fromUrl( url );
  ItemFetchJob *job = new ItemFetchJob( item );
  job->addFetchPart( Item::PartBody );

  if ( !job->exec() ) {
    error( KIO::ERR_INTERNAL, job->errorString() );
    return;
  }

  if ( job->items().count() != 1 ) {
    error( KIO::ERR_DOES_NOT_EXIST, "No such item." );
  } else {
    const Item item = job->items().first();
    QByteArray tmp = item.part( Item::PartBody );
    data( tmp );
    data( QByteArray() );
    finished();
  }

  finished();
}

void AkonadiSlave::stat(const KUrl & url)
{
  kDebug( 7129 ) << url;

  // Stats for a collection
  if ( Collection::fromUrl( url ).isValid() )
  {
      Collection collection = Collection::fromUrl( url );

      if ( collection != Collection::root() ) {
        // Check that the collection exists.
        CollectionFetchJob *job = new CollectionFetchJob( collection, CollectionFetchJob::Local );
        if ( !job->exec() ) {
          error( KIO::ERR_INTERNAL, job->errorString() );
          return;
        }

        if ( job->collections().count() != 1 ) {
          error( KIO::ERR_DOES_NOT_EXIST, "No such item." );
          return;
        }

        collection = job->collections().first();
      }

      KIO::UDSEntry entry;
      entry.insert( KIO::UDSEntry::UDS_NAME, collection.name()  );
      entry.insert( KIO::UDSEntry::UDS_MIME_TYPE, Collection::mimeType() );
      entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR );
      entry.insert( KIO::UDSEntry::UDS_URL, url.url() );
      statEntry( entry );
      finished();
  }
  // Stats for an item
  else if ( Item::fromUrl( url ).isValid() )
  {
    ItemFetchJob *job = new ItemFetchJob( Item::fromUrl( url ) );

    if ( !job->exec() ) {
      error( KIO::ERR_INTERNAL, job->errorString() );
      return;
    }

    if ( job->items().count() != 1 ) {
      error( KIO::ERR_DOES_NOT_EXIST, "No such item." );
      return;
    }

    const Item item = job->items().first();
    KIO::UDSEntry entry;
    entry.insert( KIO::UDSEntry::UDS_NAME, QString::number( item.id() ) );
    entry.insert( KIO::UDSEntry::UDS_MIME_TYPE, item.mimeType() );
    entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG );

    statEntry( entry );
    finished();
  }
}

void AkonadiSlave::del( const KUrl &url, bool isFile )
{
  kDebug( 7129 ) << url;

  if ( !isFile ) // It's a directory
  {
    Collection collection = Collection::fromUrl( url );
    CollectionDeleteJob *job = new CollectionDeleteJob( collection );
    if ( !job->exec() ) {
      error( KIO::ERR_INTERNAL, job->errorString() );
      return;
    }
    finished();
  } else // It's a file
  {
    ItemDeleteJob* job = new ItemDeleteJob( Item::fromUrl( url ) );
    if ( !job->exec() ) {
      error( KIO::ERR_INTERNAL, job->errorString() );
      return;
    }
    finished();
  }
}

void AkonadiSlave::listDir( const KUrl &url )
{
  kDebug( 7129 ) << url;

  if ( !Collection::fromUrl( url ).isValid() )
  {
    error( KIO::ERR_DOES_NOT_EXIST, "No such collection." );
    return;
  }

  // Fetching collections
  Collection collection = Collection::fromUrl( url );
  if ( !collection.isValid() ) {
    error( KIO::ERR_DOES_NOT_EXIST, "No such collection." );
    return;
  }
  CollectionFetchJob *job = new CollectionFetchJob( collection, CollectionFetchJob::Flat );
  if ( !job->exec() ) {
    error( KIO::ERR_CANNOT_ENTER_DIRECTORY, job->errorString() );
    return;
  }

  Collection::List collections = job->collections();

  KIO::UDSEntry entry;
  foreach( Collection col, collections )
  {
    kDebug( 7129 ) <<"Collection (" << col.id() <<"," << col.name() <<")";
    entry.clear();
    entry.insert( KIO::UDSEntry::UDS_NAME, col.name() );
    entry.insert( KIO::UDSEntry::UDS_MIME_TYPE, Collection::mimeType() );
    entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR );
    entry.insert( KIO::UDSEntry::UDS_URL, col.url().url() );
    listEntry( entry, false );
  }

  // Fetching items
  if ( collection != Collection::root() ) {
    ItemFetchJob* fjob = new ItemFetchJob( collection );
    if ( !fjob->exec() ) {
      error( KIO::ERR_INTERNAL, job->errorString() );
      return;
    }
    Item::List items = fjob->items();
    totalSize( collections.count() + items.count() );
    foreach( Item item, items )
    {
      kDebug( 7129 ) <<"Item (" << item.id()  <<")";
      entry.clear();
      entry.insert( KIO::UDSEntry::UDS_NAME, QString::number( item.id() ) );
      entry.insert( KIO::UDSEntry::UDS_MIME_TYPE, item.mimeType() );
      entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG );
      entry.insert( KIO::UDSEntry::UDS_URL, item.url().url() );
      listEntry( entry, false );
    }
  }

  listEntry( entry, true );
  finished();
}

<commit_msg>Adapt to latest kdepimlibs/akonadi<commit_after>/*
    Copyright (c) 2006 Volker Krause <vkrause@kde.org>

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Library General Public License as published by
    the Free Software Foundation; either version 2 of the License, or (at your
    option) any later version.

    This library 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 Library General Public
    License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to the
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
*/

#include "akonadislave.h"

#include <akonadi/itemfetchjob.h>
#include <akonadi/itemdeletejob.h>
#include <akonadi/collection.h>
#include <akonadi/collectionfetchjob.h>
#include <akonadi/collectiondeletejob.h>

#include <kapplication.h>
#include <kcmdlineargs.h>
#include <kdebug.h>
#include <klocale.h>

extern "C" { int KDE_EXPORT kdemain(int argc, char **argv); }

int kdemain(int argc, char **argv) {

  KCmdLineArgs::init(argc, argv, "kio_akonadi", 0, KLocalizedString(), 0);

  KCmdLineOptions options;
  options.add("+protocol", ki18n( "Protocol name" ));
  options.add("+pool", ki18n( "Socket name" ));
  options.add("+app", ki18n( "Socket name" ));
  KCmdLineArgs::addCmdLineOptions( options );
  KApplication app( false );

  KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
  AkonadiSlave slave( args->arg(1).toLocal8Bit(), args->arg(2).toLocal8Bit() );
  slave.dispatchLoop();

  return 0;
}

using namespace Akonadi;

AkonadiSlave::AkonadiSlave(const QByteArray & pool_socket, const QByteArray & app_socket) :
    KIO::SlaveBase( "akonadi", pool_socket, app_socket )
{
  kDebug( 7129 ) << "kio_akonadi starting up";
}

AkonadiSlave::~ AkonadiSlave()
{
  kDebug( 7129 ) << "kio_akonadi shutting down";
}

void AkonadiSlave::get(const KUrl & url)
{
  const Item item = Item::fromUrl( url );
  ItemFetchJob *job = new ItemFetchJob( item );
  job->addFetchPart( Item::PartBody );

  if ( !job->exec() ) {
    error( KIO::ERR_INTERNAL, job->errorString() );
    return;
  }

  if ( job->items().count() != 1 ) {
    error( KIO::ERR_DOES_NOT_EXIST, "No such item." );
  } else {
    const Item item = job->items().first();
    QByteArray tmp = item.part( Item::PartBody );
    data( tmp );
    data( QByteArray() );
    finished();
  }

  finished();
}

void AkonadiSlave::stat(const KUrl & url)
{
  kDebug( 7129 ) << url;

  // Stats for a collection
  if ( Collection::fromUrl( url ).isValid() )
  {
      Collection collection = Collection::fromUrl( url );

      if ( collection != Collection::root() ) {
        // Check that the collection exists.
        CollectionFetchJob *job = new CollectionFetchJob( collection, CollectionFetchJob::Base );
        if ( !job->exec() ) {
          error( KIO::ERR_INTERNAL, job->errorString() );
          return;
        }

        if ( job->collections().count() != 1 ) {
          error( KIO::ERR_DOES_NOT_EXIST, "No such item." );
          return;
        }

        collection = job->collections().first();
      }

      KIO::UDSEntry entry;
      entry.insert( KIO::UDSEntry::UDS_NAME, collection.name()  );
      entry.insert( KIO::UDSEntry::UDS_MIME_TYPE, Collection::mimeType() );
      entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR );
      entry.insert( KIO::UDSEntry::UDS_URL, url.url() );
      statEntry( entry );
      finished();
  }
  // Stats for an item
  else if ( Item::fromUrl( url ).isValid() )
  {
    ItemFetchJob *job = new ItemFetchJob( Item::fromUrl( url ) );

    if ( !job->exec() ) {
      error( KIO::ERR_INTERNAL, job->errorString() );
      return;
    }

    if ( job->items().count() != 1 ) {
      error( KIO::ERR_DOES_NOT_EXIST, "No such item." );
      return;
    }

    const Item item = job->items().first();
    KIO::UDSEntry entry;
    entry.insert( KIO::UDSEntry::UDS_NAME, QString::number( item.id() ) );
    entry.insert( KIO::UDSEntry::UDS_MIME_TYPE, item.mimeType() );
    entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG );

    statEntry( entry );
    finished();
  }
}

void AkonadiSlave::del( const KUrl &url, bool isFile )
{
  kDebug( 7129 ) << url;

  if ( !isFile ) // It's a directory
  {
    Collection collection = Collection::fromUrl( url );
    CollectionDeleteJob *job = new CollectionDeleteJob( collection );
    if ( !job->exec() ) {
      error( KIO::ERR_INTERNAL, job->errorString() );
      return;
    }
    finished();
  } else // It's a file
  {
    ItemDeleteJob* job = new ItemDeleteJob( Item::fromUrl( url ) );
    if ( !job->exec() ) {
      error( KIO::ERR_INTERNAL, job->errorString() );
      return;
    }
    finished();
  }
}

void AkonadiSlave::listDir( const KUrl &url )
{
  kDebug( 7129 ) << url;

  if ( !Collection::fromUrl( url ).isValid() )
  {
    error( KIO::ERR_DOES_NOT_EXIST, "No such collection." );
    return;
  }

  // Fetching collections
  Collection collection = Collection::fromUrl( url );
  if ( !collection.isValid() ) {
    error( KIO::ERR_DOES_NOT_EXIST, "No such collection." );
    return;
  }
  CollectionFetchJob *job = new CollectionFetchJob( collection, CollectionFetchJob::FirstLevel );
  if ( !job->exec() ) {
    error( KIO::ERR_CANNOT_ENTER_DIRECTORY, job->errorString() );
    return;
  }

  Collection::List collections = job->collections();

  KIO::UDSEntry entry;
  foreach( Collection col, collections )
  {
    kDebug( 7129 ) <<"Collection (" << col.id() <<"," << col.name() <<")";
    entry.clear();
    entry.insert( KIO::UDSEntry::UDS_NAME, col.name() );
    entry.insert( KIO::UDSEntry::UDS_MIME_TYPE, Collection::mimeType() );
    entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR );
    entry.insert( KIO::UDSEntry::UDS_URL, col.url().url() );
    listEntry( entry, false );
  }

  // Fetching items
  if ( collection != Collection::root() ) {
    ItemFetchJob* fjob = new ItemFetchJob( collection );
    if ( !fjob->exec() ) {
      error( KIO::ERR_INTERNAL, job->errorString() );
      return;
    }
    Item::List items = fjob->items();
    totalSize( collections.count() + items.count() );
    foreach( Item item, items )
    {
      kDebug( 7129 ) <<"Item (" << item.id()  <<")";
      entry.clear();
      entry.insert( KIO::UDSEntry::UDS_NAME, QString::number( item.id() ) );
      entry.insert( KIO::UDSEntry::UDS_MIME_TYPE, item.mimeType() );
      entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG );
      entry.insert( KIO::UDSEntry::UDS_URL, item.url().url() );
      listEntry( entry, false );
    }
  }

  listEntry( entry, true );
  finished();
}

<|endoftext|>