From 2fe8489d368a371aefbfbe66e74621a8df14cdc2 Mon Sep 17 00:00:00 2001 From: fiaxh Date: Fri, 10 Mar 2017 17:01:45 +0100 Subject: Rename vala-xmpp library to xmpp-vala --- CMakeLists.txt | 2 +- client/CMakeLists.txt | 12 +- vala-xmpp/CMakeLists.txt | 92 ------- vala-xmpp/src/core/namespace_state.vala | 80 ------ vala-xmpp/src/core/stanza_attribute.vala | 29 -- vala-xmpp/src/core/stanza_node.vala | 297 --------------------- vala-xmpp/src/core/stanza_reader.vala | 260 ------------------ vala-xmpp/src/core/stanza_writer.vala | 27 -- vala-xmpp/src/core/xmpp_stream.vala | 245 ----------------- vala-xmpp/src/module/bind.vala | 93 ------- vala-xmpp/src/module/iq/module.vala | 89 ------ vala-xmpp/src/module/iq/stanza.vala | 51 ---- vala-xmpp/src/module/message/module.vala | 50 ---- vala-xmpp/src/module/message/stanza.vala | 63 ----- vala-xmpp/src/module/presence/flag.vala | 64 ----- vala-xmpp/src/module/presence/module.vala | 110 -------- vala-xmpp/src/module/presence/stanza.vala | 93 ------- vala-xmpp/src/module/roster/flag.vala | 30 --- vala-xmpp/src/module/roster/item.vala | 45 ---- vala-xmpp/src/module/roster/module.vala | 125 --------- vala-xmpp/src/module/sasl.vala | 139 ---------- vala-xmpp/src/module/stanza.vala | 70 ----- vala-xmpp/src/module/stanza_error.vala | 69 ----- vala-xmpp/src/module/stream_error.vala | 119 --------- vala-xmpp/src/module/tls.vala | 99 ------- vala-xmpp/src/module/util.vala | 21 -- vala-xmpp/src/module/xep/0027_pgp/flag.vala | 24 -- vala-xmpp/src/module/xep/0027_pgp/module.vala | 206 -------------- .../module/xep/0030_service_discovery/flag.vala | 33 --- .../xep/0030_service_discovery/info_result.vala | 78 ------ .../xep/0030_service_discovery/items_result.vala | 27 -- .../module/xep/0030_service_discovery/module.vala | 137 ---------- vala-xmpp/src/module/xep/0045_muc/flag.vala | 80 ------ vala-xmpp/src/module/xep/0045_muc/module.vala | 244 ----------------- .../src/module/xep/0048_bookmarks/conference.vala | 74 ----- .../src/module/xep/0048_bookmarks/module.vala | 137 ---------- .../src/module/xep/0049_private_xml_storage.vala | 65 ----- vala-xmpp/src/module/xep/0054_vcard/module.vala | 87 ------ vala-xmpp/src/module/xep/0060_pubsub.vala | 107 -------- .../src/module/xep/0082_date_time_profiles.vala | 41 --- vala-xmpp/src/module/xep/0084_user_avatars.vala | 93 ------- .../module/xep/0085_chat_state_notifications.vala | 74 ----- .../src/module/xep/0115_entitiy_capabilities.vala | 125 --------- .../module/xep/0184_message_delivery_receipts.vala | 62 ----- vala-xmpp/src/module/xep/0199_ping.vala | 56 ---- .../src/module/xep/0203_delayed_delivery.vala | 62 ----- vala-xmpp/src/module/xep/0280_message_carbons.vala | 91 ------- vala-xmpp/src/module/xep/0333_chat_markers.vala | 81 ------ vala-xmpp/src/module/xep/pixbuf_storage.vala | 9 - vala-xmpp/vapi/uuid.vapi | 60 ----- xmpp-vala/CMakeLists.txt | 92 +++++++ xmpp-vala/src/core/namespace_state.vala | 80 ++++++ xmpp-vala/src/core/stanza_attribute.vala | 29 ++ xmpp-vala/src/core/stanza_node.vala | 297 +++++++++++++++++++++ xmpp-vala/src/core/stanza_reader.vala | 260 ++++++++++++++++++ xmpp-vala/src/core/stanza_writer.vala | 27 ++ xmpp-vala/src/core/xmpp_stream.vala | 245 +++++++++++++++++ xmpp-vala/src/module/bind.vala | 93 +++++++ xmpp-vala/src/module/iq/module.vala | 89 ++++++ xmpp-vala/src/module/iq/stanza.vala | 51 ++++ xmpp-vala/src/module/message/module.vala | 50 ++++ xmpp-vala/src/module/message/stanza.vala | 63 +++++ xmpp-vala/src/module/presence/flag.vala | 64 +++++ xmpp-vala/src/module/presence/module.vala | 110 ++++++++ xmpp-vala/src/module/presence/stanza.vala | 93 +++++++ xmpp-vala/src/module/roster/flag.vala | 30 +++ xmpp-vala/src/module/roster/item.vala | 45 ++++ xmpp-vala/src/module/roster/module.vala | 125 +++++++++ xmpp-vala/src/module/sasl.vala | 139 ++++++++++ xmpp-vala/src/module/stanza.vala | 70 +++++ xmpp-vala/src/module/stanza_error.vala | 69 +++++ xmpp-vala/src/module/stream_error.vala | 119 +++++++++ xmpp-vala/src/module/tls.vala | 99 +++++++ xmpp-vala/src/module/util.vala | 21 ++ xmpp-vala/src/module/xep/0027_pgp/flag.vala | 24 ++ xmpp-vala/src/module/xep/0027_pgp/module.vala | 206 ++++++++++++++ .../module/xep/0030_service_discovery/flag.vala | 33 +++ .../xep/0030_service_discovery/info_result.vala | 78 ++++++ .../xep/0030_service_discovery/items_result.vala | 27 ++ .../module/xep/0030_service_discovery/module.vala | 137 ++++++++++ xmpp-vala/src/module/xep/0045_muc/flag.vala | 80 ++++++ xmpp-vala/src/module/xep/0045_muc/module.vala | 244 +++++++++++++++++ .../src/module/xep/0048_bookmarks/conference.vala | 74 +++++ .../src/module/xep/0048_bookmarks/module.vala | 137 ++++++++++ .../src/module/xep/0049_private_xml_storage.vala | 65 +++++ xmpp-vala/src/module/xep/0054_vcard/module.vala | 87 ++++++ xmpp-vala/src/module/xep/0060_pubsub.vala | 107 ++++++++ .../src/module/xep/0082_date_time_profiles.vala | 41 +++ xmpp-vala/src/module/xep/0084_user_avatars.vala | 93 +++++++ .../module/xep/0085_chat_state_notifications.vala | 74 +++++ .../src/module/xep/0115_entitiy_capabilities.vala | 125 +++++++++ .../module/xep/0184_message_delivery_receipts.vala | 62 +++++ xmpp-vala/src/module/xep/0199_ping.vala | 56 ++++ .../src/module/xep/0203_delayed_delivery.vala | 62 +++++ xmpp-vala/src/module/xep/0280_message_carbons.vala | 91 +++++++ xmpp-vala/src/module/xep/0333_chat_markers.vala | 81 ++++++ xmpp-vala/src/module/xep/pixbuf_storage.vala | 9 + xmpp-vala/vapi/uuid.vapi | 60 +++++ 98 files changed, 4419 insertions(+), 4421 deletions(-) delete mode 100644 vala-xmpp/CMakeLists.txt delete mode 100644 vala-xmpp/src/core/namespace_state.vala delete mode 100644 vala-xmpp/src/core/stanza_attribute.vala delete mode 100644 vala-xmpp/src/core/stanza_node.vala delete mode 100644 vala-xmpp/src/core/stanza_reader.vala delete mode 100644 vala-xmpp/src/core/stanza_writer.vala delete mode 100644 vala-xmpp/src/core/xmpp_stream.vala delete mode 100644 vala-xmpp/src/module/bind.vala delete mode 100644 vala-xmpp/src/module/iq/module.vala delete mode 100644 vala-xmpp/src/module/iq/stanza.vala delete mode 100644 vala-xmpp/src/module/message/module.vala delete mode 100644 vala-xmpp/src/module/message/stanza.vala delete mode 100644 vala-xmpp/src/module/presence/flag.vala delete mode 100644 vala-xmpp/src/module/presence/module.vala delete mode 100644 vala-xmpp/src/module/presence/stanza.vala delete mode 100644 vala-xmpp/src/module/roster/flag.vala delete mode 100644 vala-xmpp/src/module/roster/item.vala delete mode 100644 vala-xmpp/src/module/roster/module.vala delete mode 100644 vala-xmpp/src/module/sasl.vala delete mode 100644 vala-xmpp/src/module/stanza.vala delete mode 100644 vala-xmpp/src/module/stanza_error.vala delete mode 100644 vala-xmpp/src/module/stream_error.vala delete mode 100644 vala-xmpp/src/module/tls.vala delete mode 100644 vala-xmpp/src/module/util.vala delete mode 100644 vala-xmpp/src/module/xep/0027_pgp/flag.vala delete mode 100644 vala-xmpp/src/module/xep/0027_pgp/module.vala delete mode 100644 vala-xmpp/src/module/xep/0030_service_discovery/flag.vala delete mode 100644 vala-xmpp/src/module/xep/0030_service_discovery/info_result.vala delete mode 100644 vala-xmpp/src/module/xep/0030_service_discovery/items_result.vala delete mode 100644 vala-xmpp/src/module/xep/0030_service_discovery/module.vala delete mode 100644 vala-xmpp/src/module/xep/0045_muc/flag.vala delete mode 100644 vala-xmpp/src/module/xep/0045_muc/module.vala delete mode 100644 vala-xmpp/src/module/xep/0048_bookmarks/conference.vala delete mode 100644 vala-xmpp/src/module/xep/0048_bookmarks/module.vala delete mode 100644 vala-xmpp/src/module/xep/0049_private_xml_storage.vala delete mode 100644 vala-xmpp/src/module/xep/0054_vcard/module.vala delete mode 100644 vala-xmpp/src/module/xep/0060_pubsub.vala delete mode 100644 vala-xmpp/src/module/xep/0082_date_time_profiles.vala delete mode 100644 vala-xmpp/src/module/xep/0084_user_avatars.vala delete mode 100644 vala-xmpp/src/module/xep/0085_chat_state_notifications.vala delete mode 100644 vala-xmpp/src/module/xep/0115_entitiy_capabilities.vala delete mode 100644 vala-xmpp/src/module/xep/0184_message_delivery_receipts.vala delete mode 100644 vala-xmpp/src/module/xep/0199_ping.vala delete mode 100644 vala-xmpp/src/module/xep/0203_delayed_delivery.vala delete mode 100644 vala-xmpp/src/module/xep/0280_message_carbons.vala delete mode 100644 vala-xmpp/src/module/xep/0333_chat_markers.vala delete mode 100644 vala-xmpp/src/module/xep/pixbuf_storage.vala delete mode 100644 vala-xmpp/vapi/uuid.vapi create mode 100644 xmpp-vala/CMakeLists.txt create mode 100644 xmpp-vala/src/core/namespace_state.vala create mode 100644 xmpp-vala/src/core/stanza_attribute.vala create mode 100644 xmpp-vala/src/core/stanza_node.vala create mode 100644 xmpp-vala/src/core/stanza_reader.vala create mode 100644 xmpp-vala/src/core/stanza_writer.vala create mode 100644 xmpp-vala/src/core/xmpp_stream.vala create mode 100644 xmpp-vala/src/module/bind.vala create mode 100644 xmpp-vala/src/module/iq/module.vala create mode 100644 xmpp-vala/src/module/iq/stanza.vala create mode 100644 xmpp-vala/src/module/message/module.vala create mode 100644 xmpp-vala/src/module/message/stanza.vala create mode 100644 xmpp-vala/src/module/presence/flag.vala create mode 100644 xmpp-vala/src/module/presence/module.vala create mode 100644 xmpp-vala/src/module/presence/stanza.vala create mode 100644 xmpp-vala/src/module/roster/flag.vala create mode 100644 xmpp-vala/src/module/roster/item.vala create mode 100644 xmpp-vala/src/module/roster/module.vala create mode 100644 xmpp-vala/src/module/sasl.vala create mode 100644 xmpp-vala/src/module/stanza.vala create mode 100644 xmpp-vala/src/module/stanza_error.vala create mode 100644 xmpp-vala/src/module/stream_error.vala create mode 100644 xmpp-vala/src/module/tls.vala create mode 100644 xmpp-vala/src/module/util.vala create mode 100644 xmpp-vala/src/module/xep/0027_pgp/flag.vala create mode 100644 xmpp-vala/src/module/xep/0027_pgp/module.vala create mode 100644 xmpp-vala/src/module/xep/0030_service_discovery/flag.vala create mode 100644 xmpp-vala/src/module/xep/0030_service_discovery/info_result.vala create mode 100644 xmpp-vala/src/module/xep/0030_service_discovery/items_result.vala create mode 100644 xmpp-vala/src/module/xep/0030_service_discovery/module.vala create mode 100644 xmpp-vala/src/module/xep/0045_muc/flag.vala create mode 100644 xmpp-vala/src/module/xep/0045_muc/module.vala create mode 100644 xmpp-vala/src/module/xep/0048_bookmarks/conference.vala create mode 100644 xmpp-vala/src/module/xep/0048_bookmarks/module.vala create mode 100644 xmpp-vala/src/module/xep/0049_private_xml_storage.vala create mode 100644 xmpp-vala/src/module/xep/0054_vcard/module.vala create mode 100644 xmpp-vala/src/module/xep/0060_pubsub.vala create mode 100644 xmpp-vala/src/module/xep/0082_date_time_profiles.vala create mode 100644 xmpp-vala/src/module/xep/0084_user_avatars.vala create mode 100644 xmpp-vala/src/module/xep/0085_chat_state_notifications.vala create mode 100644 xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala create mode 100644 xmpp-vala/src/module/xep/0184_message_delivery_receipts.vala create mode 100644 xmpp-vala/src/module/xep/0199_ping.vala create mode 100644 xmpp-vala/src/module/xep/0203_delayed_delivery.vala create mode 100644 xmpp-vala/src/module/xep/0280_message_carbons.vala create mode 100644 xmpp-vala/src/module/xep/0333_chat_markers.vala create mode 100644 xmpp-vala/src/module/xep/pixbuf_storage.vala create mode 100644 xmpp-vala/vapi/uuid.vapi diff --git a/CMakeLists.txt b/CMakeLists.txt index ff7bbc70..82203c26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,5 +21,5 @@ set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set (VALA_CFLAGS -Wno-deprecated-declarations -Wno-incompatible-pointer-types -Wno-int-conversion) add_subdirectory(qlite) -add_subdirectory(vala-xmpp) +add_subdirectory(xmpp-vala) add_subdirectory(client) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 1ce8c1ee..574d603e 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -1,6 +1,5 @@ find_package(Vala REQUIRED) find_package(PkgConfig REQUIRED) -find_package(GPGME REQUIRED) include(${VALA_USE_FILE}) include(GlibCompileResourcesSupport) @@ -125,8 +124,7 @@ SOURCES src/ui/util.vala PACKAGES ${CLIENT_PACKAGES} - gpgme - vala-xmpp + xmpp-vala qlite GRESOURCES ${CLIENT_GRESOURCES_XML} @@ -134,15 +132,15 @@ OPTIONS --target-glib=2.38 -g --thread - --vapidir=${CMAKE_BINARY_DIR}/vala-xmpp + --vapidir=${CMAKE_BINARY_DIR}/xmpp-vala --vapidir=${CMAKE_BINARY_DIR}/qlite --vapidir=${CMAKE_SOURCE_DIR}/vapi ) -set(CFLAGS ${CLIENT_CFLAGS} ${GPGME_CFLAGS} ${LIBUUID_CFLAGS} -g -I${CMAKE_BINARY_DIR}/vala-xmpp -I${CMAKE_BINARY_DIR}/qlite ${VALA_CFLAGS}) +set(CFLAGS ${CLIENT_CFLAGS} ${GPGME_CFLAGS} ${LIBUUID_CFLAGS} -g -I${CMAKE_BINARY_DIR}/xmpp-vala -I${CMAKE_BINARY_DIR}/qlite ${VALA_CFLAGS}) add_definitions(${CFLAGS}) add_executable(dino ${CLIENT_VALA_C} ${CLIENT_GRESOURCES_TARGET}) -add_dependencies(dino vala-xmpp-vapi qlite-vapi) -target_link_libraries(dino vala-xmpp qlite ${CLIENT_LIBRARIES} ${GPGME_LIBRARIES} ${LIBUUID_LIBRARIES} -lm) +add_dependencies(dino xmpp-vala-vapi qlite-vapi) +target_link_libraries(dino xmpp-vala qlite ${CLIENT_LIBRARIES} ${GPGME_LIBRARIES} ${LIBUUID_LIBRARIES} -lm) diff --git a/vala-xmpp/CMakeLists.txt b/vala-xmpp/CMakeLists.txt deleted file mode 100644 index e7be66ae..00000000 --- a/vala-xmpp/CMakeLists.txt +++ /dev/null @@ -1,92 +0,0 @@ -find_package(Vala REQUIRED) -find_package(PkgConfig REQUIRED) -find_package(GPGME REQUIRED) -find_package(LIBUUID REQUIRED) -include(GlibCompileResourcesSupport) -include(${VALA_USE_FILE}) - -set(ENGINE_PACKAGES - gee-0.8 - gio-2.0 - glib-2.0 - gtk+-3.0 -) - -pkg_check_modules(ENGINE REQUIRED ${ENGINE_PACKAGES}) - -vala_precompile(ENGINE_VALA_C -SOURCES - "src/core/namespace_state.vala" - "src/core/stanza_attribute.vala" - "src/core/stanza_node.vala" - "src/core/stanza_reader.vala" - "src/core/stanza_writer.vala" - "src/core/xmpp_stream.vala" - - "src/module/bind.vala" - "src/module/iq/module.vala" - "src/module/iq/stanza.vala" - "src/module/message/module.vala" - "src/module/message/stanza.vala" - "src/module/presence/flag.vala" - "src/module/presence/module.vala" - "src/module/presence/stanza.vala" - "src/module/roster/flag.vala" - "src/module/roster/item.vala" - "src/module/roster/module.vala" - "src/module/sasl.vala" - "src/module/stanza.vala" - "src/module/stanza_error.vala" - "src/module/stream_error.vala" - "src/module/tls.vala" - "src/module/util.vala" - - "src/module/xep/0027_pgp/flag.vala" - "src/module/xep/0027_pgp/module.vala" - "src/module/xep/0030_service_discovery/flag.vala" - "src/module/xep/0030_service_discovery/info_result.vala" - "src/module/xep/0030_service_discovery/items_result.vala" - "src/module/xep/0030_service_discovery/module.vala" - "src/module/xep/0045_muc/flag.vala" - "src/module/xep/0045_muc/module.vala" - "src/module/xep/0048_bookmarks/module.vala" - "src/module/xep/0048_bookmarks/conference.vala" - "src/module/xep/0049_private_xml_storage.vala" - "src/module/xep/0054_vcard/module.vala" - "src/module/xep/0060_pubsub.vala" - "src/module/xep/0082_date_time_profiles.vala" - "src/module/xep/0084_user_avatars.vala" - "src/module/xep/0085_chat_state_notifications.vala" - "src/module/xep/0115_entitiy_capabilities.vala" - "src/module/xep/0199_ping.vala" - "src/module/xep/0184_message_delivery_receipts.vala" - "src/module/xep/0203_delayed_delivery.vala" - "src/module/xep/0280_message_carbons.vala" - "src/module/xep/0333_chat_markers.vala" - "src/module/xep/pixbuf_storage.vala" -CUSTOM_VAPIS - "${CMAKE_CURRENT_SOURCE_DIR}/vapi/uuid.vapi" -PACKAGES - ${ENGINE_PACKAGES} - gpgme -GENERATE_VAPI - vala-xmpp -GENERATE_HEADER - vala-xmpp -OPTIONS - --target-glib=2.38 - -g - --thread - --vapidir=${CMAKE_SOURCE_DIR}/vapi -) - -set(CFLAGS ${ENGINE_CFLAGS} ${GPGME_CFLAGS} ${LIBUUID_CFLAGS} -g ${VALA_CFLAGS}) -add_definitions(${CFLAGS}) -add_library(vala-xmpp SHARED ${ENGINE_VALA_C}) -target_link_libraries(vala-xmpp ${ENGINE_LIBRARIES} ${GPGME_LIBRARIES} ${LIBUUID_LIBRARIES}) - -add_custom_target(vala-xmpp-vapi -DEPENDS - ${CMAKE_BINARY_DIR}/vala-xmpp/vala-xmpp.vapi -) - diff --git a/vala-xmpp/src/core/namespace_state.vala b/vala-xmpp/src/core/namespace_state.vala deleted file mode 100644 index e71607fa..00000000 --- a/vala-xmpp/src/core/namespace_state.vala +++ /dev/null @@ -1,80 +0,0 @@ -using Gee; - -namespace Xmpp.Core { -public class NamespaceState { - private HashMap uri_to_name = new HashMap (); - private HashMap name_to_uri = new HashMap (); - public string current_ns_uri; - - public NamespaceState () { - add_assoc(XMLNS_URI, "xmlns"); - add_assoc("http://www.w3.org/XML/1998/namespace", "xml"); - current_ns_uri = "http://www.w3.org/XML/1998/namespace"; - } - - public NamespaceState.for_stanza () { - this(); - add_assoc("http://etherx.jabber.org/streams", "stream"); - current_ns_uri = "jabber:client"; - } - - public NamespaceState.copy (NamespaceState old) { - foreach (string key in old.uri_to_name.keys) { - add_assoc(key, old.uri_to_name[key]); - } - set_current(old.current_ns_uri); - } - - public NamespaceState.with_assoc (NamespaceState old, string ns_uri, string name) { - this.copy(old); - add_assoc(ns_uri, name); - } - - public NamespaceState.with_current (NamespaceState old, string current_ns_uri) { - this.copy(old); - set_current(current_ns_uri); - } - - public void add_assoc (string ns_uri, string name) { - name_to_uri[name] = ns_uri; - uri_to_name[ns_uri] = name; - } - - public void set_current (string current_ns_uri) { - this.current_ns_uri = current_ns_uri; - } - - public string find_name (string ns_uri) throws XmlError { - if (uri_to_name.has_key(ns_uri)) { - return uri_to_name[ns_uri]; - } - throw new XmlError.NS_DICT_ERROR(@"NS URI $ns_uri not found."); - } - - public string find_uri (string name) throws XmlError { - if (name_to_uri.has_key(name)) { - return name_to_uri[name]; - } - throw new XmlError.NS_DICT_ERROR(@"NS name $name not found."); - } - - public NamespaceState clone() { - return new NamespaceState.copy(this); - } - - public string to_string () { - StringBuilder sb = new StringBuilder (); - sb.append ("NamespaceState{"); - foreach (string key in uri_to_name.keys) { - sb.append(key); - sb.append_c('='); - sb.append(uri_to_name[key]); - sb.append_c(','); - } - sb.append("current="); - sb.append(current_ns_uri); - sb.append_c('}'); - return sb.str; - } -} -} \ No newline at end of file diff --git a/vala-xmpp/src/core/stanza_attribute.vala b/vala-xmpp/src/core/stanza_attribute.vala deleted file mode 100644 index 3169e90e..00000000 --- a/vala-xmpp/src/core/stanza_attribute.vala +++ /dev/null @@ -1,29 +0,0 @@ -namespace Xmpp.Core { -public class StanzaAttribute : StanzaEntry { - - public StanzaAttribute() {} - - public StanzaAttribute.build(string ns_uri, string name, string val) { - this.ns_uri = ns_uri; - this.name = name; - this.val = val; - } - - public string to_string() { - if (ns_uri == null) { - return @"$name='$val'"; - } else { - return @"{$ns_uri}:$name='$val'"; - } - } - - public string to_xml(NamespaceState? state_) throws XmlError { - NamespaceState state = state_ ?? new NamespaceState(); - if (ns_uri == state.current_ns_uri || (ns_uri == XMLNS_URI && name == "xmlns")) { - return @"$name='$val'"; - } else { - return "%s:%s='%s'".printf (state.find_name (ns_uri), name, val); - } - } -} -} diff --git a/vala-xmpp/src/core/stanza_node.vala b/vala-xmpp/src/core/stanza_node.vala deleted file mode 100644 index 1dfacfdd..00000000 --- a/vala-xmpp/src/core/stanza_node.vala +++ /dev/null @@ -1,297 +0,0 @@ -using Gee; - -namespace Xmpp.Core { - -public abstract class StanzaEntry { - public string? ns_uri; - public string name; - public string? val; - - public string encoded_val { - owned get { - return val.replace("&", "&").replace("\"", """).replace("'", "'").replace("<", "<").replace(">", ">"); - } - set { - string tmp = value.replace(">", ">").replace("<", "<").replace("'","'").replace(""","\""); - while (tmp.contains("&#")) { - int start = tmp.index_of("&#"); - int end = tmp.index_of(";", start); - if (end < start) break; - unichar num = -1; - if (tmp[start+2]=='x') { - tmp.substring(start+3, start-end-3).scanf("%x", &num); - } else { - num = int.parse(tmp.substring(start+2, start-end-2)); - } - tmp = tmp.splice(start, end, num.to_string()); - } - val = tmp.replace("&", "&"); - } - } - - public virtual unowned string? get_string_content() { - return val; - } -} - -public class NoStanza : StanzaEntry { - public NoStanza(string? name) { - this.name = name; - } -} - -public class StanzaNode : StanzaEntry { - public ArrayList sub_nodes = new ArrayList(); - public ArrayList attributes = new ArrayList(); - public bool has_nodes = false; - public bool pseudo = false; - - public StanzaNode () {} - - public StanzaNode.build(string name, string ns_uri = "jabber:client", ArrayList? nodes = null, ArrayList? attrs = null) { - this.ns_uri = ns_uri; - this.name = name; - if (nodes != null) this.sub_nodes.add_all(nodes); - if (attrs != null) this.attributes.add_all(attrs); - } - - public StanzaNode.text(string text) { - this.name = "#text"; - this.val = text; - } - - public StanzaNode.encoded_text(string text) { - this.name = "#text"; - this.encoded_val = text; - } - - public StanzaNode add_self_xmlns() { - return put_attribute("xmlns", ns_uri); - } - - public unowned string? get_attribute(string name, string? ns_uri = null) { - string _name = name; - string? _ns_uri = ns_uri; - if (_ns_uri == null) { - if (_name.contains(":")) { - var lastIndex = _name.last_index_of_char(':'); - _ns_uri = _name.substring(0, lastIndex); - _name = _name.substring(lastIndex + 1); - } else { - _ns_uri = this.ns_uri; - } - } - foreach(var attr in attributes) { - if (attr.ns_uri == _ns_uri && attr.name == _name) return attr.val; - } - return null; - } - - public StanzaAttribute get_attribute_raw(string name, string? ns_uri = null) { - string _name = name; - string? _ns_uri = ns_uri; - if (_ns_uri == null) { - if (_name.contains(":")) { - var lastIndex = _name.last_index_of_char(':'); - _ns_uri = _name.substring(0, lastIndex); - _name = _name.substring(lastIndex + 1); - } else { - _ns_uri = this.ns_uri; - } - } - foreach(var attr in attributes) { - if (attr.ns_uri == _ns_uri && attr.name == _name) return attr; - } - return null; - } - - public ArrayList get_attributes_by_ns_uri(string ns_uri) { - ArrayList ret = new ArrayList (); - foreach(var attr in attributes) { - if (attr.ns_uri == ns_uri) ret.add(attr); - } - return ret; - } - - public StanzaEntry get(...) { - va_list l = va_list(); - StanzaEntry? res = get_deep_attribute_(va_list.copy(l)); - if (res != null) return res; - res = get_deep_subnode_(va_list.copy(l)); - if (res != null) return res; - return new NoStanza("-"); - } - - public unowned string? get_deep_attribute(...) { - va_list l = va_list(); - var res = get_deep_attribute_(va_list.copy(l)); - if (res == null) return null; - return res.val; - } - - public StanzaAttribute? get_deep_attribute_(va_list l) { - StanzaNode? node = this; - string? attribute_name = l.arg(); - if (attribute_name == null) return null; - while(true) { - string? s = l.arg(); - if (s == null) break; - node = get_subnode(attribute_name); - if (node == null) return null; - attribute_name = s; - } - return node.get_attribute_raw(attribute_name); - } - - public StanzaNode? get_subnode(string name, string? ns_uri = null, bool recurse = false) { - string _name = name; - string? _ns_uri = ns_uri; - if (ns_uri == null) { - if (_name.contains(":")) { - var lastIndex = _name.last_index_of_char(':'); - _ns_uri = _name.substring(0, lastIndex); - _name = _name.substring(lastIndex + 1); - } else { - _ns_uri = this.ns_uri; - } - } - foreach(var node in sub_nodes) { - if (node.ns_uri == _ns_uri && node.name == _name) return node; - if (recurse) { - var x = node.get_subnode(_name, _ns_uri, recurse); - if (x != null) return x; - } - } - return null; - } - - public ArrayList get_subnodes(string name, string? ns_uri = null, bool recurse = false) { - ArrayList ret = new ArrayList(); - if (ns_uri == null) ns_uri = this.ns_uri; - foreach(var node in sub_nodes) { - if (node.ns_uri == ns_uri && node.name == name) ret.add(node); - if (recurse) { - ret.add_all(node.get_subnodes(name, ns_uri, recurse)); - } - } - return ret; - } - - public StanzaNode? get_deep_subnode(...) { - va_list l = va_list(); - return get_deep_subnode_(va_list.copy(l)); - } - - public StanzaNode? get_deep_subnode_(va_list l) { - StanzaNode? node = this; - while(true) { - string? s = l.arg(); - if (s == null) break; - node = get_subnode(s); - } - return node; - } - - public ArrayList get_all_subnodes() { - return sub_nodes; - } - - public void add_attribute(StanzaAttribute attr) { - attributes.add(attr); - } - - public override unowned string? get_string_content() { - if (val != null) return val; - if (sub_nodes.size == 1) return sub_nodes[0].get_string_content(); - return null; - } - - public StanzaNode put_attribute(string name, string val, string? ns_uri = null) { - if (name == "xmlns") ns_uri = XMLNS_URI; - if (ns_uri == null) ns_uri = this.ns_uri; - attributes.add(new StanzaAttribute.build(ns_uri, name, val)); - return this; - } - - /** - * Set only occurence - **/ - public void set_attribute(string name, string val, string? ns_uri = null) { - if (ns_uri == null) ns_uri = this.ns_uri; - foreach(var attr in attributes) { - if (attr.ns_uri == ns_uri && attr.name == name) { - attr.val = val; - return; - } - } - put_attribute(name, val, ns_uri); - } - - public StanzaNode put_node(StanzaNode node) { - sub_nodes.add(node); - return this; - } - - public string to_string(int i = 0) { - string indent = string.nfill (i * 2, ' '); - if (name == "#text") { - return @"$indent$val\n"; - } - var sb = new StringBuilder(); - sb.append(@"$indent<{$ns_uri}:$name"); - foreach (StanzaAttribute attr in attributes) { - sb.append_printf(" %s", attr.to_string()); - } - if (!has_nodes && sub_nodes.size == 0) { - sb.append(" />\n"); - } else { - sb.append(">\n"); - if (sub_nodes.size != 0) { - foreach (StanzaNode subnode in sub_nodes) { - sb.append(subnode.to_string(i+1)); - } - sb.append(@"$indent\n"); - } - } - return sb.str; - } - - public string to_xml (NamespaceState? state = null) throws XmlError { - NamespaceState my_state = state ?? new NamespaceState.for_stanza(); - if (name == "#text") return @"$encoded_val"; - foreach(var xmlns in get_attributes_by_ns_uri (XMLNS_URI)) { - if (xmlns.name == "xmlns") { - my_state = new NamespaceState.with_current(my_state, xmlns.val); - } else { - my_state = new NamespaceState.with_assoc(my_state, xmlns.val, xmlns.name); - } - } - var sb = new StringBuilder(); - if (ns_uri == my_state.current_ns_uri) { - sb.append(@"<$name"); - } else { - sb.append_printf("<%s:%s", my_state.find_name (ns_uri), name); - } - var attr_ns_state = new NamespaceState.with_current(my_state, ns_uri); - foreach (StanzaAttribute attr in attributes) { - sb.append_printf(" %s", attr.to_xml(attr_ns_state)); - } - if (!has_nodes && sub_nodes.size == 0) { - sb.append("/>"); - } else { - sb.append(">"); - if (sub_nodes.size != 0) { - foreach (StanzaNode subnode in sub_nodes) { - sb.append(subnode.to_xml(my_state)); - } - if (ns_uri == my_state.current_ns_uri) { - sb.append(@""); - } else { - sb.append_printf("", my_state.find_name (ns_uri), name); - } - } - } - return sb.str; - } -} -} diff --git a/vala-xmpp/src/core/stanza_reader.vala b/vala-xmpp/src/core/stanza_reader.vala deleted file mode 100644 index 0a90f855..00000000 --- a/vala-xmpp/src/core/stanza_reader.vala +++ /dev/null @@ -1,260 +0,0 @@ -using Gee; - -namespace Xmpp.Core { -public const string XMLNS_URI = "http://www.w3.org/2000/xmlns/"; -public const string JABBER_URI = "jabber:client"; - -public errordomain XmlError { - XML_ERROR, - NS_DICT_ERROR, - UNSUPPORTED, - EOF, - BAD_XML, - IO_ERROR -} - -public class StanzaReader { - private static int BUFFER_MAX = 4096; - - private InputStream? input; - private uint8[] buffer; - private int buffer_fill = 0; - private int buffer_pos = 0; - private Cancellable cancellable = new Cancellable(); - - private NamespaceState ns_state = new NamespaceState(); - - public StanzaReader.for_buffer(uint8[] buffer) { - this.buffer = buffer; - this.buffer_fill = buffer.length; - } - - public StanzaReader.for_string(string s) { - this.for_buffer(s.data); - } - - public StanzaReader.for_stream(InputStream input) { - this.input = input; - buffer = new uint8[BUFFER_MAX]; - } - - public void cancel() { - cancellable.cancel(); - } - - private void update_buffer() throws XmlError { - try { - if (input == null) throw new XmlError.EOF("No input stream specified and end of buffer reached."); - if (cancellable.is_cancelled()) throw new XmlError.EOF("Input stream is canceled."); - buffer_fill = (int) input.read(buffer, cancellable); - if (buffer_fill == 0) throw new XmlError.EOF("End of input stream reached."); - buffer_pos = 0; - } catch (GLib.IOError e) { - throw new XmlError.IO_ERROR("IOError in GLib: %s".printf(e.message)); - } - } - - private char read_single() throws XmlError { - if (buffer_pos >= buffer_fill) { - update_buffer(); - } - return (char) buffer[buffer_pos++]; - } - - private char peek_single() throws XmlError { - var res = read_single(); - buffer_pos--; - return res; - } - - private bool is_ws(uint8 what) { - return what == ' ' || what == '\t' || what == '\r' || what == '\n'; - } - - private void skip_single() { - buffer_pos++; - } - - private void skip_until_non_ws() throws XmlError { - while (is_ws(peek_single())) { - skip_single(); - } - } - - private string read_until_ws() throws XmlError { - var res = new StringBuilder(); - var what = peek_single(); - while(!is_ws(what)) { - res.append_c(read_single()); - what = peek_single(); - } - return res.str; - } - - private string read_until_char_or_ws(char x, char y = 0) throws XmlError { - var res = new StringBuilder(); - var what = peek_single(); - while(what != x && what != y && !is_ws(what)) { - res.append_c(read_single()); - what = peek_single(); - } - return res.str; - } - - private string read_until_char(char x) throws XmlError { - var res = new StringBuilder(); - var what = peek_single(); - while(what != x) { - res.append_c(read_single()); - what = peek_single(); - } - return res.str; - } - - private StanzaAttribute read_attribute() throws XmlError { - var res = new StanzaAttribute(); - res.name = read_until_char_or_ws('='); - if (read_single() == '=') { - var quot = peek_single(); - if (quot == '\'' || quot == '"') { - skip_single(); - res.encoded_val = read_until_char(quot); - skip_single(); - } else { - res.encoded_val = read_until_ws(); - } - } - return res; - } - - private void handle_entry_ns(StanzaEntry entry, string default_uri = ns_state.current_ns_uri) throws XmlError { - if (entry.ns_uri != null) return; - if (entry.name.contains(":")) { - var split = entry.name.split(":"); - entry.ns_uri = ns_state.find_uri(split[0]); - entry.name = split[1]; - } else { - entry.ns_uri = default_uri; - } - } - - private void handle_stanza_ns(StanzaNode res) throws XmlError { - foreach (StanzaAttribute attr in res.attributes) { - if (attr.name == "xmlns") { - attr.ns_uri = XMLNS_URI; - ns_state.set_current(attr.val); - } else if (attr.name.contains(":")) { - var split = attr.name.split(":"); - if (split[0] == "xmlns") { - attr.ns_uri = XMLNS_URI; - attr.name = split[1]; - ns_state.add_assoc(attr.val, attr.name); - } - } - } - handle_entry_ns(res); - foreach (StanzaAttribute attr in res.attributes) { - handle_entry_ns(attr, res.ns_uri); - } - } - - public StanzaNode read_node_start() throws XmlError { - var res = new StanzaNode(); - res.attributes = new ArrayList(); - var eof = false; - if (peek_single() == '<') skip_single(); - if (peek_single() == '?') res.pseudo = true; - if (peek_single() == '/') { - eof = true; - skip_single(); - res.name = read_until_char_or_ws('>'); - while(peek_single() != '>') { - skip_single(); - } - skip_single(); - res.has_nodes = false; - res.pseudo = false; - handle_stanza_ns(res); - return res; - } - res.name = read_until_char_or_ws('>', '/'); - skip_until_non_ws(); - while (peek_single() != '/' && peek_single() != '>' && peek_single() != '?') { - res.attributes.add(read_attribute()); - skip_until_non_ws(); - } - if (read_single() == '/' || res.pseudo ) { - res.has_nodes = false; - skip_single(); - } else { - res.has_nodes = true; - } - handle_stanza_ns(res); - return res; - } - - public StanzaNode read_text_node() throws XmlError { - var res = new StanzaNode(); - res.name = "#text"; - res.ns_uri = ns_state.current_ns_uri; - res.encoded_val = read_until_char('<').strip(); - return res; - } - - public StanzaNode read_root_node() throws XmlError { - skip_until_non_ws(); - if (peek_single() == '<') { - var res = read_node_start(); - if (res.pseudo) { - return read_root_node(); - } - return res; - } else { - throw new XmlError.BAD_XML("Content before root node"); - } - } - - public StanzaNode read_stanza_node(NamespaceState? baseNs = null) throws XmlError { - ns_state = baseNs ?? new NamespaceState.for_stanza(); - var res = read_node_start(); - if (res.has_nodes) { - bool finishNodeSeen = false; - do { - skip_until_non_ws(); - if (peek_single() == '<') { - skip_single(); - if (peek_single() == '/') { - skip_single(); - string desc = read_until_char('>'); - skip_single(); - if (desc.contains(":")) { - var split = desc.split(":"); - assert(split[0] == ns_state.find_name(res.ns_uri)); - assert(split[1] == res.name); - } else { - assert(ns_state.current_ns_uri == res.ns_uri); - assert(desc == res.name); - } - return res; - } else { - res.sub_nodes.add(read_stanza_node(ns_state.clone())); - ns_state = baseNs ?? new NamespaceState.for_stanza(); - } - } else { - res.sub_nodes.add(read_text_node()); - } - } while (!finishNodeSeen); - } - return res; - } - - public StanzaNode read_node(NamespaceState? baseNs = null) throws XmlError { - skip_until_non_ws(); - if (peek_single() == '<') { - return read_stanza_node(baseNs ?? new NamespaceState.for_stanza()); - } else { - return read_text_node(); - } - } -} -} diff --git a/vala-xmpp/src/core/stanza_writer.vala b/vala-xmpp/src/core/stanza_writer.vala deleted file mode 100644 index 26524d7b..00000000 --- a/vala-xmpp/src/core/stanza_writer.vala +++ /dev/null @@ -1,27 +0,0 @@ -namespace Xmpp.Core { -public class StanzaWriter { - private OutputStream output; - - public StanzaWriter.for_stream(OutputStream output) { - this.output = output; - } - - public void write_node(StanzaNode node) throws XmlError { - try { - lock(output) { - output.write_all(node.to_xml().data, null); - } - } catch (GLib.IOError e) { - throw new XmlError.IO_ERROR(@"IOError in GLib: $(e.message)"); - } - } - - public async void write(string s) throws XmlError { - try { - output.write_all(s.data, null); - } catch (GLib.IOError e) { - throw new XmlError.IO_ERROR(@"IOError in GLib: $(e.message)"); - } - } -} -} \ No newline at end of file diff --git a/vala-xmpp/src/core/xmpp_stream.vala b/vala-xmpp/src/core/xmpp_stream.vala deleted file mode 100644 index e30a1c9b..00000000 --- a/vala-xmpp/src/core/xmpp_stream.vala +++ /dev/null @@ -1,245 +0,0 @@ -using Gee; - -namespace Xmpp.Core { - -public errordomain IOStreamError { - READ, - WRITE, - CONNECT, - DISCONNECT - -} - -public class XmppStream { - private static string NS_URI = "http://etherx.jabber.org/streams"; - - public string remote_name; - public bool debug = false; - public StanzaNode? features { get; private set; default = new StanzaNode.build("features", NS_URI); } - - private IOStream? stream; - private StanzaReader? reader; - private StanzaWriter? writer; - - private ArrayList flags = new ArrayList(); - private ArrayList modules = new ArrayList(); - private bool setup_needed = false; - private bool negotiation_complete = false; - - public signal void received_node(XmppStream stream, StanzaNode node); - public signal void received_root_node(XmppStream stream, StanzaNode node); - public signal void received_features_node(XmppStream stream); - public signal void received_message_stanza(XmppStream stream, StanzaNode node); - public signal void received_presence_stanza(XmppStream stream, StanzaNode node); - public signal void received_iq_stanza(XmppStream stream, StanzaNode node); - public signal void received_nonza(XmppStream stream, StanzaNode node); - public signal void stream_negotiated(XmppStream stream); - - public void connect(string? remote_name = null) throws IOStreamError { - if (remote_name != null) this.remote_name = remote_name; - SocketClient client = new SocketClient(); - try { - SocketConnection? stream = client.connect(new NetworkService("xmpp-client", "tcp", this.remote_name)); - if (stream == null) throw new IOStreamError.CONNECT("client.connect() returned null"); - reset_stream(stream); - } catch (Error e) { - stderr.printf("CONNECTION LOST?\n"); - throw new IOStreamError.CONNECT(e.message); - } - loop(); - } - - public void disconnect() throws IOStreamError { - if (writer == null) throw new IOStreamError.DISCONNECT("trying to disconnect, but no stream open"); - if (debug) stderr.puts("OUT\n\n"); - writer.write.begin(""); - reader.cancel(); - stream.close_async.begin(); - } - - public void reset_stream(IOStream stream) { - this.stream = stream; - reader = new StanzaReader.for_stream(stream.input_stream); - writer = new StanzaWriter.for_stream(stream.output_stream); - require_setup(); - } - - public void require_setup() { - setup_needed = true; - } - - public bool is_setup_needed() { - return setup_needed; - } - - public StanzaNode read() throws IOStreamError { - if (reader == null) throw new IOStreamError.READ("trying to read, but no stream open"); - try { - var node = reader.read_node(); - if (debug) stderr.printf("IN\n%s\n", node.to_string()); - return node; - } catch (XmlError e) { - throw new IOStreamError.READ(e.message); - } - } - - public void write(StanzaNode node) throws IOStreamError { - if (writer == null) throw new IOStreamError.WRITE("trying to write, but no stream open"); - try { - if (debug) stderr.printf("OUT\n%s\n", node.to_string()); - writer.write_node(node); - } catch (XmlError e) { - throw new IOStreamError.WRITE(e.message); - } - } - - public IOStream get_stream() { - return stream; - } - - public void add_flag(XmppStreamFlag flag) { - flags.add(flag); - } - - public XmppStreamFlag? get_flag(string ns, string id) { - foreach (var flag in flags) { - if (flag.get_ns() == ns && flag.get_id() == id) { - return flag; - } - } - return null; - } - - public void remove_flag(XmppStreamFlag flag) { - flags.remove(flag); - } - - public XmppStream add_module(XmppStreamModule module) { - modules.add(module); - if (negotiation_complete || module as XmppStreamNegotiationModule != null) { - module.attach(this); - } - return this; - } - - public void remove_modules() { - foreach (XmppStreamModule module in modules) module.detach(this); - } - - public XmppStreamModule? get_module(string ns, string id) { - foreach (var module in modules) { - if (module.get_ns() == ns && module.get_id() == id) { - return module; - } - } - return null; - } - - private void setup() throws IOStreamError { - var outs = new StanzaNode.build("stream", "http://etherx.jabber.org/streams") - .put_attribute("to", remote_name) - .put_attribute("version", "1.0") - .put_attribute("xmlns", "jabber:client") - .put_attribute("stream", "http://etherx.jabber.org/streams", XMLNS_URI); - outs.has_nodes = true; - write(outs); - received_root_node(this, read_root()); - } - - private void loop() throws IOStreamError { - while(true) { - if (setup_needed) { - setup(); - setup_needed = false; - } - - StanzaNode node = read(); - received_node(this, node); - - if (node.ns_uri == NS_URI && node.name == "features") { - features = node; - received_features_node(this); - } else if (node.ns_uri == NS_URI && node.name == "stream" && node.pseudo) { - print("disconnect\n"); - disconnect(); - return; - } else if (node.ns_uri == JABBER_URI) { - if (node.name == "message") { - received_message_stanza(this, node); - } else if (node.name == "presence") { - received_presence_stanza(this, node); - } else if (node.name == "iq") { - received_iq_stanza(this, node); - } else { - received_nonza(this, node); - } - } else { - received_nonza(this, node); - } - - if (!negotiation_complete && negotiation_modules_done()) { - negotiation_complete = true; - attach_non_negotation_modules(); - stream_negotiated(this); - } - } - } - - private bool negotiation_modules_done() throws IOStreamError { - if (!setup_needed) { - bool mandatory_outstanding = false; - bool negotiation_active = false; - foreach (XmppStreamModule module in modules) { - XmppStreamNegotiationModule negotiation_module = module as XmppStreamNegotiationModule; - if (negotiation_module != null) { - if (negotiation_module.negotiation_active(this)) negotiation_active = true; - if (negotiation_module.mandatory_outstanding(this)) mandatory_outstanding = true; - } - } - if (!negotiation_active) { - if (mandatory_outstanding) { - throw new IOStreamError.CONNECT("mandatory-to-negotiate feature not negotiated"); - } else { - return true; - } - } - } - return false; - } - - private void attach_non_negotation_modules() { - foreach (XmppStreamModule module in modules) { - if (module as XmppStreamNegotiationModule == null) { - module.attach(this); - } - } - } - - private StanzaNode read_root() throws IOStreamError { - try { - var node = reader.read_root_node(); - if (debug) stderr.printf("IN\n%s\n", node.to_string()); - return node; - } catch (XmlError e) { - throw new IOStreamError.READ(e.message); - } - } -} - -public abstract class XmppStreamFlag { - internal abstract string get_ns(); - internal abstract string get_id(); -} - -public abstract class XmppStreamModule : Object { - internal abstract void attach(XmppStream stream); - internal abstract void detach(XmppStream stream); - internal abstract string get_ns(); - internal abstract string get_id(); -} - -public abstract class XmppStreamNegotiationModule : XmppStreamModule { - internal abstract bool mandatory_outstanding(XmppStream stream); - internal abstract bool negotiation_active(XmppStream stream); -} -} diff --git a/vala-xmpp/src/module/bind.vala b/vala-xmpp/src/module/bind.vala deleted file mode 100644 index d01fda7a..00000000 --- a/vala-xmpp/src/module/bind.vala +++ /dev/null @@ -1,93 +0,0 @@ -using Xmpp.Core; - -namespace Xmpp.Bind { - private const string NS_URI = "urn:ietf:params:xml:ns:xmpp-bind"; - - /** The parties to a stream MUST consider resource binding as mandatory-to-negotiate. (RFC6120 7.3.1) */ - public class Module : XmppStreamNegotiationModule { - public const string ID = "bind_module"; - - private string requested_resource; - - public signal void bound_to_resource(XmppStream stream, string my_jid); - - public Module(string requested_resource) { - this.requested_resource = requested_resource; - } - - public void iq_response_stanza(XmppStream stream, Iq.Stanza iq) { - var flag = Flag.get_flag(stream); - if (flag == null || flag.finished) return; - - if (iq.type_ == Iq.Stanza.TYPE_RESULT) { - flag.my_jid = iq.stanza.get_subnode("jid", NS_URI, true).get_string_content(); - flag.finished = true; - bound_to_resource(stream, flag.my_jid); - } - } - - public void received_features_node(XmppStream stream) { - if (stream.is_setup_needed()) return; - - var bind = stream.features.get_subnode("bind", NS_URI); - if (bind != null) { - var flag = new Flag(); - StanzaNode bind_node = new StanzaNode.build("bind", NS_URI).add_self_xmlns() - .put_node(new StanzaNode.build("resource", NS_URI).put_node(new StanzaNode.text(requested_resource))); - Iq.Module.get_module(stream).send_iq(stream, new Iq.Stanza.set(bind_node), new IqResponseListenerImpl()); - stream.add_flag(flag); - } - } - - private class IqResponseListenerImpl : Iq.ResponseListener, Object { - public void on_result(XmppStream stream, Iq.Stanza iq) { - Bind.Module.get_module(stream).iq_response_stanza(stream, iq); - } - } - - public override void attach(XmppStream stream) { - Iq.Module.require(stream); - stream.received_features_node.connect(this.received_features_node); - } - - public override void detach(XmppStream stream) { - stream.received_features_node.disconnect(this.received_features_node); - } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stream.add_module(new Bind.Module("")); - } - - public override bool mandatory_outstanding(XmppStream stream) { - return !Flag.has_flag(stream) || !Flag.get_flag(stream).finished; - } - - public override bool negotiation_active(XmppStream stream) { - return Flag.has_flag(stream) && !Flag.get_flag(stream).finished; - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - } - - public class Flag : XmppStreamFlag { - public const string ID = "bind"; - public string? my_jid; - public bool finished = false; - - public static Flag? get_flag(XmppStream stream) { - return (Flag?) stream.get_flag(NS_URI, ID); - } - - public static bool has_flag(XmppStream stream) { - return get_flag(stream) != null; - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - } -} diff --git a/vala-xmpp/src/module/iq/module.vala b/vala-xmpp/src/module/iq/module.vala deleted file mode 100644 index b5c50bd7..00000000 --- a/vala-xmpp/src/module/iq/module.vala +++ /dev/null @@ -1,89 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Iq { - private const string NS_URI = "jabber:client"; - - public class Module : XmppStreamNegotiationModule { - public const string ID = "iq_module"; - - private HashMap responseListeners = new HashMap(); - private HashMap> namespaceRegistrants = new HashMap>(); - - public void send_iq(XmppStream stream, Iq.Stanza iq, ResponseListener? listener = null) { - stream.write(iq.stanza); - if (listener != null) { - responseListeners.set(iq.id, listener); - } - } - - public void register_for_namespace(string namespace, Handler module) { - if (!namespaceRegistrants.has_key(namespace)) { - namespaceRegistrants.set(namespace, new ArrayList()); - } - namespaceRegistrants[namespace].add(module); - } - - public override void attach(XmppStream stream) { - stream.received_iq_stanza.connect(on_received_iq_stanza); - } - - public override void detach(XmppStream stream) { - stream.received_iq_stanza.disconnect(on_received_iq_stanza); - } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stream.add_module(new Iq.Module()); - } - - public override bool mandatory_outstanding(XmppStream stream) { return false; } - - public override bool negotiation_active(XmppStream stream) { return false; } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - - private void on_received_iq_stanza(XmppStream stream, StanzaNode node) { - Iq.Stanza iq = new Iq.Stanza.from_stanza(node, Bind.Flag.has_flag(stream) ? Bind.Flag.get_flag(stream).my_jid : null); - - if (iq.type_ == Iq.Stanza.TYPE_RESULT || iq.is_error()) { - if (responseListeners.has_key(iq.id)) { - ResponseListener? listener = responseListeners.get(iq.id); - if (listener != null) { - listener.on_result(stream, iq); - } - responseListeners.unset(iq.id); - } - } else { - ArrayList children = node.get_all_subnodes(); - if (children.size == 1 && namespaceRegistrants.has_key(children[0].ns_uri)) { - ArrayList handlers = namespaceRegistrants[children[0].ns_uri]; - foreach (Handler handler in handlers) { - if (iq.type_ == Iq.Stanza.TYPE_GET) { - handler.on_iq_get(stream, iq); - } else if (iq.type_ == Iq.Stanza.TYPE_SET) { - handler.on_iq_set(stream, iq); - } - } - } else { - Iq.Stanza unaviable_error = new Iq.Stanza.error(iq, new StanzaNode.build("service-unaviable", "urn:ietf:params:xml:ns:xmpp-stanzas").add_self_xmlns()); - send_iq(stream, unaviable_error); - } - } - } - } - - public interface Handler : Object { - public abstract void on_iq_get(XmppStream stream, Iq.Stanza iq); - public abstract void on_iq_set(XmppStream stream, Iq.Stanza iq); - } - - public interface ResponseListener : Object { - public abstract void on_result(XmppStream stream, Iq.Stanza iq); - } -} diff --git a/vala-xmpp/src/module/iq/stanza.vala b/vala-xmpp/src/module/iq/stanza.vala deleted file mode 100644 index 561c5866..00000000 --- a/vala-xmpp/src/module/iq/stanza.vala +++ /dev/null @@ -1,51 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Iq { - -public class Stanza : Xmpp.Stanza { - - public const string TYPE_GET = "get"; - public const string TYPE_RESULT = "result"; - public const string TYPE_SET = "set"; - - private Stanza(string? id = null) { - base.outgoing(new StanzaNode.build("iq")); - this.id = id ?? random_uuid(); - } - - public Stanza.get(StanzaNode stanza_node, string? id = null) { - this(id); - this.type_ = TYPE_GET; - stanza.put_node(stanza_node); - } - - public Stanza.result(Stanza request, StanzaNode? stanza_node = null) { - this(request.id); - this.type_ = TYPE_RESULT; - if (stanza_node != null) { - stanza.put_node(stanza_node); - } - } - - public Stanza.set(StanzaNode stanza_node, string? id = null) { - this(id); - type_ = TYPE_SET; - stanza.put_node(stanza_node); - } - - public Stanza.error(Stanza request, StanzaNode error_stanza, StanzaNode? associated_child = null) { - this(request.id); - this.type_ = TYPE_ERROR; - stanza.put_node(error_stanza); - if (associated_child != null) { - stanza.put_node(associated_child); - } - } - public Stanza.from_stanza(StanzaNode stanza_node, string? my_jid) { - base.incoming(stanza_node, my_jid); - } -} - -} diff --git a/vala-xmpp/src/module/message/module.vala b/vala-xmpp/src/module/message/module.vala deleted file mode 100644 index 10d83693..00000000 --- a/vala-xmpp/src/module/message/module.vala +++ /dev/null @@ -1,50 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Message { - private const string NS_URI = "jabber:client"; - - public class Module : XmppStreamModule { - public const string ID = "message_module"; - - public signal void pre_send_message(XmppStream stream, Message.Stanza message); - public signal void pre_received_message(XmppStream stream, Message.Stanza message); - public signal void received_message(XmppStream stream, Message.Stanza message); - - public void send_message(XmppStream stream, Message.Stanza message) { - pre_send_message(stream, message); - stream.write(message.stanza); - } - - public void received_message_stanza(XmppStream stream, StanzaNode node) { - Message.Stanza message = new Message.Stanza.from_stanza(node, Bind.Flag.get_flag(stream).my_jid); - do { - message.rerun_parsing = false; - pre_received_message(stream, message); - } while(message.rerun_parsing); - received_message(stream, message); - } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stream.add_module(new Message.Module()); - } - - public override void attach(XmppStream stream) { - Bind.Module.require(stream); - stream.received_message_stanza.connect(received_message_stanza); - } - - public override void detach(XmppStream stream) { - stream.received_message_stanza.disconnect(received_message_stanza); - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - } - -} diff --git a/vala-xmpp/src/module/message/stanza.vala b/vala-xmpp/src/module/message/stanza.vala deleted file mode 100644 index 8a9064a5..00000000 --- a/vala-xmpp/src/module/message/stanza.vala +++ /dev/null @@ -1,63 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Message { - -public class Stanza : Xmpp.Stanza { - public const string NODE_BODY = "body"; - public const string NODE_SUBJECT = "subject"; - public const string NODE_THREAD = "thread"; - - public const string TYPE_CHAT = "chat"; - public const string TYPE_GROUPCHAT = "groupchat"; - public const string TYPE_HEADLINE = "headline"; - public const string TYPE_NORMAL = "normal"; - - public bool rerun_parsing = false; - private ArrayList flags = new ArrayList(); - - public string body { - get { - StanzaNode? body_node = stanza.get_subnode(NODE_BODY); - return body_node == null? null : body_node.get_string_content(); - } - set { - StanzaNode? body_node = stanza.get_subnode(NODE_BODY); - if (body_node == null) { - body_node = new StanzaNode.build(NODE_BODY); - stanza.put_node(body_node); - } - body_node.sub_nodes.clear(); - body_node.put_node(new StanzaNode.text(value)); - } - } - - public Stanza(string? id = null) { - base.outgoing(new StanzaNode.build("message")); - stanza.set_attribute(ATTRIBUTE_ID, id ?? random_uuid()); - } - - public Stanza.from_stanza(StanzaNode stanza_node, string my_jid) { - base.incoming(stanza_node, my_jid); - } - - public void add_flag(MessageFlag flag) { - flags.add(flag); - } - - public MessageFlag? get_flag(string ns, string id) { - foreach (MessageFlag flag in flags) { - if (flag.get_ns() == ns && flag.get_id() == id) return flag; - } - return null; - } -} - -public abstract class MessageFlag : Object { - public abstract string get_ns(); - - public abstract string get_id(); -} - -} \ No newline at end of file diff --git a/vala-xmpp/src/module/presence/flag.vala b/vala-xmpp/src/module/presence/flag.vala deleted file mode 100644 index a92c9405..00000000 --- a/vala-xmpp/src/module/presence/flag.vala +++ /dev/null @@ -1,64 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Presence { - -public class Flag : XmppStreamFlag { - public const string ID = "presence"; - - private HashMap> resources = new HashMap>(); - private HashMap presences = new HashMap(); - - public Set get_available_jids() { - return resources.keys; - } - - public Gee.List? get_resources(string bare_jid) { - return resources[bare_jid]; - } - - public Presence.Stanza? get_presence(string full_jid) { - return presences[full_jid]; - } - - public void add_presence(Presence.Stanza presence) { - string bare_jid = get_bare_jid(presence.from); - if (!resources.has_key(bare_jid)) { - resources[bare_jid] = new ConcurrentList(); - } - if (resources[bare_jid].contains(presence.from)) { - resources[bare_jid].remove(presence.from); - } - resources[bare_jid].add(presence.from); - presences[presence.from] = presence; - } - - public void remove_presence(string jid) { - string bare_jid = get_bare_jid(jid); - if (resources.has_key(bare_jid)) { - if (is_bare_jid(jid)) { - foreach (string full_jid in resources[jid]) { - presences.unset(full_jid); - } - resources.unset(jid); - } else { - resources[bare_jid].remove(jid); - if (resources[bare_jid].size == 0) { - resources.unset(bare_jid); - } - presences.unset(jid); - } - } - } - - public static Flag? get_flag(XmppStream stream) { return (Flag?) stream.get_flag(NS_URI, ID); } - - public static bool has_flag(XmppStream stream) { return get_flag(stream) != null; } - - public override string get_ns() { return NS_URI; } - - public override string get_id() { return ID; } -} - -} \ No newline at end of file diff --git a/vala-xmpp/src/module/presence/module.vala b/vala-xmpp/src/module/presence/module.vala deleted file mode 100644 index 6c9d183c..00000000 --- a/vala-xmpp/src/module/presence/module.vala +++ /dev/null @@ -1,110 +0,0 @@ -using Xmpp.Core; - -namespace Xmpp.Presence { - private const string NS_URI = "jabber:client"; - - public class Module : XmppStreamModule { - public const string ID = "presence_module"; - - public signal void received_presence(XmppStream stream, Presence.Stanza presence); - public signal void pre_send_presence_stanza(XmppStream stream, Presence.Stanza presence); - public signal void initial_presence_sent(XmppStream stream, Presence.Stanza presence); - public signal void received_available(XmppStream stream, Presence.Stanza presence); - public signal void received_available_show(XmppStream stream, string jid, string show); - public signal void received_unavailable(XmppStream stream, string jid); - public signal void received_subscription_request(XmppStream stream, string jid); - public signal void received_unsubscription(XmppStream stream, string jid); - - public bool available_resource = true; - - public void request_subscription(XmppStream stream, string bare_jid) { - Presence.Stanza presence = new Presence.Stanza(); - presence.to = bare_jid; - presence.type_ = Presence.Stanza.TYPE_SUBSCRIBE; - send_presence(stream, presence); - } - - public void approve_subscription(XmppStream stream, string bare_jid) { - Presence.Stanza presence = new Presence.Stanza(); - presence.to = bare_jid; - presence.type_ = Presence.Stanza.TYPE_SUBSCRIBED; - send_presence(stream, presence); - } - - public void deny_subscription(XmppStream stream, string bare_jid) { - cancel_subscription(stream, bare_jid); - } - - public void cancel_subscription(XmppStream stream, string bare_jid) { - Presence.Stanza presence = new Presence.Stanza(); - presence.to = bare_jid; - presence.type_ = Presence.Stanza.TYPE_UNSUBSCRIBED; - send_presence(stream, presence); - } - - public void unsubscribe(XmppStream stream, string bare_jid) { - Presence.Stanza presence = new Presence.Stanza(); - presence.to = bare_jid; - presence.type_ = Presence.Stanza.TYPE_UNSUBSCRIBE; - send_presence(stream, presence); - } - - public void send_presence(XmppStream stream, Presence.Stanza presence) { - pre_send_presence_stanza(stream, presence); - stream.write(presence.stanza); - } - - public override void attach(XmppStream stream) { - stream.received_presence_stanza.connect(on_received_presence_stanza); - stream.stream_negotiated.connect(on_stream_negotiated); - stream.add_flag(new Flag()); - } - - public override void detach(XmppStream stream) { - stream.received_presence_stanza.disconnect(on_received_presence_stanza); - stream.stream_negotiated.disconnect(on_stream_negotiated); - } - - private void on_received_presence_stanza(XmppStream stream, StanzaNode node) { - Presence.Stanza presence = new Presence.Stanza.from_stanza(node, Bind.Flag.get_flag(stream).my_jid); - received_presence(stream, presence); - switch (presence.type_) { - case Presence.Stanza.TYPE_AVAILABLE: - Flag.get_flag(stream).add_presence(presence); - received_available(stream, presence); - received_available_show(stream, presence.from, presence.show); - break; - case Presence.Stanza.TYPE_UNAVAILABLE: - Flag.get_flag(stream).remove_presence(presence.from); - received_unavailable(stream, presence.from); - break; - case Presence.Stanza.TYPE_SUBSCRIBE: - received_subscription_request(stream, presence.from); - break; - case Presence.Stanza.TYPE_UNSUBSCRIBE: - received_unsubscription(stream, presence.from); - break; - } - } - - private void on_stream_negotiated(XmppStream stream) { - if (available_resource) { - Presence.Stanza presence = new Presence.Stanza(); - send_presence(stream, presence); - initial_presence_sent(stream, presence); - } - } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stream.add_module(new Presence.Module()); - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - } - -} diff --git a/vala-xmpp/src/module/presence/stanza.vala b/vala-xmpp/src/module/presence/stanza.vala deleted file mode 100644 index 9ad8f791..00000000 --- a/vala-xmpp/src/module/presence/stanza.vala +++ /dev/null @@ -1,93 +0,0 @@ -using Xmpp.Core; - -namespace Xmpp.Presence { - -public class Stanza : Xmpp.Stanza { - - public const string NODE_PRIORITY = "priority"; - public const string NODE_STATUS = "status"; - public const string NODE_SHOW = "show"; - - public const string SHOW_ONLINE = "online"; - public const string SHOW_AWAY = "away"; - public const string SHOW_CHAT = "chat"; - public const string SHOW_DND = "dnd"; - public const string SHOW_XA = "xa"; - - public const string TYPE_AVAILABLE = "available"; - public const string TYPE_PROBE = "probe"; - public const string TYPE_SUBSCRIBE = "subscribe"; - public const string TYPE_SUBSCRIBED = "subscribed"; - public const string TYPE_UNAVAILABLE = "unavailable"; - public const string TYPE_UNSUBSCRIBE = "unsubscribe"; - public const string TYPE_UNSUBSCRIBED = "unsubscribed"; - - public int priority { - get { - StanzaNode? priority_node = stanza.get_subnode(NODE_PRIORITY); - if (priority_node == null) { - return 0; - } else { - return int.parse(priority_node.get_string_content()); - } - } - set { - StanzaNode? priority_node = stanza.get_subnode(NODE_PRIORITY); - if (priority_node == null) { - priority_node = new StanzaNode.build(NODE_PRIORITY); - stanza.put_node(priority_node); - } - priority_node.val = value.to_string(); - } - } - - public string? status { - get { - StanzaNode? status_node = stanza.get_subnode(NODE_STATUS); - return status_node != null ? status_node.get_string_content() : null; - } - set { - StanzaNode? status_node = stanza.get_subnode(NODE_STATUS); - if (status_node == null) { - status_node = new StanzaNode.build(NODE_STATUS); - stanza.put_node(status_node); - } - status_node.val = value; - } - } - - public string show { - get { - StanzaNode? show_node = stanza.get_subnode(NODE_SHOW); - return show_node != null ? show_node.get_string_content() : SHOW_ONLINE; - } - set { - if (value != SHOW_ONLINE) { - StanzaNode? show_node = stanza.get_subnode(NODE_SHOW); - if (show_node == null) { - show_node = new StanzaNode.build(NODE_SHOW); - stanza.put_node(show_node); - } - show_node.val = value; - } - } - } - - public override string type_ { - get { - return base.type_ != null ? base.type_ : TYPE_AVAILABLE; - } - set { base.type_ = value; } - } - - public Stanza(string? id = null) { - stanza = new StanzaNode.build("presence"); - this.id = id ?? random_uuid(); - } - - public Stanza.from_stanza(StanzaNode stanza_node, string my_jid) { - base.incoming(stanza_node, my_jid); - } -} - -} \ No newline at end of file diff --git a/vala-xmpp/src/module/roster/flag.vala b/vala-xmpp/src/module/roster/flag.vala deleted file mode 100644 index c3e35158..00000000 --- a/vala-xmpp/src/module/roster/flag.vala +++ /dev/null @@ -1,30 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Roster { - -public class Flag : XmppStreamFlag { - public const string ID = "roster"; - public HashMap roster_items = new HashMap(); - - internal string? iq_id; - - public Collection get_roster() { - return roster_items.values; - } - - public Item? get_item(string jid) { - return roster_items[jid]; - } - - public static Flag? get_flag(XmppStream stream) { return (Flag?) stream.get_flag(NS_URI, ID); } - - public static bool has_flag(XmppStream stream) { return get_flag(stream) != null; } - - public override string get_ns() { return NS_URI; } - - public override string get_id() { return ID; } -} - -} \ No newline at end of file diff --git a/vala-xmpp/src/module/roster/item.vala b/vala-xmpp/src/module/roster/item.vala deleted file mode 100644 index 7ef76fd4..00000000 --- a/vala-xmpp/src/module/roster/item.vala +++ /dev/null @@ -1,45 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Roster { - -public class Item { - - public const string NODE_JID = "jid"; - public const string NODE_NAME = "name"; - public const string NODE_SUBSCRIPTION = "subscription"; - - public const string SUBSCRIPTION_NONE = "none"; - public const string SUBSCRIPTION_TO = "to"; - public const string SUBSCRIPTION_FROM = "from"; - public const string SUBSCRIPTION_BOTH = "both"; - public const string SUBSCRIPTION_REMOVE = "remove"; - - public StanzaNode stanza_node; - - public string jid { - get { return stanza_node.get_attribute(NODE_JID); } - set { stanza_node.set_attribute(NODE_JID, value); } - } - - public string? name { - get { return stanza_node.get_attribute(NODE_NAME); } - set { stanza_node.set_attribute(NODE_NAME, value); } - } - - public string? subscription { - get { return stanza_node.get_attribute(NODE_SUBSCRIPTION); } - set { stanza_node.set_attribute(NODE_SUBSCRIPTION, value); } - } - - public Item() { - stanza_node = new StanzaNode.build("item", NS_URI); - } - - public Item.from_stanza_node(StanzaNode stanza_node) { - this.stanza_node = stanza_node; - } -} - -} \ No newline at end of file diff --git a/vala-xmpp/src/module/roster/module.vala b/vala-xmpp/src/module/roster/module.vala deleted file mode 100644 index c8b09710..00000000 --- a/vala-xmpp/src/module/roster/module.vala +++ /dev/null @@ -1,125 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Roster { - private const string NS_URI = "jabber:iq:roster"; - - public class Module : XmppStreamModule, Iq.Handler { - public const string ID = "roster_module"; - - public signal void received_roster(XmppStream stream, Collection roster); - public signal void item_removed(XmppStream stream, Item roster_item); - public signal void item_updated(XmppStream stream, Item roster_item); - - public bool interested_resource = true; - - /** - * Add a jid to the roster - */ - public void add_jid(XmppStream stream, string jid, string? handle = null) { - Item roster_item = new Item(); - roster_item.jid = jid; - if (handle != null) { - roster_item.name = handle; - } - roster_set(stream, roster_item); - } - - /** - * Remove a jid from the roster - */ - public void remove_jid(XmppStream stream, string jid) { - Item roster_item = new Item(); - roster_item.jid = jid; - roster_item.subscription = Item.SUBSCRIPTION_REMOVE; - - roster_set(stream, roster_item); - } - - /** - * Set a handle for a jid - * @param handle Handle to be set. If null, any handle will be removed. - */ - public void set_jid_handle(XmppStream stream, string jid, string? handle) { - Item roster_item = new Item(); - roster_item.jid = jid; - if (handle != null) { - roster_item.name = handle; - } - - roster_set(stream, roster_item); - } - - public void on_iq_set(XmppStream stream, Iq.Stanza iq) { - StanzaNode? query_node = iq.stanza.get_subnode("query", NS_URI); - if (query_node == null) return; - - Flag flag = Flag.get_flag(stream); - Item item = new Item.from_stanza_node(query_node.get_subnode("item", NS_URI)); - switch (item.subscription) { - case Item.SUBSCRIPTION_REMOVE: - flag.roster_items.unset(item.jid); - item_removed(stream, item); - break; - default: - flag.roster_items[item.jid] = item; - item_updated(stream, item); - break; - } - } - - public void on_iq_get(XmppStream stream, Iq.Stanza iq) { } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stream.add_module(new Module()); - } - - public override void attach(XmppStream stream) { - Iq.Module.require(stream); - Iq.Module.get_module(stream).register_for_namespace(NS_URI, this); - Presence.Module.require(stream); - Presence.Module.get_module(stream).initial_presence_sent.connect(roster_get); - stream.add_flag(new Flag()); - } - - public override void detach(XmppStream stream) { - Presence.Module.get_module(stream).initial_presence_sent.disconnect(roster_get); - } - - internal override string get_ns() { return NS_URI; } - internal override string get_id() { return ID; } - - private void roster_get(XmppStream stream) { - Flag.get_flag(stream).iq_id = random_uuid(); - StanzaNode query_node = new StanzaNode.build("query", NS_URI).add_self_xmlns(); - Iq.Stanza iq = new Iq.Stanza.get(query_node, Flag.get_flag(stream).iq_id); - Iq.Module.get_module(stream).send_iq(stream, iq, new IqResponseListenerImpl()); - } - - private class IqResponseListenerImpl : Iq.ResponseListener, Object { - public void on_result(XmppStream stream, Iq.Stanza iq) { - Flag flag = Flag.get_flag(stream); - if (iq.id == flag.iq_id) { - StanzaNode? query_node = iq.stanza.get_subnode("query", NS_URI); - foreach (StanzaNode item_node in query_node.sub_nodes) { - Item item = new Item.from_stanza_node(item_node); - flag.roster_items[item.jid] = item; - } - Module.get_module(stream).received_roster(stream, flag.roster_items.values); - } - } - } - - private void roster_set(XmppStream stream, Item roster_item) { - StanzaNode query_node = new StanzaNode.build("query", NS_URI).add_self_xmlns() - .put_node(roster_item.stanza_node); - Iq.Stanza iq = new Iq.Stanza.set(query_node); - Iq.Module.get_module(stream).send_iq(stream, iq, null); - } - } -} diff --git a/vala-xmpp/src/module/sasl.vala b/vala-xmpp/src/module/sasl.vala deleted file mode 100644 index 07e3f5c4..00000000 --- a/vala-xmpp/src/module/sasl.vala +++ /dev/null @@ -1,139 +0,0 @@ -using Xmpp.Core; - -namespace Xmpp.PlainSasl { - private const string NS_URI = "urn:ietf:params:xml:ns:xmpp-sasl"; - - public class Module : XmppStreamNegotiationModule { - public const string ID = "plain_module"; - private const string MECHANISM = "PLAIN"; - - private string name; - private string password; - public bool use_full_name = false; - - public signal void received_auth_failure(XmppStream stream, StanzaNode node); - - public Module(string name, string password) { - this.name = name; - this.password = password; - } - - public override void attach(XmppStream stream) { - stream.received_features_node.connect(this.received_features_node); - stream.received_nonza.connect(this.received_nonza); - } - - public override void detach(XmppStream stream) { - stream.received_features_node.disconnect(this.received_features_node); - stream.received_nonza.disconnect(this.received_nonza); - } - - public void received_nonza(XmppStream stream, StanzaNode node) { - if (node.ns_uri == NS_URI) { - if (node.name == "success") { - stream.require_setup(); - Flag.get_flag(stream).finished = true; - } else if (node.name == "failure") { - stream.remove_flag(Flag.get_flag(stream)); - received_auth_failure(stream, node); - } - } - } - - public void received_features_node(XmppStream stream) { - if (Flag.has_flag(stream)) return; - if (stream.is_setup_needed()) return; - if (!Tls.Flag.has_flag(stream) || !Tls.Flag.get_flag(stream).finished) return; - - var mechanisms = stream.features.get_subnode("mechanisms", NS_URI); - if (mechanisms != null) { - bool supportsPlain = false; - foreach (var mechanism in mechanisms.sub_nodes) { - if (mechanism.name != "mechanism" || mechanism.ns_uri != NS_URI) continue; - var text = mechanism.get_subnode("#text"); - if (text != null && text.val == MECHANISM) { - supportsPlain = true; - } - } - if (!supportsPlain) { - stderr.printf("Server at %s does not support %s auth, use full-features Sasl implementation!\n", stream.remote_name, MECHANISM); - return; - } - - if (!name.contains("@")) { - name = "%s@%s".printf(name, stream.remote_name); - } - if (!use_full_name && name.contains("@")) { - var split = name.split("@"); - if (split[1] == stream.remote_name) { - name = split[0]; - } else { - use_full_name = true; - } - } - var name = this.name; - if (!use_full_name && name.contains("@")) { - var split = name.split("@"); - if (split[1] == stream.remote_name) { - name = split[0]; - } - } - stream.write(new StanzaNode.build("auth", NS_URI).add_self_xmlns() - .put_attribute("mechanism", MECHANISM) - .put_node(new StanzaNode.text(Base64.encode(get_plain_bytes(name, password))))); - var flag = new Flag(); - flag.mechanism = MECHANISM; - flag.name = name; - stream.add_flag(flag); - } - } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stderr.printf("PlainSaslModule required but not attached!\n"); - } - - private static uchar[] get_plain_bytes(string name_s, string password_s) { - var name = name_s.to_utf8(); - var password = password_s.to_utf8(); - uchar[] res = new uchar[name.length + password.length + 2]; - res[0] = 0; - res[name.length + 1] = 0; - for(int i = 0; i < name.length; i++) { res[i + 1] = (uchar) name[i]; } - for(int i = 0; i < password.length; i++) { res[i + name.length + 2] = (uchar) password[i]; } - return res; - } - - public override bool mandatory_outstanding(XmppStream stream) { - return !Flag.has_flag(stream) || !Flag.get_flag(stream).finished; - } - - public override bool negotiation_active(XmppStream stream) { - return Flag.has_flag(stream) && !Flag.get_flag(stream).finished; - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - } - - public class Flag : XmppStreamFlag { - public const string ID = "sasl"; - public string mechanism; - public string name; - public bool finished = false; - - public static Flag? get_flag(XmppStream stream) { - return (Flag?) stream.get_flag(NS_URI, ID); - } - - public static bool has_flag(XmppStream stream) { - return get_flag(stream) != null; - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - } -} diff --git a/vala-xmpp/src/module/stanza.vala b/vala-xmpp/src/module/stanza.vala deleted file mode 100644 index f6af9623..00000000 --- a/vala-xmpp/src/module/stanza.vala +++ /dev/null @@ -1,70 +0,0 @@ -using Xmpp.Core; - -namespace Xmpp { - - public class Stanza { - - public const string ATTRIBUTE_FROM = "from"; - public const string ATTRIBUTE_ID = "id"; - public const string ATTRIBUTE_TO = "to"; - public const string ATTRIBUTE_TYPE = "type"; - - public const string TYPE_ERROR = "error"; - - private string? my_jid; - - public virtual string? from { - owned get { - string? from_attribute = stanza.get_attribute(ATTRIBUTE_FROM); - // "when a client receives a stanza that does not include a 'from' attribute, it MUST assume that the stanza - // is from the user's account on the server." (RFC6120 8.1.2.1) - if (from_attribute != null) return from_attribute; - if (my_jid != null) { - string my_bare_jid = get_bare_jid(my_jid); // has to be left-side value - return my_bare_jid; - } - return null; - } - set { stanza.set_attribute(ATTRIBUTE_FROM, value); } - } - - public virtual string? id { - get { return stanza.get_attribute(ATTRIBUTE_ID); } - set { stanza.set_attribute(ATTRIBUTE_ID, value); } - } - - public virtual string? to { - owned get { - string? to_attribute = stanza.get_attribute(ATTRIBUTE_TO); - // "if the stanza does not include a 'to' address then the client MUST treat it as if the 'to' address were - // included with a value of the client's full JID." (RFC6120 8.1.1.1) - return to_attribute == null ? my_jid : to_attribute; - } - set { stanza.set_attribute(ATTRIBUTE_TO, value); } - } - - public virtual string type_ { - get { return stanza.get_attribute(ATTRIBUTE_TYPE); } - set { stanza.set_attribute(ATTRIBUTE_TYPE, value); } - } - - public StanzaNode stanza; - - public Stanza.incoming(StanzaNode stanza, string? my_jid) { - this.stanza = stanza; - this.my_jid = my_jid; - } - - public Stanza.outgoing(StanzaNode stanza) { - this.stanza = stanza; - } - - public bool is_error() { - return type_ == TYPE_ERROR; - } - - public ErrorStanza? get_error() { - return new ErrorStanza.from_stanza(this.stanza); - } - } -} \ No newline at end of file diff --git a/vala-xmpp/src/module/stanza_error.vala b/vala-xmpp/src/module/stanza_error.vala deleted file mode 100644 index be4633e9..00000000 --- a/vala-xmpp/src/module/stanza_error.vala +++ /dev/null @@ -1,69 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp { - - public class ErrorStanza { - public const string CONDITION_BAD_REQUEST = "bad-request"; - public const string CONDITION_CONFLICT = "conflict"; - public const string CONDITION_FEATURE_NOT_IMPLEMENTED = "feature-not-implemented"; - public const string CONDITION_FORBIDDEN = "forbidden"; - public const string CONDITION_GONE = "gone"; - public const string CONDITION_INTERNAL_SERVER_ERROR = "internal-server-error"; - public const string CONDITION_ITEM_NOT_FOUND = "item-not-found"; - public const string CONDITION_JID_MALFORMED = "jid-malformed"; - public const string CONDITION_NOT_ACCEPTABLE = "not-acceptable"; - public const string CONDITION_NOT_ALLOWED = "not-allowed"; - public const string CONDITION_NOT_AUTHORIZED = "not-authorized"; - public const string CONDITION_POLICY_VIOLATION = "policy-violation"; - public const string CONDITION_RECIPIENT_UNAVAILABLE = "recipient-unavailable"; - public const string CONDITION_REDIRECT = "redirect"; - public const string CONDITION_REGISTRATION_REQUIRED = "registration-required"; - public const string CONDITION_REMOTE_SERVER_NOT_FOUND = "remote-server-not-found"; - public const string CONDITION_REMOTE_SERVER_TIMEOUT = "remote-server-timeout"; - public const string CONDITION_RESOURCE_CONSTRAINT = "resource-constraint"; - public const string CONDITION_SERVICE_UNAVAILABLE = "service-unavailable"; - public const string CONDITION_SUBSCRIPTION_REQUIRED = "subscription-required"; - public const string CONDITION_UNDEFINED_CONDITION = "undefined-condition"; - public const string CONDITION_UNEXPECTED_REQUEST = "unexpected-request"; - - public const string TYPE_AUTH = "auth"; - public const string TYPE_CANCEL = "cancel"; - public const string TYPE_CONTINUE = "continue"; - public const string TYPE_MODIFY = "modify"; - public const string TYPE_WAIT = "wait"; - - public string? by { - get { return error_node.get_attribute("by"); } - } - - public string condition { - get { - ArrayList subnodes = error_node.sub_nodes; - foreach (StanzaNode subnode in subnodes) { // TODO get subnode by ns - if (subnode.ns_uri == "urn:ietf:params:xml:ns:xmpp-stanzas") { - return subnode.name; - } - } - return CONDITION_UNDEFINED_CONDITION; // TODO hm! - } - } - - public string? original_id { - get { return stanza.get_attribute("id"); } - } - - public string type_ { - get { return stanza.get_attribute("type"); } - } - - public StanzaNode stanza; - private StanzaNode error_node; - - public ErrorStanza.from_stanza(StanzaNode stanza) { - this.stanza = stanza; - error_node = stanza.get_subnode("error"); - } - } -} \ No newline at end of file diff --git a/vala-xmpp/src/module/stream_error.vala b/vala-xmpp/src/module/stream_error.vala deleted file mode 100644 index 73e2bb36..00000000 --- a/vala-xmpp/src/module/stream_error.vala +++ /dev/null @@ -1,119 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.StreamError { - private const string NS_URI = "jabber:client"; - private const string NS_ERROR = "urn:ietf:params:xml:ns:xmpp-streams"; - - public class Module : XmppStreamModule { - public const string ID = "stream_error_module"; - - public override void attach(XmppStream stream) { - stream.received_nonza.connect(on_received_nonstanza); - } - - public override void detach(XmppStream stream) { - stream.received_nonza.disconnect(on_received_nonstanza); - } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stream.add_module(new StreamError.Module()); - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - - private void on_received_nonstanza(XmppStream stream, StanzaNode node) { - if (node.name == "error" && node.ns_uri == "http://etherx.jabber.org/streams") { - stream.add_flag(generate_error_flag(node)); - } - } - - private Flag generate_error_flag(StanzaNode node) { - string? subnode_name = null; - ArrayList subnodes = node.sub_nodes; - foreach (StanzaNode subnode in subnodes) { // TODO get subnode by ns - if (subnode.ns_uri == "urn:ietf:params:xml:ns:xmpp-streams" && subnode.name != "text") { - subnode_name = subnode.name; - } - } - Flag flag = new StreamError.Flag(); - flag.error_type = subnode_name; - switch (subnode_name) { - case "bad-format": - case "conflict": - case "connection-timeout": - case "bad-namespace-prefix": - flag.reconnection_recomendation = StreamError.Flag.Reconnect.NOW; - break; - case "host-gone": - case "host-unknown": - flag.reconnection_recomendation = StreamError.Flag.Reconnect.LATER; - break; - case "improper-addressing": - case "internal-server-error": - case "invalid-from": - case "invalid-namespace": - case "invalid-xml": - case "not-authorized": - case "not-well-formed": - case "policy-violation": - case "remote-connection-failed": - case "reset": - flag.reconnection_recomendation = StreamError.Flag.Reconnect.NOW; - break; - case "resource-constraint": - flag.reconnection_recomendation = StreamError.Flag.Reconnect.LATER; - break; - case "restricted-xml": - flag.reconnection_recomendation = StreamError.Flag.Reconnect.NOW; - break; - case "see-other-host": - case "system-shutdown": - flag.reconnection_recomendation = StreamError.Flag.Reconnect.LATER; - break; - case "undefined-condition": - case "unsupported-encoding": - case "unsupported-feature": - case "unsupported-stanza-type": - case "unsupported-version": - flag.reconnection_recomendation = StreamError.Flag.Reconnect.NOW; - break; - } - - if (subnode_name == "conflict") flag.resource_rejected = true; - return flag; - } - } - - public class Flag : XmppStreamFlag { - public const string ID = "stream_error"; - - public enum Reconnect { - UNKNOWN, - NOW, - LATER, - NEVER - } - - public string? error_type; - public Reconnect reconnection_recomendation = Reconnect.UNKNOWN; - public bool resource_rejected = false; - - public static Flag? get_flag(XmppStream stream) { - return (Flag?) stream.get_flag(NS_URI, ID); - } - - public static bool has_flag(XmppStream stream) { - return get_flag(stream) != null; - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - } -} diff --git a/vala-xmpp/src/module/tls.vala b/vala-xmpp/src/module/tls.vala deleted file mode 100644 index 1f8447ec..00000000 --- a/vala-xmpp/src/module/tls.vala +++ /dev/null @@ -1,99 +0,0 @@ -using Xmpp.Core; - -namespace Xmpp.Tls { - private const string NS_URI = "urn:ietf:params:xml:ns:xmpp-tls"; - - public class Module : XmppStreamNegotiationModule { - public const string ID = "tls_module"; - - public bool require { get; set; default = true; } - public bool server_supports_tls = false; - public bool server_requires_tls = false; - public SocketConnectable? identity = null; - - public override void attach(XmppStream stream) { - stream.received_features_node.connect(this.received_features_node); - stream.received_nonza.connect(this.received_nonza); - } - - public override void detach(XmppStream stream) { - stream.received_features_node.disconnect(this.received_features_node); - stream.received_nonza.disconnect(this.received_nonza); - } - - private void received_nonza(XmppStream stream, StanzaNode node) { - if (node.ns_uri == NS_URI && node.name == "proceed") { - try { - var conn = TlsClientConnection.new(stream.get_stream(), identity); - // TODO: Add certificate error handling, that is, allow the - // program to handle certificate errors. The certificate - // *is checked* by TlsClientConnection, and connection is - // not allowed to continue in case that there is an error. - stream.reset_stream(conn); - - var flag = Flag.get_flag(stream); - flag.peer_certificate = conn.get_peer_certificate(); - flag.finished = true; - } catch (Error e) { - stderr.printf("Failed to start TLS: %s\n", e.message); - } - } - } - - private void received_features_node(XmppStream stream) { - if (Flag.has_flag(stream)) return; - if (stream.is_setup_needed()) return; - - var starttls = stream.features.get_subnode("starttls", NS_URI); - if (starttls != null) { - server_supports_tls = true; - if (starttls.get_subnode("required") != null || stream.features.get_all_subnodes().size == 1) { - server_requires_tls = true; - } - if (server_requires_tls || require) { - try { - stream.write(new StanzaNode.build("starttls", NS_URI).add_self_xmlns()); - } catch (IOStreamError e) { - stderr.printf("Failed to request TLS: %s\n", e.message); - } - } - if (identity == null) { - identity = new NetworkService("xmpp-client", "tcp", stream.remote_name); - } - stream.add_flag(new Flag()); - } - } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public override bool mandatory_outstanding(XmppStream stream) { - return require && (!Flag.has_flag(stream) || !Flag.get_flag(stream).finished); - } - - public override bool negotiation_active(XmppStream stream) { - return Flag.has_flag(stream) && !Flag.get_flag(stream).finished; - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - } - - public class Flag : XmppStreamFlag { - public const string ID = "tls_flag"; - public TlsCertificate? peer_certificate; - public bool finished = false; - - public static Flag? get_flag(XmppStream stream) { - return (Flag?) stream.get_flag(NS_URI, ID); - } - - public static bool has_flag(XmppStream stream) { - return get_flag(stream) != null; - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - } -} diff --git a/vala-xmpp/src/module/util.vala b/vala-xmpp/src/module/util.vala deleted file mode 100644 index 65a9b261..00000000 --- a/vala-xmpp/src/module/util.vala +++ /dev/null @@ -1,21 +0,0 @@ -namespace Xmpp { - string? get_bare_jid(string jid) { - return jid.split("/")[0]; - } - - bool is_bare_jid(string jid) { - return !jid.contains("/"); - } - - string? get_resource_part(string jid) { - return jid.split("/")[1]; - } - - public string random_uuid() { - uint8[] rand = new uint8[16]; - char[] str = new char[37]; - UUID.generate_random(rand); - UUID.unparse_upper(rand, str); - return (string) str; - } -} \ No newline at end of file diff --git a/vala-xmpp/src/module/xep/0027_pgp/flag.vala b/vala-xmpp/src/module/xep/0027_pgp/flag.vala deleted file mode 100644 index 03844afa..00000000 --- a/vala-xmpp/src/module/xep/0027_pgp/flag.vala +++ /dev/null @@ -1,24 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Xep.Pgp { - -public class Flag : XmppStreamFlag { - public const string ID = "pgp"; - public HashMap key_ids = new HashMap(); - - public string? get_key_id(string jid) { return key_ids[get_bare_jid(jid)]; } - - public void set_key_id(string jid, string key) { key_ids[get_bare_jid(jid)] = key; } - - public static Flag? get_flag(XmppStream stream) { return (Flag?) stream.get_flag(NS_URI, ID); } - - public static bool has_flag(XmppStream stream) { return get_flag(stream) != null; } - - public override string get_ns() { return NS_URI; } - - public override string get_id() { return ID; } -} - -} \ No newline at end of file diff --git a/vala-xmpp/src/module/xep/0027_pgp/module.vala b/vala-xmpp/src/module/xep/0027_pgp/module.vala deleted file mode 100644 index fee6b9e4..00000000 --- a/vala-xmpp/src/module/xep/0027_pgp/module.vala +++ /dev/null @@ -1,206 +0,0 @@ -using GPG; - -using Xmpp.Core; - -namespace Xmpp.Xep.Pgp { - private const string NS_URI = "jabber:x"; - private const string NS_URI_ENCRYPTED = NS_URI + ":encrypted"; - private const string NS_URI_SIGNED = NS_URI + ":signed"; - - public class Module : XmppStreamModule { - public const string ID = "0027_current_pgp_usage"; - - public signal void received_jid_key_id(XmppStream stream, string jid, string key_id); - - private static Object mutex = new Object(); - - private string? signed_status; - private string? own_key_id; - - public Module() { - GPG.check_version(); - signed_status = gpg_sign(""); - if (signed_status != null) own_key_id = gpg_verify(signed_status, ""); - } - - public bool encrypt(Message.Stanza message, string key_id) { - string? enc_body = gpg_encrypt(message.body, new string[] {key_id, own_key_id}); - if (enc_body != null) { - message.stanza.put_node(new StanzaNode.build("x", NS_URI_ENCRYPTED).add_self_xmlns().put_node(new StanzaNode.text(enc_body))); - message.body = "[This message is OpenPGP encrypted (see XEP-0027)]"; - return true; - } - return false; - } - - public string? get_cyphertext(Message.Stanza message) { - StanzaNode? x_node = message.stanza.get_subnode("x", NS_URI_ENCRYPTED); - return x_node == null ? null : x_node.get_string_content(); - } - - public override void attach(XmppStream stream) { - Presence.Module.require(stream); - Presence.Module.get_module(stream).received_presence.connect(on_received_presence); - Presence.Module.get_module(stream).pre_send_presence_stanza.connect(on_pre_send_presence_stanza); - Message.Module.require(stream); - Message.Module.get_module(stream).pre_received_message.connect(on_pre_received_message); - stream.add_flag(new Flag()); - } - - public override void detach(XmppStream stream) { - Presence.Module.get_module(stream).received_presence.disconnect(on_received_presence); - Presence.Module.get_module(stream).pre_send_presence_stanza.disconnect(on_pre_send_presence_stanza); - Message.Module.get_module(stream).pre_received_message.disconnect(on_pre_received_message); - } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stream.add_module(new Module()); - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - - private void on_received_presence(XmppStream stream, Presence.Stanza presence) { - StanzaNode x_node = presence.stanza.get_subnode("x", NS_URI_SIGNED); - if (x_node != null) { - string? sig = x_node.get_string_content(); - if (sig != null) { - string signed_data = presence.status == null ? "" : presence.status; - string? key_id = gpg_verify(sig, signed_data); - if (key_id != null) { - Flag.get_flag(stream).set_key_id(presence.from, key_id); - received_jid_key_id(stream, presence.from, key_id); - } - } - } - } - - private void on_pre_send_presence_stanza(XmppStream stream, Presence.Stanza presence) { - if (presence.type_ == Presence.Stanza.TYPE_AVAILABLE && signed_status != null) { - presence.stanza.put_node(new StanzaNode.build("x", NS_URI_SIGNED).add_self_xmlns().put_node(new StanzaNode.text(signed_status))); - } - } - - private void on_pre_received_message(XmppStream stream, Message.Stanza message) { - string? encrypted = get_cyphertext(message); - if (encrypted != null) { - MessageFlag flag = new MessageFlag(); - message.add_flag(flag); - string? decrypted = gpg_decrypt(encrypted); - if (decrypted != null) { - flag.decrypted = true; - message.body = decrypted; - } - } - } - - private static string? gpg_encrypt(string plain, string[] key_ids) { - lock (mutex) { - GPG.Context context; - GPGError.ErrorCode e = GPG.Context.Context(out context); if (e != GPGError.ErrorCode.NO_ERROR) return null; - context.set_armor(true); - - Key[] keys = new Key[key_ids.length]; - for (int i = 0; i < key_ids.length; i++) { - Key key; - e = context.get_key(key_ids[i], out key, false); if (e != GPGError.ErrorCode.NO_ERROR) return null; - keys[i] = key; - } - - GPG.Data plain_data; - e = GPG.Data.create_from_memory(out plain_data, plain.data, false); - GPG.Data enc_data; - e = GPG.Data.create(out enc_data); - e = context.op_encrypt(keys, GPG.EncryptFlags.ALWAYS_TRUST, plain_data, enc_data); - - string encr = get_string_from_data(enc_data); - int encryption_start = encr.index_of("\n\n") + 2; - return encr.substring(encryption_start, encr.length - "\n-----END PGP MESSAGE-----".length - encryption_start); - } - } - - private static string? gpg_decrypt(string enc) { - lock (mutex) { - string armor = "-----BEGIN PGP MESSAGE-----\n\n" + enc + "\n-----END PGP MESSAGE-----"; - - GPG.Data enc_data; - GPGError.ErrorCode e = GPG.Data.create_from_memory(out enc_data, armor.data, false); if (e != GPGError.ErrorCode.NO_ERROR) return null; - GPG.Data dec_data; - e = GPG.Data.create(out dec_data); if (e != GPGError.ErrorCode.NO_ERROR) return null; - GPG.Context context; - e = GPG.Context.Context(out context); if (e != GPGError.ErrorCode.NO_ERROR) return null; - e = context.op_decrypt(enc_data, dec_data); if (e != GPGError.ErrorCode.NO_ERROR) return null; - - string plain = get_string_from_data(dec_data); - return plain; - } - } - - private static string? gpg_verify(string sig, string signed_text) { - lock (mutex) { - string armor = "-----BEGIN PGP MESSAGE-----\n\n" + sig + "\n-----END PGP MESSAGE-----"; - - GPG.Data sig_data; - GPGError.ErrorCode e = GPG.Data.create_from_memory(out sig_data, armor.data, false); if (e != GPGError.ErrorCode.NO_ERROR) return null; - GPG.Data plain_data; - e = GPG.Data.create(out plain_data); if (e != GPGError.ErrorCode.NO_ERROR) return null; - GPG.Context context; - e = GPG.Context.Context(out context); if (e != GPGError.ErrorCode.NO_ERROR) return null; - e = context.op_verify(sig_data, null, plain_data); if (e != GPGError.ErrorCode.NO_ERROR) return null; - GPG.VerifyResult* verify_res = context.op_verify_result(); - if (verify_res == null || verify_res.signatures == null) return null; - return verify_res.signatures.fpr; - } - } - - private static string? gpg_sign(string status) { - lock (mutex) { - GPG.Data status_data; - GPGError.ErrorCode e = GPG.Data.create_from_memory(out status_data, status.data, false); if (e != GPGError.ErrorCode.NO_ERROR) return null; - GPG.Data signed_data; - e = GPG.Data.create(out signed_data); if (e != GPGError.ErrorCode.NO_ERROR) return null; - GPG.Context context; - e = GPG.Context.Context(out context); if (e != GPGError.ErrorCode.NO_ERROR) return null; - e = context.op_sign(status_data, signed_data, GPG.SigMode.CLEAR); if (e != GPGError.ErrorCode.NO_ERROR) return null; - - string signed = get_string_from_data(signed_data); - int signature_start = signed.index_of("-----BEGIN PGP SIGNATURE-----"); - signature_start = signed.index_of("\n\n", signature_start) + 2; - return signed.substring(signature_start, signed.length - "\n-----END PGP SIGNATURE-----".length - signature_start); - } - } - - private static string get_string_from_data(GPG.Data data) { - data.seek(0); - uint8[] buf = new uint8[256]; - ssize_t? len = null; - string res = ""; - do { - len = data.read(buf); - if (len > 0) { - string part = (string) buf; - part = part.substring(0, (long) len); - res += part; - } - } while (len > 0); - return res; - } - } - - public class MessageFlag : Message.MessageFlag { - public const string id = "pgp"; - - public bool decrypted = false; - - public static MessageFlag? get_flag(Message.Stanza message) { - return (MessageFlag) message.get_flag(NS_URI, id); - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return id; } - } -} diff --git a/vala-xmpp/src/module/xep/0030_service_discovery/flag.vala b/vala-xmpp/src/module/xep/0030_service_discovery/flag.vala deleted file mode 100644 index 5be9f2eb..00000000 --- a/vala-xmpp/src/module/xep/0030_service_discovery/flag.vala +++ /dev/null @@ -1,33 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Xep.ServiceDiscovery { - -public class Flag : XmppStreamFlag { - public const string ID = "service_discovery"; - - private HashMap> entity_features = new HashMap>(); - public ArrayList features = new ArrayList(); - - public bool? has_entity_feature(string jid, string feature) { - if (!entity_features.has_key(jid)) return null; - return entity_features[jid].contains(feature); - } - - public void set_entitiy_features(string jid, ArrayList features) { - entity_features[jid] = features; - } - - public void add_own_feature(string feature) { features.add(feature); } - - public static Flag? get_flag(XmppStream stream) { return (Flag?) stream.get_flag(NS_URI, ID); } - - public static bool has_flag(XmppStream stream) { return get_flag(stream) != null; } - - public override string get_ns() { return NS_URI; } - - public override string get_id() { return ID; } -} - -} \ No newline at end of file diff --git a/vala-xmpp/src/module/xep/0030_service_discovery/info_result.vala b/vala-xmpp/src/module/xep/0030_service_discovery/info_result.vala deleted file mode 100644 index 7e0f0ea4..00000000 --- a/vala-xmpp/src/module/xep/0030_service_discovery/info_result.vala +++ /dev/null @@ -1,78 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Xep.ServiceDiscovery { - -public class InfoResult { - public Iq.Stanza iq { get; private set; } - - public ArrayList features { - owned get { - ArrayList ret = new ArrayList(); - foreach (StanzaNode feature_node in iq.stanza.get_subnode("query", NS_URI_INFO).get_subnodes("feature", NS_URI_INFO)) { - ret.add(feature_node.get_attribute("var", NS_URI_INFO)); - } - return ret; - } - set { - foreach (string feature in value) { - add_feature(feature); - } - } - } - - public ArrayList identities { - owned get { - ArrayList ret = new ArrayList(); - foreach (StanzaNode feature_node in iq.stanza.get_subnode("query", NS_URI_INFO).get_subnodes("identity", NS_URI_INFO)) { - ret.add(new Identity(feature_node.get_attribute("category", NS_URI_INFO), - feature_node.get_attribute("type", NS_URI_INFO), - feature_node.get_attribute("name", NS_URI_INFO))); - } - return ret; - } - set { - foreach (Identity identity in value) { - add_identity(identity); - } - } - } - - public void add_feature(string feature) { - iq.stanza.get_subnode("query", NS_URI_INFO).put_node(new StanzaNode.build("feature", NS_URI_INFO).put_attribute("var", feature)); - } - - public void add_identity(Identity identity) { - StanzaNode identity_node = new StanzaNode.build("identity", NS_URI_INFO) - .put_attribute("category", identity.category) - .put_attribute("type", identity.type_); - if (identity.name != null) { - identity_node.put_attribute("name", identity.name); - } - iq.stanza.get_subnode("query", NS_URI_INFO).put_node(identity_node); - } - - private InfoResult.from_iq(Iq.Stanza iq) { - this.iq = iq; - } - - public InfoResult(Iq.Stanza iq_request) { - iq = new Iq.Stanza.result(iq_request); - iq.to = iq_request.from; - iq.stanza.put_node(new StanzaNode.build("query", NS_URI_INFO).add_self_xmlns()); - } - - public static InfoResult? create_from_iq(Iq.Stanza iq) { - if (iq.is_error()) return null; - StanzaNode query_node = iq.stanza.get_subnode("query", NS_URI_INFO); - if (query_node == null) return null; - StanzaNode feature_node = query_node.get_subnode("feature", NS_URI_INFO); - if (feature_node == null) return null; - StanzaNode identity_node = query_node.get_subnode("identity", NS_URI_INFO); - if (identity_node == null) return null; - return new ServiceDiscovery.InfoResult.from_iq(iq); - } -} - -} \ No newline at end of file diff --git a/vala-xmpp/src/module/xep/0030_service_discovery/items_result.vala b/vala-xmpp/src/module/xep/0030_service_discovery/items_result.vala deleted file mode 100644 index 2c29c320..00000000 --- a/vala-xmpp/src/module/xep/0030_service_discovery/items_result.vala +++ /dev/null @@ -1,27 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Xep.ServiceDiscovery { - -public class ItemsResult { - public Iq.Stanza iq { get; private set; } - - public ArrayList items { - owned get { - ArrayList ret = new ArrayList(); - foreach (StanzaNode feature_node in iq.stanza.get_subnode("query", NS_URI_ITEMS).get_subnodes("identity", NS_URI_INFO)) { - ret.add(new Item(feature_node.get_attribute("jid", NS_URI_ITEMS), - feature_node.get_attribute("name", NS_URI_ITEMS), - feature_node.get_attribute("node", NS_URI_ITEMS))); - } - return ret; - } - } - - public ItemsResult.from_iq(Iq.Stanza iq) { - this.iq = iq; - } -} - -} \ No newline at end of file diff --git a/vala-xmpp/src/module/xep/0030_service_discovery/module.vala b/vala-xmpp/src/module/xep/0030_service_discovery/module.vala deleted file mode 100644 index 109da897..00000000 --- a/vala-xmpp/src/module/xep/0030_service_discovery/module.vala +++ /dev/null @@ -1,137 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Xep.ServiceDiscovery { - private const string NS_URI = "http://jabber.org/protocol/disco"; - public const string NS_URI_INFO = NS_URI + "#info"; - public const string NS_URI_ITEMS = NS_URI + "#items"; - - public class Module : XmppStreamModule, Iq.Handler { - public const string ID = "0030_service_discovery_module"; - - public ArrayList identities = new ArrayList(); - - public Module.with_identity(string category, string type, string? name = null) { - add_identity(category, type, name); - } - - public void add_feature(XmppStream stream, string feature) { - Flag.get_flag(stream).add_own_feature(feature); - } - - public void add_feature_notify(XmppStream stream, string feature) { - add_feature(stream, feature + "+notify"); - } - - public void add_identity(string category, string type, string? name = null) { - identities.add(new Identity(category, type, name)); - } - - public void request_info(XmppStream stream, string jid, InfoResponseListener response_listener) { - Iq.Stanza iq = new Iq.Stanza.get(new StanzaNode.build("query", NS_URI_INFO).add_self_xmlns()); - iq.to = jid; - Iq.Module.get_module(stream).send_iq(stream, iq, new IqInfoResponseListener(response_listener)); - } - - private class IqInfoResponseListener : Iq.ResponseListener, Object { - InfoResponseListener response_listener; - public IqInfoResponseListener(InfoResponseListener response_listener) { - this.response_listener = response_listener; - } - public void on_result(XmppStream stream, Iq.Stanza iq) { - InfoResult? result = InfoResult.create_from_iq(iq); - if (result != null) { - Flag.get_flag(stream).set_entitiy_features(iq.from, result.features); - response_listener.on_result(stream, result); - } else { - response_listener.on_error(stream, iq); - } - } - } - - public void request_items(XmppStream stream, string jid, ItemsResponseListener response_listener) { - Iq.Stanza iq = new Iq.Stanza.get(new StanzaNode.build("query", NS_URI_ITEMS).add_self_xmlns()); - iq.to = jid; - Iq.Module.get_module(stream).send_iq(stream, iq, new IqItemsResponseListener(response_listener)); - } - - private class IqItemsResponseListener : Iq.ResponseListener, Object { - ItemsResponseListener response_listener; - public IqItemsResponseListener(ItemsResponseListener response_listener) { this.response_listener = response_listener; } - public void on_result(XmppStream stream, Iq.Stanza iq) { - //response_listener.on_result(stream, new ServiceDiscoveryItemsResult.from_iq(iq)); - } - } - - public void on_iq_get(XmppStream stream, Iq.Stanza iq) { - StanzaNode? query_node = iq.stanza.get_subnode("query", NS_URI_INFO); - if (query_node != null) { - send_query_result(stream, iq); - } - } - - public void on_iq_set(XmppStream stream, Iq.Stanza iq) { } - - public override void attach(XmppStream stream) { - Iq.Module.require(stream); - Iq.Module.get_module(stream).register_for_namespace(NS_URI_INFO, this); - stream.add_flag(new Flag()); - add_feature(stream, NS_URI_INFO); - } - - public override void detach(XmppStream stream) { } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stream.add_module(new ServiceDiscovery.Module()); - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - - private void send_query_result(XmppStream stream, Iq.Stanza iq_request) { - InfoResult query_result = new ServiceDiscovery.InfoResult(iq_request); - query_result.features = Flag.get_flag(stream).features; - query_result.identities = identities; - Iq.Module.get_module(stream).send_iq(stream, query_result.iq, null); - } - } - - public class Identity { - public string category { get; set; } - public string type_ { get; set; } - public string? name { get; set; } - - public Identity(string category, string type, string? name = null) { - this.category = category; - this.type_ = type; - this.name = name; - } - } - - public class Item { - public string jid; - public string? name; - public string? node; - - public Item(string jid, string? name = null, string? node = null) { - this.jid = jid; - this.name = name; - this.node = node; - } - } - - public interface InfoResponseListener : Object { - public abstract void on_result(XmppStream stream, InfoResult query_result); - public void on_error(XmppStream stream, Iq.Stanza iq) { } - } - - public interface ItemsResponseListener : Object { - public abstract void on_result(XmppStream stream, ItemsResult query_result); - public void on_error(XmppStream stream, Iq.Stanza iq) { } - } -} diff --git a/vala-xmpp/src/module/xep/0045_muc/flag.vala b/vala-xmpp/src/module/xep/0045_muc/flag.vala deleted file mode 100644 index 6c1ef508..00000000 --- a/vala-xmpp/src/module/xep/0045_muc/flag.vala +++ /dev/null @@ -1,80 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Xep.Muc { - -public class Flag : XmppStreamFlag { - public const string ID = "muc"; - - private HashMap enter_listeners = new HashMap(); - private HashMap enter_ids = new HashMap(); - private HashMap own_nicks = new HashMap(); - private HashMap subjects = new HashMap(); - private HashMap subjects_by = new HashMap(); - private HashMap occupant_real_jids = new HashMap(); - private HashMap occupant_affiliation = new HashMap(); - private HashMap occupant_role = new HashMap(); - - public string? get_real_jid(string full_jid) { return occupant_real_jids[full_jid]; } - - public void set_real_jid(string full_jid, string real_jid) { occupant_real_jids[full_jid] = real_jid; } - - public string? get_occupant_affiliation(string full_jid) { return occupant_affiliation[full_jid]; } - - public void set_occupant_affiliation(string full_jid, string affiliation) { occupant_affiliation[full_jid] = affiliation; } - - public string? get_occupant_role(string full_jid) { return occupant_role[full_jid]; } - - public void set_occupant_role(string full_jid, string role) { occupant_role[full_jid] = role; } - - public string? get_muc_nick(string bare_jid) { return own_nicks[bare_jid]; } - - public string? get_enter_id(string bare_jid) { return enter_ids[bare_jid]; } - - public MucEnterListener? get_enter_listener(string bare_jid) { return enter_listeners[bare_jid]; } - - public bool is_muc(string jid) { return own_nicks[jid] != null; } - - public bool is_occupant(string jid) { - string bare_jid = get_bare_jid(jid); - return own_nicks.has_key(bare_jid) || enter_ids.has_key(bare_jid); - } - - public bool is_muc_enter_outstanding() { return enter_ids.size != 0; } - - public string? get_muc_subject(string bare_jid) { return subjects[bare_jid]; } - - public void set_muc_subject(string full_jid, string subject) { - string bare_jid = get_bare_jid(full_jid); - subjects[bare_jid] = subject; - subjects_by[bare_jid] = full_jid; - } - - public void start_muc_enter(string bare_jid, string presence_id, MucEnterListener listener) { - enter_listeners[bare_jid] = listener; - enter_ids[bare_jid] = presence_id; - } - - public void finish_muc_enter(string bare_jid, string? nick = null) { - if (nick != null) own_nicks[bare_jid] = nick; - enter_listeners.unset(bare_jid); - enter_ids.unset(bare_jid); - } - - public void remove_occupant_info(string full_jid) { - occupant_real_jids.unset(full_jid); - occupant_affiliation.unset(full_jid); - occupant_role.unset(full_jid); - } - - public static Flag? get_flag(XmppStream stream) { return (Flag?) stream.get_flag(NS_URI, ID); } - - public static bool has_flag(XmppStream stream) { return get_flag(stream) != null; } - - public override string get_ns() { return NS_URI; } - - public override string get_id() { return ID; } -} - -} \ No newline at end of file diff --git a/vala-xmpp/src/module/xep/0045_muc/module.vala b/vala-xmpp/src/module/xep/0045_muc/module.vala deleted file mode 100644 index f9ed9539..00000000 --- a/vala-xmpp/src/module/xep/0045_muc/module.vala +++ /dev/null @@ -1,244 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Xep.Muc { - -private const string NS_URI = "http://jabber.org/protocol/muc"; -private const string NS_URI_ADMIN = NS_URI + "#admin"; -private const string NS_URI_USER = NS_URI + "#user"; - -public const string AFFILIATION_ADMIN = "admin"; -public const string AFFILIATION_MEMBER = "member"; -public const string AFFILIATION_NONE = "none"; -public const string AFFILIATION_OUTCAST = "outcast"; -public const string AFFILIATION_OWNER = "owner"; - -public const string ROLE_MODERATOR = "moderator"; -public const string ROLE_NONE = "none"; -public const string ROLE_PARTICIPANT = "participant"; -public const string ROLE_VISITOR = "visitor"; - -public enum MucEnterError { - PASSWORD_REQUIRED, - NOT_IN_MEMBER_LIST, - BANNED, - NICK_CONFLICT, - OCCUPANT_LIMIT_REACHED, - ROOM_DOESNT_EXIST -} - -public class Module : XmppStreamModule { - public const string ID = "0045_muc_module"; - - public signal void received_occupant_affiliation(XmppStream stream, string jid, string? affiliation); - public signal void received_occupant_jid(XmppStream stream, string jid, string? real_jid); - public signal void received_occupant_role(XmppStream stream, string jid, string? role); - public signal void subject_set(XmppStream stream, string subject, string jid); - - public void enter(XmppStream stream, string bare_jid, string nick, string? password, MucEnterListener listener) { - Presence.Stanza presence = new Presence.Stanza(); - presence.to = bare_jid + "/" + nick; - StanzaNode x_node = new StanzaNode.build("x", NS_URI).add_self_xmlns(); - if (password != null) { - x_node.put_node(new StanzaNode.build("password", NS_URI).put_node(new StanzaNode.text(password))); - } - presence.stanza.put_node(x_node); - - Muc.Flag.get_flag(stream).start_muc_enter(bare_jid, presence.id, listener); - - Presence.Module.get_module(stream).send_presence(stream, presence); - } - - public void exit(XmppStream stream, string jid) { - string nick = Flag.get_flag(stream).get_muc_nick(jid); - Presence.Stanza presence = new Presence.Stanza(); - presence.to = jid + "/" + nick; - presence.type_ = Presence.Stanza.TYPE_UNAVAILABLE; - Presence.Module.get_module(stream).send_presence(stream, presence); - } - - public void change_subject(XmppStream stream, string jid, string subject) { - Message.Stanza message = new Message.Stanza(); - message.to = jid; - message.type_ = Message.Stanza.TYPE_GROUPCHAT; - message.stanza.put_node((new StanzaNode.build("subject")).put_node(new StanzaNode.text(subject))); - Message.Module.get_module(stream).send_message(stream, message); - } - - public void change_nick(XmppStream stream, string jid, string new_nick) { - Presence.Stanza presence = new Presence.Stanza(); - presence.to = jid + "/" + new_nick; - Presence.Module.get_module(stream).send_presence(stream, presence); - } - - public void kick(XmppStream stream, string jid, string nick) { - change_role(stream, jid, nick, "none"); - } - - public override void attach(XmppStream stream) { - stream.add_flag(new Muc.Flag()); - Message.Module.require(stream); - Message.Module.get_module(stream).received_message.connect(on_received_message); - Presence.Module.require(stream); - Presence.Module.get_module(stream).received_presence.connect(on_received_presence); - Presence.Module.get_module(stream).received_available.connect(on_received_available); - Presence.Module.get_module(stream).received_unavailable.connect(on_received_unavailable); - if (ServiceDiscovery.Module.get_module(stream) != null) { - ServiceDiscovery.Module.get_module(stream).add_feature(stream, NS_URI); - } - } - - public override void detach(XmppStream stream) { - Message.Module.get_module(stream).received_message.disconnect(on_received_message); - Presence.Module.get_module(stream).received_presence.disconnect(on_received_presence); - Presence.Module.get_module(stream).received_available.disconnect(on_received_available); - Presence.Module.get_module(stream).received_unavailable.disconnect(on_received_unavailable); - } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - Presence.Module.require(stream); - if (get_module(stream) == null) stream.add_module(new Muc.Module()); - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - - private void change_role(XmppStream stream, string jid, string nick, string new_role) { - StanzaNode query = new StanzaNode.build("query", NS_URI_ADMIN).add_self_xmlns(); - query.put_node(new StanzaNode.build("item", NS_URI_ADMIN).put_attribute("nick", nick, NS_URI_ADMIN).put_attribute("role", new_role, NS_URI_ADMIN)); - Iq.Stanza iq = new Iq.Stanza.set(query); - iq.to = jid; - Iq.Module.get_module(stream).send_iq(stream, iq); - } - - private void on_received_message(XmppStream stream, Message.Stanza message) { - if (message.type_ == Message.Stanza.TYPE_GROUPCHAT) { - StanzaNode? subject_node = message.stanza.get_subnode("subject"); - if (subject_node != null) { - string subject = subject_node.get_string_content(); - Muc.Flag.get_flag(stream).set_muc_subject(message.from, subject); - subject_set(stream, subject, message.from); - } - } - } - - private void on_received_presence(XmppStream stream, Presence.Stanza presence) { - Flag flag = Flag.get_flag(stream); - if (presence.is_error() && flag.is_muc_enter_outstanding() && flag.is_occupant(presence.from)) { - string bare_jid = get_bare_jid(presence.from); - ErrorStanza? error_stanza = presence.get_error(); - if (flag.get_enter_id(bare_jid) == error_stanza.original_id) { - MucEnterListener listener = flag.get_enter_listener(bare_jid); - if (error_stanza.condition == ErrorStanza.CONDITION_NOT_AUTHORIZED && ErrorStanza.TYPE_AUTH == error_stanza.type_) { - listener.on_error(MucEnterError.PASSWORD_REQUIRED); - } else if (ErrorStanza.CONDITION_REGISTRATION_REQUIRED == error_stanza.condition && ErrorStanza.TYPE_AUTH == error_stanza.type_) { - listener.on_error(MucEnterError.NOT_IN_MEMBER_LIST); - } else if (ErrorStanza.CONDITION_FORBIDDEN == error_stanza.condition && ErrorStanza.TYPE_AUTH == error_stanza.type_) { - listener.on_error(MucEnterError.BANNED); - } else if (ErrorStanza.CONDITION_CONFLICT == error_stanza.condition && ErrorStanza.TYPE_CANCEL == error_stanza.type_) { - listener.on_error(MucEnterError.NICK_CONFLICT); - } else if (ErrorStanza.CONDITION_SERVICE_UNAVAILABLE == error_stanza.condition && ErrorStanza.TYPE_WAIT == error_stanza.type_) { - listener.on_error(MucEnterError.OCCUPANT_LIMIT_REACHED); - } else if (ErrorStanza.CONDITION_ITEM_NOT_FOUND == error_stanza.condition && ErrorStanza.TYPE_CANCEL == error_stanza.type_) { - listener.on_error(MucEnterError.ROOM_DOESNT_EXIST); - } - flag.finish_muc_enter(bare_jid); - } - } - } - - private void on_received_available(XmppStream stream, Presence.Stanza presence) { - Flag flag = Flag.get_flag(stream); - if (flag.is_occupant(presence.from)) { - StanzaNode? x_node = presence.stanza.get_subnode("x", NS_URI_USER); - if (x_node != null) { - ArrayList status_codes = get_status_codes(x_node); - if (status_codes.contains(StatusCode.SELF_PRESENCE)) { - string bare_jid = get_bare_jid(presence.from); - flag.get_enter_listener(bare_jid).on_success(); - flag.finish_muc_enter(bare_jid, get_resource_part(presence.from)); - } - string? affiliation = x_node["item", "affiliation"].val; - if (affiliation != null) { - received_occupant_affiliation(stream, presence.from, affiliation); - } - string? jid = x_node["item", "jid"].val; - if (jid != null) { - flag.set_real_jid(presence.from, jid); - received_occupant_jid(stream, presence.from, jid); - } - string? role = x_node["item", "role"].val; - if (role != null) { - received_occupant_role(stream, presence.from, role); - } - } - } - } - - private void on_received_unavailable(XmppStream stream, string jid) { - Flag flag = Flag.get_flag(stream); - if (flag.is_occupant(jid)) { - flag.remove_occupant_info(jid); - } - } - - private ArrayList get_status_codes(StanzaNode x_node) { - ArrayList ret = new ArrayList(); - foreach (StanzaNode status_node in x_node.get_subnodes("status", NS_URI_USER)) { - ret.add(int.parse(status_node.get_attribute("code"))); - } - return ret; - } -} - -public enum StatusCode { - /** Inform user that any occupant is allowed to see the user's full JID */ - JID_VISIBLE = 100, - /** Inform user that his or her affiliation changed while not in the room */ - AFFILIATION_CHANGED = 101, - /** Inform occupants that room now shows unavailable members */ - SHOWS_UNAVIABLE_MEMBERS = 102, - /** Inform occupants that room now does not show unavailable members */ - SHOWS_UNAVIABLE_MEMBERS_NOT = 103, - /** Inform occupants that a non-privacy-related room configuration change has occurred */ - CONFIG_CHANGE_NON_PRIVACY = 104, - /** Inform user that presence refers to itself */ - SELF_PRESENCE = 110, - /** Inform occupants that room logging is now enabled */ - LOGGING_ENABLED = 170, - /** Inform occupants that room logging is now disabled */ - LOGGING_DISABLED = 171, - /** Inform occupants that the room is now non-anonymous */ - NON_ANONYMOUS = 172, - /** Inform occupants that the room is now semi-anonymous */ - SEMI_ANONYMOUS = 173, - /** Inform user that a new room has been created */ - NEW_ROOM_CREATED = 201, - /** Inform user that service has assigned or modified occupant's roomnick */ - MODIFIED_NICK = 210, - /** Inform user that he or she has been banned from the room */ - BANNED = 301, - /** Inform all occupants of new room nickname */ - ROOM_NICKNAME = 303, - /** Inform user that he or she has been kicked from the room */ - KICKED = 307, - /** Inform user that he or she is being removed from the room */ - REMOVED_AFFILIATION_CHANGE = 321, - /** Inform user that he or she is being removed from the room because the room has been changed to members-only - and the user is not a member */ - REMOVED_MEMBERS_ONLY = 322, - /** Inform user that he or she is being removed from the room because the MUC service is being shut down */ - REMOVED_SHUTDOWN = 332 -} - -public interface MucEnterListener : Object { - public abstract void on_success(); - public abstract void on_error(MucEnterError error); -} - -} diff --git a/vala-xmpp/src/module/xep/0048_bookmarks/conference.vala b/vala-xmpp/src/module/xep/0048_bookmarks/conference.vala deleted file mode 100644 index 818ab3d0..00000000 --- a/vala-xmpp/src/module/xep/0048_bookmarks/conference.vala +++ /dev/null @@ -1,74 +0,0 @@ -using Xmpp.Core; - -namespace Xmpp.Xep.Bookmarks { - -public class Conference { - - public const string ATTRIBUTE_AUTOJOIN = "autojoin"; - public const string ATTRIBUTE_JID = "jid"; - public const string ATTRIBUTE_NAME = "name"; - - public const string NODE_NICK = "nick"; - public const string NODE_PASSWORD = "password"; - - public StanzaNode stanza_node; - - public bool autojoin { - get { - string? attr = stanza_node.get_attribute(ATTRIBUTE_AUTOJOIN); - return attr == "true" || attr == "1"; // "1" isn't standard, but it's used - } - set { stanza_node.set_attribute(ATTRIBUTE_AUTOJOIN, value.to_string()); } - } - - public string jid { - get { return stanza_node.get_attribute(ATTRIBUTE_JID); } - set { stanza_node.set_attribute(ATTRIBUTE_JID, value); } - } - - public string? name { - get { return stanza_node.get_attribute(ATTRIBUTE_NAME); } - set { stanza_node.set_attribute(ATTRIBUTE_NAME, value); } - } - - public string? nick { - get { - StanzaNode? nick_node = stanza_node.get_subnode(NODE_NICK); - return nick_node == null? null : nick_node.get_string_content(); - } - set { - StanzaNode? nick_node = stanza_node.get_subnode(NODE_NICK); - if (nick_node == null) { - nick_node = new StanzaNode.build(NODE_NICK, NS_URI); - stanza_node.put_node(nick_node); - } - nick_node.put_node(new StanzaNode.text(value)); - } - } - - public string? password { - get { - StanzaNode? password_node = stanza_node.get_subnode(NODE_PASSWORD); - return password_node == null? null : password_node.get_string_content(); - } - set { - StanzaNode? password_node = stanza_node.get_subnode(NODE_PASSWORD); - if (password_node == null) { - password_node = new StanzaNode.build(NODE_PASSWORD); - stanza_node.put_node(password_node); - } - password_node.put_node(new StanzaNode.text(value)); - } - } - - public Conference.from_stanza_node(StanzaNode stanza_node) { - this.stanza_node = stanza_node; - } - - public Conference(string jid) { - this.stanza_node = new StanzaNode.build("conference", NS_URI); - this.jid = jid; - } -} - -} \ No newline at end of file diff --git a/vala-xmpp/src/module/xep/0048_bookmarks/module.vala b/vala-xmpp/src/module/xep/0048_bookmarks/module.vala deleted file mode 100644 index d7767208..00000000 --- a/vala-xmpp/src/module/xep/0048_bookmarks/module.vala +++ /dev/null @@ -1,137 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Xep.Bookmarks { -private const string NS_URI = "storage:bookmarks"; - -public class Module : XmppStreamModule { - public const string ID = "0048_bookmarks_module"; - - public signal void conferences_updated(XmppStream stream, ArrayList conferences); - - public void get_conferences(XmppStream stream, ConferencesRetrieveResponseListener response_listener) { - StanzaNode get_node = new StanzaNode.build("storage", NS_URI).add_self_xmlns(); - PrivateXmlStorage.Module.get_module(stream).retrieve(stream, get_node, new GetConferences(response_listener)); - } - - public void set_conferences(XmppStream stream, ArrayList conferences) { - StanzaNode storage_node = (new StanzaNode.build("storage", NS_URI)).add_self_xmlns(); - foreach (Conference conference in conferences) { - storage_node.put_node(conference.stanza_node); - } - PrivateXmlStorage.Module.get_module(stream).store(stream, storage_node, new StoreResponseListenerImpl(conferences)); - } - - private class StoreResponseListenerImpl : PrivateXmlStorage.StoreResponseListener, Object { - ArrayList conferences; - public StoreResponseListenerImpl(ArrayList conferences) { - this.conferences = conferences; - } - public void on_success(XmppStream stream) { - Module.get_module(stream).conferences_updated(stream, conferences); - } - } - - public void add_conference(XmppStream stream, Conference add) { - get_conferences(stream, new AddConference(add)); - } - - public void replace_conference(XmppStream stream, Conference was, Conference modified) { - get_conferences(stream, new ModifyConference(was, modified)); - } - - public void remove_conference(XmppStream stream, Conference conference) { - get_conferences(stream, new RemoveConference(conference)); - } - - private class GetConferences : PrivateXmlStorage.RetrieveResponseListener, Object { - ConferencesRetrieveResponseListener response_listener; - - public GetConferences(ConferencesRetrieveResponseListener response_listener) { - this.response_listener = response_listener; - } - - public void on_result(XmppStream stream, StanzaNode node) { - response_listener.on_result(stream, get_conferences_from_stanza(node)); - } - } - - private class AddConference : ConferencesRetrieveResponseListener, Object { - private Conference conference; - public AddConference(Conference conference) { - this.conference = conference; - } - public void on_result(XmppStream stream, ArrayList conferences) { - conferences.add(conference); - Module.get_module(stream).set_conferences(stream, conferences); - } - } - - private class ModifyConference : ConferencesRetrieveResponseListener, Object { - private Conference was; - private Conference modified; - public ModifyConference(Conference was, Conference modified) { - this.was = was; - this.modified = modified; - } - public void on_result(XmppStream stream, ArrayList conferences) { - foreach (Conference conference in conferences) { - if (conference.name == was.name && conference.jid == was.jid && conference.autojoin == was.autojoin) { - conference.autojoin = modified.autojoin; - conference.name = modified.name; - conference.jid = modified.jid; - break; - } - } - Module.get_module(stream).set_conferences(stream, conferences); - } - } - - private class RemoveConference : ConferencesRetrieveResponseListener, Object { - private Conference remove; - public RemoveConference(Conference remove) { - this.remove = remove; - } - public void on_result(XmppStream stream, ArrayList conferences) { - Conference? rem = null; - foreach (Conference conference in conferences) { - if (conference.name == remove.name && conference.jid == remove.jid && conference.autojoin == remove.autojoin) { - rem = conference; - } - } - if (rem != null) conferences.remove(rem); - Module.get_module(stream).set_conferences(stream, conferences); - } - } - - public override void attach(XmppStream stream) { } - - public override void detach(XmppStream stream) { } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stderr.printf(""); - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - - private static ArrayList get_conferences_from_stanza(StanzaNode node) { - ArrayList conferences = new ArrayList(); - ArrayList conferenceNodes = node.get_subnode("storage", NS_URI).get_subnodes("conference", NS_URI); - foreach (StanzaNode conferenceNode in conferenceNodes) { - conferences.add(new Conference.from_stanza_node(conferenceNode)); - } - return conferences; - } -} - -public interface ConferencesRetrieveResponseListener : Object { - public abstract void on_result(XmppStream stream, ArrayList conferences); -} - -} diff --git a/vala-xmpp/src/module/xep/0049_private_xml_storage.vala b/vala-xmpp/src/module/xep/0049_private_xml_storage.vala deleted file mode 100644 index c57acdde..00000000 --- a/vala-xmpp/src/module/xep/0049_private_xml_storage.vala +++ /dev/null @@ -1,65 +0,0 @@ -using Xmpp.Core; - -namespace Xmpp.Xep.PrivateXmlStorage { - private const string NS_URI = "jabber:iq:private"; - - public class Module : XmppStreamModule { - public const string ID = "0049_private_xml_storage"; - - public void store(XmppStream stream, StanzaNode node, StoreResponseListener listener) { - StanzaNode queryNode = new StanzaNode.build("query", NS_URI).add_self_xmlns().put_node(node); - Iq.Stanza iq = new Iq.Stanza.set(queryNode); - Iq.Module.get_module(stream).send_iq(stream, iq, new IqStoreResponse(listener)); - } - - private class IqStoreResponse : Iq.ResponseListener, Object { - StoreResponseListener listener; - public IqStoreResponse(StoreResponseListener listener) { - this.listener = listener; - } - public void on_result(XmppStream stream, Iq.Stanza iq) { - listener.on_success(stream); - } - } - - public void retrieve(XmppStream stream, StanzaNode node, RetrieveResponseListener responseListener) { - StanzaNode queryNode = new StanzaNode.build("query", NS_URI).add_self_xmlns().put_node(node); - Iq.Stanza iq = new Iq.Stanza.get(queryNode); - Iq.Module.get_module(stream).send_iq(stream, iq, new IqRetrieveResponse(responseListener)); - } - - private class IqRetrieveResponse : Iq.ResponseListener, Object { - RetrieveResponseListener response_listener; - public IqRetrieveResponse(RetrieveResponseListener response_listener) { this.response_listener = response_listener; } - - public void on_result(XmppStream stream, Iq.Stanza iq) { - response_listener.on_result(stream, iq.stanza.get_subnode("query", NS_URI)); - } - } - - public override void attach(XmppStream stream) { - Iq.Module.require(stream); - } - - public override void detach(XmppStream stream) { } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stream.add_module(new PrivateXmlStorage.Module()); - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - } - - public interface StoreResponseListener : Object { - public abstract void on_success(XmppStream stream); - } - - public interface RetrieveResponseListener : Object { - public abstract void on_result(XmppStream stream, StanzaNode stanzaNode); - } -} diff --git a/vala-xmpp/src/module/xep/0054_vcard/module.vala b/vala-xmpp/src/module/xep/0054_vcard/module.vala deleted file mode 100644 index 58b71d2c..00000000 --- a/vala-xmpp/src/module/xep/0054_vcard/module.vala +++ /dev/null @@ -1,87 +0,0 @@ -using Xmpp.Core; - -namespace Xmpp.Xep.VCard { -private const string NS_URI = "vcard-temp"; -private const string NS_URI_UPDATE = NS_URI + ":x:update"; - -public class Module : XmppStreamModule { - public const string ID = "0027_current_pgp_usage"; - - public signal void received_avatar(XmppStream stream, string jid, string id); - - private PixbufStorage storage; - - public Module(PixbufStorage storage) { - this.storage = storage; - } - - public override void attach(XmppStream stream) { - Iq.Module.require(stream); - Presence.Module.require(stream); - Presence.Module.get_module(stream).received_presence.connect(on_received_presence); - } - - public override void detach(XmppStream stream) { - Presence.Module.get_module(stream).received_presence.disconnect(on_received_presence); - } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stderr.printf("VCardModule required but not attached!\n"); ; - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - - private void on_received_presence(XmppStream stream, Presence.Stanza presence) { - StanzaNode? update_node = presence.stanza.get_subnode("x", NS_URI_UPDATE); - if (update_node == null) return; - StanzaNode? photo_node = update_node.get_subnode("photo", NS_URI_UPDATE); - if (photo_node == null) return; - string? sha1 = photo_node.get_string_content(); - if (sha1 == null) return; - if (storage.has_image(sha1)) { - if (Muc.Flag.get_flag(stream).is_occupant(presence.from)) { - received_avatar(stream, presence.from, sha1); - } else { - received_avatar(stream, get_bare_jid(presence.from), sha1); - } - } else { - Iq.Stanza iq = new Iq.Stanza.get(new StanzaNode.build("vCard", NS_URI).add_self_xmlns()); - if (Muc.Flag.get_flag(stream).is_occupant(presence.from)) { - iq.to = presence.from; - } else { - iq.to = get_bare_jid(presence.from); - } - Iq.Module.get_module(stream).send_iq(stream, iq, new IqResponseListenerImpl(this, storage, sha1)); - } - } - - private class IqResponseListenerImpl : Iq.ResponseListener, Object { - Module outer; - PixbufStorage storage; - string id; - public IqResponseListenerImpl(Module outer, PixbufStorage storage, string id) { - this.outer = outer; - this.id = id; - this.storage = storage; - } - public void on_result(XmppStream stream, Iq.Stanza iq) { - if (iq.is_error()) return; - StanzaNode? vcard_node = iq.stanza.get_subnode("vCard", NS_URI); - if (vcard_node == null) return; - StanzaNode? photo_node = vcard_node.get_subnode("PHOTO", NS_URI); - if (photo_node == null) return; - StanzaNode? binary_node = photo_node.get_subnode("BINVAL", NS_URI); - if (binary_node == null) return; - string? content = binary_node.get_string_content(); - if (content == null) return; - storage.store(id, Base64.decode(content)); - outer.received_avatar(stream, iq.from, id); - } - } -} -} diff --git a/vala-xmpp/src/module/xep/0060_pubsub.vala b/vala-xmpp/src/module/xep/0060_pubsub.vala deleted file mode 100644 index 3f96e7a1..00000000 --- a/vala-xmpp/src/module/xep/0060_pubsub.vala +++ /dev/null @@ -1,107 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Xep.Pubsub { - private const string NS_URI = "http://jabber.org/protocol/pubsub"; - private const string NS_URI_EVENT = NS_URI + "#event"; - - public class Module : XmppStreamModule { - public const string ID = "0060_pubsub_module"; - - private HashMap event_listeners = new HashMap(); - - public void add_filtered_notification(XmppStream stream, string node, EventListener listener) { - ServiceDiscovery.Module.get_module(stream).add_feature_notify(stream, node); - event_listeners[node] = listener; - } - - public void request(XmppStream stream, string jid, string node, RequestResponseListener listener) { // TODO multiple nodes gehen auch - Iq.Stanza a = new Iq.Stanza.get(new StanzaNode.build("pubsub", NS_URI).add_self_xmlns().put_node(new StanzaNode.build("items", NS_URI).put_attribute("node", node))); - a.to = jid; - Iq.Module.get_module(stream).send_iq(stream, a, new IqRequestResponseListener(listener)); - } - - private class IqRequestResponseListener : Iq.ResponseListener, Object { - RequestResponseListener response_listener; - public IqRequestResponseListener(RequestResponseListener response_listener) { this.response_listener = response_listener; } - public void on_result(XmppStream stream, Iq.Stanza iq) { - StanzaNode event_node = iq.stanza.get_subnode("pubsub", NS_URI); if (event_node == null) return; - StanzaNode items_node = event_node.get_subnode("items", NS_URI); if (items_node == null) return; - StanzaNode item_node = items_node.get_subnode("item", NS_URI); if (item_node == null) return; - string node = items_node.get_attribute("node", NS_URI); - string id = item_node.get_attribute("id", NS_URI); - response_listener.on_result(stream, iq.from, id, item_node.sub_nodes[0]); - } - } - - public void publish(XmppStream stream, string? jid, string node_id, string node, string item_id, StanzaNode content) { - StanzaNode pubsub_node = new StanzaNode.build("pubsub", NS_URI).add_self_xmlns(); - StanzaNode publish_node = new StanzaNode.build("publish", NS_URI).put_attribute("node", node_id); - pubsub_node.put_node(publish_node); - StanzaNode items_node = new StanzaNode.build("item", NS_URI).put_attribute("id", item_id); - items_node.put_node(content); - publish_node.put_node(items_node); - Iq.Stanza iq = new Iq.Stanza.set(pubsub_node); - Iq.Module.get_module(stream).send_iq(stream, iq, null); - } - - private class IqPublishResponseListener : Iq.ResponseListener, Object { - PublishResponseListener response_listener; - public IqPublishResponseListener(PublishResponseListener response_listener) { this.response_listener = response_listener; } - public void on_result(XmppStream stream, Iq.Stanza iq) { - if (iq.is_error()) { - response_listener.on_error(stream); - } else { - response_listener.on_success(stream); - } - } - } - - public override void attach(XmppStream stream) { - Iq.Module.require(stream); - Message.Module.require(stream); - ServiceDiscovery.Module.require(stream); - Message.Module.get_module(stream).received_message.connect(on_received_message); - } - - public override void detach(XmppStream stream) { - Message.Module.get_module(stream).received_message.disconnect(on_received_message); - } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stream.add_module(new Module()); - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - - private void on_received_message(XmppStream stream, Message.Stanza message) { - StanzaNode event_node = message.stanza.get_subnode("event", NS_URI_EVENT); if (event_node == null) return; - StanzaNode items_node = event_node.get_subnode("items", NS_URI_EVENT); if (items_node == null) return; - StanzaNode item_node = items_node.get_subnode("item", NS_URI_EVENT); if (item_node == null) return; - string node = items_node.get_attribute("node", NS_URI_EVENT); - string id = item_node.get_attribute("id", NS_URI_EVENT); - if (event_listeners.has_key(node)) { - event_listeners[node].on_result(stream, message.from, id, item_node.sub_nodes[0]); - } - } - } - - public interface RequestResponseListener : Object { - public abstract void on_result(XmppStream stream, string jid, string id, StanzaNode node); - } - - public interface EventListener : Object { - public abstract void on_result(XmppStream stream, string jid, string id, StanzaNode node); - } - - public interface PublishResponseListener : Object { - public abstract void on_success(XmppStream stream); - public abstract void on_error(XmppStream stream); - } -} diff --git a/vala-xmpp/src/module/xep/0082_date_time_profiles.vala b/vala-xmpp/src/module/xep/0082_date_time_profiles.vala deleted file mode 100644 index b2ce1077..00000000 --- a/vala-xmpp/src/module/xep/0082_date_time_profiles.vala +++ /dev/null @@ -1,41 +0,0 @@ -namespace Xmpp.Xep.DateTimeProfiles { - - public class Module { - public Regex DATETIME_REGEX; - - public Module() { - DATETIME_REGEX = new Regex("""^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.(\d{3}))?(Z|((\+|\-)(\d{2}):(\d{2})))$"""); - } - - public DateTime? parse_string(string time_string) { - MatchInfo match_info; - if (DATETIME_REGEX.match(time_string, RegexMatchFlags.ANCHORED, out match_info)) { - int year = int.parse(match_info.fetch(1)); - int month = int.parse(match_info.fetch(2)); - int day = int.parse(match_info.fetch(3)); - int hour = int.parse(match_info.fetch(4)); - int minute = int.parse(match_info.fetch(5)); - int second = int.parse(match_info.fetch(6)); - DateTime datetime = new DateTime.utc(year, month, day, hour, minute, second); - if (match_info.fetch(9) != "Z") { - char plusminus = match_info.fetch(11)[0]; - int tz_hour = int.parse(match_info.fetch(12)); - int tz_minute = int.parse(match_info.fetch(13)); - if (plusminus == '-') { - tz_hour *= -1; - tz_minute *= -1; - } - datetime.add_hours(tz_hour); - datetime.add_minutes(tz_minute); - } - return datetime; - } - return null; - } - - public string to_datetime(DateTime time) { - return time.format("%Y-%m-%dT%H:%M:%SZ"); - } -} - -} \ No newline at end of file diff --git a/vala-xmpp/src/module/xep/0084_user_avatars.vala b/vala-xmpp/src/module/xep/0084_user_avatars.vala deleted file mode 100644 index 13d19674..00000000 --- a/vala-xmpp/src/module/xep/0084_user_avatars.vala +++ /dev/null @@ -1,93 +0,0 @@ -using Xmpp.Core; - -namespace Xmpp.Xep.UserAvatars { - private const string NS_URI = "urn:xmpp:avatar"; - private const string NS_URI_DATA = NS_URI + ":data"; - private const string NS_URI_METADATA = NS_URI + ":metadata"; - - public class Module : XmppStreamModule { - public const string ID = "0084_user_avatars"; - - public signal void received_avatar(XmppStream stream, string jid, string id); - - private PixbufStorage storage; - - public Module(PixbufStorage storage) { - this.storage = storage; - } - - public void publish_png(XmppStream stream, uint8[] image, int width, int height) { - string sha1 = Checksum.compute_for_data(ChecksumType.SHA1, image); - StanzaNode data_node = new StanzaNode.build("data", NS_URI_DATA).add_self_xmlns() - .put_node(new StanzaNode.text(Base64.encode(image))); - Pubsub.Module.get_module(stream).publish(stream, null, NS_URI_DATA, NS_URI_DATA, sha1, data_node); - - StanzaNode metadata_node = new StanzaNode.build("metadata", NS_URI_METADATA).add_self_xmlns(); - StanzaNode info_node = new StanzaNode.build("info", NS_URI_METADATA) - .put_attribute("bytes", image.length.to_string()) - .put_attribute("id", sha1) - .put_attribute("width", width.to_string()) - .put_attribute("height", height.to_string()) - .put_attribute("type", "image/png"); - metadata_node.put_node(info_node); - Pubsub.Module.get_module(stream).publish(stream, null, NS_URI_METADATA, NS_URI_METADATA, sha1, metadata_node); - } - - private class PublishResponseListenerImpl : Pubsub.PublishResponseListener, Object { - PublishResponseListener listener; - PublishResponseListenerImpl other; - public PublishResponseListenerImpl(PublishResponseListener listener, PublishResponseListenerImpl other) { - this.listener = listener; - this.other = other; - } - public void on_success(XmppStream stream) { listener.on_success(stream); } - public void on_error(XmppStream stream) { listener.on_error(stream); } - } - - public override void attach(XmppStream stream) { - Pubsub.Module.require(stream); - Pubsub.Module.get_module(stream).add_filtered_notification(stream, NS_URI_METADATA, new PubsubEventListenerImpl(storage)); - } - - public override void detach(XmppStream stream) { } - - class PubsubEventListenerImpl : Pubsub.EventListener, Object { - PixbufStorage storage; - public PubsubEventListenerImpl(PixbufStorage storage) { this.storage = storage; } - public void on_result(XmppStream stream, string jid, string id, StanzaNode node) { - StanzaNode info_node = node.get_subnode("info", NS_URI_METADATA); - if (info_node.get_attribute("type") != "image/png") return; - if (storage.has_image(id)) { - Module.get_module(stream).received_avatar(stream, jid, id); - } else { - Pubsub.Module.get_module(stream).request(stream, jid, NS_URI_DATA, new PubsubRequestResponseListenerImpl(storage)); - } - } - } - - class PubsubRequestResponseListenerImpl : Pubsub.RequestResponseListener, Object { - PixbufStorage storage; - public PubsubRequestResponseListenerImpl(PixbufStorage storage) { this.storage = storage; } - public void on_result(XmppStream stream, string jid, string id, StanzaNode node) { - storage.store(id, Base64.decode(node.get_string_content())); - Module.get_module(stream).received_avatar(stream, jid, id); - } - } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stderr.printf("UserAvatarsModule required but not attached!\n"); - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - } - - public interface PublishResponseListener : Object { - public abstract void on_success(XmppStream stream); - public abstract void on_error(XmppStream stream); - } -} diff --git a/vala-xmpp/src/module/xep/0085_chat_state_notifications.vala b/vala-xmpp/src/module/xep/0085_chat_state_notifications.vala deleted file mode 100644 index cefc7a18..00000000 --- a/vala-xmpp/src/module/xep/0085_chat_state_notifications.vala +++ /dev/null @@ -1,74 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Xep.ChatStateNotifications { -private const string NS_URI = "http://jabber.org/protocol/chatstates"; - -public const string STATE_ACTIVE = "active"; -public const string STATE_INACTIVE = "inactive"; -public const string STATE_GONE = "gone"; -public const string STATE_COMPOSING = "composing"; -public const string STATE_PAUSED = "paused"; - -private const string[] STATES = {STATE_ACTIVE, STATE_INACTIVE, STATE_GONE, STATE_COMPOSING, STATE_PAUSED}; - -public class Module : XmppStreamModule { - public const string ID = "0085_chat_state_notifications"; - - public signal void chat_state_received(XmppStream stream, string jid, string state); - - /** - * "A message stanza that does not contain standard messaging content [...] SHOULD be a state other than " (0085, 5.6) - */ - public void send_state(XmppStream stream, string jid, string state) { - Message.Stanza message = new Message.Stanza(); - message.to = jid; - message.type_ = Message.Stanza.TYPE_CHAT; - message.stanza.put_node(new StanzaNode.build(state, NS_URI).add_self_xmlns()); - Message.Module.get_module(stream).send_message(stream, message); - } - - public override void attach(XmppStream stream) { - ServiceDiscovery.Module.require(stream); - ServiceDiscovery.Module.get_module(stream).add_feature(stream, NS_URI); - Message.Module.get_module(stream).pre_send_message.connect(on_pre_send_message); - Message.Module.get_module(stream).received_message.connect(on_received_message); - } - - public override void detach(XmppStream stream) { - Message.Module.get_module(stream).pre_send_message.disconnect(on_pre_send_message); - Message.Module.get_module(stream).received_message.disconnect(on_received_message); - } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stream.add_module(new Module()); ; - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - - private void on_pre_send_message(XmppStream stream, Message.Stanza message) { - if (message.body == null) return; - if (message.type_ != Message.Stanza.TYPE_CHAT) return; - message.stanza.put_node(new StanzaNode.build(STATE_ACTIVE, NS_URI).add_self_xmlns()); - } - - private void on_received_message(XmppStream stream, Message.Stanza message) { - if (!message.is_error()) { - ArrayList nodes = message.stanza.get_all_subnodes(); - foreach (StanzaNode node in nodes) { - if (node.ns_uri == NS_URI && - node.name in STATES) { - chat_state_received(stream, message.from, node.name); - } - } - } - } -} - -} diff --git a/vala-xmpp/src/module/xep/0115_entitiy_capabilities.vala b/vala-xmpp/src/module/xep/0115_entitiy_capabilities.vala deleted file mode 100644 index 472eb9bd..00000000 --- a/vala-xmpp/src/module/xep/0115_entitiy_capabilities.vala +++ /dev/null @@ -1,125 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Xep.EntityCapabilities { - private const string NS_URI = "http://jabber.org/protocol/caps"; - - public class Module : XmppStreamModule { - public const string ID = "0115_entity_capabilities"; - - private string own_ver_hash; - private Storage storage; - - public Module(Storage storage) { - this.storage = storage; - } - - private string get_own_hash(XmppStream stream) { - if (own_ver_hash == null) { - own_ver_hash = compute_hash(ServiceDiscovery.Module.get_module(stream).identities, ServiceDiscovery.Flag.get_flag(stream).features); - } - return own_ver_hash; - } - - public override void attach(XmppStream stream) { - ServiceDiscovery.Module.require(stream); - Presence.Module.require(stream); - Presence.Module.get_module(stream).pre_send_presence_stanza.connect(on_pre_send_presence_stanza); - Presence.Module.get_module(stream).received_presence.connect(on_received_presence); - ServiceDiscovery.Module.get_module(stream).add_feature(stream, NS_URI); - } - - public override void detach(XmppStream stream) { - Presence.Module.get_module(stream).pre_send_presence_stanza.disconnect(on_pre_send_presence_stanza); - Presence.Module.get_module(stream).received_presence.disconnect(on_received_presence); - } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stderr.printf("EntityCapabilitiesModule required but not attached!\n"); - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - - private void on_pre_send_presence_stanza(XmppStream stream, Presence.Stanza presence) { - if (presence.type_ == Presence.Stanza.TYPE_AVAILABLE) { - presence.stanza.put_node(new StanzaNode.build("c", NS_URI).add_self_xmlns() - .put_attribute("hash", "sha-1") - .put_attribute("node", "http://dino-im.org") - .put_attribute("ver", get_own_hash(stream))); - } - } - - private void on_received_presence(XmppStream stream, Presence.Stanza presence) { - StanzaNode? c_node = presence.stanza.get_subnode("c", NS_URI); - if (c_node != null) { - string ver_attribute = c_node.get_attribute("ver", NS_URI); - ArrayList capabilities = storage.get_features(ver_attribute); - if (capabilities.size == 0) { - ServiceDiscovery.Module.get_module(stream) - .request_info(stream, presence.from, new ServiceDiscoveryInfoResponseListenerImpl(storage, ver_attribute)); - } else { - ServiceDiscovery.Flag.get_flag(stream).set_entitiy_features(presence.from, capabilities); - } - } - } - - private class ServiceDiscoveryInfoResponseListenerImpl : ServiceDiscovery.InfoResponseListener, Object { - private Storage storage; - private string entity; - - public ServiceDiscoveryInfoResponseListenerImpl(Storage storage, string entity) { - this.storage = storage; - this.entity = entity; - } - public void on_result(XmppStream stream, ServiceDiscovery.InfoResult query_result) { - if (compute_hash(query_result.identities, query_result.features) == entity) { - storage.store_features(entity, query_result.features); - } - } - } - - private static string compute_hash(ArrayList identities, ArrayList features) { - identities.sort(compare_identities); - features.sort(); - - string s = ""; - foreach (ServiceDiscovery.Identity identity in identities) { - string s_identity = identity.category + "/" + identity.type_ + "//"; - if (identity.name != null) s_identity += identity.name; - s_identity += "<"; - s += s_identity; - } - foreach (string feature in features) { - s += feature + "<"; - } - - Checksum c = new Checksum(ChecksumType.SHA1); - c.update(s.data, -1); - size_t size = 20; - uint8[] buf = new uint8[size]; - c.get_digest(buf, ref size); - - return Base64.encode(buf); - } - - private static int compare_identities(ServiceDiscovery.Identity a, ServiceDiscovery.Identity b) { - int category_comp = a.category.collate(b.category); - if (category_comp != 0) return category_comp; - int type_comp = a.type_.collate(b.type_); - if (type_comp != 0) return type_comp; - // TODO lang - return 0; - } - } - - public interface Storage : Object { - public abstract void store_features(string entitiy, ArrayList capabilities); - public abstract ArrayList get_features(string entitiy); - } -} diff --git a/vala-xmpp/src/module/xep/0184_message_delivery_receipts.vala b/vala-xmpp/src/module/xep/0184_message_delivery_receipts.vala deleted file mode 100644 index 489592fa..00000000 --- a/vala-xmpp/src/module/xep/0184_message_delivery_receipts.vala +++ /dev/null @@ -1,62 +0,0 @@ -using Gdk; - -using Xmpp.Core; - -namespace Xmpp.Xep.MessageDeliveryReceipts { - private const string NS_URI = "urn:xmpp:receipts"; - - public class Module : XmppStreamModule { - public const string ID = "0184_message_delivery_receipts"; - - public signal void receipt_received(XmppStream stream, string jid, string id); - - public override void attach(XmppStream stream) { - ServiceDiscovery.Module.require(stream); - Message.Module.require(stream); - - ServiceDiscovery.Module.get_module(stream).add_feature(stream, NS_URI); - Message.Module.get_module(stream).received_message.connect(received_message); - Message.Module.get_module(stream).pre_send_message.connect(pre_send_message); - } - - public override void detach(XmppStream stream) { - Message.Module.get_module(stream).received_message.disconnect(received_message); - Message.Module.get_module(stream).pre_send_message.disconnect(pre_send_message); - } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stream.add_module(new Module()); - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - - private void received_message(XmppStream stream, Message.Stanza message) { - StanzaNode? received_node = message.stanza.get_subnode("received", NS_URI); - if (received_node != null) { - receipt_received(stream, message.from, received_node.get_attribute("id", NS_URI)); - } else if (message.stanza.get_subnode("request", NS_URI) != null) { - send_received(stream, message); - } - } - - private void send_received(XmppStream stream, Message.Stanza message) { - Message.Stanza received_message = new Message.Stanza(); - received_message.to = message.from; - received_message.stanza.put_node(new StanzaNode.build("received", NS_URI).add_self_xmlns().put_attribute("id", message.id)); - Message.Module.get_module(stream).send_message(stream, received_message); - } - - private void pre_send_message(XmppStream stream, Message.Stanza message) { - StanzaNode? received_node = message.stanza.get_subnode("received", NS_URI); - if (received_node != null) return; - if (message.body == null) return; - if (message.type_ == Message.Stanza.TYPE_GROUPCHAT) return; - message.stanza.put_node(new StanzaNode.build("request", NS_URI).add_self_xmlns()); - } - } -} diff --git a/vala-xmpp/src/module/xep/0199_ping.vala b/vala-xmpp/src/module/xep/0199_ping.vala deleted file mode 100644 index 82da1d23..00000000 --- a/vala-xmpp/src/module/xep/0199_ping.vala +++ /dev/null @@ -1,56 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Xep.Ping { - private const string NS_URI = "urn:xmpp:ping"; - - public class Module : XmppStreamModule { - public const string ID = "0199_ping"; - - public void send_ping(XmppStream stream, string jid, ResponseListener? listener = null) { - Iq.Stanza iq = new Iq.Stanza.get(new StanzaNode.build("ping", NS_URI).add_self_xmlns()); - iq.to = jid; - Iq.Module.get_module(stream).send_iq(stream, iq, listener == null? null : new IqResponseListenerImpl(listener)); - } - - private class IqResponseListenerImpl : Iq.ResponseListener, Object { - ResponseListener listener; - public IqResponseListenerImpl(ResponseListener listener) { - this.listener = listener; - } - public void on_result(XmppStream stream, Iq.Stanza iq) { - listener.on_result(stream); - } - } - - public override void attach(XmppStream stream) { - Iq.Module.require(stream); - Iq.Module.get_module(stream).register_for_namespace(NS_URI, new IqHandlerImpl()); - } - - public override void detach(XmppStream stream) { } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stream.add_module(new Module()); - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - - private class IqHandlerImpl : Iq.Handler, Object { - public void on_iq_get(XmppStream stream, Iq.Stanza iq) { - Iq.Module.get_module(stream).send_iq(stream, new Iq.Stanza.result(iq)); - } - public void on_iq_set(XmppStream stream, Iq.Stanza iq) { } - } - } - - public interface ResponseListener : Object { - public abstract void on_result(XmppStream stream); - } -} diff --git a/vala-xmpp/src/module/xep/0203_delayed_delivery.vala b/vala-xmpp/src/module/xep/0203_delayed_delivery.vala deleted file mode 100644 index 9f9761f2..00000000 --- a/vala-xmpp/src/module/xep/0203_delayed_delivery.vala +++ /dev/null @@ -1,62 +0,0 @@ -using Xmpp.Core; - -namespace Xmpp.Xep.DelayedDelivery { - private const string NS_URI = "urn:xmpp:delay"; - - public class Module : XmppStreamModule { - public const string ID = "0203_delayed_delivery"; - - public static void set_message_delay(Message.Stanza message, DateTime datetime) { - StanzaNode delay_node = (new StanzaNode.build("delay", NS_URI)).add_self_xmlns(); - delay_node.put_attribute("stamp", (new DateTimeProfiles.Module()).to_datetime(datetime)); - message.stanza.put_node(delay_node); - } - - public static DateTime? get_send_time(Message.Stanza message) { - StanzaNode? delay_node = message.stanza.get_subnode("delay", NS_URI); - if (delay_node != null) { - string time = delay_node.get_attribute("stamp"); - return (new DateTimeProfiles.Module()).parse_string(time); - } else { - return null; - } - } - - public override void attach(XmppStream stream) { - Message.Module.get_module(stream).pre_received_message.connect(on_pre_received_message); - } - - public override void detach(XmppStream stream) { } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stream.add_module(new Module()); - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - - private void on_pre_received_message(XmppStream stream, Message.Stanza message) { - DateTime? datetime = get_send_time(message); - if (datetime != null) message.add_flag(new MessageFlag(datetime)); - } - } - - public class MessageFlag : Message.MessageFlag { - public const string ID = "delayed_delivery"; - - public DateTime datetime { get; private set; } - - public MessageFlag(DateTime datetime) { - this.datetime = datetime; - } - - public static MessageFlag? get_flag(Message.Stanza message) { return (MessageFlag) message.get_flag(NS_URI, ID); } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - } -} diff --git a/vala-xmpp/src/module/xep/0280_message_carbons.vala b/vala-xmpp/src/module/xep/0280_message_carbons.vala deleted file mode 100644 index 18b2ecdf..00000000 --- a/vala-xmpp/src/module/xep/0280_message_carbons.vala +++ /dev/null @@ -1,91 +0,0 @@ -using Xmpp.Core; - -namespace Xmpp.Xep.MessageCarbons { - private const string NS_URI = "urn:xmpp:carbons:2"; - - public class Module : XmppStreamModule { - public const string ID = "0280_message_carbons_module"; - - public void enable(XmppStream stream) { - Iq.Stanza iq = new Iq.Stanza.set(new StanzaNode.build("enable", NS_URI).add_self_xmlns()); - Iq.Module.get_module(stream).send_iq(stream, iq); - } - - public void disable(XmppStream stream) { - Iq.Stanza iq = new Iq.Stanza.set(new StanzaNode.build("disable", NS_URI).add_self_xmlns()); - Iq.Module.get_module(stream).send_iq(stream, iq); - } - - public override void attach(XmppStream stream) { - Bind.Module.require(stream); - Iq.Module.require(stream); - Message.Module.require(stream); - ServiceDiscovery.Module.require(stream); - - stream.stream_negotiated.connect(enable); - Message.Module.get_module(stream).pre_received_message.connect(pre_received_message); - ServiceDiscovery.Module.get_module(stream).add_feature(stream, NS_URI); - } - - public override void detach(XmppStream stream) { - stream.stream_negotiated.disconnect(enable); - Message.Module.get_module(stream).pre_received_message.disconnect(pre_received_message); - } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stream.add_module(new Module()); - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - - private void pre_received_message(XmppStream stream, Message.Stanza message) { - StanzaNode? received_node = message.stanza.get_subnode("received", NS_URI); - StanzaNode? sent_node = received_node == null ? message.stanza.get_subnode("sent", NS_URI) : null; - StanzaNode? carbons_node = received_node != null ? received_node : sent_node; - if (carbons_node != null) { - StanzaNode? forwarded_node = carbons_node.get_subnode("forwarded", "urn:xmpp:forward:0"); - if (forwarded_node != null) { - StanzaNode? message_node = forwarded_node.get_subnode("message", Message.NS_URI); - string? from_attribute = message_node.get_attribute("from", Message.NS_URI); - // The security model assumed by this document is that all of the resources for a single user are in the same trust boundary. - // Any forwarded copies received by a Carbons-enabled client MUST be from that user's bare JID; any copies that do not meet this requirement MUST be ignored. - if (from_attribute != null && from_attribute == get_bare_jid(Bind.Flag.get_flag(stream).my_jid)) { - if (received_node != null) { - message.add_flag(new MessageFlag(MessageFlag.TYPE_RECEIVED)); - } else if (sent_node != null) { - message.add_flag(new MessageFlag(MessageFlag.TYPE_SENT)); - } - message.stanza = message_node; - message.rerun_parsing = true; - } - message.stanza = message_node; - message.rerun_parsing = true; - } - } - } - } - - public class MessageFlag : Message.MessageFlag { - public const string id = "message_carbons"; - - public const string TYPE_RECEIVED = "received"; - public const string TYPE_SENT = "sent"; - private string type_; - - public MessageFlag(string type) { - this.type_ = type; - } - - public static MessageFlag? get_flag(Message.Stanza message) { - return (MessageFlag) message.get_flag(NS_URI, id); - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return id; } - } -} diff --git a/vala-xmpp/src/module/xep/0333_chat_markers.vala b/vala-xmpp/src/module/xep/0333_chat_markers.vala deleted file mode 100644 index 0dc0e637..00000000 --- a/vala-xmpp/src/module/xep/0333_chat_markers.vala +++ /dev/null @@ -1,81 +0,0 @@ -using Gee; - -using Xmpp.Core; - -namespace Xmpp.Xep.ChatMarkers { -private const string NS_URI = "urn:xmpp:chat-markers:0"; - -public const string MARKER_RECEIVED = "received"; -public const string MARKER_DISPLAYED = "displayed"; -public const string MARKER_ACKNOWLEDGED = "acknowledged"; - -private const string[] MARKERS = {MARKER_RECEIVED, MARKER_DISPLAYED, MARKER_ACKNOWLEDGED}; - -public class Module : XmppStreamModule { - public const string ID = "0333_chat_markers"; - - public signal void marker_received(XmppStream stream, string jid, string marker, string id); - - public void send_marker(XmppStream stream, string jid, string message_id, string type_, string marker) { - Message.Stanza received_message = new Message.Stanza(); - received_message.to = jid; - received_message.type_ = type_; - received_message.stanza.put_node(new StanzaNode.build(marker, NS_URI).add_self_xmlns().put_attribute("id", message_id)); - Message.Module.get_module(stream).send_message(stream, received_message); - } - - public static bool requests_marking(Message.Stanza message) { - StanzaNode markable_node = message.stanza.get_subnode("markable", NS_URI); - return markable_node != null; - } - - public override void attach(XmppStream stream) { - Iq.Module.require(stream); - Message.Module.require(stream); - ServiceDiscovery.Module.require(stream); - - ServiceDiscovery.Module.get_module(stream).add_feature(stream, NS_URI); - Message.Module.get_module(stream).pre_send_message.connect(on_pre_send_message); - Message.Module.get_module(stream).received_message.connect(on_received_message); - } - - public override void detach(XmppStream stream) { - Message.Module.get_module(stream).pre_send_message.disconnect(on_pre_send_message); - Message.Module.get_module(stream).received_message.disconnect(on_received_message); - } - - public static Module? get_module(XmppStream stream) { - return (Module?) stream.get_module(NS_URI, ID); - } - - public static void require(XmppStream stream) { - if (get_module(stream) == null) stream.add_module(new ChatMarkers.Module()); - } - - public override string get_ns() { return NS_URI; } - public override string get_id() { return ID; } - - private void on_received_message(XmppStream stream, Message.Stanza message) { - if (message.type_ != Message.Stanza.TYPE_CHAT) return; - if (requests_marking(message)) { - send_marker(stream, message.from, message.id, message.type_, MARKER_RECEIVED); - return; - } - ArrayList nodes = message.stanza.get_all_subnodes(); - foreach (StanzaNode node in nodes) { - if (node.ns_uri == NS_URI && node.name in MARKERS) { - marker_received(stream, message.from, node.name, node.get_attribute("id", NS_URI)); - } - } - } - - private void on_pre_send_message(XmppStream stream, Message.Stanza message) { - StanzaNode? received_node = message.stanza.get_subnode("received", NS_URI); - if (received_node != null) return; - if (message.body == null) return; - if (message.type_ != Message.Stanza.TYPE_CHAT) return; - message.stanza.put_node(new StanzaNode.build("markable", NS_URI).add_self_xmlns()); - } -} - -} diff --git a/vala-xmpp/src/module/xep/pixbuf_storage.vala b/vala-xmpp/src/module/xep/pixbuf_storage.vala deleted file mode 100644 index 0caf4924..00000000 --- a/vala-xmpp/src/module/xep/pixbuf_storage.vala +++ /dev/null @@ -1,9 +0,0 @@ -using Gdk; - -namespace Xmpp.Xep { -public interface PixbufStorage : Object { - public abstract void store(string id, uint8[] data); - public abstract bool has_image(string id); - public abstract Pixbuf? get_image(string id); -} -} \ No newline at end of file diff --git a/vala-xmpp/vapi/uuid.vapi b/vala-xmpp/vapi/uuid.vapi deleted file mode 100644 index 991917a7..00000000 --- a/vala-xmpp/vapi/uuid.vapi +++ /dev/null @@ -1,60 +0,0 @@ -/* libuuid Vala Bindings - * Copyright 2014 Evan Nemerson - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, - * modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -[CCode (cheader_filename = "uuid.h", lower_case_cprefix = "uuid_")] -namespace UUID { - [CCode (cname = "int", has_type_id = false)] - public enum Variant { - NCS, - DCE, - MICROSOFT, - OTHER - } - - [CCode (cname = "int", has_type_id = false)] - public enum Type { - DCE_TIME, - DCE_RANDOM - } - - public static void clear ([CCode (array_length = false)] uint8 uu[16]); - public static void copy (uint8 dst[16], uint8 src[16]); - - public static void generate ([CCode (array_length = false)] uint8 @out[16]); - public static void generate_random ([CCode (array_length = false)] uint8 @out[16]); - public static void generate_time ([CCode (array_length = false)] uint8 @out[16]); - public static void generate_time_safe ([CCode (array_length = false)] uint8 @out[16]); - - public static bool is_null ([CCode (array_length = false)] uint8 uu[16]); - - public static int parse (string in, [CCode (array_length = false)] uint8 uu[16]); - - public static void unparse ([CCode (array_length = false)] uint8 uu[16], [CCode (array_length = false)] char @out[37]); - public static void unparse_lower ([CCode (array_length = false)] uint8 uu[16], [CCode (array_length = false)] char @out[37]); - public static void unparse_upper ([CCode (array_length = false)] uint8 uu[16], [CCode (array_length = false)] char @out[37]); - -// public static time_t time ([CCode (array_length = false)] uint8 uu[16], out Posix.timeval ret_tv); - public static UUID.Type type ([CCode (array_length = false)] uint8 uu[16]); - public static UUID.Variant variant ([CCode (array_length = false)] uint8 uu[16]); -} diff --git a/xmpp-vala/CMakeLists.txt b/xmpp-vala/CMakeLists.txt new file mode 100644 index 00000000..dd846c24 --- /dev/null +++ b/xmpp-vala/CMakeLists.txt @@ -0,0 +1,92 @@ +find_package(Vala REQUIRED) +find_package(PkgConfig REQUIRED) +find_package(GPGME REQUIRED) +find_package(LIBUUID REQUIRED) +include(GlibCompileResourcesSupport) +include(${VALA_USE_FILE}) + +set(ENGINE_PACKAGES + gee-0.8 + gio-2.0 + glib-2.0 + gtk+-3.0 +) + +pkg_check_modules(ENGINE REQUIRED ${ENGINE_PACKAGES}) + +vala_precompile(ENGINE_VALA_C +SOURCES + "src/core/namespace_state.vala" + "src/core/stanza_attribute.vala" + "src/core/stanza_node.vala" + "src/core/stanza_reader.vala" + "src/core/stanza_writer.vala" + "src/core/xmpp_stream.vala" + + "src/module/bind.vala" + "src/module/iq/module.vala" + "src/module/iq/stanza.vala" + "src/module/message/module.vala" + "src/module/message/stanza.vala" + "src/module/presence/flag.vala" + "src/module/presence/module.vala" + "src/module/presence/stanza.vala" + "src/module/roster/flag.vala" + "src/module/roster/item.vala" + "src/module/roster/module.vala" + "src/module/sasl.vala" + "src/module/stanza.vala" + "src/module/stanza_error.vala" + "src/module/stream_error.vala" + "src/module/tls.vala" + "src/module/util.vala" + + "src/module/xep/0027_pgp/flag.vala" + "src/module/xep/0027_pgp/module.vala" + "src/module/xep/0030_service_discovery/flag.vala" + "src/module/xep/0030_service_discovery/info_result.vala" + "src/module/xep/0030_service_discovery/items_result.vala" + "src/module/xep/0030_service_discovery/module.vala" + "src/module/xep/0045_muc/flag.vala" + "src/module/xep/0045_muc/module.vala" + "src/module/xep/0048_bookmarks/module.vala" + "src/module/xep/0048_bookmarks/conference.vala" + "src/module/xep/0049_private_xml_storage.vala" + "src/module/xep/0054_vcard/module.vala" + "src/module/xep/0060_pubsub.vala" + "src/module/xep/0082_date_time_profiles.vala" + "src/module/xep/0084_user_avatars.vala" + "src/module/xep/0085_chat_state_notifications.vala" + "src/module/xep/0115_entitiy_capabilities.vala" + "src/module/xep/0199_ping.vala" + "src/module/xep/0184_message_delivery_receipts.vala" + "src/module/xep/0203_delayed_delivery.vala" + "src/module/xep/0280_message_carbons.vala" + "src/module/xep/0333_chat_markers.vala" + "src/module/xep/pixbuf_storage.vala" +CUSTOM_VAPIS + "${CMAKE_CURRENT_SOURCE_DIR}/vapi/uuid.vapi" +PACKAGES + ${ENGINE_PACKAGES} + gpgme +GENERATE_VAPI + xmpp-vala +GENERATE_HEADER + xmpp-vala +OPTIONS + --target-glib=2.38 + -g + --thread + --vapidir=${CMAKE_SOURCE_DIR}/vapi +) + +set(CFLAGS ${ENGINE_CFLAGS} ${GPGME_CFLAGS} ${LIBUUID_CFLAGS} -g ${VALA_CFLAGS}) +add_definitions(${CFLAGS}) +add_library(xmpp-vala SHARED ${ENGINE_VALA_C}) +target_link_libraries(xmpp-vala ${ENGINE_LIBRARIES} ${GPGME_LIBRARIES} ${LIBUUID_LIBRARIES}) + +add_custom_target(xmpp-vala-vapi +DEPENDS + ${CMAKE_BINARY_DIR}/xmpp-vala/xmpp-vala.vapi +) + diff --git a/xmpp-vala/src/core/namespace_state.vala b/xmpp-vala/src/core/namespace_state.vala new file mode 100644 index 00000000..e71607fa --- /dev/null +++ b/xmpp-vala/src/core/namespace_state.vala @@ -0,0 +1,80 @@ +using Gee; + +namespace Xmpp.Core { +public class NamespaceState { + private HashMap uri_to_name = new HashMap (); + private HashMap name_to_uri = new HashMap (); + public string current_ns_uri; + + public NamespaceState () { + add_assoc(XMLNS_URI, "xmlns"); + add_assoc("http://www.w3.org/XML/1998/namespace", "xml"); + current_ns_uri = "http://www.w3.org/XML/1998/namespace"; + } + + public NamespaceState.for_stanza () { + this(); + add_assoc("http://etherx.jabber.org/streams", "stream"); + current_ns_uri = "jabber:client"; + } + + public NamespaceState.copy (NamespaceState old) { + foreach (string key in old.uri_to_name.keys) { + add_assoc(key, old.uri_to_name[key]); + } + set_current(old.current_ns_uri); + } + + public NamespaceState.with_assoc (NamespaceState old, string ns_uri, string name) { + this.copy(old); + add_assoc(ns_uri, name); + } + + public NamespaceState.with_current (NamespaceState old, string current_ns_uri) { + this.copy(old); + set_current(current_ns_uri); + } + + public void add_assoc (string ns_uri, string name) { + name_to_uri[name] = ns_uri; + uri_to_name[ns_uri] = name; + } + + public void set_current (string current_ns_uri) { + this.current_ns_uri = current_ns_uri; + } + + public string find_name (string ns_uri) throws XmlError { + if (uri_to_name.has_key(ns_uri)) { + return uri_to_name[ns_uri]; + } + throw new XmlError.NS_DICT_ERROR(@"NS URI $ns_uri not found."); + } + + public string find_uri (string name) throws XmlError { + if (name_to_uri.has_key(name)) { + return name_to_uri[name]; + } + throw new XmlError.NS_DICT_ERROR(@"NS name $name not found."); + } + + public NamespaceState clone() { + return new NamespaceState.copy(this); + } + + public string to_string () { + StringBuilder sb = new StringBuilder (); + sb.append ("NamespaceState{"); + foreach (string key in uri_to_name.keys) { + sb.append(key); + sb.append_c('='); + sb.append(uri_to_name[key]); + sb.append_c(','); + } + sb.append("current="); + sb.append(current_ns_uri); + sb.append_c('}'); + return sb.str; + } +} +} \ No newline at end of file diff --git a/xmpp-vala/src/core/stanza_attribute.vala b/xmpp-vala/src/core/stanza_attribute.vala new file mode 100644 index 00000000..3169e90e --- /dev/null +++ b/xmpp-vala/src/core/stanza_attribute.vala @@ -0,0 +1,29 @@ +namespace Xmpp.Core { +public class StanzaAttribute : StanzaEntry { + + public StanzaAttribute() {} + + public StanzaAttribute.build(string ns_uri, string name, string val) { + this.ns_uri = ns_uri; + this.name = name; + this.val = val; + } + + public string to_string() { + if (ns_uri == null) { + return @"$name='$val'"; + } else { + return @"{$ns_uri}:$name='$val'"; + } + } + + public string to_xml(NamespaceState? state_) throws XmlError { + NamespaceState state = state_ ?? new NamespaceState(); + if (ns_uri == state.current_ns_uri || (ns_uri == XMLNS_URI && name == "xmlns")) { + return @"$name='$val'"; + } else { + return "%s:%s='%s'".printf (state.find_name (ns_uri), name, val); + } + } +} +} diff --git a/xmpp-vala/src/core/stanza_node.vala b/xmpp-vala/src/core/stanza_node.vala new file mode 100644 index 00000000..1dfacfdd --- /dev/null +++ b/xmpp-vala/src/core/stanza_node.vala @@ -0,0 +1,297 @@ +using Gee; + +namespace Xmpp.Core { + +public abstract class StanzaEntry { + public string? ns_uri; + public string name; + public string? val; + + public string encoded_val { + owned get { + return val.replace("&", "&").replace("\"", """).replace("'", "'").replace("<", "<").replace(">", ">"); + } + set { + string tmp = value.replace(">", ">").replace("<", "<").replace("'","'").replace(""","\""); + while (tmp.contains("&#")) { + int start = tmp.index_of("&#"); + int end = tmp.index_of(";", start); + if (end < start) break; + unichar num = -1; + if (tmp[start+2]=='x') { + tmp.substring(start+3, start-end-3).scanf("%x", &num); + } else { + num = int.parse(tmp.substring(start+2, start-end-2)); + } + tmp = tmp.splice(start, end, num.to_string()); + } + val = tmp.replace("&", "&"); + } + } + + public virtual unowned string? get_string_content() { + return val; + } +} + +public class NoStanza : StanzaEntry { + public NoStanza(string? name) { + this.name = name; + } +} + +public class StanzaNode : StanzaEntry { + public ArrayList sub_nodes = new ArrayList(); + public ArrayList attributes = new ArrayList(); + public bool has_nodes = false; + public bool pseudo = false; + + public StanzaNode () {} + + public StanzaNode.build(string name, string ns_uri = "jabber:client", ArrayList? nodes = null, ArrayList? attrs = null) { + this.ns_uri = ns_uri; + this.name = name; + if (nodes != null) this.sub_nodes.add_all(nodes); + if (attrs != null) this.attributes.add_all(attrs); + } + + public StanzaNode.text(string text) { + this.name = "#text"; + this.val = text; + } + + public StanzaNode.encoded_text(string text) { + this.name = "#text"; + this.encoded_val = text; + } + + public StanzaNode add_self_xmlns() { + return put_attribute("xmlns", ns_uri); + } + + public unowned string? get_attribute(string name, string? ns_uri = null) { + string _name = name; + string? _ns_uri = ns_uri; + if (_ns_uri == null) { + if (_name.contains(":")) { + var lastIndex = _name.last_index_of_char(':'); + _ns_uri = _name.substring(0, lastIndex); + _name = _name.substring(lastIndex + 1); + } else { + _ns_uri = this.ns_uri; + } + } + foreach(var attr in attributes) { + if (attr.ns_uri == _ns_uri && attr.name == _name) return attr.val; + } + return null; + } + + public StanzaAttribute get_attribute_raw(string name, string? ns_uri = null) { + string _name = name; + string? _ns_uri = ns_uri; + if (_ns_uri == null) { + if (_name.contains(":")) { + var lastIndex = _name.last_index_of_char(':'); + _ns_uri = _name.substring(0, lastIndex); + _name = _name.substring(lastIndex + 1); + } else { + _ns_uri = this.ns_uri; + } + } + foreach(var attr in attributes) { + if (attr.ns_uri == _ns_uri && attr.name == _name) return attr; + } + return null; + } + + public ArrayList get_attributes_by_ns_uri(string ns_uri) { + ArrayList ret = new ArrayList (); + foreach(var attr in attributes) { + if (attr.ns_uri == ns_uri) ret.add(attr); + } + return ret; + } + + public StanzaEntry get(...) { + va_list l = va_list(); + StanzaEntry? res = get_deep_attribute_(va_list.copy(l)); + if (res != null) return res; + res = get_deep_subnode_(va_list.copy(l)); + if (res != null) return res; + return new NoStanza("-"); + } + + public unowned string? get_deep_attribute(...) { + va_list l = va_list(); + var res = get_deep_attribute_(va_list.copy(l)); + if (res == null) return null; + return res.val; + } + + public StanzaAttribute? get_deep_attribute_(va_list l) { + StanzaNode? node = this; + string? attribute_name = l.arg(); + if (attribute_name == null) return null; + while(true) { + string? s = l.arg(); + if (s == null) break; + node = get_subnode(attribute_name); + if (node == null) return null; + attribute_name = s; + } + return node.get_attribute_raw(attribute_name); + } + + public StanzaNode? get_subnode(string name, string? ns_uri = null, bool recurse = false) { + string _name = name; + string? _ns_uri = ns_uri; + if (ns_uri == null) { + if (_name.contains(":")) { + var lastIndex = _name.last_index_of_char(':'); + _ns_uri = _name.substring(0, lastIndex); + _name = _name.substring(lastIndex + 1); + } else { + _ns_uri = this.ns_uri; + } + } + foreach(var node in sub_nodes) { + if (node.ns_uri == _ns_uri && node.name == _name) return node; + if (recurse) { + var x = node.get_subnode(_name, _ns_uri, recurse); + if (x != null) return x; + } + } + return null; + } + + public ArrayList get_subnodes(string name, string? ns_uri = null, bool recurse = false) { + ArrayList ret = new ArrayList(); + if (ns_uri == null) ns_uri = this.ns_uri; + foreach(var node in sub_nodes) { + if (node.ns_uri == ns_uri && node.name == name) ret.add(node); + if (recurse) { + ret.add_all(node.get_subnodes(name, ns_uri, recurse)); + } + } + return ret; + } + + public StanzaNode? get_deep_subnode(...) { + va_list l = va_list(); + return get_deep_subnode_(va_list.copy(l)); + } + + public StanzaNode? get_deep_subnode_(va_list l) { + StanzaNode? node = this; + while(true) { + string? s = l.arg(); + if (s == null) break; + node = get_subnode(s); + } + return node; + } + + public ArrayList get_all_subnodes() { + return sub_nodes; + } + + public void add_attribute(StanzaAttribute attr) { + attributes.add(attr); + } + + public override unowned string? get_string_content() { + if (val != null) return val; + if (sub_nodes.size == 1) return sub_nodes[0].get_string_content(); + return null; + } + + public StanzaNode put_attribute(string name, string val, string? ns_uri = null) { + if (name == "xmlns") ns_uri = XMLNS_URI; + if (ns_uri == null) ns_uri = this.ns_uri; + attributes.add(new StanzaAttribute.build(ns_uri, name, val)); + return this; + } + + /** + * Set only occurence + **/ + public void set_attribute(string name, string val, string? ns_uri = null) { + if (ns_uri == null) ns_uri = this.ns_uri; + foreach(var attr in attributes) { + if (attr.ns_uri == ns_uri && attr.name == name) { + attr.val = val; + return; + } + } + put_attribute(name, val, ns_uri); + } + + public StanzaNode put_node(StanzaNode node) { + sub_nodes.add(node); + return this; + } + + public string to_string(int i = 0) { + string indent = string.nfill (i * 2, ' '); + if (name == "#text") { + return @"$indent$val\n"; + } + var sb = new StringBuilder(); + sb.append(@"$indent<{$ns_uri}:$name"); + foreach (StanzaAttribute attr in attributes) { + sb.append_printf(" %s", attr.to_string()); + } + if (!has_nodes && sub_nodes.size == 0) { + sb.append(" />\n"); + } else { + sb.append(">\n"); + if (sub_nodes.size != 0) { + foreach (StanzaNode subnode in sub_nodes) { + sb.append(subnode.to_string(i+1)); + } + sb.append(@"$indent\n"); + } + } + return sb.str; + } + + public string to_xml (NamespaceState? state = null) throws XmlError { + NamespaceState my_state = state ?? new NamespaceState.for_stanza(); + if (name == "#text") return @"$encoded_val"; + foreach(var xmlns in get_attributes_by_ns_uri (XMLNS_URI)) { + if (xmlns.name == "xmlns") { + my_state = new NamespaceState.with_current(my_state, xmlns.val); + } else { + my_state = new NamespaceState.with_assoc(my_state, xmlns.val, xmlns.name); + } + } + var sb = new StringBuilder(); + if (ns_uri == my_state.current_ns_uri) { + sb.append(@"<$name"); + } else { + sb.append_printf("<%s:%s", my_state.find_name (ns_uri), name); + } + var attr_ns_state = new NamespaceState.with_current(my_state, ns_uri); + foreach (StanzaAttribute attr in attributes) { + sb.append_printf(" %s", attr.to_xml(attr_ns_state)); + } + if (!has_nodes && sub_nodes.size == 0) { + sb.append("/>"); + } else { + sb.append(">"); + if (sub_nodes.size != 0) { + foreach (StanzaNode subnode in sub_nodes) { + sb.append(subnode.to_xml(my_state)); + } + if (ns_uri == my_state.current_ns_uri) { + sb.append(@""); + } else { + sb.append_printf("", my_state.find_name (ns_uri), name); + } + } + } + return sb.str; + } +} +} diff --git a/xmpp-vala/src/core/stanza_reader.vala b/xmpp-vala/src/core/stanza_reader.vala new file mode 100644 index 00000000..0a90f855 --- /dev/null +++ b/xmpp-vala/src/core/stanza_reader.vala @@ -0,0 +1,260 @@ +using Gee; + +namespace Xmpp.Core { +public const string XMLNS_URI = "http://www.w3.org/2000/xmlns/"; +public const string JABBER_URI = "jabber:client"; + +public errordomain XmlError { + XML_ERROR, + NS_DICT_ERROR, + UNSUPPORTED, + EOF, + BAD_XML, + IO_ERROR +} + +public class StanzaReader { + private static int BUFFER_MAX = 4096; + + private InputStream? input; + private uint8[] buffer; + private int buffer_fill = 0; + private int buffer_pos = 0; + private Cancellable cancellable = new Cancellable(); + + private NamespaceState ns_state = new NamespaceState(); + + public StanzaReader.for_buffer(uint8[] buffer) { + this.buffer = buffer; + this.buffer_fill = buffer.length; + } + + public StanzaReader.for_string(string s) { + this.for_buffer(s.data); + } + + public StanzaReader.for_stream(InputStream input) { + this.input = input; + buffer = new uint8[BUFFER_MAX]; + } + + public void cancel() { + cancellable.cancel(); + } + + private void update_buffer() throws XmlError { + try { + if (input == null) throw new XmlError.EOF("No input stream specified and end of buffer reached."); + if (cancellable.is_cancelled()) throw new XmlError.EOF("Input stream is canceled."); + buffer_fill = (int) input.read(buffer, cancellable); + if (buffer_fill == 0) throw new XmlError.EOF("End of input stream reached."); + buffer_pos = 0; + } catch (GLib.IOError e) { + throw new XmlError.IO_ERROR("IOError in GLib: %s".printf(e.message)); + } + } + + private char read_single() throws XmlError { + if (buffer_pos >= buffer_fill) { + update_buffer(); + } + return (char) buffer[buffer_pos++]; + } + + private char peek_single() throws XmlError { + var res = read_single(); + buffer_pos--; + return res; + } + + private bool is_ws(uint8 what) { + return what == ' ' || what == '\t' || what == '\r' || what == '\n'; + } + + private void skip_single() { + buffer_pos++; + } + + private void skip_until_non_ws() throws XmlError { + while (is_ws(peek_single())) { + skip_single(); + } + } + + private string read_until_ws() throws XmlError { + var res = new StringBuilder(); + var what = peek_single(); + while(!is_ws(what)) { + res.append_c(read_single()); + what = peek_single(); + } + return res.str; + } + + private string read_until_char_or_ws(char x, char y = 0) throws XmlError { + var res = new StringBuilder(); + var what = peek_single(); + while(what != x && what != y && !is_ws(what)) { + res.append_c(read_single()); + what = peek_single(); + } + return res.str; + } + + private string read_until_char(char x) throws XmlError { + var res = new StringBuilder(); + var what = peek_single(); + while(what != x) { + res.append_c(read_single()); + what = peek_single(); + } + return res.str; + } + + private StanzaAttribute read_attribute() throws XmlError { + var res = new StanzaAttribute(); + res.name = read_until_char_or_ws('='); + if (read_single() == '=') { + var quot = peek_single(); + if (quot == '\'' || quot == '"') { + skip_single(); + res.encoded_val = read_until_char(quot); + skip_single(); + } else { + res.encoded_val = read_until_ws(); + } + } + return res; + } + + private void handle_entry_ns(StanzaEntry entry, string default_uri = ns_state.current_ns_uri) throws XmlError { + if (entry.ns_uri != null) return; + if (entry.name.contains(":")) { + var split = entry.name.split(":"); + entry.ns_uri = ns_state.find_uri(split[0]); + entry.name = split[1]; + } else { + entry.ns_uri = default_uri; + } + } + + private void handle_stanza_ns(StanzaNode res) throws XmlError { + foreach (StanzaAttribute attr in res.attributes) { + if (attr.name == "xmlns") { + attr.ns_uri = XMLNS_URI; + ns_state.set_current(attr.val); + } else if (attr.name.contains(":")) { + var split = attr.name.split(":"); + if (split[0] == "xmlns") { + attr.ns_uri = XMLNS_URI; + attr.name = split[1]; + ns_state.add_assoc(attr.val, attr.name); + } + } + } + handle_entry_ns(res); + foreach (StanzaAttribute attr in res.attributes) { + handle_entry_ns(attr, res.ns_uri); + } + } + + public StanzaNode read_node_start() throws XmlError { + var res = new StanzaNode(); + res.attributes = new ArrayList(); + var eof = false; + if (peek_single() == '<') skip_single(); + if (peek_single() == '?') res.pseudo = true; + if (peek_single() == '/') { + eof = true; + skip_single(); + res.name = read_until_char_or_ws('>'); + while(peek_single() != '>') { + skip_single(); + } + skip_single(); + res.has_nodes = false; + res.pseudo = false; + handle_stanza_ns(res); + return res; + } + res.name = read_until_char_or_ws('>', '/'); + skip_until_non_ws(); + while (peek_single() != '/' && peek_single() != '>' && peek_single() != '?') { + res.attributes.add(read_attribute()); + skip_until_non_ws(); + } + if (read_single() == '/' || res.pseudo ) { + res.has_nodes = false; + skip_single(); + } else { + res.has_nodes = true; + } + handle_stanza_ns(res); + return res; + } + + public StanzaNode read_text_node() throws XmlError { + var res = new StanzaNode(); + res.name = "#text"; + res.ns_uri = ns_state.current_ns_uri; + res.encoded_val = read_until_char('<').strip(); + return res; + } + + public StanzaNode read_root_node() throws XmlError { + skip_until_non_ws(); + if (peek_single() == '<') { + var res = read_node_start(); + if (res.pseudo) { + return read_root_node(); + } + return res; + } else { + throw new XmlError.BAD_XML("Content before root node"); + } + } + + public StanzaNode read_stanza_node(NamespaceState? baseNs = null) throws XmlError { + ns_state = baseNs ?? new NamespaceState.for_stanza(); + var res = read_node_start(); + if (res.has_nodes) { + bool finishNodeSeen = false; + do { + skip_until_non_ws(); + if (peek_single() == '<') { + skip_single(); + if (peek_single() == '/') { + skip_single(); + string desc = read_until_char('>'); + skip_single(); + if (desc.contains(":")) { + var split = desc.split(":"); + assert(split[0] == ns_state.find_name(res.ns_uri)); + assert(split[1] == res.name); + } else { + assert(ns_state.current_ns_uri == res.ns_uri); + assert(desc == res.name); + } + return res; + } else { + res.sub_nodes.add(read_stanza_node(ns_state.clone())); + ns_state = baseNs ?? new NamespaceState.for_stanza(); + } + } else { + res.sub_nodes.add(read_text_node()); + } + } while (!finishNodeSeen); + } + return res; + } + + public StanzaNode read_node(NamespaceState? baseNs = null) throws XmlError { + skip_until_non_ws(); + if (peek_single() == '<') { + return read_stanza_node(baseNs ?? new NamespaceState.for_stanza()); + } else { + return read_text_node(); + } + } +} +} diff --git a/xmpp-vala/src/core/stanza_writer.vala b/xmpp-vala/src/core/stanza_writer.vala new file mode 100644 index 00000000..26524d7b --- /dev/null +++ b/xmpp-vala/src/core/stanza_writer.vala @@ -0,0 +1,27 @@ +namespace Xmpp.Core { +public class StanzaWriter { + private OutputStream output; + + public StanzaWriter.for_stream(OutputStream output) { + this.output = output; + } + + public void write_node(StanzaNode node) throws XmlError { + try { + lock(output) { + output.write_all(node.to_xml().data, null); + } + } catch (GLib.IOError e) { + throw new XmlError.IO_ERROR(@"IOError in GLib: $(e.message)"); + } + } + + public async void write(string s) throws XmlError { + try { + output.write_all(s.data, null); + } catch (GLib.IOError e) { + throw new XmlError.IO_ERROR(@"IOError in GLib: $(e.message)"); + } + } +} +} \ No newline at end of file diff --git a/xmpp-vala/src/core/xmpp_stream.vala b/xmpp-vala/src/core/xmpp_stream.vala new file mode 100644 index 00000000..e30a1c9b --- /dev/null +++ b/xmpp-vala/src/core/xmpp_stream.vala @@ -0,0 +1,245 @@ +using Gee; + +namespace Xmpp.Core { + +public errordomain IOStreamError { + READ, + WRITE, + CONNECT, + DISCONNECT + +} + +public class XmppStream { + private static string NS_URI = "http://etherx.jabber.org/streams"; + + public string remote_name; + public bool debug = false; + public StanzaNode? features { get; private set; default = new StanzaNode.build("features", NS_URI); } + + private IOStream? stream; + private StanzaReader? reader; + private StanzaWriter? writer; + + private ArrayList flags = new ArrayList(); + private ArrayList modules = new ArrayList(); + private bool setup_needed = false; + private bool negotiation_complete = false; + + public signal void received_node(XmppStream stream, StanzaNode node); + public signal void received_root_node(XmppStream stream, StanzaNode node); + public signal void received_features_node(XmppStream stream); + public signal void received_message_stanza(XmppStream stream, StanzaNode node); + public signal void received_presence_stanza(XmppStream stream, StanzaNode node); + public signal void received_iq_stanza(XmppStream stream, StanzaNode node); + public signal void received_nonza(XmppStream stream, StanzaNode node); + public signal void stream_negotiated(XmppStream stream); + + public void connect(string? remote_name = null) throws IOStreamError { + if (remote_name != null) this.remote_name = remote_name; + SocketClient client = new SocketClient(); + try { + SocketConnection? stream = client.connect(new NetworkService("xmpp-client", "tcp", this.remote_name)); + if (stream == null) throw new IOStreamError.CONNECT("client.connect() returned null"); + reset_stream(stream); + } catch (Error e) { + stderr.printf("CONNECTION LOST?\n"); + throw new IOStreamError.CONNECT(e.message); + } + loop(); + } + + public void disconnect() throws IOStreamError { + if (writer == null) throw new IOStreamError.DISCONNECT("trying to disconnect, but no stream open"); + if (debug) stderr.puts("OUT\n\n"); + writer.write.begin(""); + reader.cancel(); + stream.close_async.begin(); + } + + public void reset_stream(IOStream stream) { + this.stream = stream; + reader = new StanzaReader.for_stream(stream.input_stream); + writer = new StanzaWriter.for_stream(stream.output_stream); + require_setup(); + } + + public void require_setup() { + setup_needed = true; + } + + public bool is_setup_needed() { + return setup_needed; + } + + public StanzaNode read() throws IOStreamError { + if (reader == null) throw new IOStreamError.READ("trying to read, but no stream open"); + try { + var node = reader.read_node(); + if (debug) stderr.printf("IN\n%s\n", node.to_string()); + return node; + } catch (XmlError e) { + throw new IOStreamError.READ(e.message); + } + } + + public void write(StanzaNode node) throws IOStreamError { + if (writer == null) throw new IOStreamError.WRITE("trying to write, but no stream open"); + try { + if (debug) stderr.printf("OUT\n%s\n", node.to_string()); + writer.write_node(node); + } catch (XmlError e) { + throw new IOStreamError.WRITE(e.message); + } + } + + public IOStream get_stream() { + return stream; + } + + public void add_flag(XmppStreamFlag flag) { + flags.add(flag); + } + + public XmppStreamFlag? get_flag(string ns, string id) { + foreach (var flag in flags) { + if (flag.get_ns() == ns && flag.get_id() == id) { + return flag; + } + } + return null; + } + + public void remove_flag(XmppStreamFlag flag) { + flags.remove(flag); + } + + public XmppStream add_module(XmppStreamModule module) { + modules.add(module); + if (negotiation_complete || module as XmppStreamNegotiationModule != null) { + module.attach(this); + } + return this; + } + + public void remove_modules() { + foreach (XmppStreamModule module in modules) module.detach(this); + } + + public XmppStreamModule? get_module(string ns, string id) { + foreach (var module in modules) { + if (module.get_ns() == ns && module.get_id() == id) { + return module; + } + } + return null; + } + + private void setup() throws IOStreamError { + var outs = new StanzaNode.build("stream", "http://etherx.jabber.org/streams") + .put_attribute("to", remote_name) + .put_attribute("version", "1.0") + .put_attribute("xmlns", "jabber:client") + .put_attribute("stream", "http://etherx.jabber.org/streams", XMLNS_URI); + outs.has_nodes = true; + write(outs); + received_root_node(this, read_root()); + } + + private void loop() throws IOStreamError { + while(true) { + if (setup_needed) { + setup(); + setup_needed = false; + } + + StanzaNode node = read(); + received_node(this, node); + + if (node.ns_uri == NS_URI && node.name == "features") { + features = node; + received_features_node(this); + } else if (node.ns_uri == NS_URI && node.name == "stream" && node.pseudo) { + print("disconnect\n"); + disconnect(); + return; + } else if (node.ns_uri == JABBER_URI) { + if (node.name == "message") { + received_message_stanza(this, node); + } else if (node.name == "presence") { + received_presence_stanza(this, node); + } else if (node.name == "iq") { + received_iq_stanza(this, node); + } else { + received_nonza(this, node); + } + } else { + received_nonza(this, node); + } + + if (!negotiation_complete && negotiation_modules_done()) { + negotiation_complete = true; + attach_non_negotation_modules(); + stream_negotiated(this); + } + } + } + + private bool negotiation_modules_done() throws IOStreamError { + if (!setup_needed) { + bool mandatory_outstanding = false; + bool negotiation_active = false; + foreach (XmppStreamModule module in modules) { + XmppStreamNegotiationModule negotiation_module = module as XmppStreamNegotiationModule; + if (negotiation_module != null) { + if (negotiation_module.negotiation_active(this)) negotiation_active = true; + if (negotiation_module.mandatory_outstanding(this)) mandatory_outstanding = true; + } + } + if (!negotiation_active) { + if (mandatory_outstanding) { + throw new IOStreamError.CONNECT("mandatory-to-negotiate feature not negotiated"); + } else { + return true; + } + } + } + return false; + } + + private void attach_non_negotation_modules() { + foreach (XmppStreamModule module in modules) { + if (module as XmppStreamNegotiationModule == null) { + module.attach(this); + } + } + } + + private StanzaNode read_root() throws IOStreamError { + try { + var node = reader.read_root_node(); + if (debug) stderr.printf("IN\n%s\n", node.to_string()); + return node; + } catch (XmlError e) { + throw new IOStreamError.READ(e.message); + } + } +} + +public abstract class XmppStreamFlag { + internal abstract string get_ns(); + internal abstract string get_id(); +} + +public abstract class XmppStreamModule : Object { + internal abstract void attach(XmppStream stream); + internal abstract void detach(XmppStream stream); + internal abstract string get_ns(); + internal abstract string get_id(); +} + +public abstract class XmppStreamNegotiationModule : XmppStreamModule { + internal abstract bool mandatory_outstanding(XmppStream stream); + internal abstract bool negotiation_active(XmppStream stream); +} +} diff --git a/xmpp-vala/src/module/bind.vala b/xmpp-vala/src/module/bind.vala new file mode 100644 index 00000000..d01fda7a --- /dev/null +++ b/xmpp-vala/src/module/bind.vala @@ -0,0 +1,93 @@ +using Xmpp.Core; + +namespace Xmpp.Bind { + private const string NS_URI = "urn:ietf:params:xml:ns:xmpp-bind"; + + /** The parties to a stream MUST consider resource binding as mandatory-to-negotiate. (RFC6120 7.3.1) */ + public class Module : XmppStreamNegotiationModule { + public const string ID = "bind_module"; + + private string requested_resource; + + public signal void bound_to_resource(XmppStream stream, string my_jid); + + public Module(string requested_resource) { + this.requested_resource = requested_resource; + } + + public void iq_response_stanza(XmppStream stream, Iq.Stanza iq) { + var flag = Flag.get_flag(stream); + if (flag == null || flag.finished) return; + + if (iq.type_ == Iq.Stanza.TYPE_RESULT) { + flag.my_jid = iq.stanza.get_subnode("jid", NS_URI, true).get_string_content(); + flag.finished = true; + bound_to_resource(stream, flag.my_jid); + } + } + + public void received_features_node(XmppStream stream) { + if (stream.is_setup_needed()) return; + + var bind = stream.features.get_subnode("bind", NS_URI); + if (bind != null) { + var flag = new Flag(); + StanzaNode bind_node = new StanzaNode.build("bind", NS_URI).add_self_xmlns() + .put_node(new StanzaNode.build("resource", NS_URI).put_node(new StanzaNode.text(requested_resource))); + Iq.Module.get_module(stream).send_iq(stream, new Iq.Stanza.set(bind_node), new IqResponseListenerImpl()); + stream.add_flag(flag); + } + } + + private class IqResponseListenerImpl : Iq.ResponseListener, Object { + public void on_result(XmppStream stream, Iq.Stanza iq) { + Bind.Module.get_module(stream).iq_response_stanza(stream, iq); + } + } + + public override void attach(XmppStream stream) { + Iq.Module.require(stream); + stream.received_features_node.connect(this.received_features_node); + } + + public override void detach(XmppStream stream) { + stream.received_features_node.disconnect(this.received_features_node); + } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stream.add_module(new Bind.Module("")); + } + + public override bool mandatory_outstanding(XmppStream stream) { + return !Flag.has_flag(stream) || !Flag.get_flag(stream).finished; + } + + public override bool negotiation_active(XmppStream stream) { + return Flag.has_flag(stream) && !Flag.get_flag(stream).finished; + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + } + + public class Flag : XmppStreamFlag { + public const string ID = "bind"; + public string? my_jid; + public bool finished = false; + + public static Flag? get_flag(XmppStream stream) { + return (Flag?) stream.get_flag(NS_URI, ID); + } + + public static bool has_flag(XmppStream stream) { + return get_flag(stream) != null; + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + } +} diff --git a/xmpp-vala/src/module/iq/module.vala b/xmpp-vala/src/module/iq/module.vala new file mode 100644 index 00000000..b5c50bd7 --- /dev/null +++ b/xmpp-vala/src/module/iq/module.vala @@ -0,0 +1,89 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Iq { + private const string NS_URI = "jabber:client"; + + public class Module : XmppStreamNegotiationModule { + public const string ID = "iq_module"; + + private HashMap responseListeners = new HashMap(); + private HashMap> namespaceRegistrants = new HashMap>(); + + public void send_iq(XmppStream stream, Iq.Stanza iq, ResponseListener? listener = null) { + stream.write(iq.stanza); + if (listener != null) { + responseListeners.set(iq.id, listener); + } + } + + public void register_for_namespace(string namespace, Handler module) { + if (!namespaceRegistrants.has_key(namespace)) { + namespaceRegistrants.set(namespace, new ArrayList()); + } + namespaceRegistrants[namespace].add(module); + } + + public override void attach(XmppStream stream) { + stream.received_iq_stanza.connect(on_received_iq_stanza); + } + + public override void detach(XmppStream stream) { + stream.received_iq_stanza.disconnect(on_received_iq_stanza); + } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stream.add_module(new Iq.Module()); + } + + public override bool mandatory_outstanding(XmppStream stream) { return false; } + + public override bool negotiation_active(XmppStream stream) { return false; } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + + private void on_received_iq_stanza(XmppStream stream, StanzaNode node) { + Iq.Stanza iq = new Iq.Stanza.from_stanza(node, Bind.Flag.has_flag(stream) ? Bind.Flag.get_flag(stream).my_jid : null); + + if (iq.type_ == Iq.Stanza.TYPE_RESULT || iq.is_error()) { + if (responseListeners.has_key(iq.id)) { + ResponseListener? listener = responseListeners.get(iq.id); + if (listener != null) { + listener.on_result(stream, iq); + } + responseListeners.unset(iq.id); + } + } else { + ArrayList children = node.get_all_subnodes(); + if (children.size == 1 && namespaceRegistrants.has_key(children[0].ns_uri)) { + ArrayList handlers = namespaceRegistrants[children[0].ns_uri]; + foreach (Handler handler in handlers) { + if (iq.type_ == Iq.Stanza.TYPE_GET) { + handler.on_iq_get(stream, iq); + } else if (iq.type_ == Iq.Stanza.TYPE_SET) { + handler.on_iq_set(stream, iq); + } + } + } else { + Iq.Stanza unaviable_error = new Iq.Stanza.error(iq, new StanzaNode.build("service-unaviable", "urn:ietf:params:xml:ns:xmpp-stanzas").add_self_xmlns()); + send_iq(stream, unaviable_error); + } + } + } + } + + public interface Handler : Object { + public abstract void on_iq_get(XmppStream stream, Iq.Stanza iq); + public abstract void on_iq_set(XmppStream stream, Iq.Stanza iq); + } + + public interface ResponseListener : Object { + public abstract void on_result(XmppStream stream, Iq.Stanza iq); + } +} diff --git a/xmpp-vala/src/module/iq/stanza.vala b/xmpp-vala/src/module/iq/stanza.vala new file mode 100644 index 00000000..561c5866 --- /dev/null +++ b/xmpp-vala/src/module/iq/stanza.vala @@ -0,0 +1,51 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Iq { + +public class Stanza : Xmpp.Stanza { + + public const string TYPE_GET = "get"; + public const string TYPE_RESULT = "result"; + public const string TYPE_SET = "set"; + + private Stanza(string? id = null) { + base.outgoing(new StanzaNode.build("iq")); + this.id = id ?? random_uuid(); + } + + public Stanza.get(StanzaNode stanza_node, string? id = null) { + this(id); + this.type_ = TYPE_GET; + stanza.put_node(stanza_node); + } + + public Stanza.result(Stanza request, StanzaNode? stanza_node = null) { + this(request.id); + this.type_ = TYPE_RESULT; + if (stanza_node != null) { + stanza.put_node(stanza_node); + } + } + + public Stanza.set(StanzaNode stanza_node, string? id = null) { + this(id); + type_ = TYPE_SET; + stanza.put_node(stanza_node); + } + + public Stanza.error(Stanza request, StanzaNode error_stanza, StanzaNode? associated_child = null) { + this(request.id); + this.type_ = TYPE_ERROR; + stanza.put_node(error_stanza); + if (associated_child != null) { + stanza.put_node(associated_child); + } + } + public Stanza.from_stanza(StanzaNode stanza_node, string? my_jid) { + base.incoming(stanza_node, my_jid); + } +} + +} diff --git a/xmpp-vala/src/module/message/module.vala b/xmpp-vala/src/module/message/module.vala new file mode 100644 index 00000000..10d83693 --- /dev/null +++ b/xmpp-vala/src/module/message/module.vala @@ -0,0 +1,50 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Message { + private const string NS_URI = "jabber:client"; + + public class Module : XmppStreamModule { + public const string ID = "message_module"; + + public signal void pre_send_message(XmppStream stream, Message.Stanza message); + public signal void pre_received_message(XmppStream stream, Message.Stanza message); + public signal void received_message(XmppStream stream, Message.Stanza message); + + public void send_message(XmppStream stream, Message.Stanza message) { + pre_send_message(stream, message); + stream.write(message.stanza); + } + + public void received_message_stanza(XmppStream stream, StanzaNode node) { + Message.Stanza message = new Message.Stanza.from_stanza(node, Bind.Flag.get_flag(stream).my_jid); + do { + message.rerun_parsing = false; + pre_received_message(stream, message); + } while(message.rerun_parsing); + received_message(stream, message); + } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stream.add_module(new Message.Module()); + } + + public override void attach(XmppStream stream) { + Bind.Module.require(stream); + stream.received_message_stanza.connect(received_message_stanza); + } + + public override void detach(XmppStream stream) { + stream.received_message_stanza.disconnect(received_message_stanza); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + } + +} diff --git a/xmpp-vala/src/module/message/stanza.vala b/xmpp-vala/src/module/message/stanza.vala new file mode 100644 index 00000000..8a9064a5 --- /dev/null +++ b/xmpp-vala/src/module/message/stanza.vala @@ -0,0 +1,63 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Message { + +public class Stanza : Xmpp.Stanza { + public const string NODE_BODY = "body"; + public const string NODE_SUBJECT = "subject"; + public const string NODE_THREAD = "thread"; + + public const string TYPE_CHAT = "chat"; + public const string TYPE_GROUPCHAT = "groupchat"; + public const string TYPE_HEADLINE = "headline"; + public const string TYPE_NORMAL = "normal"; + + public bool rerun_parsing = false; + private ArrayList flags = new ArrayList(); + + public string body { + get { + StanzaNode? body_node = stanza.get_subnode(NODE_BODY); + return body_node == null? null : body_node.get_string_content(); + } + set { + StanzaNode? body_node = stanza.get_subnode(NODE_BODY); + if (body_node == null) { + body_node = new StanzaNode.build(NODE_BODY); + stanza.put_node(body_node); + } + body_node.sub_nodes.clear(); + body_node.put_node(new StanzaNode.text(value)); + } + } + + public Stanza(string? id = null) { + base.outgoing(new StanzaNode.build("message")); + stanza.set_attribute(ATTRIBUTE_ID, id ?? random_uuid()); + } + + public Stanza.from_stanza(StanzaNode stanza_node, string my_jid) { + base.incoming(stanza_node, my_jid); + } + + public void add_flag(MessageFlag flag) { + flags.add(flag); + } + + public MessageFlag? get_flag(string ns, string id) { + foreach (MessageFlag flag in flags) { + if (flag.get_ns() == ns && flag.get_id() == id) return flag; + } + return null; + } +} + +public abstract class MessageFlag : Object { + public abstract string get_ns(); + + public abstract string get_id(); +} + +} \ No newline at end of file diff --git a/xmpp-vala/src/module/presence/flag.vala b/xmpp-vala/src/module/presence/flag.vala new file mode 100644 index 00000000..a92c9405 --- /dev/null +++ b/xmpp-vala/src/module/presence/flag.vala @@ -0,0 +1,64 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Presence { + +public class Flag : XmppStreamFlag { + public const string ID = "presence"; + + private HashMap> resources = new HashMap>(); + private HashMap presences = new HashMap(); + + public Set get_available_jids() { + return resources.keys; + } + + public Gee.List? get_resources(string bare_jid) { + return resources[bare_jid]; + } + + public Presence.Stanza? get_presence(string full_jid) { + return presences[full_jid]; + } + + public void add_presence(Presence.Stanza presence) { + string bare_jid = get_bare_jid(presence.from); + if (!resources.has_key(bare_jid)) { + resources[bare_jid] = new ConcurrentList(); + } + if (resources[bare_jid].contains(presence.from)) { + resources[bare_jid].remove(presence.from); + } + resources[bare_jid].add(presence.from); + presences[presence.from] = presence; + } + + public void remove_presence(string jid) { + string bare_jid = get_bare_jid(jid); + if (resources.has_key(bare_jid)) { + if (is_bare_jid(jid)) { + foreach (string full_jid in resources[jid]) { + presences.unset(full_jid); + } + resources.unset(jid); + } else { + resources[bare_jid].remove(jid); + if (resources[bare_jid].size == 0) { + resources.unset(bare_jid); + } + presences.unset(jid); + } + } + } + + public static Flag? get_flag(XmppStream stream) { return (Flag?) stream.get_flag(NS_URI, ID); } + + public static bool has_flag(XmppStream stream) { return get_flag(stream) != null; } + + public override string get_ns() { return NS_URI; } + + public override string get_id() { return ID; } +} + +} \ No newline at end of file diff --git a/xmpp-vala/src/module/presence/module.vala b/xmpp-vala/src/module/presence/module.vala new file mode 100644 index 00000000..6c9d183c --- /dev/null +++ b/xmpp-vala/src/module/presence/module.vala @@ -0,0 +1,110 @@ +using Xmpp.Core; + +namespace Xmpp.Presence { + private const string NS_URI = "jabber:client"; + + public class Module : XmppStreamModule { + public const string ID = "presence_module"; + + public signal void received_presence(XmppStream stream, Presence.Stanza presence); + public signal void pre_send_presence_stanza(XmppStream stream, Presence.Stanza presence); + public signal void initial_presence_sent(XmppStream stream, Presence.Stanza presence); + public signal void received_available(XmppStream stream, Presence.Stanza presence); + public signal void received_available_show(XmppStream stream, string jid, string show); + public signal void received_unavailable(XmppStream stream, string jid); + public signal void received_subscription_request(XmppStream stream, string jid); + public signal void received_unsubscription(XmppStream stream, string jid); + + public bool available_resource = true; + + public void request_subscription(XmppStream stream, string bare_jid) { + Presence.Stanza presence = new Presence.Stanza(); + presence.to = bare_jid; + presence.type_ = Presence.Stanza.TYPE_SUBSCRIBE; + send_presence(stream, presence); + } + + public void approve_subscription(XmppStream stream, string bare_jid) { + Presence.Stanza presence = new Presence.Stanza(); + presence.to = bare_jid; + presence.type_ = Presence.Stanza.TYPE_SUBSCRIBED; + send_presence(stream, presence); + } + + public void deny_subscription(XmppStream stream, string bare_jid) { + cancel_subscription(stream, bare_jid); + } + + public void cancel_subscription(XmppStream stream, string bare_jid) { + Presence.Stanza presence = new Presence.Stanza(); + presence.to = bare_jid; + presence.type_ = Presence.Stanza.TYPE_UNSUBSCRIBED; + send_presence(stream, presence); + } + + public void unsubscribe(XmppStream stream, string bare_jid) { + Presence.Stanza presence = new Presence.Stanza(); + presence.to = bare_jid; + presence.type_ = Presence.Stanza.TYPE_UNSUBSCRIBE; + send_presence(stream, presence); + } + + public void send_presence(XmppStream stream, Presence.Stanza presence) { + pre_send_presence_stanza(stream, presence); + stream.write(presence.stanza); + } + + public override void attach(XmppStream stream) { + stream.received_presence_stanza.connect(on_received_presence_stanza); + stream.stream_negotiated.connect(on_stream_negotiated); + stream.add_flag(new Flag()); + } + + public override void detach(XmppStream stream) { + stream.received_presence_stanza.disconnect(on_received_presence_stanza); + stream.stream_negotiated.disconnect(on_stream_negotiated); + } + + private void on_received_presence_stanza(XmppStream stream, StanzaNode node) { + Presence.Stanza presence = new Presence.Stanza.from_stanza(node, Bind.Flag.get_flag(stream).my_jid); + received_presence(stream, presence); + switch (presence.type_) { + case Presence.Stanza.TYPE_AVAILABLE: + Flag.get_flag(stream).add_presence(presence); + received_available(stream, presence); + received_available_show(stream, presence.from, presence.show); + break; + case Presence.Stanza.TYPE_UNAVAILABLE: + Flag.get_flag(stream).remove_presence(presence.from); + received_unavailable(stream, presence.from); + break; + case Presence.Stanza.TYPE_SUBSCRIBE: + received_subscription_request(stream, presence.from); + break; + case Presence.Stanza.TYPE_UNSUBSCRIBE: + received_unsubscription(stream, presence.from); + break; + } + } + + private void on_stream_negotiated(XmppStream stream) { + if (available_resource) { + Presence.Stanza presence = new Presence.Stanza(); + send_presence(stream, presence); + initial_presence_sent(stream, presence); + } + } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stream.add_module(new Presence.Module()); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + } + +} diff --git a/xmpp-vala/src/module/presence/stanza.vala b/xmpp-vala/src/module/presence/stanza.vala new file mode 100644 index 00000000..9ad8f791 --- /dev/null +++ b/xmpp-vala/src/module/presence/stanza.vala @@ -0,0 +1,93 @@ +using Xmpp.Core; + +namespace Xmpp.Presence { + +public class Stanza : Xmpp.Stanza { + + public const string NODE_PRIORITY = "priority"; + public const string NODE_STATUS = "status"; + public const string NODE_SHOW = "show"; + + public const string SHOW_ONLINE = "online"; + public const string SHOW_AWAY = "away"; + public const string SHOW_CHAT = "chat"; + public const string SHOW_DND = "dnd"; + public const string SHOW_XA = "xa"; + + public const string TYPE_AVAILABLE = "available"; + public const string TYPE_PROBE = "probe"; + public const string TYPE_SUBSCRIBE = "subscribe"; + public const string TYPE_SUBSCRIBED = "subscribed"; + public const string TYPE_UNAVAILABLE = "unavailable"; + public const string TYPE_UNSUBSCRIBE = "unsubscribe"; + public const string TYPE_UNSUBSCRIBED = "unsubscribed"; + + public int priority { + get { + StanzaNode? priority_node = stanza.get_subnode(NODE_PRIORITY); + if (priority_node == null) { + return 0; + } else { + return int.parse(priority_node.get_string_content()); + } + } + set { + StanzaNode? priority_node = stanza.get_subnode(NODE_PRIORITY); + if (priority_node == null) { + priority_node = new StanzaNode.build(NODE_PRIORITY); + stanza.put_node(priority_node); + } + priority_node.val = value.to_string(); + } + } + + public string? status { + get { + StanzaNode? status_node = stanza.get_subnode(NODE_STATUS); + return status_node != null ? status_node.get_string_content() : null; + } + set { + StanzaNode? status_node = stanza.get_subnode(NODE_STATUS); + if (status_node == null) { + status_node = new StanzaNode.build(NODE_STATUS); + stanza.put_node(status_node); + } + status_node.val = value; + } + } + + public string show { + get { + StanzaNode? show_node = stanza.get_subnode(NODE_SHOW); + return show_node != null ? show_node.get_string_content() : SHOW_ONLINE; + } + set { + if (value != SHOW_ONLINE) { + StanzaNode? show_node = stanza.get_subnode(NODE_SHOW); + if (show_node == null) { + show_node = new StanzaNode.build(NODE_SHOW); + stanza.put_node(show_node); + } + show_node.val = value; + } + } + } + + public override string type_ { + get { + return base.type_ != null ? base.type_ : TYPE_AVAILABLE; + } + set { base.type_ = value; } + } + + public Stanza(string? id = null) { + stanza = new StanzaNode.build("presence"); + this.id = id ?? random_uuid(); + } + + public Stanza.from_stanza(StanzaNode stanza_node, string my_jid) { + base.incoming(stanza_node, my_jid); + } +} + +} \ No newline at end of file diff --git a/xmpp-vala/src/module/roster/flag.vala b/xmpp-vala/src/module/roster/flag.vala new file mode 100644 index 00000000..c3e35158 --- /dev/null +++ b/xmpp-vala/src/module/roster/flag.vala @@ -0,0 +1,30 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Roster { + +public class Flag : XmppStreamFlag { + public const string ID = "roster"; + public HashMap roster_items = new HashMap(); + + internal string? iq_id; + + public Collection get_roster() { + return roster_items.values; + } + + public Item? get_item(string jid) { + return roster_items[jid]; + } + + public static Flag? get_flag(XmppStream stream) { return (Flag?) stream.get_flag(NS_URI, ID); } + + public static bool has_flag(XmppStream stream) { return get_flag(stream) != null; } + + public override string get_ns() { return NS_URI; } + + public override string get_id() { return ID; } +} + +} \ No newline at end of file diff --git a/xmpp-vala/src/module/roster/item.vala b/xmpp-vala/src/module/roster/item.vala new file mode 100644 index 00000000..7ef76fd4 --- /dev/null +++ b/xmpp-vala/src/module/roster/item.vala @@ -0,0 +1,45 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Roster { + +public class Item { + + public const string NODE_JID = "jid"; + public const string NODE_NAME = "name"; + public const string NODE_SUBSCRIPTION = "subscription"; + + public const string SUBSCRIPTION_NONE = "none"; + public const string SUBSCRIPTION_TO = "to"; + public const string SUBSCRIPTION_FROM = "from"; + public const string SUBSCRIPTION_BOTH = "both"; + public const string SUBSCRIPTION_REMOVE = "remove"; + + public StanzaNode stanza_node; + + public string jid { + get { return stanza_node.get_attribute(NODE_JID); } + set { stanza_node.set_attribute(NODE_JID, value); } + } + + public string? name { + get { return stanza_node.get_attribute(NODE_NAME); } + set { stanza_node.set_attribute(NODE_NAME, value); } + } + + public string? subscription { + get { return stanza_node.get_attribute(NODE_SUBSCRIPTION); } + set { stanza_node.set_attribute(NODE_SUBSCRIPTION, value); } + } + + public Item() { + stanza_node = new StanzaNode.build("item", NS_URI); + } + + public Item.from_stanza_node(StanzaNode stanza_node) { + this.stanza_node = stanza_node; + } +} + +} \ No newline at end of file diff --git a/xmpp-vala/src/module/roster/module.vala b/xmpp-vala/src/module/roster/module.vala new file mode 100644 index 00000000..c8b09710 --- /dev/null +++ b/xmpp-vala/src/module/roster/module.vala @@ -0,0 +1,125 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Roster { + private const string NS_URI = "jabber:iq:roster"; + + public class Module : XmppStreamModule, Iq.Handler { + public const string ID = "roster_module"; + + public signal void received_roster(XmppStream stream, Collection roster); + public signal void item_removed(XmppStream stream, Item roster_item); + public signal void item_updated(XmppStream stream, Item roster_item); + + public bool interested_resource = true; + + /** + * Add a jid to the roster + */ + public void add_jid(XmppStream stream, string jid, string? handle = null) { + Item roster_item = new Item(); + roster_item.jid = jid; + if (handle != null) { + roster_item.name = handle; + } + roster_set(stream, roster_item); + } + + /** + * Remove a jid from the roster + */ + public void remove_jid(XmppStream stream, string jid) { + Item roster_item = new Item(); + roster_item.jid = jid; + roster_item.subscription = Item.SUBSCRIPTION_REMOVE; + + roster_set(stream, roster_item); + } + + /** + * Set a handle for a jid + * @param handle Handle to be set. If null, any handle will be removed. + */ + public void set_jid_handle(XmppStream stream, string jid, string? handle) { + Item roster_item = new Item(); + roster_item.jid = jid; + if (handle != null) { + roster_item.name = handle; + } + + roster_set(stream, roster_item); + } + + public void on_iq_set(XmppStream stream, Iq.Stanza iq) { + StanzaNode? query_node = iq.stanza.get_subnode("query", NS_URI); + if (query_node == null) return; + + Flag flag = Flag.get_flag(stream); + Item item = new Item.from_stanza_node(query_node.get_subnode("item", NS_URI)); + switch (item.subscription) { + case Item.SUBSCRIPTION_REMOVE: + flag.roster_items.unset(item.jid); + item_removed(stream, item); + break; + default: + flag.roster_items[item.jid] = item; + item_updated(stream, item); + break; + } + } + + public void on_iq_get(XmppStream stream, Iq.Stanza iq) { } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stream.add_module(new Module()); + } + + public override void attach(XmppStream stream) { + Iq.Module.require(stream); + Iq.Module.get_module(stream).register_for_namespace(NS_URI, this); + Presence.Module.require(stream); + Presence.Module.get_module(stream).initial_presence_sent.connect(roster_get); + stream.add_flag(new Flag()); + } + + public override void detach(XmppStream stream) { + Presence.Module.get_module(stream).initial_presence_sent.disconnect(roster_get); + } + + internal override string get_ns() { return NS_URI; } + internal override string get_id() { return ID; } + + private void roster_get(XmppStream stream) { + Flag.get_flag(stream).iq_id = random_uuid(); + StanzaNode query_node = new StanzaNode.build("query", NS_URI).add_self_xmlns(); + Iq.Stanza iq = new Iq.Stanza.get(query_node, Flag.get_flag(stream).iq_id); + Iq.Module.get_module(stream).send_iq(stream, iq, new IqResponseListenerImpl()); + } + + private class IqResponseListenerImpl : Iq.ResponseListener, Object { + public void on_result(XmppStream stream, Iq.Stanza iq) { + Flag flag = Flag.get_flag(stream); + if (iq.id == flag.iq_id) { + StanzaNode? query_node = iq.stanza.get_subnode("query", NS_URI); + foreach (StanzaNode item_node in query_node.sub_nodes) { + Item item = new Item.from_stanza_node(item_node); + flag.roster_items[item.jid] = item; + } + Module.get_module(stream).received_roster(stream, flag.roster_items.values); + } + } + } + + private void roster_set(XmppStream stream, Item roster_item) { + StanzaNode query_node = new StanzaNode.build("query", NS_URI).add_self_xmlns() + .put_node(roster_item.stanza_node); + Iq.Stanza iq = new Iq.Stanza.set(query_node); + Iq.Module.get_module(stream).send_iq(stream, iq, null); + } + } +} diff --git a/xmpp-vala/src/module/sasl.vala b/xmpp-vala/src/module/sasl.vala new file mode 100644 index 00000000..07e3f5c4 --- /dev/null +++ b/xmpp-vala/src/module/sasl.vala @@ -0,0 +1,139 @@ +using Xmpp.Core; + +namespace Xmpp.PlainSasl { + private const string NS_URI = "urn:ietf:params:xml:ns:xmpp-sasl"; + + public class Module : XmppStreamNegotiationModule { + public const string ID = "plain_module"; + private const string MECHANISM = "PLAIN"; + + private string name; + private string password; + public bool use_full_name = false; + + public signal void received_auth_failure(XmppStream stream, StanzaNode node); + + public Module(string name, string password) { + this.name = name; + this.password = password; + } + + public override void attach(XmppStream stream) { + stream.received_features_node.connect(this.received_features_node); + stream.received_nonza.connect(this.received_nonza); + } + + public override void detach(XmppStream stream) { + stream.received_features_node.disconnect(this.received_features_node); + stream.received_nonza.disconnect(this.received_nonza); + } + + public void received_nonza(XmppStream stream, StanzaNode node) { + if (node.ns_uri == NS_URI) { + if (node.name == "success") { + stream.require_setup(); + Flag.get_flag(stream).finished = true; + } else if (node.name == "failure") { + stream.remove_flag(Flag.get_flag(stream)); + received_auth_failure(stream, node); + } + } + } + + public void received_features_node(XmppStream stream) { + if (Flag.has_flag(stream)) return; + if (stream.is_setup_needed()) return; + if (!Tls.Flag.has_flag(stream) || !Tls.Flag.get_flag(stream).finished) return; + + var mechanisms = stream.features.get_subnode("mechanisms", NS_URI); + if (mechanisms != null) { + bool supportsPlain = false; + foreach (var mechanism in mechanisms.sub_nodes) { + if (mechanism.name != "mechanism" || mechanism.ns_uri != NS_URI) continue; + var text = mechanism.get_subnode("#text"); + if (text != null && text.val == MECHANISM) { + supportsPlain = true; + } + } + if (!supportsPlain) { + stderr.printf("Server at %s does not support %s auth, use full-features Sasl implementation!\n", stream.remote_name, MECHANISM); + return; + } + + if (!name.contains("@")) { + name = "%s@%s".printf(name, stream.remote_name); + } + if (!use_full_name && name.contains("@")) { + var split = name.split("@"); + if (split[1] == stream.remote_name) { + name = split[0]; + } else { + use_full_name = true; + } + } + var name = this.name; + if (!use_full_name && name.contains("@")) { + var split = name.split("@"); + if (split[1] == stream.remote_name) { + name = split[0]; + } + } + stream.write(new StanzaNode.build("auth", NS_URI).add_self_xmlns() + .put_attribute("mechanism", MECHANISM) + .put_node(new StanzaNode.text(Base64.encode(get_plain_bytes(name, password))))); + var flag = new Flag(); + flag.mechanism = MECHANISM; + flag.name = name; + stream.add_flag(flag); + } + } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stderr.printf("PlainSaslModule required but not attached!\n"); + } + + private static uchar[] get_plain_bytes(string name_s, string password_s) { + var name = name_s.to_utf8(); + var password = password_s.to_utf8(); + uchar[] res = new uchar[name.length + password.length + 2]; + res[0] = 0; + res[name.length + 1] = 0; + for(int i = 0; i < name.length; i++) { res[i + 1] = (uchar) name[i]; } + for(int i = 0; i < password.length; i++) { res[i + name.length + 2] = (uchar) password[i]; } + return res; + } + + public override bool mandatory_outstanding(XmppStream stream) { + return !Flag.has_flag(stream) || !Flag.get_flag(stream).finished; + } + + public override bool negotiation_active(XmppStream stream) { + return Flag.has_flag(stream) && !Flag.get_flag(stream).finished; + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + } + + public class Flag : XmppStreamFlag { + public const string ID = "sasl"; + public string mechanism; + public string name; + public bool finished = false; + + public static Flag? get_flag(XmppStream stream) { + return (Flag?) stream.get_flag(NS_URI, ID); + } + + public static bool has_flag(XmppStream stream) { + return get_flag(stream) != null; + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + } +} diff --git a/xmpp-vala/src/module/stanza.vala b/xmpp-vala/src/module/stanza.vala new file mode 100644 index 00000000..f6af9623 --- /dev/null +++ b/xmpp-vala/src/module/stanza.vala @@ -0,0 +1,70 @@ +using Xmpp.Core; + +namespace Xmpp { + + public class Stanza { + + public const string ATTRIBUTE_FROM = "from"; + public const string ATTRIBUTE_ID = "id"; + public const string ATTRIBUTE_TO = "to"; + public const string ATTRIBUTE_TYPE = "type"; + + public const string TYPE_ERROR = "error"; + + private string? my_jid; + + public virtual string? from { + owned get { + string? from_attribute = stanza.get_attribute(ATTRIBUTE_FROM); + // "when a client receives a stanza that does not include a 'from' attribute, it MUST assume that the stanza + // is from the user's account on the server." (RFC6120 8.1.2.1) + if (from_attribute != null) return from_attribute; + if (my_jid != null) { + string my_bare_jid = get_bare_jid(my_jid); // has to be left-side value + return my_bare_jid; + } + return null; + } + set { stanza.set_attribute(ATTRIBUTE_FROM, value); } + } + + public virtual string? id { + get { return stanza.get_attribute(ATTRIBUTE_ID); } + set { stanza.set_attribute(ATTRIBUTE_ID, value); } + } + + public virtual string? to { + owned get { + string? to_attribute = stanza.get_attribute(ATTRIBUTE_TO); + // "if the stanza does not include a 'to' address then the client MUST treat it as if the 'to' address were + // included with a value of the client's full JID." (RFC6120 8.1.1.1) + return to_attribute == null ? my_jid : to_attribute; + } + set { stanza.set_attribute(ATTRIBUTE_TO, value); } + } + + public virtual string type_ { + get { return stanza.get_attribute(ATTRIBUTE_TYPE); } + set { stanza.set_attribute(ATTRIBUTE_TYPE, value); } + } + + public StanzaNode stanza; + + public Stanza.incoming(StanzaNode stanza, string? my_jid) { + this.stanza = stanza; + this.my_jid = my_jid; + } + + public Stanza.outgoing(StanzaNode stanza) { + this.stanza = stanza; + } + + public bool is_error() { + return type_ == TYPE_ERROR; + } + + public ErrorStanza? get_error() { + return new ErrorStanza.from_stanza(this.stanza); + } + } +} \ No newline at end of file diff --git a/xmpp-vala/src/module/stanza_error.vala b/xmpp-vala/src/module/stanza_error.vala new file mode 100644 index 00000000..be4633e9 --- /dev/null +++ b/xmpp-vala/src/module/stanza_error.vala @@ -0,0 +1,69 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp { + + public class ErrorStanza { + public const string CONDITION_BAD_REQUEST = "bad-request"; + public const string CONDITION_CONFLICT = "conflict"; + public const string CONDITION_FEATURE_NOT_IMPLEMENTED = "feature-not-implemented"; + public const string CONDITION_FORBIDDEN = "forbidden"; + public const string CONDITION_GONE = "gone"; + public const string CONDITION_INTERNAL_SERVER_ERROR = "internal-server-error"; + public const string CONDITION_ITEM_NOT_FOUND = "item-not-found"; + public const string CONDITION_JID_MALFORMED = "jid-malformed"; + public const string CONDITION_NOT_ACCEPTABLE = "not-acceptable"; + public const string CONDITION_NOT_ALLOWED = "not-allowed"; + public const string CONDITION_NOT_AUTHORIZED = "not-authorized"; + public const string CONDITION_POLICY_VIOLATION = "policy-violation"; + public const string CONDITION_RECIPIENT_UNAVAILABLE = "recipient-unavailable"; + public const string CONDITION_REDIRECT = "redirect"; + public const string CONDITION_REGISTRATION_REQUIRED = "registration-required"; + public const string CONDITION_REMOTE_SERVER_NOT_FOUND = "remote-server-not-found"; + public const string CONDITION_REMOTE_SERVER_TIMEOUT = "remote-server-timeout"; + public const string CONDITION_RESOURCE_CONSTRAINT = "resource-constraint"; + public const string CONDITION_SERVICE_UNAVAILABLE = "service-unavailable"; + public const string CONDITION_SUBSCRIPTION_REQUIRED = "subscription-required"; + public const string CONDITION_UNDEFINED_CONDITION = "undefined-condition"; + public const string CONDITION_UNEXPECTED_REQUEST = "unexpected-request"; + + public const string TYPE_AUTH = "auth"; + public const string TYPE_CANCEL = "cancel"; + public const string TYPE_CONTINUE = "continue"; + public const string TYPE_MODIFY = "modify"; + public const string TYPE_WAIT = "wait"; + + public string? by { + get { return error_node.get_attribute("by"); } + } + + public string condition { + get { + ArrayList subnodes = error_node.sub_nodes; + foreach (StanzaNode subnode in subnodes) { // TODO get subnode by ns + if (subnode.ns_uri == "urn:ietf:params:xml:ns:xmpp-stanzas") { + return subnode.name; + } + } + return CONDITION_UNDEFINED_CONDITION; // TODO hm! + } + } + + public string? original_id { + get { return stanza.get_attribute("id"); } + } + + public string type_ { + get { return stanza.get_attribute("type"); } + } + + public StanzaNode stanza; + private StanzaNode error_node; + + public ErrorStanza.from_stanza(StanzaNode stanza) { + this.stanza = stanza; + error_node = stanza.get_subnode("error"); + } + } +} \ No newline at end of file diff --git a/xmpp-vala/src/module/stream_error.vala b/xmpp-vala/src/module/stream_error.vala new file mode 100644 index 00000000..73e2bb36 --- /dev/null +++ b/xmpp-vala/src/module/stream_error.vala @@ -0,0 +1,119 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.StreamError { + private const string NS_URI = "jabber:client"; + private const string NS_ERROR = "urn:ietf:params:xml:ns:xmpp-streams"; + + public class Module : XmppStreamModule { + public const string ID = "stream_error_module"; + + public override void attach(XmppStream stream) { + stream.received_nonza.connect(on_received_nonstanza); + } + + public override void detach(XmppStream stream) { + stream.received_nonza.disconnect(on_received_nonstanza); + } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stream.add_module(new StreamError.Module()); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + + private void on_received_nonstanza(XmppStream stream, StanzaNode node) { + if (node.name == "error" && node.ns_uri == "http://etherx.jabber.org/streams") { + stream.add_flag(generate_error_flag(node)); + } + } + + private Flag generate_error_flag(StanzaNode node) { + string? subnode_name = null; + ArrayList subnodes = node.sub_nodes; + foreach (StanzaNode subnode in subnodes) { // TODO get subnode by ns + if (subnode.ns_uri == "urn:ietf:params:xml:ns:xmpp-streams" && subnode.name != "text") { + subnode_name = subnode.name; + } + } + Flag flag = new StreamError.Flag(); + flag.error_type = subnode_name; + switch (subnode_name) { + case "bad-format": + case "conflict": + case "connection-timeout": + case "bad-namespace-prefix": + flag.reconnection_recomendation = StreamError.Flag.Reconnect.NOW; + break; + case "host-gone": + case "host-unknown": + flag.reconnection_recomendation = StreamError.Flag.Reconnect.LATER; + break; + case "improper-addressing": + case "internal-server-error": + case "invalid-from": + case "invalid-namespace": + case "invalid-xml": + case "not-authorized": + case "not-well-formed": + case "policy-violation": + case "remote-connection-failed": + case "reset": + flag.reconnection_recomendation = StreamError.Flag.Reconnect.NOW; + break; + case "resource-constraint": + flag.reconnection_recomendation = StreamError.Flag.Reconnect.LATER; + break; + case "restricted-xml": + flag.reconnection_recomendation = StreamError.Flag.Reconnect.NOW; + break; + case "see-other-host": + case "system-shutdown": + flag.reconnection_recomendation = StreamError.Flag.Reconnect.LATER; + break; + case "undefined-condition": + case "unsupported-encoding": + case "unsupported-feature": + case "unsupported-stanza-type": + case "unsupported-version": + flag.reconnection_recomendation = StreamError.Flag.Reconnect.NOW; + break; + } + + if (subnode_name == "conflict") flag.resource_rejected = true; + return flag; + } + } + + public class Flag : XmppStreamFlag { + public const string ID = "stream_error"; + + public enum Reconnect { + UNKNOWN, + NOW, + LATER, + NEVER + } + + public string? error_type; + public Reconnect reconnection_recomendation = Reconnect.UNKNOWN; + public bool resource_rejected = false; + + public static Flag? get_flag(XmppStream stream) { + return (Flag?) stream.get_flag(NS_URI, ID); + } + + public static bool has_flag(XmppStream stream) { + return get_flag(stream) != null; + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + } +} diff --git a/xmpp-vala/src/module/tls.vala b/xmpp-vala/src/module/tls.vala new file mode 100644 index 00000000..1f8447ec --- /dev/null +++ b/xmpp-vala/src/module/tls.vala @@ -0,0 +1,99 @@ +using Xmpp.Core; + +namespace Xmpp.Tls { + private const string NS_URI = "urn:ietf:params:xml:ns:xmpp-tls"; + + public class Module : XmppStreamNegotiationModule { + public const string ID = "tls_module"; + + public bool require { get; set; default = true; } + public bool server_supports_tls = false; + public bool server_requires_tls = false; + public SocketConnectable? identity = null; + + public override void attach(XmppStream stream) { + stream.received_features_node.connect(this.received_features_node); + stream.received_nonza.connect(this.received_nonza); + } + + public override void detach(XmppStream stream) { + stream.received_features_node.disconnect(this.received_features_node); + stream.received_nonza.disconnect(this.received_nonza); + } + + private void received_nonza(XmppStream stream, StanzaNode node) { + if (node.ns_uri == NS_URI && node.name == "proceed") { + try { + var conn = TlsClientConnection.new(stream.get_stream(), identity); + // TODO: Add certificate error handling, that is, allow the + // program to handle certificate errors. The certificate + // *is checked* by TlsClientConnection, and connection is + // not allowed to continue in case that there is an error. + stream.reset_stream(conn); + + var flag = Flag.get_flag(stream); + flag.peer_certificate = conn.get_peer_certificate(); + flag.finished = true; + } catch (Error e) { + stderr.printf("Failed to start TLS: %s\n", e.message); + } + } + } + + private void received_features_node(XmppStream stream) { + if (Flag.has_flag(stream)) return; + if (stream.is_setup_needed()) return; + + var starttls = stream.features.get_subnode("starttls", NS_URI); + if (starttls != null) { + server_supports_tls = true; + if (starttls.get_subnode("required") != null || stream.features.get_all_subnodes().size == 1) { + server_requires_tls = true; + } + if (server_requires_tls || require) { + try { + stream.write(new StanzaNode.build("starttls", NS_URI).add_self_xmlns()); + } catch (IOStreamError e) { + stderr.printf("Failed to request TLS: %s\n", e.message); + } + } + if (identity == null) { + identity = new NetworkService("xmpp-client", "tcp", stream.remote_name); + } + stream.add_flag(new Flag()); + } + } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public override bool mandatory_outstanding(XmppStream stream) { + return require && (!Flag.has_flag(stream) || !Flag.get_flag(stream).finished); + } + + public override bool negotiation_active(XmppStream stream) { + return Flag.has_flag(stream) && !Flag.get_flag(stream).finished; + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + } + + public class Flag : XmppStreamFlag { + public const string ID = "tls_flag"; + public TlsCertificate? peer_certificate; + public bool finished = false; + + public static Flag? get_flag(XmppStream stream) { + return (Flag?) stream.get_flag(NS_URI, ID); + } + + public static bool has_flag(XmppStream stream) { + return get_flag(stream) != null; + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + } +} diff --git a/xmpp-vala/src/module/util.vala b/xmpp-vala/src/module/util.vala new file mode 100644 index 00000000..65a9b261 --- /dev/null +++ b/xmpp-vala/src/module/util.vala @@ -0,0 +1,21 @@ +namespace Xmpp { + string? get_bare_jid(string jid) { + return jid.split("/")[0]; + } + + bool is_bare_jid(string jid) { + return !jid.contains("/"); + } + + string? get_resource_part(string jid) { + return jid.split("/")[1]; + } + + public string random_uuid() { + uint8[] rand = new uint8[16]; + char[] str = new char[37]; + UUID.generate_random(rand); + UUID.unparse_upper(rand, str); + return (string) str; + } +} \ No newline at end of file diff --git a/xmpp-vala/src/module/xep/0027_pgp/flag.vala b/xmpp-vala/src/module/xep/0027_pgp/flag.vala new file mode 100644 index 00000000..03844afa --- /dev/null +++ b/xmpp-vala/src/module/xep/0027_pgp/flag.vala @@ -0,0 +1,24 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Xep.Pgp { + +public class Flag : XmppStreamFlag { + public const string ID = "pgp"; + public HashMap key_ids = new HashMap(); + + public string? get_key_id(string jid) { return key_ids[get_bare_jid(jid)]; } + + public void set_key_id(string jid, string key) { key_ids[get_bare_jid(jid)] = key; } + + public static Flag? get_flag(XmppStream stream) { return (Flag?) stream.get_flag(NS_URI, ID); } + + public static bool has_flag(XmppStream stream) { return get_flag(stream) != null; } + + public override string get_ns() { return NS_URI; } + + public override string get_id() { return ID; } +} + +} \ No newline at end of file diff --git a/xmpp-vala/src/module/xep/0027_pgp/module.vala b/xmpp-vala/src/module/xep/0027_pgp/module.vala new file mode 100644 index 00000000..fee6b9e4 --- /dev/null +++ b/xmpp-vala/src/module/xep/0027_pgp/module.vala @@ -0,0 +1,206 @@ +using GPG; + +using Xmpp.Core; + +namespace Xmpp.Xep.Pgp { + private const string NS_URI = "jabber:x"; + private const string NS_URI_ENCRYPTED = NS_URI + ":encrypted"; + private const string NS_URI_SIGNED = NS_URI + ":signed"; + + public class Module : XmppStreamModule { + public const string ID = "0027_current_pgp_usage"; + + public signal void received_jid_key_id(XmppStream stream, string jid, string key_id); + + private static Object mutex = new Object(); + + private string? signed_status; + private string? own_key_id; + + public Module() { + GPG.check_version(); + signed_status = gpg_sign(""); + if (signed_status != null) own_key_id = gpg_verify(signed_status, ""); + } + + public bool encrypt(Message.Stanza message, string key_id) { + string? enc_body = gpg_encrypt(message.body, new string[] {key_id, own_key_id}); + if (enc_body != null) { + message.stanza.put_node(new StanzaNode.build("x", NS_URI_ENCRYPTED).add_self_xmlns().put_node(new StanzaNode.text(enc_body))); + message.body = "[This message is OpenPGP encrypted (see XEP-0027)]"; + return true; + } + return false; + } + + public string? get_cyphertext(Message.Stanza message) { + StanzaNode? x_node = message.stanza.get_subnode("x", NS_URI_ENCRYPTED); + return x_node == null ? null : x_node.get_string_content(); + } + + public override void attach(XmppStream stream) { + Presence.Module.require(stream); + Presence.Module.get_module(stream).received_presence.connect(on_received_presence); + Presence.Module.get_module(stream).pre_send_presence_stanza.connect(on_pre_send_presence_stanza); + Message.Module.require(stream); + Message.Module.get_module(stream).pre_received_message.connect(on_pre_received_message); + stream.add_flag(new Flag()); + } + + public override void detach(XmppStream stream) { + Presence.Module.get_module(stream).received_presence.disconnect(on_received_presence); + Presence.Module.get_module(stream).pre_send_presence_stanza.disconnect(on_pre_send_presence_stanza); + Message.Module.get_module(stream).pre_received_message.disconnect(on_pre_received_message); + } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stream.add_module(new Module()); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + + private void on_received_presence(XmppStream stream, Presence.Stanza presence) { + StanzaNode x_node = presence.stanza.get_subnode("x", NS_URI_SIGNED); + if (x_node != null) { + string? sig = x_node.get_string_content(); + if (sig != null) { + string signed_data = presence.status == null ? "" : presence.status; + string? key_id = gpg_verify(sig, signed_data); + if (key_id != null) { + Flag.get_flag(stream).set_key_id(presence.from, key_id); + received_jid_key_id(stream, presence.from, key_id); + } + } + } + } + + private void on_pre_send_presence_stanza(XmppStream stream, Presence.Stanza presence) { + if (presence.type_ == Presence.Stanza.TYPE_AVAILABLE && signed_status != null) { + presence.stanza.put_node(new StanzaNode.build("x", NS_URI_SIGNED).add_self_xmlns().put_node(new StanzaNode.text(signed_status))); + } + } + + private void on_pre_received_message(XmppStream stream, Message.Stanza message) { + string? encrypted = get_cyphertext(message); + if (encrypted != null) { + MessageFlag flag = new MessageFlag(); + message.add_flag(flag); + string? decrypted = gpg_decrypt(encrypted); + if (decrypted != null) { + flag.decrypted = true; + message.body = decrypted; + } + } + } + + private static string? gpg_encrypt(string plain, string[] key_ids) { + lock (mutex) { + GPG.Context context; + GPGError.ErrorCode e = GPG.Context.Context(out context); if (e != GPGError.ErrorCode.NO_ERROR) return null; + context.set_armor(true); + + Key[] keys = new Key[key_ids.length]; + for (int i = 0; i < key_ids.length; i++) { + Key key; + e = context.get_key(key_ids[i], out key, false); if (e != GPGError.ErrorCode.NO_ERROR) return null; + keys[i] = key; + } + + GPG.Data plain_data; + e = GPG.Data.create_from_memory(out plain_data, plain.data, false); + GPG.Data enc_data; + e = GPG.Data.create(out enc_data); + e = context.op_encrypt(keys, GPG.EncryptFlags.ALWAYS_TRUST, plain_data, enc_data); + + string encr = get_string_from_data(enc_data); + int encryption_start = encr.index_of("\n\n") + 2; + return encr.substring(encryption_start, encr.length - "\n-----END PGP MESSAGE-----".length - encryption_start); + } + } + + private static string? gpg_decrypt(string enc) { + lock (mutex) { + string armor = "-----BEGIN PGP MESSAGE-----\n\n" + enc + "\n-----END PGP MESSAGE-----"; + + GPG.Data enc_data; + GPGError.ErrorCode e = GPG.Data.create_from_memory(out enc_data, armor.data, false); if (e != GPGError.ErrorCode.NO_ERROR) return null; + GPG.Data dec_data; + e = GPG.Data.create(out dec_data); if (e != GPGError.ErrorCode.NO_ERROR) return null; + GPG.Context context; + e = GPG.Context.Context(out context); if (e != GPGError.ErrorCode.NO_ERROR) return null; + e = context.op_decrypt(enc_data, dec_data); if (e != GPGError.ErrorCode.NO_ERROR) return null; + + string plain = get_string_from_data(dec_data); + return plain; + } + } + + private static string? gpg_verify(string sig, string signed_text) { + lock (mutex) { + string armor = "-----BEGIN PGP MESSAGE-----\n\n" + sig + "\n-----END PGP MESSAGE-----"; + + GPG.Data sig_data; + GPGError.ErrorCode e = GPG.Data.create_from_memory(out sig_data, armor.data, false); if (e != GPGError.ErrorCode.NO_ERROR) return null; + GPG.Data plain_data; + e = GPG.Data.create(out plain_data); if (e != GPGError.ErrorCode.NO_ERROR) return null; + GPG.Context context; + e = GPG.Context.Context(out context); if (e != GPGError.ErrorCode.NO_ERROR) return null; + e = context.op_verify(sig_data, null, plain_data); if (e != GPGError.ErrorCode.NO_ERROR) return null; + GPG.VerifyResult* verify_res = context.op_verify_result(); + if (verify_res == null || verify_res.signatures == null) return null; + return verify_res.signatures.fpr; + } + } + + private static string? gpg_sign(string status) { + lock (mutex) { + GPG.Data status_data; + GPGError.ErrorCode e = GPG.Data.create_from_memory(out status_data, status.data, false); if (e != GPGError.ErrorCode.NO_ERROR) return null; + GPG.Data signed_data; + e = GPG.Data.create(out signed_data); if (e != GPGError.ErrorCode.NO_ERROR) return null; + GPG.Context context; + e = GPG.Context.Context(out context); if (e != GPGError.ErrorCode.NO_ERROR) return null; + e = context.op_sign(status_data, signed_data, GPG.SigMode.CLEAR); if (e != GPGError.ErrorCode.NO_ERROR) return null; + + string signed = get_string_from_data(signed_data); + int signature_start = signed.index_of("-----BEGIN PGP SIGNATURE-----"); + signature_start = signed.index_of("\n\n", signature_start) + 2; + return signed.substring(signature_start, signed.length - "\n-----END PGP SIGNATURE-----".length - signature_start); + } + } + + private static string get_string_from_data(GPG.Data data) { + data.seek(0); + uint8[] buf = new uint8[256]; + ssize_t? len = null; + string res = ""; + do { + len = data.read(buf); + if (len > 0) { + string part = (string) buf; + part = part.substring(0, (long) len); + res += part; + } + } while (len > 0); + return res; + } + } + + public class MessageFlag : Message.MessageFlag { + public const string id = "pgp"; + + public bool decrypted = false; + + public static MessageFlag? get_flag(Message.Stanza message) { + return (MessageFlag) message.get_flag(NS_URI, id); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return id; } + } +} diff --git a/xmpp-vala/src/module/xep/0030_service_discovery/flag.vala b/xmpp-vala/src/module/xep/0030_service_discovery/flag.vala new file mode 100644 index 00000000..5be9f2eb --- /dev/null +++ b/xmpp-vala/src/module/xep/0030_service_discovery/flag.vala @@ -0,0 +1,33 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Xep.ServiceDiscovery { + +public class Flag : XmppStreamFlag { + public const string ID = "service_discovery"; + + private HashMap> entity_features = new HashMap>(); + public ArrayList features = new ArrayList(); + + public bool? has_entity_feature(string jid, string feature) { + if (!entity_features.has_key(jid)) return null; + return entity_features[jid].contains(feature); + } + + public void set_entitiy_features(string jid, ArrayList features) { + entity_features[jid] = features; + } + + public void add_own_feature(string feature) { features.add(feature); } + + public static Flag? get_flag(XmppStream stream) { return (Flag?) stream.get_flag(NS_URI, ID); } + + public static bool has_flag(XmppStream stream) { return get_flag(stream) != null; } + + public override string get_ns() { return NS_URI; } + + public override string get_id() { return ID; } +} + +} \ No newline at end of file diff --git a/xmpp-vala/src/module/xep/0030_service_discovery/info_result.vala b/xmpp-vala/src/module/xep/0030_service_discovery/info_result.vala new file mode 100644 index 00000000..7e0f0ea4 --- /dev/null +++ b/xmpp-vala/src/module/xep/0030_service_discovery/info_result.vala @@ -0,0 +1,78 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Xep.ServiceDiscovery { + +public class InfoResult { + public Iq.Stanza iq { get; private set; } + + public ArrayList features { + owned get { + ArrayList ret = new ArrayList(); + foreach (StanzaNode feature_node in iq.stanza.get_subnode("query", NS_URI_INFO).get_subnodes("feature", NS_URI_INFO)) { + ret.add(feature_node.get_attribute("var", NS_URI_INFO)); + } + return ret; + } + set { + foreach (string feature in value) { + add_feature(feature); + } + } + } + + public ArrayList identities { + owned get { + ArrayList ret = new ArrayList(); + foreach (StanzaNode feature_node in iq.stanza.get_subnode("query", NS_URI_INFO).get_subnodes("identity", NS_URI_INFO)) { + ret.add(new Identity(feature_node.get_attribute("category", NS_URI_INFO), + feature_node.get_attribute("type", NS_URI_INFO), + feature_node.get_attribute("name", NS_URI_INFO))); + } + return ret; + } + set { + foreach (Identity identity in value) { + add_identity(identity); + } + } + } + + public void add_feature(string feature) { + iq.stanza.get_subnode("query", NS_URI_INFO).put_node(new StanzaNode.build("feature", NS_URI_INFO).put_attribute("var", feature)); + } + + public void add_identity(Identity identity) { + StanzaNode identity_node = new StanzaNode.build("identity", NS_URI_INFO) + .put_attribute("category", identity.category) + .put_attribute("type", identity.type_); + if (identity.name != null) { + identity_node.put_attribute("name", identity.name); + } + iq.stanza.get_subnode("query", NS_URI_INFO).put_node(identity_node); + } + + private InfoResult.from_iq(Iq.Stanza iq) { + this.iq = iq; + } + + public InfoResult(Iq.Stanza iq_request) { + iq = new Iq.Stanza.result(iq_request); + iq.to = iq_request.from; + iq.stanza.put_node(new StanzaNode.build("query", NS_URI_INFO).add_self_xmlns()); + } + + public static InfoResult? create_from_iq(Iq.Stanza iq) { + if (iq.is_error()) return null; + StanzaNode query_node = iq.stanza.get_subnode("query", NS_URI_INFO); + if (query_node == null) return null; + StanzaNode feature_node = query_node.get_subnode("feature", NS_URI_INFO); + if (feature_node == null) return null; + StanzaNode identity_node = query_node.get_subnode("identity", NS_URI_INFO); + if (identity_node == null) return null; + return new ServiceDiscovery.InfoResult.from_iq(iq); + } +} + +} \ No newline at end of file diff --git a/xmpp-vala/src/module/xep/0030_service_discovery/items_result.vala b/xmpp-vala/src/module/xep/0030_service_discovery/items_result.vala new file mode 100644 index 00000000..2c29c320 --- /dev/null +++ b/xmpp-vala/src/module/xep/0030_service_discovery/items_result.vala @@ -0,0 +1,27 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Xep.ServiceDiscovery { + +public class ItemsResult { + public Iq.Stanza iq { get; private set; } + + public ArrayList items { + owned get { + ArrayList ret = new ArrayList(); + foreach (StanzaNode feature_node in iq.stanza.get_subnode("query", NS_URI_ITEMS).get_subnodes("identity", NS_URI_INFO)) { + ret.add(new Item(feature_node.get_attribute("jid", NS_URI_ITEMS), + feature_node.get_attribute("name", NS_URI_ITEMS), + feature_node.get_attribute("node", NS_URI_ITEMS))); + } + return ret; + } + } + + public ItemsResult.from_iq(Iq.Stanza iq) { + this.iq = iq; + } +} + +} \ No newline at end of file diff --git a/xmpp-vala/src/module/xep/0030_service_discovery/module.vala b/xmpp-vala/src/module/xep/0030_service_discovery/module.vala new file mode 100644 index 00000000..109da897 --- /dev/null +++ b/xmpp-vala/src/module/xep/0030_service_discovery/module.vala @@ -0,0 +1,137 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Xep.ServiceDiscovery { + private const string NS_URI = "http://jabber.org/protocol/disco"; + public const string NS_URI_INFO = NS_URI + "#info"; + public const string NS_URI_ITEMS = NS_URI + "#items"; + + public class Module : XmppStreamModule, Iq.Handler { + public const string ID = "0030_service_discovery_module"; + + public ArrayList identities = new ArrayList(); + + public Module.with_identity(string category, string type, string? name = null) { + add_identity(category, type, name); + } + + public void add_feature(XmppStream stream, string feature) { + Flag.get_flag(stream).add_own_feature(feature); + } + + public void add_feature_notify(XmppStream stream, string feature) { + add_feature(stream, feature + "+notify"); + } + + public void add_identity(string category, string type, string? name = null) { + identities.add(new Identity(category, type, name)); + } + + public void request_info(XmppStream stream, string jid, InfoResponseListener response_listener) { + Iq.Stanza iq = new Iq.Stanza.get(new StanzaNode.build("query", NS_URI_INFO).add_self_xmlns()); + iq.to = jid; + Iq.Module.get_module(stream).send_iq(stream, iq, new IqInfoResponseListener(response_listener)); + } + + private class IqInfoResponseListener : Iq.ResponseListener, Object { + InfoResponseListener response_listener; + public IqInfoResponseListener(InfoResponseListener response_listener) { + this.response_listener = response_listener; + } + public void on_result(XmppStream stream, Iq.Stanza iq) { + InfoResult? result = InfoResult.create_from_iq(iq); + if (result != null) { + Flag.get_flag(stream).set_entitiy_features(iq.from, result.features); + response_listener.on_result(stream, result); + } else { + response_listener.on_error(stream, iq); + } + } + } + + public void request_items(XmppStream stream, string jid, ItemsResponseListener response_listener) { + Iq.Stanza iq = new Iq.Stanza.get(new StanzaNode.build("query", NS_URI_ITEMS).add_self_xmlns()); + iq.to = jid; + Iq.Module.get_module(stream).send_iq(stream, iq, new IqItemsResponseListener(response_listener)); + } + + private class IqItemsResponseListener : Iq.ResponseListener, Object { + ItemsResponseListener response_listener; + public IqItemsResponseListener(ItemsResponseListener response_listener) { this.response_listener = response_listener; } + public void on_result(XmppStream stream, Iq.Stanza iq) { + //response_listener.on_result(stream, new ServiceDiscoveryItemsResult.from_iq(iq)); + } + } + + public void on_iq_get(XmppStream stream, Iq.Stanza iq) { + StanzaNode? query_node = iq.stanza.get_subnode("query", NS_URI_INFO); + if (query_node != null) { + send_query_result(stream, iq); + } + } + + public void on_iq_set(XmppStream stream, Iq.Stanza iq) { } + + public override void attach(XmppStream stream) { + Iq.Module.require(stream); + Iq.Module.get_module(stream).register_for_namespace(NS_URI_INFO, this); + stream.add_flag(new Flag()); + add_feature(stream, NS_URI_INFO); + } + + public override void detach(XmppStream stream) { } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stream.add_module(new ServiceDiscovery.Module()); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + + private void send_query_result(XmppStream stream, Iq.Stanza iq_request) { + InfoResult query_result = new ServiceDiscovery.InfoResult(iq_request); + query_result.features = Flag.get_flag(stream).features; + query_result.identities = identities; + Iq.Module.get_module(stream).send_iq(stream, query_result.iq, null); + } + } + + public class Identity { + public string category { get; set; } + public string type_ { get; set; } + public string? name { get; set; } + + public Identity(string category, string type, string? name = null) { + this.category = category; + this.type_ = type; + this.name = name; + } + } + + public class Item { + public string jid; + public string? name; + public string? node; + + public Item(string jid, string? name = null, string? node = null) { + this.jid = jid; + this.name = name; + this.node = node; + } + } + + public interface InfoResponseListener : Object { + public abstract void on_result(XmppStream stream, InfoResult query_result); + public void on_error(XmppStream stream, Iq.Stanza iq) { } + } + + public interface ItemsResponseListener : Object { + public abstract void on_result(XmppStream stream, ItemsResult query_result); + public void on_error(XmppStream stream, Iq.Stanza iq) { } + } +} diff --git a/xmpp-vala/src/module/xep/0045_muc/flag.vala b/xmpp-vala/src/module/xep/0045_muc/flag.vala new file mode 100644 index 00000000..6c1ef508 --- /dev/null +++ b/xmpp-vala/src/module/xep/0045_muc/flag.vala @@ -0,0 +1,80 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Xep.Muc { + +public class Flag : XmppStreamFlag { + public const string ID = "muc"; + + private HashMap enter_listeners = new HashMap(); + private HashMap enter_ids = new HashMap(); + private HashMap own_nicks = new HashMap(); + private HashMap subjects = new HashMap(); + private HashMap subjects_by = new HashMap(); + private HashMap occupant_real_jids = new HashMap(); + private HashMap occupant_affiliation = new HashMap(); + private HashMap occupant_role = new HashMap(); + + public string? get_real_jid(string full_jid) { return occupant_real_jids[full_jid]; } + + public void set_real_jid(string full_jid, string real_jid) { occupant_real_jids[full_jid] = real_jid; } + + public string? get_occupant_affiliation(string full_jid) { return occupant_affiliation[full_jid]; } + + public void set_occupant_affiliation(string full_jid, string affiliation) { occupant_affiliation[full_jid] = affiliation; } + + public string? get_occupant_role(string full_jid) { return occupant_role[full_jid]; } + + public void set_occupant_role(string full_jid, string role) { occupant_role[full_jid] = role; } + + public string? get_muc_nick(string bare_jid) { return own_nicks[bare_jid]; } + + public string? get_enter_id(string bare_jid) { return enter_ids[bare_jid]; } + + public MucEnterListener? get_enter_listener(string bare_jid) { return enter_listeners[bare_jid]; } + + public bool is_muc(string jid) { return own_nicks[jid] != null; } + + public bool is_occupant(string jid) { + string bare_jid = get_bare_jid(jid); + return own_nicks.has_key(bare_jid) || enter_ids.has_key(bare_jid); + } + + public bool is_muc_enter_outstanding() { return enter_ids.size != 0; } + + public string? get_muc_subject(string bare_jid) { return subjects[bare_jid]; } + + public void set_muc_subject(string full_jid, string subject) { + string bare_jid = get_bare_jid(full_jid); + subjects[bare_jid] = subject; + subjects_by[bare_jid] = full_jid; + } + + public void start_muc_enter(string bare_jid, string presence_id, MucEnterListener listener) { + enter_listeners[bare_jid] = listener; + enter_ids[bare_jid] = presence_id; + } + + public void finish_muc_enter(string bare_jid, string? nick = null) { + if (nick != null) own_nicks[bare_jid] = nick; + enter_listeners.unset(bare_jid); + enter_ids.unset(bare_jid); + } + + public void remove_occupant_info(string full_jid) { + occupant_real_jids.unset(full_jid); + occupant_affiliation.unset(full_jid); + occupant_role.unset(full_jid); + } + + public static Flag? get_flag(XmppStream stream) { return (Flag?) stream.get_flag(NS_URI, ID); } + + public static bool has_flag(XmppStream stream) { return get_flag(stream) != null; } + + public override string get_ns() { return NS_URI; } + + public override string get_id() { return ID; } +} + +} \ No newline at end of file diff --git a/xmpp-vala/src/module/xep/0045_muc/module.vala b/xmpp-vala/src/module/xep/0045_muc/module.vala new file mode 100644 index 00000000..f9ed9539 --- /dev/null +++ b/xmpp-vala/src/module/xep/0045_muc/module.vala @@ -0,0 +1,244 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Xep.Muc { + +private const string NS_URI = "http://jabber.org/protocol/muc"; +private const string NS_URI_ADMIN = NS_URI + "#admin"; +private const string NS_URI_USER = NS_URI + "#user"; + +public const string AFFILIATION_ADMIN = "admin"; +public const string AFFILIATION_MEMBER = "member"; +public const string AFFILIATION_NONE = "none"; +public const string AFFILIATION_OUTCAST = "outcast"; +public const string AFFILIATION_OWNER = "owner"; + +public const string ROLE_MODERATOR = "moderator"; +public const string ROLE_NONE = "none"; +public const string ROLE_PARTICIPANT = "participant"; +public const string ROLE_VISITOR = "visitor"; + +public enum MucEnterError { + PASSWORD_REQUIRED, + NOT_IN_MEMBER_LIST, + BANNED, + NICK_CONFLICT, + OCCUPANT_LIMIT_REACHED, + ROOM_DOESNT_EXIST +} + +public class Module : XmppStreamModule { + public const string ID = "0045_muc_module"; + + public signal void received_occupant_affiliation(XmppStream stream, string jid, string? affiliation); + public signal void received_occupant_jid(XmppStream stream, string jid, string? real_jid); + public signal void received_occupant_role(XmppStream stream, string jid, string? role); + public signal void subject_set(XmppStream stream, string subject, string jid); + + public void enter(XmppStream stream, string bare_jid, string nick, string? password, MucEnterListener listener) { + Presence.Stanza presence = new Presence.Stanza(); + presence.to = bare_jid + "/" + nick; + StanzaNode x_node = new StanzaNode.build("x", NS_URI).add_self_xmlns(); + if (password != null) { + x_node.put_node(new StanzaNode.build("password", NS_URI).put_node(new StanzaNode.text(password))); + } + presence.stanza.put_node(x_node); + + Muc.Flag.get_flag(stream).start_muc_enter(bare_jid, presence.id, listener); + + Presence.Module.get_module(stream).send_presence(stream, presence); + } + + public void exit(XmppStream stream, string jid) { + string nick = Flag.get_flag(stream).get_muc_nick(jid); + Presence.Stanza presence = new Presence.Stanza(); + presence.to = jid + "/" + nick; + presence.type_ = Presence.Stanza.TYPE_UNAVAILABLE; + Presence.Module.get_module(stream).send_presence(stream, presence); + } + + public void change_subject(XmppStream stream, string jid, string subject) { + Message.Stanza message = new Message.Stanza(); + message.to = jid; + message.type_ = Message.Stanza.TYPE_GROUPCHAT; + message.stanza.put_node((new StanzaNode.build("subject")).put_node(new StanzaNode.text(subject))); + Message.Module.get_module(stream).send_message(stream, message); + } + + public void change_nick(XmppStream stream, string jid, string new_nick) { + Presence.Stanza presence = new Presence.Stanza(); + presence.to = jid + "/" + new_nick; + Presence.Module.get_module(stream).send_presence(stream, presence); + } + + public void kick(XmppStream stream, string jid, string nick) { + change_role(stream, jid, nick, "none"); + } + + public override void attach(XmppStream stream) { + stream.add_flag(new Muc.Flag()); + Message.Module.require(stream); + Message.Module.get_module(stream).received_message.connect(on_received_message); + Presence.Module.require(stream); + Presence.Module.get_module(stream).received_presence.connect(on_received_presence); + Presence.Module.get_module(stream).received_available.connect(on_received_available); + Presence.Module.get_module(stream).received_unavailable.connect(on_received_unavailable); + if (ServiceDiscovery.Module.get_module(stream) != null) { + ServiceDiscovery.Module.get_module(stream).add_feature(stream, NS_URI); + } + } + + public override void detach(XmppStream stream) { + Message.Module.get_module(stream).received_message.disconnect(on_received_message); + Presence.Module.get_module(stream).received_presence.disconnect(on_received_presence); + Presence.Module.get_module(stream).received_available.disconnect(on_received_available); + Presence.Module.get_module(stream).received_unavailable.disconnect(on_received_unavailable); + } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + Presence.Module.require(stream); + if (get_module(stream) == null) stream.add_module(new Muc.Module()); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + + private void change_role(XmppStream stream, string jid, string nick, string new_role) { + StanzaNode query = new StanzaNode.build("query", NS_URI_ADMIN).add_self_xmlns(); + query.put_node(new StanzaNode.build("item", NS_URI_ADMIN).put_attribute("nick", nick, NS_URI_ADMIN).put_attribute("role", new_role, NS_URI_ADMIN)); + Iq.Stanza iq = new Iq.Stanza.set(query); + iq.to = jid; + Iq.Module.get_module(stream).send_iq(stream, iq); + } + + private void on_received_message(XmppStream stream, Message.Stanza message) { + if (message.type_ == Message.Stanza.TYPE_GROUPCHAT) { + StanzaNode? subject_node = message.stanza.get_subnode("subject"); + if (subject_node != null) { + string subject = subject_node.get_string_content(); + Muc.Flag.get_flag(stream).set_muc_subject(message.from, subject); + subject_set(stream, subject, message.from); + } + } + } + + private void on_received_presence(XmppStream stream, Presence.Stanza presence) { + Flag flag = Flag.get_flag(stream); + if (presence.is_error() && flag.is_muc_enter_outstanding() && flag.is_occupant(presence.from)) { + string bare_jid = get_bare_jid(presence.from); + ErrorStanza? error_stanza = presence.get_error(); + if (flag.get_enter_id(bare_jid) == error_stanza.original_id) { + MucEnterListener listener = flag.get_enter_listener(bare_jid); + if (error_stanza.condition == ErrorStanza.CONDITION_NOT_AUTHORIZED && ErrorStanza.TYPE_AUTH == error_stanza.type_) { + listener.on_error(MucEnterError.PASSWORD_REQUIRED); + } else if (ErrorStanza.CONDITION_REGISTRATION_REQUIRED == error_stanza.condition && ErrorStanza.TYPE_AUTH == error_stanza.type_) { + listener.on_error(MucEnterError.NOT_IN_MEMBER_LIST); + } else if (ErrorStanza.CONDITION_FORBIDDEN == error_stanza.condition && ErrorStanza.TYPE_AUTH == error_stanza.type_) { + listener.on_error(MucEnterError.BANNED); + } else if (ErrorStanza.CONDITION_CONFLICT == error_stanza.condition && ErrorStanza.TYPE_CANCEL == error_stanza.type_) { + listener.on_error(MucEnterError.NICK_CONFLICT); + } else if (ErrorStanza.CONDITION_SERVICE_UNAVAILABLE == error_stanza.condition && ErrorStanza.TYPE_WAIT == error_stanza.type_) { + listener.on_error(MucEnterError.OCCUPANT_LIMIT_REACHED); + } else if (ErrorStanza.CONDITION_ITEM_NOT_FOUND == error_stanza.condition && ErrorStanza.TYPE_CANCEL == error_stanza.type_) { + listener.on_error(MucEnterError.ROOM_DOESNT_EXIST); + } + flag.finish_muc_enter(bare_jid); + } + } + } + + private void on_received_available(XmppStream stream, Presence.Stanza presence) { + Flag flag = Flag.get_flag(stream); + if (flag.is_occupant(presence.from)) { + StanzaNode? x_node = presence.stanza.get_subnode("x", NS_URI_USER); + if (x_node != null) { + ArrayList status_codes = get_status_codes(x_node); + if (status_codes.contains(StatusCode.SELF_PRESENCE)) { + string bare_jid = get_bare_jid(presence.from); + flag.get_enter_listener(bare_jid).on_success(); + flag.finish_muc_enter(bare_jid, get_resource_part(presence.from)); + } + string? affiliation = x_node["item", "affiliation"].val; + if (affiliation != null) { + received_occupant_affiliation(stream, presence.from, affiliation); + } + string? jid = x_node["item", "jid"].val; + if (jid != null) { + flag.set_real_jid(presence.from, jid); + received_occupant_jid(stream, presence.from, jid); + } + string? role = x_node["item", "role"].val; + if (role != null) { + received_occupant_role(stream, presence.from, role); + } + } + } + } + + private void on_received_unavailable(XmppStream stream, string jid) { + Flag flag = Flag.get_flag(stream); + if (flag.is_occupant(jid)) { + flag.remove_occupant_info(jid); + } + } + + private ArrayList get_status_codes(StanzaNode x_node) { + ArrayList ret = new ArrayList(); + foreach (StanzaNode status_node in x_node.get_subnodes("status", NS_URI_USER)) { + ret.add(int.parse(status_node.get_attribute("code"))); + } + return ret; + } +} + +public enum StatusCode { + /** Inform user that any occupant is allowed to see the user's full JID */ + JID_VISIBLE = 100, + /** Inform user that his or her affiliation changed while not in the room */ + AFFILIATION_CHANGED = 101, + /** Inform occupants that room now shows unavailable members */ + SHOWS_UNAVIABLE_MEMBERS = 102, + /** Inform occupants that room now does not show unavailable members */ + SHOWS_UNAVIABLE_MEMBERS_NOT = 103, + /** Inform occupants that a non-privacy-related room configuration change has occurred */ + CONFIG_CHANGE_NON_PRIVACY = 104, + /** Inform user that presence refers to itself */ + SELF_PRESENCE = 110, + /** Inform occupants that room logging is now enabled */ + LOGGING_ENABLED = 170, + /** Inform occupants that room logging is now disabled */ + LOGGING_DISABLED = 171, + /** Inform occupants that the room is now non-anonymous */ + NON_ANONYMOUS = 172, + /** Inform occupants that the room is now semi-anonymous */ + SEMI_ANONYMOUS = 173, + /** Inform user that a new room has been created */ + NEW_ROOM_CREATED = 201, + /** Inform user that service has assigned or modified occupant's roomnick */ + MODIFIED_NICK = 210, + /** Inform user that he or she has been banned from the room */ + BANNED = 301, + /** Inform all occupants of new room nickname */ + ROOM_NICKNAME = 303, + /** Inform user that he or she has been kicked from the room */ + KICKED = 307, + /** Inform user that he or she is being removed from the room */ + REMOVED_AFFILIATION_CHANGE = 321, + /** Inform user that he or she is being removed from the room because the room has been changed to members-only + and the user is not a member */ + REMOVED_MEMBERS_ONLY = 322, + /** Inform user that he or she is being removed from the room because the MUC service is being shut down */ + REMOVED_SHUTDOWN = 332 +} + +public interface MucEnterListener : Object { + public abstract void on_success(); + public abstract void on_error(MucEnterError error); +} + +} diff --git a/xmpp-vala/src/module/xep/0048_bookmarks/conference.vala b/xmpp-vala/src/module/xep/0048_bookmarks/conference.vala new file mode 100644 index 00000000..818ab3d0 --- /dev/null +++ b/xmpp-vala/src/module/xep/0048_bookmarks/conference.vala @@ -0,0 +1,74 @@ +using Xmpp.Core; + +namespace Xmpp.Xep.Bookmarks { + +public class Conference { + + public const string ATTRIBUTE_AUTOJOIN = "autojoin"; + public const string ATTRIBUTE_JID = "jid"; + public const string ATTRIBUTE_NAME = "name"; + + public const string NODE_NICK = "nick"; + public const string NODE_PASSWORD = "password"; + + public StanzaNode stanza_node; + + public bool autojoin { + get { + string? attr = stanza_node.get_attribute(ATTRIBUTE_AUTOJOIN); + return attr == "true" || attr == "1"; // "1" isn't standard, but it's used + } + set { stanza_node.set_attribute(ATTRIBUTE_AUTOJOIN, value.to_string()); } + } + + public string jid { + get { return stanza_node.get_attribute(ATTRIBUTE_JID); } + set { stanza_node.set_attribute(ATTRIBUTE_JID, value); } + } + + public string? name { + get { return stanza_node.get_attribute(ATTRIBUTE_NAME); } + set { stanza_node.set_attribute(ATTRIBUTE_NAME, value); } + } + + public string? nick { + get { + StanzaNode? nick_node = stanza_node.get_subnode(NODE_NICK); + return nick_node == null? null : nick_node.get_string_content(); + } + set { + StanzaNode? nick_node = stanza_node.get_subnode(NODE_NICK); + if (nick_node == null) { + nick_node = new StanzaNode.build(NODE_NICK, NS_URI); + stanza_node.put_node(nick_node); + } + nick_node.put_node(new StanzaNode.text(value)); + } + } + + public string? password { + get { + StanzaNode? password_node = stanza_node.get_subnode(NODE_PASSWORD); + return password_node == null? null : password_node.get_string_content(); + } + set { + StanzaNode? password_node = stanza_node.get_subnode(NODE_PASSWORD); + if (password_node == null) { + password_node = new StanzaNode.build(NODE_PASSWORD); + stanza_node.put_node(password_node); + } + password_node.put_node(new StanzaNode.text(value)); + } + } + + public Conference.from_stanza_node(StanzaNode stanza_node) { + this.stanza_node = stanza_node; + } + + public Conference(string jid) { + this.stanza_node = new StanzaNode.build("conference", NS_URI); + this.jid = jid; + } +} + +} \ No newline at end of file diff --git a/xmpp-vala/src/module/xep/0048_bookmarks/module.vala b/xmpp-vala/src/module/xep/0048_bookmarks/module.vala new file mode 100644 index 00000000..d7767208 --- /dev/null +++ b/xmpp-vala/src/module/xep/0048_bookmarks/module.vala @@ -0,0 +1,137 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Xep.Bookmarks { +private const string NS_URI = "storage:bookmarks"; + +public class Module : XmppStreamModule { + public const string ID = "0048_bookmarks_module"; + + public signal void conferences_updated(XmppStream stream, ArrayList conferences); + + public void get_conferences(XmppStream stream, ConferencesRetrieveResponseListener response_listener) { + StanzaNode get_node = new StanzaNode.build("storage", NS_URI).add_self_xmlns(); + PrivateXmlStorage.Module.get_module(stream).retrieve(stream, get_node, new GetConferences(response_listener)); + } + + public void set_conferences(XmppStream stream, ArrayList conferences) { + StanzaNode storage_node = (new StanzaNode.build("storage", NS_URI)).add_self_xmlns(); + foreach (Conference conference in conferences) { + storage_node.put_node(conference.stanza_node); + } + PrivateXmlStorage.Module.get_module(stream).store(stream, storage_node, new StoreResponseListenerImpl(conferences)); + } + + private class StoreResponseListenerImpl : PrivateXmlStorage.StoreResponseListener, Object { + ArrayList conferences; + public StoreResponseListenerImpl(ArrayList conferences) { + this.conferences = conferences; + } + public void on_success(XmppStream stream) { + Module.get_module(stream).conferences_updated(stream, conferences); + } + } + + public void add_conference(XmppStream stream, Conference add) { + get_conferences(stream, new AddConference(add)); + } + + public void replace_conference(XmppStream stream, Conference was, Conference modified) { + get_conferences(stream, new ModifyConference(was, modified)); + } + + public void remove_conference(XmppStream stream, Conference conference) { + get_conferences(stream, new RemoveConference(conference)); + } + + private class GetConferences : PrivateXmlStorage.RetrieveResponseListener, Object { + ConferencesRetrieveResponseListener response_listener; + + public GetConferences(ConferencesRetrieveResponseListener response_listener) { + this.response_listener = response_listener; + } + + public void on_result(XmppStream stream, StanzaNode node) { + response_listener.on_result(stream, get_conferences_from_stanza(node)); + } + } + + private class AddConference : ConferencesRetrieveResponseListener, Object { + private Conference conference; + public AddConference(Conference conference) { + this.conference = conference; + } + public void on_result(XmppStream stream, ArrayList conferences) { + conferences.add(conference); + Module.get_module(stream).set_conferences(stream, conferences); + } + } + + private class ModifyConference : ConferencesRetrieveResponseListener, Object { + private Conference was; + private Conference modified; + public ModifyConference(Conference was, Conference modified) { + this.was = was; + this.modified = modified; + } + public void on_result(XmppStream stream, ArrayList conferences) { + foreach (Conference conference in conferences) { + if (conference.name == was.name && conference.jid == was.jid && conference.autojoin == was.autojoin) { + conference.autojoin = modified.autojoin; + conference.name = modified.name; + conference.jid = modified.jid; + break; + } + } + Module.get_module(stream).set_conferences(stream, conferences); + } + } + + private class RemoveConference : ConferencesRetrieveResponseListener, Object { + private Conference remove; + public RemoveConference(Conference remove) { + this.remove = remove; + } + public void on_result(XmppStream stream, ArrayList conferences) { + Conference? rem = null; + foreach (Conference conference in conferences) { + if (conference.name == remove.name && conference.jid == remove.jid && conference.autojoin == remove.autojoin) { + rem = conference; + } + } + if (rem != null) conferences.remove(rem); + Module.get_module(stream).set_conferences(stream, conferences); + } + } + + public override void attach(XmppStream stream) { } + + public override void detach(XmppStream stream) { } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stderr.printf(""); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + + private static ArrayList get_conferences_from_stanza(StanzaNode node) { + ArrayList conferences = new ArrayList(); + ArrayList conferenceNodes = node.get_subnode("storage", NS_URI).get_subnodes("conference", NS_URI); + foreach (StanzaNode conferenceNode in conferenceNodes) { + conferences.add(new Conference.from_stanza_node(conferenceNode)); + } + return conferences; + } +} + +public interface ConferencesRetrieveResponseListener : Object { + public abstract void on_result(XmppStream stream, ArrayList conferences); +} + +} diff --git a/xmpp-vala/src/module/xep/0049_private_xml_storage.vala b/xmpp-vala/src/module/xep/0049_private_xml_storage.vala new file mode 100644 index 00000000..c57acdde --- /dev/null +++ b/xmpp-vala/src/module/xep/0049_private_xml_storage.vala @@ -0,0 +1,65 @@ +using Xmpp.Core; + +namespace Xmpp.Xep.PrivateXmlStorage { + private const string NS_URI = "jabber:iq:private"; + + public class Module : XmppStreamModule { + public const string ID = "0049_private_xml_storage"; + + public void store(XmppStream stream, StanzaNode node, StoreResponseListener listener) { + StanzaNode queryNode = new StanzaNode.build("query", NS_URI).add_self_xmlns().put_node(node); + Iq.Stanza iq = new Iq.Stanza.set(queryNode); + Iq.Module.get_module(stream).send_iq(stream, iq, new IqStoreResponse(listener)); + } + + private class IqStoreResponse : Iq.ResponseListener, Object { + StoreResponseListener listener; + public IqStoreResponse(StoreResponseListener listener) { + this.listener = listener; + } + public void on_result(XmppStream stream, Iq.Stanza iq) { + listener.on_success(stream); + } + } + + public void retrieve(XmppStream stream, StanzaNode node, RetrieveResponseListener responseListener) { + StanzaNode queryNode = new StanzaNode.build("query", NS_URI).add_self_xmlns().put_node(node); + Iq.Stanza iq = new Iq.Stanza.get(queryNode); + Iq.Module.get_module(stream).send_iq(stream, iq, new IqRetrieveResponse(responseListener)); + } + + private class IqRetrieveResponse : Iq.ResponseListener, Object { + RetrieveResponseListener response_listener; + public IqRetrieveResponse(RetrieveResponseListener response_listener) { this.response_listener = response_listener; } + + public void on_result(XmppStream stream, Iq.Stanza iq) { + response_listener.on_result(stream, iq.stanza.get_subnode("query", NS_URI)); + } + } + + public override void attach(XmppStream stream) { + Iq.Module.require(stream); + } + + public override void detach(XmppStream stream) { } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stream.add_module(new PrivateXmlStorage.Module()); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + } + + public interface StoreResponseListener : Object { + public abstract void on_success(XmppStream stream); + } + + public interface RetrieveResponseListener : Object { + public abstract void on_result(XmppStream stream, StanzaNode stanzaNode); + } +} diff --git a/xmpp-vala/src/module/xep/0054_vcard/module.vala b/xmpp-vala/src/module/xep/0054_vcard/module.vala new file mode 100644 index 00000000..58b71d2c --- /dev/null +++ b/xmpp-vala/src/module/xep/0054_vcard/module.vala @@ -0,0 +1,87 @@ +using Xmpp.Core; + +namespace Xmpp.Xep.VCard { +private const string NS_URI = "vcard-temp"; +private const string NS_URI_UPDATE = NS_URI + ":x:update"; + +public class Module : XmppStreamModule { + public const string ID = "0027_current_pgp_usage"; + + public signal void received_avatar(XmppStream stream, string jid, string id); + + private PixbufStorage storage; + + public Module(PixbufStorage storage) { + this.storage = storage; + } + + public override void attach(XmppStream stream) { + Iq.Module.require(stream); + Presence.Module.require(stream); + Presence.Module.get_module(stream).received_presence.connect(on_received_presence); + } + + public override void detach(XmppStream stream) { + Presence.Module.get_module(stream).received_presence.disconnect(on_received_presence); + } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stderr.printf("VCardModule required but not attached!\n"); ; + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + + private void on_received_presence(XmppStream stream, Presence.Stanza presence) { + StanzaNode? update_node = presence.stanza.get_subnode("x", NS_URI_UPDATE); + if (update_node == null) return; + StanzaNode? photo_node = update_node.get_subnode("photo", NS_URI_UPDATE); + if (photo_node == null) return; + string? sha1 = photo_node.get_string_content(); + if (sha1 == null) return; + if (storage.has_image(sha1)) { + if (Muc.Flag.get_flag(stream).is_occupant(presence.from)) { + received_avatar(stream, presence.from, sha1); + } else { + received_avatar(stream, get_bare_jid(presence.from), sha1); + } + } else { + Iq.Stanza iq = new Iq.Stanza.get(new StanzaNode.build("vCard", NS_URI).add_self_xmlns()); + if (Muc.Flag.get_flag(stream).is_occupant(presence.from)) { + iq.to = presence.from; + } else { + iq.to = get_bare_jid(presence.from); + } + Iq.Module.get_module(stream).send_iq(stream, iq, new IqResponseListenerImpl(this, storage, sha1)); + } + } + + private class IqResponseListenerImpl : Iq.ResponseListener, Object { + Module outer; + PixbufStorage storage; + string id; + public IqResponseListenerImpl(Module outer, PixbufStorage storage, string id) { + this.outer = outer; + this.id = id; + this.storage = storage; + } + public void on_result(XmppStream stream, Iq.Stanza iq) { + if (iq.is_error()) return; + StanzaNode? vcard_node = iq.stanza.get_subnode("vCard", NS_URI); + if (vcard_node == null) return; + StanzaNode? photo_node = vcard_node.get_subnode("PHOTO", NS_URI); + if (photo_node == null) return; + StanzaNode? binary_node = photo_node.get_subnode("BINVAL", NS_URI); + if (binary_node == null) return; + string? content = binary_node.get_string_content(); + if (content == null) return; + storage.store(id, Base64.decode(content)); + outer.received_avatar(stream, iq.from, id); + } + } +} +} diff --git a/xmpp-vala/src/module/xep/0060_pubsub.vala b/xmpp-vala/src/module/xep/0060_pubsub.vala new file mode 100644 index 00000000..3f96e7a1 --- /dev/null +++ b/xmpp-vala/src/module/xep/0060_pubsub.vala @@ -0,0 +1,107 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Xep.Pubsub { + private const string NS_URI = "http://jabber.org/protocol/pubsub"; + private const string NS_URI_EVENT = NS_URI + "#event"; + + public class Module : XmppStreamModule { + public const string ID = "0060_pubsub_module"; + + private HashMap event_listeners = new HashMap(); + + public void add_filtered_notification(XmppStream stream, string node, EventListener listener) { + ServiceDiscovery.Module.get_module(stream).add_feature_notify(stream, node); + event_listeners[node] = listener; + } + + public void request(XmppStream stream, string jid, string node, RequestResponseListener listener) { // TODO multiple nodes gehen auch + Iq.Stanza a = new Iq.Stanza.get(new StanzaNode.build("pubsub", NS_URI).add_self_xmlns().put_node(new StanzaNode.build("items", NS_URI).put_attribute("node", node))); + a.to = jid; + Iq.Module.get_module(stream).send_iq(stream, a, new IqRequestResponseListener(listener)); + } + + private class IqRequestResponseListener : Iq.ResponseListener, Object { + RequestResponseListener response_listener; + public IqRequestResponseListener(RequestResponseListener response_listener) { this.response_listener = response_listener; } + public void on_result(XmppStream stream, Iq.Stanza iq) { + StanzaNode event_node = iq.stanza.get_subnode("pubsub", NS_URI); if (event_node == null) return; + StanzaNode items_node = event_node.get_subnode("items", NS_URI); if (items_node == null) return; + StanzaNode item_node = items_node.get_subnode("item", NS_URI); if (item_node == null) return; + string node = items_node.get_attribute("node", NS_URI); + string id = item_node.get_attribute("id", NS_URI); + response_listener.on_result(stream, iq.from, id, item_node.sub_nodes[0]); + } + } + + public void publish(XmppStream stream, string? jid, string node_id, string node, string item_id, StanzaNode content) { + StanzaNode pubsub_node = new StanzaNode.build("pubsub", NS_URI).add_self_xmlns(); + StanzaNode publish_node = new StanzaNode.build("publish", NS_URI).put_attribute("node", node_id); + pubsub_node.put_node(publish_node); + StanzaNode items_node = new StanzaNode.build("item", NS_URI).put_attribute("id", item_id); + items_node.put_node(content); + publish_node.put_node(items_node); + Iq.Stanza iq = new Iq.Stanza.set(pubsub_node); + Iq.Module.get_module(stream).send_iq(stream, iq, null); + } + + private class IqPublishResponseListener : Iq.ResponseListener, Object { + PublishResponseListener response_listener; + public IqPublishResponseListener(PublishResponseListener response_listener) { this.response_listener = response_listener; } + public void on_result(XmppStream stream, Iq.Stanza iq) { + if (iq.is_error()) { + response_listener.on_error(stream); + } else { + response_listener.on_success(stream); + } + } + } + + public override void attach(XmppStream stream) { + Iq.Module.require(stream); + Message.Module.require(stream); + ServiceDiscovery.Module.require(stream); + Message.Module.get_module(stream).received_message.connect(on_received_message); + } + + public override void detach(XmppStream stream) { + Message.Module.get_module(stream).received_message.disconnect(on_received_message); + } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stream.add_module(new Module()); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + + private void on_received_message(XmppStream stream, Message.Stanza message) { + StanzaNode event_node = message.stanza.get_subnode("event", NS_URI_EVENT); if (event_node == null) return; + StanzaNode items_node = event_node.get_subnode("items", NS_URI_EVENT); if (items_node == null) return; + StanzaNode item_node = items_node.get_subnode("item", NS_URI_EVENT); if (item_node == null) return; + string node = items_node.get_attribute("node", NS_URI_EVENT); + string id = item_node.get_attribute("id", NS_URI_EVENT); + if (event_listeners.has_key(node)) { + event_listeners[node].on_result(stream, message.from, id, item_node.sub_nodes[0]); + } + } + } + + public interface RequestResponseListener : Object { + public abstract void on_result(XmppStream stream, string jid, string id, StanzaNode node); + } + + public interface EventListener : Object { + public abstract void on_result(XmppStream stream, string jid, string id, StanzaNode node); + } + + public interface PublishResponseListener : Object { + public abstract void on_success(XmppStream stream); + public abstract void on_error(XmppStream stream); + } +} diff --git a/xmpp-vala/src/module/xep/0082_date_time_profiles.vala b/xmpp-vala/src/module/xep/0082_date_time_profiles.vala new file mode 100644 index 00000000..b2ce1077 --- /dev/null +++ b/xmpp-vala/src/module/xep/0082_date_time_profiles.vala @@ -0,0 +1,41 @@ +namespace Xmpp.Xep.DateTimeProfiles { + + public class Module { + public Regex DATETIME_REGEX; + + public Module() { + DATETIME_REGEX = new Regex("""^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.(\d{3}))?(Z|((\+|\-)(\d{2}):(\d{2})))$"""); + } + + public DateTime? parse_string(string time_string) { + MatchInfo match_info; + if (DATETIME_REGEX.match(time_string, RegexMatchFlags.ANCHORED, out match_info)) { + int year = int.parse(match_info.fetch(1)); + int month = int.parse(match_info.fetch(2)); + int day = int.parse(match_info.fetch(3)); + int hour = int.parse(match_info.fetch(4)); + int minute = int.parse(match_info.fetch(5)); + int second = int.parse(match_info.fetch(6)); + DateTime datetime = new DateTime.utc(year, month, day, hour, minute, second); + if (match_info.fetch(9) != "Z") { + char plusminus = match_info.fetch(11)[0]; + int tz_hour = int.parse(match_info.fetch(12)); + int tz_minute = int.parse(match_info.fetch(13)); + if (plusminus == '-') { + tz_hour *= -1; + tz_minute *= -1; + } + datetime.add_hours(tz_hour); + datetime.add_minutes(tz_minute); + } + return datetime; + } + return null; + } + + public string to_datetime(DateTime time) { + return time.format("%Y-%m-%dT%H:%M:%SZ"); + } +} + +} \ No newline at end of file diff --git a/xmpp-vala/src/module/xep/0084_user_avatars.vala b/xmpp-vala/src/module/xep/0084_user_avatars.vala new file mode 100644 index 00000000..13d19674 --- /dev/null +++ b/xmpp-vala/src/module/xep/0084_user_avatars.vala @@ -0,0 +1,93 @@ +using Xmpp.Core; + +namespace Xmpp.Xep.UserAvatars { + private const string NS_URI = "urn:xmpp:avatar"; + private const string NS_URI_DATA = NS_URI + ":data"; + private const string NS_URI_METADATA = NS_URI + ":metadata"; + + public class Module : XmppStreamModule { + public const string ID = "0084_user_avatars"; + + public signal void received_avatar(XmppStream stream, string jid, string id); + + private PixbufStorage storage; + + public Module(PixbufStorage storage) { + this.storage = storage; + } + + public void publish_png(XmppStream stream, uint8[] image, int width, int height) { + string sha1 = Checksum.compute_for_data(ChecksumType.SHA1, image); + StanzaNode data_node = new StanzaNode.build("data", NS_URI_DATA).add_self_xmlns() + .put_node(new StanzaNode.text(Base64.encode(image))); + Pubsub.Module.get_module(stream).publish(stream, null, NS_URI_DATA, NS_URI_DATA, sha1, data_node); + + StanzaNode metadata_node = new StanzaNode.build("metadata", NS_URI_METADATA).add_self_xmlns(); + StanzaNode info_node = new StanzaNode.build("info", NS_URI_METADATA) + .put_attribute("bytes", image.length.to_string()) + .put_attribute("id", sha1) + .put_attribute("width", width.to_string()) + .put_attribute("height", height.to_string()) + .put_attribute("type", "image/png"); + metadata_node.put_node(info_node); + Pubsub.Module.get_module(stream).publish(stream, null, NS_URI_METADATA, NS_URI_METADATA, sha1, metadata_node); + } + + private class PublishResponseListenerImpl : Pubsub.PublishResponseListener, Object { + PublishResponseListener listener; + PublishResponseListenerImpl other; + public PublishResponseListenerImpl(PublishResponseListener listener, PublishResponseListenerImpl other) { + this.listener = listener; + this.other = other; + } + public void on_success(XmppStream stream) { listener.on_success(stream); } + public void on_error(XmppStream stream) { listener.on_error(stream); } + } + + public override void attach(XmppStream stream) { + Pubsub.Module.require(stream); + Pubsub.Module.get_module(stream).add_filtered_notification(stream, NS_URI_METADATA, new PubsubEventListenerImpl(storage)); + } + + public override void detach(XmppStream stream) { } + + class PubsubEventListenerImpl : Pubsub.EventListener, Object { + PixbufStorage storage; + public PubsubEventListenerImpl(PixbufStorage storage) { this.storage = storage; } + public void on_result(XmppStream stream, string jid, string id, StanzaNode node) { + StanzaNode info_node = node.get_subnode("info", NS_URI_METADATA); + if (info_node.get_attribute("type") != "image/png") return; + if (storage.has_image(id)) { + Module.get_module(stream).received_avatar(stream, jid, id); + } else { + Pubsub.Module.get_module(stream).request(stream, jid, NS_URI_DATA, new PubsubRequestResponseListenerImpl(storage)); + } + } + } + + class PubsubRequestResponseListenerImpl : Pubsub.RequestResponseListener, Object { + PixbufStorage storage; + public PubsubRequestResponseListenerImpl(PixbufStorage storage) { this.storage = storage; } + public void on_result(XmppStream stream, string jid, string id, StanzaNode node) { + storage.store(id, Base64.decode(node.get_string_content())); + Module.get_module(stream).received_avatar(stream, jid, id); + } + } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stderr.printf("UserAvatarsModule required but not attached!\n"); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + } + + public interface PublishResponseListener : Object { + public abstract void on_success(XmppStream stream); + public abstract void on_error(XmppStream stream); + } +} diff --git a/xmpp-vala/src/module/xep/0085_chat_state_notifications.vala b/xmpp-vala/src/module/xep/0085_chat_state_notifications.vala new file mode 100644 index 00000000..cefc7a18 --- /dev/null +++ b/xmpp-vala/src/module/xep/0085_chat_state_notifications.vala @@ -0,0 +1,74 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Xep.ChatStateNotifications { +private const string NS_URI = "http://jabber.org/protocol/chatstates"; + +public const string STATE_ACTIVE = "active"; +public const string STATE_INACTIVE = "inactive"; +public const string STATE_GONE = "gone"; +public const string STATE_COMPOSING = "composing"; +public const string STATE_PAUSED = "paused"; + +private const string[] STATES = {STATE_ACTIVE, STATE_INACTIVE, STATE_GONE, STATE_COMPOSING, STATE_PAUSED}; + +public class Module : XmppStreamModule { + public const string ID = "0085_chat_state_notifications"; + + public signal void chat_state_received(XmppStream stream, string jid, string state); + + /** + * "A message stanza that does not contain standard messaging content [...] SHOULD be a state other than " (0085, 5.6) + */ + public void send_state(XmppStream stream, string jid, string state) { + Message.Stanza message = new Message.Stanza(); + message.to = jid; + message.type_ = Message.Stanza.TYPE_CHAT; + message.stanza.put_node(new StanzaNode.build(state, NS_URI).add_self_xmlns()); + Message.Module.get_module(stream).send_message(stream, message); + } + + public override void attach(XmppStream stream) { + ServiceDiscovery.Module.require(stream); + ServiceDiscovery.Module.get_module(stream).add_feature(stream, NS_URI); + Message.Module.get_module(stream).pre_send_message.connect(on_pre_send_message); + Message.Module.get_module(stream).received_message.connect(on_received_message); + } + + public override void detach(XmppStream stream) { + Message.Module.get_module(stream).pre_send_message.disconnect(on_pre_send_message); + Message.Module.get_module(stream).received_message.disconnect(on_received_message); + } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stream.add_module(new Module()); ; + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + + private void on_pre_send_message(XmppStream stream, Message.Stanza message) { + if (message.body == null) return; + if (message.type_ != Message.Stanza.TYPE_CHAT) return; + message.stanza.put_node(new StanzaNode.build(STATE_ACTIVE, NS_URI).add_self_xmlns()); + } + + private void on_received_message(XmppStream stream, Message.Stanza message) { + if (!message.is_error()) { + ArrayList nodes = message.stanza.get_all_subnodes(); + foreach (StanzaNode node in nodes) { + if (node.ns_uri == NS_URI && + node.name in STATES) { + chat_state_received(stream, message.from, node.name); + } + } + } + } +} + +} diff --git a/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala b/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala new file mode 100644 index 00000000..472eb9bd --- /dev/null +++ b/xmpp-vala/src/module/xep/0115_entitiy_capabilities.vala @@ -0,0 +1,125 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Xep.EntityCapabilities { + private const string NS_URI = "http://jabber.org/protocol/caps"; + + public class Module : XmppStreamModule { + public const string ID = "0115_entity_capabilities"; + + private string own_ver_hash; + private Storage storage; + + public Module(Storage storage) { + this.storage = storage; + } + + private string get_own_hash(XmppStream stream) { + if (own_ver_hash == null) { + own_ver_hash = compute_hash(ServiceDiscovery.Module.get_module(stream).identities, ServiceDiscovery.Flag.get_flag(stream).features); + } + return own_ver_hash; + } + + public override void attach(XmppStream stream) { + ServiceDiscovery.Module.require(stream); + Presence.Module.require(stream); + Presence.Module.get_module(stream).pre_send_presence_stanza.connect(on_pre_send_presence_stanza); + Presence.Module.get_module(stream).received_presence.connect(on_received_presence); + ServiceDiscovery.Module.get_module(stream).add_feature(stream, NS_URI); + } + + public override void detach(XmppStream stream) { + Presence.Module.get_module(stream).pre_send_presence_stanza.disconnect(on_pre_send_presence_stanza); + Presence.Module.get_module(stream).received_presence.disconnect(on_received_presence); + } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stderr.printf("EntityCapabilitiesModule required but not attached!\n"); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + + private void on_pre_send_presence_stanza(XmppStream stream, Presence.Stanza presence) { + if (presence.type_ == Presence.Stanza.TYPE_AVAILABLE) { + presence.stanza.put_node(new StanzaNode.build("c", NS_URI).add_self_xmlns() + .put_attribute("hash", "sha-1") + .put_attribute("node", "http://dino-im.org") + .put_attribute("ver", get_own_hash(stream))); + } + } + + private void on_received_presence(XmppStream stream, Presence.Stanza presence) { + StanzaNode? c_node = presence.stanza.get_subnode("c", NS_URI); + if (c_node != null) { + string ver_attribute = c_node.get_attribute("ver", NS_URI); + ArrayList capabilities = storage.get_features(ver_attribute); + if (capabilities.size == 0) { + ServiceDiscovery.Module.get_module(stream) + .request_info(stream, presence.from, new ServiceDiscoveryInfoResponseListenerImpl(storage, ver_attribute)); + } else { + ServiceDiscovery.Flag.get_flag(stream).set_entitiy_features(presence.from, capabilities); + } + } + } + + private class ServiceDiscoveryInfoResponseListenerImpl : ServiceDiscovery.InfoResponseListener, Object { + private Storage storage; + private string entity; + + public ServiceDiscoveryInfoResponseListenerImpl(Storage storage, string entity) { + this.storage = storage; + this.entity = entity; + } + public void on_result(XmppStream stream, ServiceDiscovery.InfoResult query_result) { + if (compute_hash(query_result.identities, query_result.features) == entity) { + storage.store_features(entity, query_result.features); + } + } + } + + private static string compute_hash(ArrayList identities, ArrayList features) { + identities.sort(compare_identities); + features.sort(); + + string s = ""; + foreach (ServiceDiscovery.Identity identity in identities) { + string s_identity = identity.category + "/" + identity.type_ + "//"; + if (identity.name != null) s_identity += identity.name; + s_identity += "<"; + s += s_identity; + } + foreach (string feature in features) { + s += feature + "<"; + } + + Checksum c = new Checksum(ChecksumType.SHA1); + c.update(s.data, -1); + size_t size = 20; + uint8[] buf = new uint8[size]; + c.get_digest(buf, ref size); + + return Base64.encode(buf); + } + + private static int compare_identities(ServiceDiscovery.Identity a, ServiceDiscovery.Identity b) { + int category_comp = a.category.collate(b.category); + if (category_comp != 0) return category_comp; + int type_comp = a.type_.collate(b.type_); + if (type_comp != 0) return type_comp; + // TODO lang + return 0; + } + } + + public interface Storage : Object { + public abstract void store_features(string entitiy, ArrayList capabilities); + public abstract ArrayList get_features(string entitiy); + } +} diff --git a/xmpp-vala/src/module/xep/0184_message_delivery_receipts.vala b/xmpp-vala/src/module/xep/0184_message_delivery_receipts.vala new file mode 100644 index 00000000..489592fa --- /dev/null +++ b/xmpp-vala/src/module/xep/0184_message_delivery_receipts.vala @@ -0,0 +1,62 @@ +using Gdk; + +using Xmpp.Core; + +namespace Xmpp.Xep.MessageDeliveryReceipts { + private const string NS_URI = "urn:xmpp:receipts"; + + public class Module : XmppStreamModule { + public const string ID = "0184_message_delivery_receipts"; + + public signal void receipt_received(XmppStream stream, string jid, string id); + + public override void attach(XmppStream stream) { + ServiceDiscovery.Module.require(stream); + Message.Module.require(stream); + + ServiceDiscovery.Module.get_module(stream).add_feature(stream, NS_URI); + Message.Module.get_module(stream).received_message.connect(received_message); + Message.Module.get_module(stream).pre_send_message.connect(pre_send_message); + } + + public override void detach(XmppStream stream) { + Message.Module.get_module(stream).received_message.disconnect(received_message); + Message.Module.get_module(stream).pre_send_message.disconnect(pre_send_message); + } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stream.add_module(new Module()); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + + private void received_message(XmppStream stream, Message.Stanza message) { + StanzaNode? received_node = message.stanza.get_subnode("received", NS_URI); + if (received_node != null) { + receipt_received(stream, message.from, received_node.get_attribute("id", NS_URI)); + } else if (message.stanza.get_subnode("request", NS_URI) != null) { + send_received(stream, message); + } + } + + private void send_received(XmppStream stream, Message.Stanza message) { + Message.Stanza received_message = new Message.Stanza(); + received_message.to = message.from; + received_message.stanza.put_node(new StanzaNode.build("received", NS_URI).add_self_xmlns().put_attribute("id", message.id)); + Message.Module.get_module(stream).send_message(stream, received_message); + } + + private void pre_send_message(XmppStream stream, Message.Stanza message) { + StanzaNode? received_node = message.stanza.get_subnode("received", NS_URI); + if (received_node != null) return; + if (message.body == null) return; + if (message.type_ == Message.Stanza.TYPE_GROUPCHAT) return; + message.stanza.put_node(new StanzaNode.build("request", NS_URI).add_self_xmlns()); + } + } +} diff --git a/xmpp-vala/src/module/xep/0199_ping.vala b/xmpp-vala/src/module/xep/0199_ping.vala new file mode 100644 index 00000000..82da1d23 --- /dev/null +++ b/xmpp-vala/src/module/xep/0199_ping.vala @@ -0,0 +1,56 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Xep.Ping { + private const string NS_URI = "urn:xmpp:ping"; + + public class Module : XmppStreamModule { + public const string ID = "0199_ping"; + + public void send_ping(XmppStream stream, string jid, ResponseListener? listener = null) { + Iq.Stanza iq = new Iq.Stanza.get(new StanzaNode.build("ping", NS_URI).add_self_xmlns()); + iq.to = jid; + Iq.Module.get_module(stream).send_iq(stream, iq, listener == null? null : new IqResponseListenerImpl(listener)); + } + + private class IqResponseListenerImpl : Iq.ResponseListener, Object { + ResponseListener listener; + public IqResponseListenerImpl(ResponseListener listener) { + this.listener = listener; + } + public void on_result(XmppStream stream, Iq.Stanza iq) { + listener.on_result(stream); + } + } + + public override void attach(XmppStream stream) { + Iq.Module.require(stream); + Iq.Module.get_module(stream).register_for_namespace(NS_URI, new IqHandlerImpl()); + } + + public override void detach(XmppStream stream) { } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stream.add_module(new Module()); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + + private class IqHandlerImpl : Iq.Handler, Object { + public void on_iq_get(XmppStream stream, Iq.Stanza iq) { + Iq.Module.get_module(stream).send_iq(stream, new Iq.Stanza.result(iq)); + } + public void on_iq_set(XmppStream stream, Iq.Stanza iq) { } + } + } + + public interface ResponseListener : Object { + public abstract void on_result(XmppStream stream); + } +} diff --git a/xmpp-vala/src/module/xep/0203_delayed_delivery.vala b/xmpp-vala/src/module/xep/0203_delayed_delivery.vala new file mode 100644 index 00000000..9f9761f2 --- /dev/null +++ b/xmpp-vala/src/module/xep/0203_delayed_delivery.vala @@ -0,0 +1,62 @@ +using Xmpp.Core; + +namespace Xmpp.Xep.DelayedDelivery { + private const string NS_URI = "urn:xmpp:delay"; + + public class Module : XmppStreamModule { + public const string ID = "0203_delayed_delivery"; + + public static void set_message_delay(Message.Stanza message, DateTime datetime) { + StanzaNode delay_node = (new StanzaNode.build("delay", NS_URI)).add_self_xmlns(); + delay_node.put_attribute("stamp", (new DateTimeProfiles.Module()).to_datetime(datetime)); + message.stanza.put_node(delay_node); + } + + public static DateTime? get_send_time(Message.Stanza message) { + StanzaNode? delay_node = message.stanza.get_subnode("delay", NS_URI); + if (delay_node != null) { + string time = delay_node.get_attribute("stamp"); + return (new DateTimeProfiles.Module()).parse_string(time); + } else { + return null; + } + } + + public override void attach(XmppStream stream) { + Message.Module.get_module(stream).pre_received_message.connect(on_pre_received_message); + } + + public override void detach(XmppStream stream) { } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stream.add_module(new Module()); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + + private void on_pre_received_message(XmppStream stream, Message.Stanza message) { + DateTime? datetime = get_send_time(message); + if (datetime != null) message.add_flag(new MessageFlag(datetime)); + } + } + + public class MessageFlag : Message.MessageFlag { + public const string ID = "delayed_delivery"; + + public DateTime datetime { get; private set; } + + public MessageFlag(DateTime datetime) { + this.datetime = datetime; + } + + public static MessageFlag? get_flag(Message.Stanza message) { return (MessageFlag) message.get_flag(NS_URI, ID); } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + } +} diff --git a/xmpp-vala/src/module/xep/0280_message_carbons.vala b/xmpp-vala/src/module/xep/0280_message_carbons.vala new file mode 100644 index 00000000..18b2ecdf --- /dev/null +++ b/xmpp-vala/src/module/xep/0280_message_carbons.vala @@ -0,0 +1,91 @@ +using Xmpp.Core; + +namespace Xmpp.Xep.MessageCarbons { + private const string NS_URI = "urn:xmpp:carbons:2"; + + public class Module : XmppStreamModule { + public const string ID = "0280_message_carbons_module"; + + public void enable(XmppStream stream) { + Iq.Stanza iq = new Iq.Stanza.set(new StanzaNode.build("enable", NS_URI).add_self_xmlns()); + Iq.Module.get_module(stream).send_iq(stream, iq); + } + + public void disable(XmppStream stream) { + Iq.Stanza iq = new Iq.Stanza.set(new StanzaNode.build("disable", NS_URI).add_self_xmlns()); + Iq.Module.get_module(stream).send_iq(stream, iq); + } + + public override void attach(XmppStream stream) { + Bind.Module.require(stream); + Iq.Module.require(stream); + Message.Module.require(stream); + ServiceDiscovery.Module.require(stream); + + stream.stream_negotiated.connect(enable); + Message.Module.get_module(stream).pre_received_message.connect(pre_received_message); + ServiceDiscovery.Module.get_module(stream).add_feature(stream, NS_URI); + } + + public override void detach(XmppStream stream) { + stream.stream_negotiated.disconnect(enable); + Message.Module.get_module(stream).pre_received_message.disconnect(pre_received_message); + } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stream.add_module(new Module()); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + + private void pre_received_message(XmppStream stream, Message.Stanza message) { + StanzaNode? received_node = message.stanza.get_subnode("received", NS_URI); + StanzaNode? sent_node = received_node == null ? message.stanza.get_subnode("sent", NS_URI) : null; + StanzaNode? carbons_node = received_node != null ? received_node : sent_node; + if (carbons_node != null) { + StanzaNode? forwarded_node = carbons_node.get_subnode("forwarded", "urn:xmpp:forward:0"); + if (forwarded_node != null) { + StanzaNode? message_node = forwarded_node.get_subnode("message", Message.NS_URI); + string? from_attribute = message_node.get_attribute("from", Message.NS_URI); + // The security model assumed by this document is that all of the resources for a single user are in the same trust boundary. + // Any forwarded copies received by a Carbons-enabled client MUST be from that user's bare JID; any copies that do not meet this requirement MUST be ignored. + if (from_attribute != null && from_attribute == get_bare_jid(Bind.Flag.get_flag(stream).my_jid)) { + if (received_node != null) { + message.add_flag(new MessageFlag(MessageFlag.TYPE_RECEIVED)); + } else if (sent_node != null) { + message.add_flag(new MessageFlag(MessageFlag.TYPE_SENT)); + } + message.stanza = message_node; + message.rerun_parsing = true; + } + message.stanza = message_node; + message.rerun_parsing = true; + } + } + } + } + + public class MessageFlag : Message.MessageFlag { + public const string id = "message_carbons"; + + public const string TYPE_RECEIVED = "received"; + public const string TYPE_SENT = "sent"; + private string type_; + + public MessageFlag(string type) { + this.type_ = type; + } + + public static MessageFlag? get_flag(Message.Stanza message) { + return (MessageFlag) message.get_flag(NS_URI, id); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return id; } + } +} diff --git a/xmpp-vala/src/module/xep/0333_chat_markers.vala b/xmpp-vala/src/module/xep/0333_chat_markers.vala new file mode 100644 index 00000000..0dc0e637 --- /dev/null +++ b/xmpp-vala/src/module/xep/0333_chat_markers.vala @@ -0,0 +1,81 @@ +using Gee; + +using Xmpp.Core; + +namespace Xmpp.Xep.ChatMarkers { +private const string NS_URI = "urn:xmpp:chat-markers:0"; + +public const string MARKER_RECEIVED = "received"; +public const string MARKER_DISPLAYED = "displayed"; +public const string MARKER_ACKNOWLEDGED = "acknowledged"; + +private const string[] MARKERS = {MARKER_RECEIVED, MARKER_DISPLAYED, MARKER_ACKNOWLEDGED}; + +public class Module : XmppStreamModule { + public const string ID = "0333_chat_markers"; + + public signal void marker_received(XmppStream stream, string jid, string marker, string id); + + public void send_marker(XmppStream stream, string jid, string message_id, string type_, string marker) { + Message.Stanza received_message = new Message.Stanza(); + received_message.to = jid; + received_message.type_ = type_; + received_message.stanza.put_node(new StanzaNode.build(marker, NS_URI).add_self_xmlns().put_attribute("id", message_id)); + Message.Module.get_module(stream).send_message(stream, received_message); + } + + public static bool requests_marking(Message.Stanza message) { + StanzaNode markable_node = message.stanza.get_subnode("markable", NS_URI); + return markable_node != null; + } + + public override void attach(XmppStream stream) { + Iq.Module.require(stream); + Message.Module.require(stream); + ServiceDiscovery.Module.require(stream); + + ServiceDiscovery.Module.get_module(stream).add_feature(stream, NS_URI); + Message.Module.get_module(stream).pre_send_message.connect(on_pre_send_message); + Message.Module.get_module(stream).received_message.connect(on_received_message); + } + + public override void detach(XmppStream stream) { + Message.Module.get_module(stream).pre_send_message.disconnect(on_pre_send_message); + Message.Module.get_module(stream).received_message.disconnect(on_received_message); + } + + public static Module? get_module(XmppStream stream) { + return (Module?) stream.get_module(NS_URI, ID); + } + + public static void require(XmppStream stream) { + if (get_module(stream) == null) stream.add_module(new ChatMarkers.Module()); + } + + public override string get_ns() { return NS_URI; } + public override string get_id() { return ID; } + + private void on_received_message(XmppStream stream, Message.Stanza message) { + if (message.type_ != Message.Stanza.TYPE_CHAT) return; + if (requests_marking(message)) { + send_marker(stream, message.from, message.id, message.type_, MARKER_RECEIVED); + return; + } + ArrayList nodes = message.stanza.get_all_subnodes(); + foreach (StanzaNode node in nodes) { + if (node.ns_uri == NS_URI && node.name in MARKERS) { + marker_received(stream, message.from, node.name, node.get_attribute("id", NS_URI)); + } + } + } + + private void on_pre_send_message(XmppStream stream, Message.Stanza message) { + StanzaNode? received_node = message.stanza.get_subnode("received", NS_URI); + if (received_node != null) return; + if (message.body == null) return; + if (message.type_ != Message.Stanza.TYPE_CHAT) return; + message.stanza.put_node(new StanzaNode.build("markable", NS_URI).add_self_xmlns()); + } +} + +} diff --git a/xmpp-vala/src/module/xep/pixbuf_storage.vala b/xmpp-vala/src/module/xep/pixbuf_storage.vala new file mode 100644 index 00000000..0caf4924 --- /dev/null +++ b/xmpp-vala/src/module/xep/pixbuf_storage.vala @@ -0,0 +1,9 @@ +using Gdk; + +namespace Xmpp.Xep { +public interface PixbufStorage : Object { + public abstract void store(string id, uint8[] data); + public abstract bool has_image(string id); + public abstract Pixbuf? get_image(string id); +} +} \ No newline at end of file diff --git a/xmpp-vala/vapi/uuid.vapi b/xmpp-vala/vapi/uuid.vapi new file mode 100644 index 00000000..991917a7 --- /dev/null +++ b/xmpp-vala/vapi/uuid.vapi @@ -0,0 +1,60 @@ +/* libuuid Vala Bindings + * Copyright 2014 Evan Nemerson + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +[CCode (cheader_filename = "uuid.h", lower_case_cprefix = "uuid_")] +namespace UUID { + [CCode (cname = "int", has_type_id = false)] + public enum Variant { + NCS, + DCE, + MICROSOFT, + OTHER + } + + [CCode (cname = "int", has_type_id = false)] + public enum Type { + DCE_TIME, + DCE_RANDOM + } + + public static void clear ([CCode (array_length = false)] uint8 uu[16]); + public static void copy (uint8 dst[16], uint8 src[16]); + + public static void generate ([CCode (array_length = false)] uint8 @out[16]); + public static void generate_random ([CCode (array_length = false)] uint8 @out[16]); + public static void generate_time ([CCode (array_length = false)] uint8 @out[16]); + public static void generate_time_safe ([CCode (array_length = false)] uint8 @out[16]); + + public static bool is_null ([CCode (array_length = false)] uint8 uu[16]); + + public static int parse (string in, [CCode (array_length = false)] uint8 uu[16]); + + public static void unparse ([CCode (array_length = false)] uint8 uu[16], [CCode (array_length = false)] char @out[37]); + public static void unparse_lower ([CCode (array_length = false)] uint8 uu[16], [CCode (array_length = false)] char @out[37]); + public static void unparse_upper ([CCode (array_length = false)] uint8 uu[16], [CCode (array_length = false)] char @out[37]); + +// public static time_t time ([CCode (array_length = false)] uint8 uu[16], out Posix.timeval ret_tv); + public static UUID.Type type ([CCode (array_length = false)] uint8 uu[16]); + public static UUID.Variant variant ([CCode (array_length = false)] uint8 uu[16]); +} -- cgit v1.2.3-54-g00ecf