source: nick.c @ ec8b369

Last change on this file since ec8b369 was 830864d, checked in by dequis <dx@…>, at 2015-03-15T09:31:18Z

WIP placeholder channels with hipchat implementation

i was going to clean this up and split in two commits but uhhh...
maybe some other day, i'm tired now

not very tested and i'm not 100% happy about the design, but sucks way
less than what i had in the hip-cat branch

feedback still appreciated.

this adds channels to the channel list without creating groupchats for
them, allowing users to /join them. what the hip-cat branch did before
but with proper api and hopefully less dumb behavior

it still 'leaks' them intentionally, just like it did before, but now it
prevents saving them to the xml so yay

also slightly improved channel name generation, refactored
bee_irc_chat_name_hint into three or four functions, and so on

  • Property mode set to 100644
File size: 10.2 KB
Line 
1/********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* Some stuff to fetch, save and handle nicknames for your buddies      */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23  Fifth Floor, Boston, MA  02110-1301  USA
24*/
25
26#define BITLBEE_CORE
27#include "bitlbee.h"
28
29/* Character maps, _lc_[x] == _uc_[x] (but uppercase), according to the RFC's.
30   With one difference, we allow dashes. These are used to do uc/lc conversions
31   and strip invalid chars. */
32static char *nick_lc_chars = "0123456789abcdefghijklmnopqrstuvwxyz{}^`-_|";
33static char *nick_uc_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ[]~`-_\\";
34
35/* Store handles in lower case and strip spaces, because AIM is braindead. */
36static char *clean_handle(const char *orig)
37{
38        char *new = g_malloc(strlen(orig) + 1);
39        int i = 0;
40
41        do {
42                if (*orig != ' ') {
43                        new[i++] = g_ascii_tolower(*orig);
44                }
45        } while (*(orig++));
46
47        return new;
48}
49
50void nick_set_raw(account_t *acc, const char *handle, const char *nick)
51{
52        char *store_handle, *store_nick = g_malloc(MAX_NICK_LENGTH + 1);
53        irc_t *irc = (irc_t *) acc->bee->ui_data;
54
55        store_handle = clean_handle(handle);
56        store_nick[MAX_NICK_LENGTH] = '\0';
57        strncpy(store_nick, nick, MAX_NICK_LENGTH);
58        nick_strip(irc, store_nick);
59
60        g_hash_table_replace(acc->nicks, store_handle, store_nick);
61}
62
63void nick_set(bee_user_t *bu, const char *nick)
64{
65        nick_set_raw(bu->ic->acc, bu->handle, nick);
66}
67
68char *nick_get(bee_user_t *bu)
69{
70        static char nick[MAX_NICK_LENGTH + 1];
71        char *store_handle, *found_nick;
72        irc_t *irc = (irc_t *) bu->bee->ui_data;
73
74        memset(nick, 0, MAX_NICK_LENGTH + 1);
75
76        store_handle = clean_handle(bu->handle);
77        /* Find out if we stored a nick for this person already. If not, try
78           to generate a sane nick automatically. */
79        if ((found_nick = g_hash_table_lookup(bu->ic->acc->nicks, store_handle))) {
80                strncpy(nick, found_nick, MAX_NICK_LENGTH);
81        } else if ((found_nick = nick_gen(bu))) {
82                strncpy(nick, found_nick, MAX_NICK_LENGTH);
83                g_free(found_nick);
84        } else {
85                /* Keep this fallback since nick_gen() can return NULL in some cases. */
86                char *s;
87
88                g_snprintf(nick, MAX_NICK_LENGTH, "%s", bu->handle);
89                if ((s = strchr(nick, '@'))) {
90                        while (*s) {
91                                *(s++) = 0;
92                        }
93                }
94
95                nick_strip(irc, nick);
96                if (set_getbool(&bu->bee->set, "lcnicks")) {
97                        nick_lc(irc, nick);
98                }
99        }
100        g_free(store_handle);
101
102        /* Make sure the nick doesn't collide with an existing one by adding
103           underscores and that kind of stuff, if necessary. */
104        nick_dedupe(bu, nick);
105
106        return nick;
107}
108
109char *nick_gen(bee_user_t *bu)
110{
111        gboolean ok = FALSE; /* Set to true once the nick contains something unique. */
112        GString *ret = g_string_sized_new(MAX_NICK_LENGTH + 1);
113        char *rets;
114        irc_t *irc = (irc_t *) bu->bee->ui_data;
115        char *fmt = set_getstr(&bu->ic->acc->set, "nick_format") ? :
116                    set_getstr(&bu->bee->set, "nick_format");
117
118        while (fmt && *fmt && ret->len < MAX_NICK_LENGTH) {
119                char *part = NULL, chop = '\0', *asc = NULL, *s;
120                int len = INT_MAX;
121
122                if (*fmt != '%') {
123                        g_string_append_c(ret, *fmt);
124                        fmt++;
125                        continue;
126                }
127
128                fmt++;
129                while (*fmt) {
130                        /* -char means chop off everything from char */
131                        if (*fmt == '-') {
132                                chop = fmt[1];
133                                if (chop == '\0') {
134                                        g_string_free(ret, TRUE);
135                                        return NULL;
136                                }
137                                fmt += 2;
138                        } else if (g_ascii_isdigit(*fmt)) {
139                                len = 0;
140                                /* Grab a number. */
141                                while (g_ascii_isdigit(*fmt)) {
142                                        len = len * 10 + (*(fmt++) - '0');
143                                }
144                        } else if (g_strncasecmp(fmt, "nick", 4) == 0) {
145                                part = bu->nick ? : bu->handle;
146                                fmt += 4;
147                                ok |= TRUE;
148                                break;
149                        } else if (g_strncasecmp(fmt, "handle", 6) == 0) {
150                                part = bu->handle;
151                                fmt += 6;
152                                ok |= TRUE;
153                                break;
154                        } else if (g_strncasecmp(fmt, "full_name", 9) == 0) {
155                                part = bu->fullname;
156                                fmt += 9;
157                                ok |= part && *part;
158                                break;
159                        } else if (g_strncasecmp(fmt, "first_name", 10) == 0) {
160                                part = bu->fullname;
161                                fmt += 10;
162                                ok |= part && *part;
163                                chop = ' ';
164                                break;
165                        } else if (g_strncasecmp(fmt, "group", 5) == 0) {
166                                part = bu->group ? bu->group->name : NULL;
167                                fmt += 5;
168                                break;
169                        } else if (g_strncasecmp(fmt, "account", 7) == 0) {
170                                part = bu->ic->acc->tag;
171                                fmt += 7;
172                                break;
173                        } else {
174                                g_string_free(ret, TRUE);
175                                return NULL;
176                        }
177                }
178
179                if (!part) {
180                        continue;
181                }
182
183                /* Credits to Josay_ in #bitlbee for this idea. //TRANSLIT
184                   should do lossy/approximate conversions, so letters with
185                   accents don't just get stripped. Note that it depends on
186                   LC_CTYPE being set to something other than C/POSIX. */
187                if (!(irc && irc->status & IRC_UTF8_NICKS)) {
188                        part = asc = g_convert_with_fallback(part, -1, "ASCII//TRANSLIT",
189                                                             "UTF-8", "", NULL, NULL, NULL);
190                }
191
192                if (part && chop && (s = strchr(part, chop))) {
193                        len = MIN(len, s - part);
194                }
195
196                if (part) {
197                        if (len < INT_MAX) {
198                                g_string_append_len(ret, part, len);
199                        } else {
200                                g_string_append(ret, part);
201                        }
202                }
203                g_free(asc);
204        }
205
206        rets = g_string_free(ret, FALSE);
207        if (ok && rets && *rets) {
208                nick_strip(irc, rets);
209                truncate_utf8(rets, MAX_NICK_LENGTH);
210                return rets;
211        }
212        g_free(rets);
213        return NULL;
214}
215
216/* Used for nicks and channel names too! */
217void underscore_dedupe(char nick[MAX_NICK_LENGTH + 1])
218{
219        if (strlen(nick) < (MAX_NICK_LENGTH - 1)) {
220                nick[strlen(nick) + 1] = 0;
221                nick[strlen(nick)] = '_';
222        } else {
223                /* We've got no more space for underscores,
224                   so truncate it and replace the last three
225                   chars with a random "_XX" suffix */
226                int len = truncate_utf8(nick, MAX_NICK_LENGTH - 3);
227                nick[len] = '_';
228                g_snprintf(nick + len + 1, 3, "%2x", rand());
229        }
230}
231
232void nick_dedupe(bee_user_t *bu, char nick[MAX_NICK_LENGTH + 1])
233{
234        irc_t *irc = (irc_t *) bu->bee->ui_data;
235        int inf_protection = 256;
236        irc_user_t *iu;
237
238        /* Now, find out if the nick is already in use at the moment, and make
239           subtle changes to make it unique. */
240        while (!nick_ok(irc, nick) ||
241               ((iu = irc_user_by_name(irc, nick)) && iu->bu != bu)) {
242
243                underscore_dedupe(nick);
244
245                if (inf_protection-- == 0) {
246                        g_snprintf(nick, MAX_NICK_LENGTH + 1, "xx%x", rand());
247
248                        irc_rootmsg(irc, "Warning: Something went wrong while trying "
249                                    "to generate a nickname for contact %s on %s.",
250                                    bu->handle, bu->ic->acc->tag);
251                        irc_rootmsg(irc, "This might be a bug in BitlBee, or the result "
252                                    "of a faulty nick_format setting. Will use %s "
253                                    "instead.", nick);
254
255                        break;
256                }
257        }
258}
259
260/* Just check if there is a nickname set for this buddy or if we'd have to
261   generate one. */
262int nick_saved(bee_user_t *bu)
263{
264        char *store_handle, *found;
265
266        store_handle = clean_handle(bu->handle);
267        found = g_hash_table_lookup(bu->ic->acc->nicks, store_handle);
268        g_free(store_handle);
269
270        return found != NULL;
271}
272
273void nick_del(bee_user_t *bu)
274{
275        g_hash_table_remove(bu->ic->acc->nicks, bu->handle);
276}
277
278
279void nick_strip(irc_t *irc, char *nick)
280{
281        int len = 0;
282
283        if (irc && (irc->status & IRC_UTF8_NICKS)) {
284                gunichar c;
285                char *p = nick, *n, tmp[strlen(nick) + 1];
286
287                while (p && *p) {
288                        c = g_utf8_get_char_validated(p, -1);
289                        n = g_utf8_find_next_char(p, NULL);
290
291                        if ((c < 0x7f && !(strchr(nick_lc_chars, c) ||
292                                           strchr(nick_uc_chars, c))) ||
293                            !g_unichar_isgraph(c)) {
294                                strcpy(tmp, n);
295                                strcpy(p, tmp);
296                        } else {
297                                p = n;
298                        }
299                }
300                if (p) {
301                        len = p - nick;
302                }
303        } else {
304                int i;
305
306                for (i = len = 0; nick[i] && len < MAX_NICK_LENGTH; i++) {
307                        if (strchr(nick_lc_chars, nick[i]) ||
308                            strchr(nick_uc_chars, nick[i])) {
309                                nick[len] = nick[i];
310                                len++;
311                        }
312                }
313        }
314        if (g_ascii_isdigit(nick[0])) {
315                char *orig;
316
317                /* First character of a nick can't be a digit, so insert an
318                   underscore if necessary. */
319                orig = g_strdup(nick);
320                g_snprintf(nick, MAX_NICK_LENGTH, "_%s", orig);
321                g_free(orig);
322                len++;
323        }
324        while (len <= MAX_NICK_LENGTH) {
325                nick[len++] = '\0';
326        }
327}
328
329gboolean nick_ok(irc_t *irc, const char *nick)
330{
331        const char *s;
332
333        /* Empty/long nicks are not allowed, nor numbers at [0] */
334        if (!*nick || g_ascii_isdigit(nick[0]) || strlen(nick) > MAX_NICK_LENGTH) {
335                return 0;
336        }
337
338        if (irc && (irc->status & IRC_UTF8_NICKS)) {
339                gunichar c;
340                const char *p = nick, *n;
341
342                while (p && *p) {
343                        c = g_utf8_get_char_validated(p, -1);
344                        n = g_utf8_find_next_char(p, NULL);
345
346                        if ((c < 0x7f && !(strchr(nick_lc_chars, c) ||
347                                           strchr(nick_uc_chars, c))) ||
348                            !g_unichar_isgraph(c)) {
349                                return FALSE;
350                        }
351                        p = n;
352                }
353        } else {
354                for (s = nick; *s; s++) {
355                        if (!strchr(nick_lc_chars, *s) && !strchr(nick_uc_chars, *s)) {
356                                return FALSE;
357                        }
358                }
359        }
360
361        return TRUE;
362}
363
364int nick_lc(irc_t *irc, char *nick)
365{
366        static char tab[128] = { 0 };
367        int i;
368
369        if (tab['A'] == 0) {
370                for (i = 0; nick_lc_chars[i]; i++) {
371                        tab[(int) nick_uc_chars[i]] = nick_lc_chars[i];
372                        tab[(int) nick_lc_chars[i]] = nick_lc_chars[i];
373                }
374        }
375
376        if (irc && (irc->status & IRC_UTF8_NICKS)) {
377                gchar *down = g_utf8_strdown(nick, -1);
378                if (strlen(down) > strlen(nick)) {
379                        truncate_utf8(down, strlen(nick));
380                }
381                strcpy(nick, down);
382                g_free(down);
383        }
384
385        for (i = 0; nick[i]; i++) {
386                if (((guchar) nick[i]) < 0x7f) {
387                        nick[i] = tab[(guchar) nick[i]];
388                }
389        }
390
391        return nick_ok(irc, nick);
392}
393
394int nick_cmp(irc_t *irc, const char *a, const char *b)
395{
396        char aa[1024] = "", bb[1024] = "";
397
398        strncpy(aa, a, sizeof(aa) - 1);
399        strncpy(bb, b, sizeof(bb) - 1);
400        if (nick_lc(irc, aa) && nick_lc(irc, bb)) {
401                return(strcmp(aa, bb));
402        } else {
403                return(-1);     /* Hmm... Not a clear answer.. :-/ */
404        }
405}
Note: See TracBrowser for help on using the repository browser.