Merge branch 'dev' of github.com:amnezia-vpn/desktop-client into feature/check-user-in-sudo

This commit is contained in:
vladimir.kuznetsov 2023-04-08 17:12:23 +03:00
commit f620f4a92e
77 changed files with 1254 additions and 585 deletions

1
client/3rd/mbedtls Submodule

@ -0,0 +1 @@
Subproject commit 8c89224991adff88d53cd380f42a2baa36f91454

View file

@ -65,6 +65,7 @@ include_directories(
)
set(HEADERS ${HEADERS}
${CMAKE_CURRENT_LIST_DIR}/migrations.h
${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc.h
${CMAKE_CURRENT_LIST_DIR}/amnezia_application.h
${CMAKE_CURRENT_LIST_DIR}/containers/containers_defs.h
@ -92,6 +93,7 @@ if(NOT IOS)
endif()
set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/migrations.cpp
${CMAKE_CURRENT_LIST_DIR}/amnezia_application.cpp
${CMAKE_CURRENT_LIST_DIR}/containers/containers_defs.cpp
${CMAKE_CURRENT_LIST_DIR}/core/errorstrings.cpp
@ -481,7 +483,6 @@ if(IOS)
set_target_properties("networkextension" PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual)
set_target_properties("networkextension" PROPERTIES XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "match AppStore org.amnezia.AmneziaVPN.network-extension")
set_target_properties("networkextension" PROPERTIES XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "match Development org.amnezia.AmneziaVPN.network-extension")
endif()
if(ANDROID)

View file

@ -68,8 +68,7 @@
#endif
m_settings = std::shared_ptr<Settings>(new Settings);
m_serverController = std::shared_ptr<ServerController>(new ServerController(m_settings, this));
m_configurator = std::shared_ptr<VpnConfigurator>(new VpnConfigurator(m_settings, m_serverController, this));
m_configurator = std::shared_ptr<VpnConfigurator>(new VpnConfigurator(m_settings, this));
}
AmneziaApplication::~AmneziaApplication()
@ -90,7 +89,7 @@ AmneziaApplication::~AmneziaApplication()
void AmneziaApplication::init()
{
m_engine = new QQmlApplicationEngine;
m_uiLogic = new UiLogic(m_settings, m_configurator, m_serverController);
m_uiLogic = new UiLogic(m_settings, m_configurator);
const QUrl url(QStringLiteral("qrc:/ui/qml/main.qml"));
QObject::connect(m_engine, &QQmlApplicationEngine::objectCreated,

View file

@ -49,7 +49,6 @@ private:
UiLogic *m_uiLogic {};
std::shared_ptr<Settings> m_settings;
std::shared_ptr<VpnConfigurator> m_configurator;
std::shared_ptr<ServerController> m_serverController;
ContainerProps* m_containerProps {};
ProtocolProps* m_protocolProps {};

View file

@ -51,50 +51,10 @@
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter android:label="AmneziaVPN">
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="file"/>
<data android:scheme="content"/>
<data android:mimeType="*/*"/>
<data android:host="*"/>
<data android:pathPattern=".*\\.vpn"/>
<data android:pathPattern=".*\\..*\\.vpn"/>
<data android:pathPattern=".*\\..*\\..*\\.vpn"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.vpn"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.vpn"/>
</intent-filter>
<intent-filter android:label="AmneziaVPN">
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="file"/>
<data android:scheme="content"/>
<data android:mimeType="*/*"/>
<data android:host="*"/>
<data android:pathPattern=".*\\.cfg"/>
<data android:pathPattern=".*\\..*\\.cfg"/>
<data android:pathPattern=".*\\..*\\..*\\.cfg"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.cfg"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.cfg"/>
</intent-filter>
<intent-filter android:label="AmneziaVPN">
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="file"/>
<data android:scheme="content"/>
<data android:mimeType="*/*"/>
<data android:host="*"/>
<data android:pathPattern=".*\\.conf"/>
<data android:pathPattern=".*\\..*\\.conf"/>
<data android:pathPattern=".*\\..*\\..*\\.conf"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.conf"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.conf"/>
<intent-filter>
<action android:name="org.amnezia.vpn.qt.IMPORT_CONFIG" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
@ -118,6 +78,56 @@
<activity
android:name=".qt.CameraActivity"
android:exported="false" />
<activity
android:name=".qt.ImportConfigActivity"
android:exported="true" >
<intent-filter android:label="AmneziaVPN">
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="file"/>
<data android:scheme="content"/>
<data android:mimeType="*/*"/>
<data android:host="*"/>
<data android:pathPattern=".*\\.vpn"/>
<data android:pathPattern=".*\\..*\\.vpn"/>
<data android:pathPattern=".*\\..*\\..*\\.vpn"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.vpn"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.vpn"/>
</intent-filter>
<intent-filter android:label="AmneziaVPN">
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="file"/>
<data android:scheme="content"/>
<data android:mimeType="*/*"/>
<data android:host="*"/>
<data android:pathPattern=".*\\.cfg"/>
<data android:pathPattern=".*\\..*\\.cfg"/>
<data android:pathPattern=".*\\..*\\..*\\.cfg"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.cfg"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.cfg"/>
</intent-filter>
<intent-filter android:label="AmneziaVPN">
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="file"/>
<data android:scheme="content"/>
<data android:mimeType="*/*"/>
<data android:host="*"/>
<data android:pathPattern=".*\\.conf"/>
<data android:pathPattern=".*\\..*\\.conf"/>
<data android:pathPattern=".*\\..*\\..*\\.conf"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\.conf"/>
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.conf"/>
</intent-filter>
</activity>
<service
android:name=".VPNService"

View file

@ -75,7 +75,7 @@ android {
* The following variables:
* - androidBuildToolsVersion,
* - androidCompileSdkVersion
* - qt5AndroidDir - holds the path to qt android files
* - qtAndroidDir - holds the path to qt android files
* needed to build any Qt application
* on Android.
*
@ -106,7 +106,7 @@ android {
renderscript.srcDirs = ['src']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
androidTest.assets.srcDirs += files("${qt5AndroidDir}/schemas".toString())
androidTest.assets.srcDirs += files("${qtAndroidDir}/schemas".toString())
}
}
@ -140,7 +140,7 @@ android {
versionName "2.0.10" // Change to a higher number
javaCompileOptions.annotationProcessorOptions.arguments = [
"room.schemaLocation": "${qt5AndroidDir}/schemas".toString()
"room.schemaLocation": "${qtAndroidDir}/schemas".toString()
]
}

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View file

@ -35,7 +35,7 @@ androidExtensions {
}
//def lifecycleVersion = '2.0.0'
//def roomVersion = '2.0.0'
def roomVersion = "2.4.3"
//def preferencexVersion = '1.0.0'
dependencies {
@ -45,13 +45,12 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
implementation "androidx.core:core-ktx:1.2.0"
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0"
implementation "androidx.lifecycle:lifecycle-livedata-core-ktx:2.4.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
implementation "androidx.room:room-runtime:2.2.5" // runtime
implementation "androidx.room:room-runtime:$roomVersion" // runtime
implementation "androidx.preference:preference:1.1.0"
implementation "androidx.work:work-runtime-ktx:2.7.1"
implementation "androidx.browser:browser:1.3.0-alpha01"
@ -65,6 +64,7 @@ dependencies {
// api "com.takisoft.preferencex:preferencex:1.0.0"
implementation 'com.takisoft.preferencex:preferencex:1.1.0'
api 'org.connectbot.jsocks:jsocks:1.0.0'
kapt "androidx.room:room-compiler:2.2.5"
kapt "androidx.room:room-compiler:$roomVersion"
kapt "androidx.lifecycle:lifecycle-compiler:2.4.0"
}

View file

@ -287,6 +287,8 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
}
}
set(value) {
field = value
if (value) {
mBinder.dispatchEvent(VPNServiceBinder.EVENTS.connected, "")
mConnectionTime = System.currentTimeMillis()
@ -886,45 +888,4 @@ class VPNService : BaseVpnService(), LocalDnsService.Interface {
class CloseableFd(val fd: FileDescriptor) : Closeable {
override fun close() = Os.close(fd)
}
fun saveAsFile(configContent: String?, suggestedFileName: String): String {
val rootDirPath = cacheDir.absolutePath
val rootDir = File(rootDirPath)
if (!rootDir.exists()) {
rootDir.mkdirs()
}
val fileName = if (!TextUtils.isEmpty(suggestedFileName)) suggestedFileName else "amnezia.cfg"
val file = File(rootDir, fileName)
try {
file.bufferedWriter().use { out -> out.write(configContent) }
return file.toString()
} catch (e: Exception) {
e.printStackTrace()
}
return ""
}
fun shareFile(attachmentFile: String?) {
try {
val intent = Intent(Intent.ACTION_SEND)
intent.type = "text/*"
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
val file = File(attachmentFile)
val uri = FileProvider.getUriForFile(this, "${BuildConfig.APPLICATION_ID}.fileprovider", file)
intent.putExtra(Intent.EXTRA_STREAM, uri)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
val createChooser = Intent.createChooser(intent, "Config sharing")
createChooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(createChooser)
} catch (e: Exception) {
Log.i(tag, e.message.toString())
}
}
}

View file

@ -32,7 +32,6 @@ class VPNServiceBinder(service: VPNService) : Binder() {
const val resumeActivate = 7
const val setNotificationText = 8
const val setFallBackNotification = 9
const val shareConfig = 10
const val importConfig = 11
}
@ -139,20 +138,6 @@ class VPNServiceBinder(service: VPNService) : Binder() {
return true
}
ACTIONS.shareConfig -> {
val byteArray = data.createByteArray()
val json = byteArray?.let { String(it) }
val config = JSONObject(json)
val configContent = config.getString("data")
val suggestedName = config.getString("suggestedName")
val filePath = mService.saveAsFile(configContent, suggestedName)
Log.i(tag, "save file: $filePath")
mService.shareFile(filePath)
return true
}
ACTIONS.importConfig -> {
val buffer = data.readString()
@ -196,7 +181,6 @@ class VPNServiceBinder(service: VPNService) : Binder() {
try {
mListener?.let {
if (it.isBinderAlive) {
Log.i(tag, "Dispatching event: binder alive")
val data = Parcel.obtain()
data.writeByteArray(payload?.toByteArray(charset("UTF-8")))
it.transact(code, data, Parcel.obtain(), 0)

View file

@ -2,11 +2,11 @@ package org.amnezia.vpn.qt
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.util.Log
import android.view.MotionEvent
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.*
@ -17,9 +17,9 @@ import com.google.mlkit.vision.barcode.BarcodeScannerOptions
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.common.InputImage
import org.amnezia.vpn.R
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import org.amnezia.vpn.R
class CameraActivity : AppCompatActivity() {
@ -78,7 +78,7 @@ class CameraActivity : AppCompatActivity() {
}
}
@SuppressLint("UnsafeOptInUsageError")
@SuppressLint("UnsafeOptInUsageError", "ClickableViewAccessibility")
private fun configureVideoPreview() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
val imageCapture = ImageCapture.Builder().build()
@ -101,7 +101,20 @@ class CameraActivity : AppCompatActivity() {
try {
preview.setSurfaceProvider(viewFinder.surfaceProvider)
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture, analysisUseCase)
val camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture, analysisUseCase)
viewFinder.setOnTouchListener(View.OnTouchListener { view: View, motionEvent: MotionEvent ->
when (motionEvent.action) {
MotionEvent.ACTION_DOWN -> return@OnTouchListener true
MotionEvent.ACTION_UP -> {
val factory = viewFinder.meteringPointFactory
val point = factory.createPoint(motionEvent.x, motionEvent.y)
val action = FocusMeteringAction.Builder(point).build()
camera.cameraControl.startFocusAndMetering(action)
return@OnTouchListener true
}
else -> return@OnTouchListener false
}
})
} catch(exc: Exception) {
Log.e("WUTT", "Use case binding failed", exc)
}
@ -146,7 +159,6 @@ class CameraActivity : AppCompatActivity() {
}
}
}
imageProxy.close()
}
.addOnFailureListener {

View file

@ -0,0 +1,140 @@
package org.amnezia.vpn.qt
import android.Manifest
import android.app.Activity
import android.content.pm.PackageManager
import android.content.ContentResolver
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import java.io.*
import org.amnezia.vpn.R
const val INTENT_ACTION_IMPORT_CONFIG = "org.amnezia.vpn.qt.IMPORT_CONFIG"
class ImportConfigActivity : Activity() {
private val STORAGE_PERMISSION_CODE = 42
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_import_config)
startReadConfig(intent)
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
startReadConfig(intent)
}
private fun startMainActivity(config: String?) {
if (config == null || config.length == 0) {
return
}
val activityIntent = Intent(applicationContext, VPNActivity::class.java)
activityIntent.action = INTENT_ACTION_IMPORT_CONFIG
activityIntent.addCategory("android.intent.category.DEFAULT")
activityIntent.putExtra("CONFIG", config)
startActivity(activityIntent)
finish()
}
private fun startReadConfig(intent: Intent?) {
val newIntent = intent
val newIntentAction: String = newIntent?.action ?: ""
if (newIntent != null && newIntentAction == Intent.ACTION_VIEW) {
readConfig(newIntent, newIntentAction)
}
}
private fun readConfig(newIntent: Intent, newIntentAction: String) {
if (isReadStorageAllowed()) {
val configString = processIntent(newIntent, newIntentAction)
startMainActivity(configString)
} else {
requestStoragePermission()
}
}
private fun requestStoragePermission() {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), STORAGE_PERMISSION_CODE)
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String?>, grantResults: IntArray) {
if (requestCode == STORAGE_PERMISSION_CODE) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
val configString = processIntent(intent, intent.action!!)
if (configString != null) {
startMainActivity(configString)
}
} else {
Toast.makeText(this, "Oops you just denied the permission", Toast.LENGTH_LONG).show()
}
}
}
private fun processIntent(intent: Intent, action: String): String? {
val scheme = intent.scheme
if (scheme == null) {
return null
}
if (action.compareTo(Intent.ACTION_VIEW) == 0) {
val resolver = contentResolver
if (scheme.compareTo(ContentResolver.SCHEME_CONTENT) == 0) {
val uri = intent.data
val name: String? = getContentName(resolver, uri)
println("Content intent detected: " + action + " : " + intent.dataString + " : " + intent.type + " : " + name)
val input = resolver.openInputStream(uri!!)
return input?.bufferedReader()?.use(BufferedReader::readText)
} else if (scheme.compareTo(ContentResolver.SCHEME_FILE) == 0) {
val uri = intent.data
val name = uri!!.lastPathSegment
println("File intent detected: " + action + " : " + intent.dataString + " : " + intent.type + " : " + name)
val input = resolver.openInputStream(uri)
return input?.bufferedReader()?.use(BufferedReader::readText)
}
}
return null
}
private fun isReadStorageAllowed(): Boolean {
val permissionStatus = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
return permissionStatus == PackageManager.PERMISSION_GRANTED
}
private fun getContentName(resolver: ContentResolver?, uri: Uri?): String? {
val cursor = resolver!!.query(uri!!, null, null, null, null)
cursor.use {
cursor!!.moveToFirst()
val nameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME)
return if (nameIndex >= 0) {
return cursor.getString(nameIndex)
} else {
null
}
}
}
}

View file

@ -5,6 +5,8 @@
package org.amnezia.vpn.qt;
import android.Manifest
import android.content.ClipData
import android.content.ClipboardManager
import android.content.ComponentName
import android.content.ContentResolver
import android.content.Context
@ -32,11 +34,22 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
private var configString: String? = null
private var vpnServiceBinder: IBinder? = null
private var isBound = false
set(value) {
field = value
if (value && configString != null) {
sendImportConfigCommand()
}
}
private val TAG = "VPNActivity"
private val STORAGE_PERMISSION_CODE = 42
private val CAMERA_ACTION_CODE = 101
private val CREATE_FILE_ACTION_CODE = 102
private var tmpFileContentToSave: String = ""
private val delayedCommands: ArrayList<Pair<Int, String>> = ArrayList()
companion object {
private lateinit var instance: VPNActivity
@ -56,19 +69,27 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
@JvmStatic fun sendToService(actionCode: Int, body: String) {
VPNActivity.getInstance().dispatchParcel(actionCode, body)
}
@JvmStatic fun saveFileAs(fileContent: String, suggestedName: String) {
VPNActivity.getInstance().saveFile(fileContent, suggestedName)
}
@JvmStatic fun putTextToClipboard(text: String) {
VPNActivity.getInstance().putToClipboard(text)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
val newIntent = intent
val newIntentAction = newIntent.action
if (newIntent != null && newIntentAction != null) {
configString = processIntent(newIntent, newIntentAction)
}
super.onCreate(savedInstanceState)
instance = this
val newIntent = intent
val newIntentAction: String? = newIntent.action
if (newIntent != null && newIntentAction != null && newIntentAction == "org.amnezia.vpn.qt.IMPORT_CONFIG") {
configString = newIntent.getStringExtra("CONFIG")
}
}
private fun startQrCodeActivity() {
@ -76,6 +97,18 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
startActivityForResult(intent, CAMERA_ACTION_CODE)
}
private fun saveFile(fileContent: String, suggestedName: String) {
tmpFileContentToSave = fileContent
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "text/*"
putExtra(Intent.EXTRA_TITLE, suggestedName)
}
startActivityForResult(intent, CREATE_FILE_ACTION_CODE)
}
override fun getSystemService(name: String): Any? {
return if (Build.VERSION.SDK_INT >= 29 && name == "clipboard") {
// QT will always attempt to read the clipboard if content is there.
@ -98,11 +131,22 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
private fun dispatchParcel(actionCode: Int, body: String) {
if (!isBound) {
Log.d(TAG, "dispatchParcel: not bound")
delayedCommands.add(Pair(actionCode, body))
return
} else {
Log.d(TAG, "dispatchParcel: bound")
}
if (delayedCommands.size > 0) {
for (command in delayedCommands) {
processCommand(command.first, command.second)
}
delayedCommands.clear()
}
processCommand(actionCode, body)
}
private fun processCommand(actionCode: Int, body: String) {
val out: Parcel = Parcel.obtain()
out.writeByteArray(body.toByteArray())
@ -118,19 +162,15 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
}
override fun onNewIntent(newIntent: Intent) {
intent = newIntent
super.onNewIntent(intent)
setIntent(newIntent)
val newIntentAction = newIntent.action
if (newIntent != null && newIntentAction != null && newIntentAction != Intent.ACTION_MAIN) {
if (isReadStorageAllowed()) {
configString = processIntent(newIntent, newIntentAction)
} else {
requestStoragePermission()
}
}
super.onNewIntent(intent)
if (newIntent != null && newIntentAction != null && newIntentAction == INTENT_ACTION_IMPORT_CONFIG) {
configString = newIntent.getStringExtra("CONFIG")
}
}
override fun onResume() {
@ -141,84 +181,6 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
}
}
private fun isReadStorageAllowed(): Boolean {
val permissionStatus = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
return permissionStatus == PackageManager.PERMISSION_GRANTED
}
private fun requestStoragePermission() {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), STORAGE_PERMISSION_CODE)
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String?>, grantResults: IntArray) {
if (requestCode == STORAGE_PERMISSION_CODE) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Storage read permission granted")
if (configString == null) {
configString = processIntent(intent, intent.action!!)
}
if (configString != null) {
Log.d(TAG, "not empty")
sendImportConfigCommand()
} else {
Log.d(TAG, "empty")
}
} else {
Toast.makeText(this, "Oops you just denied the permission", Toast.LENGTH_LONG).show()
}
}
}
private fun processIntent(intent: Intent, action: String): String? {
val scheme = intent.scheme
if (scheme == null) {
return null
}
if (action.compareTo(Intent.ACTION_VIEW) == 0) {
val resolver = contentResolver
if (scheme.compareTo(ContentResolver.SCHEME_CONTENT) == 0) {
val uri = intent.data
val name: String? = getContentName(resolver, uri)
Log.d(TAG, "Content intent detected: " + action + " : " + intent.dataString + " : " + intent.type + " : " + name)
val input = resolver.openInputStream(uri!!)
return input?.bufferedReader()?.use(BufferedReader::readText)
} else if (scheme.compareTo(ContentResolver.SCHEME_FILE) == 0) {
val uri = intent.data
val name = uri!!.lastPathSegment
Log.d(TAG, "File intent detected: " + action + " : " + intent.dataString + " : " + intent.type + " : " + name)
val input = resolver.openInputStream(uri)
return input?.bufferedReader()?.use(BufferedReader::readText)
}
}
return null
}
private fun getContentName(resolver: ContentResolver?, uri: Uri?): String? {
val cursor = resolver!!.query(uri!!, null, null, null, null)
cursor.use {
cursor!!.moveToFirst()
val nameIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME)
return if (nameIndex >= 0) {
return cursor.getString(nameIndex)
} else {
null
}
}
}
private fun sendImportConfigCommand() {
if (configString != null) {
val msg: Parcel = Parcel.obtain()
@ -234,7 +196,7 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
}
}
private var connection: ServiceConnection = object : ServiceConnection {
private fun createConnection() = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, binder: IBinder) {
vpnServiceBinder = binder
@ -260,6 +222,8 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
}
}
private var connection: ServiceConnection = createConnection()
private fun registerBinder(): Boolean {
val binder = VPNClientBinder()
val out: Parcel = Parcel.obtain()
@ -330,13 +294,38 @@ class VPNActivity : org.qtproject.qt.android.bindings.QtActivity() {
val extra = data?.getStringExtra("result") ?: ""
onActivityMessage(UI_EVENT_QR_CODE_RECEIVED, extra)
}
if (requestCode == CREATE_FILE_ACTION_CODE && resultCode == RESULT_OK) {
data?.data?.also { uri ->
alterDocument(uri)
}
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (keyCode == KeyEvent.KEYCODE_BACK && event.repeatCount == 0) {
onBackPressed()
return true
private fun alterDocument(uri: Uri) {
try {
applicationContext.contentResolver.openFileDescriptor(uri, "w")?.use { fd ->
FileOutputStream(fd.fileDescriptor).use { fos ->
fos.write(tmpFileContentToSave.toByteArray())
}
}
} catch (e: FileNotFoundException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
tmpFileContentToSave = ""
}
private fun putToClipboard(text: String) {
this.runOnUiThread {
val clipboard = applicationContext.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager?
if (clipboard != null) {
val clip: ClipData = ClipData.newPlainText("", text)
clipboard.setPrimaryClip(clip)
}
}
return super.onKeyDown(keyCode, event)
}
}

View file

@ -1,5 +1,7 @@
set(CLIENT_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/Modules;${CMAKE_MODULE_PATH}")
if(NOT IOS AND NOT ANDROID)
include(${CLIENT_ROOT_DIR}/3rd/SingleApplication/singleapplication.cmake)
endif()
@ -21,52 +23,76 @@ set(ZLIB_INCLUDE_DIR "${CLIENT_ROOT_DIR}/3rd/zlib" "${CMAKE_CURRENT_BINARY_DIR}/
link_directories(${CMAKE_CURRENT_BINARY_DIR}/3rd/zlib)
link_libraries(${ZLIB_LIBRARY})
if(NOT LINUX)
set(OPENSSL_ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}/3rd/OpenSSL")
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/include")
set(OPENSSL_LIBRARIES_DIR "${OPENSSL_ROOT_DIR}/lib")
set(OPENSSL_LIBRARIES "ssl" "crypto")
if(IOS)
set(ENABLE_PROGRAMS OFF CACHE BOOL "" FORCE)
set(ENABLE_TESTING OFF CACHE BOOL "" FORCE)
add_subdirectory(${CLIENT_ROOT_DIR}/3rd/mbedtls)
set(WITH_MBEDTLS ON CACHE BOOL "" FORCE)
set(WITH_GCRYPT OFF CACHE BOOL "" FORCE)
set(WITH_EXAMPLES OFF CACHE BOOL "" FORCE)
set(ENABLE_PROGRAMS OFF CACHE BOOL "" FORCE)
set(ENABLE_TESTING OFF CACHE BOOL "" FORCE)
set(HAVE_LIBCRYPTO OFF CACHE BOOL "" FORCE)
set(MBEDTLS_ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}/3rd/mbedtls" CACHE PATH "" FORCE)
set(MBEDTLS_INCLUDE_DIR "${CLIENT_ROOT_DIR}/3rd/mbedtls/include" CACHE PATH "" FORCE)
set(MBEDTLS_LIBRARIES "mbedtls" "mbedx509" "mbedcrypto" CACHE STRING "" FORCE)
set(MBEDTLS_FOUND TRUE CACHE BOOL "" FORCE)
set(MBEDTLS_CRYPTO_LIBRARY "mbedcrypto" CACHE STRING "" FORCE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DMBEDTLS_ALLOW_PRIVATE_ACCESS")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMBEDTLS_ALLOW_PRIVATE_ACCESS")
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
set(WITH_STATIC_LIB ON CACHE BOOL "" FORCE)
set(WITH_SYMBOL_VERSIONING OFF CACHE BOOL "" FORCE)
set(OPENSSL_PATH "${CLIENT_ROOT_DIR}/3rd/OpenSSL")
if(WIN32)
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_PATH}/lib/windows/x86_64/libssl.lib")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_PATH}/lib/windows/x86_64/libcrypto.lib")
else()
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_PATH}/lib/windows/x86/libssl.lib")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_PATH}/lib/windows/x86/libcrypto.lib")
include_directories(${CLIENT_ROOT_DIR}/3rd/mbedtls/include)
else(IOS)
if(NOT LINUX)
set(OPENSSL_ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}/3rd/OpenSSL")
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/include")
set(OPENSSL_LIBRARIES_DIR "${OPENSSL_ROOT_DIR}/lib")
set(OPENSSL_LIBRARIES "ssl" "crypto")
set(OPENSSL_PATH "${CLIENT_ROOT_DIR}/3rd/OpenSSL")
if(WIN32)
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_PATH}/lib/windows/x86_64/libssl.lib")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_PATH}/lib/windows/x86_64/libcrypto.lib")
else()
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_PATH}/lib/windows/x86/libssl.lib")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_PATH}/lib/windows/x86/libcrypto.lib")
endif()
elseif(APPLE AND NOT IOS)
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_PATH}/lib/macos/x86_64/libssl.a")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_PATH}/lib/macos/x86_64/libcrypto.a")
elseif(IOS)
set(OPENSSL_CRYPTO_LIBRARY "${OPENSSL_LIBRARIES_DIR}/libcrypto.a")
set(OPENSSL_SSL_LIBRARY "${OPENSSL_LIBRARIES_DIR}/libssl.a")
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_PATH}/lib/ios/iphone/libssl.a")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_PATH}/lib/ios/iphone/libcrypto.a")
elseif(ANDROID)
set(abi ${CMAKE_ANDROID_ARCH_ABI})
set(OPENSSL_CRYPTO_LIBRARY "${OPENSSL_LIBRARIES_DIR}/android/${abi}/libcrypto.a")
set(OPENSSL_SSL_LIBRARY "${OPENSSL_LIBRARIES_DIR}/android/${abi}/libssl.a")
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_PATH}/lib/android/${abi}/libssl.a")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_PATH}/lib/android/${abi}/libcrypto.a")
set(OPENSSL_LIBRARIES_DIR "${OPENSSL_LIBRARIES_DIR}/android/${abi}")
endif()
elseif(APPLE AND NOT IOS)
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_PATH}/lib/macos/x86_64/libssl.a")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_PATH}/lib/macos/x86_64/libcrypto.a")
elseif(IOS)
set(OPENSSL_CRYPTO_LIBRARY "${OPENSSL_LIBRARIES_DIR}/libcrypto.a")
set(OPENSSL_SSL_LIBRARY "${OPENSSL_LIBRARIES_DIR}/libssl.a")
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_PATH}/lib/ios/iphone/libssl.a")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_PATH}/lib/ios/iphone/libcrypto.a")
elseif(ANDROID)
set(abi ${CMAKE_ANDROID_ARCH_ABI})
set(OPENSSL_CRYPTO_LIBRARY "${OPENSSL_LIBRARIES_DIR}/android/${abi}/libcrypto.a")
set(OPENSSL_SSL_LIBRARY "${OPENSSL_LIBRARIES_DIR}/android/${abi}/libssl.a")
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_PATH}/lib/android/${abi}/libssl.a")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_PATH}/lib/android/${abi}/libcrypto.a")
set(OPENSSL_LIBRARIES_DIR "${OPENSSL_LIBRARIES_DIR}/android/${abi}")
file(COPY ${OPENSSL_LIB_SSL_PATH} ${OPENSSL_LIB_CRYPTO_PATH}
DESTINATION ${OPENSSL_LIBRARIES_DIR})
file(COPY "${OPENSSL_PATH}/include"
DESTINATION ${OPENSSL_ROOT_DIR})
endif()
file(COPY ${OPENSSL_LIB_SSL_PATH} ${OPENSSL_LIB_CRYPTO_PATH}
DESTINATION ${OPENSSL_LIBRARIES_DIR})
file(COPY "${OPENSSL_PATH}/include"
DESTINATION ${OPENSSL_ROOT_DIR})
endif()
set(OPENSSL_USE_STATIC_LIBS TRUE)
find_package(OpenSSL REQUIRED)
set(LIBS ${LIBS}
OpenSSL::Crypto
OpenSSL::SSL
)
set(OPENSSL_USE_STATIC_LIBS TRUE)
find_package(OpenSSL REQUIRED)
set(LIBS ${LIBS}
OpenSSL::Crypto
OpenSSL::SSL
)
endif(IOS)
set(WITH_GSSAPI OFF CACHE BOOL "" FORCE)
set(WITH_EXAMPLES OFF CACHE BOOL "" FORCE)
@ -74,7 +100,7 @@ add_subdirectory(${CLIENT_ROOT_DIR}/3rd/libssh)
add_compile_definitions(_WINSOCKAPI_)
set(LIBS ${LIBS} ssh)
# set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
set(BUILD_WITH_QT6 ON)
add_subdirectory(${CLIENT_ROOT_DIR}/3rd/qtkeychain)
set(LIBS ${LIBS} qt6keychain)

View file

@ -0,0 +1,25 @@
set(MBEDTLS_ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}/3rd/mbedtls" CACHE PATH "" FORCE)
set(MBEDTLS_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rd/mbedtls/include" CACHE PATH "" FORCE)
set(MBEDTLS_LIBRARIES "mbedtls" "mbedx509" "mbedcrypto" CACHE STRING "" FORCE)
set(MBEDTLS_FOUND TRUE CACHE BOOL "" FORCE)
set(MBEDTLS_CRYPTO_LIBRARY "mbedcrypto" CACHE STRING "" FORCE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DMBEDTLS_ALLOW_PRIVATE_ACCESS -DMBEDTLS_THREADING_C -DMBEDTLS_THREADING_PTHREAD")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMBEDTLS_ALLOW_PRIVATE_ACCESS -DMBEDTLS_THREADING_C -DMBEDTLS_THREADING_PTHREAD")
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
set(WITH_STATIC_LIB ON CACHE BOOL "" FORCE)
include_directories(${MBEDTLS_INCLUDE_DIR})
# show the MBEDTLS_INCLUDE_DIRS and MBEDTLS_LIBRARIES variables only in the advanced view
mark_as_advanced(MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARIES)
install(TARGETS mbedtls mbedx509 mbedcrypto
EXPORT mbedtls-config
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT libraries)
install(EXPORT mbedtls-config
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})

View file

@ -7,8 +7,8 @@
#include "core/servercontroller.h"
#include "containers/containers_defs.h"
CloakConfigurator::CloakConfigurator(std::shared_ptr<Settings> settings, std::shared_ptr<ServerController> serverController, QObject *parent):
ConfiguratorBase(settings, serverController, parent)
CloakConfigurator::CloakConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
ConfiguratorBase(settings, parent)
{
}
@ -17,12 +17,13 @@ QString CloakConfigurator::genCloakConfig(const ServerCredentials &credentials,
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode)
{
ErrorCode e = ErrorCode::NoError;
ServerController serverController(m_settings);
QString cloakPublicKey = m_serverController->getTextFileFromContainer(container, credentials,
QString cloakPublicKey = serverController.getTextFileFromContainer(container, credentials,
amnezia::protocols::cloak::ckPublicKeyPath, &e);
cloakPublicKey.replace("\n", "");
QString cloakBypassUid = m_serverController->getTextFileFromContainer(container, credentials,
QString cloakBypassUid = serverController.getTextFileFromContainer(container, credentials,
amnezia::protocols::cloak::ckBypassUidKeyPath, &e);
cloakBypassUid.replace("\n", "");
@ -47,8 +48,8 @@ QString CloakConfigurator::genCloakConfig(const ServerCredentials &credentials,
config.insert(config_key::remote, credentials.hostName);
config.insert(config_key::port, "$CLOAK_SERVER_PORT");
QString textCfg = m_serverController->replaceVars(QJsonDocument(config).toJson(),
m_serverController->genVarsForScript(credentials, container, containerConfig));
QString textCfg = serverController.replaceVars(QJsonDocument(config).toJson(),
serverController.genVarsForScript(credentials, container, containerConfig));
// qDebug().noquote() << textCfg;
return textCfg;

View file

@ -11,8 +11,7 @@ class CloakConfigurator : ConfiguratorBase
{
Q_OBJECT
public:
CloakConfigurator(std::shared_ptr<Settings> settings,
std::shared_ptr<ServerController> serverController, QObject *parent = nullptr);
CloakConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
QString genCloakConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);

View file

@ -1,10 +1,8 @@
#include "configurator_base.h"
ConfiguratorBase::ConfiguratorBase(std::shared_ptr<Settings> settings,
std::shared_ptr<ServerController> serverController, QObject *parent)
ConfiguratorBase::ConfiguratorBase(std::shared_ptr<Settings> settings, QObject *parent)
: QObject{parent},
m_settings(settings),
m_serverController(serverController)
m_settings(settings)
{
}

View file

@ -4,7 +4,6 @@
#include <QObject>
class Settings;
class ServerController;
#include "containers/containers_defs.h"
#include "core/defs.h"
@ -13,13 +12,10 @@ class ConfiguratorBase : public QObject
{
Q_OBJECT
public:
explicit ConfiguratorBase(std::shared_ptr<Settings> settings,
std::shared_ptr<ServerController> serverController, QObject *parent = nullptr);
explicit ConfiguratorBase(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
protected:
std::shared_ptr<Settings> m_settings;
std::shared_ptr<ServerController> m_serverController;
};
#endif // CONFIGURATORBASE_H

View file

@ -15,8 +15,8 @@
#include "core/servercontroller.h"
Ikev2Configurator::Ikev2Configurator(std::shared_ptr<Settings> settings, std::shared_ptr<ServerController> serverController, QObject *parent):
ConfiguratorBase(settings, serverController, parent)
Ikev2Configurator::Ikev2Configurator(std::shared_ptr<Settings> settings, QObject *parent):
ConfiguratorBase(settings, parent)
{
}
@ -41,16 +41,17 @@ Ikev2Configurator::ConnectionData Ikev2Configurator::prepareIkev2Config(const Se
"--extKeyUsage serverAuth,clientAuth -8 \"%1\"")
.arg(connData.clientId);
ErrorCode e = m_serverController->runContainerScript(credentials, container, scriptCreateCert);
ServerController serverController(m_settings);
ErrorCode e = serverController.runContainerScript(credentials, container, scriptCreateCert);
QString scriptExportCert = QString("pk12util -W \"%1\" -d sql:/etc/ipsec.d -n \"%2\" -o \"%3\"")
.arg(connData.password)
.arg(connData.clientId)
.arg(certFileName);
e = m_serverController->runContainerScript(credentials, container, scriptExportCert);
e = serverController.runContainerScript(credentials, container, scriptExportCert);
connData.clientCert = m_serverController->getTextFileFromContainer(container, credentials, certFileName, &e);
connData.caCert = m_serverController->getTextFileFromContainer(container, credentials, "/etc/ipsec.d/ca_cert_base64.p12", &e);
connData.clientCert = serverController.getTextFileFromContainer(container, credentials, certFileName, &e);
connData.caCert = serverController.getTextFileFromContainer(container, credentials, "/etc/ipsec.d/ca_cert_base64.p12", &e);
qDebug() << "Ikev2Configurator::ConnectionData client cert size:" << connData.clientCert.size();
qDebug() << "Ikev2Configurator::ConnectionData ca cert size:" << connData.caCert.size();

View file

@ -11,8 +11,7 @@ class Ikev2Configurator : ConfiguratorBase
{
Q_OBJECT
public:
Ikev2Configurator(std::shared_ptr<Settings> settings,
std::shared_ptr<ServerController> serverController, QObject *parent = nullptr);
Ikev2Configurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
struct ConnectionData {
QByteArray clientCert; // p12 client cert

View file

@ -19,8 +19,8 @@
#include <openssl/x509.h>
#include <openssl/pem.h>
OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr<Settings> settings, std::shared_ptr<ServerController> serverController, QObject *parent):
ConfiguratorBase(settings, serverController, parent)
OpenVpnConfigurator::OpenVpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
ConfiguratorBase(settings, parent)
{
}
@ -40,7 +40,8 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
arg(amnezia::protocols::openvpn::clientsDirPath).
arg(connData.clientId);
ErrorCode e = m_serverController->uploadTextFileToContainer(container, credentials, connData.request, reqFileName);
ServerController serverController(m_settings);
ErrorCode e = serverController.uploadTextFileToContainer(container, credentials, connData.request, reqFileName);
if (e) {
if (errorCode) *errorCode = e;
return connData;
@ -52,8 +53,8 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
return connData;
}
connData.caCert = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::caCertPath, &e);
connData.clientCert = m_serverController->getTextFileFromContainer(container, credentials,
connData.caCert = serverController.getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::caCertPath, &e);
connData.clientCert = serverController.getTextFileFromContainer(container, credentials,
QString("%1/%2.crt").arg(amnezia::protocols::openvpn::clientCertPath).arg(connData.clientId), &e);
if (e) {
@ -61,7 +62,7 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
return connData;
}
connData.taKey = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::taKeyPath, &e);
connData.taKey = serverController.getTextFileFromContainer(container, credentials, amnezia::protocols::openvpn::taKeyPath, &e);
if (connData.caCert.isEmpty() || connData.clientCert.isEmpty() || connData.taKey.isEmpty()) {
if (errorCode) *errorCode = ErrorCode::SshSftpFailureError;
@ -73,8 +74,9 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentials,
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode)
{
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::openvpn_template, container),
m_serverController->genVarsForScript(credentials, container, containerConfig));
ServerController serverController(m_settings);
QString config = serverController.replaceVars(amnezia::scriptData(ProtocolScriptType::openvpn_template, container),
serverController.genVarsForScript(credentials, container, containerConfig));
ConnectionData connData = prepareOpenVpnConfig(credentials, container, errorCode);
if (errorCode && *errorCode) {
@ -163,10 +165,11 @@ ErrorCode OpenVpnConfigurator::signCert(DockerContainer container,
.arg(ContainerProps::containerToString(container))
.arg(clientId);
ServerController serverController(m_settings);
QStringList scriptList {script_import, script_sign};
QString script = m_serverController->replaceVars(scriptList.join("\n"), m_serverController->genVarsForScript(credentials, container));
QString script = serverController.replaceVars(scriptList.join("\n"), serverController.genVarsForScript(credentials, container));
return m_serverController->runScript(credentials, script);
return serverController.runScript(credentials, script);
}
OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::createCertRequest()

View file

@ -11,8 +11,7 @@ class OpenVpnConfigurator : ConfiguratorBase
{
Q_OBJECT
public:
OpenVpnConfigurator(std::shared_ptr<Settings> settings,
std::shared_ptr<ServerController> serverController, QObject *parent = nullptr);
OpenVpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
struct ConnectionData {
QString clientId;

View file

@ -7,8 +7,8 @@
#include "containers/containers_defs.h"
#include "core/servercontroller.h"
ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr<Settings> settings, std::shared_ptr<ServerController> serverController, QObject *parent):
ConfiguratorBase(settings, serverController, parent)
ShadowSocksConfigurator::ShadowSocksConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
ConfiguratorBase(settings, parent)
{
}
@ -17,9 +17,10 @@ QString ShadowSocksConfigurator::genShadowSocksConfig(const ServerCredentials &c
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode)
{
ErrorCode e = ErrorCode::NoError;
ServerController serverController(m_settings);
QString ssKey = m_serverController->getTextFileFromContainer(container, credentials,
amnezia::protocols::shadowsocks::ssKeyPath, &e);
QString ssKey = serverController.getTextFileFromContainer(container, credentials,
amnezia::protocols::shadowsocks::ssKeyPath, &e);
ssKey.replace("\n", "");
if (e) {
@ -36,8 +37,8 @@ QString ShadowSocksConfigurator::genShadowSocksConfig(const ServerCredentials &c
config.insert("method", "$SHADOWSOCKS_CIPHER");
QString textCfg = m_serverController->replaceVars(QJsonDocument(config).toJson(),
m_serverController->genVarsForScript(credentials, container, containerConfig));
QString textCfg = serverController.replaceVars(QJsonDocument(config).toJson(),
serverController.genVarsForScript(credentials, container, containerConfig));
//qDebug().noquote() << textCfg;
return textCfg;

View file

@ -10,8 +10,7 @@ class ShadowSocksConfigurator : ConfiguratorBase
{
Q_OBJECT
public:
ShadowSocksConfigurator(std::shared_ptr<Settings> settings,
std::shared_ptr<ServerController> serverController, QObject *parent = nullptr);
ShadowSocksConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
QString genShadowSocksConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, ErrorCode *errorCode = nullptr);

View file

@ -15,8 +15,8 @@
#include "utilities.h"
SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, std::shared_ptr<ServerController> serverController, QObject *parent):
ConfiguratorBase(settings, serverController, parent)
SshConfigurator::SshConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
ConfiguratorBase(settings, parent)
{
}

View file

@ -11,8 +11,7 @@ class SshConfigurator : ConfiguratorBase
{
Q_OBJECT
public:
SshConfigurator(std::shared_ptr<Settings> settings,
std::shared_ptr<ServerController> serverController, QObject *parent = nullptr);
SshConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
QProcessEnvironment prepareEnv();
QString convertOpenSShKey(const QString &key);

View file

@ -14,16 +14,15 @@
#include "utilities.h"
#include "settings.h"
VpnConfigurator::VpnConfigurator(std::shared_ptr<Settings> settings,
std::shared_ptr<ServerController> serverController, QObject *parent):
ConfiguratorBase(settings, serverController, parent)
VpnConfigurator::VpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
ConfiguratorBase(settings, parent)
{
openVpnConfigurator = std::shared_ptr<OpenVpnConfigurator>(new OpenVpnConfigurator(settings, serverController, this));
shadowSocksConfigurator = std::shared_ptr<ShadowSocksConfigurator>(new ShadowSocksConfigurator(settings, serverController, this));
cloakConfigurator = std::shared_ptr<CloakConfigurator>(new CloakConfigurator(settings, serverController, this));
wireguardConfigurator = std::shared_ptr<WireguardConfigurator>(new WireguardConfigurator(settings, serverController, this));
ikev2Configurator = std::shared_ptr<Ikev2Configurator>(new Ikev2Configurator(settings, serverController, this));
sshConfigurator = std::shared_ptr<SshConfigurator>(new SshConfigurator(settings, serverController, this));
openVpnConfigurator = std::shared_ptr<OpenVpnConfigurator>(new OpenVpnConfigurator(settings, this));
shadowSocksConfigurator = std::shared_ptr<ShadowSocksConfigurator>(new ShadowSocksConfigurator(settings, this));
cloakConfigurator = std::shared_ptr<CloakConfigurator>(new CloakConfigurator(settings, this));
wireguardConfigurator = std::shared_ptr<WireguardConfigurator>(new WireguardConfigurator(settings, this));
ikev2Configurator = std::shared_ptr<Ikev2Configurator>(new Ikev2Configurator(settings, this));
sshConfigurator = std::shared_ptr<SshConfigurator>(new SshConfigurator(settings, this));
}
QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentials,

View file

@ -19,8 +19,7 @@ class VpnConfigurator : ConfiguratorBase
{
Q_OBJECT
public:
VpnConfigurator(std::shared_ptr<Settings> settings,
std::shared_ptr<ServerController> serverController, QObject *parent = nullptr);
VpnConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
QString genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode = nullptr);
@ -35,8 +34,6 @@ public:
void updateContainerConfigAfterInstallation(DockerContainer container,
QJsonObject &containerConfig, const QString &stdOut);
std::shared_ptr<ServerController> m_serverController;
std::shared_ptr<OpenVpnConfigurator> openVpnConfigurator;
std::shared_ptr<ShadowSocksConfigurator> shadowSocksConfigurator;
std::shared_ptr<CloakConfigurator> cloakConfigurator;

View file

@ -21,8 +21,8 @@
#include "core/servercontroller.h"
#include "settings.h"
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, std::shared_ptr<ServerController> serverController, QObject *parent):
ConfiguratorBase(settings, serverController, parent)
WireguardConfigurator::WireguardConfigurator(std::shared_ptr<Settings> settings, QObject *parent):
ConfiguratorBase(settings, parent)
{
}
@ -70,6 +70,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
}
ErrorCode e = ErrorCode::NoError;
ServerController serverController(m_settings);
// Get list of already created clients (only IP addreses)
QString nextIpNumber;
@ -81,7 +82,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
return ErrorCode::NoError;
};
e = m_serverController->runContainerScript(credentials, container, script, cbReadStdOut);
e = serverController.runContainerScript(credentials, container, script, cbReadStdOut);
if (errorCode && e) {
*errorCode = e;
return connData;
@ -119,14 +120,14 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
}
// Get keys
connData.serverPubKey = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::wireguard::serverPublicKeyPath, &e);
connData.serverPubKey = serverController.getTextFileFromContainer(container, credentials, amnezia::protocols::wireguard::serverPublicKeyPath, &e);
connData.serverPubKey.replace("\n", "");
if (e) {
if (errorCode) *errorCode = e;
return connData;
}
connData.pskKey = m_serverController->getTextFileFromContainer(container, credentials, amnezia::protocols::wireguard::serverPskKeyPath, &e);
connData.pskKey = serverController.getTextFileFromContainer(container, credentials, amnezia::protocols::wireguard::serverPskKeyPath, &e);
connData.pskKey.replace("\n", "");
if (e) {
@ -144,7 +145,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
arg(connData.pskKey).
arg(connData.clientIP);
e = m_serverController->uploadTextFileToContainer(container, credentials, configPart,
e = serverController.uploadTextFileToContainer(container, credentials, configPart,
protocols::wireguard::serverConfigPath, libssh::SftpOverwriteMode::SftpAppendToExisting);
if (e) {
@ -152,9 +153,9 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
return connData;
}
e = m_serverController->runScript(credentials,
m_serverController->replaceVars("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip /opt/amnezia/wireguard/wg0.conf)'",
m_serverController->genVarsForScript(credentials, container)));
e = serverController.runScript(credentials,
serverController.replaceVars("sudo docker exec -i $CONTAINER_NAME bash -c 'wg syncconf wg0 <(wg-quick strip /opt/amnezia/wireguard/wg0.conf)'",
serverController.genVarsForScript(credentials, container)));
return connData;
}
@ -162,8 +163,9 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &credentials,
DockerContainer container, const QJsonObject &containerConfig, ErrorCode *errorCode)
{
QString config = m_serverController->replaceVars(amnezia::scriptData(ProtocolScriptType::wireguard_template, container),
m_serverController->genVarsForScript(credentials, container, containerConfig));
ServerController serverController(m_settings);
QString config = serverController.replaceVars(amnezia::scriptData(ProtocolScriptType::wireguard_template, container),
serverController.genVarsForScript(credentials, container, containerConfig));
ConnectionData connData = prepareWireguardConfig(credentials, container, containerConfig, errorCode);
if (errorCode && *errorCode) {

View file

@ -11,8 +11,7 @@ class WireguardConfigurator : ConfiguratorBase
{
Q_OBJECT
public:
WireguardConfigurator(std::shared_ptr<Settings> settings,
std::shared_ptr<ServerController> serverController, QObject *parent = nullptr);
WireguardConfigurator(std::shared_ptr<Settings> settings, QObject *parent = nullptr);
struct ConnectionData {
QString clientPrivKey; // client private key

View file

@ -36,6 +36,7 @@ enum ErrorCode
// Ssh connection errors
SshRequsetDeniedError, SshInterruptedError, SshInternalError,
SshPrivateKeyError, SshPrivateKeyFormatError,
// Ssh sftp errors
SshSftpEofError, SshSftpNoSuchFileError, SshSftpPermissionDeniedError,

View file

@ -22,6 +22,8 @@ QString errorString(ErrorCode code){
case(SshRequsetDeniedError): return QObject::tr("Ssh request was denied");
case(SshInterruptedError): return QObject::tr("Ssh request was interrupted");
case(SshInternalError): return QObject::tr("Ssh internal error");
case(SshPrivateKeyError): return QObject::tr("Invalid private key or invalid passphrase entered");
case(SshPrivateKeyFormatError): return QObject::tr("The selected private key format is not supported, use openssh ED25519 key types or PEM key types");
// Libssh sftp errors
case(SshSftpEofError): return QObject::tr("Sftp error: End-of-file encountered");

View file

@ -39,6 +39,7 @@ ServerController::ServerController(std::shared_ptr<Settings> settings, QObject *
ServerController::~ServerController()
{
m_sshClient.disconnectFromHost();
}
@ -92,7 +93,6 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
return ErrorCode::NoError;
}
ErrorCode ServerController::runContainerScript(const ServerCredentials &credentials,
DockerContainer container, QString script,
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdOut,
@ -203,21 +203,6 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container,
return QByteArray::fromHex(stdOut.toUtf8());
}
ErrorCode ServerController::checkOpenVpnServer(DockerContainer container, const ServerCredentials &credentials)
{
QString caCert = ServerController::getTextFileFromContainer(container,
credentials, protocols::openvpn::caCertPath);
QString taKey = ServerController::getTextFileFromContainer(container,
credentials, protocols::openvpn::taKeyPath);
if (!caCert.isEmpty() && !taKey.isEmpty()) {
return ErrorCode::NoError;
}
else {
return ErrorCode::ServerCheckFailed;
}
}
ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath,
libssh::SftpOverwriteMode overwriteMode)
{
@ -260,8 +245,6 @@ ErrorCode ServerController::setupContainer(const ServerCredentials &credentials,
//qDebug().noquote() << QJsonDocument(config).toJson();
ErrorCode e = ErrorCode::NoError;
disconnectFromHost(credentials);
e = isUserInSudo(credentials, container);
if (e) return e;
@ -622,11 +605,6 @@ void ServerController::setCancelInstallation(const bool cancel)
m_cancelInstallation = cancel;
}
void ServerController::disconnectFromHost(const ServerCredentials &credentials)
{
m_sshClient.disconnectFromHost();
}
ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials)
{
return runScript(credentials,
@ -647,6 +625,10 @@ QString ServerController::replaceVars(const QString &script, const Vars &vars)
ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config)
{
if (container == DockerContainer::Dns) {
return ErrorCode::NoError;
}
QString stdOut;
auto cbReadStdOut = [&](const QString &data, libssh::Client &) {
stdOut += data + "\n";
@ -657,21 +639,27 @@ ErrorCode ServerController::isServerPortBusy(const ServerCredentials &credential
return ErrorCode::NoError;
};
const QString containerString = ProtocolProps::protoToString(ContainerProps::defaultProtocol(container));
const Proto protocol = ContainerProps::defaultProtocol(container);
const QString containerString = ProtocolProps::protoToString(protocol);
const QJsonObject containerConfig = config.value(containerString).toObject();
QStringList fixedPorts = ContainerProps::fixedPortsForContainer(container);
QString port = containerConfig.value(config_key::port).toString();
QString transportProto = containerConfig.value(config_key::transport_proto).toString();
QString defaultPort("%1");
QString port = containerConfig.value(config_key::port).toString(defaultPort.arg(ProtocolProps::defaultPort(protocol)));
QString defaultTransportProto = ProtocolProps::transportProtoToString(ProtocolProps::defaultTransportProto(protocol), protocol);
QString transportProto = containerConfig.value(config_key::transport_proto).toString(defaultTransportProto);
QString script = QString("sudo lsof -i -P -n | grep -E ':%1").arg(port);
QString script = QString("sudo lsof -i -P -n | grep -E ':%1 ").arg(port);
for (auto &port : fixedPorts) {
script = script.append("|:%1").arg(port);
}
script = script.append("' | grep -i %1").arg(transportProto);
runScript(credentials,
ErrorCode errorCode = runScript(credentials,
replaceVars(script, genVarsForScript(credentials, container)), cbReadStdOut, cbReadStdErr);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
if (!stdOut.isEmpty()) {
return ErrorCode::ServerPortAlreadyAllocatedError;
@ -786,3 +774,9 @@ ErrorCode ServerController::getAlreadyInstalledContainers(const ServerCredential
return ErrorCode::NoError;
}
ErrorCode ServerController::getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey, const std::function<QString()> &callback)
{
auto error = m_sshClient.getDecryptedPrivateKey(credentials, decryptedPrivateKey, callback);
return error;
}

View file

@ -22,41 +22,26 @@ public:
typedef QList<QPair<QString, QString>> Vars;
// ErrorCode fromSshConnectionErrorCode(QSsh::SshError error);
// QSsh exitCode and exitStatus are different things
// ErrorCode fromSshProcessExitStatus(int exitStatus);
// QSsh::SshConnectionParameters sshParams(const ServerCredentials &credentials);
void disconnectFromHost(const ServerCredentials &credentials);
ErrorCode removeAllContainers(const ServerCredentials &credentials);
ErrorCode removeContainer(const ServerCredentials &credentials, DockerContainer container);
ErrorCode setupContainer(const ServerCredentials &credentials, DockerContainer container,
QJsonObject &config, bool isUpdate = false);
ErrorCode updateContainer(const ServerCredentials &credentials, DockerContainer container,
const QJsonObject &oldConfig, QJsonObject &newConfig);
ErrorCode getAlreadyInstalledContainers(const ServerCredentials &credentials, QMap<DockerContainer, QJsonObject> &installedContainers);
// create initial config - generate passwords, etc
QJsonObject createContainerInitialConfig(DockerContainer container, int port, TransportProto tp);
bool isReinstallContainerRequred(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig);
ErrorCode uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials,
const QString &file, const QString &path,
libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting);
ErrorCode checkOpenVpnServer(DockerContainer container, const ServerCredentials &credentials);
ErrorCode uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data,
const QString &remotePath, libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting);
ErrorCode uploadTextFileToContainer(DockerContainer container,
const ServerCredentials &credentials, const QString &file, const QString &path,
libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting);
QByteArray getTextFileFromContainer(DockerContainer container,
const ServerCredentials &credentials, const QString &path, ErrorCode *errorCode = nullptr);
ErrorCode setupServerFirewall(const ServerCredentials &credentials);
QByteArray getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials,
const QString &path, ErrorCode *errorCode = nullptr);
QString replaceVars(const QString &script, const Vars &vars);
Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None, const QJsonObject &config = QJsonObject());
ErrorCode runScript(const ServerCredentials &credentials, QString script,
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdOut = nullptr,
@ -66,12 +51,11 @@ public:
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdOut = nullptr,
const std::function<ErrorCode (const QString &, libssh::Client &)> &cbReadStdErr = nullptr);
Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None, const QJsonObject &config = QJsonObject());
QString checkSshConnection(const ServerCredentials &credentials, ErrorCode *errorCode = nullptr);
void setCancelInstallation(const bool cancel);
ErrorCode getAlreadyInstalledContainers(const ServerCredentials &credentials, QMap<DockerContainer, QJsonObject> &installedContainers);
ErrorCode getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey, const std::function<QString()> &callback);
private:
ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container);
ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject());
@ -81,8 +65,14 @@ private:
ErrorCode startupContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject());
ErrorCode isServerPortBusy(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config);
bool isReinstallContainerRequred(DockerContainer container, const QJsonObject &oldConfig, const QJsonObject &newConfig);
ErrorCode isUserInSudo(const ServerCredentials &credentials, DockerContainer container);
ErrorCode isServerDpkgBusy(const ServerCredentials &credentials, DockerContainer container);
ErrorCode uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data,
const QString &remotePath, libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting);
ErrorCode setupServerFirewall(const ServerCredentials &credentials);
std::shared_ptr<Settings> m_settings;
std::shared_ptr<VpnConfigurator> m_configurator;

View file

@ -10,18 +10,23 @@
#endif
namespace libssh {
std::function<QString()> Client::m_passphraseCallback;
Client::Client(QObject *parent) : QObject(parent)
{ }
Client::~Client()
{ }
int Client::callback(const char *prompt, char *buf, size_t len, int echo, int verify, void *userdata)
{
auto passphrase = m_passphraseCallback();
passphrase.toStdString().copy(buf, passphrase.size() + 1);
return 0;
}
ErrorCode Client::connectToHost(const ServerCredentials &credentials)
{
// if (is_ssh_initialized()) {
// qDebug() << "Failed to initialize ssh";
// return ErrorCode::InternalError;
// }
if (m_session == nullptr) {
m_session = ssh_new();
@ -51,20 +56,42 @@ namespace libssh {
int authResult = SSH_ERROR;
if (credentials.password.contains("BEGIN") && credentials.password.contains("PRIVATE KEY")) {
ssh_key privateKey;
ssh_pki_import_privkey_base64(credentials.password.toStdString().c_str(), nullptr, nullptr, nullptr, &privateKey);
authResult = ssh_userauth_publickey(m_session, authUsername.c_str(), privateKey);
}
else {
ssh_key privateKey = nullptr;
ssh_key publicKey = nullptr;
authResult = ssh_pki_import_privkey_base64(credentials.password.toStdString().c_str(), nullptr, callback, nullptr, &privateKey);
if (authResult == SSH_OK) {
authResult = ssh_pki_export_privkey_to_pubkey(privateKey, &publicKey);
}
if (authResult == SSH_OK) {
authResult = ssh_userauth_try_publickey(m_session, authUsername.c_str(), publicKey);
}
if (authResult == SSH_OK) {
authResult = ssh_userauth_publickey(m_session, authUsername.c_str(), privateKey);
}
if (publicKey) {
ssh_key_free(publicKey);
}
if (privateKey) {
ssh_key_free(privateKey);
}
if (authResult != SSH_OK) {
qDebug() << ssh_get_error(m_session);
ErrorCode errorCode = fromLibsshErrorCode(ssh_get_error_code(m_session));
if (errorCode == ErrorCode::NoError) {
errorCode = ErrorCode::SshPrivateKeyFormatError;
}
return errorCode;
}
} else {
authResult = ssh_userauth_password(m_session, authUsername.c_str(), credentials.password.toStdString().c_str());
if (authResult != SSH_OK) {
qDebug() << ssh_get_error(m_session);
return fromLibsshErrorCode(ssh_get_error_code(m_session));
}
}
if (authResult != SSH_OK) {
qDebug() << ssh_get_error(m_session);
return fromLibsshErrorCode(ssh_get_error_code(m_session));
}
return fromLibsshErrorCode(ssh_get_error_code(m_session));
}
return ErrorCode::NoError;
}
@ -319,4 +346,33 @@ namespace libssh {
default: return ErrorCode::SshSftpFailureError;
}
}
ErrorCode Client::getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey, const std::function<QString()> &passphraseCallback)
{
int authResult = SSH_ERROR;
ErrorCode errorCode = ErrorCode::NoError;
ssh_key privateKey = nullptr;
m_passphraseCallback = passphraseCallback;
authResult = ssh_pki_import_privkey_base64(credentials.password.toStdString().c_str(), nullptr, callback, nullptr, &privateKey);
if (authResult == SSH_OK) {
char* key = new char[65535];
authResult = ssh_pki_export_privkey_base64(privateKey, nullptr, nullptr, nullptr, &key);
decryptedPrivateKey = key;
delete[] key;
if (authResult != SSH_OK) {
qDebug() << "failed to export private key";
errorCode = ErrorCode::InternalError;
}
} else {
errorCode = ErrorCode::SshPrivateKeyError;
}
if (privateKey) {
ssh_key_free(privateKey);
}
return errorCode;
}
}

View file

@ -36,15 +36,19 @@ namespace libssh {
const std::string& localPath,
const std::string& remotePath,
const std::string& fileDesc);
ErrorCode getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey, const std::function<QString()> &passphraseCallback);
private:
ErrorCode closeChannel();
ErrorCode closeSftpSession();
ErrorCode fromLibsshErrorCode(int errorCode);
ErrorCode fromLibsshSftpErrorCode(int errorCode);
static int callback(const char *prompt, char *buf, size_t len, int echo, int verify, void *userdata);
ssh_session m_session = nullptr;
ssh_channel m_channel = nullptr;
sftp_session m_sftpSession = nullptr;
static std::function<QString()> m_passphraseCallback;
signals:
void writeToChannelFinished();
void sftpFileCopyFinished();

View file

@ -4,6 +4,7 @@
#include "amnezia_application.h"
#include "defines.h"
#include "migrations.h"
#ifdef Q_OS_WIN
#include "Windows.h"
@ -16,6 +17,9 @@
int main(int argc, char *argv[])
{
Migrations migrationsManager;
migrationsManager.doMigrations();
QLoggingCategory::setFilterRules(QStringLiteral("qtc.ssh=false"));
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);

86
client/migrations.cpp Normal file
View file

@ -0,0 +1,86 @@
#include "migrations.h"
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QStandardPaths>
#include "defines.h"
Migrations::Migrations(QObject *parent)
: QObject{parent}
{
QString version(APP_MAJOR_VERSION);
QStringList versionDigits = version.split(".");
if (versionDigits.size() >= 3) {
currentMajor = versionDigits[0].toInt();
currentMinor = versionDigits[1].toInt();
currentMicro = versionDigits[2].toInt();
}
if (versionDigits.size() == 4) {
currentPatch = versionDigits[3].toInt();
}
}
void Migrations::doMigrations()
{
if (currentMajor == 3) {
migrateV3();
}
}
void Migrations::migrateV3()
{
#ifdef Q_OS_ANDROID
qDebug() << "Migration to V3 on Android...";
QString packageName = "org.amnezia.vpn";
QDir dir(".");
QString currentDir = dir.absolutePath();
int packageNameIndex = currentDir.indexOf(packageName);
if (packageNameIndex == -1) {
return;
}
QString rootLocation = currentDir.left(packageNameIndex + packageName.size());
if (rootLocation.isEmpty()) {
return;
}
QString location = rootLocation + "/files/.config/AmneziaVPN.ORG/AmneziaVPN.conf";
QFile oldConfig(location);
if (oldConfig.exists()) {
QString newConfigPath = rootLocation + "/files/settings";
QDir newConfigDir(newConfigPath);
newConfigPath += "/AmneziaVPN.ORG";
bool mkPathRes = newConfigDir.mkpath(newConfigPath);
if (!mkPathRes) {
return;
}
QFile newConfigFile(newConfigPath + "/AmneziaVPN.conf");
if (!newConfigFile.exists()) {
bool cpResult = QFile::copy(oldConfig.fileName(), newConfigFile.fileName());
if (cpResult) {
oldConfig.remove();
QDir oldConfigDir(rootLocation + "/files/.config");
oldConfigDir.rmdir("AmneziaVPN.ORG");
}
}
}
#endif
}

24
client/migrations.h Normal file
View file

@ -0,0 +1,24 @@
#ifndef MIGRATIONS_H
#define MIGRATIONS_H
#include <QObject>
class Migrations : public QObject
{
Q_OBJECT
public:
explicit Migrations(QObject *parent = nullptr);
void doMigrations();
private:
void migrateV3();
private:
int currentMajor = 0;
int currentMinor = 0;
int currentMicro = 0;
int currentPatch = 0;
};
#endif // MIGRATIONS_H

View file

@ -15,6 +15,7 @@
#include "private/qandroidextras_p.h"
#include "ui/pages_logic/StartPageLogic.h"
#include "androidvpnactivity.h"
#include "androidutils.h"
namespace {
@ -54,6 +55,10 @@ AndroidController::AndroidController() : QObject()
isConnected = doc.object()["connected"].toBool();
if (isConnected) {
emit scheduleStatusCheckSignal();
}
emit initialized(
true, isConnected,
time > 0 ? QDateTime::fromMSecsSinceEpoch(time) : QDateTime());
@ -66,9 +71,11 @@ AndroidController::AndroidController() : QObject()
Q_UNUSED(parcelBody);
qDebug() << "Transact: connected";
isConnected = true;
if (!isConnected) {
emit scheduleStatusCheckSignal();
}
emit scheduleStatusCheckSignal();
isConnected = true;
emit connectionStateChanged(VpnProtocol::Connected);
}, Qt::QueuedConnection);
@ -85,7 +92,6 @@ AndroidController::AndroidController() : QObject()
connect(activity, &AndroidVPNActivity::eventStatisticUpdate, this,
[this](const QString& parcelBody) {
qDebug() << "Transact: update";
auto doc = QJsonDocument::fromJson(parcelBody.toUtf8());
QString rx = doc.object()["rx_bytes"].toString();
@ -203,12 +209,7 @@ void AndroidController::setNotificationText(const QString& title,
}
void AndroidController::shareConfig(const QString& configContent, const QString& suggestedName) {
QJsonObject rootObject;
rootObject["data"] = configContent;
rootObject["suggestedName"] = suggestedName;
QJsonDocument doc(rootObject);
AndroidVPNActivity::sendToService(ServiceAction::ACTION_SHARE_CONFIG, doc.toJson());
AndroidVPNActivity::saveFileAs(configContent, suggestedName);
}
/*
@ -248,7 +249,7 @@ void AndroidController::cleanupBackendLogs() {
}
void AndroidController::importConfig(const QString& data){
m_startPageLogic->importConnectionFromCode(data);
m_startPageLogic->selectConfigFormat(data);
}
const QJsonObject &AndroidController::vpnConfig() const
@ -266,6 +267,11 @@ void AndroidController::startQrReaderActivity()
AndroidVPNActivity::instance()->startQrCodeReader();
}
void AndroidController::copyTextToClipboard(QString text)
{
AndroidVPNActivity::instance()->copyTextToClipboard(text);
}
void AndroidController::scheduleStatusCheckSlot()
{
QTimer::singleShot(1000, [this]() {

View file

@ -11,7 +11,6 @@
#include "ui/pages_logic/StartPageLogic.h"
#include "protocols/vpnprotocol.h"
#include "androidvpnactivity.h"
using namespace amnezia;
@ -44,6 +43,7 @@ public:
void setVpnConfig(const QJsonObject &newVpnConfig);
void startQrReaderActivity();
void copyTextToClipboard(QString text);
signals:
void connectionStateChanged(VpnProtocol::VpnConnectionState state);

View file

@ -57,6 +57,22 @@ void AndroidVPNActivity::startQrCodeReader()
QJniObject::callStaticMethod<void>(CLASSNAME, "startQrCodeReader", "()V");
}
void AndroidVPNActivity::saveFileAs(QString fileContent, QString suggestedFilename) {
QJniObject::callStaticMethod<void>(
CLASSNAME,
"saveFileAs", "(Ljava/lang/String;Ljava/lang/String;)V",
QJniObject::fromString(fileContent).object<jstring>(),
QJniObject::fromString(suggestedFilename).object<jstring>());
}
void AndroidVPNActivity::copyTextToClipboard(QString text)
{
QJniObject::callStaticMethod<void>(
CLASSNAME,
"putTextToClipboard", "(Ljava/lang/String;)V",
QJniObject::fromString(text).object<jstring>());
}
// static
AndroidVPNActivity* AndroidVPNActivity::instance() {
if (s_instance == nullptr) {
@ -70,9 +86,9 @@ AndroidVPNActivity* AndroidVPNActivity::instance() {
void AndroidVPNActivity::sendToService(ServiceAction type, const QString& data) {
int messageType = (int)type;
QJniEnvironment env;
QJniObject::callStaticMethod<void>(
CLASSNAME, "sendToService", "(ILjava/lang/String;)V",
CLASSNAME,
"sendToService", "(ILjava/lang/String;)V",
static_cast<int>(messageType),
QJniObject::fromString(data).object<jstring>());
}

View file

@ -75,6 +75,8 @@ public:
static void sendToService(ServiceAction type, const QString& data);
static void connectService();
static void startQrCodeReader();
static void saveFileAs(QString fileContent, QString suggestedFilename);
static void copyTextToClipboard(QString text);
signals:
void serviceConnected();

View file

@ -8,8 +8,7 @@
#include "wireguardprotocol.h"
#include "utilities.h"
WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject* parent) :
VpnProtocol(configuration, parent)
WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject* parent) : VpnProtocol(configuration, parent)
{
m_configFile.setFileName(QDir::tempPath() + QDir::separator() + serviceName() + ".conf");
writeWireguardConfiguration(configuration);
@ -47,11 +46,8 @@ void WireguardProtocol::stop()
m_wireguardStopProcess->setProgram(PermittedProcess::Wireguard);
QStringList arguments({"--remove", configPath()});
m_wireguardStopProcess->setArguments(arguments);
qDebug() << arguments.join(" ");
m_wireguardStopProcess->setArguments(stopArgs());
qDebug() << stopArgs().join(" ");
connect(m_wireguardStopProcess.data(), &PrivilegedProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
qDebug() << "WireguardProtocol::WireguardProtocol Stop errorOccurred" << error;
@ -62,12 +58,25 @@ void WireguardProtocol::stop()
qDebug() << "WireguardProtocol::WireguardProtocol Stop stateChanged" << newState;
});
#ifdef Q_OS_LINUX
if (IpcClient::Interface()) {
QRemoteObjectPendingReply<bool> result = IpcClient::Interface()->isWireguardRunning();
if (result.returnValue()) {
setConnectionState(VpnProtocol::Disconnected);
return;
}
} else {
qCritical() << "IPC client not initialized";
setConnectionState(VpnProtocol::Disconnected);
return;
}
#endif
m_wireguardStopProcess->start();
m_wireguardStopProcess->waitForFinished(10000);
setConnectionState(VpnProtocol::Disconnected);
#endif
}
void WireguardProtocol::writeWireguardConfiguration(const QJsonObject &configuration)
@ -79,13 +88,28 @@ void WireguardProtocol::writeWireguardConfiguration(const QJsonObject &configura
return;
}
m_isConfigLoaded = true;
m_configFile.write(jConfig.value(config_key::config).toString().toUtf8());
m_configFile.close();
m_configFileName = m_configFile.fileName();
qDebug().noquote() << QString("Set config data") << m_configFileName;
#ifdef Q_OS_LINUX
if (IpcClient::Interface()) {
QRemoteObjectPendingReply<bool> result = IpcClient::Interface()->copyWireguardConfig(m_configFile.fileName());
if (result.returnValue()) {
qCritical() << "Failed to copy wireguard config";
return;
}
} else {
qCritical() << "IPC client not initialized";
return;
}
m_configFileName = "/etc/wireguard/wg99.conf";
#else
m_configFileName = m_configFile.fileName();
#endif
m_isConfigLoaded = true;
qDebug().noquote() << QString("Set config data") << configPath();
qDebug().noquote() << QString("Set config data") << configuration.value(ProtocolProps::key_proto_config_data(Proto::WireGuard)).toString().toUtf8();
}
@ -120,8 +144,15 @@ ErrorCode WireguardProtocol::start()
return lastError();
}
if (!QFileInfo::exists(configPath())) {
setLastError(ErrorCode::ConfigMissing);
if (IpcClient::Interface()) {
QRemoteObjectPendingReply<bool> result = IpcClient::Interface()->isWireguardConfigExists(configPath());
if (result.returnValue()) {
setLastError(ErrorCode::ConfigMissing);
return lastError();
}
} else {
qCritical() << "IPC client not initialized";
setLastError(ErrorCode::InternalError);
return lastError();
}
@ -143,11 +174,8 @@ ErrorCode WireguardProtocol::start()
m_wireguardStartProcess->setProgram(PermittedProcess::Wireguard);
QStringList arguments({"--add", configPath()});
m_wireguardStartProcess->setArguments(arguments);
qDebug() << arguments.join(" ");
m_wireguardStartProcess->setArguments(startArgs());
qDebug() << startArgs().join(" ");
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
qDebug() << "WireguardProtocol::WireguardProtocol errorOccurred" << error;
@ -176,7 +204,7 @@ ErrorCode WireguardProtocol::start()
connect(m_wireguardStartProcess.data(), &PrivilegedProcess::readyReadStandardError, this, [this]() {
QRemoteObjectPendingReply<QByteArray> reply = m_wireguardStartProcess->readAllStandardError();
reply.waitForFinished(1000);
reply.waitForFinished(10);
qDebug() << "WireguardProtocol::WireguardProtocol readAllStandardError" << reply.returnValue();
});
@ -204,10 +232,33 @@ void WireguardProtocol::updateVpnGateway(const QString &line)
// qDebug() << QString("Set vpn local address %1, gw %2").arg(m_vpnLocalAddress).arg(vpnGateway());
// }
// }
// }
// }
}
QString WireguardProtocol::serviceName() const
{
return "AmneziaVPN.WireGuard0";
}
QStringList WireguardProtocol::stopArgs()
{
#ifdef Q_OS_WIN
return {"--remove", configPath()};
#elif defined Q_OS_LINUX
return {"down", "wg99"};
#else
return {"--remove", configPath()};
#endif
}
QStringList WireguardProtocol::startArgs()
{
#ifdef Q_OS_WIN
return {"--add", configPath()};
#elif defined Q_OS_LINUX
return {"up", "wg99"};
#else
return {"--add", configPath()};
#endif
}

View file

@ -28,7 +28,8 @@ private:
void updateRouteGateway(QString line);
void updateVpnGateway(const QString &line);
QString serviceName() const;
QStringList stopArgs();
QStringList startArgs();
private:
QString m_configFileName;

View file

@ -165,6 +165,7 @@
<file>ui/qml/Controls/PopupWithQuestion.qml</file>
<file>ui/qml/Pages/PageAdvancedServerSettings.qml</file>
<file>ui/qml/Controls/PopupWarning.qml</file>
<file>ui/qml/Controls/PopupWithTextField.qml</file>
<file>server_scripts/check_user_in_sudo.sh</file>
</qresource>
</RCC>

View file

@ -1,3 +1,3 @@
sudo docker ps | grep amnezia | awk '{print $1}' | xargs sudo docker stop
sudo docker ps | grep amnezia | awk '{print $1}' | xargs sudo docker rm
sudo docker ps -a | grep amnezia | awk '{print $1}' | xargs sudo docker stop
sudo docker ps -a | grep amnezia | awk '{print $1}' | xargs sudo docker rm
sudo docker images -a | grep amnezia | awk '{print $3}' | xargs sudo docker rmi

View file

@ -44,11 +44,11 @@ void AdvancedServerSettingsLogic::onPushButtonClearServerClicked()
uiLogic()->pageLogic<VpnLogic>()->onDisconnect();
}
ErrorCode e = m_serverController->removeAllContainers(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex));
m_serverController->disconnectFromHost(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex));
if (e) {
ServerController serverController(m_settings);
ErrorCode errorCode = serverController.removeAllContainers(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex));
if (errorCode) {
emit uiLogic()->showWarningMessage(tr("Error occurred while cleaning the server.") + "\n" +
tr("Error message: ") + errorString(e) + "\n" +
tr("Error message: ") + errorString(errorCode) + "\n" +
tr("See logs for details."));
} else {
set_labelWaitInfoVisible(true);
@ -69,7 +69,7 @@ void AdvancedServerSettingsLogic::onPushButtonScanServerClicked()
bool isServerCreated;
auto containersCount = m_settings->containers(uiLogic()->m_selectedServerIndex).size();
ErrorCode errorCode = uiLogic()->addAlreadyInstalledContainersGui(false, isServerCreated);
ErrorCode errorCode = uiLogic()->addAlreadyInstalledContainersGui(isServerCreated);
if (errorCode != ErrorCode::NoError) {
emit uiLogic()->showWarningMessage(tr("Error occurred while scanning the server.") + "\n" +
tr("Error message: ") + errorString(errorCode) + "\n" +

View file

@ -8,6 +8,7 @@
#include <QDesktopServices>
#include <QFileDialog>
#include <QStandardPaths>
#include <utilities.h>
using namespace amnezia;
using namespace PageEnumNS;
@ -82,8 +83,8 @@ void AppSettingsLogic::onPushButtonBackupAppConfigClicked()
void AppSettingsLogic::onPushButtonRestoreAppConfigClicked()
{
QString fileName = QFileDialog::getOpenFileName(Q_NULLPTR, tr("Open backup"),
QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*.backup");
QString fileName = UiLogic::getOpenFileName(Q_NULLPTR, tr("Open backup"),
QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*.backup");
if (fileName.isEmpty()) return;
@ -98,6 +99,5 @@ void AppSettingsLogic::onPushButtonRestoreAppConfigClicked()
} else {
emit uiLogic()->showWarningMessage(tr("Can't import config, file is corrupted."));
}
}

View file

@ -13,7 +13,7 @@ GeneralSettingsLogic::GeneralSettingsLogic(UiLogic *logic, QObject *parent):
void GeneralSettingsLogic::onUpdatePage()
{
uiLogic()->m_selectedServerIndex = m_settings->defaultServerIndex();
set_existsAnyServer(uiLogic()->m_selectedServerIndex >= 0);
set_existsAnyServer(m_settings->serversCount() > 0);
uiLogic()->m_selectedDockerContainer = m_settings->defaultContainer(m_settings->defaultServerIndex());
set_pushButtonGeneralSettingsShareConnectionEnable(m_settings->haveAuthData(m_settings->defaultServerIndex()));

View file

@ -11,7 +11,6 @@ PageLogicBase::PageLogicBase(UiLogic *logic, QObject *parent):
{
m_settings = logic->m_settings;
m_configurator = logic->m_configurator;
m_serverController = logic->m_serverController;
}

View file

@ -28,7 +28,6 @@ protected:
std::shared_ptr<Settings> m_settings;
std::shared_ptr<VpnConfigurator> m_configurator;
std::shared_ptr<ServerController> m_serverController;
signals:
void updatePage();

View file

@ -96,19 +96,18 @@ ErrorCode ServerConfiguringProgressLogic::doInstallAction(const std::function<Er
progress.setValueFunc(0);
timer.start(1000);
ServerController serverController(m_settings);
QMetaObject::Connection cancelDoInstallActionConnection;
if (cancelButton.setVisibleFunc) {
cancelDoInstallActionConnection = connect(this, &ServerConfiguringProgressLogic::cancelDoInstallAction,
m_serverController.get(), &ServerController::setCancelInstallation);
&serverController, &ServerController::setCancelInstallation);
}
QMetaObject::Connection serverBusyConnection;
if (serverBusyInfo.setVisibleFunc && serverBusyInfo.setTextFunc) {
serverBusyConnection = connect(m_serverController.get(),
&ServerController::serverIsBusy,
this,
[&serverBusyInfo, &timer, &cancelButton](const bool isBusy) {
auto onServerIsBusy = [&serverBusyInfo, &timer, &cancelButton](const bool isBusy) {
isBusy ? timer.stop() : timer.start(1000);
serverBusyInfo.setVisibleFunc(isBusy);
serverBusyInfo.setTextFunc(isBusy ? "Amnesia has detected that your server is currently "
@ -118,7 +117,9 @@ ErrorCode ServerConfiguringProgressLogic::doInstallAction(const std::function<Er
if (cancelButton.setVisibleFunc) {
cancelButton.setVisibleFunc(isBusy ? true : false);
}
});
};
serverBusyConnection = connect(&serverController, &ServerController::serverIsBusy, this, onServerIsBusy);
}
ErrorCode e = action();
@ -182,5 +183,5 @@ ErrorCode ServerConfiguringProgressLogic::doInstallAction(const std::function<Er
void ServerConfiguringProgressLogic::onPushButtonCancelClicked()
{
cancelDoInstallAction(true);
emit cancelDoInstallAction(true);
}

View file

@ -4,9 +4,7 @@
#include <QApplication>
#include "protocols/CloakLogic.h"
#include "protocols/OpenVpnLogic.h"
#include "protocols/ShadowSocksLogic.h"
#include "protocols/PageProtocolLogicBase.h"
#include "core/servercontroller.h"
#include <functional>
@ -31,6 +29,7 @@ void ServerContainersLogic::onUpdatePage()
p_model->setSelectedServerIndex(uiLogic()->m_selectedServerIndex);
set_isManagedServer(m_settings->haveAuthData(uiLogic()->m_selectedServerIndex));
uiLogic()->m_installCredentials = m_settings->serverCredentials(uiLogic()->m_selectedServerIndex);
emit updatePage();
}
@ -69,7 +68,8 @@ void ServerContainersLogic::onPushButtonShareClicked(DockerContainer c)
void ServerContainersLogic::onPushButtonRemoveClicked(DockerContainer container)
{
//buttonSetEnabledFunc(false);
ErrorCode e = m_serverController->removeContainer(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex), container);
ServerController serverController(m_settings);
ErrorCode e = serverController.removeContainer(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex), container);
m_settings->removeContainerConfig(uiLogic()->m_selectedServerIndex, container);
//buttonSetEnabledFunc(true);
@ -83,18 +83,20 @@ void ServerContainersLogic::onPushButtonRemoveClicked(DockerContainer container)
void ServerContainersLogic::onPushButtonContinueClicked(DockerContainer c, int port, TransportProto tp)
{
QJsonObject config = m_serverController->createContainerInitialConfig(c, port, tp);
ServerController serverController(m_settings);
QJsonObject config = serverController.createContainerInitialConfig(c, port, tp);
emit uiLogic()->goToPage(Page::ServerConfiguringProgress);
qApp->processEvents();
bool isServerCreated = false;
ErrorCode errorCode = uiLogic()->addAlreadyInstalledContainersGui(false, isServerCreated);
ErrorCode errorCode = uiLogic()->addAlreadyInstalledContainersGui(isServerCreated);
if (errorCode == ErrorCode::NoError) {
if (!uiLogic()->isContainerAlreadyAddedToGui(c)) {
auto installAction = [this, c, &config]() {
return m_serverController->setupContainer(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex), c, config);
ServerController serverController(m_settings);
return serverController.setupContainer(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex), c, config);
};
errorCode = uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->doInstallAction(installAction);

View file

@ -21,7 +21,6 @@ void ServerListLogic::onServerListPushbuttonDefaultClicked(int index)
void ServerListLogic::onServerListPushbuttonSettingsClicked(int index)
{
uiLogic()->m_selectedServerIndex = index;
uiLogic()->m_installCredentials = m_settings->serverCredentials(index);
uiLogic()->goToPage(Page::ServerSettings);
}

View file

@ -6,9 +6,11 @@
#include "configurators/vpn_configurator.h"
#include "../uilogic.h"
#include "utilities.h"
#include "core/servercontroller.h"
#include <QFileDialog>
#include <QStandardPaths>
#include <QEventLoop>
#ifdef Q_OS_ANDROID
#include <QJniObject>
@ -94,8 +96,7 @@ void StartPageLogic::onPushButtonConnect()
set_labelWaitInfoText(tr("Please fill in all fields"));
return;
}
}
else {
} else {
if (lineEditIpText().isEmpty() ||
lineEditLoginText().isEmpty() ||
lineEditPasswordText().isEmpty() ) {
@ -111,7 +112,7 @@ void StartPageLogic::onPushButtonConnect()
serverCredentials.hostName = serverCredentials.hostName.split(":").at(0);
}
serverCredentials.userName = lineEditLoginText();
if (pushButtonConnectKeyChecked()){
if (pushButtonConnectKeyChecked()) {
QString key = textEditSshKeyText();
if (key.startsWith("ssh-rsa")) {
emit uiLogic()->showPublicKeyWarning();
@ -123,28 +124,44 @@ void StartPageLogic::onPushButtonConnect()
}
serverCredentials.password = key;
}
else {
} else {
serverCredentials.password = lineEditPasswordText();
}
set_pushButtonConnectEnabled(false);
set_pushButtonConnectText(tr("Connecting..."));
ErrorCode e = ErrorCode::NoError;
#ifdef Q_DEBUG
//QString output = m_serverController->checkSshConnection(serverCredentials, &e);
#else
ServerController serverController(m_settings);
ErrorCode errorCode = ErrorCode::NoError;
if (pushButtonConnectKeyChecked()) {
auto passphraseCallback = [this, &serverController]() {
emit showPassphraseRequestMessage();
QEventLoop loop;
QObject::connect(this, &StartPageLogic::passphraseDialogClosed, &loop, &QEventLoop::quit);
loop.exec();
return m_privateKeyPassphrase;
};
QString decryptedPrivateKey;
errorCode = serverController.getDecryptedPrivateKey(serverCredentials, decryptedPrivateKey, passphraseCallback);
if (errorCode == ErrorCode::NoError) {
serverCredentials.password = decryptedPrivateKey;
}
}
QString output;
#endif
if (errorCode == ErrorCode::NoError) {
output = serverController.checkSshConnection(serverCredentials, &errorCode);
}
bool ok = true;
if (e) {
if (errorCode) {
set_labelWaitInfoVisible(true);
set_labelWaitInfoText(errorString(e));
set_labelWaitInfoText(errorString(errorCode));
ok = false;
}
else {
} else {
if (output.contains("Please login as the user")) {
output.replace("\n", "");
set_labelWaitInfoVisible(true);
@ -167,7 +184,7 @@ void StartPageLogic::onPushButtonImport()
void StartPageLogic::onPushButtonImportOpenFile()
{
QString fileName = QFileDialog::getOpenFileName(Q_NULLPTR, tr("Open config file"),
QString fileName = UiLogic::getOpenFileName(Q_NULLPTR, tr("Open config file"),
QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*.vpn *.ovpn *.conf");
if (fileName.isEmpty()) return;
@ -175,14 +192,7 @@ void StartPageLogic::onPushButtonImportOpenFile()
file.open(QIODevice::ReadOnly);
QByteArray data = file.readAll();
auto configFormat = checkConfigFormat(QString(data));
if (configFormat == ConfigTypes::OpenVpn) {
importConnectionFromOpenVpnConfig(QString(data));
} else if (configFormat == ConfigTypes::WireGuard) {
importConnectionFromWireguardConfig(QString(data));
} else {
importConnectionFromCode(QString(data));
}
selectConfigFormat(QString(data));
}
#ifdef Q_OS_ANDROID
@ -192,6 +202,18 @@ void StartPageLogic::startQrDecoder()
}
#endif
void StartPageLogic::selectConfigFormat(QString configData)
{
auto configFormat = checkConfigFormat(configData);
if (configFormat == ConfigTypes::OpenVpn) {
importConnectionFromOpenVpnConfig(configData);
} else if (configFormat == ConfigTypes::WireGuard) {
importConnectionFromWireguardConfig(configData);
} else {
importConnectionFromCode(configData);
}
}
bool StartPageLogic::importConnection(const QJsonObject &profile)
{
ServerCredentials credentials;

View file

@ -23,6 +23,8 @@ class StartPageLogic : public PageLogicBase
AUTO_PROPERTY(QString, labelWaitInfoText)
AUTO_PROPERTY(bool, pushButtonBackFromStartVisible)
AUTO_PROPERTY(QString, privateKeyPassphrase);
READONLY_PROPERTY(QRegularExpression, ipAddressPortRegex)
public:
Q_INVOKABLE void onUpdatePage() override;
@ -35,6 +37,8 @@ public:
Q_INVOKABLE void startQrDecoder();
#endif
void selectConfigFormat(QString configData);
bool importConnection(const QJsonObject &profile);
bool importConnectionFromCode(QString code);
bool importConnectionFromQr(const QByteArray &data);
@ -45,5 +49,8 @@ public:
explicit StartPageLogic(UiLogic *uiLogic, QObject *parent = nullptr);
~StartPageLogic() = default;
signals:
void showPassphraseRequestMessage();
void passphraseDialogClosed();
};
#endif // START_PAGE_LOGIC_H

View file

@ -112,15 +112,16 @@ void CloakLogic::onPushButtonSaveClicked()
progressBarFunc.setTextVisibleFunc(true);
progressBarFunc.setTextFunc(QString("Configuring..."));
ErrorCode e = uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->doInstallAction([this, containerConfig, &newContainerConfig](){
return m_serverController->updateContainer(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex),
uiLogic()->m_selectedDockerContainer,
containerConfig,
newContainerConfig);
},
pageFunc, progressBarFunc,
saveButtonFunc, waitInfoFunc,
busyInfoFuncy, cancelButtonFunc);
auto installAction = [this, containerConfig, &newContainerConfig]() {
ServerController serverController(m_settings);
return serverController.updateContainer(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex),
uiLogic()->m_selectedDockerContainer, containerConfig, newContainerConfig);
};
ErrorCode e = uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->doInstallAction(installAction, pageFunc, progressBarFunc,
saveButtonFunc, waitInfoFunc,
busyInfoFuncy, cancelButtonFunc);
if (!e) {
m_settings->setContainerConfig(uiLogic()->m_selectedServerIndex, uiLogic()->m_selectedDockerContainer, newContainerConfig);

View file

@ -162,15 +162,16 @@ void OpenVpnLogic::onPushButtonSaveClicked()
progressBarFunc.setTextVisibleFunc(true);
progressBarFunc.setTextFunc(QString("Configuring..."));
ErrorCode e = uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->doInstallAction([this, containerConfig, &newContainerConfig](){
return m_serverController->updateContainer(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex),
uiLogic()->m_selectedDockerContainer,
containerConfig,
newContainerConfig);
},
pageFunc, progressBarFunc,
saveButtonFunc, waitInfoFunc,
busyInfoFuncy, cancelButtonFunc);
auto installAction = [this, containerConfig, &newContainerConfig]() {
ServerController serverController(m_settings);
return serverController.updateContainer(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex),
uiLogic()->m_selectedDockerContainer, containerConfig, newContainerConfig);
};
ErrorCode e = uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->doInstallAction(installAction, pageFunc, progressBarFunc,
saveButtonFunc, waitInfoFunc,
busyInfoFuncy, cancelButtonFunc);
if (!e) {
m_settings->setContainerConfig(uiLogic()->m_selectedServerIndex, uiLogic()->m_selectedDockerContainer, newContainerConfig);

View file

@ -104,15 +104,15 @@ void ShadowSocksLogic::onPushButtonSaveClicked()
progressBarFunc.setTextVisibleFunc(true);
progressBarFunc.setTextFunc(QString("Configuring..."));
ErrorCode e = uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->doInstallAction([this, containerConfig, &newContainerConfig](){
return m_serverController->updateContainer(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex),
uiLogic()->m_selectedDockerContainer,
containerConfig,
newContainerConfig);
},
pageFunc, progressBarFunc,
saveButtonFunc, waitInfoFunc,
busyInfoFuncy, cancelButtonFunc);
auto installAction = [this, containerConfig, &newContainerConfig]() {
ServerController serverController(m_settings);
return serverController.updateContainer(m_settings->serverCredentials(uiLogic()->m_selectedServerIndex),
uiLogic()->m_selectedDockerContainer, containerConfig, newContainerConfig);
};
ErrorCode e = uiLogic()->pageLogic<ServerConfiguringProgressLogic>()->doInstallAction(installAction, pageFunc, progressBarFunc,
saveButtonFunc, waitInfoFunc,
busyInfoFuncy, cancelButtonFunc);
if (!e) {
m_settings->setContainerConfig(uiLogic()->m_selectedServerIndex, uiLogic()->m_selectedDockerContainer, newContainerConfig);

View file

@ -0,0 +1,62 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Popup {
id: root
property alias text: textField.text
property alias placeholderText: textField.placeholderText
property string yesText: "yes"
property string noText: "no"
property var yesFunc
property var noFunc
signal editingFinished()
anchors.centerIn: Overlay.overlay
modal: true
closePolicy: Popup.NoAutoClose
width: parent.width - 20
ColumnLayout {
width: parent.width
TextField {
id: textField
horizontalAlignment: Text.AlignHCenter
Layout.fillWidth: true
font.pixelSize: 16
echoMode: TextInput.Password
}
RowLayout {
Layout.fillWidth: true
BlueButtonType {
id: yesButton
Layout.preferredWidth: parent.width / 2
Layout.fillWidth: true
text: yesText
onClicked: {
root.enabled = false
if (yesFunc && typeof yesFunc === "function") {
yesFunc()
}
root.enabled = true
}
}
BlueButtonType {
id: noButton
Layout.preferredWidth: parent.width / 2
Layout.fillWidth: true
text: noText
onClicked: {
if (noFunc && typeof noFunc === "function") {
noFunc()
}
}
}
}
}
}

View file

@ -10,6 +10,14 @@ PageBase {
page: PageEnum.Start
logic: StartPageLogic
Connections {
target: StartPageLogic
function onShowPassphraseRequestMessage() {
popupWithTextField.open()
}
}
BackButton {
id: back_from_start
visible: pageLoader.depth > 1
@ -325,4 +333,22 @@ PageBase {
}
}
}
PopupWithTextField {
id: popupWithTextField
placeholderText: "Enter private key passphrase"
yesFunc: function() {
editingFinished()
close()
StartPageLogic.passphraseDialogClosed()
text = ""
}
noFunc: function() {
close()
StartPageLogic.passphraseDialogClosed()
}
onEditingFinished: {
StartPageLogic.privateKeyPassphrase = text
}
}
}

View file

@ -61,8 +61,12 @@ Window {
function close_page() {
if (pageLoader.depth <= 1) {
if (GC.isMobile()) {
root.close()
}
return
}
pageLoader.currentItem.deactivated()
pageLoader.pop()
}

View file

@ -3,7 +3,6 @@
#include <QDebug>
#include <QDesktopServices>
#include <QFile>
#include <QFileDialog>
#include <QHostInfo>
#include <QItemSelectionModel>
#include <QJsonDocument>
@ -78,16 +77,14 @@ using namespace amnezia;
using namespace PageEnumNS;
UiLogic::UiLogic(std::shared_ptr<Settings> settings, std::shared_ptr<VpnConfigurator> configurator,
std::shared_ptr<ServerController> serverController,
QObject *parent) :
QObject(parent),
m_settings(settings),
m_configurator(configurator),
m_serverController(serverController)
m_configurator(configurator)
{
m_containersModel = new ContainersModel(settings, this);
m_protocolsModel = new ProtocolsModel(settings, this);
m_vpnConnection = new VpnConnection(settings, configurator, serverController);
m_vpnConnection = new VpnConnection(settings, configurator);
m_vpnConnection->moveToThread(&m_vpnConnectionThread);
m_vpnConnectionThread.start();
@ -133,6 +130,7 @@ void UiLogic::initalizeUiLogic()
connect(AndroidController::instance(), &AndroidController::initialized, [this](bool status, bool connected, const QDateTime& connectionDate) {
if (connected) {
pageLogic<VpnLogic>()->onConnectionStateChanged(VpnProtocol::Connected);
m_vpnConnection->restoreConnection();
}
});
if (!AndroidController::instance()->initialize(pageLogic<StartPageLogic>())) {
@ -224,9 +222,10 @@ void UiLogic::keyPressEvent(Qt::Key key)
m_configurator->sshConfigurator->openSshTerminal(m_settings->serverCredentials(m_settings->defaultServerIndex()));
break;
case Qt::Key_Escape:
case Qt::Key_Back:
if (currentPage() == Page::Vpn) break;
if (currentPage() == Page::ServerConfiguringProgress) break;
case Qt::Key_Back:
// if (currentPage() == Page::Start && pagesStack.size() < 2) break;
// if (currentPage() == Page::Sites &&
// ui->tableView_sites->selectionModel()->selection().indexes().size() > 0) {
@ -243,10 +242,16 @@ void UiLogic::keyPressEvent(Qt::Key key)
void UiLogic::onCloseWindow()
{
if (m_settings->serversCount() == 0) qApp->quit();
else {
hide();
#ifdef Q_OS_ANDROID
qApp->quit();
#else
if (m_settings->serversCount() == 0)
{
qApp->quit();
} else {
emit hide();
}
#endif
}
QString UiLogic::containerName(int container)
@ -324,18 +329,17 @@ void UiLogic::installServer(QPair<DockerContainer, QJsonObject> &container)
};
bool isServerCreated = false;
ErrorCode errorCode = addAlreadyInstalledContainersGui(true, isServerCreated);
ErrorCode errorCode = addAlreadyInstalledContainersGui(isServerCreated);
if (errorCode == ErrorCode::NoError) {
if (!isContainerAlreadyAddedToGui(container.first)) {
progressBarFunc.setTextFunc(QString("Installing %1").arg(ContainerProps::containerToString(container.first)));
auto installAction = [&] () {
return m_serverController->setupContainer(m_installCredentials, container.first, container.second);
ServerController serverController(m_settings);
return serverController.setupContainer(m_installCredentials, container.first, container.second);
};
errorCode = pageLogic<ServerConfiguringProgressLogic>()->doInstallAction(installAction, pageFunc, progressBarFunc,
noButton, waitInfoFunc,
busyInfoFunc, cancelButtonFunc);
m_serverController->disconnectFromHost(m_installCredentials);
if (errorCode == ErrorCode::NoError) {
if (!isServerCreated) {
QJsonObject server;
@ -466,7 +470,11 @@ void UiLogic::saveBinaryFile(const QString &desc, QString ext, const QString &da
void UiLogic::copyToClipboard(const QString &text)
{
#ifdef Q_OS_ANDROID
AndroidController::instance()->copyTextToClipboard(text);
#else
qApp->clipboard()->setText(text);
#endif
}
void UiLogic::shareTempFile(const QString &suggestedName, QString ext, const QString& data) {
@ -488,6 +496,24 @@ void UiLogic::shareTempFile(const QString &suggestedName, QString ext, const QSt
MobileUtils::shareText(filesToSend);
}
QString UiLogic::getOpenFileName(QWidget *parent, const QString &caption, const QString &dir,
const QString &filter, QString *selectedFilter, QFileDialog::Options options)
{
QString fileName = QFileDialog::getOpenFileName(parent, caption, dir, filter, selectedFilter, options);
#ifdef Q_OS_ANDROID
// patch for files containing spaces etc
const QString sep {"raw%3A%2F"};
if (fileName.startsWith("content://") && fileName.contains(sep)) {
QString contentUrl = fileName.split(sep).at(0);
QString rawUrl = fileName.split(sep).at(1);
rawUrl.replace(" ", "%20");
fileName = contentUrl + sep + rawUrl;
}
#endif
return fileName;
}
void UiLogic::registerPagesLogic()
{
amnApp->qmlEngine()->rootContext()->setContextProperty("UiLogic", this);
@ -510,19 +536,27 @@ void UiLogic::registerPagesLogic()
registerPageLogic<AdvancedServerSettingsLogic>();
}
ErrorCode UiLogic::addAlreadyInstalledContainersGui(bool createNewServer, bool &isServerCreated)
ErrorCode UiLogic::addAlreadyInstalledContainersGui(bool &isServerCreated)
{
isServerCreated = false;
ServerCredentials credentials;
if (createNewServer) {
credentials = m_installCredentials;
} else {
credentials = m_settings->serverCredentials(m_selectedServerIndex);
ServerCredentials installCredentials = m_installCredentials;
bool createNewServer = true;
int serverIndex;
for (int i = 0; i < m_settings->serversCount(); i++) {
const ServerCredentials credentials = m_settings->serverCredentials(i);
if (m_installCredentials.hostName == credentials.hostName && m_installCredentials.port == credentials.port) {
createNewServer = false;
isServerCreated = true;
installCredentials = credentials;
serverIndex = i;
break;
}
}
QMap<DockerContainer, QJsonObject> installedContainers;
ErrorCode errorCode = m_serverController->getAlreadyInstalledContainers(credentials, installedContainers);
m_serverController->disconnectFromHost(credentials);
ServerController serverController(m_settings);
ErrorCode errorCode = serverController.getAlreadyInstalledContainers(installCredentials, installedContainers);
if (errorCode != ErrorCode::NoError) {
return errorCode;
}
@ -531,10 +565,10 @@ ErrorCode UiLogic::addAlreadyInstalledContainersGui(bool createNewServer, bool &
QJsonObject server;
QJsonArray containerConfigs;
if (createNewServer) {
server.insert(config_key::hostName, credentials.hostName);
server.insert(config_key::userName, credentials.userName);
server.insert(config_key::password, credentials.password);
server.insert(config_key::port, credentials.port);
server.insert(config_key::hostName, installCredentials.hostName);
server.insert(config_key::userName, installCredentials.userName);
server.insert(config_key::password, installCredentials.password);
server.insert(config_key::port, installCredentials.port);
server.insert(config_key::description, m_settings->nextAvailableServerName());
}
@ -547,8 +581,8 @@ ErrorCode UiLogic::addAlreadyInstalledContainersGui(bool createNewServer, bool &
containerConfigs.append(container.value());
server.insert(config_key::containers, containerConfigs);
} else {
m_settings->setContainerConfig(m_selectedServerIndex, container.key(), container.value());
m_settings->setDefaultContainer(m_selectedServerIndex, installedContainers.firstKey());
m_settings->setContainerConfig(serverIndex, container.key(), container.value());
m_settings->setDefaultContainer(serverIndex, installedContainers.firstKey());
}
}
@ -576,3 +610,4 @@ bool UiLogic::isContainerAlreadyAddedToGui(DockerContainer container)
}
return false;
}

View file

@ -1,12 +1,13 @@
#ifndef UILOGIC_H
#define UILOGIC_H
#include <QRegularExpressionValidator>
#include <QQmlEngine>
#include <functional>
#include <QFileDialog>
#include <QKeyEvent>
#include <QRegularExpressionValidator>
#include <QThread>
#include <QQmlEngine>
#include <functional>
#include <typeindex>
#include <typeinfo>
#include <unordered_map>
@ -62,14 +63,12 @@ class UiLogic : public QObject
AUTO_PROPERTY(bool, pageEnabled)
AUTO_PROPERTY(int, pagesStackDepth)
AUTO_PROPERTY(int, currentPageValue)
AUTO_PROPERTY(QString, popupWarningText)
READONLY_PROPERTY(QObject *, containersModel)
READONLY_PROPERTY(QObject *, protocolsModel)
public:
explicit UiLogic(std::shared_ptr<Settings> settings, std::shared_ptr<VpnConfigurator> configurator,
std::shared_ptr<ServerController> serverController, QObject *parent = nullptr);
explicit UiLogic(std::shared_ptr<Settings> settings, std::shared_ptr<VpnConfigurator> configurator, QObject *parent = nullptr);
~UiLogic();
void showOnStartup();
@ -117,10 +116,15 @@ public:
Q_INVOKABLE void saveBinaryFile(const QString& desc, QString ext, const QString& data);
Q_INVOKABLE void copyToClipboard(const QString& text);
Q_INVOKABLE amnezia::ErrorCode addAlreadyInstalledContainersGui(bool createNewServer, bool &isServerCreated);
Q_INVOKABLE amnezia::ErrorCode addAlreadyInstalledContainersGui(bool &isServerCreated);
void shareTempFile(const QString &suggestedName, QString ext, const QString& data);
static QString getOpenFileName(QWidget *parent = nullptr,
const QString &caption = QString(),
const QString &dir = QString(),
const QString &filter = QString(),
QString *selectedFilter = nullptr,
QFileDialog::Options options = QFileDialog::Options());
signals:
void goToPage(PageEnumNS::Page page, bool reset = true, bool slide = true);
void goToProtocolPage(Proto protocol, bool reset = true, bool slide = true);
@ -181,7 +185,6 @@ private:
std::shared_ptr<Settings> m_settings;
std::shared_ptr<VpnConfigurator> m_configurator;
std::shared_ptr<ServerController> m_serverController;
NotificationHandler* m_notificationHandler;

View file

@ -231,7 +231,7 @@ QString Utils::wireguardExecPath()
#ifdef Q_OS_WIN
return Utils::executable("wireguard/wireguard-service", true);
#elif defined Q_OS_LINUX
return Utils::usrExecutable("wg");
return Utils::usrExecutable("wg-quick");
#else
return Utils::executable("/wireguard", true);
#endif

View file

@ -50,7 +50,6 @@ public:
static QString wireguardExecPath();
static QString certUtilPath();
#ifdef Q_OS_WIN
static bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent);
#endif

View file

@ -20,7 +20,6 @@
#ifdef Q_OS_ANDROID
#include "../../platforms/android/android_controller.h"
#include "protocols/android_vpnprotocol.h"
#endif
#ifdef Q_OS_IOS
@ -31,11 +30,9 @@
#include "vpnconnection.h"
VpnConnection::VpnConnection(std::shared_ptr<Settings> settings,
std::shared_ptr<VpnConfigurator> configurator,
std::shared_ptr<ServerController> serverController, QObject* parent) : QObject(parent),
std::shared_ptr<VpnConfigurator> configurator, QObject* parent) : QObject(parent),
m_settings(settings),
m_configurator(configurator),
m_serverController(serverController),
m_isIOSConnected(false)
{
}
@ -353,10 +350,8 @@ void VpnConnection::connectToVpn(int serverIndex,
}
m_vpnProtocol->prepare();
#elif defined Q_OS_ANDROID
Proto proto = ContainerProps::defaultProtocol(container);
AndroidVpnProtocol *androidVpnProtocol = new AndroidVpnProtocol(proto, m_vpnConfiguration);
connect(AndroidController::instance(), &AndroidController::connectionStateChanged, androidVpnProtocol, &AndroidVpnProtocol::setConnectionState);
connect(AndroidController::instance(), &AndroidController::statusUpdated, androidVpnProtocol, &AndroidVpnProtocol::connectionDataUpdated);
androidVpnProtocol = createDefaultAndroidVpnProtocol(container);
createAndroidConnections(container);
m_vpnProtocol.reset(androidVpnProtocol);
#elif defined Q_OS_IOS
@ -373,16 +368,52 @@ void VpnConnection::connectToVpn(int serverIndex,
m_vpnProtocol.reset(iosVpnProtocol);
#endif
connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(VpnProtocol::VpnConnectionState)), this, SLOT(onConnectionStateChanged(VpnProtocol::VpnConnectionState)));
connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64)));
m_serverController->disconnectFromHost(credentials);
createProtocolConnections();
e = m_vpnProtocol.data()->start();
if (e) emit VpnProtocol::Error;
}
void VpnConnection::createProtocolConnections() {
connect(m_vpnProtocol.data(), &VpnProtocol::protocolError, this, &VpnConnection::vpnProtocolError);
connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(VpnProtocol::VpnConnectionState)), this, SLOT(onConnectionStateChanged(VpnProtocol::VpnConnectionState)));
connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64)));
}
#ifdef Q_OS_ANDROID
void VpnConnection::restoreConnection() {
createAndroidConnections();
m_vpnProtocol.reset(androidVpnProtocol);
createProtocolConnections();
}
void VpnConnection::createAndroidConnections()
{
int serverIndex = m_settings->defaultServerIndex();
DockerContainer container = m_settings->defaultContainer(serverIndex);
createAndroidConnections(container);
}
void VpnConnection::createAndroidConnections(DockerContainer container)
{
androidVpnProtocol = createDefaultAndroidVpnProtocol(container);
connect(AndroidController::instance(), &AndroidController::connectionStateChanged, androidVpnProtocol, &AndroidVpnProtocol::setConnectionState);
connect(AndroidController::instance(), &AndroidController::statusUpdated, androidVpnProtocol, &AndroidVpnProtocol::connectionDataUpdated);
}
AndroidVpnProtocol* VpnConnection::createDefaultAndroidVpnProtocol(DockerContainer container)
{
Proto proto = ContainerProps::defaultProtocol(container);
AndroidVpnProtocol *androidVpnProtocol = new AndroidVpnProtocol(proto, m_vpnConfiguration);
return androidVpnProtocol;
}
#endif
QString VpnConnection::bytesPerSecToText(quint64 bytes)
{
double mbps = bytes * 8 / 1e6;
@ -401,8 +432,6 @@ void VpnConnection::disconnectFromVpn()
}
#endif
if (!m_vpnProtocol.data()) {
emit connectionStateChanged(VpnProtocol::Disconnected);
#ifdef Q_OS_ANDROID
@ -415,11 +444,8 @@ void VpnConnection::disconnectFromVpn()
VpnProtocol::VpnConnectionState VpnConnection::connectionState()
{
if (!m_vpnProtocol) return VpnProtocol::Disconnected;
return m_vpnProtocol->connectionState();
}
bool VpnConnection::isConnected() const

View file

@ -18,6 +18,10 @@
#include "core/ipcclient.h"
#endif
#ifdef Q_OS_ANDROID
#include "protocols/android_vpnprotocol.h"
#endif
class VpnConfigurator;
class ServerController;
@ -29,8 +33,7 @@ class VpnConnection : public QObject
public:
explicit VpnConnection(std::shared_ptr<Settings> settings,
std::shared_ptr<VpnConfigurator> configurator,
std::shared_ptr<ServerController> serverController, QObject* parent = nullptr);
std::shared_ptr<VpnConfigurator> configurator, QObject* parent = nullptr);
~VpnConnection() override;
static QString bytesPerSecToText(quint64 bytes);
@ -61,6 +64,10 @@ public:
const QString &remoteAddress() const;
void addSitesRoutes(const QString &gw, Settings::RouteMode mode);
#ifdef Q_OS_ANDROID
void restoreConnection();
#endif
public slots:
void connectToVpn(int serverIndex,
const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig);
@ -88,7 +95,6 @@ protected:
private:
std::shared_ptr<Settings> m_settings;
std::shared_ptr<VpnConfigurator> m_configurator;
std::shared_ptr<ServerController> m_serverController;
QJsonObject m_vpnConfiguration;
QJsonObject m_routeMode;
@ -101,6 +107,15 @@ private:
#ifdef Q_OS_IOS
IOSVpnProtocol * iosVpnProtocol{nullptr};
#endif
#ifdef Q_OS_ANDROID
AndroidVpnProtocol* androidVpnProtocol = nullptr;
AndroidVpnProtocol* createDefaultAndroidVpnProtocol(DockerContainer container);
void createAndroidConnections();
void createAndroidConnections(DockerContainer container);
#endif
void createProtocolConnections();
};
#endif // VPNCONNECTION_H