/** * chanacs.C: Channel access lists * * 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 chanacs_t::callbacks chanacs_t::callback; /***************** * C H A N A C S * *****************/ /* private destructor for chanacs_t */ static void chanacs_delete (chanacs_t *ca) { node_t *n; return_if_fail (ca != NULL); return_if_fail (ca->mychan != NULL); if (!(runflags & RF_STARTING)) slog (LG_DEBUG, "chanacs_delete(): %s -> %s", ca->mychan->name, ca->myuser != NULL ? ca->myuser->name : ca->host); n = node_find (ca, &ca->mychan->chanacs); node_del (n, &ca->mychan->chanacs); node_free (n); if (ca->myuser != NULL) { n = node_find (ca, &ca->myuser->chanacs); node_del (n, &ca->myuser->chanacs); node_free (n); } ca->clear_metadata (); delete ca; cnt.chanacs--; } /* * chanacs_add(mychan_t *mychan, myuser_t *myuser, unsigned int level, time_t ts) * * Creates an access entry mapping between a user and channel. * * Inputs: * - a channel to create a mapping for * - a user to create a mapping for * - a bitmask which describes the access entry's privileges * - a timestamp for this access entry * * Outputs: * - a chanacs_t object which describes the mapping * * Side Effects: * - the channel access list is updated for mychan. */ chanacs_t * chanacs_add (mychan_t *mychan, myuser_t *myuser, unsigned int level, time_t ts) { chanacs_t *ca; node_t *n1; node_t *n2; return_val_if_fail (mychan != NULL && myuser != NULL, NULL); if (*mychan->name != '#') { slog (LG_DEBUG, "chanacs_add(): got non #channel: %s", mychan->name); return NULL; } if (!(runflags & RF_STARTING)) slog (LG_DEBUG, "chanacs_add(): %s -> %s", mychan->name, myuser->name); n1 = node_create (); n2 = node_create (); ca = new chanacs_t; object_init (asobject (ca), NULL, (destructor_t) chanacs_delete); ca->mychan = mychan; ca->myuser = myuser; ca->level = level & ca_all; ca->ts = ts; node_add (ca, n1, &mychan->chanacs); node_add (ca, n2, &myuser->chanacs); cnt.chanacs++; return ca; } /* * chanacs_add_host(mychan_t *mychan, char *host, unsigned int level, time_t ts) * * Creates an access entry mapping between a hostmask and channel. * * Inputs: * - a channel to create a mapping for * - a hostmask to create a mapping for * - a bitmask which describes the access entry's privileges * - a timestamp for this access entry * * Outputs: * - a chanacs_t object which describes the mapping * * Side Effects: * - the channel access list is updated for mychan. */ chanacs_t * chanacs_add_host (mychan_t *mychan, char const * const host, unsigned int level, time_t ts) { chanacs_t *ca; node_t *n; return_val_if_fail (mychan != NULL && host != NULL, NULL); if (*mychan->name != '#') { slog (LG_DEBUG, "chanacs_add_host(): got non #channel: %s", mychan->name); return NULL; } if (!(runflags & RF_STARTING)) slog (LG_DEBUG, "chanacs_add_host(): %s -> %s", mychan->name, host); n = node_create (); ca = new chanacs_t; object_init (asobject (ca), NULL, (destructor_t) chanacs_delete); ca->mychan = mychan; ca->myuser = NULL; strlcpy (ca->host, host, HOSTLEN); ca->level |= level; ca->ts = ts; node_add (ca, n, &mychan->chanacs); cnt.chanacs++; return ca; } chanacs_t * chanacs_find (mychan_t *mychan, myuser_t const * const myuser, unsigned int level) { node_t *n; chanacs_t *ca; return_val_if_fail (mychan != NULL && myuser != NULL, NULL); LIST_FOREACH (n, mychan->chanacs.head) { ca = static_cast (n->data); if (level != 0x0) { if ((ca->myuser == myuser) && ((ca->level & level) == level)) return ca; } else if (ca->myuser == myuser) return ca; } return NULL; } chanacs_t * chanacs_find_host (mychan_t *mychan, char const * const host, unsigned int level) { node_t *n; chanacs_t *ca; return_val_if_fail (mychan != NULL && host != NULL, NULL); LIST_FOREACH (n, mychan->chanacs.head) { ca = static_cast (n->data); if (level != 0x0) { if ((ca->myuser == NULL) && (!match (ca->host, host)) && ((ca->level & level) == level)) return ca; } else if ((ca->myuser == NULL) && (!match (ca->host, host))) return ca; } return NULL; } unsigned int chanacs_host_flags (mychan_t *mychan, char const * const host) { node_t *n; chanacs_t *ca; unsigned int result = 0; return_val_if_fail (mychan != NULL && host != NULL, 0); LIST_FOREACH (n, mychan->chanacs.head) { ca = (chanacs_t *) n->data; if (ca->myuser == NULL && !match (ca->host, host)) result |= ca->level; } return result; } chanacs_t * chanacs_find_host_literal (mychan_t *mychan, char const * const host, unsigned int level) { node_t *n; chanacs_t *ca; if ((!mychan) || (!host)) return NULL; LIST_FOREACH (n, mychan->chanacs.head) { ca = (chanacs_t *) n->data; if (level != 0x0) { if ((ca->myuser == NULL) && (!strcasecmp (ca->host, host)) && ((ca->level & level) == level)) return ca; } else if ((ca->myuser == NULL) && (!strcasecmp (ca->host, host))) return ca; } return NULL; } chanacs_t * chanacs_find_host_by_user (mychan_t *mychan, user_t *u, unsigned int level) { node_t *n; chanacs_t *ca; return_val_if_fail (mychan != NULL && u != NULL, 0); for (n = phandler->next_matching_host_chanacs (mychan, u, mychan->chanacs.head); n != NULL; n = phandler->next_matching_host_chanacs (mychan, u, n->next)) { ca = static_cast (n->data); if ((ca->level & level) == level) return ca; } return NULL; } unsigned int chanacs_host_flags_by_user (mychan_t *mychan, user_t *u) { node_t *n; unsigned int result = 0; chanacs_t *ca; return_val_if_fail (mychan != NULL && u != NULL, 0); for (n = phandler->next_matching_host_chanacs (mychan, u, mychan->chanacs.head); n != NULL; n = phandler->next_matching_host_chanacs (mychan, u, n->next)) { ca = static_cast (n->data); result |= ca->level; } return result; } chanacs_t * chanacs_find_by_mask (mychan_t *mychan, char const * const mask, unsigned int level) { myuser_t *mu; chanacs_t *ca; return_val_if_fail (mychan != NULL && mask != NULL, NULL); mu = myuser_t::find (mask); if (mu != NULL) { ca = chanacs_find (mychan, mu, level); if (ca) return ca; } return chanacs_find_host_literal (mychan, mask, level); } bool chanacs_user_has_flag (mychan_t *mychan, user_t *u, unsigned int level) { myuser_t *mu; return_val_if_fail (mychan != NULL && u != NULL, false); mu = u->myuser; if (mu != NULL) { if (chanacs_find (mychan, mu, level)) return true; } if (chanacs_find_host_by_user (mychan, u, level)) return true; return false; } unsigned int chanacs_user_flags (mychan_t *mychan, user_t *u) { myuser_t *mu; chanacs_t *ca; unsigned int result = 0; return_val_if_fail (mychan != NULL && u != NULL, 0); mu = u->myuser; if (mu != NULL) { ca = chanacs_find (mychan, mu, 0); if (ca != NULL) result |= ca->level; } result |= chanacs_host_flags_by_user (mychan, u); return result; } bool chanacs_source_has_flag (mychan_t *mychan, sourceinfo_t *si, unsigned int level) { return si->su != NULL ? chanacs_user_has_flag (mychan, si->su, level) : chanacs_find (mychan, si->smu, level) != NULL; } unsigned int chanacs_source_flags (mychan_t *mychan, sourceinfo_t *si) { chanacs_t *ca; if (si->su != NULL) { return chanacs_user_flags (mychan, si->su); } else { ca = chanacs_find (mychan, si->smu, 0); return ca != NULL ? ca->level : 0; } } /* Look for the chanacs exactly matching mu or host (exactly one of mu and * host must be non-NULL). If not found, and create is true, create a new * chanacs with no flags. */ chanacs_t * chanacs_open (mychan_t *mychan, myuser_t *mu, char const * const hostmask, bool create) { chanacs_t *ca; /* wrt the second assert: only one of mu or hostmask can be not-NULL --nenolod */ return_val_if_fail (mychan != NULL, false); return_val_if_fail ((mu != NULL && hostmask == NULL) || (mu == NULL && hostmask != NULL), false); if (mu != NULL) { ca = chanacs_find (mychan, mu, 0); if (ca != NULL) return ca; else if (create) return chanacs_add (mychan, mu, 0, NOW); } else { ca = chanacs_find_host_literal (mychan, hostmask, 0); if (ca != NULL) return ca; else if (create) return chanacs_add_host (mychan, hostmask, 0, NOW); } return NULL; } /* Destroy a chanacs if it has no flags */ void chanacs_close (chanacs_t *ca) { if (ca->level == 0) object_unref (ca); } /* Call this with a chanacs_t with level==0 */ bool chanacs_is_table_full (chanacs_t *ca) { return chansvs.maxchanacs > 0 && LIST_LENGTH (&ca->mychan->chanacs) > chansvs.maxchanacs; } /* Change channel access * * Either mu or hostmask must be specified. * Add the flags in *addflags and remove the flags in *removeflags, updating * these to reflect the actual change. Only allow changes to restrictflags. * Returns true if successful, false if an unallowed change was attempted. * -- jilles */ bool chanacs_modify (chanacs_t *ca, unsigned int *addflags, unsigned int *removeflags, unsigned int restrictflags) { return_val_if_fail (ca != NULL, false); return_val_if_fail (addflags != NULL && removeflags != NULL, false); *addflags &= ~ca->level; *removeflags &= ca->level & ~*addflags; /* no change? */ if ((*addflags | *removeflags) == 0) return true; /* attempting to add bad flag? */ if (~restrictflags & *addflags) return false; /* attempting to remove bad flag? */ if (~restrictflags & *removeflags) return false; /* attempting to manipulate user with more privs? */ if (~restrictflags & ca->level) return false; ca->level = (ca->level | *addflags) & ~*removeflags; ca->ts = NOW; return true; } /* version that doesn't return the changes made */ bool chanacs_modify_simple (chanacs_t *ca, unsigned int addflags, unsigned int removeflags) { unsigned int a, r; a = addflags & ca_all; r = removeflags & ca_all; return chanacs_modify (ca, &a, &r, ca_all); } /* Change channel access * * Either mu or hostmask must be specified. * Add the flags in *addflags and remove the flags in *removeflags, updating * these to reflect the actual change. Only allow changes to restrictflags. * Returns true if successful, false if an unallowed change was attempted. * -- jilles */ bool chanacs_change (mychan_t *mychan, myuser_t *mu, char const * const hostmask, unsigned int *addflags, unsigned int *removeflags, unsigned int restrictflags) { chanacs_t *ca; /* wrt the second assert: only one of mu or hostmask can be not-NULL --nenolod */ return_val_if_fail (mychan != NULL, false); return_val_if_fail ((mu != NULL && hostmask == NULL) || (mu == NULL && hostmask != NULL), false); return_val_if_fail (addflags != NULL && removeflags != NULL, false); if (mu != NULL) { ca = chanacs_find (mychan, mu, 0); if (ca == NULL) { *removeflags = 0; /* no change? */ if ((*addflags | *removeflags) == 0) return true; /* attempting to add bad flag? */ if (~restrictflags & *addflags) return false; chanacs_add (mychan, mu, *addflags, NOW); } else { *addflags &= ~ca->level; *removeflags &= ca->level & ~*addflags; /* no change? */ if ((*addflags | *removeflags) == 0) return true; /* attempting to add bad flag? */ if (~restrictflags & *addflags) return false; /* attempting to remove bad flag? */ if (~restrictflags & *removeflags) return false; /* attempting to manipulate user with more privs? */ if (~restrictflags & ca->level) return false; ca->level = (ca->level | *addflags) & ~*removeflags; ca->ts = NOW; if (ca->level == 0) object_unref (ca); } } else /* hostmask != NULL */ { ca = chanacs_find_host_literal (mychan, hostmask, 0); if (ca == NULL) { *removeflags = 0; /* no change? */ if ((*addflags | *removeflags) == 0) return true; /* attempting to add bad flag? */ if (~restrictflags & *addflags) return false; chanacs_add_host (mychan, hostmask, *addflags, NOW); } else { *addflags &= ~ca->level; *removeflags &= ca->level & ~*addflags; /* no change? */ if ((*addflags | *removeflags) == 0) return true; /* attempting to add bad flag? */ if (~restrictflags & *addflags) return false; /* attempting to remove bad flag? */ if (~restrictflags & *removeflags) return false; /* attempting to manipulate user with more privs? */ if (~restrictflags & ca->level) return false; ca->level = (ca->level | *addflags) & ~*removeflags; ca->ts = NOW; if (ca->level == 0) object_unref (ca); } } return true; } /* version that doesn't return the changes made */ bool chanacs_change_simple (mychan_t *mychan, myuser_t *mu, char const * const hostmask, unsigned int addflags, unsigned int removeflags) { unsigned int a, r; a = addflags & ca_all; r = removeflags & ca_all; return chanacs_change (mychan, mu, hostmask, &a, &r, ca_all); }