use DUMPH in the xchat 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 #define _GNU_SOURCE
23
24 #include <unistd.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <errno.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <netdb.h>
33 #include <sys/uio.h>
34
35 #include "xchat-plugin.h"
36
37 #define PNAME "Rxpd"
38 #define PDESC "Uses rxpd to act on content";
39 #define PVERSION "0.1"
40
41 /*
42   TODO
43   * reconnect on connection loose
44   * config-file and auto startup
45 */
46
47
48 #define PREFIXCMP(str, pfx) (!strncmp (str, pfx, sizeof (pfx)-1))
49
50 static xchat_plugin *ph;   /* plugin handle */
51
52 static struct rx_plugin_data
53 {
54   int debug;
55   const char* server;
56   const char* port;
57   const char* prefix;
58   const char* list;
59   struct addrinfo* addrs;
60   FILE* rxpd;
61 } rx_private;
62
63
64 void
65 xchat_plugin_get_info (char **name,
66                        char **desc,
67                        char **version,
68                        void **reserved)
69 {
70    *name = PNAME;
71    *desc = PDESC;
72    *version = PVERSION;
73 }
74
75 static FILE*
76 rxopen (struct rx_plugin_data* rx)
77 {
78   if (!rx->addrs)
79     {
80       xchat_printf (ph, "RX: no server set up");
81       return NULL;
82     }
83
84   int fd;
85
86   fd = socket (rx->addrs->ai_family, rx->addrs->ai_socktype, rx->addrs->ai_protocol);
87   if (fd == -1 || connect (fd, rx->addrs->ai_addr, rx->addrs->ai_addrlen))
88     {
89       xchat_printf (ph, "RX: error connecting %s:%s, %s\n", rx->server, rx->port, strerror (errno));
90       return NULL;
91     }
92
93   FILE* conn;
94   conn = fdopen (fd, "rb+");
95   if (!conn)
96     {
97       close (fd);
98       xchat_printf (ph, "RX: error connecting %s:%s, %s\n", rx->server, rx->port, strerror (errno));
99       return NULL;
100     }
101
102   return conn;
103 }
104
105
106 static int
107 rxstart_cb (char *word[], char *word_eol[], void *userdata)
108 {
109   struct rx_plugin_data* rx = (struct rx_plugin_data*)userdata;
110
111   if (rx->rxpd)
112     {
113       xchat_printf (ph, "RX: already connected");
114       return XCHAT_EAT_ALL;
115     }
116
117   rx->server = strdup (word[2]);
118   rx->port = strdup (word[3]);
119   rx->list = strdup (word[4]);
120   rx->prefix = strdup (word[5]);
121
122   int aierr;
123
124   /* resolve peer */
125   aierr = getaddrinfo (rx->server, rx->port, NULL, &rx->addrs);
126   if (aierr)
127     {
128       xchat_printf (ph, "could not resolve %s:%s, %s\n", rx->server, rx->port, gai_strerror (aierr));
129       return XCHAT_EAT_ALL;
130     }
131
132
133   rx->rxpd = rxopen (rx);
134   if (!rx->rxpd)
135     {
136       freeaddrinfo (rx->addrs);
137       rx->addrs = NULL;
138       return XCHAT_EAT_ALL;
139     }
140
141   fprintf (rx->rxpd, "CHECK:%s%s\n", rx->prefix, rx->list);
142   /*error handling here*/
143
144   xchat_printf (ph, "established rxpd connection to %s:%s\n", rx->server, rx->port);
145
146   return XCHAT_EAT_ALL;
147 }
148
149 static int
150 rxstop_cb (char *word[], char *word_eol[], void *userdata)
151 {
152   struct rx_plugin_data* rx = (struct rx_plugin_data*)userdata;
153
154   if (rx->rxpd)
155     {
156       fclose (rx->rxpd);
157       rx->rxpd = NULL;
158       freeaddrinfo (rx->addrs);
159       rx->addrs = NULL;
160       xchat_printf (ph, "closed rxpd connection to %s:%s\n", rx->server, rx->port);
161     }
162   else
163     xchat_printf (ph, "no rxpd connection established\n");
164
165   return XCHAT_EAT_ALL;
166 }
167
168 static int
169 rxadd_cb (char *word[], char *word_eol[], void *userdata)
170 {
171   struct rx_plugin_data* rx = (struct rx_plugin_data*)userdata;
172
173   if (*word[2] == '\0' || *word[3] == '\0')
174     {
175       xchat_printf (ph, "RXADD needs two parameters (/RXADD list rule...)\n");
176       return XCHAT_EAT_ALL;
177     }
178
179   FILE* conn;
180   conn = rxopen (rx);
181   if (!conn)
182     return XCHAT_EAT_ALL;
183
184   if (fprintf (conn, "APPEND:%s%s\n%s\n!EXIT\n", rx->prefix, word[2], word_eol[3]) < 0)
185     {
186       xchat_printf (ph, "RX: sending command failed\n");
187       fclose (conn);
188       return XCHAT_EAT_ALL;
189     }
190
191   char buffer[4096];
192   *buffer = '\0';
193   fgets (buffer, 4095, conn);
194   xchat_printf (ph, "RX: add '%s' %s", word_eol[3], buffer);
195
196   fclose (conn);
197
198   conn = rxopen (rx);
199   if (!conn)
200     return XCHAT_EAT_ALL;
201
202   if (fprintf (conn, "SAVE:%s%s\n", rx->prefix, word[2]) < 0)
203     xchat_printf (ph, "RX: saving failed\n");
204   fclose (conn);
205   return XCHAT_EAT_ALL;
206 }
207
208 static int
209 rxdel_cb (char *word[], char *word_eol[], void *userdata)
210 {
211   struct rx_plugin_data* rx = (struct rx_plugin_data*)userdata;
212
213   if (*word[2] == '\0' || *word[3] == '\0')
214     {
215       xchat_printf (ph, "RXDEL needs two parameters (/RXADD list rule...)\n");
216       return XCHAT_EAT_ALL;
217     }
218
219   FILE* conn;
220   conn = rxopen (rx);
221   if (!conn)
222     return XCHAT_EAT_ALL;
223
224   if (fprintf (conn, "REMOVE:%s%s\n%s\n!EXIT\n", rx->prefix, word[2], word_eol[3]) < 0)
225     {
226       xchat_printf (ph, "RX: sending command failed\n");
227       fclose (conn);
228       return XCHAT_EAT_ALL;
229     }
230
231   char buffer[4096];
232   *buffer = '\0';
233   fgets (buffer, 4095, conn);
234   xchat_printf (ph, "RX: del '%s' %s", word_eol[3], buffer);
235
236   fclose (conn);
237   conn = rxopen (rx);
238   if (!conn)
239     return XCHAT_EAT_ALL;
240
241   if (fprintf (conn, "SAVE:%s%s\n", rx->prefix, word[2]) < 0)
242     xchat_printf (ph, "RX: saving failed\n");
243   fclose (conn);
244   return XCHAT_EAT_ALL;
245 }
246
247 static int
248 rxlist_cb (char *word[], char *word_eol[], void *userdata)
249 {
250   struct rx_plugin_data* rx = (struct rx_plugin_data*)userdata;
251
252   FILE* conn = rxopen (rx);
253   if (!conn)
254     return XCHAT_EAT_ALL;
255
256   const char* list = *word[2]?word[2]:rx->list;
257
258   xchat_printf (ph, "\nRX: Listing '%s%s'\n", rx->prefix, list);
259
260   fprintf (conn, "DUMPH:%s%s\n", rx->prefix, list);
261   /*error handling here*/
262
263   char buffer[4096];
264   while (fgets (buffer, 4095, conn))
265     {
266       xchat_printf (ph, "%s", buffer);
267     }
268   xchat_printf (ph, "\n");
269
270   fclose (conn);
271   return XCHAT_EAT_ALL;
272 }
273
274 #if 0
275 static int
276 rxraw_cb (char *word[], char *word_eol[], void *userdata)
277 {
278   //struct rx_plugin_data* rx = (struct rx_plugin_data*)userdata;
279   return XCHAT_EAT_ALL;   /* eat this command so xchat and other plugins can't process it */
280 }
281 #endif
282
283 static int
284 rxdebug_cb (char *word[], char *word_eol[], void *userdata)
285 {
286   struct rx_plugin_data* rx = (struct rx_plugin_data*)userdata;
287   rx->debug ^= 1;
288   xchat_printf (ph, "Turned rxpd debugging %s\n", rx->debug?"on":"off");  
289   return XCHAT_EAT_ALL;
290 }
291
292 static int
293 rxinfo_cb (char *word[], char *word_eol[], void *userdata)
294 {
295   struct rx_plugin_data* rx = (struct rx_plugin_data*)userdata;
296   xchat_printf (ph, "RX: debugging is %s\n", rx->debug?"on":"off");  
297   if (rx->addrs)
298     {
299       xchat_printf (ph, "RX: using server %s:%s\n", rx->server, rx->port);  
300       xchat_printf (ph, "RX: using prefix %s\n", rx->prefix);
301       xchat_printf (ph, "RX: using list   %s%s\n", rx->prefix, rx->list);
302       xchat_printf (ph, "RX: connection is %s\n", rx->rxpd?"active":"broken");  
303     }
304   else
305     xchat_printf (ph, "RX: not started\n");
306
307   return XCHAT_EAT_ALL;
308 }
309
310 static int
311 rxhook_cb (char *word[], char *word_eol[], void *userdata)
312 {
313   struct rx_plugin_data* rx = (struct rx_plugin_data*)userdata;
314
315   if (rx->rxpd)
316     {
317       /* ok do the checking*/
318       if (fprintf (rx->rxpd, "%s\n", word_eol[1]) < 0)
319         {
320           xchat_printf (ph, "RX: rxpd connection lost\n");
321           fclose (rx->rxpd);
322           rx->rxpd = NULL;
323           return XCHAT_EAT_NONE;
324         }
325
326       char buffer[4096];
327
328       if (!fgets (buffer, 4095, rx->rxpd))
329         {
330           xchat_printf (ph, "RX: rxpd connection lost\n");
331           fclose (rx->rxpd);
332           rx->rxpd = NULL;
333           return XCHAT_EAT_NONE;
334         }
335
336       char* nl = strrchr(buffer, '\n');
337       if (nl)
338         *nl = '\0';
339
340       if (rx->debug && !PREFIXCMP(buffer, "ok:"))
341         xchat_printf (ph, "RX: '%s' matched '%s'\n", word_eol[1], buffer);
342
343       if (PREFIXCMP(buffer, "ignore:"))
344         return XCHAT_EAT_XCHAT;
345
346       else if (PREFIXCMP(buffer, "op:"))
347         {
348           const char* iam = xchat_get_info (ph, "nick");
349
350           char* hisnickend = strchr (word_eol[1]+1, '!');
351           if (hisnickend)
352             {
353               char* hisnick = strndupa (word_eol[1]+1, hisnickend - word_eol[1]-1);
354
355               int op_him = 0;
356
357               xchat_list* users = xchat_list_get (ph, "users");
358               if (users)
359                 {
360                   while (xchat_list_next (ph, users))
361                     {
362                       if (!strcmp (iam, xchat_list_str (ph, users, "nick")))
363                         {
364                           if (*xchat_list_str (ph, users, "prefix") != '@')
365                             {
366                               op_him = 0;
367                               break;
368                             }
369                         }
370
371                       if (!strcmp (hisnick, xchat_list_str (ph, users, "nick")))
372                         {
373                           if (*xchat_list_str (ph, users, "prefix") != '@')
374                             op_him = 1;
375                           else
376                             {
377                               op_him = 0;
378                               break;
379                             }
380                         }
381                     }
382                   xchat_list_free (ph, users);
383
384                   if (op_him)
385                     xchat_commandf (ph, "OP %s\n", hisnick);
386                 }
387             }
388         }
389       //else if (PREFIXCMP(buffer, "kick:"))
390       //else if (PREFIXCMP(buffer, "kickban:"))
391       //else if (PREFIXCMP(buffer, "ban:"))
392     }
393
394   return XCHAT_EAT_NONE;
395 }
396
397 static struct commands
398 {
399   const char* command;
400   const char* help;
401   int (*callback) (char *[], char *[], void *);
402 } command_table[] =
403   {
404     {"RXSTART", "Usage: RXSTART server port list prefix; enables and connects to rxpd", rxstart_cb},
405     {"RXSTOP", "Usage: RXSTOP; disconnects and stops rxpd plugin", rxstop_cb},
406     {"RXADD", "Usage: RXADD listname rule; adds rule to list", rxadd_cb},
407     {"RXDEL", "Usage: RXDEL list rule; removes rule from list", rxdel_cb},
408     {"RXLIST", "Usage: RXLIST [list]; shows list", rxlist_cb},
409     //{"RXRAW", "Usage: RXRAW ...; sends a raw command to the rxpd", rxraw_cb},
410     {"RXDEBUG", "Usage: RXDEBUG; toggle rxpd plugin debugging", rxdebug_cb},
411     {"RXINFO", "Usage: RXINFO; show some information about the rxpd plugin", rxinfo_cb},
412     {NULL, NULL}
413   };
414
415
416 int
417 xchat_plugin_init (xchat_plugin *plugin_handle,
418                    char **plugin_name,
419                    char **plugin_desc,
420                    char **plugin_version,
421                    char *arg)
422 {
423   /* we need to save this for use with any xchat_* functions */
424   ph = plugin_handle;
425
426   /* tell xchat our info */
427   *plugin_name = PNAME;
428   *plugin_desc = PDESC;
429   *plugin_version = PVERSION;
430
431   rx_private.debug = 0;
432   rx_private.server = NULL;
433   rx_private.port = NULL;
434   rx_private.prefix = NULL;
435   rx_private.list = NULL;
436   rx_private.addrs = NULL;
437   rx_private.rxpd = NULL;
438
439   struct commands* itr;
440   for (itr = command_table; itr->command; ++itr)
441     xchat_hook_command (ph, itr->command, XCHAT_PRI_NORM, itr->callback,
442                         itr->help, &rx_private);
443
444   char buffer[4096];
445   *buffer = '\0';
446   snprintf (buffer, 4095, "%s/rxpd.conf", xchat_get_info(ph, "xchatdirfs"));
447   FILE* conf = fopen (buffer, "r");
448   if (conf)
449     {
450       if (fgets (buffer, 4095, conf))
451         {
452           char* nl = strchr (buffer, '\n');
453           if (nl)
454             *nl = '\0';
455           xchat_commandf (ph, "RXSTART %s", buffer);
456         }
457       fclose (conf);
458     }
459
460   xchat_hook_server (ph, "RAW LINE", XCHAT_PRI_NORM, rxhook_cb, &rx_private);
461
462   xchat_print (ph, "Rxpd plugin loaded successfully!\n");
463
464   return 1;
465 }
466
467 int
468 xchat_plugin_deinit (void)
469 {
470   if (rx_private.rxpd)
471     fclose (rx_private.rxpd);
472
473   if (rx_private.addrs)
474     freeaddrinfo (rx_private.addrs);
475
476   xchat_printf (ph, "rxpd plugin unloaded\n");
477
478   return 1;
479