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, 8 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

# User Rev Content
1 pippijn 1.1 /*
2     * function.C: Miscellaneous functions.
3 pippijn 1.8 *
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 pippijn 1.2 * Rights to this code are documented in doc/pod/license.pod.
10 pippijn 1.4 * Copyright © 2005-2007 Atheme Project (http://www.atheme.org)
11 pippijn 1.1 */
12    
13 pippijn 1.9 static char const rcsid[] = "$Id: function.C,v 1.8 2007-09-16 18:54:45 pippijn Exp $";
14 pippijn 1.1
15     #include "atheme.h"
16 pippijn 1.9 #include <libermyth.h>
17 pippijn 1.4 #include <ermyth/crypto.h>
18 pippijn 1.1 #include <account/mychan.h>
19     #include <account/chanacs.h>
20     #include <account/myuser.h>
21 pippijn 1.9 #include <util/random.h>
22 pippijn 1.1
23     char ch[27] = "abcdefghijklmnopqrstuvwxyz";
24    
25 pippijn 1.4 /* This function uses allocates memory.
26 pippijn 1.1 * You MUST free the result when you are done with it!
27     */
28     char *
29     gen_pw (int sz)
30     {
31     int i;
32 pippijn 1.5 char *buf = salloc<char> (sz + 1); /* padding */
33 pippijn 1.1
34     for (i = 0; i < sz; i++)
35     {
36 pippijn 1.4 buf[i] = ch[gen_rand32 () % 26];
37 pippijn 1.1 }
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 pippijn 1.4 replace (char *s, int size, char const * const oldstr, char const * const newstr)
57 pippijn 1.1 {
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 pippijn 1.4 k = gen_rand32 () & 0x7FFFFFFF;
95 pippijn 1.1
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 pippijn 1.4 char *p, *q;
145    
146 pippijn 1.1 if (strchr (host, ' '))
147     return false;
148    
149 pippijn 1.4 /* 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 pippijn 1.1 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 pippijn 1.4 sendemail (user_t *u, int type, myuser_t *mu, char const * const param)
175 pippijn 1.1 {
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 pippijn 1.6 metadata *md = mu->find_metadata ("private:verify:emailchg:newemail");
202 pippijn 1.1
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 pippijn 1.4 myuser_t::is_founder (mychan_t *mc) const
340 pippijn 1.1 {
341 pippijn 1.4 if (chanacs_find (mc, this, CA_FOUNDER))
342 pippijn 1.1 return true;
343    
344     return false;
345     }
346    
347     bool
348 pippijn 1.4 myuser_t::should_owner (mychan_t *mc) const
349 pippijn 1.1 {
350 pippijn 1.4 if (MC_NOOP & mc->flags)
351 pippijn 1.1 return false;
352    
353 pippijn 1.4 if (MU_NOOP & flags)
354 pippijn 1.1 return false;
355    
356 pippijn 1.4 if (is_founder (mc))
357 pippijn 1.1 return true;
358    
359     return false;
360     }
361    
362     bool
363 pippijn 1.4 myuser_t::should_protect (mychan_t *mc) const
364 pippijn 1.1 {
365 pippijn 1.4 if (MC_NOOP & mc->flags)
366 pippijn 1.1 return false;
367    
368 pippijn 1.4 if (MU_NOOP & flags)
369 pippijn 1.1 return false;
370    
371 pippijn 1.4 if (chanacs_find (mc, this, CA_SET))
372 pippijn 1.1 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 pippijn 1.4 myuser_t::set_password (char const * const newpassword)
397 pippijn 1.1 {
398 pippijn 1.4 if (newpassword == NULL)
399 pippijn 1.1 return;
400    
401     /* if we can, try to crypt it */
402 pippijn 1.4 if (crypto::handler::loaded)
403 pippijn 1.1 {
404 pippijn 1.4 flags |= MU_CRYPTPASS;
405     strlcpy (pass, crypter->crypt (newpassword, crypto::gen_salt ()), NICKLEN);
406 pippijn 1.1 }
407     else
408     {
409 pippijn 1.4 flags &= ~MU_CRYPTPASS; /* just in case */
410     strlcpy (pass, newpassword, NICKLEN);
411 pippijn 1.1 }
412     }
413    
414     bool
415 pippijn 1.4 myuser_t::verify_password (char const * const password) const
416 pippijn 1.1 {
417 pippijn 1.4 if (password == NULL)
418 pippijn 1.1 return false;
419    
420 pippijn 1.4 if (flags & MU_CRYPTPASS)
421     if (crypto::handler::loaded)
422     return crypto::verify_password (password, pass);
423 pippijn 1.1 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 pippijn 1.4 return (strcmp (pass, password) == 0);
430 pippijn 1.1 }
431    
432 pippijn 1.4 char const *
433 pippijn 1.1 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 pippijn 1.9
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     }