ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/rpc/xmlrpc.C
Revision: 1.2
Committed: Sat Jul 21 01:29:10 2007 UTC (19 years, 2 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.1: +3 -3 lines
Log Message:
- moved to new documentation system
- fixed small build error

File Contents

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