source: irc_channel.c @ 57b534b

Last change on this file since 57b534b was 0e788f5, checked in by Wilmer van der Gaast <wilmer@…>, at 2013-02-21T19:15:59Z

I'm still bored on a long flight. Wrote a script to automatically update
my copyright mentions since some were getting pretty stale. Left files not
touched since before 2012 alone so that this change doesn't touch almost
EVERY source file.

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