ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/src/function.C
Revision: 1.9
Committed: Sat Sep 22 14:27:30 2007 UTC (16 years, 7 months ago) by pippijn
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.8: +34 -106 lines
Log Message:
split up ermyth into ermyth-modules, libermyth (currently just ermyth-util) and ermyth-core

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