source: protocols/nogaim.c @ 2309152

Last change on this file since 2309152 was ec86b22, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-15T23:28:16Z

Mainline merge. (Probably mostly irrelevant for this branch, oh well.)

  • Property mode set to 100644
File size: 34.6 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( set_getbool( &ic->irc->set, "show_offline" ) && !u->online )
668        {
669                /* always set users as online */
670                irc_spawn( ic->irc, u );
671                u->online = 1;
672                if( !( flags & OPT_LOGGED_IN ) )
673                {
674                        /* set away message if user isn't really online */
675                        u->away = g_strdup( "User is offline" );
676                }
677        }
678        else if( ( flags & OPT_LOGGED_IN ) && !u->online )
679        {
680                irc_spawn( ic->irc, u );
681                u->online = 1;
682        }
683        else if( !( flags & OPT_LOGGED_IN ) && u->online )
684        {
685                struct groupchat *c;
686               
687                if( set_getbool( &ic->irc->set, "show_offline" ) )
688                {
689                        /* keep offline users in channel and set away message to "offline" */
690                        u->away = g_strdup( "User is offline" );
691
692                        /* Keep showing him/her in the control channel but not in groupchats. */
693                        for( c = ic->groupchats; c; c = c->next )
694                        {
695                                if( remove_chat_buddy_silent( c, handle ) && c->joined )
696                                        irc_part( c->ic->irc, u, c->channel );
697                        }
698                }
699                else
700                {
701                        /* kill offline users */
702                        irc_kill( ic->irc, u );
703                        u->online = 0;
704
705                        /* Remove him/her from the groupchats to prevent PART messages after he/she QUIT already */
706                        for( c = ic->groupchats; c; c = c->next )
707                                remove_chat_buddy_silent( c, handle );
708                }
709        }
710
711        if( flags & OPT_AWAY )
712        {
713                if( state && message )
714                {
715                        u->away = g_strdup_printf( "%s (%s)", state, message );
716                }
717                else if( state )
718                {
719                        u->away = g_strdup( state );
720                }
721                else if( message )
722                {
723                        u->away = g_strdup( message );
724                }
725                else
726                {
727                        u->away = g_strdup( "Away" );
728                }
729        }
730        else
731        {
732                u->status_msg = g_strdup( message );
733        }
734       
735        /* early if-clause for show_offline even if there is some redundant code here because this isn't LISP but C ;) */
736        if( set_getbool( &ic->irc->set, "show_offline" ) && set_getbool( &ic->irc->set, "away_devoice" ) )
737        {
738                char *from;
739               
740                if( set_getbool( &ic->irc->set, "simulate_netsplit" ) )
741                {
742                        from = g_strdup( ic->irc->myhost );
743                }
744                else
745                {
746                        from = g_strdup_printf( "%s!%s@%s", ic->irc->mynick, ic->irc->mynick,
747                                                            ic->irc->myhost );
748                }
749
750                /* if we use show_offline, we op online users, voice away users, and devoice/deop offline users */
751                if( flags & OPT_LOGGED_IN )
752                {
753                        /* user is "online" (either really online or away) */
754                        irc_write( ic->irc, ":%s MODE %s %cv%co %s %s", from, ic->irc->channel,
755                                                                  u->away?'+':'-', u->away?'-':'+', u->nick, u->nick );
756                }
757                else
758                {
759                        /* user is offline */
760                        irc_write( ic->irc, ":%s MODE %s -vo %s %s", from, ic->irc->channel, u->nick, u->nick );
761                }
762        }
763        else
764        { 
765                /* LISPy... */
766                if( ( set_getbool( &ic->irc->set, "away_devoice" ) ) &&         /* Don't do a thing when user doesn't want it */
767                    ( u->online ) &&                                            /* Don't touch offline people */
768                    ( ( ( u->online != oo ) && !u->away ) ||                    /* Voice joining people */
769                      ( ( u->online == oo ) && ( oa == !u->away ) ) ) )         /* (De)voice people changing state */
770                {
771                        char *from;
772
773                        if( set_getbool( &ic->irc->set, "simulate_netsplit" ) )
774                        {
775                                from = g_strdup( ic->irc->myhost );
776                        }
777                        else
778                        {
779                                from = g_strdup_printf( "%s!%s@%s", ic->irc->mynick, ic->irc->mynick,
780                                                                    ic->irc->myhost );
781                        }
782                        irc_write( ic->irc, ":%s MODE %s %cv %s", from, ic->irc->channel,
783                                                                  u->away?'-':'+', u->nick );
784                        g_free( from );
785                }
786        }
787}
788
789void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at )
790{
791        irc_t *irc = ic->irc;
792        char *wrapped, *ts = NULL;
793        user_t *u;
794       
795        u = user_findhandle( ic, handle );
796       
797        if( !u )
798        {
799                char *h = set_getstr( &irc->set, "handle_unknown" );
800               
801                if( g_strcasecmp( h, "ignore" ) == 0 )
802                {
803                        if( set_getbool( &irc->set, "debug" ) )
804                                imcb_log( ic, "Ignoring message from unknown handle %s", handle );
805                       
806                        return;
807                }
808                else if( g_strncasecmp( h, "add", 3 ) == 0 )
809                {
810                        int private = set_getbool( &irc->set, "private" );
811                       
812                        if( h[3] )
813                        {
814                                if( g_strcasecmp( h + 3, "_private" ) == 0 )
815                                        private = 1;
816                                else if( g_strcasecmp( h + 3, "_channel" ) == 0 )
817                                        private = 0;
818                        }
819                       
820                        imcb_add_buddy( ic, handle, NULL );
821                        u = user_findhandle( ic, handle );
822                        u->is_private = private;
823                }
824                else
825                {
826                        imcb_log( ic, "Message from unknown handle %s:", handle );
827                        u = user_find( irc, irc->mynick );
828                }
829        }
830       
831        if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) ||
832            ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) )
833                strip_html( msg );
834       
835        if( set_getbool( &ic->irc->set, "display_timestamps" ) &&
836            ( ts = format_timestamp( irc, sent_at ) ) )
837        {
838                char *new = g_strconcat( ts, msg, NULL );
839                g_free( ts );
840                ts = msg = new;
841        }
842       
843        wrapped = word_wrap( msg, 425 );
844        irc_msgfrom( irc, u->nick, wrapped );
845        g_free( wrapped );
846        g_free( ts );
847}
848
849void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags )
850{
851        user_t *u;
852       
853        if( !set_getbool( &ic->irc->set, "typing_notice" ) )
854                return;
855       
856        if( ( u = user_findhandle( ic, handle ) ) )
857        {
858                char buf[256]; 
859               
860                g_snprintf( buf, 256, "\1TYPING %d\1", ( flags >> 8 ) & 3 );
861                irc_privmsg( ic->irc, u, "PRIVMSG", ic->irc->nick, NULL, buf );
862        }
863}
864
865struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle )
866{
867        struct groupchat *c;
868       
869        /* This one just creates the conversation structure, user won't see anything yet */
870       
871        if( ic->groupchats )
872        {
873                for( c = ic->groupchats; c->next; c = c->next );
874                c = c->next = g_new0( struct groupchat, 1 );
875        }
876        else
877                ic->groupchats = c = g_new0( struct groupchat, 1 );
878       
879        c->ic = ic;
880        c->title = g_strdup( handle );
881        c->channel = g_strdup_printf( "&chat_%03d", ic->irc->c_id++ );
882        c->topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title );
883       
884        if( set_getbool( &ic->irc->set, "debug" ) )
885                imcb_log( ic, "Creating new conversation: (id=%p,handle=%s)", c, handle );
886       
887        return c;
888}
889
890void imcb_chat_name_hint( struct groupchat *c, const char *name )
891{
892        if( !c->joined )
893        {
894                struct im_connection *ic = c->ic;
895                char stripped[MAX_NICK_LENGTH+1], *full_name;
896               
897                strncpy( stripped, name, MAX_NICK_LENGTH );
898                stripped[MAX_NICK_LENGTH] = '\0';
899                nick_strip( stripped );
900                if( set_getbool( &ic->irc->set, "lcnicks" ) )
901                        nick_lc( stripped );
902               
903                full_name = g_strdup_printf( "&%s", stripped );
904               
905                if( stripped[0] &&
906                    nick_cmp( stripped, ic->irc->channel + 1 ) != 0 &&
907                    irc_chat_by_channel( ic->irc, full_name ) == NULL )
908                {
909                        g_free( c->channel );
910                        c->channel = full_name;
911                }
912                else
913                {
914                        g_free( full_name );
915                }
916        }
917}
918
919void imcb_chat_free( struct groupchat *c )
920{
921        struct im_connection *ic = c->ic;
922        struct groupchat *l;
923        GList *ir;
924       
925        if( set_getbool( &ic->irc->set, "debug" ) )
926                imcb_log( ic, "You were removed from conversation %p", c );
927       
928        if( c )
929        {
930                if( c->joined )
931                {
932                        user_t *u, *r;
933                       
934                        r = user_find( ic->irc, ic->irc->mynick );
935                        irc_privmsg( ic->irc, r, "PRIVMSG", c->channel, "", "Cleaning up channel, bye!" );
936                       
937                        u = user_find( ic->irc, ic->irc->nick );
938                        irc_kick( ic->irc, u, c->channel, r );
939                        /* irc_part( ic->irc, u, c->channel ); */
940                }
941               
942                /* Find the previous chat in the linked list. */
943                for( l = ic->groupchats; l && l->next != c; l = l->next );
944               
945                if( l )
946                        l->next = c->next;
947                else
948                        ic->groupchats = c->next;
949               
950                for( ir = c->in_room; ir; ir = ir->next )
951                        g_free( ir->data );
952                g_list_free( c->in_room );
953                g_free( c->channel );
954                g_free( c->title );
955                g_free( c->topic );
956                g_free( c );
957        }
958}
959
960void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t flags, time_t sent_at )
961{
962        struct im_connection *ic = c->ic;
963        char *wrapped;
964        user_t *u;
965       
966        /* Gaim sends own messages through this too. IRC doesn't want this, so kill them */
967        if( g_strcasecmp( who, ic->acc->user ) == 0 )
968                return;
969       
970        u = user_findhandle( ic, who );
971       
972        if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) ||
973            ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) )
974                strip_html( msg );
975       
976        wrapped = word_wrap( msg, 425 );
977        if( c && u )
978        {
979                char *ts = NULL;
980                if( set_getbool( &ic->irc->set, "display_timestamps" ) )
981                        ts = format_timestamp( ic->irc, sent_at );
982                irc_privmsg( ic->irc, u, "PRIVMSG", c->channel, ts ? : "", wrapped );
983                g_free( ts );
984        }
985        else
986        {
987                imcb_log( ic, "Message from/to conversation %s@%p (unknown conv/user): %s", who, c, wrapped );
988        }
989        g_free( wrapped );
990}
991
992void imcb_chat_log( struct groupchat *c, char *format, ... )
993{
994        irc_t *irc = c->ic->irc;
995        va_list params;
996        char *text;
997        user_t *u;
998       
999        va_start( params, format );
1000        text = g_strdup_vprintf( format, params );
1001        va_end( params );
1002       
1003        u = user_find( irc, irc->mynick );
1004       
1005        irc_privmsg( irc, u, "PRIVMSG", c->channel, "System message: ", text );
1006       
1007        g_free( text );
1008}
1009
1010void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at )
1011{
1012        struct im_connection *ic = c->ic;
1013        user_t *u = NULL;
1014       
1015        if( who == NULL)
1016                u = user_find( ic->irc, ic->irc->mynick );
1017        else if( g_strcasecmp( who, ic->acc->user ) == 0 )
1018                u = user_find( ic->irc, ic->irc->nick );
1019        else
1020                u = user_findhandle( ic, who );
1021       
1022        if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) ||
1023            ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) )
1024                strip_html( topic );
1025       
1026        g_free( c->topic );
1027        c->topic = g_strdup( topic );
1028       
1029        if( c->joined && u )
1030                irc_write( ic->irc, ":%s!%s@%s TOPIC %s :%s", u->nick, u->user, u->host, c->channel, topic );
1031}
1032
1033
1034/* buddy_chat.c */
1035
1036void imcb_chat_add_buddy( struct groupchat *b, const char *handle )
1037{
1038        user_t *u = user_findhandle( b->ic, handle );
1039        int me = 0;
1040       
1041        if( set_getbool( &b->ic->irc->set, "debug" ) )
1042                imcb_log( b->ic, "User %s added to conversation %p", handle, b );
1043       
1044        /* It might be yourself! */
1045        if( b->ic->acc->prpl->handle_cmp( handle, b->ic->acc->user ) == 0 )
1046        {
1047                u = user_find( b->ic->irc, b->ic->irc->nick );
1048                if( !b->joined )
1049                        irc_join( b->ic->irc, u, b->channel );
1050                b->joined = me = 1;
1051        }
1052       
1053        /* Most protocols allow people to join, even when they're not in
1054           your contact list. Try to handle that here */
1055        if( !u )
1056        {
1057                imcb_add_buddy( b->ic, handle, NULL );
1058                u = user_findhandle( b->ic, handle );
1059        }
1060       
1061        /* Add the handle to the room userlist, if it's not 'me' */
1062        if( !me )
1063        {
1064                if( b->joined )
1065                        irc_join( b->ic->irc, u, b->channel );
1066                b->in_room = g_list_append( b->in_room, g_strdup( handle ) );
1067        }
1068}
1069
1070/* This function is one BIG hack... :-( EREWRITE */
1071void imcb_chat_remove_buddy( struct groupchat *b, const char *handle, const char *reason )
1072{
1073        user_t *u;
1074        int me = 0;
1075       
1076        if( set_getbool( &b->ic->irc->set, "debug" ) )
1077                imcb_log( b->ic, "User %s removed from conversation %p (%s)", handle, b, reason ? reason : "" );
1078       
1079        /* It might be yourself! */
1080        if( g_strcasecmp( handle, b->ic->acc->user ) == 0 )
1081        {
1082                if( b->joined == 0 )
1083                        return;
1084               
1085                u = user_find( b->ic->irc, b->ic->irc->nick );
1086                b->joined = 0;
1087                me = 1;
1088        }
1089        else
1090        {
1091                u = user_findhandle( b->ic, handle );
1092        }
1093       
1094        if( me || ( remove_chat_buddy_silent( b, handle ) && b->joined && u ) )
1095                irc_part( b->ic->irc, u, b->channel );
1096}
1097
1098static int remove_chat_buddy_silent( struct groupchat *b, const char *handle )
1099{
1100        GList *i;
1101       
1102        /* Find the handle in the room userlist and shoot it */
1103        i = b->in_room;
1104        while( i )
1105        {
1106                if( g_strcasecmp( handle, i->data ) == 0 )
1107                {
1108                        g_free( i->data );
1109                        b->in_room = g_list_remove( b->in_room, i->data );
1110                        return( 1 );
1111                }
1112               
1113                i = i->next;
1114        }
1115       
1116        return( 0 );
1117}
1118
1119
1120/* Misc. BitlBee stuff which shouldn't really be here */
1121
1122char *set_eval_away_devoice( set_t *set, char *value )
1123{
1124        irc_t *irc = set->data;
1125        int st;
1126       
1127        if( !is_bool( value ) )
1128                return SET_INVALID;
1129       
1130        st = bool2int( value );
1131       
1132        /* Horror.... */
1133       
1134        if( st != set_getbool( &irc->set, "away_devoice" ) )
1135        {
1136                char list[80] = "";
1137                user_t *u = irc->users;
1138                int i = 0, count = 0;
1139                char pm;
1140                char v[80];
1141               
1142                if( st )
1143                        pm = '+';
1144                else
1145                        pm = '-';
1146               
1147                while( u )
1148                {
1149                        if( u->ic && u->online && !u->away )
1150                        {
1151                                if( ( strlen( list ) + strlen( u->nick ) ) >= 79 )
1152                                {
1153                                        for( i = 0; i < count; v[i++] = 'v' ); v[i] = 0;
1154                                        irc_write( irc, ":%s MODE %s %c%s%s",
1155                                                   irc->myhost,
1156                                                   irc->channel, pm, v, list );
1157                                       
1158                                        *list = 0;
1159                                        count = 0;
1160                                }
1161                               
1162                                sprintf( list + strlen( list ), " %s", u->nick );
1163                                count ++;
1164                        }
1165                        u = u->next;
1166                }
1167               
1168                /* $v = 'v' x $i */
1169                for( i = 0; i < count; v[i++] = 'v' ); v[i] = 0;
1170                irc_write( irc, ":%s MODE %s %c%s%s", irc->myhost,
1171                                                            irc->channel, pm, v, list );
1172        }
1173       
1174        return value;
1175}
1176
1177char *set_eval_timezone( set_t *set, char *value )
1178{
1179        char *s;
1180       
1181        if( strcmp( value, "local" ) == 0 ||
1182            strcmp( value, "gmt" ) == 0 || strcmp( value, "utc" ) == 0 )
1183                return value;
1184       
1185        /* Otherwise: +/- at the beginning optional, then one or more numbers,
1186           possibly followed by a colon and more numbers. Don't bother bound-
1187           checking them since users are free to shoot themselves in the foot. */
1188        s = value;
1189        if( *s == '+' || *s == '-' )
1190                s ++;
1191       
1192        /* \d+ */
1193        if( !isdigit( *s ) )
1194                return SET_INVALID;
1195        while( *s && isdigit( *s ) ) s ++;
1196       
1197        /* EOS? */
1198        if( *s == '\0' )
1199                return value;
1200       
1201        /* Otherwise, colon */
1202        if( *s != ':' )
1203                return SET_INVALID;
1204        s ++;
1205       
1206        /* \d+ */
1207        if( !isdigit( *s ) )
1208                return SET_INVALID;
1209        while( *s && isdigit( *s ) ) s ++;
1210       
1211        /* EOS */
1212        return *s == '\0' ? value : SET_INVALID;
1213}
1214
1215static char *format_timestamp( irc_t *irc, time_t msg_ts )
1216{
1217        time_t now_ts = time( NULL );
1218        struct tm now, msg;
1219        char *set;
1220       
1221        /* If the timestamp is <= 0 or less than a minute ago, discard it as
1222           it doesn't seem to add to much useful info and/or might be noise. */
1223        if( msg_ts <= 0 || msg_ts > now_ts - 60 )
1224                return NULL;
1225       
1226        set = set_getstr( &irc->set, "timezone" );
1227        if( strcmp( set, "local" ) == 0 )
1228        {
1229                localtime_r( &now_ts, &now );
1230                localtime_r( &msg_ts, &msg );
1231        }
1232        else
1233        {
1234                int hr, min = 0, sign = 60;
1235               
1236                if( set[0] == '-' )
1237                {
1238                        sign *= -1;
1239                        set ++;
1240                }
1241                else if( set[0] == '+' )
1242                {
1243                        set ++;
1244                }
1245               
1246                if( sscanf( set, "%d:%d", &hr, &min ) >= 1 )
1247                {
1248                        msg_ts += sign * ( hr * 60 + min );
1249                        now_ts += sign * ( hr * 60 + min );
1250                }
1251               
1252                gmtime_r( &now_ts, &now );
1253                gmtime_r( &msg_ts, &msg );
1254        }
1255       
1256        if( msg.tm_year == now.tm_year && msg.tm_yday == now.tm_yday )
1257                return g_strdup_printf( "\x02[\x02\x02\x02%02d:%02d:%02d\x02]\x02 ",
1258                                        msg.tm_hour, msg.tm_min, msg.tm_sec );
1259        else
1260                return g_strdup_printf( "\x02[\x02\x02\x02%04d-%02d-%02d "
1261                                        "%02d:%02d:%02d\x02]\x02 ",
1262                                        msg.tm_year + 1900, msg.tm_mon + 1, msg.tm_mday,
1263                                        msg.tm_hour, msg.tm_min, msg.tm_sec );
1264}
1265
1266/* The plan is to not allow straight calls to prpl functions anymore, but do
1267   them all from some wrappers. We'll start to define some down here: */
1268
1269int imc_buddy_msg( struct im_connection *ic, char *handle, char *msg, int flags )
1270{
1271        char *buf = NULL;
1272        int st;
1273       
1274        if( ( ic->flags & OPT_DOES_HTML ) && ( g_strncasecmp( msg, "<html>", 6 ) != 0 ) )
1275        {
1276                buf = escape_html( msg );
1277                msg = buf;
1278        }
1279       
1280        st = ic->acc->prpl->buddy_msg( ic, handle, msg, flags );
1281        g_free( buf );
1282       
1283        return st;
1284}
1285
1286int imc_chat_msg( struct groupchat *c, char *msg, int flags )
1287{
1288        char *buf = NULL;
1289       
1290        if( ( c->ic->flags & OPT_DOES_HTML ) && ( g_strncasecmp( msg, "<html>", 6 ) != 0 ) )
1291        {
1292                buf = escape_html( msg );
1293                msg = buf;
1294        }
1295       
1296        c->ic->acc->prpl->chat_msg( c, msg, flags );
1297        g_free( buf );
1298       
1299        return 1;
1300}
1301
1302static char *imc_away_state_find( GList *gcm, char *away, char **message );
1303
1304int imc_away_send_update( struct im_connection *ic )
1305{
1306        char *away, *msg = NULL;
1307       
1308        if( ic->acc->prpl->away_states == NULL ||
1309            ic->acc->prpl->set_away == NULL )
1310                return 0;
1311       
1312        away = set_getstr( &ic->acc->set, "away" ) ?
1313             : set_getstr( &ic->irc->set, "away" );
1314        if( away && *away )
1315        {
1316                GList *m = ic->acc->prpl->away_states( ic );
1317                msg = ic->acc->flags & ACC_FLAG_AWAY_MESSAGE ? away : NULL;
1318                away = imc_away_state_find( m, away, &msg ) ? : m->data;
1319        }
1320        else if( ic->acc->flags & ACC_FLAG_STATUS_MESSAGE )
1321        {
1322                away = NULL;
1323                msg = set_getstr( &ic->acc->set, "status" ) ?
1324                    : set_getstr( &ic->irc->set, "status" );
1325        }
1326       
1327        ic->acc->prpl->set_away( ic, away, msg );
1328       
1329        return 1;
1330}
1331
1332static char *imc_away_alias_list[8][5] =
1333{
1334        { "Away from computer", "Away", "Extended away", NULL },
1335        { "NA", "N/A", "Not available", NULL },
1336        { "Busy", "Do not disturb", "DND", "Occupied", NULL },
1337        { "Be right back", "BRB", NULL },
1338        { "On the phone", "Phone", "On phone", NULL },
1339        { "Out to lunch", "Lunch", "Food", NULL },
1340        { "Invisible", "Hidden" },
1341        { NULL }
1342};
1343
1344static char *imc_away_state_find( GList *gcm, char *away, char **message )
1345{
1346        GList *m;
1347        int i, j;
1348       
1349        for( m = gcm; m; m = m->next )
1350                if( g_strncasecmp( m->data, away, strlen( m->data ) ) == 0 )
1351                {
1352                        /* At least the Yahoo! module works better if message
1353                           contains no data unless it adds something to what
1354                           we have in state already. */
1355                        if( strlen( m->data ) == strlen( away ) )
1356                                *message = NULL;
1357                       
1358                        return m->data;
1359                }
1360       
1361        for( i = 0; *imc_away_alias_list[i]; i ++ )
1362        {
1363                int keep_message;
1364               
1365                for( j = 0; imc_away_alias_list[i][j]; j ++ )
1366                        if( g_strncasecmp( away, imc_away_alias_list[i][j], strlen( imc_away_alias_list[i][j] ) ) == 0 )
1367                        {
1368                                keep_message = strlen( away ) != strlen( imc_away_alias_list[i][j] );
1369                                break;
1370                        }
1371               
1372                if( !imc_away_alias_list[i][j] )        /* If we reach the end, this row */
1373                        continue;                       /* is not what we want. Next!    */
1374               
1375                /* Now find an entry in this row which exists in gcm */
1376                for( j = 0; imc_away_alias_list[i][j]; j ++ )
1377                {
1378                        for( m = gcm; m; m = m->next )
1379                                if( g_strcasecmp( imc_away_alias_list[i][j], m->data ) == 0 )
1380                                {
1381                                        if( !keep_message )
1382                                                *message = NULL;
1383                                       
1384                                        return imc_away_alias_list[i][j];
1385                                }
1386                }
1387               
1388                /* No need to look further, apparently this state doesn't
1389                   have any good alias for this protocol. */
1390                break;
1391        }
1392       
1393        return NULL;
1394}
1395
1396void imc_add_allow( struct im_connection *ic, char *handle )
1397{
1398        if( g_slist_find_custom( ic->permit, handle, (GCompareFunc) ic->acc->prpl->handle_cmp ) == NULL )
1399        {
1400                ic->permit = g_slist_prepend( ic->permit, g_strdup( handle ) );
1401        }
1402       
1403        ic->acc->prpl->add_permit( ic, handle );
1404}
1405
1406void imc_rem_allow( struct im_connection *ic, char *handle )
1407{
1408        GSList *l;
1409       
1410        if( ( l = g_slist_find_custom( ic->permit, handle, (GCompareFunc) ic->acc->prpl->handle_cmp ) ) )
1411        {
1412                g_free( l->data );
1413                ic->permit = g_slist_delete_link( ic->permit, l );
1414        }
1415       
1416        ic->acc->prpl->rem_permit( ic, handle );
1417}
1418
1419void imc_add_block( struct im_connection *ic, char *handle )
1420{
1421        if( g_slist_find_custom( ic->deny, handle, (GCompareFunc) ic->acc->prpl->handle_cmp ) == NULL )
1422        {
1423                ic->deny = g_slist_prepend( ic->deny, g_strdup( handle ) );
1424        }
1425       
1426        ic->acc->prpl->add_deny( ic, handle );
1427}
1428
1429void imc_rem_block( struct im_connection *ic, char *handle )
1430{
1431        GSList *l;
1432       
1433        if( ( l = g_slist_find_custom( ic->deny, handle, (GCompareFunc) ic->acc->prpl->handle_cmp ) ) )
1434        {
1435                g_free( l->data );
1436                ic->deny = g_slist_delete_link( ic->deny, l );
1437        }
1438       
1439        ic->acc->prpl->rem_deny( ic, handle );
1440}
1441
1442void imcb_clean_handle( struct im_connection *ic, char *handle )
1443{
1444        /* Accepts a handle and does whatever is necessary to make it
1445           BitlBee-friendly. Currently this means removing everything
1446           outside 33-127 (ASCII printable excl spaces), @ (only one
1447           is allowed) and ! and : */
1448        char out[strlen(handle)+1];
1449        int s, d;
1450       
1451        s = d = 0;
1452        while( handle[s] )
1453        {
1454                if( handle[s] > ' ' && handle[s] != '!' && handle[s] != ':' &&
1455                    ( handle[s] & 0x80 ) == 0 )
1456                {
1457                        if( handle[s] == '@' )
1458                        {
1459                                /* See if we got an @ already? */
1460                                out[d] = 0;
1461                                if( strchr( out, '@' ) )
1462                                        continue;
1463                        }
1464                       
1465                        out[d++] = handle[s];
1466                }
1467                s ++;
1468        }
1469        out[d] = handle[s];
1470       
1471        strcpy( handle, out );
1472}
Note: See TracBrowser for help on using the repository browser.