Compare commits

..

223 Commits

Author SHA1 Message Date
fiaxh
dbedfe3f49 Conversation details: Allow topic edits for moderators 2025-06-15 11:59:23 +02:00
fiaxh
b2163a7ca5 File Send Dialog: Make file widget static 2025-06-14 21:48:14 +02:00
fiaxh
bd8f277b91 Add basic support for BOB thumbnails in SFS 2025-06-14 17:50:07 +02:00
fiaxh
1dae4e1abd Support media elements in data forms 2025-06-14 17:06:45 +02:00
fiaxh
1254a753eb Support media elements in data forms 2025-06-14 17:02:01 +02:00
fiaxh
5eabd7ac38 xmpp-vala: Add basic BOB support 2025-06-14 17:00:24 +02:00
fiaxh
2956401e48 xmpp-vala: Fix SFS mime_type wire-format 2025-06-14 16:59:18 +02:00
fiaxh
915b18789f Disregard SFS fallback messages even without source attachment given a full-body fallback indication 2025-06-13 19:40:09 +02:00
fiaxh
2e6454a92e Add support for whole body fallbacks 2025-06-13 19:37:27 +02:00
Jacques Comeaux
715b874801 Voice processor: add back call to set stream delay 2025-05-23 15:08:28 -06:00
fabrixxm
22616c7458 FileTransfer fix store source
Store source in db and add sources to `sfs_sources` is divided
in two functions. `persists()` now calls `store_source`, because
the sources it want to persist are already added to `sfs_sources`.
2025-05-23 19:05:53 +02:00
fabrixxm
0a564f80ab Fix parse incoming stateless file sharing sources 2025-05-23 18:18:50 +02:00
fiaxh
c40577ed76 Conversation details: Show used account if multiple are active 2025-05-05 14:32:17 +02:00
fiaxh
953b384ef7 Account details: Make own jid selectable 2025-05-05 14:12:30 +02:00
fiaxh
c8da1cfb53 Remove pixbuf from libdino api surface and xmpp-vala 2025-04-18 16:52:46 +02:00
Marvin W
5af1b2cd0d
AppData: Set display_length to 360
"small" was removed about a year ago.
2025-04-11 21:46:32 +02:00
Marvin W
af420fe443
Update flatpak dependencies 2025-04-11 20:50:52 +02:00
Marvin W
618af16bc7
Strip leading "v" from tag when generating display version 2025-04-11 19:22:51 +02:00
Marvin W
e23f2d8a68
Prepare for 0.5 release 2025-04-11 18:30:23 +02:00
Marvin W
f6815def35
Use debug instead of print 2025-04-11 18:30:23 +02:00
Translations
010d0c40ef Update translations 2025-04-11 18:09:39 +02:00
Marvin W
f5fde137bb
Fix potential crashes during account disabling 2025-04-11 11:51:36 +02:00
Marvin W
643a31535b
Clamp color channel values before converting to hex
This is needed as `GdkRGBA` may return out of range values when doing
colorspace conversion.

See https://gitlab.gnome.org/GNOME/gtk/-/issues/7488
2025-04-10 13:05:20 +02:00
Marvin W
e6e1d4aef4
xmpp: Remove connection timeout before trying a new connection
Fixes #1666
2025-04-09 20:44:51 +02:00
Marvin W
50eeb2f16d
RTP: Tear down most of GStreamer while not in a call.
This includes #1679 and works around an upstream bug in pipewire causing #1685
2025-04-09 20:05:13 +02:00
Translations
75b5aa9171 Update translations 2025-04-09 19:31:04 +02:00
fiaxh
50351d63c6 Fix locale search path finding on meson builds 2025-04-09 19:21:19 +02:00
fiaxh
5024f7c815 Fix corner cases in file transmission progress widget 2025-04-09 18:25:33 +02:00
fiaxh
f0e64ff96d Fix call window button style 2025-04-08 13:09:32 +02:00
fiaxh
fc6447c56e Set OMEMO as default encryption 2025-04-08 12:16:07 +02:00
fiaxh
a21800860e Show call button regardless of contact online status 2025-04-08 12:15:12 +02:00
fiaxh
ea54d659e5 Fix sending images with unknown formats 2025-04-07 18:12:33 +02:00
Marvin W
d621c96539
Use deprecated StyleContext.get_color() on GTK<4.14 2025-04-07 16:13:51 +02:00
Marvin W
5bf4223aca
Add Cairo fallback for GTK<4.14 2025-04-07 15:28:51 +02:00
Marvin W
7dc815ac4d
Fix unexpected behavior of get_attribute_uint on systems with 32-bit long 2025-04-07 14:11:05 +02:00
Marvin W
db1830c54e
Improve UI for sfs image progress indicator 2025-04-07 14:11:04 +02:00
Translations
97e7d4c18c Update translations 2025-04-06 21:31:38 +02:00
Marvin W
5b4d301d18
MUC: Close chat when it was closed elsewhere while disconnected 2025-04-06 18:11:38 +02:00
Marvin W
4234dce595
Flatpak: Update to use libomemo-c 2025-04-06 13:58:06 +02:00
Marvin W
7fa44a6ff6
OMEMO: Use libomemo-c instead of libsignal-protocol-c 2025-04-06 13:58:06 +02:00
Marvin W
12c0d27814
Drop CMake support 2025-04-06 13:58:06 +02:00
Marvin W
e434f51103
Meson: Add devel tag to installed VAPI .deps files 2025-04-04 21:00:19 +02:00
Marvin W
01cf823855
Meson: Add option to set rpath on installed binaries 2025-04-04 20:58:31 +02:00
Marvin W
87dfa8e670
Meson: Make plugindir relative to libdir 2025-04-04 20:14:49 +02:00
Marvin W
4b1be6f88f
Improve 24h time format detection 2025-04-04 20:14:45 +02:00
fiaxh
b979436069 Muji calls: Send accept/left to parent MUC 2025-03-27 18:41:18 +01:00
Metalhearf
aa59553c00 Update copyright year in about window 2025-03-27 18:41:00 +01:00
Translations
8320d5a357 Update translations 2025-03-12 16:43:59 +01:00
fiaxh
4cde7809d1 Fix try reconnecting account that hasn't been connected before 2025-03-12 16:13:00 +01:00
Marvin W
9a2ef6ba50
Update Flatpak runtime 2025-03-11 13:54:23 +01:00
fiaxh
fa3a77958a Fix subscription-missing notification appearing in self-chat 2025-02-27 20:56:11 +01:00
Marvin W
e2872d8bcc
Voice processor: merge init and setup methods
This ensures we are never in an uninitialized state when processing audio data
2025-02-24 21:26:04 +01:00
eerielili
76261a2ac5 Update README.md with meson build instructions 2025-02-24 18:12:42 +01:00
Marvin W
a2768774e3
Fix build with webrtc-audio-processing 2.x 2025-02-23 19:31:09 +01:00
Marvin W
1c365daa14
Voice processor: add support for webrtc-audio-processing 1.x and 2.x 2025-02-22 21:06:26 +01:00
Ferdinand Stehle
f796286650
Apply image orientation in conversation view (#1672)
* Apply image orientation in conversation

* Load the full image into pixbuf

---------

Co-authored-by: fiaxh <git@lightrise.org>
2025-02-22 09:32:33 -06:00
Marvin W
fc42c657ee
Fix vertical picture padding for certain aspect ratios 2025-02-22 16:15:33 +01:00
Marvin W
834b1336db
Fix various sizing issues 2025-02-22 11:27:48 +01:00
Marvin W
efe39b8ea0
Fix compatibility with GTK 4.17+ 2025-02-22 11:27:48 +01:00
fiaxh
21869c92a2 SFS: Fix video metadata querying not returning 2025-02-21 15:05:55 +01:00
Marvin W
0b17090231
CMake: Mark as deprecated 2025-02-12 21:23:53 -06:00
Marvin W
b65120373c
Update GitHub actions 2025-02-12 21:23:53 -06:00
Marvin W
f99f0dee52
Flatpak: Use Meson build 2025-02-12 21:23:53 -06:00
Marvin W
901dfff081
DOAP: Fix XML 2025-01-27 11:17:53 +01:00
Miquel Lionel
2ce0d9d3e2 fix: Query duplication in global message search (#1636) 2025-01-24 10:44:47 +01:00
Marvin W
fd915ac76f
Fix build for Vala < 0.56.5 2025-01-18 20:05:46 +01:00
Marvin W
1e5bb8b7e2
Fix build with GTK<4.8 or Meson<1.1.0 2025-01-18 19:49:09 +01:00
Marvin W
b4241eb94a
Meson: Add build option summary 2025-01-18 19:39:25 +01:00
Marvin W
73e28e6c61
Meson: Add all defines to handle dependency versions 2025-01-18 19:39:13 +01:00
Marvin W
b98b597c92
Meson: Add tests 2025-01-12 20:00:51 +01:00
Marvin W
fb26066be4
Meson: Install icons to correct location 2025-01-12 20:00:51 +01:00
Marvin W
613cb5e2d7
Meson: Adjust version generation to match CMake
and be compatible with vala-language-server
2025-01-12 20:00:51 +01:00
Marvin W
a63b5ccb9a
RTP: gtksink no longer needed 2025-01-12 20:00:51 +01:00
fiaxh
6322ed819e Show banner when contact is not yet subscribed 2025-01-12 19:35:58 +01:00
fiaxh
5ee322cbd9 Contact details dialog: Add encryption tab 2025-01-12 19:35:58 +01:00
fiaxh
d78ec05622 Show file upload/download progress 2025-01-12 19:35:06 +01:00
Marvin W
d5c2804769
Search: Build preview string based on chars not bytes 2025-01-03 17:44:43 +01:00
Marvin W
398c52e1b9
Work around pango bug
See https://gitlab.gnome.org/GNOME/pango/-/issues/798 and https://gitlab.gnome.org/GNOME/pango/-/issues/832
2025-01-03 17:44:43 +01:00
fiaxh
b490eb662f
Update README.md 2025-01-02 08:01:45 -06:00
Marvin W
b3976246ec
Fix memory leak when displaying image files 2024-12-27 16:44:22 +01:00
Marvin W
045e555357
Update DOAP 2024-12-27 16:44:21 +01:00
mesonium
eb827175ae
fix: Apply CSS with toolkit and platform version checks (#1643) 2024-12-18 12:51:39 +01:00
fiaxh
79f792e090 Fix and improve stateless file-sharing 2024-11-15 14:40:08 -06:00
Patiga
aaf4542e62 Implement XEP-0447: Stateless file sharing 2024-11-14 10:20:12 -06:00
fiaxh
909f569318 xmpp-vala: StanzaNode.get_attribute_int: Return default value if not parsable as int 2024-11-14 10:20:12 -06:00
Marvin W
a6554e81c5
Fix 0482 call initiation in groups 2024-10-30 11:45:09 +01:00
rodneyrod
f4df3f9a7a Update build.yml to use Gnome 46
Fixes #1596
2024-10-25 15:43:14 +02:00
fiaxh
1e9a45f481 Use XEP-0482 for multi-party call invites 2024-10-18 14:16:33 +02:00
fiaxh
3abe9fdb9e Fix crash on account creation (resource generation) 2024-10-18 14:16:19 +02:00
fiaxh
4d4ba7dde6 Generate new resource on resource conflict 2024-09-29 16:57:31 +02:00
fiaxh
64e4fb0037 Handle TlsError when reading from stream (caused uncatched Exception) 2024-09-29 16:57:31 +02:00
eerielili
439c375393
Store avatars in the user's cache directory. (#1621)
* Store avatars in the user's cache directory.

	- Not anymore in ~/.local/share, where media files are stored.
	- Already existing ~/.local/share/dino/avatars directory will be
	  moved to ~/.cache/dino/avatars
	- If both directories already exists, the old one (in
	  ~/.local/share) is removed.

* Simplify old-to-new-location logic

---------

Co-authored-by: fiaxh <git@lightrise.org>
2024-09-28 16:40:03 +02:00
Linux in a Bit
8853ffead3
Change message padding/margins (#1564)
3px padding on top and bottom of all messages
10px margin on top of messages with usernames
This improves readability among other things.

Co-authored-by: Linux in a Bit <105567407+RageGamerBoi@users.noreply.github.com>
2024-09-23 14:27:34 +02:00
eerielili
d20553a111
Add keyboard shortcut to show preferences (Ctrl+,) (#1435) 2024-09-22 13:27:55 +02:00
fiaxh
3a4ac7c11d XEP-0215: Remove timeout from previous connection 2024-09-22 11:39:13 +01:00
Alexandre Jousset
63439cd206 XEP-0215: management of the field expires
Add a field in `Xmpp.Xep.ExternalServiceDiscovery` to keep track
of the `expires` TURN service value and use it (divided by 2) to
restart periodically the external services discovery.
2024-09-22 11:39:13 +01:00
fiaxh
a7f7b1ceeb Ignore bad stun/turn IP addresses 2024-09-22 11:39:13 +01:00
fiaxh
f8c004630f Add change password functionality
Co-authored-by: Stanislav Malishevskiy <stanislav.malishevskiy@gmail.com>
2024-09-21 17:06:20 +01:00
fiaxh
b09556f033 Preferences Window: Small improvements 2024-09-16 22:45:47 +02:00
hrxi
65404b2442 Discover plugins in Meson build directories
If the environment looks like a Meson build dir (parent directory is
called "main"), then also look for plugins in `../plugins/*`.

Fixes #1591.
2024-09-15 21:07:59 +02:00
fiaxh
228640a881 Code cleanup: Remove remnants of previous accounts dialog 2024-09-15 21:01:24 +02:00
fiaxh
e8f82fd328 Code cleanup: Remove broken disable CSD code 2024-09-15 20:15:14 +02:00
fiaxh
63ba0bc936 Refresh "Add account" UI 2024-09-15 19:35:00 +02:00
fiaxh
13123dced1 Use Adwaita 1.2, bump CI to Ubuntu 24.04 2024-09-15 19:35:00 +02:00
Pigpog
91c8c8eb49 Use new call.incoming notification category
This notification category is pending merge in FreeDesktop. See https://gitlab.freedesktop.org/xdg/xdg-specs/-/merge_requests/50
2024-09-14 22:08:49 +02:00
Translations
a9a21a2dc8 Update translations 2024-09-14 21:55:16 +02:00
Translations
5a4e31e888 Update translations 2024-09-14 20:43:55 +02:00
Translations
51252f74c9 Update translations 2024-08-22 19:52:09 +02:00
fiaxh
1431426581 Omemo: Connect listener only once on account added 2024-08-19 12:28:45 +02:00
fiaxh
dc57561dcf Add cancellable to stream connect 2024-08-19 12:28:45 +02:00
fiaxh
88376cd6f7 Conversation details: Make block button a stateful menu button 2024-08-19 11:36:17 +02:00
Miquel Lionel
8e7dedcaa6 Allow blocking entire domain from conversation details
- the block domain option is in a drop down of the block button
	- when blocking the domain, the "Blocked domain" button appears
	  and block button disappears and vice versa.
2024-08-19 11:36:17 +02:00
Matthew Fennell
cfb0ca2a64
Add omemo key management tooltip (#1603) 2024-08-18 20:40:55 +02:00
fiaxh
b0ff90a14a Add initial message markup (XEP-0394) support 2024-07-29 13:16:54 +02:00
fiaxh
ceb921a014 Store reply message as sent, with fallback 2024-07-29 13:15:14 +02:00
fiaxh
ca980ea409 Fix shift+enter in chat input not scrolling down 2024-07-20 19:15:13 +02:00
Marvin W
c95b65e5b4
OMEMO: Do not show message for OMEMO messages without payload 2024-07-19 18:26:18 +02:00
Translations
3497b3898c Update translations 2024-07-01 12:55:23 +02:00
Translations
7374218ece Update translations 2024-07-01 12:11:43 +02:00
Marvin W
5cf8809e3b
Update appdata 2024-06-30 22:33:06 +02:00
Matthew Fennell
f55b27716a Allow self-signed .onion file transfer certs (#1149)
Most Certificate Authorities don't support issuing X.509 certificates for onion
sites. However, it can still be useful to provide a certificate over Tor in
some circumstances, for instance to tie your alphanumeric Tor address to your
site's main identity.

Therefore, many Tor services provide self-signed certificates. This is OK,
since the onion service itself guarantees that you are connecting to the entity
you think you are.

Dino already allows self-signed certs when communicating over Tor (see
81a5505). However, the same exception does not exist yet for HTTP uploads and
downloads - causing these to fail over Tor.

Therefore, in this commit, we add the same exception for uploads/downloads, by
passing the host of the upload/download urls to the already existing invalid
certificate connection handler.

Note that this handler only allows certificates with type
TlsCertificateFlags.UNKNOWN_CA. This means the certificate of your server must
also include the onion http upload and download URLs in its certificate -
otherwise, the file transfer will fail with TlsCertificateFlags.BAD_IDENTITY.
2024-06-29 11:54:08 +02:00
Matthew Fennell
da4ded964f
Self-ping with server-given roomnick (#1594)
* Self-ping with server-given roomnick (#1467)

XEP-0045 describes how a server can modify a user's roomnick when joining a
room. To do this, it assigns status code 210, and sets the "from" attribute to
a JID with thew newly assigned nickname when sending the user's presence back
to the client.

This is used in IRC gateways such as biboumi. Since you can only have one nick
per IRC server, if you try and join multiple IRC-bridged MUCs on the same IRC
server, it will modify your nick to match the existing one you have on that
server.

Currently, when Dino performs a self-ping after joining a room, it uses the
nick that the client first requested, instead of the nick returned from the
server. This can lead to incorrect self-pings being sent out (until the client
is restarted, and populates mucs_todo correctly). This happens because it adds
the requested Jid to mucs_todo, before getting the given nick from the server.

Therefore, this commit adds the jid that has been given from the server
instead. If there is any error in requesting to join the MUC, it adds the
requested nick, to match existing behaviour and allow the request to be retried
via future self-pings.

fixes #1467

* Minor coding style changes

---------

Co-authored-by: fiaxh <git@lightrise.org>
2024-06-28 16:33:59 +02:00
fiaxh
749a0f1fae Join Channel dialog: Fix displaying of bookmark details 2024-06-23 23:02:27 +02:00
fiaxh
90ea9c4da4 Preferences dialog: Various improvements
- Only show avatar remove button if one is set, fixes #1589
- Only show account picker if user has more than one account
2024-06-21 17:58:04 +02:00
fiaxh
8b15417e0f Start chat: Adjust status icon position, prioritize statuses, update status 2024-06-21 14:09:44 +02:00
Miquel Lionel
00188bdf9b Show contact status in "Start chat" window
should close #139
- adds svg dino-status-offline.svg
2024-06-21 13:08:28 +02:00
fiaxh
984ae3f5b8 Settings dialog: Fix account subpage for Adwaita < 1.4, other fixes
fixes #1592
2024-06-20 18:57:24 +02:00
fiaxh
680f0dd0a7 Fix meson build files 2024-06-20 14:03:14 +02:00
fiaxh
21ae42762d Redesign and rewrite accounts and settings dialog into a combined one 2024-06-20 12:14:46 +02:00
fiaxh
c8b20d0f5f Store requested disco results with computed hash, use for offline determining of private MUCs 2024-06-19 20:08:07 +02:00
fiaxh
f1be90c02f Add logic for OMEMO by default setting 2024-05-26 17:28:28 +02:00
fiaxh
fe45ab575c Support avatar deletion 2024-05-26 17:21:04 +02:00
Marvin W
8be4f02723
AppData/DOAP: Use sentence case for summary.
The suggestion is to not use title case which we did before.
2024-05-09 16:06:32 +02:00
Marvin W
6eeac8a5a1
Flatpak: Bump runtime version to 46 2024-05-09 14:02:14 +02:00
Marvin W
20d84a4945
AppData: Add brand color 2024-05-09 12:48:11 +02:00
Christopher Vollick
d473efcbfe
Add WeakTimeout Pattern to Prevent Leaks
While doing testing I noticed that skeletons were being leaked, and
eventually tracked it down to the timer that updates the time label
closing over "this" and then keeping the reference alive, potentially
for 24 hours.

I noticed a few other places in the code doing some version of this, and
one of them had the "static and weak pointer" approach, which I pulled
out into a util class. Now, we still have to make sure we're passing it
a static method instead of a lambda, as that would also close over
"this" and render the whole thing useless, but at least most of the
annoying parts live in the util class now.

Also the call_widget version was doing a weird thing where it was
removing itself, but then returning "true"? I'm not sure what that
accomplishes, because returning "false" means to not run this again. So
I think my new version is the same in practice, but simpler...

There are other timeouts in the code that I briefly looked over, but all
of them seemed to be relatively short hard-coded durations, so I left
them alone.

But if any of them are long-lived, it's possible they could also benefit
from this class in the future.

Closes #1480

Co-Authored-By: Marvin W <git@larma.de>
2024-04-28 21:36:27 +02:00
Marvin W
ba83a4ba3d
Calls: Correctly display information in partially encrypted calls
This should never happen in practice, but now we will correctly display
if a call has encrypted audio, but unencrypted video, or vice-versa.
2024-04-28 21:35:07 +02:00
Rico Tzschichholz
6575029555
Annotating with array_null_terminated doesn't imply a missing array_length
Taken from 6b8a3e4faa
2024-04-28 21:35:07 +02:00
eerielili
d9fa4daa6a
Fix message stanza with body changing MUC subject (#1569)
- fixes https://github.com/dino/dino/issues/1542
            - more consistent with
              https://xmpp.org/extensions/xep-0045.html#enter-subject:
                    "Note: In accordance with the core definition of XML stanzas,
                    any message can contain a <subject/> element; only a message that
                    contains a <subject/> but no <body/> element shall be considered a
                    subject change for MUC purposes."
2024-04-01 13:33:11 +02:00
fiaxh
bd8e03b7ea Remove conversation closing via hover button 2024-03-21 13:52:39 +01:00
fiaxh
2f1b806dfe Introduce conversation menu, add close option 2024-03-21 13:52:39 +01:00
fiaxh
37e81a86dc Conversation details dialog: Fix runtime critical 2024-03-21 13:34:20 +01:00
Alexandre Jousset
bf9f401743
configure: fix typo (VALACFLAGS) (#1550) 2024-03-02 13:30:25 +01:00
eerielili
c8700b44f4
Fix poor contrast of highlight in search results with dark theme (#1557)
- fixes #1308
2024-03-02 13:27:44 +01:00
fiaxh
4cc7e076e6 Add unread indicator
Co-authored-by: Alexandre Jousset <mid@gtmp.org>
Co-authored-by: Aidan Epstein <aidan@jmad.org>
2024-03-02 13:18:53 +01:00
eerielili
7e3cedaf3f
Enable hyperlinks in topic text to be clicked (#1523)
fixes #1042
2024-01-13 14:27:30 +01:00
Teemu Ikonen
732d3a9814
Change select contact dialog container to AdwClamp (#1533) 2024-01-13 13:56:13 +01:00
eerielili
22516c1862
Fix crash on removing conference not in roster (#1516) 2024-01-10 21:20:50 +01:00
fiaxh
384ef1d3f1 Conversation details dialog: Fix notification+block icons 2023-12-10 15:03:02 +01:00
eerielili
4689fcb53c
Fix segfault opening conversation details when no XEP-0191 support (#1513)
fixes #1508
2023-12-10 13:28:22 +01:00
eerielili
85ea7e5008
Fix http upload for servers without file size limit (#1512)
* Fix for ejabberd XMPP server 'infinity' http upload file size announce

	- fixes https://github.com/dino/dino/issues/1222

* Update 0363_http_file_upload.vala
2023-11-24 22:13:57 +01:00
Alexandre Jousset
cb78cec9e2 main/meson.build:121: fix typo 2023-11-13 22:45:40 +01:00
hrxi
e93e14b12c rtp plugin doesn't depend on GnuTLS 2023-11-13 22:27:50 +01:00
eerielili
86b101900c
Start conversation if closed when receiving an audio or video call (#1485)
* Start conversation if closed when receiving an audio or video call

* Fix starting conversation on new calls, move setting conversation.last_active

---------

Co-authored-by: fiaxh <git@lightrise.org>
2023-10-08 13:51:30 +02:00
fiaxh
8cb195a274 Fix crash due to gpg binding issue 2023-10-07 16:54:09 +02:00
fiaxh
1e167eeea6 Fix some compiler warnings 2023-10-07 14:34:23 +02:00
fiaxh
0c45387bf9 Fix implicit-function-declaration compiler warnings 2023-10-07 13:56:38 +02:00
hrxi
c312fb282f meson: Add version detection for some dependencies 2023-10-06 15:25:12 +02:00
hrxi
a55a10e88f meson: Add RTP options that are also present in the CMakeLists.txt 2023-10-06 15:25:12 +02:00
hrxi
bfc1962f70 meson: Allow enabling/disabling plugins 2023-10-06 15:25:12 +02:00
hrxi
e6938c2965 meson: Add rtp plugin 2023-10-06 15:25:12 +02:00
hrxi
715fabb5bb meson: Add omemo plugin 2023-10-06 15:25:12 +02:00
hrxi
3edda368f3 meson: Add ice plugin 2023-10-06 15:25:12 +02:00
hrxi
7dd0e0aa4a meson: Add crypto-vala library 2023-10-06 15:25:12 +02:00
hrxi
7dd12e7dec meson: Add notification-sound plugin 2023-10-06 15:25:12 +02:00
hrxi
7326ca4d1b meson: Add openpgp plugin 2023-10-06 15:25:12 +02:00
hrxi
6d838c1c31 meson: Add http-files plugin 2023-10-06 15:25:12 +02:00
hrxi
62ed82a495 meson: Install more stuff
Install .vapi, .deps, .h files for the Vala libraries. Also install the
data files. .deps files have to be manually generated, there's a feature
request for automated generation at
https://github.com/mesonbuild/meson/issues/9756.

Import the gnome module globally.

Install dependencies on Meson CI.
2023-10-06 15:25:12 +02:00
hrxi
6eb1b53e60 Merge signal-protocol into omemo plugin
Same reasoning as for the `openpgp` plugin.
2023-10-06 15:25:12 +02:00
hrxi
e2d801b5f7 Merge gpgme-vala into openpgp plugin
There's no reason for it to be a statically linked library anymore, it
can be directly compiled into the plugin.
2023-10-06 15:25:12 +02:00
hrxi
dd0038f5e2 Fix every inclusion of gpgme_fix.h getting their own mutex 2023-10-06 15:25:12 +02:00
fiaxh
c2efb214af conversation details: Fix for libadwaita < 1.4 2023-09-25 15:02:03 +02:00
fiaxh
e2c34bf223 Rewrite contact details dialog 2023-09-24 19:54:04 +02:00
Marvin W
9eafe4139d Fix build on some Vala compiler versions
See https://gitlab.gnome.org/GNOME/vala/-/issues/1474 and https://gitlab.gnome.org/GNOME/vala/-/issues/1478
2023-09-24 19:51:33 +02:00
fiaxh
2fba24ccae Fix subscription notification clearing 2023-09-07 21:30:47 +02:00
mesonium
bc5a1d35cb
fix: Add x node to MUC PM stanza (#1462)
Add <x/> tag in MUC-PMs to support better Carbon delivery in
compliance with XEP-0045 v1.28 and above.

Fixes #1306
2023-07-29 14:02:38 +02:00
Kim Alvefur
d0fca291ac Fix showing the kick option to owners
Missing case in the switch defaulted to returning false for Owners, thus
preventing they with the most privileges from using those privileges.
2023-07-29 13:52:11 +02:00
Marvin W
8c8c2dc4b0
Fix potential crash in video calls 2023-07-09 15:32:53 +02:00
Marvin W
7357b7ecfb Fix certificate start time
I doubt anyone ever looked at it, but it shouldn't be 1 day in the future ;)
2023-07-09 14:32:33 +02:00
Marvin W
1bf57a42fa Do not send DTLS datagrams to RTP even after handshake
Also post debug message in case we drop datagrams
2023-07-09 14:32:33 +02:00
Stephen Paul Weber
f82f788f43 Ignore non-DTLS data before handshake is complete
https://datatracker.ietf.org/doc/html/rfc9147#name-demul
https://datatracker.ietf.org/doc/html/rfc5764#section-5.1.2

If data is received before handshake is complete, discard it rather than
forwarding it blindly to GnuTLS which can get confused.
2023-07-09 14:32:33 +02:00
Robert Mader
35163f08f9 data: Set X-Purism-FormFactor in .desktop file
So the app is detected as mobile-friendly on Phosh.
2023-07-08 11:23:44 +02:00
fiaxh
b830b796a5 Cleanup automatically loaded help overlay 2023-07-08 11:20:51 +02:00
Christopher Davis
3d5dad25d8 application: Load help overlay automatically
GTK automatically loads and sets up the action
and keyboard shortcut for the Keyboard Shortcuts
dialog. We don't need to manually do it as long as
we put everything in the right place.

See https://docs.gtk.org/gtk4/class.Application.html#automatic-resources
2023-07-08 11:20:51 +02:00
Christopher Davis
a36a63d7e4 main_window: Use AdwApplicationWindow
The main window of an app should be an ApplicationWindow.
These windows provide nicer APIs for actions and more.
2023-07-08 11:20:51 +02:00
eerielili
da7be50f05
Add a keyboard shortcut to show keyboard shortcuts (#1432)
Add a keyboard shortcut to show keyboard shortcuts

    - It's Ctrl+?
2023-06-25 13:39:07 +02:00
Tobias Bernard
4bb0c465fc
icons: Refresh some symbolic icons (#1444) 2023-05-29 22:01:33 +02:00
Marvin W
9a04573fdc Fix reactions being made to the wrong message
fixes #1426
2023-05-14 16:44:37 +02:00
fiaxh
9e4def221f Fix chat input for IME
fixes #1419

Co-authored-by: Marvin W <git@larma.de>
2023-05-14 14:12:05 +02:00
fiaxh
287d5bee6e Fix chat input status having a fixed width requirement
fixes #1439
2023-05-13 14:45:37 +02:00
fiaxh
4dfe853fbf Fix xml output intendation 2023-05-13 14:45:37 +02:00
fiaxh
0bddf9f3da Fix character counting for fallbacks
fixes #1420
2023-05-01 19:21:05 +02:00
Karim Malhas
ec6c24c2b4 Focus ChatInput textbox after selecting emoji
After selecting an emoji, the emoji is inserted
into the textbox, but focus remains on the emoji_button.

This causes the EmojiChooser to be opened again if a user
hits the Enter key directly, but text is inserted into the textbox
if they continue to type.

This commit just explicitely focuses on the textbox after
an emoji has been selected.
2023-04-23 11:53:57 +02:00
fiaxh
10315a245d Code cleanup: Remove left-over usages of mam_earliest_synced 2023-04-23 11:48:29 +02:00
fiaxh
2b9a0ccf7e Fix crash on NS_URI call when own server has no MAM; drop broken mam:1 "support"
fixes #1405
2023-04-23 11:40:06 +02:00
Marvin W
6e60cfcbbe
Fix empty alias being handled different than none 2023-04-22 20:08:49 +02:00
fiaxh
03e367ecb8 Fix call window styling 2023-04-22 19:52:28 +02:00
Marvin W
83476d1cad
Fix Flatpak pipewire socket access 2023-04-22 17:19:40 +02:00
fiaxh
5815e757b7 Fix call window controlls hiding 2023-04-22 17:07:29 +02:00
Marvin W
dbb8abc117
Fix video for cameras with rotated image 2023-04-22 17:04:28 +02:00
Marvin W
cad066628a
Build: Adjust to never build with libsignal-protocol-c 2023-04-22 17:03:22 +02:00
Marvin W
bc3738aba1
Fix GitHub CI build-flatpak 2023-04-22 17:03:21 +02:00
Sonny Piers
9b83e5ccc9 Add Github CI job for Flatpak 2023-04-21 00:41:52 +02:00
Sonny Piers
d2ac7a8aeb Add Flatpak manifest 2023-04-21 00:41:52 +02:00
Klemens Nanni
b75b6062ab Always export symbols to fix startup on BSDs
```
$ dino
(dino:38515): Gtk-ERROR **: 15:38:38.538: failed to add UI from resource /im/dino/Dino/unified_main_content.ui: .:26:1 Invalid object type 'DinoUiConversationSelector'
Trace/BPT trap (core dumped)
```

This works on Linux because CMake itself links with `-rdynamic` by default
as per its `Modules/Platform/Linux-*.cmake`.

OpenBSD carries this as local patch, FreeBSD links with `--export-dynamics`.
Just linking with `-rdynamic` also fixes it on OpenBSD, as expected.

https://cmake.org/cmake/help/latest/prop_tgt/ENABLE_EXPORTS.html

Fix #438.
2023-03-24 19:36:32 +01:00
hrxi
32e535a79c Add CI for the meson build 2023-03-24 19:32:50 +01:00
hrxi
5a90e793dd First steps of meson support
Basic configuration of qlite, xmpp-vala, the Dino library and the Dino
application are supported. There's no support for the plugins.

This e.g. enables using the Vala language server.
2023-03-24 19:32:50 +01:00
hrxi
b617bf7cc4 Make members of Plugins.Registry public instead of internal
They are being used from outside the library.
2023-03-24 19:32:50 +01:00
fiaxh
65efaca6fd
Fix images from another client in our account not being displayed right away 2023-03-23 12:14:22 -06:00
Marvin W
ef8fb0e94c
Check sender of bookmark:1 updates 2023-03-23 11:37:47 -06:00
Marvin W
6690d8e4a4
Bind soup session lifetime to File provider/sender lifetime
Required since libsoup 3.4. Fixes #1395
2023-03-22 12:35:13 -06:00
Bohdan Horbeshko
adb2b58b61 Fix a crash if a message subnode is not found in a carbon
Fixes #1392
2023-03-21 17:57:56 -06:00
Sebastian Krzyszkowiak
444275a99d FreeDesktopNotifier: Set notification categories
This provides notifications servers some context on how to handle
the notification.
2023-03-21 17:57:33 -06:00
Michael Vetter
ecf94dd2e6 Remove gspell
7e7dcedaf ported from GTK3 to GTK4.
It also removed gspell from main/CMakeLists.txt.

I assume that gspell is not needed anymore and we can thus remove the
requirement from the CI and the cmake file as well.
2023-03-21 17:57:07 -06:00
Sebastian Krzyszkowiak
57d47b9575 data: Set StartupNotify to true in .desktop file
GTK handles startup notifications, so advertise it in desktop
file. This allows splash screens and other startup indications
in DEs to work.
2023-03-21 17:56:53 -06:00
Marvin W
4e1311dfa9
Improve database performance while reconnecting and syncing
Also move some tasks to low priority idle queue so they won't block UI updates
2023-03-21 17:35:58 -06:00
Marvin W
3721027edb
Improve history sync
- Ensure we fully fetch desired history if possible (previously, duplicates
  from offline message queue could hinder MAM sync)
- Early drop illegal MAM messages so they don't pile up in the pending queue
  waiting for their query to end (which it never will if they were not
  requested in first place).

Fixes #1386
2023-03-21 17:35:58 -06:00
Marvin W
cb10110c57
Fix C binding for gst_video_frame_get_data
Fixes #1267
2023-03-20 15:53:53 -06:00
Marvin W
748d507a3e
Add missing since to DOAP 2023-03-20 15:53:53 -06:00
Marvin W
47a066987d
DOAP: Add first supported version for more XEPs 2023-03-07 09:47:29 -06:00
Marvin W
56195fd2b0
Update XEPs in DOAP
Fixes #1376
2023-03-06 19:38:22 +01:00
Marvin W
db3b0d5f23
New Avatar UI 2023-03-05 16:47:46 +01:00
Marvin W
d818296520
Implement XEP-0392: Consistent Color Generation 2023-03-05 16:47:46 +01:00
510 changed files with 54713 additions and 35375 deletions

View File

@ -0,0 +1,17 @@
{
"problemMatcher": [
{
"owner": "gcc-problem-matcher",
"pattern": [
{
"regexp": "^(.*?):(\\d+):(\\d*):?\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
]
}
]
}

View File

@ -0,0 +1,17 @@
{
"problemMatcher": [
{
"owner": "meson-problem-matcher",
"pattern": [
{
"regexp": "^(.*?)?:(\\d+)?:(\\d+)?: (WARNING|ERROR):\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
]
}
]
}

View File

@ -0,0 +1,17 @@
{
"problemMatcher": [
{
"owner": "vala-problem-matcher",
"pattern": [
{
"regexp": "^(?:../)?(.*?):(\\d+).(\\d+)-\\d+.\\d+:?\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,
"severity": 4,
"message": 5
}
]
}
]
}

View File

@ -2,13 +2,42 @@ name: Build
on: [pull_request, push]
jobs:
build:
runs-on: ubuntu-22.04
name: "Build"
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v2
- run: sudo apt-get update
- run: sudo apt-get remove libunwind-14-dev
- run: sudo apt-get install -y build-essential gettext cmake valac libgee-0.8-dev libsqlite3-dev libgtk-4-dev libnotify-dev libgpgme-dev libsoup2.4-dev libgcrypt20-dev libqrencode-dev libnice-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libsrtp2-dev libwebrtc-audio-processing-dev libadwaita-1-dev
- run: ./configure --with-tests --with-libsignal-in-tree
- run: make
- run: build/xmpp-vala-test
- run: build/signal-protocol-vala-test
- name: "Checkout sources"
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: "Setup matchers"
run: |
echo '::add-matcher::${{ github.workspace }}/.github/matchers/gcc-problem-matcher.json'
echo '::add-matcher::${{ github.workspace }}/.github/matchers/vala-problem-matcher.json'
echo '::add-matcher::${{ github.workspace }}/.github/matchers/meson-problem-matcher.json'
- name: "Setup dependencies"
run: |
sudo apt-get update
sudo apt-get remove libunwind-14-dev
sudo apt-get install -y build-essential gettext libadwaita-1-dev libcanberra-dev libgcrypt20-dev libgee-0.8-dev libgpgme-dev libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev libgtk-4-dev libnice-dev libnotify-dev libqrencode-dev libsignal-protocol-c-dev libsoup-3.0-dev libsqlite3-dev libsrtp2-dev libwebrtc-audio-processing-dev meson valac
- name: "Configure"
run: meson setup build
- name: "Build"
run: meson compile -C build
- name: "Test"
run: meson test -C build
build-flatpak:
name: "Build flatpak"
runs-on: ubuntu-24.04
container:
image: bilelmoussaoui/flatpak-github-actions:gnome-46
options: --privileged
steps:
- name: "Checkout sources"
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: "Build"
uses: flathub-infra/flatpak-github-actions/flatpak-builder@master
with:
manifest-path: im.dino.Dino.json
bundle: im.dino.Dino.flatpak

4
.gitmodules vendored
View File

@ -1,4 +0,0 @@
[submodule "libsignal-protocol-c"]
path = plugins/signal-protocol/libsignal-protocol-c
url = https://github.com/WhisperSystems/libsignal-protocol-c.git
branch = v2.3.3

View File

@ -1,215 +0,0 @@
cmake_minimum_required(VERSION 3.3)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
include(ComputeVersion)
if (NOT VERSION_FOUND)
project(Dino LANGUAGES C CXX)
elseif (VERSION_IS_RELEASE)
project(Dino VERSION ${VERSION_FULL} LANGUAGES C CXX)
else ()
project(Dino LANGUAGES C CXX)
set(PROJECT_VERSION ${VERSION_FULL})
endif ()
# Prepare Plugins
set(DEFAULT_PLUGINS omemo;openpgp;http-files;ice;rtp)
foreach (plugin ${DEFAULT_PLUGINS})
if ("$CACHE{DINO_PLUGIN_ENABLED_${plugin}}" STREQUAL "")
if (NOT DEFINED DINO_PLUGIN_ENABLED_${plugin}})
set(DINO_PLUGIN_ENABLED_${plugin} "yes" CACHE BOOL "Enable plugin ${plugin}")
else ()
set(DINO_PLUGIN_ENABLED_${plugin} "${DINO_PLUGIN_ENABLED_${plugin}}" CACHE BOOL "Enable plugin ${plugin}" FORCE)
endif ()
if (DINO_PLUGIN_ENABLED_${plugin})
message(STATUS "Enabled plugin: ${plugin}")
else ()
message(STATUS "Disabled plugin: ${plugin}")
endif ()
endif ()
endforeach (plugin)
if (DISABLED_PLUGINS)
foreach(plugin ${DISABLED_PLUGINS})
set(DINO_PLUGIN_ENABLED_${plugin} "no" CACHE BOOL "Enable plugin ${plugin}" FORCE)
message(STATUS "Disabled plugin: ${plugin}")
endforeach(plugin)
endif (DISABLED_PLUGINS)
if (ENABLED_PLUGINS)
foreach(plugin ${ENABLED_PLUGINS})
set(DINO_PLUGIN_ENABLED_${plugin} "yes" CACHE BOOL "Enable plugin ${plugin}" FORCE)
message(STATUS "Enabled plugin: ${plugin}")
endforeach(plugin)
endif (ENABLED_PLUGINS)
set(PLUGINS "")
get_cmake_property(all_variables VARIABLES)
foreach (variable_name ${all_variables})
if (variable_name MATCHES "^DINO_PLUGIN_ENABLED_(.+)$" AND ${variable_name})
list(APPEND PLUGINS ${CMAKE_MATCH_1})
endif()
endforeach ()
list(SORT PLUGINS)
string(REPLACE ";" ", " PLUGINS_TEXT "${PLUGINS}")
message(STATUS "Configuring Dino ${PROJECT_VERSION} with plugins: ${PLUGINS_TEXT}")
# Prepare instal paths
macro(set_path what val desc)
if (NOT ${what})
unset(${what} CACHE)
set(${what} ${val})
endif ()
if (NOT "${${what}}" STREQUAL "${_${what}_SET}")
message(STATUS "${desc}: ${${what}}")
set(_${what}_SET ${${what}} CACHE INTERNAL ${desc})
endif()
endmacro(set_path)
string(REGEX REPLACE "^liblib" "lib" LIBDIR_NAME "lib${LIB_SUFFIX}")
set_path(CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" "Installation directory for architecture-independent files")
set_path(EXEC_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" "Installation directory for architecture-dependent files")
set_path(SHARE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/share" "Installation directory for read-only architecture-independent data")
set_path(BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/bin" "Installation directory for user executables")
set_path(DATA_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/dino" "Installation directory for dino-specific data")
set_path(APPDATA_FILE_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/metainfo" "Installation directory for .appdata.xml files")
set_path(DESKTOP_FILE_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/applications" "Installation directory for .desktop files")
set_path(SERVICE_FILE_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/dbus-1/services" "Installation directory for .service files")
set_path(ICON_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/icons" "Installation directory for icons")
set_path(INCLUDE_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/include" "Installation directory for C header files")
set_path(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/${LIBDIR_NAME}" "Installation directory for object code libraries")
set_path(LOCALE_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/locale" "Installation directory for locale files")
set_path(PLUGIN_INSTALL_DIR "${LIB_INSTALL_DIR}/dino/plugins" "Installation directory for dino plugin object code files")
set_path(VAPI_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/vala/vapi" "Installation directory for Vala API files")
set(TARGET_INSTALL LIBRARY DESTINATION ${LIB_INSTALL_DIR} RUNTIME DESTINATION ${BIN_INSTALL_DIR} PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
set(PLUGIN_INSTALL LIBRARY DESTINATION ${PLUGIN_INSTALL_DIR} RUNTIME DESTINATION ${PLUGIN_INSTALL_DIR})
include(CheckCCompilerFlag)
include(CheckCSourceCompiles)
macro(AddCFlagIfSupported list flag)
string(REGEX REPLACE "[^a-z^A-Z^_^0-9]+" "_" flag_name ${flag})
check_c_compiler_flag(${flag} COMPILER_SUPPORTS${flag_name})
if (${COMPILER_SUPPORTS${flag_name}})
set(${list} "${${list}} ${flag}")
endif ()
endmacro()
if ("Ninja" STREQUAL ${CMAKE_GENERATOR})
AddCFlagIfSupported(CMAKE_C_FLAGS -fdiagnostics-color)
endif ()
# Flags for all C files
AddCFlagIfSupported(CMAKE_C_FLAGS -Wall)
AddCFlagIfSupported(CMAKE_C_FLAGS -Wextra)
AddCFlagIfSupported(CMAKE_C_FLAGS -Werror=format-security)
AddCFlagIfSupported(CMAKE_C_FLAGS -Wno-duplicate-decl-specifier)
AddCFlagIfSupported(CMAKE_C_FLAGS -fno-omit-frame-pointer)
if (NOT VALA_WARN)
set(VALA_WARN "conversion")
endif ()
set(VALA_WARN "${VALA_WARN}" CACHE STRING "Which warnings to show when invoking C compiler on Vala compiler output")
set_property(CACHE VALA_WARN PROPERTY STRINGS "all;unused;qualifier;conversion;deprecated;format;none")
# Vala generates some unused stuff
if (NOT ("all" IN_LIST VALA_WARN OR "unused" IN_LIST VALA_WARN))
AddCFlagIfSupported(VALA_CFLAGS -Wno-unused-but-set-variable)
AddCFlagIfSupported(VALA_CFLAGS -Wno-unused-function)
AddCFlagIfSupported(VALA_CFLAGS -Wno-unused-label)
AddCFlagIfSupported(VALA_CFLAGS -Wno-unused-parameter)
AddCFlagIfSupported(VALA_CFLAGS -Wno-unused-value)
AddCFlagIfSupported(VALA_CFLAGS -Wno-unused-variable)
endif ()
if (NOT ("all" IN_LIST VALA_WARN OR "qualifier" IN_LIST VALA_WARN))
AddCFlagIfSupported(VALA_CFLAGS -Wno-discarded-qualifiers)
AddCFlagIfSupported(VALA_CFLAGS -Wno-discarded-array-qualifiers)
AddCFlagIfSupported(VALA_CFLAGS -Wno-incompatible-pointer-types-discards-qualifiers)
endif ()
if (NOT ("all" IN_LIST VALA_WARN OR "deprecated" IN_LIST VALA_WARN))
AddCFlagIfSupported(VALA_CFLAGS -Wno-deprecated-declarations)
endif ()
if (NOT ("all" IN_LIST VALA_WARN OR "format" IN_LIST VALA_WARN))
AddCFlagIfSupported(VALA_CFLAGS -Wno-missing-braces)
endif ()
if (NOT ("all" IN_LIST VALA_WARN OR "conversion" IN_LIST VALA_WARN))
AddCFlagIfSupported(VALA_CFLAGS -Wno-int-conversion)
AddCFlagIfSupported(VALA_CFLAGS -Wno-pointer-sign)
AddCFlagIfSupported(VALA_CFLAGS -Wno-incompatible-pointer-types)
endif ()
try_compile(__WITHOUT_FILE_OFFSET_BITS_64 ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/cmake/LargeFileOffsets.c COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS})
if (NOT __WITHOUT_FILE_OFFSET_BITS_64)
try_compile(__WITH_FILE_OFFSET_BITS_64 ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR}/cmake/LargeFileOffsets.c COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -D_FILE_OFFSET_BITS=64)
if (__WITH_FILE_OFFSET_BITS_64)
AddCFlagIfSupported(CMAKE_C_FLAGS -D_FILE_OFFSET_BITS=64)
message(STATUS "Enabled large file support using _FILE_OFFSET_BITS=64")
else (__WITH_FILE_OFFSET_BITS_64)
message(STATUS "Large file support not available")
endif (__WITH_FILE_OFFSET_BITS_64)
unset(__WITH_FILE_OFFSET_BITS_64)
endif (NOT __WITHOUT_FILE_OFFSET_BITS_64)
unset(__WITHOUT_FILE_OFFSET_BITS_64)
if ($ENV{USE_CCACHE})
# Configure CCache if available
find_program(CCACHE_BIN ccache)
mark_as_advanced(CCACHE_BIN)
if (CCACHE_BIN)
message(STATUS "Using ccache")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_BIN})
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_BIN})
else (CCACHE_BIN)
message(STATUS "USE_CCACHE was set but ccache was not found")
endif (CCACHE_BIN)
endif ($ENV{USE_CCACHE})
if (NOT NO_DEBUG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g")
set(CMAKE_VALA_FLAGS "${CMAKE_VALA_FLAGS} -g")
endif (NOT NO_DEBUG)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(GLib_GLOBAL_VERSION 2.38)
set(ICU_GLOBAL_VERSION 57)
if (NOT VALA_EXECUTABLE)
unset(VALA_EXECUTABLE CACHE)
endif ()
find_package(Vala 0.34 REQUIRED)
if (VALA_VERSION VERSION_GREATER "0.34.90" AND VALA_VERSION VERSION_LESS "0.36.1" OR # Due to a bug on 0.36.0 (and pre-releases), we need to disable FAST_VAPI
VALA_VERSION VERSION_EQUAL "0.44.10" OR VALA_VERSION VERSION_EQUAL "0.46.4" OR VALA_VERSION VERSION_EQUAL "0.47.1" OR # See Dino issue #646
VALA_VERSION VERSION_EQUAL "0.40.21" OR VALA_VERSION VERSION_EQUAL "0.46.8" OR VALA_VERSION VERSION_EQUAL "0.48.4") # See Dino issue #816
set(DISABLE_FAST_VAPI yes)
endif ()
include(${VALA_USE_FILE})
include(MultiFind)
include(GlibCompileResourcesSupport)
find_package(GLib ${GLib_GLOBAL_VERSION} REQUIRED)
string(REGEX REPLACE "^([0-9]+)\\.[0-9]+(\\.[0-9]+)?" "\\1" GLib_MAJOR_VERSION "${GLib_VERSION}")
string(REGEX REPLACE "^[0-9]+\\.([0-9]+)(\\.[0-9]+)?" "\\1" GLib_MINOR_VERSION "${GLib_VERSION}")
math(EXPR GLib_LAST_RELEASE_MINOR_VERSION "${GLib_MINOR_VERSION} / 2 * 2")
set(CMAKE_VALA_FLAGS "${CMAKE_VALA_FLAGS} --target-glib=${GLib_MAJOR_VERSION}.${GLib_LAST_RELEASE_MINOR_VERSION}")
add_subdirectory(qlite)
add_subdirectory(xmpp-vala)
add_subdirectory(libdino)
add_subdirectory(main)
add_subdirectory(crypto-vala)
add_subdirectory(plugins)
# uninstall target
configure_file("${CMAKE_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/cmake_uninstall.cmake COMMENT "Uninstall the project...")

View File

@ -1,7 +1,11 @@
![Dino](https://dino.im/img/readme_header.svg)
=======
<img src="https://dino.im/img/logo.svg" width="80">
![screenshots](https://dino.im/img/screenshot-main.png)
# Dino
Dino is an XMPP messaging app for Linux using GTK and Vala.
It supports calls, encryption, file transfers, group chats and more.
![screenshot](https://dino.im/img/appdata/screenshot-dino-0.4-main-2244x1644@2.png)
Installation
------------
@ -11,9 +15,9 @@ Build
-----
Make sure to install all [dependencies](https://github.com/dino/dino/wiki/Build#dependencies).
./configure
make
build/dino
meson setup build
meson compile -C build
build/main/dino
Resources
---------
@ -30,8 +34,8 @@ Contribute
License
-------
Dino - Modern Jabber/XMPP Client using GTK+/Vala
Copyright (C) 2016-2023 Dino contributors
Dino - XMPP messaging app using GTK/Vala
Copyright (C) 2016-2025 Dino contributors
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

View File

@ -1,57 +0,0 @@
# This file is used to be invoked at build time. It generates the needed
# resource XML file.
# Input variables that need to provided when invoking this script:
# GXML_OUTPUT The output file path where to save the XML file.
# GXML_COMPRESS_ALL Sets all COMPRESS flags in all resources in resource
# list.
# GXML_NO_COMPRESS_ALL Removes all COMPRESS flags in all resources in
# resource list.
# GXML_STRIPBLANKS_ALL Sets all STRIPBLANKS flags in all resources in
# resource list.
# GXML_NO_STRIPBLANKS_ALL Removes all STRIPBLANKS flags in all resources in
# resource list.
# GXML_TOPIXDATA_ALL Sets all TOPIXDATA flags i nall resources in resource
# list.
# GXML_NO_TOPIXDATA_ALL Removes all TOPIXDATA flags in all resources in
# resource list.
# GXML_PREFIX Overrides the resource prefix that is prepended to
# each relative name in registered resources.
# GXML_RESOURCES The list of resource files. Whether absolute or
# relative path is equal.
# Include the GENERATE_GXML() function.
include(${CMAKE_CURRENT_LIST_DIR}/GenerateGXML.cmake)
# Set flags to actual invocation flags.
if(GXML_COMPRESS_ALL)
set(GXML_COMPRESS_ALL COMPRESS_ALL)
endif()
if(GXML_NO_COMPRESS_ALL)
set(GXML_NO_COMPRESS_ALL NO_COMPRESS_ALL)
endif()
if(GXML_STRIPBLANKS_ALL)
set(GXML_STRIPBLANKS_ALL STRIPBLANKS_ALL)
endif()
if(GXML_NO_STRIPBLANKS_ALL)
set(GXML_NO_STRIPBLANKS_ALL NO_STRIPBLANKS_ALL)
endif()
if(GXML_TOPIXDATA_ALL)
set(GXML_TOPIXDATA_ALL TOPIXDATA_ALL)
endif()
if(GXML_NO_TOPIXDATA_ALL)
set(GXML_NO_TOPIXDATA_ALL NO_TOPIXDATA_ALL)
endif()
# Replace " " with ";" to import the list over the command line. Otherwise
# CMake would interprete the passed resources as a whole string.
string(REPLACE " " ";" GXML_RESOURCES ${GXML_RESOURCES})
# Invoke the gresource XML generation function.
generate_gxml(${GXML_OUTPUT}
${GXML_COMPRESS_ALL} ${GXML_NO_COMPRESS_ALL}
${GXML_STRIPBLANKS_ALL} ${GXML_NO_STRIPBLANKS_ALL}
${GXML_TOPIXDATA_ALL} ${GXML_NO_TOPIXDATA_ALL}
PREFIX ${GXML_PREFIX}
RESOURCES ${GXML_RESOURCES})

View File

@ -1,221 +0,0 @@
include(CMakeParseArguments)
# Path to this file.
set(GCR_CMAKE_MACRO_DIR ${CMAKE_CURRENT_LIST_DIR})
# Compiles a gresource resource file from given resource files. Automatically
# creates the XML controlling file.
# The type of resource to generate (header, c-file or bundle) is automatically
# determined from TARGET file ending, if no TYPE is explicitly specified.
# The output file is stored in the provided variable "output".
# "xml_out" contains the variable where to output the XML path. Can be used to
# create custom targets or doing postprocessing.
# If you want to use preprocessing, you need to manually check the existence
# of the tools you use. This function doesn't check this for you, it just
# generates the XML file. glib-compile-resources will then throw a
# warning/error.
function(COMPILE_GRESOURCES output xml_out)
# Available options:
# COMPRESS_ALL, NO_COMPRESS_ALL Overrides the COMPRESS flag in all
# registered resources.
# STRIPBLANKS_ALL, NO_STRIPBLANKS_ALL Overrides the STRIPBLANKS flag in all
# registered resources.
# TOPIXDATA_ALL, NO_TOPIXDATA_ALL Overrides the TOPIXDATA flag in all
# registered resources.
set(CG_OPTIONS COMPRESS_ALL NO_COMPRESS_ALL
STRIPBLANKS_ALL NO_STRIPBLANKS_ALL
TOPIXDATA_ALL NO_TOPIXDATA_ALL)
# Available one value options:
# TYPE Type of resource to create. Valid options are:
# EMBED_C: A C-file that can be compiled with your project.
# EMBED_H: A header that can be included into your project.
# BUNDLE: Generates a resource bundle file that can be loaded
# at runtime.
# AUTO: Determine from target file ending. Need to specify
# target argument.
# PREFIX Overrides the resource prefix that is prepended to each
# relative file name in registered resources.
# SOURCE_DIR Overrides the resources base directory to search for resources.
# Normally this is set to the source directory with that CMake
# was invoked (CMAKE_SOURCE_DIR).
# TARGET Overrides the name of the output file/-s. Normally the output
# names from glib-compile-resources tool is taken.
set(CG_ONEVALUEARGS TYPE PREFIX SOURCE_DIR TARGET)
# Available multi-value options:
# RESOURCES The list of resource files. Whether absolute or relative path is
# equal, absolute paths are stripped down to relative ones. If the
# absolute path is not inside the given base directory SOURCE_DIR
# or CMAKE_SOURCE_DIR (if SOURCE_DIR is not overriden), this
# function aborts.
# OPTIONS Extra command line options passed to glib-compile-resources.
set(CG_MULTIVALUEARGS RESOURCES OPTIONS)
# Parse the arguments.
cmake_parse_arguments(CG_ARG
"${CG_OPTIONS}"
"${CG_ONEVALUEARGS}"
"${CG_MULTIVALUEARGS}"
"${ARGN}")
# Variable to store the double-quote (") string. Since escaping
# double-quotes in strings is not possible we need a helper variable that
# does this job for us.
set(Q \")
# Check invocation validity with the <prefix>_UNPARSED_ARGUMENTS variable.
# If other not recognized parameters were passed, throw error.
if (CG_ARG_UNPARSED_ARGUMENTS)
set(CG_WARNMSG "Invocation of COMPILE_GRESOURCES with unrecognized")
set(CG_WARNMSG "${CG_WARNMSG} parameters. Parameters are:")
set(CG_WARNMSG "${CG_WARNMSG} ${CG_ARG_UNPARSED_ARGUMENTS}.")
message(WARNING ${CG_WARNMSG})
endif()
# Check invocation validity depending on generation mode (EMBED_C, EMBED_H
# or BUNDLE).
if ("${CG_ARG_TYPE}" STREQUAL "EMBED_C")
# EMBED_C mode, output compilable C-file.
set(CG_GENERATE_COMMAND_LINE "--generate-source")
set(CG_TARGET_FILE_ENDING "c")
elseif ("${CG_ARG_TYPE}" STREQUAL "EMBED_H")
# EMBED_H mode, output includable header file.
set(CG_GENERATE_COMMAND_LINE "--generate-header")
set(CG_TARGET_FILE_ENDING "h")
elseif ("${CG_ARG_TYPE}" STREQUAL "BUNDLE")
# BUNDLE mode, output resource bundle. Don't do anything since
# glib-compile-resources outputs a bundle when not specifying
# something else.
set(CG_TARGET_FILE_ENDING "gresource")
else()
# Everything else is AUTO mode, determine from target file ending.
if (CG_ARG_TARGET)
set(CG_GENERATE_COMMAND_LINE "--generate")
else()
set(CG_ERRMSG "AUTO mode given, but no target specified. Can't")
set(CG_ERRMSG "${CG_ERRMSG} determine output type. In function")
set(CG_ERRMSG "${CG_ERRMSG} COMPILE_GRESOURCES.")
message(FATAL_ERROR ${CG_ERRMSG})
endif()
endif()
# Check flag validity.
if (CG_ARG_COMPRESS_ALL AND CG_ARG_NO_COMPRESS_ALL)
set(CG_ERRMSG "COMPRESS_ALL and NO_COMPRESS_ALL simultaneously set. In")
set(CG_ERRMSG "${CG_ERRMSG} function COMPILE_GRESOURCES.")
message(FATAL_ERROR ${CG_ERRMSG})
endif()
if (CG_ARG_STRIPBLANKS_ALL AND CG_ARG_NO_STRIPBLANKS_ALL)
set(CG_ERRMSG "STRIPBLANKS_ALL and NO_STRIPBLANKS_ALL simultaneously")
set(CG_ERRMSG "${CG_ERRMSG} set. In function COMPILE_GRESOURCES.")
message(FATAL_ERROR ${CG_ERRMSG})
endif()
if (CG_ARG_TOPIXDATA_ALL AND CG_ARG_NO_TOPIXDATA_ALL)
set(CG_ERRMSG "TOPIXDATA_ALL and NO_TOPIXDATA_ALL simultaneously set.")
set(CG_ERRMSG "${CG_ERRMSG} In function COMPILE_GRESOURCES.")
message(FATAL_ERROR ${CG_ERRMSG})
endif()
# Check if there are any resources.
if (NOT CG_ARG_RESOURCES)
set(CG_ERRMSG "No resource files to process. In function")
set(CG_ERRMSG "${CG_ERRMSG} COMPILE_GRESOURCES.")
message(FATAL_ERROR ${CG_ERRMSG})
endif()
# Extract all dependencies for targets from resource list.
foreach(res ${CG_ARG_RESOURCES})
if (NOT(("${res}" STREQUAL "COMPRESS") OR
("${res}" STREQUAL "STRIPBLANKS") OR
("${res}" STREQUAL "TOPIXDATA")))
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/resources/${res}"
COMMAND ${CMAKE_COMMAND} -E copy "${CG_ARG_SOURCE_DIR}/${res}" "${CMAKE_CURRENT_BINARY_DIR}/resources/${res}"
MAIN_DEPENDENCY "${CG_ARG_SOURCE_DIR}/${res}")
list(APPEND CG_RESOURCES_DEPENDENCIES "${CMAKE_CURRENT_BINARY_DIR}/resources/${res}")
endif()
endforeach()
# Construct .gresource.xml path.
set(CG_XML_FILE_PATH "${CMAKE_CURRENT_BINARY_DIR}/resources/.gresource.xml")
# Generate gresources XML target.
list(APPEND CG_CMAKE_SCRIPT_ARGS "-D")
list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_OUTPUT=${Q}${CG_XML_FILE_PATH}${Q}")
if(CG_ARG_COMPRESS_ALL)
list(APPEND CG_CMAKE_SCRIPT_ARGS "-D")
list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_COMPRESS_ALL")
endif()
if(CG_ARG_NO_COMPRESS_ALL)
list(APPEND CG_CMAKE_SCRIPT_ARGS "-D")
list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_NO_COMPRESS_ALL")
endif()
if(CG_ARG_STRPIBLANKS_ALL)
list(APPEND CG_CMAKE_SCRIPT_ARGS "-D")
list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_STRIPBLANKS_ALL")
endif()
if(CG_ARG_NO_STRIPBLANKS_ALL)
list(APPEND CG_CMAKE_SCRIPT_ARGS "-D")
list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_NO_STRIPBLANKS_ALL")
endif()
if(CG_ARG_TOPIXDATA_ALL)
list(APPEND CG_CMAKE_SCRIPT_ARGS "-D")
list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_TOPIXDATA_ALL")
endif()
if(CG_ARG_NO_TOPIXDATA_ALL)
list(APPEND CG_CMAKE_SCRIPT_ARGS "-D")
list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_NO_TOPIXDATA_ALL")
endif()
list(APPEND CG_CMAKE_SCRIPT_ARGS "-D")
list(APPEND CG_CMAKE_SCRIPT_ARGS "GXML_PREFIX=${Q}${CG_ARG_PREFIX}${Q}")
list(APPEND CG_CMAKE_SCRIPT_ARGS "-D")
list(APPEND CG_CMAKE_SCRIPT_ARGS
"GXML_RESOURCES=${Q}${CG_ARG_RESOURCES}${Q}")
list(APPEND CG_CMAKE_SCRIPT_ARGS "-P")
list(APPEND CG_CMAKE_SCRIPT_ARGS
"${Q}${GCR_CMAKE_MACRO_DIR}/BuildTargetScript.cmake${Q}")
get_filename_component(CG_XML_FILE_PATH_ONLY_NAME
"${CG_XML_FILE_PATH}" NAME)
set(CG_XML_CUSTOM_COMMAND_COMMENT
"Creating gresources XML file (${CG_XML_FILE_PATH_ONLY_NAME})")
add_custom_command(OUTPUT ${CG_XML_FILE_PATH}
COMMAND ${CMAKE_COMMAND}
ARGS ${CG_CMAKE_SCRIPT_ARGS}
DEPENDS ${CG_RESOURCES_DEPENDENCIES}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT ${CG_XML_CUSTOM_COMMAND_COMMENT})
# Create target manually if not set (to make sure glib-compile-resources
# doesn't change behaviour with it's naming standards).
if (NOT CG_ARG_TARGET)
set(CG_ARG_TARGET "${CMAKE_CURRENT_BINARY_DIR}/resources")
set(CG_ARG_TARGET "${CG_ARG_TARGET}.${CG_TARGET_FILE_ENDING}")
endif()
# Create source directory automatically if not set.
if (NOT CG_ARG_SOURCE_DIR)
set(CG_ARG_SOURCE_DIR "${CMAKE_SOURCE_DIR}")
endif()
# Add compilation target for resources.
add_custom_command(OUTPUT ${CG_ARG_TARGET}
COMMAND ${GLIB_COMPILE_RESOURCES_EXECUTABLE}
ARGS
${OPTIONS}
"--target=${Q}${CG_ARG_TARGET}${Q}"
"--sourcedir=${Q}${CG_ARG_SOURCE_DIR}${Q}"
${CG_GENERATE_COMMAND_LINE}
${CG_XML_FILE_PATH}
MAIN_DEPENDENCY ${CG_XML_FILE_PATH}
DEPENDS ${CG_RESOURCES_DEPENDENCIES}
WORKING_DIRECTORY ${CMAKE_BUILD_DIR})
# Set output and XML_OUT to parent scope.
set(${xml_out} ${CG_XML_FILE_PATH} PARENT_SCOPE)
set(${output} ${CG_ARG_TARGET} PARENT_SCOPE)
endfunction()

View File

@ -1,105 +0,0 @@
include(CMakeParseArguments)
function(_compute_version_from_file)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/VERSION)
if (NOT EXISTS ${CMAKE_SOURCE_DIR}/VERSION)
set(VERSION_FOUND 0 PARENT_SCOPE)
return()
endif ()
file(STRINGS ${CMAKE_SOURCE_DIR}/VERSION VERSION_FILE)
string(REPLACE " " ";" VERSION_FILE "${VERSION_FILE}")
cmake_parse_arguments(VERSION_FILE "" "RELEASE;PRERELEASE" "" ${VERSION_FILE})
if (DEFINED VERSION_FILE_RELEASE)
string(STRIP "${VERSION_FILE_RELEASE}" VERSION_FILE_RELEASE)
set(VERSION_IS_RELEASE 1 PARENT_SCOPE)
set(VERSION_FULL "${VERSION_FILE_RELEASE}" PARENT_SCOPE)
set(VERSION_FOUND 1 PARENT_SCOPE)
elseif (DEFINED VERSION_FILE_PRERELEASE)
string(STRIP "${VERSION_FILE_PRERELEASE}" VERSION_FILE_PRERELEASE)
set(VERSION_IS_RELEASE 0 PARENT_SCOPE)
set(VERSION_FULL "${VERSION_FILE_PRERELEASE}" PARENT_SCOPE)
set(VERSION_FOUND 1 PARENT_SCOPE)
else ()
set(VERSION_FOUND 0 PARENT_SCOPE)
endif ()
endfunction(_compute_version_from_file)
function(_compute_version_from_git)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/.git)
if (NOT GIT_EXECUTABLE)
find_package(Git QUIET)
if (NOT GIT_FOUND)
return()
endif ()
endif (NOT GIT_EXECUTABLE)
# Git tag
execute_process(
COMMAND "${GIT_EXECUTABLE}" describe --tags --abbrev=0
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
RESULT_VARIABLE git_result
OUTPUT_VARIABLE git_tag
ERROR_VARIABLE git_error
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_STRIP_TRAILING_WHITESPACE
)
if (NOT git_result EQUAL 0)
return()
endif (NOT git_result EQUAL 0)
if (git_tag MATCHES "^v?([0-9]+[.]?[0-9]*[.]?[0-9]*)(-[.0-9A-Za-z-]+)?([+][.0-9A-Za-z-]+)?$")
set(VERSION_LAST_RELEASE "${CMAKE_MATCH_1}")
else ()
return()
endif ()
# Git describe
execute_process(
COMMAND "${GIT_EXECUTABLE}" describe --tags
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
RESULT_VARIABLE git_result
OUTPUT_VARIABLE git_describe
ERROR_VARIABLE git_error
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_STRIP_TRAILING_WHITESPACE
)
if (NOT git_result EQUAL 0)
return()
endif (NOT git_result EQUAL 0)
if ("${git_tag}" STREQUAL "${git_describe}")
set(VERSION_IS_RELEASE 1)
else ()
set(VERSION_IS_RELEASE 0)
if (git_describe MATCHES "-([0-9]+)-g([0-9a-f]+)$")
set(VERSION_TAG_OFFSET "${CMAKE_MATCH_1}")
set(VERSION_COMMIT_HASH "${CMAKE_MATCH_2}")
endif ()
execute_process(
COMMAND "${GIT_EXECUTABLE}" show --format=%cd --date=format:%Y%m%d -s
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
RESULT_VARIABLE git_result
OUTPUT_VARIABLE git_time
ERROR_VARIABLE git_error
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_STRIP_TRAILING_WHITESPACE
)
if (NOT git_result EQUAL 0)
return()
endif (NOT git_result EQUAL 0)
set(VERSION_COMMIT_DATE "${git_time}")
endif ()
if (NOT VERSION_IS_RELEASE)
set(VERSION_SUFFIX "~git${VERSION_TAG_OFFSET}.${VERSION_COMMIT_DATE}.${VERSION_COMMIT_HASH}")
else (NOT VERSION_IS_RELEASE)
set(VERSION_SUFFIX "")
endif (NOT VERSION_IS_RELEASE)
set(VERSION_IS_RELEASE ${VERSION_IS_RELEASE} PARENT_SCOPE)
set(VERSION_FULL "${VERSION_LAST_RELEASE}${VERSION_SUFFIX}" PARENT_SCOPE)
set(VERSION_FOUND 1 PARENT_SCOPE)
endfunction(_compute_version_from_git)
_compute_version_from_file()
if (NOT VERSION_FOUND)
_compute_version_from_git()
endif (NOT VERSION_FOUND)

View File

@ -1,31 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(ATK
PKG_CONFIG_NAME atk
LIB_NAMES atk-1.0
INCLUDE_NAMES atk/atk.h
INCLUDE_DIR_SUFFIXES atk-1.0 atk-1.0/include
DEPENDS GObject
)
if(ATK_FOUND AND NOT ATK_VERSION)
find_file(ATK_VERSION_HEADER "atk/atkversion.h" HINTS ${ATK_INCLUDE_DIRS})
mark_as_advanced(ATK_VERSION_HEADER)
if(ATK_VERSION_HEADER)
file(STRINGS "${ATK_VERSION_HEADER}" ATK_MAJOR_VERSION REGEX "^#define ATK_MAJOR_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define ATK_MAJOR_VERSION \\(?([0-9]+)\\)?$" "\\1" ATK_MAJOR_VERSION "${ATK_MAJOR_VERSION}")
file(STRINGS "${ATK_VERSION_HEADER}" ATK_MINOR_VERSION REGEX "^#define ATK_MINOR_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define ATK_MINOR_VERSION \\(?([0-9]+)\\)?$" "\\1" ATK_MINOR_VERSION "${ATK_MINOR_VERSION}")
file(STRINGS "${ATK_VERSION_HEADER}" ATK_MICRO_VERSION REGEX "^#define ATK_MICRO_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define ATK_MICRO_VERSION \\(?([0-9]+)\\)?$" "\\1" ATK_MICRO_VERSION "${ATK_MICRO_VERSION}")
set(ATK_VERSION "${ATK_MAJOR_VERSION}.${ATK_MINOR_VERSION}.${ATK_MICRO_VERSION}")
unset(ATK_MAJOR_VERSION)
unset(ATK_MINOR_VERSION)
unset(ATK_MICRO_VERSION)
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ATK
REQUIRED_VARS ATK_LIBRARY
VERSION_VAR ATK_VERSION)

View File

@ -1,11 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(Adwaita
PKG_CONFIG_NAME libadwaita-1
LIB_NAMES libadwaita-1
INCLUDE_NAMES adwaita.h
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Adwaita
REQUIRED_VARS Adwaita_LIBRARY
VERSION_VAR Adwaita_VERSION)

View File

@ -1,30 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(Cairo
PKG_CONFIG_NAME cairo
LIB_NAMES cairo
INCLUDE_NAMES cairo.h
INCLUDE_DIR_SUFFIXES cairo cairo/include
)
if(Cairo_FOUND AND NOT Cairo_VERSION)
find_file(Cairo_VERSION_HEADER "cairo-version.h" HINTS ${Cairo_INCLUDE_DIRS})
mark_as_advanced(Cairo_VERSION_HEADER)
if(Cairo_VERSION_HEADER)
file(STRINGS "${Cairo_VERSION_HEADER}" Cairo_MAJOR_VERSION REGEX "^#define CAIRO_VERSION_MAJOR +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define CAIRO_VERSION_MAJOR \\(?([0-9]+)\\)?$" "\\1" Cairo_MAJOR_VERSION "${Cairo_MAJOR_VERSION}")
file(STRINGS "${Cairo_VERSION_HEADER}" Cairo_MINOR_VERSION REGEX "^#define CAIRO_VERSION_MINOR +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define CAIRO_VERSION_MINOR \\(?([0-9]+)\\)?$" "\\1" Cairo_MINOR_VERSION "${Cairo_MINOR_VERSION}")
file(STRINGS "${Cairo_VERSION_HEADER}" Cairo_MICRO_VERSION REGEX "^#define CAIRO_VERSION_MICRO +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define CAIRO_VERSION_MICRO \\(?([0-9]+)\\)?$" "\\1" Cairo_MICRO_VERSION "${Cairo_MICRO_VERSION}")
set(Cairo_VERSION "${Cairo_MAJOR_VERSION}.${Cairo_MINOR_VERSION}.${Cairo_MICRO_VERSION}")
unset(Cairo_MAJOR_VERSION)
unset(Cairo_MINOR_VERSION)
unset(Cairo_MICRO_VERSION)
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Cairo
REQUIRED_VARS Cairo_LIBRARY
VERSION_VAR Cairo_VERSION)

View File

@ -1,10 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(Canberra
PKG_CONFIG_NAME libcanberra
LIB_NAMES canberra
INCLUDE_NAMES canberra.h
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Canberra
REQUIRED_VARS Canberra_LIBRARY)

View File

@ -1,10 +0,0 @@
include(PkgConfigWithFallbackOnConfigScript)
find_pkg_config_with_fallback_on_config_script(GCrypt
PKG_CONFIG_NAME libgcrypt
CONFIG_SCRIPT_NAME libgcrypt
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GCrypt
REQUIRED_VARS GCrypt_LIBRARY
VERSION_VAR GCrypt_VERSION)

View File

@ -1,38 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(GDK3
PKG_CONFIG_NAME gdk-3.0
LIB_NAMES gdk-3
INCLUDE_NAMES gdk/gdk.h
INCLUDE_DIR_SUFFIXES gtk-3.0 gtk-3.0/include gtk+-3.0 gtk+-3.0/include
DEPENDS Pango Cairo GDKPixbuf2
)
if(GDK3_FOUND AND NOT GDK3_VERSION)
find_file(GDK3_VERSION_HEADER "gdk/gdkversionmacros.h" HINTS ${GDK3_INCLUDE_DIRS})
mark_as_advanced(GDK3_VERSION_HEADER)
if(GDK3_VERSION_HEADER)
file(STRINGS "${GDK3_VERSION_HEADER}" GDK3_MAJOR_VERSION REGEX "^#define GDK_MAJOR_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define GDK_MAJOR_VERSION \\(?([0-9]+)\\)?$" "\\1" GDK3_MAJOR_VERSION "${GDK3_MAJOR_VERSION}")
file(STRINGS "${GDK3_VERSION_HEADER}" GDK3_MINOR_VERSION REGEX "^#define GDK_MINOR_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define GDK_MINOR_VERSION \\(?([0-9]+)\\)?$" "\\1" GDK3_MINOR_VERSION "${GDK3_MINOR_VERSION}")
file(STRINGS "${GDK3_VERSION_HEADER}" GDK3_MICRO_VERSION REGEX "^#define GDK_MICRO_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define GDK_MICRO_VERSION \\(?([0-9]+)\\)?$" "\\1" GDK3_MICRO_VERSION "${GDK3_MICRO_VERSION}")
set(GDK3_VERSION "${GDK3_MAJOR_VERSION}.${GDK3_MINOR_VERSION}.${GDK3_MICRO_VERSION}")
unset(GDK3_MAJOR_VERSION)
unset(GDK3_MINOR_VERSION)
unset(GDK3_MICRO_VERSION)
endif()
endif()
if (GDK3_FOUND)
find_file(GDK3_WITH_X11 "gdk/gdkx.h" HINTS ${GDK3_INCLUDE_DIRS})
if (GDK3_WITH_X11)
set(GDK3_WITH_X11 yes CACHE INTERNAL "Does GDK3 support X11")
endif (GDK3_WITH_X11)
endif ()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GDK3
REQUIRED_VARS GDK3_LIBRARY
VERSION_VAR GDK3_VERSION)

View File

@ -1,38 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(GDK4
PKG_CONFIG_NAME gdk-4.0
LIB_NAMES gdk-4
INCLUDE_NAMES gdk/gdk.h
INCLUDE_DIR_SUFFIXES gtk-4.0 gtk-4.0/include gtk+-4.0 gtk+-4.0/include
DEPENDS Pango Cairo GDKPixbuf2
)
if(GDK4_FOUND AND NOT GDK4_VERSION)
find_file(GDK4_VERSION_HEADER "gdk/gdkversionmacros.h" HINTS ${GDK4_INCLUDE_DIRS})
mark_as_advanced(GDK4_VERSION_HEADER)
if(GDK4_VERSION_HEADER)
file(STRINGS "${GDK4_VERSION_HEADER}" GDK4_MAJOR_VERSION REGEX "^#define GDK_MAJOR_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define GDK_MAJOR_VERSION \\(?([0-9]+)\\)?$" "\\1" GDK4_MAJOR_VERSION "${GDK4_MAJOR_VERSION}")
file(STRINGS "${GDK4_VERSION_HEADER}" GDK4_MINOR_VERSION REGEX "^#define GDK_MINOR_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define GDK_MINOR_VERSION \\(?([0-9]+)\\)?$" "\\1" GDK4_MINOR_VERSION "${GDK4_MINOR_VERSION}")
file(STRINGS "${GDK4_VERSION_HEADER}" GDK4_MICRO_VERSION REGEX "^#define GDK_MICRO_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define GDK_MICRO_VERSION \\(?([0-9]+)\\)?$" "\\1" GDK4_MICRO_VERSION "${GDK4_MICRO_VERSION}")
set(GDK4_VERSION "${GDK4_MAJOR_VERSION}.${GDK4_MINOR_VERSION}.${GDK4_MICRO_VERSION}")
unset(GDK4_MAJOR_VERSION)
unset(GDK4_MINOR_VERSION)
unset(GDK4_MICRO_VERSION)
endif()
endif()
if (GDK4_FOUND)
find_file(GDK4_WITH_X11 "gdk/gdkx.h" HINTS ${GDK4_INCLUDE_DIRS})
if (GDK4_WITH_X11)
set(GDK4_WITH_X11 yes CACHE INTERNAL "Does GDK4 support X11")
endif (GDK4_WITH_X11)
endif ()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GDK4
REQUIRED_VARS GDK4_LIBRARY
VERSION_VAR GDK4_VERSION)

View File

@ -1,23 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(GDKPixbuf2
PKG_CONFIG_NAME gdk-pixbuf-2.0
LIB_NAMES gdk_pixbuf-2.0
INCLUDE_NAMES gdk-pixbuf/gdk-pixbuf.h
INCLUDE_DIR_SUFFIXES gdk-pixbuf-2.0 gdk-pixbuf-2.0/include
DEPENDS GLib
)
if(GDKPixbuf2_FOUND AND NOT GDKPixbuf2_VERSION)
find_file(GDKPixbuf2_FEATURES_HEADER "gdk-pixbuf/gdk-pixbuf-features.h" HINTS ${GDKPixbuf2_INCLUDE_DIRS})
mark_as_advanced(GDKPixbuf2_FEATURES_HEADER)
if(GDKPixbuf2_FEATURES_HEADER)
file(STRINGS "${GDKPixbuf2_FEATURES_HEADER}" GDKPixbuf2_VERSION REGEX "^#define GDK_PIXBUF_VERSION \\\"[^\\\"]+\\\"")
string(REGEX REPLACE "^#define GDK_PIXBUF_VERSION \\\"([0-9]+)\\.([0-9]+)\\.([0-9]+)\\\"$" "\\1.\\2.\\3" GDKPixbuf2_VERSION "${GDKPixbuf2_VERSION}")
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GDKPixbuf2
REQUIRED_VARS GDKPixbuf2_LIBRARY
VERSION_VAR GDKPixbuf2_VERSION)

View File

@ -1,18 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(GIO
PKG_CONFIG_NAME gio-2.0
LIB_NAMES gio-2.0
INCLUDE_NAMES gio/gio.h
INCLUDE_DIR_SUFFIXES glib-2.0 glib-2.0/include
DEPENDS GObject
)
if(GIO_FOUND AND NOT GIO_VERSION)
find_package(GLib ${GLib_GLOBAL_VERSION})
set(GIO_VERSION ${GLib_VERSION})
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GIO
REQUIRED_VARS GIO_LIBRARY
VERSION_VAR GIO_VERSION)

View File

@ -1,32 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(GLib
PKG_CONFIG_NAME glib-2.0
LIB_NAMES glib-2.0
INCLUDE_NAMES glib.h glibconfig.h
INCLUDE_DIR_HINTS ${CMAKE_LIBRARY_PATH} ${CMAKE_SYSTEM_LIBRARY_PATH}
INCLUDE_DIR_PATHS ${CMAKE_PREFIX_PATH}/lib64 ${CMAKE_PREFIX_PATH}/lib
INCLUDE_DIR_SUFFIXES glib-2.0 glib-2.0/include
)
if(GLib_FOUND AND NOT GLib_VERSION)
find_file(GLib_CONFIG_HEADER "glibconfig.h" HINTS ${GLib_INCLUDE_DIRS})
mark_as_advanced(GLib_CONFIG_HEADER)
if(GLib_CONFIG_HEADER)
file(STRINGS "${GLib_CONFIG_HEADER}" GLib_MAJOR_VERSION REGEX "^#define GLIB_MAJOR_VERSION +([0-9]+)")
string(REGEX REPLACE "^#define GLIB_MAJOR_VERSION ([0-9]+)$" "\\1" GLib_MAJOR_VERSION "${GLib_MAJOR_VERSION}")
file(STRINGS "${GLib_CONFIG_HEADER}" GLib_MINOR_VERSION REGEX "^#define GLIB_MINOR_VERSION +([0-9]+)")
string(REGEX REPLACE "^#define GLIB_MINOR_VERSION ([0-9]+)$" "\\1" GLib_MINOR_VERSION "${GLib_MINOR_VERSION}")
file(STRINGS "${GLib_CONFIG_HEADER}" GLib_MICRO_VERSION REGEX "^#define GLIB_MICRO_VERSION +([0-9]+)")
string(REGEX REPLACE "^#define GLIB_MICRO_VERSION ([0-9]+)$" "\\1" GLib_MICRO_VERSION "${GLib_MICRO_VERSION}")
set(GLib_VERSION "${GLib_MAJOR_VERSION}.${GLib_MINOR_VERSION}.${GLib_MICRO_VERSION}")
unset(GLib_MAJOR_VERSION)
unset(GLib_MINOR_VERSION)
unset(GLib_MICRO_VERSION)
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GLib
REQUIRED_VARS GLib_LIBRARY
VERSION_VAR GLib_VERSION)

View File

@ -1,19 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(GModule
PKG_CONFIG_NAME gmodule-2.0
LIB_NAMES gmodule-2.0
INCLUDE_NAMES gmodule.h
INCLUDE_DIR_SUFFIXES glib-2.0 glib-2.0/include
DEPENDS GLib
)
if(GModule_FOUND AND NOT GModule_VERSION)
# TODO
find_package(GLib ${GLib_GLOBAL_VERSION})
set(GModule_VERSION ${GLib_VERSION})
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GModule
REQUIRED_VARS GModule_LIBRARY
VERSION_VAR GModule_VERSION)

View File

@ -1,19 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(GObject
PKG_CONFIG_NAME gobject-2.0
LIB_NAMES gobject-2.0
INCLUDE_NAMES gobject/gobject.h
INCLUDE_DIR_SUFFIXES glib-2.0 glib-2.0/include
DEPENDS GLib
)
if(GObject_FOUND AND NOT GObject_VERSION)
# TODO
find_package(GLib ${GLib_GLOBAL_VERSION})
set(GObject_VERSION ${GLib_VERSION})
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GObject
REQUIRED_VARS GObject_LIBRARY
VERSION_VAR GObject_VERSION)

View File

@ -1,10 +0,0 @@
include(PkgConfigWithFallbackOnConfigScript)
find_pkg_config_with_fallback_on_config_script(GPGME
PKG_CONFIG_NAME gpgme
CONFIG_SCRIPT_NAME gpgme
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GPGME
REQUIRED_VARS GPGME_LIBRARY
VERSION_VAR GPGME_VERSION)

View File

@ -1,31 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(GTK3
PKG_CONFIG_NAME gtk+-3.0
LIB_NAMES gtk-3
INCLUDE_NAMES gtk/gtk.h
INCLUDE_DIR_SUFFIXES gtk-3.0 gtk-3.0/include gtk+-3.0 gtk+-3.0/include
DEPENDS GDK3 ATK
)
if(GTK3_FOUND AND NOT GTK3_VERSION)
find_file(GTK3_VERSION_HEADER "gtk/gtkversion.h" HINTS ${GTK3_INCLUDE_DIRS})
mark_as_advanced(GTK3_VERSION_HEADER)
if(GTK3_VERSION_HEADER)
file(STRINGS "${GTK3_VERSION_HEADER}" GTK3_MAJOR_VERSION REGEX "^#define GTK_MAJOR_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define GTK_MAJOR_VERSION \\(?([0-9]+)\\)?$" "\\1" GTK3_MAJOR_VERSION "${GTK3_MAJOR_VERSION}")
file(STRINGS "${GTK3_VERSION_HEADER}" GTK3_MINOR_VERSION REGEX "^#define GTK_MINOR_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define GTK_MINOR_VERSION \\(?([0-9]+)\\)?$" "\\1" GTK3_MINOR_VERSION "${GTK3_MINOR_VERSION}")
file(STRINGS "${GTK3_VERSION_HEADER}" GTK3_MICRO_VERSION REGEX "^#define GTK_MICRO_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define GTK_MICRO_VERSION \\(?([0-9]+)\\)?$" "\\1" GTK3_MICRO_VERSION "${GTK3_MICRO_VERSION}")
set(GTK3_VERSION "${GTK3_MAJOR_VERSION}.${GTK3_MINOR_VERSION}.${GTK3_MICRO_VERSION}")
unset(GTK3_MAJOR_VERSION)
unset(GTK3_MINOR_VERSION)
unset(GTK3_MICRO_VERSION)
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GTK3
REQUIRED_VARS GTK3_LIBRARY
VERSION_VAR GTK3_VERSION)

View File

@ -1,30 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(GTK4
PKG_CONFIG_NAME gtk4
LIB_NAMES gtk-4
INCLUDE_NAMES gtk/gtk.h
INCLUDE_DIR_SUFFIXES gtk-4.0 gtk-4.0/include gtk+-4.0 gtk+-4.0/include gtk4 gtk4/include
)
if(GTK4_FOUND AND NOT GTK4_VERSION)
find_file(GTK4_VERSION_HEADER "gtk/gtkversion.h" HINTS ${GTK4_INCLUDE_DIRS})
mark_as_advanced(GTK4_VERSION_HEADER)
if(GTK4_VERSION_HEADER)
file(STRINGS "${GTK4_VERSION_HEADER}" GTK4_MAJOR_VERSION REGEX "^#define GTK_MAJOR_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define GTK_MAJOR_VERSION \\(?([0-9]+)\\)?$" "\\1" GTK4_MAJOR_VERSION "${GTK4_MAJOR_VERSION}")
file(STRINGS "${GTK4_VERSION_HEADER}" GTK4_MINOR_VERSION REGEX "^#define GTK_MINOR_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define GTK_MINOR_VERSION \\(?([0-9]+)\\)?$" "\\1" GTK4_MINOR_VERSION "${GTK4_MINOR_VERSION}")
file(STRINGS "${GTK4_VERSION_HEADER}" GTK4_MICRO_VERSION REGEX "^#define GTK_MICRO_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define GTK_MICRO_VERSION \\(?([0-9]+)\\)?$" "\\1" GTK4_MICRO_VERSION "${GTK4_MICRO_VERSION}")
set(GTK4_VERSION "${GTK4_MAJOR_VERSION}.${GTK4_MINOR_VERSION}.${GTK4_MICRO_VERSION}")
unset(GTK4_MAJOR_VERSION)
unset(GTK4_MINOR_VERSION)
unset(GTK4_MICRO_VERSION)
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GTK4
REQUIRED_VARS GTK4_LIBRARY
VERSION_VAR GTK4_VERSION)

View File

@ -1,13 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(Gee
PKG_CONFIG_NAME gee-0.8
LIB_NAMES gee-0.8
INCLUDE_NAMES gee.h
INCLUDE_DIR_SUFFIXES gee-0.8 gee-0.8/include
DEPENDS GObject
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Gee
REQUIRED_VARS Gee_LIBRARY
VERSION_VAR Gee_VERSION)

View File

@ -1,20 +0,0 @@
find_program(XGETTEXT_EXECUTABLE xgettext)
find_program(MSGMERGE_EXECUTABLE msgmerge)
find_program(MSGFMT_EXECUTABLE msgfmt)
find_program(MSGCAT_EXECUTABLE msgcat)
mark_as_advanced(XGETTEXT_EXECUTABLE MSGMERGE_EXECUTABLE MSGFMT_EXECUTABLE MSGCAT_EXECUTABLE)
if(XGETTEXT_EXECUTABLE)
execute_process(COMMAND ${XGETTEXT_EXECUTABLE} "--version"
OUTPUT_VARIABLE Gettext_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE)
string(REGEX REPLACE "xgettext \\(GNU gettext-tools\\) ([0-9\\.]*).*" "\\1" Gettext_VERSION "${Gettext_VERSION}")
endif(XGETTEXT_EXECUTABLE)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Gettext
FOUND_VAR Gettext_FOUND
REQUIRED_VARS XGETTEXT_EXECUTABLE MSGMERGE_EXECUTABLE MSGFMT_EXECUTABLE MSGCAT_EXECUTABLE
VERSION_VAR Gettext_VERSION)
set(GETTEXT_USE_FILE "${CMAKE_CURRENT_LIST_DIR}/UseGettext.cmake")

View File

@ -1,13 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(GnuTLS
PKG_CONFIG_NAME gnutls
LIB_NAMES gnutls
INCLUDE_NAMES gnutls/gnutls.h
INCLUDE_DIR_SUFFIXES gnutls gnutls/include
DEPENDS GLib
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GnuTLS
REQUIRED_VARS GnuTLS_LIBRARY
VERSION_VAR GnuTLS_VERSION)

View File

@ -1,12 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(Gst
PKG_CONFIG_NAME gstreamer-1.0
LIB_NAMES gstreamer-1.0
INCLUDE_NAMES gst/gst.h
INCLUDE_DIR_SUFFIXES gstreamer-1.0 gstreamer-1.0/include
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Gst
REQUIRED_VARS Gst_LIBRARY
VERSION_VAR Gst_VERSION)

View File

@ -1,14 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(GstApp
PKG_CONFIG_NAME gstreamer-app-1.0
LIB_NAMES gstapp
LIB_DIR_HINTS gstreamer-1.0
INCLUDE_NAMES gst/app/app.h
INCLUDE_DIR_SUFFIXES gstreamer-1.0 gstreamer-1.0/include gstreamer-app-1.0 gstreamer-app-1.0/include
DEPENDS Gst
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GstApp
REQUIRED_VARS GstApp_LIBRARY
VERSION_VAR GstApp_VERSION)

View File

@ -1,14 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(GstAudio
PKG_CONFIG_NAME gstreamer-audio-1.0
LIB_NAMES gstaudio
LIB_DIR_HINTS gstreamer-1.0
INCLUDE_NAMES gst/audio/audio.h
INCLUDE_DIR_SUFFIXES gstreamer-1.0 gstreamer-1.0/include gstreamer-audio-1.0 gstreamer-audio-1.0/include
DEPENDS Gst
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GstAudio
REQUIRED_VARS GstAudio_LIBRARY
VERSION_VAR GstAudio_VERSION)

View File

@ -1,19 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(GstRtp
PKG_CONFIG_NAME gstreamer-rtp-1.0
LIB_NAMES gstrtp
LIB_DIR_HINTS gstreamer-1.0
INCLUDE_NAMES gst/rtp/rtp.h
INCLUDE_DIR_SUFFIXES gstreamer-1.0 gstreamer-1.0/include gstreamer-rtp-1.0 gstreamer-rtp-1.0/include
DEPENDS Gst
)
if(GstRtp_FOUND AND NOT GstRtp_VERSION)
find_package(Gst)
set(GstRtp_VERSION ${Gst_VERSION})
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GstRtp
REQUIRED_VARS GstRtp_LIBRARY
VERSION_VAR GstRtp_VERSION)

View File

@ -1,14 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(GstVideo
PKG_CONFIG_NAME gstreamer-video-1.0
LIB_NAMES gstvideo
LIB_DIR_HINTS gstreamer-1.0
INCLUDE_NAMES gst/video/video.h
INCLUDE_DIR_SUFFIXES gstreamer-1.0 gstreamer-1.0/include gstreamer-video-1.0 gstreamer-video-1.0/include
DEPENDS Gst
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GstVideo
REQUIRED_VARS GstVideo_LIBRARY
VERSION_VAR GstVideo_VERSION)

View File

@ -1,11 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(ICU
PKG_CONFIG_NAME icu-uc
LIB_NAMES icuuc icudata
INCLUDE_NAMES unicode/umachine.h
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ICU
REQUIRED_VARS ICU_LIBRARY
VERSION_VAR ICU_VERSION)

View File

@ -1,13 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(Nice
PKG_CONFIG_NAME nice
LIB_NAMES nice
INCLUDE_NAMES nice.h
INCLUDE_DIR_SUFFIXES nice nice/include
DEPENDS GIO
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Nice
REQUIRED_VARS Nice_LIBRARY
VERSION_VAR Nice_VERSION)

View File

@ -1,33 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(Pango
PKG_CONFIG_NAME pango
LIB_NAMES pango-1.0
INCLUDE_NAMES pango/pango.h
INCLUDE_DIR_SUFFIXES pango-1.0 pango-1.0/include
DEPENDS GObject
)
if(Pango_FOUND AND NOT Pango_VERSION)
find_file(Pango_FEATURES_HEADER "pango/pango-features.h" HINTS ${Pango_INCLUDE_DIRS})
mark_as_advanced(Pango_FEATURES_HEADER)
if(Pango_FEATURES_HEADER)
file(STRINGS "${Pango_FEATURES_HEADER}" Pango_MAJOR_VERSION REGEX "^#define PANGO_VERSION_MAJOR +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define PANGO_VERSION_MAJOR \\(?([0-9]+)\\)?$" "\\1" Pango_MAJOR_VERSION "${Pango_MAJOR_VERSION}")
file(STRINGS "${Pango_FEATURES_HEADER}" Pango_MINOR_VERSION REGEX "^#define PANGO_VERSION_MINOR +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define PANGO_VERSION_MINOR \\(?([0-9]+)\\)?$" "\\1" Pango_MINOR_VERSION "${Pango_MINOR_VERSION}")
file(STRINGS "${Pango_FEATURES_HEADER}" Pango_MICRO_VERSION REGEX "^#define PANGO_VERSION_MICRO +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define PANGO_VERSION_MICRO \\(?([0-9]+)\\)?$" "\\1" Pango_MICRO_VERSION "${Pango_MICRO_VERSION}")
set(Pango_VERSION "${Pango_MAJOR_VERSION}.${Pango_MINOR_VERSION}.${Pango_MICRO_VERSION}")
unset(Pango_MAJOR_VERSION)
unset(Pango_MINOR_VERSION)
unset(Pango_MICRO_VERSION)
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Pango
FOUND_VAR Pango_FOUND
REQUIRED_VARS Pango_LIBRARY
VERSION_VAR Pango_VERSION
)

View File

@ -1,11 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(Qrencode
PKG_CONFIG_NAME libqrencode
LIB_NAMES qrencode
INCLUDE_NAMES qrencode.h
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Qrencode
REQUIRED_VARS Qrencode_LIBRARY
VERSION_VAR Qrencode_VERSION)

View File

@ -1,21 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(SQLite3
PKG_CONFIG_NAME sqlite3
LIB_NAMES sqlite3
INCLUDE_NAMES sqlite3.h
)
if(SQLite3_FOUND AND NOT SQLite3_VERSION)
find_file(SQLite3_HEADER "sqlite3.h" HINTS ${SQLite3_INCLUDE_DIRS})
mark_as_advanced(SQLite3_HEADER)
if(SQLite3_HEADER)
file(STRINGS "${SQLite3_HEADER}" SQLite3_VERSION REGEX "^#define SQLITE_VERSION +\\\"[^\\\"]+\\\"")
string(REGEX REPLACE "^#define SQLITE_VERSION +\\\"([0-9]+)\\.([0-9]+)\\.([0-9]+)\\\"$" "\\1.\\2.\\3" SQLite3_VERSION "${SQLite3_VERSION}")
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SQLite3
REQUIRED_VARS SQLite3_LIBRARY
VERSION_VAR SQLite3_VERSION)

View File

@ -1,11 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(SignalProtocol
PKG_CONFIG_NAME libsignal-protocol-c
LIB_NAMES signal-protocol-c
INCLUDE_NAMES signal/signal_protocol.h
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SignalProtocol
REQUIRED_VARS SignalProtocol_LIBRARY
VERSION_VAR SignalProtocol_VERSION)

View File

@ -1,31 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(Soup2
PKG_CONFIG_NAME libsoup-2.4
LIB_NAMES soup-2.4
INCLUDE_NAMES libsoup/soup.h
INCLUDE_DIR_SUFFIXES libsoup-2.4 libsoup-2.4/include libsoup libsoup/include
DEPENDS GIO
)
if(Soup2_FOUND AND NOT Soup2_VERSION)
find_file(Soup2_VERSION_HEADER "libsoup/soup-version.h" HINTS ${Soup_INCLUDE_DIRS})
mark_as_advanced(Soup2_VERSION_HEADER)
if(Soup_VERSION_HEADER)
file(STRINGS "${Soup2_VERSION_HEADER}" Soup2_MAJOR_VERSION REGEX "^#define SOUP_MAJOR_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define SOUP_MAJOR_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup_MAJOR_VERSION "${Soup2_MAJOR_VERSION}")
file(STRINGS "${Soup2_VERSION_HEADER}" Soup2_MINOR_VERSION REGEX "^#define SOUP_MINOR_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define SOUP_MINOR_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup_MINOR_VERSION "${Soup2_MINOR_VERSION}")
file(STRINGS "${Soup2_VERSION_HEADER}" Soup2_MICRO_VERSION REGEX "^#define SOUP_MICRO_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define SOUP_MICRO_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup_MICRO_VERSION "${Soup2_MICRO_VERSION}")
set(Soup_VERSION "${Soup2_MAJOR_VERSION}.${Soup2_MINOR_VERSION}.${Soup2_MICRO_VERSION}")
unset(Soup2_MAJOR_VERSION)
unset(Soup2_MINOR_VERSION)
unset(Soup2_MICRO_VERSION)
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Soup2
REQUIRED_VARS Soup2_LIBRARY
VERSION_VAR Soup2_VERSION)

View File

@ -1,31 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(Soup3
PKG_CONFIG_NAME libsoup-3.0
LIB_NAMES soup-3.0
INCLUDE_NAMES libsoup/soup.h
INCLUDE_DIR_SUFFIXES libsoup-2.4 libsoup-2.4/include libsoup libsoup/include
DEPENDS GIO
)
if(Soup3_FOUND AND NOT Soup3_VERSION)
find_file(Soup3_VERSION_HEADER "libsoup/soup-version.h" HINTS ${Soup3_INCLUDE_DIRS})
mark_as_advanced(Soup3_VERSION_HEADER)
if(Soup3_VERSION_HEADER)
file(STRINGS "${Soup3_VERSION_HEADER}" Soup3_MAJOR_VERSION REGEX "^#define SOUP_MAJOR_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define SOUP_MAJOR_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup3_MAJOR_VERSION "${Soup3_MAJOR_VERSION}")
file(STRINGS "${Soup3_VERSION_HEADER}" Soup3_MINOR_VERSION REGEX "^#define SOUP_MINOR_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define SOUP_MINOR_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup3_MINOR_VERSION "${Soup3_MINOR_VERSION}")
file(STRINGS "${Soup3_VERSION_HEADER}" Soup3_MICRO_VERSION REGEX "^#define SOUP_MICRO_VERSION +\\(?([0-9]+)\\)?$")
string(REGEX REPLACE "^#define SOUP_MICRO_VERSION \\(?([0-9]+)\\)?$" "\\1" Soup3_MICRO_VERSION "${Soup3_MICRO_VERSION}")
set(Soup3_VERSION "${Soup3_MAJOR_VERSION}.${Soup3_MINOR_VERSION}.${Soup3_MICRO_VERSION}")
unset(Soup3_MAJOR_VERSION)
unset(Soup3_MINOR_VERSION)
unset(Soup3_MICRO_VERSION)
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Soup3
REQUIRED_VARS Soup3_LIBRARY
VERSION_VAR Soup3_VERSION)

View File

@ -1,12 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(Srtp2
PKG_CONFIG_NAME libsrtp2
LIB_NAMES srtp2
INCLUDE_NAMES srtp2/srtp.h
INCLUDE_DIR_SUFFIXES srtp2 srtp2/include
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Srtp2
REQUIRED_VARS Srtp2_LIBRARY
VERSION_VAR Srtp2_VERSION)

View File

@ -1,73 +0,0 @@
##
# Find module for the Vala compiler (valac)
#
# This module determines wheter a Vala compiler is installed on the current
# system and where its executable is.
#
# Call the module using "find_package(Vala) from within your CMakeLists.txt.
#
# The following variables will be set after an invocation:
#
# VALA_FOUND Whether the vala compiler has been found or not
# VALA_EXECUTABLE Full path to the valac executable if it has been found
# VALA_VERSION Version number of the available valac
# VALA_USE_FILE Include this file to define the vala_precompile function
##
##
# Copyright 2009-2010 Jakob Westhoff. All rights reserved.
# Copyright 2010-2011 Daniel Pfeifer
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY JAKOB WESTHOFF ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
# EVENT SHALL JAKOB WESTHOFF OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are those
# of the authors and should not be interpreted as representing official policies,
# either expressed or implied, of Jakob Westhoff
##
# Search for the valac executable in the usual system paths
# Some distributions rename the valac to contain the major.minor in the binary name
find_package(GObject REQUIRED)
find_program(VALA_EXECUTABLE NAMES valac valac-0.38 valac-0.36 valac-0.34 valac-0.32)
mark_as_advanced(VALA_EXECUTABLE)
# Determine the valac version
if(VALA_EXECUTABLE)
file(TO_NATIVE_PATH "${VALA_EXECUTABLE}" VALA_EXECUTABLE)
execute_process(COMMAND ${VALA_EXECUTABLE} "--version"
OUTPUT_VARIABLE VALA_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE)
string(REPLACE "Vala " "" VALA_VERSION "${VALA_VERSION}")
endif(VALA_EXECUTABLE)
# Handle the QUIETLY and REQUIRED arguments, which may be given to the find call.
# Furthermore set VALA_FOUND to TRUE if Vala has been found (aka.
# VALA_EXECUTABLE is set)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Vala
FOUND_VAR VALA_FOUND
REQUIRED_VARS VALA_EXECUTABLE
VERSION_VAR VALA_VERSION)
set(VALA_USE_FILE "${CMAKE_CURRENT_LIST_DIR}/UseVala.cmake")

View File

@ -1,12 +0,0 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(WebRTCAudioProcessing
PKG_CONFIG_NAME webrtc-audio-processing
LIB_NAMES webrtc_audio_processing
INCLUDE_NAMES webrtc/modules/audio_processing/include/audio_processing.h
INCLUDE_DIR_SUFFIXES webrtc-audio-processing webrtc_audio_processing
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(WebRTCAudioProcessing
REQUIRED_VARS WebRTCAudioProcessing_LIBRARY
VERSION_VAR WebRTCAudioProcessing_VERSION)

View File

@ -1,124 +0,0 @@
include(CMakeParseArguments)
# Generates the resource XML controlling file from resource list (and saves it
# to xml_path). It's not recommended to use this function directly, since it
# doesn't handle invalid arguments. It is used by the function
# COMPILE_GRESOURCES() to create a custom command, so that this function is
# invoked at build-time in script mode from CMake.
function(GENERATE_GXML xml_path)
# Available options:
# COMPRESS_ALL, NO_COMPRESS_ALL Overrides the COMPRESS flag in all
# registered resources.
# STRIPBLANKS_ALL, NO_STRIPBLANKS_ALL Overrides the STRIPBLANKS flag in all
# registered resources.
# TOPIXDATA_ALL, NO_TOPIXDATA_ALL Overrides the TOPIXDATA flag in all
# registered resources.
set(GXML_OPTIONS COMPRESS_ALL NO_COMPRESS_ALL
STRIPBLANKS_ALL NO_STRIPBLANKS_ALL
TOPIXDATA_ALL NO_TOPIXDATA_ALL)
# Available one value options:
# PREFIX Overrides the resource prefix that is prepended to each
# relative file name in registered resources.
set(GXML_ONEVALUEARGS PREFIX)
# Available multi-value options:
# RESOURCES The list of resource files. Whether absolute or relative path is
# equal, absolute paths are stripped down to relative ones. If the
# absolute path is not inside the given base directory SOURCE_DIR
# or CMAKE_SOURCE_DIR (if SOURCE_DIR is not overriden), this
# function aborts.
set(GXML_MULTIVALUEARGS RESOURCES)
# Parse the arguments.
cmake_parse_arguments(GXML_ARG
"${GXML_OPTIONS}"
"${GXML_ONEVALUEARGS}"
"${GXML_MULTIVALUEARGS}"
"${ARGN}")
# Variable to store the double-quote (") string. Since escaping
# double-quotes in strings is not possible we need a helper variable that
# does this job for us.
set(Q \")
# Process resources and generate XML file.
# Begin with the XML header and header nodes.
set(GXML_XML_FILE "<?xml version=${Q}1.0${Q} encoding=${Q}UTF-8${Q}?>")
set(GXML_XML_FILE "${GXML_XML_FILE}<gresources><gresource prefix=${Q}")
# Set the prefix for the resources. Depending on the user-override we choose
# the standard prefix "/" or the override.
if (GXML_ARG_PREFIX)
set(GXML_XML_FILE "${GXML_XML_FILE}${GXML_ARG_PREFIX}")
else()
set(GXML_XML_FILE "${GXML_XML_FILE}/")
endif()
set(GXML_XML_FILE "${GXML_XML_FILE}${Q}>")
# Process each resource.
foreach(res ${GXML_ARG_RESOURCES})
if ("${res}" STREQUAL "COMPRESS")
set(GXML_COMPRESSION_FLAG ON)
elseif ("${res}" STREQUAL "STRIPBLANKS")
set(GXML_STRIPBLANKS_FLAG ON)
elseif ("${res}" STREQUAL "TOPIXDATA")
set(GXML_TOPIXDATA_FLAG ON)
else()
# The file name.
set(GXML_RESOURCE_PATH "${res}")
# Append to real resource file dependency list.
list(APPEND GXML_RESOURCES_DEPENDENCIES ${GXML_RESOURCE_PATH})
# Assemble <file> node.
set(GXML_RES_LINE "<file")
if ((GXML_ARG_COMPRESS_ALL OR GXML_COMPRESSION_FLAG) AND NOT
GXML_ARG_NO_COMPRESS_ALL)
set(GXML_RES_LINE "${GXML_RES_LINE} compressed=${Q}true${Q}")
endif()
# Check preprocess flag validity.
if ((GXML_ARG_STRIPBLANKS_ALL OR GXML_STRIPBLANKS_FLAG) AND
(GXML_ARG_TOPIXDATA_ALL OR GXML_TOPIXDATA_FLAG))
set(GXML_ERRMSG "Resource preprocessing option conflict. Tried")
set(GXML_ERRMSG "${GXML_ERRMSG} to specify both, STRIPBLANKS")
set(GXML_ERRMSG "${GXML_ERRMSG} and TOPIXDATA. In resource")
set(GXML_ERRMSG "${GXML_ERRMSG} ${GXML_RESOURCE_PATH} in")
set(GXML_ERRMSG "${GXML_ERRMSG} function COMPILE_GRESOURCES.")
message(FATAL_ERROR ${GXML_ERRMSG})
endif()
if ((GXML_ARG_STRIPBLANKS_ALL OR GXML_STRIPBLANKS_FLAG) AND NOT
GXML_ARG_NO_STRIPBLANKS_ALL)
set(GXML_RES_LINE "${GXML_RES_LINE} preprocess=")
set(GXML_RES_LINE "${GXML_RES_LINE}${Q}xml-stripblanks${Q}")
elseif((GXML_ARG_TOPIXDATA_ALL OR GXML_TOPIXDATA_FLAG) AND NOT
GXML_ARG_NO_TOPIXDATA_ALL)
set(GXML_RES_LINE "${GXML_RES_LINE} preprocess=")
set(GXML_RES_LINE "${GXML_RES_LINE}${Q}to-pixdata${Q}")
endif()
set(GXML_RES_LINE "${GXML_RES_LINE}>${GXML_RESOURCE_PATH}</file>")
# Append to file string.
set(GXML_XML_FILE "${GXML_XML_FILE}${GXML_RES_LINE}")
# Unset variables.
unset(GXML_COMPRESSION_FLAG)
unset(GXML_STRIPBLANKS_FLAG)
unset(GXML_TOPIXDATA_FLAG)
endif()
endforeach()
# Append closing nodes.
set(GXML_XML_FILE "${GXML_XML_FILE}</gresource></gresources>")
# Use "file" function to generate XML controlling file.
get_filename_component(xml_path_only_name "${xml_path}" NAME)
file(WRITE ${xml_path} ${GXML_XML_FILE})
endfunction()

View File

@ -1,11 +0,0 @@
# Path to this file.
set(GCR_CMAKE_MACRO_DIR ${CMAKE_CURRENT_LIST_DIR})
# Finds the glib-compile-resources executable.
find_program(GLIB_COMPILE_RESOURCES_EXECUTABLE glib-compile-resources)
mark_as_advanced(GLIB_COMPILE_RESOURCES_EXECUTABLE)
# Include the cmake files containing the functions.
include(${GCR_CMAKE_MACRO_DIR}/CompileGResources.cmake)
include(${GCR_CMAKE_MACRO_DIR}/GenerateGXML.cmake)

View File

@ -1,11 +0,0 @@
#include <sys/types.h>
#define _K ((off_t)1024)
#define _M ((off_t)1024 * _K)
#define _G ((off_t)1024 * _M)
#define _T ((off_t)1024 * _G)
int test[(((64 * _G -1) % 671088649) == 268434537) && (((_T - (64 * _G -1) + 255) % 1792151290) == 305159546)? 1: -1];
int main() {
return 0;
}

View File

@ -1,47 +0,0 @@
include(CMakeParseArguments)
function(find_packages result)
cmake_parse_arguments(ARGS "" "" "REQUIRED;OPTIONAL" ${ARGN})
set(_res "")
set(_res_libs "")
foreach(pkg ${ARGS_REQUIRED})
string(REPLACE ">=" ";" pkg_ ${pkg})
list(GET pkg_ "0" pkg)
list(LENGTH pkg_ pkg_has_version)
if(pkg_has_version GREATER 1)
list(GET pkg_ "1" pkg_version)
else()
if(${pkg}_GLOBAL_VERSION)
set(pkg_version ${${pkg}_GLOBAL_VERSION})
else()
unset(pkg_version)
endif()
endif()
find_package(${pkg} ${pkg_version} REQUIRED)
list(APPEND _res ${${pkg}_PKG_CONFIG_NAME})
list(APPEND _res_libs ${${pkg}_LIBRARIES})
set(${pkg}_VERSION "${${pkg}_VERSION}" PARENT_SCOPE)
endforeach(pkg)
foreach(pkg ${ARGS_OPTIONAL})
string(REPLACE ">=" ";" pkg_ ${pkg})
list(GET pkg_ "0" pkg)
list(LENGTH pkg_ pkg_has_version)
if(pkg_has_version GREATER 1)
list(GET pkg_ "1" pkg_version)
else()
if(${pkg}_GLOBAL_VERSION)
set(pkg_version ${${pkg}_GLOBAL_VERSION})
else()
unset(pkg_version)
endif()
endif()
find_package(${pkg} ${pkg_version})
if(${pkg}_FOUND)
list(APPEND _res ${${pkg}_PKG_CONFIG_NAME})
list(APPEND _res_libs ${${pkg}_LIBRARIES})
set(${pkg}_VERSION "${${pkg}_VERSION}" PARENT_SCOPE)
endif()
endforeach(pkg)
set(${result} "${_res}" PARENT_SCOPE)
set(${result}_LIBS "${_res_libs}" PARENT_SCOPE)
endfunction()

View File

@ -1,102 +0,0 @@
include(CMakeParseArguments)
function(find_pkg_config_with_fallback name)
cmake_parse_arguments(ARGS "" "PKG_CONFIG_NAME" "LIB_NAMES;LIB_DIR_HINTS;INCLUDE_NAMES;INCLUDE_DIR_PATHS;INCLUDE_DIR_HINTS;INCLUDE_DIR_SUFFIXES;DEPENDS" ${ARGN})
set(${name}_PKG_CONFIG_NAME ${ARGS_PKG_CONFIG_NAME} PARENT_SCOPE)
find_package(PkgConfig)
if(PKG_CONFIG_FOUND)
pkg_search_module(${name}_PKG_CONFIG QUIET ${ARGS_PKG_CONFIG_NAME})
endif(PKG_CONFIG_FOUND)
if (${name}_PKG_CONFIG_FOUND)
# Found via pkg-config, using its result values
set(${name}_FOUND ${${name}_PKG_CONFIG_FOUND})
# Try to find real file name of libraries
foreach(lib ${${name}_PKG_CONFIG_LIBRARIES})
find_library(${name}_${lib}_LIBRARY ${lib} HINTS ${${name}_PKG_CONFIG_LIBRARY_DIRS})
mark_as_advanced(${name}_${lib}_LIBRARY)
if(NOT ${name}_${lib}_LIBRARY)
unset(${name}_FOUND)
endif(NOT ${name}_${lib}_LIBRARY)
endforeach(lib)
if(${name}_FOUND)
set(${name}_LIBRARIES "")
foreach(lib ${${name}_PKG_CONFIG_LIBRARIES})
list(APPEND ${name}_LIBRARIES ${${name}_${lib}_LIBRARY})
endforeach(lib)
list(REMOVE_DUPLICATES ${name}_LIBRARIES)
set(${name}_LIBRARIES ${${name}_LIBRARIES} PARENT_SCOPE)
list(GET ${name}_LIBRARIES "0" ${name}_LIBRARY)
set(${name}_FOUND ${${name}_FOUND} PARENT_SCOPE)
set(${name}_INCLUDE_DIRS ${${name}_PKG_CONFIG_INCLUDE_DIRS} PARENT_SCOPE)
set(${name}_LIBRARIES ${${name}_PKG_CONFIG_LIBRARIES} PARENT_SCOPE)
set(${name}_LIBRARY ${${name}_LIBRARY} PARENT_SCOPE)
set(${name}_VERSION ${${name}_PKG_CONFIG_VERSION} PARENT_SCOPE)
if(NOT TARGET ${ARGS_PKG_CONFIG_NAME})
add_library(${ARGS_PKG_CONFIG_NAME} INTERFACE IMPORTED)
set_property(TARGET ${ARGS_PKG_CONFIG_NAME} PROPERTY INTERFACE_COMPILE_OPTIONS "${${name}_PKG_CONFIG_CFLAGS_OTHER}")
set_property(TARGET ${ARGS_PKG_CONFIG_NAME} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${name}_PKG_CONFIG_INCLUDE_DIRS}")
set_property(TARGET ${ARGS_PKG_CONFIG_NAME} PROPERTY INTERFACE_LINK_LIBRARIES "${${name}_LIBRARIES}")
endif(NOT TARGET ${ARGS_PKG_CONFIG_NAME})
endif(${name}_FOUND)
else(${name}_PKG_CONFIG_FOUND)
# No success with pkg-config, try via find_library on all lib_names
set(${name}_FOUND "1")
foreach(lib ${ARGS_LIB_NAMES})
find_library(${name}_${lib}_LIBRARY ${ARGS_LIB_NAMES} HINTS ${ARGS_LIB_DIR_HINTS})
mark_as_advanced(${name}_${lib}_LIBRARY)
if(NOT ${name}_${lib}_LIBRARY)
unset(${name}_FOUND)
endif(NOT ${name}_${lib}_LIBRARY)
endforeach(lib)
foreach(inc ${ARGS_INCLUDE_NAMES})
find_path(${name}_${inc}_INCLUDE_PATH ${inc} HINTS ${ARGS_INCLUDE_DIR_HINTS} PATHS ${ARGS_INCLUDE_DIR_PATHS} PATH_SUFFIXES ${ARGS_INCLUDE_DIR_SUFFIXES})
mark_as_advanced(${name}_${inc}_INCLUDE_PATH)
if(NOT ${name}_${inc}_INCLUDE_PATH)
unset(${name}_FOUND)
endif(NOT ${name}_${inc}_INCLUDE_PATH)
endforeach(inc)
if(${name}_FOUND)
set(${name}_LIBRARIES "")
set(${name}_INCLUDE_DIRS "")
foreach(lib ${ARGS_LIB_NAMES})
list(APPEND ${name}_LIBRARIES ${${name}_${lib}_LIBRARY})
endforeach(lib)
foreach(inc ${ARGS_INCLUDE_NAMES})
list(APPEND ${name}_INCLUDE_DIRS ${${name}_${inc}_INCLUDE_PATH})
endforeach(inc)
list(GET ${name}_LIBRARIES "0" ${name}_LIBRARY)
foreach(dep ${ARGS_DEPENDS})
find_package(${dep} ${${dep}_GLOBAL_VERSION} QUIET)
if(${dep}_FOUND)
list(APPEND ${name}_INCLUDE_DIRS ${${dep}_INCLUDE_DIRS})
list(APPEND ${name}_LIBRARIES ${${dep}_LIBRARIES})
else(${dep}_FOUND)
unset(${name}_FOUND)
endif(${dep}_FOUND)
endforeach(dep)
set(${name}_FOUND ${${name}_FOUND} PARENT_SCOPE)
set(${name}_INCLUDE_DIRS ${${name}_INCLUDE_DIRS} PARENT_SCOPE)
set(${name}_LIBRARIES ${${name}_LIBRARIES} PARENT_SCOPE)
set(${name}_LIBRARY ${${name}_LIBRARY} PARENT_SCOPE)
unset(${name}_VERSION PARENT_SCOPE)
if(NOT TARGET ${ARGS_PKG_CONFIG_NAME})
add_library(${ARGS_PKG_CONFIG_NAME} INTERFACE IMPORTED)
set_property(TARGET ${ARGS_PKG_CONFIG_NAME} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${name}_INCLUDE_DIRS}")
set_property(TARGET ${ARGS_PKG_CONFIG_NAME} PROPERTY INTERFACE_LINK_LIBRARIES "${${name}_LIBRARIES}")
endif(NOT TARGET ${ARGS_PKG_CONFIG_NAME})
endif(${name}_FOUND)
endif(${name}_PKG_CONFIG_FOUND)
endfunction()

View File

@ -1,103 +0,0 @@
include(CMakeParseArguments)
function(find_pkg_config_with_fallback_on_config_script name)
cmake_parse_arguments(ARGS "" "PKG_CONFIG_NAME" "CONFIG_SCRIPT_NAME" ${ARGN})
set(${name}_PKG_CONFIG_NAME ${ARGS_PKG_CONFIG_NAME} PARENT_SCOPE)
find_package(PkgConfig)
if(PKG_CONFIG_FOUND)
pkg_search_module(${name}_PKG_CONFIG QUIET ${ARGS_PKG_CONFIG_NAME})
endif(PKG_CONFIG_FOUND)
if (${name}_PKG_CONFIG_FOUND)
# Found via pkg-config, using it's result values
set(${name}_FOUND ${${name}_PKG_CONFIG_FOUND})
# Try to find real file name of libraries
foreach(lib ${${name}_PKG_CONFIG_LIBRARIES})
find_library(${name}_${lib}_LIBRARY ${lib} HINTS ${${name}_PKG_CONFIG_LIBRARY_DIRS})
mark_as_advanced(${name}_${lib}_LIBRARY)
if(NOT ${name}_${lib}_LIBRARY)
unset(${name}_FOUND)
endif(NOT ${name}_${lib}_LIBRARY)
endforeach(lib)
if(${name}_FOUND)
set(${name}_LIBRARIES "")
foreach(lib ${${name}_PKG_CONFIG_LIBRARIES})
list(APPEND ${name}_LIBRARIES ${${name}_${lib}_LIBRARY})
endforeach(lib)
list(REMOVE_DUPLICATES ${name}_LIBRARIES)
set(${name}_LIBRARIES ${${name}_LIBRARIES} PARENT_SCOPE)
list(GET ${name}_LIBRARIES "0" ${name}_LIBRARY)
set(${name}_FOUND ${${name}_FOUND} PARENT_SCOPE)
set(${name}_INCLUDE_DIRS ${${name}_PKG_CONFIG_INCLUDE_DIRS} PARENT_SCOPE)
set(${name}_LIBRARIES ${${name}_PKG_CONFIG_LIBRARIES} PARENT_SCOPE)
set(${name}_LIBRARY ${${name}_LIBRARY} PARENT_SCOPE)
set(${name}_VERSION ${${name}_PKG_CONFIG_VERSION} PARENT_SCOPE)
if(NOT TARGET ${ARGS_PKG_CONFIG_NAME})
add_library(${ARGS_PKG_CONFIG_NAME} INTERFACE IMPORTED)
set_property(TARGET ${ARGS_PKG_CONFIG_NAME} PROPERTY INTERFACE_COMPILE_OPTIONS "${${name}_PKG_CONFIG_CFLAGS_OTHER}")
set_property(TARGET ${ARGS_PKG_CONFIG_NAME} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${name}_PKG_CONFIG_INCLUDE_DIRS}")
set_property(TARGET ${ARGS_PKG_CONFIG_NAME} PROPERTY INTERFACE_LINK_LIBRARIES "${${name}_LIBRARIES}")
endif(NOT TARGET ${ARGS_PKG_CONFIG_NAME})
endif(${name}_FOUND)
else(${name}_PKG_CONFIG_FOUND)
# No success with pkg-config, try via custom a *-config script
find_program(${name}_CONFIG_EXECUTABLE NAMES ${ARGS_CONFIG_SCRIPT_NAME}-config)
mark_as_advanced(${name}_CONFIG_EXECUTABLE)
find_program(${name}_SH_EXECUTABLE NAMES sh)
mark_as_advanced(${name}_SH_EXECUTABLE)
if(${name}_CONFIG_EXECUTABLE)
macro(config_script_fail errcode)
if(${errcode})
message(FATAL_ERROR "Error invoking ${ARGS_CONFIG_SCRIPT_NAME}-config: ${errcode}")
endif(${errcode})
endmacro(config_script_fail)
file(TO_NATIVE_PATH "${${name}_CONFIG_EXECUTABLE}" ${name}_CONFIG_EXECUTABLE)
file(TO_NATIVE_PATH "${${name}_SH_EXECUTABLE}" ${name}_SH_EXECUTABLE)
execute_process(COMMAND "${${name}_SH_EXECUTABLE}" "${${name}_CONFIG_EXECUTABLE}" --version
OUTPUT_VARIABLE ${name}_VERSION
RESULT_VARIABLE ERRCODE
OUTPUT_STRIP_TRAILING_WHITESPACE)
config_script_fail(${ERRCODE})
execute_process(COMMAND "${${name}_SH_EXECUTABLE}" "${${name}_CONFIG_EXECUTABLE}" --api-version
OUTPUT_VARIABLE ${name}_API_VERSION
RESULT_VARIABLE ERRCODE
OUTPUT_STRIP_TRAILING_WHITESPACE)
config_script_fail(${ERRCODE})
execute_process(COMMAND "${${name}_SH_EXECUTABLE}" "${${name}_CONFIG_EXECUTABLE}" --cflags
OUTPUT_VARIABLE ${name}_CFLAGS
RESULT_VARIABLE ERRCODE
OUTPUT_STRIP_TRAILING_WHITESPACE)
config_script_fail(${ERRCODE})
execute_process(COMMAND "${${name}_SH_EXECUTABLE}" "${${name}_CONFIG_EXECUTABLE}" --libs
OUTPUT_VARIABLE ${name}_LDFLAGS
RESULT_VARIABLE ERRCODE
OUTPUT_STRIP_TRAILING_WHITESPACE)
config_script_fail(${ERRCODE})
string(TOLOWER ${name} "${name}_LOWER")
string(REGEX REPLACE "^(.* |)-l([^ ]*${${name}_LOWER}[^ ]*)( .*|)$" "\\2" ${name}_LIBRARY_NAME "${${name}_LDFLAGS}")
string(REGEX REPLACE "^(.* |)-L([^ ]*)( .*|)$" "\\2" ${name}_LIBRARY_DIRS "${${name}_LDFLAGS}")
find_library(${name}_LIBRARY ${${name}_LIBRARY_NAME} HINTS ${${name}_LIBRARY_DIRS})
mark_as_advanced(${name}_LIBRARY)
set(${name}_LIBRARY ${${name}_LIBRARY} PARENT_SCOPE)
set(${name}_VERSION ${${name}_VERSION} PARENT_SCOPE)
unset(${name}_LIBRARY_NAME)
unset(${name}_LIBRARY_DIRS)
if(NOT TARGET ${name}_LOWER)
add_library(${name}_LOWER INTERFACE IMPORTED)
set_property(TARGET ${name}_LOWER PROPERTY INTERFACE_LINK_LIBRARIES "${${name}_LDFLAGS}")
set_property(TARGET ${name}_LOWER PROPERTY INTERFACE_COMPILE_OPTIONS "${${name}_CFLAGS}")
endif(NOT TARGET ${name}_LOWER)
endif(${name}_CONFIG_EXECUTABLE)
endif(${name}_PKG_CONFIG_FOUND)
endfunction()

View File

@ -1,33 +0,0 @@
find_package(Nice QUIET)
if (Nice_FOUND AND NOT SOUP_VERSION AND NOT USE_SOUP3)
file(GET_RUNTIME_DEPENDENCIES
RESOLVED_DEPENDENCIES_VAR Nice_DEPENDENCIES
UNRESOLVED_DEPENDENCIES_VAR Nice_UNRESOLVED_DEPENDENCIES
LIBRARIES ${Nice_LIBRARY}
PRE_INCLUDE_REGEXES "soup|gupnp"
PRE_EXCLUDE_REGEXES "."
)
foreach (lib ${Nice_DEPENDENCIES})
if (lib MATCHES ".*/libsoup-3.*")
set(SOUP_VERSION 3)
endif ()
endforeach ()
foreach (lib ${Nice_DEPENDENCIES})
if (lib MATCHES ".*/libsoup-2.*")
set(SOUP_VERSION 2)
endif ()
endforeach ()
set(SOUP_VERSION ${SOUP_VERSION} CACHE STRING "Version of libsoup to use")
set_property(CACHE SOUP_VERSION PROPERTY STRINGS "2" "3")
message(STATUS "Using Soup${SOUP_VERSION} to provide Soup")
elseif (NOT SOUP_VERSION)
find_package(Soup2 QUIET)
find_package(Soup3 QUIET)
# Only use libsoup 3 if specifically requested or when libsoup 2 is not available
if (Soup3_FOUND AND NOT Soup2_FOUND OR USE_SOUP3)
set(SOUP_VERSION 3)
else ()
set(SOUP_VERSION 2)
endif ()
endif ()
set(Soup "Soup${SOUP_VERSION}")

View File

@ -1,28 +0,0 @@
function(_gettext_mkdir_for_file file)
get_filename_component(dir "${file}" DIRECTORY)
file(MAKE_DIRECTORY "${dir}")
endfunction()
function(gettext_compile project_name)
cmake_parse_arguments(ARGS "" "MO_FILES_NAME;TARGET_NAME;SOURCE_DIR;PROJECT_NAME" "" ${ARGN})
if(NOT ARGS_SOURCE_DIR)
set(ARGS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
endif(NOT ARGS_SOURCE_DIR)
file(STRINGS "${ARGS_SOURCE_DIR}/LINGUAS" LINGUAS)
set(target_files)
foreach(lang ${LINGUAS})
set(source_file ${ARGS_SOURCE_DIR}/${lang}.po)
set(target_file ${CMAKE_BINARY_DIR}/locale/${lang}/LC_MESSAGES/${project_name}.mo)
_gettext_mkdir_for_file(${target_file})
list(APPEND target_files ${target_file})
add_custom_command(OUTPUT ${target_file} COMMAND ${MSGFMT_EXECUTABLE} --check-format -o ${target_file} ${source_file} DEPENDS ${source_file})
install(FILES ${target_file} DESTINATION ${LOCALE_INSTALL_DIR}/${lang}/LC_MESSAGES)
endforeach(lang)
if(ARGS_MO_FILES_NAME)
set(${ARGS_MO_FILES_NAME} ${target_files} PARENT_SCOPE)
endif(ARGS_MO_FILES_NAME)
if(ARGS_TARGET_NAME)
add_custom_target(${ARGS_TARGET_NAME} DEPENDS ${target_files})
endif(ARGS_TARGET_NAME)
endfunction(gettext_compile)

View File

@ -1,337 +0,0 @@
##
# Compile vala files to their c equivalents for further processing.
#
# The "vala_precompile" function takes care of calling the valac executable on
# the given source to produce c files which can then be processed further using
# default cmake functions.
#
# The first parameter provided is a variable, which will be filled with a list
# of c files outputted by the vala compiler. This list can than be used in
# conjuction with functions like "add_executable" or others to create the
# neccessary compile rules with CMake.
#
# The following sections may be specified afterwards to provide certain options
# to the vala compiler:
#
# SOURCES
# A list of .vala files to be compiled. Please take care to add every vala
# file belonging to the currently compiled project or library as Vala will
# otherwise not be able to resolve all dependencies.
#
# PACKAGES
# A list of vala packages/libraries to be used during the compile cycle. The
# package names are exactly the same, as they would be passed to the valac
# "--pkg=" option.
#
# OPTIONS
# A list of optional options to be passed to the valac executable. This can be
# used to pass "--thread" for example to enable multi-threading support.
#
# DEFINITIONS
# A list of symbols to be used for conditional compilation. They are the same
# as they would be passed using the valac "--define=" option.
#
# CUSTOM_VAPIS
# A list of custom vapi files to be included for compilation. This can be
# useful to include freshly created vala libraries without having to install
# them in the system.
#
# GENERATE_VAPI
# Pass all the needed flags to the compiler to create a vapi for
# the compiled library. The provided name will be used for this and a
# <provided_name>.vapi file will be created.
#
# GENERATE_HEADER
# Let the compiler generate a header file for the compiled code. There will
# be a header file as well as an internal header file being generated called
# <provided_name>.h and <provided_name>_internal.h
#
# The following call is a simple example to the vala_precompile macro showing
# an example to every of the optional sections:
#
# find_package(Vala "0.12" REQUIRED)
# include(${VALA_USE_FILE})
#
# vala_precompile(VALA_C
# SOURCES
# source1.vala
# source2.vala
# source3.vala
# PACKAGES
# gtk+-2.0
# gio-1.0
# posix
# DIRECTORY
# gen
# OPTIONS
# --thread
# CUSTOM_VAPIS
# some_vapi.vapi
# GENERATE_VAPI
# myvapi
# GENERATE_HEADER
# myheader
# )
#
# Most important is the variable VALA_C which will contain all the generated c
# file names after the call.
##
##
# Copyright 2009-2010 Jakob Westhoff. All rights reserved.
# Copyright 2010-2011 Daniel Pfeifer
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY JAKOB WESTHOFF ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
# EVENT SHALL JAKOB WESTHOFF OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are those
# of the authors and should not be interpreted as representing official policies,
# either expressed or implied, of Jakob Westhoff
##
include(CMakeParseArguments)
function(_vala_mkdir_for_file file)
get_filename_component(dir "${file}" DIRECTORY)
file(MAKE_DIRECTORY "${dir}")
endfunction()
function(vala_precompile output)
cmake_parse_arguments(ARGS "FAST_VAPI" "DIRECTORY;GENERATE_HEADER;GENERATE_VAPI;EXPORTS_DIR"
"SOURCES;PACKAGES;OPTIONS;DEFINITIONS;CUSTOM_VAPIS;CUSTOM_DEPS;GRESOURCES" ${ARGN})
# Header and internal header is needed to generate internal vapi
if (ARGS_GENERATE_VAPI AND NOT ARGS_GENERATE_HEADER)
set(ARGS_GENERATE_HEADER ${ARGS_GENERATE_VAPI})
endif(ARGS_GENERATE_VAPI AND NOT ARGS_GENERATE_HEADER)
if("Ninja" STREQUAL ${CMAKE_GENERATOR} AND NOT DISABLE_FAST_VAPI AND NOT ARGS_GENERATE_HEADER)
set(ARGS_FAST_VAPI true)
endif()
if(ARGS_DIRECTORY)
get_filename_component(DIRECTORY ${ARGS_DIRECTORY} ABSOLUTE)
else(ARGS_DIRECTORY)
set(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
endif(ARGS_DIRECTORY)
if(ARGS_EXPORTS_DIR)
set(ARGS_EXPORTS_DIR ${CMAKE_BINARY_DIR}/${ARGS_EXPORTS_DIR})
else(ARGS_EXPORTS_DIR)
set(ARGS_EXPORTS_DIR ${CMAKE_BINARY_DIR}/exports)
endif(ARGS_EXPORTS_DIR)
file(MAKE_DIRECTORY "${ARGS_EXPORTS_DIR}")
include_directories(${DIRECTORY} ${ARGS_EXPORTS_DIR})
set(vala_pkg_opts "")
foreach(pkg ${ARGS_PACKAGES})
list(APPEND vala_pkg_opts "--pkg=${pkg}")
endforeach(pkg ${ARGS_PACKAGES})
set(vala_define_opts "")
foreach(def ${ARGS_DEFINITIONS})
list(APPEND vala_define_opts "--define=${def}")
endforeach(def ${ARGS_DEFINITIONS})
set(custom_vapi_arguments "")
if(ARGS_CUSTOM_VAPIS)
foreach(vapi ${ARGS_CUSTOM_VAPIS})
if(${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR})
list(APPEND custom_vapi_arguments ${vapi})
else (${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR})
list(APPEND custom_vapi_arguments ${CMAKE_CURRENT_SOURCE_DIR}/${vapi})
endif(${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR})
endforeach(vapi ${ARGS_CUSTOM_VAPIS})
endif(ARGS_CUSTOM_VAPIS)
set(gresources_args "")
if(ARGS_GRESOURCES)
set(gresources_args --gresources "${ARGS_GRESOURCES}")
endif(ARGS_GRESOURCES)
set(in_files "")
set(fast_vapi_files "")
set(out_files "")
set(out_extra_files "")
set(out_deps_files "")
set(vapi_arguments "")
if(ARGS_GENERATE_VAPI)
list(APPEND out_extra_files "${ARGS_EXPORTS_DIR}/${ARGS_GENERATE_VAPI}.vapi")
list(APPEND out_extra_files "${ARGS_EXPORTS_DIR}/${ARGS_GENERATE_VAPI}_internal.vapi")
set(vapi_arguments "--vapi=${ARGS_EXPORTS_DIR}/${ARGS_GENERATE_VAPI}.vapi" "--internal-vapi=${ARGS_EXPORTS_DIR}/${ARGS_GENERATE_VAPI}_internal.vapi")
if(ARGS_PACKAGES)
string(REPLACE ";" "\\n" pkgs "${ARGS_PACKAGES};${ARGS_CUSTOM_DEPS}")
add_custom_command(OUTPUT "${ARGS_EXPORTS_DIR}/${ARGS_GENERATE_VAPI}.deps" COMMAND echo -e "\"${pkgs}\"" > "${ARGS_EXPORTS_DIR}/${ARGS_GENERATE_VAPI}.deps" COMMENT "Generating ${ARGS_GENERATE_VAPI}.deps")
endif(ARGS_PACKAGES)
endif(ARGS_GENERATE_VAPI)
set(header_arguments "")
if(ARGS_GENERATE_HEADER)
list(APPEND out_extra_files "${ARGS_EXPORTS_DIR}/${ARGS_GENERATE_HEADER}.h")
list(APPEND out_extra_files "${ARGS_EXPORTS_DIR}/${ARGS_GENERATE_HEADER}_internal.h")
list(APPEND header_arguments "--header=${ARGS_EXPORTS_DIR}/${ARGS_GENERATE_HEADER}.h")
list(APPEND header_arguments "--internal-header=${ARGS_EXPORTS_DIR}/${ARGS_GENERATE_HEADER}_internal.h")
endif(ARGS_GENERATE_HEADER)
string(REPLACE " " ";" VALAC_FLAGS ${CMAKE_VALA_FLAGS})
if (VALA_VERSION VERSION_GREATER "0.38")
set(VALAC_COLORS "--color=always")
endif ()
if(ARGS_FAST_VAPI)
foreach(src ${ARGS_SOURCES} ${ARGS_UNPARSED_ARGUMENTS})
set(in_file "${CMAKE_CURRENT_SOURCE_DIR}/${src}")
list(APPEND in_files "${in_file}")
string(REPLACE ".vala" ".c" src ${src})
string(REPLACE ".gs" ".c" src ${src})
string(REPLACE ".c" ".vapi" fast_vapi ${src})
set(fast_vapi_file "${DIRECTORY}/${fast_vapi}")
list(APPEND fast_vapi_files "${fast_vapi_file}")
list(APPEND out_files "${DIRECTORY}/${src}")
_vala_mkdir_for_file("${fast_vapi_file}")
add_custom_command(OUTPUT ${fast_vapi_file}
COMMAND
${VALA_EXECUTABLE}
ARGS
${VALAC_COLORS}
--fast-vapi ${fast_vapi_file}
${vala_define_opts}
${ARGS_OPTIONS}
${VALAC_FLAGS}
${in_file}
DEPENDS
${in_file}
COMMENT
"Generating fast VAPI ${fast_vapi}"
)
endforeach(src ${ARGS_SOURCES} ${ARGS_UNPARSED_ARGUMENTS})
foreach(src ${ARGS_SOURCES} ${ARGS_UNPARSED_ARGUMENTS})
set(in_file "${CMAKE_CURRENT_SOURCE_DIR}/${src}")
string(REPLACE ".vala" ".c" c_code ${src})
string(REPLACE ".gs" ".c" c_code ${c_code})
string(REPLACE ".c" ".vapi" fast_vapi ${c_code})
set(my_fast_vapi_file "${DIRECTORY}/${fast_vapi}")
set(c_code_file "${DIRECTORY}/${c_code}")
set(fast_vapi_flags "")
set(fast_vapi_stamp "")
foreach(fast_vapi_file ${fast_vapi_files})
if(NOT "${fast_vapi_file}" STREQUAL "${my_fast_vapi_file}")
list(APPEND fast_vapi_flags --use-fast-vapi "${fast_vapi_file}")
list(APPEND fast_vapi_stamp "${fast_vapi_file}")
endif()
endforeach(fast_vapi_file)
_vala_mkdir_for_file("${fast_vapi_file}")
get_filename_component(dir "${c_code_file}" DIRECTORY)
add_custom_command(OUTPUT ${c_code_file}
COMMAND
${VALA_EXECUTABLE}
ARGS
${VALAC_COLORS}
"-C"
"-d" ${dir}
${vala_pkg_opts}
${vala_define_opts}
${gresources_args}
${ARGS_OPTIONS}
${VALAC_FLAGS}
${fast_vapi_flags}
${in_file}
${custom_vapi_arguments}
DEPENDS
${fast_vapi_stamp}
${in_file}
${ARGS_CUSTOM_VAPIS}
${ARGS_GRESOURCES}
COMMENT
"Generating C source ${c_code}"
)
endforeach(src)
if(NOT "${out_extra_files}" STREQUAL "")
add_custom_command(OUTPUT ${out_extra_files}
COMMAND
${VALA_EXECUTABLE}
ARGS
${VALAC_COLORS}
-C -q --disable-warnings
${header_arguments}
${vapi_arguments}
"-b" ${CMAKE_CURRENT_SOURCE_DIR}
"-d" ${DIRECTORY}
${vala_pkg_opts}
${vala_define_opts}
${gresources_args}
${ARGS_OPTIONS}
${VALAC_FLAGS}
${in_files}
${custom_vapi_arguments}
DEPENDS
${in_files}
${ARGS_CUSTOM_VAPIS}
${ARGS_GRESOURCES}
COMMENT
"Generating VAPI and headers for target ${output}"
)
endif()
else(ARGS_FAST_VAPI)
foreach(src ${ARGS_SOURCES} ${ARGS_UNPARSED_ARGUMENTS})
set(in_file "${CMAKE_CURRENT_SOURCE_DIR}/${src}")
list(APPEND in_files "${in_file}")
string(REPLACE ".vala" ".c" src ${src})
string(REPLACE ".gs" ".c" src ${src})
list(APPEND out_files "${DIRECTORY}/${src}")
_vala_mkdir_for_file("${fast_vapi_file}")
endforeach(src ${ARGS_SOURCES} ${ARGS_UNPARSED_ARGUMENTS})
add_custom_command(OUTPUT ${out_files} ${out_extra_files}
COMMAND
${VALA_EXECUTABLE}
ARGS
${VALAC_COLORS}
-C
${header_arguments}
${vapi_arguments}
"-b" ${CMAKE_CURRENT_SOURCE_DIR}
"-d" ${DIRECTORY}
${vala_pkg_opts}
${vala_define_opts}
${gresources_args}
${ARGS_OPTIONS}
${VALAC_FLAGS}
${in_files}
${custom_vapi_arguments}
DEPENDS
${in_files}
${ARGS_CUSTOM_VAPIS}
${ARGS_GRESOURCES}
COMMENT
"Generating C code for target ${output}"
)
endif(ARGS_FAST_VAPI)
set(${output} ${out_files} PARENT_SCOPE)
endfunction(vala_precompile)

View File

@ -1,21 +0,0 @@
if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
string(REGEX REPLACE "\n" ";" files "${files}")
foreach(file ${files})
message(STATUS "Uninstalling: $ENV{DESTDIR}${file}")
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
exec_program(
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval
)
if(NOT "${rm_retval}" STREQUAL 0)
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
endif(NOT "${rm_retval}" STREQUAL 0)
else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
endforeach(file)

298
configure vendored
View File

@ -1,298 +0,0 @@
#!/bin/sh
OPTS=`getopt -o "h" --long \
help,fetch-only,no-debug,disable-fast-vapi,with-tests,release,with-libsignal-in-tree,with-libsoup3,\
enable-plugin:,disable-plugin:,\
prefix:,program-prefix:,exec-prefix:,lib-suffix:,\
bindir:,libdir:,includedir:,datadir:,\
host:,build:,\
sbindir:,sysconfdir:,libexecdir:,localstatedir:,sharedstatedir:,mandir:,infodir:,\
-n './configure' -- "$@"`
if [ $? != 0 ] ; then echo "-- Ignoring unrecognized options." >&2 ; fi
eval set -- "$OPTS"
PREFIX=${PREFIX:-/usr/local}
ENABLED_PLUGINS=
DISABLED_PLUGINS=
BUILD_LIBSIGNAL_IN_TREE=
BUILD_TESTS=
BUILD_TYPE=Debug
DISABLE_FAST_VAPI=
LIB_SUFFIX=
NO_DEBUG=
FETCH_ONLY=
USE_SOUP3=
EXEC_PREFIX=
BINDIR=
SBINDIR=n
SYSCONFDIR=
DATADIR=
INCLUDEDIR=
LIBDIR=
LIBEXECDIR=
LOCALSTATEDIR=
SHAREDSTATEDIR=
MANDIR=
INFODIR=
help() {
cat << EOF
Usage:
./configure [OPTION]...
Defaults for the options (based on current environment) are specified in
brackets.
Configuration:
-h, --help Print this help and exit
--disable-fast-vapi Disable the usage of Vala compilers fast-vapi
feature. fast-vapi mode is slower when doing
clean builds, but faster when doing incremental
builds (during development).
--fetch-only Only fetch the files required to run ./configure
without network access later and exit.
--no-debug Build without debug symbols
--release Configure to build an optimized release version
--with-libsignal-in-tree Build libsignal-protocol-c in tree and link it
statically.
--with-libsoup3 Build with libsoup-3.0
--with-tests Also build tests.
Plugin configuration:
--enable-plugin=PLUGIN Enable compilation of plugin PLUGIN.
--disable-plugin=PLUGIN Disable compilation of plugin PLUGIN.
Installation directories:
--prefix=PREFIX Install architecture-independent files in PREFIX
[$PREFIX]
--program-prefix=PREFIX Same as --prefix
--exec-prefix= Install architecture-dependent files in EPREFIX
[PREFIX]
--lib-suffix=SUFFIX Append SUFFIX to the directory name for libraries
By default, \`make install' will install all the files in
\`/usr/local/bin', \`/usr/local/lib' etc. You can specify
an installation prefix other than \`/usr/local' using \`--prefix',
for instance \`--prefix=\$HOME'.
For better control, use the options below.
Fine tuning of the installation directories:
--bindir=DIR user executables [EPREFIX/bin]
--libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include]
--datadir=DIR read-only data [PREFIX/share]
For compatibility with autotools, these options will be silently ignored:
--host, --build, --sbindir, --sysconfdir, --libexecdir, --sharedstatedir,
--localstatedir, --mandir, --infodir
Some influential environment variables:
CC C compiler command
CFLAGS C compiler flags
PKG_CONFIG_PATH directories to add to pkg-config's search path
PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path
USE_CCACHE decide to use ccache when compiling C objects
VALAC Vala compiler command
VALACFLAGS Vala compiler flags
Use these variables to override the choices made by \`configure' or to help
it to find libraries and programs with nonstandard names/locations.
EOF
}
while true; do
case "$1" in
--prefix ) PREFIX="$2"; shift; shift ;;
--enable-plugin ) if [ -z "$ENABLED_PLUGINS" ]; then ENABLED_PLUGINS="$2"; else ENABLED_PLUGINS="$ENABLED_PLUGINS;$2"; fi; shift; shift ;;
--disable-plugin ) if [ -z "$DISABLED_PLUGINS" ]; then DISABLED_PLUGINS="$2"; else DISABLED_PLUGINS="$DISABLED_PLUGINS;$2"; fi; shift; shift ;;
--valac ) VALA_EXECUTABLE="$2"; shift; shift ;;
--valac-flags ) VALAC_FLAGS="$2"; shift; shift ;;
--lib-suffix ) LIB_SUFFIX="$2"; shift; shift ;;
--with-libsignal-in-tree ) BUILD_LIBSIGNAL_IN_TREE=yes; shift ;;
--with-libsoup3 ) USE_SOUP3=yes; shift ;;
--disable-fast-vapi ) DISABLE_FAST_VAPI=yes; shift ;;
--no-debug ) NO_DEBUG=yes; shift ;;
--fetch-only ) FETCH_ONLY=yes; shift ;;
--release ) BUILD_TYPE=RelWithDebInfo; shift ;;
--with-tests ) BUILD_TESTS=yes; shift ;;
# Autotools paths
--program-prefix ) PREFIX="$2"; shift; shift ;;
--exec-prefix ) EXEC_PREFIX="$2"; shift; shift ;;
--bindir ) BINDIR="$2"; shift; shift ;;
--datadir ) DATADIR="$2"; shift; shift ;;
--includedir ) INCLUDEDIR="$2"; shift; shift ;;
--libdir ) LIBDIR="$2"; shift; shift ;;
# Autotools paths not used
--sbindir ) SBINDIR="$2"; shift; shift ;;
--sysconfdir ) SYSCONFDIR="$2"; shift; shift ;;
--libexecdir ) LIBEXECDIR="$2"; shift; shift ;;
--localstatedir ) LOCALSTATEDIR="$2"; shift; shift ;;
--sharedstatedir ) SHAREDSTATEDIR="$2"; shift; shift ;;
--mandir ) MANDIR="$2"; shift; shift ;;
--infodir ) INFODIR="$2"; shift; shift ;;
--host | --build ) shift; shift ;;
-h | --help ) help; exit 0 ;;
-- ) shift; break ;;
* ) break ;;
esac
done
if [ "$BUILD_LIBSIGNAL_IN_TREE" = "yes" ] || [ "$FETCH_ONLY" = "yes" ]; then
if [ -d ".git" ]; then
git submodule update --init 2>/dev/null
else
tmp=0
for i in $(cat .gitmodules | grep -n submodule | awk -F ':' '{print $1}') $(wc -l .gitmodules | awk '{print $1}'); do
if ! [ $tmp -eq 0 ]; then
name=$(cat .gitmodules | head -n $tmp | tail -n 1 | awk -F '"' '{print $2}')
def=$(cat .gitmodules | head -n $i | tail -n $(expr "$i" - "$tmp") | awk -F ' ' '{print $1 $2 $3}')
path=$(echo "$def" | grep '^path=' | awk -F '=' '{print $2}')
url=$(echo "$def" | grep '^url=' | awk -F '=' '{print $2}')
branch=$(echo "$def" | grep '^branch=' | awk -F '=' '{print $2}')
if ! ls "$path"/* >/dev/null 2>/dev/null; then
git=$(which git)
if ! [ $? -eq 0 ] || ! [ -x $git ]; then
echo "Failed retrieving missing files"
exit 5
fi
res=$(git clone "$url" "$path" 2>&1)
if ! [ $? -eq 0 ] || ! [ -d $path ]; then
echo "Failed retrieving missing files: $res"
exit 5
fi
if [ -n "$branch" ]; then
olddir="$(pwd)"
cd "$path"
res=$(git checkout "$branch" 2>&1)
if ! [ $? -eq 0 ]; then
echo "Failed retrieving missing files: $res"
exit 5
fi
cd "$olddir"
fi
echo "Submodule path '$path': checked out '$branch' (via git clone)"
fi
fi
tmp=$i
done
fi
fi
if [ "$FETCH_ONLY" = "yes" ]; then exit 0; fi
if [ ! -x "$(which cmake 2>/dev/null)" ]
then
echo "-!- CMake required."
exit 1
fi
ninja_bin="$(which ninja-build 2>/dev/null)"
if ! [ -x "$ninja_bin" ]; then
ninja_bin="$(which ninja 2>/dev/null)"
fi
if [ -x "$ninja_bin" ]; then
ninja_version=`$ninja_bin --version 2>/dev/null`
if [ $? -eq 0 ]; then
if [ -d build ]; then
last_ninja_version=`cat build/.ninja_version 2>/dev/null`
else
last_ninja_version=0
fi
if [ "$ninja_version" != "$last_ninja_version" ]; then
echo "-- Found Ninja: $ninja_bin (found version \"$ninja_version\")"
fi
cmake_type="Ninja"
exec_bin="$ninja_bin"
exec_command="$exec_bin"
elif [ "/usr/sbin/ninja" = "$ninja_bin" ]; then
echo "-- Ninja at $ninja_bin is not usable. Did you install 'ninja' instead of 'ninja-build'?"
fi
fi
if ! [ -x "$exec_bin" ]; then
make_bin="$(which make 2>/dev/null)"
if [ -x "$make_bin" ]; then
echo "-- Found Make: $make_bin"
cmake_type="Unix Makefiles"
exec_bin="$make_bin"
exec_command="$exec_bin"
echo "-- Running with make. Using Ninja (ninja-build) might improve build experience."
fi
fi
if ! [ -x "$exec_bin" ]; then
echo "-!- No compatible build system (Ninja, Make) found."
exit 4
fi
if [ -f ./build ]; then
echo "-!- ./build file exists. ./configure can't continue"
exit 2
fi
if [ -d build ]; then
last_type=`cat build/.cmake_type`
if [ "$cmake_type" != "$last_type" ]
then
echo "-- Using different build system, cleaning build system files"
cd build
rm -r CMakeCache.txt CMakeFiles
cd ..
fi
fi
mkdir -p build
cd build
echo "$cmake_type" > .cmake_type
echo "$ninja_version" > .ninja_version
cmake -G "$cmake_type" \
-DCMAKE_INSTALL_PREFIX="$PREFIX" \
-DCMAKE_BUILD_TYPE="$BUILD_TYPE" \
-DENABLED_PLUGINS="$ENABLED_PLUGINS" \
-DDISABLED_PLUGINS="$DISABLED_PLUGINS" \
-DBUILD_TESTS="$BUILD_TESTS" \
-DBUILD_LIBSIGNAL_IN_TREE="$BUILD_LIBSIGNAL_IN_TREE" \
-DUSE_SOUP3="$USE_SOUP3" \
-DVALA_EXECUTABLE="$VALAC" \
-DCMAKE_VALA_FLAGS="$VALACFLAGS" \
-DDISABLE_FAST_VAPI="$DISABLE_FAST_VAPI" \
-DLIB_SUFFIX="$LIB_SUFFIX" \
-DNO_DEBUG="$NO_DEBUG" \
-DEXEC_INSTALL_PREFIX="$EXEC_PREFIX" \
-DSHARE_INSTALL_PREFIX="$DATADIR" \
-DBIN_INSTALL_DIR="$BINDIR" \
-DINCLUDE_INSTALL_DIR="$INCLUDEDIR" \
-DLIB_INSTALL_DIR="$LIBDIR" \
-Wno-dev \
.. || exit 9
if [ "$cmake_type" = "Ninja" ]; then
cat << EOF > Makefile
default:
@sh -c "$exec_command"
%:
@sh -c "$exec_command \"\$@\""
EOF
fi
cd ..
cat << EOF > Makefile
default:
@sh -c "cd build; $exec_command"
distclean: clean uninstall
test: default
echo "make test not yet supported"
%:
@sh -c "cd build; $exec_command \"\$@\""
EOF
echo "-- Configured. Type 'make' to build, 'make install' to install."

View File

@ -1,41 +0,0 @@
find_package(GCrypt REQUIRED)
find_package(Srtp2 REQUIRED)
find_packages(CRYPTO_VALA_PACKAGES REQUIRED
GLib
GObject
GIO
)
vala_precompile(CRYPTO_VALA_C
SOURCES
"src/cipher.vala"
"src/cipher_converter.vala"
"src/error.vala"
"src/random.vala"
"src/srtp.vala"
CUSTOM_VAPIS
"${CMAKE_CURRENT_SOURCE_DIR}/vapi/gcrypt.vapi"
"${CMAKE_CURRENT_SOURCE_DIR}/vapi/libsrtp2.vapi"
PACKAGES
${CRYPTO_VALA_PACKAGES}
GENERATE_VAPI
crypto-vala
GENERATE_HEADER
crypto-vala
)
add_custom_target(crypto-vala-vapi
DEPENDS
${CMAKE_BINARY_DIR}/exports/crypto-vala.vapi
${CMAKE_BINARY_DIR}/exports/crypto-vala.deps
)
add_definitions(${VALA_CFLAGS} -DG_LOG_DOMAIN="crypto-vala")
add_library(crypto-vala SHARED ${CRYPTO_VALA_C})
add_dependencies(crypto-vala crypto-vala-vapi)
target_link_libraries(crypto-vala ${CRYPTO_VALA_PACKAGES} gcrypt libsrtp2)
set_target_properties(crypto-vala PROPERTIES VERSION 0.0 SOVERSION 0)
install(TARGETS crypto-vala ${TARGET_INSTALL})
install(FILES ${CMAKE_BINARY_DIR}/exports/crypto-vala.vapi ${CMAKE_BINARY_DIR}/exports/crypto-vala.deps DESTINATION ${VAPI_INSTALL_DIR})
install(FILES ${CMAKE_BINARY_DIR}/exports/crypto-vala.h DESTINATION ${INCLUDE_INSTALL_DIR})

View File

@ -0,0 +1,2 @@
gio-2.0
glib-2.0

23
crypto-vala/meson.build Normal file
View File

@ -0,0 +1,23 @@
dependencies = [
dep_gio,
dep_glib,
dep_libgcrypt,
dep_libsrtp2,
]
sources = files(
'src/cipher.vala',
'src/cipher_converter.vala',
'src/error.vala',
'src/random.vala',
'src/srtp.vala',
)
c_args = [
'-DG_LOG_DOMAIN="crypto-vala"',
]
vala_args = [
'--vapidir', meson.current_source_dir() / 'vapi',
]
lib_crypto_vala = library('crypto-vala', sources, c_args: c_args, vala_args: vala_args, dependencies: dependencies, version: '0.0', install: true, install_dir: [true, true, true], install_rpath: default_install_rpath)
dep_crypto_vala = declare_dependency(link_with: lib_crypto_vala, include_directories: include_directories('.'))
install_data('crypto-vala.deps', install_dir: get_option('datadir') / 'vala/vapi', install_tag: 'devel') # TODO: workaround for https://github.com/mesonbuild/meson/issues/9756

View File

@ -55,7 +55,7 @@ public class SymmetricCipherEncrypter : SymmetricCipherConverter {
}
return ConverterResult.CONVERTED;
} catch (Crypto.Error e) {
throw new IOError.FAILED(@"$(e.domain) error while decrypting: $(e.message)");
throw new IOError.FAILED(@"$(e.domain) error while encrypting: $(e.message)");
}
}
}

194
dino.doap
View File

@ -3,7 +3,7 @@
<Project>
<name>Dino</name>
<short-name>dino</short-name>
<shortdesc xml:lang="en">Modern XMPP Chat Client</shortdesc>
<shortdesc xml:lang="en">Modern XMPP chat client</shortdesc>
<shortdesc xml:lang="zh-TW">現代化的 XMPP 用戶端聊天軟件</shortdesc>
<shortdesc xml:lang="zh-CN">现代 XMPP 聊天客户端</shortdesc>
<shortdesc xml:lang="tr">Modern XMPP Sohbet İstemcisi</shortdesc>
@ -227,24 +227,28 @@
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0004.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0027.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0030.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0045.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -252,6 +256,7 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0047.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:note>For use with XEP-0261</xmpp:note>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -259,12 +264,14 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0048.html"/>
<xmpp:status>deprecated</xmpp:status>
<xmpp:note>Migrating to XEP-0402 if supported by server</xmpp:note>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0049.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -272,6 +279,7 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0054.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:note>Only for viewing avatars</xmpp:note>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -279,12 +287,14 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0059.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:note>For use with XEP-0313</xmpp:note>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0060.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -292,6 +302,7 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0065.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:note>For use with XEP-0260</xmpp:note>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -299,42 +310,49 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0066.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:note>For file transfers using XEP-0363</xmpp:note>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0077.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0082.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0084.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0085.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0115.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0153.html"/>
<xmpp:status>deprecated</xmpp:status>
<xmpp:since>0.1</xmpp:since>
<xmpp:note>Only to fetch Avatars from other users</xmpp:note>
</xmpp:SupportedXep>
</implements>
@ -342,78 +360,98 @@
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0163.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0166.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0167.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0176.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0177.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0184.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0191.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0198.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0199.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0203.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0215.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0222.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0223.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0234.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -421,6 +459,7 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0245.html"/>
<xmpp:version>1.0</xmpp:version>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -428,43 +467,70 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0249.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:note>No support for sending</xmpp:note>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0260.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:version>1.0.3</xmpp:version>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0261.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:version>1.0</xmpp:version>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0272.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:version>0.2.0</xmpp:version>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0280.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:version>1.0.1</xmpp:version>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0293.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:version>1.0.2</xmpp:version>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0294.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:version>1.1.2</xmpp:version>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0297.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:note>For use with XEP-0280</xmpp:note>
<xmpp:status>partial</xmpp:status>
<xmpp:version>1.0</xmpp:version>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0298.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -479,7 +545,7 @@
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0313.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:note>Not for MUCs</xmpp:note>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -487,18 +553,21 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0320.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:version>1.0.0</xmpp:version>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0333.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0334.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -506,24 +575,37 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0353.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:version>0.3.1</xmpp:version>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0359.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0363.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0367.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:version>0.3</xmpp:version>
<xmpp:since>0.5</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0368.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:version>1.1.0</xmpp:version>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -531,6 +613,7 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0380.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:note>Only for outgoing messages</xmpp:note>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -538,42 +621,121 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0384.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:version>0.3.0</xmpp:version>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0391.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:version>0.1.2</xmpp:version>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0392.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:version>1.0.0</xmpp:version>
<xmpp:since>0.5</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0393.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:version>1.1.1</xmpp:version>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0394.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:version>0.3.0</xmpp:version>
<xmpp:since>0.5</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0396.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0398.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0402.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:version>1.2.0</xmpp:version>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0410.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.2</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0421.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:version>0.2.0</xmpp:version>
<xmpp:since>0.4</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0426.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:version>0.3.0</xmpp:version>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0428.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:version>0.2.1</xmpp:version>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0444.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:version>0.1.1</xmpp:version>
<xmpp:since>0.4</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0446.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:version>0.2.0</xmpp:version>
<xmpp:since>0.5</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0447.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:version>0.3.0</xmpp:version>
<xmpp:since>0.5</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0453.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:version>0.1.2</xmpp:version>
</xmpp:SupportedXep>
</implements>
<implements>
@ -581,6 +743,30 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0454.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:note>No support for embedded thumbnails</xmpp:note>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0461.html"/>
<xmpp:status>complete</xmpp:status>
<xmpp:version>0.2.0</xmpp:version>
<xmpp:since>0.4</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0482.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:version>0.1.0</xmpp:version>
<xmpp:since>0.5</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0486.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:version>0.1.0</xmpp:version>
</xmpp:SupportedXep>
</implements>
</Project>

View File

@ -9,7 +9,7 @@
<name>Dino</name>
<short-name>dino</short-name>
<shortdesc xml:lang="en">Modern XMPP Chat Client</shortdesc>
<shortdesc xml:lang="en">Modern XMPP chat client</shortdesc>
<description xml:lang="en">
Dino is a modern open-source chat client for the desktop. It focuses on providing a clean and reliable Jabber/XMPP experience while having your privacy in mind.
It supports end-to-end encryption with OMEMO and OpenPGP and allows configuring privacy-related features such as read receipts and typing notifications.
@ -47,24 +47,28 @@
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0004.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0027.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0030.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0045.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -72,6 +76,7 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0047.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:note>For use with XEP-0261</xmpp:note>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -79,12 +84,14 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0048.html" />
<xmpp:status>deprecated</xmpp:status>
<xmpp:note>Migrating to XEP-0402 if supported by server</xmpp:note>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0049.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -92,6 +99,7 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0054.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:note>Only for viewing avatars</xmpp:note>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -99,12 +107,14 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0059.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:note>For use with XEP-0313</xmpp:note>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0060.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -112,6 +122,7 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0065.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:note>For use with XEP-0260</xmpp:note>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -119,42 +130,49 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0066.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:note>For file transfers using XEP-0363</xmpp:note>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0077.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0082.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0084.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0085.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0115.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0153.html" />
<xmpp:status>deprecated</xmpp:status>
<xmpp:since>0.1</xmpp:since>
<xmpp:note>Only to fetch Avatars from other users</xmpp:note>
</xmpp:SupportedXep>
</implements>
@ -162,78 +180,98 @@
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0163.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0166.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0167.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0176.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0177.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0184.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0191.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0198.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0199.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0203.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0215.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0222.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0223.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0234.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -241,6 +279,7 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0245.html" />
<xmpp:version>1.0</xmpp:version>
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -248,43 +287,70 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0249.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:note>No support for sending</xmpp:note>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0260.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:version>1.0.3</xmpp:version>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0261.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:version>1.0</xmpp:version>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0272.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:version>0.2.0</xmpp:version>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0280.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:version>1.0.1</xmpp:version>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0293.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:version>1.0.2</xmpp:version>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0294.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:version>1.1.2</xmpp:version>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0297.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:note>For use with XEP-0280</xmpp:note>
<xmpp:status>partial</xmpp:status>
<xmpp:version>1.0</xmpp:version>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0298.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -299,7 +365,7 @@
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0313.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:note>Not for MUCs</xmpp:note>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -307,18 +373,21 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0320.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:version>1.0.0</xmpp:version>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0333.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0334.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -326,24 +395,37 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0353.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:version>0.3.1</xmpp:version>
<xmpp:since>0.3</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0359.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0363.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0367.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:version>0.3</xmpp:version>
<xmpp:since>0.5</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0368.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:version>1.1.0</xmpp:version>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -351,6 +433,7 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0380.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:note>Only for outgoing messages</xmpp:note>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
@ -358,42 +441,121 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0384.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:version>0.3.0</xmpp:version>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0391.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:version>0.1.2</xmpp:version>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0392.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:version>1.0.0</xmpp:version>
<xmpp:since>0.5</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0393.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:version>1.1.1</xmpp:version>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0394.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:version>0.3.0</xmpp:version>
<xmpp:since>0.5</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0396.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0398.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0402.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:version>1.2.0</xmpp:version>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0410.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:since>0.2</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0421.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:version>0.2.0</xmpp:version>
<xmpp:since>0.4</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0426.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:version>0.3.0</xmpp:version>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0428.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:version>0.2.1</xmpp:version>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0444.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:version>0.1.1</xmpp:version>
<xmpp:since>0.4</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0446.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:version>0.2.0</xmpp:version>
<xmpp:since>0.5</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0447.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:version>0.3.0</xmpp:version>
<xmpp:since>0.5</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0453.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:version>0.1.2</xmpp:version>
</xmpp:SupportedXep>
</implements>
<implements>
@ -401,6 +563,30 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0454.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:note>No support for embedded thumbnails</xmpp:note>
<xmpp:since>0.1</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0461.html" />
<xmpp:status>complete</xmpp:status>
<xmpp:version>0.2.0</xmpp:version>
<xmpp:since>0.4</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0482.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:version>0.1.0</xmpp:version>
<xmpp:since>0.5</xmpp:since>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0486.html" />
<xmpp:status>partial</xmpp:status>
<xmpp:version>0.1.0</xmpp:version>
</xmpp:SupportedXep>
</implements>
</Project>

133
im.dino.Dino.json Normal file
View File

@ -0,0 +1,133 @@
{
"id": "im.dino.Dino",
"runtime": "org.gnome.Platform",
"runtime-version": "48",
"sdk": "org.gnome.Sdk",
"command": "dino",
"finish-args": [
"--share=ipc",
"--socket=fallback-x11",
"--socket=wayland",
"--socket=pulseaudio",
"--socket=gpg-agent",
"--filesystem=xdg-run/pipewire-0",
"--share=network",
"--device=dri",
"--talk-name=org.freedesktop.Notifications"
],
"modules": [
{
"name": "protobuf",
"buildsystem": "cmake-ninja",
"cleanup": [
"*"
],
"config-opts": [
"-Dprotobuf_BUILD_TESTS=OFF",
"-Dprotobuf_BUILD_LIBUPB=OFF"
],
"sources": [
{
"type": "archive",
"url": "https://github.com/protocolbuffers/protobuf/releases/download/v30.2/protobuf-30.2.tar.gz",
"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"
}
}
]
},
{
"name": "libprotobuf-c",
"buildsystem": "autotools",
"config-opts": [
"CFLAGS=-fPIC"
],
"post-install": [
"rm /app/lib/*.so"
],
"cleanup": [
"*"
],
"sources": [
{
"type": "archive",
"url": "https://github.com/protobuf-c/protobuf-c/releases/download/v1.5.2/protobuf-c-1.5.2.tar.gz",
"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"
}
}
]
},
{
"name": "libomemo-c",
"buildsystem": "meson",
"cleanup": [
"/lib/pkgconfig",
"/include"
],
"config-opts": [
"-Dtests=false",
"-Ddefault_library=static"
],
"sources": [
{
"type": "archive",
"url": "https://github.com/dino/libomemo-c/releases/download/v0.5.1/libomemo-c-0.5.1.tar.gz",
"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"
}
}
]
},
{
"name": "qrencode",
"buildsystem": "cmake-ninja",
"cleanup": [
"*"
],
"config-opts": [
"-DCMAKE_C_FLAGS=-fPIC"
],
"sources": [
{
"type": "archive",
"url": "https://github.com/fukuchi/libqrencode/archive/refs/tags/v4.1.1.tar.gz",
"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"
}
}
]
},
{
"name": "dino",
"buildsystem": "meson",
"builddir": true,
"cleanup": [
"/include",
"/share/vala"
],
"sources": [
{
"type": "dir",
"path": "."
}
]
}
]
}

View File

@ -1,125 +0,0 @@
find_packages(LIBDINO_PACKAGES REQUIRED
GDKPixbuf2
Gee
GLib
GModule
GObject
)
vala_precompile(LIBDINO_VALA_C
SOURCES
src/application.vala
src/dbus/login1.vala
src/dbus/notifications.vala
src/dbus/upower.vala
src/entity/account.vala
src/entity/call.vala
src/entity/conversation.vala
src/entity/encryption.vala
src/entity/file_transfer.vala
src/entity/message.vala
src/entity/settings.vala
src/plugin/interfaces.vala
src/plugin/loader.vala
src/plugin/registry.vala
src/service/avatar_manager.vala
src/service/blocking_manager.vala
src/service/call_store.vala
src/service/call_state.vala
src/service/call_peer_state.vala
src/service/calls.vala
src/service/chat_interaction.vala
src/service/connection_manager.vala
src/service/content_item_store.vala
src/service/conversation_manager.vala
src/service/counterpart_interaction_manager.vala
src/service/database.vala
src/service/entity_capabilities_storage.vala
src/service/entity_info.vala
src/service/fallback_body.vala
src/service/file_manager.vala
src/service/file_transfer_storage.vala
src/service/history_sync.vala
src/service/jingle_file_transfers.vala
src/service/message_correction.vala
src/service/message_processor.vala
src/service/message_storage.vala
src/service/module_manager.vala
src/service/muc_manager.vala
src/service/notification_events.vala
src/service/presence_manager.vala
src/service/replies.vala
src/service/reactions.vala
src/service/registration.vala
src/service/roster_manager.vala
src/service/search_processor.vala
src/service/stream_interactor.vala
src/service/util.vala
src/util/display_name.vala
src/util/util.vala
src/util/weak_map.vala
CUSTOM_VAPIS
"${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi"
"${CMAKE_BINARY_DIR}/exports/qlite.vapi"
CUSTOM_DEPS
xmpp-vala
qlite
PACKAGES
${LIBDINO_PACKAGES}
GENERATE_VAPI
dino
GENERATE_HEADER
dino
)
add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/exports/dino_i18n.h"
COMMAND
cp "${CMAKE_CURRENT_SOURCE_DIR}/src/dino_i18n.h" "${CMAKE_BINARY_DIR}/exports/dino_i18n.h"
DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/src/dino_i18n.h"
COMMENT
Copy header file dino_i18n.h
)
add_custom_target(dino-vapi
DEPENDS
${CMAKE_BINARY_DIR}/exports/dino.vapi
${CMAKE_BINARY_DIR}/exports/dino.deps
${CMAKE_BINARY_DIR}/exports/dino_i18n.h
)
add_definitions(${VALA_CFLAGS} -DDINO_SYSTEM_PLUGIN_DIR="${PLUGIN_INSTALL_DIR}" -DDINO_SYSTEM_LIBDIR_NAME="${LIBDIR_NAME}" -DG_LOG_DOMAIN="libdino" -DDINO_VERSION=\"${PROJECT_VERSION}\")
add_library(libdino SHARED ${LIBDINO_VALA_C} ${CMAKE_BINARY_DIR}/exports/dino_i18n.h)
add_dependencies(libdino dino-vapi)
target_link_libraries(libdino xmpp-vala qlite ${LIBDINO_PACKAGES} m)
set_target_properties(libdino PROPERTIES PREFIX "" VERSION 0.0 SOVERSION 0)
install(TARGETS libdino ${TARGET_INSTALL})
install(FILES ${CMAKE_BINARY_DIR}/exports/dino.vapi ${CMAKE_BINARY_DIR}/exports/dino.deps DESTINATION ${VAPI_INSTALL_DIR})
install(FILES ${CMAKE_BINARY_DIR}/exports/dino.h ${CMAKE_BINARY_DIR}/exports/dino_i18n.h DESTINATION ${INCLUDE_INSTALL_DIR})
if(BUILD_TESTS)
vala_precompile(LIBDINO_TEST_VALA_C
SOURCES
"tests/weak_map.vala"
"tests/testcase.vala"
"tests/common.vala"
CUSTOM_VAPIS
${CMAKE_BINARY_DIR}/exports/dino_internal.vapi
${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi
${CMAKE_BINARY_DIR}/exports/qlite.vapi
PACKAGES
${LIBDINO_PACKAGES}
OPTIONS
${LIBDINO_EXTRA_OPTIONS}
)
add_definitions(${VALA_CFLAGS})
add_executable(libdino-test ${LIBDINO_TEST_VALA_C})
target_link_libraries(libdino-test libdino)
endif(BUILD_TESTS)

6
libdino/dino.deps Normal file
View File

@ -0,0 +1,6 @@
gdk-pixbuf-2.0
gee-0.8
glib-2.0
gmodule-2.0
qlite
xmpp-vala

92
libdino/meson.build Normal file
View File

@ -0,0 +1,92 @@
# version_vala
dot_git = meson.current_source_dir() / '../.git'
version_file = meson.current_source_dir() / '../VERSION'
command = [prog_python, files('version.py'), version_file, '--git-repo', meson.current_source_dir()]
if prog_git.found()
command += ['--git', prog_git]
endif
version_vala = vcs_tag(command: command, input: 'src/version.vala.in', output: 'version.vala', replace_string: '%VERSION%')
# libdino
dependencies = [
dep_gdk_pixbuf,
dep_gee,
dep_gio,
dep_glib,
dep_gmodule,
dep_qlite,
dep_xmpp_vala
]
sources = files(
'src/application.vala',
'src/dbus/login1.vala',
'src/dbus/notifications.vala',
'src/dbus/upower.vala',
'src/entity/account.vala',
'src/entity/call.vala',
'src/entity/conversation.vala',
'src/entity/encryption.vala',
'src/entity/file_transfer.vala',
'src/entity/message.vala',
'src/entity/settings.vala',
'src/plugin/interfaces.vala',
'src/plugin/loader.vala',
'src/plugin/registry.vala',
'src/service/avatar_manager.vala',
'src/service/blocking_manager.vala',
'src/service/call_store.vala',
'src/service/call_state.vala',
'src/service/call_peer_state.vala',
'src/service/calls.vala',
'src/service/chat_interaction.vala',
'src/service/connection_manager.vala',
'src/service/contact_model.vala',
'src/service/content_item_store.vala',
'src/service/conversation_manager.vala',
'src/service/counterpart_interaction_manager.vala',
'src/service/database.vala',
'src/service/entity_capabilities_storage.vala',
'src/service/entity_info.vala',
'src/service/fallback_body.vala',
'src/service/file_manager.vala',
'src/service/file_transfer_storage.vala',
'src/service/history_sync.vala',
'src/service/jingle_file_transfers.vala',
'src/service/message_correction.vala',
'src/service/message_processor.vala',
'src/service/message_storage.vala',
'src/service/module_manager.vala',
'src/service/muc_manager.vala',
'src/service/notification_events.vala',
'src/service/presence_manager.vala',
'src/service/replies.vala',
'src/service/reactions.vala',
'src/service/registration.vala',
'src/service/roster_manager.vala',
'src/service/search_processor.vala',
'src/service/sfs_metadata.vala',
'src/service/stateless_file_sharing.vala',
'src/service/stream_interactor.vala',
'src/service/util.vala',
'src/util/display_name.vala',
'src/util/limit_input_stream.vala',
'src/util/send_message.vala',
'src/util/util.vala',
'src/util/weak_map.vala',
'src/util/weak_timeout.vala',
)
sources += [version_vala]
c_args = [
'-DDINO_SYSTEM_LIBDIR_NAME="@0@"'.format(get_option('prefix') / get_option('libdir')),
'-DDINO_SYSTEM_PLUGIN_DIR="@0@"'.format(get_option('prefix') / get_option('libdir') / get_option('plugindir')),
'-DG_LOG_DOMAIN="libdino"',
]
vala_args = []
if meson.get_compiler('vala').version().version_compare('=0.56.11')
vala_args += ['-D', 'VALA_0_56_11']
endif
lib_dino = library('libdino', sources, c_args: c_args, vala_args: vala_args, include_directories: include_directories('src'), dependencies: dependencies, name_prefix: '', version: '0.0', install: true, install_dir: [true, true, true], install_rpath: default_install_rpath)
dep_dino = declare_dependency(link_with: lib_dino, include_directories: include_directories('.', 'src'))
install_data('dino.deps', install_dir: get_option('datadir') / 'vala/vapi', install_tag: 'devel') # TODO: workaround for https://github.com/mesonbuild/meson/issues/9756
install_headers('src/dino_i18n.h')

View File

@ -2,7 +2,6 @@ using Dino.Entities;
namespace Dino {
extern const string VERSION;
public string get_version() { return VERSION; }
public string get_short_version() {
if (!VERSION.contains("~")) return VERSION;
@ -40,12 +39,12 @@ public interface Application : GLib.Application {
PresenceManager.start(stream_interactor);
CounterpartInteractionManager.start(stream_interactor);
BlockingManager.start(stream_interactor);
Calls.start(stream_interactor, db);
ConversationManager.start(stream_interactor, db);
MucManager.start(stream_interactor);
AvatarManager.start(stream_interactor, db);
RosterManager.start(stream_interactor, db);
FileManager.start(stream_interactor, db);
Calls.start(stream_interactor, db);
CallStore.start(stream_interactor, db);
ContentItemStore.start(stream_interactor, db);
ChatInteraction.start(stream_interactor);
@ -58,6 +57,8 @@ public interface Application : GLib.Application {
Reactions.start(stream_interactor, db);
Replies.start(stream_interactor, db);
FallbackBody.start(stream_interactor, db);
ContactModels.start(stream_interactor);
StatelessFileSharing.start(stream_interactor, db);
create_actions();

View File

@ -8,7 +8,10 @@ public class Account : Object {
public int id { get; set; }
public string localpart { get { return full_jid.localpart; } }
public string domainpart { get { return full_jid.domainpart; } }
public string resourcepart { get { return full_jid.resourcepart;} }
public string resourcepart {
get { return full_jid.resourcepart; }
private set { full_jid.resourcepart = value; }
}
public Jid bare_jid { owned get { return full_jid.bare_jid; } }
public Jid full_jid { get; private set; }
public string? password { get; set; }
@ -18,28 +21,17 @@ public class Account : Object {
public string? alias { get; set; }
public bool enabled { get; set; default = false; }
public string? roster_version { get; set; }
public DateTime mam_earliest_synced { get; set; default=new DateTime.from_unix_utc(0); }
private Database? db;
public Account(Jid bare_jid, string? resourcepart, string? password, string? alias) {
public Account(Jid bare_jid, string password) {
this.id = -1;
if (resourcepart != null) {
try {
this.full_jid = bare_jid.with_resource(resourcepart);
} catch (InvalidJidError e) {
warning("Tried to create account with invalid resource (%s), defaulting to auto generated", e.message);
}
}
if (this.full_jid == null) {
try {
this.full_jid = bare_jid.with_resource("dino." + Random.next_int().to_string("%x"));
} catch (InvalidJidError e) {
error("Auto-generated resource was invalid (%s)", e.message);
}
try {
this.full_jid = bare_jid.with_resource(get_random_resource());
} catch (InvalidJidError e) {
error("Auto-generated resource was invalid (%s)", e.message);
}
this.password = password;
this.alias = alias;
}
public Account.from_row(Database db, Qlite.Row row) throws InvalidJidError {
@ -50,7 +42,6 @@ public class Account : Object {
alias = row[db.account.alias];
enabled = row[db.account.enabled];
roster_version = row[db.account.roster_version];
mam_earliest_synced = new DateTime.from_unix_utc(row[db.account.mam_earliest_synced]);
notify.connect(on_update);
}
@ -66,7 +57,6 @@ public class Account : Object {
.value(db.account.alias, alias)
.value(db.account.enabled, enabled)
.value(db.account.roster_version, roster_version)
.value(db.account.mam_earliest_synced, (long)mam_earliest_synced.to_unix())
.perform();
notify.connect(on_update);
@ -79,6 +69,14 @@ public class Account : Object {
db = null;
}
public void set_random_resource() {
this.resourcepart = get_random_resource();
}
private static string get_random_resource() {
return "dino." + Random.next_int().to_string("%x");
}
public bool equals(Account acc) {
return equals_func(this, acc);
}
@ -106,8 +104,6 @@ public class Account : Object {
update.set(db.account.enabled, enabled); break;
case "roster-version":
update.set(db.account.roster_version, roster_version); break;
case "mam-earliest-synced":
update.set(db.account.mam_earliest_synced, (long)mam_earliest_synced.to_unix()); break;
}
update.perform();
}

View File

@ -33,7 +33,7 @@ public class Conversation : Object {
}
}
}
public Encryption encryption { get; set; default = Encryption.NONE; }
public Encryption encryption { get; set; default = Encryption.UNKNOWN; }
public Message? read_up_to { get; set; }
public int read_up_to_item { get; set; default=-1; }

View File

@ -4,6 +4,8 @@ namespace Dino.Entities {
public class FileTransfer : Object {
public signal void sources_changed();
public const bool DIRECTION_SENT = true;
public const bool DIRECTION_RECEIVED = false;
@ -15,6 +17,7 @@ public class FileTransfer : Object {
}
public int id { get; set; default=-1; }
public string? file_sharing_id { get; set; }
public Account account { get; set; }
public Jid counterpart { get; set; }
public Jid ourpart { get; set; }
@ -64,14 +67,52 @@ public class FileTransfer : Object {
}
public string path { get; set; }
public string? mime_type { get; set; }
// TODO(hrxi): expand to 64 bit
public int size { get; set; default=-1; }
public int64 size { get; set; }
public State state { get; set; default=State.NOT_STARTED; }
public int provider { get; set; }
public string info { get; set; }
public Cancellable cancellable { get; default=new Cancellable(); }
// This value is not persisted
public int64 transferred_bytes { get; set; }
public Xep.FileMetadataElement.FileMetadata file_metadata {
owned get {
return new Xep.FileMetadataElement.FileMetadata() {
name = this.file_name,
mime_type = this.mime_type,
size = this.size,
desc = this.desc,
date = this.modification_date,
width = this.width,
height = this.height,
length = this.length,
hashes = this.hashes,
thumbnails = this.thumbnails
};
}
set {
this.file_name = value.name;
this.mime_type = value.mime_type;
this.size = value.size;
this.desc = value.desc;
this.modification_date = value.date;
this.width = value.width;
this.height = value.height;
this.length = value.length;
this.hashes = value.hashes;
this.thumbnails = value.thumbnails;
}
}
public string? desc { get; set; }
public DateTime? modification_date { get; set; }
public int width { get; set; default=-1; }
public int height { get; set; default=-1; }
public int64 length { get; set; default=-1; }
public Gee.List<Xep.CryptographicHashes.Hash> hashes = new Gee.ArrayList<Xep.CryptographicHashes.Hash>();
public Gee.List<Xep.StatelessFileSharing.Source> sfs_sources = new Gee.ArrayList<Xep.StatelessFileSharing.Source>(Xep.StatelessFileSharing.Source.equals_func);
public Gee.List<Xep.JingleContentThumbnails.Thumbnail> thumbnails = new Gee.ArrayList<Xep.JingleContentThumbnails.Thumbnail>();
private Database? db;
private string storage_dir;
@ -80,6 +121,7 @@ public class FileTransfer : Object {
this.storage_dir = storage_dir;
id = row[db.file_transfer.id];
file_sharing_id = row[db.file_transfer.file_sharing_id];
account = db.get_account_by_id(row[db.file_transfer.account_id]); // TODO dont have to generate acc new
counterpart = db.get_jid_by_id(row[db.file_transfer.counterpart_id]);
@ -99,10 +141,37 @@ public class FileTransfer : Object {
file_name = row[db.file_transfer.file_name];
path = row[db.file_transfer.path];
mime_type = row[db.file_transfer.mime_type];
size = row[db.file_transfer.size];
size = (int64) row[db.file_transfer.size];
state = (State) row[db.file_transfer.state];
provider = row[db.file_transfer.provider];
info = row[db.file_transfer.info];
modification_date = new DateTime.from_unix_utc(row[db.file_transfer.modification_date]);
width = row[db.file_transfer.width];
height = row[db.file_transfer.height];
length = (int64) row[db.file_transfer.length];
// TODO put those into the initial query
foreach(var hash_row in db.file_hashes.select().with(db.file_hashes.id, "=", id)) {
Xep.CryptographicHashes.Hash hash = new Xep.CryptographicHashes.Hash();
hash.algo = hash_row[db.file_hashes.algo];
hash.val = hash_row[db.file_hashes.value];
hashes.add(hash);
}
foreach(var thumbnail_row in db.file_thumbnails.select().with(db.file_thumbnails.id, "=", id)) {
Xep.JingleContentThumbnails.Thumbnail thumbnail = new Xep.JingleContentThumbnails.Thumbnail();
thumbnail.data = Xmpp.get_data_for_uri(thumbnail_row[db.file_thumbnails.uri]);
thumbnail.media_type = thumbnail_row[db.file_thumbnails.mime_type];
thumbnail.width = thumbnail_row[db.file_thumbnails.width];
thumbnail.height = thumbnail_row[db.file_thumbnails.height];
thumbnails.add(thumbnail);
}
foreach(Qlite.Row source_row in db.sfs_sources.select().with(db.sfs_sources.file_transfer_id, "=", id)) {
if (source_row[db.sfs_sources.type] == "http") {
sfs_sources.add(new Xep.StatelessFileSharing.HttpSource() { url=source_row[db.sfs_sources.data] });
}
}
notify.connect(on_update);
}
@ -121,26 +190,79 @@ public class FileTransfer : Object {
.value(db.file_transfer.local_time, (long) local_time.to_unix())
.value(db.file_transfer.encryption, encryption)
.value(db.file_transfer.file_name, file_name)
.value(db.file_transfer.size, size)
.value(db.file_transfer.size, (long) size)
.value(db.file_transfer.state, state)
.value(db.file_transfer.provider, provider)
.value(db.file_transfer.info, info);
if (file_name != null) builder.value(db.file_transfer.file_name, file_name);
if (file_sharing_id != null) builder.value(db.file_transfer.file_sharing_id, file_sharing_id);
if (path != null) builder.value(db.file_transfer.path, path);
if (mime_type != null) builder.value(db.file_transfer.mime_type, mime_type);
if (path != null) builder.value(db.file_transfer.path, path);
if (modification_date != null) builder.value(db.file_transfer.modification_date, (long) modification_date.to_unix());
if (width != -1) builder.value(db.file_transfer.width, width);
if (height != -1) builder.value(db.file_transfer.height, height);
if (length != -1) builder.value(db.file_transfer.length, (long) length);
id = (int) builder.perform();
foreach (Xep.CryptographicHashes.Hash hash in hashes) {
db.file_hashes.insert()
.value(db.file_hashes.id, id)
.value(db.file_hashes.algo, hash.algo)
.value(db.file_hashes.value, hash.val)
.perform();
}
foreach (Xep.JingleContentThumbnails.Thumbnail thumbnail in thumbnails) {
string data_uri = "data:image/png;base64," + Base64.encode(thumbnail.data.get_data());
db.file_thumbnails.insert()
.value(db.file_thumbnails.id, id)
.value(db.file_thumbnails.uri, data_uri)
.value(db.file_thumbnails.mime_type, thumbnail.media_type)
.value(db.file_thumbnails.width, thumbnail.width)
.value(db.file_thumbnails.height, thumbnail.height)
.perform();
}
foreach (Xep.StatelessFileSharing.Source source in sfs_sources) {
persist_source(source);
}
notify.connect(on_update);
}
public File get_file() {
public void add_sfs_source(Xep.StatelessFileSharing.Source source) {
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);
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;
if (http_source != null) {
db.sfs_sources.insert()
.value(db.sfs_sources.file_transfer_id, id)
.value(db.sfs_sources.type, "http")
.value(db.sfs_sources.data, http_source.url)
.perform();
}
}
public File? get_file() {
if (path == null) return null;
return File.new_for_path(Path.build_filename(Dino.get_storage_dir(), "files", path));
}
private void on_update(Object o, ParamSpec sp) {
Qlite.UpdateBuilder update_builder = db.file_transfer.update().with(db.file_transfer.id, "=", id);
switch (sp.name) {
case "file-sharing-id":
update_builder.set(db.file_transfer.file_sharing_id, file_sharing_id); break;
case "counterpart":
update_builder.set(db.file_transfer.counterpart_id, db.get_jid_id(counterpart));
update_builder.set(db.file_transfer.counterpart_resource, counterpart.resourcepart); break;
@ -161,7 +283,7 @@ public class FileTransfer : Object {
case "mime-type":
update_builder.set(db.file_transfer.mime_type, mime_type); break;
case "size":
update_builder.set(db.file_transfer.size, size); break;
update_builder.set(db.file_transfer.size, (long) size); break;
case "state":
if (state == State.IN_PROGRESS) return;
update_builder.set(db.file_transfer.state, state); break;
@ -169,6 +291,14 @@ public class FileTransfer : Object {
update_builder.set(db.file_transfer.provider, provider); break;
case "info":
update_builder.set(db.file_transfer.info, info); break;
case "modification-date":
update_builder.set(db.file_transfer.modification_date, (long) modification_date.to_unix()); break;
case "width":
update_builder.set(db.file_transfer.width, width); break;
case "height":
update_builder.set(db.file_transfer.height, height); break;
case "length":
update_builder.set(db.file_transfer.length, (long) length); break;
}
update_builder.perform();
}

View File

@ -67,9 +67,10 @@ public class Message : Object {
}
}
public string? edit_to = null;
public int quoted_item_id = 0;
public int quoted_item_id { get; private set; default=0; }
private Gee.List<Xep.FallbackIndication.Fallback> fallbacks = null;
private Gee.List<Xep.MessageMarkup.Span> markups = null;
private Database? db;
@ -142,30 +143,101 @@ public class Message : Object {
notify.connect(on_update);
}
public void set_quoted_item(int quoted_content_item_id) {
if (id == -1) {
warning("Message needs to be persisted before setting quoted item");
return;
}
this.quoted_item_id = quoted_content_item_id;
db.reply.upsert()
.value(db.reply.message_id, id, true)
.value(db.reply.quoted_content_item_id, quoted_content_item_id)
.value_null(db.reply.quoted_message_stanza_id)
.value_null(db.reply.quoted_message_from)
.perform();
}
public Gee.List<Xep.FallbackIndication.Fallback> get_fallbacks() {
if (fallbacks != null) return fallbacks;
fetch_body_meta();
var fallbacks_by_ns = new HashMap<string, ArrayList<Xep.FallbackIndication.FallbackLocation>>();
foreach (Qlite.Row row in db.body_meta.select().with(db.body_meta.message_id, "=", id)) {
if (row[db.body_meta.info_type] != Xep.FallbackIndication.NS_URI) continue;
return fallbacks;
}
string ns_uri = row[db.body_meta.info];
if (!fallbacks_by_ns.has_key(ns_uri)) {
fallbacks_by_ns[ns_uri] = new ArrayList<Xep.FallbackIndication.FallbackLocation>();
public Gee.List<Xep.MessageMarkup.Span> get_markups() {
if (markups != null) return markups;
fetch_body_meta();
return markups;
}
public void persist_markups(Gee.List<Xep.MessageMarkup.Span> markups, int message_id) {
this.markups = markups;
foreach (var span in markups) {
foreach (var ty in span.types) {
db.body_meta.insert()
.value(db.body_meta.info_type, Xep.MessageMarkup.NS_URI)
.value(db.body_meta.message_id, message_id)
.value(db.body_meta.info, Xep.MessageMarkup.span_type_to_str(ty))
.value(db.body_meta.from_char, span.start_char)
.value(db.body_meta.to_char, span.end_char)
.perform();
}
}
}
private void fetch_body_meta() {
var fallbacks_by_ns = new HashMap<string, ArrayList<Xep.FallbackIndication.FallbackLocation>>();
var markups = new ArrayList<Xep.MessageMarkup.Span>();
foreach (Qlite.Row row in db.body_meta.select().with(db.body_meta.message_id, "=", id)) {
switch (row[db.body_meta.info_type]) {
case Xep.FallbackIndication.NS_URI:
string ns_uri = row[db.body_meta.info];
if (!fallbacks_by_ns.has_key(ns_uri)) {
fallbacks_by_ns[ns_uri] = new ArrayList<Xep.FallbackIndication.FallbackLocation>();
}
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;
case Xep.MessageMarkup.NS_URI:
var types = new ArrayList<Xep.MessageMarkup.SpanType>();
types.add(Xep.MessageMarkup.str_to_span_type(row[db.body_meta.info]));
markups.add(new Xep.MessageMarkup.Span() { types=types, start_char=row[db.body_meta.from_char], end_char=row[db.body_meta.to_char] });
break;
}
fallbacks_by_ns[ns_uri].add(new Xep.FallbackIndication.FallbackLocation(row[db.body_meta.from_char], row[db.body_meta.to_char]));
}
var fallbacks = new ArrayList<Xep.FallbackIndication.Fallback>();
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;
return fallbacks;
this.markups = markups;
}
public void set_fallbacks(Gee.List<Xep.FallbackIndication.Fallback> fallbacks) {
if (id == -1) {
warning("Message needs to be persisted before setting fallbacks");
return;
}
this.fallbacks = fallbacks;
foreach (var fallback in fallbacks) {
foreach (var location in fallback.locations) {
db.body_meta.insert()
.value(db.body_meta.message_id, id)
.value(db.body_meta.info_type, Xep.FallbackIndication.NS_URI)
.value(db.body_meta.info, fallback.ns_uri)
.value(db.body_meta.from_char, location.from_char)
.value(db.body_meta.to_char, location.to_char)
.perform();
}
}
}
public void set_type_string(string type) {
@ -202,6 +274,7 @@ public class Message : Object {
}
public static uint hash_func(Message message) {
if (message.body == null) return 0;
return message.body.hash();
}

View File

@ -79,6 +79,24 @@ public class Settings : Object {
check_spelling_ = value;
}
}
public Encryption get_default_encryption(Account account) {
string? setting = db.account_settings.get_value(account.id, "default-encryption");
if (setting != null) {
return (Encryption) int.parse(setting);
}
return Encryption.OMEMO;
}
public void set_default_encryption(Account account, Encryption encryption) {
db.account_settings.upsert()
.value(db.account_settings.key, "default-encryption", true)
.value(db.account_settings.account_id, account.id, true)
.value(db.account_settings.value, ((int)encryption).to_string())
.perform();
}
}
}

View File

@ -55,10 +55,19 @@ public abstract class AccountSettingsEntry : Object {
public abstract Object? get_widget(WidgetType type);
}
public abstract class EncryptionPreferencesEntry : Object {
public abstract string id { get; }
public virtual Priority priority { get { return Priority.DEFAULT; } }
public abstract Object? get_widget(Account account, WidgetType type);
}
public interface ContactDetailsProvider : Object {
public abstract string id { get; }
public abstract string tab { get; }
public abstract void populate(Conversation conversation, ContactDetails contact_details, WidgetType type);
public abstract Object? get_widget(Conversation conversation);
}
public class ContactDetails : Object {
@ -93,7 +102,7 @@ public abstract interface ConversationAdditionPopulator : ConversationItemPopula
public abstract interface VideoCallPlugin : Object {
public abstract bool supports(string? media);
public abstract bool supported();
// Video widget
public abstract VideoCallWidget? create_widget(WidgetType type);

View File

@ -3,14 +3,15 @@ using Gee;
namespace Dino.Plugins {
public class Registry {
internal HashMap<Entities.Encryption, EncryptionListEntry> encryption_list_entries = new HashMap<Entities.Encryption, EncryptionListEntry>();
internal HashMap<string, CallEncryptionEntry> call_encryption_entries = new HashMap<string, CallEncryptionEntry>();
internal ArrayList<AccountSettingsEntry> account_settings_entries = new ArrayList<AccountSettingsEntry>();
internal ArrayList<ContactDetailsProvider> contact_details_entries = new ArrayList<ContactDetailsProvider>();
internal Map<string, TextCommand> text_commands = new HashMap<string, TextCommand>();
internal Gee.List<ConversationAdditionPopulator> conversation_addition_populators = new ArrayList<ConversationAdditionPopulator>();
internal Gee.List<NotificationPopulator> notification_populators = new ArrayList<NotificationPopulator>();
internal Gee.Collection<ConversationTitlebarEntry> conversation_titlebar_entries = new Gee.TreeSet<ConversationTitlebarEntry>((a, b) => {
public HashMap<Entities.Encryption, EncryptionListEntry> encryption_list_entries = new HashMap<Entities.Encryption, EncryptionListEntry>();
public HashMap<string, CallEncryptionEntry> call_encryption_entries = new HashMap<string, CallEncryptionEntry>();
public ArrayList<AccountSettingsEntry> account_settings_entries = new ArrayList<AccountSettingsEntry>();
public ArrayList<EncryptionPreferencesEntry> encryption_preferences_entries = new ArrayList<EncryptionPreferencesEntry>();
public ArrayList<ContactDetailsProvider> contact_details_entries = new ArrayList<ContactDetailsProvider>();
public Map<string, TextCommand> text_commands = new HashMap<string, TextCommand>();
public Gee.List<ConversationAdditionPopulator> conversation_addition_populators = new ArrayList<ConversationAdditionPopulator>();
public Gee.List<NotificationPopulator> notification_populators = new ArrayList<NotificationPopulator>();
public Gee.Collection<ConversationTitlebarEntry> conversation_titlebar_entries = new Gee.TreeSet<ConversationTitlebarEntry>((a, b) => {
return (int)(a.order - b.order);
});
public VideoCallPlugin? video_call_plugin;
@ -43,6 +44,18 @@ public class Registry {
}
}
public bool register_encryption_preferences_entry(EncryptionPreferencesEntry entry) {
lock(encryption_preferences_entries) {
foreach(var e in encryption_preferences_entries) {
if (e.id == entry.id) return false;
}
encryption_preferences_entries.add(entry);
// TODO: Order by priority
// encryption_preferences_entries.sort((a,b) => b.name.collate(a.name));
return true;
}
}
public bool register_contact_details_entry(ContactDetailsProvider entry) {
lock(contact_details_entries) {
foreach(ContactDetailsProvider e in contact_details_entries) {

View File

@ -12,6 +12,7 @@ public class AvatarManager : StreamInteractionModule, Object {
public string id { get { return IDENTITY.id; } }
public signal void received_avatar(Jid jid, Account account);
public signal void fetched_avatar(Jid jid, Account account);
private enum Source {
USER_AVATARS,
@ -23,8 +24,7 @@ public class AvatarManager : StreamInteractionModule, Object {
private string folder = null;
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<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 const int MAX_PIXEL = 192;
public static void start(StreamInteractor stream_interactor, Database db) {
@ -35,8 +35,39 @@ public class AvatarManager : StreamInteractionModule, Object {
private AvatarManager(StreamInteractor stream_interactor, Database db) {
this.stream_interactor = stream_interactor;
this.db = db;
this.folder = Path.build_filename(Dino.get_storage_dir(), "avatars");
DirUtils.create_with_parents(this.folder, 0700);
File old_avatars = File.new_build_filename(Dino.get_storage_dir(), "avatars");
File new_avatars = File.new_build_filename(Dino.get_cache_dir(), "avatars");
this.folder = new_avatars.get_path();
// Move old avatar location to new one
if (old_avatars.query_exists()) {
if (!new_avatars.query_exists()) {
// Move old avatars folder (~/.local/share/dino) to new location (~/.cache/dino)
try {
new_avatars.get_parent().make_directory_with_parents();
} catch (Error e) { }
try {
old_avatars.move(new_avatars, FileCopyFlags.NONE);
debug("Avatars directory %s moved to %s", old_avatars.get_path(), new_avatars.get_path());
} catch (Error e) { }
} else {
// If both old and new folders exist, remove the old one
try {
FileEnumerator enumerator = old_avatars.enumerate_children("standard::*", FileQueryInfoFlags.NOFOLLOW_SYMLINKS);
FileInfo info = null;
while ((info = enumerator.next_file()) != null) {
FileUtils.remove(old_avatars.get_path() + "/" + info.get_name());
}
DirUtils.remove(old_avatars.get_path());
} catch (Error e) { }
}
}
// Create avatar folder
try {
new_avatars.make_directory_with_parents();
} catch (Error e) { }
stream_interactor.account_added.connect(on_account_added);
stream_interactor.module_manager.initialize_account_modules.connect((_, modules) => {
@ -45,6 +76,18 @@ public class AvatarManager : StreamInteractionModule, Object {
});
}
public File? get_avatar_file(Account account, Jid jid_) {
string? hash = get_avatar_hash(account, jid_);
if (hash == null) return null;
File file = File.new_for_path(Path.build_filename(folder, hash));
if (!file.query_exists()) {
fetch_and_store_for_jid.begin(account, jid_);
return null;
} else {
return file;
}
}
private string? get_avatar_hash(Account account, Jid jid_) {
Jid jid = jid_;
if (!stream_interactor.get_module(MucManager.IDENTITY).is_groupchat_occupant(jid_, account)) {
@ -59,79 +102,10 @@ public class AvatarManager : StreamInteractionModule, Object {
}
}
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) {
return get_avatar_hash(account, jid) != null;
}
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;
}
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 {
Bytes? bytes = null;
if (source == 1) {
bytes = yield Xmpp.Xep.UserAvatars.fetch_image(stream, jid, hash);
} else if (source == 2) {
bytes = yield Xmpp.Xep.VCard.fetch_image(stream, jid, hash);
if (bytes == null && jid.is_bare()) {
db.avatar.delete().with(db.avatar.jid_id, "=", db.get_jid_id(jid)).perform();
}
}
if (bytes != null) {
store_image(hash, bytes);
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) {
try {
Pixbuf pixbuf = new Pixbuf.from_file(file);
@ -153,30 +127,32 @@ public class AvatarManager : StreamInteractionModule, Object {
}
}
public void unset_avatar(Account account) {
XmppStream stream = stream_interactor.get_stream(account);
if (stream == null) return;
Xmpp.Xep.UserAvatars.unset_avatar(stream);
}
private void on_account_added(Account account) {
stream_interactor.module_manager.get_module(account, Xep.UserAvatars.Module.IDENTITY).received_avatar_hash.connect((stream, jid, id) =>
on_user_avatar_received.begin(account, jid, id)
on_user_avatar_received(account, jid, id)
);
stream_interactor.module_manager.get_module(account, Xep.UserAvatars.Module.IDENTITY).avatar_removed.connect((stream, jid) => {
on_user_avatar_removed(account, jid);
});
stream_interactor.module_manager.get_module(account, Xep.VCard.Module.IDENTITY).received_avatar_hash.connect((stream, jid, id) =>
on_vcard_avatar_received.begin(account, jid, id)
on_vcard_avatar_received(account, jid, id)
);
foreach (var entry in get_avatar_hashes(account, Source.USER_AVATARS).entries) {
user_avatars[entry.key] = entry.value;
on_user_avatar_received(account, entry.key, entry.value);
}
foreach (var entry in get_avatar_hashes(account, Source.VCARD).entries) {
// FIXME: remove. temporary to remove falsely saved avatars.
if (stream_interactor.get_module(MucManager.IDENTITY).is_groupchat(entry.key, account)) {
db.avatar.delete().with(db.avatar.jid_id, "=", db.get_jid_id(entry.key)).perform();
continue;
}
vcard_avatars[entry.key] = entry.value;
on_vcard_avatar_received(account, entry.key, entry.value);
}
}
private async void on_user_avatar_received(Account account, Jid jid_, string id) {
private void on_user_avatar_received(Account account, Jid jid_, string id) {
Jid jid = jid_.bare_jid;
if (!user_avatars.has_key(jid) || user_avatars[jid] != id) {
@ -186,7 +162,14 @@ public class AvatarManager : StreamInteractionModule, Object {
received_avatar(jid, account);
}
private async void on_vcard_avatar_received(Account account, Jid jid_, string id) {
private void on_user_avatar_removed(Account account, Jid jid_) {
Jid jid = jid_.bare_jid;
user_avatars.unset(jid);
remove_avatar_hash(account, jid, Source.USER_AVATARS);
received_avatar(jid, account);
}
private void on_vcard_avatar_received(Account account, Jid jid_, string id) {
bool is_gc = stream_interactor.get_module(MucManager.IDENTITY).might_be_groupchat(jid_.bare_jid, account);
Jid jid = is_gc ? jid_ : jid_.bare_jid;
@ -208,6 +191,14 @@ public class AvatarManager : StreamInteractionModule, Object {
.perform();
}
public void remove_avatar_hash(Account account, Jid jid, int type) {
db.avatar.delete()
.with(db.avatar.jid_id, "=", db.get_jid_id(jid))
.with(db.avatar.account_id, "=", account.id)
.with(db.avatar.type_, "=", type)
.perform();
}
public HashMap<Jid, string> get_avatar_hashes(Account account, int type) {
HashMap<Jid, string> ret = new HashMap<Jid, string>(Jid.hash_func, Jid.equals_func);
foreach (Row row in db.avatar.select({db.avatar.jid_id, db.avatar.hash})
@ -218,12 +209,53 @@ public class AvatarManager : StreamInteractionModule, Object {
return ret;
}
public void store_image(string id, Bytes data) {
public async bool fetch_and_store_for_jid(Account account, Jid 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;
} else {
return false;
}
XmppStream? stream = stream_interactor.get_stream(account);
if (stream == null || !stream.negotiation_complete) return false;
return yield fetch_and_store(stream, account, jid, source, hash);
}
private async bool fetch_and_store(XmppStream stream, Account account, Jid jid, int source, string? hash) {
if (hash == null || pending_fetch.contains(hash)) return false;
pending_fetch.add(hash);
Bytes? bytes = null;
if (source == 1) {
bytes = yield Xmpp.Xep.UserAvatars.fetch_image(stream, jid, hash);
} else if (source == 2) {
bytes = yield Xmpp.Xep.VCard.fetch_image(stream, jid, hash);
if (bytes == null && jid.is_bare()) {
db.avatar.delete().with(db.avatar.jid_id, "=", db.get_jid_id(jid)).perform();
}
}
if (bytes != null) {
yield store_image(hash, bytes);
fetched_avatar(jid, account);
}
pending_fetch.remove(hash);
return bytes != null;
}
private async void store_image(string id, Bytes data) {
File file = File.new_for_path(Path.build_filename(folder, id));
try {
if (file.query_exists()) file.delete(); //TODO y?
DataOutputStream fos = new DataOutputStream(file.create(FileCreateFlags.REPLACE_DESTINATION));
fos.write_bytes_async.begin(data);
yield fos.write_bytes_async(data);
} catch (Error e) {
// Ignore: we failed in storing, so we refuse to display later...
}
@ -233,29 +265,6 @@ public class AvatarManager : StreamInteractionModule, Object {
File file = File.new_for_path(Path.build_filename(folder, id));
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;
}
}
}
}

View File

@ -9,7 +9,7 @@ public class Dino.PeerState : Object {
public signal void connection_ready();
public signal void session_terminated(bool we_terminated, string? reason_name, string? reason_text);
public signal void encryption_updated(Xep.Jingle.ContentEncryption? audio_encryption, Xep.Jingle.ContentEncryption? video_encryption, bool same);
public signal void encryption_updated(Xep.Jingle.ContentEncryption? audio_encryption, Xep.Jingle.ContentEncryption? video_encryption);
public StreamInteractor stream_interactor;
public CallState call_state;
@ -412,7 +412,7 @@ public class Dino.PeerState : Object {
if ((audio_encryptions != null && audio_encryptions.is_empty) || (video_encryptions != null && video_encryptions.is_empty)) {
call.encryption = Encryption.NONE;
encryption_updated(null, null, true);
encryption_updated(null, null);
return;
}
@ -462,7 +462,7 @@ public class Dino.PeerState : Object {
encryption_keys_same = true;
}
encryption_updated(audio_encryption, video_encryption, encryption_keys_same);
encryption_updated(audio_encryption, video_encryption);
}
}

View File

@ -18,6 +18,7 @@ public class Dino.CallState : Object {
public bool use_cim = false;
public string? cim_call_id = null;
public Jid? cim_counterpart = null;
public ArrayList<Jid> cim_jids_to_inform = new ArrayList<Jid>();
public string cim_message_type { get; set; default=Xmpp.MessageStanza.TYPE_CHAT; }
public Xep.Muji.GroupCall? group_call { get; set; }
@ -49,7 +50,7 @@ public class Dino.CallState : Object {
}
internal async void initiate_groupchat_call(Jid muc) {
parent_muc = muc;
cim_jids_to_inform.add(muc);
cim_message_type = MessageStanza.TYPE_GROUPCHAT;
if (this.group_call == null) yield convert_into_group_call();
@ -97,29 +98,27 @@ public class Dino.CallState : Object {
accepted = true;
call.state = Call.State.ESTABLISHING;
XmppStream stream = stream_interactor.get_stream(call.account);
if (stream == null) return;
if (use_cim) {
XmppStream stream = stream_interactor.get_stream(call.account);
if (stream == null) return;
StanzaNode? inner_node = null;
if (group_call != null) {
inner_node = new StanzaNode.build("muji", Xep.Muji.NS_URI).add_self_xmlns()
.put_attribute("room", group_call.muc_jid.to_string());
if (invited_to_group_call != null) {
join_group_call.begin(invited_to_group_call);
foreach (Jid jid_to_inform in cim_jids_to_inform) {
stream.get_module(Xep.CallInvites.Module.IDENTITY).send_muji_accept(stream, jid_to_inform, cim_call_id, invited_to_group_call, cim_message_type);
}
} else if (peers.size == 1) {
foreach (PeerState peer in peers.values) {
inner_node = new StanzaNode.build("jingle", Xep.CallInvites.NS_URI)
.put_attribute("sid", peer.sid);
string sid = peers.values.to_array()[0].sid;
foreach (Jid jid_to_inform in cim_jids_to_inform) {
stream.get_module(Xep.CallInvites.Module.IDENTITY).send_jingle_accept(stream, jid_to_inform, cim_call_id, sid, cim_message_type);
}
}
stream.get_module(Xep.CallInvites.Module.IDENTITY).send_accept(stream, cim_counterpart, cim_call_id, inner_node, cim_message_type);
} else {
foreach (PeerState peer in peers.values) {
peer.accept();
}
}
if (invited_to_group_call != null) {
join_group_call.begin(invited_to_group_call);
}
}
public void reject() {
@ -128,7 +127,10 @@ public class Dino.CallState : Object {
if (use_cim) {
XmppStream stream = stream_interactor.get_stream(call.account);
if (stream == null) return;
stream.get_module(Xep.CallInvites.Module.IDENTITY).send_reject(stream, cim_counterpart, cim_call_id, cim_message_type);
foreach (Jid jid_to_inform in cim_jids_to_inform) {
stream.get_module(Xep.CallInvites.Module.IDENTITY).send_reject(stream, jid_to_inform, cim_call_id, cim_message_type);
}
}
var peers_cpy = new ArrayList<PeerState>();
peers_cpy.add_all(peers.values);
@ -142,32 +144,38 @@ public class Dino.CallState : Object {
var peers_cpy = new ArrayList<PeerState>();
peers_cpy.add_all(peers.values);
if (group_call != null) {
XmppStream stream = stream_interactor.get_stream(call.account);
if (stream != null) {
// Terminate sessions, send out messages about the ended call, exit MUC if applicable
XmppStream stream = stream_interactor.get_stream(call.account);
if (stream != null) {
if (group_call != null) {
stream.get_module(Xep.Muc.Module.IDENTITY).exit(stream, group_call.muc_jid);
}
if (call.state == Call.State.IN_PROGRESS || call.state == Call.State.ESTABLISHING) {
foreach (PeerState peer in peers_cpy) {
peer.end(Xep.Jingle.ReasonElement.SUCCESS, reason_text);
}
if (use_cim) {
foreach (Jid jid_to_inform in cim_jids_to_inform) {
stream.get_module(Xep.CallInvites.Module.IDENTITY).send_left(stream, jid_to_inform, cim_call_id, cim_message_type);
}
}
} else if (call.state == Call.State.RINGING) {
foreach (PeerState peer in peers_cpy) {
peer.end(Xep.Jingle.ReasonElement.CANCEL, reason_text);
}
if (call.direction == Call.DIRECTION_OUTGOING && use_cim) {
foreach (Jid jid_to_inform in cim_jids_to_inform) {
stream.get_module(Xep.CallInvites.Module.IDENTITY).send_retract(stream, jid_to_inform, cim_call_id, cim_message_type);
}
}
}
}
// Update the call state
if (call.state == Call.State.IN_PROGRESS || call.state == Call.State.ESTABLISHING) {
foreach (PeerState peer in peers_cpy) {
peer.end(Xep.Jingle.ReasonElement.SUCCESS, reason_text);
}
if (use_cim) {
XmppStream stream = stream_interactor.get_stream(call.account);
if (stream == null) return;
stream.get_module(Xep.CallInvites.Module.IDENTITY).send_finish(stream, cim_counterpart, cim_call_id, cim_message_type);
}
call.state = Call.State.ENDED;
} else if (call.state == Call.State.RINGING) {
foreach (PeerState peer in peers_cpy) {
peer.end(Xep.Jingle.ReasonElement.CANCEL, reason_text);
}
if (call.direction == Call.DIRECTION_OUTGOING && use_cim) {
XmppStream stream = stream_interactor.get_stream(call.account);
if (stream == null) return;
stream.get_module(Xep.CallInvites.Module.IDENTITY).send_retract(stream, cim_counterpart, cim_call_id, cim_message_type);
}
call.state = Call.State.MISSED;
} else {
return;

View File

@ -61,8 +61,6 @@ namespace Dino {
call_state.initiate_groupchat_call.begin(conversation.counterpart);
}
conversation.last_active = call.time;
call_outgoing(call, call_state, conversation);
return call_state;
@ -72,14 +70,14 @@ namespace Dino {
Plugins.VideoCallPlugin? plugin = Application.get_default().plugin_registry.video_call_plugin;
if (plugin == null) return false;
return plugin.supports(null);
return plugin.supported();
}
public async bool can_conversation_do_calls(Conversation conversation) {
if (!can_we_do_calls(conversation.account)) return false;
if (conversation.type_ == Conversation.Type.CHAT) {
return (yield get_call_resources(conversation.account, conversation.counterpart)).size > 0 || has_jmi_resources(conversation.counterpart);
return !conversation.counterpart.equals_bare(conversation.account.bare_jid);
} else {
bool is_private = stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart);
return is_private && can_initiate_groupcall(conversation.account);
@ -221,7 +219,6 @@ namespace Dino {
Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(call.counterpart.bare_jid, account, Conversation.Type.CHAT);
stream_interactor.get_module(CallStore.IDENTITY).add_call(call, conversation);
conversation.last_active = call.time;
var call_state = new CallState(call, stream_interactor);
connect_call_state_signals(call_state);
@ -294,12 +291,12 @@ namespace Dino {
Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(inviter_jid.bare_jid, account);
if (conversation == null) return null;
stream_interactor.get_module(CallStore.IDENTITY).add_call(call, conversation);
conversation.last_active = call.time;
CallState call_state = new CallState(call, stream_interactor);
connect_call_state_signals(call_state);
call_state.invited_to_group_call = muc_jid;
call_state.parent_muc = inviter_jid.bare_jid;
call_state.use_cim = true;
call_state.cim_jids_to_inform.add(inviter_jid.bare_jid);
debug("[%s] on_muji_call_received accepting", account.bare_jid.to_string());
@ -460,12 +457,11 @@ namespace Dino {
call_state.use_cim = true;
call_state.cim_call_id = call_id;
call_state.cim_counterpart = message_stanza.type_ == MessageStanza.TYPE_GROUPCHAT ? from_jid.bare_jid : from_jid;
call_state.cim_jids_to_inform.add(message_stanza.type_ == MessageStanza.TYPE_GROUPCHAT ? from_jid.bare_jid : from_jid);
call_state.cim_message_type = message_stanza.type_;
Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).approx_conversation_for_stanza(from_jid, to_jid, account, message_stanza.type_);
if (conversation == null) return;
conversation.last_active = call_state.call.time;
if (call_state.call.direction == Call.DIRECTION_INCOMING) {
call_incoming(call_state.call, call_state, conversation, video_requested, multiparty);

View File

@ -114,7 +114,7 @@ public class ConnectionManager : Object {
Timeout.add_seconds(60, () => {
foreach (Account account in connections.keys) {
if (connections[account].last_activity != null &&
if (connections[account].last_activity == null ||
connections[account].last_activity.compare(new DateTime.now_utc().add_minutes(-1)) < 0) {
check_reconnect(account);
}
@ -179,13 +179,12 @@ public class ConnectionManager : Object {
}
}
private async void connect_stream(Account account, string? resource = null) {
private async void connect_stream(Account account) {
if (!connections.has_key(account)) return;
debug("[%s] (Maybe) Establishing a new connection", account.bare_jid.to_string());
connection_errors.unset(account);
if (resource == null) resource = account.resourcepart;
XmppStreamResult stream_result;
@ -201,7 +200,7 @@ public class ConnectionManager : Object {
connection_directly_retry[account] = false;
change_connection_state(account, ConnectionState.CONNECTING);
stream_result = yield Xmpp.establish_stream(account.bare_jid, module_manager.get_modules(account, resource), log_options,
stream_result = yield Xmpp.establish_stream(account.bare_jid, module_manager.get_modules(account), log_options,
(peer_cert, errors) => { return on_invalid_certificate(account.domainpart, peer_cert, errors); }
);
connections[account].stream = stream_result.stream;
@ -226,7 +225,7 @@ public class ConnectionManager : Object {
XmppStream stream = stream_result.stream;
debug("[%s] New connection with resource %s: %p", account.bare_jid.to_string(), resource, stream);
debug("[%s] New connection: %p", account.full_jid.to_string(), stream);
connections[account].established = new DateTime.now_utc();
stream.attached_modules.connect((stream) => {
@ -255,6 +254,7 @@ public class ConnectionManager : Object {
debug("[%s %p] Connection error: %s", account.bare_jid.to_string(), stream, e.message);
change_connection_state(account, ConnectionState.DISCONNECTED);
if (!connections.has_key(account)) return;
connections[account].reset();
StreamError.Flag? flag = stream.get_flag(StreamError.Flag.IDENTITY);
@ -263,7 +263,8 @@ public class ConnectionManager : Object {
set_connection_error(account, new ConnectionError(ConnectionError.Source.STREAM_ERROR, flag.error_type));
if (flag.resource_rejected) {
connect_stream.begin(account, account.resourcepart + "-" + random_uuid());
account.set_random_resource();
connect_stream.begin(account);
return;
}
}

View File

@ -0,0 +1,58 @@
using Xmpp;
using Gee;
using Qlite;
using Dino.Entities;
public class Dino.Model.ConversationDisplayName : Object {
public string display_name { get; set; }
}
namespace Dino {
public class ContactModels : StreamInteractionModule, Object {
public static ModuleIdentity<ContactModels> IDENTITY = new ModuleIdentity<ContactModels>("contact_models");
public string id { get { return IDENTITY.id; } }
private StreamInteractor stream_interactor;
private HashMap<Conversation, Model.ConversationDisplayName> conversation_models = new HashMap<Conversation, Model.ConversationDisplayName>(Conversation.hash_func, Conversation.equals_func);
public static void start(StreamInteractor stream_interactor) {
ContactModels m = new ContactModels(stream_interactor);
stream_interactor.add_module(m);
}
private ContactModels(StreamInteractor stream_interactor) {
this.stream_interactor = stream_interactor;
stream_interactor.get_module(MucManager.IDENTITY).room_info_updated.connect((account, jid) => {
check_update_models(account, jid, Conversation.Type.GROUPCHAT);
});
stream_interactor.get_module(MucManager.IDENTITY).private_room_occupant_updated.connect((account, room, occupant) => {
check_update_models(account, room, Conversation.Type.GROUPCHAT);
});
stream_interactor.get_module(MucManager.IDENTITY).subject_set.connect((account, jid, subject) => {
check_update_models(account, jid, Conversation.Type.GROUPCHAT);
});
stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect((account, jid, roster_item) => {
check_update_models(account, jid, Conversation.Type.CHAT);
});
}
private void check_update_models(Account account, Jid jid, Conversation.Type conversation_ty) {
var conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid, account, conversation_ty);
if (conversation == null) return;
var display_name_model = conversation_models[conversation];
if (display_name_model == null) return;
display_name_model.display_name = Dino.get_conversation_display_name(stream_interactor, conversation, "%s (%s)");
}
public Model.ConversationDisplayName get_display_name_model(Conversation conversation) {
if (conversation_models.has_key(conversation)) return conversation_models[conversation];
var model = new Model.ConversationDisplayName();
model.display_name = Dino.get_conversation_display_name(stream_interactor, conversation, "%s (%s)");
conversation_models[conversation] = model;
return model;
}
}
}

View File

@ -121,13 +121,7 @@ public class ContentItemStore : StreamInteractionModule, Object {
Message? message = get_message_for_content_item(conversation, content_item);
if (message == null) return null;
if (message.edit_to != null) return message.edit_to;
if (conversation.type_ == Conversation.Type.CHAT) {
return message.stanza_id;
} else {
return message.server_id;
}
return MessageStorage.get_reference_id(message);
}
public Jid? get_message_sender_for_content_item(Conversation conversation, ContentItem content_item) {

View File

@ -29,6 +29,8 @@ public class ConversationManager : StreamInteractionModule, Object {
stream_interactor.account_removed.connect(on_account_removed);
stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(new MessageListener(stream_interactor));
stream_interactor.get_module(MessageProcessor.IDENTITY).message_sent.connect(handle_sent_message);
stream_interactor.get_module(Calls.IDENTITY).call_incoming.connect(handle_new_call);
stream_interactor.get_module(Calls.IDENTITY).call_outgoing.connect(handle_new_call);
}
public Conversation create_conversation(Jid jid, Account account, Conversation.Type? type = null) {
@ -46,18 +48,28 @@ public class ConversationManager : StreamInteractionModule, Object {
// Create a new converation
Conversation conversation = new Conversation(jid, account, type);
// Set encryption for conversation
if (type == Conversation.Type.CHAT ||
(type == Conversation.Type.GROUPCHAT && stream_interactor.get_module(MucManager.IDENTITY).is_private_room(account, jid))) {
conversation.encryption = Application.get_default().settings.get_default_encryption(account);
} else {
conversation.encryption = Encryption.NONE;
}
add_conversation(conversation);
conversation.persist(db);
return conversation;
}
public Conversation? get_conversation_for_message(Entities.Message message) {
if (message.type_ == Entities.Message.Type.CHAT) {
return create_conversation(message.counterpart.bare_jid, message.account, Conversation.Type.CHAT);
} else if (message.type_ == Entities.Message.Type.GROUPCHAT) {
return create_conversation(message.counterpart.bare_jid, message.account, Conversation.Type.GROUPCHAT);
} else if (message.type_ == Entities.Message.Type.GROUPCHAT_PM) {
return create_conversation(message.counterpart, message.account, Conversation.Type.GROUPCHAT_PM);
if (conversations.has_key(message.account)) {
if (message.type_ == Entities.Message.Type.CHAT) {
return create_conversation(message.counterpart.bare_jid, message.account, Conversation.Type.CHAT);
} else if (message.type_ == Entities.Message.Type.GROUPCHAT) {
return create_conversation(message.counterpart.bare_jid, message.account, Conversation.Type.GROUPCHAT);
} else if (message.type_ == Entities.Message.Type.GROUPCHAT_PM) {
return create_conversation(message.counterpart, message.account, Conversation.Type.GROUPCHAT_PM);
}
}
return null;
}
@ -194,6 +206,11 @@ public class ConversationManager : StreamInteractionModule, Object {
}
}
private void handle_new_call(Call call, CallState state, Conversation conversation) {
conversation.last_active = call.time;
start_conversation(conversation);
}
private void add_conversation(Conversation conversation) {
if (!conversations[conversation.account].has_key(conversation.counterpart)) {
conversations[conversation.account][conversation.counterpart] = new ArrayList<Conversation>(Conversation.equals_func);

View File

@ -7,7 +7,7 @@ using Dino.Entities;
namespace Dino {
public class Database : Qlite.Database {
private const int VERSION = 26;
private const int VERSION = 29;
public class AccountTable : Table {
public Column<int> id = new Column.Integer("id") { primary_key = true, auto_increment = true };
@ -17,6 +17,7 @@ public class Database : Qlite.Database {
public Column<string> alias = new Column.Text("alias");
public Column<bool> enabled = new Column.BoolInt("enabled");
public Column<string> roster_version = new Column.Text("roster_version") { min_version=2 };
// no longer used. all usages already removed. remove db column at some point.
public Column<long> mam_earliest_synced = new Column.Long("mam_earliest_synced") { min_version=4 };
internal AccountTable(Database db) {
@ -179,6 +180,7 @@ public class Database : Qlite.Database {
public class FileTransferTable : Table {
public Column<int> id = new Column.Integer("id") { primary_key = true, auto_increment = true };
public Column<string> file_sharing_id = new Column.Text("file_sharing_id") { min_version=28 };
public Column<int> account_id = new Column.Integer("account_id") { not_null = true };
public Column<int> counterpart_id = new Column.Integer("counterpart_id") { not_null = true };
public Column<string> counterpart_resource = new Column.Text("counterpart_resource");
@ -190,15 +192,58 @@ public class Database : Qlite.Database {
public Column<string> file_name = new Column.Text("file_name");
public Column<string> path = new Column.Text("path");
public Column<string> mime_type = new Column.Text("mime_type");
public Column<int> size = new Column.Integer("size");
public Column<long> size = new Column.Long("size");
public Column<int> state = new Column.Integer("state");
public Column<int> provider = new Column.Integer("provider");
public Column<string> info = new Column.Text("info");
public Column<long> modification_date = new Column.Long("modification_date") { default = "-1", min_version=28 };
public Column<int> width = new Column.Integer("width") { default = "-1", min_version=28 };
public Column<int> height = new Column.Integer("height") { default = "-1", min_version=28 };
public Column<long> length = new Column.Integer("length") { default = "-1", min_version=28 };
internal FileTransferTable(Database db) {
base(db, "file_transfer");
init({id, account_id, counterpart_id, counterpart_resource, our_resource, direction, time, local_time,
encryption, file_name, path, mime_type, size, state, provider, info});
init({id, file_sharing_id, account_id, counterpart_id, counterpart_resource, our_resource, direction,
time, local_time, encryption, file_name, path, mime_type, size, state, provider, info, modification_date,
width, height, length});
}
}
public class FileHashesTable : Table {
public Column<int> id = new Column.Integer("id");
public Column<string> algo = new Column.Text("algo") { not_null = true };
public Column<string> value = new Column.Text("value") { not_null = true };
internal FileHashesTable(Database db) {
base(db, "file_hashes");
init({id, algo, value});
unique({id, algo}, "REPLACE");
}
}
public class FileThumbnailsTable : Table {
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> mime_type = new Column.Text("mime_type");
public Column<int> width = new Column.Integer("width");
public Column<int> height = new Column.Integer("height");
internal FileThumbnailsTable(Database db) {
base(db, "file_thumbnails");
init({id, uri, mime_type, width, height});
}
}
public class SourcesTable : Table {
public Column<int> file_transfer_id = new Column.Integer("file_transfer_id");
public Column<string> type = new Column.Text("type") { not_null = true };
public Column<string> data = new Column.Text("data") { not_null = true };
internal SourcesTable(Database db) {
base(db, "sfs_sources");
init({file_transfer_id, type, data});
index("sfs_sources_file_transfer_id_idx", {file_transfer_id});
}
}
@ -301,10 +346,11 @@ public class Database : Qlite.Database {
public Column<string> jid = new Column.Text("jid");
public Column<string> handle = new Column.Text("name");
public Column<string> subscription = new Column.Text("subscription");
public Column<string> ask = new Column.Text("ask") { min_version=29 };
internal RosterTable(Database db) {
base(db, "roster");
init({account_id, jid, handle, subscription});
init({account_id, jid, handle, subscription, ask});
unique({account_id, jid}, "IGNORE");
}
}
@ -353,6 +399,29 @@ public class Database : Qlite.Database {
}
}
public class AccountSettingsTable : Table {
public Column<int> id = new Column.Integer("id") { primary_key = true, auto_increment = true };
public Column<int> account_id = new Column.Integer("account_id") { not_null = true };
public Column<string> key = new Column.Text("key") { not_null = true };
public Column<string> value = new Column.Text("value");
internal AccountSettingsTable(Database db) {
base(db, "account_settings");
init({id, account_id, key, value});
unique({account_id, key}, "REPLACE");
}
public string? get_value(int account_id, string key) {
var row_opt = select({value})
.with(this.account_id, "=", account_id)
.with(this.key, "=", key)
.single()
.row();
if (row_opt.is_present()) return row_opt[value];
return null;
}
}
public class ConversationSettingsTable : Table {
public Column<int> id = new Column.Integer("id") { primary_key = true, auto_increment = true };
public Column<int> conversation_id = new Column.Integer("conversation_id") {not_null=true};
@ -377,6 +446,9 @@ public class Database : Qlite.Database {
public RealJidTable real_jid { get; private set; }
public OccupantIdTable occupantid { get; private set; }
public FileTransferTable file_transfer { get; private set; }
public FileHashesTable file_hashes { get; private set; }
public FileThumbnailsTable file_thumbnails { get; private set; }
public SourcesTable sfs_sources { get; private set; }
public CallTable call { get; private set; }
public CallCounterpartTable call_counterpart { get; private set; }
public ConversationTable conversation { get; private set; }
@ -387,6 +459,7 @@ public class Database : Qlite.Database {
public MamCatchupTable mam_catchup { get; private set; }
public ReactionTable reaction { get; private set; }
public SettingsTable settings { get; private set; }
public AccountSettingsTable account_settings { get; private set; }
public ConversationSettingsTable conversation_settings { get; private set; }
public Map<int, Jid> jid_table_cache = new HashMap<int, Jid>();
@ -406,6 +479,9 @@ public class Database : Qlite.Database {
occupantid = new OccupantIdTable(this);
real_jid = new RealJidTable(this);
file_transfer = new FileTransferTable(this);
file_hashes = new FileHashesTable(this);
file_thumbnails = new FileThumbnailsTable(this);
sfs_sources = new SourcesTable(this);
call = new CallTable(this);
call_counterpart = new CallCounterpartTable(this);
conversation = new ConversationTable(this);
@ -416,8 +492,9 @@ public class Database : Qlite.Database {
mam_catchup = new MamCatchupTable(this);
reaction = new ReactionTable(this);
settings = new SettingsTable(this);
account_settings = new AccountSettingsTable(this);
conversation_settings = new ConversationSettingsTable(this);
init({ account, jid, entity, content_item, message, body_meta, message_correction, reply, real_jid, occupantid, file_transfer, call, call_counterpart, conversation, avatar, entity_identity, entity_feature, roster, mam_catchup, reaction, settings, conversation_settings });
init({ account, jid, entity, content_item, message, body_meta, message_correction, reply, real_jid, occupantid, file_transfer, file_hashes, file_thumbnails, sfs_sources, call, call_counterpart, conversation, avatar, entity_identity, entity_feature, roster, mam_catchup, reaction, settings, account_settings, conversation_settings });
try {
exec("PRAGMA journal_mode = WAL");
@ -575,6 +652,9 @@ public class Database : Qlite.Database {
foreach(Row row in account.select()) {
try {
Account account = new Account.from_row(this, row);
if (account_table_cache.has_key(account.id)) {
account = account_table_cache[account.id];
}
ret.add(account);
account_table_cache[account.id] = account;
} catch (InvalidJidError e) {

View File

@ -90,6 +90,20 @@ public class EntityInfo : StreamInteractionModule, Object {
return info_result.features.contains(feature);
}
public bool has_feature_offline(Account account, Jid jid, string feature) {
int ret = has_feature_cached_int(account, jid, feature);
if (ret == -1) {
return db.entity.select()
.with(db.entity.account_id, "=", account.id)
.with(db.entity.jid_id, "=", db.get_jid_id(jid))
.with(db.entity.resource, "=", jid.resourcepart ?? "")
.join_with(db.entity_feature, db.entity.caps_hash, db.entity_feature.entity)
.with(db.entity_feature.feature, "=", feature)
.count() > 0;
}
return ret == 1;
}
public bool has_feature_cached(Account account, Jid jid, string feature) {
return has_feature_cached_int(account, jid, feature) == 1;
}
@ -203,13 +217,24 @@ public class EntityInfo : StreamInteractionModule, Object {
ServiceDiscovery.InfoResult? info_result = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).request_info(stream, jid);
if (info_result == null) return null;
if (hash != null && EntityCapabilities.Module.compute_hash_for_info_result(info_result) == hash) {
store_features(hash, info_result.features);
store_identities(hash, info_result.identities);
var computed_hash = EntityCapabilities.Module.compute_hash_for_info_result(info_result);
if (hash == null || computed_hash == hash) {
db.entity.upsert()
.value(db.entity.account_id, account.id, true)
.value(db.entity.jid_id, db.get_jid_id(jid), true)
.value(db.entity.resource, jid.resourcepart ?? "", true)
.value(db.entity.last_seen, (long)(new DateTime.now_local()).to_unix())
.value(db.entity.caps_hash, computed_hash)
.perform();
store_features(computed_hash, info_result.features);
store_identities(computed_hash, info_result.identities);
} else {
jid_features[jid] = info_result.features;
jid_identity[jid] = info_result.identities;
warning("Claimed entity caps hash from %s doesn't match computed one", jid.to_string());
}
jid_features[jid] = info_result.features;
jid_identity[jid] = info_result.identities;
return info_result;
}

View File

@ -46,20 +46,9 @@ public class Dino.FallbackBody : StreamInteractionModule, Object {
if (fallbacks.is_empty) return false;
foreach (var fallback in fallbacks) {
if (fallback.ns_uri != Xep.Replies.NS_URI) continue;
foreach (var location in fallback.locations) {
db.body_meta.insert()
.value(db.body_meta.message_id, message.id)
.value(db.body_meta.info_type, Xep.FallbackIndication.NS_URI)
.value(db.body_meta.info, fallback.ns_uri)
.value(db.body_meta.from_char, location.from_char)
.value(db.body_meta.to_char, location.to_char)
.perform();
}
message.set_fallbacks(fallbacks);
if (fallback.ns_uri != Xep.Replies.NS_URI) continue; // TODO what if it's not
}
message.set_fallbacks(fallbacks);
return false;
}

View File

@ -2,6 +2,7 @@ using Gdk;
using Gee;
using Xmpp;
using Xmpp.Xep;
using Dino.Entities;
namespace Dino {
@ -19,6 +20,12 @@ public class FileManager : StreamInteractionModule, Object {
private Gee.List<FileEncryptor> file_encryptors = new ArrayList<FileEncryptor>();
private Gee.List<FileDecryptor> file_decryptors = new ArrayList<FileDecryptor>();
private Gee.List<FileProvider> file_providers = new ArrayList<FileProvider>();
private Gee.List<FileMetadataProvider> file_metadata_providers = new ArrayList<FileMetadataProvider>();
public StatelessFileSharing sfs {
owned get { return stream_interactor.get_module(StatelessFileSharing.IDENTITY); }
private set { }
}
public static void start(StreamInteractor stream_interactor, Database db) {
FileManager m = new FileManager(stream_interactor, db);
@ -36,6 +43,24 @@ public class FileManager : StreamInteractionModule, Object {
this.add_provider(new JingleFileProvider(stream_interactor));
this.add_sender(new JingleFileSender(stream_interactor));
this.add_metadata_provider(new GenericFileMetadataProvider());
this.add_metadata_provider(new ImageFileMetadataProvider());
}
public const int HTTP_PROVIDER_ID = 0;
public const int SFS_PROVIDER_ID = 2;
public FileProvider? select_file_provider(FileTransfer file_transfer) {
bool http_usable = file_transfer.provider == SFS_PROVIDER_ID;
foreach (FileProvider file_provider in this.file_providers) {
if (file_transfer.provider == file_provider.get_id()) {
return file_provider;
}
if (http_usable && file_provider.get_id() == HTTP_PROVIDER_ID) {
return file_provider;
}
}
return null;
}
public async HashMap<int, long> get_file_size_limits(Conversation conversation) {
@ -60,11 +85,15 @@ public class FileManager : StreamInteractionModule, Object {
file_transfer.local_time = new DateTime.now_utc();
file_transfer.encryption = conversation.encryption;
Xep.FileMetadataElement.FileMetadata metadata = new Xep.FileMetadataElement.FileMetadata();
foreach (FileMetadataProvider file_metadata_provider in this.file_metadata_providers) {
if (file_metadata_provider.supports_file(file)) {
yield file_metadata_provider.fill_metadata(file, metadata);
}
}
file_transfer.file_metadata = metadata;
try {
FileInfo file_info = file.query_info("*", FileQueryInfoFlags.NONE);
file_transfer.file_name = file_info.get_display_name();
file_transfer.mime_type = file_info.get_content_type();
file_transfer.size = (int)file_info.get_size();
file_transfer.input_stream = yield file.read_async();
yield save_file(file_transfer);
@ -119,7 +148,20 @@ public class FileManager : StreamInteractionModule, Object {
file_send_data = file_encryptor.preprocess_send_file(conversation, file_transfer, file_send_data, file_meta);
}
file_transfer.state = FileTransfer.State.IN_PROGRESS;
// Update current download progress in the FileTransfer
LimitInputStream? limit_stream = file_transfer.input_stream as LimitInputStream;
if (limit_stream == null) {
limit_stream = new LimitInputStream(file_transfer.input_stream, file_meta.size);
file_transfer.input_stream = limit_stream;
}
if (limit_stream != null) {
limit_stream.bind_property("retrieved-bytes", file_transfer, "transferred-bytes", BindingFlags.SYNC_CREATE);
}
yield file_sender.send_file(conversation, file_transfer, file_send_data, file_meta);
file_transfer.state = FileTransfer.State.COMPLETE;
} catch (Error e) {
warning("Send file error: %s", e.message);
@ -130,12 +172,7 @@ public class FileManager : StreamInteractionModule, Object {
public async void download_file(FileTransfer file_transfer) {
Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(file_transfer.counterpart.bare_jid, file_transfer.account);
FileProvider? file_provider = null;
foreach (FileProvider fp in file_providers) {
if (file_transfer.provider == fp.get_id()) {
file_provider = fp;
}
}
FileProvider? file_provider = this.select_file_provider(file_transfer);
yield download_file_internal(file_provider, file_transfer, conversation);
}
@ -174,6 +211,10 @@ public class FileManager : StreamInteractionModule, Object {
file_decryptors.add(decryptor);
}
public void add_metadata_provider(FileMetadataProvider file_metadata_provider) {
file_metadata_providers.add(file_metadata_provider);
}
public bool is_sender_trustworthy(FileTransfer file_transfer, Conversation conversation) {
if (file_transfer.direction == FileTransfer.DIRECTION_SENT) return true;
@ -211,7 +252,11 @@ public class FileManager : StreamInteractionModule, Object {
private async void download_file_internal(FileProvider file_provider, FileTransfer file_transfer, Conversation conversation) {
try {
// Get meta info
FileReceiveData receive_data = file_provider.get_file_receive_data(file_transfer);
FileReceiveData? receive_data = file_provider.get_file_receive_data(file_transfer);
if (receive_data == null) {
warning("Don't have download data (yet)");
return;
}
FileDecryptor? file_decryptor = null;
foreach (FileDecryptor decryptor in file_decryptors) {
if (decryptor.can_decrypt_file(conversation, file_transfer, receive_data)) {
@ -226,9 +271,6 @@ public class FileManager : StreamInteractionModule, Object {
FileMeta file_meta = yield get_file_meta(file_provider, file_transfer, conversation, receive_data);
InputStream? input_stream = null;
// Download and decrypt file
file_transfer.state = FileTransfer.State.IN_PROGRESS;
@ -236,15 +278,23 @@ public class FileManager : StreamInteractionModule, Object {
file_meta = file_decryptor.prepare_download_file(conversation, file_transfer, receive_data, file_meta);
}
input_stream = yield file_provider.download(file_transfer, receive_data, file_meta);
InputStream download_input_stream = yield file_provider.download(file_transfer, receive_data, file_meta);
InputStream input_stream = download_input_stream;
if (file_decryptor != null) {
input_stream = yield file_decryptor.decrypt_file(input_stream, conversation, file_transfer, receive_data);
}
// Update current download progress in the FileTransfer
LimitInputStream? limit_stream = download_input_stream as LimitInputStream;
if (limit_stream != null) {
limit_stream.bind_property("retrieved-bytes", file_transfer, "transferred-bytes", BindingFlags.SYNC_CREATE);
}
// Save file
string filename = Random.next_int().to_string("%x") + "_" + file_transfer.file_name;
File file = File.new_for_path(Path.build_filename(get_storage_dir(), filename));
// libsoup doesn't properly support splicing
OutputStream os = file.create(FileCreateFlags.REPLACE_DESTINATION);
uint8[] buffer = new uint8[1024];
ssize_t read;
@ -255,20 +305,49 @@ public class FileManager : StreamInteractionModule, Object {
}
yield input_stream.close_async(Priority.LOW, file_transfer.cancellable);
yield os.close_async(Priority.LOW, file_transfer.cancellable);
// Verify the hash of the downloaded file, if it is known
var supported_hashes = Xep.CryptographicHashes.get_supported_hashes(file_transfer.hashes);
if (!supported_hashes.is_empty) {
var checksum_types = new ArrayList<ChecksumType>();
var hashes = new HashMap<ChecksumType, string>();
foreach (var hash in supported_hashes) {
var checksum_type = Xep.CryptographicHashes.hash_string_to_type(hash.algo);
checksum_types.add(checksum_type);
hashes[checksum_type] = hash.val;
}
var computed_hashes = yield compute_file_hashes(file, checksum_types);
foreach (var checksum_type in hashes.keys) {
if (hashes[checksum_type] != computed_hashes[checksum_type]) {
warning("Hash of downloaded file does not equal advertised hash, discarding: %s. %s should be %s, was %s",
file_transfer.file_name, checksum_type.to_string(), hashes[checksum_type], computed_hashes[checksum_type]);
FileUtils.remove(file.get_path());
file_transfer.state = FileTransfer.State.FAILED;
return;
}
}
}
file_transfer.path = file.get_basename();
file_transfer.input_stream = yield file.read_async();
FileInfo file_info = file_transfer.get_file().query_info("*", FileQueryInfoFlags.NONE);
file_transfer.mime_type = file_info.get_content_type();
file_transfer.state = FileTransfer.State.COMPLETE;
} catch (IOError.CANCELLED e) {
print("cancelled\n");
} catch (Error e) {
warning("Error downloading file: %s", e.message);
file_transfer.state = FileTransfer.State.FAILED;
if (file_transfer.provider == 0 || file_transfer.provider == FileManager.SFS_PROVIDER_ID) {
file_transfer.state = FileTransfer.State.NOT_STARTED;
} else {
file_transfer.state = FileTransfer.State.FAILED;
}
}
}
private async void handle_incoming_file(FileProvider file_provider, string info, Jid from, DateTime time, DateTime local_time, Conversation conversation, FileReceiveData receive_data, FileMeta file_meta) {
public FileTransfer create_file_transfer_from_provider_incoming(FileProvider file_provider, string info, Jid from, DateTime time, DateTime local_time, Conversation conversation, FileReceiveData receive_data, FileMeta file_meta) {
FileTransfer file_transfer = new FileTransfer();
file_transfer.account = conversation.account;
file_transfer.counterpart = file_transfer.direction == FileTransfer.DIRECTION_RECEIVED ? from : conversation.counterpart;
@ -276,8 +355,13 @@ public class FileManager : StreamInteractionModule, Object {
file_transfer.ourpart = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account) ?? conversation.account.bare_jid;
file_transfer.direction = from.equals(file_transfer.ourpart) ? FileTransfer.DIRECTION_SENT : FileTransfer.DIRECTION_RECEIVED;
} else {
file_transfer.ourpart = conversation.account.full_jid;
file_transfer.direction = from.equals_bare(file_transfer.ourpart) ? FileTransfer.DIRECTION_SENT : FileTransfer.DIRECTION_RECEIVED;
if (from.equals_bare(conversation.account.bare_jid)) {
file_transfer.ourpart = from;
file_transfer.direction = FileTransfer.DIRECTION_SENT;
} else {
file_transfer.ourpart = conversation.account.full_jid;
file_transfer.direction = FileTransfer.DIRECTION_RECEIVED;
}
}
file_transfer.time = time;
file_transfer.local_time = local_time;
@ -295,6 +379,11 @@ public class FileManager : StreamInteractionModule, Object {
}
}
return file_transfer;
}
private async void handle_incoming_file(FileProvider file_provider, string info, Jid from, DateTime time, DateTime local_time, Conversation conversation, FileReceiveData receive_data, FileMeta file_meta) {
FileTransfer file_transfer = create_file_transfer_from_provider_incoming(file_provider, info, from, time, local_time, conversation, receive_data, file_meta);
stream_interactor.get_module(FileTransferStorage.IDENTITY).add_file(file_transfer);
if (is_sender_trustworthy(file_transfer, conversation)) {
@ -320,10 +409,10 @@ public class FileManager : StreamInteractionModule, Object {
string filename = Random.next_int().to_string("%x") + "_" + file_transfer.file_name;
File file = File.new_for_path(Path.build_filename(get_storage_dir(), filename));
OutputStream os = file.create(FileCreateFlags.REPLACE_DESTINATION);
yield os.splice_async(file_transfer.input_stream, OutputStreamSpliceFlags.CLOSE_SOURCE|OutputStreamSpliceFlags.CLOSE_TARGET);
yield os.splice_async(file_transfer.input_stream, OutputStreamSpliceFlags.CLOSE_SOURCE | OutputStreamSpliceFlags.CLOSE_TARGET);
file_transfer.state = FileTransfer.State.COMPLETE;
file_transfer.path = filename;
file_transfer.input_stream = yield file.read_async();
file_transfer.input_stream = new LimitInputStream(yield file.read_async(), file_transfer.size);
} catch (Error e) {
throw new FileSendError.SAVE_FAILED("Saving file error: %s".printf(e.message));
}
@ -336,10 +425,10 @@ public errordomain FileSendError {
SAVE_FAILED
}
// Get rid of this Error and pass IoErrors instead - DOWNLOAD_FAILED already removed
public errordomain FileReceiveError {
GET_METADATA_FAILED,
DECRYPTION_FAILED,
DOWNLOAD_FAILED
DECRYPTION_FAILED
}
public class FileMeta {
@ -377,7 +466,7 @@ public interface FileProvider : Object {
public abstract FileReceiveData? get_file_receive_data(FileTransfer file_transfer);
public abstract async FileMeta get_meta_info(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws FileReceiveError;
public abstract async InputStream download(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws FileReceiveError;
public abstract async InputStream download(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws IOError;
public abstract int get_id();
}

View File

@ -14,6 +14,8 @@ namespace Dino {
private Database db;
private WeakMap<int, FileTransfer> files_by_db_id = new WeakMap<int, FileTransfer>();
private WeakMap<int, FileTransfer> files_by_message_id = new WeakMap<int, FileTransfer>();
private WeakMap<string, FileTransfer> files_by_message_and_file_id = new WeakMap<string, FileTransfer>();
public static void start(StreamInteractor stream_interactor, Database db) {
FileTransferStorage m = new FileTransferStorage(stream_interactor, db);
@ -41,6 +43,42 @@ namespace Dino {
return create_file_from_row_opt(row_option, conversation);
}
// Http file transfers store the corresponding message id in the `info` field
public FileTransfer? get_file_by_message_id(int id, Conversation conversation) {
FileTransfer? file_transfer = files_by_message_id[id];
if (file_transfer != null) {
return file_transfer;
}
RowOption row_option = db.file_transfer.select()
.with(db.file_transfer.info, "=", id.to_string())
.single()
.row();
return create_file_from_row_opt(row_option, conversation);
}
public FileTransfer get_files_by_message_and_file_id(int message_id, string file_sharing_id, Conversation conversation) {
string combined_identifier = message_id.to_string() + file_sharing_id;
FileTransfer? file_transfer = files_by_message_and_file_id[combined_identifier];
if (file_transfer == null) {
RowOption row_option = db.file_transfer.select()
.with(db.file_transfer.info, "=", message_id.to_string())
.with(db.file_transfer.file_sharing_id, "=", file_sharing_id)
.single()
.row();
file_transfer = create_file_from_row_opt(row_option, conversation);
}
// There can be collisions in the combined identifier, check it's the correct FileTransfer
if (file_transfer != null && file_transfer.info == message_id.to_string() && file_transfer.file_sharing_id == file_sharing_id) {
return file_transfer;
}
return null;
}
private FileTransfer? create_file_from_row_opt(RowOption row_opt, Conversation conversation) {
if (!row_opt.is_present()) return null;
@ -61,6 +99,15 @@ namespace Dino {
private void cache_file(FileTransfer file_transfer) {
files_by_db_id[file_transfer.id] = file_transfer;
if (file_transfer.info != null && file_transfer.info != "") {
files_by_message_id[int.parse(file_transfer.info)] = file_transfer;
if (file_transfer.file_sharing_id != null && file_transfer.info != null) {
string combined_identifier = file_transfer.info + file_transfer.file_sharing_id;
files_by_message_and_file_id[combined_identifier] = file_transfer;
}
}
}
}
}

View File

@ -388,9 +388,6 @@ public class Dino.HistorySync {
page_result = PageResult.NoMoreMessages;
}
string selection = null;
string[] selection_args = {};
string query_id = query_params.query_id;
string? after_id = query_params.start_id;

View File

@ -95,19 +95,15 @@ public class JingleFileProvider : FileProvider, Object {
return Encryption.NONE;
}
public async InputStream download(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws FileReceiveError {
public async InputStream download(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws IOError {
// TODO(hrxi) What should happen if `stream == null`?
XmppStream? stream = stream_interactor.get_stream(file_transfer.account);
Xmpp.Xep.JingleFileTransfer.FileTransfer? jingle_file_transfer = file_transfers[file_transfer.info];
if (jingle_file_transfer == null) {
throw new FileReceiveError.DOWNLOAD_FAILED("Transfer data not available anymore");
throw new IOError.FAILED("Transfer data not available anymore");
}
try {
yield jingle_file_transfer.accept(stream);
} catch (IOError e) {
throw new FileReceiveError.DOWNLOAD_FAILED("Establishing connection did not work");
}
return jingle_file_transfer.stream;
yield jingle_file_transfer.accept(stream);
return new LimitInputStream(jingle_file_transfer.stream, file_meta.size);
}
public int get_id() {

View File

@ -39,27 +39,21 @@ public class MessageCorrection : StreamInteractionModule, MessageListener {
});
}
public void send_correction(Conversation conversation, Message old_message, string correction_text) {
string stanza_id = old_message.edit_to ?? old_message.stanza_id;
public void set_correction(Conversation conversation, Message message, Message old_message) {
string reference_stanza_id = old_message.edit_to ?? old_message.stanza_id;
Message out_message = stream_interactor.get_module(MessageProcessor.IDENTITY).create_out_message(correction_text, conversation);
out_message.edit_to = stanza_id;
out_message.quoted_item_id = old_message.quoted_item_id;
outstanding_correction_nodes[out_message.stanza_id] = stanza_id;
stream_interactor.get_module(MessageProcessor.IDENTITY).send_xmpp_message(out_message, conversation);
outstanding_correction_nodes[message.stanza_id] = reference_stanza_id;
db.message_correction.insert()
.value(db.message_correction.message_id, out_message.id)
.value(db.message_correction.to_stanza_id, stanza_id)
.perform();
.value(db.message_correction.message_id, message.id)
.value(db.message_correction.to_stanza_id, reference_stanza_id)
.perform();
db.content_item.update()
.with(db.content_item.foreign_id, "=", old_message.id)
.with(db.content_item.content_type, "=", 1)
.set(db.content_item.foreign_id, out_message.id)
.perform();
on_received_correction(conversation, out_message.id);
.with(db.content_item.foreign_id, "=", old_message.id)
.with(db.content_item.content_type, "=", 1)
.set(db.content_item.foreign_id, message.id)
.perform();
}
public bool is_own_correction_allowed(Conversation conversation, Message message) {
@ -145,7 +139,7 @@ public class MessageCorrection : StreamInteractionModule, MessageListener {
return false;
}
private void on_received_correction(Conversation conversation, int message_id) {
public void on_received_correction(Conversation conversation, int message_id) {
ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_foreign(conversation, 1, message_id);
if (content_item != null) {
received_correction(content_item);

View File

@ -38,7 +38,7 @@ public class MessageProcessor : StreamInteractionModule, Object {
received_pipeline.connect(new FilterMessageListener());
received_pipeline.connect(new StoreMessageListener(this, stream_interactor));
received_pipeline.connect(new StoreContentItemListener(stream_interactor));
received_pipeline.connect(new MamMessageListener(stream_interactor));
received_pipeline.connect(new MarkupListener(stream_interactor));
stream_interactor.account_added.connect(on_account_added);
@ -46,18 +46,6 @@ public class MessageProcessor : StreamInteractionModule, Object {
stream_interactor.stream_resumed.connect(send_unsent_chat_messages);
}
public Entities.Message send_text(string text, Conversation conversation) {
Entities.Message message = create_out_message(text, conversation);
return send_message(message, conversation);
}
public Entities.Message send_message(Entities.Message message, Conversation conversation) {
stream_interactor.get_module(ContentItemStore.IDENTITY).insert_message(message, conversation);
send_xmpp_message(message, conversation);
message_sent(message, conversation);
return message;
}
private void convert_sending_to_unsent_msgs(Account account) {
db.message.update()
.with(db.message.account_id, "=", account.id)
@ -168,7 +156,6 @@ public class MessageProcessor : StreamInteractionModule, Object {
new_message.counterpart = counterpart_override ?? (new_message.direction == Entities.Message.DIRECTION_SENT ? message.to : message.from);
new_message.ourpart = new_message.direction == Entities.Message.DIRECTION_SENT ? message.from : message.to;
XmppStream? stream = stream_interactor.get_stream(account);
Xmpp.MessageArchiveManagement.MessageFlag? mam_message_flag = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(message);
EntityInfo entity_info = stream_interactor.get_module(EntityInfo.IDENTITY);
if (mam_message_flag != null && mam_message_flag.mam_id != null) {
@ -320,7 +307,8 @@ public class MessageProcessor : StreamInteractionModule, Object {
public override string[] after_actions { get { return after_actions_const; } }
public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) {
return (message.body == null);
return message.body == null &&
Xep.StatelessFileSharing.get_file_shares(stanza) == null;
}
}
@ -339,13 +327,30 @@ public class MessageProcessor : StreamInteractionModule, Object {
}
public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) {
if (message.body == null || outer.is_duplicate(message, stanza, conversation)) return true;
stream_interactor.get_module(MessageStorage.IDENTITY).add_message(message, conversation);
return false;
}
}
private class MarkupListener : MessageListener {
public string[] after_actions_const = new string[]{ "STORE" };
public override string action_group { get { return "Markup"; } }
public override string[] after_actions { get { return after_actions_const; } }
private StreamInteractor stream_interactor;
public MarkupListener(StreamInteractor stream_interactor) {
this.stream_interactor = stream_interactor;
}
public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) {
Gee.List<MessageMarkup.Span> markups = MessageMarkup.get_spans(stanza);
message.persist_markups(markups, message.id);
return false;
}
}
private class StoreContentItemListener : MessageListener {
public string[] after_actions_const = new string[]{ "DEDUPLICATE", "DECRYPT", "FILTER_EMPTY", "STORE", "CORRECTION", "MESSAGE_REINTERPRETING" };
@ -365,30 +370,7 @@ public class MessageProcessor : StreamInteractionModule, Object {
}
}
private class MamMessageListener : MessageListener {
public string[] after_actions_const = new string[]{ "DEDUPLICATE" };
public override string action_group { get { return "MAM_NODE"; } }
public override string[] after_actions { get { return after_actions_const; } }
private StreamInteractor stream_interactor;
public MamMessageListener(StreamInteractor stream_interactor) {
this.stream_interactor = stream_interactor;
}
public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) {
bool is_mam_message = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(stanza) != null;
XmppStream? stream = stream_interactor.get_stream(conversation.account);
Xmpp.MessageArchiveManagement.Flag mam_flag = Xmpp.MessageArchiveManagement.Flag.get_flag(stream);
if (is_mam_message || (mam_flag != null && mam_flag.cought_up == true)) {
conversation.account.mam_earliest_synced = message.local_time;
}
return false;
}
}
public Entities.Message create_out_message(string text, Conversation conversation) {
public Entities.Message create_out_message(string? text, Conversation conversation) {
Entities.Message message = new Entities.Message(text);
message.type_ = Util.get_message_type_for_conversation(conversation);
message.stanza_id = random_uuid();
@ -431,8 +413,22 @@ public class MessageProcessor : StreamInteractionModule, Object {
new_message.type_ = MessageStanza.TYPE_CHAT;
}
string? fallback = get_fallback_body_set_infos(message, new_message, conversation);
new_message.body = fallback == null ? message.body : fallback + message.body;
if (message.quoted_item_id != 0) {
ContentItem? quoted_content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, message.quoted_item_id);
if (quoted_content_item != null) {
Jid? quoted_sender = message.from;
string? quoted_stanza_id = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_id_for_content_item(conversation, quoted_content_item);
if (quoted_sender != null && quoted_stanza_id != null) {
Xep.Replies.set_reply_to(new_message, new Xep.Replies.ReplyTo(quoted_sender, quoted_stanza_id));
}
foreach (var fallback in message.get_fallbacks()) {
Xep.FallbackIndication.set_fallback(new_message, fallback);
}
}
}
MessageMarkup.add_spans(new_message, message.get_markups());
build_message_stanza(message, new_message, conversation);
pre_message_send(message, new_message, conversation);
@ -481,26 +477,6 @@ public class MessageProcessor : StreamInteractionModule, Object {
}
});
}
public string? get_fallback_body_set_infos(Entities.Message message, MessageStanza new_stanza, Conversation conversation) {
if (message.quoted_item_id == 0) return null;
ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, message.quoted_item_id);
if (content_item == null) return null;
Jid? quoted_sender = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_sender_for_content_item(conversation, content_item);
string? quoted_stanza_id = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_id_for_content_item(conversation, content_item);
if (quoted_sender != null && quoted_stanza_id != null) {
Xep.Replies.set_reply_to(new_stanza, new Xep.Replies.ReplyTo(quoted_sender, quoted_stanza_id));
}
string fallback = FallbackBody.get_quoted_fallback_body(content_item);
var fallback_location = new Xep.FallbackIndication.FallbackLocation(0, (int)fallback.char_count());
Xep.FallbackIndication.set_fallback(new_stanza, new Xep.FallbackIndication.Fallback(Xep.Replies.NS_URI, new Xep.FallbackIndication.FallbackLocation[] { fallback_location }));
return fallback;
}
}
public abstract class MessageListener : Xmpp.OrderedListener {

View File

@ -99,6 +99,14 @@ public class MessageStorage : StreamInteractionModule, Object {
return create_message_from_row_opt(row_option, conversation);
}
public Message? get_message_by_referencing_id(string id, Conversation conversation) {
if (conversation.type_ == Conversation.Type.CHAT) {
return stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_stanza_id(id, conversation);
} else {
return stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_server_id(id, conversation);
}
}
public Message? get_message_by_stanza_id(string stanza_id, Conversation conversation) {
if (messages_by_stanza_id.has_key(conversation)) {
Message? message = messages_by_stanza_id[conversation][stanza_id];
@ -191,6 +199,16 @@ public class MessageStorage : StreamInteractionModule, Object {
message_refs.remove_at(message_refs.size - 1);
}
}
public static string? get_reference_id(Message message) {
if (message.edit_to != null) return message.edit_to;
if (message.type_ == Message.Type.CHAT) {
return message.stanza_id;
} else {
return message.server_id;
}
}
}
}

View File

@ -24,7 +24,7 @@ public class ModuleManager {
return null;
}
public ArrayList<XmppStreamModule> get_modules(Account account, string? resource = null) {
public ArrayList<XmppStreamModule> get_modules(Account account) {
ArrayList<XmppStreamModule> modules = new ArrayList<XmppStreamModule>();
lock (module_map) {
@ -34,7 +34,7 @@ public class ModuleManager {
foreach (XmppStreamModule module in module_map[account]) {
if (module.get_id() == Bind.Module.IDENTITY.id) {
((Bind.Module) module).requested_resource = resource ?? account.resourcepart;
((Bind.Module) module).requested_resource = account.resourcepart;
} else if (module.get_id() == Sasl.Module.IDENTITY.id) {
((Sasl.Module) module).password = account.password;
}
@ -59,6 +59,7 @@ public class ModuleManager {
module_map[account].add(new Xmpp.MessageModule());
module_map[account].add(new Xmpp.MessageArchiveManagement.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.Pubsub.Module());
module_map[account].add(new Xep.MessageDeliveryReceipts.Module());

View File

@ -21,7 +21,7 @@ public class MucManager : StreamInteractionModule, Object {
public signal void conference_removed(Account account, Jid jid);
private StreamInteractor stream_interactor;
private HashMap<Account, HashSet<Jid>> mucs_todo = new HashMap<Account, HashSet<Jid>>(Account.hash_func, Account.equals_func);
private HashMap<Account, HashSet<Jid>> mucs_joined = new HashMap<Account, HashSet<Jid>>(Account.hash_func, Account.equals_func);
private HashMap<Account, HashSet<Jid>> mucs_joining = new HashMap<Account, HashSet<Jid>>(Account.hash_func, Account.equals_func);
private HashMap<Account, HashMap<Jid, Cancellable>> mucs_sync_cancellables = new HashMap<Account, HashMap<Jid, Cancellable>>(Account.hash_func, Account.equals_func);
private HashMap<Jid, Xep.Muc.MucEnterError> enter_errors = new HashMap<Jid, Xep.Muc.MucEnterError>(Jid.hash_func, Jid.equals_func);
@ -54,6 +54,7 @@ public class MucManager : StreamInteractionModule, Object {
}
return true;
});
stream_interactor.get_module(MessageProcessor.IDENTITY).build_message_stanza.connect(on_build_message_stanza);
}
// already_autojoin: Without this flag we'd be retrieving bookmarks (to check for autojoin) from the sender on every join
@ -83,11 +84,6 @@ public class MucManager : StreamInteractionModule, Object {
}
mucs_joining[account].add(jid);
if (!mucs_todo.has_key(account)) {
mucs_todo[account] = new HashSet<Jid>(Jid.hash_bare_func, Jid.equals_bare_func);
}
mucs_todo[account].add(jid.with_resource(nick_));
Muc.JoinResult? res = yield stream.get_module(Xep.Muc.Module.IDENTITY).enter(stream, jid.bare_jid, nick_, password, history_since, receive_history, null);
mucs_joining[account].remove(jid);
@ -126,20 +122,26 @@ public class MucManager : StreamInteractionModule, Object {
enter_errors[jid] = res.muc_error;
}
if (!mucs_joined.has_key(account)) {
mucs_joined[account] = new HashSet<Jid>(Jid.hash_bare_func, Jid.equals_bare_func);
}
mucs_joined[account].add(jid.with_resource(res.nick ?? nick_));
return res;
}
public void part(Account account, Jid jid) {
if (!mucs_todo.has_key(account) || !mucs_todo[account].contains(jid)) return;
mucs_todo[account].remove(jid);
if (mucs_joined.has_key(account) && mucs_joined[account].contains(jid)) {
mucs_joined[account].remove(jid);
}
XmppStream? stream = stream_interactor.get_stream(account);
if (stream == null) return;
unset_autojoin(account, stream, jid);
stream.get_module(Xep.Muc.Module.IDENTITY).exit(stream, jid.bare_jid);
if (stream != null) {
unset_autojoin(account, stream, jid);
stream.get_module(Xep.Muc.Module.IDENTITY).exit(stream, jid.bare_jid);
}
Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid, account);
Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid, account, Conversation.Type.GROUPCHAT);
if (conversation != null) stream_interactor.get_module(ConversationManager.IDENTITY).close_conversation(conversation);
cancel_sync(account, jid);
@ -181,9 +183,9 @@ public class MucManager : StreamInteractionModule, Object {
conversation.nickname = new_nick;
if (mucs_todo.has_key(conversation.account)) {
mucs_todo[conversation.account].remove(conversation.counterpart);
mucs_todo[conversation.account].add(conversation.counterpart.with_resource(new_nick));
if (mucs_joined.has_key(conversation.account)) {
mucs_joined[conversation.account].remove(conversation.counterpart);
mucs_joined[conversation.account].add(conversation.counterpart.with_resource(new_nick));
}
// Update nick in bookmark
@ -231,15 +233,8 @@ public class MucManager : StreamInteractionModule, Object {
//the term `private room` is a short hand for members-only+non-anonymous rooms
public bool is_private_room(Account account, Jid jid) {
XmppStream? stream = stream_interactor.get_stream(account);
if (stream == null) {
return false;
}
Xep.Muc.Flag? flag = stream.get_flag(Xep.Muc.Flag.IDENTITY);
if (flag == null) {
return false;
}
return flag.has_room_feature(jid, Xep.Muc.Feature.NON_ANONYMOUS) && flag.has_room_feature(jid, Xep.Muc.Feature.MEMBERS_ONLY);
var entity_info = stream_interactor.get_module(EntityInfo.IDENTITY);
return entity_info.has_feature_offline(account, jid, "muc_membersonly") && entity_info.has_feature_offline(account, jid, "muc_nonanonymous");
}
public bool is_moderated_room(Account account, Jid jid) {
@ -636,8 +631,7 @@ public class MucManager : StreamInteractionModule, Object {
} else if (conversation.active && !conference.autojoin) {
part(account, conference.jid);
}
}
if (conference.autojoin) {
} else if (conference.autojoin) {
join.begin(account, conference.jid, conference.nick, conference.password);
}
conference_added(account, conference);
@ -651,13 +645,19 @@ public class MucManager : StreamInteractionModule, Object {
conference_removed(account, jid);
}
private void on_build_message_stanza(Entities.Message message, Xmpp.MessageStanza message_stanza, Conversation conversation) {
if (conversation.type_ == Conversation.Type.GROUPCHAT_PM) {
Xmpp.Xep.Muc.add_muc_pm_message_stanza_x_node(message_stanza);
}
}
private void self_ping(Account account) {
XmppStream? stream = stream_interactor.get_stream(account);
if (stream == null) return;
if (!mucs_todo.has_key(account)) return;
if (!mucs_joined.has_key(account)) return;
foreach (Jid jid in mucs_todo[account]) {
foreach (Jid jid in mucs_joined[account]) {
bool joined = false;
@ -666,7 +666,7 @@ public class MucManager : StreamInteractionModule, Object {
});
Timeout.add_seconds(10, () => {
if (joined || !mucs_todo.has_key(account) || stream_interactor.get_stream(account) != stream) return false;
if (joined || !mucs_joined.has_key(account) || stream_interactor.get_stream(account) != stream) return false;
join.begin(account, jid.bare_jid, jid.resourcepart, null, true);
return false;

View File

@ -71,6 +71,12 @@ public class Register : StreamInteractionModule, Object{
return ret;
}
public async string? change_password(Account account, string new_pw){
XmppStream stream = stream_interactor.get_stream(account);
if (stream == null) return null;
return (yield stream.get_module(Xep.InBandRegistration.Module.IDENTITY).change_password(stream, account.full_jid, new_pw)).condition;
}
public class ServerAvailabilityReturn {
public bool available { get; set; }
public TlsCertificateFlags? error_flags { get; set; }
@ -135,6 +141,7 @@ public class Register : StreamInteractionModule, Object{
Gee.List<XmppStreamModule> list = new ArrayList<XmppStreamModule>();
list.add(new Iq.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,
(peer_cert, errors) => { return ConnectionManager.on_invalid_certificate(jid.domainpart, peer_cert, errors); }

View File

@ -38,17 +38,6 @@ public class Dino.Replies : StreamInteractionModule, Object {
return null;
}
public void set_message_is_reply_to(Message message, ContentItem reply_to) {
message.quoted_item_id = reply_to.id;
db.reply.upsert()
.value(db.reply.message_id, message.id, true)
.value(db.reply.quoted_content_item_id, reply_to.id)
.value_null(db.reply.quoted_message_stanza_id)
.value_null(db.reply.quoted_message_from)
.perform();
}
private void on_incoming_message(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) {
// Check if a previous message was in reply to this one
var reply_qry = db.reply.select();
@ -67,7 +56,7 @@ public class Dino.Replies : StreamInteractionModule, Object {
ContentItem? message_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_foreign(conversation, 1, message.id);
Message? reply_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(reply_row[db.message.id], conversation);
if (message_item != null && reply_message != null) {
set_message_is_reply_to(reply_message, message_item);
reply_message.set_quoted_item(message_item.id);
}
}
@ -78,7 +67,7 @@ public class Dino.Replies : StreamInteractionModule, Object {
ContentItem? quoted_content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_content_item_for_message_id(conversation, reply_to.to_message_id);
if (quoted_content_item == null) return;
set_message_is_reply_to(message, quoted_content_item);
message.set_quoted_item(quoted_content_item.id);
}
private class ReceivedMessageListener : MessageListener {

View File

@ -133,6 +133,7 @@ public class RosterStoreImpl : Roster.Storage, Object {
.value(db.roster.jid, item.jid.to_string(), true)
.value(db.roster.handle, item.name)
.value(db.roster.subscription, item.subscription)
.value(db.roster.ask, item.ask)
.perform();
}

View File

@ -0,0 +1,80 @@
using Gdk;
using GLib;
using Gee;
using Xmpp;
using Xmpp.Xep;
using Dino.Entities;
namespace Dino {
public interface FileMetadataProvider : Object {
public abstract bool supports_file(File file);
public abstract async void fill_metadata(File file, Xep.FileMetadataElement.FileMetadata metadata);
}
class GenericFileMetadataProvider: Dino.FileMetadataProvider, Object {
public bool supports_file(File file) {
return true;
}
public async void fill_metadata(File file, Xep.FileMetadataElement.FileMetadata metadata) {
FileInfo info = file.query_info("*", FileQueryInfoFlags.NONE);
metadata.name = info.get_display_name();
metadata.mime_type = info.get_content_type();
metadata.size = info.get_size();
metadata.date = info.get_modification_date_time();
var checksum_types = new ArrayList<ChecksumType>.wrap(new ChecksumType[] { ChecksumType.SHA256, ChecksumType.SHA512 });
var file_hashes = yield compute_file_hashes(file, checksum_types);
metadata.hashes.add(new CryptographicHashes.Hash.with_checksum(ChecksumType.SHA256, file_hashes[ChecksumType.SHA256]));
metadata.hashes.add(new CryptographicHashes.Hash.with_checksum(ChecksumType.SHA512, file_hashes[ChecksumType.SHA512]));
}
}
public class ImageFileMetadataProvider: Dino.FileMetadataProvider, Object {
public bool supports_file(File file) {
string mime_type = file.query_info("*", FileQueryInfoFlags.NONE).get_content_type();
return Dino.Util.is_pixbuf_supported_mime_type(mime_type);
}
private const int[] THUMBNAIL_DIMS = { 1, 2, 3, 4, 8 };
private const string IMAGE_TYPE = "png";
private const string MIME_TYPE = "image/png";
public async void fill_metadata(File file, Xep.FileMetadataElement.FileMetadata metadata) {
Pixbuf pixbuf = new Pixbuf.from_stream(yield file.read_async());
metadata.width = pixbuf.get_width();
metadata.height = pixbuf.get_height();
float ratio = (float)metadata.width / (float) metadata.height;
int thumbnail_width = -1;
int thumbnail_height = -1;
float diff = float.INFINITY;
for (int i = 0; i < THUMBNAIL_DIMS.length; i++) {
int test_width = THUMBNAIL_DIMS[i];
int test_height = THUMBNAIL_DIMS[THUMBNAIL_DIMS.length - 1 - i];
float test_ratio = (float)test_width / (float)test_height;
float test_diff = (test_ratio - ratio).abs();
if (test_diff < diff) {
thumbnail_width = test_width;
thumbnail_height = test_height;
diff = test_diff;
}
}
Pixbuf thumbnail_pixbuf = pixbuf.scale_simple(thumbnail_width, thumbnail_height, InterpType.BILINEAR);
uint8[] buffer;
thumbnail_pixbuf.save_to_buffer(out buffer, IMAGE_TYPE);
var thumbnail = new Xep.JingleContentThumbnails.Thumbnail();
thumbnail.data = new Bytes.take(buffer);
thumbnail.media_type = MIME_TYPE;
thumbnail.width = thumbnail_width;
thumbnail.height = thumbnail_height;
metadata.thumbnails.add(thumbnail);
}
}
}

View File

@ -0,0 +1,168 @@
using Gdk;
using Gee;
using Xmpp;
using Xmpp.Xep;
using Dino.Entities;
public class Dino.StatelessFileSharing : StreamInteractionModule, Object {
public static ModuleIdentity<StatelessFileSharing> IDENTITY = new ModuleIdentity<StatelessFileSharing>("sfs");
public string id { get { return IDENTITY.id; } }
public const int SFS_PROVIDER_ID = 2;
public StreamInteractor stream_interactor {
owned get { return Application.get_default().stream_interactor; }
private set { }
}
public FileManager file_manager {
owned get { return stream_interactor.get_module(FileManager.IDENTITY); }
private set { }
}
public Database db {
owned get { return Application.get_default().db; }
private set { }
}
private StatelessFileSharing(StreamInteractor stream_interactor, Database db) {
this.stream_interactor = stream_interactor;
this.db = db;
stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(new ReceivedMessageListener(this));
}
public static void start(StreamInteractor stream_interactor, Database db) {
StatelessFileSharing m = new StatelessFileSharing(stream_interactor, db);
stream_interactor.add_module(m);
}
public async void create_file_transfer(Conversation conversation, Message message, string? file_sharing_id, Xep.FileMetadataElement.FileMetadata metadata, Gee.List<Xep.StatelessFileSharing.Source>? sources) {
FileTransfer file_transfer = new FileTransfer();
file_transfer.file_sharing_id = file_sharing_id;
file_transfer.account = message.account;
file_transfer.counterpart = message.counterpart;
file_transfer.ourpart = message.ourpart;
file_transfer.direction = message.direction;
file_transfer.time = message.time;
file_transfer.local_time = message.local_time;
file_transfer.provider = SFS_PROVIDER_ID;
file_transfer.file_metadata = metadata;
file_transfer.info = message.id.to_string();
if (sources != null) {
file_transfer.sfs_sources = sources;
}
stream_interactor.get_module(FileTransferStorage.IDENTITY).add_file(file_transfer);
conversation.last_active = file_transfer.time;
file_manager.received_file(file_transfer, conversation);
}
public void on_received_sources(Jid from, Conversation conversation, string attach_to_message_id, string? attach_to_file_id, Gee.List<Xep.StatelessFileSharing.Source> sources) {
Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_referencing_id(attach_to_message_id, conversation);
if (message == null) return;
FileTransfer? file_transfer = null;
if (attach_to_file_id != null) {
file_transfer = stream_interactor.get_module(FileTransferStorage.IDENTITY).get_files_by_message_and_file_id(message.id, attach_to_file_id, conversation);
} else {
file_transfer = stream_interactor.get_module(FileTransferStorage.IDENTITY).get_file_by_message_id(message.id, conversation);
}
if (file_transfer == null) return;
// "If no <hash/> is provided or the <hash/> elements provided use unsupported algorithms, receiving clients MUST ignore
// any attached sources from other senders and only obtain the file from the sources announced by the original sender."
// For now we only allow the original sender
if (from.equals(file_transfer.from) && Xep.CryptographicHashes.get_supported_hashes(file_transfer.hashes).is_empty) {
warning("Ignoring sfs source: Not from original sender or no known file hashes");
return;
}
foreach (var source in sources) {
file_transfer.add_sfs_source(source);
}
if (file_manager.is_sender_trustworthy(file_transfer, conversation) && file_transfer.state == FileTransfer.State.NOT_STARTED && file_transfer.size >= 0 && file_transfer.size < 5000000) {
file_manager.download_file(file_transfer);
}
}
/*
public async void create_sfs_for_legacy_transfer(FileProvider file_provider, string info, Jid from, DateTime time, DateTime local_time, Conversation conversation, FileReceiveData receive_data, FileMeta file_meta) {
FileTransfer file_transfer = file_manager.create_file_transfer_from_provider_incoming(file_provider, info, from, time, local_time, conversation, receive_data, file_meta);
HttpFileReceiveData? http_receive_data = receive_data as HttpFileReceiveData;
if (http_receive_data == null) return;
var sources = new ArrayList<Xep.StatelessFileSharing.Source>();
Xep.StatelessFileSharing.HttpSource source = new Xep.StatelessFileSharing.HttpSource();
source.url = http_receive_data.url;
sources.add(source);
if (file_manager.is_jid_trustworthy(from, conversation)) {
try {
file_meta = yield file_provider.get_meta_info(file_transfer, http_receive_data, file_meta);
} catch (Error e) {
warning("Http meta request failed: %s", e.message);
}
}
var metadata = new Xep.FileMetadataElement.FileMetadata();
metadata.size = file_meta.size;
metadata.name = file_meta.file_name;
metadata.mime_type = file_meta.mime_type;
file_transfer.provider = SFS_PROVIDER_ID;
file_transfer.file_metadata = metadata;
file_transfer.sfs_sources = sources;
}
*/
private class ReceivedMessageListener : MessageListener {
public string[] after_actions_const = new string[]{ "STORE" };
public override string action_group { get { return "MESSAGE_REINTERPRETING"; } }
public override string[] after_actions { get { return after_actions_const; } }
private StatelessFileSharing outer;
private StreamInteractor stream_interactor;
public ReceivedMessageListener(StatelessFileSharing outer) {
this.outer = outer;
this.stream_interactor = outer.stream_interactor;
}
public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) {
Gee.List<Xep.StatelessFileSharing.FileShare> file_shares = Xep.StatelessFileSharing.get_file_shares(stanza);
if (file_shares != null) {
// For now, only accept file shares that have at least one supported hash
foreach (Xep.StatelessFileSharing.FileShare file_share in file_shares) {
if (!Xep.CryptographicHashes.has_supported_hashes(file_share.metadata.hashes)) {
return false;
}
}
foreach (Xep.StatelessFileSharing.FileShare file_share in file_shares) {
outer.create_file_transfer(conversation, message, file_share.id, file_share.metadata, file_share.sources);
}
return true;
}
var source_attachments = Xep.StatelessFileSharing.get_source_attachments(stanza);
if (source_attachments != null) {
foreach (var source_attachment in source_attachments) {
outer.on_received_sources(stanza.from, conversation, source_attachment.to_message_id, source_attachment.to_file_transfer_id, source_attachment.sources);
return true;
}
}
// Don't process messages that are fallback for legacy clients
if (Xep.StatelessFileSharing.is_sfs_fallback_message(stanza)) {
return true;
}
return false;
}
}
}

View File

@ -89,7 +89,12 @@ public class ModuleIdentity<T> : Object {
}
public T? cast(StreamInteractionModule module) {
#if VALA_0_56_11
// We can't typecheck due to compiler bug
return (T) module;
#else
return module.get_type().is_a(typeof(T)) ? (T?) module : null;
#endif
}
public bool matches(StreamInteractionModule module) {

Some files were not shown because too many files have changed in this diff Show More