aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCedric Bail <cedric.bail@free.fr>2014-03-03 06:11:49 -0300
committerCedric Bail <cedric.bail@free.fr>2014-03-03 06:11:49 -0300
commitfa33725fddf743286da4a18731b78505d93ac831 (patch)
tree613c10f089856938c614d5447a8fb42084ffc3a8
parentfix Alt+X. Adopt the rxvt way. Closes T713 (diff)
downloadterminology-devs/cedric/ssh.tar.gz
control: beginning integration of ssh into terminology.devs/cedric/ssh
-rw-r--r--configure.ac11
-rw-r--r--src/bin/Makefile.am5
-rw-r--r--src/bin/controls.c21
-rw-r--r--src/bin/main.c2
-rw-r--r--src/bin/options.c1
-rw-r--r--src/bin/options_ssh.c408
-rw-r--r--src/bin/options_ssh.h14
7 files changed, 458 insertions, 4 deletions
diff --git a/configure.ac b/configure.ac
index 55e5b5b..35cd2bf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -53,6 +53,16 @@ PKG_CHECK_MODULES([ELDBUS],
[have_eldbus="no"]
)
+PKG_CHECK_MODULES([AVAHI],
+ [avahi-client ecore-avahi],
+ [
+ AC_DEFINE(HAVE_AVAHI, 1, [Avahi support])
+ have_avahi="yes"
+ ],
+ [
+ have_avahi="no"
+ ])
+
AC_CHECK_FUNCS(mkstemps)
EFL_WITH_BIN([edje], [edje-cc], [edje_cc])
@@ -114,4 +124,5 @@ echo " prefix...................: $prefix"
echo
echo "Features:"
echo " dbus................: $have_eldbus"
+echo " avahi...............: $have_avahi"
echo
diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am
index 46ba0e9..f7a34ae 100644
--- a/src/bin/Makefile.am
+++ b/src/bin/Makefile.am
@@ -5,9 +5,9 @@ bin_PROGRAMS = terminology tybg tyalpha typop tyq tycat tyls
terminology_CPPFLAGS = -I. \
-DPACKAGE_BIN_DIR=\"$(bindir)\" -DPACKAGE_LIB_DIR=\"$(libdir)\" \
--DPACKAGE_DATA_DIR=\"$(pkgdatadir)\" @TERMINOLOGY_CFLAGS@
+-DPACKAGE_DATA_DIR=\"$(pkgdatadir)\" @TERMINOLOGY_CFLAGS@ @AVAHI_CFLAGS@
-terminology_LDADD = @TERMINOLOGY_LIBS@ @ELDBUS_LIBS@
+terminology_LDADD = @TERMINOLOGY_LIBS@ @ELDBUS_LIBS@ @AVAHI_LIBS@
terminology_SOURCES = \
private.h \
@@ -20,6 +20,7 @@ keyin.c keyin.h \
main.c main.h \
media.c media.h \
options.c options.h \
+options_ssh.h options_ssh.c \
options_font.c options_font.h \
options_theme.c options_theme.h \
options_themepv.c options_themepv.h \
diff --git a/src/bin/controls.c b/src/bin/controls.c
index 3fb8658..25ab897 100644
--- a/src/bin/controls.c
+++ b/src/bin/controls.c
@@ -8,7 +8,7 @@
#include "main.h"
static Evas_Object *ct_frame = NULL, *ct_boxh = NULL, *ct_box = NULL;
-static Evas_Object *ct_box2 = NULL, *ct_over = NULL;
+static Evas_Object *ct_box2 = NULL, *ct_boxssh = NULL, *ct_over = NULL;
static Eina_Bool ct_out = EINA_FALSE;
static Ecore_Timer *ct_del_timer = NULL;
static Evas_Object *ct_win = NULL, *ct_bg = NULL, *ct_term = NULL;
@@ -240,9 +240,13 @@ controls_toggle(Evas_Object *win, Evas_Object *bg, Evas_Object *term,
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_object_text_set(o, "Controls");
+ ct_boxssh = o = elm_box_add(win);
+ elm_object_content_set(ct_frame, o);
+ evas_object_show(o);
+
ct_boxh = o = elm_box_add(win);
elm_box_horizontal_set(o, EINA_TRUE);
- elm_object_content_set(ct_frame, o);
+ elm_box_pack_end(ct_boxssh, o);
evas_object_show(o);
ct_box2 = o = elm_box_add(win);
@@ -293,6 +297,19 @@ controls_toggle(Evas_Object *win, Evas_Object *bg, Evas_Object *term,
o = _button_add(win, "About", "about", _cb_ct_about, NULL);
elm_box_pack_end(ct_box, o);
+ o = _sep_add_h(win);
+ elm_box_pack_end(ct_boxssh, o);
+
+ o = elm_genlist_add(win);
+ evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_size_hint_request_set(o, 64, 64);
+ elm_genlist_mode_set(o, ELM_LIST_COMPRESS);
+ elm_genlist_homogeneous_set(o, EINA_TRUE);
+ elm_box_pack_end(ct_boxssh, o);
+ evas_object_show(o);
+ options_genlist_add(o);
+
evas_object_event_callback_add(ct_frame, EVAS_CALLBACK_DEL,
_cb_frame_del, NULL);
diff --git a/src/bin/main.c b/src/bin/main.c
index 9ccb4a0..7eb8a25 100644
--- a/src/bin/main.c
+++ b/src/bin/main.c
@@ -2704,6 +2704,7 @@ elm_main(int argc, char **argv)
elm_app_info_set(elm_main, "terminology", "themes/default.edj");
config_init();
+ options_ssh_init();
main_config = config_load("config");
@@ -3058,6 +3059,7 @@ remote:
termpty_shutdown();
config_del(main_config);
+ options_ssh_shutdown();
config_shutdown();
eina_log_domain_unregister(_log_domain);
_log_domain = -1;
diff --git a/src/bin/options.c b/src/bin/options.c
index 6eea718..1ccb022 100644
--- a/src/bin/options.c
+++ b/src/bin/options.c
@@ -10,6 +10,7 @@
#include "options_behavior.h"
#include "options_keys.h"
#include "options_helpers.h"
+#include "options_ssh.h"
#include "config.h"
#include "termio.h"
diff --git a/src/bin/options_ssh.c b/src/bin/options_ssh.c
new file mode 100644
index 0000000..c821182
--- /dev/null
+++ b/src/bin/options_ssh.c
@@ -0,0 +1,408 @@
+#include "private.h"
+
+#include <Elementary.h>
+
+#ifdef HAVE_AVAHI
+# include <Ecore_Avahi.h>
+# include <avahi-client/client.h>
+# include <avahi-client/lookup.h>
+# include <avahi-common/error.h>
+#endif
+
+#include "options_ssh.h"
+
+#define FREE_CLEAN(h, cb) do { if (h) cb(h); h = NULL; } while (0);
+
+typedef struct _SSH_Genlist SSH_Genlist;
+struct _SSH_Genlist
+{
+ Evas_Object *gl;
+ Elm_Object_Item *announced_servers;
+ Elm_Object_Item *ww_servers;
+ Eina_List *items;
+};
+
+struct _SSH_Server
+{
+ const char *domain;
+ const char *name;
+ const char *ip;
+ unsigned int port;
+
+ unsigned char announced : 1;
+};
+
+static Eina_List *announced_servers = NULL;
+static Eina_List *ww_servers = NULL;
+static Eina_List *genlists = NULL;
+
+static Elm_Genlist_Item_Class *itc_group = NULL;
+static Elm_Genlist_Item_Class *itc = NULL;
+
+static void
+_option_genlist_ssh_add(SSH_Genlist *genlist, unsigned char announced, const SSH_Server *server)
+{
+ Elm_Object_Item *parent;
+ Elm_Object_Item *it;
+
+ if (announced)
+ {
+ if (!genlist->announced_servers)
+ {
+ genlist->announced_servers = elm_genlist_item_append(genlist->gl, itc_group,
+ (void *)(uintptr_t)1 /* item data */,
+ NULL, ELM_GENLIST_ITEM_GROUP, NULL, NULL);
+ elm_genlist_item_select_mode_set(genlist->announced_servers, ELM_OBJECT_SELECT_MODE_DISPLAY_ONLY);
+ }
+ parent = genlist->announced_servers;
+ }
+ else
+ {
+ if (!genlist->ww_servers)
+ {
+ genlist->ww_servers = elm_genlist_item_append(genlist->gl, itc_group, NULL,
+ NULL, ELM_GENLIST_ITEM_GROUP, NULL, NULL);
+ elm_genlist_item_select_mode_set(genlist->ww_servers, ELM_OBJECT_SELECT_MODE_DISPLAY_ONLY);
+ }
+ parent = genlist->ww_servers;
+ }
+
+ it = elm_genlist_item_append(genlist->gl, itc, server /* item data */,
+ parent /* parent */,
+ ELM_GENLIST_ITEM_NONE,
+ NULL /* func */, // FIXME: there should the action come
+ NULL);
+
+ genlist->items = eina_list_append(genlist->items, it);
+}
+
+static void
+_option_genlist_ssh_del(SSH_Genlist *genlist, const SSH_Server *server)
+{
+ Elm_Object_Item *it;
+ Eina_List *l, *ll;
+
+ EINA_LIST_FOREACH_SAFE(genlist->items, l, ll, it)
+ if (elm_object_item_data_get(it) == server)
+ {
+ elm_object_item_del(it);
+ genlist->items = eina_list_remove_list(genlist->items, l);
+ }
+}
+
+void
+options_ssh_server_add(unsigned char announced, const char *domain, const char *name, const char *ip, unsigned int port)
+{
+ SSH_Server *srv;
+ SSH_Genlist *genlist;
+ Eina_List* l;
+
+ srv = calloc(1, sizeof (SSH_Server));
+ if (!srv) return ;
+
+ srv->domain = eina_stringshare_add(domain);
+ srv->name = eina_stringshare_add(name);
+ srv->ip = eina_stringshare_add(ip);
+ srv->port = port;
+ srv->announced = announced;
+
+ INF("Avahi discovered new host '%s' (%s:%i) from domain '%s'.",
+ name, ip, port, domain);
+
+ if (announced)
+ announced_servers = eina_list_append(announced_servers, srv);
+ else
+ ww_servers = eina_list_append(ww_servers, srv);
+
+ EINA_LIST_FOREACH(genlists, l, genlist)
+ _option_genlist_ssh_add(genlist, announced, srv);
+}
+
+void
+options_ssh_server_data_del(SSH_Server *server)
+{
+ SSH_Genlist *genlist;
+ Eina_List *l;
+
+ INF("Avahi discovered host disapeared '%s' (%s:%i) from domain '%s'.",
+ server->name, server->ip, server->port, server->domain);
+
+ EINA_LIST_FOREACH(genlists, l, genlist)
+ _option_genlist_ssh_del(genlist, server);
+
+ eina_stringshare_del(server->name);
+ eina_stringshare_del(server->domain);
+ eina_stringshare_del(server->ip);
+
+ if (server->announced)
+ announced_servers = eina_list_remove(announced_servers, server);
+ else
+ ww_servers = eina_list_remove(ww_servers, server);
+
+ free(server);
+}
+
+void
+options_ssh_server_del(unsigned char announced, const char *domain, const char *name)
+{
+ SSH_Server *srv;
+ Eina_List *l;
+
+ domain = eina_stringshare_add(domain);
+ name = eina_stringshare_add(name);
+
+ l = announced ? announced_servers : ww_servers;
+
+ EINA_LIST_FOREACH(l, l, srv)
+ if (srv->name == name &&
+ srv->domain == domain)
+ {
+ options_ssh_server_data_del(srv);
+ break;
+ }
+
+ eina_stringshare_del(domain);
+ eina_stringshare_del(name);
+}
+
+static Eina_Bool
+_options_genlist_death(void *data, Eo *obj EINA_UNUSED,
+ const Eo_Event_Description *desc EINA_UNUSED,
+ void *event_info EINA_UNUSED)
+{
+ SSH_Genlist *genlist = data;
+
+ eina_list_free(genlist->items);
+ free(genlist);
+
+ return EINA_TRUE;
+}
+
+void
+options_genlist_add(Evas_Object *gl)
+{
+ SSH_Genlist *genlist;
+ SSH_Server *srv;
+ Eina_List *l;
+
+ genlist = calloc(1, sizeof(SSH_Genlist));
+ if (!genlist) return ;
+
+ genlist->gl = gl;
+
+ EINA_LIST_FOREACH(announced_servers, l, srv)
+ _option_genlist_ssh_add(genlist, 1, srv);
+ EINA_LIST_FOREACH(ww_servers, l, srv)
+ _option_genlist_ssh_add(genlist, 0, srv);
+
+ eo_do(gl, eo_event_callback_add(EO_EV_DEL, _options_genlist_death, genlist));
+}
+
+static char *
+gl_text_get(void *data, Evas_Object *obj EINA_UNUSED, const char *part EINA_UNUSED)
+{
+ SSH_Server *srv = data;
+ Eina_Strbuf *buf;
+ char *r;
+
+ buf = eina_strbuf_new();
+
+ if (srv->port != 22)
+ eina_strbuf_append_printf(buf, "%s:%i", srv->name, srv->port);
+ else
+ eina_strbuf_append_printf(buf, "%s", srv->name);
+
+ r = eina_strbuf_string_steal(buf);
+ eina_strbuf_free(buf);
+
+ return r;
+}
+
+static char *
+gl_group_get(void *data, Evas_Object *obj EINA_UNUSED, const char *part EINA_UNUSED)
+{
+ if (data) return strdup("Announced server");
+ return strdup("World wide server");
+}
+
+#ifdef HAVE_AVAHI
+static Ecore_Avahi *context = NULL;
+static AvahiClient *client = NULL;
+static AvahiServiceBrowser *sb = NULL;
+static int error = 0;
+
+// FIXME: Check what it does
+static void
+_resolve_callback(AvahiServiceResolver *r,
+ AvahiIfIndex interface EINA_UNUSED,
+ AvahiProtocol protocol EINA_UNUSED,
+ AvahiResolverEvent event,
+ const char *name,
+ const char *type,
+ const char *domain,
+ const char *host_name EINA_UNUSED,
+ const AvahiAddress *address,
+ uint16_t port,
+ AvahiStringList *txt EINA_UNUSED,
+ AvahiLookupResultFlags flags EINA_UNUSED,
+ void* userdata EINA_UNUSED)
+{
+ /* Called whenever a service has been resolved successfully or timed out */
+ switch (event)
+ {
+ case AVAHI_RESOLVER_FAILURE:
+ ERR("Avahi failed to resolve service '%s' of type '%s' in domain '%s': %s\n",
+ name, type, domain,
+ avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r))));
+ break;
+
+ case AVAHI_RESOLVER_FOUND:
+ {
+ char a[AVAHI_ADDRESS_STR_MAX];
+
+ avahi_address_snprint(a, sizeof(a), address);
+ options_ssh_server_add(1, domain, name, a, port);
+ }
+ }
+
+ avahi_service_resolver_free(r);
+}
+
+static void
+_browse_callback(AvahiServiceBrowser *b EINA_UNUSED,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name,
+ const char *type,
+ const char *domain,
+ AvahiLookupResultFlags flags EINA_UNUSED,
+ void* userdata)
+{
+ AvahiClient *c = userdata;
+
+ /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
+ switch (event)
+ {
+ case AVAHI_BROWSER_FAILURE:
+ // FIXME: What to do in case of failure ? Schedule another browser ?
+ break;
+
+ case AVAHI_BROWSER_NEW:
+ /* We ignore the returned resolver object. In the callback
+ function we free it. If the server is terminated before
+ the callback function is called the server will free
+ the resolver for us. */
+
+ avahi_service_resolver_new(c, interface,
+ protocol, name, type, domain,
+ AVAHI_PROTO_UNSPEC, 0, _resolve_callback, c);
+
+ break;
+
+ case AVAHI_BROWSER_REMOVE:
+ options_ssh_server_del(1, domain, name);
+ break;
+
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ /* Nothing to do in our case */
+ break;
+ }
+}
+
+static void
+_client_callback(AvahiClient *c, AvahiClientState state, void * userdata EINA_UNUSED)
+{
+ switch (state)
+ {
+ case AVAHI_CLIENT_S_REGISTERING:
+ case AVAHI_CLIENT_S_COLLISION:
+ case AVAHI_CLIENT_S_RUNNING:
+ if (!sb)
+ {
+ sb = avahi_service_browser_new(c, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_ssh._tcp", NULL, 0, _browse_callback, c);
+ if (!sb)
+ {
+ WRN("Failed to setup an Avahi Service Browser.");
+ return ;
+ }
+ }
+ break;
+
+ case AVAHI_CLIENT_FAILURE:
+ WRN("Server connection failure: %s.", avahi_strerror(avahi_client_errno(c)));
+ if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED)
+ {
+ FREE_CLEAN(sb, avahi_service_browser_free);
+ FREE_CLEAN(client, avahi_client_free);
+
+ client = avahi_client_new(ecore_avahi_poll_get(context), AVAHI_CLIENT_NO_FAIL, _client_callback, NULL, &error);
+ if (!client)
+ {
+ WRN("Unable to setup an AvahiClient context.");
+ return ;
+ }
+ break;
+ }
+
+ case AVAHI_CLIENT_CONNECTING:
+ FREE_CLEAN(sb, avahi_service_browser_free);
+ break;
+ }
+}
+#endif
+
+void
+options_ssh_init(void)
+{
+ if (!itc)
+ {
+ itc = elm_genlist_item_class_new();
+ itc->item_style = "default";
+ itc->func.text_get = gl_text_get;
+ itc->func.content_get = NULL;
+ itc->func.state_get = NULL;
+ itc->func.del = NULL;
+ }
+ if (!itc_group)
+ {
+ itc_group = elm_genlist_item_class_new();
+ itc_group->item_style = "group_index";
+ itc_group->func.text_get = gl_group_get;
+ itc_group->func.content_get = NULL;
+ itc_group->func.state_get = NULL;
+ itc_group->func.del = NULL;
+ }
+
+#ifdef HAVE_AVAHI
+ if (context) return ;
+
+ context = ecore_avahi_add();
+ if (!context)
+ {
+ WRN("Unable to setup an Ecore_Avahi context.");
+ return ;
+ }
+
+ client = avahi_client_new(ecore_avahi_poll_get(context), AVAHI_CLIENT_NO_FAIL, _client_callback, NULL, &error);
+ if (!client)
+ {
+ WRN("Unable to setup an AvahiClient context.");
+ options_ssh_shutdown();
+ return ;
+ }
+#endif
+}
+
+void
+options_ssh_shutdown(void)
+{
+ FREE_CLEAN(itc, elm_genlist_item_class_free);
+ FREE_CLEAN(itc_group, elm_genlist_item_class_free);
+#ifdef HAVE_AVAHI
+ FREE_CLEAN(sb, avahi_service_browser_free);
+ FREE_CLEAN(client, avahi_client_free);
+ FREE_CLEAN(context, ecore_avahi_del);
+#endif
+}
diff --git a/src/bin/options_ssh.h b/src/bin/options_ssh.h
new file mode 100644
index 0000000..c4fab02
--- /dev/null
+++ b/src/bin/options_ssh.h
@@ -0,0 +1,14 @@
+#ifndef _OPTIONS_SSH_H__
+#define _OPTIONS_SSH_H__
+
+typedef struct _SSH_Server SSH_Server;
+
+void options_ssh_server_add(unsigned char local, const char *domain, const char *name, const char *ip, unsigned int port);
+void options_ssh_server_data_del(SSH_Server *server);
+void options_ssh_server_del(unsigned char local, const char *domain, const char *name);
+void options_genlist_add(Evas_Object *gl);
+
+void options_ssh_init(void);
+void options_ssh_shutdown(void);
+
+#endif