Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Localization/Localizations/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -1368,6 +1368,9 @@
}
}
}
},
"Basic Authentication" : {

},
"Books" : {
"extractionState" : "manual",
Expand Down
4 changes: 4 additions & 0 deletions qBitControl.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
90BCFE8D2DB4262400A54003 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 90BCFE8C2DB4262400A54003 /* Localizable.xcstrings */; };
90DCA7E52B51C393008A9C1B /* ServersHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90DCA7E42B51C393008A9C1B /* ServersHelper.swift */; };
90E1D0542D30438F00B81F12 /* Version.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90E1D0532D30438C00B81F12 /* Version.swift */; };
90E6DD6F2DFF407000BC1D76 /* ServerAdvancedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90E6DD6E2DFF406A00BC1D76 /* ServerAdvancedView.swift */; };
90E86FFE2C81C89A00F4EA01 /* qBitDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90E86FFD2C81C89A00F4EA01 /* qBitDataClass.swift */; };
90EF6A492909267A001E9E7F /* AuthClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90EF6A482909267A001E9E7F /* AuthClass.swift */; };
90EF6A4D29093142001E9E7F /* qBitRequestClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90EF6A4C29093142001E9E7F /* qBitRequestClass.swift */; };
Expand Down Expand Up @@ -194,6 +195,7 @@
90BCFE8C2DB4262400A54003 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
90DCA7E42B51C393008A9C1B /* ServersHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServersHelper.swift; sourceTree = "<group>"; };
90E1D0532D30438C00B81F12 /* Version.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Version.swift; sourceTree = "<group>"; };
90E6DD6E2DFF406A00BC1D76 /* ServerAdvancedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerAdvancedView.swift; sourceTree = "<group>"; };
90E86FFD2C81C89A00F4EA01 /* qBitDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = qBitDataClass.swift; sourceTree = "<group>"; };
90EF6A482909267A001E9E7F /* AuthClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthClass.swift; sourceTree = "<group>"; };
90EF6A4C29093142001E9E7F /* qBitRequestClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = qBitRequestClass.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -315,6 +317,7 @@
90726B382CDA22190032993E /* ServersViews */ = {
isa = PBXGroup;
children = (
90E6DD6E2DFF406A00BC1D76 /* ServerAdvancedView.swift */,
90726B352CDA22190032993E /* ServerAddView.swift */,
90726B362CDA22190032993E /* ServerRowView.swift */,
90726B372CDA22190032993E /* ServersView.swift */,
Expand Down Expand Up @@ -649,6 +652,7 @@
907C632A2DAEDACA004B1F41 /* SearchStartResult.swift in Sources */,
90726B652CDA22190032993E /* PeersRowView.swift in Sources */,
90726B662CDA22190032993E /* PeersView.swift in Sources */,
90E6DD6F2DFF407000BC1D76 /* ServerAdvancedView.swift in Sources */,
90726B672CDA22190032993E /* TrackerRow.swift in Sources */,
907023322CDA62FD007B2199 /* TorrentListView.swift in Sources */,
90726B682CDA22190032993E /* TrackersView.swift in Sources */,
Expand Down
9 changes: 8 additions & 1 deletion qBitControl/Classes/AuthClass.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class Auth {
cookies[id] = cookie
}

static func getCookie(url: String, username: String, password: String, isSuccess: @escaping (Bool) -> Void, setCookie: Bool = true) async {
static func getCookie(url: String, username: String, password: String, basicAuth: Server.BasicAuth? = nil, isSuccess: @escaping (Bool) -> Void, setCookie: Bool = true) async {
let urlString = url
guard let url = URL(string: "\(url)/api/v2/auth/login") else { return }

Expand All @@ -37,6 +37,12 @@ class Auth {

let sessionConfiguration = URLSessionConfiguration.default
sessionConfiguration.timeoutIntervalForRequest = 10
if let basicAuth = basicAuth {
sessionConfiguration.httpAdditionalHeaders = [
"Authorization": "Basic \(basicAuth.getAuthString())"
]
}

let session = URLSession(configuration: sessionConfiguration)

await session.reset()
Expand All @@ -48,6 +54,7 @@ class Auth {
if setCookie {
qBittorrent.setURL(url: urlString)
qBittorrent.setCookie(cookie: cookie)
qBitRequest.setBasicAuth(auth: basicAuth)
}
isSuccess(true)
} else {
Expand Down
4 changes: 2 additions & 2 deletions qBitControl/Classes/ServersHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ class ServersHelper: ObservableObject {

func checkConnection(server: Server, result: @escaping (Bool) -> Void) {
Task {
await Auth.getCookie(url: server.url, username: server.username, password: server.password, isSuccess: {
await Auth.getCookie(url: server.url, username: server.username, password: server.password, basicAuth: server.basicAuth, isSuccess: {
success in
result(success);
}, setCookie: false)
Expand All @@ -107,7 +107,7 @@ class ServersHelper: ObservableObject {
connectingServerId = server.id

Task {
await Auth.getCookie(url: server.url, username: server.username, password: server.password, isSuccess: {
await Auth.getCookie(url: server.url, username: server.username, password: server.password, basicAuth: server.basicAuth, isSuccess: {
success in
DispatchQueue.main.async {
if let result = result {
Expand Down
52 changes: 36 additions & 16 deletions qBitControl/Classes/qBitRequestClass.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,26 @@ import Foundation


class qBitRequest {
static private var basicAuth: Server.BasicAuth?

static func setBasicAuth(auth: Server.BasicAuth?) {
self.basicAuth = auth
}

private static func getSession() -> URLSession {
let configuration = URLSessionConfiguration.default

if let basicAuth = qBitRequest.basicAuth {
// Set the auth header
configuration.httpAdditionalHeaders = [
"Authorization": "Basic \(basicAuth.getAuthString())"
]
}

// Return a new session with this configuration
return URLSession(configuration: configuration)
}

static func prepareURLRequest(path: String, queryItems: [URLQueryItem]) -> URLRequest {
let cookie = qBittorrent.getCookie()
let url = qBittorrent.getURL()
Expand Down Expand Up @@ -51,7 +71,7 @@ class qBitRequest {
}

static func requestTorrentListJSON(request: URLRequest, completionHandler: @escaping ([Torrent]) -> Void) {
URLSession.shared.dataTask(with: request) {
self.getSession().dataTask(with: request) {
data, response, error in
if let data = data {
do {
Expand All @@ -65,13 +85,13 @@ class qBitRequest {
}

static func requestUniversal(request: URLRequest) {
URLSession.shared.dataTask(with: request) {
self.getSession().dataTask(with: request) {
data, response, error in
}.resume()
}

static func requestTorrentManagement(request: URLRequest, statusCode: @escaping (Int?) -> Void) {
URLSession.shared.dataTask(with: request) {
self.getSession().dataTask(with: request) {
data, response, error in
if let response = response as? HTTPURLResponse {
statusCode(response.statusCode)
Expand All @@ -82,7 +102,7 @@ class qBitRequest {
}

static func requestPreferencesJSON(request: URLRequest, completionHandler: @escaping (qBitPreferences) -> Void) {
URLSession.shared.dataTask(with: request) {
self.getSession().dataTask(with: request) {
data, response, error in
if let data = data {
do {
Expand All @@ -96,7 +116,7 @@ class qBitRequest {
}

static func requestSearchStart(request: URLRequest, completionHandler: @escaping (SearchStartResult) -> Void) {
URLSession.shared.dataTask(with: request) {
self.getSession().dataTask(with: request) {
data, response, error in
if let data = data {
do {
Expand All @@ -110,7 +130,7 @@ class qBitRequest {
}

static func requestSearchResults(request: URLRequest, completionHandler: @escaping (SearchResponse) -> Void) {
URLSession.shared.dataTask(with: request) {
self.getSession().dataTask(with: request) {
data, response, error in
if let data = data {
do {
Expand All @@ -124,7 +144,7 @@ class qBitRequest {
}

static func requestSearchPlugins(request: URLRequest, completionHandler: @escaping ([SearchPlugin]) -> Void) {
URLSession.shared.dataTask(with: request) {
self.getSession().dataTask(with: request) {
data, response, error in
if let data = data {
do {
Expand All @@ -138,7 +158,7 @@ class qBitRequest {
}

static func requestGlobalTransferInfo(request: URLRequest, completionHandler: @escaping (GlobalTransferInfo) -> Void) {
URLSession.shared.dataTask(with: request) {
self.getSession().dataTask(with: request) {
data, response, error in
if let data = data {
do {
Expand All @@ -152,7 +172,7 @@ class qBitRequest {
}

static func requestMainData(request: URLRequest, completionHandler: @escaping (MainData) -> Void) {
URLSession.shared.dataTask(with: request) {
self.getSession().dataTask(with: request) {
data, response, error in
if let data = data {
do {
Expand All @@ -167,7 +187,7 @@ class qBitRequest {


static func requestPeersJSON(request: URLRequest, completionHandler: @escaping (Peers) -> Void) {
URLSession.shared.dataTask(with: request) {
self.getSession().dataTask(with: request) {
data, response, error in
if let data = data {
do {
Expand All @@ -181,7 +201,7 @@ class qBitRequest {
}

static func requestTrackersJSON(request: URLRequest, completionHandler: @escaping ([Tracker]) -> Void) {
URLSession.shared.dataTask(with: request) {
self.getSession().dataTask(with: request) {
data, response, error in
if let data = data {
do {
Expand All @@ -195,7 +215,7 @@ class qBitRequest {
}

static func requestFilesJSON(request: URLRequest, completionHandler: @escaping ([File]) -> Void) {
URLSession.shared.dataTask(with: request) {
self.getSession().dataTask(with: request) {
data, response, error in
if let data = data {
do {
Expand All @@ -209,7 +229,7 @@ class qBitRequest {
}

static func requestCategoriesJSON(request: URLRequest, completionHandler: @escaping ([String: Category]) -> Void) {
URLSession.shared.dataTask(with: request) {
self.getSession().dataTask(with: request) {
data, response, error in
if let data = data {
do {
Expand All @@ -223,7 +243,7 @@ class qBitRequest {
}

static func requestVersion(request: URLRequest, completionHandler: @escaping (Version) -> Void) {
URLSession.shared.dataTask(with: request) {
self.getSession().dataTask(with: request) {
data, response, error in
if let data = data, let versionData = String(data: data, encoding: .utf8) {
let versionString = versionData.filter { "0123456789.".contains($0) }
Expand All @@ -237,7 +257,7 @@ class qBitRequest {
}

static func requestTagsJSON(request: URLRequest, completionHandler: @escaping ([String]) -> Void) {
URLSession.shared.dataTask(with: request) {
self.getSession().dataTask(with: request) {
data, response, error in
if let data = data {
do {
Expand All @@ -251,7 +271,7 @@ class qBitRequest {
}

static func requestRSSFeedJSON(request: URLRequest, completion: @escaping (RSSNode) -> Void) {
URLSession.shared.dataTask(with: request) {
self.getSession().dataTask(with: request) {
data, response, error in

if let data = data {
Expand Down
18 changes: 17 additions & 1 deletion qBitControl/Models/Server.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,20 @@ struct Server: Codable, Identifiable {
let url: String
let username: String
let password: String
}
let basicAuth: BasicAuth?

struct BasicAuth: Codable {
let username: String
let password: String

init(_ username: String, _ password: String) {
self.username = username
self.password = password
}

func getAuthString() -> String {
let authString = "\(username):\(password)".data(using: .utf8)!
return authString.base64EncodedString()
}
}
}
4 changes: 3 additions & 1 deletion qBitControl/ViewModels/ServersView/ServerAddViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class ServerAddViewModel: ObservableObject {
@Published var url = ""
@Published var username = ""
@Published var password = ""
@Published var basicAuth: Server.BasicAuth?
@Published var isCheckConnection = true;

@Published var isInvalidAlert = false;
Expand All @@ -34,6 +35,7 @@ class ServerAddViewModel: ObservableObject {
url = server.url
username = server.username
password = server.password
basicAuth = server.basicAuth
}
}

Expand Down Expand Up @@ -83,7 +85,7 @@ class ServerAddViewModel: ObservableObject {
if(!validateInputs()) { return; }
if(!validateIsConnecting()) { return; }

let server = Server(name: friendlyName, url: url, username: username, password: password)
let server = Server(name: friendlyName, url: url, username: username, password: password, basicAuth: basicAuth)

if(!isCheckConnection) {
if let editServerId = self.editServerId { serversHelper.removeServer(id: editServerId) }
Expand Down
8 changes: 8 additions & 0 deletions qBitControl/Views/ServersViews/ServerAddView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ struct ServerAddView: View {
.autocorrectionDisabled()
}

Section {
NavigationLink {
ServerAdvancedView(basicAuth: $viewModel.basicAuth)
} label: {
Text("Advanced")
}
}

Section {
Toggle(isOn: $viewModel.isCheckConnection, label: {
Text("Check Connection")
Expand Down
51 changes: 51 additions & 0 deletions qBitControl/Views/ServersViews/ServerAdvancedView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import SwiftUI

struct ServerAdvancedView: View {
@Binding var basicAuth: Server.BasicAuth?

@State var isBasicAuthEnabled: Bool = false
@State var username = ""
@State var password = ""

var body: some View {
List {
Section(header: Text("Basic Authentication")) {
self.basicAuthView()
}
}.onAppear { self.restoreValues() }
}

@ViewBuilder
private func basicAuthView() -> some View {
Toggle("Basic Authentication", isOn: self.$isBasicAuthEnabled)
.onChange(of: isBasicAuthEnabled) { _ in self.onChangeHandler() }

if(self.isBasicAuthEnabled) {
TextField("Username", text: self.$username)
.disableAutocorrection(true)
.autocapitalization(.none)
.onChange(of: username) { _ in self.onChangeHandler() }
SecureField("Password", text: self.$password)
.onChange(of: password) { _ in self.onChangeHandler() }
}
}

private func restoreValues() {
if let basicAuth = self.basicAuth {
self.isBasicAuthEnabled = true
self.username = basicAuth.username
self.password = basicAuth.password
}
}

private func onChangeHandler() {
if(self.isBasicAuthEnabled == false) {
self.basicAuth = nil
return
}

if(!username.isEmpty && !password.isEmpty) {
self.basicAuth = Server.BasicAuth(username, password)
}
}
}