qint64 THttpSocket::writeRawData(const char *data, qint64 size)
{
    qint64 total = 0;

    if (Q_UNLIKELY(!data || size == 0)) {
        return total;
    }

    for (;;) {
        if (QTcpSocket::bytesToWrite() > SEND_BUF_SIZE * 3 / 4) {
            if (Q_UNLIKELY(!waitForBytesWritten())) {
                tWarn("socket error: waitForBytesWritten function [%s]", qPrintable(errorString()));
                break;
            }
        }

        qint64 written = QTcpSocket::write(data + total, qMin(size - total, WRITE_LENGTH));
        if (Q_UNLIKELY(written <= 0)) {
            tWarn("socket write error: total:%d (%d)", (int)total, (int)written);
            return -1;
        }

        total += written;
        if (total >= size) {
            break;
        }
    }

    idleElapsed = std::time(nullptr);
    return total;
}
/*!
  Sets the validation rule for the key \a key and sets the error
  message of it to \a errorMessage.
 */
void TFormValidator::setRule(const QString &key, Tf::ValidationRule rule, double val, const QString &errorMessage)
{
    // arg check
    switch ((int)rule) {
    case Tf::Required:
    case Tf::MaxLength:
    case Tf::MinLength:
    case Tf::IntMax:
    case Tf::IntMin:
    case Tf::EmailAddress:
    case Tf::Url:
    case Tf::Date:
    case Tf::Time:
    case Tf::DateTime:
        tWarn("Validation: Bad rule spedified [key:%s  rule:%d]. Use another setRule method.", qPrintable(key), rule);
        return;

    case Tf::Pattern:
        tWarn("Validation: Bad rule spedified [key:%s  rule:%d]. Use setPatternRule method.", qPrintable(key), rule);
        return;
    }

    removeRule(key, rule);
    rules.prepend(RuleEntry(key, (int)rule, val, (errorMessage.isEmpty() ? Tf::app()->validationErrorMessage(rule) : errorMessage)));
}
/*!
  Inserts new record into the database, based on the current properties
  of the object.
*/
bool TSqlObject::create()
{
    // Sets the values of 'created_at', 'updated_at' or 'modified_at' properties
    for (int i = metaObject()->propertyOffset(); i < metaObject()->propertyCount(); ++i) {
        const char *propName = metaObject()->property(i).name();
        QByteArray prop = QByteArray(propName).toLower();

        if (prop == CreatedAt || prop == UpdatedAt || prop == ModifiedAt) {
            setProperty(propName, QDateTime::currentDateTime());
        } else if (prop == LockRevision) {
            // Sets the default value of 'revision' property
            setProperty(propName, 1);  // 1 : default value
        } else {
            // do nothing
        }
    }

    syncToSqlRecord();

    QString autoValName;
    QSqlRecord record = *this;
    if (autoValueIndex() >= 0) {
        autoValName = field(autoValueIndex()).name();
        record.remove(autoValueIndex()); // not insert the value of auto-value field
    }

    QSqlDatabase &database = Tf::currentSqlDatabase(databaseId());
    QString ins = database.driver()->sqlStatement(QSqlDriver::InsertStatement, tableName(), record, false);
    if (Q_UNLIKELY(ins.isEmpty())) {
        sqlError = QSqlError(QLatin1String("No fields to insert"),
                             QString(), QSqlError::StatementError);
        tWarn("SQL statement error, no fields to insert");
        return false;
    }

    TSqlQuery query(database);
    bool ret = query.exec(ins);
    sqlError = query.lastError();
    if (Q_LIKELY(ret)) {
        // Gets the last inserted value of auto-value field
        if (autoValueIndex() >= 0) {
            QVariant lastid = query.lastInsertId();

            if (!lastid.isValid() && database.driverName().toUpper() == QLatin1String("QPSQL")) {
                // For PostgreSQL without OIDS
                ret = query.exec("SELECT LASTVAL()");
                sqlError = query.lastError();
                if (Q_LIKELY(ret)) {
                    lastid = query.getNextValue();
                }
            }

            if (lastid.isValid()) {
                QObject::setProperty(autoValName.toLatin1().constData(), lastid);
                QSqlRecord::setValue(autoValueIndex(), lastid);
            }
        }
    }
    return ret;
}
Exemple #4
0
bool TSqlObject::create()
{
    // Sets the default value of 'revision' property
    int index = metaObject()->indexOfProperty(REVISION_PROPERTY_NAME);
    if (index >= 0) {
        setProperty(REVISION_PROPERTY_NAME, 1);  // 1 : default value
    }

    // Sets the values of 'created_at' and 'updated_at' properties
    for (int i = metaObject()->propertyOffset(); i < metaObject()->propertyCount(); ++i) {
        const char *propName = metaObject()->property(i).name();
        if (QLatin1String("created_at") == propName
            || QLatin1String("updated_at") == propName) {
            setProperty(propName, QDateTime::currentDateTime());
        }
    }

    syncToSqlRecord();
    QString ins = TActionContext::currentDatabase().driver()->sqlStatement(QSqlDriver::InsertStatement, tableName(), *static_cast<QSqlRecord *>(this), false);
    if (ins.isEmpty()) {
        sqlError = QSqlError(QLatin1String("No fields to insert"),
                             QString(), QSqlError::StatementError);
        tWarn("SQL statement error, no fields to insert");
        return false;
    }

    tSystemDebug("SQL statement: %s", qPrintable(ins));
    QSqlQuery query(TActionContext::currentDatabase());
    bool res = query.exec(ins);
    sqlError = query.lastError();
    if (!res) {
        tSystemError("SQL insert error: %s", qPrintable(sqlError.text()));
    }
    return res;
}
Exemple #5
0
bool TSqlObject::remove()
{
    syncToSqlRecord();
    QString del = TActionContext::currentDatabase().driver()->sqlStatement(QSqlDriver::DeleteStatement, tableName(), *static_cast<QSqlRecord *>(this), false);
    
    if (del.isEmpty()) {
        sqlError = QSqlError(QLatin1String("Unable to delete row"),
                             QString(), QSqlError::StatementError);
        return false;
    }

    del.append(" WHERE ");
    int revIndex = metaObject()->indexOfProperty(REVISION_PROPERTY_NAME);
    if (revIndex >= 0) {
        bool ok;
        int revsion = property(REVISION_PROPERTY_NAME).toInt(&ok);
        if (!ok || revsion <= 0) {
            sqlError = QSqlError(QLatin1String("Unable to convert the 'revision' property to an int"),
                                 QString(), QSqlError::UnknownError);
            tError("Unable to convert the 'revsion' property to an int, %s", qPrintable(objectName()));
            return false;
        }

        del.append(TSqlQuery::escapeIdentifier(REVISION_PROPERTY_NAME));
        del.append("=").append(TSqlQuery::formatValue(revsion));
        del.append(" AND ");
    }

    const char *pkName = metaObject()->property(metaObject()->propertyOffset() + primaryKeyIndex()).name();
    if (primaryKeyIndex() < 0 || !pkName) {
        QString msg = QString("Not found the primary key for table ") + tableName();
        sqlError = QSqlError(msg, QString(), QSqlError::StatementError);
        tError("%s", qPrintable(msg));
        return false;
    }
    del.append(TSqlQuery::escapeIdentifier(pkName));
    del.append("=").append(TSqlQuery::formatValue(property(pkName)));

    tSystemDebug("SQL statement: %s", qPrintable(del));
    QSqlQuery query(TActionContext::currentDatabase());
    bool res = query.exec(del);
    sqlError = query.lastError();
    if (!res) {
        tSystemError("SQL delete error: %s", qPrintable(sqlError.text()));
        return false;
    }
    
    // Optimistic lock check
    if (query.numRowsAffected() != 1) {
        if (revIndex >= 0) {
            QString msg = QString("Row was updated or deleted from table ") + tableName() + QLatin1String(" by another transaction");
            sqlError = QSqlError(msg, QString(), QSqlError::UnknownError);
            throw SqlException(msg, __FILE__, __LINE__);
        }
        tWarn("Row was deleted by another transaction, %s", qPrintable(tableName()));
    }

    clear();
    return true;
}
/*!
  \~english
  Sends the file \a filePath as HTTP response.

  \~japanese
  HTTPレスポンスとして、ファイル \a filePath の内容を送信する
*/
bool TActionController::sendFile(const QString &filePath, const QByteArray &contentType, const QString &name, bool autoRemove)
{
    if (rendered) {
        tWarn("Has rendered already: %s", qPrintable(className() + '#' + activeAction()));
        return false;
    }
    rendered = true;

    setStatusCode(200);
    if (!name.isEmpty()) {
        QByteArray filename;
        filename += "attachment; filename=\"";
        filename += name.toUtf8();
        filename += '"';
        response.header().setRawHeader("Content-Disposition", filename);
    }

    response.setBodyFile(filePath);
    response.header().setContentType(contentType);

    if (autoRemove)
        setAutoRemove(filePath);

    return true;
}
qint64 THttpSocket::writeRawData(const char *data, qint64 size)
{
    qint64 total = 0;
    for (;;) {
        qint64 written = QTcpSocket::write(data + total, qMin(size - total, WRITE_LENGTH));
        if (written <= 0) {
            tWarn("socket write error: total:%d (%d)", (int)total, (int)written);
            return -1;
        }
        total += written;
        if (total >= size)
            break;

        if (!waitForBytesWritten()) {
            tWarn("socket error: waitForBytesWritten function [%s]", qPrintable(errorString()));
            break;
        }
    }
    return total;
}
static TKvsDriver *createDriver(const QString &driverName)
{
    TKvsDriver *ret = 0;

    if (driverName == QLatin1String("MONGODB")) {
        ret = new TMongoDriver();
    }

    if (!ret) {
        tWarn("TKvsDatabase: %s driver not loaded", qPrintable(driverName));
    }
    return ret;
}
Exemple #9
0
/*!
  Returns the module name for multi-processing that is set by the setting
  \a MultiProcessingModule in the application.ini.
*/
TWebApplication::MultiProcessingModule TWebApplication::multiProcessingModule() const
{
    if (mpm == Invalid) {
        QString str = Tf::appSettings()->value(Tf::MultiProcessingModule).toString().toLower();
        if (str == "thread") {
            mpm = Thread;
        } else if (str == "hybrid") {
#ifdef Q_OS_LINUX
            mpm = Hybrid;
#else
            tSystemWarn("Unsupported MPM: hybrid  (Linux only)");
            tWarn("Unsupported MPM: hybrid  (Linux only)");
            mpm = Thread;
#endif
        } else {
            tSystemWarn("Unsupported MPM: %s", qPrintable(str));
            tWarn("Unsupported MPM: %s", qPrintable(str));
            mpm = Thread;
        }
    }
    return mpm;
}
/*!
  Synchronizes the properties to the internal record data.
  This function is for internal use only.
*/
void TSqlObject::syncToSqlRecord()
{
    QSqlRecord::operator=(Tf::currentSqlDatabase(databaseId()).record(tableName()));
    const QMetaObject *metaObj = metaObject();
    for (int i = metaObj->propertyOffset(); i < metaObj->propertyCount(); ++i) {
        const char *propName = metaObj->property(i).name();
        int idx = indexOf(propName);
        if (idx >= 0) {
            QSqlRecord::setValue(idx, QObject::property(propName));
        } else {
            tWarn("invalid name: %s", propName);
        }
    }
}
/*!
  \~english
  Renders the template of the action \a action with the layout \a layout.

  \~japanese
  レイアウト \a layout を適用し、アクション \a action のテンプレートを描画する
 */
bool TActionController::render(const QString &action, const QString &layout)
{
    T_TRACEFUNC("");

    if (rendered) {
        tWarn("Has rendered already: %s", qPrintable(className() + '#' + activeAction()));
        return false;
    }
    rendered = true;

    // Creates view-object and displays it
    TDispatcher<TActionView> viewDispatcher(viewClassName(action));
    setLayout(layout);
    response.setBody(renderView(viewDispatcher.object()));
    return !response.isBodyNull();
}
void TScheduler::run()
{
    _rollback = false;
    TDatabaseContext::setCurrentDatabaseContext(this);

    try {
        // Executes the job
        job();

        if (_rollback) {
            TDatabaseContext::rollbackTransactions();
        } else {
            TDatabaseContext::commitTransactions();
        }

    } catch (ClientErrorException &e) {
        tWarn("Caught ClientErrorException: status code:%d", e.statusCode());
        tSystemWarn("Caught ClientErrorException: status code:%d", 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());
    } 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());
    } 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());
    } 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());
    } 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());
    } catch (std::exception &e) {
        tError("Caught Exception: %s", e.what());
        tSystemError("Caught Exception: %s", e.what());
    }

    TDatabaseContext::release();
    TDatabaseContext::setCurrentDatabaseContext(nullptr);

    if (_autoDelete && !_timer->isActive()) {
        connect(this, &TScheduler::finished, this, &QObject::deleteLater);
    }
}
/*!
  \~english
  Renders the text \a text with the layout \a layout.

  \~japanese
  レイアウト \a layout を適用し、テキストを描画する
*/
bool TActionController::renderText(const QString &text, bool layoutEnable, const QString &layout)
{
    T_TRACEFUNC("");

    if (rendered) {
        tWarn("Has rendered already: %s", qPrintable(className() + '#' + activeAction()));
        return false;
    }
    rendered = true;

    // Creates TTextView object and displays it
    setLayout(layout);
    setLayoutEnabled(layoutEnable);
    TTextView *view = new TTextView(text);
    response.setBody(renderView(view));
    delete view;
    return (!response.isBodyNull());
}
/*!
  Returns true if the user \a user is allowed to access to the requested
  action; otherwise returns false.
*/
bool TAccessValidator::validate(const TAbstractUser *user) const
{
    bool ret = allowDefault;
    const TActionController *controller = Tf::currentContext()->currentController();
    Q_ASSERT(controller);

    if (accessRules.isEmpty()) {
        tWarn("No rule for access validation: %s", qPrintable(controller->className()));
        return ret;
    }

    if (!user || user->identityKey().isEmpty()) {
        // Searches a access rule for an unauthenticated user
        for (const auto &rule : accessRules) {
            if (rule.type == AccessRule::UnauthenticatedUser
                && rule.action == controller->activeAction()) {
                ret = rule.allow;
                break;
            }
        }
        if (ret) {
            tSystemDebug("Access '%s' action by an unauthenticated user : Allow", qPrintable(controller->activeAction()));
        } else {
            tSystemWarn("Access '%s' action by an unauthenticated user : Deny", qPrintable(controller->activeAction()));
        }

    } else {
        for (const auto &rule : accessRules) {
            if (rule.action == controller->activeAction()
                && ((rule.type == AccessRule::User && rule.key == user->identityKey())
                    || (!user->groupKey().isEmpty() && rule.key == user->groupKey()))) {
                ret = rule.allow;
                break;
            }
        }
        if (ret) {
            tSystemDebug("Access '%s' action by '%s' user : Allow", qPrintable(controller->activeAction()), qPrintable(user->identityKey()));
        } else {
            tSystemWarn("Access '%s' action by '%s' user : Deny", qPrintable(controller->activeAction()), qPrintable(user->identityKey()));
        }
    }

    return ret;
}
/*!
  Renders a static error page with the status code, which page is [statusCode].html
  in the \a public directory.
 */
bool TActionController::renderErrorResponse(int statusCode)
{
    bool ret = false;

    if (rendered) {
        tWarn("Has rendered already: %s", qPrintable(className() + '#' + activeAction()));
        return ret;
    }
    
    QString file = Tf::app()->publicPath() + QString::number(statusCode) + QLatin1String(".html");
    if (QFileInfo(file).exists())  {
        ret = sendFile(file, "text/html", "", false);
    } else {        
        response.setBody("");
    }
    setStatusCode(statusCode);
    rendered = true;
    return ret;
}
/*!
  \~english
  Renders the template given by \a templateName with the layout \a layout.
 
  \~japanese
  レイアウト \a layout を適用し、テンプレート \a templateName を描画する
*/
bool TActionController::renderTemplate(const QString &templateName, const QString &layout)
{
    T_TRACEFUNC("");

    if (rendered) {
        tWarn("Has rendered already: %s", qPrintable(className() + '#' + activeAction()));
        return false;
    }
    rendered = true;

    // Creates view-object and displays it
    QStringList names = templateName.split("/");
    if (names.count() != 2) {
        tError("Invalid patameter: %s", qPrintable(templateName));
        return false;
    }
    TDispatcher<TActionView> viewDispatcher(viewClassName(names[0], names[1]));
    setLayout(layout);
    response.setBody(renderView(viewDispatcher.object()));
    return (!response.isBodyNull());
}
qint64 THttpSocket::write(const THttpHeader *header, QIODevice *body)
{
    T_TRACEFUNC("");

    if (body && !body->isOpen()) {
        if (!body->open(QIODevice::ReadOnly)) {
            tWarn("open failed");
            return -1;
        }
    }

    // Writes HTTP header
    QByteArray hdata = header->toByteArray();
    qint64 total = writeRawData(hdata.data(), hdata.size());
    if (total < 0) {
        return -1;
    }

    if (body) {
        QBuffer *buffer = qobject_cast<QBuffer *>(body);
        if (buffer) {
            if (writeRawData(buffer->data().data(), buffer->size()) != buffer->size()) {
                return -1;
            }
            total += buffer->size();
        } else {
            QByteArray buf(WRITE_BUFFER_LENGTH, 0);
            qint64 readLen = 0;
            while ((readLen = body->read(buf.data(), buf.size())) > 0) {
                if (writeRawData(buf.data(), readLen) != readLen) {
                    return -1;
                }
                total += readLen;
            }
        }
    }
    return total;
}
void TActionContext::execute()
{
    T_TRACEFUNC("");
    THttpResponseHeader responseHeader;
    accessLogger.open();

    try {
        if (!readRequest()) {
            return;
        }
        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::app()->appSettings().value(LISTEN_PORT).toUInt() > 0) ? clientAddress().toString().toLatin1() : QByteArray("(unix)") );

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

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

        // Routing info exists?
        TRouting rt = TUrlRoute::instance().findRouting(method, path);
        tSystemDebug("Routing: controller:%s  action:%s", rt.controller.data(),
                     rt.action.data());

        if (rt.isEmpty()) {
            // Default URL routing
            rt.params = path.split('/');
            if (path.startsWith(QLatin1Char('/')) && !rt.params.isEmpty()) {
                rt.params.removeFirst();  // unuse first item
            }
            if (path.endsWith(QLatin1Char('/')) && !rt.params.isEmpty()) {
                rt.params.removeLast();  // unuse last item
            }

            // Direct view render mode?
            if (Tf::app()->appSettings().value(DIRECT_VIEW_RENDER_MODE).toBool()) {
                // Direct view setting
                rt.controller = "directcontroller";
                rt.action = "show";
            } else {
                if (!rt.params.value(0).isEmpty()) {
                    rt.controller = rt.params.takeFirst().toLower().toLatin1() + "controller";

                    if (rt.controller == "applicationcontroller") {
                        rt.controller.clear();  // Can not call 'ApplicationController'
                    }

                    // Default action: index
                    rt.action = rt.params.value(0, QLatin1String("index")).toLatin1();
                    if (!rt.params.isEmpty()) {
                        rt.params.takeFirst();
                    }
                }
                tSystemDebug("Active Controller : %s", rt.controller.data());
            }
        }

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

            // 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::app()->appSettings().value(ENABLE_CSRF_PROTECTION_MODULE, true).toBool()
                && currController->csrfProtectionEnabled() && !currController->exceptionActionsOfCsrfProtection().contains(rt.action)) {

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

            if (currController->sessionEnabled()) {
                if (currController->session().id().isEmpty() || Tf::app()->appSettings().value(AUTO_ID_REGENERATION).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
            transactions.setEnabled(currController->transactionEnabled());

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

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

                    // Post fileter
                    currController->postFilter();

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

                    // Session store
                    if (currController->sessionEnabled()) {
                        bool stored = TSessionManager::instance().store(currController->session());
                        if (stored) {
                            QDateTime expire;
                            if (TSessionManager::sessionLifeTime() > 0) {
                                expire = QDateTime::currentDateTime().addSecs(TSessionManager::sessionLifeTime());
                            }

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

            // 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
            int bytes = writeResponse(currController->response.header(), currController->response.bodyIODevice(),
                                      currController->response.bodyLength());
            accessLogger.setResponseBytes(bytes);

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

        } else {
            accessLogger.setStatusCode( Tf::BadRequest );

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

                if (fi.isFile() && fi.isReadable()) {
                    // 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);
                        sendfile = (!dt.isValid() || dt != fi.lastModified());
                    }

                    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());
        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 (...) {
        tError("Caught Exception");
        tSystemError("Caught Exception");
        closeHttpSocket();
    }

    // Push to the pool
    TActionContext::releaseSqlDatabases();
    TActionContext::releaseKvsDatabases();

    TActionContext::accessLogger.write();  // Writes access log
    releaseHttpSocket();
}
Exemple #19
0
bool TSqlObject::update()
{
    if (isNew()) {
        sqlError = QSqlError(QLatin1String("No record to update"),
                             QString(), QSqlError::UnknownError);
        tWarn("Unable to update the '%s' object. Create it before!", metaObject()->className());
        return false;
    }

    QString where(" WHERE ");
    int revIndex = metaObject()->indexOfProperty(REVISION_PROPERTY_NAME);
    if (revIndex >= 0) {
        bool ok;
        int oldRevision = property(REVISION_PROPERTY_NAME).toInt(&ok);
        if (!ok || oldRevision <= 0) {
            sqlError = QSqlError(QLatin1String("Unable to convert the 'revision' property to an int"),
                                 QString(), QSqlError::UnknownError);
            tError("Unable to convert the 'revision' property to an int, %s", qPrintable(objectName()));
            return false;
        }

        setProperty(REVISION_PROPERTY_NAME, oldRevision + 1);
        
        where.append(TSqlQuery::escapeIdentifier(REVISION_PROPERTY_NAME));
        where.append("=").append(TSqlQuery::formatValue(oldRevision));
        where.append(" AND ");
    }

    // Updates the value of 'updated_at' property
    for (int i = metaObject()->propertyOffset(); i < metaObject()->propertyCount(); ++i) {
        const char *propName = metaObject()->property(i).name();
        if (QLatin1String("updated_at") == propName) {
            setProperty(propName, QDateTime::currentDateTime());
            break;
        }
    }

    syncToSqlRecord();
    QString upd = TActionContext::currentDatabase().driver()->sqlStatement(QSqlDriver::UpdateStatement, tableName(), *static_cast<QSqlRecord *>(this), false);
    
    if (upd.isEmpty()) {
        sqlError = QSqlError(QLatin1String("No Fields to update"),
                             QString(), QSqlError::StatementError);
        return false;
    }
    
    const char *pkName = metaObject()->property(metaObject()->propertyOffset() + primaryKeyIndex()).name();
    if (primaryKeyIndex() < 0 || !pkName) {
        QString msg = QString("Not found the primary key for table ") + tableName();
        sqlError = QSqlError(msg, QString(), QSqlError::StatementError);
        tError("%s", qPrintable(msg));
        return false;
    }
    where.append(TSqlQuery::escapeIdentifier(pkName));
    where.append("=").append(TSqlQuery::formatValue(property(pkName)));
    upd.append(where);

    tSystemDebug("SQL statement: %s", qPrintable(upd));
    QSqlQuery query(TActionContext::currentDatabase());
    bool res = query.exec(upd);
    sqlError = query.lastError();
    if (!res) {
        tSystemError("SQL update error: %s", qPrintable(sqlError.text()));
        return false;
    }
    
    // Optimistic lock check
    if (revIndex >= 0 && query.numRowsAffected() != 1) {
        QString msg = QString("Row was updated or deleted from table ") + tableName() + QLatin1String(" by another transaction");
        sqlError = QSqlError(msg, QString(), QSqlError::UnknownError);
        throw SqlException(msg, __FILE__, __LINE__);
    }
    return true;
}
Exemple #20
0
int main(int argc, char *argv[])
{
    TWebApplication webapp(argc, argv);

    // Setup loggers
    tSetupSystemLogger();
    tSetupLoggers();
    qInstallMsgHandler(messageOutput);

#if defined(Q_OS_UNIX)
    webapp.watchUnixSignal(SIGTERM);
    webapp.ignoreUnixSignal(SIGINT);

    // Setup signal handlers for SIGSEGV, SIGILL, SIGFPE, SIGABRT and SIGBUS
    setupFailureWriter(writeFailure);
    setupSignalHandler();
#endif

    // Sets codec
    QTextCodec *codec = QTextCodec::codecForName("UTF-8");  // default value
    QByteArray codecName = webapp.treefrogSettings().value("InternalEncoding").toByteArray().trimmed();
    if (!codecName.isEmpty()) {
        QTextCodec *c = QTextCodec::codecForName(codecName);
        if (c) {
            codec = c;
        } else {
            tWarn("no codec matching the name could be found: %s", codecName.data());
        }
    }
    QTextCodec::setCodecForTr(codec);
    QTextCodec::setCodecForLocale(codec);
    tSystemDebug("setCodecForTr: %s", codec->name().data());

    if (!webapp.webRootExists()) {
        tSystemError("No such directory");
        return 1;
    }
    tSystemDebug("Web Root: %s", qPrintable(webapp.webRootPath()));

    if (!webapp.settingsFileExists()) {
        tSystemError("Settings file not found");
        return 2;
    }
  
    QHash<QString, QString> args = convertArgs(QCoreApplication::arguments());
    TApplicationServer *server = new TApplicationServer(&webapp);
    QString arg = args.value("-s");
    if (!arg.isEmpty()) {
        // Sets a listening socket descriptor
        int sd = arg.toInt();
        if (sd > 0) {
            if (server->setSocketDescriptor(sd)) {
                tSystemDebug("Set socket descriptor: %d", sd);
            } else {
                tSystemError("Failed to set socket descriptor: %d", sd);
                return 3;
            }
        } else {
            tSystemError("Invalid socket descriptor: %d", sd);
            return 4;
        }
    }
    
    server->open();
    return webapp.exec();
}
Exemple #21
0
void TActionContext::execute()
{
    T_TRACEFUNC();
    try {
        httpSocket = new THttpSocket;
        if (!httpSocket->setSocketDescriptor(socketDesc)) {
            emitError(httpSocket->error());
            delete httpSocket;
            httpSocket = 0;
            return;
        } else {
            socketDesc = 0;
        }

        while (!httpSocket->canReadRequest()) {
            if (stopped) {
                tSystemDebug("Detected stop request");
                break;
            }
            
            // Check timeout
            if (!httpSocket->waitForReadyRead(10000)) {
                tWarn("Read timed out after 10 seconds.");
                break;
            }
        }
        
        if (!httpSocket->canReadRequest()) {
            httpSocket->abort();
            delete httpSocket;
            httpSocket = 0;
            return;
        }

        THttpRequest httpRequest = httpSocket->read();
        const THttpRequestHeader &hd = httpRequest.header();

        // Access log
        QByteArray firstLine = hd.method() + ' ' + hd.path();
        firstLine += QString(" HTTP/%1.%2").arg(hd.majorVersion()).arg(hd.minorVersion()).toLatin1();
        TAccessLog accessLog(httpSocket->peerAddress().toString().toLatin1(), firstLine);

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

        Tf::HttpMethod method = httpRequest.method();
        QString path = THttpUtility::fromUrlEncoding(hd.path().split('?').value(0));

        // Routing info exists?
        Routing rt = TUrlRoute::instance().findRouting(method, path);
        tSystemDebug("Routing: controller:%s  action:%s", rt.controller.data(),
                     rt.action.data());
        
        if (rt.isEmpty()) {
            // Default URL routing
            rt.params = path.split('/');
            if (path.startsWith(QLatin1Char('/')) && !rt.params.isEmpty()) {
                rt.params.removeFirst();  // unuse first item
            }
            if (path.endsWith(QLatin1Char('/')) && !rt.params.isEmpty()) {
                rt.params.removeLast();  // unuse last item
            }

            // Direct view render mode?
            if (Tf::app()->treefrogSettings().value(DIRECT_VIEW_RENDER_MODE).toBool()) {
                // Direct view setting
                rt.controller = "directcontroller";
                rt.action = "show";
                QString tmp = rt.params.join("/");
                rt.params.clear();
                rt.params << tmp;
            } else {
                if (!rt.params.value(0).isEmpty()) {
                    rt.controller = rt.params.takeFirst().toLower().toLatin1() + "controller";
                    if (!rt.params.value(0).isEmpty()) {
                        rt.action = rt.params.takeFirst().toLatin1();
                    }
                }
                tSystemDebug("Active Controller : %s", rt.controller.data());
            }
        }

        // Call controller method
        TDispatcher<TActionController> ctlrDispatcher(rt.controller);
        TActionController *controller = ctlrDispatcher.object();
        if (controller) {
            controller->setActionName(rt.action);
            controller->setHttpRequest(httpRequest);
            
            openDatabase();
            
            // Session
            if (controller->sessionEnabled()) {
                TSession session;
                QByteArray sessionId = httpRequest.cookie(TSession::sessionName());
                if (!sessionId.isEmpty()) {
                    session = TSessionManager::instance().find(sessionId);
                }
                controller->setSession(session);
                
                // Exports flash-variant
                controller->exportAllFlashVariants();
            }
            
            // Verify authenticity token
            if (controller->forgeryProtectionEnabled() && !controller->protectFromForgeryExcept().contains(rt.action)) {
                if (method == Tf::Post || method == Tf::Put || method == Tf::Delete) {
                    if (!controller->verifyRequest(httpRequest)) {
                        throw SecurityException("Invalid authenticity token", __FILE__, __LINE__);
                    }
                }
            }
            
            // Sets the default status code of HTTP response
            int statusCode = 404;  // Not Found

            // Re-generate session ID
            if (controller->sessionEnabled() && (controller->session().id().isEmpty() || Tf::app()->treefrogSettings().value(AUTO_ID_REGENERATION).toBool())) {
                controller->session().regenerateId();
            }
            
            if (controller->transactionEnabled()) {
                // Begins a transaction on the database
                beginTransaction();
            }
            
            // Do filters
            if (controller->preFilter()) {
                
                // Dispathes
                bool dispatched = ctlrDispatcher.invoke(rt.action, rt.params);
                if (dispatched) {
                    autoRemoveFiles = controller->autoRemoveFiles;  // Set auto remove files
                    
                    // Post fileter
                    controller->postFilter();
                    
                    if (controller->transactionEnabled() && !controller->rollbackEnabled()) {
                        // Commits a transaction to the database
                        commitTransaction();
                    }
                    
                    // Session store
                    if (controller->sessionEnabled()) {
                        bool stored = TSessionManager::instance().store(controller->session());
                        if (stored) {
                            controller->addCookie(TSession::sessionName(), controller->session().id(), QDateTime(), "/");
                        }
                    }
                    
                    // Sets status code
                    statusCode = (!controller->response.isBodyNull()) ? controller->statusCode() : 500 /* Internal Server Error */;
                }
            }
            
            controller->response.header().setStatusLine(statusCode, THttpUtility::getResponseReasonPhrase(statusCode));
            accessLog.statusCode = statusCode;
            accessLog.responseBytes = writeResponse(controller->response.header(), controller->response.bodyIODevice(), controller->response.bodyLength());
            // Outputs access log
            tAccessLog(accessLog);
        }

        if (!controller) {
            // GET Method?
            if (method == Tf::Get) {
                path.remove(0, 1);
                QFile reqPath(Tf::app()->publicPath() + path);
                QFileInfo fi(reqPath);
                if (fi.isFile() && fi.isReadable()) {
                    QByteArray type = Tf::app()->internetMediaType(fi.suffix());
                    writeResponse(200, type, &reqPath, reqPath.size());
                } else {
                    writeResponse(404); // Not Found
                }
            } else if (method == Tf::Post) {
                // file upload?

            } else {
                // HEAD, DELETE, ...
            }
        }

    } catch (ClientErrorException &e) {
        tWarn("Caught ClientErrorException: status code:%d", e.statusCode());
        writeResponse(e.statusCode());
    } catch (SqlException &e) {
        tError("Caught SqlException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
    } catch (SecurityException &e) {
        tError("Caught SecurityException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
    } catch (RuntimeException &e) {
        tError("Caught RuntimeException: %s  [%s:%d]", qPrintable(e.message()), qPrintable(e.fileName()), e.lineNumber());
    } catch (...) {
        tError("Caught Exception");
    }

    closeDatabase();
    httpSocket->disconnectFromHost();
    // Destorys the object in the thread which created it
    delete httpSocket;
    httpSocket = 0;
}
/*!
  Deletes the record with this primary key from the database.
*/
bool TSqlObject::remove()
{
    if (isNew()) {
        sqlError = QSqlError(QLatin1String("No record to remove"),
                             QString(), QSqlError::UnknownError);
        tWarn("Unable to remove the '%s' object. Create it before!", metaObject()->className());
        return false;
    }

    QSqlDatabase &database = Tf::currentSqlDatabase(databaseId());
    QString del = database.driver()->sqlStatement(QSqlDriver::DeleteStatement, tableName(), *static_cast<QSqlRecord *>(this), false);
    if (del.isEmpty()) {
        sqlError = QSqlError(QLatin1String("Unable to delete row"),
                             QString(), QSqlError::StatementError);
        return false;
    }

    del.append(" WHERE ");
    int revIndex = -1;

    for (int i = metaObject()->propertyOffset(); i < metaObject()->propertyCount(); ++i) {
        const char *propName = metaObject()->property(i).name();
        QByteArray prop = QByteArray(propName).toLower();

        if (prop == LockRevision) {
            bool ok;
            int revision = property(propName).toInt(&ok);

            if (!ok || revision <= 0) {
                sqlError = QSqlError(QLatin1String("Unable to convert the 'revision' property to an int"),
                                     QString(), QSqlError::UnknownError);
                tError("Unable to convert the 'revision' property to an int, %s", qPrintable(objectName()));
                return false;
            }

            del.append(QLatin1String(propName));
            del.append("=").append(TSqlQuery::formatValue(revision, database));
            del.append(" AND ");

            revIndex = i;
            break;
        }
    }

    const char *pkName = metaObject()->property(metaObject()->propertyOffset() + primaryKeyIndex()).name();
    if (primaryKeyIndex() < 0 || !pkName) {
        QString msg = QString("Primary key not found for table ") + tableName() + QLatin1String(". Create a primary key!");
        sqlError = QSqlError(msg, QString(), QSqlError::StatementError);
        tError("%s", qPrintable(msg));
        return false;
    }
    del.append(QLatin1String(pkName));
    del.append("=").append(TSqlQuery::formatValue(value(pkName), database));

    TSqlQuery query(database);
    bool ret = query.exec(del);
    sqlError = query.lastError();
    if (ret) {
        // Optimistic lock check
        if (query.numRowsAffected() != 1) {
            if (revIndex >= 0) {
                QString msg = QString("Row was updated or deleted from table ") + tableName() + QLatin1String(" by another transaction");
                sqlError = QSqlError(msg, QString(), QSqlError::UnknownError);
                throw SqlException(msg, __FILE__, __LINE__);
            }
            tWarn("Row was deleted by another transaction, %s", qPrintable(tableName()));
        }
        clear();
    }
    return ret;
}
/*!
  Updates the corresponding record with the properties of the object.
*/
bool TSqlObject::update()
{
    if (isNew()) {
        sqlError = QSqlError(QLatin1String("No record to update"),
                             QString(), QSqlError::UnknownError);
        tWarn("Unable to update the '%s' object. Create it before!", metaObject()->className());
        return false;
    }

    QSqlDatabase &database = Tf::currentSqlDatabase(databaseId());
    QString where(" WHERE ");

    // Updates the value of 'updated_at' or 'modified_at' property
    bool updflag = false;
    int revIndex = -1;

    for (int i = metaObject()->propertyOffset(); i < metaObject()->propertyCount(); ++i) {
        const char *propName = metaObject()->property(i).name();
        QByteArray prop = QByteArray(propName).toLower();

        if (!updflag && (prop == UpdatedAt || prop == ModifiedAt)) {
            setProperty(propName, QDateTime::currentDateTime());
            updflag = true;

        } else if (revIndex < 0 && prop == LockRevision) {
            bool ok;
            int oldRevision = property(propName).toInt(&ok);

            if (!ok || oldRevision <= 0) {
                sqlError = QSqlError(QLatin1String("Unable to convert the 'revision' property to an int"),
                                     QString(), QSqlError::UnknownError);
                tError("Unable to convert the 'revision' property to an int, %s", qPrintable(objectName()));
                return false;
            }

            setProperty(propName, oldRevision + 1);
            revIndex = i;

            where.append(QLatin1String(propName));
            where.append("=").append(TSqlQuery::formatValue(oldRevision, database));
            where.append(" AND ");
        } else {
            // continue
        }
    }

    QString upd;   // UPDATE Statement
    upd.reserve(255);
    upd.append(QLatin1String("UPDATE ")).append(tableName()).append(QLatin1String(" SET "));

    int pkidx = metaObject()->propertyOffset() + primaryKeyIndex();
    const char *pkName = metaObject()->property(pkidx).name();
    if (primaryKeyIndex() < 0 || !pkName) {
        QString msg = QString("Primary key not found for table ") + tableName() + QLatin1String(". Create a primary key!");
        sqlError = QSqlError(msg, QString(), QSqlError::StatementError);
        tError("%s", qPrintable(msg));
        return false;
    }

    QVariant origpkval = value(pkName);
    where.append(QLatin1String(pkName));
    where.append("=").append(TSqlQuery::formatValue(origpkval, database));
    // Restore the value of primary key
    QObject::setProperty(pkName, origpkval);

    for (int i = metaObject()->propertyOffset(); i < metaObject()->propertyCount(); ++i) {
        const char *propName = metaObject()->property(i).name();
        QVariant newval = QObject::property(propName);
        QVariant recval = QSqlRecord::value(QLatin1String(propName));
        if (i != pkidx && recval.isValid() && recval != newval) {
            upd.append(QLatin1String(propName));
            upd.append(QLatin1Char('='));
            upd.append(TSqlQuery::formatValue(newval, database));
            upd.append(QLatin1String(", "));
        }
    }

    if (!upd.endsWith(QLatin1String(", "))) {
        tSystemDebug("SQL UPDATE: Same values as that of the record. No need to update.");
        return true;
    }

    upd.chop(2);
    syncToSqlRecord();
    upd.append(where);

    TSqlQuery query(database);
    bool ret = query.exec(upd);
    sqlError = query.lastError();
    if (ret) {
        // Optimistic lock check
        if (revIndex >= 0 && query.numRowsAffected() != 1) {
            QString msg = QString("Row was updated or deleted from table ") + tableName() + QLatin1String(" by another transaction");
            sqlError = QSqlError(msg, QString(), QSqlError::UnknownError);
            throw SqlException(msg, __FILE__, __LINE__);
        }
    }
    return ret;
}
QString TPrototypeAjaxHelper::optionsToString(const TOption &options) const
{
    QString string;

    // Adds authenticity_token
    TOption opt(options);
    QVariantMap map;
    QVariant v = opt[Tf::Parameters];
    if (v.isValid() && v.canConvert(QVariant::Map)) {
        map = v.toMap();
    }
    map.insert("authenticity_token", actionView()->authenticityToken());
    opt.insert(Tf::Parameters, map);

    for (QMapIterator<int, QVariant> i(opt); i.hasNext(); ) {
        i.next();

        // Appends ajax option
        QString s = stringOptionHash()->value(i.key());
        if (!s.isEmpty() && i.value().canConvert(QVariant::String)) {
            string += s;
            string += QLatin1Char('\'');
            string += i.value().toString();
            string += QLatin1String("', ");
            continue;
        }

        s = boolOptionHash()->value(i.key());
        if (!s.isEmpty() && i.value().canConvert(QVariant::Bool)) {
            string += s;
            string += (i.value().toBool()) ? QLatin1String("true, ") : QLatin1String("false, ");
            continue;
        }

        if (i.key() == Tf::Method && i.value().canConvert(QVariant::Int)) {
            string += QLatin1String("method:'");
            string += methodHash()->value(i.value().toInt());
            string += QLatin1String("', ");
            continue;
        }

        // Appends 'parameters' option
        if (i.key() == Tf::Parameters) {
            QString val;
            if (i.value().canConvert(QVariant::Map)) {
                QVariantMap m = i.value().toMap();
                for (QMapIterator<QString, QVariant> it(m); it.hasNext(); ) {
                    it.next();
                    if (it.value().canConvert<TJavaScriptObject>()) {
                        val += it.key();
                        val += QLatin1String(":");
                        val += it.value().value<TJavaScriptObject>().toString();
                        val += QLatin1String(", ");
                    } else if (it.value().canConvert(QVariant::String)) {
                        val += it.key();
                        val += QLatin1String(":'");
                        val += THttpUtility::toUrlEncoding(it.value().toString());
                        val += QLatin1String("', ");
                    }
                }
                val.chop(2);
            }
            
            if (!val.isEmpty()) {
                string += QLatin1String("parameters: { ");
                string += val;
                string += QLatin1String(" }, ");
            }
            continue;
        }

        // Appends ajax callbacks
        s = eventStringHash()->value(i.key());
        if (!s.isEmpty() && i.value().canConvert(QVariant::String)) {
            string += s;
            string += i.value().toString();
            string += QLatin1String(", ");
            continue;
        } else {
            tWarn("invalid parameter: %d [%s:%d]", i.key(), __FILE__, __LINE__);
        }
    }

    string.chop(2);
    return string;
}
void TWebSocketWorker::run()
{
    QString es = TUrlRoute::splitPath(requestPath).value(0).toLower() + "endpoint";
    TDispatcher<TWebSocketEndpoint> dispatcher(es);
    TWebSocketEndpoint *endpoint = dispatcher.object();

    if (endpoint) {
        tSystemDebug("Found endpoint: %s", qPrintable(es));
        tSystemDebug("TWebSocketWorker opcode: %d", opcode);

        switch (opcode) {
        case TWebSocketFrame::Continuation:   // means session opening
            if (sessionStore.id().isEmpty()) {
                endpoint->onOpen(sessionStore);
            } else {
                tError("Invalid logic  [%s:%d]",  __FILE__, __LINE__);
            }
            break;

        case TWebSocketFrame::TextFrame:
            endpoint->onTextReceived(QString::fromUtf8(requestData));
            break;

        case TWebSocketFrame::BinaryFrame:
            endpoint->onBinaryReceived(requestData);
            break;

        case TWebSocketFrame::Close:
            endpoint->onClose();
            endpoint->closeWebSocket();
            break;

        case TWebSocketFrame::Ping:
            endpoint->onPing();
            endpoint->sendPong();
            break;

        case TWebSocketFrame::Pong:
            endpoint->onPong();
            break;

        default:
            tWarn("Invalid opcode: 0x%x  [%s:%d]", (int)opcode, __FILE__, __LINE__);
            break;
        }

        // Sends payload
        for (QListIterator<QVariant> it(endpoint->payloadList); it.hasNext(); ) {
            const QVariant &var = it.next();
            switch (var.type()) {
            case QVariant::String:
                socket->sendText(var.toString());
                break;

            case QVariant::ByteArray:
                socket->sendBinary(var.toByteArray());
                break;

            case QVariant::Int: {

                int opcode = var.toInt();
                switch (opcode) {
                case TWebSocketFrame::Close:
                    socket->disconnect();
                    break;

                case TWebSocketFrame::Ping:
                    socket->sendPing();
                    break;

                case TWebSocketFrame::Pong:
                    socket->sendPong();
                    break;

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

                break; }

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