source: irc_cap.c @ 2f73692

Last change on this file since 2f73692 was 687ec88, checked in by dequis <dx@…>, at 2015-10-08T05:25:56Z

IRCv3 multi-prefix... but mostly just adding prefixes to WHO

We can't actually have multiple prefixes internally, so the only thing
missing for multi-prefix compliance is actually having the prefix in the
WHO reply, which is a rfc1459 thing.

Note to future self: check irc logs for the implementation I threw away.
The one that actually handled multiple prefixes. I hope that's useful.

  • Property mode set to 100644
File size: 4.1 KB
Line 
1/********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2013 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* IRCv3 CAP command
8 *
9 * Specs:
10 *  - v3.1: http://ircv3.net/specs/core/capability-negotiation-3.1.html
11 *  - v3.2: http://ircv3.net/specs/core/capability-negotiation-3.2.html
12 *
13 * */
14
15/*
16  This program is free software; you can redistribute it and/or modify
17  it under the terms of the GNU General Public License as published by
18  the Free Software Foundation; either version 2 of the License, or
19  (at your option) any later version.
20
21  This program is distributed in the hope that it will be useful,
22  but WITHOUT ANY WARRANTY; without even the implied warranty of
23  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  GNU General Public License for more details.
25
26  You should have received a copy of the GNU General Public License with
27  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
28  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
29  Fifth Floor, Boston, MA  02110-1301  USA
30*/
31
32#include "bitlbee.h"
33
34typedef struct {
35        char *name;
36        irc_cap_flag_t flag;
37} cap_info_t;
38
39static const cap_info_t supported_caps[] = {
40        {"multi-prefix", CAP_MULTI_PREFIX},
41        {NULL},
42};
43
44static irc_cap_flag_t cap_flag_from_string(char *cap_name)
45{
46        int i;
47
48        if (!cap_name || !cap_name[0]) {
49                return 0;
50        }
51
52        if (cap_name[0] == '-') {
53                cap_name++;
54        }
55
56        for (i = 0; supported_caps[i].name; i++) {
57                if (strcmp(supported_caps[i].name, cap_name) == 0) {
58                        return supported_caps[i].flag;
59                }
60        }
61        return 0;
62}
63
64static gboolean irc_cmd_cap_req(irc_t *irc, char *caps)
65{
66        int i;
67        char *lower = NULL;
68        char **split = NULL;
69        irc_cap_flag_t new_caps = irc->caps;
70
71        if (!caps || !caps[0]) {
72                return FALSE;
73        }
74
75        lower = g_ascii_strdown(caps, -1);
76        split = g_strsplit(lower, " ", -1);
77        g_free(lower);
78
79        for (i = 0; split[i]; i++) {
80                gboolean remove;
81                irc_cap_flag_t flag;
82
83                if (!split[i][0]) {
84                        continue;   /* skip empty items (consecutive spaces) */
85                }
86
87                remove = (split[i][0] == '-');
88                flag = cap_flag_from_string(split[i]);
89               
90                if (!flag || (remove && !(irc->caps & flag))) {
91                        /* unsupported cap, or removing something that isn't there */
92                        g_strfreev(split);
93                        return FALSE;
94                }
95
96                if (remove) {
97                        new_caps &= ~flag;
98                } else {
99                        new_caps |= flag;
100                }
101        }
102
103        /* if we got here, set the new caps and ack */
104        irc->caps = new_caps;
105
106        g_strfreev(split);
107        return TRUE;
108}
109
110/* version can be "302" or NULL, but we don't need cap-3.2 for anything yet */
111static void irc_cmd_cap_ls(irc_t *irc, char *version)
112{
113        int i;
114        GString *str = g_string_sized_new(256);
115
116        for (i = 0; supported_caps[i].name; i++) {
117                if (i != 0) {
118                        g_string_append_c(str, ' ');
119                }
120                g_string_append(str, supported_caps[i].name);
121        }
122
123        irc_send_cap(irc, "LS", str->str);
124
125        g_string_free(str, TRUE);
126}
127
128/* this one looks suspiciously similar to cap ls,
129 * but cap-3.2 will make them very different */
130static void irc_cmd_cap_list(irc_t *irc)
131{
132        int i;
133        gboolean first = TRUE;
134        GString *str = g_string_sized_new(256);
135
136        for (i = 0; supported_caps[i].name; i++) {
137                if (irc->caps & supported_caps[i].flag) {
138                        if (!first) {
139                                g_string_append_c(str, ' ');
140                        }
141                        first = FALSE;
142
143                        g_string_append(str, supported_caps[i].name);
144                }
145        }
146
147        irc_send_cap(irc, "LIST", str->str);
148
149        g_string_free(str, TRUE);
150}
151
152void irc_cmd_cap(irc_t *irc, char **cmd)
153{
154        if (!(irc->status & USTATUS_LOGGED_IN)) {
155                /* Put registration on hold until CAP END */
156                irc->status |= USTATUS_CAP_PENDING;
157        }
158
159        if (g_strcasecmp(cmd[1], "LS") == 0) {
160                irc_cmd_cap_ls(irc, cmd[2]);
161
162        } else if (g_strcasecmp(cmd[1], "LIST") == 0) {
163                irc_cmd_cap_list(irc);
164
165        } else if (g_strcasecmp(cmd[1], "REQ") == 0) {
166                gboolean ack = irc_cmd_cap_req(irc, cmd[2]);
167
168                irc_send_cap(irc, ack ? "ACK" : "NAK", cmd[2] ? : "");
169
170        } else if (g_strcasecmp(cmd[1], "END") == 0) {
171                irc->status &= ~USTATUS_CAP_PENDING;
172                irc_check_login(irc);
173
174        } else {
175                irc_send_num(irc, 410, "%s :Invalid CAP command", cmd[1]);
176        }
177
178}
179
Note: See TracBrowser for help on using the repository browser.