1 /*
2   Europa - Copyright (c) 1999, Ed Schlunder <zilym@asu.edu>
3 
4   This is free software distributable under the terms of the GNU GPL-- See
5   the file COPYING for details.
6  */
7 
8 #define MOD_VERSION "0.01"
9 #define MOD_NAME "Europa"
10 #include "europa.h"
11 
12 #include <mysql/mysql.h>
13 
14 /* yay! mysql.h and irc.h both define NAME_LEN... */
15 #undef NAME_LEN
16 #include "irc.h"
17 #include "struct.h"
18 #include "server.h"
19 #include "ircaux.h"
20 #include "status.h"
21 #include "screen.h"
22 #include "vars.h"
23 #include "misc.h"
24 #include "output.h"
25 #include "hook.h"
26 #include "module.h"
27 #define INIT_MODULE
28 #include "modval.h"
29 #include "irc_std.h"
30 
31 #define MAX_WORD 100
32 #define MAX_WORDS 1000
33 #define MAX_CHANNELS 10
34 #define MAX_QUERY 1000
35 
36 int beQuiet = 0;
37 MYSQL mysql;
38 
39 /* called when bitchx user enters IRC command "/europa DATA" */
BUILT_IN_DLL(europa)40 BUILT_IN_DLL(europa)
41 {
42   int i;
43 
44   put_it("** /europa %s baby!", args);
45 }
46 
47 /* print some chat to both the IRC server (other channel members) and the local
48    client window... */
dualOut(const char * str,const char * chan)49 void dualOut(const char *str, const char *chan) {
50   put_it("<europa> %s", str);
51   send_to_server("PRIVMSG %s :%s", chan, str);
52 }
53 
54 /* a wrapper on dualOut() to do printf-like text formatting on outgoing text */
sout(char * chan,const char * format,...)55 void sout(char *chan, const char *format, ...) {
56   va_list args;
57   char tmp[MAX_WORD * MAX_WORDS];
58 
59   if(beQuiet)
60     return;
61 
62   va_start(args, format);
63   vsnprintf(tmp, MAX_WORD*MAX_WORDS, format, args);
64   va_end(args);
65 
66   dualOut(tmp, chan);
67 }
68 
sdunno(char * args[])69 void sdunno(char *args[]) {
70   sout(args[1], "%s: Hmm, I don't know that one...", args[0]);
71 }
72 
shello(char * chan,char * who)73 void shello(char *chan, char *who) {
74   if(who == NULL)
75     sout(chan, "hey, how's it going?");
76   else
77     sout(chan, "%s: hey, how's it going?", who);
78 }
79 
80 /* attempts to find an answer to a keyword that we've been asked about... */
dbLookup(char * keyword,char * table)81 char *dbLookup(char *keyword, char *table) {
82   MYSQL_RES *res;
83   MYSQL_ROW row;
84   char query[MAX_QUERY];
85   char *answer, *encodedKeyword = malloc(strlen(keyword) * 2 + 1);
86 
87   mysql_escape_string(encodedKeyword, keyword, strlen(keyword));
88   if(snprintf(query, MAX_QUERY,
89 	      "select answer from %s where keyword like '%s'",
90 	      table, encodedKeyword) == -1) {
91     put_it("** Europa query overflow (increase MAX_QUERY)");
92 
93     free(encodedKeyword);
94     return NULL;
95   }
96   free(encodedKeyword);
97 
98   if(mysql_query(&mysql, query))
99     return NULL;
100 
101   if(!(res = mysql_store_result(&mysql))) {
102     /* this shouldn't happen... */
103     put_it("** Europa query failure: %s", query);
104     return NULL;
105   }
106 
107   row = mysql_fetch_row(res);
108   if(row == NULL) {
109       mysql_free_result(res);
110       return NULL;
111   }
112 
113   answer = strdup(row[0]);
114 
115   mysql_free_result(res);
116   return answer;
117 }
118 
119 /* args[0] - nick of sender
120    args[1] - channel text was said on
121   args[2] - actual text */
processChat(int argc,char * args[],char * argl[])122 void processChat(int argc, char *args[], char *argl[]) {
123   int i;
124 
125   if(argc < 3) return;
126   if(strcmp(args[3], "shutup") == 0) {
127     sout(args[1], "%s: okay, okay...", args[0]);
128     beQuiet = -1;
129     return;
130   }
131 
132   if(strcmp(args[3], "hello") == 0 ||
133      strcmp(args[3], "hello?") == 0) {
134     if(beQuiet)
135       beQuiet = 0;
136     else
137       shello(args[1], args[0]);
138   }
139 
140   if(argc < 4) return;
141   if(strcmp(args[3], "ex") == 0 ||
142      strcmp(args[3], "explain") == 0) {
143     int pengy = 0;
144     char *answer;
145 
146     answer = dbLookup(args[4], "fact");
147     if(answer == NULL) {
148       answer = dbLookup(args[4], "facts");
149       if(answer == NULL) {
150 	sdunno(args);
151 	return;
152       }
153       pengy = -1;
154     }
155 
156     if(pengy)
157       sout(args[1], "%s: %s (from Pengy)", args[0], answer);
158     else
159       sout(args[1], "%s: %s", args[0], answer);
160 
161     free(answer);
162     return;
163   }
164 
165   if(strcmp(args[3], "learn") == 0) {
166     char query[MAX_QUERY];
167     char *encodedKeyword = malloc(strlen(args[4]) * 2 + 1),
168       *encodedAnswer = malloc(strlen(argl[5]) * 2 + 1);
169 
170     mysql_escape_string(encodedKeyword, args[4], strlen(args[4]));
171     mysql_escape_string(encodedAnswer, argl[5], strlen(argl[5]));
172 
173     snprintf(query, MAX_QUERY, "insert into fact values('%s','%s')",
174 	     encodedKeyword, encodedAnswer);
175 
176     free(encodedKeyword);
177     free(encodedAnswer);
178 
179     if(mysql_query(&mysql, query))
180       put_it("** Europa db query failed: %s", query);
181     else
182       sout(args[1], "%s: %s learned, thanks...", args[0], args[4]);
183 
184     return;
185   }
186 
187   if(strcmp(args[3], "forget") == 0) {
188     char query[MAX_QUERY];
189     char *encodedKeyword = malloc(strlen(args[4]) * 2 + 1);
190 
191     mysql_escape_string(encodedKeyword, args[4], strlen(args[4]));
192     snprintf(query, MAX_QUERY,
193 	     "delete from fact where keyword='%s'", encodedKeyword);
194     free(encodedKeyword);
195 
196     if(mysql_query(&mysql, query)) {
197       snprintf(query, MAX_QUERY,
198 	       "delete from facts where keyword='%s'", args[4]);
199       if(mysql_query(&mysql, query)) {
200 	put_it("** Europa db query failed: %s", query);
201 	sout(args[1], "%s: I didn't know anything about %s anyway...",
202 	     args[0], args[4]);
203       }
204       else
205 	sout(args[1], "%s: %s forgotten from Pengy db...", args[0], args[4]);
206     }
207     else
208       sout(args[1], "%s: %s forgotten...", args[0], args[4]);
209 
210     return;
211   }
212 }
213 
214 /* called by BitchX whenever someone says something in the channel directed to
215    this user... */
public_ar_proc(char * which,char * str,char ** unused)216 int public_ar_proc(char *which, char *str, char **unused) {
217   char *local, *args[MAX_WORDS], *argl[MAX_WORDS];
218   int i = 0, total, w = 0;
219 
220   /*
221     we want to parse out the text into separate words. args[WORD_N] is
222     a pointer to a single word while argl[WORD_N] is a pointer to the
223     rest of the sentence beginning at word WORD_N.
224   */
225   argl[0] = str;
226   while(i < strlen(str)) {
227     if(str[i] != ' ') break;
228     i++;
229   }
230   local = strdup(str + i);
231   args[0] = local;
232 
233   total = strlen(local);
234   while(i < total && w < MAX_WORDS) {
235     if(local[i] == ' ') {
236       local[i] = 0;
237       w++;
238       while(++i < total)
239 	if(local[i] != ' ') break;
240 
241       args[w] = local + i;
242       argl[w] = str + i;
243     }
244 
245     i++;
246   }
247 
248   processChat(w, args, argl);
249 
250   free(local);
251   return 0;
252 }
253 
public_proc(char * which,char * str,char ** unused)254 int public_proc(char *which, char *str, char **unused) {
255   char *local, *args[MAX_WORDS], *argl[MAX_WORDS];
256   int i = 0, total, w = 0;
257 
258   /*
259     we want to parse out the text into separate words. args[WORD_N] is
260     a pointer to a single word while argl[WORD_N] is a pointer to the
261     rest of the sentence beginning at word WORD_N.
262   */
263   argl[0] = str;
264   while(i < strlen(str)) {
265     if(str[i] != ' ') break;
266     i++;
267   }
268   local = strdup(str + i);
269   args[0] = local;
270 
271   total = strlen(local);
272   while(i < total && w < MAX_WORDS) {
273     if(local[i] == ' ') {
274       local[i] = 0;
275       w++;
276       while(++i < total)
277 	if(local[i] != ' ') break;
278 
279       args[w] = local + i;
280       argl[w] = str + i;
281     }
282 
283     i++;
284   }
285 
286   if(w > 1) {
287     if(strstr(argl[2], "hello") != NULL)
288       shello(args[1], args[0]);
289   }
290 
291   free(local);
292   return 0;
293 }
294 
295 /* called when user enters irc command "/explain USER/CHANNEL KEYWORD" */
BUILT_IN_DLL(cmdExplain)296 BUILT_IN_DLL(cmdExplain)
297 {
298   char *local, *arg[MAX_WORDS], *argl[MAX_WORDS];
299   int i = 0, total, w = 0;
300 
301   argl[0] = args;
302   while(i < strlen(args)) {
303     if(args[i] != ' ') break;
304     i++;
305   }
306   local = strdup(args + i);
307   arg[0] = local;
308 
309   total = strlen(local);
310   while(i < total && w < MAX_WORDS) {
311     if(local[i] == ' ') {
312       local[i] = 0;
313       w++;
314       while(++i < total)
315 	if(local[i] != ' ') break;
316 
317       arg[w] = local + i;
318       argl[w] = args + i;
319     }
320 
321     i++;
322   }
323 
324   if(w) {
325     int pengy = 0;
326     char *answer = dbLookup(arg[1], "fact");
327 
328     if(answer == NULL) {
329       answer = dbLookup(arg[1], "facts");
330       if(answer == NULL) {
331 	put_it("** Europa doesn't know about %s", arg[1]);
332 	free(local);
333 	return;
334       }
335 
336       pengy = -1;
337     }
338 
339     if(pengy)
340       sout(arg[0], "%s (from Pengy)", answer);
341     else
342       sout(arg[0], answer);
343   }
344 
345   free(local);
346   return;
347 }
348 
Europa_Init(IrcCommandDll ** intp,Function_ptr * global_table)349 int Europa_Init(IrcCommandDll **intp, Function_ptr *global_table) {
350   initialize_module(MOD_NAME);
351 
352   add_module_proc(COMMAND_PROC, MOD_NAME, "europa", NULL, 0, 0, europa, NULL);
353   add_module_proc(COMMAND_PROC, MOD_NAME, "explain", NULL, 0, 0, cmdExplain, NULL);
354   add_module_proc(HOOK_PROC, MOD_NAME, NULL, "*", PUBLIC_AR_LIST, 1, NULL, public_ar_proc);
355   add_module_proc(HOOK_PROC, MOD_NAME, NULL, "*", PUBLIC_LIST, 1, NULL, public_proc);
356 
357   put_it("** Europa v%s connecting to database backend...", MOD_VERSION);
358 
359   /* connect to the database server */
360   if(!(mysql_connect(&mysql, DBHOST, DBUSER, DBPASSWD))) {
361     put_it("** Server refused login/password.");
362     return 0;
363   }
364   if(mysql_select_db(&mysql, DBNAME)) {
365     put_it("** Server refused connection to '%s' database.", DBNAME);
366     return 0;
367   }
368 
369   put_it("** Europa loaded!");
370   return 0;
371 }
372