aboutsummaryrefslogtreecommitdiff
path: root/plugins/irc.c
blob: acf2f0b860b483f05cdf28ce0e9fbb80bbfa51ef (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
/*
 * http.c: IRC service detection plugin
 *
 * Copyright (c) 2014, Přemysl Janouch <p@janouch.name>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#define LIBERTY_WANT_PROTO_IRC

#include "config.h"
#include "../liberty/liberty.c"
#include "../plugin-api.h"

// --- Selected IRC stuff ------------------------------------------------------

#define IRC_MAX_NICKNAME  9             ///< The limit from RFC 2812

#define IRC_RPL_WELCOME   1
#define IRC_RPL_MYINFO    4

// --- Service detection -------------------------------------------------------

static struct plugin_data
{
	void *ctx;                          ///< Application context
	struct plugin_api *api;             ///< Plugin API vtable
}
g_data;

struct scan_data
{
	struct str input;                   ///< Input buffer
	struct unit *u;                     ///< Scan unit
};

static void *
scan_init (struct service *service, struct unit *u)
{
	(void) service;

	char nick[IRC_MAX_NICKNAME + 1];
	size_t i;
	for (i = 0; i < sizeof nick - 1; i++)
		nick[i] = 'a' + rand () % ('z' - 'a' + 1);
	nick[i] = '\0';

	struct str hello = str_make ();
	str_append_printf (&hello,
		"NICK %s\r\nUSER %s 8 * :%s\r\n", nick, nick, nick);
	g_data.api->unit_write (u, hello.str, hello.len);
	str_free (&hello);

	struct scan_data *scan = xcalloc (1, sizeof *scan);
	scan->input = str_make ();
	scan->u = u;
	return scan;
}

static void
scan_free (void *handle)
{
	struct scan_data *scan = handle;
	str_free (&scan->input);
	free (scan);
}

static void
on_irc_message (const struct irc_message *msg, const char *raw, void *user_data)
{
	(void) raw;
	struct scan_data *scan = user_data;

	unsigned long code;
	if (!irc_strcmp (msg->command, "PING"))
	{
		// Without this we might be unable to finish registration
		struct str pong = str_make ();
		str_append_printf (&pong, "PONG :%s\r\n",
			msg->params.len > 0 ? msg->params.vector[0] : "");
		g_data.api->unit_write (scan->u, pong.str, pong.len);
	}
	else if (strlen (msg->command) == 3 && xstrtoul (&code, msg->command, 10))
	{
		// It looks like we've successfully registered
		if (msg->prefix && code == IRC_RPL_WELCOME)
			g_data.api->unit_set_success (scan->u, true);

		// Extract the server name at least
		if (code == IRC_RPL_MYINFO && msg->params.len > 0)
		{
			char *info = xstrdup_printf ("%s: %s",
				"server name", msg->params.vector[1]);
			g_data.api->unit_add_info (scan->u, info);
			free (info);

			g_data.api->unit_stop (scan->u);
		}
	}
}

static void
on_data (void *handle, const void *data, size_t len)
{
	struct scan_data *scan = handle;
	str_append_data (&scan->input, data, len);
	irc_process_buffer (&scan->input, on_irc_message, scan);
}

static struct service g_irc_service =
{
	.name        = "IRC",
	.flags       = SERVICE_SUPPORTS_TLS,

	.scan_init   = scan_init,
	.scan_free   = scan_free,
	.on_data     = on_data,
	.on_eof      = NULL,
	.on_error    = NULL,
	.on_stopped  = NULL
};

static bool
initialize (void *ctx, struct plugin_api *api)
{
	g_data = (struct plugin_data) { .ctx = ctx, .api = api };
	api->register_service (ctx, &g_irc_service);
	return true;
}

struct plugin_info ponymap_plugin_info =
{
	.api_version  = API_VERSION,
	.initialize   = initialize
};