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

File Contents

# Content
1 /**
2 * account.h: Data structures for account information.
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 * XML RPC Library
10 *
11 * Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team
12 * Copyright © 2005 Trystan Scott Lee
13 * Contact trystan@nomadirc.net
14 *
15 * Please read doc/poddoc/license.pod and README for further details.
16 *
17 * Based on the original code from Denora
18 */
19
20 #include <util/string.h>
21
22 #include "xml/rpc.h"
23
24 int xmlrpc_error_code;
25
26 xmlrpc_cmdhash *xmlrpc_cmds[MAX_CMD_HASH];
27
28 xmlrpc_set xmlrpc;
29
30 static char *xmlrpc_get_token (char const *str, char const dilim, int token_number);
31 static char *xmlrpc_get_token_remainder (char const *str, char const dilim, int token_number);
32 static char *xmlrpc_substr (char const *src, int start, int end);
33 static char *xmlrpc_parse (char *buffer);
34 static char *xmlrpc_method (char *buffer);
35 static char *xmlrpc_normalize_buffer (char *buf);
36 #ifndef HAVE_STRCASESTR
37 static char *strcasestr (char *s1, char *s2);
38 #endif
39 static int xmlrpc_split_buf (char *buffer, char ***argv);
40
41 xmlrpc_cmd *create_xml_command (char const *name, XMLRPCMethodFunc func);
42 xmlrpc_cmd *find_xmlrpc_command (xmlrpc_cmdhash *hook_event_table[], char const *name);
43 int add_xml_command (xmlrpc_cmdhash *hook_event_table[], xmlrpc_cmd *xml);
44 int destroy_xmlrpc_command (xmlrpc_cmd *xml);
45
46 /*************************************************************************/
47
48 void
49 xmlrpc_process (char *buffer, void *userdata)
50 {
51 int retVal = 0;
52 xmlrpc_cmd *current = NULL;
53 xmlrpc_cmd *xml;
54 char *tmp;
55 int ac = 0;
56 char **av;
57 char *name = NULL;
58
59 xmlrpc_error_code = 0;
60
61 if (!buffer)
62 {
63 xmlrpc_error_code = -1;
64 return;
65 }
66
67 tmp = xmlrpc_parse (buffer);
68 if (tmp)
69 {
70 name = xmlrpc_method (tmp);
71 if (name)
72 {
73 xml = find_xmlrpc_command (xmlrpc_cmds, name);
74 if (xml)
75 {
76 ac = xmlrpc_split_buf (tmp, &av);
77 if (xml->func)
78 {
79 retVal = xml->func (userdata, ac, av);
80 if (retVal == XMLRPC_CONT)
81 {
82 current = xml->next;
83 while (current && current->func && retVal == XMLRPC_CONT)
84 {
85 retVal = current->func (userdata, ac, av);
86 current = current->next;
87 }
88 }
89 else
90 { /* we assume that XMLRPC_STOP means the handler has given no output */
91 xmlrpc_error_code = -7;
92 xmlrpc_generic_error (xmlrpc_error_code, "XMLRPC error: First eligible function returned XMLRPC_STOP");
93 }
94 }
95 else
96 {
97 xmlrpc_error_code = -6;
98 xmlrpc_generic_error (xmlrpc_error_code, "XMLRPC error: Method has no registered function");
99 }
100 }
101 else
102 {
103 xmlrpc_error_code = -4;
104 xmlrpc_generic_error (xmlrpc_error_code, "XMLRPC error: Unknown routine called");
105 }
106 }
107 else
108 {
109 xmlrpc_error_code = -3;
110 xmlrpc_generic_error (xmlrpc_error_code, "XMLRPC error: Missing methodRequest or methodName.");
111 }
112 }
113 else
114 {
115 xmlrpc_error_code = -2;
116 xmlrpc_generic_error (xmlrpc_error_code, "XMLRPC error: Invalid document end at line 1");
117 }
118
119 if (ac)
120 sfree (av);
121
122 if (tmp)
123 sfree (tmp);
124
125 if (name)
126 sfree (name);
127 }
128
129 /*************************************************************************/
130
131 void
132 xmlrpc_set_buffer (char *(*func) (char *buffer, int len))
133 {
134 xmlrpc.setbuffer = func;
135 }
136
137 /*************************************************************************/
138
139 int
140 xmlrpc_register_method (char const *name, XMLRPCMethodFunc func)
141 {
142 xmlrpc_cmd *xml;
143 xml = create_xml_command (name, func);
144 return add_xml_command (xmlrpc_cmds, xml);
145 }
146
147 /*************************************************************************/
148
149 xmlrpc_cmd *
150 create_xml_command (char const *name, XMLRPCMethodFunc func)
151 {
152 xmlrpc_cmd *xml = new xmlrpc_cmd;
153 xml->name = sstrdup (name);
154 xml->func = func;
155 xml->next = NULL;
156 return xml;
157 }
158
159 /*************************************************************************/
160
161 xmlrpc_cmd *
162 find_xmlrpc_command (xmlrpc_cmdhash *hook_event_table[], char const *name)
163 {
164 int idx;
165 xmlrpc_cmdhash *current = NULL;
166 if (!hook_event_table || !name)
167 return NULL;
168 idx = gen_cmd_hash (name);
169
170 for (current = hook_event_table[idx]; current; current = current->next)
171 if (stricmp (name, current->name) == 0)
172 return current->xml;
173
174 return NULL;
175 }
176
177 /*************************************************************************/
178
179 int
180 add_xml_command (xmlrpc_cmdhash *hook_event_table[], xmlrpc_cmd *xml)
181 {
182 /* We can assume both param's have been checked by this point.. */
183 int idx = 0;
184 xmlrpc_cmdhash *current = NULL;
185 xmlrpc_cmdhash *newHash = NULL;
186 xmlrpc_cmdhash *lastHash = NULL;
187
188 if (!hook_event_table || !xml)
189 return XMLRPC_ERR_PARAMS;
190 idx = gen_cmd_hash (xml->name);
191
192 for (current = hook_event_table[idx]; current; current = current->next)
193 {
194 if (stricmp (xml->name, current->name) == 0)
195 {
196 xml->next = current->xml;
197 current->xml = xml;
198 return XMLRPC_ERR_OK;
199 }
200 lastHash = current;
201 }
202
203 if ((newHash = new xmlrpc_cmdhash) == NULL)
204 return XMLRPC_ERR_OK;
205 newHash->next = NULL;
206 newHash->name = sstrdup (xml->name);
207 newHash->xml = xml;
208
209 if (lastHash == NULL)
210 hook_event_table[idx] = newHash;
211 else
212 lastHash->next = newHash;
213 return XMLRPC_ERR_OK;
214 }
215
216 /*************************************************************************/
217
218 int
219 xmlrpc_unregister_method (char const *method)
220 {
221 xmlrpc_cmd *xml;
222
223 xml = find_xmlrpc_command (xmlrpc_cmds, method);
224 return destroy_xmlrpc_command (xml);
225 }
226
227 /*************************************************************************/
228
229 /**
230 * Destroy a command, freeing its memory.
231 *
232 * \param xml the command to be destroyed
233 *
234 * \return XMLRPC_ERR_OK on success
235 **/
236 int
237 destroy_xmlrpc_command (xmlrpc_cmd *xml)
238 {
239 if (xml->name)
240 sfree (xml->name);
241 xml->func = NULL;
242 xml->next = NULL;
243
244 return XMLRPC_ERR_OK;
245 }
246
247 /*************************************************************************/
248
249 /**
250 * Parse Data down to just the <xml> without any \r\n or \t
251 * @param buffer Incoming data buffer
252 * @return cleaned up buffer
253 */
254 char *
255 xmlrpc_parse (char *buffer)
256 {
257 char *tmp = NULL;
258
259 /*
260 * Okay since the buffer could contain
261 * HTTP header information, lets break
262 * off at the point that the <?xml?> starts
263 */
264 tmp = strcasestr (buffer, const_cast<char *> ("<?xml"));
265
266 /* check if its xml doc */
267 if (tmp)
268 /* get all the odd characters out of the data */
269 return xmlrpc_normalize_buffer (tmp);
270
271 return NULL;
272 }
273
274 /*************************************************************************/
275
276 static int
277 xmlrpc_split_buf (char *buffer, char ***argv)
278 {
279 int ac = 0;
280 int argvsize = 8;
281 char *data, *str;
282 char *str2 = NULL;
283 char *nexttag = NULL;
284 char *temp = NULL;
285 char *final;
286 int tagtype = 0;
287
288 *argv = salloc<char *> (argvsize);
289 while ((data = strstr (buffer, "<value>")))
290 {
291 if (data)
292 {
293 temp = xmlrpc_get_token (data, '<', 2);
294 nexttag = xmlrpc_get_token (temp, '>', 0);
295 /* strings */
296 if (!stricmp ("string", nexttag))
297 tagtype = 1;
298 else
299 tagtype = 0;
300 str = xmlrpc_get_token (data, '>', 2);
301 str2 = xmlrpc_get_token_remainder (data, '>', 2);
302 if (str)
303 {
304 final = xmlrpc_get_token (str, '<', 0);
305 if (final)
306 {
307 if (tagtype == 1)
308 (*argv)[ac++] = xmlrpc_decode_string (final);
309 else
310 (*argv)[ac++] = final;
311 }
312 sfree (str);
313 } /* str */
314 } /* data */
315 buffer = str2;
316 } /* while */
317
318 sfree (str2);
319
320 return ac;
321 }
322
323 /*************************************************************************/
324
325 char *
326 xmlrpc_method (char *buffer)
327 {
328 char *data;
329 char *tmp, *tmp2;
330
331 data = strcasestr (buffer, const_cast<char *> ("<methodname>"));
332 if (data)
333 {
334 tmp = xmlrpc_get_token (data, '>', 1);
335 tmp2 = xmlrpc_get_token (tmp, '<', 0);
336 sfree (tmp);
337 return tmp2;
338 }
339
340 return NULL;
341 }
342
343 /*************************************************************************/
344
345 void
346 xmlrpc_generic_error (int code, char const *string)
347 {
348 char buf[XMLRPC_BUFSIZE];
349 int len;
350
351 snprintf (buf, XMLRPC_BUFSIZE, "<?xml version=\"1.0\"?>\r\n"
352 "<methodResponse>\r\n"
353 " <fault>\r\n"
354 " <value>\r\n"
355 " <struct>\r\n"
356 " <member>\r\n"
357 " <name>faultCode</name>\r\n"
358 " <value><int>%d</int></value>\r\n" // code
359 " </member>\r\n"
360 " <member>\r\n"
361 " <name>faultString</name>\r\n"
362 " <value><string>%s</string></value>\r\n" // string
363 " </member>\r\n"
364 " </struct>\r\n"
365 " </value>\r\n"
366 " </fault>\r\n"
367 "</methodResponse>",
368 code, string);
369 len = strlen (buf);
370
371 xmlrpc.setbuffer (buf, len);
372 }
373
374 /*************************************************************************/
375
376 void
377 xmlrpc_send (int argc, ...)
378 {
379 va_list va;
380 char *a;
381 int idx = 0;
382 char *s = NULL;
383 int len;
384 char buf[XMLRPC_BUFSIZE];
385 char buf2[XMLRPC_BUFSIZE];
386
387 *buf = '\0';
388 *buf2 = '\0';
389
390 va_start (va, argc);
391 for (idx = 0; idx < argc; idx++)
392 {
393 a = va_arg (va, char *);
394 if (expect_false (!s))
395 {
396 snprintf (buf, XMLRPC_BUFSIZE, " <param>\r\n"
397 " <value>\r\n"
398 " %s\r\n" // a
399 " </value>\r\n"
400 " </param>",
401 a);
402 s = sstrdup (buf);
403 }
404 else
405 {
406 snprintf (buf, XMLRPC_BUFSIZE, "%s\r\n" // s
407 " <param>\r\n"
408 " <value>\r\n"
409 " %s\r\n" // a
410 " </value>\r\n"
411 " </param>",
412 s, a);
413 sfree (s);
414 s = sstrdup (buf);
415 }
416 }
417 va_end (va);
418
419 if (xmlrpc.encode)
420 snprintf (buf, XMLRPC_BUFSIZE, "<?xml version=\"1.0\" encoding=\"%s\"?>\r\n" // xmlrpc.encode
421 "<methodResponse>\r\n"
422 " <params>\r\n"
423 " %s\r\n" // s
424 " </params>\r\n"
425 "</methodResponse>",
426 xmlrpc.encode, s);
427 else
428 snprintf (buf, XMLRPC_BUFSIZE, "<?xml version=\"1.0\"?>\r\n"
429 "<methodResponse>\r\n"
430 " <params>\r\n"
431 " %s\r\n" // s
432 " </params>\r\n"
433 "</methodResponse>",
434 s);
435 len = strlen (buf);
436
437 xmlrpc.setbuffer (buf, len);
438
439 if (xmlrpc.encode)
440 {
441 sfree (xmlrpc.encode);
442 xmlrpc.encode = NULL;
443 }
444 sfree (s);
445 }
446
447 /*************************************************************************/
448
449 char *
450 xmlrpc_string (char *buf, char const *value)
451 {
452 char encoded[XMLRPC_BUFSIZE];
453 *buf = '\0';
454
455 xmlrpc_char_encode (encoded, value);
456 snprintf (buf, XMLRPC_BUFSIZE, "<string>%s</string>", encoded);
457 return buf;
458 }
459
460 /*************************************************************************/
461
462 /**
463 * Get the token
464 * @param str String to search in
465 * @param dilim Character to search for
466 * @param token_number the token number
467 * @return token
468 */
469 static char *
470 xmlrpc_get_token (char const *str, char const dilim, int token_number)
471 {
472 int len, idx, counter = 0, start_pos = 0;
473 char *substring = NULL;
474 if (!str)
475 return NULL;
476
477 len = strlen (str);
478
479 for (idx = 0; idx <= len; idx++)
480 if ((str[idx] == dilim) || (idx == len))
481 {
482 if (counter == token_number)
483 {
484 substring = xmlrpc_substr (str, start_pos, idx);
485 counter++;
486 }
487 else
488 {
489 start_pos = idx + 1;
490 counter++;
491 }
492 }
493
494 return substring;
495 }
496
497 /*************************************************************************/
498
499 /**
500 * Get the Remaining tokens
501 * @param str String to search in
502 * @param dilim Character to search for
503 * @param token_number the token number
504 * @return token
505 */
506 static char *
507 xmlrpc_get_token_remainder (char const *str, char const dilim, int token_number)
508 {
509 int len, idx, counter = 0, start_pos = 0;
510 char *substring = NULL;
511 if (!str)
512 return NULL;
513
514 len = strlen (str);
515
516 for (idx = 0; idx <= len; idx++)
517 if ((str[idx] == dilim) || (idx == len))
518 {
519 if (counter == token_number)
520 {
521 substring = xmlrpc_substr (str, start_pos, len);
522 counter++;
523 }
524 else
525 {
526 start_pos = idx + 1;
527 counter++;
528 }
529 }
530
531 return substring;
532 }
533
534 /*************************************************************************/
535
536 /**
537 * Get the string between point A and point B
538 * @param str String to search in
539 * @param start Point A
540 * @param end Point B
541 * @return the string in between
542 */
543 static char *
544 xmlrpc_substr (char const *src, int start, int end)
545 {
546 char *substring = NULL;
547 int len, idx;
548
549 if (!src)
550 return NULL;
551
552 len = strlen (src);
553
554 if (((start >= 0) && (end <= len)) && (end > start))
555 {
556 substring = salloc<char> ((end - start) + 1);
557 for (idx = 0; idx <= end - start; idx++)
558 substring[idx] = src[start + idx];
559 substring[end - start] = '\0';
560 }
561
562 return substring;
563 }
564
565 /*************************************************************************/
566
567 char *
568 xmlrpc_normalize_buffer (char *buf)
569 {
570 char *newbuf;
571 int i, len, j = 0;
572
573 len = strlen (buf);
574 newbuf = salloc<char> (len + 1);
575
576 for (i = 0; i < len; i++)
577 {
578 switch (buf[i])
579 {
580 /* ctrl char */
581 case 1:
582 break;
583 /* Bold ctrl char */
584 case 2:
585 break;
586 /* Color ctrl char */
587 case 3:
588 /* If the next character is a digit, its also removed */
589 if (isdigit (buf[i + 1]))
590 {
591 i++;
592
593 /* not the best way to remove colors
594 * which are two digit but no worse then
595 * how the Unreal does with +S - TSL
596 */
597 if (isdigit (buf[i + 1]))
598 i++;
599
600 /* Check for background color code
601 * and remove it as well
602 */
603 if (buf[i + 1] == ',')
604 {
605 i++;
606
607 if (isdigit (buf[i + 1]))
608 i++;
609 /* not the best way to remove colors
610 * which are two digit but no worse then
611 * how the Unreal does with +S - TSL
612 */
613 if (isdigit (buf[i + 1]))
614 i++;
615 }
616 }
617
618 break;
619 /* tabs char */
620 case 9:
621 break;
622 /* line feed char */
623 case 10:
624 break;
625 /* carrage returns char */
626 case 13:
627 break;
628 /* Reverse ctrl char */
629 case 22:
630 break;
631 /* Underline ctrl char */
632 case 31:
633 break;
634 /* A valid char gets copied into the new buffer */
635 default:
636 newbuf[j++] = buf[i];
637 }
638 }
639
640 /* Terminate the string */
641 newbuf[j] = 0;
642
643 return (newbuf);
644 }
645
646 /*************************************************************************/
647
648 #ifndef HAVE_STRCASESTR
649 char *
650 strcasestr (char *s1, char *s2)
651 {
652 register char *s = s1, *d = s2;
653
654 while (*s1)
655 if (tolower (*s1) == tolower (*d))
656 {
657 s1++;
658 d++;
659 if (*d == 0)
660 return s;
661 }
662 else
663 {
664 s = ++s1;
665 d = s2;
666 }
667
668 return NULL;
669 }
670 #endif
671
672 /*************************************************************************/
673
674 void
675 xmlrpc_char_encode (char *outbuffer, char const *s1)
676 {
677 unsigned char c;
678 dynstr s (XMLRPC_BUFSIZE);
679
680 *outbuffer = '\0';
681
682 if ((!(s1) || (*(s1) == '\0')))
683 return;
684
685 while ((c = *s1++))
686 {
687 if (c > 0x7f)
688 {
689 char buf[15];
690 snprintf (buf, 15, "&#%c;", c);
691 s.add (buf);
692 }
693 else if (c == '&')
694 s.add ("&amp;");
695 else if (c == '<')
696 s.add ("&lt;");
697 else if (c == '>')
698 s.add ("&gt;");
699 else if (c == '"')
700 s.add ("&quot;");
701 else
702 s.add (c);
703 }
704
705 memcpy (outbuffer, s.c_str (), XMLRPC_BUFSIZE);
706 }
707
708 char *
709 xmlrpc_decode_string (char *buf)
710 {
711 int count;
712 int i;
713 char *token, *temp;
714 char **tokens = salloc<char *> (128);
715 char *temptoken;
716 char buf2[12];
717 char buf3[12];
718
719 replace (buf, XMLRPC_BUFSIZE, "&gt;", ">");
720 replace (buf, XMLRPC_BUFSIZE, "&lt;", "<");
721 replace (buf, XMLRPC_BUFSIZE, "&quot;", "\"");
722 replace (buf, XMLRPC_BUFSIZE, "&amp;", "&");
723
724 temp = sstrdup (buf);
725 count = sjtoken (temp, '&', tokens);
726
727 for (i = 1; i < count; i++)
728 {
729 token = tokens[i];
730 if (token && *token == '#')
731 {
732 temptoken = strtok ((token + 1), ";");
733 snprintf (buf2, 12, "%c", atoi (temptoken));
734 snprintf (buf3, 12, "&%s;", token);
735 replace (buf, XMLRPC_BUFSIZE, buf3, buf2);
736 }
737 }
738
739 sfree (temp);
740
741 return buf;
742 }