+ assert( Name != NULL );
+
+#ifdef STRICT_RFC
+ if (strlen(Name) <= 1)
+ return false;
+#endif
+ if (strchr("#&+", Name[0]) == NULL)
+ return false;
+ if (strlen(Name) >= CHANNEL_NAME_LEN)
+ return false;
+
+ return Name[strcspn(Name, " ,:\007")] == 0;
+} /* Channel_IsValidName */
+
+
+GLOBAL bool
+Channel_ModeAdd( CHANNEL *Chan, char Mode )
+{
+ /* set Mode.
+ * If the channel already had this mode, return false.
+ * If the channel mode was newly set return true.
+ */
+
+ char x[2];
+
+ assert( Chan != NULL );
+
+ x[0] = Mode; x[1] = '\0';
+ if( ! Channel_HasMode( Chan, x[0] ))
+ {
+ /* Channel does not have this mode yet, set it */
+ strlcat( Chan->modes, x, sizeof( Chan->modes ));
+ return true;
+ }
+ else return false;
+} /* Channel_ModeAdd */
+
+
+GLOBAL bool
+Channel_ModeDel( CHANNEL *Chan, char Mode )
+{
+ /* Delete mode.
+ * if the mode was removed return true.
+ * if the channel did not have the mode, return false.
+ */
+ char *p;
+
+ assert( Chan != NULL );
+
+ p = strchr( Chan->modes, Mode );
+ if( ! p ) return false;
+
+ /* Channel has mode -> delete */
+ while( *p )
+ {
+ *p = *(p + 1);
+ p++;
+ }
+ return true;
+} /* Channel_ModeDel */
+
+
+GLOBAL bool
+Channel_UserModeAdd( CHANNEL *Chan, CLIENT *Client, char Mode )
+{
+ /* Set Channel-User-Mode.
+ * if mode was newly set, return true.
+ * if the User already had this channel-mode, return false.
+ */
+
+ CL2CHAN *cl2chan;
+ char x[2];
+
+ assert( Chan != NULL );
+ assert( Client != NULL );
+
+ cl2chan = Get_Cl2Chan( Chan, Client );
+ assert( cl2chan != NULL );
+
+ x[0] = Mode; x[1] = '\0';
+ if( ! strchr( cl2chan->modes, x[0] ))
+ {
+ /* mode not set, -> set it */
+ strlcat( cl2chan->modes, x, sizeof( cl2chan->modes ));
+ return true;
+ }
+ else return false;
+} /* Channel_UserModeAdd */
+
+
+GLOBAL bool
+Channel_UserModeDel( CHANNEL *Chan, CLIENT *Client, char Mode )
+{
+ /* Delete Channel-User-Mode.
+ * If Mode was removed, return true.
+ * If User did not have the Channel-Mode, return false.
+ */
+
+ CL2CHAN *cl2chan;
+ char *p;
+
+ assert( Chan != NULL );
+ assert( Client != NULL );
+
+ cl2chan = Get_Cl2Chan( Chan, Client );
+ assert( cl2chan != NULL );
+
+ p = strchr( cl2chan->modes, Mode );
+ if( ! p ) return false;
+
+ /* Client has Mode -> delete */
+ while( *p )
+ {
+ *p = *(p + 1);
+ p++;
+ }
+ return true;
+} /* Channel_UserModeDel */
+
+
+GLOBAL char *
+Channel_UserModes( CHANNEL *Chan, CLIENT *Client )
+{
+ /* return Users' Channel-Modes */
+
+ CL2CHAN *cl2chan;
+
+ assert( Chan != NULL );
+ assert( Client != NULL );
+
+ cl2chan = Get_Cl2Chan( Chan, Client );
+ assert( cl2chan != NULL );
+
+ return cl2chan->modes;
+} /* Channel_UserModes */
+
+
+GLOBAL bool
+Channel_UserHasMode( CHANNEL *Chan, CLIENT *Client, char Mode )
+{
+ return strchr(Channel_UserModes(Chan, Client), Mode) != NULL;
+} /* Channel_UserHasMode */
+
+
+GLOBAL bool
+Channel_IsMemberOf( CHANNEL *Chan, CLIENT *Client )
+{
+ /* Test if Client is on Channel Chan */
+
+ assert( Chan != NULL );
+ assert( Client != NULL );
+ return Get_Cl2Chan(Chan, Client) != NULL;
+} /* Channel_IsMemberOf */
+
+
+GLOBAL char *
+Channel_Topic( CHANNEL *Chan )
+{
+ char *ret;
+ assert( Chan != NULL );
+ ret = array_start(&Chan->topic);
+ return ret ? ret : "";
+} /* Channel_Topic */
+
+
+#ifndef STRICT_RFC
+
+GLOBAL unsigned int
+Channel_TopicTime(CHANNEL *Chan)
+{
+ assert(Chan != NULL);
+ return (unsigned int) Chan->topic_time;
+} /* Channel_TopicTime */
+
+
+GLOBAL char *
+Channel_TopicWho(CHANNEL *Chan)
+{
+ assert(Chan != NULL);
+ return Chan->topic_who;
+} /* Channel_TopicWho */
+
+
+GLOBAL unsigned int
+Channel_CreationTime(CHANNEL *Chan)
+{
+ assert(Chan != NULL);
+ return (unsigned int) Chan->creation_time;
+} /* Channel_CreationTime */
+
+#endif
+
+
+GLOBAL void
+Channel_SetTopic(CHANNEL *Chan, CLIENT *Client, const char *Topic)
+{
+ size_t len;
+ assert( Chan != NULL );
+ assert( Topic != NULL );
+
+ len = strlen(Topic);
+ if (len < array_bytes(&Chan->topic))
+ array_free(&Chan->topic);
+
+ if (len >= COMMAND_LEN || !array_copyb(&Chan->topic, Topic, len+1))
+ Log(LOG_WARNING, "could not set new Topic \"%s\" on %s: %s",
+ Topic, Chan->name, strerror(errno));
+#ifndef STRICT_RFC
+ Chan->topic_time = time(NULL);
+ if (Client != NULL && Client_Type(Client) != CLIENT_SERVER)
+ strlcpy(Chan->topic_who, Client_ID(Client),
+ sizeof Chan->topic_who);
+ else
+ strlcpy(Chan->topic_who, DEFAULT_TOPIC_ID,
+ sizeof Chan->topic_who);
+#else
+ (void) Client;
+#endif
+} /* Channel_SetTopic */
+
+
+GLOBAL void
+Channel_SetModes( CHANNEL *Chan, const char *Modes )
+{
+ assert( Chan != NULL );
+ assert( Modes != NULL );
+
+ strlcpy( Chan->modes, Modes, sizeof( Chan->modes ));
+} /* Channel_SetModes */
+
+
+GLOBAL void
+Channel_SetKey( CHANNEL *Chan, const char *Key )
+{
+ assert( Chan != NULL );
+ assert( Key != NULL );
+
+ strlcpy( Chan->key, Key, sizeof( Chan->key ));
+ LogDebug("Channel %s: Key is now \"%s\".", Chan->name, Chan->key );
+} /* Channel_SetKey */
+
+
+GLOBAL void
+Channel_SetMaxUsers(CHANNEL *Chan, unsigned long Count)
+{
+ assert( Chan != NULL );
+
+ Chan->maxusers = Count;
+ LogDebug("Channel %s: Member limit is now %lu.", Chan->name, Chan->maxusers );
+} /* Channel_SetMaxUsers */
+
+
+/**
+ * Check if a client is allowed to send to a specific channel.
+ *
+ * @param Chan The channel to check.
+ * @param From The client that wants to send.
+ * @return true if the client is allowed to send, false otherwise.
+ */
+static bool
+Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From)
+{
+ bool is_member, has_voice, is_halfop, is_op, is_chanadmin, is_owner;
+
+ is_member = has_voice = is_halfop = is_op = is_chanadmin = is_owner = false;
+
+ /* The server itself always can send messages :-) */
+ if (Client_ThisServer() == From)
+ return true;
+
+ if (Channel_IsMemberOf(Chan, From)) {
+ is_member = true;
+ if (Channel_UserHasMode(Chan, From, 'v'))
+ has_voice = true;
+ if (Channel_UserHasMode(Chan, From, 'h'))
+ is_halfop = true;
+ if (Channel_UserHasMode(Chan, From, 'o'))
+ is_op = true;
+ if (Channel_UserHasMode(Chan, From, 'a'))
+ is_chanadmin = true;
+ if (Channel_UserHasMode(Chan, From, 'q'))
+ is_owner = true;
+ }
+
+ /*
+ * Is the client allowed to write to channel?
+ *
+ * If channel mode n set: non-members cannot send to channel.
+ * If channel mode m set: need voice.
+ */
+ if (Channel_HasMode(Chan, 'n') && !is_member)
+ return false;
+
+ if (Channel_HasMode(Chan, 'M') && !Client_HasMode(From, 'R')
+ && !Client_HasMode(From, 'o'))
+ return false;
+
+ if (has_voice || is_halfop || is_op || is_chanadmin || is_owner)
+ return true;
+
+ if (Channel_HasMode(Chan, 'm'))
+ return false;
+
+ if (Lists_Check(&Chan->list_excepts, From))
+ return true;
+
+ return !Lists_Check(&Chan->list_bans, From);
+}
+
+
+GLOBAL bool
+Channel_Write(CHANNEL *Chan, CLIENT *From, CLIENT *Client, const char *Command,
+ bool SendErrors, const char *Text)
+{
+ if (!Can_Send_To_Channel(Chan, From)) {
+ if (! SendErrors)
+ return CONNECTED; /* no error, see RFC 2812 */
+ if (Channel_HasMode(Chan, 'M'))
+ return IRC_WriteErrClient(From, ERR_NEEDREGGEDNICK_MSG,
+ Client_ID(From), Channel_Name(Chan));
+ else
+ return IRC_WriteErrClient(From, ERR_CANNOTSENDTOCHAN_MSG,
+ Client_ID(From), Channel_Name(Chan));
+ }
+
+ if (Client_Conn(From) > NONE)
+ Conn_UpdateIdle(Client_Conn(From));
+
+ IRC_WriteStrChannelPrefix(Client, Chan, From, true, "%s %s :%s",
+ Command, Channel_Name(Chan), Text);
+ return CONNECTED;
+}
+
+
+GLOBAL CHANNEL *
+Channel_Create( const char *Name )
+{
+ /* Create new CHANNEL structure and add it to linked list */