source: protocols/nogaim.c @ ca0981a

Last change on this file since ca0981a was ae3dc99, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-04-24T17:02:07Z

Merging stuff from mainline (1.2.6).

  • Property mode set to 100644
File size: 32.8 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/*
8 * nogaim
9 *
10 * Gaim without gaim - for BitlBee
11 *
12 * This file contains functions called by the Gaim IM-modules. It's written
13 * from scratch for BitlBee and doesn't contain any code from Gaim anymore
14 * (except for the function names).
15 */
16
17/*
18  This program is free software; you can redistribute it and/or modify
19  it under the terms of the GNU General Public License as published by
20  the Free Software Foundation; either version 2 of the License, or
21  (at your option) any later version.
22
23  This program is distributed in the hope that it will be useful,
24  but WITHOUT ANY WARRANTY; without even the implied warranty of
25  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26  GNU General Public License for more details.
27
28  You should have received a copy of the GNU General Public License with
29  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
30  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
31  Suite 330, Boston, MA  02111-1307  USA
32*/
33
34#define BITLBEE_CORE
35#include <ctype.h>
36
37#include "nogaim.h"
38#include "chat.h"
39
40static int remove_chat_buddy_silent( struct groupchat *b, const char *handle );
41static char *format_timestamp( irc_t *irc, time_t msg_ts );
42
43GSList *connections;
44
45#ifdef WITH_PLUGINS
46gboolean load_plugin(char *path)
47{
48        void (*init_function) (void);
49       
50        GModule *mod = g_module_open(path, G_MODULE_BIND_LAZY);
51
52        if(!mod) {
53                log_message(LOGLVL_ERROR, "Can't find `%s', not loading (%s)\n", path, g_module_error());
54                return FALSE;
55        }
56
57        if(!g_module_symbol(mod,"init_plugin",(gpointer *) &init_function)) {
58                log_message(LOGLVL_WARNING, "Can't find function `init_plugin' in `%s'\n", path);
59                return FALSE;
60        }
61
62        init_function();
63
64        return TRUE;
65}
66
67void load_plugins(void)
68{
69        GDir *dir;
70        GError *error = NULL;
71
72        dir = g_dir_open(global.conf->plugindir, 0, &error);
73
74        if (dir) {
75                const gchar *entry;
76                char *path;
77
78                while ((entry = g_dir_read_name(dir))) {
79                        path = g_build_filename(global.conf->plugindir, entry, NULL);
80                        if(!path) {
81                                log_message(LOGLVL_WARNING, "Can't build path for %s\n", entry);
82                                continue;
83                        }
84
85                        load_plugin(path);
86
87                        g_free(path);
88                }
89
90                g_dir_close(dir);
91        }
92}
93#endif
94
95/* nogaim.c */
96
97GList *protocols = NULL;
98 
99void register_protocol (struct prpl *p)
100{
101        int i;
102        gboolean refused = global.conf->protocols != NULL;
103 
104        for (i = 0; global.conf->protocols && global.conf->protocols[i]; i++)
105        {
106                if (g_strcasecmp(p->name, global.conf->protocols[i]) == 0)
107                        refused = FALSE;
108        }
109
110        if (refused)
111                log_message(LOGLVL_WARNING, "Protocol %s disabled\n", p->name);
112        else
113                protocols = g_list_append(protocols, p);
114}
115
116struct prpl *find_protocol(const char *name)
117{
118        GList *gl;
119       
120        for( gl = protocols; gl; gl = gl->next )
121        {
122                struct prpl *proto = gl->data;
123               
124                if( g_strcasecmp( proto->name, name ) == 0 )
125                        return proto;
126        }
127       
128        return NULL;
129}
130
131/* nogaim.c */
132void nogaim_init()
133{
134        extern void msn_initmodule();
135        extern void oscar_initmodule();
136        extern void byahoo_initmodule();
137        extern void jabber_initmodule();
138        extern void twitter_initmodule();
139        extern void purple_initmodule();
140
141#ifdef WITH_MSN
142        msn_initmodule();
143#endif
144
145#ifdef WITH_OSCAR
146        oscar_initmodule();
147#endif
148       
149#ifdef WITH_YAHOO
150        byahoo_initmodule();
151#endif
152       
153#ifdef WITH_JABBER
154        jabber_initmodule();
155#endif
156
157#ifdef WITH_TWITTER
158        twitter_initmodule();
159#endif
160       
161#ifdef WITH_PURPLE
162        purple_initmodule();
163#endif
164
165#ifdef WITH_PLUGINS
166        load_plugins();
167#endif
168}
169
170GSList *get_connections() { return connections; }
171
172/* multi.c */
173
174struct im_connection *imcb_new( account_t *acc )
175{
176        struct im_connection *ic;
177       
178        ic = g_new0( struct im_connection, 1 );
179       
180        ic->irc = acc->irc;
181        ic->acc = acc;
182        acc->ic = ic;
183       
184        connections = g_slist_append( connections, ic );
185       
186        return( ic );
187}
188
189void imc_free( struct im_connection *ic )
190{
191        account_t *a;
192       
193        /* Destroy the pointer to this connection from the account list */
194        for( a = ic->irc->accounts; a; a = a->next )
195                if( a->ic == ic )
196                {
197                        a->ic = NULL;
198                        break;
199                }
200       
201        connections = g_slist_remove( connections, ic );
202        g_free( ic );
203}
204
205static void serv_got_crap( struct im_connection *ic, char *format, ... )
206{
207        va_list params;
208        char *text;
209        account_t *a;
210       
211        va_start( params, format );
212        text = g_strdup_vprintf( format, params );
213        va_end( params );
214
215        if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) ||
216            ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) )
217                strip_html( text );
218       
219        /* Try to find a different connection on the same protocol. */
220        for( a = ic->irc->accounts; a; a = a->next )
221                if( a->prpl == ic->acc->prpl && a->ic != ic )
222                        break;
223       
224        /* If we found one, include the screenname in the message. */
225        if( a )
226                irc_usermsg( ic->irc, "%s(%s) - %s", ic->acc->prpl->name, ic->acc->user, text );
227        else
228                irc_usermsg( ic->irc, "%s - %s", ic->acc->prpl->name, text );
229       
230        g_free( text );
231}
232
233void imcb_log( struct im_connection *ic, char *format, ... )
234{
235        va_list params;
236        char *text;
237       
238        va_start( params, format );
239        text = g_strdup_vprintf( format, params );
240        va_end( params );
241       
242        if( ic->flags & OPT_LOGGED_IN )
243                serv_got_crap( ic, "%s", text );
244        else
245                serv_got_crap( ic, "Logging in: %s", text );
246       
247        g_free( text );
248}
249
250void imcb_error( struct im_connection *ic, char *format, ... )
251{
252        va_list params;
253        char *text;
254       
255        va_start( params, format );
256        text = g_strdup_vprintf( format, params );
257        va_end( params );
258       
259        if( ic->flags & OPT_LOGGED_IN )
260                serv_got_crap( ic, "Error: %s", text );
261        else
262                serv_got_crap( ic, "Couldn't log in: %s", text );
263       
264        g_free( text );
265}
266
267static gboolean send_keepalive( gpointer d, gint fd, b_input_condition cond )
268{
269        struct im_connection *ic = d;
270       
271        if( ic->acc->prpl->keepalive )
272                ic->acc->prpl->keepalive( ic );
273       
274        return TRUE;
275}
276
277void imcb_connected( struct im_connection *ic )
278{
279        irc_t *irc = ic->irc;
280        struct chat *c;
281        user_t *u;
282       
283        /* MSN servers sometimes redirect you to a different server and do
284           the whole login sequence again, so these "late" calls to this
285           function should be handled correctly. (IOW, ignored) */
286        if( ic->flags & OPT_LOGGED_IN )
287                return;
288       
289        u = user_find( ic->irc, ic->irc->nick );
290       
291        imcb_log( ic, "Logged in" );
292       
293        ic->keepalive = b_timeout_add( 60000, send_keepalive, ic );
294        ic->flags |= OPT_LOGGED_IN;
295       
296        /* Necessary to send initial presence status, even if we're not away. */
297        imc_away_send_update( ic );
298       
299        /* Apparently we're connected successfully, so reset the
300           exponential backoff timer. */
301        ic->acc->auto_reconnect_delay = 0;
302       
303        for( c = irc->chatrooms; c; c = c->next )
304        {
305                if( c->acc != ic->acc )
306                        continue;
307               
308                if( set_getbool( &c->set, "auto_join" ) )
309                        chat_join( irc, c, NULL );
310        }
311}
312
313gboolean auto_reconnect( gpointer data, gint fd, b_input_condition cond )
314{
315        account_t *a = data;
316       
317        a->reconnect = 0;
318        account_on( a->irc, a );
319       
320        return( FALSE );        /* Only have to run the timeout once */
321}
322
323void cancel_auto_reconnect( account_t *a )
324{
325        b_event_remove( a->reconnect );
326        a->reconnect = 0;
327}
328
329void imc_logout( struct im_connection *ic, int allow_reconnect )
330{
331        irc_t *irc = ic->irc;
332        user_t *t, *u;
333        account_t *a;
334        int delay;
335       
336        /* Nested calls might happen sometimes, this is probably the best
337           place to catch them. */
338        if( ic->flags & OPT_LOGGING_OUT )
339                return;
340        else
341                ic->flags |= OPT_LOGGING_OUT;
342       
343        imcb_log( ic, "Signing off.." );
344       
345        b_event_remove( ic->keepalive );
346        ic->keepalive = 0;
347        ic->acc->prpl->logout( ic );
348        b_event_remove( ic->inpa );
349       
350        g_free( ic->away );
351        ic->away = NULL;
352       
353        u = irc->users;
354        while( u )
355        {
356                if( u->ic == ic )
357                {
358                        t = u->next;
359                        user_del( irc, u->nick );
360                        u = t;
361                }
362                else
363                        u = u->next;
364        }
365       
366        query_del_by_conn( ic->irc, ic );
367       
368        for( a = irc->accounts; a; a = a->next )
369                if( a->ic == ic )
370                        break;
371       
372        if( !a )
373        {
374                /* Uhm... This is very sick. */
375        }
376        else if( allow_reconnect && set_getbool( &irc->set, "auto_reconnect" ) &&
377                 set_getbool( &a->set, "auto_reconnect" ) &&
378                 ( delay = account_reconnect_delay( a ) ) > 0 )
379        {
380                imcb_log( ic, "Reconnecting in %d seconds..", delay );
381                a->reconnect = b_timeout_add( delay * 1000, auto_reconnect, a );
382        }
383       
384        imc_free( ic );
385}
386
387
388/* dialogs.c */
389
390void imcb_ask( struct im_connection *ic, char *msg, void *data,
391               query_callback doit, query_callback dont )
392{
393        query_add( ic->irc, ic, msg, doit, dont, data );
394}
395
396
397/* list.c */
398
399void imcb_add_buddy( struct im_connection *ic, const char *handle, const char *group )
400{
401        user_t *u;
402        char nick[MAX_NICK_LENGTH+1], *s;
403        irc_t *irc = ic->irc;
404       
405        if( user_findhandle( ic, handle ) )
406        {
407                if( set_getbool( &irc->set, "debug" ) )
408                        imcb_log( ic, "User already exists, ignoring add request: %s", handle );
409               
410                return;
411               
412                /* Buddy seems to exist already. Let's ignore this request then...
413                   Eventually subsequent calls to this function *should* be possible
414                   when a buddy is in multiple groups. But for now BitlBee doesn't
415                   even support groups so let's silently ignore this for now. */
416        }
417       
418        memset( nick, 0, MAX_NICK_LENGTH + 1 );
419        strcpy( nick, nick_get( ic->acc, handle ) );
420       
421        u = user_add( ic->irc, nick );
422       
423//      if( !realname || !*realname ) realname = nick;
424//      u->realname = g_strdup( realname );
425       
426        if( ( s = strchr( handle, '@' ) ) )
427        {
428                u->host = g_strdup( s + 1 );
429                u->user = g_strndup( handle, s - handle );
430        }
431        else if( ic->acc->server )
432        {
433                u->host = g_strdup( ic->acc->server );
434                u->user = g_strdup( handle );
435               
436                /* s/ /_/ ... important for AOL screennames */
437                for( s = u->user; *s; s ++ )
438                        if( *s == ' ' )
439                                *s = '_';
440        }
441        else
442        {
443                u->host = g_strdup( ic->acc->prpl->name );
444                u->user = g_strdup( handle );
445        }
446       
447        u->ic = ic;
448        u->handle = g_strdup( handle );
449        if( group ) u->group = g_strdup( group );
450        u->send_handler = buddy_send_handler;
451        u->last_typing_notice = 0;
452}
453
454struct buddy *imcb_find_buddy( struct im_connection *ic, char *handle )
455{
456        static struct buddy b[1];
457        user_t *u;
458       
459        u = user_findhandle( ic, handle );
460       
461        if( !u )
462                return( NULL );
463       
464        memset( b, 0, sizeof( b ) );
465        strncpy( b->name, handle, 80 );
466        strncpy( b->show, u->realname, BUDDY_ALIAS_MAXLEN );
467        b->present = u->online;
468        b->ic = u->ic;
469       
470        return( b );
471}
472
473void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char *realname )
474{
475        user_t *u = user_findhandle( ic, handle );
476        char *set;
477       
478        if( !u || !realname ) return;
479       
480        if( g_strcasecmp( u->realname, realname ) != 0 )
481        {
482                if( u->realname != u->nick ) g_free( u->realname );
483               
484                u->realname = g_strdup( realname );
485               
486                if( ( ic->flags & OPT_LOGGED_IN ) && set_getbool( &ic->irc->set, "display_namechanges" ) )
487                        imcb_log( ic, "User `%s' changed name to `%s'", u->nick, u->realname );
488        }
489       
490        set = set_getstr( &ic->acc->set, "nick_source" );
491        if( strcmp( set, "handle" ) != 0 )
492        {
493                char *name = g_strdup( realname );
494               
495                if( strcmp( set, "first_name" ) == 0 )
496                {
497                        int i;
498                        for( i = 0; name[i] && !isspace( name[i] ); i ++ ) {}
499                        name[i] = '\0';
500                }
501               
502                imcb_buddy_nick_hint( ic, handle, name );
503               
504                g_free( name );
505        }
506}
507
508void imcb_remove_buddy( struct im_connection *ic, const char *handle, char *group )
509{
510        user_t *u;
511       
512        if( ( u = user_findhandle( ic, handle ) ) )
513                user_del( ic->irc, u->nick );
514}
515
516/* Mainly meant for ICQ (and now also for Jabber conferences) to allow IM
517   modules to suggest a nickname for a handle. */
518void imcb_buddy_nick_hint( struct im_connection *ic, const char *handle, const char *nick )
519{
520        user_t *u = user_findhandle( ic, handle );
521        char newnick[MAX_NICK_LENGTH+1], *orig_nick;
522       
523        if( u && !u->online && !nick_saved( ic->acc, handle ) )
524        {
525                /* Only do this if the person isn't online yet (which should
526                   be the case if we just added it) and if the user hasn't
527                   assigned a nickname to this buddy already. */
528               
529                strncpy( newnick, nick, MAX_NICK_LENGTH );
530                newnick[MAX_NICK_LENGTH] = 0;
531               
532                /* Some processing to make sure this string is a valid IRC nickname. */
533                nick_strip( newnick );
534                if( set_getbool( &ic->irc->set, "lcnicks" ) )
535                        nick_lc( newnick );
536               
537                if( strcmp( u->nick, newnick ) != 0 )
538                {
539                        /* Only do this if newnick is different from the current one.
540                           If rejoining a channel, maybe we got this nick already
541                           (and dedupe would only add an underscore. */
542                        nick_dedupe( ic->acc, handle, newnick );
543                       
544                        /* u->nick will be freed halfway the process, so it can't be
545                           passed as an argument. */
546                        orig_nick = g_strdup( u->nick );
547                        user_rename( ic->irc, orig_nick, newnick );
548                        g_free( orig_nick );
549                }
550        }
551}
552
553
554struct imcb_ask_cb_data
555{
556        struct im_connection *ic;
557        char *handle;
558};
559
560static void imcb_ask_auth_cb_no( void *data )
561{
562        struct imcb_ask_cb_data *cbd = data;
563       
564        cbd->ic->acc->prpl->auth_deny( cbd->ic, cbd->handle );
565       
566        g_free( cbd->handle );
567        g_free( cbd );
568}
569
570static void imcb_ask_auth_cb_yes( void *data )
571{
572        struct imcb_ask_cb_data *cbd = data;
573       
574        cbd->ic->acc->prpl->auth_allow( cbd->ic, cbd->handle );
575       
576        g_free( cbd->handle );
577        g_free( cbd );
578}
579
580void imcb_ask_auth( struct im_connection *ic, const char *handle, const char *realname )
581{
582        struct imcb_ask_cb_data *data = g_new0( struct imcb_ask_cb_data, 1 );
583        char *s, *realname_ = NULL;
584       
585        if( realname != NULL )
586                realname_ = g_strdup_printf( " (%s)", realname );
587       
588        s = g_strdup_printf( "The user %s%s wants to add you to his/her buddy list.",
589                             handle, realname_ ?: "" );
590       
591        g_free( realname_ );
592       
593        data->ic = ic;
594        data->handle = g_strdup( handle );
595        query_add( ic->irc, ic, s, imcb_ask_auth_cb_yes, imcb_ask_auth_cb_no, data );
596}
597
598
599static void imcb_ask_add_cb_no( void *data )
600{
601        g_free( ((struct imcb_ask_cb_data*)data)->handle );
602        g_free( data );
603}
604
605static void imcb_ask_add_cb_yes( void *data )
606{
607        struct imcb_ask_cb_data *cbd = data;
608       
609        cbd->ic->acc->prpl->add_buddy( cbd->ic, cbd->handle, NULL );
610       
611        return imcb_ask_add_cb_no( data );
612}
613
614void imcb_ask_add( struct im_connection *ic, const char *handle, const char *realname )
615{
616        struct imcb_ask_cb_data *data = g_new0( struct imcb_ask_cb_data, 1 );
617        char *s;
618       
619        /* TODO: Make a setting for this! */
620        if( user_findhandle( ic, handle ) != NULL )
621                return;
622       
623        s = g_strdup_printf( "The user %s is not in your buddy list yet. Do you want to add him/her now?", handle );
624       
625        data->ic = ic;
626        data->handle = g_strdup( handle );
627        query_add( ic->irc, ic, s, imcb_ask_add_cb_yes, imcb_ask_add_cb_no, data );
628}
629
630
631/* server.c */                   
632
633void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message )
634{
635        user_t *u;
636        int oa, oo;
637       
638        u = user_findhandle( ic, (char*) handle );
639       
640        if( !u )
641        {
642                if( g_strcasecmp( set_getstr( &ic->irc->set, "handle_unknown" ), "add" ) == 0 )
643                {
644                        imcb_add_buddy( ic, (char*) handle, NULL );
645                        u = user_findhandle( ic, (char*) handle );
646                }
647                else
648                {
649                        if( set_getbool( &ic->irc->set, "debug" ) || g_strcasecmp( set_getstr( &ic->irc->set, "handle_unknown" ), "ignore" ) != 0 )
650                        {
651                                imcb_log( ic, "imcb_buddy_status() for unknown handle %s:", handle );
652                                imcb_log( ic, "flags = %d, state = %s, message = %s", flags,
653                                          state ? state : "NULL", message ? message : "NULL" );
654                        }
655                       
656                        return;
657                }
658        }
659       
660        oa = u->away != NULL;
661        oo = u->online;
662       
663        g_free( u->away );
664        g_free( u->status_msg );
665        u->away = u->status_msg = NULL;
666       
667        if( ( flags & OPT_LOGGED_IN ) && !u->online )
668        {
669                irc_spawn( ic->irc, u );
670                u->online = 1;
671        }
672        else if( !( flags & OPT_LOGGED_IN ) && u->online )
673        {
674                struct groupchat *c;
675               
676                irc_kill( ic->irc, u );
677                u->online = 0;
678               
679                /* Remove him/her from the groupchats to prevent PART messages after he/she QUIT already */
680                for( c = ic->groupchats; c; c = c->next )
681                        remove_chat_buddy_silent( c, handle );
682        }
683       
684        if( flags & OPT_AWAY )
685        {
686                if( state && message )
687                {
688                        u->away = g_strdup_printf( "%s (%s)", state, message );
689                }
690                else if( state )
691                {
692                        u->away = g_strdup( state );
693                }
694                else if( message )
695                {
696                        u->away = g_strdup( message );
697                }
698                else
699                {
700                        u->away = g_strdup( "Away" );
701                }
702        }
703        else
704        {
705                u->status_msg = g_strdup( message );
706        }
707       
708        /* LISPy... */
709        if( ( set_getbool( &ic->irc->set, "away_devoice" ) ) &&         /* Don't do a thing when user doesn't want it */
710            ( u->online ) &&                                            /* Don't touch offline people */
711            ( ( ( u->online != oo ) && !u->away ) ||                    /* Voice joining people */
712              ( ( u->online == oo ) && ( oa == !u->away ) ) ) )         /* (De)voice people changing state */
713        {
714                char *from;
715               
716                if( set_getbool( &ic->irc->set, "simulate_netsplit" ) )
717                {
718                        from = g_strdup( ic->irc->myhost );
719                }
720                else
721                {
722                        from = g_strdup_printf( "%s!%s@%s", ic->irc->mynick, ic->irc->mynick,
723                                                            ic->irc->myhost );
724                }
725                irc_write( ic->irc, ":%s MODE %s %cv %s", from, ic->irc->channel,
726                                                          u->away?'-':'+', u->nick );
727                g_free( from );
728        }
729}
730
731void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at )
732{
733        irc_t *irc = ic->irc;
734        char *wrapped, *ts = NULL;
735        user_t *u;
736       
737        u = user_findhandle( ic, handle );
738       
739        if( !u )
740        {
741                char *h = set_getstr( &irc->set, "handle_unknown" );
742               
743                if( g_strcasecmp( h, "ignore" ) == 0 )
744                {
745                        if( set_getbool( &irc->set, "debug" ) )
746                                imcb_log( ic, "Ignoring message from unknown handle %s", handle );
747                       
748                        return;
749                }
750                else if( g_strncasecmp( h, "add", 3 ) == 0 )
751                {
752                        int private = set_getbool( &irc->set, "private" );
753                       
754                        if( h[3] )
755                        {
756                                if( g_strcasecmp( h + 3, "_private" ) == 0 )
757                                        private = 1;
758                                else if( g_strcasecmp( h + 3, "_channel" ) == 0 )
759                                        private = 0;
760                        }
761                       
762                        imcb_add_buddy( ic, handle, NULL );
763                        u = user_findhandle( ic, handle );
764                        u->is_private = private;
765                }
766                else
767                {
768                        imcb_log( ic, "Message from unknown handle %s:", handle );
769                        u = user_find( irc, irc->mynick );
770                }
771        }
772       
773        if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) ||
774            ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) )
775                strip_html( msg );
776       
777        if( set_getbool( &ic->irc->set, "display_timestamps" ) &&
778            ( ts = format_timestamp( irc, sent_at ) ) )
779        {
780                char *new = g_strconcat( ts, msg, NULL );
781                g_free( ts );
782                ts = msg = new;
783        }
784       
785        wrapped = word_wrap( msg, 425 );
786        irc_msgfrom( irc, u->nick, wrapped );
787        g_free( wrapped );
788        g_free( ts );
789}
790
791void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags )
792{
793        user_t *u;
794       
795        if( !set_getbool( &ic->irc->set, "typing_notice" ) )
796                return;
797       
798        if( ( u = user_findhandle( ic, handle ) ) )
799        {
800                char buf[256]; 
801               
802                g_snprintf( buf, 256, "\1TYPING %d\1", ( flags >> 8 ) & 3 );
803                irc_privmsg( ic->irc, u, "PRIVMSG", ic->irc->nick, NULL, buf );
804        }
805}
806
807struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle )
808{
809        struct groupchat *c;
810       
811        /* This one just creates the conversation structure, user won't see anything yet */
812       
813        if( ic->groupchats )
814        {
815                for( c = ic->groupchats; c->next; c = c->next );
816                c = c->next = g_new0( struct groupchat, 1 );
817        }
818        else
819                ic->groupchats = c = g_new0( struct groupchat, 1 );
820       
821        c->ic = ic;
822        c->title = g_strdup( handle );
823        c->channel = g_strdup_printf( "&chat_%03d", ic->irc->c_id++ );
824        c->topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title );
825       
826        if( set_getbool( &ic->irc->set, "debug" ) )
827                imcb_log( ic, "Creating new conversation: (id=%p,handle=%s)", c, handle );
828       
829        return c;
830}
831
832void imcb_chat_name_hint( struct groupchat *c, const char *name )
833{
834        if( !c->joined )
835        {
836                struct im_connection *ic = c->ic;
837                char stripped[MAX_NICK_LENGTH+1], *full_name;
838               
839                strncpy( stripped, name, MAX_NICK_LENGTH );
840                stripped[MAX_NICK_LENGTH] = '\0';
841                nick_strip( stripped );
842                if( set_getbool( &ic->irc->set, "lcnicks" ) )
843                        nick_lc( stripped );
844               
845                full_name = g_strdup_printf( "&%s", stripped );
846               
847                if( stripped[0] &&
848                    nick_cmp( stripped, ic->irc->channel + 1 ) != 0 &&
849                    irc_chat_by_channel( ic->irc, full_name ) == NULL )
850                {
851                        g_free( c->channel );
852                        c->channel = full_name;
853                }
854                else
855                {
856                        g_free( full_name );
857                }
858        }
859}
860
861void imcb_chat_free( struct groupchat *c )
862{
863        struct im_connection *ic = c->ic;
864        struct groupchat *l;
865        GList *ir;
866       
867        if( set_getbool( &ic->irc->set, "debug" ) )
868                imcb_log( ic, "You were removed from conversation %p", c );
869       
870        if( c )
871        {
872                if( c->joined )
873                {
874                        user_t *u, *r;
875                       
876                        r = user_find( ic->irc, ic->irc->mynick );
877                        irc_privmsg( ic->irc, r, "PRIVMSG", c->channel, "", "Cleaning up channel, bye!" );
878                       
879                        u = user_find( ic->irc, ic->irc->nick );
880                        irc_kick( ic->irc, u, c->channel, r );
881                        /* irc_part( ic->irc, u, c->channel ); */
882                }
883               
884                /* Find the previous chat in the linked list. */
885                for( l = ic->groupchats; l && l->next != c; l = l->next );
886               
887                if( l )
888                        l->next = c->next;
889                else
890                        ic->groupchats = c->next;
891               
892                for( ir = c->in_room; ir; ir = ir->next )
893                        g_free( ir->data );
894                g_list_free( c->in_room );
895                g_free( c->channel );
896                g_free( c->title );
897                g_free( c->topic );
898                g_free( c );
899        }
900}
901
902void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t flags, time_t sent_at )
903{
904        struct im_connection *ic = c->ic;
905        char *wrapped;
906        user_t *u;
907       
908        /* Gaim sends own messages through this too. IRC doesn't want this, so kill them */
909        if( g_strcasecmp( who, ic->acc->user ) == 0 )
910                return;
911       
912        u = user_findhandle( ic, who );
913       
914        if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) ||
915            ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) )
916                strip_html( msg );
917       
918        wrapped = word_wrap( msg, 425 );
919        if( c && u )
920        {
921                char *ts = NULL;
922                if( set_getbool( &ic->irc->set, "display_timestamps" ) )
923                        ts = format_timestamp( ic->irc, sent_at );
924                irc_privmsg( ic->irc, u, "PRIVMSG", c->channel, ts ? : "", wrapped );
925                g_free( ts );
926        }
927        else
928        {
929                imcb_log( ic, "Message from/to conversation %s@%p (unknown conv/user): %s", who, c, wrapped );
930        }
931        g_free( wrapped );
932}
933
934void imcb_chat_log( struct groupchat *c, char *format, ... )
935{
936        irc_t *irc = c->ic->irc;
937        va_list params;
938        char *text;
939        user_t *u;
940       
941        va_start( params, format );
942        text = g_strdup_vprintf( format, params );
943        va_end( params );
944       
945        u = user_find( irc, irc->mynick );
946       
947        irc_privmsg( irc, u, "PRIVMSG", c->channel, "System message: ", text );
948       
949        g_free( text );
950}
951
952void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at )
953{
954        struct im_connection *ic = c->ic;
955        user_t *u = NULL;
956       
957        if( who == NULL)
958                u = user_find( ic->irc, ic->irc->mynick );
959        else if( g_strcasecmp( who, ic->acc->user ) == 0 )
960                u = user_find( ic->irc, ic->irc->nick );
961        else
962                u = user_findhandle( ic, who );
963       
964        if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) ||
965            ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) )
966                strip_html( topic );
967       
968        g_free( c->topic );
969        c->topic = g_strdup( topic );
970       
971        if( c->joined && u )
972                irc_write( ic->irc, ":%s!%s@%s TOPIC %s :%s", u->nick, u->user, u->host, c->channel, topic );
973}
974
975
976/* buddy_chat.c */
977
978void imcb_chat_add_buddy( struct groupchat *b, const char *handle )
979{
980        user_t *u = user_findhandle( b->ic, handle );
981        int me = 0;
982       
983        if( set_getbool( &b->ic->irc->set, "debug" ) )
984                imcb_log( b->ic, "User %s added to conversation %p", handle, b );
985       
986        /* It might be yourself! */
987        if( b->ic->acc->prpl->handle_cmp( handle, b->ic->acc->user ) == 0 )
988        {
989                u = user_find( b->ic->irc, b->ic->irc->nick );
990                if( !b->joined )
991                        irc_join( b->ic->irc, u, b->channel );
992                b->joined = me = 1;
993        }
994       
995        /* Most protocols allow people to join, even when they're not in
996           your contact list. Try to handle that here */
997        if( !u )
998        {
999                imcb_add_buddy( b->ic, handle, NULL );
1000                u = user_findhandle( b->ic, handle );
1001        }
1002       
1003        /* Add the handle to the room userlist, if it's not 'me' */
1004        if( !me )
1005        {
1006                if( b->joined )
1007                        irc_join( b->ic->irc, u, b->channel );
1008                b->in_room = g_list_append( b->in_room, g_strdup( handle ) );
1009        }
1010}
1011
1012/* This function is one BIG hack... :-( EREWRITE */
1013void imcb_chat_remove_buddy( struct groupchat *b, const char *handle, const char *reason )
1014{
1015        user_t *u;
1016        int me = 0;
1017       
1018        if( set_getbool( &b->ic->irc->set, "debug" ) )
1019                imcb_log( b->ic, "User %s removed from conversation %p (%s)", handle, b, reason ? reason : "" );
1020       
1021        /* It might be yourself! */
1022        if( g_strcasecmp( handle, b->ic->acc->user ) == 0 )
1023        {
1024                if( b->joined == 0 )
1025                        return;
1026               
1027                u = user_find( b->ic->irc, b->ic->irc->nick );
1028                b->joined = 0;
1029                me = 1;
1030        }
1031        else
1032        {
1033                u = user_findhandle( b->ic, handle );
1034        }
1035       
1036        if( me || ( remove_chat_buddy_silent( b, handle ) && b->joined && u ) )
1037                irc_part( b->ic->irc, u, b->channel );
1038}
1039
1040static int remove_chat_buddy_silent( struct groupchat *b, const char *handle )
1041{
1042        GList *i;
1043       
1044        /* Find the handle in the room userlist and shoot it */
1045        i = b->in_room;
1046        while( i )
1047        {
1048                if( g_strcasecmp( handle, i->data ) == 0 )
1049                {
1050                        g_free( i->data );
1051                        b->in_room = g_list_remove( b->in_room, i->data );
1052                        return( 1 );
1053                }
1054               
1055                i = i->next;
1056        }
1057       
1058        return( 0 );
1059}
1060
1061
1062/* Misc. BitlBee stuff which shouldn't really be here */
1063
1064char *set_eval_away_devoice( set_t *set, char *value )
1065{
1066        irc_t *irc = set->data;
1067        int st;
1068       
1069        if( !is_bool( value ) )
1070                return SET_INVALID;
1071       
1072        st = bool2int( value );
1073       
1074        /* Horror.... */
1075       
1076        if( st != set_getbool( &irc->set, "away_devoice" ) )
1077        {
1078                char list[80] = "";
1079                user_t *u = irc->users;
1080                int i = 0, count = 0;
1081                char pm;
1082                char v[80];
1083               
1084                if( st )
1085                        pm = '+';
1086                else
1087                        pm = '-';
1088               
1089                while( u )
1090                {
1091                        if( u->ic && u->online && !u->away )
1092                        {
1093                                if( ( strlen( list ) + strlen( u->nick ) ) >= 79 )
1094                                {
1095                                        for( i = 0; i < count; v[i++] = 'v' ); v[i] = 0;
1096                                        irc_write( irc, ":%s MODE %s %c%s%s",
1097                                                   irc->myhost,
1098                                                   irc->channel, pm, v, list );
1099                                       
1100                                        *list = 0;
1101                                        count = 0;
1102                                }
1103                               
1104                                sprintf( list + strlen( list ), " %s", u->nick );
1105                                count ++;
1106                        }
1107                        u = u->next;
1108                }
1109               
1110                /* $v = 'v' x $i */
1111                for( i = 0; i < count; v[i++] = 'v' ); v[i] = 0;
1112                irc_write( irc, ":%s MODE %s %c%s%s", irc->myhost,
1113                                                            irc->channel, pm, v, list );
1114        }
1115       
1116        return value;
1117}
1118
1119char *set_eval_timezone( set_t *set, char *value )
1120{
1121        char *s;
1122       
1123        if( strcmp( value, "local" ) == 0 ||
1124            strcmp( value, "gmt" ) == 0 || strcmp( value, "utc" ) == 0 )
1125                return value;
1126       
1127        /* Otherwise: +/- at the beginning optional, then one or more numbers,
1128           possibly followed by a colon and more numbers. Don't bother bound-
1129           checking them since users are free to shoot themselves in the foot. */
1130        s = value;
1131        if( *s == '+' || *s == '-' )
1132                s ++;
1133       
1134        /* \d+ */
1135        if( !isdigit( *s ) )
1136                return SET_INVALID;
1137        while( *s && isdigit( *s ) ) s ++;
1138       
1139        /* EOS? */
1140        if( *s == '\0' )
1141                return value;
1142       
1143        /* Otherwise, colon */
1144        if( *s != ':' )
1145                return SET_INVALID;
1146        s ++;
1147       
1148        /* \d+ */
1149        if( !isdigit( *s ) )
1150                return SET_INVALID;
1151        while( *s && isdigit( *s ) ) s ++;
1152       
1153        /* EOS */
1154        return *s == '\0' ? value : SET_INVALID;
1155}
1156
1157static char *format_timestamp( irc_t *irc, time_t msg_ts )
1158{
1159        time_t now_ts = time( NULL );
1160        struct tm now, msg;
1161        char *set;
1162       
1163        /* If the timestamp is <= 0 or less than a minute ago, discard it as
1164           it doesn't seem to add to much useful info and/or might be noise. */
1165        if( msg_ts <= 0 || msg_ts > now_ts - 60 )
1166                return NULL;
1167       
1168        set = set_getstr( &irc->set, "timezone" );
1169        if( strcmp( set, "local" ) == 0 )
1170        {
1171                localtime_r( &now_ts, &now );
1172                localtime_r( &msg_ts, &msg );
1173        }
1174        else
1175        {
1176                int hr, min = 0, sign = 60;
1177               
1178                if( set[0] == '-' )
1179                {
1180                        sign *= -1;
1181                        set ++;
1182                }
1183                else if( set[0] == '+' )
1184                {
1185                        set ++;
1186                }
1187               
1188                if( sscanf( set, "%d:%d", &hr, &min ) >= 1 )
1189                {
1190                        msg_ts += sign * ( hr * 60 + min );
1191                        now_ts += sign * ( hr * 60 + min );
1192                }
1193               
1194                gmtime_r( &now_ts, &now );
1195                gmtime_r( &msg_ts, &msg );
1196        }
1197       
1198        if( msg.tm_year == now.tm_year && msg.tm_yday == now.tm_yday )
1199                return g_strdup_printf( "\x02[\x02\x02\x02%02d:%02d:%02d\x02]\x02 ",
1200                                        msg.tm_hour, msg.tm_min, msg.tm_sec );
1201        else
1202                return g_strdup_printf( "\x02[\x02\x02\x02%04d-%02d-%02d "
1203                                        "%02d:%02d:%02d\x02]\x02 ",
1204                                        msg.tm_year + 1900, msg.tm_mon, msg.tm_mday,
1205                                        msg.tm_hour, msg.tm_min, msg.tm_sec );
1206}
1207
1208/* The plan is to not allow straight calls to prpl functions anymore, but do
1209   them all from some wrappers. We'll start to define some down here: */
1210
1211int imc_buddy_msg( struct im_connection *ic, char *handle, char *msg, int flags )
1212{
1213        char *buf = NULL;
1214        int st;
1215       
1216        if( ( ic->flags & OPT_DOES_HTML ) && ( g_strncasecmp( msg, "<html>", 6 ) != 0 ) )
1217        {
1218                buf = escape_html( msg );
1219                msg = buf;
1220        }
1221       
1222        st = ic->acc->prpl->buddy_msg( ic, handle, msg, flags );
1223        g_free( buf );
1224       
1225        return st;
1226}
1227
1228int imc_chat_msg( struct groupchat *c, char *msg, int flags )
1229{
1230        char *buf = NULL;
1231       
1232        if( ( c->ic->flags & OPT_DOES_HTML ) && ( g_strncasecmp( msg, "<html>", 6 ) != 0 ) )
1233        {
1234                buf = escape_html( msg );
1235                msg = buf;
1236        }
1237       
1238        c->ic->acc->prpl->chat_msg( c, msg, flags );
1239        g_free( buf );
1240       
1241        return 1;
1242}
1243
1244static char *imc_away_state_find( GList *gcm, char *away, char **message );
1245
1246int imc_away_send_update( struct im_connection *ic )
1247{
1248        char *away, *msg = NULL;
1249       
1250        if( ic->acc->prpl->away_states == NULL ||
1251            ic->acc->prpl->set_away == NULL )
1252                return 0;
1253       
1254        away = set_getstr( &ic->acc->set, "away" ) ?
1255             : set_getstr( &ic->irc->set, "away" );
1256        if( away && *away )
1257        {
1258                GList *m = ic->acc->prpl->away_states( ic );
1259                msg = ic->acc->flags & ACC_FLAG_AWAY_MESSAGE ? away : NULL;
1260                away = imc_away_state_find( m, away, &msg ) ? : m->data;
1261        }
1262        else if( ic->acc->flags & ACC_FLAG_STATUS_MESSAGE )
1263        {
1264                away = NULL;
1265                msg = set_getstr( &ic->acc->set, "status" ) ?
1266                    : set_getstr( &ic->irc->set, "status" );
1267        }
1268       
1269        ic->acc->prpl->set_away( ic, away, msg );
1270       
1271        return 1;
1272}
1273
1274static char *imc_away_alias_list[8][5] =
1275{
1276        { "Away from computer", "Away", "Extended away", NULL },
1277        { "NA", "N/A", "Not available", NULL },
1278        { "Busy", "Do not disturb", "DND", "Occupied", NULL },
1279        { "Be right back", "BRB", NULL },
1280        { "On the phone", "Phone", "On phone", NULL },
1281        { "Out to lunch", "Lunch", "Food", NULL },
1282        { "Invisible", "Hidden" },
1283        { NULL }
1284};
1285
1286static char *imc_away_state_find( GList *gcm, char *away, char **message )
1287{
1288        GList *m;
1289        int i, j;
1290       
1291        for( m = gcm; m; m = m->next )
1292                if( g_strncasecmp( m->data, away, strlen( m->data ) ) == 0 )
1293                {
1294                        /* At least the Yahoo! module works better if message
1295                           contains no data unless it adds something to what
1296                           we have in state already. */
1297                        if( strlen( m->data ) == strlen( away ) )
1298                                *message = NULL;
1299                       
1300                        return m->data;
1301                }
1302       
1303        for( i = 0; *imc_away_alias_list[i]; i ++ )
1304        {
1305                int keep_message;
1306               
1307                for( j = 0; imc_away_alias_list[i][j]; j ++ )
1308                        if( g_strncasecmp( away, imc_away_alias_list[i][j], strlen( imc_away_alias_list[i][j] ) ) == 0 )
1309                        {
1310                                keep_message = strlen( away ) != strlen( imc_away_alias_list[i][j] );
1311                                break;
1312                        }
1313               
1314                if( !imc_away_alias_list[i][j] )        /* If we reach the end, this row */
1315                        continue;                       /* is not what we want. Next!    */
1316               
1317                /* Now find an entry in this row which exists in gcm */
1318                for( j = 0; imc_away_alias_list[i][j]; j ++ )
1319                {
1320                        for( m = gcm; m; m = m->next )
1321                                if( g_strcasecmp( imc_away_alias_list[i][j], m->data ) == 0 )
1322                                {
1323                                        if( !keep_message )
1324                                                *message = NULL;
1325                                       
1326                                        return imc_away_alias_list[i][j];
1327                                }
1328                }
1329               
1330                /* No need to look further, apparently this state doesn't
1331                   have any good alias for this protocol. */
1332                break;
1333        }
1334       
1335        return NULL;
1336}
1337
1338void imc_add_allow( struct im_connection *ic, char *handle )
1339{
1340        if( g_slist_find_custom( ic->permit, handle, (GCompareFunc) ic->acc->prpl->handle_cmp ) == NULL )
1341        {
1342                ic->permit = g_slist_prepend( ic->permit, g_strdup( handle ) );
1343        }
1344       
1345        ic->acc->prpl->add_permit( ic, handle );
1346}
1347
1348void imc_rem_allow( struct im_connection *ic, char *handle )
1349{
1350        GSList *l;
1351       
1352        if( ( l = g_slist_find_custom( ic->permit, handle, (GCompareFunc) ic->acc->prpl->handle_cmp ) ) )
1353        {
1354                g_free( l->data );
1355                ic->permit = g_slist_delete_link( ic->permit, l );
1356        }
1357       
1358        ic->acc->prpl->rem_permit( ic, handle );
1359}
1360
1361void imc_add_block( struct im_connection *ic, char *handle )
1362{
1363        if( g_slist_find_custom( ic->deny, handle, (GCompareFunc) ic->acc->prpl->handle_cmp ) == NULL )
1364        {
1365                ic->deny = g_slist_prepend( ic->deny, g_strdup( handle ) );
1366        }
1367       
1368        ic->acc->prpl->add_deny( ic, handle );
1369}
1370
1371void imc_rem_block( struct im_connection *ic, char *handle )
1372{
1373        GSList *l;
1374       
1375        if( ( l = g_slist_find_custom( ic->deny, handle, (GCompareFunc) ic->acc->prpl->handle_cmp ) ) )
1376        {
1377                g_free( l->data );
1378                ic->deny = g_slist_delete_link( ic->deny, l );
1379        }
1380       
1381        ic->acc->prpl->rem_deny( ic, handle );
1382}
1383
1384void imcb_clean_handle( struct im_connection *ic, char *handle )
1385{
1386        /* Accepts a handle and does whatever is necessary to make it
1387           BitlBee-friendly. Currently this means removing everything
1388           outside 33-127 (ASCII printable excl spaces), @ (only one
1389           is allowed) and ! and : */
1390        char out[strlen(handle)+1];
1391        int s, d;
1392       
1393        s = d = 0;
1394        while( handle[s] )
1395        {
1396                if( handle[s] > ' ' && handle[s] != '!' && handle[s] != ':' &&
1397                    ( handle[s] & 0x80 ) == 0 )
1398                {
1399                        if( handle[s] == '@' )
1400                        {
1401                                /* See if we got an @ already? */
1402                                out[d] = 0;
1403                                if( strchr( out, '@' ) )
1404                                        continue;
1405                        }
1406                       
1407                        out[d++] = handle[s];
1408                }
1409                s ++;
1410        }
1411        out[d] = handle[s];
1412       
1413        strcpy( handle, out );
1414}
Note: See TracBrowser for help on using the repository browser.