mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-04-25 20:42:13 +00:00
Merge branch 'ep/revert-base64' into ep/revert-base64-android
This commit is contained in:
@@ -70,14 +70,14 @@ struct CIImageView: View {
|
||||
}
|
||||
|
||||
private func imageView(_ img: UIImage) -> some View {
|
||||
let w = img.size.width <= img.size.height ? maxWidth * 0.75 : img.imageData == nil ? .infinity : maxWidth
|
||||
let w = img.size.width <= img.size.height ? maxWidth * 0.75 : maxWidth
|
||||
DispatchQueue.main.async { imgWidth = w }
|
||||
return ZStack(alignment: .topTrailing) {
|
||||
if img.imageData == nil {
|
||||
Image(uiImage: img)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(maxWidth: w)
|
||||
.frame(width: w)
|
||||
} else {
|
||||
SwiftyGif(image: img)
|
||||
.frame(width: w, height: w * img.size.height / img.size.width)
|
||||
|
||||
@@ -243,13 +243,13 @@ struct CIVideoView: View {
|
||||
}
|
||||
|
||||
private func imageView(_ img: UIImage) -> some View {
|
||||
let w = img.size.width <= img.size.height ? maxWidth * 0.75 : .infinity
|
||||
let w = img.size.width <= img.size.height ? maxWidth * 0.75 : maxWidth
|
||||
DispatchQueue.main.async { videoWidth = w }
|
||||
return ZStack(alignment: .topTrailing) {
|
||||
Image(uiImage: img)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(maxWidth: w)
|
||||
.frame(width: w)
|
||||
loadingIndicator()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,6 +514,7 @@ struct ChatView: View {
|
||||
chat: chat,
|
||||
chatItem: ci,
|
||||
maxWidth: maxWidth,
|
||||
itemWidth: maxWidth,
|
||||
composeState: $composeState,
|
||||
selectedMember: $selectedMember,
|
||||
chatView: self
|
||||
@@ -526,6 +527,7 @@ struct ChatView: View {
|
||||
@ObservedObject var chat: Chat
|
||||
var chatItem: ChatItem
|
||||
var maxWidth: CGFloat
|
||||
@State var itemWidth: CGFloat
|
||||
@Binding var composeState: ComposeState
|
||||
@Binding var selectedMember: GMember?
|
||||
var chatView: ChatView
|
||||
@@ -654,7 +656,7 @@ struct ChatView: View {
|
||||
playbackState: $playbackState,
|
||||
playbackTime: $playbackTime
|
||||
)
|
||||
.uiKitContextMenu(maxWidth: maxWidth, menu: uiMenu, allowMenu: $allowMenu)
|
||||
.uiKitContextMenu(hasImageOrVideo: ci.content.msgContent?.isImageOrVideo == true, maxWidth: maxWidth, itemWidth: $itemWidth, menu: uiMenu, allowMenu: $allowMenu)
|
||||
.accessibilityLabel("")
|
||||
if ci.content.msgContent != nil && (ci.meta.itemDeleted == nil || revealed) && ci.reactions.count > 0 {
|
||||
chatItemReactions(ci)
|
||||
|
||||
@@ -11,11 +11,20 @@ import UIKit
|
||||
import SwiftUI
|
||||
|
||||
extension View {
|
||||
func uiKitContextMenu(maxWidth: CGFloat, menu: Binding<UIMenu>, allowMenu: Binding<Bool>) -> some View {
|
||||
func uiKitContextMenu(hasImageOrVideo: Bool, maxWidth: CGFloat, itemWidth: Binding<CGFloat>, menu: Binding<UIMenu>, allowMenu: Binding<Bool>) -> some View {
|
||||
Group {
|
||||
if allowMenu.wrappedValue {
|
||||
InteractionView(content: self, maxWidth: maxWidth, menu: menu)
|
||||
.fixedSize(horizontal: true, vertical: false)
|
||||
if hasImageOrVideo {
|
||||
InteractionView(content:
|
||||
self.environmentObject(ChatModel.shared)
|
||||
.overlay(DetermineWidthImageVideoItem())
|
||||
.onPreferenceChange(DetermineWidthImageVideoItem.Key.self) { itemWidth.wrappedValue = $0 == 0 ? maxWidth : $0 }
|
||||
, maxWidth: maxWidth, itemWidth: itemWidth, menu: menu)
|
||||
.frame(maxWidth: itemWidth.wrappedValue)
|
||||
} else {
|
||||
InteractionView(content: self.environmentObject(ChatModel.shared), maxWidth: maxWidth, itemWidth: itemWidth, menu: menu)
|
||||
.fixedSize(horizontal: true, vertical: false)
|
||||
}
|
||||
} else {
|
||||
self
|
||||
}
|
||||
@@ -31,13 +40,14 @@ private class HostingViewHolder: UIView {
|
||||
struct InteractionView<Content: View>: UIViewRepresentable {
|
||||
let content: Content
|
||||
var maxWidth: CGFloat
|
||||
var itemWidth: Binding<CGFloat>
|
||||
@Binding var menu: UIMenu
|
||||
|
||||
func makeUIView(context: Context) -> UIView {
|
||||
let view = HostingViewHolder()
|
||||
view.contentSize = CGSizeMake(maxWidth, .infinity)
|
||||
view.backgroundColor = .clear
|
||||
let hostView = UIHostingController(rootView: content)
|
||||
view.contentSize = hostView.view.intrinsicContentSize
|
||||
hostView.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
let constraints = [
|
||||
hostView.view.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
@@ -57,7 +67,11 @@ struct InteractionView<Content: View>: UIViewRepresentable {
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: UIView, context: Context) {
|
||||
(uiView as! HostingViewHolder).contentSize = uiView.subviews[0].sizeThatFits(CGSizeMake(maxWidth, .infinity))
|
||||
let was = (uiView as! HostingViewHolder).contentSize
|
||||
(uiView as! HostingViewHolder).contentSize = uiView.subviews[0].sizeThatFits(CGSizeMake(itemWidth.wrappedValue, .infinity))
|
||||
if was != (uiView as! HostingViewHolder).contentSize {
|
||||
uiView.invalidateIntrinsicContentSize()
|
||||
}
|
||||
}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
|
||||
@@ -21,6 +21,19 @@ struct DetermineWidth: View {
|
||||
}
|
||||
}
|
||||
|
||||
struct DetermineWidthImageVideoItem: View {
|
||||
typealias Key = MaximumWidthImageVideoPreferenceKey
|
||||
var body: some View {
|
||||
GeometryReader { proxy in
|
||||
Color.clear
|
||||
.preference(
|
||||
key: MaximumWidthImageVideoPreferenceKey.self,
|
||||
value: proxy.size.width
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MaximumWidthPreferenceKey: PreferenceKey {
|
||||
static var defaultValue: CGFloat = 0
|
||||
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
|
||||
@@ -28,6 +41,13 @@ struct MaximumWidthPreferenceKey: PreferenceKey {
|
||||
}
|
||||
}
|
||||
|
||||
struct MaximumWidthImageVideoPreferenceKey: PreferenceKey {
|
||||
static var defaultValue: CGFloat = 0
|
||||
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
|
||||
value = max(value, nextValue())
|
||||
}
|
||||
}
|
||||
|
||||
struct DetermineWidth_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
DetermineWidth()
|
||||
|
||||
@@ -3209,6 +3209,14 @@ public enum MsgContent: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
public var isImageOrVideo: Bool {
|
||||
switch self {
|
||||
case .image: true
|
||||
case .video: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
var cmdString: String {
|
||||
"json \(encodeJSON(self))"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="120"
|
||||
height="120"
|
||||
viewBox="121 0 40 40"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg3"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="m 126.52238,11.425398 5.80302,5.716401 5.88962,-5.889626 2.8582,2.858201 L 135.1836,20 l 5.7164,5.716402 -2.94482,2.8582 -5.7164,-5.629789 -5.88962,5.803014 -2.8582,-2.858201 5.88962,-5.803014 -5.803,-5.716402 z"
|
||||
fill="#030749"
|
||||
id="path1"
|
||||
style="stroke-width:0.866122" />
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="m 137.86858,28.661214 2.94481,-2.944812 v 0 l 5.88963,-5.803014 -5.80302,-5.62979 v 0 l -2.8582,-2.8582 -5.7164,-5.7164023 2.94481,-2.9448129 5.7164,5.7164017 5.88963,-5.8030138 2.8582,2.8582008 -5.88962,5.8030145 5.7164,5.716401 5.88962,-5.803014 2.8582,2.858201 -5.88962,5.803014 5.803,5.716402 -2.9448,2.858201 -5.7164,-5.716402 -5.88963,5.803013 5.7164,5.716402 -2.8582,2.944813 -5.80301,-5.716402 -5.80302,5.803015 -2.8582,-2.858201 z"
|
||||
fill="url(#paint0_linear_40_164)"
|
||||
id="path2"
|
||||
style="fill:url(#paint0_linear_40_164);stroke-width:0.866122" />
|
||||
<defs
|
||||
id="defs3">
|
||||
<linearGradient
|
||||
x1="135.948"
|
||||
y1="-0.81632602"
|
||||
x2="132.09599"
|
||||
y2="36.985699"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="paint0_linear_40_164"
|
||||
gradientTransform="matrix(0.86612147,0,0,0.86612147,18.863485,2.6775707)">
|
||||
<stop
|
||||
stop-color="#01f1ff"
|
||||
id="stop2" />
|
||||
<stop
|
||||
offset="1"
|
||||
stop-color="#0197ff"
|
||||
id="stop3" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -21,6 +21,7 @@ import chat.simplex.common.ui.theme.SimpleXTheme
|
||||
import chat.simplex.common.views.TerminalView
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import kotlinx.coroutines.*
|
||||
import java.awt.event.WindowEvent
|
||||
@@ -103,7 +104,7 @@ private fun ApplicationScope.AppWindow(closedByError: MutableState<Boolean>) {
|
||||
simplexWindowState.windowState = windowState
|
||||
// Reload all strings in all @Composable's after language change at runtime
|
||||
if (remember { ChatController.appPrefs.appLanguage.state }.value != "") {
|
||||
Window(state = windowState, onCloseRequest = { closedByError.value = false; exitApplication() }, onKeyEvent = {
|
||||
Window(state = windowState, icon = painterResource(MR.images.ic_simplex), onCloseRequest = { closedByError.value = false; exitApplication() }, onKeyEvent = {
|
||||
if (it.key == Key.Escape && it.type == KeyEventType.KeyUp) {
|
||||
simplexWindowState.backstack.lastOrNull()?.invoke() != null
|
||||
} else {
|
||||
|
||||
@@ -7,6 +7,8 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.simplexWindowState
|
||||
import java.awt.Window
|
||||
|
||||
@Composable
|
||||
actual fun PlayerView(player: VideoPlayer, width: Dp, onClick: () -> Unit, onLongClick: () -> Unit, stop: () -> Unit) {
|
||||
@@ -23,14 +25,15 @@ actual fun PlayerView(player: VideoPlayer, width: Dp, onClick: () -> Unit, onLon
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function doesn't take into account multi-window environment. In case more windows will be used, modify the code
|
||||
* */
|
||||
@Composable
|
||||
actual fun LocalWindowWidth(): Dp {
|
||||
return with(LocalDensity.current) { (java.awt.Window.getWindows().find { it.isActive }?.width ?: 0).toDp() }
|
||||
/*val density = LocalDensity.current
|
||||
var width by remember { mutableStateOf(with(density) { (java.awt.Window.getWindows().find { it.isActive }?.width ?: 0).toDp() }) }
|
||||
SideEffect {
|
||||
if (width != with(density) { (java.awt.Window.getWindows().find { it.isActive }?.width ?: 0).toDp() })
|
||||
width = with(density) { (java.awt.Window.getWindows().find { it.isActive }?.width ?: 0).toDp() }
|
||||
actual fun LocalWindowWidth(): Dp = with(LocalDensity.current) {
|
||||
val windows = java.awt.Window.getWindows()
|
||||
if (windows.size == 1) {
|
||||
(windows.getOrNull(0)?.width ?: 0).toDp()
|
||||
} else {
|
||||
simplexWindowState.windowState.size.width
|
||||
}
|
||||
return width.also { println("LALAL $it") }*/
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd
|
||||
source-repository-package
|
||||
type: git
|
||||
location: https://github.com/simplex-chat/simplexmq.git
|
||||
tag: bfd532e833aff36754ef766f4e021f0079a7f83c
|
||||
tag: 1e6268cc1dbba69639425f7d5a6c9a07995e0bb2
|
||||
|
||||
source-repository-package
|
||||
type: git
|
||||
@@ -34,12 +34,6 @@ source-repository-package
|
||||
location: https://github.com/simplex-chat/aeson.git
|
||||
tag: aab7b5a14d6c5ea64c64dcaee418de1bb00dcc2b
|
||||
|
||||
-- old bs/text compat for 8.10
|
||||
source-repository-package
|
||||
type: git
|
||||
location: https://github.com/simplex-chat/base64.git
|
||||
tag: 2d77b6dbcaffc00570a70be8694049f3710e7c94
|
||||
|
||||
source-repository-package
|
||||
type: git
|
||||
location: https://github.com/simplex-chat/haskell-terminal.git
|
||||
|
||||
72
docs/rfcs/2024-03-14-super-peers.md
Normal file
72
docs/rfcs/2024-03-14-super-peers.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Large public grups / channels
|
||||
|
||||
## Background
|
||||
|
||||
SimpleX Chat users participate in public groups that were created for small, fully connected p2p groups - working groups, teams, etc. The ability to join the groups via the links was added as an afterthought, without forward looking design, simply to accomodate the interest from the users to use SimpleX platform for public groups and communities. Overall, it's correct to say that the emergence of public groups was unexpected in the context of private messaging, and it shows that protecting participants and publishers identity, and having per-group identity is important for many people.
|
||||
|
||||
## Problems of the current p2p design
|
||||
|
||||
### It doesn't scale to large size
|
||||
|
||||
Current design assumes that each peer is connected to each peer and sends messages to all. It creates non-trivial cost of establishing the connections as the group grows, some abandoned connections when some members remain in "connecting" state and also linearly growing traffic to send each message.
|
||||
|
||||
Historically, there were p2p designs when peers connected not to all but some members, but they were mostly used by desktop clients with more persistent network connections, did not provide any asynchronous delivery and were trading lower traffic for latency and availability. Such designs were viable for file sharing across desktop devices, but it probably would not work well for dynamic real-time communities with active participation from a small share of members and the design of other members to observe the conversation as it happens.
|
||||
|
||||
### It doesn't account for participation asymmetry
|
||||
|
||||
Most members of large public groups do not send messages, so connecting them to all other members directly appears unnecessary and costly, and it also requires tracking when members became inactive to stop sending messages to them.
|
||||
|
||||
## Objectives for the new design
|
||||
|
||||
### Transcript integrity
|
||||
|
||||
This issue is covered in detail in [Group integrity](./2023-10-20-group-integrity.md).
|
||||
|
||||
### Asynchronous delivery to group
|
||||
|
||||
Asynchronous delivery is important to protect participants privacy from traffic observation. In addition to that, sending scheduled posts is quite often a convenient feature to schedule multiple updates for a longer period of time - for example, schedule daily updates for a week, doing it once a week.
|
||||
|
||||
### Ability to conceal members list
|
||||
|
||||
This is a rather common request for the current groups, and while it could be possible of course to hide it on the client level, this still makes data available via the database, and puts less technical users in unfair position, while not protecting users privacy from technically competent members.
|
||||
|
||||
### Support pre-moderation
|
||||
|
||||
As the group size grows, so does the activity of the bad actors. Some groups will benefit from switching all, some, most, or new members to pre-moderation - when each post needs to be approved by admin before it becomes visible to all members. It would slow down the conversations, but it would allow a better content quality and owners' control of the content.
|
||||
|
||||
## Channels based on super-peers
|
||||
|
||||
The proposal is to model the new UX design from Telegram channels, with optional subchannels, and granular participation rights for the members / followers.
|
||||
|
||||
Another consideration is to create democratically governed communities when creators don't own the community but only appoint the initial administrators, but as the community grows it can elect the new admins or moderators from the existing members, where voting power is somehow determined by the community score (which is necessary to compensate for anonymous participants who could subvert the vote if plain vote count was made). This is probably out of scope for the initial implementation, but this idea is very appealing and it doesn't exist in any other decentralised platforms.
|
||||
|
||||
Technologically, the channel or group would determine which super-peers would host the group or channel, with group content being a merkle tree with ability to remove some content creating holes - which seems to be very important quality, both to remove undesirable content and to protect participants privacy.
|
||||
|
||||
Super-peer would manage this merkle-tree state based on the messages from owners, admins and members, with the ability to make some destructive actions confirmed by more than one command. E.g., group/channel deletion may require at least 2 or 3 votes (respectively, for 3 and 5 owners), thus protecting both from accidental deletions and from attacks via owner - one of the owners being compromised won't result in group deletion if 2 votes are required. As the group size grows, owners can also modify rules (which in itself can also require m of n votes).
|
||||
|
||||
## Joining group
|
||||
|
||||
The current model when the link to join the group is, effectively, an address of one of the admins, is not censorship-resistant, reliable or convenient - the admin can be offline, be removed, etc. So we want to somehow include addresses of multiple super-peers to join the group. Without identity-layer, the addresses are quite large already, and including multiple addresses in one link, while possible, would make the qr code very hard to scan. Practically, without creating identity layer, we can use up to 2-3 super-peer addresses for the group, and increase it later. 2 addresses is likely to be satisfactory, as one of them could be super-peer hosted by SimpleX Chat, and another - by group owners.
|
||||
|
||||
The client then will be connecting to all super-peers in the address. Once connected, these super-peers could send the addresses of the additional super-peers, but this is probably unnecessary for the initial release.
|
||||
|
||||
## MVP
|
||||
|
||||
The challenge is to decide what should be in scope for the initial release, to make it a valuable upgrade and a viable starting point, without overloading it with the functions that can be added later.
|
||||
|
||||
MVP scope:
|
||||
|
||||
- Core functioning of the group - creation/deletion/choosing and changing super-peers. While a large scope, it appears essential.
|
||||
- Message delivery via super-peers.
|
||||
- Super-peer protocol extension. Most likely super-peer would receive ordinary chat messages, but some operations should be added and require additional protocol messages - adding/removing super-peers to groups.
|
||||
- Protocol extensions for owner actions with approvals - we already had several accidental deletions or lost owner accounts. Possibly, it is out of MVP scope.
|
||||
- Search and history navigation. Current decision to send 100 messages both creates unnecessary traffic spikes, and also doesn't provide access to older history and search functions. But, possibly, it should also be in follow up improvements, and only should be included as the initial protocol design.
|
||||
- New format of the group address to include more than one super-peer.
|
||||
- Granular permissions and management model. While the user interface can evolve, the protocol and the scenarios, and also rules models seems better to be added from the beginning.
|
||||
|
||||
Follow-up / improvements:
|
||||
- More scalable client - we already observe scalability issues with directory service, so replacing SQLite with Postgres, if the group participation starts growing seems very important.
|
||||
|
||||
Out of scope:
|
||||
- additional super-peers.
|
||||
- smart-contacts. While very tempting to generalise permissions and management model via smart contracts, that would radically increase complexity and delivery time.
|
||||
@@ -18,6 +18,7 @@ dependencies:
|
||||
- async == 2.2.*
|
||||
- attoparsec == 0.14.*
|
||||
- base >= 4.7 && < 5
|
||||
- base64-bytestring >= 1.0 && < 1.3
|
||||
- composition == 1.0.*
|
||||
- constraints >= 0.12 && < 0.14
|
||||
- containers == 0.6.*
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
{
|
||||
"https://github.com/simplex-chat/simplexmq.git"."bfd532e833aff36754ef766f4e021f0079a7f83c" = "1xxcdadllimk2hgzz6aggvywr14zm2h0l62c92yvnyvps9j49gdx";
|
||||
"https://github.com/simplex-chat/simplexmq.git"."1e6268cc1dbba69639425f7d5a6c9a07995e0bb2" = "14pvy7vivvigxj876kwikxcy7c2jc30azwydfgvnlj4xwcclil8q";
|
||||
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
|
||||
"https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d";
|
||||
"https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl";
|
||||
"https://github.com/simplex-chat/aeson.git"."aab7b5a14d6c5ea64c64dcaee418de1bb00dcc2b" = "0jz7kda8gai893vyvj96fy962ncv8dcsx71fbddyy8zrvc88jfrr";
|
||||
"https://github.com/simplex-chat/base64.git"."2d77b6dbcaffc00570a70be8694049f3710e7c94" = "0zdskk67fzqrrx1i29s3shp7fh9c0krmq5h6hq03qx0n3xy2m44b";
|
||||
"https://github.com/simplex-chat/haskell-terminal.git"."f708b00009b54890172068f168bf98508ffcd495" = "0zmq7lmfsk8m340g47g5963yba7i88n4afa6z93sg9px5jv1mijj";
|
||||
"https://github.com/simplex-chat/android-support.git"."9aa09f148089d6752ce563b14c2df1895718d806" = "0pbf2pf13v2kjzi397nr13f1h3jv0imvsq8rpiyy2qyx5vd50pqn";
|
||||
"https://github.com/simplex-chat/zip.git"."bd421c6b19cc4c465cd7af1f6f26169fb8ee1ebc" = "1csqfjhvc8wb5h4kxxndmb6iw7b4ib9ff2n81hrizsmnf45a6gg0";
|
||||
|
||||
@@ -190,6 +190,7 @@ library
|
||||
, async ==2.2.*
|
||||
, attoparsec ==0.14.*
|
||||
, base >=4.7 && <5
|
||||
, base64-bytestring >=1.0 && <1.3
|
||||
, composition ==1.0.*
|
||||
, constraints >=0.12 && <0.14
|
||||
, containers ==0.6.*
|
||||
@@ -251,6 +252,7 @@ executable simplex-bot
|
||||
, async ==2.2.*
|
||||
, attoparsec ==0.14.*
|
||||
, base >=4.7 && <5
|
||||
, base64-bytestring >=1.0 && <1.3
|
||||
, composition ==1.0.*
|
||||
, constraints >=0.12 && <0.14
|
||||
, containers ==0.6.*
|
||||
@@ -313,6 +315,7 @@ executable simplex-bot-advanced
|
||||
, async ==2.2.*
|
||||
, attoparsec ==0.14.*
|
||||
, base >=4.7 && <5
|
||||
, base64-bytestring >=1.0 && <1.3
|
||||
, composition ==1.0.*
|
||||
, constraints >=0.12 && <0.14
|
||||
, containers ==0.6.*
|
||||
@@ -378,6 +381,7 @@ executable simplex-broadcast-bot
|
||||
, async ==2.2.*
|
||||
, attoparsec ==0.14.*
|
||||
, base >=4.7 && <5
|
||||
, base64-bytestring >=1.0 && <1.3
|
||||
, composition ==1.0.*
|
||||
, constraints >=0.12 && <0.14
|
||||
, containers ==0.6.*
|
||||
@@ -441,6 +445,7 @@ executable simplex-chat
|
||||
, async ==2.2.*
|
||||
, attoparsec ==0.14.*
|
||||
, base >=4.7 && <5
|
||||
, base64-bytestring >=1.0 && <1.3
|
||||
, composition ==1.0.*
|
||||
, constraints >=0.12 && <0.14
|
||||
, containers ==0.6.*
|
||||
@@ -510,6 +515,7 @@ executable simplex-directory-service
|
||||
, async ==2.2.*
|
||||
, attoparsec ==0.14.*
|
||||
, base >=4.7 && <5
|
||||
, base64-bytestring >=1.0 && <1.3
|
||||
, composition ==1.0.*
|
||||
, constraints >=0.12 && <0.14
|
||||
, containers ==0.6.*
|
||||
@@ -604,6 +610,7 @@ test-suite simplex-chat-test
|
||||
, async ==2.2.*
|
||||
, attoparsec ==0.14.*
|
||||
, base >=4.7 && <5
|
||||
, base64-bytestring >=1.0 && <1.3
|
||||
, composition ==1.0.*
|
||||
, constraints >=0.12 && <0.14
|
||||
, containers ==0.6.*
|
||||
|
||||
@@ -29,6 +29,7 @@ import qualified Data.Attoparsec.ByteString.Char8 as A
|
||||
import Data.Bifunctor (bimap, first, second)
|
||||
import Data.ByteArray (ScrubbedBytes)
|
||||
import qualified Data.ByteArray as BA
|
||||
import qualified Data.ByteString.Base64 as B64
|
||||
import Data.ByteString.Char8 (ByteString)
|
||||
import qualified Data.ByteString.Char8 as B
|
||||
import qualified Data.ByteString.Lazy.Char8 as LB
|
||||
@@ -103,9 +104,8 @@ import qualified Simplex.Messaging.Crypto.File as CF
|
||||
import Simplex.Messaging.Crypto.Ratchet (PQEncryption (..), PQSupport (..), pattern IKNoPQ, pattern IKPQOff, pattern PQEncOff, pattern PQEncOn, pattern PQSupportOff, pattern PQSupportOn)
|
||||
import qualified Simplex.Messaging.Crypto.Ratchet as CR
|
||||
import Simplex.Messaging.Encoding
|
||||
import Simplex.Messaging.Encoding.Base64 (base64P)
|
||||
import qualified Simplex.Messaging.Encoding.Base64 as B64
|
||||
import Simplex.Messaging.Encoding.String
|
||||
import Simplex.Messaging.Parsers (base64P)
|
||||
import Simplex.Messaging.Protocol (AProtoServerWithAuth (..), AProtocolType (..), EntityId, ErrorType (..), MsgBody, MsgFlags (..), NtfServer, ProtoServerWithAuth, ProtocolTypeI, SProtocolType (..), SubscriptionMode (..), UserProtocol, userProtocol)
|
||||
import qualified Simplex.Messaging.Protocol as SMP
|
||||
import Simplex.Messaging.ServiceScheme (ServiceScheme (..))
|
||||
|
||||
@@ -24,6 +24,7 @@ import qualified Data.Aeson as J
|
||||
import qualified Data.Aeson.Encoding as JE
|
||||
import qualified Data.Aeson.TH as JQ
|
||||
import qualified Data.Attoparsec.ByteString.Char8 as A
|
||||
import qualified Data.ByteString.Base64 as B64
|
||||
import qualified Data.ByteString.Lazy.Char8 as LB
|
||||
import Data.Char (isSpace)
|
||||
import Data.Int (Int64)
|
||||
@@ -47,7 +48,6 @@ import Simplex.Chat.Types.Preferences
|
||||
import Simplex.Messaging.Agent.Protocol (AgentMsgId, MsgMeta (..), MsgReceiptStatus (..))
|
||||
import Simplex.Messaging.Crypto.File (CryptoFile (..))
|
||||
import qualified Simplex.Messaging.Crypto.File as CF
|
||||
import qualified Simplex.Messaging.Encoding.Base64 as B64
|
||||
import Simplex.Messaging.Encoding.String
|
||||
import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, enumJSON, fromTextField_, parseAll, sumTypeJSON)
|
||||
import Simplex.Messaging.Protocol (MsgBody)
|
||||
|
||||
@@ -17,6 +17,7 @@ import qualified Data.Aeson.TH as JQ
|
||||
import Data.Bifunctor (first)
|
||||
import Data.ByteArray (ScrubbedBytes)
|
||||
import qualified Data.ByteArray as BA
|
||||
import qualified Data.ByteString.Base64.URL as U
|
||||
import Data.ByteString.Char8 (ByteString)
|
||||
import qualified Data.ByteString.Char8 as B
|
||||
import qualified Data.ByteString.Lazy.Char8 as LB
|
||||
@@ -49,7 +50,6 @@ import Simplex.Messaging.Agent.Env.SQLite (createAgentStore)
|
||||
import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), MigrationError, closeSQLiteStore, reopenSQLiteStore)
|
||||
import Simplex.Messaging.Client (defaultNetworkConfig)
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import qualified Simplex.Messaging.Encoding.Base64.URL as U
|
||||
import Simplex.Messaging.Encoding.String
|
||||
import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, sumTypeJSON)
|
||||
import Simplex.Messaging.Protocol (AProtoServerWithAuth (..), AProtocolType (..), BasicAuth (..), CorrId (..), ProtoServerWithAuth (..), ProtocolServer (..))
|
||||
|
||||
@@ -17,6 +17,7 @@ import Data.Bifunctor (bimap)
|
||||
import qualified Data.ByteArray as BA
|
||||
import Data.ByteString (ByteString)
|
||||
import qualified Data.ByteString as B
|
||||
import qualified Data.ByteString.Base64.URL as U
|
||||
import Data.Either (fromLeft)
|
||||
import Data.Word (Word8)
|
||||
import Foreign.C (CInt, CString, newCAString)
|
||||
@@ -25,7 +26,6 @@ import Foreign.StablePtr
|
||||
import Simplex.Chat.Controller (ChatController (..))
|
||||
import Simplex.Chat.Mobile.Shared
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import qualified Simplex.Messaging.Encoding.Base64.URL as U
|
||||
import UnliftIO (atomically)
|
||||
|
||||
cChatEncryptMedia :: StablePtr ChatController -> CString -> Ptr Word8 -> CInt -> IO CString
|
||||
|
||||
@@ -22,6 +22,7 @@ import Crypto.Random (getRandomBytes)
|
||||
import qualified Data.Aeson as J
|
||||
import qualified Data.Aeson.Types as JT
|
||||
import Data.ByteString (ByteString)
|
||||
import qualified Data.ByteString.Base64.URL as B64U
|
||||
import Data.ByteString.Builder (Builder)
|
||||
import qualified Data.ByteString.Char8 as B
|
||||
import Data.Functor (($>))
|
||||
@@ -55,7 +56,6 @@ import Simplex.Messaging.Agent
|
||||
import Simplex.Messaging.Agent.Protocol (AgentErrorType (RCP))
|
||||
import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..))
|
||||
import qualified Simplex.Messaging.Crypto.File as CF
|
||||
import qualified Simplex.Messaging.Encoding.Base64.URL as B64U
|
||||
import Simplex.Messaging.Encoding.String (StrEncoding (..))
|
||||
import qualified Simplex.Messaging.TMap as TM
|
||||
import Simplex.Messaging.Transport (TLS, closeConnection, tlsUniq)
|
||||
|
||||
@@ -18,6 +18,7 @@ import Control.Monad.Except
|
||||
import Control.Monad.IO.Class
|
||||
import Crypto.Random (ChaChaDRG)
|
||||
import qualified Data.Aeson.TH as J
|
||||
import qualified Data.ByteString.Base64 as B64
|
||||
import Data.ByteString.Char8 (ByteString)
|
||||
import Data.Int (Int64)
|
||||
import Data.Maybe (fromMaybe, isJust, listToMaybe)
|
||||
@@ -38,7 +39,6 @@ import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import Simplex.Messaging.Crypto.Ratchet (PQEncryption (..), PQSupport (..))
|
||||
import qualified Simplex.Messaging.Crypto.Ratchet as CR
|
||||
import qualified Simplex.Messaging.Encoding.Base64 as B64
|
||||
import Simplex.Messaging.Parsers (dropPrefix, sumTypeJSON)
|
||||
import Simplex.Messaging.Protocol (SubscriptionMode (..))
|
||||
import Simplex.Messaging.Util (allFinally)
|
||||
|
||||
@@ -14,6 +14,7 @@ import Control.Concurrent.STM
|
||||
import Control.Monad (unless, when)
|
||||
import Control.Monad.Except (runExceptT)
|
||||
import Data.ByteString (ByteString)
|
||||
import qualified Data.ByteString.Base64 as B64
|
||||
import qualified Data.ByteString.Char8 as B
|
||||
import Data.Char (isDigit)
|
||||
import Data.List (isPrefixOf, isSuffixOf)
|
||||
@@ -34,7 +35,6 @@ import Simplex.Messaging.Agent.Store.SQLite (maybeFirstRow, withTransaction)
|
||||
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import Simplex.Messaging.Crypto.Ratchet (PQEncryption (..), PQSupport, pattern PQEncOff, pattern PQEncOn, pattern PQSupportOff)
|
||||
import qualified Simplex.Messaging.Encoding.Base64 as B64
|
||||
import Simplex.Messaging.Encoding.String
|
||||
import Simplex.Messaging.Version
|
||||
import System.Directory (doesFileExist)
|
||||
|
||||
@@ -4,12 +4,12 @@ module WebRTCTests where
|
||||
|
||||
import Control.Monad.Except
|
||||
import Crypto.Random (getRandomBytes)
|
||||
import qualified Data.ByteString.Base64.URL as U
|
||||
import qualified Data.ByteString.Char8 as B
|
||||
import Foreign.StablePtr
|
||||
import Simplex.Chat.Mobile
|
||||
import Simplex.Chat.Mobile.WebRTC
|
||||
import qualified Simplex.Messaging.Crypto as C
|
||||
import qualified Simplex.Messaging.Encoding.Base64.URL as U
|
||||
import System.FilePath ((</>))
|
||||
import Test.Hspec
|
||||
|
||||
@@ -36,8 +36,8 @@ webRTCTests = describe "WebRTC crypto" $ do
|
||||
cc <- newStablePtr c
|
||||
let key = B.replicate 32 '#'
|
||||
frame <- (<> B.replicate reservedSize '\NUL') <$> getRandomBytes 100
|
||||
runExceptT (chatEncryptMedia cc key frame) `shouldReturn` Left "invalid key: invalid base64 encoding near offset: 0"
|
||||
runExceptT (chatDecryptMedia key frame) `shouldReturn` Left "invalid key: invalid base64 encoding near offset: 0"
|
||||
runExceptT (chatEncryptMedia cc key frame) `shouldReturn` Left "invalid key: invalid character at offset: 0"
|
||||
runExceptT (chatDecryptMedia key frame) `shouldReturn` Left "invalid key: invalid character at offset: 0"
|
||||
it "should fail on invalid auth tag" $ \tmp -> do
|
||||
Right c <- chatMigrateInit (tmp </> "1") "" "yesUp"
|
||||
cc <- newStablePtr c
|
||||
|
||||
Reference in New Issue
Block a user