ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/src/csvlib.c
Revision: 139
Committed: Sun Oct 16 06:01:13 2005 UTC (20 years, 9 months ago) by db
Content type: text/x-csrc
File size: 17522 byte(s)
Log Message:
- get rid of map_conf_item and unmap_conf_item
- Use an union in struct ConfItem, but only allocate memory needed


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

Properties

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