ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/src/function.C
Revision: 1.3
Committed: Sat Jul 21 13:23:21 2007 UTC (16 years, 11 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.2: +1 -1 lines
Log Message:
- added rcsid to some files
- more documentation tweaks
- made most protocol commands local to phandler.C
- added ircd metadata (inspircd only for now)
- added inspircd swhois support

File Contents

# User Rev Content
1 pippijn 1.1 /*
2     * function.C: Miscellaneous functions.
3 pippijn 1.2 * Rights to this code are documented in doc/pod/license.pod.
4 pippijn 1.1 *
5     * Copyright © 2005-2007 Atheme Project (http://www.atheme.org)
6     */
7    
8 pippijn 1.3 static char const rcsid[] = "$Id$";
9 pippijn 1.1
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     */