ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/rpc/xmlrpc.C
Revision: 1.7
Committed: Sun Sep 16 18:54:44 2007 UTC (19 years, 1 month ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.6: +8 -4 lines
Log Message:
#defines to enum

File Contents

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