diff options
Diffstat (limited to 'src/csvlib.c')
-rw-r--r-- | src/csvlib.c | 671 |
1 files changed, 671 insertions, 0 deletions
diff --git a/src/csvlib.c b/src/csvlib.c new file mode 100644 index 0000000..0b6034d --- /dev/null +++ b/src/csvlib.c @@ -0,0 +1,671 @@ +/* + * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd). + * csvlib.c - set of functions to deal with csv type of conf files + * + * Copyright (C) 2003 by Diane Bruce, Stuart Walsh + * Use it anywhere you like, if you like it buy us a beer. + * If it's broken, don't bother us with the lawyers. + * + * $Id$ + */ + +#include "config.h" +#include "stdinc.h" +#include "list.h" +#include "log.h" +#include "conf.h" +#include "hostmask.h" +#include "client.h" +#include "irc_string.h" +#include "sprintf_irc.h" +#include "memory.h" +#include "send.h" +#include "resv.h" +#include "s_serv.h" + +/* Fix "statement not reached" warnings on Sun WorkShop C */ +#ifdef __SUNPRO_C +# pragma error_messages(off, E_STATEMENT_NOT_REACHED) +#endif + + +static void parse_csv_line(char *, ...); +static int write_csv_line(FILE *, const char *, ...); +static int flush_write(struct Client *, FILE *, FILE *, + const char *, const char *); +static char *getfield(char *); + +int +find_and_delete_temporary(const char *user, const char *host, int type) +{ + struct irc_ssaddr iphost, *piphost; + struct AccessItem *aconf; + int t; + + if ((t = parse_netmask(host, &iphost, NULL)) != HM_HOST) + { +#ifdef IPV6 + if (t == HM_IPV6) + t = AF_INET6; + else +#endif + t = AF_INET; + piphost = &iphost; + } + else + { + t = 0; + piphost = NULL; + } + + if ((aconf = find_conf_by_address(host, piphost, type, t, user, NULL, 0))) + { + if (IsConfTemporary(aconf)) + { + delete_one_address_conf(host, aconf); + return 1; + } + } + + return 0; +} + +/* parse_csv_file() + * + * inputs - FILE pointer + * - type of conf to parse + * output - none + * side effects - + */ +void +parse_csv_file(FILE *file, ConfType conf_type) +{ + struct ConfItem *conf; + struct AccessItem *aconf; + struct MatchItem *match_item; + char *name_field=NULL; + char *user_field=NULL; + char *reason_field=NULL; + char *oper_reason=NULL; + char *host_field=NULL; + char line[IRCD_BUFSIZE]; + char *p; + + while (fgets(line, sizeof(line), file) != NULL) + { + if ((p = strchr(line, '\n')) != NULL) + *p = '\0'; + + if ((line[0] == '\0') || (line[0] == '#')) + continue; + + switch(conf_type) + { + case KLINE_TYPE: + parse_csv_line(line, &user_field, &host_field, &reason_field, NULL); + + find_and_delete_temporary(user_field, host_field, CONF_KLINE); + + conf = make_conf_item(KLINE_TYPE); + aconf = map_to_conf(conf); + + if (host_field != NULL) + DupString(aconf->host, host_field); + if (reason_field != NULL) + DupString(aconf->reason, reason_field); + if (user_field != NULL) + DupString(aconf->user, user_field); + if (aconf->host != NULL) + add_conf_by_address(CONF_KLINE, aconf); + break; + case DLINE_TYPE: + parse_csv_line(line, &host_field, &reason_field, NULL); + + if (host_field && parse_netmask(host_field, NULL, NULL) != HM_HOST) + { + find_and_delete_temporary(NULL, host_field, CONF_DLINE); + + aconf = map_to_conf(make_conf_item(DLINE_TYPE)); + DupString(aconf->host, host_field); + + if (reason_field != NULL) + DupString(aconf->reason, reason_field); + else + DupString(aconf->reason, "No reason"); + add_conf_by_address(CONF_DLINE, aconf); + } + + break; + + case XLINE_TYPE: + parse_csv_line(line, &name_field, &reason_field, &oper_reason, NULL); + conf = make_conf_item(XLINE_TYPE); + match_item = (struct MatchItem *)map_to_conf(conf); + if (name_field != NULL) + DupString(conf->name, name_field); + if (reason_field != NULL) + DupString(match_item->reason, reason_field); + break; + case CRESV_TYPE: + parse_csv_line(line, &name_field, &reason_field, NULL); + (void)create_channel_resv(name_field, reason_field, 0); + break; + + case NRESV_TYPE: + parse_csv_line(line, &name_field, &reason_field, NULL); + (void)create_nick_resv(name_field, reason_field, 0); + break; + + case GLINE_TYPE: + case CONF_TYPE: + case OPER_TYPE: + case CLIENT_TYPE: + case SERVER_TYPE: + case CLUSTER_TYPE: + case HUB_TYPE: + case LEAF_TYPE: + case ULINE_TYPE: + case EXEMPTDLINE_TYPE: + case CLASS_TYPE: + default: + break; + } + } +} + +/* + * parse_csv_line() + * + * inputs - pointer to line to parse + * output - + * side effects - + */ + +static void +parse_csv_line(char *line, ...) +{ + va_list args; + char **dest; + char *field = NULL; + + va_start(args, line); + + for (; ;) + { + dest = va_arg(args, char **); + if ((dest == NULL) || ((field = getfield(field ? NULL : line)) == NULL)) + { + va_end(args); + return; + } + *dest = field; + } +} + +/* write_conf_line() + * + * inputs - pointer to struct AccessItem + * - string current_date (small date) + * - time_t cur_time + * output - NONE + * side effects - This function takes care of + * finding right conf file, writing + * the right lines to this file, + * notifying the oper that their kline/dline etc. is in place + * notifying the opers on the server about the k/d etc. line + * + * - Dianora + */ +void +write_conf_line(struct Client *source_p, struct ConfItem *conf, + const char *current_date, time_t cur_time) +{ + FILE *out; + const char *filename, *from, *to; + struct AccessItem *aconf; + struct MatchItem *xconf; + struct ResvChannel *cresv_p=NULL; + struct MatchItem *nresv_p=NULL; + ConfType type; + + type = conf->type; + filename = get_conf_name(type); + + if (!MyConnect(source_p) && IsCapable(source_p->from, CAP_TS6) && HasID(source_p)) + { + from = me.id; + to = source_p->id; + } + else + { + from = me.name; + to = source_p->name; + } + + if ((out = fopen(filename, "a")) == NULL) + { + sendto_realops_flags(UMODE_ALL, L_ALL, + "*** Problem opening %s ", filename); + return; + } + + switch(type) + { + case KLINE_TYPE: + aconf = (struct AccessItem *)map_to_conf(conf); + sendto_realops_flags(UMODE_ALL, L_ALL, + "%s added K-Line for [%s@%s] [%s]", + get_oper_name(source_p), + aconf->user, aconf->host, aconf->reason); + sendto_one(source_p, ":%s NOTICE %s :Added K-Line [%s@%s]", + from, to, aconf->user, aconf->host); + ilog(LOG_TYPE_KLINE, "%s added K-Line for [%s@%s] [%s]", + source_p->name, aconf->user, aconf->host, aconf->reason); + write_csv_line(out, "%s%s%s%s%s%s%d", + aconf->user, aconf->host, + aconf->reason, aconf->oper_reason, current_date, + get_oper_name(source_p), cur_time); + break; + case DLINE_TYPE: + aconf = (struct AccessItem *)map_to_conf(conf); + sendto_realops_flags(UMODE_ALL, L_ALL, + "%s added D-Line for [%s] [%s]", + get_oper_name(source_p), aconf->host, aconf->reason); + sendto_one(source_p, ":%s NOTICE %s :Added D-Line [%s] to %s", + from, to, aconf->host, filename); + ilog(LOG_TYPE_DLINE, "%s added D-Line for [%s] [%s]", + get_oper_name(source_p), aconf->host, aconf->reason); + write_csv_line(out, "%s%s%s%s%s%d", + aconf->host, aconf->reason, aconf->oper_reason, + current_date, + get_oper_name(source_p), cur_time); + break; + + case XLINE_TYPE: + xconf = (struct MatchItem *)map_to_conf(conf); + sendto_realops_flags(UMODE_ALL, L_ALL, + "%s added X-Line for [%s] [%s]", + get_oper_name(source_p), conf->name, + xconf->reason); + sendto_one(source_p, + ":%s NOTICE %s :Added X-Line [%s] [%d] [%s] to %s", + from, to, conf->name, + xconf->action, xconf->reason, filename); + ilog(LOG_TYPE_IRCD, "%s added X-Line for [%s] [%s]", + get_oper_name(source_p), conf->name, xconf->reason); + write_csv_line(out, "%s%s%s%s%s%d", + conf->name, xconf->reason, xconf->oper_reason, + current_date, get_oper_name(source_p), cur_time); + break; + case CRESV_TYPE: + cresv_p = (struct ResvChannel *)map_to_conf(conf); + + write_csv_line(out, "%s%s", + cresv_p->name, cresv_p->reason); + break; + + case NRESV_TYPE: + nresv_p = (struct MatchItem *)map_to_conf(conf); + + write_csv_line(out, "%s%s", + conf->name, nresv_p->reason); + break; + + default: + fclose(out); + return; + } + + fclose(out); +} + +/* + * write_csv_line() + * + * inputs - pointer to FILE * + * - formatted string + * output - + * side effects - single line is written to csv conf file + */ +static int +write_csv_line(FILE *out, const char *format, ...) +{ + char c; + size_t bytes = 0; + va_list args; + char tmp[1024]; + char *str = tmp; + const char *null_string = ""; + + if (out == NULL) + return(0); + + va_start(args, format); + + while ((c = *format++)) + { + if (c == '%') + { + c = *format++; + if (c == 's') + { + const char *p1 = va_arg(args, const char *); + if (p1 == NULL) + p1 = null_string; + *str++ = '\"'; + ++bytes; + while (*p1 != '\0') + { + *str++ = *p1++; + ++bytes; + } + *str++ = '\"'; + *str++ = ','; + + bytes += 2; + continue; + } + if (c == 'c') + { + *str++ = '\"'; + ++bytes; + *str++ = (char) va_arg(args, int); + ++bytes; + *str++ = '\"'; + *str++ = ','; + + bytes += 2; + continue; + } + + if (c == 'd') + { + int v = va_arg(args, int); + char t[40]; + char *p=t; + + while (v > 10) + { + *p++ = (v % 10) + '0'; + v = v/10; + } + *p++ = (v % 10) + '0'; + + *str++ = '\"'; + ++bytes; + while (p != t) + { + *str++ = *--p; + ++bytes; + } + + *str++ = '\"'; + *str++ = ','; + bytes += 2; + continue; + } + if (c != '%') + { + int ret; + + format -= 2; + ret = vsprintf(str, format, args); + str += ret; + bytes += ret; + *str++ = ','; + + ++bytes; + break; + } + } + *str++ = c; + ++bytes; + } + + if (*(str-1) == ',') + { + *(str-1) = '\n'; + *str = '\0'; + } + else + { + *str++ = '\n'; + ++bytes; + *str = '\0'; + } + + va_end(args); + str = tmp; + fputs(str, out); + + return(bytes); +} + +/* + * getfield + * + * inputs - input buffer + * output - next field + * side effects - field breakup for ircd.conf file. + */ +static char * +getfield(char *newline) +{ + static char *line = NULL; + char *end, *field; + + if (newline != NULL) + line = newline; + + if (line == NULL) + return(NULL); + + field = line; + + /* skip everything that's not a starting quote */ + for(;;) + { + if (*field == '\0') + return(NULL); + else if (*field == '"') + break; + ++field; + } + + /* skip over the beginning " */ + end = ++field; + + for (;;) + { + /* At end of string, mark it as end and return */ + if ((*end == '\0') || (*end == '\n')) + { + line = NULL; + return(NULL); + } + else if (*end == '\\') /* found escape character ? */ + { + end++; + } + else if (*end == '"') /* found terminating " */ + { + *end++ = '\0'; + line = end; + return(field); + } + + end++; + } + + return (NULL); +} + +/* remove_conf_line() + * + * inputs - type of kline to remove + * - pointer to oper removing + * - pat1 pat2 patterns to match + * output - -1 if unsuccessful 0 if no change 1 if change + * side effects - + */ +int +remove_conf_line(ConfType type, struct Client *source_p, const char *pat1, const char *pat2) +{ + const char *filename; + FILE *in, *out; + int pairme=0; + char buf[IRCD_BUFSIZE], buff[IRCD_BUFSIZE], temppath[IRCD_BUFSIZE]; + char *found1; + char *found2; + int oldumask; + + filename = get_conf_name(type); + + if ((in = fopen(filename, "r")) == NULL) + { + sendto_one(source_p, ":%s NOTICE %s :Cannot open %s", me.name, + source_p->name, filename); + return -1; + } + + ircsprintf(temppath, "%s.tmp", filename); + oldumask = umask(0); + + if ((out = fopen(temppath, "w")) == NULL) + { + sendto_one(source_p, ":%s NOTICE %s :Cannot open %s", me.name, + source_p->name, temppath); + fclose(in); + umask(oldumask); + return -1; + } + + umask(oldumask); + oldumask = umask(0); + + while (fgets(buf, sizeof(buf), in) != NULL) + { + if ((*buf == '\0') || (*buf == '#')) + { + if (flush_write(source_p, in, out, buf, temppath) < 0) + return -1; + } + + /* Keep copy of original line, getfield trashes line as it goes */ + strlcpy(buff, buf, sizeof(buff)); + + if ((found1 = getfield(buff)) == NULL) + { + if (flush_write(source_p, in, out, buf, temppath) < 0) + return -1; + continue; + } + + if (pat2 != NULL) + { + if ((found2 = getfield(NULL)) == NULL) + { + if (flush_write(source_p, in, out, buf, temppath) < 0) + return -1; + continue; + } + + if (!irccmp(pat1, found1) && !irccmp(pat2, found2)) + { + pairme = 1; + continue; + } + else + { + if(flush_write(source_p, in, out, buf, temppath) < 0) + return -1; + continue; + } + } + else + { + if (!irccmp(pat1, found1)) + { + pairme = 1; + continue; + } + else + { + if(flush_write(source_p, in, out, buf, temppath) < 0) + return -1; + continue; + } + } + } + + fclose(in); + fclose(out); + +/* The result of the rename should be checked too... oh well */ +/* If there was an error on a write above, then its been reported + * and I am not going to trash the original kline /conf file + */ + + if (pairme == 0) + { + if(temppath != NULL) + (void)unlink(temppath); + return 0; + } + else + { + (void)rename(temppath, filename); + + /* XXX + * This is a very inefficient way of removing a kline/xline etc. + * This next function call forces a complete re-read of all conf + * files, instead of a re-read of the kline/dline etc. files modified + * But, consider how often an /quote unkline etc. is done compared + * to how often a /quote kline is done. Its not a biggie in + * the grand scheme of things. If it does become a biggie, + * we will rewrite it - Dianora + */ + + rehash(0); + return 1; + } +} + +/* + * flush_write() + * + * inputs - pointer to client structure of oper requesting unkline + * - in is the input file descriptor + * - out is the output file descriptor + * - buf is the buffer to write + * - ntowrite is the expected number of character to be written + * - temppath is the temporary file name to be written + * output - -1 for error on write + * - 0 for ok + * side effects - if successful, the buf is written to output file + * if a write failure happesn, and the file pointed to + * by temppath, if its non NULL, is removed. + * + * The idea here is, to be as robust as possible when writing to the + * kline file. + * + * -Dianora + */ +static int +flush_write(struct Client *source_p, FILE *in, FILE* out, + const char *buf, const char *temppath) +{ + int error_on_write = fputs(buf, out) < 0 ? (-1) : (0); + + if (error_on_write) + { + sendto_one(source_p,":%s NOTICE %s :Unable to write to %s aborting", + me.name, source_p->name, temppath); + if(temppath != NULL) + (void)unlink(temppath); + fclose(in); + fclose(out); + } + + return (error_on_write); +} |