source: irc_channel.c @ 03e5fb7

Last change on this file since 03e5fb7 was 180ab31, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-21T19:34:17Z

Added some neat whatsnew code that keeps track of the newest version of
BitlBee used by a user, and if it looks like s/he hasn't used this one
before, show a list of new features that may be interesting.

Since I don't think im.bitlbee.org users will read any changelogs ever,
this is probably not a bad idea. If you hate it, the following command
should get rid of it forever: set last_version 9999999

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