/* Routines to export Services databases in XML format. * * IRC Services is copyright (c) 1996-2007 Andrew Church. * E-mail: * Parts written by Andrew Kempe and others. * This program is free but copyrighted software; see the file COPYING for * details. */ #include "services.h" #include "modules.h" #include "conffile.h" #include "language.h" #include "modules/nickserv/nickserv.h" #include "modules/chanserv/chanserv.h" #include "modules/memoserv/memoserv.h" #include "modules/operserv/operserv.h" #include "modules/operserv/maskdata.h" #include "modules/operserv/news.h" #include "modules/statserv/statserv.h" #include "xml.h" /*************************************************************************/ #ifndef CONVERT_DB static Module *module; #endif /* Write a field (either string, password, long, or unsigned long) in XML. * `indent' is the indent string to prefix the output with. */ #define XML_PUT_STRING(indent,structure,field) do { \ if ((structure).field) \ writefunc(data, "%s<" #field ">%s\n", indent, \ xml_quotestr((structure).field)); \ } while (0) #define XML_PUT_PASS(indent,structure,field) \ writefunc(data, "%s<" #field ">%s\n", indent, \ xml_quotebuf((structure).field,PASSMAX)); #define XML_PUT_LONG(indent,structure,field) \ writefunc(data, "%s<" #field ">%ld\n", indent, \ (long)(structure).field) #define XML_PUT_ULONG(indent,structure,field) \ writefunc(data, "%s<" #field ">%lu\n", indent, \ (unsigned long)(structure).field) /* Write an array of strings (with corresponding count) in XML. */ #define XML_PUT_STRARR(indent,structure,field) do { \ int i; \ writefunc(data, "%s<" #field " count='%lu'>\n", indent, \ (unsigned long)(structure).field##_count); \ ARRAY_FOREACH (i, (structure).field) { \ writefunc(data, "%s\t%s\n", \ indent, xml_quotestr((structure).field[i])); \ } \ writefunc(data, "%s\n", indent); \ } while (0) /*************************************************************************/ /*************************** Internal routines ***************************/ /*************************************************************************/ /* Quote any special characters in the given buffer and return the result * in a static buffer. Trailing null characters are removed. */ static char *xml_quotebuf(const char *buf_, int size) { const unsigned char *buf = (unsigned char *)buf_; static char retbuf[BUFSIZE*6+1]; uint32 i; char *d; d = retbuf; while (size > 0 && !buf[size-1]) size--; for (i = 0; i < size; i++, buf++) { if (d - retbuf >= sizeof(retbuf)-6) { #ifdef CONVERT_DB fprintf(stderr, "warning: xml_quotebuf(%p,%d) result too long," " truncated", buf, size); #else module_log("warning: xml_quotebuf(%p,%d) result too long," " truncated", buf, size); #endif break; } if (*buf < 32 || *buf > 126) { sprintf(d, "&#%u;", *buf); if (*buf < 10) d += 4; else if (*buf < 100) d += 5; else d += 6; } else switch (*buf) { case '<': memcpy(d, "<", 4); d += 4; break; case '>': memcpy(d, ">", 4); d += 4; break; case '&': memcpy(d, "&", 5); d += 5; break; default: *d++ = *buf; break; } } *d = 0; return retbuf; } /* Do the same thing to a \0-terminated string. */ #define xml_quotestr(s) xml_quotebuf(s, strlen(s)) /*************************************************************************/ /* Write a SuspendInfo structure to the output file. */ static void write_suspendinfo(xml_writefunc_t writefunc, void *data, const SuspendInfo *si) { writefunc(data, "\t\t\n"); XML_PUT_STRING("\t\t\t", *si, who); XML_PUT_STRING("\t\t\t", *si, reason); XML_PUT_LONG ("\t\t\t", *si, suspended); XML_PUT_LONG ("\t\t\t", *si, expires); writefunc(data, "\t\t\n"); } /*************************************************************************/ /* Write a MemoInfo structure to the output file. */ static void write_memoinfo(xml_writefunc_t writefunc, void *data, const MemoInfo *mi) { int i; writefunc(data, "\t\t\n\t\t\t\n", mi->memos_count); ARRAY_FOREACH (i, mi->memos) { writefunc(data, "\t\t\t\t\n"); XML_PUT_LONG ("\t\t\t\t\t", mi->memos[i], number); XML_PUT_LONG ("\t\t\t\t\t", mi->memos[i], flags); XML_PUT_LONG ("\t\t\t\t\t", mi->memos[i], time); XML_PUT_STRING("\t\t\t\t\t", mi->memos[i], sender); XML_PUT_STRING("\t\t\t\t\t", mi->memos[i], text); writefunc(data, "\t\t\t\t\n"); } writefunc(data, "\t\t\t\n"); XML_PUT_LONG ("\t\t\t", *mi, memomax); writefunc(data, "\t\t\n"); } /*************************************************************************/ /*************************** Database output *****************************/ /*************************************************************************/ /* Write all (major) Services constants to the file. */ static int export_constants(xml_writefunc_t writefunc, void *data) { writefunc(data, "\t\n"); #define XML_PUT_CONST(c) \ writefunc(data, "\t\t<" #c ">%ld\n", (long)c) XML_PUT_CONST(LANG_DEFAULT); XML_PUT_CONST(CHANMAX_UNLIMITED); XML_PUT_CONST(CHANMAX_DEFAULT); XML_PUT_CONST(TIMEZONE_DEFAULT); XML_PUT_CONST(ACCLEV_FOUNDER); XML_PUT_CONST(ACCLEV_INVALID); XML_PUT_CONST(ACCLEV_SOP); XML_PUT_CONST(ACCLEV_AOP); XML_PUT_CONST(ACCLEV_HOP); XML_PUT_CONST(ACCLEV_VOP); XML_PUT_CONST(MEMOMAX_UNLIMITED); XML_PUT_CONST(MEMOMAX_DEFAULT); XML_PUT_CONST(NEWS_LOGON); XML_PUT_CONST(NEWS_OPER); XML_PUT_CONST(MD_AKILL); XML_PUT_CONST(MD_EXCLUSION); XML_PUT_CONST(MD_EXCEPTION); XML_PUT_CONST(MD_SGLINE); XML_PUT_CONST(MD_SQLINE); XML_PUT_CONST(MD_SZLINE); #undef XML_PUT_CONST writefunc(data, "\t\n"); return 1; } /*************************************************************************/ static int export_operserv_data(xml_writefunc_t writefunc, void *data) { int32 maxusercnt; time_t maxusertime; char *supass; if (!get_operserv_data(OSDATA_MAXUSERCNT, &maxusercnt) || !get_operserv_data(OSDATA_MAXUSERTIME, &maxusertime) || !get_operserv_data(OSDATA_SUPASS, &supass)) return 0; writefunc(data, "\t%d\n", maxusercnt); writefunc(data, "\t%ld\n", (long)maxusertime); if (supass) { writefunc(data, "\t%s\n", xml_quotebuf(supass, PASSMAX)); } return 1; } /*************************************************************************/ static int export_nick_db(xml_writefunc_t writefunc, void *data) { NickInfo *ni; NickGroupInfo *ngi; for (ngi = first_nickgroupinfo(); ngi; ngi = next_nickgroupinfo()) { writefunc(data, "\t\n"); XML_PUT_ULONG ("\t\t", *ngi, id); XML_PUT_STRARR("\t\t", *ngi, nicks); XML_PUT_ULONG ("\t\t", *ngi, mainnick); XML_PUT_PASS ("\t\t", *ngi, pass); XML_PUT_STRING("\t\t", *ngi, url); XML_PUT_STRING("\t\t", *ngi, email); XML_PUT_STRING("\t\t", *ngi, info); XML_PUT_LONG ("\t\t", *ngi, authcode); XML_PUT_LONG ("\t\t", *ngi, authset); if (ngi->suspendinfo) write_suspendinfo(writefunc, data, ngi->suspendinfo); XML_PUT_LONG ("\t\t", *ngi, flags); XML_PUT_LONG ("\t\t", *ngi, os_priv); XML_PUT_LONG ("\t\t", *ngi, language); XML_PUT_LONG ("\t\t", *ngi, timezone); XML_PUT_LONG ("\t\t", *ngi, channelmax); XML_PUT_STRARR("\t\t", *ngi, access); XML_PUT_STRARR("\t\t", *ngi, ajoin); write_memoinfo(writefunc, data, &ngi->memos); XML_PUT_STRARR("\t\t", *ngi, ignore); writefunc(data, "\t\n"); } for (ni = first_nickinfo(); ni; ni = next_nickinfo()) { writefunc(data, "\t\n"); XML_PUT_STRING("\t\t", *ni, nick); writefunc(data, "\t\t%d\n", ni->status & ~NS_TEMPORARY); XML_PUT_STRING("\t\t", *ni, last_usermask); XML_PUT_STRING("\t\t", *ni, last_realmask); XML_PUT_STRING("\t\t", *ni, last_realname); XML_PUT_STRING("\t\t", *ni, last_quit); XML_PUT_LONG ("\t\t", *ni, time_registered); XML_PUT_LONG ("\t\t", *ni, last_seen); XML_PUT_ULONG ("\t\t", *ni, nickgroup); writefunc(data, "\t\n"); } return 1; } /*************************************************************************/ static int export_channel_db(xml_writefunc_t writefunc, void *data) { int i; ChannelInfo *ci; for (ci = first_channelinfo(); ci; ci = next_channelinfo()) { writefunc(data, "\t\n"); XML_PUT_STRING("\t\t", *ci, name); XML_PUT_ULONG ("\t\t", *ci, founder); XML_PUT_ULONG ("\t\t", *ci, successor); XML_PUT_PASS ("\t\t", *ci, founderpass); XML_PUT_STRING("\t\t", *ci, desc); XML_PUT_STRING("\t\t", *ci, url); XML_PUT_STRING("\t\t", *ci, email); XML_PUT_LONG ("\t\t", *ci, time_registered); XML_PUT_LONG ("\t\t", *ci, last_used); XML_PUT_STRING("\t\t", *ci, last_topic); XML_PUT_STRING("\t\t", *ci, last_topic_setter); XML_PUT_LONG ("\t\t", *ci, last_topic_time); XML_PUT_LONG ("\t\t", *ci, flags); if (ci->suspendinfo) write_suspendinfo(writefunc, data, ci->suspendinfo); if (ci->levels) { writefunc(data, "\t\t\n"); #define XML_PUT_LEVEL(lev) \ writefunc(data, "\t\t\t<" #lev ">%d\n", ci->levels[lev]) XML_PUT_LEVEL(CA_INVITE); XML_PUT_LEVEL(CA_AKICK); XML_PUT_LEVEL(CA_SET); XML_PUT_LEVEL(CA_UNBAN); XML_PUT_LEVEL(CA_AUTOOP); XML_PUT_LEVEL(CA_AUTODEOP); XML_PUT_LEVEL(CA_AUTOVOICE); XML_PUT_LEVEL(CA_OPDEOP); XML_PUT_LEVEL(CA_ACCESS_LIST); XML_PUT_LEVEL(CA_CLEAR); XML_PUT_LEVEL(CA_NOJOIN); XML_PUT_LEVEL(CA_ACCESS_CHANGE); XML_PUT_LEVEL(CA_MEMO); XML_PUT_LEVEL(CA_VOICE); XML_PUT_LEVEL(CA_AUTOHALFOP); XML_PUT_LEVEL(CA_HALFOP); XML_PUT_LEVEL(CA_AUTOPROTECT); XML_PUT_LEVEL(CA_PROTECT); XML_PUT_LEVEL(CA_AUTOOWNER); #undef XML_PUT_LEVEL writefunc(data, "\t\t\n"); } writefunc(data, "\t\t\n", ci->access_count); ARRAY_FOREACH (i, ci->access) { writefunc(data, "\t\t\t\n"); XML_PUT_ULONG ("\t\t\t\t", ci->access[i], nickgroup); XML_PUT_LONG ("\t\t\t\t", ci->access[i], level); writefunc(data, "\t\t\t\n"); } writefunc(data, "\t\t\n"); writefunc(data, "\t\t\n", ci->akick_count); ARRAY_FOREACH (i, ci->akick) { writefunc(data, "\t\t\t\n"); XML_PUT_STRING("\t\t\t\t", ci->akick[i], mask); XML_PUT_STRING("\t\t\t\t", ci->akick[i], reason); XML_PUT_STRING("\t\t\t\t", ci->akick[i], who); XML_PUT_LONG ("\t\t\t\t", ci->akick[i], set); XML_PUT_LONG ("\t\t\t\t", ci->akick[i], lastused); writefunc(data, "\t\t\t\n"); } writefunc(data, "\t\t\n"); #ifdef CONVERT_DB XML_PUT_STRING("\t\t", *ci, mlock_on); XML_PUT_STRING("\t\t", *ci, mlock_off); #else writefunc(data, "\t\t%s\n", mode_flags_to_string(ci->mlock_on, MODE_CHANNEL)); writefunc(data, "\t\t%s\n", mode_flags_to_string(ci->mlock_off, MODE_CHANNEL)); #endif XML_PUT_LONG ("\t\t", *ci, mlock_limit); XML_PUT_STRING("\t\t", *ci, mlock_key); XML_PUT_STRING("\t\t", *ci, mlock_link); XML_PUT_STRING("\t\t", *ci, mlock_flood); XML_PUT_LONG ("\t\t", *ci, mlock_joindelay); XML_PUT_LONG ("\t\t", *ci, mlock_joinrate1); XML_PUT_LONG ("\t\t", *ci, mlock_joinrate2); XML_PUT_STRING("\t\t", *ci, entry_message); write_memoinfo(writefunc, data, &ci->memos); writefunc(data, "\t\n"); } return 1; } /*************************************************************************/ static int export_news_db(xml_writefunc_t writefunc, void *data) { NewsItem *news; for (news = first_news(); news; news = next_news()) { writefunc(data, "\t\n"); XML_PUT_LONG ("\t\t", *news, type); XML_PUT_LONG ("\t\t", *news, num); XML_PUT_STRING("\t\t", *news, text); XML_PUT_STRING("\t\t", *news, who); XML_PUT_LONG ("\t\t", *news, time); writefunc(data, "\t\n"); } return 1; } /*************************************************************************/ static int export_maskdata(xml_writefunc_t writefunc, void *data) { int i; MaskData *md; for (i = 0; i < 256; i++) { for (md = first_maskdata(i); md; md = next_maskdata(i)) { writefunc(data, "\t\n", i); XML_PUT_LONG ("\t\t", *md, num); XML_PUT_STRING("\t\t", *md, mask); if (md->limit) XML_PUT_LONG("\t\t",*md, limit); XML_PUT_STRING("\t\t", *md, reason); XML_PUT_STRING("\t\t", *md, who); XML_PUT_LONG ("\t\t", *md, time); XML_PUT_LONG ("\t\t", *md, expires); XML_PUT_LONG ("\t\t", *md, lastused); writefunc(data, "\t\n"); } } return 1; } /*************************************************************************/ static int export_statserv_db(xml_writefunc_t writefunc, void *data) { ServerStats *ss; for (ss = first_serverstats(); ss; ss = next_serverstats()) { writefunc(data, "\t\n"); XML_PUT_STRING("\t\t", *ss, name); XML_PUT_LONG ("\t\t", *ss, t_join); XML_PUT_LONG ("\t\t", *ss, t_quit); XML_PUT_STRING("\t\t", *ss, quit_message); writefunc(data, "\t\n"); } return 1; } /*************************************************************************/ /**************************** Global routines ****************************/ /*************************************************************************/ EXPORT_FUNC(xml_export) int xml_export(xml_writefunc_t writefunc, void *data) { writefunc(data, "\n\n"); return export_constants(writefunc, data) && export_operserv_data(writefunc, data) && export_nick_db(writefunc, data) && export_channel_db(writefunc, data) && export_news_db(writefunc, data) && export_maskdata(writefunc, data) && export_statserv_db(writefunc, data) && (writefunc(data, "\n"), 1); } /*************************************************************************/ /*************************************************************************/ #ifndef CONVERT_DB /* Command-line option callback. */ static int do_command_line(const char *option, const char *value) { FILE *f; if (!option || strcmp(option, "export") != 0) return 0; if (!value || !*value || strcmp(value,"-") == 0) { f = stdout; } else { f = fopen(value, "w"); if (!f) { perror(value); return 2; } } if (!xml_export((xml_writefunc_t)fprintf, f)) return 2; return 3; } #endif /* CONVERT_DB */ /*************************************************************************/ /***************************** Module stuff ******************************/ /*************************************************************************/ #ifndef CONVERT_DB /*************************************************************************/ const int32 module_version = MODULE_VERSION_CODE; ConfigDirective module_config[] = { { NULL } }; /*************************************************************************/ int init_module(Module *module_) { module = module_; if (!add_callback(NULL, "command line", do_command_line)) { module_log("Unable to add callback"); exit_module(0); return 0; } return 1; } /*************************************************************************/ int exit_module(int shutdown_unused) { #ifdef CLEAN_COMPILE shutdown_unused = shutdown_unused; #endif return 1; } /*************************************************************************/ #endif /* CONVERT_DB */