summaryrefslogtreecommitdiff
path: root/src/hub/nmdc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/hub/nmdc.c')
-rw-r--r--src/hub/nmdc.c169
1 files changed, 112 insertions, 57 deletions
diff --git a/src/hub/nmdc.c b/src/hub/nmdc.c
index 61abf1d..06ca7f3 100644
--- a/src/hub/nmdc.c
+++ b/src/hub/nmdc.c
@@ -38,27 +38,34 @@
} while(0)
-static int64_t hub_nmdc_userid(hub_t *h, const char *nick, int len) {
- return len == (int)strlen(h->n.nick) && strncmp(h->n.nick, nick, len) == 0 ? 0 : hub_users_genid(h, nick, len);
+
+__KHASH_IMPL(nmdcdefer, static kh_inline, int64_t, hub_nmdc_defer_t, 1, kh_int64_hash_func, kh_int64_hash_equal);
+
+
+static hub_nmdc_defer_t *hub_nmdc_defer_del(hub_t *h, int64_t uid) {
+ khiter_t k = kh_get(nmdcdefer, h->n.defer, uid);
+ if(k == kh_end(h->n.defer))
+ return NULL;
+ /* XXX: The value is used after deleting it from the hash table. This
+ * relies on khash behaviour: It won't shrink the table on delete, nor will
+ * it modify deleted values until their slots are being re-used. */
+ kh_del(nmdcdefer, h->n.defer, k);
+ return &kh_value(h->n.defer, k);
}
-/* Initializes a user_update_t for a specific user name, and returns its uid. */
-static int64_t hub_nmdc_update_init(hub_t *h, hub_user_update_t *d, const char *nick, int len) {
- int64_t uid = hub_nmdc_userid(h, nick, len);
- hub_user_update_init(h, d, uid);
- if(!d->u->nick) {
- char *unick = nmdc_convert("UTF-8", hub_hub_encoding(d->h), nick, len);
- d->u->nick = unick;
- if(strcmp(unick, nick) != 0) {
- d->u->n.nick = malloc(len+1);
- memcpy(d->u->n.nick, nick, len);
- d->u->n.nick[len] = 0;
- }
- hub_user_update_field(d, HUBU_NICK);
- }
+static hub_nmdc_defer_t *hub_nmdc_defer_put(hub_t *h, int64_t uid) {
+ int r;
+ khiter_t k = kh_put(nmdcdefer, h->n.defer, uid, &r);
+ hub_nmdc_defer_t *def = &kh_value(h->n.defer, k);
+ if(r != 0)
+ memset(def, 0, sizeof(hub_nmdc_defer_t));
+ return def;
+}
+
- return uid;
+static int64_t hub_nmdc_userid(hub_t *h, const char *nick, int len) {
+ return len == (int)strlen(h->n.nick) && strncmp(h->n.nick, nick, len) == 0 ? 0 : hub_users_genid(h, nick, len);
}
@@ -161,6 +168,33 @@ static void hub_nmdc_myinfo_update(nmdc_myinfo_t *nfo, hub_user_update_t *d) {
}
+static void hub_nmdc_myinfo_initnewuser(nmdc_myinfo_t *nfo, hub_user_update_t *d, int64_t uid) {
+ d->u->id = uid;
+
+ char *unick = nmdc_convert("UTF-8", hub_hub_encoding(d->h), nfo->nick, strlen(nfo->nick));
+ d->u->nick = unick;
+ if(strcmp(unick, nfo->nick) != 0) {
+ d->u->n.nick = malloc(strlen(nfo->nick)+1);
+ strcpy(d->u->n.nick, nfo->nick);
+ }
+ hub_user_update_field(d, HUBU_NICK);
+
+ /* If we have some deferred info, we can merge it now */
+ hub_nmdc_defer_t *def = hub_nmdc_defer_del(d->h, d->u->id);
+ if(!def)
+ return;
+ if(def->op)
+ hub_user_update_field(d, HUBU_FLAGS);
+ if(!net_ip4_isany(def->ip4))
+ hub_user_update_field(d, HUBU_IP4);
+ if(!net_ip6_isany(def->ip6))
+ hub_user_update_field(d, HUBU_IP6);
+ d->u->op = def->op;
+ d->u->ip4 = def->ip4;
+ d->u->ip6 = def->ip6;
+}
+
+
static void hub_nmdc_myinfo(hub_t *h, const char *cmd, int len) {
nmdc_myinfo_t nfo;
if(!nmdc_myinfo_parse(&nfo, hub_hub_encoding(h), cmd)) {
@@ -168,31 +202,20 @@ static void hub_nmdc_myinfo(hub_t *h, const char *cmd, int len) {
ywarn("%d: Invalid info format: %.*s", h->id, len, cmd);
return;
}
- hub_user_update_t d;
- hub_nmdc_update_init(h, &d, nfo.nick, strlen(nfo.nick));
- bool newuser = d.u->id < 0;
- hub_nmdc_myinfo_update(&nfo, &d);
- nmdc_myinfo_free(&nfo);
+ hub_user_update_t d[1];
+ int64_t uid = hub_nmdc_userid(h, nfo.nick, strlen(nfo.nick));
+ hub_user_t *u = hub_users_get(h, uid);
+ bool newuser = !u || !u->online;
+ if(newuser)
+ u = hub_user_new(false);
+ hub_user_update_init(h, d, u);
- /* If we hadn't received a $MyINFO from this user before, make sure that
- * the users' IP address (obtained from $UserIP) and op flag (obtained from
- * $OpList) are still set in this update. This ensures that they are
- * included in the UserJoined signal. */
- if(!d.u->online) {
- if(d.u->op)
- hub_user_update_field(&d, HUBU_FLAGS);
- if(!net_ip4_isany(d.u->ip4))
- hub_user_update_field(&d, HUBU_IP4);
- if(!net_ip6_isany(d.u->ip6))
- hub_user_update_field(&d, HUBU_IP6);
- d.u->online = true;
- }
+ if(newuser)
+ hub_nmdc_myinfo_initnewuser(&nfo, d, uid);
+ hub_nmdc_myinfo_update(&nfo, d);
- /* This function would fail if there are two users with the same nick (in
- * which case the above _genid() would consider them the same user, thus
- * can't happen), or if no nick was set in the update process (can't happen
- * either, the $MyINFO parser requires a nick to be present). */
- assert(hub_user_update_done(&d));
+ hub_user_update_done(d, newuser);
+ nmdc_myinfo_free(&nfo);
/* User list completion detection: If this is a new user, reset the
* timeout. If this is an update to a user already in the list, then we
@@ -208,6 +231,7 @@ static void hub_nmdc_myinfo(hub_t *h, const char *cmd, int len) {
static void hub_nmdc_quit_reset(hub_t *h) {
if(h->n.quituser != -1) {
+ hub_nmdc_defer_del(h, h->n.quituser);
if(!hub_user_left(h, h->n.quituser, h->n.quitinitiator, h->n.quitmessage ? h->n.quitmessage : ""))
ywarn("%d: Quit for user not on the hub: %"PRIi64, h->id, h->n.quituser);
h->n.quituser = -1;
@@ -300,6 +324,28 @@ static void hub_nmdc_to(hub_t *h, const char *cmd, int len) {
}
+static void hub_nmdc_userip_set(hub_t *h, int64_t uid, const char *ip) {
+ hub_user_t *u = hub_users_get(h, uid);
+
+ /* User is online, modify struct directly */
+ if(u) {
+ hub_user_update_t d;
+ hub_user_update_init(h, &d, u);
+ if(inet_pton(AF_INET, ip, &d.u->ip4) == 1)
+ hub_user_update_field(&d, HUBU_IP4);
+ else if(inet_pton(AF_INET6, ip, &d.u->ip6) == 1)
+ hub_user_update_field(&d, HUBU_IP6);
+ hub_user_update_done(&d, false);
+ return;
+ }
+
+ /* Otherwise, defer this information */
+ hub_nmdc_defer_t *def = hub_nmdc_defer_put(h, uid);
+ if(inet_pton(AF_INET, ip, &def->ip4) != 1)
+ inet_pton(AF_INET6, ip, &def->ip6);
+}
+
+
static void hub_nmdc_userip(hub_t *h, const char *cmd, int len) {
const char *lst = cmd + sizeof "$UserIP";
char buf[40];
@@ -319,17 +365,31 @@ static void hub_nmdc_userip(hub_t *h, const char *cmd, int len) {
int l = end-sep-1 > (int)sizeof(buf)-1 ? (int)sizeof(buf)-1 : end-sep-1;
strncpy(buf, sep+1, l);
buf[l] = 0;
+ hub_nmdc_userip_set(h, hub_nmdc_userid(h, lst, sep-lst), buf);
+ lst = end + (*end == '|' ? 0 : 2);
+ }
+}
+
+static void hub_nmdc_oplist_set(hub_t *h, int64_t uid) {
+ /* If *we* are OP, update our hub counts */
+ if(uid == 0)
+ hub_manager_settype(h, HUBM_HOP);
+
+ /* User is online, modify struct directly */
+ hub_user_t *u = hub_users_get(h, uid);
+ if(u) {
hub_user_update_t d;
- hub_nmdc_update_init(h, &d, lst, sep-lst);
- /* XXX: Does this modify ip4 if it's not a valid IPv6 address? */
- if(inet_pton(AF_INET, buf, &d.u->ip4) == 1)
- hub_user_update_field(&d, HUBU_IP4);
- else if(inet_pton(AF_INET6, buf, &d.u->ip6) == 1)
- hub_user_update_field(&d, HUBU_IP6);
- assert(hub_user_update_done(&d));
- lst = end + (*end == '|' ? 0 : 2);
+ hub_user_update_init(h, &d, u);
+ if(!d.u->op)
+ hub_user_update_field(&d, HUBU_FLAGS);
+ d.u->op = true;
+ hub_user_update_done(&d, false);
+ return;
}
+
+ /* Otherwise, defer this information */
+ hub_nmdc_defer_put(h, uid)->op = true;
}
@@ -348,15 +408,7 @@ static void hub_nmdc_oplist(hub_t *h, const char *cmd, int len) {
nickend += *nickend == '|' ? 0 : 2;
continue;
}
-
- hub_user_update_t d;
- if(!hub_nmdc_update_init(h, &d, lst, nickend-lst))
- hub_manager_settype(h, HUBM_HOP);
- if(d.u->op != true) {
- d.u->op = true;
- hub_user_update_field(&d, HUBU_FLAGS);
- }
- assert(hub_user_update_done(&d));
+ hub_nmdc_oplist_set(h, hub_nmdc_userid(h, lst, nickend-lst));
lst = nickend + (*nickend == '|' ? 0 : 2);
}
}
@@ -634,6 +686,8 @@ static void hub_nmdc_connected(hub_t *h) {
h->n.lastinfo = NULL;
h->n.nick = nmdc_convert(hub_hub_encoding(h), "UTF-8", hub_hub_mynick(h), -1);
+ h->n.defer = kh_init(nmdcdefer);
+
ev_init(&h->n.listtimer, hub_nmdc_userlist_timeout);
h->n.listtimer.data = h;
@@ -649,6 +703,7 @@ static void hub_nmdc_disconnected(hub_t *h) {
free(h->n.nick);
free(h->n.lastinfo);
free(h->n.quitmessage);
+ kh_destroy(nmdcdefer, h->n.defer);
hub_manager_settype(h, HUBM_HNONE);
ev_timer_stop(EV_DEFAULT_UC_ &h->n.listtimer);
ev_timer_stop(EV_DEFAULT_UC_ &h->n.quittimer);