diff options
Diffstat (limited to 'src/hub/nmdc.c')
-rw-r--r-- | src/hub/nmdc.c | 169 |
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); |