/** * myuser.C: Account management * * Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team * Rights to this code are as documented in COPYING. * * * Portions of this file were derived from sources bearing the following license: * Rights to this code are documented in doc/pod/license.pod. * Copyright © 2005-2007 Atheme Project (http://www.atheme.org) */ #include "atheme.h" #include #include #include #include #include #include #include #include "authcookie.h" myuser_t::map_type myuser_t::map; myuser_t::callbacks myuser_t::callback; /* * myuser_t::create(char *name, char *pass, char *email, unsigned int flags) * * Creates an account and adds it to the accounts DTree. * * Inputs: * - an account name * - an account password * - an email to associate with the account or NONE * - flags for the account * * Outputs: * - on success, a new myuser_t object (account) * - on failure, NULL. * * Side Effects: * - the created account is added to the accounts DTree, * this may be undesirable for a factory. * * Caveats: * - if nicksvs.no_nick_ownership is not enabled, the caller is * responsible for adding a nick with the same name */ myuser_t * myuser_t::create (char const * const name, char const * const pass, char const * const email, unsigned int flags) { myuser_t *mu; soper_t *soper; return_val_if_fail ((mu = myuser_t::find (name)) == NULL, mu); if (!(runflags & RF_STARTING)) slog (LG_DEBUG, "myuser_t::create(): %s -> %s", name, email); mu = new myuser_t; strlcpy (mu->name, name, NICKLEN); strlcpy (mu->email, email, EMAILLEN); mu->registered = NOW; mu->flags = flags; /* If it's already crypted, don't touch the password. Otherwise, * use set_password() to initialize it. Why? Because set_password * will move the user to encrypted passwords if possible. That way, * new registers are immediately protected and the database is * immediately converted the first time we start up with crypto. */ if (flags & MU_CRYPTPASS) strlcpy (mu->pass, pass, NICKLEN); else mu->set_password (pass); myuser_t::map[mu->name] = mu; if ((soper = soper_find_named (mu->name)) != NULL) { slog (LG_DEBUG, "myuser_t::create(): user `%s' has been declared as soper, activating privileges.", mu->name); soper->myuser = mu; mu->soper = soper; } cnt.myuser++; return mu; } /* * myuser_t::_destroy(myuser_t *mu) * * Destroys and removes an account from the accounts DTree. * * Inputs: * - account to destroy * * Outputs: * - nothing * * Side Effects: * - an account is destroyed and removed from the accounts DTree. */ void myuser_t::_destroy () { myuser_t *successor; mychan_t *mc; user_t *u; node_t *n, *tn; chanacs_t *ca; if (!(runflags & RF_STARTING)) slog (LG_DEBUG, "myuser_t::_destroy(): %s", name); /* log them out */ while (!logins.empty ()) { u = logins.back (); if (!authservice_loaded || !phandler->ircd_on_logout (u->nick, name, NULL)) { u->myuser = NULL; logins.erase (u); } } /* kill all their channels and chanacs */ LIST_FOREACH_SAFE (n, tn, chanacs.head) { ca = static_cast (n->data); mc = ca->mychan; /* attempt succession */ if (ca->level & CA_FOUNDER && mc->num_founders () == 1 && (successor = mc->pick_successor ()) != NULL) { snoop (_("SUCCESSION: \2%s\2 -> \2%s\2 from \2%s\2"), successor->name, mc->name, name); slog (LG_REGISTER, "myuser_t::_destroy(): giving channel %s to %s (unused %ds, founder %s, chanacs %d)", mc->name, successor->name, NOW - mc->used, name, LIST_LENGTH (&mc->chanacs)); if (chansvs.me != NULL) verbose (mc, "Foundership changed to \2%s\2 because \2%s\2 was dropped.", successor->name, mc->founder->name); chanacs_change_simple (mc, successor, NULL, CA_FOUNDER_0, 0); if (chansvs.me != NULL) successor->notice (chansvs.nick, "You are now founder on \2%s\2 (as \2%s\2).", mc->name, successor->name); object_unref (ca); } /* no successor found */ else if (ca->level & CA_FOUNDER && mc->num_founders () == 1) { snoop (_("DELETE: \2%s\2 from \2%s\2"), mc->name, name); slog (LG_REGISTER, "myuser_t::_destroy(): deleting channel %s (unused %ds, founder %s, chanacs %d)", mc->name, NOW - mc->used, name, LIST_LENGTH (&mc->chanacs)); mc->callback.drop (mc); if ((config_options.chan && irccasecmp (mc->name, config_options.chan)) || !config_options.chan) part (mc->name, chansvs.nick); object_unref (mc); } else /* not founder */ object_unref (ca); } /* remove them from the soper list */ if (soper_find (this)) soper_delete (soper); /* delete the metadata */ clear_metadata (); /* kill any authcookies */ authcookie_destroy_all (this); /* delete memos */ while (!memos.empty ()) { delete memos.back (); memos.pop_back (); } /* delete access entries */ LIST_FOREACH_SAFE (n, tn, access_list.head) { access_delete ((char *) n->data); } /* delete their nicks */ LIST_FOREACH_SAFE (n, tn, nicks.head) { static_cast (n->data)->refcnt_dec (); } /* name is the index for this dtree */ myuser_t::map.erase (name); delete this; cnt.myuser--; } /* * myuser_t::find(char const * const name) * * Retrieves an account from the accounts DTree. * * Inputs: * - account name to retrieve * * Outputs: * - account wanted or NULL if it's not in the DTree. * * Side Effects: * - none */ myuser_t * myuser_t::find (char const * const name) { myuser_t::map_type::iterator it; if ((it = myuser_t::map.find (name)) != myuser_t::map.end ()) return it->second; return NULL; } /* * myuser_t::find_ext(char const * const name) * * Same as myuser_t::find() but with nick group support and undernet-style * `=nick' expansion. * * Inputs: * - account name/nick to retrieve or =nick notation for wanted account. * * Outputs: * - account wanted or NULL if it's not in the DTree. * * Side Effects: * - none */ myuser_t * myuser_t::find_ext (char const * const name) { user_t *u; mynick_t *mn; if (name == NULL) return NULL; if (*name == '=') { u = user_find_named (name + 1); return u != NULL ? u->myuser : NULL; } else if (nicksvs.no_nick_ownership) return myuser_t::find (name); else { mn = mynick_t::find (name); return mn != NULL ? mn->owner : NULL; } } /* * myuser_notice(char *from, myuser_t *target, char *fmt, ...) * * Sends a notice to all users logged into an account. * * Inputs: * - source of message * - target account * - format of message * - zero+ arguments for the formatter * * Outputs: * - nothing * * Side Effects: * - a notice is sent to all users logged into the account. */ void myuser_t::notice (char const * const from, char const * const fmt, ...) const { va_list ap; myuser_t::login_vector::const_iterator it = logins.begin (); myuser_t::login_vector::const_iterator et = logins.end (); va_start (ap, fmt); while (it != et) { notice (from, (*it)->nick, fmt, ap); ++it; } va_end (ap); } unsigned myuser_t::num_channels () { node_t *n; chanacs_t *ca; int count = 0; LIST_FOREACH (n, chanacs.head) { ca = static_cast (n->data); if (ca->level & CA_FOUNDER) count++; } return count; } /* * myuser_access_verify() * * Inputs: * - user to verify, account to verify against * * Outputs: * - true if user matches an accesslist entry * - false otherwise * * Side Effects: * - last login time updated if user matches */ bool myuser_t::access_verify (user_t *u) { node_t *n; char buf[USERLEN + HOSTLEN]; char buf2[USERLEN + HOSTLEN]; char buf3[USERLEN + HOSTLEN]; if (u == NULL) { slog (LG_DEBUG, "myuser_t::access_verify(): invalid parameters: u = %p", u); return false; } if (!use_myuser_access) return false; snprintf (buf, sizeof buf, "%s@%s", u->user, u->vhost); snprintf (buf2, sizeof buf2, "%s@%s", u->user, u->host); snprintf (buf3, sizeof buf3, "%s@%s", u->user, u->ip); LIST_FOREACH (n, access_list.head) { char *entry = (char *) n->data; if (!match (entry, buf) || !match (entry, buf2) || !match (entry, buf3) || !match_cidr (entry, buf3)) { lastlogin = NOW; return true; } } return false; } /* * myuser_access_add() * * Inputs: * - account to attach access mask to, access mask itself * * Outputs: * - false: me.mdlimit is reached (too many access entries) * - true : success * * Side Effects: * - an access mask is added to an account. */ bool myuser_t::access_add (char const * const mask) { node_t *n; char *msk; if (mask == NULL) { slog (LG_DEBUG, "myuser_t::access_add(): invalid parameters: mask = %p", mask); return false; } if (LIST_LENGTH (&access_list) > me.mdlimit) { slog (LG_DEBUG, "myuser_t::access_add(): access entry limit reached for %s", name); return false; } msk = sstrdup (mask); n = node_create (); node_add (msk, n, &access_list); cnt.myuser_access++; return true; } /* * myuser_access_find() * * Inputs: * - account to find access mask in, access mask * * Outputs: * - pointer to found access mask or NULL if not found * * Side Effects: * - none */ char * myuser_t::access_find (char const * const mask) { node_t *n; if (mask == NULL) { slog (LG_DEBUG, "myuser_t::access_find(): invalid parameters: mask = %p", mask); return NULL; } LIST_FOREACH (n, access_list.head) { char *entry = (char *) n->data; if (!strcasecmp (entry, mask)) return entry; } return NULL; } /* * myuser_access_delete() * * Inputs: * - account to delete access mask from, access mask itself * * Outputs: * - none * * Side Effects: * - an access mask is added to an account. */ void myuser_t::access_delete (char const * const mask) { node_t *n, *tn; if (mask == NULL) { slog (LG_DEBUG, "myuser_t::access_delete(): invalid parameters: mask = %p", mask); return; } LIST_FOREACH_SAFE (n, tn, access_list.head) { char *entry = (char *) n->data; if (!strcasecmp (entry, mask)) { node_del (n, &access_list); node_free (n); sfree (entry); cnt.myuser_access--; return; } } } // frees memory used by myusers on shutdown void myuser_t::cleanup () { myuser_t::map_type::iterator it = myuser_t::map.begin (); myuser_t::map_type::iterator et = myuser_t::map.end (); while (it != et) { myuser_t *mu = it->second; delete mu; ++it; } }