+
+void flx_server_add_text_va(
+ flxServer *s,
+ flxEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ flxEntryFlags flags,
+ const gchar *name,
+ va_list va) {
+
+ flxRecord *r;
+
+ g_assert(s);
+
+ r = flx_record_new_full(name ? name : s->hostname, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_TXT);
+ r->data.txt.string_list = flx_string_list_new_va(va);
+ flx_server_add(s, g, interface, protocol, flags, r);
+ flx_record_unref(r);
+}
+
+void flx_server_add_text(
+ flxServer *s,
+ flxEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ flxEntryFlags flags,
+ const gchar *name,
+ ...) {
+
+ va_list va;
+
+ g_assert(s);
+
+ va_start(va, name);
+ flx_server_add_text_va(s, g, interface, protocol, flags, name, va);
+ va_end(va);
+}
+
+static void escape_service_name(gchar *d, guint size, const gchar *s) {
+ g_assert(d);
+ g_assert(size);
+ g_assert(s);
+
+ while (*s && size >= 2) {
+ if (*s == '.' || *s == '\\') {
+ if (size < 3)
+ break;
+
+ *(d++) = '\\';
+ size--;
+ }
+
+ *(d++) = *(s++);
+ size--;
+ }
+
+ g_assert(size > 0);
+ *(d++) = 0;
+}
+
+
+void flx_server_add_service_va(
+ flxServer *s,
+ flxEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ const gchar *type,
+ const gchar *name,
+ const gchar *domain,
+ const gchar *host,
+ guint16 port,
+ va_list va) {
+
+ gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
+ flxRecord *r;
+
+ g_assert(s);
+ g_assert(type);
+ g_assert(name);
+
+ escape_service_name(ename, sizeof(ename), name);
+
+ if (domain) {
+ while (domain[0] == '.')
+ domain++;
+ } else
+ domain = "local";
+
+ if (!host)
+ host = s->hostname;
+
+ snprintf(ptr_name, sizeof(ptr_name), "%s.%s", type, domain);
+ snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, type, domain);
+
+ flx_server_add_ptr(s, g, interface, protocol, FALSE, ptr_name, svc_name);
+
+ r = flx_record_new_full(svc_name, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_SRV);
+ r->data.srv.priority = 0;
+ r->data.srv.weight = 0;
+ r->data.srv.port = port;
+ r->data.srv.name = flx_normalize_name(host);
+ flx_server_add(s, g, interface, protocol, TRUE, r);
+ flx_record_unref(r);
+
+ flx_server_add_text_va(s, g, interface, protocol, FALSE, svc_name, va);
+
+ snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", domain);
+ flx_server_add_ptr(s, g, interface, protocol, FALSE, enum_ptr, ptr_name);
+}
+
+void flx_server_add_service(
+ flxServer *s,
+ flxEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ const gchar *type,
+ const gchar *name,
+ const gchar *domain,
+ const gchar *host,
+ guint16 port,
+ ... ){
+
+ va_list va;
+
+ g_assert(s);
+ g_assert(type);
+ g_assert(name);
+
+ va_start(va, port);
+ flx_server_add_service_va(s, g, interface, protocol, type, name, domain, host, port, va);
+ va_end(va);
+}
+
+static void post_query_callback(flxInterfaceMonitor *m, flxInterface *i, gpointer userdata) {
+ flxKey *k = userdata;
+
+ g_assert(m);
+ g_assert(i);
+ g_assert(k);
+
+ flx_interface_post_query(i, k, FALSE);
+}
+
+void flx_server_post_query(flxServer *s, gint interface, guchar protocol, flxKey *key) {
+ g_assert(s);
+ g_assert(key);
+
+ flx_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
+}
+
+struct tmpdata {
+ flxRecord *record;
+ gboolean flush_cache;
+};
+
+static void post_response_callback(flxInterfaceMonitor *m, flxInterface *i, gpointer userdata) {
+ struct tmpdata *tmpdata = userdata;
+
+ g_assert(m);
+ g_assert(i);
+ g_assert(tmpdata);
+
+ flx_interface_post_response(i, NULL, tmpdata->record, tmpdata->flush_cache, FALSE);
+}
+
+void flx_server_post_response(flxServer *s, gint interface, guchar protocol, flxRecord *record, gboolean flush_cache) {
+ struct tmpdata tmpdata;
+
+ g_assert(s);
+ g_assert(record);
+
+ tmpdata.record = record;
+ tmpdata.flush_cache = flush_cache;
+
+ flx_interface_monitor_walk(s->monitor, interface, protocol, post_response_callback, &tmpdata);
+}
+
+void flx_entry_group_run_callback(flxEntryGroup *g, flxEntryGroupStatus status) {
+ g_assert(g);
+
+ if (g->callback) {
+ g->callback(g->server, g, status, g->userdata);
+ return;
+ }
+
+ if (status == FLX_ENTRY_GROUP_COLLISION)
+ flx_entry_group_free(g);
+
+ /* Ignore the rest */
+}
+
+flxEntryGroup *flx_entry_group_new(flxServer *s, flxEntryGroupCallback callback, gpointer userdata) {
+ flxEntryGroup *g;
+
+ g_assert(s);
+
+ g = g_new(flxEntryGroup, 1);
+ g->server = s;
+ g->callback = callback;
+ g->userdata = userdata;
+ g->dead = FALSE;
+ g->status = FLX_ENTRY_GROUP_UNCOMMITED;
+ g->n_probing = 0;
+ FLX_LLIST_HEAD_INIT(flxEntry, g->entries);
+
+ FLX_LLIST_PREPEND(flxEntryGroup, groups, s->groups, g);
+ return g;
+}
+
+void flx_entry_group_free(flxEntryGroup *g) {
+ g_assert(g);
+ g_assert(g->server);
+
+ g->dead = TRUE;
+ g->server->need_group_cleanup = TRUE;
+}
+
+void flx_entry_group_commit(flxEntryGroup *g) {
+ flxEntry *e;
+
+ g_assert(g);
+ g_assert(!g->dead);
+
+ if (g->status != FLX_ENTRY_GROUP_UNCOMMITED)
+ return;
+
+ flx_entry_group_run_callback(g, g->status = FLX_ENTRY_GROUP_REGISTERING);
+ flx_announce_group(g->server, g);
+ flx_entry_group_check_probed(g, FALSE);
+}
+
+gboolean flx_entry_commited(flxEntry *e) {
+ g_assert(e);
+ g_assert(!e->dead);
+
+ return !e->group ||
+ e->group->status == FLX_ENTRY_GROUP_REGISTERING ||
+ e->group->status == FLX_ENTRY_GROUP_ESTABLISHED;
+}
+
+flxEntryGroupStatus flx_entry_group_get_status(flxEntryGroup *g) {
+ g_assert(g);
+ g_assert(!g->dead);
+
+ return g->status;
+}