1 /*
2 * atheme-services: A collection of minimalist IRC services
3 * function.c: Miscillaneous functions.
4 *
5 * Copyright (c) 2005-2007 Atheme Project (http://www.atheme.org)
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
12 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
13 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
14 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
15 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
16 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
17 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
18 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
19 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
20 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
21 * POSSIBILITY OF SUCH DAMAGE.
22 */
23
24 #include "atheme.h"
25
26 char ch[] = "abcdefghijklmnopqrstuvwxyz";
27
28 /* This function uses smalloc() to allocate memory.
29 * You MUST free the result when you are done with it!
30 */
random_string(int sz)31 char *random_string(int sz)
32 {
33 int i;
34 char *buf = smalloc(sz + 1); /* padding */
35
36 for (i = 0; i < sz; i++)
37 {
38 buf[i] = ch[arc4random() % 26];
39 }
40
41 buf[sz] = 0;
42
43 return buf;
44 }
45
create_challenge(sourceinfo_t * si,const char * name,int v,char * dest)46 void create_challenge(sourceinfo_t *si, const char *name, int v, char *dest)
47 {
48 char buf[256];
49 int digest[4];
50 md5_state_t ctx;
51
52 snprintf(buf, sizeof buf, "%lu:%s:%s",
53 (unsigned long)(CURRTIME / 300) - v,
54 get_source_name(si),
55 name);
56 md5_init(&ctx);
57 md5_append(&ctx, (unsigned char *)buf, strlen(buf));
58 md5_finish(&ctx, (unsigned char *)digest);
59 /* note: this depends on byte order, but that's ok because
60 * it's only going to work in the same atheme instance anyway
61 */
62 snprintf(dest, 80, "%x:%x", digest[0], digest[1]);
63 }
64
65 #ifdef HAVE_GETTIMEOFDAY
66 /* starts a timer */
s_time(struct timeval * sttime)67 void s_time(struct timeval *sttime)
68 {
69 gettimeofday(sttime, NULL);
70 }
71 #endif
72
73 #ifdef HAVE_GETTIMEOFDAY
74 /* ends a timer */
e_time(struct timeval sttime,struct timeval * ttime)75 void e_time(struct timeval sttime, struct timeval *ttime)
76 {
77 struct timeval now;
78
79 gettimeofday(&now, NULL);
80 timersub(&now, &sttime, ttime);
81 }
82 #endif
83
84 #ifdef HAVE_GETTIMEOFDAY
85 /* translates microseconds into miliseconds */
tv2ms(struct timeval * tv)86 int tv2ms(struct timeval *tv)
87 {
88 return (tv->tv_sec * 1000) + (int) (tv->tv_usec / 1000);
89 }
90 #endif
91
92 /* replaces tabs with a single ASCII 32 */
tb2sp(char * line)93 void tb2sp(char *line)
94 {
95 char *c;
96
97 while ((c = strchr(line, '\t')))
98 *c = ' ';
99 }
100
101 /* replace all occurances of 'old' with 'new' */
replace(char * s,int size,const char * old,const char * new)102 char *replace(char *s, int size, const char *old, const char *new)
103 {
104 char *ptr = s;
105 int left = strlen(s);
106 int avail = size - (left + 1);
107 int oldlen = strlen(old);
108 int newlen = strlen(new);
109 int diff = newlen - oldlen;
110
111 while (left >= oldlen)
112 {
113 if (strncmp(ptr, old, oldlen))
114 {
115 left--;
116 ptr++;
117 continue;
118 }
119
120 if (diff > avail)
121 break;
122
123 if (diff != 0)
124 memmove(ptr + oldlen + diff, ptr + oldlen, left + 1 - oldlen);
125
126 memcpy(ptr, new, newlen);
127 ptr += newlen;
128 left -= oldlen;
129 }
130
131 return s;
132 }
133
134 /* reverse of atoi() */
number_to_string(int num)135 const char *number_to_string(int num)
136 {
137 static char ret[32];
138 snprintf(ret, 32, "%d", num);
139 return ret;
140 }
141
142 /* return the time elapsed since an event */
time_ago(time_t event)143 char *time_ago(time_t event)
144 {
145 static char ret[128];
146 int years, weeks, days, hours, minutes, seconds;
147
148 event = CURRTIME - event;
149 years = weeks = days = hours = minutes = 0;
150
151 while (event >= 60 * 60 * 24 * 365)
152 {
153 event -= 60 * 60 * 24 * 365;
154 years++;
155 }
156 while (event >= 60 * 60 * 24 * 7)
157 {
158 event -= 60 * 60 * 24 * 7;
159 weeks++;
160 }
161 while (event >= 60 * 60 * 24)
162 {
163 event -= 60 * 60 * 24;
164 days++;
165 }
166 while (event >= 60 * 60)
167 {
168 event -= 60 * 60;
169 hours++;
170 }
171 while (event >= 60)
172 {
173 event -= 60;
174 minutes++;
175 }
176
177 seconds = event;
178
179 if (years)
180 snprintf(ret, sizeof(ret), "%dy %dw %dd", years, weeks, days);
181 else if (weeks)
182 snprintf(ret, sizeof(ret), "%dw %dd %dh", weeks, days, hours);
183 else if (days)
184 snprintf(ret, sizeof(ret), "%dd %dh %dm %ds", days, hours, minutes, seconds);
185 else if (hours)
186 snprintf(ret, sizeof(ret), "%dh %dm %ds", hours, minutes, seconds);
187 else if (minutes)
188 snprintf(ret, sizeof(ret), "%dm %ds", minutes, seconds);
189 else
190 snprintf(ret, sizeof(ret), "%ds", seconds);
191
192 return ret;
193 }
194
timediff(time_t seconds)195 char *timediff(time_t seconds)
196 {
197 static char buf[BUFSIZE];
198 long unsigned days, hours, minutes;
199
200 days = seconds / 86400;
201 seconds %= 86400;
202 hours = seconds / 3600;
203 hours %= 3600;
204 minutes = seconds / 60;
205 minutes %= 60;
206 seconds %= 60;
207
208 snprintf(buf, sizeof(buf), "%lu day%s, %lu:%02lu:%02lu", days, (days == 1) ? "" : "s", hours, minutes, (long unsigned) seconds);
209
210 return buf;
211 }
212
213 /* generate a random number, for use as a key */
makekey(void)214 unsigned long makekey(void)
215 {
216 unsigned long k;
217
218 k = arc4random() & 0x7FFFFFFF;
219
220 /* shorten or pad it to 9 digits */
221 if (k > 1000000000)
222 k = k - 1000000000;
223 if (k < 100000000)
224 k = k + 100000000;
225
226 return k;
227 }
228
is_internal_client(user_t * u)229 bool is_internal_client(user_t *u)
230 {
231 return (u && (!u->server || u->server == me.me));
232 }
233
validemail(const char * email)234 int validemail(const char *email)
235 {
236 int i, valid = 1, chars = 0, atcnt = 0, dotcnt1 = 0;
237 char c;
238 const char *lastdot = NULL;
239
240 /* sane length */
241 if (strlen(email) >= EMAILLEN)
242 return 0;
243
244 #if 0
245 /* RFC2822 */
246 #define EXTRA_ATEXTCHARS "!#$%&'*+-/=?^_`{|}~"
247 #else
248 /* commonly used subset */
249 #define EXTRA_ATEXTCHARS "%+-=^_"
250 #endif
251 /* note that we do not allow domain literals or quoted strings */
252 for (i = 0; email[i] != '\0'; i++)
253 {
254 c = email[i];
255 if (c == '.')
256 {
257 dotcnt1++;
258 lastdot = &email[i];
259 /* dot may not be first or last, no consecutive dots */
260 if (i == 0 || email[i - 1] == '.' ||
261 email[i - 1] == '@' ||
262 email[i + 1] == '\0' ||
263 email[i + 1] == '@')
264 return 0;
265 }
266 else if (c == '@')
267 atcnt++, dotcnt1 = 0;
268 else if ((c >= 'a' && c <= 'z') ||
269 (c >= 'A' && c <= 'Z') ||
270 (c >= '0' && c <= '9') ||
271 strchr(EXTRA_ATEXTCHARS, c))
272 chars++;
273 else
274 return 0;
275 }
276
277 /* must have exactly one @, and at least one . after the @ */
278 if (atcnt != 1 || dotcnt1 == 0)
279 return 0;
280
281 /* no mail to IP addresses, this should be done using [10.2.3.4]
282 * like syntax but we do not allow that either
283 */
284 if (isdigit((unsigned char)lastdot[1]))
285 return 0;
286
287 /* make sure there are at least 4 characters besides the above
288 * mentioned @ and .
289 */
290 if (chars < 4)
291 return 0;
292
293 return valid;
294 }
295
296 static mowgli_list_t email_canonicalizers;
297
298 /* Re-canonicalize email addresses.
299 * Call this after adding or removing an email_canonicalize hook.
300 */
canonicalize_emails(void)301 static void canonicalize_emails(void)
302 {
303 myentity_iteration_state_t state;
304 myentity_t *mt;
305
306 MYENTITY_FOREACH_T(mt, &state, ENT_USER)
307 {
308 myuser_t *mu = user(mt);
309
310 strshare_unref(mu->email_canonical);
311 mu->email_canonical = canonicalize_email(mu->email);
312 }
313 }
314
315 void
register_email_canonicalizer(email_canonicalizer_t func,void * user_data)316 register_email_canonicalizer(email_canonicalizer_t func, void *user_data)
317 {
318 email_canonicalizer_item_t *item;
319
320 item = smalloc(sizeof(email_canonicalizer_item_t));
321 item->func = func;
322 item->user_data = user_data;
323
324 mowgli_node_add(item, &item->node, &email_canonicalizers);
325
326 canonicalize_emails();
327 }
328
329 void
unregister_email_canonicalizer(email_canonicalizer_t func,void * user_data)330 unregister_email_canonicalizer(email_canonicalizer_t func, void *user_data)
331 {
332 mowgli_node_t *n, *tn;
333
334 MOWGLI_LIST_FOREACH_SAFE(n, tn, email_canonicalizers.head)
335 {
336 email_canonicalizer_item_t *item = n->data;
337
338 if (item->func == func && item->user_data == user_data)
339 {
340 mowgli_node_delete(&item->node, &email_canonicalizers);
341 free(item);
342
343 canonicalize_emails();
344
345 return;
346 }
347 }
348 }
349
canonicalize_email(const char * email)350 stringref canonicalize_email(const char *email)
351 {
352 mowgli_node_t *n, *tn;
353 char buf[EMAILLEN + 1];
354
355 if (email == NULL)
356 return NULL;
357
358 mowgli_strlcpy(buf, email, sizeof buf);
359
360 MOWGLI_LIST_FOREACH_SAFE(n, tn, email_canonicalizers.head)
361 {
362 email_canonicalizer_item_t *item = n->data;
363
364 item->func(buf, item->user_data);
365 }
366
367 return strshare_get(buf);
368 }
369
canonicalize_email_case(char email[EMAILLEN+1],void * user_data)370 void canonicalize_email_case(char email[EMAILLEN + 1], void *user_data)
371 {
372 strcasecanon(email);
373 }
374
email_within_limits(const char * email)375 bool email_within_limits(const char *email)
376 {
377 mowgli_node_t *n;
378 myentity_iteration_state_t state;
379 myentity_t *mt;
380 unsigned int tcnt = 0;
381 stringref email_canonical;
382 bool result = true;
383
384 if (me.maxusers <= 0)
385 return true;
386
387 MOWGLI_ITER_FOREACH(n, nicksvs.emailexempts.head)
388 {
389 if (0 == match(n->data, email))
390 return true;
391 }
392
393 email_canonical = canonicalize_email(email);
394
395 MYENTITY_FOREACH_T(mt, &state, ENT_USER)
396 {
397 myuser_t *mu = user(mt);
398
399 if (mu->email_canonical == email_canonical)
400 tcnt++;
401
402 /* optimization: if tcnt >= me.maxusers, quit iterating. -nenolod */
403 if (tcnt >= me.maxusers) {
404 result = false;
405 break;
406 }
407 }
408
409 strshare_unref(email_canonical);
410 return result;
411 }
412
validhostmask(const char * host)413 bool validhostmask(const char *host)
414 {
415 char *p, *q;
416
417 if (strchr(host, ' '))
418 return false;
419
420 /* make sure it has ! and @ in that order and only once */
421 p = strchr(host, '!');
422 q = strchr(host, '@');
423 if (p == NULL || q == NULL || p > q || strchr(p + 1, '!') ||
424 strchr(q + 1, '@'))
425 return false;
426
427 /* XXX this NICKLEN is too long */
428 if (strlen(host) > NICKLEN + USERLEN + HOSTLEN + 1)
429 return false;
430
431 if (host[0] == ',' || host[0] == '-' || host[0] == '#' ||
432 host[0] == '@' || host[0] == '!' || host[0] == ':')
433 return false;
434
435 return true;
436 }
437
438 /* char *
439 * pretty_mask(char *mask);
440 *
441 * Input: A mask.
442 * Output: A "user-friendly" version of the mask, in mask_buf.
443 * Side-effects: mask_buf is appended to. mask_pos is incremented.
444 * Notes: The following transitions are made:
445 * x!y@z => x!y@z
446 * y@z => *!y@z
447 * x!y => x!y@*
448 * x => x!*@*
449 * z.d => *!*@z.d
450 *
451 * If either nick/user/host are > than their respective limits, they are
452 * chopped
453 */
pretty_mask(char * mask)454 char *pretty_mask(char *mask)
455 {
456 static char mask_buf[BUFSIZE];
457 int old_mask_pos;
458 char star[] = "*";
459 char *nick = star, *user = star, *host = star;
460 int mask_pos = 0;
461
462 char *t, *at, *ex;
463 char ne = 0, ue = 0, he = 0; /* save values at nick[NICKLEN], et all */
464
465 /* No point wasting CPU time if the mask is already valid */
466 if (validhostmask(mask))
467 return mask;
468
469 if((size_t) (BUFSIZE - mask_pos) < strlen(mask) + 5)
470 return (NULL);
471
472 old_mask_pos = mask_pos;
473
474 at = ex = NULL;
475 if(is_valid_host(mask))
476 {
477 if (*mask != '\0')
478 host = mask;
479 }
480 else if((t = strchr(mask, '@')) != NULL)
481 {
482 at = t;
483 *t++ = '\0';
484 if(*t != '\0')
485 host = t;
486
487 if((t = strchr(mask, '!')) != NULL)
488 {
489 ex = t;
490 *t++ = '\0';
491 if(*t != '\0')
492 user = t;
493 if(*mask != '\0')
494 nick = mask;
495 }
496 else
497 {
498 if(*mask != '\0')
499 user = mask;
500 }
501 }
502 else if((t = strchr(mask, '!')) != NULL)
503 {
504 ex = t;
505 *t++ = '\0';
506 if(*mask != '\0')
507 nick = mask;
508 if(*t != '\0')
509 user = t;
510 }
511 else if(strchr(mask, '.') != NULL && strchr(mask, ':') != NULL)
512 {
513 if(*mask != '\0')
514 host = mask;
515 }
516 else
517 {
518 if(*mask != '\0')
519 nick = mask;
520 }
521
522 /* truncate values to max lengths */
523 if(strlen(nick) > NICKLEN - 1)
524 {
525 ne = nick[NICKLEN - 1];
526 nick[NICKLEN - 1] = '\0';
527 }
528 if(strlen(user) > USERLEN)
529 {
530 ue = user[USERLEN];
531 user[USERLEN] = '\0';
532 }
533 if(strlen(host) > HOSTLEN)
534 {
535 he = host[HOSTLEN];
536 host[HOSTLEN] = '\0';
537 }
538
539 snprintf(mask_buf, sizeof mask_buf, "%s!%s@%s", nick, user, host);
540
541 /* restore mask, since we may need to use it again later */
542 if(at)
543 *at = '@';
544 if(ex)
545 *ex = '!';
546 if(ne)
547 nick[NICKLEN - 1] = ne;
548 if(ue)
549 user[USERLEN] = ue;
550 if(he)
551 host[HOSTLEN] = he;
552
553 return mask_buf;
554 }
555
validtopic(const char * topic)556 bool validtopic(const char *topic)
557 {
558 int i;
559
560 /* Most server protocols support less than this (depending on
561 * the lengths of the identifiers), but this should catch the
562 * ludicrous stuff.
563 */
564 if (strlen(topic) > 450)
565 return false;
566 for (i = 0; topic[i] != '\0'; i++)
567 {
568 switch (topic[i])
569 {
570 case '\r':
571 case '\n':
572 return false;
573 }
574 }
575 if (ircd->flags & IRCD_TOPIC_NOCOLOUR)
576 {
577 for (i = 0; topic[i] != '\0'; i++)
578 {
579 switch (topic[i])
580 {
581 case 2:
582 case 3:
583 case 6:
584 case 7:
585 case 22:
586 case 23:
587 case 27:
588 case 31:
589 return false;
590 }
591 }
592 }
593 return true;
594 }
595
has_ctrl_chars(const char * text)596 bool has_ctrl_chars(const char *text)
597 {
598 int i;
599
600 for (i = 0; text[i] != '\0'; i++)
601 {
602 if (text[i] > 0 && text[i] < 32)
603 return true;
604 }
605 return false;
606 }
607
608 #ifndef MOWGLI_OS_WIN
sendemail_waited(pid_t pid,int status,void * data)609 static void sendemail_waited(pid_t pid, int status, void *data)
610 {
611 char *email;
612
613 email = data;
614 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
615 slog(LG_INFO, "sendemail_waited(): email for %s failed", email);
616 free(email);
617 }
618 #endif
619
620 /* send the specified type of email.
621 *
622 * u is whoever caused this to be called, the corresponding service
623 * in case of xmlrpc
624 * type is EMAIL_*, see include/tools.h
625 * mu is the recipient user
626 * param depends on type, also see include/tools.h
627 */
sendemail(user_t * u,myuser_t * mu,const char * type,const char * email,const char * param)628 int sendemail(user_t *u, myuser_t *mu, const char *type, const char *email, const char *param)
629 {
630 #ifndef MOWGLI_OS_WIN
631 char *date = NULL;
632 char timebuf[BUFSIZE], to[BUFSIZE], from[BUFSIZE], buf[BUFSIZE], pathbuf[BUFSIZE], sourceinfo[BUFSIZE];
633 FILE *in, *out;
634 time_t t;
635 struct tm tm;
636 int pipfds[2];
637 pid_t pid;
638 int rc;
639 static time_t period_start = 0, lastwallops = 0;
640 static unsigned int emailcount = 0;
641 service_t *svs;
642
643 if (u == NULL || mu == NULL)
644 return 0;
645
646 if (me.mta == NULL)
647 {
648 if (strcmp(type, EMAIL_MEMO) && !is_internal_client(u))
649 {
650 svs = service_find("operserv");
651 notice(svs ? svs->nick : me.name, u->nick, "Sending email is administratively disabled.");
652 }
653 return 0;
654 }
655
656 if (!validemail(email))
657 {
658 if (strcmp(type, EMAIL_MEMO) && !is_internal_client(u))
659 {
660 svs = service_find("operserv");
661 notice(svs ? svs->nick : me.name, u->nick, "The email address is considered invalid.");
662 }
663 return 0;
664 }
665
666 if ((unsigned int)(CURRTIME - period_start) > me.emailtime)
667 {
668 emailcount = 0;
669 period_start = CURRTIME;
670 }
671 emailcount++;
672 if (emailcount > me.emaillimit)
673 {
674 if (CURRTIME - lastwallops > 60)
675 {
676 wallops(_("Rejecting email for %s[%s@%s] due to too high load (type '%s' to %s <%s>)"),
677 u->nick, u->user, u->vhost,
678 type, entity(mu)->name, email);
679 slog(LG_ERROR, "sendemail(): rejecting email for %s[%s@%s] (%s) due to too high load (type '%s' to %s <%s>)",
680 u->nick, u->user, u->vhost,
681 u->ip ? u->ip : u->host,
682 type, entity(mu)->name, email);
683 lastwallops = CURRTIME;
684 }
685 return 0;
686 }
687
688 snprintf(pathbuf, sizeof pathbuf, "%s/%s", SHAREDIR "/email", type);
689 if ((in = fopen(pathbuf, "r")) == NULL)
690 {
691 slog(LG_ERROR, "sendemail(): rejecting email for %s[%s@%s] (%s), due to unknown type '%s'",
692 u->nick, u->user, u->vhost, email, type);
693 return 0;
694 }
695
696 slog(LG_INFO, "sendemail(): email for %s[%s@%s] (%s) type %s to %s <%s>",
697 u->nick, u->user, u->vhost, u->ip ? u->ip : u->host,
698 type, entity(mu)->name, email);
699
700 /* set up the email headers */
701 time(&t);
702 tm = *localtime(&t);
703 strftime(timebuf, sizeof timebuf, "%a, %d %b %Y %H:%M:%S %z", &tm);
704
705 date = timebuf;
706
707 snprintf(from, sizeof from, "\"%s Network Services\" <%s>",
708 me.netname, me.register_email);
709 snprintf(to, sizeof to, "\"%s\" <%s>", entity(mu)->name, email);
710 /* \ is special here; escape it */
711 replace(to, sizeof to, "\\", "\\\\");
712 snprintf(sourceinfo, sizeof sourceinfo, "%s[%s@%s]", u->nick, u->user, u->vhost);
713
714 /* now set up the email */
715 if (pipe(pipfds) < 0)
716 {
717 fclose(in);
718 return 0;
719 }
720 switch (pid = fork())
721 {
722 case -1:
723 fclose(in);
724 return 0;
725 case 0:
726 connection_close_all_fds();
727 close(pipfds[1]);
728 dup2(pipfds[0], 0);
729 execl(me.mta, me.mta, "-t", "-f", me.register_email, NULL);
730 _exit(255);
731 }
732 close(pipfds[0]);
733 childproc_add(pid, "email", sendemail_waited, sstrdup(email));
734 out = fdopen(pipfds[1], "w");
735
736 while (fgets(buf, BUFSIZE, in))
737 {
738 strip(buf);
739
740 replace(buf, sizeof buf, "&from&", from);
741 replace(buf, sizeof buf, "&to&", to);
742 replace(buf, sizeof buf, "&replyto&", me.adminemail);
743 replace(buf, sizeof buf, "&date&", date);
744 replace(buf, sizeof buf, "&accountname&", entity(mu)->name);
745 replace(buf, sizeof buf, "&entityname&", u->myuser ? entity(u->myuser)->name : u->nick);
746 replace(buf, sizeof buf, "&netname&", me.netname);
747 replace(buf, sizeof buf, "¶m&", param);
748 replace(buf, sizeof buf, "&sourceinfo&", sourceinfo);
749 if ((svs = service_find("nickserv")) != NULL)
750 replace(buf, sizeof buf, "&nicksvs&", svs->me->nick);
751 if ((svs = service_find("chanserv")) != NULL)
752 replace(buf, sizeof buf, "&chansvs&", svs->me->nick);
753 if ((svs = service_find("memoserv")) != NULL)
754 replace(buf, sizeof buf, "&memosvs&", svs->me->nick);
755 if ((svs = service_find("operserv")) != NULL)
756 replace(buf, sizeof buf, "&opersvs&", svs->me->nick);
757
758 fprintf(out, "%s\n", buf);
759 }
760
761 fclose(in);
762
763 rc = 1;
764 if (ferror(out))
765 rc = 0;
766 if (fclose(out) < 0)
767 rc = 0;
768 if (rc == 0)
769 slog(LG_ERROR, "sendemail(): mta failure");
770 return rc;
771 #else
772 # warning implement me :(
773 return 0;
774 #endif
775 }
776
777 /* various access level checkers */
is_founder(mychan_t * mychan,myentity_t * mt)778 bool is_founder(mychan_t *mychan, myentity_t *mt)
779 {
780 if (mt == NULL)
781 return false;
782
783 if (chanacs_entity_has_flag(mychan, mt, CA_FOUNDER))
784 return true;
785
786 return false;
787 }
788
is_ircop(user_t * user)789 bool is_ircop(user_t *user)
790 {
791 if (UF_IRCOP & user->flags)
792 return true;
793
794 return false;
795 }
796
is_admin(user_t * user)797 bool is_admin(user_t *user)
798 {
799 if (UF_ADMIN & user->flags)
800 return true;
801
802 return false;
803 }
804
is_autokline_exempt(user_t * user)805 bool is_autokline_exempt(user_t *user)
806 {
807 mowgli_node_t *n;
808 char buf[BUFSIZE];
809
810 snprintf(buf, sizeof(buf), "%s@%s", user->user, user->host);
811 MOWGLI_ITER_FOREACH(n, config_options.exempts.head)
812 {
813 if (0 == match(n->data, buf))
814 return true;
815 }
816 return false;
817 }
818
is_service(user_t * user)819 bool is_service(user_t *user)
820 {
821 if (UF_SERVICE & user->flags)
822 return true;
823
824 return false;
825 }
826
sbytes(float x)827 char *sbytes(float x)
828 {
829 if (x > 1073741824.0)
830 return "GB";
831
832 else if (x > 1048576.0)
833 return "MB";
834
835 else if (x > 1024.0)
836 return "KB";
837
838 return "B";
839 }
840
bytes(float x)841 float bytes(float x)
842 {
843 if (x > 1073741824.0)
844 return (x / 1073741824.0);
845
846 if (x > 1048576.0)
847 return (x / 1048576.0);
848
849 if (x > 1024.0)
850 return (x / 1024.0);
851
852 return x;
853 }
854
srename(const char * old_fn,const char * new_fn)855 int srename(const char *old_fn, const char *new_fn)
856 {
857 #ifdef MOWGLI_OS_WIN
858 unlink(new_fn);
859 #endif
860
861 return rename(old_fn, new_fn);
862 }
863
combine_path(const char * parent,const char * child)864 char *combine_path(const char *parent, const char *child)
865 {
866 char buf[BUFSIZE];
867
868 return_val_if_fail(parent != NULL, NULL);
869 return_val_if_fail(child != NULL, NULL);
870
871 mowgli_strlcpy(buf, parent, sizeof buf);
872 mowgli_strlcat(buf, "/", sizeof buf);
873 mowgli_strlcat(buf, child, sizeof buf);
874
875 return sstrdup(buf);
876 }
877
878 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
879 * vim:ts=8
880 * vim:sw=8
881 * vim:noexpandtab
882 */
883