ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/src/function.C
Revision: 1.5
Committed: Thu Aug 30 19:56:25 2007 UTC (16 years, 9 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.4: +3 -3 lines
Log Message:
- put faultcodes into their own namespace
- removed old files
- limited header garbage in atheme.h
- macros to inline bools for connection_t::is_*
- put some connection_t functions into the connection_t class

File Contents

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