<commit_before>/* httpmessage.cpp
   Copyright (C) 2003-2005 Tommi Maekitalo

This file is part of tntnet.

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

Tntnet 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 tntnet; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA  02111-1307  USA
*/

#include <tnt/httpmessage.h>
#include <cxxtools/log.h>
#include <list>
#include <sstream>

namespace tnt
{
  size_t HttpMessage::maxRequestSize = 0;

  ////////////////////////////////////////////////////////////////////////
  // HttpMessage
  //
  log_define("tntnet.httpmessage")

  void HttpMessage::clear()
  {
    method.clear();
    url.clear();
    queryString.clear();
    header.clear();
    body.clear();
    majorVersion = 0;
    minorVersion = 0;
    contentSize = 0;
  }

  std::string HttpMessage::getHeader(const std::string& key, const std::string& def) const
  {
    header_type::const_iterator i = header.find(key);
    return i == header.end() ? def : i->second;
  }

  void HttpMessage::setHeader(const std::string& key, const std::string& value)
  {
    log_debug("HttpMessage::setHeader(\"" << key << "\", \"" << value << "\")");
    header.insert(header_type::value_type(key, value));
  }

  std::string HttpMessage::dumpHeader() const
  {
    std::ostringstream h;
    dumpHeader(h);
    return h.str();
  }

  void HttpMessage::dumpHeader(std::ostream& out) const
  {
    for (header_type::const_iterator it = header.begin();
         it != header.end(); ++it)
      out << it->first << ' ' << it->second << '\n';
  }

  std::string HttpMessage::htdate(time_t t)
  {
    struct ::tm tm;
    localtime_r(&t, &tm);
    return htdate(&tm);
  }

  std::string HttpMessage::htdate(struct ::tm* tm)
  {
    const char* wday[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
    const char* monthn[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
                            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
    char buffer[80];

    mktime(tm);
    sprintf(buffer, "%s, %02d %s %d %02d:%02d:%02d GMT",
      wday[tm->tm_wday], tm->tm_mday, monthn[tm->tm_mon], tm->tm_year + 1900,
      tm->tm_hour, tm->tm_min, tm->tm_sec);
    return buffer;
  }

  namespace
  {
    class Pstr
    {
        const char* start;
        const char* end;

      public:
        typedef size_t size_type;

        Pstr(const char* s, const char* e)
          : start(s), end(e)
          { }

        size_type size() const  { return end - start; }

        bool operator== (const Pstr& s) const
        {
          return size() == s.size()
            && std::equal(start, end, s.start);
        }

        bool operator== (const char* str) const
        {
          size_type i;
          for (i = 0; i < size() && str[i] != '\0'; ++i)
            if (start[i] != str[i])
              return false;
          return i == size() && str[i] == '\0';
        }
    };
  }

  bool HttpMessage::checkUrl(const std::string& url)
  {
    typedef std::list<Pstr> tokens_type;

    // teile in Komponenten auf
    enum state_type {
      state_start,
      state_token,
      state_end
    };

    state_type state = state_start;

    tokens_type tokens;
    const char* p = url.data();
    const char* e = p + url.size();
    const char* s = p;
    for (; state != state_end && p != e; ++p)
    {
      switch (state)
      {
        case state_start:
          if (*p != '/')
          {
            s = p;
            state = state_token;
          }
          break;

        case state_token:
          if (*p == '/')
          {
            tokens.push_back(Pstr(s, p));
            state = state_start;
          }
          else if (p == e)
          {
            tokens.push_back(Pstr(s, p));
            state = state_end;
          }
          break;

        case state_end:
          break;
      }
    }

    // entferne jedes .. inklusive der vorhergehenden Komponente
    tokens_type::iterator i = tokens.begin();
    while (i != tokens.end())
    {
      if (*i == "..")
      {
        if (i == tokens.begin())
          return false;
        --i;
        i = tokens.erase(i);  // lösche 2 Komponenten
        i = tokens.erase(i);
      }
      else
      {
        ++i;
      }
    }

    return true;
  }

}
<commit_msg>bugfix - check for /./../<commit_after>/* httpmessage.cpp
   Copyright (C) 2003-2005 Tommi Maekitalo

This file is part of tntnet.

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

Tntnet 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 tntnet; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA  02111-1307  USA
*/

#include <tnt/httpmessage.h>
#include <cxxtools/log.h>
#include <list>
#include <sstream>

namespace tnt
{
  size_t HttpMessage::maxRequestSize = 0;

  ////////////////////////////////////////////////////////////////////////
  // HttpMessage
  //
  log_define("tntnet.httpmessage")

  void HttpMessage::clear()
  {
    method.clear();
    url.clear();
    queryString.clear();
    header.clear();
    body.clear();
    majorVersion = 0;
    minorVersion = 0;
    contentSize = 0;
  }

  std::string HttpMessage::getHeader(const std::string& key, const std::string& def) const
  {
    header_type::const_iterator i = header.find(key);
    return i == header.end() ? def : i->second;
  }

  void HttpMessage::setHeader(const std::string& key, const std::string& value)
  {
    log_debug("HttpMessage::setHeader(\"" << key << "\", \"" << value << "\")");
    header.insert(header_type::value_type(key, value));
  }

  std::string HttpMessage::dumpHeader() const
  {
    std::ostringstream h;
    dumpHeader(h);
    return h.str();
  }

  void HttpMessage::dumpHeader(std::ostream& out) const
  {
    for (header_type::const_iterator it = header.begin();
         it != header.end(); ++it)
      out << it->first << ' ' << it->second << '\n';
  }

  std::string HttpMessage::htdate(time_t t)
  {
    struct ::tm tm;
    localtime_r(&t, &tm);
    return htdate(&tm);
  }

  std::string HttpMessage::htdate(struct ::tm* tm)
  {
    const char* wday[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
    const char* monthn[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
                            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
    char buffer[80];

    mktime(tm);
    sprintf(buffer, "%s, %02d %s %d %02d:%02d:%02d GMT",
      wday[tm->tm_wday], tm->tm_mday, monthn[tm->tm_mon], tm->tm_year + 1900,
      tm->tm_hour, tm->tm_min, tm->tm_sec);
    return buffer;
  }

  namespace
  {
    class Pstr
    {
        const char* start;
        const char* end;

      public:
        typedef size_t size_type;

        Pstr(const char* s, const char* e)
          : start(s), end(e)
          { }

        size_type size() const  { return end - start; }

        bool operator== (const Pstr& s) const
        {
          return size() == s.size()
            && std::equal(start, end, s.start);
        }

        bool operator== (const char* str) const
        {
          size_type i;
          for (i = 0; i < size() && str[i] != '\0'; ++i)
            if (start[i] != str[i])
              return false;
          return i == size() && str[i] == '\0';
        }
    };
  }

  bool HttpMessage::checkUrl(const std::string& url)
  {
    typedef std::list<Pstr> tokens_type;

    // teile in Komponenten auf
    enum state_type {
      state_start,
      state_token,
      state_end
    };

    state_type state = state_start;

    tokens_type tokens;
    const char* p = url.data();
    const char* e = p + url.size();
    const char* s = p;
    for (; state != state_end && p != e; ++p)
    {
      switch (state)
      {
        case state_start:
          if (*p != '/')
          {
            s = p;
            state = state_token;
          }
          break;

        case state_token:
          if (*p == '/')
          {
            if (p - s != 1 || *s != '.')
              tokens.push_back(Pstr(s, p));
            state = state_start;
          }
          else if (p == e)
          {
            tokens.push_back(Pstr(s, p));
            state = state_end;
          }
          break;

        case state_end:
          break;
      }
    }

    // entferne jedes .. inklusive der vorhergehenden Komponente
    tokens_type::iterator i = tokens.begin();
    while (i != tokens.end())
    {
      if (*i == "..")
      {
        if (i == tokens.begin())
          return false;
        --i;
        i = tokens.erase(i);  // lösche 2 Komponenten
        i = tokens.erase(i);
      }
      else
      {
        ++i;
      }
    }

    return true;
  }

}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2003-2005 Tommi Maekitalo
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * As a special exception, you may use this file as part of a free
 * software library without restriction. Specifically, if other files
 * instantiate templates or use macros or inline functions from this
 * file, or you compile this file and link it with other files to
 * produce an executable, this file does not by itself cause the
 * resulting executable to be covered by the GNU General Public
 * License. This exception does not however invalidate any other
 * reasons why the executable file might be covered by the GNU Library
 * General Public License.
 *
 * 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <tnt/httprequest.h>
#include <tnt/httpparser.h>
#include <tnt/httperror.h>
#include <tnt/util.h>
#include <sstream>
#include <cxxtools/log.h>
#include <cxxtools/mutex.h>
#include <cxxtools/base64stream.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <tnt/sessionscope.h>
#include <tnt/socketif.h>
#include <pthread.h>
#include "config.h"
#include <tnt/stringlessignorecase.h>
#include <cstring>

namespace tnt
{
  log_define("tntnet.httprequest")

  ////////////////////////////////////////////////////////////////////////
  // HttpRequest
  //
  cxxtools::atomic_t HttpRequest::serial_ = 0;

  HttpRequest::HttpRequest(Tntnet& application_, const SocketIf* socketIf_)
    : socketIf(socketIf_),
      locale_init(false),
      encodingRead(false),
      requestScope(0),
      applicationScope(0),
      sessionScope(0),
      secureSessionScope(0),
      threadContext(0),
      applicationScopeLocked(false),
      sessionScopeLocked(false),
      secureSessionScopeLocked(false),
      application(application_)
  {
  }

  HttpRequest::HttpRequest(Tntnet& application_, const std::string& url_, const SocketIf* socketIf_)
    : socketIf(socketIf_),
      locale_init(false),
      requestScope(0),
      applicationScope(0),
      sessionScope(0),
      secureSessionScope(0),
      threadContext(0),
      applicationScopeLocked(false),
      sessionScopeLocked(false),
      secureSessionScopeLocked(false),
      application(application_)
  {
    std::istringstream s("GET " + url_ + " HTTP/1.1\r\n\r\n");
    parse(s);
  }

  HttpRequest::HttpRequest(const HttpRequest& r)
    : methodLen(0),
      pathinfo(r.pathinfo),
      args(r.args),
      qparam(r.qparam),
      socketIf(r.socketIf),
      ct(r.ct),
      mp(r.mp),
      serial(r.serial),
      locale_init(r.locale_init),
      locale(r.locale),
      requestScope(r.requestScope),
      applicationScope(r.applicationScope),
      sessionScope(r.sessionScope),
      secureSessionScope(r.secureSessionScope),
      threadContext(r.threadContext),
      applicationScopeLocked(false),
      sessionScopeLocked(false),
      secureSessionScopeLocked(false),
      application(r.application)
  {
    if (requestScope)
      requestScope->addRef();
    if (applicationScope)
      applicationScope->addRef();
    if (sessionScope)
      sessionScope->addRef();
    if (secureSessionScope)
      secureSessionScope->addRef();
  }

  HttpRequest::~HttpRequest()
  {
    releaseLocks();

    if (requestScope)
      requestScope->release();
    if (applicationScope)
      applicationScope->release();
    if (sessionScope)
      sessionScope->release();
    if (secureSessionScope)
      secureSessionScope->release();
  }

  HttpRequest& HttpRequest::operator= (const HttpRequest& r)
  {
    pathinfo = r.pathinfo;
    args = r.args;
    qparam = r.qparam;
    ct = r.ct;
    mp = r.mp;
    socketIf = r.socketIf;
    serial = r.serial;
    locale_init = r.locale_init;
    locale = r.locale;
    requestScope = r.requestScope;
    applicationScope = r.applicationScope;
    sessionScope = r.sessionScope;
    secureSessionScope = r.secureSessionScope;
    threadContext = r.threadContext;
    applicationScopeLocked = false;
    sessionScopeLocked = false;
    secureSessionScopeLocked = false;

    if (requestScope)
      requestScope->addRef();
    if (applicationScope)
      applicationScope->addRef();
    if (sessionScope)
      sessionScope->addRef();
    if (secureSessionScope)
      secureSessionScope->addRef();

    return *this;
  }

  void HttpRequest::clear()
  {
    HttpMessage::clear();
    body.clear();
    methodLen = 0;
    method[0] = '\0';
    url.clear();
    queryString.clear();
    contentSize = 0;
    pathinfo.clear();
    args.clear();
    qparam.clear();
    ct = Contenttype();
    mp = Multipart();
    locale_init = false;
    if (requestScope)
    {
      requestScope->release();
      requestScope = 0;
    }
    httpcookies.clear();
    encodingRead = false;
    username.clear();
    password.clear();

    releaseLocks();

    if (applicationScope)
    {
      applicationScope->release();
      applicationScope = 0;
    }

    if (sessionScope)
    {
      sessionScope->release();
      sessionScope = 0;
    }

    if (secureSessionScope)
    {
      secureSessionScope->release();
      secureSessionScope = 0;
    }

    threadContext = 0;
  }

  void HttpRequest::setMethod(const char* m)
  {
    if (strlen(m) >= 7)
      throw HttpError(HTTP_BAD_REQUEST, "invalid method");
    std::strcpy(method, m);
  }

  std::string HttpRequest::getArg(const std::string& name, const std::string& def) const
  {
    for (args_type::const_iterator it = args.begin(); it != args.end(); ++it)
      if (it->size() > name.size()
        && it->compare(0, name.size(), name) == 0
        && it->at(name.size()) == '=')
        return it->substr(name.size() + 1);
    return def;
  }

  void HttpRequest::parse(std::istream& in)
  {
    Parser p(*this);
    p.parse(in);
    if (!p.failed())
      doPostParse();
  }

  void HttpRequest::doPostParse()
  {
    if (hasHeader("Expect:"))
      throw HttpError(HTTP_EXPECTATION_FAILED, "expectation failed", "Expect not supported by this server");

    qparam.parse_url(getQueryString());

    if (isMethodPOST())
    {
      std::istringstream in(getHeader(httpheader::contentType));
      in >> ct;

      if (in)
      {
        if (ct.isMultipart())
        {
          mp.set(ct.getBoundary(), getBody());
          for (Multipart::const_iterator it = mp.begin();
               it != mp.end(); ++it)
          {
            // don't copy uploaded files into qparam to prevent unnecessery
            // copies of large chunks
            if (it->getFilename().empty())
            {
              std::string multipartBody(it->getBodyBegin(), it->getBodyEnd());
              qparam.add(it->getName(), multipartBody);
            }
          }
        }
        else if (ct.getType() == "application"
              && ct.getSubtype() == "x-www-form-urlencoded")
        {
          qparam.parse_url(getBody());
        }
      }
    }

    serial = cxxtools::atomicIncrement(serial_);
  }

  namespace
  {
    const std::locale& getCacheLocale(const std::string& lang)
    {
      static std::locale stdlocale;
      static bool stdlocale_init = false;

      typedef std::map<std::string, std::locale> locale_map_type;
      static locale_map_type locale_map;
      static cxxtools::Mutex locale_monitor;

      if (!stdlocale_init)
      {
        cxxtools::MutexLock lock(locale_monitor);
        if (!stdlocale_init)
        {
          stdlocale_init = true;
          try
          {
            stdlocale = std::locale("");
          }
          catch (const std::exception& e)
          {
            log_warn("error initializing standard-locale - using locale \"C\"");
          }
        }
      }

      if (lang.empty() || lang == stdlocale.name())
        return stdlocale;

      try
      {
        cxxtools::MutexLock lock(locale_monitor);
        locale_map_type::const_iterator it = locale_map.find(lang);
        if (it == locale_map.end())
        {
          std::locale loc = std::locale(lang.c_str());
          return locale_map.insert(locale_map_type::value_type(lang, loc)).first->second;
        }
        else
          return it->second;
      }
      catch (const std::exception& e)
      {
        log_warn("unknown locale " << lang << ": " << e.what());
        locale_map.insert(locale_map_type::value_type(lang, stdlocale));
        return stdlocale;
      }
    }
  }

  const std::locale& HttpRequest::getLocale() const
  {
    if (!locale_init)
    {
      static const std::string LANG = "LANG";
      lang = qparam[LANG];
      locale = getCacheLocale(qparam[LANG]);
      if (lang.empty())
        lang = locale.name();
      locale_init = true;
    }

    return locale;
  }

  void HttpRequest::setLocale(const std::locale& loc)
  {
    locale_init = true;
    locale = loc;
    lang = loc.name();
  }

  void HttpRequest::setLang(const std::string& lang_)
  {
    lang = lang_;
    locale = getCacheLocale(lang_);
    locale_init = true;
  }

  const Cookies& HttpRequest::getCookies() const
  {
    if (!httpcookies.hasCookies())
    {
      header_type::const_iterator it = header.find(httpheader::cookie);
      if (it != header.end())
        const_cast<HttpRequest*>(this)->httpcookies.set(it->second);
    }

    return httpcookies;
  }

  const Encoding& HttpRequest::getEncoding() const
  {
    if (!encodingRead)
    {
      encoding.parse(getHeader(httpheader::acceptEncoding));
      encodingRead = true;
    }
    return encoding;
  }

  const std::string& HttpRequest::getUsername() const
  {
    if (username.empty() && hasHeader(httpheader::authorization))
    {
      std::istringstream authHeader(getHeader(httpheader::authorization));
      while (authHeader && authHeader.get() != ' ')
        ;
      cxxtools::Base64istream in(authHeader);
      std::getline(in, username, ':');
      std::getline(in, password);
    }

    return username;
  }

  const std::string& HttpRequest::getPassword() const
  {
    getUsername();
    return password;
  }

  bool HttpRequest::verifyPassword(const std::string& password_) const
  {
    getUsername();
    return password == password_;
  }

  bool HttpRequest::keepAlive() const
  {
    // request is keep alive when either a connection header is explicitely set to keep alive
    // or the http version is 1.1
    Messageheader::const_iterator it = header.find(httpheader::connection);

    return it == header.end()
               ? getMinorVersion() >= 1 && getMajorVersion() >= 1
               : tnt::StringCompareIgnoreCase<const char*>(it->second,
                    httpheader::connectionKeepAlive) == 0;
  }

  const Contenttype& HttpRequest::getContentTypePriv() const
  {
    std::istringstream in(getHeader(httpheader::contentType));
    in >> ct;
    return ct;
  }

  void HttpRequest::setApplicationScope(Scope* s)
  {
    if (applicationScope == s)
      return;

    if (applicationScope)
    {
      releaseApplicationScopeLock();
      applicationScope->release();
    }

    if (s)
      s->addRef();
    applicationScope = s;
  }

  void HttpRequest::setSessionScope(Sessionscope* s)
  {
    if (sessionScope == s)
      return;

    if (sessionScope)
    {
      if (sessionScopeLocked)
      {
        sessionScope->unlock();
        sessionScopeLocked = false;
      }
      sessionScope->release();
    }

    if (s)
      s->addRef();

    sessionScope = s;
  }

  void HttpRequest::setSecureSessionScope(Sessionscope* s)
  {
    if (secureSessionScope == s)
      return;

    if (secureSessionScope)
    {
      if (secureSessionScopeLocked)
      {
        secureSessionScope->unlock();
        secureSessionScopeLocked = false;
      }
      secureSessionScope->release();
    }

    if (s)
      s->addRef();

    secureSessionScope = s;
  }

  void HttpRequest::clearSession()
  {
    log_info("end session");

    if (sessionScope)
    {
      if (sessionScopeLocked)
      {
        sessionScope->unlock();
        sessionScopeLocked = false;
      }

      sessionScope->release();
      sessionScope = 0;
    }

    if (secureSessionScope)
    {
      if (secureSessionScopeLocked)
      {
        secureSessionScope->unlock();
        secureSessionScopeLocked = false;
      }

      secureSessionScope->release();
      secureSessionScope = 0;
    }

  }

  void HttpRequest::ensureApplicationScopeLock()
  {
    ensureSessionScopeLock();
    if (applicationScope && !applicationScopeLocked)
    {
      applicationScope->lock();
      applicationScopeLocked = true;
    }
  }

  void HttpRequest::ensureSessionScopeLock()
  {
    if (sessionScope && !sessionScopeLocked)
    {
      sessionScope->lock();
      sessionScopeLocked = true;
    }

    if (secureSessionScope && !secureSessionScopeLocked)
    {
      secureSessionScope->lock();
      secureSessionScopeLocked = true;
    }

  }

  void HttpRequest::releaseApplicationScopeLock()
  {
    releaseSessionScopeLock();

    if (applicationScope && applicationScopeLocked)
    {
      applicationScopeLocked = false;
      applicationScope->unlock();
    }
  }

  void HttpRequest::releaseSessionScopeLock()
  {
    if (secureSessionScope && secureSessionScopeLocked)
    {
      secureSessionScopeLocked = false;
      secureSessionScope->unlock();
    }

    if (sessionScope && sessionScopeLocked)
    {
      sessionScopeLocked = false;
      sessionScope->unlock();
    }
  }

  Scope& HttpRequest::getRequestScope()
  {
    if (requestScope == 0)
      requestScope = new Scope();
    return *requestScope;
  }

  Scope& HttpRequest::getThreadScope()
  {
    if (threadContext == 0)
      throwRuntimeError("threadcontext not set");
    return threadContext->getScope();
  }

  Scope& HttpRequest::getApplicationScope()
  {
    ensureApplicationScopeLock();
    return *applicationScope;
  }

  Sessionscope& HttpRequest::getSessionScope()
  {
    if (!sessionScope)
      sessionScope = new Sessionscope();
    ensureSessionScopeLock();
    return *sessionScope;
  }

  Sessionscope& HttpRequest::getSecureSessionScope()
  {
    if (!secureSessionScope)
      secureSessionScope = new Sessionscope();
    ensureSessionScopeLock();
    return *secureSessionScope;
  }

  bool HttpRequest::hasSessionScope() const
  {
    return secureSessionScope != 0 && !secureSessionScope->empty();
  }

  bool HttpRequest::hasSecureSessionScope() const
  {
    return secureSessionScope != 0 && !secureSessionScope->empty();
  }

}

<commit_msg>session handling was broken after implementation of secure session scope<commit_after>/*
 * Copyright (C) 2003-2005 Tommi Maekitalo
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * As a special exception, you may use this file as part of a free
 * software library without restriction. Specifically, if other files
 * instantiate templates or use macros or inline functions from this
 * file, or you compile this file and link it with other files to
 * produce an executable, this file does not by itself cause the
 * resulting executable to be covered by the GNU General Public
 * License. This exception does not however invalidate any other
 * reasons why the executable file might be covered by the GNU Library
 * General Public License.
 *
 * 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <tnt/httprequest.h>
#include <tnt/httpparser.h>
#include <tnt/httperror.h>
#include <tnt/util.h>
#include <sstream>
#include <cxxtools/log.h>
#include <cxxtools/mutex.h>
#include <cxxtools/base64stream.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <tnt/sessionscope.h>
#include <tnt/socketif.h>
#include <pthread.h>
#include "config.h"
#include <tnt/stringlessignorecase.h>
#include <cstring>

namespace tnt
{
  log_define("tntnet.httprequest")

  ////////////////////////////////////////////////////////////////////////
  // HttpRequest
  //
  cxxtools::atomic_t HttpRequest::serial_ = 0;

  HttpRequest::HttpRequest(Tntnet& application_, const SocketIf* socketIf_)
    : socketIf(socketIf_),
      locale_init(false),
      encodingRead(false),
      requestScope(0),
      applicationScope(0),
      sessionScope(0),
      secureSessionScope(0),
      threadContext(0),
      applicationScopeLocked(false),
      sessionScopeLocked(false),
      secureSessionScopeLocked(false),
      application(application_)
  {
  }

  HttpRequest::HttpRequest(Tntnet& application_, const std::string& url_, const SocketIf* socketIf_)
    : socketIf(socketIf_),
      locale_init(false),
      requestScope(0),
      applicationScope(0),
      sessionScope(0),
      secureSessionScope(0),
      threadContext(0),
      applicationScopeLocked(false),
      sessionScopeLocked(false),
      secureSessionScopeLocked(false),
      application(application_)
  {
    std::istringstream s("GET " + url_ + " HTTP/1.1\r\n\r\n");
    parse(s);
  }

  HttpRequest::HttpRequest(const HttpRequest& r)
    : methodLen(0),
      pathinfo(r.pathinfo),
      args(r.args),
      qparam(r.qparam),
      socketIf(r.socketIf),
      ct(r.ct),
      mp(r.mp),
      serial(r.serial),
      locale_init(r.locale_init),
      locale(r.locale),
      requestScope(r.requestScope),
      applicationScope(r.applicationScope),
      sessionScope(r.sessionScope),
      secureSessionScope(r.secureSessionScope),
      threadContext(r.threadContext),
      applicationScopeLocked(false),
      sessionScopeLocked(false),
      secureSessionScopeLocked(false),
      application(r.application)
  {
    if (requestScope)
      requestScope->addRef();
    if (applicationScope)
      applicationScope->addRef();
    if (sessionScope)
      sessionScope->addRef();
    if (secureSessionScope)
      secureSessionScope->addRef();
  }

  HttpRequest::~HttpRequest()
  {
    releaseLocks();

    if (requestScope)
      requestScope->release();
    if (applicationScope)
      applicationScope->release();
    if (sessionScope)
      sessionScope->release();
    if (secureSessionScope)
      secureSessionScope->release();
  }

  HttpRequest& HttpRequest::operator= (const HttpRequest& r)
  {
    pathinfo = r.pathinfo;
    args = r.args;
    qparam = r.qparam;
    ct = r.ct;
    mp = r.mp;
    socketIf = r.socketIf;
    serial = r.serial;
    locale_init = r.locale_init;
    locale = r.locale;
    requestScope = r.requestScope;
    applicationScope = r.applicationScope;
    sessionScope = r.sessionScope;
    secureSessionScope = r.secureSessionScope;
    threadContext = r.threadContext;
    applicationScopeLocked = false;
    sessionScopeLocked = false;
    secureSessionScopeLocked = false;

    if (requestScope)
      requestScope->addRef();
    if (applicationScope)
      applicationScope->addRef();
    if (sessionScope)
      sessionScope->addRef();
    if (secureSessionScope)
      secureSessionScope->addRef();

    return *this;
  }

  void HttpRequest::clear()
  {
    HttpMessage::clear();
    body.clear();
    methodLen = 0;
    method[0] = '\0';
    url.clear();
    queryString.clear();
    contentSize = 0;
    pathinfo.clear();
    args.clear();
    qparam.clear();
    ct = Contenttype();
    mp = Multipart();
    locale_init = false;
    if (requestScope)
    {
      requestScope->release();
      requestScope = 0;
    }
    httpcookies.clear();
    encodingRead = false;
    username.clear();
    password.clear();

    releaseLocks();

    if (applicationScope)
    {
      applicationScope->release();
      applicationScope = 0;
    }

    if (sessionScope)
    {
      sessionScope->release();
      sessionScope = 0;
    }

    if (secureSessionScope)
    {
      secureSessionScope->release();
      secureSessionScope = 0;
    }

    threadContext = 0;
  }

  void HttpRequest::setMethod(const char* m)
  {
    if (strlen(m) >= 7)
      throw HttpError(HTTP_BAD_REQUEST, "invalid method");
    std::strcpy(method, m);
  }

  std::string HttpRequest::getArg(const std::string& name, const std::string& def) const
  {
    for (args_type::const_iterator it = args.begin(); it != args.end(); ++it)
      if (it->size() > name.size()
        && it->compare(0, name.size(), name) == 0
        && it->at(name.size()) == '=')
        return it->substr(name.size() + 1);
    return def;
  }

  void HttpRequest::parse(std::istream& in)
  {
    Parser p(*this);
    p.parse(in);
    if (!p.failed())
      doPostParse();
  }

  void HttpRequest::doPostParse()
  {
    if (hasHeader("Expect:"))
      throw HttpError(HTTP_EXPECTATION_FAILED, "expectation failed", "Expect not supported by this server");

    qparam.parse_url(getQueryString());

    if (isMethodPOST())
    {
      std::istringstream in(getHeader(httpheader::contentType));
      in >> ct;

      if (in)
      {
        if (ct.isMultipart())
        {
          mp.set(ct.getBoundary(), getBody());
          for (Multipart::const_iterator it = mp.begin();
               it != mp.end(); ++it)
          {
            // don't copy uploaded files into qparam to prevent unnecessery
            // copies of large chunks
            if (it->getFilename().empty())
            {
              std::string multipartBody(it->getBodyBegin(), it->getBodyEnd());
              qparam.add(it->getName(), multipartBody);
            }
          }
        }
        else if (ct.getType() == "application"
              && ct.getSubtype() == "x-www-form-urlencoded")
        {
          qparam.parse_url(getBody());
        }
      }
    }

    serial = cxxtools::atomicIncrement(serial_);
  }

  namespace
  {
    const std::locale& getCacheLocale(const std::string& lang)
    {
      static std::locale stdlocale;
      static bool stdlocale_init = false;

      typedef std::map<std::string, std::locale> locale_map_type;
      static locale_map_type locale_map;
      static cxxtools::Mutex locale_monitor;

      if (!stdlocale_init)
      {
        cxxtools::MutexLock lock(locale_monitor);
        if (!stdlocale_init)
        {
          stdlocale_init = true;
          try
          {
            stdlocale = std::locale("");
          }
          catch (const std::exception& e)
          {
            log_warn("error initializing standard-locale - using locale \"C\"");
          }
        }
      }

      if (lang.empty() || lang == stdlocale.name())
        return stdlocale;

      try
      {
        cxxtools::MutexLock lock(locale_monitor);
        locale_map_type::const_iterator it = locale_map.find(lang);
        if (it == locale_map.end())
        {
          std::locale loc = std::locale(lang.c_str());
          return locale_map.insert(locale_map_type::value_type(lang, loc)).first->second;
        }
        else
          return it->second;
      }
      catch (const std::exception& e)
      {
        log_warn("unknown locale " << lang << ": " << e.what());
        locale_map.insert(locale_map_type::value_type(lang, stdlocale));
        return stdlocale;
      }
    }
  }

  const std::locale& HttpRequest::getLocale() const
  {
    if (!locale_init)
    {
      static const std::string LANG = "LANG";
      lang = qparam[LANG];
      locale = getCacheLocale(qparam[LANG]);
      if (lang.empty())
        lang = locale.name();
      locale_init = true;
    }

    return locale;
  }

  void HttpRequest::setLocale(const std::locale& loc)
  {
    locale_init = true;
    locale = loc;
    lang = loc.name();
  }

  void HttpRequest::setLang(const std::string& lang_)
  {
    lang = lang_;
    locale = getCacheLocale(lang_);
    locale_init = true;
  }

  const Cookies& HttpRequest::getCookies() const
  {
    if (!httpcookies.hasCookies())
    {
      header_type::const_iterator it = header.find(httpheader::cookie);
      if (it != header.end())
        const_cast<HttpRequest*>(this)->httpcookies.set(it->second);
    }

    return httpcookies;
  }

  const Encoding& HttpRequest::getEncoding() const
  {
    if (!encodingRead)
    {
      encoding.parse(getHeader(httpheader::acceptEncoding));
      encodingRead = true;
    }
    return encoding;
  }

  const std::string& HttpRequest::getUsername() const
  {
    if (username.empty() && hasHeader(httpheader::authorization))
    {
      std::istringstream authHeader(getHeader(httpheader::authorization));
      while (authHeader && authHeader.get() != ' ')
        ;
      cxxtools::Base64istream in(authHeader);
      std::getline(in, username, ':');
      std::getline(in, password);
    }

    return username;
  }

  const std::string& HttpRequest::getPassword() const
  {
    getUsername();
    return password;
  }

  bool HttpRequest::verifyPassword(const std::string& password_) const
  {
    getUsername();
    return password == password_;
  }

  bool HttpRequest::keepAlive() const
  {
    // request is keep alive when either a connection header is explicitely set to keep alive
    // or the http version is 1.1
    Messageheader::const_iterator it = header.find(httpheader::connection);

    return it == header.end()
               ? getMinorVersion() >= 1 && getMajorVersion() >= 1
               : tnt::StringCompareIgnoreCase<const char*>(it->second,
                    httpheader::connectionKeepAlive) == 0;
  }

  const Contenttype& HttpRequest::getContentTypePriv() const
  {
    std::istringstream in(getHeader(httpheader::contentType));
    in >> ct;
    return ct;
  }

  void HttpRequest::setApplicationScope(Scope* s)
  {
    if (applicationScope == s)
      return;

    if (applicationScope)
    {
      releaseApplicationScopeLock();
      applicationScope->release();
    }

    if (s)
      s->addRef();
    applicationScope = s;
  }

  void HttpRequest::setSessionScope(Sessionscope* s)
  {
    if (sessionScope == s)
      return;

    if (sessionScope)
    {
      if (sessionScopeLocked)
      {
        sessionScope->unlock();
        sessionScopeLocked = false;
      }
      sessionScope->release();
    }

    if (s)
      s->addRef();

    sessionScope = s;
  }

  void HttpRequest::setSecureSessionScope(Sessionscope* s)
  {
    if (secureSessionScope == s)
      return;

    if (secureSessionScope)
    {
      if (secureSessionScopeLocked)
      {
        secureSessionScope->unlock();
        secureSessionScopeLocked = false;
      }
      secureSessionScope->release();
    }

    if (s)
      s->addRef();

    secureSessionScope = s;
  }

  void HttpRequest::clearSession()
  {
    log_info("end session");

    if (sessionScope)
    {
      if (sessionScopeLocked)
      {
        sessionScope->unlock();
        sessionScopeLocked = false;
      }

      sessionScope->release();
      sessionScope = 0;
    }

    if (secureSessionScope)
    {
      if (secureSessionScopeLocked)
      {
        secureSessionScope->unlock();
        secureSessionScopeLocked = false;
      }

      secureSessionScope->release();
      secureSessionScope = 0;
    }

  }

  void HttpRequest::ensureApplicationScopeLock()
  {
    ensureSessionScopeLock();
    if (applicationScope && !applicationScopeLocked)
    {
      applicationScope->lock();
      applicationScopeLocked = true;
    }
  }

  void HttpRequest::ensureSessionScopeLock()
  {
    if (sessionScope && !sessionScopeLocked)
    {
      sessionScope->lock();
      sessionScopeLocked = true;
    }

    if (secureSessionScope && !secureSessionScopeLocked)
    {
      secureSessionScope->lock();
      secureSessionScopeLocked = true;
    }

  }

  void HttpRequest::releaseApplicationScopeLock()
  {
    releaseSessionScopeLock();

    if (applicationScope && applicationScopeLocked)
    {
      applicationScopeLocked = false;
      applicationScope->unlock();
    }
  }

  void HttpRequest::releaseSessionScopeLock()
  {
    if (secureSessionScope && secureSessionScopeLocked)
    {
      secureSessionScopeLocked = false;
      secureSessionScope->unlock();
    }

    if (sessionScope && sessionScopeLocked)
    {
      sessionScopeLocked = false;
      sessionScope->unlock();
    }
  }

  Scope& HttpRequest::getRequestScope()
  {
    if (requestScope == 0)
      requestScope = new Scope();
    return *requestScope;
  }

  Scope& HttpRequest::getThreadScope()
  {
    if (threadContext == 0)
      throwRuntimeError("threadcontext not set");
    return threadContext->getScope();
  }

  Scope& HttpRequest::getApplicationScope()
  {
    ensureApplicationScopeLock();
    return *applicationScope;
  }

  Sessionscope& HttpRequest::getSessionScope()
  {
    if (!sessionScope)
      sessionScope = new Sessionscope();
    ensureSessionScopeLock();
    return *sessionScope;
  }

  Sessionscope& HttpRequest::getSecureSessionScope()
  {
    if (!secureSessionScope)
      secureSessionScope = new Sessionscope();
    ensureSessionScopeLock();
    return *secureSessionScope;
  }

  bool HttpRequest::hasSessionScope() const
  {
    return sessionScope != 0 && !sessionScope->empty();
  }

  bool HttpRequest::hasSecureSessionScope() const
  {
    return secureSessionScope != 0 && !secureSessionScope->empty();
  }

}

<|endoftext|>


<commit_before>
// $Id: QueryResults.cpp 3456 2013-06-14 02:11:13Z jiaying $

/*
 * The Software is made available solely for use according to the License Agreement. Any reproduction
 * or redistribution of the Software not in accordance with the License Agreement is expressly prohibited
 * by law, and may result in severe civil and criminal penalties. Violators will be prosecuted to the
 * maximum extent possible.
 *
 * THE SOFTWARE IS WARRANTED, IF AT ALL, ONLY ACCORDING TO THE TERMS OF THE LICENSE AGREEMENT. EXCEPT
 * AS WARRANTED IN THE LICENSE AGREEMENT, SRCH2 INC. HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS WITH
 * REGARD TO THE SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT.  IN NO EVENT SHALL SRCH2 INC. BE LIABLE FOR ANY
 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF SOFTWARE.

 * Copyright © 2010 SRCH2 Inc. All rights reserved
 */

#include "QueryResultsInternal.h"
#include "operation/QueryEvaluatorInternal.h"
#include "util/RecordSerializerUtil.h"

using srch2::util::Logger;
namespace srch2
{
namespace instantsearch
{

QueryResultFactory::QueryResultFactory(){
	impl = new QueryResultFactoryInternal();
}
QueryResultFactory::~QueryResultFactory(){
    delete impl;
}

QueryResults::QueryResults(){
	impl = new QueryResultsInternal();
}

void QueryResults::init(QueryResultFactory * resultsFactory, const QueryEvaluator* queryEvaluator, Query* query){
	impl->init(resultsFactory,queryEvaluator->impl,query);
}

/**
 * Creates a QueryResults object.
 * @param[in] indexSearcher the reference to an IndexSearcher object.
 * @param[in] query the reference to a Query object.
 */
QueryResults::QueryResults(QueryResultFactory * resultsFactory, const  QueryEvaluator* queryEvaluator, Query* query){
	impl = new QueryResultsInternal(resultsFactory,queryEvaluator->impl,query );
}

QueryResults::~QueryResults(){
	delete impl;
}

/**
 * Checks if the iterator reaches the end. @return false if
 * there is no next item in the QueryResults.
 */
/*virtual bool next() = 0;*/

/**
 * Gets the number of result items in the QueryResults object.
 */
unsigned QueryResults::getNumberOfResults() const{
	 return impl->sortedFinalResults.size();
}

/**
 * Gets the current record id while iterating through the QueryResults object.
 */
/*virtual int getRecordId() = 0;*/

/**
 * Gets the record id of the 'position'-th item in the QueryResults object.
 */
std::string QueryResults::getRecordId(unsigned position) const {
    ASSERT (position < this->getNumberOfResults());
    return impl->sortedFinalResults.at(position)->externalRecordId;
}

/**
 * Gets the record id of the 'position'-th item in the QueryResults object.
 * TODO: Move/remove getInternalRecordId to internal include files. There should be a mapping from external
 * Used to access the InMemoryData.
 */
unsigned QueryResults::getInternalRecordId(unsigned position) const {
    ASSERT (position < this->getNumberOfResults());
    return impl->sortedFinalResults.at(position)->internalRecordId;
}

/*
 *   this function is called from unit test. Do not use it in wrapper layer.
 */
std::string QueryResults::getInMemoryRecordString(unsigned position) const {
    unsigned internalRecordId = this->getInternalRecordId(position);
    StoredRecordBuffer buffer = impl->queryEvaluatorInternal->getInMemoryData(internalRecordId);
    string inMemoryString = "";
    if (buffer.start)
    	inMemoryString = string(buffer.start.get(), buffer.length);
    return inMemoryString;
}

/**
 * Gets the score of the 'position'-th item in the QueryResults object.
 */
std::string QueryResults::getResultScoreString(unsigned position) const {
    ASSERT (position < this->getNumberOfResults());
    return impl->sortedFinalResults.at(position)->_score.toString();
}
TypedValue QueryResults::getResultScore(unsigned position) const {
    ASSERT (position < this->getNumberOfResults());
    return impl->sortedFinalResults.at(position)->_score;
}

/**
 *
 * Gets the matching keywords of the 'position'-th item in
 * the QueryResults object. For each query keyword return a matching
 * keyword in a record. If the query keyword is prefix then return a
 * matching prefix. In the case of multiple matching keywords for a
 * query keyword,the best match based on edit distance is returned.
 *
 * For example, for the query "ulman compilor", a result
 * record with keywords "ullman principles compiler" will
 * return ["ullman","compiler"] in the matchingKeywords
 * vector.  In particular, "compiler" is the best matching
 * keyword in the record for the second query keyword
 * "compilor".
 */
void QueryResults::getMatchingKeywords(const unsigned position, std::vector<std::string> &matchingKeywords) const {
    matchingKeywords.assign(impl->sortedFinalResults[position]->matchingKeywords.begin(),
    		impl->sortedFinalResults[position]->matchingKeywords.end());
}

/**
 * Gets the edit distances of the 'position'-th item in the
 * QueryResults object.
 * @param[in] position the position of a result in the returned
 * results (starting from 0).
 * @param[out] editDistances a vector of edit distances of the
 * best matching keywords in this result record with respect
 * to the query keywords.
 *
 * For example, for the query "ulman compilor", a result
 * recorprivate:
struct Impl;
Impl *impl;d with keywords "ullman principles conpiler" will
 * return [1,2] in the editDistances vector.  In particular,
 * "2" means that the best matching keyword ("conpiler") in
 * this record has an edit distance 2 to the second query
 * keyword "compilor".
 */
void QueryResults::getEditDistances(const unsigned position, std::vector<unsigned> &editDistances) const {
    editDistances.assign(impl->sortedFinalResults[position]->editDistances.begin(),
    		impl->sortedFinalResults[position]->editDistances.end());
}

// The following two functions only work for attribute based search
void QueryResults::getMatchedAttributeBitmaps(const unsigned position, std::vector<unsigned> &matchedAttributeBitmaps) const {
	matchedAttributeBitmaps.assign(impl->sortedFinalResults[position]->attributeBitmaps.begin(),
			impl->sortedFinalResults[position]->attributeBitmaps.end());
}

void QueryResults::getMatchedAttributes(const unsigned position, std::vector<std::vector<unsigned> > &matchedAttributes) const {
	//TODO opt
	const vector<unsigned> &matchedAttributeBitmaps = impl->sortedFinalResults[position]->attributeBitmaps;
	matchedAttributes.resize(matchedAttributeBitmaps.size());

	for(int i = 0; i < matchedAttributeBitmaps.size(); i++)
	{
		unsigned idx = 0;
		unsigned matchedAttributeBitmap = matchedAttributeBitmaps[i];
		matchedAttributes[i].clear();
		while(matchedAttributeBitmap)
		{
			if(matchedAttributeBitmap & 0x1)
			{
				matchedAttributes[i].push_back(idx);
			}
			matchedAttributeBitmap >>= 1;
			++idx;
		}
	}
}

void QueryResults::getTermTypes(unsigned position, vector<TermType>& tt) const{
	tt.assign(impl->sortedFinalResults[position]->termTypes.begin(),
    		impl->sortedFinalResults[position]->termTypes.end());
}

/*
 *   In Geo search return distance between location of the result and center of the query rank.
 *   TODO: Change the name to getGeoDistance()
 *   // only the results of MapQuery have this
 */
double QueryResults::getPhysicalDistance(const unsigned position) const {
    ASSERT (position < this->getNumberOfResults());
    return impl->sortedFinalResults.at(position)->physicalDistance;
}

const std::map<std::string, std::pair< FacetType , std::vector<std::pair<std::string, float> > > > * QueryResults::getFacetResults() const{
	return &impl->facetResults;
}

// pass information to the destination QueryResults object
// can be seen as this := sourceQueryResults (only for structures related to post processing)
void QueryResults::copyForPostProcessing(QueryResults * sourceQueryResults) const {
    this->impl->sortedFinalResults = sourceQueryResults->impl->sortedFinalResults ;
    this->impl->facetResults = sourceQueryResults->impl->facetResults ;
    this->impl->resultsApproximated = sourceQueryResults->impl->resultsApproximated;
    this->impl->estimatedNumberOfResults = sourceQueryResults->impl->estimatedNumberOfResults;
}

void QueryResults::clear(){

	this->impl->sortedFinalResults.clear();
	this->impl->facetResults.clear();

}

bool QueryResults::isResultsApproximated() const{
	return this->impl->resultsApproximated;
}

long int QueryResults::getEstimatedNumberOfResults() const{
	return this->impl->estimatedNumberOfResults;
}

//TODO: These three functions for internal debugging. remove from the header
void QueryResults::printStats() const {
    impl->stat->print();
}

void QueryResults::printResult() const {
	// show attributeBitmaps
	vector<unsigned> attributeBitmaps;
	vector<vector<unsigned> > attributes;
	vector<string> matchedKeywords;
    Logger::debug("Result count %d" ,this->getNumberOfResults());
	for(int i = 0; i < this->getNumberOfResults(); i++)
	{
        Logger::debug("Result #%d" ,i);
		this->getMatchedAttributeBitmaps(i, attributeBitmaps);
		this->getMatchingKeywords(i, matchedKeywords);
		this->getMatchedAttributes(i, attributes);
		for(int j = 0; j < attributeBitmaps.size(); j++)
		{
            Logger::debug("%s %d {", matchedKeywords[j].c_str(), attributeBitmaps[j]);
			for(int k = 0; k < attributes[j].size(); k++)
                Logger::debug("%d", attributes[j][k]);
            Logger::debug("}");
		}

	}

}

void QueryResults::addMessage(const char* msg) {
	 impl->stat->addMessage(msg);
}

}}
<commit_msg>minor review comment<commit_after>
// $Id: QueryResults.cpp 3456 2013-06-14 02:11:13Z jiaying $

/*
 * The Software is made available solely for use according to the License Agreement. Any reproduction
 * or redistribution of the Software not in accordance with the License Agreement is expressly prohibited
 * by law, and may result in severe civil and criminal penalties. Violators will be prosecuted to the
 * maximum extent possible.
 *
 * THE SOFTWARE IS WARRANTED, IF AT ALL, ONLY ACCORDING TO THE TERMS OF THE LICENSE AGREEMENT. EXCEPT
 * AS WARRANTED IN THE LICENSE AGREEMENT, SRCH2 INC. HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS WITH
 * REGARD TO THE SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT.  IN NO EVENT SHALL SRCH2 INC. BE LIABLE FOR ANY
 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF SOFTWARE.

 * Copyright © 2010 SRCH2 Inc. All rights reserved
 */

#include "QueryResultsInternal.h"
#include "operation/QueryEvaluatorInternal.h"
#include "util/RecordSerializerUtil.h"

using srch2::util::Logger;
namespace srch2
{
namespace instantsearch
{

QueryResultFactory::QueryResultFactory(){
	impl = new QueryResultFactoryInternal();
}
QueryResultFactory::~QueryResultFactory(){
    delete impl;
}

QueryResults::QueryResults(){
	impl = new QueryResultsInternal();
}

void QueryResults::init(QueryResultFactory * resultsFactory, const QueryEvaluator* queryEvaluator, Query* query){
	impl->init(resultsFactory,queryEvaluator->impl,query);
}

/**
 * Creates a QueryResults object.
 * @param[in] indexSearcher the reference to an IndexSearcher object.
 * @param[in] query the reference to a Query object.
 */
QueryResults::QueryResults(QueryResultFactory * resultsFactory, const  QueryEvaluator* queryEvaluator, Query* query){
	impl = new QueryResultsInternal(resultsFactory,queryEvaluator->impl,query );
}

QueryResults::~QueryResults(){
	delete impl;
}

/**
 * Checks if the iterator reaches the end. @return false if
 * there is no next item in the QueryResults.
 */
/*virtual bool next() = 0;*/

/**
 * Gets the number of result items in the QueryResults object.
 */
unsigned QueryResults::getNumberOfResults() const{
	 return impl->sortedFinalResults.size();
}

/**
 * Gets the current record id while iterating through the QueryResults object.
 */
/*virtual int getRecordId() = 0;*/

/**
 * Gets the record id of the 'position'-th item in the QueryResults object.
 */
std::string QueryResults::getRecordId(unsigned position) const {
    ASSERT (position < this->getNumberOfResults());
    return impl->sortedFinalResults.at(position)->externalRecordId;
}

/**
 * Gets the record id of the 'position'-th item in the QueryResults object.
 * TODO: Move/remove getInternalRecordId to internal include files. There should be a mapping from external
 * Used to access the InMemoryData.
 */
unsigned QueryResults::getInternalRecordId(unsigned position) const {
    ASSERT (position < this->getNumberOfResults());
    return impl->sortedFinalResults.at(position)->internalRecordId;
}

/*
 *   this function is called from unit test. Do not use it in wrapper layer.
 */
std::string QueryResults::getInMemoryRecordString(unsigned position) const {
    unsigned internalRecordId = this->getInternalRecordId(position);
    StoredRecordBuffer buffer = impl->queryEvaluatorInternal->getInMemoryData(internalRecordId);
    string inMemoryString = "";
    if (!buffer.start)
    	inMemoryString = string(buffer.start.get(), buffer.length);
    return inMemoryString;
}

/**
 * Gets the score of the 'position'-th item in the QueryResults object.
 */
std::string QueryResults::getResultScoreString(unsigned position) const {
    ASSERT (position < this->getNumberOfResults());
    return impl->sortedFinalResults.at(position)->_score.toString();
}
TypedValue QueryResults::getResultScore(unsigned position) const {
    ASSERT (position < this->getNumberOfResults());
    return impl->sortedFinalResults.at(position)->_score;
}

/**
 *
 * Gets the matching keywords of the 'position'-th item in
 * the QueryResults object. For each query keyword return a matching
 * keyword in a record. If the query keyword is prefix then return a
 * matching prefix. In the case of multiple matching keywords for a
 * query keyword,the best match based on edit distance is returned.
 *
 * For example, for the query "ulman compilor", a result
 * record with keywords "ullman principles compiler" will
 * return ["ullman","compiler"] in the matchingKeywords
 * vector.  In particular, "compiler" is the best matching
 * keyword in the record for the second query keyword
 * "compilor".
 */
void QueryResults::getMatchingKeywords(const unsigned position, std::vector<std::string> &matchingKeywords) const {
    matchingKeywords.assign(impl->sortedFinalResults[position]->matchingKeywords.begin(),
    		impl->sortedFinalResults[position]->matchingKeywords.end());
}

/**
 * Gets the edit distances of the 'position'-th item in the
 * QueryResults object.
 * @param[in] position the position of a result in the returned
 * results (starting from 0).
 * @param[out] editDistances a vector of edit distances of the
 * best matching keywords in this result record with respect
 * to the query keywords.
 *
 * For example, for the query "ulman compilor", a result
 * recorprivate:
struct Impl;
Impl *impl;d with keywords "ullman principles conpiler" will
 * return [1,2] in the editDistances vector.  In particular,
 * "2" means that the best matching keyword ("conpiler") in
 * this record has an edit distance 2 to the second query
 * keyword "compilor".
 */
void QueryResults::getEditDistances(const unsigned position, std::vector<unsigned> &editDistances) const {
    editDistances.assign(impl->sortedFinalResults[position]->editDistances.begin(),
    		impl->sortedFinalResults[position]->editDistances.end());
}

// The following two functions only work for attribute based search
void QueryResults::getMatchedAttributeBitmaps(const unsigned position, std::vector<unsigned> &matchedAttributeBitmaps) const {
	matchedAttributeBitmaps.assign(impl->sortedFinalResults[position]->attributeBitmaps.begin(),
			impl->sortedFinalResults[position]->attributeBitmaps.end());
}

void QueryResults::getMatchedAttributes(const unsigned position, std::vector<std::vector<unsigned> > &matchedAttributes) const {
	//TODO opt
	const vector<unsigned> &matchedAttributeBitmaps = impl->sortedFinalResults[position]->attributeBitmaps;
	matchedAttributes.resize(matchedAttributeBitmaps.size());

	for(int i = 0; i < matchedAttributeBitmaps.size(); i++)
	{
		unsigned idx = 0;
		unsigned matchedAttributeBitmap = matchedAttributeBitmaps[i];
		matchedAttributes[i].clear();
		while(matchedAttributeBitmap)
		{
			if(matchedAttributeBitmap & 0x1)
			{
				matchedAttributes[i].push_back(idx);
			}
			matchedAttributeBitmap >>= 1;
			++idx;
		}
	}
}

void QueryResults::getTermTypes(unsigned position, vector<TermType>& tt) const{
	tt.assign(impl->sortedFinalResults[position]->termTypes.begin(),
    		impl->sortedFinalResults[position]->termTypes.end());
}

/*
 *   In Geo search return distance between location of the result and center of the query rank.
 *   TODO: Change the name to getGeoDistance()
 *   // only the results of MapQuery have this
 */
double QueryResults::getPhysicalDistance(const unsigned position) const {
    ASSERT (position < this->getNumberOfResults());
    return impl->sortedFinalResults.at(position)->physicalDistance;
}

const std::map<std::string, std::pair< FacetType , std::vector<std::pair<std::string, float> > > > * QueryResults::getFacetResults() const{
	return &impl->facetResults;
}

// pass information to the destination QueryResults object
// can be seen as this := sourceQueryResults (only for structures related to post processing)
void QueryResults::copyForPostProcessing(QueryResults * sourceQueryResults) const {
    this->impl->sortedFinalResults = sourceQueryResults->impl->sortedFinalResults ;
    this->impl->facetResults = sourceQueryResults->impl->facetResults ;
    this->impl->resultsApproximated = sourceQueryResults->impl->resultsApproximated;
    this->impl->estimatedNumberOfResults = sourceQueryResults->impl->estimatedNumberOfResults;
}

void QueryResults::clear(){

	this->impl->sortedFinalResults.clear();
	this->impl->facetResults.clear();

}

bool QueryResults::isResultsApproximated() const{
	return this->impl->resultsApproximated;
}

long int QueryResults::getEstimatedNumberOfResults() const{
	return this->impl->estimatedNumberOfResults;
}

//TODO: These three functions for internal debugging. remove from the header
void QueryResults::printStats() const {
    impl->stat->print();
}

void QueryResults::printResult() const {
	// show attributeBitmaps
	vector<unsigned> attributeBitmaps;
	vector<vector<unsigned> > attributes;
	vector<string> matchedKeywords;
    Logger::debug("Result count %d" ,this->getNumberOfResults());
	for(int i = 0; i < this->getNumberOfResults(); i++)
	{
        Logger::debug("Result #%d" ,i);
		this->getMatchedAttributeBitmaps(i, attributeBitmaps);
		this->getMatchingKeywords(i, matchedKeywords);
		this->getMatchedAttributes(i, attributes);
		for(int j = 0; j < attributeBitmaps.size(); j++)
		{
            Logger::debug("%s %d {", matchedKeywords[j].c_str(), attributeBitmaps[j]);
			for(int k = 0; k < attributes[j].size(); k++)
                Logger::debug("%d", attributes[j][k]);
            Logger::debug("}");
		}

	}

}

void QueryResults::addMessage(const char* msg) {
	 impl->stat->addMessage(msg);
}

}}
<|endoftext|>


<commit_before>/*
 * DesktopMain.cpp
 *
 * Copyright (C) 2009-18 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 <QtGui>
#include <QPushButton>

#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/scoped_ptr.hpp>

#include <core/Log.hpp>

#include <core/system/FileScanner.hpp>
#include <core/SafeConvert.hpp>
#include <core/StringUtils.hpp>
#include <core/system/System.hpp>
#include <core/system/Environment.hpp>
#include <core/r_util/RProjectFile.hpp>
#include <core/r_util/RUserData.hpp>

#include "DesktopApplicationLaunch.hpp"
#include "DesktopSlotBinders.hpp"
#include "DesktopDetectRHome.hpp"
#include "DesktopUtils.hpp"
#include "DesktopSessionLauncher.hpp"
#include "DesktopProgressActivator.hpp"
#include "DesktopNetworkProxyFactory.hpp"
#include "DesktopActivationOverlay.hpp"

QProcess* pRSessionProcess;
QString sharedSecret;

using namespace rstudio;
using namespace rstudio::core;
using namespace rstudio::desktop;

namespace {

void initializeSharedSecret()
{
   sharedSecret = QString::number(rand())
                  + QString::number(rand())
                  + QString::number(rand());
   std::string value = sharedSecret.toUtf8().constData();
   core::system::setenv("RS_SHARED_SECRET", value);
}

void initializeWorkingDirectory(int argc,
                                char* argv[],
                                const QString& filename)
{
   // bail if we've already got a working directory as a result of
   // a call to openSessionInNewWindow
   if (!core::system::getenv(kRStudioInitialWorkingDir).empty())
      return;

   // calculate what our initial working directory should be
   std::string workingDir;

   // if there is a filename passed to us then use it's path
   if (filename != QString())
   {
      FilePath filePath(filename.toUtf8().constData());
      if (filePath.exists())
      {
         if (filePath.isDirectory())
            workingDir = filePath.absolutePath();
         else
            workingDir = filePath.parent().absolutePath();
      }
   }

   // do additinal detection if necessary
   if (workingDir.empty())
   {
      // get current path
      FilePath currentPath = FilePath::safeCurrentPath(
                                       core::system::userHomePath());

#if defined(_WIN32) || defined(__APPLE__)

      // detect whether we were launched from the system application menu
      // (e.g. Dock, Program File icon, etc.). we do this by checking
      // whether the executable path is within the current path. if we
      // weren't launched from the system app menu that set the initial
      // wd to the current path

      FilePath exePath;
      Error error = core::system::executablePath(argv[0], &exePath);
      if (!error)
      {
         if (!exePath.isWithin(currentPath))
            workingDir = currentPath.absolutePath();
      }
      else
      {
         LOG_ERROR(error);
      }

#else

      // on linux we take the current working dir if we were launched
      // from within a terminal
      if (core::system::stdoutIsTerminal() &&
         (currentPath != core::system::userHomePath()))
      {
         workingDir = currentPath.absolutePath();
      }

#endif

   }

   // set the working dir if we have one
   if (!workingDir.empty())
      core::system::setenv(kRStudioInitialWorkingDir, workingDir);
}

void setInitialProject(const FilePath& projectFile, QString* pFilename)
{
   core::system::setenv(kRStudioInitialProject, projectFile.absolutePath());
   pFilename->clear();
}

void initializeStartupEnvironment(QString* pFilename)
{
   // if the filename ends with .RData or .rda then this is an
   // environment file. if it ends with .Rproj then it is
   // a project file. we handle both cases by setting an environment
   // var and then resetting the pFilename so it isn't processed
   // using the standard open file logic
   FilePath filePath(pFilename->toUtf8().constData());
   if (filePath.exists())
   {
      std::string ext = filePath.extensionLowerCase();

      // if it is a directory or just an .rdata file then we can see
      // whether there is a project file we can automatically attach to
      if (filePath.isDirectory())
      {
         FilePath projectFile = r_util::projectFromDirectory(filePath);
         if (!projectFile.empty())
         {
            setInitialProject(projectFile, pFilename);
         }
      }
      else if (ext == ".rproj")
      {
         setInitialProject(filePath, pFilename);
      }
      else if (ext == ".rdata" || ext == ".rda")
      {
         core::system::setenv(kRStudioInitialEnvironment, filePath.absolutePath());
         pFilename->clear();
      }

   }
}

QString verifyAndNormalizeFilename(const QString &filename)
{
   if (filename.isNull() || filename.isEmpty())
      return QString();

   QFileInfo fileInfo(filename);
   if (fileInfo.exists())
      return fileInfo.absoluteFilePath();
   else
      return QString();
}

bool isNonProjectFilename(const QString &filename)
{
   if (filename.isNull() || filename.isEmpty())
      return false;

   FilePath filePath(filename.toUtf8().constData());
   return filePath.exists() && filePath.extensionLowerCase() != ".rproj";
}

bool useChromiumDevtools()
{
   // disable by default due to security concerns
   // https://bugreports.qt.io/browse/QTBUG-50725
   bool useDevtools = false;

#ifndef NDEBUG
   // but enable by default for development builds
   useDevtools = true;
#endif

   // enable when environment variable is set
   if (!core::system::getenv("RSTUDIO_USE_CHROMIUM_DEVTOOLS").empty())
   {
      useDevtools = true;
   }

   return useDevtools;
}

} // anonymous namespace

int main(int argc, char* argv[])
{
   core::system::initHook();

   try
   {
      initializeLang();

      if (useChromiumDevtools())
      {
         // use QTcpSocket to find an open port. this is unfortunately a bit racey
         // but AFAICS there isn't a better solution for port selection
         QByteArray port;
         QTcpSocket* pSocket = new QTcpSocket();
         if (pSocket->bind())
         {
            quint16 port = pSocket->localPort();
            desktopInfo().setChromiumDevtoolsPort(port);
            core::system::setenv("QTWEBENGINE_REMOTE_DEBUGGING", safe_convert::numberToString(port));
            pSocket->close();
         }
      }

      // initialize log
      core::system::initializeLog("rdesktop",
                                  core::system::kLogLevelWarning,
                                  desktop::userLogPath());

      // ignore SIGPIPE
      Error error = core::system::ignoreSignal(core::system::SigPipe);
      if (error)
         LOG_ERROR(error);

      // set application attributes
      QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

      // prepare command line arguments
      static std::vector<char*> arguments(argv, argv + argc);

#ifndef NDEBUG
      // disable web security for development builds (so we can
      // get access to sourcemaps)
      static char disableWebSecurity[] = "--disable-web-security";
      arguments.push_back(disableWebSecurity);
#endif

      // disable chromium renderer accessibility by default (it can cause
      // slowdown when used in conjunction with some applications; see e.g.
      // https://github.com/rstudio/rstudio/issues/1990)
      if (core::system::getenv("RSTUDIO_ACCESSIBILITY").empty())
      {
         static char disableRendererAccessibility[] = "--disable-renderer-accessibility";
         arguments.push_back(disableRendererAccessibility);
      }

#ifdef Q_OS_LINUX
      // workaround for Qt 5.10.1 bug "Could not find QtWebEngineProcess"
      // https://bugreports.qt.io/browse/QTBUG-67023
      // https://bugreports.qt.io/browse/QTBUG-66346
      static char noSandbox[] = "--no-sandbox";
      arguments.push_back(noSandbox);
#endif

#ifdef Q_OS_MAC
      // don't prefer compositing to LCD text rendering. when enabled, this causes the compositor to
      // be used too aggressively on Retina displays on macOS, with the side effect that the
      // scrollbar doesn't auto-hide because a compositor layer is present.
      // https://github.com/rstudio/rstudio/issues/1953
      static char disableCompositorPref[] = "--disable-prefer-compositing-to-lcd-text";
      arguments.push_back(disableCompositorPref);

      // speculative fixes for rendering issues on macOS
      // https://bugs.chromium.org/p/chromium/issues/detail?id=773705
      static char disableNativeGpuMemoryBuffers[] = "--disable-native-gpu-memory-buffers";
      arguments.push_back(disableNativeGpuMemoryBuffers);

      static char disableZeroCopy[] = "--disable-zero-copy";
      arguments.push_back(disableZeroCopy);

      static char disableGpuMemoryBufferVideoFrames[] = "--disable-gpu-memory-buffer-video-frames";
      arguments.push_back(disableGpuMemoryBufferVideoFrames);
#endif

      // re-assign command line arguments
      argc = (int) arguments.size();
      argv = &arguments[0];

      // prepare application for launch
      boost::scoped_ptr<QApplication> pApp;
      boost::scoped_ptr<ApplicationLaunch> pAppLaunch;
      ApplicationLaunch::init(QString::fromUtf8("RStudio"),
                              argc,
                              argv,
                              &pApp,
                              &pAppLaunch);

      // determine the filename that was passed to us
      QString filename;
#ifdef __APPLE__
      // get filename from OpenFile apple-event (pump to ensure delivery)
      pApp->processEvents();
      filename = verifyAndNormalizeFilename(
                              pAppLaunch->startupOpenFileRequest());
#endif
      // allow all platforms (including OSX) to check the command line.
      // we include OSX because the way Qt handles apple events is to
      // re-route them to the first instance to register for events. in
      // this case (for projects) we use this to initiate a launch
      // of the application with the project filename on the command line
      if (filename.isEmpty())
      {
         // get filename from command line arguments
         if (pApp->arguments().size() > 1)
         {
            QString arg = pApp->arguments().at(1);
            if (arg != QString::fromUtf8(kRunDiagnosticsOption))
               filename = verifyAndNormalizeFilename(arg);
         }
      }

      // if we have a filename and it is NOT a project file then see
      // if we can open it within an existing instance
      if (isNonProjectFilename(filename))
      {
         if (pAppLaunch->sendMessage(filename))
            return 0;
      }
      else
      {
         // try to register ourselves as a peer for others
         pAppLaunch->attemptToRegisterPeer();
      }

      // init options from command line
      desktop::options().initFromCommandLine(pApp->arguments());

      // reset log if we are in run-diagnostics mode
      if (desktop::options().runDiagnostics())
      {
         desktop::reattachConsoleIfNecessary();
         initializeStderrLog("rdesktop", core::system::kLogLevelWarning);
      }

      initializeSharedSecret();
      initializeWorkingDirectory(argc, argv, filename);
      initializeStartupEnvironment(&filename);

      Options& options = desktop::options();
      if (!prepareEnvironment(options))
         return 1;

      // get install path
      FilePath installPath;
      error = core::system::installPath("..", argv[0], &installPath);
      if (error)
      {
         LOG_ERROR(error);
         return EXIT_FAILURE;
      }

#ifdef _WIN32
      RVersion version = detectRVersion(false);
#endif

      // calculate paths to config file, rsession, and desktop scripts
      FilePath confPath, sessionPath, scriptsPath;
      bool devMode = false;

      // check for debug configuration
      FilePath currentPath = FilePath::safeCurrentPath(installPath);
      if (currentPath.complete("conf/rdesktop-dev.conf").exists())
      {
         confPath = currentPath.complete("conf/rdesktop-dev.conf");
         sessionPath = currentPath.complete("session/rsession");
         scriptsPath = currentPath.complete("desktop");
         devMode = true;
#ifdef _WIN32
         if (version.architecture() == ArchX64)
            sessionPath = installPath.complete("session/x64/rsession");
#endif
      }

      // if there is no conf path then release mode
      if (confPath.empty())
      {
         // default paths (then tweak)
         sessionPath = installPath.complete("bin/rsession");
         scriptsPath = installPath.complete("bin");

         // check for win64 binary on windows
#ifdef _WIN32
         if (version.architecture() == ArchX64)
            sessionPath = installPath.complete("bin/x64/rsession");
#endif

         // check for running in a bundle on OSX
#ifdef __APPLE__
         if (installPath.complete("Info.plist").exists())
         {
            sessionPath = installPath.complete("MacOS/rsession");
            scriptsPath = installPath.complete("MacOS");
         }
#endif
      }
      core::system::fixupExecutablePath(&sessionPath);

      auto* pProxyFactory = new NetworkProxyFactory();
      QNetworkProxyFactory::setApplicationProxyFactory(pProxyFactory);

      // set the scripts path in options
      desktop::options().setScriptsPath(scriptsPath);

      // launch session
      SessionLauncher sessionLauncher(sessionPath, confPath, filename, pAppLaunch.get());
      sessionLauncher.launchFirstSession(installPath, devMode, pApp->arguments());

      ProgressActivator progressActivator;

      int result = pApp->exec();

      desktop::activation().releaseLicense();
      options.cleanUpScratchTempDir();

      return result;
   }
   CATCH_UNEXPECTED_EXCEPTION
}

#ifdef _WIN32
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nShowCmd)
{
   return main(__argc, __argv);
}
#endif
<commit_msg>disable GPU rasterization (#2093)<commit_after>/*
 * DesktopMain.cpp
 *
 * Copyright (C) 2009-18 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 <QtGui>
#include <QPushButton>

#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/scoped_ptr.hpp>

#include <core/Log.hpp>

#include <core/system/FileScanner.hpp>
#include <core/SafeConvert.hpp>
#include <core/StringUtils.hpp>
#include <core/system/System.hpp>
#include <core/system/Environment.hpp>
#include <core/r_util/RProjectFile.hpp>
#include <core/r_util/RUserData.hpp>

#include "DesktopApplicationLaunch.hpp"
#include "DesktopSlotBinders.hpp"
#include "DesktopDetectRHome.hpp"
#include "DesktopUtils.hpp"
#include "DesktopSessionLauncher.hpp"
#include "DesktopProgressActivator.hpp"
#include "DesktopNetworkProxyFactory.hpp"
#include "DesktopActivationOverlay.hpp"

QProcess* pRSessionProcess;
QString sharedSecret;

using namespace rstudio;
using namespace rstudio::core;
using namespace rstudio::desktop;

namespace {

void initializeSharedSecret()
{
   sharedSecret = QString::number(rand())
                  + QString::number(rand())
                  + QString::number(rand());
   std::string value = sharedSecret.toUtf8().constData();
   core::system::setenv("RS_SHARED_SECRET", value);
}

void initializeWorkingDirectory(int argc,
                                char* argv[],
                                const QString& filename)
{
   // bail if we've already got a working directory as a result of
   // a call to openSessionInNewWindow
   if (!core::system::getenv(kRStudioInitialWorkingDir).empty())
      return;

   // calculate what our initial working directory should be
   std::string workingDir;

   // if there is a filename passed to us then use it's path
   if (filename != QString())
   {
      FilePath filePath(filename.toUtf8().constData());
      if (filePath.exists())
      {
         if (filePath.isDirectory())
            workingDir = filePath.absolutePath();
         else
            workingDir = filePath.parent().absolutePath();
      }
   }

   // do additinal detection if necessary
   if (workingDir.empty())
   {
      // get current path
      FilePath currentPath = FilePath::safeCurrentPath(
                                       core::system::userHomePath());

#if defined(_WIN32) || defined(__APPLE__)

      // detect whether we were launched from the system application menu
      // (e.g. Dock, Program File icon, etc.). we do this by checking
      // whether the executable path is within the current path. if we
      // weren't launched from the system app menu that set the initial
      // wd to the current path

      FilePath exePath;
      Error error = core::system::executablePath(argv[0], &exePath);
      if (!error)
      {
         if (!exePath.isWithin(currentPath))
            workingDir = currentPath.absolutePath();
      }
      else
      {
         LOG_ERROR(error);
      }

#else

      // on linux we take the current working dir if we were launched
      // from within a terminal
      if (core::system::stdoutIsTerminal() &&
         (currentPath != core::system::userHomePath()))
      {
         workingDir = currentPath.absolutePath();
      }

#endif

   }

   // set the working dir if we have one
   if (!workingDir.empty())
      core::system::setenv(kRStudioInitialWorkingDir, workingDir);
}

void setInitialProject(const FilePath& projectFile, QString* pFilename)
{
   core::system::setenv(kRStudioInitialProject, projectFile.absolutePath());
   pFilename->clear();
}

void initializeStartupEnvironment(QString* pFilename)
{
   // if the filename ends with .RData or .rda then this is an
   // environment file. if it ends with .Rproj then it is
   // a project file. we handle both cases by setting an environment
   // var and then resetting the pFilename so it isn't processed
   // using the standard open file logic
   FilePath filePath(pFilename->toUtf8().constData());
   if (filePath.exists())
   {
      std::string ext = filePath.extensionLowerCase();

      // if it is a directory or just an .rdata file then we can see
      // whether there is a project file we can automatically attach to
      if (filePath.isDirectory())
      {
         FilePath projectFile = r_util::projectFromDirectory(filePath);
         if (!projectFile.empty())
         {
            setInitialProject(projectFile, pFilename);
         }
      }
      else if (ext == ".rproj")
      {
         setInitialProject(filePath, pFilename);
      }
      else if (ext == ".rdata" || ext == ".rda")
      {
         core::system::setenv(kRStudioInitialEnvironment, filePath.absolutePath());
         pFilename->clear();
      }

   }
}

QString verifyAndNormalizeFilename(const QString &filename)
{
   if (filename.isNull() || filename.isEmpty())
      return QString();

   QFileInfo fileInfo(filename);
   if (fileInfo.exists())
      return fileInfo.absoluteFilePath();
   else
      return QString();
}

bool isNonProjectFilename(const QString &filename)
{
   if (filename.isNull() || filename.isEmpty())
      return false;

   FilePath filePath(filename.toUtf8().constData());
   return filePath.exists() && filePath.extensionLowerCase() != ".rproj";
}

bool useChromiumDevtools()
{
   // disable by default due to security concerns
   // https://bugreports.qt.io/browse/QTBUG-50725
   bool useDevtools = false;

#ifndef NDEBUG
   // but enable by default for development builds
   useDevtools = true;
#endif

   // enable when environment variable is set
   if (!core::system::getenv("RSTUDIO_USE_CHROMIUM_DEVTOOLS").empty())
   {
      useDevtools = true;
   }

   return useDevtools;
}

} // anonymous namespace

int main(int argc, char* argv[])
{
   core::system::initHook();

   try
   {
      initializeLang();

      if (useChromiumDevtools())
      {
         // use QTcpSocket to find an open port. this is unfortunately a bit racey
         // but AFAICS there isn't a better solution for port selection
         QByteArray port;
         QTcpSocket* pSocket = new QTcpSocket();
         if (pSocket->bind())
         {
            quint16 port = pSocket->localPort();
            desktopInfo().setChromiumDevtoolsPort(port);
            core::system::setenv("QTWEBENGINE_REMOTE_DEBUGGING", safe_convert::numberToString(port));
            pSocket->close();
         }
      }

      // initialize log
      core::system::initializeLog("rdesktop",
                                  core::system::kLogLevelWarning,
                                  desktop::userLogPath());

      // ignore SIGPIPE
      Error error = core::system::ignoreSignal(core::system::SigPipe);
      if (error)
         LOG_ERROR(error);

      // set application attributes
      QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

      // prepare command line arguments
      static std::vector<char*> arguments(argv, argv + argc);

#ifndef NDEBUG
      // disable web security for development builds (so we can
      // get access to sourcemaps)
      static char disableWebSecurity[] = "--disable-web-security";
      arguments.push_back(disableWebSecurity);
#endif

      // disable chromium renderer accessibility by default (it can cause
      // slowdown when used in conjunction with some applications; see e.g.
      // https://github.com/rstudio/rstudio/issues/1990)
      if (core::system::getenv("RSTUDIO_ACCESSIBILITY").empty())
      {
         static char disableRendererAccessibility[] = "--disable-renderer-accessibility";
         arguments.push_back(disableRendererAccessibility);
      }

#ifdef Q_OS_LINUX
      // workaround for Qt 5.10.1 bug "Could not find QtWebEngineProcess"
      // https://bugreports.qt.io/browse/QTBUG-67023
      // https://bugreports.qt.io/browse/QTBUG-66346
      static char noSandbox[] = "--no-sandbox";
      arguments.push_back(noSandbox);
#endif

#ifdef Q_OS_MAC
      // don't prefer compositing to LCD text rendering. when enabled, this causes the compositor to
      // be used too aggressively on Retina displays on macOS, with the side effect that the
      // scrollbar doesn't auto-hide because a compositor layer is present.
      // https://github.com/rstudio/rstudio/issues/1953
      static char disableCompositorPref[] = "--disable-prefer-compositing-to-lcd-text";
      arguments.push_back(disableCompositorPref);

      // disable gpu rasterization. this appears to successfully work around
      // some of the weird rendering issues seen while using RStudio.
      //
      // https://bugs.chromium.org/p/chromium/issues/detail?id=773705
      // https://github.com/rstudio/rstudio/issues/2093
      static char disableGpuRasterization[] = "--disable-gpu-rasterization";
      arguments.push_back(disableGpuRasterization);
#endif

      // re-assign command line arguments
      argc = (int) arguments.size();
      argv = &arguments[0];

      // prepare application for launch
      boost::scoped_ptr<QApplication> pApp;
      boost::scoped_ptr<ApplicationLaunch> pAppLaunch;
      ApplicationLaunch::init(QString::fromUtf8("RStudio"),
                              argc,
                              argv,
                              &pApp,
                              &pAppLaunch);

      // determine the filename that was passed to us
      QString filename;
#ifdef __APPLE__
      // get filename from OpenFile apple-event (pump to ensure delivery)
      pApp->processEvents();
      filename = verifyAndNormalizeFilename(
                              pAppLaunch->startupOpenFileRequest());
#endif
      // allow all platforms (including OSX) to check the command line.
      // we include OSX because the way Qt handles apple events is to
      // re-route them to the first instance to register for events. in
      // this case (for projects) we use this to initiate a launch
      // of the application with the project filename on the command line
      if (filename.isEmpty())
      {
         // get filename from command line arguments
         if (pApp->arguments().size() > 1)
         {
            QString arg = pApp->arguments().at(1);
            if (arg != QString::fromUtf8(kRunDiagnosticsOption))
               filename = verifyAndNormalizeFilename(arg);
         }
      }

      // if we have a filename and it is NOT a project file then see
      // if we can open it within an existing instance
      if (isNonProjectFilename(filename))
      {
         if (pAppLaunch->sendMessage(filename))
            return 0;
      }
      else
      {
         // try to register ourselves as a peer for others
         pAppLaunch->attemptToRegisterPeer();
      }

      // init options from command line
      desktop::options().initFromCommandLine(pApp->arguments());

      // reset log if we are in run-diagnostics mode
      if (desktop::options().runDiagnostics())
      {
         desktop::reattachConsoleIfNecessary();
         initializeStderrLog("rdesktop", core::system::kLogLevelWarning);
      }

      initializeSharedSecret();
      initializeWorkingDirectory(argc, argv, filename);
      initializeStartupEnvironment(&filename);

      Options& options = desktop::options();
      if (!prepareEnvironment(options))
         return 1;

      // get install path
      FilePath installPath;
      error = core::system::installPath("..", argv[0], &installPath);
      if (error)
      {
         LOG_ERROR(error);
         return EXIT_FAILURE;
      }

#ifdef _WIN32
      RVersion version = detectRVersion(false);
#endif

      // calculate paths to config file, rsession, and desktop scripts
      FilePath confPath, sessionPath, scriptsPath;
      bool devMode = false;

      // check for debug configuration
      FilePath currentPath = FilePath::safeCurrentPath(installPath);
      if (currentPath.complete("conf/rdesktop-dev.conf").exists())
      {
         confPath = currentPath.complete("conf/rdesktop-dev.conf");
         sessionPath = currentPath.complete("session/rsession");
         scriptsPath = currentPath.complete("desktop");
         devMode = true;
#ifdef _WIN32
         if (version.architecture() == ArchX64)
            sessionPath = installPath.complete("session/x64/rsession");
#endif
      }

      // if there is no conf path then release mode
      if (confPath.empty())
      {
         // default paths (then tweak)
         sessionPath = installPath.complete("bin/rsession");
         scriptsPath = installPath.complete("bin");

         // check for win64 binary on windows
#ifdef _WIN32
         if (version.architecture() == ArchX64)
            sessionPath = installPath.complete("bin/x64/rsession");
#endif

         // check for running in a bundle on OSX
#ifdef __APPLE__
         if (installPath.complete("Info.plist").exists())
         {
            sessionPath = installPath.complete("MacOS/rsession");
            scriptsPath = installPath.complete("MacOS");
         }
#endif
      }
      core::system::fixupExecutablePath(&sessionPath);

      auto* pProxyFactory = new NetworkProxyFactory();
      QNetworkProxyFactory::setApplicationProxyFactory(pProxyFactory);

      // set the scripts path in options
      desktop::options().setScriptsPath(scriptsPath);

      // launch session
      SessionLauncher sessionLauncher(sessionPath, confPath, filename, pAppLaunch.get());
      sessionLauncher.launchFirstSession(installPath, devMode, pApp->arguments());

      ProgressActivator progressActivator;

      int result = pApp->exec();

      desktop::activation().releaseLicense();
      options.cleanUpScratchTempDir();

      return result;
   }
   CATCH_UNEXPECTED_EXCEPTION
}

#ifdef _WIN32
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nShowCmd)
{
   return main(__argc, __argv);
}
#endif
<|endoftext|>


<commit_before>#include <torrentsync/utils/Buffer.h>
#include <torrentsync/dht/RoutingTable.h>
#include <torrentsync/dht/message/query/Ping.h>
#include <torrentsync/dht/message/query/FindNode.h>
#include <torrentsync/dht/Callback.h>
#include <torrentsync/utils/log/Logger.h>

#include <exception> // for not implemented stuff

namespace torrentsync
{
namespace dht
{

using namespace torrentsync;
namespace msg = dht::message;

void RoutingTable::recvMessage(
    const boost::system::error_code& error,
    utils::Buffer buffer,
    std::size_t bytes_transferred,
    const boost::asio::ip::udp::endpoint& sender)
{
    namespace msg = dht::message;

    buffer.resize(bytes_transferred);
    LOG(DEBUG,"Routingtable * received a message from " << sender << " e:"
            << error.message());
    LOG(DATA,"RoutingTable * " << sender << " received " <<
        bytes_transferred <<  " " << pretty_print(buffer));

    // check for errors
    if (error)
    {
        LOG(ERROR, "RoutingTable * recvMessage error: " << error << " " << error.message());
        return;
    }

    // parse the message

    try
    {
        msg::AnyMessage message = msg::parseMessage(buffer);
        LOG(DATA, "RoutingTable * message parsed: " << msg::getString(message));
        processIncomingMessage(message, buffer, sender);
    }
    catch (const msg::MessageException& e)
    {
        LOG(ERROR, "RoutingTable * message parsing failed" << e.what());
        LOG(DATA, "RoutingTable * message parsing failed: " << pretty_print(buffer));
        // @TODO send malformed message error
    }
}

void RoutingTable::processIncomingMessage(
      torrentsync::dht::message::AnyMessage& message,
      const utils::Buffer& buffer,
      const boost::asio::ip::udp::endpoint& sender)
{
    const auto type = msg::getType(message);

    // fetch the node from the tree table
    boost::optional<NodeSPtr> node;
    {
        std::lock_guard<std::mutex> lock_table(_table_mutex);
        node = _table.getNode(NodeData(msg::getID(message)));
    }

    if (!!node) // we already know the node
    {
        const auto endpoint = (*node)->getEndpoint();
        // message dropped if t he data is still fresh but with a different IP.
        if (!!endpoint && *endpoint != sender)
            return;
    }
    else
    {
        // create new node
        const utils::Buffer id = msg::getID(message);
        LOG(DEBUG, "New node: " << pretty_print(id) << " addr: " << sender);
        node = boost::optional<NodeSPtr>(NodeSPtr(new Node(id, sender)));
    }

    // if a callback is registered call it instead of the normal flow
    auto callback = getCallback(message);
    if(!!callback)
    {
        callback->call(message, **node);
    }
    else
    {
        LOG(DEBUG,"Callback not found");
        const auto* query = boost::get<msg::query::Query>(&message);
        if (query != nullptr)
        {
            try
            {
                const auto* ping = boost::get<msg::query::Ping>(query);
                if (ping != nullptr)
                {
                    handleQuery(*ping, **node);
                }

                const auto* find_node = boost::get<msg::query::FindNode>(query);
                if (find_node != nullptr)
                {
                    handleQuery(*find_node, **node);
                }
                else
                {
                   LOG(ERROR, "RoutingTable * unknown query type: " <<
                           pretty_print(buffer) << " - " <<
                           msg::getString(message));
                }
            }
            catch ( const dht::message::MessageException& e )
            {
                LOG(ERROR, " RoutingTable * malformed message: " <<
                       msg::getString(message));
            }
        }
        else if (type == msg::Type::Reply)
        {
            // Replies should be all managed by a Callback
            // Log the message and drop it, it's an unexpected reply.
            // Maybe it's a reply that came to late or what not.
            LOG(INFO, "RoutingTable * received unexpected reply: \n" <<
                    msg::getString(message) << " " << sender);
        }
        else if ( type == msg::Type::Error )
        {
            // same be behaviour as a Reply without a callback
            LOG(WARN, "RoutingTable * received unexpected error: \n" <<
                    msg::getString(message) << " " << sender);
        }
        else
        {
            LOG(ERROR, "RoutingTable * unknown message type: " <<
                    pretty_print(buffer) << " - " << msg::getString(message));
        }
    }

    // @TODO post-process
    // - add the node to the known addresses
    // - update node statistics

    // - add the node to the tree in case it's missing, or set it good
    // - update transaction id if it was a query
}

} // dht
} // torrentsync
<commit_msg>Removed \n from log lines<commit_after>#include <torrentsync/utils/Buffer.h>
#include <torrentsync/dht/RoutingTable.h>
#include <torrentsync/dht/message/query/Ping.h>
#include <torrentsync/dht/message/query/FindNode.h>
#include <torrentsync/dht/Callback.h>
#include <torrentsync/utils/log/Logger.h>

#include <exception> // for not implemented stuff

namespace torrentsync
{
namespace dht
{

using namespace torrentsync;
namespace msg = dht::message;

void RoutingTable::recvMessage(
    const boost::system::error_code& error,
    utils::Buffer buffer,
    std::size_t bytes_transferred,
    const boost::asio::ip::udp::endpoint& sender)
{
    namespace msg = dht::message;

    buffer.resize(bytes_transferred);
    LOG(DEBUG,"Routingtable * received a message from " << sender << " e:"
            << error.message());
    LOG(DATA,"RoutingTable * " << sender << " received " <<
        bytes_transferred <<  " " << pretty_print(buffer));

    // check for errors
    if (error)
    {
        LOG(ERROR, "RoutingTable * recvMessage error: " << error << " " << error.message());
        return;
    }

    // parse the message

    try
    {
        msg::AnyMessage message = msg::parseMessage(buffer);
        LOG(DATA, "RoutingTable * message parsed: " << msg::getString(message));
        processIncomingMessage(message, buffer, sender);
    }
    catch (const msg::MessageException& e)
    {
        LOG(ERROR, "RoutingTable * message parsing failed" << e.what());
        LOG(DATA, "RoutingTable * message parsing failed: " << pretty_print(buffer));
        // @TODO send malformed message error
    }
}

void RoutingTable::processIncomingMessage(
      torrentsync::dht::message::AnyMessage& message,
      const utils::Buffer& buffer,
      const boost::asio::ip::udp::endpoint& sender)
{
    const auto type = msg::getType(message);

    // fetch the node from the tree table
    boost::optional<NodeSPtr> node;
    {
        std::lock_guard<std::mutex> lock_table(_table_mutex);
        node = _table.getNode(NodeData(msg::getID(message)));
    }

    if (!!node) // we already know the node
    {
        const auto endpoint = (*node)->getEndpoint();
        // message dropped if t he data is still fresh but with a different IP.
        if (!!endpoint && *endpoint != sender)
            return;
    }
    else
    {
        // create new node
        const utils::Buffer id = msg::getID(message);
        LOG(DEBUG, "New node: " << pretty_print(id) << " addr: " << sender);
        node = boost::optional<NodeSPtr>(NodeSPtr(new Node(id, sender)));
    }

    // if a callback is registered call it instead of the normal flow
    auto callback = getCallback(message);
    if(!!callback)
    {
        callback->call(message, **node);
    }
    else
    {
        LOG(DEBUG,"Callback not found");
        const auto* query = boost::get<msg::query::Query>(&message);
        if (query != nullptr)
        {
            try
            {
                const auto* ping = boost::get<msg::query::Ping>(query);
                if (ping != nullptr)
                {
                    handleQuery(*ping, **node);
                }

                const auto* find_node = boost::get<msg::query::FindNode>(query);
                if (find_node != nullptr)
                {
                    handleQuery(*find_node, **node);
                }
                else
                {
                   LOG(ERROR, "RoutingTable * unknown query type: " <<
                           pretty_print(buffer) << " - " <<
                           msg::getString(message));
                }
            }
            catch ( const dht::message::MessageException& e )
            {
                LOG(ERROR, " RoutingTable * malformed message: " <<
                       msg::getString(message));
            }
        }
        else if (type == msg::Type::Reply)
        {
            // Replies should be all managed by a Callback
            // Log the message and drop it, it's an unexpected reply.
            // Maybe it's a reply that came to late or what not.
            LOG(INFO, "RoutingTable * received unexpected reply: n" <<
                    msg::getString(message) << " " << sender);
        }
        else if ( type == msg::Type::Error )
        {
            // same be behaviour as a Reply without a callback
            LOG(WARN, "RoutingTable * received unexpected error: n" <<
                    msg::getString(message) << " " << sender);
        }
        else
        {
            LOG(ERROR, "RoutingTable * unknown message type: " <<
                    pretty_print(buffer) << " - " << msg::getString(message));
        }
    }

    // @TODO post-process
    // - add the node to the known addresses
    // - update node statistics

    // - add the node to the tree in case it's missing, or set it good
    // - update transaction id if it was a query
}

} // dht
} // torrentsync
<|endoftext|>