aboutsummaryrefslogtreecommitdiffstats
path: root/irc_cap.c
blob: 4cfc158bd2c96e904d9931be95d7977e195b7531 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/********************************************************************\
  * BitlBee -- An IRC to other IM-networks gateway                     *
  *                                                                    *
  * Copyright 2002-2013 Wilmer van der Gaast and others                *
  \********************************************************************/

/* IRCv3 CAP command
 *
 * Specs:
 *  - v3.1: http://ircv3.net/specs/core/capability-negotiation-3.1.html
 *  - v3.2: http://ircv3.net/specs/core/capability-negotiation-3.2.html
 *
 * */

/*
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License with
  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
  Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "bitlbee.h"

typedef struct {
	char *name;
	irc_cap_flag_t flag;
} cap_info_t;

static const cap_info_t supported_caps[] = {
	{"sasl", CAP_SASL},
	{"multi-prefix", CAP_MULTI_PREFIX},
	{NULL},
};

static irc_cap_flag_t cap_flag_from_string(char *cap_name)
{
	int i;

	if (!cap_name || !cap_name[0]) {
		return 0;
	}

	if (cap_name[0] == '-') {
		cap_name++;
	}

	for (i = 0; supported_caps[i].name; i++) {
		if (strcmp(supported_caps[i].name, cap_name) == 0) {
			return supported_caps[i].flag;
		}
	}
	return 0;
}

static gboolean irc_cmd_cap_req(irc_t *irc, char *caps)
{
	int i;
	char *lower = NULL;
	char **split = NULL;
	irc_cap_flag_t new_caps = irc->caps;

	if (!caps || !caps[0]) {
		return FALSE;
	}

	lower = g_ascii_strdown(caps, -1);
	split = g_strsplit(lower, " ", -1);
	g_free(lower);

	for (i = 0; split[i]; i++) {
		gboolean remove;
		irc_cap_flag_t flag;

		if (!split[i][0]) {
			continue;   /* skip empty items (consecutive spaces) */
		}

		remove = (split[i][0] == '-');
		flag = cap_flag_from_string(split[i]);
		
		if (!flag || (remove && !(irc->caps & flag))) {
			/* unsupported cap, or removing something that isn't there */
			g_strfreev(split);
			return FALSE;
		}

		if (remove) {
			new_caps &= ~flag;
		} else {
			new_caps |= flag;
		}
	}

	/* if we got here, set the new caps and ack */
	irc->caps = new_caps;

	g_strfreev(split);
	return TRUE;
}

/* version can be "302" or NULL, but we don't need cap-3.2 for anything yet */
static void irc_cmd_cap_ls(irc_t *irc, char *version)
{
	int i;
	GString *str = g_string_sized_new(256);

	for (i = 0; supported_caps[i].name; i++) {
		if (i != 0) {
			g_string_append_c(str, ' ');
		}
		g_string_append(str, supported_caps[i].name);
	}

	irc_send_cap(irc, "LS", str->str);

	g_string_free(str, TRUE);
}

/* this one looks suspiciously similar to cap ls,
 * but cap-3.2 will make them very different */
static void irc_cmd_cap_list(irc_t *irc)
{
	int i;
	gboolean first = TRUE;
	GString *str = g_string_sized_new(256);

	for (i = 0; supported_caps[i].name; i++) {
		if (irc->caps & supported_caps[i].flag) {
			if (!first) {
				g_string_append_c(str, ' ');
			}
			first = FALSE;

			g_string_append(str, supported_caps[i].name);
		}
	}

	irc_send_cap(irc, "LIST", str->str);

	g_string_free(str, TRUE);
}

void irc_cmd_cap(irc_t *irc, char **cmd)
{
	if (!(irc->status & USTATUS_LOGGED_IN)) {
		/* Put registration on hold until CAP END */
		irc->status |= USTATUS_CAP_PENDING;
	}

	if (g_strcasecmp(cmd[1], "LS") == 0) {
		irc_cmd_cap_ls(irc, cmd[2]);

	} else if (g_strcasecmp(cmd[1], "LIST") == 0) {
		irc_cmd_cap_list(irc);

	} else if (g_strcasecmp(cmd[1], "REQ") == 0) {
		gboolean ack = irc_cmd_cap_req(irc, cmd[2]);

		irc_send_cap(irc, ack ? "ACK" : "NAK", cmd[2] ? : "");

	} else if (g_strcasecmp(cmd[1], "END") == 0) {
		irc->status &= ~USTATUS_CAP_PENDING;

		if (irc->status & USTATUS_SASL_PLAIN_PENDING) {
			irc_send_num(irc, 906, ":SASL authentication aborted");
			irc->status &= ~USTATUS_SASL_PLAIN_PENDING;
		}

		irc_check_login(irc);

	} else {
		irc_send_num(irc, 410, "%s :Invalid CAP command", cmd[1]);
	}

}
an class="w"> *value ); struct xt_node *jabber_make_packet( char *name, char *type, char *to, struct xt_node *children ); struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond, char *err_type ); void jabber_cache_add( struct im_connection *ic, struct xt_node *node, jabber_cache_event func ); struct xt_node *jabber_cache_get( struct im_connection *ic, char *id ); void jabber_cache_entry_free( gpointer entry ); void jabber_cache_clean( struct im_connection *ic ); xt_status jabber_cache_handle_packet( struct im_connection *ic, struct xt_node *node ); const struct jabber_away_state *jabber_away_state_by_code( char *code ); const struct jabber_away_state *jabber_away_state_by_name( char *name ); void jabber_buddy_ask( struct im_connection *ic, char *handle ); char *jabber_normalize( const char *orig ); typedef enum { GET_BUDDY_CREAT = 1, /* Try to create it, if necessary. */ GET_BUDDY_EXACT = 2, /* Get an exact match (only makes sense with bare JIDs). */ GET_BUDDY_FIRST = 4, /* No selection, simply get the first resource for this JID. */ } get_buddy_flags_t; struct jabber_error { char *code, *text, *type; }; struct jabber_buddy *jabber_buddy_add( struct im_connection *ic, char *full_jid ); struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid, get_buddy_flags_t flags ); struct jabber_buddy *jabber_buddy_by_ext_jid( struct im_connection *ic, char *jid, get_buddy_flags_t flags ); int jabber_buddy_remove( struct im_connection *ic, char *full_jid ); int jabber_buddy_remove_bare( struct im_connection *ic, char *bare_jid ); time_t jabber_get_timestamp( struct xt_node *xt ); struct jabber_error *jabber_error_parse( struct xt_node *node, char *xmlns ); void jabber_error_free( struct jabber_error *err ); extern const struct jabber_away_state jabber_away_state_list[]; /* io.c */ int jabber_write_packet( struct im_connection *ic, struct xt_node *node ); int jabber_write( struct im_connection *ic, char *buf, int len ); gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition cond ); gboolean jabber_connected_ssl( gpointer data, void *source, b_input_condition cond ); gboolean jabber_start_stream( struct im_connection *ic ); void jabber_end_stream( struct im_connection *ic ); /* sasl.c */ xt_status sasl_pkt_mechanisms( struct xt_node *node, gpointer data ); xt_status sasl_pkt_challenge( struct xt_node *node, gpointer data ); xt_status sasl_pkt_result( struct xt_node *node, gpointer data ); gboolean sasl_supported( struct im_connection *ic ); /* conference.c */ struct groupchat *jabber_chat_join( struct im_connection *ic, char *room, char *nick, char *password ); struct groupchat *jabber_chat_by_jid( struct im_connection *ic, const char *name ); void jabber_chat_free( struct groupchat *c ); int jabber_chat_msg( struct groupchat *ic, char *message, int flags ); int jabber_chat_topic( struct groupchat *c, char *topic ); int jabber_chat_leave( struct groupchat *c, const char *reason ); void jabber_chat_pkt_presence( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node ); void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node ); void jabber_chat_invite( struct groupchat *c, char *who, char *message ); #endif