source: irc_channel.c @ a5c6ebd

Last change on this file since a5c6ebd was a5c6ebd, checked in by Wilmer van der Gaast <wilmer@…>, at 2013-02-21T18:09:19Z

! modified for control channel fill_by setting to for example create a
separate control channel with all contacts *not* in a certain group/from
a certain IM account/network, etc.

  • 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-2010 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* The IRC-based UI - Representing (virtual) channels.                  */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23  Suite 330, Boston, MA  02111-1307  USA
24*/
25
26#include "bitlbee.h"
27
28static 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.