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