first raw version of the xchat rxpd plugin
[rxpd] / contrib / irc / xchat / rx.c
1 /*
2     rx.c - rxpd plugin for xchat
3
4   Copyright (C)
5     2007,               Christian Thaeter <ct@pipapo.org>
6
7   This program is free software; you can redistribute it and/or
8   modify it under the terms of the GNU General Public License as
9   published by the Free Software Foundation; either version 2 of the
10   License, or (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include <unistd.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 //#include <sys/resource.h>
29 #include <netinet/in.h>
30 //#include <arpa/inet.h>
31 //#include <fcntl.h>
32 //#include <time.h>
33 #include <netdb.h>
34 #include <sys/uio.h>
35
36 #include "xchat-plugin.h"
37
38 #define PNAME "RxpdPlugin"
39 #define PDESC "Uses rxpd to act on content";
40 #define PVERSION "0.1"
41
42 #define PREFIXCMP(str, pfx) (!strncmp (str, (pfx), sizeof (pfx)-1))
43
44 static xchat_plugin *ph;   /* plugin handle */
45
46 static struct rx_plugin_data
47 {
48   int debug;
49   const char* server;
50   const char* port;
51   const char* prefix;
52   const char* list;
53   int fd;               /* the fd used for CHECK:list */
54   char* eol;
55   char* eob;
56   char buffer[4096];
57 } rx_private;
58
59
60 static char*
61 buffer_readline (struct rx_plugin_data* self)
62 {
63   ssize_t r;
64
65   if (self->eol < self->eob)
66     {
67       //there was a line pending, shift buffer left
68       memmove (self->buffer, self->eol+1, self->eob - self->eol - 1);
69       self->eob = (char*)(self->eob - (self->eol - self->buffer + 1));
70       self->eol = self->buffer;
71       // TODO handle \r's
72     }
73
74   do {
75     // find next newline, terminate string there
76     char* i;
77     for (i = self->buffer; i < self->eob; ++i)
78       {
79         if (*i == '\n')
80           {
81             *i = '\0';
82             self->eol = i;
83             // have line, return it
84             return (self->eob == self->buffer) ? NULL : self->buffer;
85           }
86       }
87
88     r = 0;
89     do
90       {
91         r = read (self->fd, self->eob, 4095 - (self->eob - self->buffer));
92       }
93     while (r == -1 && errno == EINTR);
94
95     if (r != -1)
96       self->eob += r;
97
98   } while (r != -1);
99
100   return NULL;
101 }
102
103
104
105 void
106 xchat_plugin_get_info (char **name,
107                        char **desc,
108                        char **version,
109                        void **reserved)
110 {
111    *name = PNAME;
112    *desc = PDESC;
113    *version = PVERSION;
114 }
115
116 static int
117 rxstart_cb (char *word[], char *word_eol[], void *userdata)
118 {
119   //  server port list; enables and connects to rxpd", &rx_private);
120   struct rx_plugin_data* rx = (struct rx_plugin_data*)userdata;
121
122   rx->server = strdup (word[2]);
123   rx->port = strdup (word[3]);
124   rx->list = strdup (word[4]);
125
126   struct addrinfo* addrs = NULL;
127   int aierr;
128
129   /* resolve peer */
130   aierr = getaddrinfo (rx->server, rx->port, NULL, &addrs);
131   if (aierr)
132     {
133       xchat_printf (ph, "could not resolve %s:%s, %s\n", rx->server, rx->port, gai_strerror (aierr));
134       return XCHAT_EAT_ALL;
135     }
136
137   rx->fd = socket (addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
138   if (rx->fd == -1 || connect (rx->fd, addrs->ai_addr, addrs->ai_addrlen))
139     {
140       freeaddrinfo (addrs);
141       xchat_printf (ph, "error connecting %s:%s, %s\n", rx->server, rx->port, strerror (errno));
142       return XCHAT_EAT_ALL;
143     }
144   freeaddrinfo (addrs);
145
146   struct iovec cmd[3] =
147     {
148       {"CHECK:", sizeof("CHECK:")-1},
149       {(void*)rx->list, strlen (rx->list)},
150       {"\n", 1}
151     };
152
153   ssize_t written = writev (rx->fd, cmd, 3);
154   if (written < cmd[0].iov_len + cmd[1].iov_len + cmd[2].iov_len)
155     {
156       xchat_printf (ph, "error writing check commmand\n");
157       close (rx->fd);
158       rx->fd = -1;
159       return XCHAT_EAT_ALL;
160     }
161
162   if (rx->debug)
163     xchat_printf (ph, "established rxpd connection to %s:%s\n", rx->server, rx->port);
164
165   return XCHAT_EAT_ALL;
166 }
167
168 static int
169 rxstop_cb (char *word[], char *word_eol[], void *userdata)
170 {
171   struct rx_plugin_data* rx = (struct rx_plugin_data*)userdata;
172
173   if (rx->fd != -1)
174     {
175       close (rx->fd);
176       rx->fd = -1;
177       if (rx->debug)
178         xchat_printf (ph, "closed rxpd connection to %s:%s\n", rx->server, rx->port);
179     }
180   else
181     if (rx->debug)
182       xchat_printf (ph, "no rxpd connection established\n");
183
184   return XCHAT_EAT_ALL;
185 }
186
187 static int
188 rxadd_cb (char *word[], char *word_eol[], void *userdata)
189 {
190   //struct rx_plugin_data* rx = (struct rx_plugin_data*)userdata;
191   return XCHAT_EAT_ALL;   /* eat this command so xchat and other plugins can't process it */
192 }
193
194 static int
195 rxdel_cb (char *word[], char *word_eol[], void *userdata)
196 {
197   //struct rx_plugin_data* rx = (struct rx_plugin_data*)userdata;
198   return XCHAT_EAT_ALL;   /* eat this command so xchat and other plugins can't process it */
199 }
200
201 static int
202 rxlist_cb (char *word[], char *word_eol[], void *userdata)
203 {
204   //struct rx_plugin_data* rx = (struct rx_plugin_data*)userdata;
205   return XCHAT_EAT_ALL;   /* eat this command so xchat and other plugins can't process it */
206 }
207
208 static int
209 rxraw_cb (char *word[], char *word_eol[], void *userdata)
210 {
211   //struct rx_plugin_data* rx = (struct rx_plugin_data*)userdata;
212   return XCHAT_EAT_ALL;   /* eat this command so xchat and other plugins can't process it */
213 }
214
215 static int
216 rxdebug_cb (char *word[], char *word_eol[], void *userdata)
217 {
218   struct rx_plugin_data* rx = (struct rx_plugin_data*)userdata;
219   rx->debug ^= 1;
220   xchat_printf (ph, "Turned rxpd debugging %s\n", rx->debug?"on":"off");  
221   return XCHAT_EAT_ALL;
222 }
223
224 static int
225 rxhook_cb (char *word[], char *word_eol[], void *userdata)
226 {
227   struct rx_plugin_data* rx = (struct rx_plugin_data*)userdata;
228
229   if (rx->fd != -1)
230     {
231       /* ok do the checking*/
232
233       struct iovec check[2] =
234         {
235           {(void*)word_eol[1], strlen (word_eol[1])},
236           {"\n", 1}
237         };
238
239       ssize_t written = writev (rx->fd, check, 2);
240       if (written < check[0].iov_len + 1)
241         {
242           xchat_printf (ph, "RXPD: error writing\n");
243           close (rx->fd);
244           rx->fd = -1;
245           return XCHAT_EAT_NONE;
246         }
247
248       char* line = buffer_readline (rx);
249
250       if (rx->debug && !PREFIXCMP(line, "ok:"))
251         xchat_printf (ph, "RXPD: '%s'\n", line);
252
253       if (PREFIXCMP(line, "ignore:"))
254         return XCHAT_EAT_XCHAT;
255
256       //else if (PREFIXCMP(line, "kick:"))
257       //else if (PREFIXCMP(line, "kickban:"))
258       //else if (PREFIXCMP(line, "ban:"))
259       //else if (PREFIXCMP(line, "op:"))
260
261     }
262
263   return XCHAT_EAT_NONE;
264 }
265
266 int
267 xchat_plugin_init(xchat_plugin *plugin_handle,
268                   char **plugin_name,
269                   char **plugin_desc,
270                   char **plugin_version,
271                   char *arg)
272 {
273   /* we need to save this for use with any xchat_* functions */
274   ph = plugin_handle;
275
276   /* tell xchat our info */
277   *plugin_name = PNAME;
278   *plugin_desc = PDESC;
279   *plugin_version = PVERSION;
280
281   rx_private.debug = 0;
282   rx_private.server = NULL;
283   rx_private.port = NULL;
284   rx_private.list = NULL;
285   rx_private.fd = -1;
286   rx_private.eol = rx_private.eob = rx_private.buffer;
287   rx_private.buffer [4095] = '\0';
288
289   xchat_hook_command (ph, "RXSTART", XCHAT_PRI_NORM, rxstart_cb,
290                       "Usage: RXSTART server port list; enables and connects to rxpd", &rx_private);
291
292   xchat_hook_command (ph, "RXSTOP", XCHAT_PRI_NORM, rxstop_cb,
293                       "Usage: RXSTOP; disconnects and stops rxpd plugin", &rx_private);
294
295   xchat_hook_command (ph, "RXADD", XCHAT_PRI_NORM, rxadd_cb,
296                       "Usage: RXADD listname rule; adds rule to list", &rx_private);
297
298   xchat_hook_command (ph, "RXDEL", XCHAT_PRI_NORM, rxdel_cb,
299                       "Usage: RXDEL list rule; removes rule from list", &rx_private);
300
301   xchat_hook_command (ph, "RXLIST", XCHAT_PRI_NORM, rxlist_cb,
302                       "Usage: RXLIST [list]; shows list", &rx_private);
303
304   xchat_hook_command (ph, "RXRAW", XCHAT_PRI_NORM, rxraw_cb,
305                       "Usage: RXRAW ...; sends a raw command to the rxpd", &rx_private);
306
307   xchat_hook_command (ph, "RXDEBUG", XCHAT_PRI_NORM, rxdebug_cb,
308                       "Usage: RXDEBUG; toggle rxpd plugin debugging", &rx_private);
309
310   xchat_hook_server (ph, "RAW LINE", XCHAT_PRI_NORM, rxhook_cb, &rx_private);
311
312   xchat_print (ph, "Rxpd plugin loaded successfully!\n");
313
314   return 1;
315 }