Rewrote Frontend
This commit is contained in:
		
							
								
								
									
										9
									
								
								backend/.idea/cmake.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								backend/.idea/cmake.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="CMakeSharedSettings">
 | 
			
		||||
    <configurations>
 | 
			
		||||
      <configuration PROFILE_NAME="Debug" ENABLED="true" CONFIG_NAME="Debug" GENERATION_OPTIONS="-G Ninja --toolchain C:\vcpkg\scripts\buildsystems\vcpkg.cmake -DCMAKE_INSTALL_PREFIX=./cmake_install" />
 | 
			
		||||
      <configuration PROFILE_NAME="Release" ENABLED="true" CONFIG_NAME="Release" GENERATION_OPTIONS="-G Ninja --toolchain C:\vcpkg\scripts\buildsystems\vcpkg.cmake -DCMAKE_INSTALL_PREFIX=./cmake_install" />
 | 
			
		||||
    </configurations>
 | 
			
		||||
  </component>
 | 
			
		||||
</project>
 | 
			
		||||
							
								
								
									
										9
									
								
								backend/.idea/dataSources.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										9
									
								
								backend/.idea/dataSources.xml
									
									
									
										generated
									
									
									
								
							@@ -1,18 +1,11 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project version="4">
 | 
			
		||||
  <component name="DataSourceManagerImpl" format="xml" multifile-model="true">
 | 
			
		||||
    <data-source source="LOCAL" name="sqlite.db" uuid="6e8086dd-b853-422e-b48a-7c96a2403352">
 | 
			
		||||
      <driver-ref>sqlite.xerial</driver-ref>
 | 
			
		||||
      <synchronize>true</synchronize>
 | 
			
		||||
      <jdbc-driver>org.sqlite.JDBC</jdbc-driver>
 | 
			
		||||
      <jdbc-url>jdbc:sqlite:$PROJECT_DIR$/old_backend/sqlite.db</jdbc-url>
 | 
			
		||||
      <working-dir>$ProjectFileDir$</working-dir>
 | 
			
		||||
    </data-source>
 | 
			
		||||
    <data-source source="LOCAL" name="sqlite.db [2]" uuid="788293bd-abec-4b6b-a13e-26da21cb36dd">
 | 
			
		||||
      <driver-ref>sqlite.xerial</driver-ref>
 | 
			
		||||
      <synchronize>true</synchronize>
 | 
			
		||||
      <jdbc-driver>org.sqlite.JDBC</jdbc-driver>
 | 
			
		||||
      <jdbc-url>jdbc:sqlite:$PROJECT_DIR$/run/sqlite.db</jdbc-url>
 | 
			
		||||
      <jdbc-url>jdbc:sqlite:$PROJECT_DIR$/../run/sqlite.db</jdbc-url>
 | 
			
		||||
      <working-dir>$ProjectFileDir$</working-dir>
 | 
			
		||||
    </data-source>
 | 
			
		||||
  </component>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,10 @@
 | 
			
		||||
cmake_minimum_required(VERSION 3.20)
 | 
			
		||||
cmake_minimum_required(VERSION 3.21)
 | 
			
		||||
 | 
			
		||||
if (WIN32 AND (NOT VCPKG_TARGET_TRIPLET))
 | 
			
		||||
    set(VCPKG_TARGET_TRIPLET x64-windows-static)
 | 
			
		||||
endif (WIN32 AND (NOT VCPKG_TARGET_TRIPLET))
 | 
			
		||||
#set(VCPKG_LIBRARY_LINKAGE static)
 | 
			
		||||
 | 
			
		||||
project(backend)
 | 
			
		||||
 | 
			
		||||
set(CMAKE_CXX_STANDARD 20)
 | 
			
		||||
@@ -13,13 +19,6 @@ add_executable(backend
 | 
			
		||||
        src/db/db.h
 | 
			
		||||
        src/db/db.cpp
 | 
			
		||||
 | 
			
		||||
        src/db/model/Inode.cc
 | 
			
		||||
        src/db/model/Inode.h
 | 
			
		||||
        src/db/model/Tokens.cc
 | 
			
		||||
        src/db/model/Tokens.h
 | 
			
		||||
        src/db/model/User.cc
 | 
			
		||||
        src/db/model/User.h
 | 
			
		||||
 | 
			
		||||
        src/controllers/controllers.h
 | 
			
		||||
        src/controllers/admin.cpp
 | 
			
		||||
        src/controllers/fs.cpp
 | 
			
		||||
@@ -32,14 +31,23 @@ add_executable(backend
 | 
			
		||||
 | 
			
		||||
        src/filters/filters.h
 | 
			
		||||
        src/filters/filters.cpp
 | 
			
		||||
 | 
			
		||||
        model/Inode.cc
 | 
			
		||||
        model/Inode.h
 | 
			
		||||
        model/Tokens.cc
 | 
			
		||||
        model/Tokens.h
 | 
			
		||||
        model/User.cc
 | 
			
		||||
        model/User.h
 | 
			
		||||
 | 
			
		||||
        SMTPMail-drogon-master/SMTPMail.cc
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
find_package(Drogon CONFIG REQUIRED)
 | 
			
		||||
find_package(mailio CONFIG REQUIRED)
 | 
			
		||||
find_package(lodepng CONFIG REQUIRED)
 | 
			
		||||
find_package(OpenSSL REQUIRED)
 | 
			
		||||
find_package(OpenCV CONFIG REQUIRED)
 | 
			
		||||
find_package(kubazip CONFIG REQUIRED)
 | 
			
		||||
find_path(JWT_CPP_INCLUDE_DIRS "jwt-cpp/base.h")
 | 
			
		||||
find_path(BOTAN_INCLUDE_DIRS "botan/botan.h")
 | 
			
		||||
find_path(QR_INCLUDE_DIRS "qrcodegen.hpp")
 | 
			
		||||
@@ -48,6 +56,10 @@ find_library(QR_LIBRARY nayuki-qr-code-generator)
 | 
			
		||||
 | 
			
		||||
target_include_directories(backend PRIVATE
 | 
			
		||||
        src
 | 
			
		||||
        model
 | 
			
		||||
        shl
 | 
			
		||||
        SMTPMail-drogon-master
 | 
			
		||||
        ${OpenCV_INCLUDE_DIRS}
 | 
			
		||||
        ${JWT_CPP_INCLUDE_DIRS}
 | 
			
		||||
        ${BOTAN_INCLUDE_DIRS}
 | 
			
		||||
        ${QR_INCLUDE_DIRS}
 | 
			
		||||
@@ -55,14 +67,17 @@ target_include_directories(backend PRIVATE
 | 
			
		||||
 | 
			
		||||
target_link_libraries(backend
 | 
			
		||||
        Drogon::Drogon
 | 
			
		||||
        mailio
 | 
			
		||||
        lodepng
 | 
			
		||||
        OpenSSL::SSL
 | 
			
		||||
        kubazip::kubazip
 | 
			
		||||
        ${OpenCV_LIBS}
 | 
			
		||||
        ${BOTAN_LIBRARY}
 | 
			
		||||
        ${QR_LIBRARY}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
install(TARGETS backend)
 | 
			
		||||
set_property(TARGET backend PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
 | 
			
		||||
 | 
			
		||||
install(TARGETS backend RUNTIME_DEPENDENCY_SET backend_deps DESTINATION .)
 | 
			
		||||
install(RUNTIME_DEPENDENCY_SET backend_deps)
 | 
			
		||||
 | 
			
		||||
if(NOT MSVC)
 | 
			
		||||
    target_compile_options(backend PRIVATE
 | 
			
		||||
@@ -74,5 +89,6 @@ else()
 | 
			
		||||
endif(NOT MSVC)
 | 
			
		||||
 | 
			
		||||
if(WIN32)
 | 
			
		||||
    target_link_libraries(backend iphlpapi)
 | 
			
		||||
    target_compile_definitions(backend PRIVATE NOMINMAX _WIN32_WINNT=0x0A00)
 | 
			
		||||
endif()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								backend/SMTPMail-drogon-master/LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								backend/SMTPMail-drogon-master/LICENSE
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
MIT License
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2020 ihmc3jn09hk
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
SOFTWARE.
 | 
			
		||||
							
								
								
									
										79
									
								
								backend/SMTPMail-drogon-master/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								backend/SMTPMail-drogon-master/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
			
		||||
# SMTPMail-drogon
 | 
			
		||||
Simple Mail for the Drogon framework.
 | 
			
		||||
 | 
			
		||||
It is made as a plugin for the [drogon](https://github.com/an-tao/drogon) framework.
 | 
			
		||||
It can be included into the drogon build with little
 | 
			
		||||
modification of the class declaration.
 | 
			
		||||
## Updates 
 | 
			
		||||
- **[ 13-06-2022 ] Fixed vulnerability issues reported by [Sam](https://snoopysecurity.github.io/about).**
 | 
			
		||||
- [ 13-09-2021 ] Added [HTML content support](https://github.com/ihmc3jn09hk/SMTPMail-drogon/pull/1).
 | 
			
		||||
- [ 23-12-2020 ] Added DNS support.
 | 
			
		||||
 | 
			
		||||
## Acknowledgement
 | 
			
		||||
* The implementation takes SMTPClient for Qt from [kelvins](https://github.com/kelvins/SMTPClient) as reference.
 | 
			
		||||
* There requires a delay SSL encryption from the Tcp-socket (named TcpClient in trantor/drogon) and the major
 | 
			
		||||
author of drogon [reponsed](https://github.com/an-tao/drogon/issues/346) quickly.
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
Download to the plugin directory of the target drogon app, E.g. ~/drogon-app/plugins
 | 
			
		||||
```bash
 | 
			
		||||
$ git clone https://github.com/ihmc3jn09hk/SMTPMail-drogon.git
 | 
			
		||||
$ cp SMTPMail-drogon/SMTPMail.* ~/drogon-app/plugins
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
* _Be aware of add the plugin into the config.json. Set the "name" field to "SMTPMail"_
 | 
			
		||||
 | 
			
		||||
Add the reference header and get the plugin from the app(), E.g.
 | 
			
		||||
 | 
			
		||||
```c++
 | 
			
		||||
...
 | 
			
		||||
#include "../plugins/SMTPMail.h"
 | 
			
		||||
...
 | 
			
		||||
 | 
			
		||||
//Inside some function, E.g. A controller function.
 | 
			
		||||
...
 | 
			
		||||
//Send an email
 | 
			
		||||
auto *smtpmailPtr = app().getPlugin<SMTPMail>();
 | 
			
		||||
auto id = smtpmailPtr->sendEmail(
 | 
			
		||||
          "127.0.0.1",                  //The server IP/DNS
 | 
			
		||||
          587,                          //The port
 | 
			
		||||
          "mailer@something.com",       //Who send the email
 | 
			
		||||
          "receiver@otherthing.com",    //Send to whom
 | 
			
		||||
          "Testing SMTPMail Function",  //Email Subject/Title
 | 
			
		||||
          "Hello from drogon plugin",   //Content
 | 
			
		||||
          "mailer@something.com",       //Login user
 | 
			
		||||
          "123456",                     //User password
 | 
			
		||||
          false                         //Is HTML content
 | 
			
		||||
          );
 | 
			
		||||
...
 | 
			
		||||
//Or get noted when email is sent
 | 
			
		||||
...
 | 
			
		||||
void callback(const std::string &msg)
 | 
			
		||||
{
 | 
			
		||||
  LOG_INFO << msg; /*Output e.g. "EMail sent. ID : 96ESERVDDFH17588ECF0C7B00326E3"*/
 | 
			
		||||
  /*Do whatever you like*/
 | 
			
		||||
}
 | 
			
		||||
...
 | 
			
		||||
auto *smtpmailPtr = app().getPlugin<SMTPMail>();
 | 
			
		||||
auto id = smtpmailPtr->sendEmail(
 | 
			
		||||
          "127.0.0.1",                  //The server IP/DNS
 | 
			
		||||
          587,                          //The port
 | 
			
		||||
          "mailer@something.com",       //Who send the email
 | 
			
		||||
          "receiver@otherthing.com",    //Send to whom
 | 
			
		||||
          "Testing SMTPMail Function",  //Email Subject/Title
 | 
			
		||||
          "Hello from drogon plugin",   //Content
 | 
			
		||||
          "mailer@something.com",       //Login user
 | 
			
		||||
          "123456",                     //User password
 | 
			
		||||
          false,                        //Is HTML content
 | 
			
		||||
          callback                      //Callback
 | 
			
		||||
          );
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
$ cd ~/drogon-app/build
 | 
			
		||||
$ make
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Licence
 | 
			
		||||
* Feel free to use, thanks to open-source.
 | 
			
		||||
* For the sake of concern on commercial usage, a simple licence is included in each of the files.
 | 
			
		||||
							
								
								
									
										400
									
								
								backend/SMTPMail-drogon-master/SMTPMail.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										400
									
								
								backend/SMTPMail-drogon-master/SMTPMail.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,400 @@
 | 
			
		||||
/**
 | 
			
		||||
*
 | 
			
		||||
* SMTPMail.cc *
 | 
			
		||||
*
 | 
			
		||||
* This plugin is for SMTP mail delivery for the Drogon web-framework.
 | 
			
		||||
Implementation
 | 
			
		||||
* reference from the project "SMTPClient" with Qt5 by kelvins. Please check out
 | 
			
		||||
* https://github.com/kelvins/SMTPClient.
 | 
			
		||||
 | 
			
		||||
Feel free to use the code. For the sake of any concern, the following licence is
 | 
			
		||||
attached.
 | 
			
		||||
 | 
			
		||||
 Copyright 2020 ihmc3jn09hk
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
 | 
			
		||||
this software and associated documentation files (the "Software"), to deal in
 | 
			
		||||
the Software without restriction, including without limitation the rights to
 | 
			
		||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 | 
			
		||||
the Software, and to permit persons to whom the Software is furnished to do so,
 | 
			
		||||
subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 | 
			
		||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 | 
			
		||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 | 
			
		||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 | 
			
		||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | 
			
		||||
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "SMTPMail.h"
 | 
			
		||||
#include <drogon/HttpAppFramework.h>
 | 
			
		||||
#include <drogon/utils/Utilities.h>
 | 
			
		||||
#include <trantor/net/EventLoopThread.h>
 | 
			
		||||
#include <trantor/net/TcpClient.h>
 | 
			
		||||
 | 
			
		||||
using namespace drogon;
 | 
			
		||||
using namespace trantor;
 | 
			
		||||
 | 
			
		||||
struct EMail {
 | 
			
		||||
  enum states {
 | 
			
		||||
    Init,
 | 
			
		||||
    HandShake,
 | 
			
		||||
    Tls,
 | 
			
		||||
    Auth,
 | 
			
		||||
    User,
 | 
			
		||||
    Pass,
 | 
			
		||||
    Mail,
 | 
			
		||||
    Rcpt,
 | 
			
		||||
    Data,
 | 
			
		||||
    Body,
 | 
			
		||||
    Quit,
 | 
			
		||||
    Close
 | 
			
		||||
  };
 | 
			
		||||
  std::string m_from;
 | 
			
		||||
  std::string m_to;
 | 
			
		||||
  std::string m_subject;
 | 
			
		||||
  std::string m_content;
 | 
			
		||||
  std::string m_user;
 | 
			
		||||
  std::string m_passwd;
 | 
			
		||||
  states m_status;
 | 
			
		||||
  std::string m_uuid;
 | 
			
		||||
  bool m_isHTML{false};
 | 
			
		||||
  std::shared_ptr<trantor::TcpClient> m_socket;
 | 
			
		||||
 | 
			
		||||
  EMail(std::string from, std::string to, std::string subject,
 | 
			
		||||
        std::string content, std::string user, std::string passwd, bool isHTML,
 | 
			
		||||
        std::shared_ptr<trantor::TcpClient> socket)
 | 
			
		||||
      : m_from(std::move(from)), m_to(std::move(to)),
 | 
			
		||||
        m_subject(std::move(subject)), m_content(std::move(content)),
 | 
			
		||||
        m_user(std::move(user)), m_passwd(std::move(passwd)),
 | 
			
		||||
        m_socket(std::move(socket)), m_isHTML(isHTML),
 | 
			
		||||
        m_uuid(drogon::utils::getUuid()) {
 | 
			
		||||
    m_status = Init;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ~EMail() = default;
 | 
			
		||||
 | 
			
		||||
  static std::unordered_map<std::string, std::shared_ptr<EMail>>
 | 
			
		||||
      m_emails; // Container for processing emails
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::unordered_map<std::string, std::shared_ptr<EMail>> EMail::m_emails;
 | 
			
		||||
 | 
			
		||||
void SMTPMail::initAndStart(const Json::Value &config) {
 | 
			
		||||
  /// Initialize and start the plugin
 | 
			
		||||
  LOG_INFO << "SMTPMail initialized and started";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SMTPMail::shutdown() {
 | 
			
		||||
  /// Shutdown the plugin
 | 
			
		||||
  LOG_INFO << "STMPMail shutdown";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void messagesHandle(const trantor::TcpConnectionPtr &connPtr,
 | 
			
		||||
                    trantor::MsgBuffer *msg,
 | 
			
		||||
                    const std::shared_ptr<EMail> &email,
 | 
			
		||||
                    const std::function<void(const std::string &msg)> &cb) {
 | 
			
		||||
  std::string receivedMsg;
 | 
			
		||||
  while (msg->readableBytes() > 0) {
 | 
			
		||||
    std::string buf(msg->peek(), msg->readableBytes());
 | 
			
		||||
    receivedMsg.append(buf);
 | 
			
		||||
    //        LOG_INFO << buf;
 | 
			
		||||
    msg->retrieveAll();
 | 
			
		||||
  }
 | 
			
		||||
  LOG_TRACE << "receive: " << receivedMsg;
 | 
			
		||||
  std::string responseCode(receivedMsg.begin(), receivedMsg.begin() + 3);
 | 
			
		||||
  //    std::string responseMsg(receivedMsg.begin() + 4, receivedMsg.end());
 | 
			
		||||
 | 
			
		||||
  if (email->m_status == EMail::Init && responseCode == "220") {
 | 
			
		||||
    std::string outMsg;
 | 
			
		||||
    trantor::MsgBuffer out;
 | 
			
		||||
 | 
			
		||||
    outMsg.append("EHLO smtpclient.qw");
 | 
			
		||||
    outMsg.append("\r\n");
 | 
			
		||||
 | 
			
		||||
    out.append(outMsg.data(), outMsg.size());
 | 
			
		||||
 | 
			
		||||
    connPtr->send(std::move(out));
 | 
			
		||||
 | 
			
		||||
    email->m_status = EMail::HandShake;
 | 
			
		||||
  } else if (email->m_status == EMail::HandShake && responseCode == "220") {
 | 
			
		||||
    std::string outMsg;
 | 
			
		||||
    trantor::MsgBuffer out;
 | 
			
		||||
 | 
			
		||||
    outMsg.append("EHLO smtpclient.qw");
 | 
			
		||||
    outMsg.append("\r\n");
 | 
			
		||||
 | 
			
		||||
    out.append(outMsg.data(), outMsg.size());
 | 
			
		||||
 | 
			
		||||
    connPtr->startClientEncryption(
 | 
			
		||||
        [connPtr, out]() {
 | 
			
		||||
          // LOG_TRACE << "SSL established";
 | 
			
		||||
          connPtr->send(out);
 | 
			
		||||
        },
 | 
			
		||||
        false, false);
 | 
			
		||||
 | 
			
		||||
    email->m_status = EMail::Auth;
 | 
			
		||||
  } else if (email->m_status == EMail::HandShake && responseCode == "250") {
 | 
			
		||||
    std::string outMsg;
 | 
			
		||||
    trantor::MsgBuffer out;
 | 
			
		||||
 | 
			
		||||
    outMsg.append("STARTTLS");
 | 
			
		||||
    outMsg.append("\r\n");
 | 
			
		||||
 | 
			
		||||
    out.append(outMsg.data(), outMsg.size());
 | 
			
		||||
 | 
			
		||||
    connPtr->send(std::move(out));
 | 
			
		||||
 | 
			
		||||
    email->m_status = EMail::HandShake;
 | 
			
		||||
  } else if (email->m_status == EMail::Auth && responseCode == "250") {
 | 
			
		||||
    trantor::MsgBuffer out;
 | 
			
		||||
    std::string outMsg;
 | 
			
		||||
 | 
			
		||||
    outMsg.append("AUTH LOGIN");
 | 
			
		||||
    outMsg.append("\r\n");
 | 
			
		||||
 | 
			
		||||
    out.append(outMsg.data(), outMsg.size());
 | 
			
		||||
 | 
			
		||||
    connPtr->send(std::move(out));
 | 
			
		||||
 | 
			
		||||
    email->m_status = EMail::User;
 | 
			
		||||
  } else if (email->m_status == EMail::User && responseCode == "334") {
 | 
			
		||||
    trantor::MsgBuffer out;
 | 
			
		||||
    std::string outMsg;
 | 
			
		||||
 | 
			
		||||
    std::string secret(email->m_user);
 | 
			
		||||
 | 
			
		||||
    // outMsg.append(base64_encode(reinterpret_cast<const unsigned
 | 
			
		||||
    // char*>(secret.c_str()), secret.length()));
 | 
			
		||||
    outMsg.append(drogon::utils::base64Encode(
 | 
			
		||||
        reinterpret_cast<const unsigned char *>(secret.c_str()),
 | 
			
		||||
        secret.length()));
 | 
			
		||||
 | 
			
		||||
    outMsg.append("\r\n");
 | 
			
		||||
 | 
			
		||||
    out.append(outMsg.data(), outMsg.size());
 | 
			
		||||
 | 
			
		||||
    connPtr->send(std::move(out));
 | 
			
		||||
 | 
			
		||||
    email->m_status = EMail::Pass;
 | 
			
		||||
  } else if (email->m_status == EMail::Pass && responseCode == "334") {
 | 
			
		||||
    trantor::MsgBuffer out;
 | 
			
		||||
    std::string outMsg;
 | 
			
		||||
 | 
			
		||||
    std::string secret(email->m_passwd);
 | 
			
		||||
 | 
			
		||||
    outMsg.append(drogon::utils::base64Encode(
 | 
			
		||||
        reinterpret_cast<const unsigned char *>(secret.c_str()),
 | 
			
		||||
        secret.length()));
 | 
			
		||||
    outMsg.append("\r\n");
 | 
			
		||||
 | 
			
		||||
    out.append(outMsg.data(), outMsg.size());
 | 
			
		||||
 | 
			
		||||
    connPtr->send(std::move(out));
 | 
			
		||||
 | 
			
		||||
    email->m_status = EMail::Mail;
 | 
			
		||||
  } else if (email->m_status == EMail::Mail && responseCode == "235") {
 | 
			
		||||
    trantor::MsgBuffer out;
 | 
			
		||||
    std::string outMsg;
 | 
			
		||||
 | 
			
		||||
    outMsg.append("MAIL FROM:<");
 | 
			
		||||
    outMsg.append(email->m_from);
 | 
			
		||||
    outMsg.append(">\r\n");
 | 
			
		||||
 | 
			
		||||
    out.append(outMsg.data(), outMsg.size());
 | 
			
		||||
 | 
			
		||||
    connPtr->send(std::move(out));
 | 
			
		||||
 | 
			
		||||
    email->m_status = EMail::Rcpt;
 | 
			
		||||
  } else if (email->m_status == EMail::Rcpt && responseCode == "250") {
 | 
			
		||||
    trantor::MsgBuffer out;
 | 
			
		||||
    std::string outMsg;
 | 
			
		||||
 | 
			
		||||
    outMsg.append("RCPT TO:<");
 | 
			
		||||
    outMsg.append(email->m_to);
 | 
			
		||||
    outMsg.append(">\r\n");
 | 
			
		||||
 | 
			
		||||
    out.append(outMsg.data(), outMsg.size());
 | 
			
		||||
 | 
			
		||||
    connPtr->send(std::move(out));
 | 
			
		||||
 | 
			
		||||
    email->m_status = EMail::Data;
 | 
			
		||||
  } else if (email->m_status == EMail::Data && responseCode == "250") {
 | 
			
		||||
    trantor::MsgBuffer out;
 | 
			
		||||
    std::string outMsg;
 | 
			
		||||
 | 
			
		||||
    outMsg.append("DATA");
 | 
			
		||||
    outMsg.append("\r\n");
 | 
			
		||||
 | 
			
		||||
    out.append(outMsg.data(), outMsg.size());
 | 
			
		||||
 | 
			
		||||
    connPtr->send(std::move(out));
 | 
			
		||||
 | 
			
		||||
    email->m_status = EMail::Body;
 | 
			
		||||
  } else if (email->m_status == EMail::Body && responseCode == "354") {
 | 
			
		||||
    trantor::MsgBuffer out;
 | 
			
		||||
    std::string outMsg;
 | 
			
		||||
    std::time_t t = std::time(nullptr);
 | 
			
		||||
    char buf[100];
 | 
			
		||||
    std::strftime(buf, 100, "%a, %d %b %Y %T %z",  std::localtime(&t));
 | 
			
		||||
 | 
			
		||||
    outMsg.append("To: " + email->m_to + "\r\n");
 | 
			
		||||
    outMsg.append("From: " + email->m_from + "\r\n");
 | 
			
		||||
    outMsg.append("Date: " + std::string(buf) + "\r\n");
 | 
			
		||||
    if (email->m_isHTML) {
 | 
			
		||||
      outMsg.append("Content-Type: text/html;\r\n");
 | 
			
		||||
    }
 | 
			
		||||
    outMsg.append("Subject: " + email->m_subject + "\r\n\r\n");
 | 
			
		||||
 | 
			
		||||
    outMsg.append(email->m_content);
 | 
			
		||||
    outMsg.append("\r\n.\r\n");
 | 
			
		||||
 | 
			
		||||
    out.append(outMsg.data(), outMsg.size());
 | 
			
		||||
 | 
			
		||||
    connPtr->send(std::move(out));
 | 
			
		||||
 | 
			
		||||
    email->m_status = EMail::Quit;
 | 
			
		||||
  } else if (email->m_status == EMail::Quit && responseCode == "250") {
 | 
			
		||||
    trantor::MsgBuffer out;
 | 
			
		||||
    std::string outMsg;
 | 
			
		||||
 | 
			
		||||
    outMsg.append("QUIT");
 | 
			
		||||
    outMsg.append("\r\n");
 | 
			
		||||
 | 
			
		||||
    out.append(outMsg.data(), outMsg.size());
 | 
			
		||||
 | 
			
		||||
    connPtr->send(std::move(out));
 | 
			
		||||
 | 
			
		||||
    email->m_status = EMail::Close;
 | 
			
		||||
  } else if (email->m_status == EMail::Close) {
 | 
			
		||||
    /*Callback here for succeed delivery is probable*/
 | 
			
		||||
    cb("EMail sent. ID : " + email->m_uuid);
 | 
			
		||||
    return;
 | 
			
		||||
  } else {
 | 
			
		||||
    email->m_status = EMail::Close;
 | 
			
		||||
    /*Callback here for notification is probable*/
 | 
			
		||||
    cb(receivedMsg);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string
 | 
			
		||||
SMTPMail::sendEmail(const std::string &mailServer, const uint16_t &port,
 | 
			
		||||
                    const std::string &from, const std::string &to,
 | 
			
		||||
                    const std::string &subject, const std::string &content,
 | 
			
		||||
                    const std::string &user, const std::string &passwd,
 | 
			
		||||
                    bool isHTML,
 | 
			
		||||
                    const std::function<void(const std::string &)> &cb) {
 | 
			
		||||
  if (mailServer.empty() || from.empty() || to.empty() || subject.empty() ||
 | 
			
		||||
      user.empty() || passwd.empty()) {
 | 
			
		||||
    LOG_WARN << "Invalid input(s) - "
 | 
			
		||||
             << "\nServer : " << mailServer << "\nPort : " << port
 | 
			
		||||
             << "\nfrom : " << from << "\nto : " << to
 | 
			
		||||
             << "\nsubject : " << subject << "\nuser : " << user
 | 
			
		||||
             << "\npasswd : " << passwd;
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static auto hasLineBreak = [](const std::string &msg) {
 | 
			
		||||
    if (std::string::npos != msg.find_first_of("\n") ||
 | 
			
		||||
        std::string::npos != msg.find_first_of("\r")) {
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  if (hasLineBreak(from)) {
 | 
			
		||||
    LOG_WARN << "Invalid \"FROM\" data : " << from;
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
  if (hasLineBreak(to)) {
 | 
			
		||||
    LOG_WARN << "Invalid \"TO\" data : " << to;
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
  if (hasLineBreak(subject)) {
 | 
			
		||||
    LOG_WARN << "Invalid \"SUBJECT\" data : " << subject.data();
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  LOG_TRACE << "New TcpClient : " << mailServer << ":" << port;
 | 
			
		||||
 | 
			
		||||
  // Create the email
 | 
			
		||||
  auto email = std::make_shared<EMail>(from, to, subject, content, user, passwd,
 | 
			
		||||
                                       isHTML, nullptr);
 | 
			
		||||
 | 
			
		||||
  auto resolver = app().getResolver();
 | 
			
		||||
  resolver->resolve(
 | 
			
		||||
      mailServer, [email, port, cb](const trantor::InetAddress &addr) {
 | 
			
		||||
        constexpr size_t defaultLoopIdA = 10;
 | 
			
		||||
        constexpr size_t defaultLoopIdB = 9;
 | 
			
		||||
        auto loopA = app().getIOLoop(defaultLoopIdA);
 | 
			
		||||
        auto loopB = app().getIOLoop(defaultLoopIdB);
 | 
			
		||||
        
 | 
			
		||||
        if ( loopA == loopB ) {
 | 
			
		||||
          LOG_WARN << "Please provide at least 2 threads for this plugin";
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto loop = loopA->isInLoopThread() ? loopB : loopA;
 | 
			
		||||
        
 | 
			
		||||
        assert(loop);                    // Should never be null
 | 
			
		||||
        trantor::InetAddress addr_(addr.toIp(), port, false);
 | 
			
		||||
        auto tcpSocket =
 | 
			
		||||
            std::make_shared<trantor::TcpClient>(loop, addr_, "SMTPMail");
 | 
			
		||||
 | 
			
		||||
        email->m_socket = tcpSocket;
 | 
			
		||||
 | 
			
		||||
        std::weak_ptr<EMail> email_wptr = email;
 | 
			
		||||
 | 
			
		||||
        EMail::m_emails.emplace(email->m_uuid,
 | 
			
		||||
                                email); // Assuming there is no uuid collision
 | 
			
		||||
        tcpSocket->setConnectionCallback(
 | 
			
		||||
            [email_wptr](const trantor::TcpConnectionPtr &connPtr) {
 | 
			
		||||
              auto email_ptr = email_wptr.lock();
 | 
			
		||||
              if (!email_ptr) {
 | 
			
		||||
                LOG_WARN << "EMail pointer gone";
 | 
			
		||||
                return;
 | 
			
		||||
              }
 | 
			
		||||
              if (connPtr->connected()) {
 | 
			
		||||
                // send request;
 | 
			
		||||
                LOG_TRACE << "Connection established!";
 | 
			
		||||
              } else {
 | 
			
		||||
                LOG_TRACE << "Connection disconnect";
 | 
			
		||||
                EMail::m_emails.erase(
 | 
			
		||||
                    email_ptr->m_uuid); // Remove the email in list
 | 
			
		||||
                // thisPtr->onError(std::string("ReqResult::NetworkFailure"));
 | 
			
		||||
              }
 | 
			
		||||
            });
 | 
			
		||||
        tcpSocket->setConnectionErrorCallback([email_wptr]() {
 | 
			
		||||
          auto email_ptr = email_wptr.lock();
 | 
			
		||||
          if (!email_ptr) {
 | 
			
		||||
            LOG_ERROR << "EMail pointer gone";
 | 
			
		||||
            return;
 | 
			
		||||
          }
 | 
			
		||||
          // can't connect to server
 | 
			
		||||
          LOG_ERROR << "Bad Server address";
 | 
			
		||||
          EMail::m_emails.erase(email_ptr->m_uuid); // Remove the email in list
 | 
			
		||||
          // thisPtr->onError(std::string("ReqResult::BadServerAddress"));
 | 
			
		||||
        });
 | 
			
		||||
        auto cb_(cb ? cb : [](const std::string &msg) {
 | 
			
		||||
          LOG_INFO << "Default email callback : " << msg;
 | 
			
		||||
        });
 | 
			
		||||
        tcpSocket->setMessageCallback(
 | 
			
		||||
            [email_wptr, cb_](const trantor::TcpConnectionPtr &connPtr,
 | 
			
		||||
                              trantor::MsgBuffer *msg) {
 | 
			
		||||
              auto email_ptr = email_wptr.lock();
 | 
			
		||||
              if (!email_ptr) {
 | 
			
		||||
                LOG_ERROR << "EMail pointer gone";
 | 
			
		||||
                return;
 | 
			
		||||
              }
 | 
			
		||||
              // email->m_socket->disconnect();
 | 
			
		||||
              messagesHandle(connPtr, msg, email_ptr, cb_);
 | 
			
		||||
            });
 | 
			
		||||
        tcpSocket->connect(); // Start trying to send the email
 | 
			
		||||
      });
 | 
			
		||||
  return email->m_uuid;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										63
									
								
								backend/SMTPMail-drogon-master/SMTPMail.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								backend/SMTPMail-drogon-master/SMTPMail.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 *  SMTPMail.h
 | 
			
		||||
 *
 | 
			
		||||
 * This plugin is for SMTP mail delievery for the Drogon web-framework.
 | 
			
		||||
Implementation
 | 
			
		||||
 * reference from the project "SMTPClient" with Qt5 by kelvins. Please check out
 | 
			
		||||
 * https://github.com/kelvins/SMTPClient.
 | 
			
		||||
 | 
			
		||||
Copyright 2020 ihmc3jn09hk
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
 | 
			
		||||
this software and associated documentation files (the "Software"), to deal in
 | 
			
		||||
the Software without restriction, including without limitation the rights to
 | 
			
		||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 | 
			
		||||
the Software, and to permit persons to whom the Software is furnished to do so,
 | 
			
		||||
subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 | 
			
		||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 | 
			
		||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 | 
			
		||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 | 
			
		||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | 
			
		||||
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <drogon/plugins/Plugin.h>
 | 
			
		||||
 | 
			
		||||
class SMTPMail : public drogon::Plugin<SMTPMail> {
 | 
			
		||||
public:
 | 
			
		||||
  SMTPMail() = default;
 | 
			
		||||
  /// This method must be called by drogon to initialize and start the plugin.
 | 
			
		||||
  /// It must be implemented by the user.
 | 
			
		||||
  void initAndStart(const Json::Value &config) override;
 | 
			
		||||
 | 
			
		||||
  /// This method must be called by drogon to shutdown the plugin.
 | 
			
		||||
  /// It must be implemented by the user.
 | 
			
		||||
  void shutdown() override;
 | 
			
		||||
 | 
			
		||||
  /** Send an email
 | 
			
		||||
   * return : An ID of the email.
 | 
			
		||||
   */
 | 
			
		||||
  std::string sendEmail(
 | 
			
		||||
      const std::string
 | 
			
		||||
          &mailServer, // Mail server address/dns E.g. 127.0.0.1/smtp.mail.com
 | 
			
		||||
      const uint16_t &port,       // Port  E.g. 587
 | 
			
		||||
      const std::string &from,    // Send from whom E.g. drogon@gmail.com
 | 
			
		||||
      const std::string &to,      // Reciever       E.g. drogon@yahoo.com
 | 
			
		||||
      const std::string &subject, // The email title/subject
 | 
			
		||||
      const std::string &content, // The email content.
 | 
			
		||||
      const std::string &user,    // User      (Usually same as "from")
 | 
			
		||||
      const std::string &passwd,  // Password
 | 
			
		||||
      bool isHTML,                // content type
 | 
			
		||||
      const std::function<void(const std::string &)> &cb = {}
 | 
			
		||||
      // The callback for email sent notification
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "Inode.h"
 | 
			
		||||
#include <drogon/utils/Utilities.h>
 | 
			
		||||
#include "drogon/utils/Utilities.h"
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
using namespace drogon;
 | 
			
		||||
@@ -19,6 +19,7 @@ const std::string Inode::Cols::_name = "name";
 | 
			
		||||
const std::string Inode::Cols::_parent_id = "parent_id";
 | 
			
		||||
const std::string Inode::Cols::_owner_id = "owner_id";
 | 
			
		||||
const std::string Inode::Cols::_size = "size";
 | 
			
		||||
const std::string Inode::Cols::_has_preview = "has_preview";
 | 
			
		||||
const std::string Inode::primaryKeyName = "id";
 | 
			
		||||
const bool Inode::hasPrimaryKey = true;
 | 
			
		||||
const std::string Inode::tableName = "inode";
 | 
			
		||||
@@ -29,7 +30,8 @@ const std::vector<typename Inode::MetaData> Inode::metaData_={
 | 
			
		||||
{"name","std::string","text",0,0,0,0},
 | 
			
		||||
{"parent_id","uint64_t","integer",8,0,0,0},
 | 
			
		||||
{"owner_id","uint64_t","integer",8,0,0,1},
 | 
			
		||||
{"size","uint64_t","integer",8,0,0,0}
 | 
			
		||||
{"size","uint64_t","integer",8,0,0,0},
 | 
			
		||||
{"has_preview","uint64_t","integer",8,0,0,1}
 | 
			
		||||
};
 | 
			
		||||
const std::string &Inode::getColumnName(size_t index) noexcept(false)
 | 
			
		||||
{
 | 
			
		||||
@@ -64,11 +66,15 @@ Inode::Inode(const Row &r, const ssize_t indexOffset) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            size_=std::make_shared<uint64_t>(r["size"].as<uint64_t>());
 | 
			
		||||
        }
 | 
			
		||||
        if(!r["has_preview"].isNull())
 | 
			
		||||
        {
 | 
			
		||||
            hasPreview_=std::make_shared<uint64_t>(r["has_preview"].as<uint64_t>());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        size_t offset = (size_t)indexOffset;
 | 
			
		||||
        if(offset + 6 > r.size())
 | 
			
		||||
        if(offset + 7 > r.size())
 | 
			
		||||
        {
 | 
			
		||||
            LOG_FATAL << "Invalid SQL result for this model";
 | 
			
		||||
            return;
 | 
			
		||||
@@ -104,13 +110,18 @@ Inode::Inode(const Row &r, const ssize_t indexOffset) noexcept
 | 
			
		||||
        {
 | 
			
		||||
            size_=std::make_shared<uint64_t>(r[index].as<uint64_t>());
 | 
			
		||||
        }
 | 
			
		||||
        index = offset + 6;
 | 
			
		||||
        if(!r[index].isNull())
 | 
			
		||||
        {
 | 
			
		||||
            hasPreview_=std::make_shared<uint64_t>(r[index].as<uint64_t>());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Inode::Inode(const Json::Value &pJson, const std::vector<std::string> &pMasqueradingVector) noexcept(false)
 | 
			
		||||
{
 | 
			
		||||
    if(pMasqueradingVector.size() != 6)
 | 
			
		||||
    if(pMasqueradingVector.size() != 7)
 | 
			
		||||
    {
 | 
			
		||||
        LOG_ERROR << "Bad masquerading vector";
 | 
			
		||||
        return;
 | 
			
		||||
@@ -163,6 +174,14 @@ Inode::Inode(const Json::Value &pJson, const std::vector<std::string> &pMasquera
 | 
			
		||||
            size_=std::make_shared<uint64_t>((uint64_t)pJson[pMasqueradingVector[5]].asUInt64());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if(!pMasqueradingVector[6].empty() && pJson.isMember(pMasqueradingVector[6]))
 | 
			
		||||
    {
 | 
			
		||||
        dirtyFlag_[6] = true;
 | 
			
		||||
        if(!pJson[pMasqueradingVector[6]].isNull())
 | 
			
		||||
        {
 | 
			
		||||
            hasPreview_=std::make_shared<uint64_t>((uint64_t)pJson[pMasqueradingVector[6]].asUInt64());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Inode::Inode(const Json::Value &pJson) noexcept(false)
 | 
			
		||||
@@ -215,12 +234,20 @@ Inode::Inode(const Json::Value &pJson) noexcept(false)
 | 
			
		||||
            size_=std::make_shared<uint64_t>((uint64_t)pJson["size"].asUInt64());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if(pJson.isMember("has_preview"))
 | 
			
		||||
    {
 | 
			
		||||
        dirtyFlag_[6]=true;
 | 
			
		||||
        if(!pJson["has_preview"].isNull())
 | 
			
		||||
        {
 | 
			
		||||
            hasPreview_=std::make_shared<uint64_t>((uint64_t)pJson["has_preview"].asUInt64());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Inode::updateByMasqueradedJson(const Json::Value &pJson,
 | 
			
		||||
                                            const std::vector<std::string> &pMasqueradingVector) noexcept(false)
 | 
			
		||||
{
 | 
			
		||||
    if(pMasqueradingVector.size() != 6)
 | 
			
		||||
    if(pMasqueradingVector.size() != 7)
 | 
			
		||||
    {
 | 
			
		||||
        LOG_ERROR << "Bad masquerading vector";
 | 
			
		||||
        return;
 | 
			
		||||
@@ -272,6 +299,14 @@ void Inode::updateByMasqueradedJson(const Json::Value &pJson,
 | 
			
		||||
            size_=std::make_shared<uint64_t>((uint64_t)pJson[pMasqueradingVector[5]].asUInt64());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if(!pMasqueradingVector[6].empty() && pJson.isMember(pMasqueradingVector[6]))
 | 
			
		||||
    {
 | 
			
		||||
        dirtyFlag_[6] = true;
 | 
			
		||||
        if(!pJson[pMasqueradingVector[6]].isNull())
 | 
			
		||||
        {
 | 
			
		||||
            hasPreview_=std::make_shared<uint64_t>((uint64_t)pJson[pMasqueradingVector[6]].asUInt64());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Inode::updateByJson(const Json::Value &pJson) noexcept(false)
 | 
			
		||||
@@ -323,6 +358,14 @@ void Inode::updateByJson(const Json::Value &pJson) noexcept(false)
 | 
			
		||||
            size_=std::make_shared<uint64_t>((uint64_t)pJson["size"].asUInt64());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if(pJson.isMember("has_preview"))
 | 
			
		||||
    {
 | 
			
		||||
        dirtyFlag_[6] = true;
 | 
			
		||||
        if(!pJson["has_preview"].isNull())
 | 
			
		||||
        {
 | 
			
		||||
            hasPreview_=std::make_shared<uint64_t>((uint64_t)pJson["has_preview"].asUInt64());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const uint64_t &Inode::getValueOfId() const noexcept
 | 
			
		||||
@@ -452,6 +495,23 @@ void Inode::setSizeToNull() noexcept
 | 
			
		||||
    dirtyFlag_[5] = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const uint64_t &Inode::getValueOfHasPreview() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    const static uint64_t defaultValue = uint64_t();
 | 
			
		||||
    if(hasPreview_)
 | 
			
		||||
        return *hasPreview_;
 | 
			
		||||
    return defaultValue;
 | 
			
		||||
}
 | 
			
		||||
const std::shared_ptr<uint64_t> &Inode::getHasPreview() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return hasPreview_;
 | 
			
		||||
}
 | 
			
		||||
void Inode::setHasPreview(const uint64_t &pHasPreview) noexcept
 | 
			
		||||
{
 | 
			
		||||
    hasPreview_ = std::make_shared<uint64_t>(pHasPreview);
 | 
			
		||||
    dirtyFlag_[6] = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Inode::updateId(const uint64_t id)
 | 
			
		||||
{
 | 
			
		||||
    id_ = std::make_shared<uint64_t>(id);
 | 
			
		||||
@@ -464,7 +524,8 @@ const std::vector<std::string> &Inode::insertColumns() noexcept
 | 
			
		||||
        "name",
 | 
			
		||||
        "parent_id",
 | 
			
		||||
        "owner_id",
 | 
			
		||||
        "size"
 | 
			
		||||
        "size",
 | 
			
		||||
        "has_preview"
 | 
			
		||||
    };
 | 
			
		||||
    return inCols;
 | 
			
		||||
}
 | 
			
		||||
@@ -526,6 +587,17 @@ void Inode::outputArgs(drogon::orm::internal::SqlBinder &binder) const
 | 
			
		||||
            binder << nullptr;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if(dirtyFlag_[6])
 | 
			
		||||
    {
 | 
			
		||||
        if(getHasPreview())
 | 
			
		||||
        {
 | 
			
		||||
            binder << getValueOfHasPreview();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            binder << nullptr;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const std::vector<std::string> Inode::updateColumns() const
 | 
			
		||||
@@ -551,6 +623,10 @@ const std::vector<std::string> Inode::updateColumns() const
 | 
			
		||||
    {
 | 
			
		||||
        ret.push_back(getColumnName(5));
 | 
			
		||||
    }
 | 
			
		||||
    if(dirtyFlag_[6])
 | 
			
		||||
    {
 | 
			
		||||
        ret.push_back(getColumnName(6));
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -611,6 +687,17 @@ void Inode::updateArgs(drogon::orm::internal::SqlBinder &binder) const
 | 
			
		||||
            binder << nullptr;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if(dirtyFlag_[6])
 | 
			
		||||
    {
 | 
			
		||||
        if(getHasPreview())
 | 
			
		||||
        {
 | 
			
		||||
            binder << getValueOfHasPreview();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            binder << nullptr;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
Json::Value Inode::toJson() const
 | 
			
		||||
{
 | 
			
		||||
@@ -663,6 +750,14 @@ Json::Value Inode::toJson() const
 | 
			
		||||
    {
 | 
			
		||||
        ret["size"]=Json::Value();
 | 
			
		||||
    }
 | 
			
		||||
    if(getHasPreview())
 | 
			
		||||
    {
 | 
			
		||||
        ret["has_preview"]=(Json::UInt64)getValueOfHasPreview();
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        ret["has_preview"]=Json::Value();
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -670,7 +765,7 @@ Json::Value Inode::toMasqueradedJson(
 | 
			
		||||
    const std::vector<std::string> &pMasqueradingVector) const
 | 
			
		||||
{
 | 
			
		||||
    Json::Value ret;
 | 
			
		||||
    if(pMasqueradingVector.size() == 6)
 | 
			
		||||
    if(pMasqueradingVector.size() == 7)
 | 
			
		||||
    {
 | 
			
		||||
        if(!pMasqueradingVector[0].empty())
 | 
			
		||||
        {
 | 
			
		||||
@@ -738,6 +833,17 @@ Json::Value Inode::toMasqueradedJson(
 | 
			
		||||
                ret[pMasqueradingVector[5]]=Json::Value();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if(!pMasqueradingVector[6].empty())
 | 
			
		||||
        {
 | 
			
		||||
            if(getHasPreview())
 | 
			
		||||
            {
 | 
			
		||||
                ret[pMasqueradingVector[6]]=(Json::UInt64)getValueOfHasPreview();
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                ret[pMasqueradingVector[6]]=Json::Value();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
    LOG_ERROR << "Masquerade failed";
 | 
			
		||||
@@ -789,6 +895,14 @@ Json::Value Inode::toMasqueradedJson(
 | 
			
		||||
    {
 | 
			
		||||
        ret["size"]=Json::Value();
 | 
			
		||||
    }
 | 
			
		||||
    if(getHasPreview())
 | 
			
		||||
    {
 | 
			
		||||
        ret["has_preview"]=(Json::UInt64)getValueOfHasPreview();
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        ret["has_preview"]=Json::Value();
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -834,13 +948,23 @@ bool Inode::validateJsonForCreation(const Json::Value &pJson, std::string &err)
 | 
			
		||||
        if(!validJsonOfField(5, "size", pJson["size"], err, true))
 | 
			
		||||
            return false;
 | 
			
		||||
    }
 | 
			
		||||
    if(pJson.isMember("has_preview"))
 | 
			
		||||
    {
 | 
			
		||||
        if(!validJsonOfField(6, "has_preview", pJson["has_preview"], err, true))
 | 
			
		||||
            return false;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        err="The has_preview column cannot be null";
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
bool Inode::validateMasqueradedJsonForCreation(const Json::Value &pJson,
 | 
			
		||||
                                               const std::vector<std::string> &pMasqueradingVector,
 | 
			
		||||
                                               std::string &err)
 | 
			
		||||
{
 | 
			
		||||
    if(pMasqueradingVector.size() != 6)
 | 
			
		||||
    if(pMasqueradingVector.size() != 7)
 | 
			
		||||
    {
 | 
			
		||||
        err = "Bad masquerading vector";
 | 
			
		||||
        return false;
 | 
			
		||||
@@ -904,6 +1028,19 @@ bool Inode::validateMasqueradedJsonForCreation(const Json::Value &pJson,
 | 
			
		||||
                  return false;
 | 
			
		||||
          }
 | 
			
		||||
      }
 | 
			
		||||
      if(!pMasqueradingVector[6].empty())
 | 
			
		||||
      {
 | 
			
		||||
          if(pJson.isMember(pMasqueradingVector[6]))
 | 
			
		||||
          {
 | 
			
		||||
              if(!validJsonOfField(6, pMasqueradingVector[6], pJson[pMasqueradingVector[6]], err, true))
 | 
			
		||||
                  return false;
 | 
			
		||||
          }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            err="The " + pMasqueradingVector[6] + " column cannot be null";
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    catch(const Json::LogicError &e)
 | 
			
		||||
    {
 | 
			
		||||
@@ -949,13 +1086,18 @@ bool Inode::validateJsonForUpdate(const Json::Value &pJson, std::string &err)
 | 
			
		||||
        if(!validJsonOfField(5, "size", pJson["size"], err, false))
 | 
			
		||||
            return false;
 | 
			
		||||
    }
 | 
			
		||||
    if(pJson.isMember("has_preview"))
 | 
			
		||||
    {
 | 
			
		||||
        if(!validJsonOfField(6, "has_preview", pJson["has_preview"], err, false))
 | 
			
		||||
            return false;
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
bool Inode::validateMasqueradedJsonForUpdate(const Json::Value &pJson,
 | 
			
		||||
                                             const std::vector<std::string> &pMasqueradingVector,
 | 
			
		||||
                                             std::string &err)
 | 
			
		||||
{
 | 
			
		||||
    if(pMasqueradingVector.size() != 6)
 | 
			
		||||
    if(pMasqueradingVector.size() != 7)
 | 
			
		||||
    {
 | 
			
		||||
        err = "Bad masquerading vector";
 | 
			
		||||
        return false;
 | 
			
		||||
@@ -996,6 +1138,11 @@ bool Inode::validateMasqueradedJsonForUpdate(const Json::Value &pJson,
 | 
			
		||||
          if(!validJsonOfField(5, pMasqueradingVector[5], pJson[pMasqueradingVector[5]], err, false))
 | 
			
		||||
              return false;
 | 
			
		||||
      }
 | 
			
		||||
      if(!pMasqueradingVector[6].empty() && pJson.isMember(pMasqueradingVector[6]))
 | 
			
		||||
      {
 | 
			
		||||
          if(!validJsonOfField(6, pMasqueradingVector[6], pJson[pMasqueradingVector[6]], err, false))
 | 
			
		||||
              return false;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    catch(const Json::LogicError &e)
 | 
			
		||||
    {
 | 
			
		||||
@@ -1086,6 +1233,18 @@ bool Inode::validJsonOfField(size_t index,
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case 6:
 | 
			
		||||
            if(pJson.isNull())
 | 
			
		||||
            {
 | 
			
		||||
                err="The " + fieldName + " column cannot be null";
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            if(!pJson.isUInt64())
 | 
			
		||||
            {
 | 
			
		||||
                err="Type error in the "+fieldName+" field";
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            err="Internal error in the server";
 | 
			
		||||
            return false;
 | 
			
		||||
@@ -6,17 +6,17 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <drogon/orm/Result.h>
 | 
			
		||||
#include <drogon/orm/Row.h>
 | 
			
		||||
#include <drogon/orm/Field.h>
 | 
			
		||||
#include <drogon/orm/SqlBinder.h>
 | 
			
		||||
#include <drogon/orm/Mapper.h>
 | 
			
		||||
#include "drogon/orm/Result.h"
 | 
			
		||||
#include "drogon/orm/Row.h"
 | 
			
		||||
#include "drogon/orm/Field.h"
 | 
			
		||||
#include "drogon/orm/SqlBinder.h"
 | 
			
		||||
#include "drogon/orm/Mapper.h"
 | 
			
		||||
#ifdef __cpp_impl_coroutine
 | 
			
		||||
#include <drogon/orm/CoroMapper.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include <trantor/utils/Date.h>
 | 
			
		||||
#include <trantor/utils/Logger.h>
 | 
			
		||||
#include <json/json.h>
 | 
			
		||||
#include "trantor/utils/Date.h"
 | 
			
		||||
#include "trantor/utils/Logger.h"
 | 
			
		||||
#include "json/json.h"
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <vector>
 | 
			
		||||
@@ -48,6 +48,7 @@ class Inode
 | 
			
		||||
        static const std::string _parent_id;
 | 
			
		||||
        static const std::string _owner_id;
 | 
			
		||||
        static const std::string _size;
 | 
			
		||||
        static const std::string _has_preview;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const static int primaryKeyNumber;
 | 
			
		||||
@@ -151,8 +152,16 @@ class Inode
 | 
			
		||||
    void setSize(const uint64_t &pSize) noexcept;
 | 
			
		||||
    void setSizeToNull() noexcept;
 | 
			
		||||
 | 
			
		||||
    /**  For column has_preview  */
 | 
			
		||||
    ///Get the value of the column has_preview, returns the default value if the column is null
 | 
			
		||||
    const uint64_t &getValueOfHasPreview() const noexcept;
 | 
			
		||||
    ///Return a shared_ptr object pointing to the column const value, or an empty shared_ptr object if the column is null
 | 
			
		||||
    const std::shared_ptr<uint64_t> &getHasPreview() const noexcept;
 | 
			
		||||
    ///Set the value of the column has_preview
 | 
			
		||||
    void setHasPreview(const uint64_t &pHasPreview) noexcept;
 | 
			
		||||
 | 
			
		||||
    static size_t getColumnNumber() noexcept {  return 6;  }
 | 
			
		||||
 | 
			
		||||
    static size_t getColumnNumber() noexcept {  return 7;  }
 | 
			
		||||
    static const std::string &getColumnName(size_t index) noexcept(false);
 | 
			
		||||
 | 
			
		||||
    Json::Value toJson() const;
 | 
			
		||||
@@ -175,6 +184,7 @@ class Inode
 | 
			
		||||
    std::shared_ptr<uint64_t> parentId_;
 | 
			
		||||
    std::shared_ptr<uint64_t> ownerId_;
 | 
			
		||||
    std::shared_ptr<uint64_t> size_;
 | 
			
		||||
    std::shared_ptr<uint64_t> hasPreview_;
 | 
			
		||||
    struct MetaData
 | 
			
		||||
    {
 | 
			
		||||
        const std::string colName_;
 | 
			
		||||
@@ -186,7 +196,7 @@ class Inode
 | 
			
		||||
        const bool notNull_;
 | 
			
		||||
    };
 | 
			
		||||
    static const std::vector<MetaData> metaData_;
 | 
			
		||||
    bool dirtyFlag_[6]={ false };
 | 
			
		||||
    bool dirtyFlag_[7]={ false };
 | 
			
		||||
  public:
 | 
			
		||||
    static const std::string &sqlForFindingByPrimaryKey()
 | 
			
		||||
    {
 | 
			
		||||
@@ -229,6 +239,11 @@ class Inode
 | 
			
		||||
            sql += "size,";
 | 
			
		||||
            ++parametersCount;
 | 
			
		||||
        }
 | 
			
		||||
        if(dirtyFlag_[6])
 | 
			
		||||
        {
 | 
			
		||||
            sql += "has_preview,";
 | 
			
		||||
            ++parametersCount;
 | 
			
		||||
        }
 | 
			
		||||
        if(parametersCount > 0)
 | 
			
		||||
        {
 | 
			
		||||
            sql[sql.length()-1]=')';
 | 
			
		||||
@@ -261,6 +276,11 @@ class Inode
 | 
			
		||||
        {
 | 
			
		||||
            sql.append("?,");
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        if(dirtyFlag_[6])
 | 
			
		||||
        {
 | 
			
		||||
            sql.append("?,");
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        if(parametersCount > 0)
 | 
			
		||||
        {
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "Tokens.h"
 | 
			
		||||
#include <drogon/utils/Utilities.h>
 | 
			
		||||
#include "drogon/utils/Utilities.h"
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
using namespace drogon;
 | 
			
		||||
@@ -6,17 +6,17 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <drogon/orm/Result.h>
 | 
			
		||||
#include <drogon/orm/Row.h>
 | 
			
		||||
#include <drogon/orm/Field.h>
 | 
			
		||||
#include <drogon/orm/SqlBinder.h>
 | 
			
		||||
#include <drogon/orm/Mapper.h>
 | 
			
		||||
#include "drogon/orm/Result.h"
 | 
			
		||||
#include "drogon/orm/Row.h"
 | 
			
		||||
#include "drogon/orm/Field.h"
 | 
			
		||||
#include "drogon/orm/SqlBinder.h"
 | 
			
		||||
#include "drogon/orm/Mapper.h"
 | 
			
		||||
#ifdef __cpp_impl_coroutine
 | 
			
		||||
#include <drogon/orm/CoroMapper.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include <trantor/utils/Date.h>
 | 
			
		||||
#include <trantor/utils/Logger.h>
 | 
			
		||||
#include <json/json.h>
 | 
			
		||||
#include "trantor/utils/Date.h"
 | 
			
		||||
#include "trantor/utils/Logger.h"
 | 
			
		||||
#include "json/json.h"
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <vector>
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "User.h"
 | 
			
		||||
#include <drogon/utils/Utilities.h>
 | 
			
		||||
#include "drogon/utils/Utilities.h"
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
using namespace drogon;
 | 
			
		||||
@@ -6,17 +6,17 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <drogon/orm/Result.h>
 | 
			
		||||
#include <drogon/orm/Row.h>
 | 
			
		||||
#include <drogon/orm/Field.h>
 | 
			
		||||
#include <drogon/orm/SqlBinder.h>
 | 
			
		||||
#include <drogon/orm/Mapper.h>
 | 
			
		||||
#include "drogon/orm/Result.h"
 | 
			
		||||
#include "drogon/orm/Row.h"
 | 
			
		||||
#include "drogon/orm/Field.h"
 | 
			
		||||
#include "drogon/orm/SqlBinder.h"
 | 
			
		||||
#include "drogon/orm/Mapper.h"
 | 
			
		||||
#ifdef __cpp_impl_coroutine
 | 
			
		||||
#include <drogon/orm/CoroMapper.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include <trantor/utils/Date.h>
 | 
			
		||||
#include <trantor/utils/Logger.h>
 | 
			
		||||
#include <json/json.h>
 | 
			
		||||
#include "trantor/utils/Date.h"
 | 
			
		||||
#include "trantor/utils/Logger.h"
 | 
			
		||||
#include "json/json.h"
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <vector>
 | 
			
		||||
							
								
								
									
										68
									
								
								backend/shl/msd/blocking_iterator.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								backend/shl/msd/blocking_iterator.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
// Copyright (C) 2022 Andrei Avram
 | 
			
		||||
 | 
			
		||||
#ifndef MSD_CHANNEL_BLOCKING_ITERATOR_HPP_
 | 
			
		||||
#define MSD_CHANNEL_BLOCKING_ITERATOR_HPP_
 | 
			
		||||
 | 
			
		||||
#include <iterator>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
 | 
			
		||||
namespace msd {
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief An iterator that block the current thread,
 | 
			
		||||
 * waiting to fetch elements from the channel.
 | 
			
		||||
 *
 | 
			
		||||
 * Used to implement channel range-based for loop.
 | 
			
		||||
 *
 | 
			
		||||
 * @tparam Channel Instance of channel.
 | 
			
		||||
 */
 | 
			
		||||
template <typename channel>
 | 
			
		||||
class blocking_iterator {
 | 
			
		||||
   public:
 | 
			
		||||
    using value_type = typename channel::value_type;
 | 
			
		||||
 | 
			
		||||
    explicit blocking_iterator(channel& ch) : ch_{ch} {}
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Advances to next element in the channel.
 | 
			
		||||
     */
 | 
			
		||||
    blocking_iterator<channel> operator++() const noexcept { return *this; }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns an element from the channel.
 | 
			
		||||
     */
 | 
			
		||||
    value_type operator*() const
 | 
			
		||||
    {
 | 
			
		||||
        value_type value;
 | 
			
		||||
        value << ch_;
 | 
			
		||||
 | 
			
		||||
        return value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Makes iteration continue until the channel is closed and empty.
 | 
			
		||||
     */
 | 
			
		||||
    bool operator!=(blocking_iterator<channel>) const
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_lock<std::mutex> lock{ch_.mtx_};
 | 
			
		||||
        ch_.waitBeforeRead(lock);
 | 
			
		||||
 | 
			
		||||
        return !(ch_.closed() && ch_.empty());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
   private:
 | 
			
		||||
    channel& ch_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace msd
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Output iterator specialization
 | 
			
		||||
 */
 | 
			
		||||
template <typename T>
 | 
			
		||||
struct std::iterator_traits<msd::blocking_iterator<T>> {
 | 
			
		||||
    using value_type = typename msd::blocking_iterator<T>::value_type;
 | 
			
		||||
    using iterator_category = std::output_iterator_tag;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif  // MSD_CHANNEL_BLOCKING_ITERATOR_HPP_
 | 
			
		||||
							
								
								
									
										130
									
								
								backend/shl/msd/channel.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								backend/shl/msd/channel.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,130 @@
 | 
			
		||||
// Copyright (C) 2022 Andrei Avram
 | 
			
		||||
 | 
			
		||||
#ifndef MSD_CHANNEL_HPP_
 | 
			
		||||
#define MSD_CHANNEL_HPP_
 | 
			
		||||
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <condition_variable>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <queue>
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#include <type_traits>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include "blocking_iterator.hpp"
 | 
			
		||||
 | 
			
		||||
namespace msd {
 | 
			
		||||
 | 
			
		||||
#if (__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L))
 | 
			
		||||
#define NODISCARD [[nodiscard]]
 | 
			
		||||
#else
 | 
			
		||||
#define NODISCARD
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace detail {
 | 
			
		||||
template <typename T>
 | 
			
		||||
struct remove_cvref {
 | 
			
		||||
    using type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
using remove_cvref_t = typename remove_cvref<T>::type;
 | 
			
		||||
}  // namespace detail
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Exception thrown if trying to write on closed channel.
 | 
			
		||||
 */
 | 
			
		||||
class closed_channel : public std::runtime_error {
 | 
			
		||||
   public:
 | 
			
		||||
    explicit closed_channel(const char* msg) : std::runtime_error{msg} {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Thread-safe container for sharing data between threads.
 | 
			
		||||
 *
 | 
			
		||||
 * Implements a blocking input iterator.
 | 
			
		||||
 *
 | 
			
		||||
 * @tparam T The type of the elements.
 | 
			
		||||
 */
 | 
			
		||||
template <typename T>
 | 
			
		||||
class channel {
 | 
			
		||||
   public:
 | 
			
		||||
    using value_type = T;
 | 
			
		||||
    using iterator = blocking_iterator<channel<T>>;
 | 
			
		||||
    using size_type = std::size_t;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new channel.
 | 
			
		||||
     *
 | 
			
		||||
     * @param capacity Number of elements the channel can store before blocking.
 | 
			
		||||
     */
 | 
			
		||||
    explicit constexpr channel(size_type capacity = 0);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Pushes an element into the channel.
 | 
			
		||||
     *
 | 
			
		||||
     * @throws closed_channel if channel is closed.
 | 
			
		||||
     */
 | 
			
		||||
    template <typename Type>
 | 
			
		||||
    friend void operator>>(Type&&, channel<detail::remove_cvref_t<Type>>&);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Pops an element from the channel.
 | 
			
		||||
     *
 | 
			
		||||
     * @tparam Type The type of the elements
 | 
			
		||||
     */
 | 
			
		||||
    template <typename Type>
 | 
			
		||||
    friend void operator<<(Type&, channel<Type>&);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the number of elements in the channel.
 | 
			
		||||
     */
 | 
			
		||||
    NODISCARD inline size_type constexpr size() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns true if there are no elements in channel.
 | 
			
		||||
     */
 | 
			
		||||
    NODISCARD inline bool constexpr empty() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Closes the channel.
 | 
			
		||||
     */
 | 
			
		||||
    inline void close() noexcept;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns true if the channel is closed.
 | 
			
		||||
     */
 | 
			
		||||
    NODISCARD inline bool closed() const noexcept;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Iterator
 | 
			
		||||
     */
 | 
			
		||||
    iterator begin() noexcept;
 | 
			
		||||
    iterator end() noexcept;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Channel cannot be copied or moved.
 | 
			
		||||
     */
 | 
			
		||||
    channel(const channel&) = delete;
 | 
			
		||||
    channel& operator=(const channel&) = delete;
 | 
			
		||||
    channel(channel&&) = delete;
 | 
			
		||||
    channel& operator=(channel&&) = delete;
 | 
			
		||||
    virtual ~channel() = default;
 | 
			
		||||
 | 
			
		||||
   private:
 | 
			
		||||
    const size_type cap_;
 | 
			
		||||
    std::queue<T> queue_;
 | 
			
		||||
    std::mutex mtx_;
 | 
			
		||||
    std::condition_variable cnd_;
 | 
			
		||||
    std::atomic<bool> is_closed_{false};
 | 
			
		||||
 | 
			
		||||
    inline void waitBeforeRead(std::unique_lock<std::mutex>&);
 | 
			
		||||
    friend class blocking_iterator<channel>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#include "channel_impl.hpp"
 | 
			
		||||
 | 
			
		||||
}  // namespace msd
 | 
			
		||||
 | 
			
		||||
#endif  // MSD_CHANNEL_HPP_
 | 
			
		||||
							
								
								
									
										87
									
								
								backend/shl/msd/channel_impl.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								backend/shl/msd/channel_impl.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
			
		||||
// Copyright (C) 2022 Andrei Avram
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
constexpr channel<T>::channel(const size_type capacity) : cap_{capacity}
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
void operator>>(T&& in, channel<detail::remove_cvref_t<T>>& ch)
 | 
			
		||||
{
 | 
			
		||||
    if (ch.closed()) {
 | 
			
		||||
        throw closed_channel{"cannot write on closed channel"};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::unique_lock<std::mutex> lock{ch.mtx_};
 | 
			
		||||
 | 
			
		||||
    if (ch.cap_ > 0 && ch.queue_.size() == ch.cap_) {
 | 
			
		||||
        ch.cnd_.wait(lock, [&ch]() { return ch.queue_.size() < ch.cap_; });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ch.queue_.push(std::forward<T>(in));
 | 
			
		||||
 | 
			
		||||
    ch.cnd_.notify_one();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
void operator<<(T& out, channel<T>& ch)
 | 
			
		||||
{
 | 
			
		||||
    if (ch.closed() && ch.empty()) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_lock<std::mutex> lock{ch.mtx_};
 | 
			
		||||
        ch.waitBeforeRead(lock);
 | 
			
		||||
 | 
			
		||||
        if (ch.queue_.size() > 0) {
 | 
			
		||||
            out = std::move(ch.queue_.front());
 | 
			
		||||
            ch.queue_.pop();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ch.cnd_.notify_one();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
constexpr typename channel<T>::size_type channel<T>::size() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return queue_.size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
constexpr bool channel<T>::empty() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return queue_.empty();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
void channel<T>::close() noexcept
 | 
			
		||||
{
 | 
			
		||||
    is_closed_.store(true);
 | 
			
		||||
    cnd_.notify_all();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
bool channel<T>::closed() const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return is_closed_.load();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
blocking_iterator<channel<T>> channel<T>::begin() noexcept
 | 
			
		||||
{
 | 
			
		||||
    return blocking_iterator<channel<T>>{*this};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
blocking_iterator<channel<T>> channel<T>::end() noexcept
 | 
			
		||||
{
 | 
			
		||||
    return blocking_iterator<channel<T>>{*this};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
void channel<T>::waitBeforeRead(std::unique_lock<std::mutex>& lock)
 | 
			
		||||
{
 | 
			
		||||
    cnd_.wait(lock, [this] { return queue_.size() > 0 || closed(); });
 | 
			
		||||
}
 | 
			
		||||
@@ -55,14 +55,15 @@ namespace api {
 | 
			
		||||
 | 
			
		||||
    void admin::delete_user(req_type req, cbk_type cbk) {
 | 
			
		||||
        Json::Value& json = *req->jsonObject();
 | 
			
		||||
        msd::channel<std::string> chan;
 | 
			
		||||
        try {
 | 
			
		||||
            uint64_t user_id = dto::json_get<uint64_t>(json, "user").value();
 | 
			
		||||
 | 
			
		||||
            db::MapperUser user_mapper(drogon::app().getDbClient());
 | 
			
		||||
            auto user = user_mapper.findByPrimaryKey(user_id);
 | 
			
		||||
            auth::revoke_all(user);
 | 
			
		||||
             fs::delete_node(fs::get_node(user.getValueOfRootId()).value(), true);
 | 
			
		||||
             user_mapper.deleteOne(user);
 | 
			
		||||
            fs::delete_node(fs::get_node(user.getValueOfRootId()).value(), chan, true);
 | 
			
		||||
            user_mapper.deleteOne(user);
 | 
			
		||||
            cbk(dto::Responses::get_success_res());
 | 
			
		||||
        } catch (const std::exception&) {
 | 
			
		||||
            cbk(dto::Responses::get_badreq_res("Validation error"));
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@
 | 
			
		||||
#include <botan/base32.h>
 | 
			
		||||
#include <botan/base64.h>
 | 
			
		||||
#include <qrcodegen.hpp>
 | 
			
		||||
#include <lodepng.h>
 | 
			
		||||
#include <opencv2/opencv.hpp>
 | 
			
		||||
 | 
			
		||||
#include "controllers/controllers.h"
 | 
			
		||||
#include "db/db.h"
 | 
			
		||||
@@ -24,21 +24,14 @@ std::string create_totp_qrcode(const db::User& user, const std::string& b32_secr
 | 
			
		||||
    const int mod_count = code.getSize();
 | 
			
		||||
 | 
			
		||||
    const int row_size = qrcode_pixel_size * mod_count;
 | 
			
		||||
    std::vector<uint8_t> secret, image, row;
 | 
			
		||||
    row.reserve(row_size);
 | 
			
		||||
    image.reserve(row_size * row_size);
 | 
			
		||||
    cv::Mat image(mod_count, mod_count, CV_8UC1), scaled_image;
 | 
			
		||||
    std::vector<uint8_t> image_encoded;
 | 
			
		||||
    for (int y = 0; y < mod_count; y++) for (int x = 0; x < mod_count; x++)
 | 
			
		||||
        image.at<uint8_t>(x, y) = code.getModule(x, y) ? 0 : 0xff;
 | 
			
		||||
    cv::resize(image, scaled_image, cv::Size(), qrcode_pixel_size, qrcode_pixel_size, cv::INTER_NEAREST);
 | 
			
		||||
    cv::imencode(".png", scaled_image, image_encoded);
 | 
			
		||||
 | 
			
		||||
    for (int y = 0; y < mod_count; y++) {
 | 
			
		||||
        row.clear();
 | 
			
		||||
        for (int x = 0; x < mod_count; x++)
 | 
			
		||||
            row.insert(row.end(), qrcode_pixel_size, code.getModule(x, y) ? 0 : 0xff);
 | 
			
		||||
        for (int i = 0; i < qrcode_pixel_size; i++)
 | 
			
		||||
            image.insert(image.end(), row.begin(), row.end());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lodepng::encode(secret, image, row_size, row_size, LCT_GREY, 8);
 | 
			
		||||
 | 
			
		||||
    return "data:image/png;base64," + Botan::base64_encode(secret);
 | 
			
		||||
    return "data:image/png;base64," + Botan::base64_encode(image_encoded);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace api {
 | 
			
		||||
@@ -64,7 +57,7 @@ namespace api {
 | 
			
		||||
                std::string code = create_totp_qrcode(user, b32_secret);
 | 
			
		||||
                cbk(dto::Responses::get_tfa_setup_res(b32_secret, code));
 | 
			
		||||
            }
 | 
			
		||||
        } catch (const std::exception&) {
 | 
			
		||||
        } catch (const std::exception& e) {
 | 
			
		||||
            cbk(dto::Responses::get_badreq_res("Validation error"));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@
 | 
			
		||||
 | 
			
		||||
#include <jwt-cpp/traits/kazuho-picojson/traits.h>
 | 
			
		||||
#include <jwt-cpp/jwt.h>
 | 
			
		||||
#include <mailio/smtp.hpp>
 | 
			
		||||
#include <SMTPMail.h>
 | 
			
		||||
 | 
			
		||||
#include "controllers/controllers.h"
 | 
			
		||||
#include "db/db.h"
 | 
			
		||||
@@ -43,15 +43,17 @@ namespace api {
 | 
			
		||||
        char totp[16];
 | 
			
		||||
        std::snprintf(totp, 16, "%06d", Botan::TOTP(Botan::OctetString(totp_secret)).generate_totp(t));
 | 
			
		||||
 | 
			
		||||
        mailio::message msg;
 | 
			
		||||
        msg.from(mailio::mail_address("Fileserver", "fileserver@mattv.de"));
 | 
			
		||||
        msg.add_recipient(mailio::mail_address(user.getValueOfName(), user.getValueOfName()));
 | 
			
		||||
        msg.subject("Subject: Fileserver - Email 2fa code");
 | 
			
		||||
        msg.content("Your code is: " + std::string(totp) +"\r\nIt is valid for 5 Minutes");
 | 
			
		||||
 | 
			
		||||
        mailio::smtps conn("mail.mattv.de", 587);
 | 
			
		||||
        conn.authenticate("no-reply@mattv.de", "noreplyLONGPASS123", mailio::smtps::auth_method_t::START_TLS);
 | 
			
		||||
        conn.submit(msg);
 | 
			
		||||
        drogon::app().getPlugin<SMTPMail>()->sendEmail(
 | 
			
		||||
                "mail.mattv.de",
 | 
			
		||||
                587,
 | 
			
		||||
                "fileserver@mattv.de",
 | 
			
		||||
                user.getValueOfName(),
 | 
			
		||||
                "MFileserver - Email 2fa code",
 | 
			
		||||
                "Your code is: " + std::string(totp) +"\r\nIt is valid for 5 Minutes",
 | 
			
		||||
                "no-reply@mattv.de",
 | 
			
		||||
                "noreplyLONGPASS123",
 | 
			
		||||
                false
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string auth::get_token(const db::User& user) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
#ifndef BACKEND_CONTROLLERS_H
 | 
			
		||||
#define BACKEND_CONTROLLERS_H
 | 
			
		||||
#include <drogon/drogon.h>
 | 
			
		||||
#include <drogon/utils/coroutine.h>
 | 
			
		||||
#include <botan/rng.h>
 | 
			
		||||
#include <coroutine>
 | 
			
		||||
#include <variant>
 | 
			
		||||
 | 
			
		||||
#include <drogon/drogon.h>
 | 
			
		||||
#include <botan/rng.h>
 | 
			
		||||
#include <msd/channel.hpp>
 | 
			
		||||
 | 
			
		||||
#include "db/db.h"
 | 
			
		||||
 | 
			
		||||
using req_type = const drogon::HttpRequestPtr&;
 | 
			
		||||
@@ -86,14 +86,32 @@ public:
 | 
			
		||||
        METHOD_ADD(fs::create_node_req<true>, "/createFile", drogon::Post, "Login");
 | 
			
		||||
        METHOD_ADD(fs::delete_node_req, "/delete/{}", drogon::Post, "Login");
 | 
			
		||||
        METHOD_ADD(fs::upload, "/upload/{}", drogon::Post, "Login");
 | 
			
		||||
        METHOD_ADD(fs::create_zip, "/create_zip", drogon::Post, "Login");
 | 
			
		||||
        METHOD_ADD(fs::download, "/download", drogon::Post, "Login");
 | 
			
		||||
        METHOD_ADD(fs::download_multi, "/download_multi", drogon::Post, "Login");
 | 
			
		||||
        METHOD_ADD(fs::download_preview, "/download_preview/{}", drogon::Get, "Login");
 | 
			
		||||
        METHOD_ADD(fs::download_base64, "/download_base64/{}", drogon::Get, "Login");
 | 
			
		||||
        METHOD_ADD(fs::get_type, "/get_type/{}", drogon::Get, "Login");
 | 
			
		||||
    METHOD_LIST_END
 | 
			
		||||
 | 
			
		||||
    enum class create_node_error {
 | 
			
		||||
        INVALID_NAME,
 | 
			
		||||
        INVALID_PARENT,
 | 
			
		||||
        FILE_PARENT
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    struct mutex_stream {
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        std::mutex mutex;
 | 
			
		||||
        bool done = false;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    static std::optional<db::INode> get_node(uint64_t node);
 | 
			
		||||
    static std::optional<db::INode> get_node_and_validate(const db::User& user, uint64_t node);
 | 
			
		||||
    static std::vector<db::INode> get_children(const db::INode& parent);
 | 
			
		||||
    static std::variant<db::INode, std::string> create_node(std::string name, const db::User& owner, bool file, const std::optional<uint64_t> &parent, bool force = false);
 | 
			
		||||
    static void delete_node(db::INode node, bool allow_root = false);
 | 
			
		||||
    static std::variant<db::INode, fs::create_node_error, std::tuple<bool, uint64_t>>
 | 
			
		||||
        create_node(std::string name, const db::User& owner, bool file, const std::optional<uint64_t> &parent, bool force = false);
 | 
			
		||||
    static void delete_node(db::INode node, msd::channel<std::string>& chan, bool allow_root = false);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    void root(req_type, cbk_type);
 | 
			
		||||
@@ -102,7 +120,12 @@ public:
 | 
			
		||||
    template<bool file> void create_node_req(req_type req, cbk_type cbk);
 | 
			
		||||
    void delete_node_req(req_type, cbk_type, uint64_t node);
 | 
			
		||||
    void upload(req_type, cbk_type, uint64_t node);
 | 
			
		||||
    void create_zip(req_type, cbk_type);
 | 
			
		||||
    void download(req_type, cbk_type);
 | 
			
		||||
    void download_multi(req_type, cbk_type);
 | 
			
		||||
    void download_preview(req_type, cbk_type, uint64_t node);
 | 
			
		||||
    void download_base64(req_type, cbk_type, uint64_t node);
 | 
			
		||||
    void get_type(req_type, cbk_type, uint64_t node);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class user : public drogon::HttpController<user> {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,12 +3,100 @@
 | 
			
		||||
#pragma ide diagnostic ignored "readability-convert-member-functions-to-static"
 | 
			
		||||
 | 
			
		||||
#include <filesystem>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
 | 
			
		||||
#include <opencv2/opencv.hpp>
 | 
			
		||||
#include <botan/base64.h>
 | 
			
		||||
#include <trantor/net/EventLoopThread.h>
 | 
			
		||||
#include <zip/zip.h>
 | 
			
		||||
 | 
			
		||||
#include "controllers.h"
 | 
			
		||||
#include "dto/dto.h"
 | 
			
		||||
 | 
			
		||||
char windows_invalid_chars[] = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F<>:\"/\\|";
 | 
			
		||||
 | 
			
		||||
std::string generate_path(db::INode node) {
 | 
			
		||||
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types#common_image_file_types
 | 
			
		||||
const std::unordered_map<std::string, std::string> mime_type_map = {
 | 
			
		||||
        { ".apng" , "image/apng" },
 | 
			
		||||
        { ".avif" , "image/avif" },
 | 
			
		||||
        { ".bmp"  , "image/bmp"  },
 | 
			
		||||
        { ".gif"  , "image/gif"  },
 | 
			
		||||
        { ".jpg"  , "image/jpeg" },
 | 
			
		||||
        { ".jpeg" , "image/jpeg" },
 | 
			
		||||
        { ".jfif" , "image/jpeg" },
 | 
			
		||||
        { ".pjpeg", "image/jpeg" },
 | 
			
		||||
        { ".pjp"  , "image/jpeg" },
 | 
			
		||||
        { ".png"  , "image/png"  },
 | 
			
		||||
        { ".svg"  , "image/svg"  },
 | 
			
		||||
        { ".webp" , "image/webp" },
 | 
			
		||||
 | 
			
		||||
        { ".aac"  , "audio/aac"  },
 | 
			
		||||
        { ".flac" , "audio/flac" },
 | 
			
		||||
        { ".mp3"  , "audio/mp3"  },
 | 
			
		||||
        { ".m4a"  , "audio/mp4"  },
 | 
			
		||||
        { ".oga"  , "audio/ogg"  },
 | 
			
		||||
        { ".ogg"  , "audio/ogg"  },
 | 
			
		||||
        { ".wav"  , "audio/wav"  },
 | 
			
		||||
 | 
			
		||||
        { ".3gp"  , "video/3gpp" },
 | 
			
		||||
        { ".mpg"  , "video/mpeg" },
 | 
			
		||||
        { ".mpeg" , "video/mpeg" },
 | 
			
		||||
        { ".mp4"  , "video/mp4"  },
 | 
			
		||||
        { ".m4v"  , "video/mp4"  },
 | 
			
		||||
        { ".m4p"  , "video/mp4"  },
 | 
			
		||||
        { ".ogv"  , "video/ogg"  },
 | 
			
		||||
        { ".mov"  , "video/quicktime" },
 | 
			
		||||
        { ".webm" , "video/webm" },
 | 
			
		||||
        { ".mkv"  , "video/x-matroska" },
 | 
			
		||||
        { ".mk3d" , "video/x-matroska" },
 | 
			
		||||
        { ".mks"  , "video/x-matroska" },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
uint64_t next_temp_id = 0;
 | 
			
		||||
std::unordered_map<std::string, std::string> zip_to_temp_map;
 | 
			
		||||
std::unordered_map<std::string, std::tuple<std::string, uint64_t, uint64_t>> in_progress_zips;
 | 
			
		||||
 | 
			
		||||
trantor::EventLoop* get_zip_loop() {
 | 
			
		||||
    static bool init_done = false;
 | 
			
		||||
    static trantor::EventLoopThread loop("ZipEventLoop");
 | 
			
		||||
    if (!init_done) {
 | 
			
		||||
        init_done = true;
 | 
			
		||||
        loop.run();
 | 
			
		||||
        loop.getLoop()->runEvery(30*60, []{
 | 
			
		||||
            for (const auto& entry : std::filesystem::directory_iterator("./temp")) {
 | 
			
		||||
                if (!entry.is_regular_file()) continue;
 | 
			
		||||
                const std::string file_name = "./temp/" + entry.path().filename().string();
 | 
			
		||||
                const auto& progress_pos = std::find_if(in_progress_zips.begin(), in_progress_zips.end(),
 | 
			
		||||
                    [&file_name](const std::pair<std::string, std::tuple<std::string, uint64_t, uint64_t>>& entry) {
 | 
			
		||||
                        return std::get<0>(entry.second) == file_name;
 | 
			
		||||
                    }
 | 
			
		||||
                );
 | 
			
		||||
                if (progress_pos != in_progress_zips.end()) return;
 | 
			
		||||
                const auto& zip_map_pos = std::find_if(zip_to_temp_map.begin(), zip_to_temp_map.end(),
 | 
			
		||||
                    [&file_name](const std::pair<std::string, std::string>& entry){
 | 
			
		||||
                        return entry.second == file_name;
 | 
			
		||||
                    }
 | 
			
		||||
                );
 | 
			
		||||
                if (zip_map_pos != zip_to_temp_map.end()) return;
 | 
			
		||||
                std::filesystem::remove(entry.path());
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    return loop.getLoop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
trantor::EventLoop* get_delete_loop() {
 | 
			
		||||
    static bool init_done = false;
 | 
			
		||||
    static trantor::EventLoopThread loop("DeleteEventLoop");
 | 
			
		||||
    if (!init_done) {
 | 
			
		||||
        init_done = true;
 | 
			
		||||
        loop.run();
 | 
			
		||||
    }
 | 
			
		||||
    return loop.getLoop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void generate_path(db::INode node, std::string& str) {
 | 
			
		||||
    db::MapperInode inode_mapper(drogon::app().getDbClient());
 | 
			
		||||
    std::stack<db::INode> path;
 | 
			
		||||
    path.push(node);
 | 
			
		||||
@@ -16,14 +104,101 @@ std::string generate_path(db::INode node) {
 | 
			
		||||
        node = inode_mapper.findByPrimaryKey(node.getValueOfParentId());
 | 
			
		||||
        path.push(node);
 | 
			
		||||
    }
 | 
			
		||||
    std::stringstream ss;
 | 
			
		||||
    while (!path.empty()) {
 | 
			
		||||
        const db::INode& seg = path.top();
 | 
			
		||||
        ss << seg.getValueOfName();
 | 
			
		||||
        if (seg.getValueOfIsFile() == 0) ss << '/';
 | 
			
		||||
        str += seg.getValueOfName();
 | 
			
		||||
        if (seg.getValueOfIsFile() == 0) str += "/";
 | 
			
		||||
        path.pop();
 | 
			
		||||
    }
 | 
			
		||||
    return ss.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Json::Value generate_path(db::INode node) {
 | 
			
		||||
    Json::Value segments = Json::Value(Json::ValueType::arrayValue);
 | 
			
		||||
    db::MapperInode inode_mapper(drogon::app().getDbClient());
 | 
			
		||||
    std::stack<db::INode> path;
 | 
			
		||||
    path.push(node);
 | 
			
		||||
    while (node.getParentId() != nullptr) {
 | 
			
		||||
        node = inode_mapper.findByPrimaryKey(node.getValueOfParentId());
 | 
			
		||||
        path.push(node);
 | 
			
		||||
    }
 | 
			
		||||
    while (!path.empty()) {
 | 
			
		||||
        const db::INode& seg = path.top();
 | 
			
		||||
        if (seg.getParentId() == nullptr) {
 | 
			
		||||
            Json::Value json_seg;
 | 
			
		||||
            json_seg["path"] = "/";
 | 
			
		||||
            json_seg["node"] = seg.getValueOfId();
 | 
			
		||||
            segments.append(json_seg);
 | 
			
		||||
        } else {
 | 
			
		||||
            Json::Value json_seg;
 | 
			
		||||
            json_seg["path"] = seg.getValueOfName();
 | 
			
		||||
            json_seg["node"] = seg.getValueOfId();
 | 
			
		||||
            segments.append(json_seg);
 | 
			
		||||
            if (seg.getValueOfIsFile() == 0) {
 | 
			
		||||
                json_seg.removeMember("node");
 | 
			
		||||
                json_seg["path"] = "/";
 | 
			
		||||
                segments.append(json_seg);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        path.pop();
 | 
			
		||||
    }
 | 
			
		||||
    Json::Value resp;
 | 
			
		||||
    resp["segments"] = segments;
 | 
			
		||||
    return resp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t calc_total_size(const db::INode& base) {
 | 
			
		||||
    uint64_t size = 0;
 | 
			
		||||
    std::stack<db::INode> queue;
 | 
			
		||||
    queue.push(base);
 | 
			
		||||
    while (!queue.empty()) {
 | 
			
		||||
        const db::INode& node = queue.top();
 | 
			
		||||
        if (node.getValueOfIsFile() == 0) {
 | 
			
		||||
            auto children = api::fs::get_children(node);
 | 
			
		||||
            queue.pop();
 | 
			
		||||
            for (const auto& child : children) {
 | 
			
		||||
                if (child.getValueOfIsFile() == 0) queue.push(child);
 | 
			
		||||
                else if (child.getSize()) size += child.getValueOfSize();
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            size += node.getValueOfSize();
 | 
			
		||||
            queue.pop();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void add_to_zip(struct zip_t* zip, const std::string& key, const db::INode& node, const std::string& path) {
 | 
			
		||||
    if (node.getValueOfIsFile() == 0) {
 | 
			
		||||
        std::string new_path = path + node.getValueOfName() + "/";
 | 
			
		||||
        zip_entry_opencasesensitive(zip, new_path.c_str());
 | 
			
		||||
        zip_entry_close(zip);
 | 
			
		||||
        auto children = api::fs::get_children(node);
 | 
			
		||||
        for (const auto& child : children)
 | 
			
		||||
            add_to_zip(zip, key, child, new_path);
 | 
			
		||||
    } else {
 | 
			
		||||
        zip_entry_opencasesensitive(zip, (path + node.getValueOfName()).c_str());
 | 
			
		||||
        std::ifstream file("./files/" + std::to_string(node.getValueOfId()), std::ifstream::binary);
 | 
			
		||||
        std::vector<char> buffer(64*1024);
 | 
			
		||||
        while (!file.eof()) {
 | 
			
		||||
            file.read(buffer.data(), (std::streamsize)buffer.size());
 | 
			
		||||
            auto read = file.gcount();
 | 
			
		||||
            zip_entry_write(zip, buffer.data(), read);
 | 
			
		||||
            std::get<1>(in_progress_zips[key]) += read;
 | 
			
		||||
        }
 | 
			
		||||
        zip_entry_close(zip);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<typename InputIt>
 | 
			
		||||
std::string join_string(InputIt first, InputIt last, const std::string& separator = ",") {
 | 
			
		||||
    std::ostringstream result;
 | 
			
		||||
    if (first != last) {
 | 
			
		||||
        result << *first;
 | 
			
		||||
        while (++first != last) {
 | 
			
		||||
            result << separator << *first;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return result.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace api {
 | 
			
		||||
@@ -48,26 +223,31 @@ namespace api {
 | 
			
		||||
        return inode_mapper.findBy(db::Criteria(db::INode::Cols::_parent_id, db::CompareOps::EQ, parent.getValueOfId()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::variant<db::INode, std::string> fs::create_node(std::string name, const db::User& owner, bool file, const std::optional<uint64_t> &parent, bool force) {
 | 
			
		||||
    std::variant<db::INode, fs::create_node_error, std::tuple<bool, uint64_t>>
 | 
			
		||||
    fs::create_node(std::string name, const db::User& owner, bool file, const std::optional<uint64_t> &parent, bool force) {
 | 
			
		||||
        // Stolen from https://github.com/boostorg/filesystem/blob/develop/src/portability.cpp
 | 
			
		||||
        if (!force)
 | 
			
		||||
            if (name.empty() || name[0] == ' ' || name.find_first_of(windows_invalid_chars, 0, sizeof(windows_invalid_chars)) != std::string::npos || *(name.end() - 1) == ' ' || *(name.end() - 1) == '.' || name == "." || name == "..")
 | 
			
		||||
                return {"Invalid name"};
 | 
			
		||||
                return {create_node_error::INVALID_NAME};
 | 
			
		||||
 | 
			
		||||
        db::INode node;
 | 
			
		||||
        node.setIsFile(file ? 1 : 0);
 | 
			
		||||
        node.setName(name);
 | 
			
		||||
        node.setOwnerId(owner.getValueOfId());
 | 
			
		||||
        node.setHasPreview(0);
 | 
			
		||||
        if (parent.has_value()) {
 | 
			
		||||
            auto parent_node =  get_node_and_validate(owner, *parent);
 | 
			
		||||
            if (!parent_node.has_value())
 | 
			
		||||
                return {"Invalid parent"};
 | 
			
		||||
                return {create_node_error::INVALID_PARENT};
 | 
			
		||||
            if (parent_node->getValueOfIsFile() != 0)
 | 
			
		||||
                return {"Can't use file as parent"};
 | 
			
		||||
                return {create_node_error::FILE_PARENT};
 | 
			
		||||
            auto children = get_children(*parent_node);
 | 
			
		||||
            for (const auto& child : children)
 | 
			
		||||
                if (child.getValueOfName() == name)
 | 
			
		||||
                    return {"File/Folder already exists"};
 | 
			
		||||
                    return {std::make_tuple(
 | 
			
		||||
                                child.getValueOfIsFile() != 0,
 | 
			
		||||
                                child.getValueOfId()
 | 
			
		||||
                            )};
 | 
			
		||||
            node.setParentId(*parent);
 | 
			
		||||
        }
 | 
			
		||||
        db::MapperInode inode_mapper(drogon::app().getDbClient());
 | 
			
		||||
@@ -75,18 +255,56 @@ namespace api {
 | 
			
		||||
        return {node};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void fs::delete_node(db::INode node, bool allow_root) {
 | 
			
		||||
    void fs::delete_node(db::INode node, msd::channel<std::string>& chan, bool allow_root) {
 | 
			
		||||
        if (node.getValueOfParentId() == 0 && (!allow_root)) return;
 | 
			
		||||
        if (node.getValueOfIsFile() == 0) {
 | 
			
		||||
            auto children =  get_children(node);
 | 
			
		||||
            for (const auto& child : children) delete_node(child, false);
 | 
			
		||||
        } else {
 | 
			
		||||
 | 
			
		||||
        db::MapperInode inode_mapper(drogon::app().getDbClient());
 | 
			
		||||
 | 
			
		||||
        const auto delete_file = [&chan, &inode_mapper](const db::INode& node) {
 | 
			
		||||
            std::string entry = "Deleting ";
 | 
			
		||||
            generate_path(node, entry);
 | 
			
		||||
            entry >> chan;
 | 
			
		||||
            std::filesystem::path p("./files");
 | 
			
		||||
            p /= std::to_string(node.getValueOfId());
 | 
			
		||||
            std::filesystem::remove(p);
 | 
			
		||||
            if (node.getValueOfHasPreview() != 0)
 | 
			
		||||
                std::filesystem::remove(p.string() + "_preview.png");
 | 
			
		||||
            inode_mapper.deleteOne(node);
 | 
			
		||||
            std::string(" Done\n") >> chan;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        std::stack<db::INode> queue, files, folders;
 | 
			
		||||
 | 
			
		||||
        if (node.getValueOfIsFile() == 0) queue.push(node);
 | 
			
		||||
        else files.push(node);
 | 
			
		||||
 | 
			
		||||
        while (!queue.empty()) {
 | 
			
		||||
            while (!files.empty()) {
 | 
			
		||||
                delete_file(files.top());
 | 
			
		||||
                files.pop();
 | 
			
		||||
            }
 | 
			
		||||
            std::string entry = "Deleting ";
 | 
			
		||||
            generate_path(queue.top(), entry);
 | 
			
		||||
            entry += "\n";
 | 
			
		||||
            entry >> chan;
 | 
			
		||||
            auto children = get_children(queue.top());
 | 
			
		||||
            folders.push(queue.top());
 | 
			
		||||
            queue.pop();
 | 
			
		||||
            for (const auto& child : children) {
 | 
			
		||||
                if (child.getValueOfIsFile() == 0) queue.push(child);
 | 
			
		||||
                else files.push(child);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        while (!files.empty()) {
 | 
			
		||||
            delete_file(files.top());
 | 
			
		||||
            files.pop();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        while (!folders.empty()) {
 | 
			
		||||
            inode_mapper.deleteOne(folders.top());
 | 
			
		||||
            folders.pop();
 | 
			
		||||
        }
 | 
			
		||||
        db::MapperInode inode_mapper(drogon::app().getDbClient());
 | 
			
		||||
        inode_mapper.deleteOne(node);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void fs::root(req_type req, cbk_type cbk) {
 | 
			
		||||
@@ -98,23 +316,11 @@ namespace api {
 | 
			
		||||
        db::User user = dto::get_user(req);
 | 
			
		||||
        auto inode =  get_node_and_validate(user, node);
 | 
			
		||||
        if (!inode.has_value())
 | 
			
		||||
            cbk(dto::Responses::get_badreq_res("Unknown node"));
 | 
			
		||||
        else if (inode->getValueOfIsFile() == 0) {
 | 
			
		||||
            std::vector<uint64_t> children;
 | 
			
		||||
            for (const db::INode& child : get_children(*inode)) children.push_back(child.getValueOfId());
 | 
			
		||||
            cbk(dto::Responses::get_node_folder_res(
 | 
			
		||||
                    inode->getValueOfId(),
 | 
			
		||||
                    inode->getValueOfName(),
 | 
			
		||||
                    inode->getParentId(),
 | 
			
		||||
                    children
 | 
			
		||||
            ));
 | 
			
		||||
        } else
 | 
			
		||||
            cbk(dto::Responses::get_node_file_res(
 | 
			
		||||
                    inode->getValueOfId(),
 | 
			
		||||
                    inode->getValueOfName(),
 | 
			
		||||
                    inode->getParentId(),
 | 
			
		||||
                    inode->getValueOfSize()
 | 
			
		||||
            ));
 | 
			
		||||
            return cbk(dto::Responses::get_badreq_res("Unknown node"));
 | 
			
		||||
        auto dto_node = dto::Responses::GetNodeEntry(*inode);
 | 
			
		||||
        std::vector<dto::Responses::GetNodeEntry> children;
 | 
			
		||||
        if (!dto_node.is_file) for (const db::INode& child : get_children(*inode)) children.emplace_back(child);
 | 
			
		||||
        cbk(dto::Responses::get_node_res(dto_node, children));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void fs::path(req_type req, cbk_type cbk, uint64_t node) {
 | 
			
		||||
@@ -122,8 +328,10 @@ namespace api {
 | 
			
		||||
        auto inode = get_node_and_validate(user, node);
 | 
			
		||||
        if (!inode.has_value())
 | 
			
		||||
            cbk(dto::Responses::get_badreq_res("Unknown node"));
 | 
			
		||||
        else
 | 
			
		||||
            cbk(dto::Responses::get_path_res( generate_path(*inode)));
 | 
			
		||||
        else {
 | 
			
		||||
            auto path = generate_path(*inode);
 | 
			
		||||
            cbk(dto::Responses::get_success_res(path));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    template<bool file>
 | 
			
		||||
@@ -135,10 +343,18 @@ namespace api {
 | 
			
		||||
            std::string name = dto::json_get<std::string>(json, "name").value();
 | 
			
		||||
 | 
			
		||||
            auto new_node = create_node(name, user, file, std::make_optional(parent));
 | 
			
		||||
            if (std::holds_alternative<std::string>(new_node))
 | 
			
		||||
                cbk(dto::Responses::get_badreq_res(std::get<std::string>(new_node)));
 | 
			
		||||
            else
 | 
			
		||||
            if (std::holds_alternative<db::INode>(new_node))
 | 
			
		||||
                cbk(dto::Responses::get_new_node_res(std::get<db::INode>(new_node).getValueOfId()));
 | 
			
		||||
            else if (std::holds_alternative<create_node_error>(new_node))
 | 
			
		||||
                switch (std::get<create_node_error>(new_node)) {
 | 
			
		||||
                case create_node_error::INVALID_NAME: return cbk(dto::Responses::get_badreq_res("Invalid name"));
 | 
			
		||||
                case create_node_error::INVALID_PARENT: return cbk(dto::Responses::get_badreq_res("Invalid parent"));
 | 
			
		||||
                case create_node_error::FILE_PARENT: return cbk(dto::Responses::get_badreq_res("Parent is file"));
 | 
			
		||||
                }
 | 
			
		||||
            else {
 | 
			
		||||
                auto tuple = std::get<std::tuple<bool, uint64_t>>(new_node);
 | 
			
		||||
                cbk(dto::Responses::get_node_exists_res(std::get<1>(tuple), std::get<0>(tuple)));
 | 
			
		||||
            }
 | 
			
		||||
        } catch (const std::exception&) {
 | 
			
		||||
            cbk(dto::Responses::get_badreq_res("Validation error"));
 | 
			
		||||
        }
 | 
			
		||||
@@ -152,12 +368,27 @@ namespace api {
 | 
			
		||||
        else if (inode->getValueOfParentId() == 0)
 | 
			
		||||
            cbk(dto::Responses::get_badreq_res("Can't delete root"));
 | 
			
		||||
        else {
 | 
			
		||||
             delete_node(*inode);
 | 
			
		||||
            cbk(dto::Responses::get_success_res());
 | 
			
		||||
            auto chan = std::make_shared<msd::channel<std::string>>();
 | 
			
		||||
            std::string("Waiting in queue...\n") >> (*chan);
 | 
			
		||||
            get_delete_loop()->queueInLoop([chan, inode=*inode]{
 | 
			
		||||
                delete_node(inode, *chan);
 | 
			
		||||
                chan->close();
 | 
			
		||||
            });
 | 
			
		||||
            cbk(drogon::HttpResponse::newStreamResponse([chan](char* buf, std::size_t size) -> std::size_t{
 | 
			
		||||
                if (buf == nullptr) return 0;
 | 
			
		||||
                if (chan->closed() && chan->empty()) return 0;
 | 
			
		||||
                std::string buffer;
 | 
			
		||||
                buffer << *chan;
 | 
			
		||||
                if (buffer.empty()) return 0;
 | 
			
		||||
                std::size_t read = std::min(size, buffer.size());
 | 
			
		||||
                std::memcpy(buf, buffer.data(), read); // NOLINT(bugprone-not-null-terminated-result)
 | 
			
		||||
                return read;
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void fs::upload(req_type req, cbk_type cbk, uint64_t node) {
 | 
			
		||||
        constexpr int image_height = 256;
 | 
			
		||||
        db::User user = dto::get_user(req);
 | 
			
		||||
 | 
			
		||||
        auto inode = get_node_and_validate(user, node);
 | 
			
		||||
@@ -178,13 +409,73 @@ namespace api {
 | 
			
		||||
        p /= std::to_string(inode->getValueOfId());
 | 
			
		||||
 | 
			
		||||
        file.saveAs(p.string());
 | 
			
		||||
        try {
 | 
			
		||||
            if (file.fileLength() > 100 * 1024 * 1024) throw std::exception();
 | 
			
		||||
            std::filesystem::path filename(inode->getValueOfName());
 | 
			
		||||
            const std::string& mime = mime_type_map.at(filename.extension().string());
 | 
			
		||||
            if (!mime.starts_with("image")) throw std::exception();
 | 
			
		||||
 | 
			
		||||
            cv::_InputArray image_arr(file.fileData(), (int) file.fileLength());
 | 
			
		||||
            cv::Mat image = cv::imdecode(image_arr, cv::IMREAD_COLOR);
 | 
			
		||||
            if (!image.empty()) {
 | 
			
		||||
                float h_ration = ((float) image_height) / ((float) image.rows);
 | 
			
		||||
                cv::Mat preview;
 | 
			
		||||
                cv::resize(image, preview, cv::Size((int) (((float) image.cols) * h_ration), image_height), 0, 0, cv::INTER_AREA);
 | 
			
		||||
                cv::imwrite(p.string() + "_preview.png", preview);
 | 
			
		||||
                inode->setHasPreview(1);
 | 
			
		||||
            }
 | 
			
		||||
        } catch (const std::exception&) {}
 | 
			
		||||
        inode->setSize(file.fileLength());
 | 
			
		||||
 | 
			
		||||
        db::MapperInode inode_mapper(drogon::app().getDbClient());
 | 
			
		||||
        inode_mapper.update(*inode);
 | 
			
		||||
 | 
			
		||||
        cbk(dto::Responses::get_success_res());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void fs::create_zip(req_type req, cbk_type cbk) {
 | 
			
		||||
        db::User user = dto::get_user(req);
 | 
			
		||||
        Json::Value& json = *req->jsonObject();
 | 
			
		||||
        try {
 | 
			
		||||
            if (!json.isMember("nodes")) throw std::exception();
 | 
			
		||||
            Json::Value node_arr = json["nodes"];
 | 
			
		||||
            if (!node_arr.isArray()) throw std::exception();
 | 
			
		||||
            std::vector<uint64_t> node_ids;
 | 
			
		||||
            for (const auto& node : node_arr)
 | 
			
		||||
                node_ids.push_back(node.asUInt64());
 | 
			
		||||
 | 
			
		||||
            std::vector<db::INode> nodes;
 | 
			
		||||
            std::transform(node_ids.begin(), node_ids.end(), std::back_inserter(nodes), [&user](uint64_t node) {
 | 
			
		||||
                return api::fs::get_node_and_validate(user, node).value();
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            std::string key = join_string(node_ids.begin(), node_ids.end());
 | 
			
		||||
 | 
			
		||||
            if (zip_to_temp_map.contains(key)) return cbk(dto::Responses::get_create_zip_done_res());
 | 
			
		||||
            if (in_progress_zips.contains(key)) {
 | 
			
		||||
                auto progress = in_progress_zips.at(key);
 | 
			
		||||
                return cbk(dto::Responses::get_create_zip_done_res(std::get<1>(progress), std::get<2>(progress)));
 | 
			
		||||
            }
 | 
			
		||||
            uint64_t size = 0;
 | 
			
		||||
            for (const auto& node : nodes) size += calc_total_size(node);
 | 
			
		||||
            std::string file_name = "./temp/fs_" + std::to_string(next_temp_id++) + ".zip";
 | 
			
		||||
            in_progress_zips.emplace(key, std::make_tuple(file_name, 0, size));
 | 
			
		||||
            get_zip_loop()->queueInLoop([key = std::move(key), nodes = std::move(nodes), file_name = std::move(file_name)]{
 | 
			
		||||
                {
 | 
			
		||||
                    struct zip_t* zip = zip_open(file_name.c_str(), ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
 | 
			
		||||
                    for (const db::INode& node : nodes)
 | 
			
		||||
                        add_to_zip(zip, key, node, "");
 | 
			
		||||
                    zip_close(zip);
 | 
			
		||||
                }
 | 
			
		||||
                zip_to_temp_map.emplace(key, file_name);
 | 
			
		||||
                in_progress_zips.erase(key);
 | 
			
		||||
            });
 | 
			
		||||
            return cbk(dto::Responses::get_create_zip_done_res(0, size));
 | 
			
		||||
        } catch (const std::exception&) {
 | 
			
		||||
            cbk(dto::Responses::get_badreq_res("Validation error"));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void fs::download(req_type req, cbk_type cbk) {
 | 
			
		||||
        db::User user = dto::get_user(req);
 | 
			
		||||
 | 
			
		||||
@@ -193,19 +484,114 @@ namespace api {
 | 
			
		||||
            cbk(dto::Responses::get_badreq_res("Invalid node"));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        auto inode =  get_node_and_validate(user, *node_id);
 | 
			
		||||
        auto inode = get_node_and_validate(user, *node_id);
 | 
			
		||||
        if (!inode.has_value()) {
 | 
			
		||||
            cbk(dto::Responses::get_badreq_res("Invalid node"));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (inode->getValueOfIsFile() != 0) {
 | 
			
		||||
            std::filesystem::path p("./files");
 | 
			
		||||
            p /= std::to_string(inode->getValueOfId());
 | 
			
		||||
 | 
			
		||||
            cbk(drogon::HttpResponse::newFileResponse(
 | 
			
		||||
                    p.string(),
 | 
			
		||||
                    inode->getValueOfName()
 | 
			
		||||
            ));
 | 
			
		||||
        } else {
 | 
			
		||||
            try {
 | 
			
		||||
                std::string key = std::to_string(inode->getValueOfId());
 | 
			
		||||
                std::string file = zip_to_temp_map.at(key);
 | 
			
		||||
                zip_to_temp_map.erase(key);
 | 
			
		||||
                cbk(drogon::HttpResponse::newFileResponse(
 | 
			
		||||
                        file,
 | 
			
		||||
                        inode->getValueOfName() + ".zip"
 | 
			
		||||
                ));
 | 
			
		||||
            } catch (const std::exception&) {
 | 
			
		||||
                cbk(dto::Responses::get_badreq_res("Invalid node"));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void fs::download_multi(req_type req, cbk_type cbk) {
 | 
			
		||||
        db::User user = dto::get_user(req);
 | 
			
		||||
 | 
			
		||||
        auto node_ids_str = req->getOptionalParameter<std::string>("id");
 | 
			
		||||
        if (!node_ids_str.has_value())
 | 
			
		||||
            return cbk(dto::Responses::get_badreq_res("No nodes"));
 | 
			
		||||
 | 
			
		||||
        std::stringstream node_ids_ss(*node_ids_str);
 | 
			
		||||
        std::string temp;
 | 
			
		||||
        try {
 | 
			
		||||
            while (std::getline(node_ids_ss, temp, ','))
 | 
			
		||||
                if (!get_node_and_validate(user, std::stoull(temp)).has_value()) throw std::exception();
 | 
			
		||||
 | 
			
		||||
            std::string file = zip_to_temp_map.at(*node_ids_str);
 | 
			
		||||
            zip_to_temp_map.erase(*node_ids_str);
 | 
			
		||||
            cbk(drogon::HttpResponse::newFileResponse(
 | 
			
		||||
                    file,
 | 
			
		||||
                    "files.zip"
 | 
			
		||||
            ));
 | 
			
		||||
        } catch (const std::exception&) {
 | 
			
		||||
            cbk(dto::Responses::get_badreq_res("Invalid nodes"));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void fs::download_preview(req_type req, cbk_type cbk, uint64_t node) {
 | 
			
		||||
        db::User user = dto::get_user(req);
 | 
			
		||||
 | 
			
		||||
        auto inode = get_node_and_validate(user, node);
 | 
			
		||||
        if (!inode.has_value())
 | 
			
		||||
            return cbk(dto::Responses::get_badreq_res("Unknown node"));
 | 
			
		||||
        if (inode->getValueOfHasPreview() == 0)
 | 
			
		||||
            return cbk(dto::Responses::get_badreq_res("No preview"));
 | 
			
		||||
 | 
			
		||||
        std::filesystem::path p("./files");
 | 
			
		||||
        p /= std::to_string(inode->getValueOfId()) + "_preview.png";
 | 
			
		||||
        std::ifstream file(p, std::ios::in | std::ios::binary);
 | 
			
		||||
        std::vector<uint8_t> image((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
 | 
			
		||||
 | 
			
		||||
        cbk(dto::Responses::get_download_base64_res("data:image/png;base64," + Botan::base64_encode(image)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void fs::download_base64(req_type req, cbk_type cbk, uint64_t node) {
 | 
			
		||||
        db::User user = dto::get_user(req);
 | 
			
		||||
 | 
			
		||||
        auto inode = get_node_and_validate(user, node);
 | 
			
		||||
        if (!inode.has_value())
 | 
			
		||||
            return cbk(dto::Responses::get_badreq_res("Unknown node"));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        std::filesystem::path p("./files"), name(inode->getValueOfName());
 | 
			
		||||
        p /= std::to_string(inode->getValueOfId());
 | 
			
		||||
 | 
			
		||||
        cbk(drogon::HttpResponse::newFileResponse(
 | 
			
		||||
            p.string(),
 | 
			
		||||
            inode->getValueOfName()
 | 
			
		||||
        ));
 | 
			
		||||
        try {
 | 
			
		||||
            std::string mime = mime_type_map.at(name.extension().string());
 | 
			
		||||
            std::ifstream file(p, std::ios::in | std::ios::binary);
 | 
			
		||||
            std::vector<uint8_t> content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
 | 
			
		||||
 | 
			
		||||
            cbk(dto::Responses::get_download_base64_res("data:" + mime + ";base64," + Botan::base64_encode(content)));
 | 
			
		||||
        } catch (const std::exception&) {
 | 
			
		||||
            cbk(dto::Responses::get_badreq_res("Invalid file type"));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void fs::get_type(req_type req, cbk_type cbk, uint64_t node){
 | 
			
		||||
        db::User user = dto::get_user(req);
 | 
			
		||||
 | 
			
		||||
        auto inode = get_node_and_validate(user, node);
 | 
			
		||||
        if (!inode.has_value())
 | 
			
		||||
            return cbk(dto::Responses::get_badreq_res("Unknown node"));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        std::filesystem::path p("./files"), name(inode->getValueOfName());
 | 
			
		||||
        p /= std::to_string(inode->getValueOfId());
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            cbk(dto::Responses::get_type_res(mime_type_map.at(name.extension().string())));
 | 
			
		||||
        } catch (const std::exception&) {
 | 
			
		||||
            cbk(dto::Responses::get_badreq_res("Invalid file type"));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#pragma clang diagnostic pop
 | 
			
		||||
@@ -18,9 +18,10 @@ namespace api {
 | 
			
		||||
    void user::delete_user(req_type req, cbk_type cbk) {
 | 
			
		||||
        db::MapperUser user_mapper(drogon::app().getDbClient());
 | 
			
		||||
 | 
			
		||||
        msd::channel<std::string> chan;
 | 
			
		||||
        db::User user = dto::get_user(req);
 | 
			
		||||
        auth::revoke_all(user);
 | 
			
		||||
        fs::delete_node((fs::get_node(user.getValueOfRootId())).value(), true);
 | 
			
		||||
        fs::delete_node((fs::get_node(user.getValueOfRootId())).value(), chan, true);
 | 
			
		||||
        user_mapper.deleteOne(user);
 | 
			
		||||
 | 
			
		||||
        cbk(dto::Responses::get_success_res());
 | 
			
		||||
 
 | 
			
		||||
@@ -6,9 +6,9 @@
 | 
			
		||||
#include <drogon/utils/coroutine.h>
 | 
			
		||||
#include <drogon/drogon.h>
 | 
			
		||||
 | 
			
		||||
#include "model/Inode.h"
 | 
			
		||||
#include "model/Tokens.h"
 | 
			
		||||
#include "model/User.h"
 | 
			
		||||
#include "Inode.h"
 | 
			
		||||
#include "Tokens.h"
 | 
			
		||||
#include "User.h"
 | 
			
		||||
 | 
			
		||||
const std::string jwt_secret = "CUM";
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,16 @@ namespace dto {
 | 
			
		||||
            db::UserRole role;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        struct GetNodeEntry {
 | 
			
		||||
            explicit GetNodeEntry(const db::INode& node) : id(node.getValueOfId()), name(node.getValueOfName()), is_file(node.getValueOfIsFile() != 0), has_preview(node.getValueOfHasPreview() != 0), parent(node.getParentId()) {
 | 
			
		||||
                if (node.getValueOfIsFile() != 0) size = node.getValueOfSize();
 | 
			
		||||
            }
 | 
			
		||||
            uint64_t id, size;
 | 
			
		||||
            std::string name;
 | 
			
		||||
            bool is_file, has_preview;
 | 
			
		||||
            std::shared_ptr<uint64_t> parent;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        drogon::HttpResponsePtr get_error_res(drogon::HttpStatusCode, const std::string &msg);
 | 
			
		||||
        drogon::HttpResponsePtr get_success_res();
 | 
			
		||||
        drogon::HttpResponsePtr get_success_res(Json::Value &);
 | 
			
		||||
@@ -46,10 +56,13 @@ namespace dto {
 | 
			
		||||
        drogon::HttpResponsePtr get_admin_users_res(const std::vector<GetUsersEntry>& users);
 | 
			
		||||
 | 
			
		||||
        drogon::HttpResponsePtr get_root_res(uint64_t root);
 | 
			
		||||
        drogon::HttpResponsePtr get_node_folder_res(uint64_t id, const std::string& name, const std::shared_ptr<uint64_t>& parent, const std::vector<uint64_t>& children);
 | 
			
		||||
        drogon::HttpResponsePtr get_node_file_res(uint64_t id, const std::string& name, const std::shared_ptr<uint64_t>& parent, uint64_t size);
 | 
			
		||||
        drogon::HttpResponsePtr get_path_res(const std::string& path);
 | 
			
		||||
        drogon::HttpResponsePtr get_node_res(const GetNodeEntry& node, const std::vector<GetNodeEntry>& children);
 | 
			
		||||
        drogon::HttpResponsePtr get_new_node_res(uint64_t id);
 | 
			
		||||
        drogon::HttpResponsePtr get_node_exists_res(uint64_t id, bool file);
 | 
			
		||||
        drogon::HttpResponsePtr get_download_base64_res(const std::string& data);
 | 
			
		||||
        drogon::HttpResponsePtr get_type_res(const std::string& type);
 | 
			
		||||
        drogon::HttpResponsePtr get_create_zip_done_res();
 | 
			
		||||
        drogon::HttpResponsePtr get_create_zip_done_res(uint64_t progress, uint64_t total);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -63,30 +63,24 @@ namespace dto::Responses {
 | 
			
		||||
        return get_success_res(json);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    drogon::HttpResponsePtr get_node_folder_res(uint64_t id, const std::string &name, const std::shared_ptr<uint64_t> &parent, const std::vector<uint64_t> &children) {
 | 
			
		||||
    Json::Value parse_node(const GetNodeEntry& node) {
 | 
			
		||||
        Json::Value json;
 | 
			
		||||
        json["id"] = id;
 | 
			
		||||
        json["name"] = name;
 | 
			
		||||
        json["isFile"] = false;
 | 
			
		||||
        json["parent"] = (parent != nullptr) ? *parent : Json::Value::nullSingleton();
 | 
			
		||||
        for (uint64_t child : children)
 | 
			
		||||
            json["children"].append(child);
 | 
			
		||||
        return get_success_res(json);
 | 
			
		||||
        json["id"] = node.id;
 | 
			
		||||
        json["name"] = node.name;
 | 
			
		||||
        json["isFile"] = node.is_file;
 | 
			
		||||
        json["preview"] = node.has_preview;
 | 
			
		||||
        json["parent"] = (node.parent != nullptr) ? *node.parent : Json::Value::nullSingleton();
 | 
			
		||||
        if (node.is_file) json["size"] = node.size;
 | 
			
		||||
        return json;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    drogon::HttpResponsePtr get_node_file_res(uint64_t id, const std::string &name, const std::shared_ptr<uint64_t> &parent, uint64_t size) {
 | 
			
		||||
        Json::Value json;
 | 
			
		||||
        json["id"] = id;
 | 
			
		||||
        json["name"] = name;
 | 
			
		||||
        json["isFile"] = true;
 | 
			
		||||
        json["parent"] = (parent != nullptr) ? *parent : Json::Value::nullSingleton();
 | 
			
		||||
        json["size"] = size;
 | 
			
		||||
        return get_success_res(json);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    drogon::HttpResponsePtr get_path_res(const std::string& path) {
 | 
			
		||||
        Json::Value json;
 | 
			
		||||
        json["path"] = path;
 | 
			
		||||
    drogon::HttpResponsePtr get_node_res(const GetNodeEntry& node, const std::vector<GetNodeEntry>& children) {
 | 
			
		||||
        Json::Value json = parse_node(node);
 | 
			
		||||
        if (!node.is_file) {
 | 
			
		||||
            json["children"] = Json::Value(Json::arrayValue);
 | 
			
		||||
            for (const GetNodeEntry& child : children)
 | 
			
		||||
                json["children"].append(parse_node(child));
 | 
			
		||||
        }
 | 
			
		||||
        return get_success_res(json);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -95,4 +89,38 @@ namespace dto::Responses {
 | 
			
		||||
        json["id"] = id;
 | 
			
		||||
        return get_success_res(json);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    drogon::HttpResponsePtr get_node_exists_res(uint64_t id, bool file) {
 | 
			
		||||
        Json::Value json;
 | 
			
		||||
        json["id"] = id;
 | 
			
		||||
        json["exists"] = true;
 | 
			
		||||
        json["isFile"] = file;
 | 
			
		||||
        return get_success_res(json);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    drogon::HttpResponsePtr get_download_base64_res(const std::string &data) {
 | 
			
		||||
        Json::Value json;
 | 
			
		||||
        json["data"] = data;
 | 
			
		||||
        return get_success_res(json);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    drogon::HttpResponsePtr get_type_res(const std::string &type) {
 | 
			
		||||
        Json::Value json;
 | 
			
		||||
        json["type"] = type;
 | 
			
		||||
        return get_success_res(json);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    drogon::HttpResponsePtr get_create_zip_done_res() {
 | 
			
		||||
        Json::Value json;
 | 
			
		||||
        json["done"] = true;
 | 
			
		||||
        return get_success_res(json);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    drogon::HttpResponsePtr get_create_zip_done_res(uint64_t progress, uint64_t total) {
 | 
			
		||||
        Json::Value json;
 | 
			
		||||
        json["done"] = false;
 | 
			
		||||
        json["progress"] = progress;
 | 
			
		||||
        json["total"] = total;
 | 
			
		||||
        return get_success_res(json);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ void cleanup_tokens(db::MapperToken& mapper) {
 | 
			
		||||
 | 
			
		||||
void Login::doFilter(const drogon::HttpRequestPtr& req, drogon::FilterCallback&& cb, drogon::FilterChainCallback&& ccb) {
 | 
			
		||||
    std::string token_str;
 | 
			
		||||
    if (req->path() == "/api/fs/download") {
 | 
			
		||||
    if (req->path() == "/api/fs/download" || req->path() == "/api/fs/download_multi") {
 | 
			
		||||
        token_str = req->getParameter("jwtToken");
 | 
			
		||||
    } else {
 | 
			
		||||
        std::string auth_header = req->getHeader("Authorization");
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,9 @@ void cleanup() {
 | 
			
		||||
    std::cout << "Cleanup up uploads...";
 | 
			
		||||
    std::filesystem::remove_all("uploads");
 | 
			
		||||
    std::cout << " [Done]" << std::endl;
 | 
			
		||||
    std::cout << "Removing temp folder..." << std::flush;
 | 
			
		||||
    std::filesystem::remove_all("temp");
 | 
			
		||||
    std::cout << " [Done]" << std::endl;
 | 
			
		||||
    std::cout << "Goodbye!" << std::endl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -47,6 +50,15 @@ int main(int argc, char* argv[]) {
 | 
			
		||||
        std::filesystem::create_directory("logs");
 | 
			
		||||
        std::cout << " [Done]" << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
    if (std::filesystem::exists("temp")) {
 | 
			
		||||
        std::cout << "Removing existing temp folder..." << std::flush;
 | 
			
		||||
        std::filesystem::remove_all("temp");
 | 
			
		||||
        std::cout << " [Done]" << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
    std::cout << "Creating temp folder..." << std::flush;
 | 
			
		||||
    std::filesystem::create_directory("temp");
 | 
			
		||||
    std::cout << " [Done]" << std::endl;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    auto* loop = drogon::app().getLoop();
 | 
			
		||||
    loop->queueInLoop([]{
 | 
			
		||||
@@ -76,7 +88,8 @@ int main(int argc, char* argv[]) {
 | 
			
		||||
                        "  'name' TEXT,\n"
 | 
			
		||||
                        "  'parent_id' INTEGER,\n"
 | 
			
		||||
                        "  'owner_id' INTEGER NOT NULL,\n"
 | 
			
		||||
                        "  'size' INTEGER\n"
 | 
			
		||||
                        "  'size' INTEGER,\n"
 | 
			
		||||
                        "  'has_preview' INTEGER NOT NULL\n"
 | 
			
		||||
                        ")");
 | 
			
		||||
        std::cout << " [Done]" << std::endl;
 | 
			
		||||
        std::cout << "Started!" << std::endl;
 | 
			
		||||
@@ -101,8 +114,12 @@ int main(int argc, char* argv[]) {
 | 
			
		||||
    Json::Value access_logger;
 | 
			
		||||
    access_logger["name"] = "drogon::plugin::AccessLogger";
 | 
			
		||||
 | 
			
		||||
    Json::Value smtp_mail;
 | 
			
		||||
    smtp_mail["name"] = "SMTPMail";
 | 
			
		||||
 | 
			
		||||
    Json::Value config;
 | 
			
		||||
    config["plugins"].append(access_logger);
 | 
			
		||||
    config["plugins"].append(smtp_mail);
 | 
			
		||||
 | 
			
		||||
    drogon::app()
 | 
			
		||||
            .setClientMaxBodySize(std::numeric_limits<size_t>::max())
 | 
			
		||||
@@ -123,8 +140,10 @@ int main(int argc, char* argv[]) {
 | 
			
		||||
            .setIntSignalHandler(cleanup)
 | 
			
		||||
            .setTermSignalHandler(cleanup)
 | 
			
		||||
 | 
			
		||||
            .addListener("0.0.0.0", 5678)
 | 
			
		||||
            .setThreadNum(2);
 | 
			
		||||
            .enableRelaunchOnError()
 | 
			
		||||
 | 
			
		||||
            .addListener("0.0.0.0", 2345)
 | 
			
		||||
            .setThreadNum(8);
 | 
			
		||||
    std::cout << "Setup done!" << std::endl;
 | 
			
		||||
 | 
			
		||||
    drogon::app().run();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								backend/vcpkg-configuration.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								backend/vcpkg-configuration.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
{
 | 
			
		||||
  "default-registry": {
 | 
			
		||||
    "kind": "git",
 | 
			
		||||
    "repository": "https://github.com/microsoft/vcpkg.git",
 | 
			
		||||
    "baseline": "927006b24c3a28dfd8aa0ec5f8ce43098480a7f1"
 | 
			
		||||
  },
 | 
			
		||||
  "registries": [
 | 
			
		||||
    {
 | 
			
		||||
      "kind": "filesystem",
 | 
			
		||||
      "baseline": "default",
 | 
			
		||||
      "path": "./vcpkg_reg",
 | 
			
		||||
      "packages": [ "drogon" ]
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
@@ -7,11 +7,15 @@
 | 
			
		||||
      "name": "drogon",
 | 
			
		||||
      "features": ["orm", "sqlite3"]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "opencv4",
 | 
			
		||||
      "default-features": false,
 | 
			
		||||
      "features": ["tiff", "png", "jpeg", "webp", "openexr"]
 | 
			
		||||
    },
 | 
			
		||||
    "jwt-cpp",
 | 
			
		||||
    "botan",
 | 
			
		||||
    "mailio",
 | 
			
		||||
    "nayuki-qr-code-generator",
 | 
			
		||||
    "lodepng",
 | 
			
		||||
    "openssl"
 | 
			
		||||
    "openssl",
 | 
			
		||||
    "kubazip"
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								backend/vcpkg_reg/ports/drogon/drogon_config.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								backend/vcpkg_reg/ports/drogon/drogon_config.patch
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
diff --git a/cmake/templates/DrogonConfig.cmake.in b/cmake/templates/DrogonConfig.cmake.in
 | 
			
		||||
index a21122a..6367259 100644
 | 
			
		||||
--- a/cmake/templates/DrogonConfig.cmake.in
 | 
			
		||||
+++ b/cmake/templates/DrogonConfig.cmake.in
 | 
			
		||||
@@ -19,7 +19,7 @@ find_dependency(UUID REQUIRED)
 | 
			
		||||
 endif(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD" AND NOT WIN32)
 | 
			
		||||
 find_dependency(ZLIB REQUIRED)
 | 
			
		||||
 if(@pg_FOUND@)
 | 
			
		||||
-find_dependency(pg)
 | 
			
		||||
+find_dependency(PostgreSQL)
 | 
			
		||||
 endif()
 | 
			
		||||
 if(@SQLite3_FOUND@)
 | 
			
		||||
 find_dependency(SQLite3)
 | 
			
		||||
							
								
								
									
										61
									
								
								backend/vcpkg_reg/ports/drogon/portfile.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								backend/vcpkg_reg/ports/drogon/portfile.cmake
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
vcpkg_from_github(
 | 
			
		||||
    OUT_SOURCE_PATH SOURCE_PATH
 | 
			
		||||
    REPO an-tao/drogon
 | 
			
		||||
    REF v1.8.0
 | 
			
		||||
    SHA512 a834d937e3719059223d9bf19d777dbc92eaf09c5c9c44b5a742bfefcbcd95a146a6568cef8c058050fb87e330f221434ffe784dfa29a49de12b031f86ab1a33
 | 
			
		||||
    HEAD_REF master
 | 
			
		||||
    PATCHES
 | 
			
		||||
        vcpkg.patch
 | 
			
		||||
        drogon_config.patch
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
vcpkg_check_features(
 | 
			
		||||
    OUT_FEATURE_OPTIONS FEATURE_OPTIONS
 | 
			
		||||
    FEATURES
 | 
			
		||||
        ctl      BUILD_CTL
 | 
			
		||||
        mysql    BUILD_MYSQL
 | 
			
		||||
        orm      BUILD_ORM
 | 
			
		||||
        postgres BUILD_POSTGRESQL
 | 
			
		||||
        postgres LIBPQ_BATCH_MODE
 | 
			
		||||
        redis    BUILD_REDIS
 | 
			
		||||
        sqlite3  BUILD_SQLITE
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
string(COMPARE EQUAL "${VCPKG_LIBRARY_LINKAGE}" "dynamic" BUILD_DROGON_SHARED)
 | 
			
		||||
 | 
			
		||||
vcpkg_cmake_configure(
 | 
			
		||||
    SOURCE_PATH "${SOURCE_PATH}"
 | 
			
		||||
    DISABLE_PARALLEL_CONFIGURE
 | 
			
		||||
    OPTIONS
 | 
			
		||||
        -DBUILD_SHARED_LIBS=${BUILD_DROGON_SHARED}
 | 
			
		||||
        -DBUILD_EXAMPLES=OFF
 | 
			
		||||
        -DCMAKE_DISABLE_FIND_PACKAGE_Boost=ON
 | 
			
		||||
        ${FEATURE_OPTIONS}
 | 
			
		||||
    MAYBE_UNUSED_VARIABLES
 | 
			
		||||
        CMAKE_DISABLE_FIND_PACKAGE_Boost
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
vcpkg_cmake_install(ADD_BIN_TO_PATH)
 | 
			
		||||
 | 
			
		||||
# Fix CMake files
 | 
			
		||||
vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/Drogon)
 | 
			
		||||
 | 
			
		||||
vcpkg_fixup_pkgconfig()
 | 
			
		||||
 | 
			
		||||
# Copy drogon_ctl
 | 
			
		||||
if("ctl" IN_LIST FEATURES)
 | 
			
		||||
    vcpkg_copy_tools(TOOL_NAMES drogon_ctl AUTO_CLEAN)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# Remove includes in debug
 | 
			
		||||
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")
 | 
			
		||||
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/share")
 | 
			
		||||
if(VCPKG_LIBRARY_LINKAGE STREQUAL "static")
 | 
			
		||||
    file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/bin" "${CURRENT_PACKAGES_DIR}/debug/bin")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")
 | 
			
		||||
file(INSTALL "${SOURCE_PATH}/LICENSE" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright)
 | 
			
		||||
 | 
			
		||||
# Copy pdb files
 | 
			
		||||
vcpkg_copy_pdbs()
 | 
			
		||||
							
								
								
									
										4
									
								
								backend/vcpkg_reg/ports/drogon/usage
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								backend/vcpkg_reg/ports/drogon/usage
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
The package drogon provides CMake targets:
 | 
			
		||||
 | 
			
		||||
    find_package(Drogon CONFIG REQUIRED)
 | 
			
		||||
    target_link_libraries(main PRIVATE Drogon::Drogon)
 | 
			
		||||
							
								
								
									
										92
									
								
								backend/vcpkg_reg/ports/drogon/vcpkg.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								backend/vcpkg_reg/ports/drogon/vcpkg.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "drogon",
 | 
			
		||||
  "version-semver": "1.8.0",
 | 
			
		||||
  "description": "A C++14/17 based HTTP web application framework running on Linux/macOS/Unix/Windows",
 | 
			
		||||
  "homepage": "https://github.com/an-tao/drogon",
 | 
			
		||||
  "documentation": "https://drogon.docsforge.com/master/overview/",
 | 
			
		||||
  "license": "MIT",
 | 
			
		||||
  "dependencies": [
 | 
			
		||||
    "brotli",
 | 
			
		||||
    "jsoncpp",
 | 
			
		||||
    {
 | 
			
		||||
      "name": "libuuid",
 | 
			
		||||
      "platform": "!windows & !osx"
 | 
			
		||||
    },
 | 
			
		||||
    "trantor",
 | 
			
		||||
    {
 | 
			
		||||
      "name": "vcpkg-cmake",
 | 
			
		||||
      "host": true
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "vcpkg-cmake-config",
 | 
			
		||||
      "host": true
 | 
			
		||||
    },
 | 
			
		||||
    "zlib"
 | 
			
		||||
  ],
 | 
			
		||||
  "features": {
 | 
			
		||||
    "ctl": {
 | 
			
		||||
      "description": "Build drogon_ctl tool."
 | 
			
		||||
    },
 | 
			
		||||
    "mysql": {
 | 
			
		||||
      "description": "Support reading and writing from/to MySQL databases.",
 | 
			
		||||
      "dependencies": [
 | 
			
		||||
        {
 | 
			
		||||
          "name": "drogon",
 | 
			
		||||
          "features": [
 | 
			
		||||
            "orm"
 | 
			
		||||
          ]
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "name": "libmariadb",
 | 
			
		||||
          "features": [
 | 
			
		||||
            "iconv"
 | 
			
		||||
          ],
 | 
			
		||||
          "platform": "osx"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "name": "libmariadb",
 | 
			
		||||
          "platform": "!osx"
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "orm": {
 | 
			
		||||
      "description": "Build with object-relational mapping support."
 | 
			
		||||
    },
 | 
			
		||||
    "postgres": {
 | 
			
		||||
      "description": "Support reading and writing from/to Postgres databases.",
 | 
			
		||||
      "dependencies": [
 | 
			
		||||
        {
 | 
			
		||||
          "name": "drogon",
 | 
			
		||||
          "features": [
 | 
			
		||||
            "orm"
 | 
			
		||||
          ]
 | 
			
		||||
        },
 | 
			
		||||
        "libpq"
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "redis": {
 | 
			
		||||
      "description": "Support reading and writing from/to Redis databases.",
 | 
			
		||||
      "dependencies": [
 | 
			
		||||
        {
 | 
			
		||||
          "name": "drogon",
 | 
			
		||||
          "features": [
 | 
			
		||||
            "orm"
 | 
			
		||||
          ]
 | 
			
		||||
        },
 | 
			
		||||
        "hiredis"
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "sqlite3": {
 | 
			
		||||
      "description": "Support reading and writing from/to SQLite databases.",
 | 
			
		||||
      "dependencies": [
 | 
			
		||||
        {
 | 
			
		||||
          "name": "drogon",
 | 
			
		||||
          "features": [
 | 
			
		||||
            "orm"
 | 
			
		||||
          ]
 | 
			
		||||
        },
 | 
			
		||||
        "sqlite3"
 | 
			
		||||
      ]
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										53
									
								
								backend/vcpkg_reg/ports/drogon/vcpkg.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								backend/vcpkg_reg/ports/drogon/vcpkg.patch
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
 | 
			
		||||
--- a/CMakeLists.txt
 | 
			
		||||
+++ b/CMakeLists.txt
 | 
			
		||||
@@ -120,9 +120,9 @@ if (WIN32)
 | 
			
		||||
         PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/third_party/mman-win32>)
 | 
			
		||||
 endif (WIN32)
 | 
			
		||||
 
 | 
			
		||||
-add_subdirectory(trantor)
 | 
			
		||||
+find_package(Trantor CONFIG REQUIRED)
 | 
			
		||||
 
 | 
			
		||||
-target_link_libraries(${PROJECT_NAME} PUBLIC trantor)
 | 
			
		||||
+target_link_libraries(${PROJECT_NAME} PUBLIC Trantor::Trantor)
 | 
			
		||||
 
 | 
			
		||||
 if(${CMAKE_SYSTEM_NAME} STREQUAL "Haiku")
 | 
			
		||||
     target_link_libraries(${PROJECT_NAME} PRIVATE network)
 | 
			
		||||
@@ -316,11 +316,10 @@ endif (NOT WIN32)
 | 
			
		||||
 
 | 
			
		||||
 if (BUILD_POSTGRESQL)
 | 
			
		||||
     # find postgres
 | 
			
		||||
-    find_package(pg)
 | 
			
		||||
-    if (pg_FOUND)
 | 
			
		||||
-        message(STATUS "libpq inc path:" ${PG_INCLUDE_DIRS})
 | 
			
		||||
-        message(STATUS "libpq lib:" ${PG_LIBRARIES})
 | 
			
		||||
-        target_link_libraries(${PROJECT_NAME} PRIVATE pg_lib)
 | 
			
		||||
+    find_package(PostgreSQL REQUIRED)
 | 
			
		||||
+    if(PostgreSQL_FOUND)
 | 
			
		||||
+      set(pg_FOUND true)
 | 
			
		||||
+      target_link_libraries(${PROJECT_NAME} PRIVATE PostgreSQL::PostgreSQL)
 | 
			
		||||
         set(DROGON_SOURCES
 | 
			
		||||
             ${DROGON_SOURCES}
 | 
			
		||||
             orm_lib/src/postgresql_impl/PostgreSQLResultImpl.cc)
 | 
			
		||||
@@ -348,7 +348,7 @@ if (BUILD_POSTGRESQL)
 | 
			
		||||
                 ${private_headers}
 | 
			
		||||
                 orm_lib/src/postgresql_impl/PgConnection.h)
 | 
			
		||||
         endif (libpq_supports_batch)
 | 
			
		||||
-    endif (pg_FOUND)
 | 
			
		||||
+    endif (PostgreSQL_FOUND)
 | 
			
		||||
 endif (BUILD_POSTGRESQL)
 | 
			
		||||
 
 | 
			
		||||
 if (BUILD_MYSQL)
 | 
			
		||||
diff --git a/drogon_ctl/CMakeLists.txt b/drogon_ctl/CMakeLists.txt
 | 
			
		||||
index 9f2f1e7..09871f8 100755
 | 
			
		||||
--- a/drogon_ctl/CMakeLists.txt
 | 
			
		||||
+++ b/drogon_ctl/CMakeLists.txt
 | 
			
		||||
@@ -19,7 +19,7 @@ add_executable(_drogon_ctl
 | 
			
		||||
 target_link_libraries(_drogon_ctl ${PROJECT_NAME})
 | 
			
		||||
 if (WIN32 AND BUILD_SHARED_LIBS)
 | 
			
		||||
   set(DROGON_FILE $<TARGET_FILE:drogon>)
 | 
			
		||||
-  set(TRANTOR_FILE $<TARGET_FILE:trantor>)
 | 
			
		||||
+  set(TRANTOR_FILE $<TARGET_FILE:Trantor::Trantor>)
 | 
			
		||||
   add_custom_command(TARGET _drogon_ctl POST_BUILD
 | 
			
		||||
           COMMAND ${CMAKE_COMMAND}
 | 
			
		||||
           -DCTL_FILE=${DROGON_FILE}
 | 
			
		||||
							
								
								
									
										8
									
								
								backend/vcpkg_reg/versions/baseline.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								backend/vcpkg_reg/versions/baseline.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
{
 | 
			
		||||
  "default": {
 | 
			
		||||
    "drogon": {
 | 
			
		||||
      "baseline": "1.8.0",
 | 
			
		||||
      "port-version": 0
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								backend/vcpkg_reg/versions/d-/drogon.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								backend/vcpkg_reg/versions/d-/drogon.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "versions": [
 | 
			
		||||
    {
 | 
			
		||||
      "version-semver": "1.8.0",
 | 
			
		||||
      "port-version": 0,
 | 
			
		||||
      "path": "$/ports/drogon"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user