ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid-7.2/src/csvlib.c
Revision: 521
Committed: Sat Mar 11 21:37:16 2006 UTC (18 years ago) by db
Content type: text/x-csrc
File size: 17818 byte(s)
Log Message:
- When checking for first " in a line, make sure it doesn't run off
  the end of the line before it finds one.


File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
3 * csvlib.c - set of functions to deal with csv type of conf files
4 *
5 * Copyright (C) 2003 by Diane Bruce, Stuart Walsh
6 * Use it anywhere you like, if you like it buy us a beer.
7 * If it's broken, don't bother us with the lawyers.
8 *
9 * $Id$
10 */
11
12 #include "stdinc.h"
13 #include "tools.h"
14 #include "s_log.h"
15 #include "s_conf.h"
16 #include "hostmask.h"
17 #include "client.h"
18 #include "pcre.h"
19 #include "irc_string.h"
20 #include "sprintf_irc.h"
21 #include "memory.h"
22 #include "send.h"
23 #include "resv.h"
24 #include "s_serv.h"
25
26 /* Fix "statement not reached" warnings on Sun WorkShop C */
27 #ifdef __SUNPRO_C
28 # pragma error_messages(off, E_STATEMENT_NOT_REACHED)
29 #endif
30
31
32 static void parse_csv_line(char *, ...);
33 static int write_csv_line(FBFILE *, const char *, ...);
34 static int flush_write(struct Client *, FBFILE *, FBFILE *,
35 const char *, const char *);
36 static char *getfield(char *);
37
38 /* parse_csv_file()
39 *
40 * inputs - FILE pointer
41 * - type of conf to parse
42 * output - none
43 * side effects -
44 */
45 void
46 parse_csv_file(FBFILE *file, ConfType conf_type)
47 {
48 struct ConfItem *conf;
49 struct AccessItem *aconf;
50 struct MatchItem *match_item;
51 char *name_field=NULL;
52 char *user_field=NULL;
53 char *reason_field=NULL;
54 char *oper_reason=NULL;
55 char *host_field=NULL;
56 char line[IRCD_BUFSIZE];
57 char *p;
58
59 while (fbgets(line, sizeof(line), file) != NULL)
60 {
61 if ((p = strchr(line, '\n')) != NULL)
62 *p = '\0';
63
64 if ((line[0] == '\0') || (line[0] == '#'))
65 continue;
66
67 switch(conf_type)
68 {
69 case KLINE_TYPE:
70 parse_csv_line(line, &user_field, &host_field, &reason_field, NULL);
71 conf = make_conf_item(KLINE_TYPE);
72 aconf = map_to_conf(conf);
73
74 if (host_field != NULL)
75 DupString(aconf->host, host_field);
76 if (reason_field != NULL)
77 DupString(aconf->reason, reason_field);
78 if (user_field != NULL)
79 DupString(aconf->user, user_field);
80 if (aconf->host != NULL)
81 add_conf_by_address(CONF_KILL, aconf);
82 break;
83
84 case RKLINE_TYPE:
85 {
86 const char *errptr = NULL;
87 pcre *exp_user = NULL, *exp_host = NULL;
88
89 parse_csv_line(line, &user_field, &host_field, &reason_field, NULL);
90
91 if (host_field == NULL || user_field == NULL)
92 break;
93
94 if (!(exp_user = ircd_pcre_compile(user_field, &errptr)) ||
95 !(exp_host = ircd_pcre_compile(host_field, &errptr)))
96 {
97 sendto_realops_flags(UMODE_ALL, L_ALL,
98 "Failed to add regular expression based K-Line: %s", errptr);
99 break;
100 }
101
102 aconf = map_to_conf(make_conf_item(RKLINE_TYPE));
103
104 aconf->regexuser = exp_user;
105 aconf->regexhost = exp_host;
106
107 DupString(aconf->user, user_field);
108 DupString(aconf->host, host_field);
109
110 if (reason_field != NULL)
111 DupString(aconf->reason, reason_field);
112 else
113 DupString(aconf->reason, "No reason");
114
115 }
116 break;
117
118 case DLINE_TYPE:
119 parse_csv_line(line, &host_field, &reason_field, NULL);
120 conf = make_conf_item(DLINE_TYPE);
121 aconf = (struct AccessItem *)map_to_conf(conf);
122 if (host_field != NULL)
123 DupString(aconf->host, host_field);
124 if (reason_field != NULL)
125 DupString(aconf->reason, reason_field);
126 conf_add_d_conf(aconf);
127 break;
128
129 case XLINE_TYPE:
130 parse_csv_line(line, &name_field, &reason_field, &oper_reason, NULL);
131 conf = make_conf_item(XLINE_TYPE);
132 match_item = (struct MatchItem *)map_to_conf(conf);
133 if (name_field != NULL)
134 DupString(conf->name, name_field);
135 if (reason_field != NULL)
136 DupString(match_item->reason, reason_field);
137 break;
138
139 case RXLINE_TYPE:
140 {
141 const char *errptr = NULL;
142 pcre *exp_p = NULL;
143
144 parse_csv_line(line, &name_field, &reason_field, &oper_reason, NULL);
145
146 if (name_field == NULL)
147 break;
148
149 if (!(exp_p = ircd_pcre_compile(name_field, &errptr)))
150 {
151 sendto_realops_flags(UMODE_ALL, L_ALL,
152 "Failed to add regular expression based X-Line: %s", errptr);
153 break;
154 }
155
156 conf = make_conf_item(RXLINE_TYPE);
157 conf->regexpname = exp_p;
158 match_item = map_to_conf(conf);
159 DupString(conf->name, name_field);
160
161 if (reason_field != NULL)
162 DupString(match_item->reason, reason_field);
163 else
164 DupString(match_item->reason, "No reason");
165 }
166 break;
167
168 case CRESV_TYPE:
169 parse_csv_line(line, &name_field, &reason_field, NULL);
170 (void)create_channel_resv(name_field, reason_field, 0);
171 break;
172
173 case NRESV_TYPE:
174 parse_csv_line(line, &name_field, &reason_field, NULL);
175 (void)create_nick_resv(name_field, reason_field, 0);
176 break;
177
178 case GLINE_TYPE:
179 case GDENY_TYPE:
180 case CONF_TYPE:
181 case OPER_TYPE:
182 case CLIENT_TYPE:
183 case SERVER_TYPE:
184 case CLUSTER_TYPE:
185 case HUB_TYPE:
186 case LEAF_TYPE:
187 case ULINE_TYPE:
188 case EXEMPTDLINE_TYPE:
189 case CLASS_TYPE:
190 break;
191 }
192 }
193 }
194
195 /*
196 * parse_csv_line()
197 *
198 * inputs - pointer to line to parse
199 * output -
200 * side effects -
201 */
202
203 static void
204 parse_csv_line(char *line, ...)
205 {
206 va_list args;
207 char **dest;
208 char *field = NULL;
209
210 va_start(args, line);
211
212 for (; ;)
213 {
214 dest = va_arg(args, char **);
215 if ((dest == NULL) || ((field = getfield(field ? NULL : line)) == NULL))
216 {
217 va_end(args);
218 return;
219 }
220 *dest = field;
221 }
222 }
223
224 /* write_conf_line()
225 *
226 * inputs - pointer to struct AccessItem
227 * - string current_date (small date)
228 * - time_t cur_time
229 * output - NONE
230 * side effects - This function takes care of
231 * finding right conf file, writing
232 * the right lines to this file,
233 * notifying the oper that their kline/dline etc. is in place
234 * notifying the opers on the server about the k/d etc. line
235 *
236 * - Dianora
237 */
238 void
239 write_conf_line(struct Client *source_p, struct ConfItem *conf,
240 const char *current_date, time_t cur_time)
241 {
242 FBFILE *out;
243 const char *filename, *from, *to;
244 struct AccessItem *aconf;
245 struct MatchItem *xconf;
246 struct ResvChannel *cresv_p=NULL;
247 struct MatchItem *nresv_p=NULL;
248 ConfType type;
249
250 type = conf->type;
251 filename = get_conf_name(type);
252
253 if (!MyConnect(source_p) && IsCapable(source_p->from, CAP_TS6) && HasID(source_p))
254 {
255 from = me.id;
256 to = source_p->id;
257 }
258 else
259 {
260 from = me.name;
261 to = source_p->name;
262 }
263
264 if ((out = fbopen(filename, "a")) == NULL)
265 {
266 sendto_realops_flags(UMODE_ALL, L_ALL,
267 "*** Problem opening %s ", filename);
268 return;
269 }
270
271 switch(type)
272 {
273 case KLINE_TYPE:
274 aconf = (struct AccessItem *)map_to_conf(conf);
275 sendto_realops_flags(UMODE_ALL, L_ALL,
276 "%s added K-Line for [%s@%s] [%s]",
277 get_oper_name(source_p),
278 aconf->user, aconf->host, aconf->reason);
279 sendto_one(source_p, ":%s NOTICE %s :Added K-Line [%s@%s]",
280 from, to, aconf->user, aconf->host);
281 ilog(L_TRACE, "%s added K-Line for [%s@%s] [%s]",
282 source_p->name, aconf->user, aconf->host, aconf->reason);
283 log_oper_action(LOG_KLINE_TYPE, source_p, "[%s@%s] [%s]\n",
284 aconf->user, aconf->host, aconf->reason);
285 write_csv_line(out, "%s%s%s%s%s%s%d",
286 aconf->user, aconf->host,
287 aconf->reason, aconf->oper_reason, current_date,
288 get_oper_name(source_p), cur_time);
289 break;
290
291 case RKLINE_TYPE:
292 aconf = map_to_conf(conf);
293 sendto_realops_flags(UMODE_ALL, L_ALL,
294 "%s added RK-Line for [%s@%s] [%s]",
295 get_oper_name(source_p),
296 aconf->user, aconf->host, aconf->reason);
297 sendto_one(source_p, ":%s NOTICE %s :Added RK-Line [%s@%s]",
298 from, to, aconf->user, aconf->host);
299 ilog(L_TRACE, "%s added K-Line for [%s@%s] [%s]",
300 source_p->name, aconf->user, aconf->host, aconf->reason);
301 log_oper_action(LOG_RKLINE_TYPE, source_p, "[%s@%s] [%s]\n",
302 aconf->user, aconf->host, aconf->reason);
303 write_csv_line(out, "%s%s%s%s%s%s%d",
304 aconf->user, aconf->host,
305 aconf->reason, aconf->oper_reason, current_date,
306 get_oper_name(source_p), cur_time);
307 break;
308
309 case DLINE_TYPE:
310 aconf = (struct AccessItem *)map_to_conf(conf);
311 sendto_realops_flags(UMODE_ALL, L_ALL,
312 "%s added D-Line for [%s] [%s]",
313 get_oper_name(source_p), aconf->host, aconf->reason);
314 sendto_one(source_p, ":%s NOTICE %s :Added D-Line [%s] to %s",
315 from, to, aconf->host, filename);
316 ilog(L_TRACE, "%s added D-Line for [%s] [%s]",
317 get_oper_name(source_p), aconf->host, aconf->reason);
318 log_oper_action(LOG_DLINE_TYPE, source_p, "[%s] [%s]\n",
319 aconf->host, aconf->reason);
320 write_csv_line(out, "%s%s%s%s%s%d",
321 aconf->host, aconf->reason, aconf->oper_reason,
322 current_date,
323 get_oper_name(source_p), cur_time);
324 break;
325
326 case XLINE_TYPE:
327 xconf = (struct MatchItem *)map_to_conf(conf);
328 sendto_realops_flags(UMODE_ALL, L_ALL,
329 "%s added X-Line for [%s] [%s]",
330 get_oper_name(source_p), conf->name,
331 xconf->reason);
332 sendto_one(source_p,
333 ":%s NOTICE %s :Added X-Line [%s] [%d] [%s] to %s",
334 from, to, conf->name,
335 xconf->action, xconf->reason, filename);
336 ilog(L_TRACE, "%s added X-Line for [%s] [%s]",
337 get_oper_name(source_p), conf->name, xconf->reason);
338 write_csv_line(out, "%s%s%s%s%s%d",
339 conf->name, xconf->reason, xconf->oper_reason,
340 current_date, get_oper_name(source_p), cur_time);
341 break;
342
343 case RXLINE_TYPE:
344 xconf = (struct MatchItem *)map_to_conf(conf);
345 sendto_realops_flags(UMODE_ALL, L_ALL,
346 "%s added RX-Line for [%s] [%s]",
347 get_oper_name(source_p), conf->name,
348 xconf->reason);
349 sendto_one(source_p,
350 ":%s NOTICE %s :Added RX-Line [%s] [%s] to %s",
351 from, to, conf->name,
352 xconf->reason, filename);
353 ilog(L_TRACE, "%s added X-Line for [%s] [%s]",
354 get_oper_name(source_p), conf->name, xconf->reason);
355 write_csv_line(out, "%s%s%s%s%s%d",
356 conf->name, xconf->reason, xconf->oper_reason,
357 current_date, get_oper_name(source_p), cur_time);
358 break;
359
360 case CRESV_TYPE:
361 cresv_p = (struct ResvChannel *)map_to_conf(conf);
362
363 write_csv_line(out, "%s%s",
364 cresv_p->name, cresv_p->reason);
365 break;
366
367 case NRESV_TYPE:
368 nresv_p = (struct MatchItem *)map_to_conf(conf);
369
370 write_csv_line(out, "%s%s",
371 conf->name, nresv_p->reason);
372 break;
373
374 default:
375 fbclose(out);
376 return;
377 }
378
379 fbclose(out);
380 }
381
382 /*
383 * write_csv_line()
384 *
385 * inputs - pointer to FBFILE *
386 * - formatted string
387 * output -
388 * side effects - single line is written to csv conf file
389 */
390 static int
391 write_csv_line(FBFILE *out, const char *format, ...)
392 {
393 char c;
394 size_t bytes = 0;
395 va_list args;
396 char tmp[1024];
397 char *str = tmp;
398 const char *null_string = "";
399
400 if (out == NULL)
401 return(0);
402
403 va_start(args, format);
404
405 while ((c = *format++))
406 {
407 if (c == '%')
408 {
409 c = *format++;
410 if (c == 's')
411 {
412 const char *p1 = va_arg(args, const char *);
413 if (p1 == NULL)
414 p1 = null_string;
415 *str++ = '\"';
416 ++bytes;
417 while (*p1 != '\0')
418 {
419 *str++ = *p1++;
420 ++bytes;
421 }
422 *str++ = '\"';
423 *str++ = ',';
424
425 bytes += 2;
426 continue;
427 }
428 if (c == 'c')
429 {
430 *str++ = '\"';
431 ++bytes;
432 *str++ = (char) va_arg(args, int);
433 ++bytes;
434 *str++ = '\"';
435 *str++ = ',';
436
437 bytes += 2;
438 continue;
439 }
440
441 if (c == 'd')
442 {
443 int v = va_arg(args, int);
444 char t[40];
445 char *p=t;
446
447 while (v > 10)
448 {
449 *p++ = (v % 10) + '0';
450 v = v/10;
451 }
452 *p++ = (v % 10) + '0';
453
454 *str++ = '\"';
455 ++bytes;
456 while (p != t)
457 {
458 *str++ = *--p;
459 ++bytes;
460 }
461
462 *str++ = '\"';
463 *str++ = ',';
464 bytes += 2;
465 continue;
466 }
467 if (c != '%')
468 {
469 int ret;
470
471 format -= 2;
472 ret = vsprintf(str, format, args);
473 str += ret;
474 bytes += ret;
475 *str++ = ',';
476
477 ++bytes;
478 break;
479 }
480 }
481 *str++ = c;
482 ++bytes;
483 }
484
485 if (*(str-1) == ',')
486 {
487 *(str-1) = '\n';
488 *str = '\0';
489 }
490 else
491 {
492 *str++ = '\n';
493 ++bytes;
494 *str = '\0';
495 }
496
497 va_end(args);
498 str = tmp;
499 fbputs(str, out, bytes);
500
501 return(bytes);
502 }
503
504 /*
505 * getfield
506 *
507 * inputs - input buffer
508 * output - next field
509 * side effects - field breakup for ircd.conf file.
510 */
511 static char *
512 getfield(char *newline)
513 {
514 static char *line = NULL;
515 char *end, *field;
516
517 if (newline != NULL)
518 line = newline;
519
520 if (line == NULL)
521 return(NULL);
522
523 field = line;
524
525 /* skip everything that's not a starting quote */
526 for(;;)
527 {
528 if (*field == '\0')
529 return(NULL);
530 else if (*field == '"')
531 break;
532 ++field;
533 }
534
535 /* skip over the beginning " */
536 end = ++field;
537
538 for (;;)
539 {
540 /* At end of string, mark it as end and return */
541 if ((*end == '\0') || (*end == '\n'))
542 {
543 line = NULL;
544 return(NULL);
545 }
546 else if (*end == '\\') /* found escape character ? */
547 {
548 end++;
549 }
550 else if (*end == '"') /* found terminating " */
551 {
552 *end++ = '\0';
553 line = end;
554 return(field);
555 }
556
557 end++;
558 }
559
560 return (NULL);
561 }
562
563 /* remove_conf_line()
564 *
565 * inputs - type of kline to remove
566 * - pointer to oper removing
567 * - pat1 pat2 patterns to match
568 * output - -1 if unsuccessful 0 if no change 1 if change
569 * side effects -
570 */
571 int
572 remove_conf_line(ConfType type, struct Client *source_p, const char *pat1, const char *pat2)
573 {
574 const char *filename;
575 FBFILE *in, *out;
576 int pairme=0;
577 char buf[IRCD_BUFSIZE], buff[IRCD_BUFSIZE], temppath[IRCD_BUFSIZE];
578 char *found1;
579 char *found2;
580 int oldumask;
581 int (*cmpfunc)(const char *, const char *) = irccmp;
582
583 if (type == RXLINE_TYPE || type == RKLINE_TYPE)
584 cmpfunc = strcmp;
585
586 filename = get_conf_name(type);
587
588 if ((in = fbopen(filename, "r")) == NULL)
589 {
590 sendto_one(source_p, ":%s NOTICE %s :Cannot open %s", me.name,
591 source_p->name, filename);
592 return -1;
593 }
594
595 ircsprintf(temppath, "%s.tmp", filename);
596 oldumask = umask(0);
597
598 if ((out = fbopen(temppath, "w")) == NULL)
599 {
600 sendto_one(source_p, ":%s NOTICE %s :Cannot open %s", me.name,
601 source_p->name, temppath);
602 fbclose(in);
603 umask(oldumask);
604 return -1;
605 }
606
607 umask(oldumask);
608 oldumask = umask(0);
609
610 while (fbgets(buf, sizeof(buf), in) != NULL)
611 {
612 if ((*buf == '\0') || (*buf == '#'))
613 {
614 if (flush_write(source_p, in, out, buf, temppath) < 0)
615 return -1;
616 }
617
618 /* Keep copy of original line, getfield trashes line as it goes */
619 strlcpy(buff, buf, sizeof(buff));
620
621 if ((found1 = getfield(buff)) == NULL)
622 {
623 if (flush_write(source_p, in, out, buf, temppath) < 0)
624 return -1;
625 continue;
626 }
627
628 if (pat2 != NULL)
629 {
630 if ((found2 = getfield(NULL)) == NULL)
631 {
632 if (flush_write(source_p, in, out, buf, temppath) < 0)
633 return -1;
634 continue;
635 }
636
637 if (!cmpfunc(pat1, found1) && !cmpfunc(pat2, found2))
638 {
639 pairme = 1;
640 continue;
641 }
642 else
643 {
644 if(flush_write(source_p, in, out, buf, temppath) < 0)
645 return -1;
646 continue;
647 }
648 }
649 else
650 {
651 if (!cmpfunc(pat1, found1))
652 {
653 pairme = 1;
654 continue;
655 }
656 else
657 {
658 if(flush_write(source_p, in, out, buf, temppath) < 0)
659 return -1;
660 continue;
661 }
662 }
663 }
664
665 fbclose(in);
666 fbclose(out);
667
668 /* The result of the rename should be checked too... oh well */
669 /* If there was an error on a write above, then its been reported
670 * and I am not going to trash the original kline /conf file
671 */
672
673 if (pairme == 0)
674 {
675 if(temppath != NULL)
676 (void)unlink(temppath);
677 return 0;
678 }
679 else
680 {
681 (void)rename(temppath, filename);
682
683 /* XXX
684 * This is a very inefficient way of removing a kline/xline etc.
685 * This next function call forces a complete re-read of all conf
686 * files, instead of a re-read of the kline/dline etc. files modified
687 * But, consider how often an /quote unkline etc. is done compared
688 * to how often a /quote kline is done. Its not a biggie in
689 * the grand scheme of things. If it does become a biggie,
690 * we will rewrite it - Dianora
691 */
692
693 rehash(0);
694 return 1;
695 }
696 }
697
698 /*
699 * flush_write()
700 *
701 * inputs - pointer to client structure of oper requesting unkline
702 * - in is the input file descriptor
703 * - out is the output file descriptor
704 * - buf is the buffer to write
705 * - ntowrite is the expected number of character to be written
706 * - temppath is the temporary file name to be written
707 * output - -1 for error on write
708 * - 0 for ok
709 * side effects - if successful, the buf is written to output file
710 * if a write failure happesn, and the file pointed to
711 * by temppath, if its non NULL, is removed.
712 *
713 * The idea here is, to be as robust as possible when writing to the
714 * kline file.
715 *
716 * -Dianora
717 */
718 static int
719 flush_write(struct Client *source_p, FBFILE *in, FBFILE* out,
720 const char *buf, const char *temppath)
721 {
722 int error_on_write = (fbputs(buf, out, strlen(buf)) < 0) ? (-1) : (0);
723
724 if (error_on_write)
725 {
726 sendto_one(source_p,":%s NOTICE %s :Unable to write to %s aborting",
727 me.name, source_p->name, temppath);
728 if(temppath != NULL)
729 (void)unlink(temppath);
730 fbclose(in);
731 fbclose(out);
732 }
733
734 return (error_on_write);
735 }

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision