<commit_before>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "mdetailedlistitem.h"
#include "mdetailedlistitem_p.h"

#include <MImageWidget>
#include <MLabel>
#include <MStylableWidget>

#include <QGraphicsGridLayout>
#include <QGraphicsLinearLayout>

MDetailedListItemPrivate::MDetailedListItemPrivate(MDetailedListItem::ItemStyle style)
    : layoutGrid(NULL),
    image(NULL),
    sideTopImage(NULL),
    sideBottomImage(NULL),
    titleLabel(NULL),
    subtitleLabel(NULL),
    sideBottomLabel(NULL),
    listItemStyle(style)
{

}

MDetailedListItemPrivate::~MDetailedListItemPrivate()
{
}

void MDetailedListItemPrivate::createLayout()
{
    Q_Q(MDetailedListItem);

    if (!layoutGrid) {
        layoutGrid = new QGraphicsGridLayout(q);
        layoutGrid->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
        layoutGrid->setContentsMargins(0, 0, 0, 0);
        layoutGrid->setSpacing(0);
    }

    switch (listItemStyle) {
    case MDetailedListItem::IconTitleSubtitleAndTwoSideIcons: {
            q->titleLabelWidget()->setObjectName("CommonTitle");
            q->setIconStyle(MDetailedListItem::Icon);

            layout()->addItem(q->imageWidget(), 0, 0, 3, 1, Qt::AlignLeft | Qt::AlignVCenter);
            layout()->addItem(q->titleLabelWidget(), 0, 1, Qt::AlignLeft | Qt::AlignTop);
            layout()->addItem(q->subtitleLabelWidget(), 1, 1, Qt::AlignLeft | Qt::AlignBottom);
            layout()->addItem(new QGraphicsWidget(q), 2, 1, 1, 2);
            layout()->addItem(q->sideTopImageWidget(), 0, 2, Qt::AlignRight | Qt::AlignBottom);
            layout()->addItem(q->sideBottomImageWidget(), 1, 2, Qt::AlignRight | Qt::AlignTop);

            break;
        }
    case MDetailedListItem::IconTitleSubtitleAndSideIconWithLabel: {
            q->titleLabelWidget()->setObjectName("CommonTitle");
            q->setIconStyle(MDetailedListItem::Icon);

            layout()->addItem(q->imageWidget(), 0, 0, 3, 1, Qt::AlignLeft | Qt::AlignVCenter);

            layout()->addItem(q->titleLabelWidget(), 0, 1, 1, 3, Qt::AlignLeft | Qt::AlignTop);
            layout()->addItem(q->sideTopImageWidget(), 0, 4, Qt::AlignRight | Qt::AlignBottom);

            layout()->addItem(q->subtitleLabelWidget(), 1, 1, 1, 2);

            layout()->addItem(q->sideBottomLabelWidget(), 1, 3, 1, 2);

            layout()->addItem(new QGraphicsWidget(q), 2, 1);
            break;
        }
    case MDetailedListItem::ThumbnailTitleAndTwoSideIcons: {
            q->titleLabelWidget()->setObjectName("CommonSingleTitle");
            q->setIconStyle(MDetailedListItem::Thumbnail);

            layout()->addItem(q->imageWidget(), 0, 0, 2, 1, Qt::AlignLeft | Qt::AlignVCenter);

            layout()->addItem(q->titleLabelWidget(), 0, 1, 2, 1, Qt::AlignLeft | Qt::AlignVCenter);
            layout()->addItem(new QGraphicsWidget(q), 1, 1, Qt::AlignRight | Qt::AlignVCenter);

            QGraphicsWidget * panel = new QGraphicsWidget(q);
            QGraphicsLinearLayout * panelLayout = new QGraphicsLinearLayout(Qt::Vertical);
            panelLayout->setContentsMargins(0, 0, 0, 0);
            panelLayout->setSpacing(0);
            panel->setLayout(panelLayout);

            panelLayout->addItem(q->sideTopImageWidget());
            panelLayout->addItem(q->sideBottomImageWidget());

            layout()->addItem(panel, 0, 2, 2, 1, Qt::AlignVCenter);

            break;
        }
    case MDetailedListItem::ThumbnailTitleSubtitleAndTwoSideIcons: {
            q->titleLabelWidget()->setObjectName("CommonTitle");
            q->setIconStyle(MDetailedListItem::Thumbnail);

            layout()->addItem(q->imageWidget(), 0, 0, 3, 1);

            layout()->addItem(q->titleLabelWidget(), 0, 1);
            layout()->addItem(q->subtitleLabelWidget(), 1, 1);

            QGraphicsWidget * panel = new QGraphicsWidget(q);
            QGraphicsLinearLayout * panelLayout = new QGraphicsLinearLayout(Qt::Vertical);
            panelLayout->setContentsMargins(0, 0, 0, 0);
            panelLayout->setSpacing(0);
            panel->setLayout(panelLayout);

            panelLayout->addItem(q->sideTopImageWidget());
            panelLayout->addItem(q->sideBottomImageWidget());

            layout()->addItem(panel, 0, 2, 3, 1, Qt::AlignVCenter);

            layout()->addItem(new QGraphicsWidget(q), 2, 1);
            break;
        }
    default:
        break;
    }
}

void MDetailedListItemPrivate::clearLayout()
{
    if (layout()) {
        for (int i = 0; i < layout()->count(); i++) {
            QGraphicsLayoutItem *item = layoutGrid->itemAt(0);
            layoutGrid->removeAt(0);
            delete item;
        }
        image = NULL;
        titleLabel = NULL;
        subtitleLabel = NULL;
        sideTopImage = NULL;
        sideBottomImage = NULL;
        sideBottomLabel = NULL;
    }
}

QGraphicsGridLayout *MDetailedListItemPrivate::layout()
{
    return layoutGrid;
}

MDetailedListItem::MDetailedListItem(MDetailedListItem::ItemStyle style, QGraphicsItem *parent)
    : MListItem(parent), d_ptr(new MDetailedListItemPrivate(style))
{
    Q_D(MDetailedListItem);
    d->q_ptr = this;

    setItemStyle(style);
    setObjectName("CommonPanel");
}

MDetailedListItem::~MDetailedListItem()
{
}

void MDetailedListItem::initLayout()
{
    setLayout(createLayout());
}

QGraphicsLayout *MDetailedListItem::createLayout()
{
    Q_D(MDetailedListItem);

    clearLayout();
    d->createLayout();

    return d->layout();
}

void MDetailedListItem::clearLayout()
{
    Q_D(MDetailedListItem);

    d->clearLayout();
}

void MDetailedListItem::setItemStyle(ItemStyle itemStyle)
{
    Q_D(MDetailedListItem);

    if (itemStyle == d->listItemStyle)
        return;

    initLayout();
}

MDetailedListItem::ItemStyle MDetailedListItem::itemStyle() const
{
    Q_D(const MDetailedListItem);
    return d->listItemStyle;
}

void MDetailedListItem::setIconStyle(IconStyle style)
{
    if (style == Thumbnail)
        imageWidget()->setObjectName("CommonThumbnail");
    else if (style == Icon)
        imageWidget()->setObjectName("CommonMainIcon");
}

void MDetailedListItem::setImageWidget(MImageWidget *image)
{
    Q_D(MDetailedListItem);

    for (int i = 0; i < d->layout()->count(); i++) {
        if (d->layout()->itemAt(i) == d->image) {
            d->layout()->removeAt(i);
            delete d->image;
            d->image = NULL;
            break;
        }
    }

    if (image) {
        d->image = image;
        if (d->listItemStyle == MDetailedListItem::IconTitleSubtitleAndTwoSideIcons)
            d->layout()->addItem(d->image, 0, 0, 2, 1, Qt::AlignLeft | Qt::AlignVCenter);
        else if (d->listItemStyle == MDetailedListItem::ThumbnailTitleAndTwoSideIcons ||
                 d->listItemStyle == MDetailedListItem::ThumbnailTitleSubtitleAndTwoSideIcons)
            d->layout()->addItem(d->image, 0, 0, Qt::AlignLeft | Qt::AlignVCenter);
    }
}

MImageWidget *MDetailedListItem::imageWidget()
{
    Q_D(MDetailedListItem);

    if (!d->image) {
        d->image = new MImageWidget(this);
    }
    return d->image;
}

MImageWidget *MDetailedListItem::sideTopImageWidget()
{
    Q_D(MDetailedListItem);

    if (!d->sideTopImage) {
        d->sideTopImage = new MImageWidget(this);
        d->sideTopImage->setObjectName("CommonSubIconTop");
    }
    return d->sideTopImage;
}

MImageWidget *MDetailedListItem::sideBottomImageWidget()
{
    Q_D(MDetailedListItem);

    if (!d->sideBottomImage) {
        d->sideBottomImage = new MImageWidget(this);
        d->sideBottomImage->setObjectName("CommonSubIconBottom");
    }
    return d->sideBottomImage;
}

MLabel *MDetailedListItem::titleLabelWidget()
{
    Q_D(MDetailedListItem);

    if (!d->titleLabel) {
        d->titleLabel = new MLabel(this);
        d->titleLabel->setTextElide(true);
        d->titleLabel->setObjectName("CommonTitle");
    }

    return d->titleLabel;
}

void MDetailedListItem::setTitle(const QString &title)
{
    titleLabelWidget()->setText(title);
}

QString MDetailedListItem::title()
{
    return titleLabelWidget()->text();
}

MLabel *MDetailedListItem::subtitleLabelWidget()
{
    Q_D(MDetailedListItem);

    if (!d->subtitleLabel) {
        d->subtitleLabel = new MLabel(this);
        d->subtitleLabel->setTextElide(true);
        d->subtitleLabel->setObjectName("CommonSubTitle");
    }

    return d->subtitleLabel;
}

void MDetailedListItem::setSubtitle(const QString &subtitle)
{
    subtitleLabelWidget()->setText(subtitle);
}

QString MDetailedListItem::subtitle()
{
    return subtitleLabelWidget()->text();
}

MLabel *MDetailedListItem::sideBottomLabelWidget()
{
    Q_D(MDetailedListItem);

    if (!d->sideBottomLabel) {
        d->sideBottomLabel = new MLabel(this);
        d->sideBottomLabel->setTextElide(true);
        d->sideBottomLabel->setAlignment(Qt::AlignRight);
        d->sideBottomLabel->setObjectName("CommonItemInfo");
    }

    return d->sideBottomLabel;
}

void MDetailedListItem::setSideBottomTitle(const QString &text)
{
    sideBottomLabelWidget()->setText(text);
}

QString MDetailedListItem::sideBottomTitle()
{
    return sideBottomLabelWidget()->text();
}

<commit_msg>fix coverity id 845<commit_after>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "mdetailedlistitem.h"
#include "mdetailedlistitem_p.h"

#include <MImageWidget>
#include <MLabel>
#include <MStylableWidget>

#include <QGraphicsGridLayout>
#include <QGraphicsLinearLayout>

MDetailedListItemPrivate::MDetailedListItemPrivate(MDetailedListItem::ItemStyle style)
    : q_ptr(NULL),
    layoutGrid(NULL),
    image(NULL),
    sideTopImage(NULL),
    sideBottomImage(NULL),
    titleLabel(NULL),
    subtitleLabel(NULL),
    sideBottomLabel(NULL),
    listItemStyle(style)
{

}

MDetailedListItemPrivate::~MDetailedListItemPrivate()
{
}

void MDetailedListItemPrivate::createLayout()
{
    Q_Q(MDetailedListItem);

    if (!layoutGrid) {
        layoutGrid = new QGraphicsGridLayout(q);
        layoutGrid->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
        layoutGrid->setContentsMargins(0, 0, 0, 0);
        layoutGrid->setSpacing(0);
    }

    switch (listItemStyle) {
    case MDetailedListItem::IconTitleSubtitleAndTwoSideIcons: {
            q->titleLabelWidget()->setObjectName("CommonTitle");
            q->setIconStyle(MDetailedListItem::Icon);

            layout()->addItem(q->imageWidget(), 0, 0, 3, 1, Qt::AlignLeft | Qt::AlignVCenter);
            layout()->addItem(q->titleLabelWidget(), 0, 1, Qt::AlignLeft | Qt::AlignTop);
            layout()->addItem(q->subtitleLabelWidget(), 1, 1, Qt::AlignLeft | Qt::AlignBottom);
            layout()->addItem(new QGraphicsWidget(q), 2, 1, 1, 2);
            layout()->addItem(q->sideTopImageWidget(), 0, 2, Qt::AlignRight | Qt::AlignBottom);
            layout()->addItem(q->sideBottomImageWidget(), 1, 2, Qt::AlignRight | Qt::AlignTop);

            break;
        }
    case MDetailedListItem::IconTitleSubtitleAndSideIconWithLabel: {
            q->titleLabelWidget()->setObjectName("CommonTitle");
            q->setIconStyle(MDetailedListItem::Icon);

            layout()->addItem(q->imageWidget(), 0, 0, 3, 1, Qt::AlignLeft | Qt::AlignVCenter);

            layout()->addItem(q->titleLabelWidget(), 0, 1, 1, 3, Qt::AlignLeft | Qt::AlignTop);
            layout()->addItem(q->sideTopImageWidget(), 0, 4, Qt::AlignRight | Qt::AlignBottom);

            layout()->addItem(q->subtitleLabelWidget(), 1, 1, 1, 2);

            layout()->addItem(q->sideBottomLabelWidget(), 1, 3, 1, 2);

            layout()->addItem(new QGraphicsWidget(q), 2, 1);
            break;
        }
    case MDetailedListItem::ThumbnailTitleAndTwoSideIcons: {
            q->titleLabelWidget()->setObjectName("CommonSingleTitle");
            q->setIconStyle(MDetailedListItem::Thumbnail);

            layout()->addItem(q->imageWidget(), 0, 0, 2, 1, Qt::AlignLeft | Qt::AlignVCenter);

            layout()->addItem(q->titleLabelWidget(), 0, 1, 2, 1, Qt::AlignLeft | Qt::AlignVCenter);
            layout()->addItem(new QGraphicsWidget(q), 1, 1, Qt::AlignRight | Qt::AlignVCenter);

            QGraphicsWidget * panel = new QGraphicsWidget(q);
            QGraphicsLinearLayout * panelLayout = new QGraphicsLinearLayout(Qt::Vertical);
            panelLayout->setContentsMargins(0, 0, 0, 0);
            panelLayout->setSpacing(0);
            panel->setLayout(panelLayout);

            panelLayout->addItem(q->sideTopImageWidget());
            panelLayout->addItem(q->sideBottomImageWidget());

            layout()->addItem(panel, 0, 2, 2, 1, Qt::AlignVCenter);

            break;
        }
    case MDetailedListItem::ThumbnailTitleSubtitleAndTwoSideIcons: {
            q->titleLabelWidget()->setObjectName("CommonTitle");
            q->setIconStyle(MDetailedListItem::Thumbnail);

            layout()->addItem(q->imageWidget(), 0, 0, 3, 1);

            layout()->addItem(q->titleLabelWidget(), 0, 1);
            layout()->addItem(q->subtitleLabelWidget(), 1, 1);

            QGraphicsWidget * panel = new QGraphicsWidget(q);
            QGraphicsLinearLayout * panelLayout = new QGraphicsLinearLayout(Qt::Vertical);
            panelLayout->setContentsMargins(0, 0, 0, 0);
            panelLayout->setSpacing(0);
            panel->setLayout(panelLayout);

            panelLayout->addItem(q->sideTopImageWidget());
            panelLayout->addItem(q->sideBottomImageWidget());

            layout()->addItem(panel, 0, 2, 3, 1, Qt::AlignVCenter);

            layout()->addItem(new QGraphicsWidget(q), 2, 1);
            break;
        }
    default:
        break;
    }
}

void MDetailedListItemPrivate::clearLayout()
{
    if (layout()) {
        for (int i = 0; i < layout()->count(); i++) {
            QGraphicsLayoutItem *item = layoutGrid->itemAt(0);
            layoutGrid->removeAt(0);
            delete item;
        }
        image = NULL;
        titleLabel = NULL;
        subtitleLabel = NULL;
        sideTopImage = NULL;
        sideBottomImage = NULL;
        sideBottomLabel = NULL;
    }
}

QGraphicsGridLayout *MDetailedListItemPrivate::layout()
{
    return layoutGrid;
}

MDetailedListItem::MDetailedListItem(MDetailedListItem::ItemStyle style, QGraphicsItem *parent)
    : MListItem(parent), d_ptr(new MDetailedListItemPrivate(style))
{
    Q_D(MDetailedListItem);
    d->q_ptr = this;

    setItemStyle(style);
    setObjectName("CommonPanel");
}

MDetailedListItem::~MDetailedListItem()
{
}

void MDetailedListItem::initLayout()
{
    setLayout(createLayout());
}

QGraphicsLayout *MDetailedListItem::createLayout()
{
    Q_D(MDetailedListItem);

    clearLayout();
    d->createLayout();

    return d->layout();
}

void MDetailedListItem::clearLayout()
{
    Q_D(MDetailedListItem);

    d->clearLayout();
}

void MDetailedListItem::setItemStyle(ItemStyle itemStyle)
{
    Q_D(MDetailedListItem);

    if (itemStyle == d->listItemStyle)
        return;

    initLayout();
}

MDetailedListItem::ItemStyle MDetailedListItem::itemStyle() const
{
    Q_D(const MDetailedListItem);
    return d->listItemStyle;
}

void MDetailedListItem::setIconStyle(IconStyle style)
{
    if (style == Thumbnail)
        imageWidget()->setObjectName("CommonThumbnail");
    else if (style == Icon)
        imageWidget()->setObjectName("CommonMainIcon");
}

void MDetailedListItem::setImageWidget(MImageWidget *image)
{
    Q_D(MDetailedListItem);

    for (int i = 0; i < d->layout()->count(); i++) {
        if (d->layout()->itemAt(i) == d->image) {
            d->layout()->removeAt(i);
            delete d->image;
            d->image = NULL;
            break;
        }
    }

    if (image) {
        d->image = image;
        if (d->listItemStyle == MDetailedListItem::IconTitleSubtitleAndTwoSideIcons)
            d->layout()->addItem(d->image, 0, 0, 2, 1, Qt::AlignLeft | Qt::AlignVCenter);
        else if (d->listItemStyle == MDetailedListItem::ThumbnailTitleAndTwoSideIcons ||
                 d->listItemStyle == MDetailedListItem::ThumbnailTitleSubtitleAndTwoSideIcons)
            d->layout()->addItem(d->image, 0, 0, Qt::AlignLeft | Qt::AlignVCenter);
    }
}

MImageWidget *MDetailedListItem::imageWidget()
{
    Q_D(MDetailedListItem);

    if (!d->image) {
        d->image = new MImageWidget(this);
    }
    return d->image;
}

MImageWidget *MDetailedListItem::sideTopImageWidget()
{
    Q_D(MDetailedListItem);

    if (!d->sideTopImage) {
        d->sideTopImage = new MImageWidget(this);
        d->sideTopImage->setObjectName("CommonSubIconTop");
    }
    return d->sideTopImage;
}

MImageWidget *MDetailedListItem::sideBottomImageWidget()
{
    Q_D(MDetailedListItem);

    if (!d->sideBottomImage) {
        d->sideBottomImage = new MImageWidget(this);
        d->sideBottomImage->setObjectName("CommonSubIconBottom");
    }
    return d->sideBottomImage;
}

MLabel *MDetailedListItem::titleLabelWidget()
{
    Q_D(MDetailedListItem);

    if (!d->titleLabel) {
        d->titleLabel = new MLabel(this);
        d->titleLabel->setTextElide(true);
        d->titleLabel->setObjectName("CommonTitle");
    }

    return d->titleLabel;
}

void MDetailedListItem::setTitle(const QString &title)
{
    titleLabelWidget()->setText(title);
}

QString MDetailedListItem::title()
{
    return titleLabelWidget()->text();
}

MLabel *MDetailedListItem::subtitleLabelWidget()
{
    Q_D(MDetailedListItem);

    if (!d->subtitleLabel) {
        d->subtitleLabel = new MLabel(this);
        d->subtitleLabel->setTextElide(true);
        d->subtitleLabel->setObjectName("CommonSubTitle");
    }

    return d->subtitleLabel;
}

void MDetailedListItem::setSubtitle(const QString &subtitle)
{
    subtitleLabelWidget()->setText(subtitle);
}

QString MDetailedListItem::subtitle()
{
    return subtitleLabelWidget()->text();
}

MLabel *MDetailedListItem::sideBottomLabelWidget()
{
    Q_D(MDetailedListItem);

    if (!d->sideBottomLabel) {
        d->sideBottomLabel = new MLabel(this);
        d->sideBottomLabel->setTextElide(true);
        d->sideBottomLabel->setAlignment(Qt::AlignRight);
        d->sideBottomLabel->setObjectName("CommonItemInfo");
    }

    return d->sideBottomLabel;
}

void MDetailedListItem::setSideBottomTitle(const QString &text)
{
    sideBottomLabelWidget()->setText(text);
}

QString MDetailedListItem::sideBottomTitle()
{
    return sideBottomLabelWidget()->text();
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015, The Regents of the University of California (Regents).
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *
 *    3. Neither the name of the copyright holder nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Please contact the author(s) of this library if you have any questions.
 * Authors: Erik Nelson            ( eanelson@eecs.berkeley.edu )
 *          David Fridovich-Keil   ( dfk@eecs.berkeley.edu )
 */

#include "pose_estimator_2d_3d.h"

#include <ceres/ceres.h>
#include <Eigen/SVD>
#include <glog/logging.h>

#include "normalization.h"
#include "../optimization/cost_functors.h"

namespace bsfm {

PoseEstimator2D3D::PoseEstimator2D3D()
    : T_(Matrix3d::Identity()), U_(Matrix4d::Identity()) {}

PoseEstimator2D3D::~PoseEstimator2D3D() {}

bool PoseEstimator2D3D::Initialize(const FeatureList& points_2d,
                                   const Point3DList& points_3d,
                                   const CameraIntrinsics& intrinsics) {
  if (points_2d.size() != points_3d.size()) {
    VLOG(1) << "Inputs 'points_2d' and 'points_3d' do not contain the "
               "same number of elements.";
    return false;
  }

  // Copy camera intrinsics.
  intrinsics_ = intrinsics;

  // Normalize the 3D points.
  T_ = ComputeNormalization(points_2d);
  U_ = ComputeNormalization(points_3d);

  points_2d_.clear();
  for (size_t ii = 0; ii < points_2d.size(); ++ii) {
    // Get (u, v) image-space point and normalize.
    double u = points_2d[ii].u_;
    double v = points_2d[ii].v_;
    u = T_(0, 0) * u + T_(0, 2);
    v = T_(1, 1) * v + T_(1, 2);

    points_2d_.push_back(Feature(u, v));
  }

  points_3d_.clear();
  for (size_t ii = 0; ii < points_3d.size(); ++ii) {
    // Get (x, y, z) world-space point and normalize.
    double x = points_3d[ii].X();
    double y = points_3d[ii].Y();
    double z = points_3d[ii].Z();
    x = U_(0, 0) * x + U_(0, 3);
    y = U_(1, 1) * y + U_(1, 3);
    z = U_(2, 2) * z + U_(2, 3);

    points_3d_.push_back(Point3D(x, y, z));
  }

  return true;
}

bool PoseEstimator2D3D::Solve(Pose& camera_pose) {
  // Get an initial P.
  Matrix34d P;
  if (!ComputeInitialSolution(P)) {
    VLOG(1) << "Failed to compute an initial solution for P.";
    return false;
  }

  // Refine P with non-linear optimization.
  Matrix34d P_opt = P;
  if (!OptimizeSolution(P_opt)) {
    VLOG(1) << "Failed to optimize P. Continuing using the initial solution.";
    P_opt = P;
  }

  // Get the camera pose from the computed projection matrix.
  if (!ExtractPose(P_opt, camera_pose)) {
    VLOG(1) << "Computed rotation is non-invertible.";
    return false;
  }

  return true;
}

bool PoseEstimator2D3D::ComputeInitialSolution(Matrix34d& initial_solution) {
  // Use Eq. 7.2 from H&Z: Multiple-View Geometry to determine an
  // over-determined solution for the camera projection matrix P.

  // First build the A matrix, which is 2n x 12.
  MatrixXd A;
  A.resize(points_2d_.size() * 2, 12);
  for (size_t ii = 0; ii < points_2d_.size(); ii++) {
    // Get (u, v) image-space point.
    double u = points_2d_[ii].u_;
    double v = points_2d_[ii].v_;

    // Get (x, y, z) world-space point.
    double x = points_3d_[ii].X();
    double y = points_3d_[ii].Y();
    double z = points_3d_[ii].Z();

    // First 4 columns, top row of block.
    A(2*ii+0, 0) = 0;
    A(2*ii+0, 1) = 0;
    A(2*ii+0, 2) = 0;
    A(2*ii+0, 3) = 0;

    // Second 4 columns, top row of block.
    A(2*ii+0, 4) = -x;
    A(2*ii+0, 5) = -y;
    A(2*ii+0, 6) = -z;
    A(2*ii+0, 7) = -1;

    // Third 4 columns, top row of block.
    A(2*ii+0, 8) = v*x;
    A(2*ii+0, 9) = v*y;
    A(2*ii+0, 10) = v*z;
    A(2*ii+0, 11) = v;

    // First 4 columns, bottom row of block.
    A(2*ii+1, 0) = x;
    A(2*ii+1, 1) = y;
    A(2*ii+1, 2) = z;
    A(2*ii+1, 3) = 1;

    // Second 4 columns, bottom row of block.
    A(2*ii+1, 4) = 0;
    A(2*ii+1, 5) = 0;
    A(2*ii+1, 6) = 0;
    A(2*ii+1, 7) = 0;

    // Third 4 columns, bottom row of block.
    A(2*ii+1, 8) = -u*x;
    A(2*ii+1, 9) = -u*y;
    A(2*ii+1, 10) = -u*z;
    A(2*ii+1, 11) = -u;
  }

  // Get svd(A). Save some time and compute a thin U. We still need a full V.
  Eigen::JacobiSVD<Eigen::MatrixXd> svd;
  svd.compute(A, Eigen::ComputeThinU | Eigen::ComputeFullV);
  if (!svd.computeV()) {
    VLOG(1) << "Failed to compute a singular value decomposition of A matrix.";
    return false;
  }

  // Get the projection matrix elements from the SVD decomposition.
  const VectorXd P_vec = svd.matrixV().col(11).normalized();

  // Reshape the vector into the initial solution.
  initial_solution.row(0) = P_vec.topRows(4).transpose();
  initial_solution.row(1) = P_vec.middleRows(4, 4).transpose();
  initial_solution.row(2) = P_vec.bottomRows(4).transpose();

  return true;
}

bool PoseEstimator2D3D::OptimizeSolution(Matrix34d& solution) {
  // Create the cost function.
  ceres::Problem problem;

  // Create the output container and initialize with the least-squares solution.
  double P[12] = {solution(0,0), solution(0,1), solution(0,2), solution(0,3),
                  solution(1,0), solution(1,1), solution(1,2), solution(1,3),
                  solution(2,0), solution(2,1), solution(2,2), solution(2,3)};

  // Set up the problem and solve it.
  const int kNumResiduals = 1;  // each correspondence generates 1 residual.
  const int kNumVariables = 1;  // only optimizing P.

  // Make a cost function for each correspondence using the
  // GeometricProjectionError cost functor.
  for (size_t ii = 0; ii < points_2d_.size(); ++ii) {
    problem.AddResidualBlock(new ceres::AutoDiffCostFunction<
                             GeometricProjectionError, kNumResiduals, kNumVariables>(
        new GeometricProjectionError(points_2d_[ii], points_3d_[ii])), NULL, P);
  }

  ceres::Solver::Summary summary;
  ceres::Solver::Options options;
  options.trust_region_strategy_type = ceres::LEVENBERG_MARQUARDT;
  ceres::Solve(options, &problem, &summary);

  // Store the solved variable back in 'solution'.
  if (summary.IsSolutionUsable()) {
    for (int row = 0; row < 3; ++row)
      for (int col = 0; col < 4; ++col)
        solution(row, col) = P[row * 4 + col];
  }

  return summary.IsSolutionUsable();
}

bool PoseEstimator2D3D::ExtractPose(const Matrix34d& P, Pose& pose) {
  // Un-normalize the projection matrix.
  const  Matrix34d P_unnormalized = T_.inverse() * P * U_;

  // Extract camera extrinsics matrix.
  Matrix34d Rt = intrinsics_.Kinv() * P_unnormalized;

  // [R|t] is only determined up to scale. To get this scale, note that det(R)
  // must equal 1. Also note that for an nxn matrix, c^n*det(R) = det(cR).
  // Use this property to scale our matrix.
  double alpha = Rt.block(0, 0, 3, 3).determinant();
  if (std::abs(alpha) < 1e-8) {
    LOG(WARNING) << "Computed rotation has a determinant of 0.";
    return false;
  }

  // Make sure the determinant is positive.
  if (alpha < 0.0) {
    alpha *= -1.0;
    Rt *= -1.0;
  }

  // Normalize the rotation and translation.
  Rt *= std::pow(1.0 / alpha, 1.0 / 3.0);

  // Initialize the output pose from the rotation and translation blocks.
  pose = Pose(Rt);
  return true;
}

}  //\namespace bsfm
<commit_msg>Nit. 'alpha'-->'det', since it's a determinant.<commit_after>/*
 * Copyright (c) 2015, The Regents of the University of California (Regents).
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *
 *    3. Neither the name of the copyright holder nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Please contact the author(s) of this library if you have any questions.
 * Authors: Erik Nelson            ( eanelson@eecs.berkeley.edu )
 *          David Fridovich-Keil   ( dfk@eecs.berkeley.edu )
 */

#include "pose_estimator_2d_3d.h"

#include <ceres/ceres.h>
#include <Eigen/SVD>
#include <glog/logging.h>

#include "normalization.h"
#include "../optimization/cost_functors.h"

namespace bsfm {

PoseEstimator2D3D::PoseEstimator2D3D()
    : T_(Matrix3d::Identity()), U_(Matrix4d::Identity()) {}

PoseEstimator2D3D::~PoseEstimator2D3D() {}

bool PoseEstimator2D3D::Initialize(const FeatureList& points_2d,
                                   const Point3DList& points_3d,
                                   const CameraIntrinsics& intrinsics) {
  if (points_2d.size() != points_3d.size()) {
    VLOG(1) << "Inputs 'points_2d' and 'points_3d' do not contain the "
               "same number of elements.";
    return false;
  }

  // Copy camera intrinsics.
  intrinsics_ = intrinsics;

  // Normalize the 3D points.
  T_ = ComputeNormalization(points_2d);
  U_ = ComputeNormalization(points_3d);

  points_2d_.clear();
  for (size_t ii = 0; ii < points_2d.size(); ++ii) {
    // Get (u, v) image-space point and normalize.
    double u = points_2d[ii].u_;
    double v = points_2d[ii].v_;
    u = T_(0, 0) * u + T_(0, 2);
    v = T_(1, 1) * v + T_(1, 2);

    points_2d_.push_back(Feature(u, v));
  }

  points_3d_.clear();
  for (size_t ii = 0; ii < points_3d.size(); ++ii) {
    // Get (x, y, z) world-space point and normalize.
    double x = points_3d[ii].X();
    double y = points_3d[ii].Y();
    double z = points_3d[ii].Z();
    x = U_(0, 0) * x + U_(0, 3);
    y = U_(1, 1) * y + U_(1, 3);
    z = U_(2, 2) * z + U_(2, 3);

    points_3d_.push_back(Point3D(x, y, z));
  }

  return true;
}

bool PoseEstimator2D3D::Solve(Pose& camera_pose) {
  // Get an initial P.
  Matrix34d P;
  if (!ComputeInitialSolution(P)) {
    VLOG(1) << "Failed to compute an initial solution for P.";
    return false;
  }

  // Refine P with non-linear optimization.
  Matrix34d P_opt = P;
  if (!OptimizeSolution(P_opt)) {
    VLOG(1) << "Failed to optimize P. Continuing using the initial solution.";
    P_opt = P;
  }

  // Get the camera pose from the computed projection matrix.
  if (!ExtractPose(P_opt, camera_pose)) {
    VLOG(1) << "Computed rotation is non-invertible.";
    return false;
  }

  return true;
}

bool PoseEstimator2D3D::ComputeInitialSolution(Matrix34d& initial_solution) {
  // Use Eq. 7.2 from H&Z: Multiple-View Geometry to determine an
  // over-determined solution for the camera projection matrix P.

  // First build the A matrix, which is 2n x 12.
  MatrixXd A;
  A.resize(points_2d_.size() * 2, 12);
  for (size_t ii = 0; ii < points_2d_.size(); ii++) {
    // Get (u, v) image-space point.
    double u = points_2d_[ii].u_;
    double v = points_2d_[ii].v_;

    // Get (x, y, z) world-space point.
    double x = points_3d_[ii].X();
    double y = points_3d_[ii].Y();
    double z = points_3d_[ii].Z();

    // First 4 columns, top row of block.
    A(2*ii+0, 0) = 0;
    A(2*ii+0, 1) = 0;
    A(2*ii+0, 2) = 0;
    A(2*ii+0, 3) = 0;

    // Second 4 columns, top row of block.
    A(2*ii+0, 4) = -x;
    A(2*ii+0, 5) = -y;
    A(2*ii+0, 6) = -z;
    A(2*ii+0, 7) = -1;

    // Third 4 columns, top row of block.
    A(2*ii+0, 8) = v*x;
    A(2*ii+0, 9) = v*y;
    A(2*ii+0, 10) = v*z;
    A(2*ii+0, 11) = v;

    // First 4 columns, bottom row of block.
    A(2*ii+1, 0) = x;
    A(2*ii+1, 1) = y;
    A(2*ii+1, 2) = z;
    A(2*ii+1, 3) = 1;

    // Second 4 columns, bottom row of block.
    A(2*ii+1, 4) = 0;
    A(2*ii+1, 5) = 0;
    A(2*ii+1, 6) = 0;
    A(2*ii+1, 7) = 0;

    // Third 4 columns, bottom row of block.
    A(2*ii+1, 8) = -u*x;
    A(2*ii+1, 9) = -u*y;
    A(2*ii+1, 10) = -u*z;
    A(2*ii+1, 11) = -u;
  }

  // Get svd(A). Save some time and compute a thin U. We still need a full V.
  Eigen::JacobiSVD<Eigen::MatrixXd> svd;
  svd.compute(A, Eigen::ComputeThinU | Eigen::ComputeFullV);
  if (!svd.computeV()) {
    VLOG(1) << "Failed to compute a singular value decomposition of A matrix.";
    return false;
  }

  // Get the projection matrix elements from the SVD decomposition.
  const VectorXd P_vec = svd.matrixV().col(11).normalized();

  // Reshape the vector into the initial solution.
  initial_solution.row(0) = P_vec.topRows(4).transpose();
  initial_solution.row(1) = P_vec.middleRows(4, 4).transpose();
  initial_solution.row(2) = P_vec.bottomRows(4).transpose();

  return true;
}

bool PoseEstimator2D3D::OptimizeSolution(Matrix34d& solution) {
  // Create the cost function.
  ceres::Problem problem;

  // Create the output container and initialize with the least-squares solution.
  double P[12] = {solution(0,0), solution(0,1), solution(0,2), solution(0,3),
                  solution(1,0), solution(1,1), solution(1,2), solution(1,3),
                  solution(2,0), solution(2,1), solution(2,2), solution(2,3)};

  // Set up the problem and solve it.
  const int kNumResiduals = 1;  // each correspondence generates 1 residual.
  const int kNumVariables = 1;  // only optimizing P.

  // Make a cost function for each correspondence using the
  // GeometricProjectionError cost functor.
  for (size_t ii = 0; ii < points_2d_.size(); ++ii) {
    problem.AddResidualBlock(new ceres::AutoDiffCostFunction<
                             GeometricProjectionError, kNumResiduals, kNumVariables>(
        new GeometricProjectionError(points_2d_[ii], points_3d_[ii])), NULL, P);
  }

  ceres::Solver::Summary summary;
  ceres::Solver::Options options;
  options.trust_region_strategy_type = ceres::LEVENBERG_MARQUARDT;
  ceres::Solve(options, &problem, &summary);

  // Store the solved variable back in 'solution'.
  if (summary.IsSolutionUsable()) {
    for (int row = 0; row < 3; ++row)
      for (int col = 0; col < 4; ++col)
        solution(row, col) = P[row * 4 + col];
  }

  return summary.IsSolutionUsable();
}

bool PoseEstimator2D3D::ExtractPose(const Matrix34d& P, Pose& pose) {
  // Un-normalize the projection matrix.
  const  Matrix34d P_unnormalized = T_.inverse() * P * U_;

  // Extract camera extrinsics matrix.
  Matrix34d Rt = intrinsics_.Kinv() * P_unnormalized;

  // [R|t] is only determined up to scale. To get this scale, note that det(R)
  // must equal 1. Also note that for an nxn matrix, c^n*det(R) = det(cR).
  // Use this property to scale our matrix.
  double det = Rt.block(0, 0, 3, 3).determinant();
  if (std::abs(det) < 1e-8) {
    LOG(WARNING) << "Computed rotation has a determinant of 0.";
    return false;
  }

  // Make sure the determinant is positive.
  if (det < 0.0) {
    det *= -1.0;
    Rt *= -1.0;
  }

  // Normalize the rotation and translation.
  Rt *= std::pow(1.0 / det, 1.0 / 3.0);

  // Initialize the output pose from the rotation and translation blocks.
  pose = Pose(Rt);
  return true;
}

}  //\namespace bsfm
<|endoftext|>


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

#ifndef SERVER_AUTH_HANDLER_CPP
#define SERVER_AUTH_HANDLER_CPP

#include <server/auth/ServerAuthHandler.hpp>

#include <boost/algorithm/string.hpp>

#include <core/FileLock.hpp>
#include <core/FileSerializer.hpp>
#include <core/json/JsonRpc.hpp>
#include <core/system/FileMode.hpp>
#include <core/system/PosixUser.hpp>
#include <core/Thread.hpp>

#include <server/ServerConstants.hpp>
#include <server/ServerObject.hpp>
#include <server/ServerOptions.hpp>
#include <server/ServerUriHandlers.hpp>

#include <server/auth/ServerSecureUriHandler.hpp>

#include <session/SessionScopes.hpp>

using namespace rstudio::core;

namespace rstudio {
namespace server {
namespace auth {
namespace handler {

namespace {

FilePath s_revocationList;
FilePath s_revocationLockFile;

boost::posix_time::ptime cookieExpiration(const std::string& cookie)
{
   std::vector<std::string> cookieParts;
   boost::algorithm::split(cookieParts, cookie, boost::is_any_of("|"));

   if (cookieParts.size() > 1)
   {
      std::string expiration = cookieParts[1];
      std::string expirationStr = http::util::urlDecode(expiration);
      return http::util::parseHttpDate(expirationStr);
   }
   core::Error initialize();
   core::Error isUserLicensed(const std::string& username,
                              bool* pLicensed);
   bool isUserListCookieValid(const std::string& cookieValue);
   bool shouldShowUserLicenseWarning();
   std::string getUserListCookieValue();
   unsigned int getActiveUserCount();
   core::json::Array getLicensedUsers();
   core::Error lockUser(boost::asio::io_service& ioService, const std::string& username);
   core::Error unlockUser(boost::asio::io_service& ioService, const std::string& username);
   return boost::posix_time::second_clock::universal_time();
}

// map of last user sign in times to prevent users from creating
// inordinate amounts of revocation entries
std::map<std::string, boost::posix_time::ptime> s_loginTimes;

// sorted array of revoked cookies, where the first to expire appears as the first element
// allows for quickly removing the first element
std::deque<RevokedCookie> s_revokedCookies;

// mutex for providing concurrent access to internal structures
// necessary because auth happens on the thread pool
boost::mutex s_mutex;

// global auth handler
Handler s_handler;

void updateCredentialsNotSupported(
      boost::shared_ptr<core::http::AsyncConnection> pConnection)
{
   // alias response
   http::Response* pResponse = &(pConnection->response());

   // gwt form panel requires text/html content type
   pResponse->setContentType("text/html");

   // set method not found error
   Error methodNotFoundError(json::errc::MethodNotFound, ERROR_LOCATION);
   json::setJsonRpcError(methodNotFoundError, pResponse);

   // write response
   pConnection->writeResponse();
}

bool isCookieRevoked(const std::string& cookie)
{
   boost::posix_time::ptime now = boost::posix_time::second_clock::universal_time();

   LOCK_MUTEX(s_mutex)
   {
      // check for cookie in revocation list, deleting expired elements as we go
      for (auto it = s_revokedCookies.begin(); it != s_revokedCookies.end();)
      {
         const RevokedCookie& other = *it;
         if (other.cookie == cookie)
            return true;

         if (other.expiration <= now)
         {
            it = s_revokedCookies.erase(it);
            continue;
         }
         else
         {
            ++it;
         }
      }
   }
   END_LOCK_MUTEX

   return false;
}

Error readRevocationList(std::vector<std::string>* pEntries)
{
   // read the current revocation list
   Error error = readStringVectorFromFile(s_revocationList, pEntries);
   if (error)
      return error;

   // remove stale entries from the list
   boost::posix_time::ptime now = boost::posix_time::second_clock::universal_time();
   for (int i = pEntries->size() - 1; i >= 0; i--)
   {
      const std::string& cookie = pEntries->at(i);

      boost::posix_time::ptime expirationDate = cookieExpiration(cookie);

      if (expirationDate <= now)
         pEntries->erase(pEntries->begin() + i);
   }

   return Success();
}

} // anonymous namespace

namespace overlay {

Error initialize()
{
   return Success();
}

Error isUserLicensed(const std::string& username,
                     bool* pLicensed)
{
   *pLicensed = true;
   return Success();
}

bool isUserListCookieValid(const std::string& cookieValue)
{
   return true;
}

bool shouldShowUserLicenseWarning()
{
   return false;
}

std::string getUserListCookieValue()
{
   return "9c16856330a7400cbbbba228392a5d83";
}

unsigned int getActiveUserCount()
{
   return 0;
}

json::Array getLicensedUsers()
{
   return json::Array();
}

Error lockUser(boost::asio::io_service& ioService,
               const std::string& username)
{
   return Success();
}

Error unlockUser(boost::asio::io_service& ioService,
                 const std::string& username)
{
   return Success();
}

} // namespace overlay

void onCookieRevoked(const std::string& cookie)
{
}

// uri constants
const char * const kSignIn = "/auth-sign-in";
const char * const kSignOut = "/auth-sign-out";
const char * const kRefreshCredentialsAndContinue = "/auth-refresh-credentials";

void onCookieRevoked(const std::string& cookie);

RevokedCookie::RevokedCookie(const std::string& cookie)
{
   this->cookie = cookie;
   this->expiration = cookieExpiration(cookie);
}

std::string getUserIdentifier(const core::http::Request& request,
                              bool requireUserListCookie,
                              http::Response* pResponse)
{
   if (isCookieRevoked(request.cookieValue(kUserIdCookie)))
      return std::string();

   if (requireUserListCookie)
   {
      if (!overlay::isUserListCookieValid(request.cookieValue(kUserListCookie)))
         return std::string();
   }

   return s_handler.getUserIdentifier(request, pResponse);
}

std::string userIdentifierToLocalUsername(const std::string& userIdentifier)
{
   return s_handler.userIdentifierToLocalUsername(userIdentifier);
}

bool mainPageFilter(const core::http::Request& request,
                    core::http::Response* pResponse)
{
   return s_handler.mainPageFilter(request, pResponse);
}

void signInThenContinue(const core::http::Request& request,
                        core::http::Response* pResponse)
{
   s_handler.signInThenContinue(request, pResponse);
}

void refreshCredentialsThenContinue(
      boost::shared_ptr<core::http::AsyncConnection> pConnection)
{
   s_handler.refreshCredentialsThenContinue(pConnection);
}

// register the auth handler
void registerHandler(const Handler& handler)
{
   // set handler functions
   s_handler = handler;

   // register uri handlers
   uri_handlers::addBlocking(kSignIn, s_handler.signIn);

   uri_handlers::addBlocking(kSignOut,
                             auth::secureHttpHandler(
                                boost::bind(s_handler.signOut, _2, _3)));

   uri_handlers::add(kRefreshCredentialsAndContinue,
                     s_handler.refreshCredentialsThenContinue);

   uri_handlers::add("/auth-update-credentials",
                     s_handler.updateCredentials ?
                        s_handler.updateCredentials :
                        updateCredentialsNotSupported);
}

// is there a handler already registered?
bool isRegistered()
{
   return ! s_handler.getUserIdentifier.empty();
}

bool canSetSignInCookies()
{
   return !s_handler.setSignInCookies.empty();
}

void setSignInCookies(const core::http::Request& request,
                      const std::string& username,
                      bool persist,
                      core::http::Response* pResponse)
{
   s_handler.setSignInCookies(request, username, persist, pResponse);
}

void signOut(const http::Request& request, http::Response* pResponse)
{
   s_handler.signOut(request, pResponse);
}

bool isUserSignInThrottled(const std::string& user)
{
   LOCK_MUTEX(s_mutex)
   {
      auto it = s_loginTimes.find(user);

      boost::posix_time::ptime now = boost::posix_time::second_clock::universal_time();
      if (it == s_loginTimes.end())
      {
         s_loginTimes.insert({user, now});
         return false;
      }

      if (it->second >
          now - boost::posix_time::seconds(options().authSignInThrottleSeconds()))
      {
         // user is trying to sign back in too quickly
         // prevent the request
         return true;
      }
      else
      {
         // user is fine to sign in - update their last sign in time to now
         it->second = now;
      }

      return false;
   }
   END_LOCK_MUTEX

   return false;
}

void refreshAuthCookies(const std::string& userIdentifier,
                        const http::Request& request,
                        http::Response* pResponse)
{
   if (server::options().authTimeoutMinutes() > 0 &&
       !s_handler.refreshAuthCookies.empty())
   {
      // clear any existing auth cookies first - this method can be invoked multiple
      // times depending on the handler type (for example, an upload handler)
      pResponse->clearCookies();
      bool persist = request.cookieValue(kPersistAuthCookie) == "1" ? true : false;
      s_handler.refreshAuthCookies(request, userIdentifier, persist, pResponse);
   }
}

void insertRevokedCookie(const RevokedCookie& cookie)
{
   // do not insert the cookie if it is already expired
   if (cookie.expiration <= boost::posix_time::second_clock::universal_time())
      return;

   LOCK_MUTEX(s_mutex)
   {
      for (auto it = s_revokedCookies.begin(); it != s_revokedCookies.end(); ++it)
      {
         const RevokedCookie& other = *it;
         if (other.expiration > cookie.expiration)
         {
            s_revokedCookies.insert(it, cookie);
            return;
         }
      }

      // no elements were greater - insert at the end
      s_revokedCookies.insert(s_revokedCookies.end(), cookie);
   }
   END_LOCK_MUTEX
}

void invalidateAuthCookie(const std::string& cookie,
                          ExponentialBackoffPtr backoffPtr)
{
   if (cookie.empty())
      return;

   // create a file lock to gain exclusive access to the revocation list
   boost::shared_ptr<FileLock> lock = FileLock::createDefault();
   ScopedFileLock fileLock(lock, s_revocationLockFile);
   Error error = fileLock.error();
   if (error)
   {
      // if we could not acquire the lock, some other rserver process has
      // attempt to retry the operation on an exponential backoff timer
      if (!backoffPtr)
      {
         backoffPtr = boost::make_shared<ExponentialBackoff>(server::server()->ioService(),
                                                             boost::posix_time::seconds(1),
                                                             boost::posix_time::seconds(15),
                                                             boost::bind(invalidateAuthCookie, cookie, _1));
      }

      bool keepTrying = backoffPtr->next();
      if (!keepTrying)
      {
         LOG_ERROR_MESSAGE("Could not invalidate user auth cookie - could not acquire revocation list lockfile");
         LOG_ERROR(error);
      }

      return;
   }

   // read the current revocation list
   std::vector<std::string> revokedCookies;
   error = readRevocationList(&revokedCookies);
   if (error)
   {
      LOG_ERROR_MESSAGE("Could not invalidate user auth cookie - could not read revocation list");
      LOG_ERROR(error);
      return;
   }

   // add the new entry
   revokedCookies.push_back(cookie);

   // write the new contents to file
   error = writeStringVectorToFile(s_revocationList, revokedCookies);
   if (error)
   {
      LOG_ERROR_MESSAGE("Could not invalidate user auth cookie - could not write to revocation list");
      LOG_ERROR(error);
      return;
   }

   // store the revoked cookie in memory - we only check the memory cache when
   // checking incoming requests to see if the cookie presented has been revoked
   // because it is too expensive to hit the disk every time
   insertRevokedCookie(RevokedCookie(cookie));

   onCookieRevoked(cookie);
}

Error initialize()
{
   // initialize by loading the current contents of the revocation list into memory

   // create revocation list directory and ensure the server user has permission to write to it
   // if the directory already exists, we will not attempt to change ownership as this means
   // the directory was setup by an administrator and we should respect its permissions
   FilePath rootDir = options().authRevocationListDir();
   if (!rootDir.exists())
   {
      Error error = rootDir.ensureDirectory();
      if (error)
      {
         LOG_ERROR_MESSAGE("Could not create revocation list directory " + rootDir.getAbsolutePath());
         return error;
      }

      if (core::system::effectiveUserIsRoot())
      {
         error = file_utils::changeOwnership(rootDir, options().serverUser());
         if (error)
         {
            LOG_ERROR_MESSAGE("Could not change ownership of revocation list directory " + rootDir.getAbsolutePath());
            return error;
         }
      }
   }

   s_revocationList = rootDir.completeChildPath("revocation-list");
   s_revocationLockFile = rootDir.completeChildPath("revocation-list.lock");

   // create a file lock to gain exclusive access to the revocation list
   boost::shared_ptr<FileLock> lock = FileLock::createDefault();
   int numTries = 0;

   while (numTries < 30)
   {
      ScopedFileLock fileLock(lock, s_revocationLockFile);
      Error error = fileLock.error();
      if (error)
      {
         // if we could not acquire the lock, some other rserver process has
         // keep trying for some time before giving up
         ++numTries;
         boost::this_thread::sleep(boost::posix_time::seconds(1));
         continue;
      }

      // successfully acquired lock
      // create file if it does not exist
      error = s_revocationList.ensureFile();
      if (error)
      {
         LOG_ERROR_MESSAGE("Could not create revocation list");
         return error;
      }

      if (core::system::effectiveUserIsRoot())
      {
         // ensure revocation file is owned by the server user
         // this ensures that it can be written to even when we drop privilege
         error = file_utils::changeOwnership(s_revocationList, options().serverUser());
         if (error)
            return error;
      }

      // ensure that only the server user can read/write to it, so other users of the system
      // cannot muck with the contents!
      error = core::system::changeFileMode(s_revocationList, core::system::UserReadWriteMode);
      if (error)
      {
         LOG_ERROR_MESSAGE("Could not set revocation file permissions");
         return error;
      }

      // read the current revocation list into memory
      std::vector<std::string> revokedCookies;
      error = readRevocationList(&revokedCookies);
      if (error)
      {
         LOG_ERROR_MESSAGE("Could not read revocation list");
         return error;
      }

      for (const std::string& cookie : revokedCookies)
         insertRevokedCookie(RevokedCookie(cookie));

      // write the contents back out to file as stale entries have been removed
      error = writeStringVectorToFile(s_revocationList, revokedCookies);
      if (error)
      {
         LOG_ERROR_MESSAGE("Could not write to revocation list");
         return error;
      }

      return overlay::initialize();
   }

   // the file lock could not be acquired
   return systemError(boost::system::errc::resource_unavailable_try_again,
                      "Could not acquire revocation list file lock",
                      ERROR_LOCATION);
}

} // namespace handler
} // namespace auth
} // namespace server
} // namespace rstudio

#endif // SERVER_AUTH_HANDLER_CPP

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

#ifndef SERVER_AUTH_HANDLER_CPP
#define SERVER_AUTH_HANDLER_CPP

#include <server/auth/ServerAuthHandler.hpp>

#include <boost/algorithm/string.hpp>

#include <core/FileLock.hpp>
#include <core/FileSerializer.hpp>
#include <core/json/JsonRpc.hpp>
#include <core/system/FileMode.hpp>
#include <core/system/PosixUser.hpp>
#include <core/Thread.hpp>

#include <server/ServerConstants.hpp>
#include <server/ServerObject.hpp>
#include <server/ServerOptions.hpp>
#include <server/ServerUriHandlers.hpp>

#include <server/auth/ServerSecureUriHandler.hpp>

#include <session/SessionScopes.hpp>

using namespace rstudio::core;

namespace rstudio {
namespace server {
namespace auth {
namespace handler {

namespace {

FilePath s_revocationList;
FilePath s_revocationLockFile;

boost::posix_time::ptime cookieExpiration(const std::string& cookie)
{
   std::vector<std::string> cookieParts;
   boost::algorithm::split(cookieParts, cookie, boost::is_any_of("|"));

   if (cookieParts.size() > 1)
   {
      std::string expiration = cookieParts[1];
      std::string expirationStr = http::util::urlDecode(expiration);
      return http::util::parseHttpDate(expirationStr);
   }
   return boost::posix_time::second_clock::universal_time();
}

// map of last user sign in times to prevent users from creating
// inordinate amounts of revocation entries
std::map<std::string, boost::posix_time::ptime> s_loginTimes;

// sorted array of revoked cookies, where the first to expire appears as the first element
// allows for quickly removing the first element
std::deque<RevokedCookie> s_revokedCookies;

// mutex for providing concurrent access to internal structures
// necessary because auth happens on the thread pool
boost::mutex s_mutex;

// global auth handler
Handler s_handler;

void updateCredentialsNotSupported(
      boost::shared_ptr<core::http::AsyncConnection> pConnection)
{
   // alias response
   http::Response* pResponse = &(pConnection->response());

   // gwt form panel requires text/html content type
   pResponse->setContentType("text/html");

   // set method not found error
   Error methodNotFoundError(json::errc::MethodNotFound, ERROR_LOCATION);
   json::setJsonRpcError(methodNotFoundError, pResponse);

   // write response
   pConnection->writeResponse();
}

bool isCookieRevoked(const std::string& cookie)
{
   boost::posix_time::ptime now = boost::posix_time::second_clock::universal_time();

   LOCK_MUTEX(s_mutex)
   {
      // check for cookie in revocation list, deleting expired elements as we go
      for (auto it = s_revokedCookies.begin(); it != s_revokedCookies.end();)
      {
         const RevokedCookie& other = *it;
         if (other.cookie == cookie)
            return true;

         if (other.expiration <= now)
         {
            it = s_revokedCookies.erase(it);
            continue;
         }
         else
         {
            ++it;
         }
      }
   }
   END_LOCK_MUTEX

   return false;
}

Error readRevocationList(std::vector<std::string>* pEntries)
{
   // read the current revocation list
   Error error = readStringVectorFromFile(s_revocationList, pEntries);
   if (error)
      return error;

   // remove stale entries from the list
   boost::posix_time::ptime now = boost::posix_time::second_clock::universal_time();
   for (int i = pEntries->size() - 1; i >= 0; i--)
   {
      const std::string& cookie = pEntries->at(i);

      boost::posix_time::ptime expirationDate = cookieExpiration(cookie);

      if (expirationDate <= now)
         pEntries->erase(pEntries->begin() + i);
   }

   return Success();
}

} // anonymous namespace

namespace overlay {

Error initialize()
{
   return Success();
}

Error isUserLicensed(const std::string& username,
                     bool* pLicensed)
{
   *pLicensed = true;
   return Success();
}

bool isUserListCookieValid(const std::string& cookieValue)
{
   return true;
}

bool shouldShowUserLicenseWarning()
{
   return false;
}

std::string getUserListCookieValue()
{
   return "9c16856330a7400cbbbba228392a5d83";
}

unsigned int getActiveUserCount()
{
   return 0;
}

json::Array getLicensedUsers()
{
   return json::Array();
}

Error lockUser(boost::asio::io_service& ioService,
               const std::string& username)
{
   return Success();
}

Error unlockUser(boost::asio::io_service& ioService,
                 const std::string& username)
{
   return Success();
}

} // namespace overlay

void onCookieRevoked(const std::string& cookie)
{
}

// uri constants
const char * const kSignIn = "/auth-sign-in";
const char * const kSignOut = "/auth-sign-out";
const char * const kRefreshCredentialsAndContinue = "/auth-refresh-credentials";

void onCookieRevoked(const std::string& cookie);

RevokedCookie::RevokedCookie(const std::string& cookie)
{
   this->cookie = cookie;
   this->expiration = cookieExpiration(cookie);
}

std::string getUserIdentifier(const core::http::Request& request,
                              bool requireUserListCookie,
                              http::Response* pResponse)
{
   if (isCookieRevoked(request.cookieValue(kUserIdCookie)))
      return std::string();

   if (requireUserListCookie)
   {
      if (!overlay::isUserListCookieValid(request.cookieValue(kUserListCookie)))
         return std::string();
   }

   return s_handler.getUserIdentifier(request, pResponse);
}

std::string userIdentifierToLocalUsername(const std::string& userIdentifier)
{
   return s_handler.userIdentifierToLocalUsername(userIdentifier);
}

bool mainPageFilter(const core::http::Request& request,
                    core::http::Response* pResponse)
{
   return s_handler.mainPageFilter(request, pResponse);
}

void signInThenContinue(const core::http::Request& request,
                        core::http::Response* pResponse)
{
   s_handler.signInThenContinue(request, pResponse);
}

void refreshCredentialsThenContinue(
      boost::shared_ptr<core::http::AsyncConnection> pConnection)
{
   s_handler.refreshCredentialsThenContinue(pConnection);
}

// register the auth handler
void registerHandler(const Handler& handler)
{
   // set handler functions
   s_handler = handler;

   // register uri handlers
   uri_handlers::addBlocking(kSignIn, s_handler.signIn);

   uri_handlers::addBlocking(kSignOut,
                             auth::secureHttpHandler(
                                boost::bind(s_handler.signOut, _2, _3)));

   uri_handlers::add(kRefreshCredentialsAndContinue,
                     s_handler.refreshCredentialsThenContinue);

   uri_handlers::add("/auth-update-credentials",
                     s_handler.updateCredentials ?
                        s_handler.updateCredentials :
                        updateCredentialsNotSupported);
}

// is there a handler already registered?
bool isRegistered()
{
   return ! s_handler.getUserIdentifier.empty();
}

bool canSetSignInCookies()
{
   return !s_handler.setSignInCookies.empty();
}

void setSignInCookies(const core::http::Request& request,
                      const std::string& username,
                      bool persist,
                      core::http::Response* pResponse)
{
   s_handler.setSignInCookies(request, username, persist, pResponse);
}

void signOut(const http::Request& request, http::Response* pResponse)
{
   s_handler.signOut(request, pResponse);
}

bool isUserSignInThrottled(const std::string& user)
{
   LOCK_MUTEX(s_mutex)
   {
      auto it = s_loginTimes.find(user);

      boost::posix_time::ptime now = boost::posix_time::second_clock::universal_time();
      if (it == s_loginTimes.end())
      {
         s_loginTimes.insert({user, now});
         return false;
      }

      if (it->second >
          now - boost::posix_time::seconds(options().authSignInThrottleSeconds()))
      {
         // user is trying to sign back in too quickly
         // prevent the request
         return true;
      }
      else
      {
         // user is fine to sign in - update their last sign in time to now
         it->second = now;
      }

      return false;
   }
   END_LOCK_MUTEX

   return false;
}

void refreshAuthCookies(const std::string& userIdentifier,
                        const http::Request& request,
                        http::Response* pResponse)
{
   if (server::options().authTimeoutMinutes() > 0 &&
       !s_handler.refreshAuthCookies.empty())
   {
      // clear any existing auth cookies first - this method can be invoked multiple
      // times depending on the handler type (for example, an upload handler)
      pResponse->clearCookies();
      bool persist = request.cookieValue(kPersistAuthCookie) == "1" ? true : false;
      s_handler.refreshAuthCookies(request, userIdentifier, persist, pResponse);
   }
}

void insertRevokedCookie(const RevokedCookie& cookie)
{
   // do not insert the cookie if it is already expired
   if (cookie.expiration <= boost::posix_time::second_clock::universal_time())
      return;

   LOCK_MUTEX(s_mutex)
   {
      for (auto it = s_revokedCookies.begin(); it != s_revokedCookies.end(); ++it)
      {
         const RevokedCookie& other = *it;
         if (other.expiration > cookie.expiration)
         {
            s_revokedCookies.insert(it, cookie);
            return;
         }
      }

      // no elements were greater - insert at the end
      s_revokedCookies.insert(s_revokedCookies.end(), cookie);
   }
   END_LOCK_MUTEX
}

void invalidateAuthCookie(const std::string& cookie,
                          ExponentialBackoffPtr backoffPtr)
{
   if (cookie.empty())
      return;

   // create a file lock to gain exclusive access to the revocation list
   boost::shared_ptr<FileLock> lock = FileLock::createDefault();
   ScopedFileLock fileLock(lock, s_revocationLockFile);
   Error error = fileLock.error();
   if (error)
   {
      // if we could not acquire the lock, some other rserver process has
      // attempt to retry the operation on an exponential backoff timer
      if (!backoffPtr)
      {
         backoffPtr = boost::make_shared<ExponentialBackoff>(server::server()->ioService(),
                                                             boost::posix_time::seconds(1),
                                                             boost::posix_time::seconds(15),
                                                             boost::bind(invalidateAuthCookie, cookie, _1));
      }

      bool keepTrying = backoffPtr->next();
      if (!keepTrying)
      {
         LOG_ERROR_MESSAGE("Could not invalidate user auth cookie - could not acquire revocation list lockfile");
         LOG_ERROR(error);
      }

      return;
   }

   // read the current revocation list
   std::vector<std::string> revokedCookies;
   error = readRevocationList(&revokedCookies);
   if (error)
   {
      LOG_ERROR_MESSAGE("Could not invalidate user auth cookie - could not read revocation list");
      LOG_ERROR(error);
      return;
   }

   // add the new entry
   revokedCookies.push_back(cookie);

   // write the new contents to file
   error = writeStringVectorToFile(s_revocationList, revokedCookies);
   if (error)
   {
      LOG_ERROR_MESSAGE("Could not invalidate user auth cookie - could not write to revocation list");
      LOG_ERROR(error);
      return;
   }

   // store the revoked cookie in memory - we only check the memory cache when
   // checking incoming requests to see if the cookie presented has been revoked
   // because it is too expensive to hit the disk every time
   insertRevokedCookie(RevokedCookie(cookie));

   onCookieRevoked(cookie);
}

Error initialize()
{
   // initialize by loading the current contents of the revocation list into memory

   // create revocation list directory and ensure the server user has permission to write to it
   // if the directory already exists, we will not attempt to change ownership as this means
   // the directory was setup by an administrator and we should respect its permissions
   FilePath rootDir = options().authRevocationListDir();
   if (!rootDir.exists())
   {
      Error error = rootDir.ensureDirectory();
      if (error)
      {
         LOG_ERROR_MESSAGE("Could not create revocation list directory " + rootDir.getAbsolutePath());
         return error;
      }

      if (core::system::effectiveUserIsRoot())
      {
         error = file_utils::changeOwnership(rootDir, options().serverUser());
         if (error)
         {
            LOG_ERROR_MESSAGE("Could not change ownership of revocation list directory " + rootDir.getAbsolutePath());
            return error;
         }
      }
   }

   s_revocationList = rootDir.completeChildPath("revocation-list");
   s_revocationLockFile = rootDir.completeChildPath("revocation-list.lock");

   // create a file lock to gain exclusive access to the revocation list
   boost::shared_ptr<FileLock> lock = FileLock::createDefault();
   int numTries = 0;

   while (numTries < 30)
   {
      ScopedFileLock fileLock(lock, s_revocationLockFile);
      Error error = fileLock.error();
      if (error)
      {
         // if we could not acquire the lock, some other rserver process has
         // keep trying for some time before giving up
         ++numTries;
         boost::this_thread::sleep(boost::posix_time::seconds(1));
         continue;
      }

      // successfully acquired lock
      // create file if it does not exist
      error = s_revocationList.ensureFile();
      if (error)
      {
         LOG_ERROR_MESSAGE("Could not create revocation list");
         return error;
      }

      if (core::system::effectiveUserIsRoot())
      {
         // ensure revocation file is owned by the server user
         // this ensures that it can be written to even when we drop privilege
         error = file_utils::changeOwnership(s_revocationList, options().serverUser());
         if (error)
            return error;
      }

      // ensure that only the server user can read/write to it, so other users of the system
      // cannot muck with the contents!
      error = core::system::changeFileMode(s_revocationList, core::system::UserReadWriteMode);
      if (error)
      {
         LOG_ERROR_MESSAGE("Could not set revocation file permissions");
         return error;
      }

      // read the current revocation list into memory
      std::vector<std::string> revokedCookies;
      error = readRevocationList(&revokedCookies);
      if (error)
      {
         LOG_ERROR_MESSAGE("Could not read revocation list");
         return error;
      }

      for (const std::string& cookie : revokedCookies)
         insertRevokedCookie(RevokedCookie(cookie));

      // write the contents back out to file as stale entries have been removed
      error = writeStringVectorToFile(s_revocationList, revokedCookies);
      if (error)
      {
         LOG_ERROR_MESSAGE("Could not write to revocation list");
         return error;
      }

      return overlay::initialize();
   }

   // the file lock could not be acquired
   return systemError(boost::system::errc::resource_unavailable_try_again,
                      "Could not acquire revocation list file lock",
                      ERROR_LOCATION);
}

} // namespace handler
} // namespace auth
} // namespace server
} // namespace rstudio

#endif // SERVER_AUTH_HANDLER_CPP

<|endoftext|>


<commit_before>/*
 * SessionThemes.cpp
 *
 * Copyright (C) 2018 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 "SessionThemes.hpp"

#include <boost/bind.hpp>

#include <core/Error.hpp>
#include <core/Exec.hpp>
#include <core/FilePath.hpp>
#include <core/json/JsonRpc.hpp>
#include <core/system/System.hpp>

#include <session/SessionModuleContext.hpp>

#include <r/RRoutines.hpp>
#include <r/RSexp.hpp>

#include <fstream>
#include <map>
#include <regex>
#include <string>

using namespace rstudio::core;

namespace rstudio {
namespace session {
namespace modules {
namespace themes {

namespace {

// A map from the name of the theme to the location of the file and a boolean representing
// whether or not the theme is dark.
typedef std::map<std::string, std::pair<std::string, bool> > ThemeMap;
void getThemesInLocation(const rstudio::core::FilePath& location, ThemeMap& themeMap)
{
   using rstudio::core::FilePath;
   if (location.isDirectory())
   {
      std::vector<FilePath> locationChildren;
      location.children(&locationChildren);
      for (const FilePath& themeFile: locationChildren)
      {
         if (themeFile.hasExtensionLowerCase(".rstheme"))
         {
            const std::string k_themeFileStr = themeFile.canonicalPath();
            std::ifstream themeIFStream(k_themeFileStr);
            std::string themeContents(
               (std::istreambuf_iterator<char>(themeIFStream)),
               (std::istreambuf_iterator<char>()));
            themeIFStream.close();

            std::smatch matches;
            std::regex_search(
               themeContents,
               matches,
               std::regex("rs-theme-name\\s*:\\s*([^\\*]+?)\\s*(?:\\*|$)"));

            // If there's no name specified,use the name of the file
            std::string name;
            if (matches.size() < 2)
            {
               name = themeFile.stem();
            }
            else
            {
               // If there's at least one name specified, get the first one.
               name = matches[1];
            }

            // Find out if the theme is dark or not.
            std::regex_search(
                     themeContents,
                     matches,
                     std::regex("rs-theme-is-dark\\s*:\\s*([^\\*]+?)\\s*(?:\\*|$)"));

            bool isDark = false;
            if (matches.size() >= 2)
            {
               isDark = std::regex_match(
                  std::string(matches[1]),
                  std::regex("(?:1|t(?:rue)?))", std::regex_constants::icase));
            }
            if ((matches.size() < 2) ||
                (!isDark &&
                !std::regex_match(
                   std::string(matches[1]),
                   std::regex("(?:0|f(?:alse)?))", std::regex_constants::icase))))
            {
               // TODO: warning / logging about using default isDark value.
            }

            themeMap[name] = std::pair<std::string, bool>(k_themeFileStr, isDark);
         }
      }
   }
}

/**
 * @brief Gets the list of all RStudio editor themes.
 *
 * @return The list of all RStudio editor themes.
 */
SEXP rs_getThemes()
{
   // List all files in the global location first.
   // TODO: Windows
   using rstudio::core::FilePath;

#ifdef _WIN32
   FilePath globalPath = rstudio::core::system::systemSettingsPath("RStudio\\themes", false);
   FilePath localPath = rstudio::core::system::userHomePath().childPath(".R\\rstudio\\themes");
#else
   FilePath globalPath("/etc/rstudio/themes/");
   FilePath localPath = rstudio::core::system::userHomePath().childPath(".R/rstudio/themes/");
#endif

   const char* k_globalPathAlt = std::getenv("RS_THEME_GLOBAL_HOME");
   const char* k_localPathAlt = std::getenv("RS_THEME_LOCAL_HOME");
   if (k_globalPathAlt)
   {
      globalPath = FilePath(k_globalPathAlt);
   }
   if (k_localPathAlt)
   {
      localPath = FilePath(k_localPathAlt);
   }

   // Intentionally get global themes before getting user specific themes so that user specific
   // themes will override global ones.
   ThemeMap themeMap;
   getThemesInLocation(globalPath, themeMap);
   getThemesInLocation(localPath, themeMap);
   // TODO: get default themes.

   // Convert to an R list.
   rstudio::r::sexp::Protect protect;
   rstudio::r::sexp::ListBuilder themeListBuilder(&protect);

   for (auto theme: themeMap)
   {
      rstudio::r::sexp::ListBuilder themeDetailsListBuilder(&protect);
      themeDetailsListBuilder.add("fileName", theme.second.first);
      themeDetailsListBuilder.add("isDark", theme.second.second);

      themeListBuilder.add(theme.first, themeDetailsListBuilder);
   }

   return rstudio::r::sexp::create(themeListBuilder, &protect);
}

} // anonymous namespace

Error initialize()
{
   using boost::bind;
   using namespace module_context;

   RS_REGISTER_CALL_METHOD(rs_getThemes, 0);

   ExecBlock initBlock;
   initBlock.addFunctions()
      (bind(sourceModuleRFile, "SessionThemes.R"));

   return initBlock.execute();
}

} // namespace themes
} // namespace modules
} // namespace session
} // namespace rstudio
<commit_msg>Add path for where pre installed themes will be.<commit_after>/*
 * SessionThemes.cpp
 *
 * Copyright (C) 2018 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 "SessionThemes.hpp"

#include <boost/bind.hpp>

#include <core/Error.hpp>
#include <core/Exec.hpp>
#include <core/FilePath.hpp>
#include <core/json/JsonRpc.hpp>
#include <core/system/System.hpp>

#include <session/SessionModuleContext.hpp>

#include <r/RRoutines.hpp>
#include <r/RSexp.hpp>

#include <fstream>
#include <map>
#include <regex>
#include <string>

using namespace rstudio::core;

namespace rstudio {
namespace session {
namespace modules {
namespace themes {

namespace {

// A map from the name of the theme to the location of the file and a boolean representing
// whether or not the theme is dark.
typedef std::map<std::string, std::pair<std::string, bool> > ThemeMap;
void getThemesInLocation(const rstudio::core::FilePath& location, ThemeMap& themeMap)
{
   using rstudio::core::FilePath;
   if (location.isDirectory())
   {
      std::vector<FilePath> locationChildren;
      location.children(&locationChildren);
      for (const FilePath& themeFile: locationChildren)
      {
         if (themeFile.hasExtensionLowerCase(".rstheme"))
         {
            const std::string k_themeFileStr = themeFile.canonicalPath();
            std::ifstream themeIFStream(k_themeFileStr);
            std::string themeContents(
               (std::istreambuf_iterator<char>(themeIFStream)),
               (std::istreambuf_iterator<char>()));
            themeIFStream.close();

            std::smatch matches;
            std::regex_search(
               themeContents,
               matches,
               std::regex("rs-theme-name\\s*:\\s*([^\\*]+?)\\s*(?:\\*|$)"));

            // If there's no name specified,use the name of the file
            std::string name;
            if (matches.size() < 2)
            {
               name = themeFile.stem();
            }
            else
            {
               // If there's at least one name specified, get the first one.
               name = matches[1];
            }

            // Find out if the theme is dark or not.
            std::regex_search(
                     themeContents,
                     matches,
                     std::regex("rs-theme-is-dark\\s*:\\s*([^\\*]+?)\\s*(?:\\*|$)"));

            bool isDark = false;
            if (matches.size() >= 2)
            {
               isDark = std::regex_match(
                  std::string(matches[1]),
                  std::regex("(?:1|t(?:rue)?))", std::regex_constants::icase));
            }
            if ((matches.size() < 2) ||
                (!isDark &&
                !std::regex_match(
                   std::string(matches[1]),
                   std::regex("(?:0|f(?:alse)?))", std::regex_constants::icase))))
            {
               // TODO: warning / logging about using default isDark value.
            }

            themeMap[name] = std::pair<std::string, bool>(k_themeFileStr, isDark);
         }
      }
   }
}

/**
 * @brief Gets the list of all RStudio editor themes.
 *
 * @return The list of all RStudio editor themes.
 */
SEXP rs_getThemes()
{
   // List all files in the global location first.
   // TODO: Windows
   using rstudio::core::FilePath;

   FilePath preInstalledPath = session::options.rResourcesPath().childPath("themes");
#ifdef _WIN32
   FilePath globalPath = core::system::systemSettingsPath("RStudio\\themes", false);
   FilePath localPath = core::system::userHomePath().childPath(".R\\rstudio\\themes");
#else
   FilePath globalPath("/etc/rstudio/themes/");
   FilePath localPath = core::system::userHomePath().childPath(".R/rstudio/themes/");
#endif

   const char* k_globalPathAlt = std::getenv("RS_THEME_GLOBAL_HOME");
   const char* k_localPathAlt = std::getenv("RS_THEME_LOCAL_HOME");
   if (k_globalPathAlt)
   {
      globalPath = FilePath(k_globalPathAlt);
   }
   if (k_localPathAlt)
   {
      localPath = FilePath(k_localPathAlt);
   }

   // Intentionally get global themes before getting user specific themes so that user specific
   // themes will override global ones.
   ThemeMap themeMap;
   getThemesInLocation(preInstalledPath, themeMap);
   getThemesInLocation(globalPath, themeMap);
   getThemesInLocation(localPath, themeMap);
   // TODO: get default themes.

   // Convert to an R list.
   rstudio::r::sexp::Protect protect;
   rstudio::r::sexp::ListBuilder themeListBuilder(&protect);

   for (auto theme: themeMap)
   {
      rstudio::r::sexp::ListBuilder themeDetailsListBuilder(&protect);
      themeDetailsListBuilder.add("fileName", theme.second.first);
      themeDetailsListBuilder.add("isDark", theme.second.second);

      themeListBuilder.add(theme.first, themeDetailsListBuilder);
   }

   return rstudio::r::sexp::create(themeListBuilder, &protect);
}

} // anonymous namespace

Error initialize()
{
   using boost::bind;
   using namespace module_context;

   RS_REGISTER_CALL_METHOD(rs_getThemes, 0);

   ExecBlock initBlock;
   initBlock.addFunctions()
      (bind(sourceModuleRFile, "SessionThemes.R"));

   return initBlock.execute();
}

} // namespace themes
} // namespace modules
} // namespace session
} // namespace rstudio
<|endoftext|>


<commit_before>#include <cppunit/Portability.h>

#if defined(CPPUNIT_HAVE_UNIX_DLL_LOADER)
#include <cppunit/plugin/DynamicLibraryManager.h>

#include <dlfcn.h>
#include <unistd.h>

namespace CppUnit
{

DynamicLibraryManager::LibraryHandle
DynamicLibraryManager::doLoadLibrary( const std::string &libraryName )
{
  return ::dlopen( libraryFileName.c_str(), DLOPEN_MODE );
}

void
DynamicLibraryManager::doReleaseLibrary()
{
  ::dlclose( m_libraryHandle);
}

DynamicLibraryManager::Symbol
DynamicLibraryManager::doFindSymbol( const std::string &symbol )
{
  return ::dlsym ( m_libraryHandle, symbol.c_str() );
}

} //  namespace CppUnit

#endif // defined(CPPUNIT_HAVE_UNIX_DLL_LOADER)
<commit_msg>* fixed typo<commit_after>#include <cppunit/Portability.h>

#if defined(CPPUNIT_HAVE_UNIX_DLL_LOADER)
#include <cppunit/plugin/DynamicLibraryManager.h>

#include <dlfcn.h>
#include <unistd.h>

namespace CppUnit
{

DynamicLibraryManager::LibraryHandle
DynamicLibraryManager::doLoadLibrary( const std::string &libraryName )
{
  return ::dlopen( libraryName.c_str(), DLOPEN_MODE );
}

void
DynamicLibraryManager::doReleaseLibrary()
{
  ::dlclose( m_libraryHandle);
}

DynamicLibraryManager::Symbol
DynamicLibraryManager::doFindSymbol( const std::string &symbol )
{
  return ::dlsym ( m_libraryHandle, symbol.c_str() );
}

} //  namespace CppUnit

#endif // defined(CPPUNIT_HAVE_UNIX_DLL_LOADER)
<|endoftext|>


<commit_before>// Jubatus: Online machine learning framework for distributed environment
// Copyright (C) 2011,2012 Preferred Infrastructure and Nippon Telegraph and Telephone Corporation.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License version 2.1 as published by the Free Software Foundation.
//
// 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

#ifndef JUBATUS_FRAMEWORK_MIXABLE_HPP_
#define JUBATUS_FRAMEWORK_MIXABLE_HPP_

#include <iostream>
#include <string>
#include <vector>

#include <msgpack.hpp>
#include <pficommon/concurrent/rwmutex.h>

#include "../common/exception.hpp"
#include "../common/byte_buffer.hpp"
#include "../common/shared_ptr.hpp"
#include "../common/byte_buffer.hpp"

namespace jubatus {
namespace core {
namespace framework {

class mixable0 {
 public:
  mixable0() {
  }

  virtual ~mixable0() {
  }

  virtual common::byte_buffer get_diff() const = 0;
  virtual void put_diff(const common::byte_buffer&) = 0;
  virtual void mix(const common::byte_buffer&,
                   const common::byte_buffer&,
                   common::byte_buffer&) const = 0;

  virtual void save(std::ostream& ofs) = 0;
  virtual void load(std::istream& ifs) = 0;
  virtual void clear() = 0;
};

class mixable_holder {
 public:
  typedef std::vector<mixable0*> mixable_list;

  mixable_holder() {
  }

  virtual ~mixable_holder() {
  }

  void register_mixable(mixable0* m) {
    mixables_.push_back(m);
  }

  mixable_list get_mixables() const {
    return mixables_;
  }

  pfi::concurrent::rw_mutex& rw_mutex() {
    return rw_mutex_;
  }

 protected:
  pfi::concurrent::rw_mutex rw_mutex_;
  std::vector<mixable0*> mixables_;
};

template<typename Model, typename Diff>
class mixable : public mixable0 {
 public:
  typedef Model model_type;
  typedef Diff diff_type;
  typedef common::cshared_ptr<Model> model_ptr;

  virtual ~mixable() {
  }

  virtual void clear() = 0;

  virtual Diff get_diff_impl() const = 0;
  virtual void put_diff_impl(const Diff&) = 0;
  virtual void mix_impl(const Diff&, const Diff&, Diff&) const = 0;

  void set_model(model_ptr m) {
    model_ = m;
  }

  common::byte_buffer get_diff() const {
    if (model_) {
      common::byte_buffer buf;
      pack_(get_diff_impl(), buf);
      return buf;
    } else {
      throw JUBATUS_EXCEPTION(config_not_set());
    }
  }

  void put_diff(const common::byte_buffer& d) {
    if (model_) {
      Diff diff;
      unpack_(d, diff);
      put_diff_impl(diff);
    } else {
      throw JUBATUS_EXCEPTION(config_not_set());
    }
  }

  void mix(
      const common::byte_buffer& lhs,
      const common::byte_buffer& rhs,
      common::byte_buffer& mixed_buf) const {
    Diff left, right, mixed;
    unpack_(lhs, left);
    unpack_(rhs, right);
    mix_impl(left, right, mixed);
    pack_(mixed, mixed_buf);
  }

  void save(std::ostream& os) {
    model_->save(os);
  }

  void load(std::istream& is) {
    model_->load(is);
  }

  model_ptr get_model() const {
    return model_;
  }

 private:
  void unpack_(const common::byte_buffer& buf, Diff& d) const {
    msgpack::unpacked msg;
    msgpack::unpack(&msg, buf.ptr(), buf.size());
    msg.get().convert(&d);
  }

  void pack_(const Diff& d, common::byte_buffer& buf) const {
    msgpack::sbuffer sbuf;
    msgpack::pack(sbuf, d);
    buf.assign(sbuf.data(), sbuf.size());
  }

  model_ptr model_;
};

}  // namespace framework
}  // namespace core
}  // namespace jubatus

#endif  // JUBATUS_FRAMEWORK_MIXABLE_HPP_
<commit_msg>fix include guard in mixable.hpp<commit_after>// Jubatus: Online machine learning framework for distributed environment
// Copyright (C) 2011,2012 Preferred Infrastructure and Nippon Telegraph and Telephone Corporation.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License version 2.1 as published by the Free Software Foundation.
//
// 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

#ifndef JUBATUS_CORE_FRAMEWORK_MIXABLE_HPP_
#define JUBATUS_CORE_FRAMEWORK_MIXABLE_HPP_

#include <iostream>
#include <string>
#include <vector>

#include <msgpack.hpp>
#include <pficommon/concurrent/rwmutex.h>

#include "../common/exception.hpp"
#include "../common/byte_buffer.hpp"
#include "../common/shared_ptr.hpp"
#include "../common/byte_buffer.hpp"

namespace jubatus {
namespace core {
namespace framework {

class mixable0 {
 public:
  mixable0() {
  }

  virtual ~mixable0() {
  }

  virtual common::byte_buffer get_diff() const = 0;
  virtual void put_diff(const common::byte_buffer&) = 0;
  virtual void mix(const common::byte_buffer&,
                   const common::byte_buffer&,
                   common::byte_buffer&) const = 0;

  virtual void save(std::ostream& ofs) = 0;
  virtual void load(std::istream& ifs) = 0;
  virtual void clear() = 0;
};

class mixable_holder {
 public:
  typedef std::vector<mixable0*> mixable_list;

  mixable_holder() {
  }

  virtual ~mixable_holder() {
  }

  void register_mixable(mixable0* m) {
    mixables_.push_back(m);
  }

  mixable_list get_mixables() const {
    return mixables_;
  }

  pfi::concurrent::rw_mutex& rw_mutex() {
    return rw_mutex_;
  }

 protected:
  pfi::concurrent::rw_mutex rw_mutex_;
  std::vector<mixable0*> mixables_;
};

template<typename Model, typename Diff>
class mixable : public mixable0 {
 public:
  typedef Model model_type;
  typedef Diff diff_type;
  typedef common::cshared_ptr<Model> model_ptr;

  virtual ~mixable() {
  }

  virtual void clear() = 0;

  virtual Diff get_diff_impl() const = 0;
  virtual void put_diff_impl(const Diff&) = 0;
  virtual void mix_impl(const Diff&, const Diff&, Diff&) const = 0;

  void set_model(model_ptr m) {
    model_ = m;
  }

  common::byte_buffer get_diff() const {
    if (model_) {
      common::byte_buffer buf;
      pack_(get_diff_impl(), buf);
      return buf;
    } else {
      throw JUBATUS_EXCEPTION(config_not_set());
    }
  }

  void put_diff(const common::byte_buffer& d) {
    if (model_) {
      Diff diff;
      unpack_(d, diff);
      put_diff_impl(diff);
    } else {
      throw JUBATUS_EXCEPTION(config_not_set());
    }
  }

  void mix(
      const common::byte_buffer& lhs,
      const common::byte_buffer& rhs,
      common::byte_buffer& mixed_buf) const {
    Diff left, right, mixed;
    unpack_(lhs, left);
    unpack_(rhs, right);
    mix_impl(left, right, mixed);
    pack_(mixed, mixed_buf);
  }

  void save(std::ostream& os) {
    model_->save(os);
  }

  void load(std::istream& is) {
    model_->load(is);
  }

  model_ptr get_model() const {
    return model_;
  }

 private:
  void unpack_(const common::byte_buffer& buf, Diff& d) const {
    msgpack::unpacked msg;
    msgpack::unpack(&msg, buf.ptr(), buf.size());
    msg.get().convert(&d);
  }

  void pack_(const Diff& d, common::byte_buffer& buf) const {
    msgpack::sbuffer sbuf;
    msgpack::pack(sbuf, d);
    buf.assign(sbuf.data(), sbuf.size());
  }

  model_ptr model_;
};

}  // namespace framework
}  // namespace core
}  // namespace jubatus

#endif  // JUBATUS_CORE_FRAMEWORK_MIXABLE_HPP_
<|endoftext|>