ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/src/function.C
Revision: 1.1
Committed: Thu Jul 19 08:24:57 2007 UTC (16 years, 10 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Log Message:
initial import. the most important changes since Atheme are:
- fixed many memory leaks
- fixed many bugs
- converted to C++ and use more STL containers
- added a (not very enhanced yet) perl module
- greatly improved XML-RPC speed
- added a JSON-RPC module with code from json-cpp
- added a valgrind memcheck module to operserv
- added a more object oriented base64 implementation
- added a specialised unit test framework
- improved stability
- use gettimeofday() if available
- reworked adding/removing commands
- MemoServ IGNORE DEL can now remove indices

File Contents

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