mirror of
https://github.com/dino/dino.git
synced 2025-07-04 00:02:11 -04:00
Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
dbedfe3f49 | ||
|
b2163a7ca5 | ||
|
bd8f277b91 | ||
|
1dae4e1abd | ||
|
1254a753eb | ||
|
5eabd7ac38 | ||
|
2956401e48 | ||
|
915b18789f | ||
|
2e6454a92e | ||
|
715b874801 | ||
|
22616c7458 | ||
|
0a564f80ab | ||
|
c40577ed76 | ||
|
953b384ef7 | ||
|
c8da1cfb53 | ||
|
5af1b2cd0d | ||
|
af420fe443 | ||
|
618af16bc7 |
@ -28,9 +28,15 @@
|
|||||||
],
|
],
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"type": "git",
|
"type": "archive",
|
||||||
"url": "https://github.com/protocolbuffers/protobuf.git",
|
"url": "https://github.com/protocolbuffers/protobuf/releases/download/v30.2/protobuf-30.2.tar.gz",
|
||||||
"tag": "v29.4"
|
"sha512": "555d1b18d175eeaf17f3879f124d33080f490367840d35b34bfc4e4a5b383bf6a1d09f1570acb6af9c53ac4940a14572d46423b6e3dd0c712e7802c986fb6be6",
|
||||||
|
"x-checker-data": {
|
||||||
|
"type": "anitya",
|
||||||
|
"project-id": 3715,
|
||||||
|
"stable-only": true,
|
||||||
|
"url-template": "https://github.com/protocolbuffers/protobuf/releases/download/v$version/protobuf-$version.tar.gz"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -48,9 +54,15 @@
|
|||||||
],
|
],
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"type": "git",
|
"type": "archive",
|
||||||
"url": "https://github.com/protobuf-c/protobuf-c.git",
|
"url": "https://github.com/protobuf-c/protobuf-c/releases/download/v1.5.2/protobuf-c-1.5.2.tar.gz",
|
||||||
"tag": "v1.5.1"
|
"sha512": "78dc72988d7e8232c1b967849aa00939bc05ab7d39b86a8e2af005e38aa4ef4c9b03920d51fb5337399d980e65f35d11bd4742bea745a893ecc909f56a51c9ac",
|
||||||
|
"x-checker-data": {
|
||||||
|
"type": "anitya",
|
||||||
|
"project-id": 3716,
|
||||||
|
"stable-only": true,
|
||||||
|
"url-template": "https://github.com/protobuf-c/protobuf-c/releases/download/v$version/protobuf-c-$version.tar.gz"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -67,9 +79,15 @@
|
|||||||
],
|
],
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"type": "git",
|
"type": "archive",
|
||||||
"url": "https://github.com/dino/libomemo-c.git",
|
"url": "https://github.com/dino/libomemo-c/releases/download/v0.5.1/libomemo-c-0.5.1.tar.gz",
|
||||||
"tag": "v0.5.1"
|
"sha512": "ff59565406c51663f2944e9a7c12c5b0e3fa01073039f5161472dd81f59194b1cf2685bc1e0cc930a141bc409b965c5d93313cfc3e0e237250102af3b5e88826",
|
||||||
|
"x-checker-data": {
|
||||||
|
"type": "anitya",
|
||||||
|
"project-id": 359676,
|
||||||
|
"stable-only": true,
|
||||||
|
"url-template": "https://github.com/dino/libomemo-c/releases/download/v$version/libomemo-c-$version.tar.gz"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -85,8 +103,14 @@
|
|||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"type": "archive",
|
"type": "archive",
|
||||||
"url": "https://fukuchi.org/works/qrencode/qrencode-4.1.1.tar.gz",
|
"url": "https://github.com/fukuchi/libqrencode/archive/refs/tags/v4.1.1.tar.gz",
|
||||||
"sha512": "209bb656ae3f391b03c7b3ceb03e34f7320b0105babf48b619e7a299528b8828449e0e7696f0b5db0d99170a81709d0518e34835229a748701e7df784e58a9ce"
|
"sha512": "584106e7bcaaa1ef2efe63d653daad38d4ff436eb4b185a1db3c747169c1ffa74149c3b1329bb0b8ae007903db0a7034aabf135cc196d91a37b5c61348154a65",
|
||||||
|
"x-checker-data": {
|
||||||
|
"type": "anitya",
|
||||||
|
"project-id": 12834,
|
||||||
|
"stable-only": true,
|
||||||
|
"url-template": "https://github.com/fukuchi/libqrencode/archive/refs/tags/v$version.tar.gz"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -160,7 +160,7 @@ public class FileTransfer : Object {
|
|||||||
|
|
||||||
foreach(var thumbnail_row in db.file_thumbnails.select().with(db.file_thumbnails.id, "=", id)) {
|
foreach(var thumbnail_row in db.file_thumbnails.select().with(db.file_thumbnails.id, "=", id)) {
|
||||||
Xep.JingleContentThumbnails.Thumbnail thumbnail = new Xep.JingleContentThumbnails.Thumbnail();
|
Xep.JingleContentThumbnails.Thumbnail thumbnail = new Xep.JingleContentThumbnails.Thumbnail();
|
||||||
thumbnail.uri = thumbnail_row[db.file_thumbnails.uri];
|
thumbnail.data = Xmpp.get_data_for_uri(thumbnail_row[db.file_thumbnails.uri]);
|
||||||
thumbnail.media_type = thumbnail_row[db.file_thumbnails.mime_type];
|
thumbnail.media_type = thumbnail_row[db.file_thumbnails.mime_type];
|
||||||
thumbnail.width = thumbnail_row[db.file_thumbnails.width];
|
thumbnail.width = thumbnail_row[db.file_thumbnails.width];
|
||||||
thumbnail.height = thumbnail_row[db.file_thumbnails.height];
|
thumbnail.height = thumbnail_row[db.file_thumbnails.height];
|
||||||
@ -214,9 +214,11 @@ public class FileTransfer : Object {
|
|||||||
.perform();
|
.perform();
|
||||||
}
|
}
|
||||||
foreach (Xep.JingleContentThumbnails.Thumbnail thumbnail in thumbnails) {
|
foreach (Xep.JingleContentThumbnails.Thumbnail thumbnail in thumbnails) {
|
||||||
|
string data_uri = "data:image/png;base64," + Base64.encode(thumbnail.data.get_data());
|
||||||
|
|
||||||
db.file_thumbnails.insert()
|
db.file_thumbnails.insert()
|
||||||
.value(db.file_thumbnails.id, id)
|
.value(db.file_thumbnails.id, id)
|
||||||
.value(db.file_thumbnails.uri, thumbnail.uri)
|
.value(db.file_thumbnails.uri, data_uri)
|
||||||
.value(db.file_thumbnails.mime_type, thumbnail.media_type)
|
.value(db.file_thumbnails.mime_type, thumbnail.media_type)
|
||||||
.value(db.file_thumbnails.width, thumbnail.width)
|
.value(db.file_thumbnails.width, thumbnail.width)
|
||||||
.value(db.file_thumbnails.height, thumbnail.height)
|
.value(db.file_thumbnails.height, thumbnail.height)
|
||||||
@ -224,7 +226,7 @@ public class FileTransfer : Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach (Xep.StatelessFileSharing.Source source in sfs_sources) {
|
foreach (Xep.StatelessFileSharing.Source source in sfs_sources) {
|
||||||
add_sfs_source(source);
|
persist_source(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
notify.connect(on_update);
|
notify.connect(on_update);
|
||||||
@ -234,7 +236,13 @@ public class FileTransfer : Object {
|
|||||||
if (sfs_sources.contains(source)) return; // Don't add the same source twice. Might happen due to MAM and lacking deduplication.
|
if (sfs_sources.contains(source)) return; // Don't add the same source twice. Might happen due to MAM and lacking deduplication.
|
||||||
|
|
||||||
sfs_sources.add(source);
|
sfs_sources.add(source);
|
||||||
|
if (id != -1) {
|
||||||
|
persist_source(source);
|
||||||
|
}
|
||||||
|
sources_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persist_source(Xep.StatelessFileSharing.Source source) {
|
||||||
Xep.StatelessFileSharing.HttpSource? http_source = source as Xep.StatelessFileSharing.HttpSource;
|
Xep.StatelessFileSharing.HttpSource? http_source = source as Xep.StatelessFileSharing.HttpSource;
|
||||||
if (http_source != null) {
|
if (http_source != null) {
|
||||||
db.sfs_sources.insert()
|
db.sfs_sources.insert()
|
||||||
@ -243,8 +251,6 @@ public class FileTransfer : Object {
|
|||||||
.value(db.sfs_sources.data, http_source.url)
|
.value(db.sfs_sources.data, http_source.url)
|
||||||
.perform();
|
.perform();
|
||||||
}
|
}
|
||||||
|
|
||||||
sources_changed();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public File? get_file() {
|
public File? get_file() {
|
||||||
|
@ -200,7 +200,7 @@ public class Message : Object {
|
|||||||
if (!fallbacks_by_ns.has_key(ns_uri)) {
|
if (!fallbacks_by_ns.has_key(ns_uri)) {
|
||||||
fallbacks_by_ns[ns_uri] = new ArrayList<Xep.FallbackIndication.FallbackLocation>();
|
fallbacks_by_ns[ns_uri] = new ArrayList<Xep.FallbackIndication.FallbackLocation>();
|
||||||
}
|
}
|
||||||
fallbacks_by_ns[ns_uri].add(new Xep.FallbackIndication.FallbackLocation(row[db.body_meta.from_char], row[db.body_meta.to_char]));
|
fallbacks_by_ns[ns_uri].add(new Xep.FallbackIndication.FallbackLocation.partial_body(row[db.body_meta.from_char], row[db.body_meta.to_char]));
|
||||||
break;
|
break;
|
||||||
case Xep.MessageMarkup.NS_URI:
|
case Xep.MessageMarkup.NS_URI:
|
||||||
var types = new ArrayList<Xep.MessageMarkup.SpanType>();
|
var types = new ArrayList<Xep.MessageMarkup.SpanType>();
|
||||||
@ -212,7 +212,7 @@ public class Message : Object {
|
|||||||
|
|
||||||
var fallbacks = new ArrayList<Xep.FallbackIndication.Fallback>();
|
var fallbacks = new ArrayList<Xep.FallbackIndication.Fallback>();
|
||||||
foreach (string ns_uri in fallbacks_by_ns.keys) {
|
foreach (string ns_uri in fallbacks_by_ns.keys) {
|
||||||
fallbacks.add(new Xep.FallbackIndication.Fallback(ns_uri, fallbacks_by_ns[ns_uri].to_array()));
|
fallbacks.add(new Xep.FallbackIndication.Fallback(ns_uri, fallbacks_by_ns[ns_uri]));
|
||||||
}
|
}
|
||||||
this.fallbacks = fallbacks;
|
this.fallbacks = fallbacks;
|
||||||
this.markups = markups;
|
this.markups = markups;
|
||||||
|
@ -24,8 +24,6 @@ public class AvatarManager : StreamInteractionModule, Object {
|
|||||||
private string folder = null;
|
private string folder = null;
|
||||||
private HashMap<Jid, string> user_avatars = new HashMap<Jid, string>(Jid.hash_func, Jid.equals_func);
|
private HashMap<Jid, string> user_avatars = new HashMap<Jid, string>(Jid.hash_func, Jid.equals_func);
|
||||||
private HashMap<Jid, string> vcard_avatars = new HashMap<Jid, string>(Jid.hash_func, Jid.equals_func);
|
private HashMap<Jid, string> vcard_avatars = new HashMap<Jid, string>(Jid.hash_func, Jid.equals_func);
|
||||||
private HashMap<string, Pixbuf> cached_pixbuf = new HashMap<string, Pixbuf>();
|
|
||||||
private HashMap<string, Gee.List<SourceFuncWrapper>> pending_pixbuf = new HashMap<string, Gee.List<SourceFuncWrapper>>();
|
|
||||||
private HashSet<string> pending_fetch = new HashSet<string>();
|
private HashSet<string> pending_fetch = new HashSet<string>();
|
||||||
private const int MAX_PIXEL = 192;
|
private const int MAX_PIXEL = 192;
|
||||||
|
|
||||||
@ -104,72 +102,10 @@ public class AvatarManager : StreamInteractionModule, Object {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Version (deprecated = true)]
|
|
||||||
public bool has_avatar_cached(Account account, Jid jid) {
|
|
||||||
string? hash = get_avatar_hash(account, jid);
|
|
||||||
return hash != null && cached_pixbuf.has_key(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool has_avatar(Account account, Jid jid) {
|
public bool has_avatar(Account account, Jid jid) {
|
||||||
return get_avatar_hash(account, jid) != null;
|
return get_avatar_hash(account, jid) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Version (deprecated = true)]
|
|
||||||
public Pixbuf? get_cached_avatar(Account account, Jid jid_) {
|
|
||||||
string? hash = get_avatar_hash(account, jid_);
|
|
||||||
if (hash == null) return null;
|
|
||||||
if (cached_pixbuf.has_key(hash)) return cached_pixbuf[hash];
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Version (deprecated = true)]
|
|
||||||
public async Pixbuf? get_avatar(Account account, Jid jid_) {
|
|
||||||
Jid jid = jid_;
|
|
||||||
if (!stream_interactor.get_module(MucManager.IDENTITY).is_groupchat_occupant(jid_, account)) {
|
|
||||||
jid = jid_.bare_jid;
|
|
||||||
}
|
|
||||||
|
|
||||||
int source = -1;
|
|
||||||
string? hash = null;
|
|
||||||
if (user_avatars.has_key(jid)) {
|
|
||||||
hash = user_avatars[jid];
|
|
||||||
source = 1;
|
|
||||||
} else if (vcard_avatars.has_key(jid)) {
|
|
||||||
hash = vcard_avatars[jid];
|
|
||||||
source = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hash == null) return null;
|
|
||||||
|
|
||||||
if (cached_pixbuf.has_key(hash)) {
|
|
||||||
return cached_pixbuf[hash];
|
|
||||||
}
|
|
||||||
|
|
||||||
XmppStream? stream = stream_interactor.get_stream(account);
|
|
||||||
if (stream == null || !stream.negotiation_complete) return null;
|
|
||||||
|
|
||||||
if (pending_pixbuf.has_key(hash)) {
|
|
||||||
pending_pixbuf[hash].add(new SourceFuncWrapper(get_avatar.callback));
|
|
||||||
yield;
|
|
||||||
return cached_pixbuf[hash];
|
|
||||||
}
|
|
||||||
|
|
||||||
pending_pixbuf[hash] = new ArrayList<SourceFuncWrapper>();
|
|
||||||
Pixbuf? image = yield get_image(hash);
|
|
||||||
if (image != null) {
|
|
||||||
cached_pixbuf[hash] = image;
|
|
||||||
} else {
|
|
||||||
if (yield fetch_and_store(stream, account, jid, source, hash)) {
|
|
||||||
image = yield get_image(hash);
|
|
||||||
}
|
|
||||||
cached_pixbuf[hash] = image;
|
|
||||||
}
|
|
||||||
foreach (SourceFuncWrapper sfw in pending_pixbuf[hash]) {
|
|
||||||
sfw.sfun();
|
|
||||||
}
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void publish(Account account, string file) {
|
public void publish(Account account, string file) {
|
||||||
try {
|
try {
|
||||||
Pixbuf pixbuf = new Pixbuf.from_file(file);
|
Pixbuf pixbuf = new Pixbuf.from_file(file);
|
||||||
@ -329,29 +265,6 @@ public class AvatarManager : StreamInteractionModule, Object {
|
|||||||
File file = File.new_for_path(Path.build_filename(folder, id));
|
File file = File.new_for_path(Path.build_filename(folder, id));
|
||||||
return file.query_exists();
|
return file.query_exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Pixbuf? get_image(string id) {
|
|
||||||
try {
|
|
||||||
File file = File.new_for_path(Path.build_filename(folder, id));
|
|
||||||
FileInputStream stream = yield file.read_async(Priority.LOW);
|
|
||||||
|
|
||||||
uint8 fbuf[1024];
|
|
||||||
size_t size;
|
|
||||||
|
|
||||||
Checksum checksum = new Checksum (ChecksumType.SHA1);
|
|
||||||
while ((size = yield stream.read_async(fbuf, Priority.LOW)) > 0) {
|
|
||||||
checksum.update(fbuf, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checksum.get_string() != id) {
|
|
||||||
FileUtils.remove(file.get_path());
|
|
||||||
}
|
|
||||||
stream.seek(0, SeekType.SET);
|
|
||||||
return yield new Pixbuf.from_stream_async(stream, null);
|
|
||||||
} catch (Error e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -223,6 +223,7 @@ public class Database : Qlite.Database {
|
|||||||
|
|
||||||
public class FileThumbnailsTable : Table {
|
public class FileThumbnailsTable : Table {
|
||||||
public Column<int> id = new Column.Integer("id");
|
public Column<int> id = new Column.Integer("id");
|
||||||
|
// TODO store data as bytes, not as data uri
|
||||||
public Column<string> uri = new Column.Text("uri") { not_null = true };
|
public Column<string> uri = new Column.Text("uri") { not_null = true };
|
||||||
public Column<string> mime_type = new Column.Text("mime_type");
|
public Column<string> mime_type = new Column.Text("mime_type");
|
||||||
public Column<int> width = new Column.Integer("width");
|
public Column<int> width = new Column.Integer("width");
|
||||||
|
@ -59,6 +59,7 @@ public class ModuleManager {
|
|||||||
module_map[account].add(new Xmpp.MessageModule());
|
module_map[account].add(new Xmpp.MessageModule());
|
||||||
module_map[account].add(new Xmpp.MessageArchiveManagement.Module());
|
module_map[account].add(new Xmpp.MessageArchiveManagement.Module());
|
||||||
module_map[account].add(new Xep.MessageCarbons.Module());
|
module_map[account].add(new Xep.MessageCarbons.Module());
|
||||||
|
module_map[account].add(new Xep.BitsOfBinary.Module());
|
||||||
module_map[account].add(new Xep.Muc.Module());
|
module_map[account].add(new Xep.Muc.Module());
|
||||||
module_map[account].add(new Xep.Pubsub.Module());
|
module_map[account].add(new Xep.Pubsub.Module());
|
||||||
module_map[account].add(new Xep.MessageDeliveryReceipts.Module());
|
module_map[account].add(new Xep.MessageDeliveryReceipts.Module());
|
||||||
|
@ -141,6 +141,7 @@ public class Register : StreamInteractionModule, Object{
|
|||||||
Gee.List<XmppStreamModule> list = new ArrayList<XmppStreamModule>();
|
Gee.List<XmppStreamModule> list = new ArrayList<XmppStreamModule>();
|
||||||
list.add(new Iq.Module());
|
list.add(new Iq.Module());
|
||||||
list.add(new Xep.InBandRegistration.Module());
|
list.add(new Xep.InBandRegistration.Module());
|
||||||
|
list.add(new Xep.BitsOfBinary.Module());
|
||||||
|
|
||||||
XmppStreamResult stream_result = yield Xmpp.establish_stream(jid.domain_jid, list, Application.print_xmpp,
|
XmppStreamResult stream_result = yield Xmpp.establish_stream(jid.domain_jid, list, Application.print_xmpp,
|
||||||
(peer_cert, errors) => { return ConnectionManager.on_invalid_certificate(jid.domainpart, peer_cert, errors); }
|
(peer_cert, errors) => { return ConnectionManager.on_invalid_certificate(jid.domainpart, peer_cert, errors); }
|
||||||
|
@ -68,10 +68,8 @@ namespace Dino {
|
|||||||
Pixbuf thumbnail_pixbuf = pixbuf.scale_simple(thumbnail_width, thumbnail_height, InterpType.BILINEAR);
|
Pixbuf thumbnail_pixbuf = pixbuf.scale_simple(thumbnail_width, thumbnail_height, InterpType.BILINEAR);
|
||||||
uint8[] buffer;
|
uint8[] buffer;
|
||||||
thumbnail_pixbuf.save_to_buffer(out buffer, IMAGE_TYPE);
|
thumbnail_pixbuf.save_to_buffer(out buffer, IMAGE_TYPE);
|
||||||
string base_64 = GLib.Base64.encode(buffer);
|
var thumbnail = new Xep.JingleContentThumbnails.Thumbnail();
|
||||||
string uri = @"data:$MIME_TYPE;base64,$base_64";
|
thumbnail.data = new Bytes.take(buffer);
|
||||||
Xep.JingleContentThumbnails.Thumbnail thumbnail = new Xep.JingleContentThumbnails.Thumbnail();
|
|
||||||
thumbnail.uri = uri;
|
|
||||||
thumbnail.media_type = MIME_TYPE;
|
thumbnail.media_type = MIME_TYPE;
|
||||||
thumbnail.width = thumbnail_width;
|
thumbnail.width = thumbnail_width;
|
||||||
thumbnail.height = thumbnail_height;
|
thumbnail.height = thumbnail_height;
|
||||||
|
@ -156,6 +156,12 @@ public class Dino.StatelessFileSharing : StreamInteractionModule, Object {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't process messages that are fallback for legacy clients
|
||||||
|
if (Xep.StatelessFileSharing.is_sfs_fallback_message(stanza)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,9 +26,10 @@ namespace Dino {
|
|||||||
out_message.body = fallback + out_message.body;
|
out_message.body = fallback + out_message.body;
|
||||||
|
|
||||||
// Store fallback location
|
// Store fallback location
|
||||||
var fallback_location = new Xep.FallbackIndication.FallbackLocation(0, (int)fallback.char_count());
|
var fallback_locations = new ArrayList<Xep.FallbackIndication.FallbackLocation>();
|
||||||
|
fallback_locations.add(new Xep.FallbackIndication.FallbackLocation.partial_body(0, (int)fallback.char_count()));
|
||||||
var fallback_list = new ArrayList<Xep.FallbackIndication.Fallback>();
|
var fallback_list = new ArrayList<Xep.FallbackIndication.Fallback>();
|
||||||
fallback_list.add(new Xep.FallbackIndication.Fallback(Xep.Replies.NS_URI, new Xep.FallbackIndication.FallbackLocation[] { fallback_location }));
|
fallback_list.add(new Xep.FallbackIndication.Fallback(Xep.Replies.NS_URI, fallback_locations));
|
||||||
out_message.set_fallbacks(fallback_list);
|
out_message.set_fallbacks(fallback_list);
|
||||||
|
|
||||||
// Adjust markups to new prefix
|
// Adjust markups to new prefix
|
||||||
|
@ -26,7 +26,7 @@ def compute_version_from_git(git_repo, git):
|
|||||||
return None
|
return None
|
||||||
git_describe = subprocess.check_output([git, "describe", "--tags"], cwd=git_repo, text=True).strip()
|
git_describe = subprocess.check_output([git, "describe", "--tags"], cwd=git_repo, text=True).strip()
|
||||||
if git_release_tag == git_describe:
|
if git_release_tag == git_describe:
|
||||||
return git_release_tag
|
return git_release_tag[1:]
|
||||||
matches = re.match("^.*-([0-9]+)-g([0-9a-f]+)$", git_describe)
|
matches = re.match("^.*-([0-9]+)-g([0-9a-f]+)$", git_describe)
|
||||||
if matches is None:
|
if matches is None:
|
||||||
return None
|
return None
|
||||||
@ -34,7 +34,7 @@ def compute_version_from_git(git_repo, git):
|
|||||||
git_commit_hash = matches.groups()[1]
|
git_commit_hash = matches.groups()[1]
|
||||||
git_commit_time = subprocess.check_output([git, "show", "--format=%cd", "--date=format:%Y%m%d", "-s"],
|
git_commit_time = subprocess.check_output([git, "show", "--format=%cd", "--date=format:%Y%m%d", "-s"],
|
||||||
cwd=git_repo, text=True).strip()
|
cwd=git_repo, text=True).strip()
|
||||||
return "%s~git%s.%s.%s" % (git_release_tag, git_tag_offset, git_commit_time, git_commit_hash)
|
return "%s~git%s.%s.%s" % (git_release_tag[1:], git_tag_offset, git_commit_time, git_commit_hash)
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
pass
|
pass
|
||||||
return None
|
return None
|
||||||
|
@ -227,7 +227,7 @@
|
|||||||
<control>pointing</control>
|
<control>pointing</control>
|
||||||
<control>keyboard</control>
|
<control>keyboard</control>
|
||||||
<control>touch</control>
|
<control>touch</control>
|
||||||
<display_length>small</display_length>
|
<display_length>360</display_length>
|
||||||
</supports>
|
</supports>
|
||||||
<project_license>GPL-3.0+</project_license>
|
<project_license>GPL-3.0+</project_license>
|
||||||
<developer id="im.dino">
|
<developer id="im.dino">
|
||||||
|
@ -64,7 +64,7 @@
|
|||||||
<control>pointing</control>
|
<control>pointing</control>
|
||||||
<control>keyboard</control>
|
<control>keyboard</control>
|
||||||
<control>touch</control>
|
<control>touch</control>
|
||||||
<display_length>small</display_length>
|
<display_length>360</display_length>
|
||||||
</supports>
|
</supports>
|
||||||
<project_license>GPL-3.0+</project_license>
|
<project_license>GPL-3.0+</project_license>
|
||||||
<developer id="im.dino">
|
<developer id="im.dino">
|
||||||
|
@ -69,6 +69,7 @@
|
|||||||
<child>
|
<child>
|
||||||
<object class="AdwActionRow" id="xmpp_address">
|
<object class="AdwActionRow" id="xmpp_address">
|
||||||
<property name="title" translatable="yes">XMPP Address</property>
|
<property name="title" translatable="yes">XMPP Address</property>
|
||||||
|
<property name="subtitle_selectable">True</property>
|
||||||
<style>
|
<style>
|
||||||
<class name="property"/>
|
<class name="property"/>
|
||||||
</style>
|
</style>
|
||||||
|
@ -20,7 +20,7 @@ public class FileDefaultWidget : Box {
|
|||||||
|
|
||||||
private FileTransfer.State state;
|
private FileTransfer.State state;
|
||||||
|
|
||||||
public FileDefaultWidget() {
|
public void init_updating_file_info() {
|
||||||
EventControllerMotion this_motion_events = new EventControllerMotion();
|
EventControllerMotion this_motion_events = new EventControllerMotion();
|
||||||
this.add_controller(this_motion_events);
|
this.add_controller(this_motion_events);
|
||||||
this_motion_events.enter.connect(on_pointer_entered_event);
|
this_motion_events.enter.connect(on_pointer_entered_event);
|
||||||
@ -39,6 +39,14 @@ public class FileDefaultWidget : Box {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void set_static_file_info(string? mime_type) {
|
||||||
|
spinner.stop(); // A hidden spinning spinner still uses CPU. Deactivate asap
|
||||||
|
|
||||||
|
image_stack.set_visible_child_name("content_type_image");
|
||||||
|
content_type_image.icon_name = get_file_icon_name(mime_type);
|
||||||
|
mime_label.label = mime_type != null ? ContentType.get_description(mime_type) : null;
|
||||||
|
}
|
||||||
|
|
||||||
public void update_file_info(string? mime_type, FileTransfer.State state, bool direction, int64 size, int64 transferred_bytes) {
|
public void update_file_info(string? mime_type, FileTransfer.State state, bool direction, int64 size, int64 transferred_bytes) {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
|
|
||||||
|
@ -217,37 +217,8 @@ public class FileImageWidget : Widget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Pixbuf? parse_thumbnail(Xep.JingleContentThumbnails.Thumbnail thumbnail) {
|
public static Pixbuf? parse_thumbnail(Xep.JingleContentThumbnails.Thumbnail thumbnail) {
|
||||||
string[] splits = thumbnail.uri.split(":", 2);
|
MemoryInputStream input_stream = new MemoryInputStream.from_data(thumbnail.data.get_data());
|
||||||
if (splits.length != 2) {
|
return new Pixbuf.from_stream(input_stream);
|
||||||
warning("Thumbnail parsing error: ':' not found");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (splits[0] != "data") {
|
|
||||||
warning("Unsupported thumbnail: unimplemented uri type\n");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
splits = splits[1].split(";", 2);
|
|
||||||
if (splits.length != 2) {
|
|
||||||
warning("Thumbnail parsing error: ';' not found");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (splits[0] != "image/png") {
|
|
||||||
warning("Unsupported thumbnail: unsupported mime-type\n");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
splits = splits[1].split(",", 2);
|
|
||||||
if (splits.length != 2) {
|
|
||||||
warning("Thumbnail parsing error: ',' not found");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (splits[0] != "base64") {
|
|
||||||
warning("Unsupported thumbnail: data is not base64 encoded\n");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
uint8[] data = Base64.decode(splits[1]);
|
|
||||||
MemoryInputStream input_stream = new MemoryInputStream.from_data(data);
|
|
||||||
Pixbuf pixbuf = new Pixbuf.from_stream(input_stream);
|
|
||||||
return pixbuf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool can_display(FileTransfer file_transfer) {
|
public static bool can_display(FileTransfer file_transfer) {
|
||||||
|
@ -213,6 +213,7 @@ public class FileDefaultWidgetController : Object {
|
|||||||
public void set_file_transfer(FileTransfer file_transfer) {
|
public void set_file_transfer(FileTransfer file_transfer) {
|
||||||
this.file_transfer = file_transfer;
|
this.file_transfer = file_transfer;
|
||||||
|
|
||||||
|
widget.init_updating_file_info();
|
||||||
widget.name_label.label = file_transfer.file_name;
|
widget.name_label.label = file_transfer.file_name;
|
||||||
|
|
||||||
file_transfer.bind_property("state", this, "file-transfer-state");
|
file_transfer.bind_property("state", this, "file-transfer-state");
|
||||||
|
@ -23,6 +23,7 @@ namespace Dino.Ui.ConversationDetails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void bind_dialog(Model.ConversationDetails model, ViewModel.ConversationDetails view_model, StreamInteractor stream_interactor) {
|
public void bind_dialog(Model.ConversationDetails model, ViewModel.ConversationDetails view_model, StreamInteractor stream_interactor) {
|
||||||
|
// Set some data once
|
||||||
view_model.avatar = new ViewModel.CompatAvatarPictureModel(stream_interactor).set_conversation(model.conversation);
|
view_model.avatar = new ViewModel.CompatAvatarPictureModel(stream_interactor).set_conversation(model.conversation);
|
||||||
view_model.show_blocked = model.conversation.type_ == Conversation.Type.CHAT && stream_interactor.get_module(BlockingManager.IDENTITY).is_supported(model.conversation.account);
|
view_model.show_blocked = model.conversation.type_ == Conversation.Type.CHAT && stream_interactor.get_module(BlockingManager.IDENTITY).is_supported(model.conversation.account);
|
||||||
view_model.members_sorted.set_model(model.members);
|
view_model.members_sorted.set_model(model.members);
|
||||||
@ -36,6 +37,7 @@ namespace Dino.Ui.ConversationDetails {
|
|||||||
affiliation = conference_member.affiliation
|
affiliation = conference_member.affiliation
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
view_model.account_jid = stream_interactor.get_accounts().size > 1 ? model.conversation.account.bare_jid.to_string() : null;
|
||||||
|
|
||||||
if (model.domain_blocked) {
|
if (model.domain_blocked) {
|
||||||
view_model.blocked = DOMAIN;
|
view_model.blocked = DOMAIN;
|
||||||
@ -45,6 +47,7 @@ namespace Dino.Ui.ConversationDetails {
|
|||||||
view_model.blocked = UNBLOCK;
|
view_model.blocked = UNBLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bind properties
|
||||||
model.display_name.bind_property("display-name", view_model, "name", BindingFlags.SYNC_CREATE);
|
model.display_name.bind_property("display-name", view_model, "name", BindingFlags.SYNC_CREATE);
|
||||||
model.conversation.bind_property("notify-setting", view_model, "notification", BindingFlags.SYNC_CREATE, (_, from, ref to) => {
|
model.conversation.bind_property("notify-setting", view_model, "notification", BindingFlags.SYNC_CREATE, (_, from, ref to) => {
|
||||||
switch (model.conversation.get_notification_setting(stream_interactor)) {
|
switch (model.conversation.get_notification_setting(stream_interactor)) {
|
||||||
@ -135,38 +138,65 @@ namespace Dino.Ui.ConversationDetails {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Dialog setup_dialog(Conversation conversation, StreamInteractor stream_interactor, Window parent) {
|
public void set_about_rows(Model.ConversationDetails model, ViewModel.ConversationDetails view_model, StreamInteractor stream_interactor) {
|
||||||
var dialog = new Dialog() { transient_for = parent };
|
view_model.about_rows.append(new ViewModel.PreferencesRow.Text() {
|
||||||
var model = new Model.ConversationDetails();
|
|
||||||
model.populate(stream_interactor, conversation);
|
|
||||||
// populate_dialog(model, conversation, stream_interactor);
|
|
||||||
bind_dialog(model, dialog.model, stream_interactor);
|
|
||||||
|
|
||||||
dialog.model.about_rows.append(new ViewModel.PreferencesRow.Text() {
|
|
||||||
title = _("XMPP Address"),
|
title = _("XMPP Address"),
|
||||||
text = conversation.counterpart.to_string()
|
text = model.conversation.counterpart.to_string()
|
||||||
});
|
});
|
||||||
if (model.conversation.type_ == Conversation.Type.CHAT) {
|
if (model.conversation.type_ == Conversation.Type.CHAT) {
|
||||||
var about_row = new ViewModel.PreferencesRow.Entry() {
|
var about_row = new ViewModel.PreferencesRow.Entry() {
|
||||||
title = _("Display name"),
|
title = _("Display name"),
|
||||||
text = dialog.model.name
|
text = model.display_name.display_name
|
||||||
};
|
};
|
||||||
about_row.changed.connect(() => {
|
about_row.changed.connect(() => {
|
||||||
if (about_row.text != Util.get_conversation_display_name(stream_interactor, conversation)) {
|
if (about_row.text != model.display_name.display_name) {
|
||||||
stream_interactor.get_module(RosterManager.IDENTITY).set_jid_handle(conversation.account, conversation.counterpart, about_row.text);
|
stream_interactor.get_module(RosterManager.IDENTITY).set_jid_handle(model.conversation.account, model.conversation.counterpart, about_row.text);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
dialog.model.about_rows.append(about_row);
|
view_model.about_rows.append(about_row);
|
||||||
}
|
}
|
||||||
if (model.conversation.type_ == Conversation.Type.GROUPCHAT) {
|
if (model.conversation.type_ == Conversation.Type.GROUPCHAT) {
|
||||||
var topic = stream_interactor.get_module(MucManager.IDENTITY).get_groupchat_subject(conversation.counterpart, conversation.account);
|
var topic = stream_interactor.get_module(MucManager.IDENTITY).get_groupchat_subject(model.conversation.counterpart, model.conversation.account);
|
||||||
if (topic != null && topic != "") {
|
|
||||||
dialog.model.about_rows.append(new ViewModel.PreferencesRow.Text() {
|
Ui.ViewModel.PreferencesRow.Any preferences_row = null;
|
||||||
|
Jid? own_muc_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(model.conversation.counterpart, model.conversation.account);
|
||||||
|
if (own_muc_jid != null) {
|
||||||
|
Xep.Muc.Role? own_role = stream_interactor.get_module(MucManager.IDENTITY).get_role(own_muc_jid, model.conversation.account);
|
||||||
|
if (own_role != null) {
|
||||||
|
if (own_role == MODERATOR) {
|
||||||
|
var preferences_row_entry = new ViewModel.PreferencesRow.Entry() {
|
||||||
|
title = _("Topic"),
|
||||||
|
text = topic
|
||||||
|
};
|
||||||
|
preferences_row_entry.changed.connect(() => {
|
||||||
|
if (preferences_row_entry.text != topic) {
|
||||||
|
stream_interactor.get_module(MucManager.IDENTITY).change_subject(model.conversation.account, model.conversation.counterpart, preferences_row_entry.text);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
preferences_row = preferences_row_entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (preferences_row == null && topic != null && topic != "") {
|
||||||
|
preferences_row = new ViewModel.PreferencesRow.Text() {
|
||||||
title = _("Topic"),
|
title = _("Topic"),
|
||||||
text = Util.parse_add_markup(topic, null, true, true)
|
text = Util.parse_add_markup(topic, null, true, true)
|
||||||
});
|
};
|
||||||
|
}
|
||||||
|
if (preferences_row != null) {
|
||||||
|
view_model.about_rows.append(preferences_row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dialog setup_dialog(Conversation conversation, StreamInteractor stream_interactor, Window parent) {
|
||||||
|
var dialog = new Dialog() { transient_for = parent };
|
||||||
|
var model = new Model.ConversationDetails();
|
||||||
|
model.populate(stream_interactor, conversation);
|
||||||
|
bind_dialog(model, dialog.model, stream_interactor);
|
||||||
|
|
||||||
|
set_about_rows(model, dialog.model, stream_interactor);
|
||||||
|
|
||||||
dialog.close_request.connect(() => {
|
dialog.close_request.connect(() => {
|
||||||
// Only send the config form if something was changed
|
// Only send the config form if something was changed
|
||||||
if (model.data_form_bak != null && model.data_form_bak != model.data_form.stanza_node.to_string()) {
|
if (model.data_form_bak != null && model.data_form_bak != model.data_form.stanza_node.to_string()) {
|
||||||
|
@ -73,7 +73,7 @@ public class FileSendOverlay {
|
|||||||
if (widget == null) {
|
if (widget == null) {
|
||||||
FileDefaultWidget default_widget = new FileDefaultWidget();
|
FileDefaultWidget default_widget = new FileDefaultWidget();
|
||||||
default_widget.name_label.label = file_name;
|
default_widget.name_label.label = file_name;
|
||||||
default_widget.update_file_info(mime_type, FileTransfer.State.COMPLETE, FileTransfer.DIRECTION_SENT, (long)file_info.get_size(), 0);
|
default_widget.set_static_file_info(mime_type);
|
||||||
widget = default_widget;
|
widget = default_widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,6 +115,12 @@ public static ViewModel.PreferencesRow.Any? get_data_form_field_view_model(DataF
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var media_node = field.node.get_subnode("media", "urn:xmpp:media-element");
|
||||||
|
if (media_node != null) {
|
||||||
|
view_model.media_type = media_node.get_attribute("type", "urn:xmpp:media-element");
|
||||||
|
view_model.media_uri = media_node.get_deep_string_content("uri");
|
||||||
|
}
|
||||||
|
|
||||||
view_model.title = label;
|
view_model.title = label;
|
||||||
return view_model;
|
return view_model;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Gee;
|
using Gee;
|
||||||
|
using Gdk;
|
||||||
using Gtk;
|
using Gtk;
|
||||||
|
|
||||||
using Dino.Entities;
|
using Dino.Entities;
|
||||||
@ -24,6 +25,11 @@ namespace Dino.Ui.Util {
|
|||||||
var entry_view_model = preferences_row as ViewModel.PreferencesRow.Entry;
|
var entry_view_model = preferences_row as ViewModel.PreferencesRow.Entry;
|
||||||
if (entry_view_model != null) {
|
if (entry_view_model != null) {
|
||||||
Adw.EntryRow view = new Adw.EntryRow() { title = entry_view_model.title, show_apply_button=true };
|
Adw.EntryRow view = new Adw.EntryRow() { title = entry_view_model.title, show_apply_button=true };
|
||||||
|
if (preferences_row.media_uri != null) {
|
||||||
|
var bytes = Xmpp.get_data_for_uri(preferences_row.media_uri);
|
||||||
|
Picture picture = new Picture.for_paintable(Texture.from_bytes(bytes));
|
||||||
|
view.add_suffix(picture);
|
||||||
|
}
|
||||||
entry_view_model.bind_property("text", view, "text", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL, (_, from, ref to) => {
|
entry_view_model.bind_property("text", view, "text", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL, (_, from, ref to) => {
|
||||||
var str = (string) from;
|
var str = (string) from;
|
||||||
to = str ?? "";
|
to = str ?? "";
|
||||||
|
@ -40,6 +40,8 @@ public class Dino.Ui.ViewModel.ConversationDetails : Object {
|
|||||||
public BlockState blocked { get; set; }
|
public BlockState blocked { get; set; }
|
||||||
|
|
||||||
public GLib.ListStore about_rows = new GLib.ListStore(typeof(PreferencesRow.Any));
|
public GLib.ListStore about_rows = new GLib.ListStore(typeof(PreferencesRow.Any));
|
||||||
|
public string? account_jid { get; set; }
|
||||||
|
|
||||||
public GLib.ListStore settings_rows = new GLib.ListStore(typeof(PreferencesRow.Any));
|
public GLib.ListStore settings_rows = new GLib.ListStore(typeof(PreferencesRow.Any));
|
||||||
public GLib.ListStore room_configuration_rows { get; set; }
|
public GLib.ListStore room_configuration_rows { get; set; }
|
||||||
public MapListModel members = new MapListModel(null, null);
|
public MapListModel members = new MapListModel(null, null);
|
||||||
|
@ -7,6 +7,9 @@ using Gtk;
|
|||||||
namespace Dino.Ui.ViewModel.PreferencesRow {
|
namespace Dino.Ui.ViewModel.PreferencesRow {
|
||||||
public abstract class Any : Object {
|
public abstract class Any : Object {
|
||||||
public string title { get; set; }
|
public string title { get; set; }
|
||||||
|
|
||||||
|
public string? media_type { get; set; }
|
||||||
|
public string? media_uri { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Text : Any {
|
public class Text : Any {
|
||||||
|
@ -198,6 +198,12 @@ namespace Dino.Ui.ConversationDetails {
|
|||||||
if (model.room_configuration_rows != null && model.room_configuration_rows.get_n_items() > 0) {
|
if (model.room_configuration_rows != null && model.room_configuration_rows.get_n_items() > 0) {
|
||||||
about_box.append(Util.rows_to_preference_group(model.room_configuration_rows, _("Room Configuration")));
|
about_box.append(Util.rows_to_preference_group(model.room_configuration_rows, _("Room Configuration")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (model.account_jid != null) {
|
||||||
|
var account_label = new Label(@"via $(model.account_jid)") { halign=Align.START, margin_start=14, margin_top=4 };
|
||||||
|
account_label.add_css_class("dim-label");
|
||||||
|
about_box.append(account_label);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add_encryption_tab_element(Adw.PreferencesGroup preferences_group) {
|
public void add_encryption_tab_element(Adw.PreferencesGroup preferences_group) {
|
||||||
|
@ -3,7 +3,6 @@ dependencies = [
|
|||||||
dep_gee,
|
dep_gee,
|
||||||
dep_glib,
|
dep_glib,
|
||||||
dep_gmodule,
|
dep_gmodule,
|
||||||
dep_gtk4,
|
|
||||||
dep_libsoup,
|
dep_libsoup,
|
||||||
dep_qlite,
|
dep_qlite,
|
||||||
dep_xmpp_vala,
|
dep_xmpp_vala,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using Gee;
|
using Gee;
|
||||||
using Gtk;
|
|
||||||
|
|
||||||
using Dino.Entities;
|
using Dino.Entities;
|
||||||
using Xmpp;
|
using Xmpp;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
dep_crypto_vala,
|
dep_crypto_vala,
|
||||||
dep_dino,
|
dep_dino,
|
||||||
dep_gdk_pixbuf,
|
|
||||||
dep_gee,
|
dep_gee,
|
||||||
|
dep_gio,
|
||||||
dep_glib,
|
dep_glib,
|
||||||
dep_gmodule,
|
dep_gmodule,
|
||||||
dep_gnutls,
|
dep_gnutls,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
dep_dino,
|
dep_dino,
|
||||||
dep_gdk_pixbuf,
|
|
||||||
dep_gee,
|
dep_gee,
|
||||||
dep_glib,
|
dep_glib,
|
||||||
dep_gmodule,
|
dep_gmodule,
|
||||||
|
@ -218,6 +218,7 @@ dino_plugins_rtp_voice_processor_process_stream(void *native_ptr, GstAudioInfo *
|
|||||||
frame.samples_per_channel_ = info->rate / 100;
|
frame.samples_per_channel_ = info->rate / 100;
|
||||||
memcpy(frame.data_, map.data, frame.samples_per_channel_ * info->bpf);
|
memcpy(frame.data_, map.data, frame.samples_per_channel_ * info->bpf);
|
||||||
|
|
||||||
|
apm->set_stream_delay_ms(native->stream_delay);
|
||||||
err = apm->ProcessStream(&frame);
|
err = apm->ProcessStream(&frame);
|
||||||
if (err >= 0) memcpy(map.data, frame.data_, frame.samples_per_channel_ * info->bpf);
|
if (err >= 0) memcpy(map.data, frame.data_, frame.samples_per_channel_ * info->bpf);
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
dep_gdk_pixbuf,
|
|
||||||
dep_gee,
|
dep_gee,
|
||||||
dep_gio,
|
dep_gio,
|
||||||
dep_glib,
|
dep_glib,
|
||||||
@ -96,6 +95,7 @@ sources = files(
|
|||||||
'src/module/xep/0199_ping.vala',
|
'src/module/xep/0199_ping.vala',
|
||||||
'src/module/xep/0203_delayed_delivery.vala',
|
'src/module/xep/0203_delayed_delivery.vala',
|
||||||
'src/module/xep/0215_external_service_discovery.vala',
|
'src/module/xep/0215_external_service_discovery.vala',
|
||||||
|
'src/module/xep/0231_bits_of_binary.vala',
|
||||||
'src/module/xep/0234_jingle_file_transfer.vala',
|
'src/module/xep/0234_jingle_file_transfer.vala',
|
||||||
'src/module/xep/0249_direct_muc_invitations.vala',
|
'src/module/xep/0249_direct_muc_invitations.vala',
|
||||||
'src/module/xep/0260_jingle_socks5_bytestreams.vala',
|
'src/module/xep/0260_jingle_socks5_bytestreams.vala',
|
||||||
@ -130,7 +130,6 @@ sources = files(
|
|||||||
'src/module/xep/0447_stateless_file_sharing.vala',
|
'src/module/xep/0447_stateless_file_sharing.vala',
|
||||||
'src/module/xep/0461_replies.vala',
|
'src/module/xep/0461_replies.vala',
|
||||||
'src/module/xep/0482_call_invites.vala',
|
'src/module/xep/0482_call_invites.vala',
|
||||||
'src/module/xep/pixbuf_storage.vala',
|
|
||||||
'src/util.vala',
|
'src/util.vala',
|
||||||
)
|
)
|
||||||
c_args = [
|
c_args = [
|
||||||
|
@ -12,6 +12,18 @@ public string random_uuid() {
|
|||||||
return "%08x-%04x-%04x-%04x-%04x%08x".printf(b1, b2, b3, b4, b5_1, b5_2);
|
return "%08x-%04x-%04x-%04x-%04x%08x".printf(b1, b2, b3, b4, b5_1, b5_2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Bytes? get_data_for_uri(string uri) {
|
||||||
|
if (uri.has_suffix("@bob.xmpp.org")) {
|
||||||
|
string cid = uri.replace("cid:", "");
|
||||||
|
return Xep.BitsOfBinary.known_bobs[cid];
|
||||||
|
} else if (uri.has_prefix("data:image/png;base64,")) {
|
||||||
|
return new Bytes.take(Base64.decode(uri.replace("data:image/png;base64,", "")));
|
||||||
|
} else {
|
||||||
|
warning("Couldn't parse data from uri %s", uri);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public abstract class StanzaListener<T> : OrderedListener {
|
public abstract class StanzaListener<T> : OrderedListener {
|
||||||
|
|
||||||
public abstract async bool run(XmppStream stream, T stanza);
|
public abstract async bool run(XmppStream stream, T stanza);
|
||||||
|
66
xmpp-vala/src/module/xep/0231_bits_of_binary.vala
Normal file
66
xmpp-vala/src/module/xep/0231_bits_of_binary.vala
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
using Gee;
|
||||||
|
|
||||||
|
namespace Xmpp.Xep.BitsOfBinary {
|
||||||
|
|
||||||
|
public const string NS_URI = "urn:xmpp:bob";
|
||||||
|
|
||||||
|
public static HashMap<string, Bytes> known_bobs = null;
|
||||||
|
|
||||||
|
public class Module : XmppStreamModule {
|
||||||
|
public static ModuleIdentity<Module> IDENTITY = new ModuleIdentity<Module>(NS_URI, "bits_of_binary");
|
||||||
|
|
||||||
|
private ReceivedPipelineListener received_pipeline_listener = new ReceivedPipelineListener();
|
||||||
|
|
||||||
|
public override void attach(XmppStream stream) {
|
||||||
|
known_bobs = new HashMap<string, Bytes>();
|
||||||
|
var message_module = stream.get_module(MessageModule.IDENTITY);
|
||||||
|
if (message_module != null) {
|
||||||
|
message_module.received_pipeline.connect(received_pipeline_listener);
|
||||||
|
}
|
||||||
|
stream.received_iq_stanza.connect(on_received_iq_stanza);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void detach(XmppStream stream) {
|
||||||
|
var message_module = stream.get_module(MessageModule.IDENTITY);
|
||||||
|
if (message_module != null) {
|
||||||
|
message_module.received_pipeline.disconnect(received_pipeline_listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.received_iq_stanza.disconnect(on_received_iq_stanza);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void on_received_iq_stanza(XmppStream stream, StanzaNode node) {
|
||||||
|
if (node.sub_nodes == null || node.sub_nodes.size == 0) return;
|
||||||
|
Gee.List<StanzaNode> data_nodes = node.sub_nodes[0].get_subnodes("data", NS_URI);
|
||||||
|
foreach (var data_node in data_nodes) {
|
||||||
|
string cid = data_node.get_attribute("cid", NS_URI);
|
||||||
|
if (cid == null) continue;
|
||||||
|
|
||||||
|
known_bobs[cid] = new Bytes.take(Base64.decode(data_node.get_string_content()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string get_ns() { return NS_URI; }
|
||||||
|
public override string get_id() { return IDENTITY.id; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReceivedPipelineListener : StanzaListener<MessageStanza> {
|
||||||
|
|
||||||
|
private const string[] after_actions_const = {};
|
||||||
|
|
||||||
|
public override string action_group { get { return ""; } }
|
||||||
|
public override string[] after_actions { get { return after_actions_const; } }
|
||||||
|
|
||||||
|
public override async bool run(XmppStream stream, MessageStanza message) {
|
||||||
|
Gee.List<StanzaNode> data_nodes = message.stanza.get_subnodes("data", NS_URI);
|
||||||
|
foreach (var data_node in data_nodes) {
|
||||||
|
string cid = data_node.get_attribute("cid", NS_URI);
|
||||||
|
if (cid == null) continue;
|
||||||
|
|
||||||
|
known_bobs[cid] = new Bytes.take(Base64.decode(data_node.get_string_content()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,14 +13,16 @@ namespace Xmpp.Xep.JingleContentThumbnails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class Thumbnail {
|
public class Thumbnail {
|
||||||
public string uri;
|
public Bytes data;
|
||||||
public string? media_type;
|
public string? media_type;
|
||||||
public int width;
|
public int width;
|
||||||
public int height;
|
public int height;
|
||||||
|
|
||||||
public StanzaNode to_stanza_node() {
|
public StanzaNode to_stanza_node() {
|
||||||
|
string data_uri = "data:image/png;base64," + Base64.encode(data.get_data());
|
||||||
|
|
||||||
StanzaNode node = new StanzaNode.build("thumbnail", NS_URI).add_self_xmlns()
|
StanzaNode node = new StanzaNode.build("thumbnail", NS_URI).add_self_xmlns()
|
||||||
.put_attribute("uri", this.uri);
|
.put_attribute("uri", data_uri);
|
||||||
if (this.media_type != null) {
|
if (this.media_type != null) {
|
||||||
node.put_attribute("media-type", this.media_type);
|
node.put_attribute("media-type", this.media_type);
|
||||||
}
|
}
|
||||||
@ -34,11 +36,13 @@ namespace Xmpp.Xep.JingleContentThumbnails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Thumbnail? from_stanza_node(StanzaNode node) {
|
public static Thumbnail? from_stanza_node(StanzaNode node) {
|
||||||
|
string uri = node.get_attribute("uri");
|
||||||
|
if (uri == null) return null;
|
||||||
|
|
||||||
|
Bytes data = Xmpp.get_data_for_uri(uri);
|
||||||
|
|
||||||
Thumbnail thumbnail = new Thumbnail();
|
Thumbnail thumbnail = new Thumbnail();
|
||||||
thumbnail.uri = node.get_attribute("uri");
|
thumbnail.data = data;
|
||||||
if (thumbnail.uri == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
thumbnail.media_type = node.get_attribute("media-type");
|
thumbnail.media_type = node.get_attribute("media-type");
|
||||||
thumbnail.width = node.get_attribute_int("width");
|
thumbnail.width = node.get_attribute_int("width");
|
||||||
thumbnail.height = node.get_attribute_int("height");
|
thumbnail.height = node.get_attribute_int("height");
|
||||||
|
@ -6,23 +6,28 @@ namespace Xmpp.Xep.FallbackIndication {
|
|||||||
|
|
||||||
public class Fallback {
|
public class Fallback {
|
||||||
public string ns_uri { get; set; }
|
public string ns_uri { get; set; }
|
||||||
public FallbackLocation[] locations;
|
public Gee.List<FallbackLocation> locations;
|
||||||
|
|
||||||
|
public Fallback(string ns_uri, Gee.List<FallbackLocation> locations) {
|
||||||
public Fallback(string ns_uri, FallbackLocation[] locations) {
|
|
||||||
this.ns_uri = ns_uri;
|
this.ns_uri = ns_uri;
|
||||||
this.locations = locations;
|
this.locations = locations;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FallbackLocation {
|
public class FallbackLocation {
|
||||||
|
public bool is_whole { get { return from_char == -1 && to_char == -1; } }
|
||||||
public int from_char { get; set; }
|
public int from_char { get; set; }
|
||||||
public int to_char { get; set; }
|
public int to_char { get; set; }
|
||||||
|
|
||||||
public FallbackLocation(int from_char, int to_char) {
|
public FallbackLocation.partial_body(int from_char, int to_char) {
|
||||||
this.from_char = from_char;
|
this.from_char = from_char;
|
||||||
this.to_char = to_char;
|
this.to_char = to_char;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FallbackLocation.whole_body() {
|
||||||
|
this.from_char = -1;
|
||||||
|
this.to_char = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void set_fallback(MessageStanza message, Fallback fallback) {
|
public static void set_fallback(MessageStanza message, Fallback fallback) {
|
||||||
@ -30,10 +35,12 @@ namespace Xmpp.Xep.FallbackIndication {
|
|||||||
.add_self_xmlns()
|
.add_self_xmlns()
|
||||||
.put_attribute("for", fallback.ns_uri);
|
.put_attribute("for", fallback.ns_uri);
|
||||||
foreach (FallbackLocation location in fallback.locations) {
|
foreach (FallbackLocation location in fallback.locations) {
|
||||||
fallback_node.put_node(new StanzaNode.build("body", NS_URI)
|
var node = new StanzaNode.build("body", NS_URI).add_self_xmlns();
|
||||||
.add_self_xmlns()
|
if (!location.is_whole) {
|
||||||
.put_attribute("start", location.from_char.to_string())
|
node.put_attribute("start", location.from_char.to_string())
|
||||||
.put_attribute("end", location.to_char.to_string()));
|
.put_attribute("end", location.to_char.to_string());
|
||||||
|
}
|
||||||
|
fallback_node.put_node(node);
|
||||||
}
|
}
|
||||||
message.stanza.put_node(fallback_node);
|
message.stanza.put_node(fallback_node);
|
||||||
}
|
}
|
||||||
@ -48,18 +55,24 @@ namespace Xmpp.Xep.FallbackIndication {
|
|||||||
string? ns_uri = fallback_node.get_attribute("for");
|
string? ns_uri = fallback_node.get_attribute("for");
|
||||||
if (ns_uri == null) continue;
|
if (ns_uri == null) continue;
|
||||||
|
|
||||||
Gee.List<StanzaNode> body_nodes = fallback_node.get_subnodes("body", NS_URI);
|
|
||||||
if (body_nodes.is_empty) continue;
|
|
||||||
|
|
||||||
var locations = new ArrayList<FallbackLocation>();
|
var locations = new ArrayList<FallbackLocation>();
|
||||||
|
if (fallback_node.get_all_subnodes().is_empty) {
|
||||||
|
locations.add(new FallbackLocation.whole_body());
|
||||||
|
} else {
|
||||||
|
Gee.List<StanzaNode> body_nodes = fallback_node.get_subnodes("body", NS_URI);
|
||||||
foreach (StanzaNode body_node in body_nodes) {
|
foreach (StanzaNode body_node in body_nodes) {
|
||||||
int start_char = body_node.get_attribute_int("start", -1);
|
int start_char = body_node.get_attribute_int("start", -1);
|
||||||
int end_char = body_node.get_attribute_int("end", -1);
|
int end_char = body_node.get_attribute_int("end", -1);
|
||||||
|
if (start_char == -1 && end_char == -1) {
|
||||||
|
locations.add(new FallbackLocation.whole_body());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (start_char == -1 || end_char == -1) continue;
|
if (start_char == -1 || end_char == -1) continue;
|
||||||
locations.add(new FallbackLocation(start_char, end_char));
|
locations.add(new FallbackLocation.partial_body(start_char, end_char));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (locations.is_empty) continue;
|
if (locations.is_empty) continue;
|
||||||
ret.add(new Fallback(ns_uri, locations.to_array()));
|
ret.add(new Fallback(ns_uri, locations));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -20,7 +20,9 @@ namespace Xmpp.Xep.FileMetadataElement {
|
|||||||
node.put_node(new StanzaNode.build("name", NS_URI).put_node(new StanzaNode.text(this.name)));
|
node.put_node(new StanzaNode.build("name", NS_URI).put_node(new StanzaNode.text(this.name)));
|
||||||
}
|
}
|
||||||
if (this.mime_type != null) {
|
if (this.mime_type != null) {
|
||||||
|
// TODO remove the media_type node, it was implemented by accident and temporary provides backwards-compatibility
|
||||||
node.put_node(new StanzaNode.build("media_type", NS_URI).put_node(new StanzaNode.text(this.mime_type)));
|
node.put_node(new StanzaNode.build("media_type", NS_URI).put_node(new StanzaNode.text(this.mime_type)));
|
||||||
|
node.put_node(new StanzaNode.build("media-type", NS_URI).put_node(new StanzaNode.text(this.mime_type)));
|
||||||
}
|
}
|
||||||
if (this.size != -1) {
|
if (this.size != -1) {
|
||||||
node.put_node(new StanzaNode.build("size", NS_URI).put_node(new StanzaNode.text(this.size.to_string())));
|
node.put_node(new StanzaNode.build("size", NS_URI).put_node(new StanzaNode.text(this.size.to_string())));
|
||||||
@ -65,7 +67,12 @@ namespace Xmpp.Xep.FileMetadataElement {
|
|||||||
if (desc_node != null && desc_node.get_string_content() != null) {
|
if (desc_node != null && desc_node.get_string_content() != null) {
|
||||||
metadata.desc = desc_node.get_string_content();
|
metadata.desc = desc_node.get_string_content();
|
||||||
}
|
}
|
||||||
StanzaNode? mime_node = file_node.get_subnode("media_type");
|
// TODO remove media_type handling, it was implemented by accident and temporary provides backwards-compatibility
|
||||||
|
StanzaNode? mime_node_bad = file_node.get_subnode("media_type");
|
||||||
|
if (mime_node_bad != null && mime_node_bad.get_string_content() != null) {
|
||||||
|
metadata.mime_type = mime_node_bad.get_string_content();
|
||||||
|
}
|
||||||
|
StanzaNode? mime_node = file_node.get_subnode("media-type");
|
||||||
if (mime_node != null && mime_node.get_string_content() != null) {
|
if (mime_node != null && mime_node.get_string_content() != null) {
|
||||||
metadata.mime_type = mime_node.get_string_content();
|
metadata.mime_type = mime_node.get_string_content();
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ namespace Xmpp.Xep.StatelessFileSharing {
|
|||||||
var metadata = Xep.FileMetadataElement.get_file_metadata(file_sharing_node);
|
var metadata = Xep.FileMetadataElement.get_file_metadata(file_sharing_node);
|
||||||
if (metadata == null) continue;
|
if (metadata == null) continue;
|
||||||
|
|
||||||
var sources_node = message.stanza.get_subnode("sources", NS_URI);
|
var sources_node = file_sharing_node.get_subnode("sources", NS_URI);
|
||||||
|
|
||||||
ret.add(new FileShare() {
|
ret.add(new FileShare() {
|
||||||
id = file_sharing_node.get_attribute("id", NS_URI),
|
id = file_sharing_node.get_attribute("id", NS_URI),
|
||||||
@ -44,6 +44,16 @@ namespace Xmpp.Xep.StatelessFileSharing {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool is_sfs_fallback_message(MessageStanza message) {
|
||||||
|
Gee.List<FallbackIndication.Fallback> fallbacks = Xep.FallbackIndication.get_fallbacks(message);
|
||||||
|
foreach (var fallback in fallbacks) {
|
||||||
|
if (fallback.ns_uri == StatelessFileSharing.NS_URI && fallback.locations.any_match((it) => it.is_whole)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Currently only returns a single http source
|
// Currently only returns a single http source
|
||||||
private static Gee.List<Source>? get_sources(StanzaNode sources_node) {
|
private static Gee.List<Source>? get_sources(StanzaNode sources_node) {
|
||||||
string? url = HttpSchemeForUrlData.get_url(sources_node);
|
string? url = HttpSchemeForUrlData.get_url(sources_node);
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
using Gdk;
|
|
||||||
|
|
||||||
namespace Xmpp.Xep {
|
|
||||||
public interface PixbufStorage : Object {
|
|
||||||
public abstract void store(string id, Bytes data);
|
|
||||||
public abstract bool has_image(string id);
|
|
||||||
public abstract async Pixbuf? get_image(string id);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user