Commit 98d14226 authored by Oliver Wiese's avatar Oliver Wiese

Merge branch 'dev'

Conflicts:
	enzevalos_iphone.xcodeproj/project.pbxproj
	enzevalos_iphone.xcodeproj/project.pbxproj.orig
	enzevalos_iphone/AppDelegate.swift
	enzevalos_iphone/AuthenticationModel.swift
	enzevalos_iphone/AuthenticationScreen.swift
	enzevalos_iphone/AuthenticationViewModel.swift
	enzevalos_iphone/Base.lproj/Main.storyboard
	enzevalos_iphone/CHelpers.swift
	enzevalos_iphone/Certificate.swift
	enzevalos_iphone/MailAccount.swift
	enzevalos_iphone/SMIME.swift
	enzevalos_iphone/SMIMEHelpers.swift
	enzevalos_iphone/SearchHelper.swift
	enzevalos_iphone/Settings.bundle/Acknowledgements.plist
	enzevalos_iphone/SwiftUI/Inbox/Inbox.swift
	enzevalos_iphone/SwiftUI/SupportingViews/MailView.swift
	enzevalos_iphone/SwiftUI/SupportingViews/SearchView.swift
	enzevalos_iphone/c/certificate-helpers.c
	enzevalos_iphone/c/certificate-helpers.h
	enzevalos_iphone/c/general-helpers.c
	enzevalos_iphone/c/generic-helpers.h
	enzevalos_iphone/c/smime-helpers.c
	enzevalos_iphone/c/smime-helpers.h
	enzevalos_iphone/de.lproj/Localizable.strings
	enzevalos_iphone/en.lproj/Localizable.strings
	enzevalos_iphoneTests/GeneratedMocks.swift
parents eb9f2fed eb9f2fed
This diff is collapsed.
......@@ -425,37 +425,6 @@ third-party archives.
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>The MIT License (MIT)
Copyright (c) 2016 Brightify.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>Cuckoo</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>Copyright (c) 2012 Alexey Denisov
......
//
// ReadMainView.swift
// enzevalos_iphone
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import SwiftUI
struct ReadMainView: View {
public var mail:PersistentMail
public var coord: ReadViewCoordinator
@State var currentScreen: Int = 1 //doesnt work
@State var isSecIndExpanded:Bool=false
var body: some View {
//TODO: put into zstack instead
VStack(spacing:0){
SecIndicator //TODO: Why the hell does expanding it reset the whole tabview
TabView{
ForEach(Tabs, id: \.id ){ tab in
tab.content
.tabItem {
tab.image
Text(tab.description)
}
}
}
.animation(.easeInOut(duration: 0.4))
}
.navigationBarTitle(Text((mail.trouble ? "❌" :( mail.isSecure ? "🔒" : "👀" )) + " " + (mail.subject ?? ""))
, displayMode: .inline) //TODO: make smooth or find better solution
.navigationBarItems(trailing: moreInfoButton)
.onAppear(perform: self.coord.setup)
.onDisappear(perform: self.coord.reset)
}
//TODO: not use AnyView-workaround
var Tabs:[Tab] {
get {
return [
Tab(
image: Image(systemName: "person.fill"),
description: "sender",
content: AnyView(SenderViewMain(mail: mail))
),
Tab(
image: Image(systemName: "text.bubble.fill"),
description: "message",
content: AnyView(MessageViewMain(coord: coord, mail: mail))
),
Tab(
image: Image(systemName: "rectangle.and.paperclip"),
description: "attachments",
content: AnyView(AttachmentsViewMain(mail: mail, coord: coord))
)
]
}
}
var moreInfoButton: some View{
return Button(action: {self.isSecIndExpanded.toggle()}){
Image(systemName: isSecIndExpanded ? "arrow.up.circle" : "info.circle")
}
}
//securityIndicator
var SecIndicator:some View{
get{
if mail.trouble{
return AnyView(SecInd(secNum:1,isExpanded: $isSecIndExpanded, color:Color(ThemeManager.troubleMessageColor())))
}else if mail.isSecure{
return AnyView(VStack{
SecInd(secNum:2,isExpanded: $isSecIndExpanded,color:Color(ThemeManager.encryptedMessageColor()))
//TODO: Design and test
Button(action: {
self.coord.pushExportKeyView()
//let duration = Date().timeIntervalSince(opening)
//Logger.log(close: icon, mail: m, action: .exportKey, duration: duration)
//self.performSegue(withIdentifier: "exportKeyFromReadView", sender: nil)
}){
Text(NSLocalizedString("ReadMailOnOtherDevice", comment: "email is not readable on other devices"))
}
})
}
//special subcases of unsecure mail
else if mail.isCorrectlySigned {
return AnyView(SecInd(secNum:4,isExpanded: $isSecIndExpanded,color:Color(ThemeManager.unencryptedMessageColor())))
} else if mail.isEncrypted && !mail.unableToDecrypt {
return AnyView(SecInd(secNum:5,isExpanded: $isSecIndExpanded,color:Color(ThemeManager.unencryptedMessageColor())))
} else if mail.isEncrypted && mail.unableToDecrypt {
return AnyView(SecInd(secNum:6,isExpanded: $isSecIndExpanded,color:Color(ThemeManager.unencryptedMessageColor())))
}
else{
return AnyView(SecInd(secNum:3,isExpanded: $isSecIndExpanded,color:Color(ThemeManager.unencryptedMessageColor())))
}
}
}
}
struct Tab{
let id = UUID()
var image : Image = Image(systemName: "tag.fill")
var description : String = "---"
var content : AnyView = AnyView(Text("Still in development"))
}
struct SecInd:View{
var secNum:UInt8=0 //1: trouble ; 2:safe ; 3 not safe
@Binding var isExpanded:Bool
var color:Color
@State private var pullInfo:Bool = true;
var body : some View {
VStack(spacing:0){
Rectangle()
.fill(color)
.frame(height: 2)
.frame(height: 2)
if isExpanded {
expandedSecInd(secNum: secNum).onTapGesture {
self.isExpanded = false
}
.padding(/*[.leading, .bottom, .trailing],*/20)
.background(RoundedCorners(color: color, tl: 0, tr: 0, bl: 30, br: 30))
}
if pullInfo {Text("tap info-icon to get more info").font(.system(size: 8)).padding(.vertical,4)}
}
.padding(0)
.background((isExpanded || !pullInfo) ? RoundedCorners(color: color, tl: 0, tr: 0, bl: 30, br: 30) : nil)
.animation(.easeInOut(duration: 1))
.onAppear(perform:{
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
self.pullInfo=false
}}
)
}
}
struct expandedSecInd: View {
var secNum:UInt8 //1: trouble ; 2:safe ; 3 not safe & subcases of unsecure(4,5,6)
var icon = LogData.IndicatorButton.self
var body: some View {
VStack(){
if secNum == 1 {
layout(
title: NSLocalizedString("LetterDamaged", comment: "Modified email received")/*"Angerissener Brief"*/,
text: NSLocalizedString("ReceiveDamagedInfo", comment: "Modified email infotext"),
icon: Image(icon.error.name)//TODO: ask oli how to do this correctly
)
} else if secNum == 2 {
layout(
title: NSLocalizedString("Letter", comment: "letter label"),
text: NSLocalizedString("ReceiveSecureInfo", comment: "Letter infotext"),
icon: Image(icon.confidential.name)//TODO: ask oli how to do this correctly
)
}else if secNum==4 {
layout(
title: NSLocalizedString("Postcard", comment: "postcard label"),
text: NSLocalizedString("ReceiveInsecureInfoVerified", comment: "Postcard infotext"),
icon: Image(icon.signedOnly.name)//TODO: ask oli how to do this correctly
)
} else if secNum==5 {
layout(
title: NSLocalizedString("Postcard", comment: "postcard label"),
text: NSLocalizedString("ReceiveInsecureInfoEncrypted", comment: "Postcard infotext"),
icon: Image(icon.encryptedOnly.name)//TODO: ask oli how to do this correctly
)
} else if secNum==6 {
layout(
title: NSLocalizedString("Postcard", comment: "postcard label"),
text: NSLocalizedString("ReceiveInsecureInfoDecryptionFailed", comment: "Postcard infotext"),
icon: Image(icon.unableToDecrypt.name)//TODO: ask oli how to do this correctly
)
}else if secNum == 3 {
layout(
title: NSLocalizedString("Postcard", comment: "postcard label"),
text: NSLocalizedString("ReceiveInsecureInfo", comment: "Postcard infotext"),
icon: Image("work")//Image(icon.notConfidential.name)//TODO: ask oli how to do this correctly
)
}
}
}
func layout(title:String, text:String, icon:Image)->some View{
//TODO: add a cool layout
return VStack{
HStack{
Spacer()
icon.resizable()
.scaledToFit()
.frame(width:30,height: 30)
Text(title).font(.system(size:22)).fontWeight(.light)
Spacer()
}
Text(text).font(.caption).multilineTextAlignment(.center)
}
}
}
#if DEBUG
struct Layout_Previews: PreviewProvider {
static var previews: some View {
Group {
expandedSecInd(secNum: 2)
}
}
}
#endif
//
// ReadViewCoordinator.swift
// enzevalos_iphone
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import Foundation
import SwiftUI
class ReadViewCoordinator {
let root: UINavigationController
private let mainStoryboardName = "Main"
private let mainStoryboard: UIStoryboard
private var readView: UIViewController?
private var mail:PersistentMail
init(root: UINavigationController, mail:PersistentMail) {
self.mail = mail
self.root = root
mainStoryboard = UIStoryboard(name: self.mainStoryboardName, bundle: nil)
}
func pushReadView(){
root.isToolbarHidden = true
try? AppDelegate.getAppDelegate().mailHandler.startIMAPIdleIfSupported()
AppDelegate.getAppDelegate().mailHandler.updateFolder(folder: Folder.inbox, completionCallback: {_ in
})
if let vc = readView {
if root.viewControllers.contains(vc) {
while root.topViewController != vc {
root.popViewController(animated: true)
}
} else {
root.pushViewController(vc, animated: true)
}
}
else {
let context = DataHandler.handler.managedObjectContext
let vc = UIHostingController(rootView: ReadMainView(mail: mail, coord: self).environment(\.managedObjectContext,context))
readView = vc
root.pushViewController(vc, animated: true)
}
}
func delete(){
//TODO: make this work
Logger.log(delete: mail, toTrash: true)
AppDelegate.getAppDelegate().mailHandler.move(mails: [mail], from: mail.folder.path, to: UserManager.backendTrashFolderPath)
}
func archive(){
//TODO: make this work
Logger.log(archive: mail)
AppDelegate.getAppDelegate().mailHandler.move(mails: [mail], from: mail.folder.path, to: UserManager.backendArchiveFolderPath)
}
func pushComposeView() {
let vc = mainStoryboard.instantiateViewController(identifier: ViewID.ComposeView.rawValue)
if let vc = vc as? SendViewController {
vc.wasPushed = true
}
root.isToolbarHidden = false
root.pushViewController(vc, animated: true)
}
func pushExportKeyView() {
//TODO: make this work
let vc = mainStoryboard.instantiateViewController(identifier: "exportKeyFromReadView")
if let vc = vc as? ImportKeyOverviewController {
//vc.wasPushed = true
}
root.isToolbarHidden = false
root.pushViewController(vc, animated: true)
}
func shareData(_ data:NSData){
//let shareText = "Hello, world!"
//let data:NSData = data
let vc = UIActivityViewController(activityItems: [data], applicationActivities: [])
root.present(vc, animated: true)
}
func setup(){
hideUIKitToolbar()
setupNavigationbar()
}
func reset(){
animateBarColor(ThemeManager.defaultColor)
}
func hideUIKitToolbar(){
root.isToolbarHidden = true
}
func setupNavigationbar(){
///those dont work
//root.isNavigationBarHidden=true
//root.navigationBar.isHidden=true
//root.navigationBar.isTranslucent=true
/*if mail.trouble{animateBarColor(ThemeManager.troubleMessageColor()) }
else{animateBarColor(mail.isSecure ? ThemeManager.encryptedMessageColor() : ThemeManager.unencryptedMessageColor())}*/
}
private func animateBarColor(_ c:UIColor){
//TODO: why desnt it work? it just jumps
UIView.animate(withDuration: 0.3, animations: {
self.root.navigationBar.barTintColor = c })
}
}
//
// AttPreview.swift
// enzevalos_iphone
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import SwiftUI
import QuickLook
//To use the QuickLook in SwiftUI
struct QuickLookView: UIViewControllerRepresentable {
var name: String
var data: Data?
//if it should download the data as a file if not yet downloaded
var shallDL: Bool = true
var allowScaling: Bool = true
func makeCoordinator() -> QuickLookView.Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> QLPreviewController {
let controller = QLPreviewController()
//the coordinator provides the real preview data as a NSURL
controller.dataSource = context.coordinator
return controller
}
func updateUIViewController(_ controller: QLPreviewController,
context: Context) {
print("updated")
controller.reloadData()//not beautiful but works
}
class Coordinator: NSObject, QLPreviewControllerDataSource {
let parent: QuickLookView
init(_ parent: QuickLookView) {
self.parent = parent
super.init()
}
// The QLPreviewController asks its delegate how many items it has:
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
// For each item (see method above), the QLPreviewController asks for
// a QLPreviewItem instance describing that item:
func previewController(
_ controller: QLPreviewController,
previewItemAt index: Int
) -> QLPreviewItem {
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
let str = parent.data!
//This is where the file is stored
let filename = getDocumentsDirectory().appendingPathComponent(parent.name)
if parent.shallDL{
do {
try str.write(to: filename)
} catch {
// failed to write file – bad permissions, bad filename, missing permissions
}
}
_ = parent.name.components(separatedBy: ".")
let path = filename
return path as NSURL
}
}
}
//
// CardWithTitle.swift
// enzevalos_iphone
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import SwiftUI
// MARK: a CardView with title
struct CardV<Content:View>: View{
var elevation:CGFloat = 3
var title:String?
var actions:[AnyView]?
var actionsVertical:Bool=false
var content: () -> Content
@State private var isAdvancedVisable:Bool = false
@/*Gesture*/State private var isTapDownB:Bool = false
private let colo=Color(red:0.85,green:0.85,blue:0.88)
var body: some View{
ZStack {
VStack{
VStack{
content()
.background(Color.white)
.clipShape(
RoundedRectangle(cornerRadius: 15)
)
.padding(3)
.opacity(isTapDownB ? 0.7 : 1)
if title != nil {
Text(title!).padding(7)
}
}
.background(isTapDownB ? colo : Color.white)
.clipShape(
RoundedRectangle(cornerRadius: 15)
)
.gesture (
TapGesture()
/*.updating($isTapDownB, body: { (currentState, state, transaction) in
//sadly doesnt work ; iwant to make it visable if a user touches
self.isTapDown=state
})*/
.onEnded{ _ in
self.isTapDownB.toggle()
self.isAdvancedVisable.toggle()
}
)
if isAdvancedVisable{
Advanced.padding(.horizontal,10)
}
}
.shadow(color: isTapDownB ? Color.clear : colo , radius: 2*elevation, x: elevation, y:elevation)
}
.padding(.leading, 10)
.padding(2*elevation)
.padding(.bottom,isTapDownB ? elevation : 2*elevation)
.animation(.easeIn(duration: 0.2))
.scaleEffect(isTapDownB ? 0.82 : 1)
.opacity(isTapDownB ? 0.9 : 1)
}
private func Actions(_ nr:Int)-> some View{
self.actions![nr]
.padding(10)
.background(Color.white)
.clipShape(
RoundedRectangle(cornerRadius: 15)
)
.shadow(color: colo , radius: 2*elevation, x: elevation, y:elevation)
}
//the button section beneath
private var Advanced: AnyView{
if actions != nil{
if actionsVertical{
return AnyView(VStack{
ForEach(0..<actions!.count){ i in
self.Actions(i)
}
})
}else{
return AnyView(HStack{
ForEach(0..<actions!.count){ i in
self.Actions(i)
}
})
}
}
return AnyView(EmptyView())
}
}
//
// AttachmentsViewMain.swift
// enzevalos_iphone
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.