<commit_before>/* Copyright (c) 2010-2017, AOYAMA Kazuharu
 * All rights reserved.
 *
 * This software may be used and distributed according to the terms of
 * the New BSD License, which is incorporated herein by reference.
 */

#include <QtCore>
#include <QHostAddress>
#include <QSet>
#include <TActionContext>
#include <TWebApplication>
#include <TAppSettings>
#include <THttpRequest>
#include <THttpResponse>
#include <THttpUtility>
#include <TDispatcher>
#include <TActionController>
#include <TSessionStore>
#include "tsystemglobal.h"
#include "thttpsocket.h"
#include "tsessionmanager.h"
#include "turlroute.h"
#include "tabstractwebsocket.h"

/*!
  \class TActionContext
  \brief The TActionContext class is the base class of contexts for
  action controllers.
*/

TActionContext::TActionContext()
    : TDatabaseContext(),
      stopped(false),
      socketDesc(0),
      currController(nullptr),
      httpReq(nullptr)
{ }

TActionContext::~TActionContext()
{
    release();
}

static bool directViewRenderMode()
{
    static int mode = -1;
    if (mode < 0) {
        mode = (int)Tf::appSettings()->value(Tf::DirectViewRenderMode).toBool();
    }
    return (bool)mode;
}

void TActionContext::execute(THttpRequest &request, int sid)
{
    T_TRACEFUNC("");

    THttpResponseHeader responseHeader;
    accessLogger.open();

    try {
        httpReq = &request;
        const THttpRequestHeader &hdr = httpReq->header();

        // Access log
        accessLogger.setTimestamp(QDateTime::currentDateTime());
        QByteArray firstLine = hdr.method() + ' ' + hdr.path();
        firstLine += QString(" HTTP/%1.%2").arg(hdr.majorVersion()).arg(hdr.minorVersion()).toLatin1();
        accessLogger.setRequest(firstLine);
        accessLogger.setRemoteHost( (Tf::appSettings()->value(Tf::ListenPort).toUInt() > 0) ? clientAddress().toString().toLatin1() : QByteArray("(unix)") );

        tSystemDebug("method : %s", hdr.method().data());
        tSystemDebug("path : %s", hdr.path().data());

        // HTTP method
        Tf::HttpMethod method = httpReq->method();
        QString path = THttpUtility::fromUrlEncoding(hdr.path().mid(0, hdr.path().indexOf('?')));

        // Routing info exists?
        QStringList components = TUrlRoute::splitPath(path);
        TRouting rt = TUrlRoute::instance().findRouting(method, components);

        tSystemDebug("Routing: controller:%s  action:%s", rt.controller.data(),
                     rt.action.data());

        if (rt.isEmpty()) {
            // Default URL routing

            if (Q_UNLIKELY(directViewRenderMode())) { // Direct view render mode?
                // Direct view setting
                rt.setRouting("directcontroller", "show", components);
            } else {
                QByteArray c = components.value(0).toLatin1().toLower();
                if (Q_LIKELY(!c.isEmpty())) {
                    if (Q_LIKELY(!TActionController::disabledControllers().contains(c))) { // Can not call 'ApplicationController'
                        // Default action: "index"
                        QByteArray action = components.value(1, QLatin1String("index")).toLatin1();
                        rt.setRouting(c + "controller", action, components.mid(2));
                    }
                }
                tSystemDebug("Active Controller : %s", rt.controller.data());
            }
        }

        // Call controller method
        TDispatcher<TActionController> ctlrDispatcher(rt.controller);
        currController = ctlrDispatcher.object();
        if (currController) {
            currController->setActionName(rt.action);
            currController->setSocketId(sid);

            // Session
            if (currController->sessionEnabled()) {
                TSession session;
                QByteArray sessionId = httpReq->cookie(TSession::sessionName());
                if (!sessionId.isEmpty()) {
                    // Finds a session
                    session = TSessionManager::instance().findSession(sessionId);
                }
                currController->setSession(session);

                // Exports flash-variant
                currController->exportAllFlashVariants();
            }

            // Verify authenticity token
            if (Tf::appSettings()->value(Tf::EnableCsrfProtectionModule, true).toBool()
                && currController->csrfProtectionEnabled() && !currController->exceptionActionsOfCsrfProtection().contains(rt.action)) {

                if (method == Tf::Post || method == Tf::Put || method == Tf::Patch || method == Tf::Delete) {
                    if (!currController->verifyRequest(*httpReq)) {
                        throw SecurityException("Invalid authenticity token", __FILE__, __LINE__);
                    }
                }
            }

            if (currController->sessionEnabled()) {
                if (currController->session().id().isEmpty() || Tf::appSettings()->value(Tf::SessionAutoIdRegeneration).toBool()) {
                    TSessionManager::instance().remove(currController->session().sessionId); // Removes the old session
                    // Re-generate session ID
                    currController->session().sessionId = TSessionManager::instance().generateId();
                    tSystemDebug("Re-generate session ID: %s", currController->session().sessionId.data());
                }
                // Sets CSRF protection informaion
                TActionController::setCsrfProtectionInto(currController->session());
            }

            // Database Transaction
            setTransactionEnabled(currController->transactionEnabled());

            // Do filters
            if (Q_LIKELY(currController->preFilter())) {

                // Dispathes
                bool dispatched = ctlrDispatcher.invoke(rt.action, rt.params);
                if (Q_LIKELY(dispatched)) {
                    autoRemoveFiles << currController->autoRemoveFiles;  // Adds auto-remove files

                    // Post fileter
                    currController->postFilter();

                    if (Q_UNLIKELY(currController->rollbackRequested())) {
                        rollbackTransactions();
                    } else {
                        // Commits a transaction to the database
                        commitTransactions();
                    }

                    // Session store
                    if (currController->sessionEnabled()) {
                        bool stored = TSessionManager::instance().store(currController->session());
                        if (Q_LIKELY(stored)) {
                            static int cookielifetime = -1;
                            QDateTime expire;

                            if (cookielifetime < 0) {
                                cookielifetime = Tf::appSettings()->value(Tf::SessionLifeTime).toInt();
                            }
                            if (cookielifetime > 0) {
                                expire = QDateTime::currentDateTime().addSecs(cookielifetime);
                            }

                            // Sets the path in the session cookie
                            QString cookiePath = Tf::appSettings()->value(Tf::SessionCookiePath).toString();
                            currController->addCookie(TSession::sessionName(), currController->session().id(), expire, cookiePath, QString(), false, true);
                        }
                    }

                    // WebSocket tasks
                    if (!currController->taskList.isEmpty()) {
                        QVariantList lst;
                        for (auto &task : (const QList<QPair<int, QVariant>>&)currController->taskList) {
                            const QVariant &taskData = task.second;

                            switch (task.first) {
                            case TActionController::SendTextTo: {
                                lst = taskData.toList();
                                TAbstractWebSocket *websocket = TAbstractWebSocket::searchWebSocket(lst[0].toInt());
                                if (websocket) {
                                    websocket->sendText(lst[1].toString());
                                }
                                break; }

                            case TActionController::SendBinaryTo: {
                                lst = taskData.toList();
                                TAbstractWebSocket *websocket = TAbstractWebSocket::searchWebSocket(lst[0].toInt());
                                if (websocket) {
                                    websocket->sendBinary(lst[1].toByteArray());
                                }
                                break; }

                            case TActionController::SendCloseTo: {
                                lst = taskData.toList();
                                TAbstractWebSocket *websocket = TAbstractWebSocket::searchWebSocket(lst[0].toInt());
                                if (websocket) {
                                    websocket->sendClose(lst[1].toInt());
                                }
                                break; }

                            default:
                                tSystemError("Invalid logic  [%s:%d]",  __FILE__, __LINE__);
                                break;
                            }
                        }
                    }
                }
            }

            // Sets charset to the content-type
            QByteArray ctype = currController->response.header().contentType().toLower();
            if (ctype.startsWith("text") && !ctype.contains("charset")) {
                ctype += "; charset=";
                ctype += Tf::app()->codecForHttpOutput()->name();
                currController->response.header().setContentType(ctype);
            }

            // Sets the default status code of HTTP response
            accessLogger.setStatusCode( (!currController->response.isBodyNull()) ? currController->statusCode() : Tf::InternalServerError );
            currController->response.header().setStatusLine(accessLogger.statusCode(), THttpUtility::getResponseReasonPhrase(accessLogger.statusCode()));

            // Writes a response and access log
            qint64 bodyLength = (currController->response.header().contentLength() > 0) ? currController->response.header().contentLength() : currController->response.bodyLength();
            int bytes = writeResponse(currController->response.header(), currController->response.bodyIODevice(),
                                      bodyLength);
            accessLogger.setResponseBytes(bytes);

            // Session GC
            TSessionManager::instance().collectGarbage();

        } else {
            accessLogger.setStatusCode( Tf::BadRequest );  // Set a default status code

            if (Q_LIKELY(method == Tf::Get)) {  // GET Method
                path.remove(0, 1);
                QFile reqPath(Tf::app()->publicPath() + path);
                QFileInfo fi(reqPath);

                if (fi.isFile() && fi.isReadable() && (fi.canonicalPath() + '/').contains(Tf::app()->publicPath())) {
                    // Check "If-Modified-Since" header for caching
                    bool sendfile = true;
                    QByteArray ifModifiedSince = hdr.rawHeader("If-Modified-Since");

                    if (!ifModifiedSince.isEmpty()) {
                        QDateTime dt = THttpUtility::fromHttpDateTimeString(ifModifiedSince);
                        if (dt.isValid()) {
                            sendfile = (dt.toMSecsSinceEpoch() / 1000 != fi.lastModified().toMSecsSinceEpoch() / 1000);
                        }
                    }

                    if (sendfile) {
                        // Sends a request file
                        responseHeader.setRawHeader("Last-Modified", THttpUtility::toHttpDateTimeString(fi.lastModified()));
                        QByteArray type = Tf::app()->internetMediaType(fi.suffix());
                        int bytes = writeResponse(Tf::OK, responseHeader, type, &reqPath, reqPath.size());
                        accessLogger.setResponseBytes( bytes );
                    } else {
                        // Not send the data
                        int bytes = writeResponse(Tf::NotModified, responseHeader);
                        accessLogger.setResponseBytes( bytes );
                    }
                } else {
                    int bytes = writeResponse(Tf::NotFound, responseHeader);
                    accessLogger.setResponseBytes( bytes );
                }
                accessLogger.setStatusCode( responseHeader.statusCode() );

            } else if (method == Tf::Post) {
                // file upload?
            } else {
                // HEAD, DELETE, ...
            }
        }

    } catch (ClientErrorException &e) {
        tWarn("Caught ClientErrorException: status code:%d", e.statusCode());
        tSystemWarn("Caught ClientErrorException: status code:%d", e.statusCode());
        int bytes = writeResponse(e.statusCode(), responseHeader);
        accessLogger.setResponseBytes( bytes );
        accessLogger.setStatusCode( e.statusCode() );
    } catch (SqlException &e) {
        tError("Caught SqlException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught SqlException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        closeHttpSocket();
    } catch (KvsException &e) {
        tError("Caught KvsException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught KvsException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        closeHttpSocket();
    } catch (SecurityException &e) {
        tError("Caught SecurityException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught SecurityException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        closeHttpSocket();
    } catch (RuntimeException &e) {
        tError("Caught RuntimeException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught RuntimeException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        closeHttpSocket();
    } catch (StandardException &e) {
        tError("Caught StandardException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught StandardException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        closeHttpSocket();
    } catch (...) {
        tError("Caught Exception");
        tSystemError("Caught Exception");
        closeHttpSocket();
    }

    TActionContext::accessLogger.write();  // Writes access log
}

void TActionContext::release()
{
    TDatabaseContext::release();

    for (auto temp : (const QList<TTemporaryFile*> &)tempFiles) {
        delete temp;
    }
    tempFiles.clear();

    for (auto &file : (const QStringList&)autoRemoveFiles) {
        QFile(file).remove();
    }
    autoRemoveFiles.clear();
}

qint64 TActionContext::writeResponse(int statusCode, THttpResponseHeader &header)
{
    T_TRACEFUNC("statusCode:%d", statusCode);
    QByteArray body;
    if (statusCode >= 400) {
        QFile html(Tf::app()->publicPath() + QString::number(statusCode) + ".html");
        if (html.exists() && html.open(QIODevice::ReadOnly)) {
            body = html.readAll();
            html.close();
        }
    }
    if (body.isEmpty()) {
        body  = "";
        body += THttpUtility::getResponseReasonPhrase(statusCode);
        body += " (";
        body += QByteArray::number(statusCode);
        body += ")";
    }

    QBuffer buf(&body);
    return writeResponse(statusCode, header, "text/html", &buf, body.length());
}

qint64 TActionContext::writeResponse(int statusCode, THttpResponseHeader &header, const QByteArray &contentType, QIODevice *body, qint64 length)
{
    T_TRACEFUNC("statusCode:%d  contentType:%s  length:%s", statusCode, contentType.data(), qPrintable(QString::number(length)));

    header.setStatusLine(statusCode, THttpUtility::getResponseReasonPhrase(statusCode));
    if (!contentType.isEmpty())
        header.setContentType(contentType);

    return writeResponse(header, body, length);
}

qint64 TActionContext::writeResponse(THttpResponseHeader &header, QIODevice *body, qint64 length)
{
    T_TRACEFUNC("length:%s", qPrintable(QString::number(length)));

    header.setContentLength(length);
    header.setRawHeader("Server", "TreeFrog server");
    header.setCurrentDate();

    // Write data
    return writeResponse(header, body);
}

void TActionContext::emitError(int )
{ }

TTemporaryFile &TActionContext::createTemporaryFile()
{
    TTemporaryFile *file = new TTemporaryFile();
    tempFiles << file;
    return *file;
}

QHostAddress TActionContext::clientAddress() const
{
    return httpReq->clientAddress();
}
<commit_msg>   fix a bug of URL path traversal on windows.<commit_after>/* Copyright (c) 2010-2017, AOYAMA Kazuharu
 * All rights reserved.
 *
 * This software may be used and distributed according to the terms of
 * the New BSD License, which is incorporated herein by reference.
 */

#include <QtCore>
#include <QHostAddress>
#include <QSet>
#include <TActionContext>
#include <TWebApplication>
#include <TAppSettings>
#include <THttpRequest>
#include <THttpResponse>
#include <THttpUtility>
#include <TDispatcher>
#include <TActionController>
#include <TSessionStore>
#include "tsystemglobal.h"
#include "thttpsocket.h"
#include "tsessionmanager.h"
#include "turlroute.h"
#include "tabstractwebsocket.h"

/*!
  \class TActionContext
  \brief The TActionContext class is the base class of contexts for
  action controllers.
*/

TActionContext::TActionContext()
    : TDatabaseContext(),
      stopped(false),
      socketDesc(0),
      currController(nullptr),
      httpReq(nullptr)
{ }

TActionContext::~TActionContext()
{
    release();
}

static bool directViewRenderMode()
{
    static int mode = -1;
    if (mode < 0) {
        mode = (int)Tf::appSettings()->value(Tf::DirectViewRenderMode).toBool();
    }
    return (bool)mode;
}

void TActionContext::execute(THttpRequest &request, int sid)
{
    T_TRACEFUNC("");

    THttpResponseHeader responseHeader;
    accessLogger.open();

    try {
        httpReq = &request;
        const THttpRequestHeader &hdr = httpReq->header();

        // Access log
        accessLogger.setTimestamp(QDateTime::currentDateTime());
        QByteArray firstLine = hdr.method() + ' ' + hdr.path();
        firstLine += QString(" HTTP/%1.%2").arg(hdr.majorVersion()).arg(hdr.minorVersion()).toLatin1();
        accessLogger.setRequest(firstLine);
        accessLogger.setRemoteHost( (Tf::appSettings()->value(Tf::ListenPort).toUInt() > 0) ? clientAddress().toString().toLatin1() : QByteArray("(unix)") );

        tSystemDebug("method : %s", hdr.method().data());
        tSystemDebug("path : %s", hdr.path().data());

        // HTTP method
        Tf::HttpMethod method = httpReq->method();
        QString path = THttpUtility::fromUrlEncoding(hdr.path().mid(0, hdr.path().indexOf('?')));

        // Routing info exists?
        QStringList components = TUrlRoute::splitPath(path);
        TRouting rt = TUrlRoute::instance().findRouting(method, components);

        tSystemDebug("Routing: controller:%s  action:%s", rt.controller.data(),
                     rt.action.data());

        if (rt.isEmpty()) {
            // Default URL routing

            if (Q_UNLIKELY(directViewRenderMode())) { // Direct view render mode?
                // Direct view setting
                rt.setRouting("directcontroller", "show", components);
            } else {
                QByteArray c = components.value(0).toLatin1().toLower();
                if (Q_LIKELY(!c.isEmpty())) {
                    if (Q_LIKELY(!TActionController::disabledControllers().contains(c))) { // Can not call 'ApplicationController'
                        // Default action: "index"
                        QByteArray action = components.value(1, QLatin1String("index")).toLatin1();
                        rt.setRouting(c + "controller", action, components.mid(2));
                    }
                }
                tSystemDebug("Active Controller : %s", rt.controller.data());
            }
        }

        // Call controller method
        TDispatcher<TActionController> ctlrDispatcher(rt.controller);
        currController = ctlrDispatcher.object();
        if (currController) {
            currController->setActionName(rt.action);
            currController->setSocketId(sid);

            // Session
            if (currController->sessionEnabled()) {
                TSession session;
                QByteArray sessionId = httpReq->cookie(TSession::sessionName());
                if (!sessionId.isEmpty()) {
                    // Finds a session
                    session = TSessionManager::instance().findSession(sessionId);
                }
                currController->setSession(session);

                // Exports flash-variant
                currController->exportAllFlashVariants();
            }

            // Verify authenticity token
            if (Tf::appSettings()->value(Tf::EnableCsrfProtectionModule, true).toBool()
                && currController->csrfProtectionEnabled() && !currController->exceptionActionsOfCsrfProtection().contains(rt.action)) {

                if (method == Tf::Post || method == Tf::Put || method == Tf::Patch || method == Tf::Delete) {
                    if (!currController->verifyRequest(*httpReq)) {
                        throw SecurityException("Invalid authenticity token", __FILE__, __LINE__);
                    }
                }
            }

            if (currController->sessionEnabled()) {
                if (currController->session().id().isEmpty() || Tf::appSettings()->value(Tf::SessionAutoIdRegeneration).toBool()) {
                    TSessionManager::instance().remove(currController->session().sessionId); // Removes the old session
                    // Re-generate session ID
                    currController->session().sessionId = TSessionManager::instance().generateId();
                    tSystemDebug("Re-generate session ID: %s", currController->session().sessionId.data());
                }
                // Sets CSRF protection informaion
                TActionController::setCsrfProtectionInto(currController->session());
            }

            // Database Transaction
            setTransactionEnabled(currController->transactionEnabled());

            // Do filters
            if (Q_LIKELY(currController->preFilter())) {

                // Dispathes
                bool dispatched = ctlrDispatcher.invoke(rt.action, rt.params);
                if (Q_LIKELY(dispatched)) {
                    autoRemoveFiles << currController->autoRemoveFiles;  // Adds auto-remove files

                    // Post fileter
                    currController->postFilter();

                    if (Q_UNLIKELY(currController->rollbackRequested())) {
                        rollbackTransactions();
                    } else {
                        // Commits a transaction to the database
                        commitTransactions();
                    }

                    // Session store
                    if (currController->sessionEnabled()) {
                        bool stored = TSessionManager::instance().store(currController->session());
                        if (Q_LIKELY(stored)) {
                            static int cookielifetime = -1;
                            QDateTime expire;

                            if (cookielifetime < 0) {
                                cookielifetime = Tf::appSettings()->value(Tf::SessionLifeTime).toInt();
                            }
                            if (cookielifetime > 0) {
                                expire = QDateTime::currentDateTime().addSecs(cookielifetime);
                            }

                            // Sets the path in the session cookie
                            QString cookiePath = Tf::appSettings()->value(Tf::SessionCookiePath).toString();
                            currController->addCookie(TSession::sessionName(), currController->session().id(), expire, cookiePath, QString(), false, true);
                        }
                    }

                    // WebSocket tasks
                    if (!currController->taskList.isEmpty()) {
                        QVariantList lst;
                        for (auto &task : (const QList<QPair<int, QVariant>>&)currController->taskList) {
                            const QVariant &taskData = task.second;

                            switch (task.first) {
                            case TActionController::SendTextTo: {
                                lst = taskData.toList();
                                TAbstractWebSocket *websocket = TAbstractWebSocket::searchWebSocket(lst[0].toInt());
                                if (websocket) {
                                    websocket->sendText(lst[1].toString());
                                }
                                break; }

                            case TActionController::SendBinaryTo: {
                                lst = taskData.toList();
                                TAbstractWebSocket *websocket = TAbstractWebSocket::searchWebSocket(lst[0].toInt());
                                if (websocket) {
                                    websocket->sendBinary(lst[1].toByteArray());
                                }
                                break; }

                            case TActionController::SendCloseTo: {
                                lst = taskData.toList();
                                TAbstractWebSocket *websocket = TAbstractWebSocket::searchWebSocket(lst[0].toInt());
                                if (websocket) {
                                    websocket->sendClose(lst[1].toInt());
                                }
                                break; }

                            default:
                                tSystemError("Invalid logic  [%s:%d]",  __FILE__, __LINE__);
                                break;
                            }
                        }
                    }
                }
            }

            // Sets charset to the content-type
            QByteArray ctype = currController->response.header().contentType().toLower();
            if (ctype.startsWith("text") && !ctype.contains("charset")) {
                ctype += "; charset=";
                ctype += Tf::app()->codecForHttpOutput()->name();
                currController->response.header().setContentType(ctype);
            }

            // Sets the default status code of HTTP response
            accessLogger.setStatusCode( (!currController->response.isBodyNull()) ? currController->statusCode() : Tf::InternalServerError );
            currController->response.header().setStatusLine(accessLogger.statusCode(), THttpUtility::getResponseReasonPhrase(accessLogger.statusCode()));

            // Writes a response and access log
            qint64 bodyLength = (currController->response.header().contentLength() > 0) ? currController->response.header().contentLength() : currController->response.bodyLength();
            int bytes = writeResponse(currController->response.header(), currController->response.bodyIODevice(),
                                      bodyLength);
            accessLogger.setResponseBytes(bytes);

            // Session GC
            TSessionManager::instance().collectGarbage();

        } else {
            accessLogger.setStatusCode( Tf::BadRequest );  // Set a default status code

            if (Q_LIKELY(method == Tf::Get)) {  // GET Method
                path.remove(0, 1);
                QFile reqPath(Tf::app()->publicPath() + path);
                QFileInfo fi(reqPath);

                if (fi.isFile() && fi.isReadable() && fi.canonicalPath().contains(QFileInfo(Tf::app()->publicPath()).canonicalFilePath())) {
                    // Check "If-Modified-Since" header for caching
                    bool sendfile = true;
                    QByteArray ifModifiedSince = hdr.rawHeader("If-Modified-Since");

                    if (!ifModifiedSince.isEmpty()) {
                        QDateTime dt = THttpUtility::fromHttpDateTimeString(ifModifiedSince);
                        if (dt.isValid()) {
                            sendfile = (dt.toMSecsSinceEpoch() / 1000 != fi.lastModified().toMSecsSinceEpoch() / 1000);
                        }
                    }

                    if (sendfile) {
                        // Sends a request file
                        responseHeader.setRawHeader("Last-Modified", THttpUtility::toHttpDateTimeString(fi.lastModified()));
                        QByteArray type = Tf::app()->internetMediaType(fi.suffix());
                        int bytes = writeResponse(Tf::OK, responseHeader, type, &reqPath, reqPath.size());
                        accessLogger.setResponseBytes( bytes );
                    } else {
                        // Not send the data
                        int bytes = writeResponse(Tf::NotModified, responseHeader);
                        accessLogger.setResponseBytes( bytes );
                    }
                } else {
                    int bytes = writeResponse(Tf::NotFound, responseHeader);
                    accessLogger.setResponseBytes( bytes );
                }
                accessLogger.setStatusCode( responseHeader.statusCode() );

            } else if (method == Tf::Post) {
                // file upload?
            } else {
                // HEAD, DELETE, ...
            }
        }

    } catch (ClientErrorException &e) {
        tWarn("Caught ClientErrorException: status code:%d", e.statusCode());
        tSystemWarn("Caught ClientErrorException: status code:%d", e.statusCode());
        int bytes = writeResponse(e.statusCode(), responseHeader);
        accessLogger.setResponseBytes( bytes );
        accessLogger.setStatusCode( e.statusCode() );
    } catch (SqlException &e) {
        tError("Caught SqlException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught SqlException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        closeHttpSocket();
    } catch (KvsException &e) {
        tError("Caught KvsException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught KvsException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        closeHttpSocket();
    } catch (SecurityException &e) {
        tError("Caught SecurityException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught SecurityException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        closeHttpSocket();
    } catch (RuntimeException &e) {
        tError("Caught RuntimeException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught RuntimeException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        closeHttpSocket();
    } catch (StandardException &e) {
        tError("Caught StandardException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        tSystemError("Caught StandardException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
        closeHttpSocket();
    } catch (...) {
        tError("Caught Exception");
        tSystemError("Caught Exception");
        closeHttpSocket();
    }

    TActionContext::accessLogger.write();  // Writes access log
}

void TActionContext::release()
{
    TDatabaseContext::release();

    for (auto temp : (const QList<TTemporaryFile*> &)tempFiles) {
        delete temp;
    }
    tempFiles.clear();

    for (auto &file : (const QStringList&)autoRemoveFiles) {
        QFile(file).remove();
    }
    autoRemoveFiles.clear();
}

qint64 TActionContext::writeResponse(int statusCode, THttpResponseHeader &header)
{
    T_TRACEFUNC("statusCode:%d", statusCode);
    QByteArray body;
    if (statusCode >= 400) {
        QFile html(Tf::app()->publicPath() + QString::number(statusCode) + ".html");
        if (html.exists() && html.open(QIODevice::ReadOnly)) {
            body = html.readAll();
            html.close();
        }
    }
    if (body.isEmpty()) {
        body  = "";
        body += THttpUtility::getResponseReasonPhrase(statusCode);
        body += " (";
        body += QByteArray::number(statusCode);
        body += ")";
    }

    QBuffer buf(&body);
    return writeResponse(statusCode, header, "text/html", &buf, body.length());
}

qint64 TActionContext::writeResponse(int statusCode, THttpResponseHeader &header, const QByteArray &contentType, QIODevice *body, qint64 length)
{
    T_TRACEFUNC("statusCode:%d  contentType:%s  length:%s", statusCode, contentType.data(), qPrintable(QString::number(length)));

    header.setStatusLine(statusCode, THttpUtility::getResponseReasonPhrase(statusCode));
    if (!contentType.isEmpty())
        header.setContentType(contentType);

    return writeResponse(header, body, length);
}

qint64 TActionContext::writeResponse(THttpResponseHeader &header, QIODevice *body, qint64 length)
{
    T_TRACEFUNC("length:%s", qPrintable(QString::number(length)));

    header.setContentLength(length);
    header.setRawHeader("Server", "TreeFrog server");
    header.setCurrentDate();

    // Write data
    return writeResponse(header, body);
}

void TActionContext::emitError(int )
{ }

TTemporaryFile &TActionContext::createTemporaryFile()
{
    TTemporaryFile *file = new TTemporaryFile();
    tempFiles << file;
    return *file;
}

QHostAddress TActionContext::clientAddress() const
{
    return httpReq->clientAddress();
}
<|endoftext|>


<commit_before>// Copyright (c) 2012-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "addrman.h"
#include "chainparams.h"
#include "hash.h"
#include "net.h"
#include "serialize.h"
#include "streams.h"

#include "test/test_pivx.h"

#include <string>

#include <boost/test/unit_test.hpp>

class CAddrManSerializationMock : public CAddrMan
{
public:
    virtual void Serialize(CDataStream& s, int nType, int nVersionDummy) const = 0;
};

class CAddrManUncorrupted : public CAddrManSerializationMock
{
public:
    void Serialize(CDataStream& s, int nType, int nVersionDummy) const
    {
        CAddrMan::Serialize(s, nType, nVersionDummy);
    }
};

class CAddrManCorrupted : public CAddrManSerializationMock
{
public:
    void Serialize(CDataStream& s, int nType, int nVersionDummy) const
    {
        // Produces corrupt output that claims addrman has 20 addrs when it only has one addr.
        unsigned char nVersion = 1;
        s << nVersion;
        s << ((unsigned char)32);
        s << nKey;
        s << 10; // nNew
        s << 10; // nTried

        int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
        s << nUBuckets;

        CAddress addr = CAddress(CService("252.1.1.1", 7777));
        CAddrInfo info = CAddrInfo(addr, CNetAddr("252.2.2.2"));
        s << info;
    }
};

CDataStream AddrmanToStream(CAddrManSerializationMock& addrman)
{
    CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION);
    ssPeersIn << FLATDATA(Params().MessageStart());
    ssPeersIn << addrman;
    std::string str = ssPeersIn.str();
    std::vector<unsigned char> vchData(str.begin(), str.end());
    return CDataStream(vchData, SER_DISK, CLIENT_VERSION);
}

BOOST_FIXTURE_TEST_SUITE(net_tests, BasicTestingSetup)

BOOST_AUTO_TEST_CASE(caddrdb_read)
{
    CAddrManUncorrupted addrmanUncorrupted;

    CService addr1 = CService("250.7.1.1", 8333);
    CService addr2 = CService("250.7.2.2", 9999);
    CService addr3 = CService("250.7.3.3", 9999);

    // Add three addresses to new table.
    addrmanUncorrupted.Add(CAddress(addr1), CService("252.5.1.1", 8333));
    addrmanUncorrupted.Add(CAddress(addr2), CService("252.5.1.1", 8333));
    addrmanUncorrupted.Add(CAddress(addr3), CService("252.5.1.1", 8333));

    // Test that the de-serialization does not throw an exception.
    CDataStream ssPeers1 = AddrmanToStream(addrmanUncorrupted);
    bool exceptionThrown = false;
    CAddrMan addrman1;

    BOOST_CHECK(addrman1.size() == 0);
    try {
        unsigned char pchMsgTmp[4];
        ssPeers1 >> FLATDATA(pchMsgTmp);
        ssPeers1 >> addrman1;
    } catch (const std::exception& e) {
        exceptionThrown = true;
    }

    BOOST_CHECK(addrman1.size() == 3);
    BOOST_CHECK(exceptionThrown == false);

    // Test that CAddrDB::Read creates an addrman with the correct number of addrs.
    CDataStream ssPeers2 = AddrmanToStream(addrmanUncorrupted);

    CAddrMan addrman2;
    CAddrDB adb;
    BOOST_CHECK(addrman2.size() == 0);
    adb.Read(addrman2, ssPeers2);
    BOOST_CHECK(addrman2.size() == 3);
}

BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
{
    CAddrManCorrupted addrmanCorrupted;

    // Test that the de-serialization of corrupted addrman throws an exception.
    CDataStream ssPeers1 = AddrmanToStream(addrmanCorrupted);
    bool exceptionThrown = false;
    CAddrMan addrman1;
    BOOST_CHECK(addrman1.size() == 0);
    try {
        unsigned char pchMsgTmp[4];
        ssPeers1 >> FLATDATA(pchMsgTmp);
        ssPeers1 >> addrman1;
    } catch (const std::exception& e) {
        exceptionThrown = true;
    }
    // Even through de-serialization failed addrman is not left in a clean state.
    BOOST_CHECK(addrman1.size() == 1);
    BOOST_CHECK(exceptionThrown);

    // Test that CAddrDB::Read leaves addrman in a clean state if de-serialization fails.
    CDataStream ssPeers2 = AddrmanToStream(addrmanCorrupted);

    CAddrMan addrman2;
    CAddrDB adb;
    BOOST_CHECK(addrman2.size() == 0);
    adb.Read(addrman2, ssPeers2);
    BOOST_CHECK(addrman2.size() == 0);
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>[Tests] Remove non-determinism which is breaking net_tests<commit_after>// Copyright (c) 2012-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "addrman.h"
#include "chainparams.h"
#include "hash.h"
#include "net.h"
#include "serialize.h"
#include "streams.h"

#include "test/test_pivx.h"

#include <string>

#include <boost/test/unit_test.hpp>

class CAddrManSerializationMock : public CAddrMan
{
public:
    virtual void Serialize(CDataStream& s, int nType, int nVersionDummy) const = 0;

    //! Ensure that bucket placement is always the same for testing purposes.
    void MakeDeterministic()
    {
        nKey.SetNull();
        SeedInsecureRand(true);
    }
};

class CAddrManUncorrupted : public CAddrManSerializationMock
{
public:
    void Serialize(CDataStream& s, int nType, int nVersionDummy) const
    {
        CAddrMan::Serialize(s, nType, nVersionDummy);
    }
};

class CAddrManCorrupted : public CAddrManSerializationMock
{
public:
    void Serialize(CDataStream& s, int nType, int nVersionDummy) const
    {
        // Produces corrupt output that claims addrman has 20 addrs when it only has one addr.
        unsigned char nVersion = 1;
        s << nVersion;
        s << ((unsigned char)32);
        s << nKey;
        s << 10; // nNew
        s << 10; // nTried

        int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
        s << nUBuckets;

        CAddress addr = CAddress(CService("252.1.1.1", 7777));
        CAddrInfo info = CAddrInfo(addr, CNetAddr("252.2.2.2"));
        s << info;
    }
};

CDataStream AddrmanToStream(CAddrManSerializationMock& addrman)
{
    CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION);
    ssPeersIn << FLATDATA(Params().MessageStart());
    ssPeersIn << addrman;
    std::string str = ssPeersIn.str();
    std::vector<unsigned char> vchData(str.begin(), str.end());
    return CDataStream(vchData, SER_DISK, CLIENT_VERSION);
}

BOOST_FIXTURE_TEST_SUITE(net_tests, BasicTestingSetup)

BOOST_AUTO_TEST_CASE(caddrdb_read)
{
    CAddrManUncorrupted addrmanUncorrupted;
    addrmanUncorrupted.MakeDeterministic();

    CService addr1 = CService("250.7.1.1", 8333);
    CService addr2 = CService("250.7.2.2", 9999);
    CService addr3 = CService("250.7.3.3", 9999);

    // Add three addresses to new table.
    addrmanUncorrupted.Add(CAddress(addr1), CService("252.5.1.1", 8333));
    addrmanUncorrupted.Add(CAddress(addr2), CService("252.5.1.1", 8333));
    addrmanUncorrupted.Add(CAddress(addr3), CService("252.5.1.1", 8333));

    // Test that the de-serialization does not throw an exception.
    CDataStream ssPeers1 = AddrmanToStream(addrmanUncorrupted);
    bool exceptionThrown = false;
    CAddrMan addrman1;

    BOOST_CHECK(addrman1.size() == 0);
    try {
        unsigned char pchMsgTmp[4];
        ssPeers1 >> FLATDATA(pchMsgTmp);
        ssPeers1 >> addrman1;
    } catch (const std::exception& e) {
        exceptionThrown = true;
    }

    BOOST_CHECK(addrman1.size() == 3);
    BOOST_CHECK(exceptionThrown == false);

    // Test that CAddrDB::Read creates an addrman with the correct number of addrs.
    CDataStream ssPeers2 = AddrmanToStream(addrmanUncorrupted);

    CAddrMan addrman2;
    CAddrDB adb;
    BOOST_CHECK(addrman2.size() == 0);
    adb.Read(addrman2, ssPeers2);
    BOOST_CHECK(addrman2.size() == 3);
}

BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
{
    CAddrManCorrupted addrmanCorrupted;
    addrmanCorrupted.MakeDeterministic();

    // Test that the de-serialization of corrupted addrman throws an exception.
    CDataStream ssPeers1 = AddrmanToStream(addrmanCorrupted);
    bool exceptionThrown = false;
    CAddrMan addrman1;
    BOOST_CHECK(addrman1.size() == 0);
    try {
        unsigned char pchMsgTmp[4];
        ssPeers1 >> FLATDATA(pchMsgTmp);
        ssPeers1 >> addrman1;
    } catch (const std::exception& e) {
        exceptionThrown = true;
    }
    // Even through de-serialization failed addrman is not left in a clean state.
    BOOST_CHECK(addrman1.size() == 1);
    BOOST_CHECK(exceptionThrown);

    // Test that CAddrDB::Read leaves addrman in a clean state if de-serialization fails.
    CDataStream ssPeers2 = AddrmanToStream(addrmanCorrupted);

    CAddrMan addrman2;
    CAddrDB adb;
    BOOST_CHECK(addrman2.size() == 0);
    adb.Read(addrman2, ssPeers2);
    BOOST_CHECK(addrman2.size() == 0);
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>#include <QtGui>
#include <QApplication>
#include <QDesktopServices>
#include <QFile>
#include <QTextStream>
#include <QDir>
#include <QCoreApplication>

#include "QtAwesome.h"
#include "cloud-view.h"
#include "seafile-applet.h"
#include "configurator.h"
#include "tray-icon.h"
#include "login-dialog.h"
#include "utils/utils.h"

#include "main-window.h"

namespace {

enum WIDGET_INDEX {
    INDEX_CLOUD_VIEW = 0,
    INDEX_LOCAL_VIEW
};

} // namespace

MainWindow::MainWindow()
{
    setWindowIcon(QIcon(":/images/seafile.png"));
    setWindowTitle(getBrand());

    // Qt::Tool hides the taskbar entry on windows
    // setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint);

    setWindowFlags(Qt::Window
#ifndef Q_WS_MAC
                   | Qt::FramelessWindowHint
#endif
                   | Qt::WindowSystemMenuHint
                   | Qt::WindowMinimizeButtonHint);

    cloud_view_ = new CloudView;

    QVBoxLayout *layout = new QVBoxLayout;
    layout->setContentsMargins(0, 0, 0, 0);
    layout->addWidget(cloud_view_);

    QWidget *wrapper = new QWidget;
    wrapper->setObjectName("mainWrapper");
    wrapper->setLayout(layout);

    setCentralWidget(wrapper);

    createActions();
    setAttribute(Qt::WA_TranslucentBackground, true);
}

void MainWindow::hide()
{
    writeSettings();
    QMainWindow::hide();
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    hide();
    event->ignore();
}

bool MainWindow::event(QEvent *ev)
{
    bool ret = QMainWindow::event(ev);

    if (isMinimized() && ev->type() == QEvent::WindowStateChange) {
        QWindowStateChangeEvent *wev = (QWindowStateChangeEvent *)ev;
        if (wev->oldState() != Qt::WindowMinimized) {
            writeSettings();
        }
    }
    return ret;
}

void MainWindow::changeEvent(QEvent *event)
{
#ifdef Q_WS_WIN
    /*
     * Solve the problem of restoring a minimized frameless window on Windows
     * See http://stackoverflow.com/questions/18614661/how-to-not-hide-taskbar-item-during-using-hide
     */
    if(event->type() == QEvent::WindowStateChange) {
        if(windowState() & Qt::WindowMinimized ) {
            //do something after minimize
        } else {
            cloud_view_->hide();
            cloud_view_->show();
        }
    }
#endif
}

void MainWindow::showEvent(QShowEvent *event)
{
    readSettings();
    QMainWindow::showEvent(event);
}

void MainWindow::createActions()
{
    refresh_qss_action_ = new QAction(QIcon(":/images/refresh.png"), tr("Refresh"), this);
    connect(refresh_qss_action_, SIGNAL(triggered()), this, SLOT(refreshQss()));
}

void MainWindow::keyPressEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_F5) {
        refreshQss();
        return;
    }

    QMainWindow::keyPressEvent(event);
}

void MainWindow::showWindow()
{
    showNormal();
    show();
    raise();
    activateWindow();
}

void MainWindow::refreshQss()
{
    seafApplet->refreshQss();
}

void MainWindow::writeSettings()
{
    QSettings settings;

    settings.beginGroup("MainWindow");
    settings.setValue("size", size());
    settings.setValue("pos", pos());
    settings.endGroup();
}

QPoint MainWindow::getDefaultPosition()
{
    const QRect screen = QApplication::desktop()->screenGeometry();
    const QPoint top_right = screen.topRight();

    int top_margin = rect().width() + qMin(150, (int)(0.1 * screen.width()));
    int right_margin = qMin(150, (int)(0.1 * screen.width()));
    QPoint default_pos(top_right.x() -top_margin, top_right.y() + right_margin);

    return default_pos;
}

void MainWindow::readSettings()
{
    QPoint pos;
    QSize size;
    QSettings settings;
    settings.beginGroup("MainWindow");

    static bool first_show = true;

    if (first_show && seafApplet->configurator()->firstUse()) {
        pos = getDefaultPosition();
    } else {
        pos = settings.value("pos", getDefaultPosition()).toPoint();
        size = settings.value("size", QSize()).toSize();
    }

    first_show = false;

    move(pos);
    resize(size);
}
<commit_msg>fixed system shutdown problem<commit_after>#include <QtGui>
#include <QApplication>
#include <QDesktopServices>
#include <QFile>
#include <QTextStream>
#include <QDir>
#include <QCoreApplication>

#include "QtAwesome.h"
#include "cloud-view.h"
#include "seafile-applet.h"
#include "configurator.h"
#include "tray-icon.h"
#include "login-dialog.h"
#include "utils/utils.h"

#include "main-window.h"

namespace {

enum WIDGET_INDEX {
    INDEX_CLOUD_VIEW = 0,
    INDEX_LOCAL_VIEW
};

} // namespace

MainWindow::MainWindow()
{
    setWindowIcon(QIcon(":/images/seafile.png"));
    setWindowTitle(getBrand());

    // Qt::Tool hides the taskbar entry on windows
    // setWindowFlags(Qt::FramelessWindowHint | Qt::Tool | Qt::WindowStaysOnTopHint);

    setWindowFlags(Qt::Window
#ifndef Q_WS_MAC
                   | Qt::FramelessWindowHint
#endif
                   | Qt::WindowSystemMenuHint
                   | Qt::WindowMinimizeButtonHint);

    cloud_view_ = new CloudView;

    QVBoxLayout *layout = new QVBoxLayout;
    layout->setContentsMargins(0, 0, 0, 0);
    layout->addWidget(cloud_view_);

    QWidget *wrapper = new QWidget;
    wrapper->setObjectName("mainWrapper");
    wrapper->setLayout(layout);

    setCentralWidget(wrapper);

    createActions();
    setAttribute(Qt::WA_TranslucentBackground, true);
}

void MainWindow::hide()
{
    writeSettings();
    QMainWindow::hide();
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    hide();
    // event->ignore();
}

bool MainWindow::event(QEvent *ev)
{
    bool ret = QMainWindow::event(ev);

    if (isMinimized() && ev->type() == QEvent::WindowStateChange) {
        QWindowStateChangeEvent *wev = (QWindowStateChangeEvent *)ev;
        if (wev->oldState() != Qt::WindowMinimized) {
            writeSettings();
        }
    }
    return ret;
}

void MainWindow::changeEvent(QEvent *event)
{
#ifdef Q_WS_WIN
    /*
     * Solve the problem of restoring a minimized frameless window on Windows
     * See http://stackoverflow.com/questions/18614661/how-to-not-hide-taskbar-item-during-using-hide
     */
    if(event->type() == QEvent::WindowStateChange) {
        if(windowState() & Qt::WindowMinimized ) {
            //do something after minimize
        } else {
            cloud_view_->hide();
            cloud_view_->show();
        }
    }
#endif
}

void MainWindow::showEvent(QShowEvent *event)
{
    readSettings();
    QMainWindow::showEvent(event);
}

void MainWindow::createActions()
{
    refresh_qss_action_ = new QAction(QIcon(":/images/refresh.png"), tr("Refresh"), this);
    connect(refresh_qss_action_, SIGNAL(triggered()), this, SLOT(refreshQss()));
}

void MainWindow::keyPressEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_F5) {
        refreshQss();
        return;
    }

    QMainWindow::keyPressEvent(event);
}

void MainWindow::showWindow()
{
    showNormal();
    show();
    raise();
    activateWindow();
}

void MainWindow::refreshQss()
{
    seafApplet->refreshQss();
}

void MainWindow::writeSettings()
{
    QSettings settings;

    settings.beginGroup("MainWindow");
    settings.setValue("size", size());
    settings.setValue("pos", pos());
    settings.endGroup();
}

QPoint MainWindow::getDefaultPosition()
{
    const QRect screen = QApplication::desktop()->screenGeometry();
    const QPoint top_right = screen.topRight();

    int top_margin = rect().width() + qMin(150, (int)(0.1 * screen.width()));
    int right_margin = qMin(150, (int)(0.1 * screen.width()));
    QPoint default_pos(top_right.x() -top_margin, top_right.y() + right_margin);

    return default_pos;
}

void MainWindow::readSettings()
{
    QPoint pos;
    QSize size;
    QSettings settings;
    settings.beginGroup("MainWindow");

    static bool first_show = true;

    if (first_show && seafApplet->configurator()->firstUse()) {
        pos = getDefaultPosition();
    } else {
        pos = settings.value("pos", getDefaultPosition()).toPoint();
        size = settings.value("size", QSize()).toSize();
    }

    first_show = false;

    move(pos);
    resize(size);
}
<|endoftext|>


<commit_before>/*
 * This file is part of the PySide project.
 *
 * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
 *
 * Contact: PySide team <contact@pyside.org>
 *
 * 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.
 *
 * 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 "pyside.h"
#include "signalmanager.h"
#include "pysideproperty_p.h"
#include "pysideproperty.h"
#include "pysidesignal.h"
#include "pysidesignal_p.h"
#include "pysideslot_p.h"
#include "pysidemetafunction_p.h"
#include "pysidemetafunction.h"
#include "dynamicqmetaobject.h"

#include <basewrapper.h>
#include <conversions.h>
#include <typeresolver.h>
#include <bindingmanager.h>
#include <algorithm>
#include <cctype>
#include <QStack>
#include <QCoreApplication>

static QStack<PySide::CleanupFunction> cleanupFunctionList;

namespace PySide
{

void init(PyObject *module)
{
    Signal::init(module);
    Slot::init(module);
    Property::init(module);
    MetaFunction::init(module);
    // Init signal manager, so it will register some meta types used by QVariant.
    SignalManager::instance();
}

bool fillQtProperties(PyObject* qObj, const QMetaObject* metaObj, PyObject* kwds, const char** blackList, unsigned int blackListSize)
{

    PyObject *key, *value;
    Py_ssize_t pos = 0;

    while (PyDict_Next(kwds, &pos, &key, &value)) {
        if (!blackListSize || !std::binary_search(blackList, blackList + blackListSize, std::string(PyString_AS_STRING(key)))) {
            QByteArray propName(PyString_AS_STRING(key));
            if (metaObj->indexOfProperty(propName) != -1) {
                propName[0] = std::toupper(propName[0]);
                propName.prepend("set");

                Shiboken::AutoDecRef propSetter(PyObject_GetAttrString(qObj, propName.constData()));
                if (!propSetter.isNull()) {
                    Shiboken::AutoDecRef args(PyTuple_Pack(1, value));
                    Shiboken::AutoDecRef retval(PyObject_CallObject(propSetter, args));
                } else {
                    PyObject* attr = PyObject_GenericGetAttr(qObj, key);
                    if (PySide::Property::isPropertyType(attr))
                        PySide::Property::setValue(reinterpret_cast<PySideProperty*>(attr), qObj, value);
                }
            } else {
                propName.append("()");
                if (metaObj->indexOfSignal(propName) != -1) {
                    propName.prepend('2');
                    PySide::Signal::connect(qObj, propName, value);
                } else {
                    PyErr_Format(PyExc_AttributeError, "'%s' is not a Qt property or a signal", propName.constData());
                    return false;
                };
            }
        }
    }
    return true;
}

void registerCleanupFunction(CleanupFunction func)
{
    cleanupFunctionList.push(func);
}

void runCleanupFunctions()
{
    while (!cleanupFunctionList.isEmpty()) {
        CleanupFunction f = cleanupFunctionList.pop();
        f();
    }
}

static void destructionVisitor(SbkObject* pyObj, void* data)
{
    void** realData = reinterpret_cast<void**>(data);
    SbkObject* pyQApp = reinterpret_cast<SbkObject*>(realData[0]);
    PyTypeObject* pyQObjectType = reinterpret_cast<PyTypeObject*>(realData[1]);

    if (pyObj != pyQApp && PyObject_TypeCheck(pyObj, pyQObjectType)) {
        if (Shiboken::Object::hasOwnership(pyObj))
            Shiboken::callCppDestructor<QObject>(Shiboken::Object::cppPointer(pyObj, Shiboken::SbkType<QObject*>()));
    }
};

void destroyQCoreApplication()
{
    SignalManager::instance().clear();
    QCoreApplication* app = QCoreApplication::instance();
    if (!app)
        return;

    Shiboken::BindingManager& bm = Shiboken::BindingManager::instance();
    SbkObject* pyQApp = bm.retrieveWrapper(app);
    PyTypeObject* pyQObjectType = Shiboken::TypeResolver::get("QObject*")->pythonType();
    assert(pyQObjectType);

    void* data[2] = {pyQApp, pyQObjectType};
    bm.visitAllPyObjects(&destructionVisitor, &data);

    // in the end destroy app
    delete app;
}

void initDynamicMetaObject(SbkObjectType* type, const QMetaObject* base)
{
    const char* typeName = type->super.ht_type.tp_name;
    int len = strlen(typeName);
    for (int i = len-1; i >= 0; --i)
        if (typeName[i] == '.')
            typeName += i + 1;
    DynamicQMetaObject* mo = new PySide::DynamicQMetaObject(typeName, base);
    Shiboken::ObjectType::setTypeUserData(type, mo, &Shiboken::callCppDestructor<DynamicQMetaObject>);
}

void initQObjectSubType(SbkObjectType* type, PyObject* args, PyObject* kwds)
{
    PyTypeObject* qObjType = Shiboken::TypeResolver::get("QObject*")->pythonType();
    QByteArray className(PyString_AS_STRING(PyTuple_GET_ITEM(args, 0)));

    PyObject* bases = PyTuple_GET_ITEM(args, 1);
    int numBases = PyTuple_GET_SIZE(args);
    QMetaObject* baseMo = 0;

    for (int i = 0; i < numBases; ++i) {
        PyTypeObject* base = reinterpret_cast<PyTypeObject*>(PyTuple_GET_ITEM(bases, i));
        if (PyType_IsSubtype(base, qObjType)) {
            baseMo = reinterpret_cast<QMetaObject*>(Shiboken::ObjectType::getTypeUserData(reinterpret_cast<SbkObjectType*>(base)));
            // If it's a class like QObject, QWidget, etc, use the original QMetaObject instead of the dynamic one
            // IMO this if is a bug, however it keeps the current behaviour.
            if (!Shiboken::ObjectType::isUserType(base))
                baseMo = const_cast<QMetaObject*>(baseMo->d.superdata);
            break;
        }
    }
    if (!baseMo) {
        qWarning("Sub class of QObject not inheriting QObject!? Crash will happen when using %s.", className.constData());
        return;
    }

    DynamicQMetaObject* mo = new PySide::DynamicQMetaObject(className.constData(), baseMo);

    Shiboken::ObjectType::setTypeUserData(type, mo, &Shiboken::callCppDestructor<DynamicQMetaObject>);

    PyObject* attrs = PyTuple_GET_ITEM(args, 2);
    PyObject* key;
    PyObject* value;
    Py_ssize_t pos = 0;

    typedef std::pair<const char*, PyObject*> PropPair;
    QList<PropPair> properties;

    Shiboken::AutoDecRef slotAttrName(PyString_FromString(PYSIDE_SLOT_LIST_ATTR));

    while (PyDict_Next(attrs, &pos, &key, &value)) {
        if (PyType_IsSubtype(value->ob_type, &PySidePropertyType)) {
            // Leave the properties to be register after signals because they may depend on notify signals
            properties << PropPair(PyString_AS_STRING(key), value);
        } else if (value->ob_type == &PySideSignalType) { // Register signals
            PySideSignal* data = reinterpret_cast<PySideSignal*>(value);
            const char* signalName = PyString_AS_STRING(key);
            data->signalName = strdup(signalName);
            QByteArray sig;
            sig.reserve(128);
            for (int i = 0; i < data->signaturesSize; ++i) {
                sig = signalName;
                sig += '(';
                if (data->signatures[i])
                    sig += data->signatures[i];
                sig += ')';
                if (baseMo->indexOfSignal(sig) == -1)
                    mo->addSignal(sig);
            }
        } else if (PyFunction_Check(value)) { // Register slots
            if (PyObject_HasAttr(value, slotAttrName)) {
                PyObject* signatureList = PyObject_GetAttr(value, slotAttrName);
                for(Py_ssize_t i = 0, i_max = PyList_Size(signatureList); i < i_max; ++i) {
                    PyObject* signature = PyList_GET_ITEM(signatureList, i);
                    QByteArray sig(PyString_AS_STRING(signature));
                    //slot the slot type and signature
                    QList<QByteArray> slotInfo = sig.split(' ');
                    int index = baseMo->indexOfSlot(slotInfo[1]);
                    if (index == -1)
                        mo->addSlot(slotInfo[1], slotInfo[0]);
                }
            }
        }
    }

    // Register properties
    foreach (PropPair propPair, properties)
        mo->addProperty(propPair.first, propPair.second);
}

PyObject* getMetaDataFromQObject(QObject* cppSelf, PyObject* self, PyObject* name)
{
    PyObject* attr = PyObject_GenericGetAttr(self, name);
    if (attr && Property::isPropertyType(attr)) {
        PyObject *value = Property::getValue(reinterpret_cast<PySideProperty*>(attr), self);
        if (!value)
            return 0;
        Py_DECREF(attr);
        Py_INCREF(value);
        attr = value;
    }

    //mutate native signals to signal instance type
    if (attr && PyObject_TypeCheck(attr, &PySideSignalType)) {
        PyObject* signal = reinterpret_cast<PyObject*>(Signal::initialize(reinterpret_cast<PySideSignal*>(attr), name, self));
        PyObject_SetAttr(self, name, reinterpret_cast<PyObject*>(signal));
        return signal;
    }

    //search on metaobject (avoid internal attributes started with '__')
    if (!attr && !QString(PyString_AS_STRING(name)).startsWith("__")) {
        const QMetaObject* metaObject = cppSelf->metaObject();
        QByteArray cname(PyString_AS_STRING(name));
        cname += '(';
        //signal
        QList<QMetaMethod> signalList;
        for(int i=0, i_max = metaObject->methodCount(); i < i_max; i++) {
            QMetaMethod method = metaObject->method(i);
            if (QString(method.signature()).startsWith(cname)) {
                if (method.methodType() == QMetaMethod::Signal) {
                    signalList.append(method);
                } else {
                    PySideMetaFunction* func = MetaFunction::newObject(cppSelf, i);
                    if (func) {
                        PyObject_SetAttr(self, name, (PyObject*)func);
                        return (PyObject*)func;
                    }
                }
            }
        }
        if (signalList.size() > 0) {
            PyObject* pySignal = reinterpret_cast<PyObject*>(Signal::newObjectFromMethod(self, signalList));
            PyObject_SetAttr(self, name, pySignal);
            return pySignal;
        }
    }
    return attr;
}

} //namespace PySide

<commit_msg>Fixed MetaObject creation based on typename.<commit_after>/*
 * This file is part of the PySide project.
 *
 * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
 *
 * Contact: PySide team <contact@pyside.org>
 *
 * 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.
 *
 * 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 "pyside.h"
#include "signalmanager.h"
#include "pysideproperty_p.h"
#include "pysideproperty.h"
#include "pysidesignal.h"
#include "pysidesignal_p.h"
#include "pysideslot_p.h"
#include "pysidemetafunction_p.h"
#include "pysidemetafunction.h"
#include "dynamicqmetaobject.h"

#include <basewrapper.h>
#include <conversions.h>
#include <typeresolver.h>
#include <bindingmanager.h>
#include <algorithm>
#include <cctype>
#include <QStack>
#include <QCoreApplication>

static QStack<PySide::CleanupFunction> cleanupFunctionList;

namespace PySide
{

void init(PyObject *module)
{
    Signal::init(module);
    Slot::init(module);
    Property::init(module);
    MetaFunction::init(module);
    // Init signal manager, so it will register some meta types used by QVariant.
    SignalManager::instance();
}

bool fillQtProperties(PyObject* qObj, const QMetaObject* metaObj, PyObject* kwds, const char** blackList, unsigned int blackListSize)
{

    PyObject *key, *value;
    Py_ssize_t pos = 0;

    while (PyDict_Next(kwds, &pos, &key, &value)) {
        if (!blackListSize || !std::binary_search(blackList, blackList + blackListSize, std::string(PyString_AS_STRING(key)))) {
            QByteArray propName(PyString_AS_STRING(key));
            if (metaObj->indexOfProperty(propName) != -1) {
                propName[0] = std::toupper(propName[0]);
                propName.prepend("set");

                Shiboken::AutoDecRef propSetter(PyObject_GetAttrString(qObj, propName.constData()));
                if (!propSetter.isNull()) {
                    Shiboken::AutoDecRef args(PyTuple_Pack(1, value));
                    Shiboken::AutoDecRef retval(PyObject_CallObject(propSetter, args));
                } else {
                    PyObject* attr = PyObject_GenericGetAttr(qObj, key);
                    if (PySide::Property::isPropertyType(attr))
                        PySide::Property::setValue(reinterpret_cast<PySideProperty*>(attr), qObj, value);
                }
            } else {
                propName.append("()");
                if (metaObj->indexOfSignal(propName) != -1) {
                    propName.prepend('2');
                    PySide::Signal::connect(qObj, propName, value);
                } else {
                    PyErr_Format(PyExc_AttributeError, "'%s' is not a Qt property or a signal", propName.constData());
                    return false;
                };
            }
        }
    }
    return true;
}

void registerCleanupFunction(CleanupFunction func)
{
    cleanupFunctionList.push(func);
}

void runCleanupFunctions()
{
    while (!cleanupFunctionList.isEmpty()) {
        CleanupFunction f = cleanupFunctionList.pop();
        f();
    }
}

static void destructionVisitor(SbkObject* pyObj, void* data)
{
    void** realData = reinterpret_cast<void**>(data);
    SbkObject* pyQApp = reinterpret_cast<SbkObject*>(realData[0]);
    PyTypeObject* pyQObjectType = reinterpret_cast<PyTypeObject*>(realData[1]);

    if (pyObj != pyQApp && PyObject_TypeCheck(pyObj, pyQObjectType)) {
        if (Shiboken::Object::hasOwnership(pyObj))
            Shiboken::callCppDestructor<QObject>(Shiboken::Object::cppPointer(pyObj, Shiboken::SbkType<QObject*>()));
    }
};

void destroyQCoreApplication()
{
    SignalManager::instance().clear();
    QCoreApplication* app = QCoreApplication::instance();
    if (!app)
        return;

    Shiboken::BindingManager& bm = Shiboken::BindingManager::instance();
    SbkObject* pyQApp = bm.retrieveWrapper(app);
    PyTypeObject* pyQObjectType = Shiboken::TypeResolver::get("QObject*")->pythonType();
    assert(pyQObjectType);

    void* data[2] = {pyQApp, pyQObjectType};
    bm.visitAllPyObjects(&destructionVisitor, &data);

    // in the end destroy app
    delete app;
}

void initDynamicMetaObject(SbkObjectType* type, const QMetaObject* base)
{
    QByteArray typeName = QByteArray(type->super.ht_type.tp_name).split('.').last();
    DynamicQMetaObject* mo = new PySide::DynamicQMetaObject(typeName, base);
    Shiboken::ObjectType::setTypeUserData(type, mo, &Shiboken::callCppDestructor<DynamicQMetaObject>);
}

void initQObjectSubType(SbkObjectType* type, PyObject* args, PyObject* kwds)
{
    PyTypeObject* qObjType = Shiboken::TypeResolver::get("QObject*")->pythonType();
    QByteArray className(PyString_AS_STRING(PyTuple_GET_ITEM(args, 0)));

    PyObject* bases = PyTuple_GET_ITEM(args, 1);
    int numBases = PyTuple_GET_SIZE(args);
    QMetaObject* baseMo = 0;

    for (int i = 0; i < numBases; ++i) {
        PyTypeObject* base = reinterpret_cast<PyTypeObject*>(PyTuple_GET_ITEM(bases, i));
        if (PyType_IsSubtype(base, qObjType)) {
            baseMo = reinterpret_cast<QMetaObject*>(Shiboken::ObjectType::getTypeUserData(reinterpret_cast<SbkObjectType*>(base)));
            // If it's a class like QObject, QWidget, etc, use the original QMetaObject instead of the dynamic one
            // IMO this if is a bug, however it keeps the current behaviour.
            if (!Shiboken::ObjectType::isUserType(base))
                baseMo = const_cast<QMetaObject*>(baseMo->d.superdata);
            break;
        }
    }
    if (!baseMo) {
        qWarning("Sub class of QObject not inheriting QObject!? Crash will happen when using %s.", className.constData());
        return;
    }

    DynamicQMetaObject* mo = new PySide::DynamicQMetaObject(className.constData(), baseMo);

    Shiboken::ObjectType::setTypeUserData(type, mo, &Shiboken::callCppDestructor<DynamicQMetaObject>);

    PyObject* attrs = PyTuple_GET_ITEM(args, 2);
    PyObject* key;
    PyObject* value;
    Py_ssize_t pos = 0;

    typedef std::pair<const char*, PyObject*> PropPair;
    QList<PropPair> properties;

    Shiboken::AutoDecRef slotAttrName(PyString_FromString(PYSIDE_SLOT_LIST_ATTR));

    while (PyDict_Next(attrs, &pos, &key, &value)) {
        if (PyType_IsSubtype(value->ob_type, &PySidePropertyType)) {
            // Leave the properties to be register after signals because they may depend on notify signals
            properties << PropPair(PyString_AS_STRING(key), value);
        } else if (value->ob_type == &PySideSignalType) { // Register signals
            PySideSignal* data = reinterpret_cast<PySideSignal*>(value);
            const char* signalName = PyString_AS_STRING(key);
            data->signalName = strdup(signalName);
            QByteArray sig;
            sig.reserve(128);
            for (int i = 0; i < data->signaturesSize; ++i) {
                sig = signalName;
                sig += '(';
                if (data->signatures[i])
                    sig += data->signatures[i];
                sig += ')';
                if (baseMo->indexOfSignal(sig) == -1)
                    mo->addSignal(sig);
            }
        } else if (PyFunction_Check(value)) { // Register slots
            if (PyObject_HasAttr(value, slotAttrName)) {
                PyObject* signatureList = PyObject_GetAttr(value, slotAttrName);
                for(Py_ssize_t i = 0, i_max = PyList_Size(signatureList); i < i_max; ++i) {
                    PyObject* signature = PyList_GET_ITEM(signatureList, i);
                    QByteArray sig(PyString_AS_STRING(signature));
                    //slot the slot type and signature
                    QList<QByteArray> slotInfo = sig.split(' ');
                    int index = baseMo->indexOfSlot(slotInfo[1]);
                    if (index == -1)
                        mo->addSlot(slotInfo[1], slotInfo[0]);
                }
            }
        }
    }

    // Register properties
    foreach (PropPair propPair, properties)
        mo->addProperty(propPair.first, propPair.second);
}

PyObject* getMetaDataFromQObject(QObject* cppSelf, PyObject* self, PyObject* name)
{
    PyObject* attr = PyObject_GenericGetAttr(self, name);
    if (attr && Property::isPropertyType(attr)) {
        PyObject *value = Property::getValue(reinterpret_cast<PySideProperty*>(attr), self);
        if (!value)
            return 0;
        Py_DECREF(attr);
        Py_INCREF(value);
        attr = value;
    }

    //mutate native signals to signal instance type
    if (attr && PyObject_TypeCheck(attr, &PySideSignalType)) {
        PyObject* signal = reinterpret_cast<PyObject*>(Signal::initialize(reinterpret_cast<PySideSignal*>(attr), name, self));
        PyObject_SetAttr(self, name, reinterpret_cast<PyObject*>(signal));
        return signal;
    }

    //search on metaobject (avoid internal attributes started with '__')
    if (!attr && !QString(PyString_AS_STRING(name)).startsWith("__")) {
        const QMetaObject* metaObject = cppSelf->metaObject();
        QByteArray cname(PyString_AS_STRING(name));
        cname += '(';
        //signal
        QList<QMetaMethod> signalList;
        for(int i=0, i_max = metaObject->methodCount(); i < i_max; i++) {
            QMetaMethod method = metaObject->method(i);
            if (QString(method.signature()).startsWith(cname)) {
                if (method.methodType() == QMetaMethod::Signal) {
                    signalList.append(method);
                } else {
                    PySideMetaFunction* func = MetaFunction::newObject(cppSelf, i);
                    if (func) {
                        PyObject_SetAttr(self, name, (PyObject*)func);
                        return (PyObject*)func;
                    }
                }
            }
        }
        if (signalList.size() > 0) {
            PyObject* pySignal = reinterpret_cast<PyObject*>(Signal::newObjectFromMethod(self, signalList));
            PyObject_SetAttr(self, name, pySignal);
            return pySignal;
        }
    }
    return attr;
}

} //namespace PySide

<|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/usr/console/uart.C $                                      */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2014,2016                        */
/* [+] Google Inc.                                                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
#include "uart.H"
#include <devicefw/userif.H>
#include <lpc/lpcif.H>
#include <kernel/console.H>
#include <sys/task.h>
#include <sys/time.h>
#include <errl/errlmanager.H>
#include <hwas/common/hwasCallout.H>
#include <console/console_reasoncodes.H>
#include <console/uartif.H>

namespace CONSOLE
{
    void Uart::initialize()
    {
        using namespace UARTREGS;

        errlHndl_t l_errl = NULL;

        do
        {
            // Check for failure from superclass initalize call.
            if (iv_failed) { break; }

            // Clear line control reg.
            l_errl = writeReg(LCR, 0x00);
            if (l_errl) { break; }

            // Check for existence of scratch register (and thus UART device).
            {
                const uint8_t value = 'h';
                uint8_t data = 0;
                l_errl = writeReg(SCR, value);
                if (l_errl) { break; }
                l_errl = readReg(SCR, data);
                if (l_errl) { break; }

                if (data != value)
                {
                    printk("UART: Device not found.\n");
                    break;
                }
            }

            // Reset interrupt register.
            l_errl = writeReg(IER, 0);
            if (l_errl) { break; }

            // Set baud rate.
            uint64_t divisor = (g_uartClock / 16) / g_uartBaud;
            l_errl = writeReg(LCR, LCR_DLAB);
            if (l_errl) { break; }
            l_errl = writeReg(DLL, divisor & 0xff);
            if (l_errl) { break; }
            l_errl = writeReg(DLM, divisor >> 8);
            if (l_errl) { break; }

            // Set 8N1 mode.
            l_errl = writeReg(LCR, LCR_DWL8 | LCR_NOP | LCR_STP1);
            if (l_errl) { break; }

            // Enable Request-to-send/Data-terminal-ready
            l_errl = writeReg(MCR, MCR_RTS | MCR_DTR);
            if (l_errl) { break; }

            // Clear and enable FIFOs.
            l_errl = writeReg(FCR, FCR_ENF | FCR_CLFR | FCR_CLFT);
            if (l_errl) { break; }

            // Found device.
            printk("UART: Device initialized.\n");
            iv_initialized = true;

        } while(0);

        if (l_errl)
        {
            iv_failed = true;
            errlCommit(l_errl, CONSOLE_COMP_ID);
        }
    }

    void Uart::putc(char c)
    {
        using namespace UARTREGS;

        errlHndl_t l_errl = NULL;
        do
        {
            if (iv_failed || !iv_initialized) { break; }

            // Wait for transmit FIFO to have space.
            {
                const uint64_t DELAY_NS = 100;
                const uint64_t DELAY_LOOPS = 10000;

                uint8_t data = 0;
                uint64_t loops = 0;

                do
                {
                    l_errl = readReg(LSR, data);
                    if (l_errl) { break; }

                    // Wait for idle or error status.
                    if (data == LSR_BAD || (data & LSR_THRE))
                    {
                        break;
                    }
                    nanosleep(0, DELAY_NS);
                    task_yield();

                    loops++;
                } while( loops < DELAY_LOOPS);

                if (l_errl)
                {
                    printk("UART: Error state in xmit - LPC error.\n");
                    iv_failed = true;
                    break;
                }
                else if (data == LSR_BAD)
                {
                    printk("UART: Error state in xmit - bad data.\n");
                    iv_failed = true;
                    /*@
                     * @errortype
                     * @moduleid    CONSOLE::MOD_CONSOLE_UART_PUTC
                     * @reasoncode  CONSOLE::RC_INVALID_DATA
                     * @devdesc     Unexpected data from LPC-UART interface.
                     */
                    l_errl = new ERRORLOG::ErrlEntry(
                                    ERRORLOG::ERRL_SEV_PREDICTIVE,
                                    CONSOLE::MOD_CONSOLE_UART_PUTC,
                                    CONSOLE::RC_INVALID_DATA);
                    //@TODO: RTC:116090 - Should be BMC hardware.
                    l_errl->addProcedureCallout(HWAS::EPUB_PRC_LVL_SUPP,
                                                HWAS::SRCI_PRIORITY_MED);
                    break;
                }
                else if (loops >= DELAY_LOOPS)
                {
                    printk("UART: FIFO timeout.\n");
                    iv_failed = true;
                    /*@
                     * @errortype
                     * @moduleid    CONSOLE::MOD_CONSOLE_UART_PUTC
                     * @reasoncode  CONSOLE::RC_TIMEOUT
                     * @devdesc     Timeout from LPC-UART interface.
                     */
                    l_errl = new ERRORLOG::ErrlEntry(
                                    ERRORLOG::ERRL_SEV_PREDICTIVE,
                                    CONSOLE::MOD_CONSOLE_UART_PUTC,
                                    CONSOLE::RC_TIMEOUT);
                    //@TODO: RTC:116090 - Should be BMC hardware.
                    l_errl->addProcedureCallout(HWAS::EPUB_PRC_LVL_SUPP,
                                                HWAS::SRCI_PRIORITY_MED);
                    break;
                }
            }

            // Write character to FIFO.
            l_errl = writeReg(THR, c);

        } while(0);

        if (l_errl)
        {
            iv_failed = true;
            errlCommit(l_errl, CONSOLE_COMP_ID);
        }
        return;
    }

    errlHndl_t Uart::writeReg(uint64_t i_addr, uint8_t i_byte)
    {
        size_t len = sizeof(i_byte);
        return deviceWrite(TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL,
                           &i_byte,
                           len,
                           DEVICE_LPC_ADDRESS(LPC::TRANS_IO,
                                              i_addr + g_uartBase));
    }

    errlHndl_t Uart::readReg(uint64_t i_addr, uint8_t& o_byte)
    {
        size_t len = sizeof(o_byte);
        return deviceRead(TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL,
                          static_cast<uint8_t*>(&o_byte),
                          len,
                          DEVICE_LPC_ADDRESS(LPC::TRANS_IO,
                                             i_addr + g_uartBase));
    }

    Uart* Uart::g_device = NULL;

    /**
     * Retrieve some information about the UART and the connection
     * we have to it.
     */
    UartInfo_t getUartInfo(void)
    {
        UartInfo_t l_info;

        l_info.lpcBaseAddr = g_uartBase;
        l_info.lpcSize = sizeof(uint8_t);
        l_info.clockFreqHz = g_uartClock;
        l_info.freqHz = g_uartBaud;
        l_info.interruptNum = SERIAL_IRQ;
        l_info.interruptTrigger = LOW_LEVEL_TRIG;

        return l_info;
    };
}
<commit_msg>Increase UART delay based on some lab issues<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/usr/console/uart.C $                                      */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2014,2017                        */
/* [+] Google Inc.                                                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
#include "uart.H"
#include <devicefw/userif.H>
#include <lpc/lpcif.H>
#include <kernel/console.H>
#include <sys/task.h>
#include <sys/time.h>
#include <errl/errlmanager.H>
#include <hwas/common/hwasCallout.H>
#include <console/console_reasoncodes.H>
#include <console/uartif.H>

namespace CONSOLE
{
    void Uart::initialize()
    {
        using namespace UARTREGS;

        errlHndl_t l_errl = NULL;

        do
        {
            // Check for failure from superclass initalize call.
            if (iv_failed) { break; }

            // Clear line control reg.
            l_errl = writeReg(LCR, 0x00);
            if (l_errl) { break; }

            // Check for existence of scratch register (and thus UART device).
            {
                const uint8_t value = 'h';
                uint8_t data = 0;
                l_errl = writeReg(SCR, value);
                if (l_errl) { break; }
                l_errl = readReg(SCR, data);
                if (l_errl) { break; }

                if (data != value)
                {
                    printk("UART: Device not found.\n");
                    break;
                }
            }

            // Reset interrupt register.
            l_errl = writeReg(IER, 0);
            if (l_errl) { break; }

            // Set baud rate.
            uint64_t divisor = (g_uartClock / 16) / g_uartBaud;
            l_errl = writeReg(LCR, LCR_DLAB);
            if (l_errl) { break; }
            l_errl = writeReg(DLL, divisor & 0xff);
            if (l_errl) { break; }
            l_errl = writeReg(DLM, divisor >> 8);
            if (l_errl) { break; }

            // Set 8N1 mode.
            l_errl = writeReg(LCR, LCR_DWL8 | LCR_NOP | LCR_STP1);
            if (l_errl) { break; }

            // Enable Request-to-send/Data-terminal-ready
            l_errl = writeReg(MCR, MCR_RTS | MCR_DTR);
            if (l_errl) { break; }

            // Clear and enable FIFOs.
            l_errl = writeReg(FCR, FCR_ENF | FCR_CLFR | FCR_CLFT);
            if (l_errl) { break; }

            // Found device.
            printk("UART: Device initialized.\n");
            iv_initialized = true;

        } while(0);

        if (l_errl)
        {
            iv_failed = true;
            errlCommit(l_errl, CONSOLE_COMP_ID);
        }
    }

    void Uart::putc(char c)
    {
        using namespace UARTREGS;

        errlHndl_t l_errl = NULL;
        do
        {
            if (iv_failed || !iv_initialized) { break; }

            // Wait for transmit FIFO to have space.
            {
                const uint64_t DELAY_NS = 100;
                const uint64_t DELAY_LOOPS = 100000000;

                uint8_t data = 0;
                uint64_t loops = 0;

                do
                {
                    l_errl = readReg(LSR, data);
                    if (l_errl) { break; }

                    // Wait for idle or error status.
                    if (data == LSR_BAD || (data & LSR_THRE))
                    {
                        break;
                    }
                    nanosleep(0, DELAY_NS);
                    task_yield();

                    loops++;
                } while( loops < DELAY_LOOPS);

                if (l_errl)
                {
                    printk("UART: Error state in xmit - LPC error.\n");
                    iv_failed = true;
                    break;
                }
                else if (data == LSR_BAD)
                {
                    printk("UART: Error state in xmit - bad data.\n");
                    iv_failed = true;
                    /*@
                     * @errortype
                     * @moduleid    CONSOLE::MOD_CONSOLE_UART_PUTC
                     * @reasoncode  CONSOLE::RC_INVALID_DATA
                     * @devdesc     Unexpected data from LPC-UART interface.
                     */
                    l_errl = new ERRORLOG::ErrlEntry(
                                    ERRORLOG::ERRL_SEV_PREDICTIVE,
                                    CONSOLE::MOD_CONSOLE_UART_PUTC,
                                    CONSOLE::RC_INVALID_DATA);
                    //@TODO: RTC:116090 - Should be BMC hardware.
                    l_errl->addProcedureCallout(HWAS::EPUB_PRC_LVL_SUPP,
                                                HWAS::SRCI_PRIORITY_MED);
                    break;
                }
                else if (loops >= DELAY_LOOPS)
                {
                    printk("UART: FIFO timeout.\n");
                    iv_failed = true;
                    /*@
                     * @errortype
                     * @moduleid    CONSOLE::MOD_CONSOLE_UART_PUTC
                     * @reasoncode  CONSOLE::RC_TIMEOUT
                     * @devdesc     Timeout from LPC-UART interface.
                     */
                    l_errl = new ERRORLOG::ErrlEntry(
                                    ERRORLOG::ERRL_SEV_PREDICTIVE,
                                    CONSOLE::MOD_CONSOLE_UART_PUTC,
                                    CONSOLE::RC_TIMEOUT);
                    //@TODO: RTC:116090 - Should be BMC hardware.
                    l_errl->addProcedureCallout(HWAS::EPUB_PRC_LVL_SUPP,
                                                HWAS::SRCI_PRIORITY_MED);
                    break;
                }
            }

            // Write character to FIFO.
            l_errl = writeReg(THR, c);

        } while(0);

        if (l_errl)
        {
            iv_failed = true;
            errlCommit(l_errl, CONSOLE_COMP_ID);
        }
        return;
    }

    errlHndl_t Uart::writeReg(uint64_t i_addr, uint8_t i_byte)
    {
        size_t len = sizeof(i_byte);
        return deviceWrite(TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL,
                           &i_byte,
                           len,
                           DEVICE_LPC_ADDRESS(LPC::TRANS_IO,
                                              i_addr + g_uartBase));
    }

    errlHndl_t Uart::readReg(uint64_t i_addr, uint8_t& o_byte)
    {
        size_t len = sizeof(o_byte);
        return deviceRead(TARGETING::MASTER_PROCESSOR_CHIP_TARGET_SENTINEL,
                          static_cast<uint8_t*>(&o_byte),
                          len,
                          DEVICE_LPC_ADDRESS(LPC::TRANS_IO,
                                             i_addr + g_uartBase));
    }

    Uart* Uart::g_device = NULL;

    /**
     * Retrieve some information about the UART and the connection
     * we have to it.
     */
    UartInfo_t getUartInfo(void)
    {
        UartInfo_t l_info;

        l_info.lpcBaseAddr = g_uartBase;
        l_info.lpcSize = sizeof(uint8_t);
        l_info.clockFreqHz = g_uartClock;
        l_info.freqHz = g_uartBaud;
        l_info.interruptNum = SERIAL_IRQ;
        l_info.interruptTrigger = LOW_LEVEL_TRIG;

        return l_info;
    };
}
<|endoftext|>