source: irc_channel.c @ 4c17d19

Last change on this file since 4c17d19 was 4c17d19, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-10T09:05:26Z

Fixed irc_channel_name_ok(): One-character channel names are okay, also the
first character after the prefix *can* be a number.

  • Property mode set to 100644
File size: 7.6 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2010 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* The IRC-based UI - Representing (virtual) channels.                  */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23  Suite 330, Boston, MA  02111-1307  USA
24*/
25
26#include "bitlbee.h"
27
28static gint irc_channel_user_cmp( gconstpointer a_, gconstpointer b_ );
29static const struct irc_channel_funcs control_channel_funcs;
30static const struct irc_channel_funcs groupchat_stub_funcs;
31
32irc_channel_t *irc_channel_new( irc_t *irc, const char *name )
33{
34        irc_channel_t *ic;
35       
36        if( !irc_channel_name_ok( name ) || irc_channel_by_name( irc, name ) )
37                return NULL;
38       
39        ic = g_new0( irc_channel_t, 1 );
40        ic->irc = irc;
41        ic->name = g_strdup( name );
42        strcpy( ic->mode, CMODE );
43       
44        irc_channel_add_user( ic, irc->root );
45        if( strcmp( set_getstr( &irc->b->set, "ops" ), "both" ) == 0 ||
46            strcmp( set_getstr( &irc->b->set, "ops" ), "root" ) == 0 )
47                irc_channel_user_set_mode( ic, irc->root, IRC_CHANNEL_USER_OP );
48       
49        irc->channels = g_slist_prepend( irc->channels, ic );
50       
51        if( name[0] == '&' )
52                ic->f = &control_channel_funcs;
53        else /* if( name[0] == '#' ) */
54                ic->f = &groupchat_stub_funcs;
55       
56        if( ic->f->_init )
57                if( !ic->f->_init( ic ) )
58                {
59                        irc_channel_free( ic );
60                        return NULL;
61                }
62       
63        return ic;
64}
65
66irc_channel_t *irc_channel_by_name( irc_t *irc, const char *name )
67{
68        GSList *l;
69       
70        for( l = irc->channels; l; l = l->next )
71        {
72                irc_channel_t *ic = l->data;
73               
74                if( name[0] == ic->name[0] && nick_cmp( name + 1, ic->name + 1 ) == 0 )
75                        return ic;
76        }
77       
78        return NULL;
79}
80
81int irc_channel_free( irc_channel_t *ic )
82{
83        irc_t *irc = ic->irc;
84       
85        if( ic->flags & IRC_CHANNEL_JOINED )
86                irc_channel_del_user( ic, irc->user );
87       
88        irc->channels = g_slist_remove( irc->channels, ic );
89        while( ic->users )
90        {
91                g_free( ic->users->data );
92                ic->users = g_slist_remove( ic->users, ic->users->data );
93        }
94       
95        g_free( ic->name );
96        g_free( ic->topic );
97        g_free( ic );
98       
99        return 1;
100}
101
102int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu )
103{
104        irc_channel_user_t *icu;
105       
106        if( irc_channel_has_user( ic, iu ) )
107                return 0;
108       
109        icu = g_new0( irc_channel_user_t, 1 );
110        icu->iu = iu;
111       
112        ic->users = g_slist_insert_sorted( ic->users, icu, irc_channel_user_cmp );
113       
114        if( iu == ic->irc->user || ic->flags & IRC_CHANNEL_JOINED )
115        {
116                ic->flags |= IRC_CHANNEL_JOINED;
117                irc_send_join( ic, iu );
118        }
119       
120        return 1;
121}
122
123int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu )
124{
125        irc_channel_user_t *icu;
126       
127        if( !( icu = irc_channel_has_user( ic, iu ) ) )
128                return 0;
129       
130        ic->users = g_slist_remove( ic->users, icu );
131        g_free( icu );
132       
133        if( ic->flags & IRC_CHANNEL_JOINED )
134                irc_send_part( ic, iu, "" );
135       
136        if( iu == ic->irc->user )
137                ic->flags &= ~IRC_CHANNEL_JOINED;
138       
139        return 1;
140}
141
142irc_channel_user_t *irc_channel_has_user( irc_channel_t *ic, irc_user_t *iu )
143{
144        GSList *l;
145       
146        for( l = ic->users; l; l = l->next )
147        {
148                irc_channel_user_t *icu = l->data;
149               
150                if( icu->iu == iu )
151                        return icu;
152        }
153       
154        return NULL;
155}
156
157int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_t *iu )
158{
159        g_free( ic->topic );
160        ic->topic = g_strdup( topic );
161       
162        g_free( ic->topic_who );
163        if( iu )
164                ic->topic_who = g_strdup_printf( "%s!%s@%s", iu->nick, iu->user, iu->host );
165        else
166                ic->topic_who = NULL;
167       
168        ic->topic_time = time( NULL );
169       
170        if( ic->flags & IRC_CHANNEL_JOINED )
171                irc_send_topic( ic, TRUE );
172       
173        return 1;
174}
175
176void irc_channel_user_set_mode( irc_channel_t *ic, irc_user_t *iu, irc_channel_user_flags_t flags )
177{
178        irc_channel_user_t *icu = irc_channel_has_user( ic, iu );
179       
180        if( icu->flags == flags )
181                return;
182       
183        if( ic->flags & IRC_CHANNEL_JOINED )
184                irc_send_channel_user_mode_diff( ic, iu, icu->flags, flags );
185       
186        icu->flags = flags;
187}
188
189void irc_channel_printf( irc_channel_t *ic, char *format, ... )
190{
191        va_list params;
192        char *text;
193       
194        va_start( params, format );
195        text = g_strdup_vprintf( format, params );
196        va_end( params );
197       
198        irc_send_msg( ic->irc->root, "PRIVMSG", ic->name, text, NULL );
199        g_free( text );
200}
201
202gboolean irc_channel_name_ok( const char *name )
203{
204        char name_[strlen(name)+1];
205       
206        /* Check if the first character is in CTYPES (#&) */
207        if( strchr( CTYPES, name[0] ) == NULL )
208                return FALSE;
209       
210        /* Check the rest of the name. Just checking name + 1 doesn't work
211           since it will fail if the first character is a number, or if
212           it's a one-char channel name - both of which are legal. */
213        name_[0] = '_';
214        strcpy( name_ + 1, name + 1 );
215        return nick_ok( name_ );
216}
217
218static gint irc_channel_user_cmp( gconstpointer a_, gconstpointer b_ )
219{
220        const irc_channel_user_t *a = a_, *b = b_;
221       
222        return irc_user_cmp( a->iu, b->iu );
223}
224
225/* Channel-type dependent functions, for control channels: */
226static gboolean control_channel_privmsg( irc_channel_t *ic, const char *msg )
227{
228        irc_t *irc = ic->irc;
229        const char *s;
230       
231        /* Scan for non-whitespace chars followed by a colon: */
232        for( s = msg; *s && !isspace( *s ) && *s != ':' && *s != ','; s ++ ) {}
233       
234        if( *s == ':' || *s == ',' )
235        {
236                char to[s-msg+1];
237                irc_user_t *iu;
238               
239                memset( to, 0, sizeof( to ) );
240                strncpy( to, msg, s - msg );
241                while( *(++s) && isspace( *s ) ) {}
242               
243                iu = irc_user_by_name( irc, to );
244                if( iu && iu->f->privmsg )
245                {
246                        iu->flags &= ~IRC_USER_PRIVATE;
247                        iu->f->privmsg( iu, s );
248                }
249                else
250                {
251                        irc_channel_printf( ic, "User does not exist: %s", to );
252                }
253        }
254        else
255        {
256                /* TODO: Maybe just use root->privmsg here now? */
257                char cmd[strlen(msg)+1];
258               
259                g_free( ic->irc->last_root_cmd );
260                ic->irc->last_root_cmd = g_strdup( ic->name );
261               
262                strcpy( cmd, msg );
263                root_command_string( ic->irc, cmd );
264        }
265       
266        return TRUE;
267}
268
269static gboolean control_channel_init( irc_channel_t *ic )
270{
271        struct irc_control_channel *icc;
272       
273        ic->data = icc = g_new0( struct irc_control_channel, 1 );
274        icc->type = IRC_CC_TYPE_DEFAULT;
275       
276        if( ( icc->group = bee_group_by_name( ic->irc->b, ic->name + 1, FALSE ) ) )
277                icc->type = IRC_CC_TYPE_GROUP;
278        else if( ( icc->account = account_get( ic->irc->b, ic->name + 1 ) ) )
279                icc->type = IRC_CC_TYPE_ACCOUNT;
280       
281        bee_irc_channel_update( ic->irc, ic, NULL );
282       
283        return TRUE;
284}
285
286static const struct irc_channel_funcs control_channel_funcs = {
287        control_channel_privmsg,
288        NULL,
289        NULL,
290        NULL,
291        NULL,
292       
293        control_channel_init,
294};
295
296/* Groupchat stub: Only handles /INVITE at least for now. */
297static gboolean groupchat_stub_invite( irc_channel_t *ic, irc_user_t *iu )
298{
299        bee_user_t *bu = iu->bu;
300       
301        if( iu->bu->ic->acc->prpl->chat_with )
302        {
303                ic->flags |= IRC_CHANNEL_CHAT_PICKME;
304                iu->bu->ic->acc->prpl->chat_with( bu->ic, bu->handle );
305                ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
306                return TRUE;
307        }
308        else
309        {
310                irc_send_num( ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name );
311                return FALSE;
312        }
313}
314
315static const struct irc_channel_funcs groupchat_stub_funcs = {
316        NULL,
317        NULL,
318        NULL,
319        NULL,
320        groupchat_stub_invite,
321};
Note: See TracBrowser for help on using the repository browser.