ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/src/function.C
Revision: 1.8
Committed: Sun Sep 16 18:54:45 2007 UTC (19 years ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.7: +7 -3 lines
Log Message:
#defines to enum

File Contents

# Content
1 /*
2 * function.C: Miscellaneous functions.
3 *
4 * Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team
5 * Rights to this code are as documented in COPYING.
6 *
7 *
8 * Portions of this file were derived from sources bearing the following license:
9 * Rights to this code are documented in doc/pod/license.pod.
10 * Copyright © 2005-2007 Atheme Project (http://www.atheme.org)
11 */
12
13 static char const rcsid[] = "$Id: function.C,v 1.7 2007-09-09 20:05:52 pippijn Exp $";
14
15 #include "atheme.h"
16 #include <common/util.h>
17 #include <ermyth/crypto.h>
18 #include <account/mychan.h>
19 #include <account/chanacs.h>
20 #include <account/myuser.h>
21 #include <common/random.h>
22
23 char ch[27] = "abcdefghijklmnopqrstuvwxyz";
24
25 /* This function uses allocates memory.
26 * You MUST free the result when you are done with it!
27 */
28 char *
29 gen_pw (int sz)
30 {
31 int i;
32 char *buf = salloc<char> (sz + 1); /* padding */
33
34 for (i = 0; i < sz; i++)
35 {
36 buf[i] = ch[gen_rand32 () % 26];
37 }
38
39 buf[sz] = 0;
40
41 return buf;
42 }
43
44 #ifdef HAVE_GETTIMEOFDAY
45 /* starts a timer */
46 void
47 s_time (struct timeval *sttime)
48 {
49 gettimeofday (sttime, NULL);
50 }
51 #endif
52
53 #ifdef HAVE_GETTIMEOFDAY
54 /* ends a timer */
55 void
56 e_time (struct timeval sttime, struct timeval *ttime)
57 {
58 struct timeval now;
59
60 gettimeofday (&now, NULL);
61 timersub (&now, &sttime, ttime);
62 }
63 #endif
64
65 #ifdef HAVE_GETTIMEOFDAY
66 /* translates microseconds into miliseconds */
67 int
68 tv2ms (struct timeval *tv)
69 {
70 return (tv->tv_sec * 1000) + (int) (tv->tv_usec / 1000);
71 }
72 #endif
73
74 /* replaces tabs with a single ASCII 32 */
75 void
76 tb2sp (char *line)
77 {
78 char *c;
79
80 while ((c = strchr (line, '\t')))
81 *c = ' ';
82 }
83
84 /* replace all occurances of 'oldstr' with 'newstr' */
85 char *
86 replace (char *s, int size, char const * const oldstr, char const * const newstr)
87 {
88 char *ptr = s;
89 int left = strlen (s);
90 int avail = size - (left + 1);
91 int oldstrlen = strlen (oldstr);
92 int newstrlen = strlen (newstr);
93 int diff = newstrlen - oldstrlen;
94
95 while (left >= oldstrlen)
96 {
97 if (strncmp (ptr, oldstr, oldstrlen))
98 {
99 left--;
100 ptr++;
101 continue;
102 }
103
104 if (diff > avail)
105 break;
106
107 if (diff != 0)
108 memmove (ptr + oldstrlen + diff, ptr + oldstrlen, left + 1);
109
110 memcpy (ptr, newstr, newstrlen);
111 ptr += newstrlen;
112 left -= oldstrlen;
113 }
114
115 return s;
116 }
117
118 /* return the time elapsed since an event */
119 char *
120 time_ago (time_t event)
121 {
122 static char ret[128];
123 int years, weeks, days, hours, minutes, seconds;
124
125 event = NOW - event;
126 years = weeks = days = hours = minutes = seconds = 0;
127
128 while (event >= 60 * 60 * 24 * 365)
129 {
130 event -= 60 * 60 * 24 * 365;
131 years++;
132 }
133 while (event >= 60 * 60 * 24 * 7)
134 {
135 event -= 60 * 60 * 24 * 7;
136 weeks++;
137 }
138 while (event >= 60 * 60 * 24)
139 {
140 event -= 60 * 60 * 24;
141 days++;
142 }
143 while (event >= 60 * 60)
144 {
145 event -= 60 * 60;
146 hours++;
147 }
148 while (event >= 60)
149 {
150 event -= 60;
151 minutes++;
152 }
153
154 seconds = event;
155
156 if (years)
157 snprintf (ret, sizeof (ret), "%d year%s, %d week%s, %d day%s, %02d:%02d:%02d", years, years == 1 ? "" : "s", weeks, weeks == 1 ? "" : "s", days, days == 1 ? "" : "s", hours, minutes, seconds);
158 else if (weeks)
159 snprintf (ret, sizeof (ret), "%d week%s, %d day%s, %02d:%02d:%02d", weeks, weeks == 1 ? "" : "s", days, days == 1 ? "" : "s", hours, minutes, seconds);
160 else if (days)
161 snprintf (ret, sizeof (ret), "%d day%s, %02d:%02d:%02d", days, days == 1 ? "" : "s", hours, minutes, seconds);
162 else if (hours)
163 snprintf (ret, sizeof (ret), "%d hour%s, %d minute%s, %d second%s", hours, hours == 1 ? "" : "s", minutes, minutes == 1 ? "" : "s", seconds, seconds == 1 ? "" : "s");
164 else if (minutes)
165 snprintf (ret, sizeof (ret), "%d minute%s, %d second%s", minutes, minutes == 1 ? "" : "s", seconds, seconds == 1 ? "" : "s");
166 else
167 snprintf (ret, sizeof (ret), "%d second%s", seconds, seconds == 1 ? "" : "s");
168
169 return ret;
170 }
171
172 char *
173 timediff (time_t seconds)
174 {
175 static char buf[BUFSIZE];
176 long unsigned days, hours, minutes;
177
178 days = seconds / 86400;
179 seconds %= 86400;
180 hours = seconds / 3600;
181 hours %= 3600;
182 minutes = seconds / 60;
183 minutes %= 60;
184 seconds %= 60;
185
186 snprintf (buf, sizeof (buf), "%lu day%s, %lu:%02lu:%02lu", days, (days == 1) ? "" : "s", hours, minutes, (long unsigned) seconds);
187
188 return buf;
189 }
190
191 /* generate a random number, for use as a key */
192 unsigned long
193 makekey (void)
194 {
195 unsigned long k;
196
197 k = gen_rand32 () & 0x7FFFFFFF;
198
199 /* shorten or pad it to 9 digits */
200 if (k > 1000000000)
201 k = k - 1000000000;
202 if (k < 100000000)
203 k = k + 100000000;
204
205 return k;
206 }
207
208 bool
209 is_internal_client (user_t *u)
210 {
211 return (u && (!u->server || u->server == me.me));
212 }
213
214 int
215 validemail (char *email)
216 {
217 int i, valid = 1, chars = 0;
218
219 /* sane length */
220 if (strlen (email) >= EMAILLEN)
221 valid = 0;
222
223 /* make sure it has @ and . */
224 if (!strchr (email, '@') || !strchr (email, '.'))
225 valid = 0;
226
227 /* check for other bad things */
228 if (strchr (email, '\'') || strchr (email, ' ') || strchr (email, ',') || strchr (email, '$') || strchr (email, '/') || strchr (email, ';') || strchr (email, '<') || strchr (email, '>') || strchr (email, '&') || strchr (email, '"'))
229 valid = 0;
230
231 /* make sure there are at least 6 characters besides the above
232 * mentioned @ and .
233 */
234 for (i = strlen (email) - 1; i > 0; i--)
235 if (!(email[i] == '@' || email[i] == '.'))
236 chars++;
237
238 if (chars < 6)
239 valid = 0;
240
241 return valid;
242 }
243
244 bool
245 validhostmask (char *host)
246 {
247 char *p, *q;
248
249 if (strchr (host, ' '))
250 return false;
251
252 /* make sure it has ! and @ in that order and only once */
253 p = strchr (host, '!');
254 q = strchr (host, '@');
255 if (p == NULL || q == NULL || p > q || strchr (p + 1, '!') || strchr (q + 1, '@'))
256 return false;
257
258 /* XXX this NICKLEN is too long */
259 if (strlen (host) > NICKLEN + USERLEN + HOSTLEN + 1)
260 return false;
261
262 if (host[0] == ',' || host[0] == '-' || host[0] == '#' || host[0] == '@' || host[0] == '!')
263 return false;
264
265 return true;
266 }
267
268 /* send the specified type of email.
269 *
270 * u is whoever caused this to be called, the corresponding service
271 * in case of xmlrpc
272 * type is EMAIL_*, see include/tools.h
273 * mu is the recipient user
274 * param depends on type, also see include/tools.h
275 */
276 int
277 sendemail (user_t *u, int type, myuser_t *mu, char const * const param)
278 {
279 char *email, *date = NULL;
280 char cmdbuf[512], timebuf[256], to[128], from[128], subject[128];
281 FILE *out;
282 time_t t;
283 struct tm tm;
284 int pipfds[2];
285 pid_t pid;
286 int status;
287 int rc;
288 static time_t period_start = 0, lastwallops = 0;
289 static unsigned emailcount = 0;
290
291 if (u == NULL || mu == NULL)
292 return 0;
293
294 if (me.mta == NULL)
295 {
296 if (type != EMAIL_MEMO && !is_internal_client (u))
297 notice (opersvs.me ? opersvs.nick : me.name, u->nick, "Sending email is administratively disabled.");
298 return 0;
299 }
300
301 if (type == EMAIL_SETEMAIL)
302 {
303 /* special case for e-mail change */
304 metadata *md = mu->find_metadata ("private:verify:emailchg:newemail");
305
306 if (md && md->value)
307 email = md->value;
308 else /* should NEVER happen */
309 {
310 slog (LG_ERROR, "sendemail(): got email change request, but newemail unset!");
311 return 0;
312 }
313 }
314 else
315 email = mu->email;
316
317 if (NOW - period_start > me.emailtime)
318 {
319 emailcount = 0;
320 period_start = NOW;
321 }
322 emailcount++;
323 if (emailcount > me.emaillimit)
324 {
325 if (NOW - lastwallops > 60)
326 {
327 wallops (_("Rejecting email for %s[%s@%s] due to too high load (type %d to %s <%s>)"), u->nick, u->user, u->vhost, type, mu->name, email);
328 slog (LG_ERROR, "sendemail(): rejecting email for %s[%s@%s] (%s) due to too high load (type %d to %s <%s>)", u->nick, u->user, u->vhost, u->ip[0] ? u->ip : u->host, type, mu->name, email);
329 lastwallops = NOW;
330 }
331 return 0;
332 }
333
334 slog (LG_INFO, "sendemail(): email for %s[%s@%s] (%s) type %d to %s <%s>", u->nick, u->user, u->vhost, u->ip[0] ? u->ip : u->host, type, mu->name, email);
335
336 /* set up the email headers */
337 time (&t);
338 tm = *gmtime (&t);
339 strftime (timebuf, sizeof (timebuf) - 1, "%a, %d %b %Y %H:%M:%S +0000", &tm);
340
341 date = timebuf;
342
343 snprintf (from, sizeof from, "%s automailer <noreply.%s>", me.netname, me.adminemail);
344 snprintf (to, sizeof to, "%s <%s>", mu->name, email);
345
346 strlcpy (subject, me.netname, sizeof subject);
347 strlcat (subject, " ", sizeof subject);
348 if (type == EMAIL_REGISTER)
349 if (nicksvs.no_nick_ownership)
350 strlcat (subject, "Account Registration", sizeof subject);
351 else
352 strlcat (subject, "Nickname Registration", sizeof subject);
353 else if (type == EMAIL_SENDPASS || type == EMAIL_SETPASS)
354 strlcat (subject, "Password Retrieval", sizeof subject);
355 else if (type == EMAIL_SETEMAIL)
356 strlcat (subject, "Change Email Confirmation", sizeof subject);
357 else if (type == EMAIL_MEMO)
358 strlcat (subject, "New memo", sizeof subject);
359
360 /* now set up the email */
361 sprintf (cmdbuf, "%s %s", me.mta, email);
362 if (pipe (pipfds) < 0)
363 return 0;
364 switch (pid = fork ())
365 {
366 case -1:
367 return 0;
368 case 0:
369 close (pipfds[1]);
370 dup2 (pipfds[0], 0);
371 switch (fork ())
372 {
373 /* fork again to avoid zombies -- jilles */
374 case -1:
375 _exit (255);
376 case 0:
377 execl ("/bin/sh", "sh", "-c", cmdbuf, NULL);
378 _exit (255);
379 default:
380 _exit (0);
381 }
382 }
383 close (pipfds[0]);
384 waitpid (pid, &status, 0);
385 out = fdopen (pipfds[1], "w");
386
387 fprintf (out, "From: %s\n", from);
388 fprintf (out, "To: %s\n", to);
389 fprintf (out, "Subject: %s\n", subject);
390 fprintf (out, "Date: %s\n\n", date);
391
392 fprintf (out, "%s,\n\n", mu->name);
393
394 if (type == EMAIL_REGISTER)
395 {
396 fprintf (out, "In order to complete your registration, you must send the following\ncommand on IRC:\n");
397 fprintf (out, "/MSG %s VERIFY REGISTER %s %s\n\n", nicksvs.nick, mu->name, param);
398 fprintf (out, "Thank you for registering your %s on the %s IRC " "network!\n\n", (nicksvs.no_nick_ownership ? "account" : "nickname"), me.netname);
399 }
400 else if (type == EMAIL_SENDPASS)
401 {
402 fprintf (out, "Someone has requested the password for %s be sent to the\n" "corresponding email address. If you did not request this action\n" "please let us know.\n\n", mu->name);
403 fprintf (out, "The password for %s is %s. Please write this down for " "future reference.\n\n", mu->name, param);
404 }
405 else if (type == EMAIL_SETEMAIL)
406 {
407 fprintf (out, "In order to complete your email change, you must send\n" "the following command on IRC:\n");
408 fprintf (out, "/MSG %s VERIFY EMAILCHG %s %s\n\n", nicksvs.nick, mu->name, param);
409 }
410 else if (type == EMAIL_MEMO)
411 {
412 if (u->myuser != NULL)
413 fprintf (out, "You have a new memo from %s.\n\n", u->myuser->name);
414 else
415 /* shouldn't happen */
416 fprintf (out, "You have a new memo from %s (unregistered?).\n\n", u->nick);
417 fprintf (out, "%s\n\n", param);
418 }
419 else if (type == EMAIL_SETPASS)
420 {
421 fprintf (out, "In order to set a new password, you must send\n" "the following command on IRC:\n");
422 fprintf (out, "/MSG %s SETPASS %s %s <password>\nwhere <password> is your desired new password.\n\n", nicksvs.nick, mu->name, param);
423 }
424
425 fprintf (out, "Thank you for your interest in the %s IRC network.\n", me.netname);
426 if (u->server != me.me)
427 fprintf (out, "\nThis email was sent due to a command from %s[%s@%s]\nat %s.\n", u->nick, u->user, u->vhost, date);
428 fprintf (out, "If this message is spam, please contact %s\nwith a full copy.\n", me.adminemail);
429 fprintf (out, ".\n");
430 rc = 1;
431 if (ferror (out))
432 rc = 0;
433 if (fclose (out) < 0)
434 rc = 0;
435 if (rc == 0)
436 slog (LG_ERROR, "sendemail(): mta failure");
437 return rc;
438 }
439
440 /* various access level checkers */
441 bool
442 myuser_t::is_founder (mychan_t *mc) const
443 {
444 if (chanacs_find (mc, this, CA_FOUNDER))
445 return true;
446
447 return false;
448 }
449
450 bool
451 myuser_t::should_owner (mychan_t *mc) const
452 {
453 if (MC_NOOP & mc->flags)
454 return false;
455
456 if (MU_NOOP & flags)
457 return false;
458
459 if (is_founder (mc))
460 return true;
461
462 return false;
463 }
464
465 bool
466 myuser_t::should_protect (mychan_t *mc) const
467 {
468 if (MC_NOOP & mc->flags)
469 return false;
470
471 if (MU_NOOP & flags)
472 return false;
473
474 if (chanacs_find (mc, this, CA_SET))
475 return true;
476
477 return false;
478 }
479
480 bool
481 is_ircop (user_t *user)
482 {
483 if (UF_IRCOP & user->flags)
484 return true;
485
486 return false;
487 }
488
489 bool
490 is_admin (user_t *user)
491 {
492 if (UF_ADMIN & user->flags)
493 return true;
494
495 return false;
496 }
497
498 void
499 myuser_t::set_password (char const * const newpassword)
500 {
501 if (newpassword == NULL)
502 return;
503
504 /* if we can, try to crypt it */
505 if (crypto::handler::loaded)
506 {
507 flags |= MU_CRYPTPASS;
508 strlcpy (pass, crypter->crypt (newpassword, crypto::gen_salt ()), NICKLEN);
509 }
510 else
511 {
512 flags &= ~MU_CRYPTPASS; /* just in case */
513 strlcpy (pass, newpassword, NICKLEN);
514 }
515 }
516
517 bool
518 myuser_t::verify_password (char const * const password) const
519 {
520 if (password == NULL)
521 return false;
522
523 if (flags & MU_CRYPTPASS)
524 if (crypto::handler::loaded)
525 return crypto::verify_password (password, pass);
526 else
527 { /* not good! */
528 slog (LG_ERROR, "check_password(): can't check crypted password -- no crypto module!");
529 return false;
530 }
531 else
532 return (strcmp (pass, password) == 0);
533 }
534
535 char const *
536 sbytes (float x)
537 {
538 if (x > 1073741824.0)
539 return "GB";
540
541 else if (x > 1048576.0)
542 return "MB";
543
544 else if (x > 1024.0)
545 return "KB";
546
547 return "B";
548 }
549
550 float
551 bytes (float x)
552 {
553 if (x > 1073741824.0)
554 return (x / 1073741824.0);
555
556 if (x > 1048576.0)
557 return (x / 1048576.0);
558
559 if (x > 1024.0)
560 return (x / 1024.0);
561
562 return x;
563 }