ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/rpc/xmlrpc.C
Revision: 1.6
Committed: Sun Sep 9 20:05:52 2007 UTC (19 years ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.5: +6 -6 lines
Log Message:
- changed configurations to the c++ stdlib
- more #defines to enum
- removed getopt.h and link.h from the system as they were unused
- reworked logstreams
- added an itoa with old syntax
- made klines objects
- moved some global variables into appropriate classes
- fixed boost.foreach's compiler workaround #if's
- allow other files to add exceptions with ADD_EXCEPTION
- changed mynick_t to c++ object
- moved servers.h out of atheme.h
- corrected PING from inspircd 1.2

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