/** * account.h: Data structures for account information. * * Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team * Rights to this code are as documented in COPYING. * * * Portions of this file were derived from sources bearing the following license: * XML RPC Library * * Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team * Copyright © 2005 Trystan Scott Lee * Contact trystan@nomadirc.net * * Please read doc/poddoc/license.pod and README for further details. * * Based on the original code from Denora */ #include #include "xml/rpc.h" int xmlrpc_error_code; xmlrpc_cmdhash *xmlrpc_cmds[MAX_CMD_HASH]; xmlrpc_set xmlrpc; static char *xmlrpc_get_token (char const *str, char const dilim, int token_number); static char *xmlrpc_get_token_remainder (char const *str, char const dilim, int token_number); static char *xmlrpc_substr (char const *src, int start, int end); static char *xmlrpc_parse (char *buffer); static char *xmlrpc_method (char *buffer); static char *xmlrpc_normalize_buffer (char *buf); #ifndef HAVE_STRCASESTR static char *strcasestr (char *s1, char *s2); #endif static int xmlrpc_split_buf (char *buffer, char ***argv); xmlrpc_cmd *create_xml_command (char const *name, XMLRPCMethodFunc func); xmlrpc_cmd *find_xmlrpc_command (xmlrpc_cmdhash *hook_event_table[], char const *name); int add_xml_command (xmlrpc_cmdhash *hook_event_table[], xmlrpc_cmd *xml); int destroy_xmlrpc_command (xmlrpc_cmd *xml); /*************************************************************************/ void xmlrpc_process (char *buffer, void *userdata) { int retVal = 0; xmlrpc_cmd *current = NULL; xmlrpc_cmd *xml; char *tmp; int ac = 0; char **av; char *name = NULL; xmlrpc_error_code = 0; if (!buffer) { xmlrpc_error_code = -1; return; } tmp = xmlrpc_parse (buffer); if (tmp) { name = xmlrpc_method (tmp); if (name) { xml = find_xmlrpc_command (xmlrpc_cmds, name); if (xml) { ac = xmlrpc_split_buf (tmp, &av); if (xml->func) { retVal = xml->func (userdata, ac, av); if (retVal == XMLRPC_CONT) { current = xml->next; while (current && current->func && retVal == XMLRPC_CONT) { retVal = current->func (userdata, ac, av); current = current->next; } } else { /* we assume that XMLRPC_STOP means the handler has given no output */ xmlrpc_error_code = -7; xmlrpc_generic_error (xmlrpc_error_code, "XMLRPC error: First eligible function returned XMLRPC_STOP"); } } else { xmlrpc_error_code = -6; xmlrpc_generic_error (xmlrpc_error_code, "XMLRPC error: Method has no registered function"); } } else { xmlrpc_error_code = -4; xmlrpc_generic_error (xmlrpc_error_code, "XMLRPC error: Unknown routine called"); } } else { xmlrpc_error_code = -3; xmlrpc_generic_error (xmlrpc_error_code, "XMLRPC error: Missing methodRequest or methodName."); } } else { xmlrpc_error_code = -2; xmlrpc_generic_error (xmlrpc_error_code, "XMLRPC error: Invalid document end at line 1"); } if (ac) sfree (av); if (tmp) sfree (tmp); if (name) sfree (name); } /*************************************************************************/ void xmlrpc_set_buffer (char *(*func) (char *buffer, int len)) { xmlrpc.setbuffer = func; } /*************************************************************************/ int xmlrpc_register_method (char const *name, XMLRPCMethodFunc func) { xmlrpc_cmd *xml; xml = create_xml_command (name, func); return add_xml_command (xmlrpc_cmds, xml); } /*************************************************************************/ xmlrpc_cmd * create_xml_command (char const *name, XMLRPCMethodFunc func) { xmlrpc_cmd *xml = new xmlrpc_cmd; xml->name = sstrdup (name); xml->func = func; xml->next = NULL; return xml; } /*************************************************************************/ xmlrpc_cmd * find_xmlrpc_command (xmlrpc_cmdhash *hook_event_table[], char const *name) { int idx; xmlrpc_cmdhash *current = NULL; if (!hook_event_table || !name) return NULL; idx = gen_cmd_hash (name); for (current = hook_event_table[idx]; current; current = current->next) if (stricmp (name, current->name) == 0) return current->xml; return NULL; } /*************************************************************************/ int add_xml_command (xmlrpc_cmdhash *hook_event_table[], xmlrpc_cmd *xml) { /* We can assume both param's have been checked by this point.. */ int idx = 0; xmlrpc_cmdhash *current = NULL; xmlrpc_cmdhash *newHash = NULL; xmlrpc_cmdhash *lastHash = NULL; if (!hook_event_table || !xml) return XMLRPC_ERR_PARAMS; idx = gen_cmd_hash (xml->name); for (current = hook_event_table[idx]; current; current = current->next) { if (stricmp (xml->name, current->name) == 0) { xml->next = current->xml; current->xml = xml; return XMLRPC_ERR_OK; } lastHash = current; } if ((newHash = new xmlrpc_cmdhash) == NULL) return XMLRPC_ERR_OK; newHash->next = NULL; newHash->name = sstrdup (xml->name); newHash->xml = xml; if (lastHash == NULL) hook_event_table[idx] = newHash; else lastHash->next = newHash; return XMLRPC_ERR_OK; } /*************************************************************************/ int xmlrpc_unregister_method (char const *method) { xmlrpc_cmd *xml; xml = find_xmlrpc_command (xmlrpc_cmds, method); return destroy_xmlrpc_command (xml); } /*************************************************************************/ /** * Destroy a command, freeing its memory. * * \param xml the command to be destroyed * * \return XMLRPC_ERR_OK on success **/ int destroy_xmlrpc_command (xmlrpc_cmd *xml) { if (xml->name) sfree (xml->name); xml->func = NULL; xml->next = NULL; return XMLRPC_ERR_OK; } /*************************************************************************/ /** * Parse Data down to just the without any \r\n or \t * @param buffer Incoming data buffer * @return cleaned up buffer */ char * xmlrpc_parse (char *buffer) { char *tmp = NULL; /* * Okay since the buffer could contain * HTTP header information, lets break * off at the point that the starts */ tmp = strcasestr (buffer, const_cast (" (argvsize); while ((data = strstr (buffer, ""))) { if (data) { temp = xmlrpc_get_token (data, '<', 2); nexttag = xmlrpc_get_token (temp, '>', 0); /* strings */ if (!stricmp ("string", nexttag)) tagtype = 1; else tagtype = 0; str = xmlrpc_get_token (data, '>', 2); str2 = xmlrpc_get_token_remainder (data, '>', 2); if (str) { final = xmlrpc_get_token (str, '<', 0); if (final) { if (tagtype == 1) (*argv)[ac++] = xmlrpc_decode_string (final); else (*argv)[ac++] = final; } sfree (str); } /* str */ } /* data */ buffer = str2; } /* while */ sfree (str2); return ac; } /*************************************************************************/ char * xmlrpc_method (char *buffer) { char *data; char *tmp, *tmp2; data = strcasestr (buffer, const_cast ("")); if (data) { tmp = xmlrpc_get_token (data, '>', 1); tmp2 = xmlrpc_get_token (tmp, '<', 0); sfree (tmp); return tmp2; } return NULL; } /*************************************************************************/ void xmlrpc_generic_error (int code, char const *string) { char buf[XMLRPC_BUFSIZE]; int len; snprintf (buf, XMLRPC_BUFSIZE, "\r\n" "\r\n" " \r\n" " \r\n" " \r\n" " \r\n" " faultCode\r\n" " %d\r\n" // code " \r\n" " \r\n" " faultString\r\n" " %s\r\n" // string " \r\n" " \r\n" " \r\n" " \r\n" "", code, string); len = strlen (buf); xmlrpc.setbuffer (buf, len); } /*************************************************************************/ void xmlrpc_send (int argc, ...) { va_list va; char *a; int idx = 0; char *s = NULL; int len; char buf[XMLRPC_BUFSIZE]; char buf2[XMLRPC_BUFSIZE]; *buf = '\0'; *buf2 = '\0'; va_start (va, argc); for (idx = 0; idx < argc; idx++) { a = va_arg (va, char *); if (expect_false (!s)) { snprintf (buf, XMLRPC_BUFSIZE, " \r\n" " \r\n" " %s\r\n" // a " \r\n" " ", a); s = sstrdup (buf); } else { snprintf (buf, XMLRPC_BUFSIZE, "%s\r\n" // s " \r\n" " \r\n" " %s\r\n" // a " \r\n" " ", s, a); sfree (s); s = sstrdup (buf); } } va_end (va); if (xmlrpc.encode) snprintf (buf, XMLRPC_BUFSIZE, "\r\n" // xmlrpc.encode "\r\n" " \r\n" " %s\r\n" // s " \r\n" "", xmlrpc.encode, s); else snprintf (buf, XMLRPC_BUFSIZE, "\r\n" "\r\n" " \r\n" " %s\r\n" // s " \r\n" "", s); len = strlen (buf); xmlrpc.setbuffer (buf, len); if (xmlrpc.encode) { sfree (xmlrpc.encode); xmlrpc.encode = NULL; } sfree (s); } /*************************************************************************/ char * xmlrpc_string (char *buf, char const *value) { char encoded[XMLRPC_BUFSIZE]; *buf = '\0'; xmlrpc_char_encode (encoded, value); snprintf (buf, XMLRPC_BUFSIZE, "%s", encoded); return buf; } /*************************************************************************/ /** * Get the token * @param str String to search in * @param dilim Character to search for * @param token_number the token number * @return token */ static char * xmlrpc_get_token (char const *str, char const dilim, int token_number) { int len, idx, counter = 0, start_pos = 0; char *substring = NULL; if (!str) return NULL; len = strlen (str); for (idx = 0; idx <= len; idx++) if ((str[idx] == dilim) || (idx == len)) { if (counter == token_number) { substring = xmlrpc_substr (str, start_pos, idx); counter++; } else { start_pos = idx + 1; counter++; } } return substring; } /*************************************************************************/ /** * Get the Remaining tokens * @param str String to search in * @param dilim Character to search for * @param token_number the token number * @return token */ static char * xmlrpc_get_token_remainder (char const *str, char const dilim, int token_number) { int len, idx, counter = 0, start_pos = 0; char *substring = NULL; if (!str) return NULL; len = strlen (str); for (idx = 0; idx <= len; idx++) if ((str[idx] == dilim) || (idx == len)) { if (counter == token_number) { substring = xmlrpc_substr (str, start_pos, len); counter++; } else { start_pos = idx + 1; counter++; } } return substring; } /*************************************************************************/ /** * Get the string between point A and point B * @param str String to search in * @param start Point A * @param end Point B * @return the string in between */ static char * xmlrpc_substr (char const *src, int start, int end) { char *substring = NULL; int len, idx; if (!src) return NULL; len = strlen (src); if (((start >= 0) && (end <= len)) && (end > start)) { substring = salloc ((end - start) + 1); for (idx = 0; idx <= end - start; idx++) substring[idx] = src[start + idx]; substring[end - start] = '\0'; } return substring; } /*************************************************************************/ char * xmlrpc_normalize_buffer (char *buf) { char *newbuf; int i, len, j = 0; len = strlen (buf); newbuf = salloc (len + 1); for (i = 0; i < len; i++) { switch (buf[i]) { /* ctrl char */ case 1: break; /* Bold ctrl char */ case 2: break; /* Color ctrl char */ case 3: /* If the next character is a digit, its also removed */ if (isdigit (buf[i + 1])) { i++; /* not the best way to remove colors * which are two digit but no worse then * how the Unreal does with +S - TSL */ if (isdigit (buf[i + 1])) i++; /* Check for background color code * and remove it as well */ if (buf[i + 1] == ',') { i++; if (isdigit (buf[i + 1])) i++; /* not the best way to remove colors * which are two digit but no worse then * how the Unreal does with +S - TSL */ if (isdigit (buf[i + 1])) i++; } } break; /* tabs char */ case 9: break; /* line feed char */ case 10: break; /* carrage returns char */ case 13: break; /* Reverse ctrl char */ case 22: break; /* Underline ctrl char */ case 31: break; /* A valid char gets copied into the new buffer */ default: newbuf[j++] = buf[i]; } } /* Terminate the string */ newbuf[j] = 0; return (newbuf); } /*************************************************************************/ #ifndef HAVE_STRCASESTR char * strcasestr (char *s1, char *s2) { register char *s = s1, *d = s2; while (*s1) if (tolower (*s1) == tolower (*d)) { s1++; d++; if (*d == 0) return s; } else { s = ++s1; d = s2; } return NULL; } #endif /*************************************************************************/ void xmlrpc_char_encode (char *outbuffer, char const *s1) { unsigned char c; dynstr s (XMLRPC_BUFSIZE); *outbuffer = '\0'; if ((!(s1) || (*(s1) == '\0'))) return; while ((c = *s1++)) { if (c > 0x7f) { char buf[15]; snprintf (buf, 15, "&#%c;", c); s.add (buf); } else if (c == '&') s.add ("&"); else if (c == '<') s.add ("<"); else if (c == '>') s.add (">"); else if (c == '"') s.add ("""); else s.add (c); } memcpy (outbuffer, s.c_str (), XMLRPC_BUFSIZE); } char * xmlrpc_decode_string (char *buf) { int count; int i; char *token, *temp; char **tokens = salloc (128); char *temptoken; char buf2[12]; char buf3[12]; replace (buf, XMLRPC_BUFSIZE, ">", ">"); replace (buf, XMLRPC_BUFSIZE, "<", "<"); replace (buf, XMLRPC_BUFSIZE, """, "\""); replace (buf, XMLRPC_BUFSIZE, "&", "&"); temp = sstrdup (buf); count = sjtoken (temp, '&', tokens); for (i = 1; i < count; i++) { token = tokens[i]; if (token && *token == '#') { temptoken = strtok ((token + 1), ";"); snprintf (buf2, 12, "%c", atoi (temptoken)); snprintf (buf3, 12, "&%s;", token); replace (buf, XMLRPC_BUFSIZE, buf3, buf2); } } sfree (temp); return buf; }