source: irc_channel.c @ dfaa46d

Last change on this file since dfaa46d 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
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2013 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., 51 Franklin St.,
23  Fifth Floor, Boston, MA  02110-1301  USA
24*/
25
26#include "bitlbee.h"
27
28static char *set_eval_channel_type( set_t *set, char *value );
29static gint irc_channel_user_cmp( gconstpointer a_, gconstpointer b_ );
30static const struct irc_channel_funcs control_channel_funcs;
31
32extern const struct irc_channel_funcs irc_channel_im_chat_funcs;
33
34irc_channel_t *irc_channel_new( irc_t *irc, const char *name )
35{
36        irc_channel_t *ic;
37        set_t *s;
38       
39        if( !irc_channel_name_ok( name ) || irc_channel_by_name( irc, name ) )
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       
49        irc->channels = g_slist_append( irc->channels, ic );
50       
51        set_add( &ic->set, "auto_join", "false", set_eval_bool, ic );
52       
53        s = set_add( &ic->set, "type", "control", set_eval_channel_type, ic );
54        s->flags |= SET_NOSAVE;    /* Layer violation (XML format detail) */
55       
56        if( name[0] == '&' )
57                set_setstr( &ic->set, "type", "control" );
58        else /* if( name[0] == '#' ) */
59                set_setstr( &ic->set, "type", "chat" );
60       
61        return ic;
62}
63
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               
72                if( irc_channel_name_cmp( name, ic->name ) == 0 )
73                        return ic;
74        }
75       
76        return NULL;
77}
78
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
119int irc_channel_free( irc_channel_t *ic )
120{
121        irc_t *irc;
122        GSList *l;
123       
124        if( ic == NULL )
125                return 0;
126        irc = ic->irc;
127       
128        if( ic->flags & IRC_CHANNEL_JOINED )
129                irc_channel_del_user( ic, irc->user, IRC_CDU_KICK, "Cleaning up channel" );
130       
131        if( ic->f->_free )
132                ic->f->_free( ic );
133       
134        while( ic->set )
135                set_del( &ic->set, ic->set->key );
136       
137        irc->channels = g_slist_remove( irc->channels, ic );
138        while( ic->users )
139        {
140                g_free( ic->users->data );
141                ic->users = g_slist_remove( ic->users, ic->users->data );
142        }
143       
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       
152        if( ic->pastebuf_timer ) b_event_remove( ic->pastebuf_timer );
153       
154        g_free( ic->name );
155        g_free( ic->topic );
156        g_free( ic->topic_who );
157        g_free( ic );
158       
159        return 1;
160}
161
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
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;
203        else if( ic != ic->irc->default_channel && strcmp( value, "chat" ) == 0 )
204                new = &irc_channel_im_chat_funcs;
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
220int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu )
221{
222        irc_channel_user_t *icu;
223       
224        if( irc_channel_has_user( ic, iu ) )
225                return 0;
226       
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 );
231       
232        irc_channel_update_ops( ic, set_getstr( &ic->irc->b->set, "ops" ) );
233       
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
243int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu, irc_channel_del_user_type_t type, const char *msg )
244{
245        irc_channel_user_t *icu;
246       
247        if( !( icu = irc_channel_has_user( ic, iu ) ) )
248                return 0;
249       
250        ic->users = g_slist_remove( ic->users, icu );
251        g_free( icu );
252       
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 )
257                irc_send_part( ic, iu, msg );
258        else if( type == IRC_CDU_KICK )
259                irc_send_kick( ic, iu, ic->irc->root, msg );
260       
261        if( iu == ic->irc->user )
262        {
263                ic->flags &= ~IRC_CHANNEL_JOINED;
264               
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                {
271                        irc_channel_free_soon( ic );
272                }
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                }
283        }
284       
285        return 1;
286}
287
288irc_channel_user_t *irc_channel_has_user( irc_channel_t *ic, irc_user_t *iu )
289{
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;
301}
302
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
340int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_t *iu )
341{
342        g_free( ic->topic );
343        ic->topic = g_strdup( topic );
344       
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       
353        if( ic->flags & IRC_CHANNEL_JOINED )
354                irc_send_topic( ic, TRUE );
355       
356        return 1;
357}
358
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       
363        if( !icu || icu->flags == flags )
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
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
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                       
434                        if( !aj || ( ic->flags & IRC_CHANNEL_JOINED ) )
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
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
468gboolean irc_channel_name_ok( const char *name_ )
469{
470        const unsigned char *name = (unsigned char*) name_;
471        int i;
472       
473        if( name_[0] == '\0' )
474                return FALSE;
475       
476        /* Check if the first character is in CTYPES (#&) */
477        if( strchr( CTYPES, name_[0] ) == NULL )
478                return FALSE;
479       
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
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
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]];
535}
536
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
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
569/* Channel-type dependent functions, for control channels: */
570static gboolean control_channel_privmsg( irc_channel_t *ic, const char *msg )
571{
572        irc_t *irc = ic->irc;
573        irc_user_t *iu;
574        const char *s;
575       
576        /* Scan for non-whitespace chars followed by a colon: */
577        for( s = msg; *s && !g_ascii_isspace( *s ) && *s != ':' && *s != ','; s ++ ) {}
578       
579        if( *s == ':' || *s == ',' )
580        {
581                char to[s-msg+1];
582               
583                memset( to, 0, sizeof( to ) );
584                strncpy( to, msg, s - msg );
585                while( *(++s) && g_ascii_isspace( *s ) ) {}
586                msg = s;
587               
588                if( !( iu = irc_user_by_name( irc, to ) ) )
589                        irc_channel_printf( ic, "User does not exist: %s", to );
590                else
591                        ic->last_target = iu;
592        }
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;
596        else
597                iu = irc->root;
598       
599        if( iu && iu->f->privmsg )
600        {
601                iu->last_channel = ic;
602                iu->f->privmsg( iu, msg );
603        }
604       
605        return TRUE;
606}
607
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
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 );
631static char *set_eval_by_protocol( set_t *set, char *value );
632static char *set_eval_show_users( set_t *set, char *value );
633
634static gboolean control_channel_init( irc_channel_t *ic )
635{
636        struct irc_control_channel *icc;
637       
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 );
641        set_add( &ic->set, "protocol", NULL, set_eval_by_protocol, ic );
642       
643        /* When changing the default, also change it below. */
644        set_add( &ic->set, "show_users", "online+,away", set_eval_show_users, ic );
645       
646        ic->data = icc = g_new0( struct irc_control_channel, 1 );
647        icc->type = IRC_CC_TYPE_DEFAULT;
648       
649        /* Have to run the evaluator to initialize icc->modes. */
650        set_setstr( &ic->set, "show_users", "online+,away" );
651       
652        /* For scripts that care. */
653        irc_channel_set_mode( ic, "+C" );
654       
655        return TRUE;
656}
657
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
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;
675        if( ( icc->type & IRC_CC_TYPE_MASK ) == IRC_CC_TYPE_ACCOUNT )
676                bee_irc_channel_update( ic->irc, ic, NULL );
677       
678        return g_strdup( acc->tag );
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;
685        char *s;
686       
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;
706        else
707                return SET_INVALID;
708       
709        bee_irc_channel_update( ic->irc, ic, NULL );
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;
717       
718        icc->group = bee_group_by_name( ic->irc->b, value, TRUE );
719        if( ( icc->type & IRC_CC_TYPE_MASK ) == IRC_CC_TYPE_GROUP )
720                bee_irc_channel_update( ic->irc, ic, NULL );
721       
722        return g_strdup( icc->group->name );
723}
724
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;
735        if( ( icc->type & IRC_CC_TYPE_MASK ) == IRC_CC_TYPE_PROTOCOL )
736                bee_irc_channel_update( ic->irc, ic, NULL );
737       
738        return value;
739}
740
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
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;
791        gboolean ret = FALSE;
792       
793        if( iu->bu == NULL )
794                return FALSE;
795       
796        switch( icc->type & IRC_CC_TYPE_MASK )
797        {
798        case IRC_CC_TYPE_GROUP:
799                ret = iu->bu->group == icc->group;
800                break;
801        case IRC_CC_TYPE_ACCOUNT:
802                ret = iu->bu->ic->acc == icc->account;
803                break;
804        case IRC_CC_TYPE_PROTOCOL:
805                ret = iu->bu->ic->acc->prpl == icc->protocol;
806                break;
807        case IRC_CC_TYPE_DEFAULT:
808        default:
809                ret = TRUE;
810                break;
811        }
812       
813        if( icc->type & IRC_CC_TYPE_INVERT )
814                ret = !ret;
815       
816        return ret;
817}
818
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" );
826        set_del( &ic->set, "protocol" );
827        set_del( &ic->set, "show_users" );
828       
829        g_free( icc );
830        ic->data = NULL;
831       
832        /* For scripts that care. */
833        irc_channel_set_mode( ic, "-C" );
834       
835        return TRUE;
836}
837
838static const struct irc_channel_funcs control_channel_funcs = {
839        control_channel_privmsg,
840        control_channel_join,
841        NULL,
842        NULL,
843        control_channel_invite,
844       
845        control_channel_init,
846        control_channel_free,
847};
Note: See TracBrowser for help on using the repository browser.