source: irc_channel.c @ ecbd22a

Last change on this file since ecbd22a was ecbd22a, checked in by dequis <dx@…>, at 2015-01-16T19:50:25Z

channel: fixed auto-join occurring when disabled

With the auto_join channel flag set to false, the channel is still
auto-joined. This can lead to the channel being "doubly" joined if
a client previously sent a channel join request. The result of being
"doubly" joined is really undefined, but most notably memory leaks
can occur.

It also appears, based on the comment under the modified condition,
the previous condition was incorrect.

Another patch should probably implement some sort of check to ensure a
channel is not already joined, assuming the auto_join flag is enabled.

  • Property mode set to 100644
File size: 20.0 KB
RevLine 
[4be8239]1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
[0e788f5]4  * Copyright 2002-2013 Wilmer van der Gaast and others                *
[4be8239]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;
[6f10697]22  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23  Fifth Floor, Boston, MA  02110-1301  USA
[4be8239]24*/
25
26#include "bitlbee.h"
27
[2b8473c]28static char *set_eval_channel_type( set_t *set, char *value );
[e54112f]29static gint irc_channel_user_cmp( gconstpointer a_, gconstpointer b_ );
[280c56a]30static const struct irc_channel_funcs control_channel_funcs;
[5a75d15]31
32extern const struct irc_channel_funcs irc_channel_im_chat_funcs;
[280c56a]33
[4be8239]34irc_channel_t *irc_channel_new( irc_t *irc, const char *name )
35{
36        irc_channel_t *ic;
[d4a4f1a]37        set_t *s;
[4be8239]38       
[9438323]39        if( !irc_channel_name_ok( name ) || irc_channel_by_name( irc, name ) )
[4be8239]40                return NULL;
41       
42        ic = g_new0( irc_channel_t, 1 );
43        ic->irc = irc;
44        ic->name = g_strdup( name );
45        strcpy( ic->mode, CMODE );
46       
47        irc_channel_add_user( ic, irc->root );
48       
[36562b0]49        irc->channels = g_slist_append( irc->channels, ic );
[4be8239]50       
[c8eeadd]51        set_add( &ic->set, "auto_join", "false", set_eval_bool, ic );
[d4a4f1a]52       
53        s = set_add( &ic->set, "type", "control", set_eval_channel_type, ic );
54        s->flags |= SET_NOSAVE;    /* Layer violation (XML format detail) */
[2b8473c]55       
[eb37735]56        if( name[0] == '&' )
[2b8473c]57                set_setstr( &ic->set, "type", "control" );
[eb37735]58        else /* if( name[0] == '#' ) */
[2b8473c]59                set_setstr( &ic->set, "type", "chat" );
[9ac3ed1]60       
[4be8239]61        return ic;
62}
63
[b9e020a]64irc_channel_t *irc_channel_by_name( irc_t *irc, const char *name )
65{
66        GSList *l;
67       
68        for( l = irc->channels; l; l = l->next )
69        {
70                irc_channel_t *ic = l->data;
71               
[6b90431]72                if( irc_channel_name_cmp( name, ic->name ) == 0 )
[b9e020a]73                        return ic;
74        }
75       
76        return NULL;
77}
78
[36562b0]79irc_channel_t *irc_channel_get( irc_t *irc, char *id )
80{
81        irc_channel_t *ic, *ret = NULL;
82        GSList *l;
83        int nr;
84       
85        if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 )
86        {
87                for( l = irc->channels; l; l = l->next )
88                {
89                        ic = l->data;
90                        if( ( nr-- ) == 0 ) 
91                                return ic;
92                }
93               
94                return NULL;
95        }
96       
97        /* Exact match first: Partial match only sucks if there's a channel
98           #aa and #aabb */
99        if( ( ret = irc_channel_by_name( irc, id ) ) )
100                return ret;
101       
102        for( l = irc->channels; l; l = l->next )
103        {
104                ic = l->data;
105               
106                if( strstr( ic->name, id ) )
107                {
108                        /* Make sure it's a unique match. */
109                        if( !ret )
110                                ret = ic;
111                        else
112                                return NULL;
113                }
114        }
115       
116        return ret;
117}
118
[63a520b]119int irc_channel_free( irc_channel_t *ic )
120{
[7d4ffc2]121        irc_t *irc;
[2fe5eb9]122        GSList *l;
[63a520b]123       
[7d4ffc2]124        if( ic == NULL )
125                return 0;
126        irc = ic->irc;
127       
[63a520b]128        if( ic->flags & IRC_CHANNEL_JOINED )
[006a84f]129                irc_channel_del_user( ic, irc->user, IRC_CDU_KICK, "Cleaning up channel" );
[63a520b]130       
[d7db346]131        if( ic->f->_free )
132                ic->f->_free( ic );
133       
134        while( ic->set )
135                set_del( &ic->set, ic->set->key );
136       
[63a520b]137        irc->channels = g_slist_remove( irc->channels, ic );
[e54112f]138        while( ic->users )
139        {
140                g_free( ic->users->data );
141                ic->users = g_slist_remove( ic->users, ic->users->data );
142        }
[63a520b]143       
[2fe5eb9]144        for( l = irc->users; l; l = l->next )
145        {
146                irc_user_t *iu = l->data;
147               
148                if( iu->last_channel == ic )
149                        iu->last_channel = irc->default_channel;
150        }
151       
[cc20520]152        if( ic->pastebuf_timer ) b_event_remove( ic->pastebuf_timer );
153       
[63a520b]154        g_free( ic->name );
155        g_free( ic->topic );
[d7db346]156        g_free( ic->topic_who );
[63a520b]157        g_free( ic );
158       
159        return 1;
160}
161
[ab6006c]162struct irc_channel_free_data
163{
164        irc_t *irc;
165        irc_channel_t *ic;
166        char *name;
167};
168
169static gboolean irc_channel_free_callback( gpointer data, gint fd, b_input_condition cond )
170{
171        struct irc_channel_free_data *d = data;
172       
173        if( g_slist_find( irc_connection_list, d->irc ) &&
174            irc_channel_by_name( d->irc, d->name ) == d->ic &&
175            !( d->ic->flags & IRC_CHANNEL_JOINED ) )
176                irc_channel_free( d->ic );
177
178        g_free( d->name );
179        g_free( d );
180        return FALSE;
181}
182
183/* Free the channel, but via the event loop, so after finishing whatever event
184   we're currently handling. */
185void irc_channel_free_soon( irc_channel_t *ic )
186{
187        struct irc_channel_free_data *d = g_new0( struct irc_channel_free_data, 1 );
188       
189        d->irc = ic->irc;
190        d->ic = ic;
191        d->name = g_strdup( ic->name );
192       
193        b_timeout_add( 0, irc_channel_free_callback, d );
194}
195
[2b8473c]196static char *set_eval_channel_type( set_t *set, char *value )
197{
198        struct irc_channel *ic = set->data;
199        const struct irc_channel_funcs *new;
200       
201        if( strcmp( value, "control" ) == 0 )
202                new = &control_channel_funcs;
[05bf2a0]203        else if( ic != ic->irc->default_channel && strcmp( value, "chat" ) == 0 )
[5a75d15]204                new = &irc_channel_im_chat_funcs;
[2b8473c]205        else
206                return SET_INVALID;
207       
208        /* TODO: Return values. */
209        if( ic->f && ic->f->_free )
210                ic->f->_free( ic );
211       
212        ic->f = new;
213       
214        if( ic->f && ic->f->_init )
215                ic->f->_init( ic );
216       
217        return value;
218}
219
[4be8239]220int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu )
221{
[e54112f]222        irc_channel_user_t *icu;
223       
[57c96f7]224        if( irc_channel_has_user( ic, iu ) )
[4be8239]225                return 0;
226       
[e54112f]227        icu = g_new0( irc_channel_user_t, 1 );
228        icu->iu = iu;
229       
230        ic->users = g_slist_insert_sorted( ic->users, icu, irc_channel_user_cmp );
[4be8239]231       
[c5aefa4]232        irc_channel_update_ops( ic, set_getstr( &ic->irc->b->set, "ops" ) );
233       
[4be8239]234        if( iu == ic->irc->user || ic->flags & IRC_CHANNEL_JOINED )
235        {
236                ic->flags |= IRC_CHANNEL_JOINED;
237                irc_send_join( ic, iu );
238        }
239       
240        return 1;
241}
242
[006a84f]243int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu, irc_channel_del_user_type_t type, const char *msg )
[4be8239]244{
[e54112f]245        irc_channel_user_t *icu;
246       
247        if( !( icu = irc_channel_has_user( ic, iu ) ) )
[4be8239]248                return 0;
249       
[e54112f]250        ic->users = g_slist_remove( ic->users, icu );
251        g_free( icu );
[4be8239]252       
[006a84f]253        if( !( ic->flags & IRC_CHANNEL_JOINED ) || type == IRC_CDU_SILENT ) {}
254                /* Do nothing. The caller should promise it won't screw
255                   up state of the IRC client. :-) */
256        else if( type == IRC_CDU_PART )
[18da20b]257                irc_send_part( ic, iu, msg );
[006a84f]258        else if( type == IRC_CDU_KICK )
259                irc_send_kick( ic, iu, ic->irc->root, msg );
[4be8239]260       
261        if( iu == ic->irc->user )
[1c40aa7]262        {
[4be8239]263                ic->flags &= ~IRC_CHANNEL_JOINED;
[1c40aa7]264               
[06f9548]265                if( ic->irc->status & USTATUS_SHUTDOWN )
266                {
267                        /* Don't do anything fancy when we're shutting down anyway. */
268                }
269                else if( ic->flags & IRC_CHANNEL_TEMP )
270                {
[ab6006c]271                        irc_channel_free_soon( ic );
[06f9548]272                }
[9052bc1]273                else
274                {
275                        /* Flush userlist now. The user won't see it anyway. */
276                        while( ic->users )
277                        {
278                                g_free( ic->users->data );
279                                ic->users = g_slist_remove( ic->users, ic->users->data );
280                        }
281                        irc_channel_add_user( ic, ic->irc->root );
282                }
[1c40aa7]283        }
[4be8239]284       
285        return 1;
286}
287
[e54112f]288irc_channel_user_t *irc_channel_has_user( irc_channel_t *ic, irc_user_t *iu )
[0b5cc72]289{
[e54112f]290        GSList *l;
291       
292        for( l = ic->users; l; l = l->next )
293        {
294                irc_channel_user_t *icu = l->data;
295               
296                if( icu->iu == iu )
297                        return icu;
298        }
299       
300        return NULL;
[0b5cc72]301}
302
[bb151f7]303/* Find a channel we're currently in, that currently has iu in it. */
304struct irc_channel *irc_channel_with_user( irc_t *irc, irc_user_t *iu )
305{
306        GSList *l;
307       
308        for( l = irc->channels; l; l = l->next )
309        {
310                irc_channel_t *ic = l->data;
311               
312                if( strcmp( set_getstr( &ic->set, "type" ), "control" ) != 0 )
313                        continue;
314               
315                if( ( ic->flags & IRC_CHANNEL_JOINED ) &&
316                    irc_channel_has_user( ic, iu ) )
317                        return ic;
318        }
319       
320        /* If there was no match, try once more but just see if the user
321           *would* be in the channel, i.e. if s/he were online. */
322        if( iu->bu == NULL )
323                return NULL;
324       
325        for( l = irc->channels; l; l = l->next )
326        {
327                irc_channel_t *ic = l->data;
328               
329                if( strcmp( set_getstr( &ic->set, "type" ), "control" ) != 0 )
330                        continue;
331               
332                if( ( ic->flags & IRC_CHANNEL_JOINED ) &&
333                    irc_channel_wants_user( ic, iu ) )
334                        return ic;
335        }
336       
337        return NULL;
338}
339
[83e92bf]340int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_t *iu )
[4be8239]341{
342        g_free( ic->topic );
343        ic->topic = g_strdup( topic );
344       
[83e92bf]345        g_free( ic->topic_who );
346        if( iu )
347                ic->topic_who = g_strdup_printf( "%s!%s@%s", iu->nick, iu->user, iu->host );
348        else
349                ic->topic_who = NULL;
350       
351        ic->topic_time = time( NULL );
352       
[4be8239]353        if( ic->flags & IRC_CHANNEL_JOINED )
[83e92bf]354                irc_send_topic( ic, TRUE );
[4be8239]355       
356        return 1;
357}
[b919363]358
[6a9d068]359void irc_channel_user_set_mode( irc_channel_t *ic, irc_user_t *iu, irc_channel_user_flags_t flags )
360{
361        irc_channel_user_t *icu = irc_channel_has_user( ic, iu );
362       
[c5aefa4]363        if( !icu || icu->flags == flags )
[6a9d068]364                return;
365       
366        if( ic->flags & IRC_CHANNEL_JOINED )
367                irc_send_channel_user_mode_diff( ic, iu, icu->flags, flags );
368       
369        icu->flags = flags;
370}
371
[65016a6]372void irc_channel_set_mode( irc_channel_t *ic, const char *s )
373{
374        irc_t *irc = ic->irc;
375        char m[128], st = 1;
376        const char *t;
377        int i;
378        char changes[512], *p, st2 = 2;
379       
380        memset( m, 0, sizeof( m ) );
381       
382        for( t = ic->mode; *t; t ++ )
383                if( *t < sizeof( m ) )
384                        m[(int)*t] = 1;
385       
386        p = changes;
387        for( t = s; *t; t ++ )
388        {
389                if( *t == '+' || *t == '-' )
390                        st = *t == '+';
391                else if( strchr( CMODES, *t ) )
392                {
393                        if( m[(int)*t] != st)
394                        {
395                                if( st != st2 )
396                                        st2 = st, *p++ = st ? '+' : '-';
397                                *p++ = *t;
398                        }
399                        m[(int)*t] = st;
400                }
401        }
402        *p = '\0';
403       
404        memset( ic->mode, 0, sizeof( ic->mode ) );
405       
406        for( i = 'A'; i <= 'z' && strlen( ic->mode ) < ( sizeof( ic->mode ) - 1 ); i ++ )
407                if( m[i] )
408                        ic->mode[strlen(ic->mode)] = i;
409       
410        if( *changes && ( ic->flags & IRC_CHANNEL_JOINED ) )
411                irc_write( irc, ":%s!%s@%s MODE %s :%s", irc->root->nick,
412                           irc->root->user, irc->root->host, ic->name,
413                           changes );
414}
415
[c8eeadd]416void irc_channel_auto_joins( irc_t *irc, account_t *acc )
417{
418        GSList *l;
419       
420        for( l = irc->channels; l; l = l->next )
421        {
422                irc_channel_t *ic = l->data;
423                gboolean aj = set_getbool( &ic->set, "auto_join" );
424                char *type;
425               
426                if( acc &&
427                    ( type = set_getstr( &ic->set, "chat_type" ) ) &&
428                    strcmp( type, "room" ) == 0 )
429                {
430                        /* Bit of an ugly special case: Handle chatrooms here, we
431                           can only auto-join them if their account is online. */
432                        char *acc_s;
433                       
[ecbd22a]434                        if( !aj || ( ic->flags & IRC_CHANNEL_JOINED ) )
[c8eeadd]435                                /* Only continue if this one's marked as auto_join
436                                   or if we're in it already. (Possible if the
437                                   client auto-rejoined it before identyfing.) */
438                                continue;
439                        else if( !( acc_s = set_getstr( &ic->set, "account" ) ) )
440                                continue;
441                        else if( account_get( irc->b, acc_s ) != acc )
442                                continue;
443                        else if( acc->ic == NULL || !( acc->ic->flags & OPT_LOGGED_IN ) )
444                                continue;
445                        else
446                                ic->f->join( ic );
447                }
448                else if( aj )
449                {
450                        irc_channel_add_user( ic, irc->user );
451                }
452        }
453}
454
[9893da3]455void irc_channel_printf( irc_channel_t *ic, char *format, ... )
456{
457        va_list params;
458        char *text;
459       
460        va_start( params, format );
461        text = g_strdup_vprintf( format, params );
462        va_end( params );
463       
464        irc_send_msg( ic->irc->root, "PRIVMSG", ic->name, text, NULL );
465        g_free( text );
466}
467
[6b90431]468gboolean irc_channel_name_ok( const char *name_ )
[b919363]469{
[6b90431]470        const unsigned char *name = (unsigned char*) name_;
471        int i;
[4c17d19]472       
[a670aeb]473        if( name_[0] == '\0' )
474                return FALSE;
475       
[4c17d19]476        /* Check if the first character is in CTYPES (#&) */
[6b90431]477        if( strchr( CTYPES, name_[0] ) == NULL )
[4c17d19]478                return FALSE;
479       
[6b90431]480        /* RFC 1459 keeps amazing me: While only a "few" chars are allowed
481           in nicknames, channel names can be pretty much anything as long
482           as they start with # or &. I'll be a little bit more strict and
483           disallow all non-printable characters. */
484        for( i = 1; name[i]; i ++ )
485                if( name[i] <= ' ' || name[i] == ',' )
486                        return FALSE;
487       
488        return TRUE;
489}
490
[134a02c]491void irc_channel_name_strip( char *name )
492{
493        int i, j;
494       
495        for( i = j = 0; name[i]; i ++ )
496                if( name[i] > ' ' && name[i] != ',' )
497                        name[j++] = name[i];
498       
499        name[j] = '\0';
500}
501
[6b90431]502int irc_channel_name_cmp( const char *a_, const char *b_ )
503{
504        static unsigned char case_map[256];
505        const unsigned char *a = (unsigned char*) a_, *b = (unsigned char*) b_;
506        int i;
507       
508        if( case_map['A'] == '\0' )
509        {
510                for( i = 33; i < 256; i ++ )
511                        if( i != ',' )
512                                case_map[i] = i;
513               
514                for( i = 0; i < 26; i ++ )
515                        case_map['A'+i] = 'a' + i;
516               
517                case_map['['] = '{';
518                case_map[']'] = '}';
519                case_map['~'] = '`';
520                case_map['\\'] = '|';
521        }
522       
523        if( !irc_channel_name_ok( a_ ) || !irc_channel_name_ok( b_ ) )
524                return -1;
525       
526        for( i = 0; a[i] && b[i] && case_map[a[i]] && case_map[b[i]]; i ++ )
527        {
528                if( case_map[a[i]] == case_map[b[i]] )
529                        continue;
530                else
531                        return case_map[a[i]] - case_map[b[i]];
532        }
533       
534        return case_map[a[i]] - case_map[b[i]];
[b919363]535}
[280c56a]536
[e54112f]537static gint irc_channel_user_cmp( gconstpointer a_, gconstpointer b_ )
538{
539        const irc_channel_user_t *a = a_, *b = b_;
540       
541        return irc_user_cmp( a->iu, b->iu );
542}
543
[c5aefa4]544void irc_channel_update_ops( irc_channel_t *ic, char *value )
545{
546        irc_channel_user_set_mode( ic, ic->irc->root,
547                ( strcmp( value, "both" ) == 0 ||
548                  strcmp( value, "root" ) == 0 ) ? IRC_CHANNEL_USER_OP : 0 );
549        irc_channel_user_set_mode( ic, ic->irc->user,
550                ( strcmp( value, "both" ) == 0 ||
551                  strcmp( value, "user" ) == 0 ) ? IRC_CHANNEL_USER_OP : 0 );
552}
553
554char *set_eval_irc_channel_ops( set_t *set, char *value )
555{
556        irc_t *irc = set->data;
557        GSList *l;
558       
559        if( strcmp( value, "both" ) != 0 && strcmp( value, "none" ) != 0 && 
560            strcmp( value, "user" ) != 0 && strcmp( value, "root" ) != 0 )
561                return SET_INVALID;
562       
563        for( l = irc->channels; l; l = l->next )
564                irc_channel_update_ops( l->data, value );
565       
566        return value;
567}
568
[280c56a]569/* Channel-type dependent functions, for control channels: */
570static gboolean control_channel_privmsg( irc_channel_t *ic, const char *msg )
571{
[bce78c8]572        irc_t *irc = ic->irc;
[f7ca587]573        irc_user_t *iu;
[bce78c8]574        const char *s;
[fb117aee]575       
[bce78c8]576        /* Scan for non-whitespace chars followed by a colon: */
[6b13103]577        for( s = msg; *s && !g_ascii_isspace( *s ) && *s != ':' && *s != ','; s ++ ) {}
[74f1cde]578       
[b0364dc]579        if( *s == ':' || *s == ',' )
[bce78c8]580        {
581                char to[s-msg+1];
582               
[1a3ba05]583                memset( to, 0, sizeof( to ) );
[bce78c8]584                strncpy( to, msg, s - msg );
[6b13103]585                while( *(++s) && g_ascii_isspace( *s ) ) {}
[f7ca587]586                msg = s;
[bce78c8]587               
[f7ca587]588                if( !( iu = irc_user_by_name( irc, to ) ) )
[9893da3]589                        irc_channel_printf( ic, "User does not exist: %s", to );
[f7ca587]590                else
591                        ic->last_target = iu;
[bce78c8]592        }
[f7ca587]593        else if( g_strcasecmp( set_getstr( &irc->b->set, "default_target" ), "last" ) == 0 &&
594                 ic->last_target && g_slist_find( irc->users, ic->last_target ) )
595                iu = ic->last_target;
[bce78c8]596        else
[f7ca587]597                iu = irc->root;
598       
599        if( iu && iu->f->privmsg )
[bce78c8]600        {
[f7ca587]601                iu->last_channel = ic;
602                iu->f->privmsg( iu, msg );
[bce78c8]603        }
[280c56a]604       
605        return TRUE;
606}
607
[46d215d]608static gboolean control_channel_invite( irc_channel_t *ic, irc_user_t *iu )
609{
610        struct irc_control_channel *icc = ic->data;
611        bee_user_t *bu = iu->bu;
612       
613        if( bu == NULL )
614                return FALSE;
615       
616        if( icc->type != IRC_CC_TYPE_GROUP )
617        {
618                irc_send_num( ic->irc, 482, "%s :Invitations are only possible to fill_by=group channels", ic->name );
619                return FALSE;
620        }
621       
622        bu->ic->acc->prpl->add_buddy( bu->ic, bu->handle,
623                                      icc->group ? icc->group->name : NULL );
624       
625        return TRUE;
626}
627
[2b8473c]628static char *set_eval_by_account( set_t *set, char *value );
629static char *set_eval_fill_by( set_t *set, char *value );
630static char *set_eval_by_group( set_t *set, char *value );
[7a6ba50]631static char *set_eval_by_protocol( set_t *set, char *value );
[94d5da9c]632static char *set_eval_show_users( set_t *set, char *value );
[2b8473c]633
[9ac3ed1]634static gboolean control_channel_init( irc_channel_t *ic )
635{
636        struct irc_control_channel *icc;
637       
[2b8473c]638        set_add( &ic->set, "account", NULL, set_eval_by_account, ic );
639        set_add( &ic->set, "fill_by", "all", set_eval_fill_by, ic );
640        set_add( &ic->set, "group", NULL, set_eval_by_group, ic );
[7a6ba50]641        set_add( &ic->set, "protocol", NULL, set_eval_by_protocol, ic );
[5a61bf59]642       
643        /* When changing the default, also change it below. */
[94d5da9c]644        set_add( &ic->set, "show_users", "online+,away", set_eval_show_users, ic );
[2b8473c]645       
[9ac3ed1]646        ic->data = icc = g_new0( struct irc_control_channel, 1 );
647        icc->type = IRC_CC_TYPE_DEFAULT;
648       
[94d5da9c]649        /* Have to run the evaluator to initialize icc->modes. */
[5a61bf59]650        set_setstr( &ic->set, "show_users", "online+,away" );
[94d5da9c]651       
[65016a6]652        /* For scripts that care. */
653        irc_channel_set_mode( ic, "+C" );
654       
[2b8473c]655        return TRUE;
656}
657
[c8eeadd]658static gboolean control_channel_join( irc_channel_t *ic )
659{
660        bee_irc_channel_update( ic->irc, ic, NULL );
661       
662        return TRUE;
663}
664
[2b8473c]665static char *set_eval_by_account( set_t *set, char *value )
666{
667        struct irc_channel *ic = set->data;
668        struct irc_control_channel *icc = ic->data;
669        account_t *acc;
670       
671        if( !( acc = account_get( ic->irc->b, value ) ) )
672                return SET_INVALID;
673       
674        icc->account = acc;
[a5c6ebd]675        if( ( icc->type & IRC_CC_TYPE_MASK ) == IRC_CC_TYPE_ACCOUNT )
[cf1a979]676                bee_irc_channel_update( ic->irc, ic, NULL );
[7a6ba50]677       
[e135cd09]678        return g_strdup( acc->tag );
[2b8473c]679}
680
681static char *set_eval_fill_by( set_t *set, char *value )
682{
683        struct irc_channel *ic = set->data;
684        struct irc_control_channel *icc = ic->data;
[a5c6ebd]685        char *s;
[2b8473c]686       
[a5c6ebd]687        icc->type &= ~( IRC_CC_TYPE_MASK | IRC_CC_TYPE_INVERT );
688       
689        s = value;
690        if( s[0] == '!' )
691        {
692                icc->type |= IRC_CC_TYPE_INVERT;
693                s ++;
694        }
695       
696        if( strcmp( s, "all" ) == 0 )
697                icc->type |= IRC_CC_TYPE_DEFAULT;
698        else if( strcmp( s, "rest" ) == 0 )
699                icc->type |= IRC_CC_TYPE_REST;
700        else if( strcmp( s, "group" ) == 0 )
701                icc->type |= IRC_CC_TYPE_GROUP;
702        else if( strcmp( s, "account" ) == 0 )
703                icc->type |= IRC_CC_TYPE_ACCOUNT;
704        else if( strcmp( s, "protocol" ) == 0 )
705                icc->type |= IRC_CC_TYPE_PROTOCOL;
[2b8473c]706        else
707                return SET_INVALID;
708       
[cf1a979]709        bee_irc_channel_update( ic->irc, ic, NULL );
[2b8473c]710        return value;
711}
712
713static char *set_eval_by_group( set_t *set, char *value )
714{
715        struct irc_channel *ic = set->data;
716        struct irc_control_channel *icc = ic->data;
[13c1a9f]717       
[2b8473c]718        icc->group = bee_group_by_name( ic->irc->b, value, TRUE );
[a5c6ebd]719        if( ( icc->type & IRC_CC_TYPE_MASK ) == IRC_CC_TYPE_GROUP )
[cf1a979]720                bee_irc_channel_update( ic->irc, ic, NULL );
[7a6ba50]721       
[2b8473c]722        return g_strdup( icc->group->name );
723}
724
[7a6ba50]725static char *set_eval_by_protocol( set_t *set, char *value )
726{
727        struct irc_channel *ic = set->data;
728        struct irc_control_channel *icc = ic->data;
729        struct prpl *prpl;
730       
731        if( !( prpl = find_protocol( value ) ) )
732                return SET_INVALID;
733       
734        icc->protocol = prpl;
[a5c6ebd]735        if( ( icc->type & IRC_CC_TYPE_MASK ) == IRC_CC_TYPE_PROTOCOL )
[7a6ba50]736                bee_irc_channel_update( ic->irc, ic, NULL );
737       
738        return value;
739}
740
[94d5da9c]741static char *set_eval_show_users( set_t *set, char *value )
742{
743        struct irc_channel *ic = set->data;
744        struct irc_control_channel *icc = ic->data;
745        char **parts = g_strsplit( value, ",", 0 ), **part;
746        char modes[4];
747       
748        memset( modes, 0, 4 );
749        for( part = parts; *part; part ++ )
750        {
751                char last, modechar = IRC_CHANNEL_USER_NONE;
752               
753                if( **part == '\0' )
754                        goto fail;
755               
756                last = (*part)[strlen(*part+1)];
757                if( last == '+' )
758                        modechar = IRC_CHANNEL_USER_VOICE;
759                else if( last == '%' )
760                        modechar = IRC_CHANNEL_USER_HALFOP;
761                else if( last == '@' )
762                        modechar = IRC_CHANNEL_USER_OP;
763               
764                if( strncmp( *part, "offline", 7 ) == 0 )
765                        modes[0] = modechar;
766                else if( strncmp( *part, "away", 4 ) == 0 )
767                        modes[1] = modechar;
768                else if( strncmp( *part, "online", 6 ) == 0 )
769                        modes[2] = modechar;
770                else
771                        goto fail;
772        }
773        memcpy( icc->modes, modes, 4 );
774        bee_irc_channel_update( ic->irc, ic, NULL );
775       
776        g_strfreev( parts );
777        return value;
778       
779fail:
780        g_strfreev( parts );
781        return SET_INVALID;     
782}
783
[ac2717b]784/* Figure out if a channel is supposed to have the user, assuming s/he is
785   online or otherwise also selected by the show_users setting. Only works
786   for control channels, but does *not* check if this channel is of that
787   type. Beware! */
788gboolean irc_channel_wants_user( irc_channel_t *ic, irc_user_t *iu )
789{
790        struct irc_control_channel *icc = ic->data;
[a5c6ebd]791        gboolean ret = FALSE;
[ac2717b]792       
793        if( iu->bu == NULL )
794                return FALSE;
795       
[a5c6ebd]796        switch( icc->type & IRC_CC_TYPE_MASK )
[ac2717b]797        {
798        case IRC_CC_TYPE_GROUP:
[a5c6ebd]799                ret = iu->bu->group == icc->group;
800                break;
[ac2717b]801        case IRC_CC_TYPE_ACCOUNT:
[a5c6ebd]802                ret = iu->bu->ic->acc == icc->account;
803                break;
[ac2717b]804        case IRC_CC_TYPE_PROTOCOL:
[a5c6ebd]805                ret = iu->bu->ic->acc->prpl == icc->protocol;
806                break;
[ac2717b]807        case IRC_CC_TYPE_DEFAULT:
808        default:
[a5c6ebd]809                ret = TRUE;
810                break;
[ac2717b]811        }
[a5c6ebd]812       
813        if( icc->type & IRC_CC_TYPE_INVERT )
814                ret = !ret;
815       
816        return ret;
[ac2717b]817}
818
[2b8473c]819static gboolean control_channel_free( irc_channel_t *ic )
820{
821        struct irc_control_channel *icc = ic->data;
822       
823        set_del( &ic->set, "account" );
824        set_del( &ic->set, "fill_by" );
825        set_del( &ic->set, "group" );
[9052bc1]826        set_del( &ic->set, "protocol" );
[180ab31]827        set_del( &ic->set, "show_users" );
[2b8473c]828       
829        g_free( icc );
830        ic->data = NULL;
[13c1a9f]831       
[65016a6]832        /* For scripts that care. */
833        irc_channel_set_mode( ic, "-C" );
834       
[9ac3ed1]835        return TRUE;
836}
837
[280c56a]838static const struct irc_channel_funcs control_channel_funcs = {
839        control_channel_privmsg,
[c8eeadd]840        control_channel_join,
[9ac3ed1]841        NULL,
842        NULL,
[46d215d]843        control_channel_invite,
[9ac3ed1]844       
845        control_channel_init,
[2b8473c]846        control_channel_free,
[280c56a]847};
Note: See TracBrowser for help on using the repository browser.