ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/modules/rpc/xmlrpc.C
Revision: 1.8
Committed: Sat Sep 22 14:27:28 2007 UTC (16 years, 8 months ago) by pippijn
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.7: +3 -2 lines
Log Message:
split up ermyth into ermyth-modules, libermyth (currently just ermyth-util) and ermyth-core

File Contents

# Content
1 /*
2 * xmlrpc.C: New xmlrpc implementation
3 *
4 * Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team
5 * Rights to this code are as documented in COPYING.
6 *
7 *
8 * Portions of this file were derived from sources bearing the following license:
9 * Copyright © 2005-2006 Jilles Tjoelker et al.
10 * Rights to this code are as documented in doc/pod/license.pod.
11 *
12 * $Id: xmlrpc.C,v 1.7 2007-09-16 18:54:44 pippijn Exp $
13 */
14
15 #include "atheme.h"
16 #include <libermyth.h>
17 #include <ermyth/module.h>
18 #include <account/myuser.h>
19 #include "datastream.h"
20 #include "authcookie.h"
21 #include "connection.h"
22 #include "confparse.h"
23
24 #include "httpd.h"
25
26 #include "xml/rpc.h"
27
28 static char const rcsid[] = "$Id: xmlrpc.C,v 1.7 2007-09-16 18:54:44 pippijn Exp $";
29
30 static void on_config_ready (void);
31
32 REGISTER_MODULE ("rpc/xmlrpc", false, "The Ermyth Team <http://ermyth.one09.net>");
33
34 static void handle_request (connection_t *cptr, void *requestbuf);
35
36 connection_t *current_cptr; /* XXX: Hack: xml/rpc.c requires us to do this */
37
38 E pathvec pathhandlers;
39
40 static void xmlrpc_command_fail (sourceinfo_t *si, fault::code code, char const *message);
41 static void xmlrpc_command_success_nodata (sourceinfo_t *si, char const *message);
42 static void xmlrpc_command_success_string (sourceinfo_t *si, char const *result, char const *message);
43
44 static int xmlrpcmethod_login (void *conn, int parc, char *parv[]);
45 static int xmlrpcmethod_logout (void *conn, int parc, char *parv[]);
46 static int xmlrpcmethod_command (void *conn, int parc, char *parv[]);
47
48 /* Configuration */
49 ConfTable::list_type conf_xmlrpc_table;
50
51 path_handler_t handle_xmlrpc = {
52 NULL,
53 handle_request
54 };
55
56 static int
57 conf_xmlrpc_path (config_entry_t * ce)
58 {
59 if (!ce->valid ())
60 return -1;
61
62 if (handle_xmlrpc.path != NULL)
63 {
64 if (!strcmp (handle_xmlrpc.path, ce->vardata<char *> ()))
65 return 0;
66 sfree (handle_xmlrpc.path);
67 }
68 handle_xmlrpc.path = sstrdup (ce->vardata<char *> ());
69
70 return 0;
71 }
72
73 static int
74 conf_xmlrpc (config_entry_t * ce)
75 {
76 subblock_handler (ce, conf_xmlrpc_table);
77 return 0;
78 }
79
80 struct sourceinfo_vtable xmlrpc_vtable = {
81 "xmlrpc",
82 xmlrpc_command_fail,
83 xmlrpc_command_success_nodata,
84 xmlrpc_command_success_string
85 };
86
87 static char *
88 dump_buffer (char *buf, int length)
89 {
90 httpddata *hd;
91 char buf1[300];
92
93 hd = static_cast<httpddata *> (current_cptr->userdata);
94 snprintf (buf1, sizeof buf1, "HTTP/1.1 200 OK\r\n"
95 "%s" // connection_close
96 "Server: " PACKAGE_NAME "/%s\r\n" // version
97 "Content-Type: text/xml\r\n"
98 "Content-Length: %d\r\n\r\n", // length
99 hd->connection_close ? "Connection: close\r\n" : "", version, length);
100 sendq_add (current_cptr, buf1, strlen (buf1));
101 sendq_add (current_cptr, buf, length);
102 if (hd->connection_close)
103 sendq_add_eof (current_cptr);
104 return buf;
105 }
106
107 static void
108 handle_request (connection_t *cptr, void *requestbuf)
109 {
110 current_cptr = cptr;
111 xmlrpc_process (static_cast<char *> (requestbuf), cptr);
112 current_cptr = NULL;
113
114 return;
115 }
116
117 static void
118 on_config_ready (void)
119 {
120 if (handle_xmlrpc.handler != NULL)
121 {
122 if (std::find_if (pathhandlers.begin (), pathhandlers.end (), pathhandler_eq (handle_xmlrpc.path))
123 != pathhandlers.end ())
124 {
125 slog (LG_INFO, "xmlrpc/main.C: handler already in the list");
126 return;
127 }
128
129 pathhandlers.push_back (handle_xmlrpc);
130 }
131 else
132 slog (LG_ERROR, "on_config_ready (): xmlrpc { } block missing or invalid");
133 }
134
135 static void
136 xmlrpc_command_fail (sourceinfo_t *si, fault::code code, char const *message)
137 {
138 connection_t *cptr;
139 httpddata *hd;
140 char newmessage[256];
141 int i;
142 char const *p;
143
144 cptr = si->connection;
145 hd = static_cast<httpddata *> (cptr->userdata);
146 if (hd->sent_reply)
147 return;
148 i = 0, p = message;
149 while (i < 255 && *p != '\0')
150 if (*p > '\0' && *p < ' ')
151 p++;
152 else
153 newmessage[i++] = *p, p++;
154 newmessage[i] = '\0';
155 xmlrpc_generic_error (code, newmessage);
156 hd->sent_reply = true;
157 }
158
159 static void
160 xmlrpc_command_success_nodata (sourceinfo_t *si, char const *message)
161 {
162 char *p;
163 char const *q;
164 size_t msglen;
165 connection_t *cptr;
166 httpddata *hd;
167
168 cptr = si->connection;
169 hd = static_cast<httpddata *> (cptr->userdata);
170
171 msglen = strlen (message);
172
173 if (hd->sent_reply)
174 return;
175
176 if (hd->replybuf == NULL)
177 {
178 hd->replybuf = salloc<char> (msglen + 1);
179 p = hd->replybuf;
180 }
181 else
182 {
183 size_t replybuf_len = strlen (hd->replybuf);
184 hd->replybuf = salloc (replybuf_len + msglen + 2, hd->replybuf);
185 p = hd->replybuf + replybuf_len;
186 *p++ = '\n';
187 }
188
189 q = message;
190
191 /* What this code does is the following:
192 * - set *p to *q
193 * - increment q (go to the next character in q
194 * - if *p is below 0x1f (the space character ' '), then
195 * do not increment p
196 * if *p is above or the same as the space character, then
197 * do increment p
198 *
199 * This incrementing/not incrementing effectively skips any character below the
200 * space character, as by not incrementing p, *p gets overwritten in the next cycle.
201 */
202 while (msglen--)
203 {
204 *p = *q++;
205 p += !!(*p & ~0x1f);
206 }
207
208 *p = '\0';
209 }
210
211 static void
212 xmlrpc_command_success_string (sourceinfo_t *si, char const *result, char const *message)
213 {
214 connection_t *cptr;
215 httpddata *hd;
216 char buf[XMLRPC_BUFSIZE];
217
218 cptr = si->connection;
219 hd = static_cast<httpddata *> (cptr->userdata);
220 if (hd->sent_reply)
221 return;
222 xmlrpc_string (buf, result);
223 xmlrpc_send (1, buf);
224 hd->sent_reply = true;
225 }
226
227 /* These taken from the old modules/xmlrpc/account.c */
228 /*
229 * ermyth.login
230 *
231 * XML Inputs:
232 * account name, password, source ip (optional)
233 *
234 * XML Outputs:
235 * fault 1 - insufficient parameters
236 * fault 3 - account is not registered
237 * fault 5 - invalid username and password
238 * fault 6 - account is frozen
239 * default - success (authcookie)
240 *
241 * Side Effects:
242 * an authcookie ticket is created for the myuser_t.
243 * the user's lastlogin is updated
244 */
245 static int
246 xmlrpcmethod_login (void *conn, int parc, char *parv[])
247 {
248 myuser_t *mu;
249 authcookie_t *ac;
250 char buf[BUFSIZE];
251 char const *sourceip;
252 connection_t *cptr = static_cast<connection_t *> (conn);
253
254 if (parc < 2)
255 {
256 xmlrpc_generic_error (fault::needmoreparams, "Insufficient parameters.");
257 return 0;
258 }
259
260 sourceip = parc >= 3 && *parv[2] != '\0' ? parv[2] : NULL;
261
262 if (!(mu = myuser_t::find (parv[0])))
263 {
264 xmlrpc_generic_error (fault::nosuch_source, "The account is not registered.");
265 return 0;
266 }
267
268 if (mu->find_metadata ("private:freeze:freezer") != NULL)
269 {
270 logcommand_external (nicksvs.me, "xmlrpc", cptr, sourceip, NULL, CMDLOG_LOGIN, "failed LOGIN to %s (frozen)", mu->name);
271 xmlrpc_generic_error (fault::noprivs, "The account has been frozen.");
272 return 0;
273 }
274
275 if (!mu->verify_password (parv[1]))
276 {
277 logcommand_external (nicksvs.me, "xmlrpc", cptr, sourceip, NULL, CMDLOG_LOGIN, "failed LOGIN to %s (bad password)", mu->name);
278 xmlrpc_generic_error (fault::authfail, "The password is not valid for this account.");
279 return 0;
280 }
281
282 mu->lastlogin = NOW;
283
284 ac = authcookie_create (mu);
285
286 logcommand_external (nicksvs.me, "xmlrpc", cptr, sourceip, mu, CMDLOG_LOGIN, "LOGIN");
287
288 xmlrpc_string (buf, ac->ticket);
289 xmlrpc_send (1, buf);
290
291 return 0;
292 }
293
294 /*
295 * ermyth.logout
296 *
297 * XML inputs:
298 * authcookie, and account name.
299 *
300 * XML outputs:
301 * fault 1 - insufficient parameters
302 * fault 3 - unknown user
303 * fault 5 - validation failed
304 * default - success message
305 *
306 * Side Effects:
307 * an authcookie ticket is destroyed.
308 */
309 static int
310 xmlrpcmethod_logout (void *conn, int parc, char *parv[])
311 {
312 authcookie_t *ac;
313 myuser_t *mu;
314 char buf[XMLRPC_BUFSIZE];
315 connection_t *cptr = static_cast<connection_t *> (conn);
316
317 if (parc < 2)
318 {
319 xmlrpc_generic_error (fault::needmoreparams, "Insufficient parameters.");
320 return 0;
321 }
322
323 if ((mu = myuser_t::find (parv[1])) == NULL)
324 {
325 xmlrpc_generic_error (fault::nosuch_source, "Unknown user.");
326 return 0;
327 }
328
329 if (authcookie_validate (parv[0], mu) == false)
330 {
331 xmlrpc_generic_error (fault::authfail, "Invalid authcookie for this account.");
332 return 0;
333 }
334
335 logcommand_external (nicksvs.me, "xmlrpc", cptr, NULL, mu, CMDLOG_LOGIN, "LOGOUT");
336
337 ac = authcookie_find (parv[0], mu);
338 authcookie_destroy (ac);
339
340 xmlrpc_string (buf, "You are now logged out.");
341 xmlrpc_send (1, buf);
342
343 return 0;
344 }
345
346 /*
347 * ermyth.command
348 *
349 * XML inputs:
350 * authcookie, account name, source ip, service name, command name,
351 * parameters.
352 *
353 * XML outputs:
354 * depends on command
355 *
356 * Side Effects:
357 * command is executed
358 */
359 static int
360 xmlrpcmethod_command (void *conn, int parc, char *parv[])
361 {
362 myuser_t *mu;
363 service_t *svs;
364 command_t const *cmd;
365 sourceinfo_t si;
366 int newparc;
367 char *newparv[20];
368 connection_t *cptr = static_cast<connection_t *> (conn);
369 httpddata *hd = static_cast<httpddata *> (cptr->userdata);
370 char buf[XMLRPC_BUFSIZE];
371 int i;
372
373 if (parc < 5)
374 {
375 xmlrpc_generic_error (fault::needmoreparams, "Insufficient parameters.");
376 return 0;
377 }
378
379 for (i = 0; i < parc; i++)
380 {
381 if (strchr (parv[i], '\r') || strchr (parv[i], '\n'))
382 {
383 xmlrpc_generic_error (fault::badparams, "Invalid parameters.");
384 return 0;
385 }
386 }
387
388 if (*parv[1] != '\0' && strlen (parv[0]) > 1)
389 {
390 if ((mu = myuser_t::find (parv[1])) == NULL)
391 {
392 xmlrpc_generic_error (fault::nosuch_source, "Unknown user.");
393 return 0;
394 }
395
396 if (authcookie_validate (parv[0], mu) == false)
397 {
398 xmlrpc_generic_error (fault::authfail, "Invalid authcookie for this account.");
399 return 0;
400 }
401 }
402 else
403 mu = NULL;
404
405 svs = find_service (parv[3]);
406 if (svs == NULL || svs->cmdtree == NULL)
407 {
408 slog (LG_DEBUG, "xmlrpcmethod_command(): invalid service %s", parv[3]);
409 xmlrpc_generic_error (fault::nosuch_source, "Invalid service name.");
410 return 0;
411 }
412 cmd = svs->cmdtree->find (parv[4]);
413 if (cmd == NULL)
414 {
415 xmlrpc_generic_error (fault::nosuch_source, "Invalid command name.");
416 return 0;
417 }
418
419 memset (newparv, '\0', sizeof newparv);
420 newparc = parc - 5;
421 if (newparc > 20)
422 newparc = 20;
423 if (newparc > 0)
424 memcpy (newparv, parv + 5, newparc * sizeof (parv[0]));
425 memset (&si, '\0', sizeof si);
426 si.smu = mu;
427 si.service = svs;
428 si.sourcedesc = parv[2][0] != '\0' ? parv[2] : NULL;
429 si.connection = cptr;
430 si.v = &xmlrpc_vtable;
431 cmd->exec (svs, &si, newparc, newparv);
432 if (!hd->sent_reply)
433 {
434 if (hd->replybuf != NULL)
435 {
436 xmlrpc_string (buf, hd->replybuf);
437 xmlrpc_send (1, buf);
438 }
439 else
440 xmlrpc_generic_error (fault::unimplemented, "Command did not return a result.");
441 }
442
443 return 0;
444 }
445
446 bool
447 _modinit (module *m)
448 {
449 ConfTable::callback.ready.attach (on_config_ready);
450
451 add_top_conf ("XMLRPC", conf_xmlrpc);
452 add_conf_item ("PATH", conf_xmlrpc_table, conf_xmlrpc_path);
453
454 xmlrpc_set_buffer (dump_buffer);
455 xmlrpc_register_method (PACKAGE_NAME ".login", xmlrpcmethod_login);
456 xmlrpc_register_method (PACKAGE_NAME ".logout", xmlrpcmethod_logout);
457 xmlrpc_register_method (PACKAGE_NAME ".command", xmlrpcmethod_command);
458
459 return true;
460 }
461
462 void
463 _moddeinit (void)
464 {
465 xmlrpc_unregister_method (PACKAGE_NAME ".login");
466 xmlrpc_unregister_method (PACKAGE_NAME ".logout");
467 xmlrpc_unregister_method (PACKAGE_NAME ".command");
468
469 if (std::find_if (pathhandlers.begin (), pathhandlers.end (), pathhandler_eq (handle_xmlrpc.path))
470 == pathhandlers.end ())
471 {
472 slog (LG_INFO, "xmlrpc/main.c: handler was not registered.");
473 return;
474 }
475
476 pathhandlers.erase (std::find_if (pathhandlers.begin (), pathhandlers.end (), pathhandler_eq (handle_xmlrpc.path)));
477
478 del_conf_item ("PATH", conf_xmlrpc_table);
479 del_top_conf ("XMLRPC");
480 if (handle_xmlrpc.path != NULL)
481 sfree (handle_xmlrpc.path);
482 handle_xmlrpc.path = NULL;
483
484 ConfTable::callback.ready.detach (on_config_ready);
485 }