Untypo
[infodrom.org/www.zeitungsliste.de] / lib / login.inc
1 <?php
2
3 include_once('extern/rfc822.php');
4
5 function account_exists($login)
6 {
7   global $cfg;
8   global $_SESSION;
9
10   $query = sprintf("SELECT id FROM users WHERE lower(nickname) = lower('%s')",
11                    pg_escape_string($login));
12
13   if (isset($_SESSION['uid']))
14     $query .= sprintf(' AND id <> %d', $_SESSION['uid']);
15
16   $sth = db_query($query);
17
18   # Return true in case of database error
19   if ($sth === false)
20     return true;
21
22   return pg_NumRows ($sth) > 0;
23 }
24
25 function is_valid_passwd ($nickname, $passwd)
26 {
27   if (strtolower($passwd) == strtolower($nickname))
28     return 'Das Passwort darf nicht dem Usernamen entsprechen.';
29
30   if (strlen($passwd) < 4)
31     return 'Das Passwort ist zu kurz.';
32
33   if (strlen($passwd) > 15)
34     return 'Das Passwort ist zu lang.';
35
36   if (!preg_match ('/^[a-zA-Z0-9%!@#^&\*\$\\\+\.\_\-]{4,15}$/', $passwd, $matches))
37     return 'Das Passwort enthält ungültige Zeichen.';
38
39   return true;
40 }
41
42 function is_valid_username($nickname)
43 {
44   $blacklist = array('hitler','stalin');
45
46   if (!preg_match ('/^[a-zA-Z0-9\.\_\-]{4,15}$/', $nickname, $matches))
47     return false;
48
49
50   foreach ($blacklist as $badnick) {
51     if (soundex($nickname) == soundex($badnick))
52       return false;
53   }
54
55   return true;
56 }
57
58 function is_valid_realname($name)
59 {
60   if (preg_match ('/[\\\\<>"\(\)\[\]]/', $name, $matches))
61     return false;
62
63   return true;
64 }
65
66 function check_account_data()
67 {
68   global $_POST;
69   global $zlist;
70
71   $zlist['replace'] = array('nickname' => $_POST['nickname'],
72                             'realname' => $_POST['realname'],
73                             'email' => $_POST['email'],
74                             'url' => $_POST['url']);
75
76   if (!strlen($_POST['nickname']) ||
77       !strlen($_POST['email']))
78     return 'Sie müssen alle Pflichtfelder ausfüllen! Siehe Beschreibung unten.';
79
80   if (account_exists($_POST['nickname']))
81     return 'Dieser Username ist in der Zeitungsliste bereits vergeben.';
82
83   if (!is_valid_email_address($_POST['email']))
84     return 'Die angegebene Mail-Adresse ist ungültig.';
85
86   if (!empty($_POST['url'])) {
87     $_POST['url'] = fix_url($_POST['url']);
88     if (!is_valid_url($_POST['url']))
89       return 'Die angegebene Homepage ist ungültig!';
90   }
91
92   if (!empty($_POST['realname']) && !is_valid_realname($_POST['realname']))
93     return 'Der angegebene Name enthält unerlaubte Zeichen.';
94
95   return is_valid_username($_POST['nickname']);
96 }
97
98 function check_passwd()
99 {
100   global $_POST;
101
102   if (!strlen($_POST['passwd']) || !strlen($_POST['pwcopy']))
103     return 'Sie müssen alle Pflichtfelder ausfüllen! Siehe Beschreibung unten.';
104
105   if (strlen($_POST['passwd']) < 5)
106     return 'Ihr Passwort ist zu kurz, bitte verwenden Sie mindestens 5 Zeichen.';
107
108   if ($_POST['passwd'] != $_POST['pwcopy'])
109     return 'Sie haben sich beim Passwort vertippt.';
110
111   return true;
112 }
113
114 function passwd ($nickname, $passwd)
115 {
116   return md5(md5($passwd). $nickname);
117 }
118
119 function pwgen ($num)
120 {
121   $chars = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz";
122   $count = strlen($chars);
123   $pass = '';
124   srand((double)microtime()*1000000);
125
126   for ($i = 0;$i < $num; $i++) {
127     $pos = rand() % $count;
128     $pass .= substr ($chars, $pos, 1);
129   }
130
131   return $pass;
132 }
133
134 function account_activate($code)
135 {
136   global $cfg;
137
138   if (!strlen($code))
139     return 'Kein Aktivierungscode angegeben.';
140
141   $query = sprintf("SELECT uid FROM activation WHERE code = '%s'", pg_escape_string($code));
142
143   $sth = db_query($query);
144
145   if ($sth === false)
146     return 'Es ist ein Datenbankfehler aufgetreten.';
147
148   if (pg_NumRows($sth) != 1)
149     return 'Der angegebene Aktivierungscode ist ungültig!';
150
151   db_query('BEGIN TRANSACTION');
152
153   $row = pg_fetch_array($sth, 0);
154
155   $query = sprintf('UPDATE users SET status = 1 WHERE id = %d', $row['uid']);
156
157   $sth = db_query($query);
158
159   if ($sth === false) {
160     db_query('ROLLBACK');
161     return 'Es ist ein Datenbankfehler aufgetreten.';
162   }
163
164   $query = sprintf("DELETE FROM activation WHERE code = '%s'", pg_escape_string($code));
165
166   $sth = db_query($query);
167
168   if ($sth === false) {
169     db_query('ROLLBACK');
170     return 'Es ist ein Datenbankfehler aufgetreten.';
171   }
172
173   db_query('COMMIT');
174
175   return true;
176 }
177
178 function send_activation($email, $user, $code)
179 {
180   global $cfg;
181   global $_SERVER;
182
183   $url = sprintf('%sactivate.html?code=%s', $cfg['home'], urlencode($code));
184   $subject = 'Aktivierung Account Zeitungsliste';
185
186   $body = sprintf('Willkommen %s!
187
188 Sie haben einen neuen Account in der Zeitungsliste beantragt.
189
190 Um diesen zu aktivieren, geben Sie bitte folgende Adresse in
191 Ihren Browser ein:
192
193 %s
194
195 ', $user, $url);
196
197   sendmail($email, '', $subject, $body);
198
199   return true;
200 }
201
202 function process_activate()
203 {
204   global $_GET;
205
206   if (($try = account_activate($_GET['code'])) !== true) {
207     $ret = warning($try);
208     } else {
209       $ret = information('Ihr Account ist aktiviert.');
210       $ret .= '<h3>Willkommen!</h3><p class="info">'.
211         'Sie werden automatisch zur <a href="login.html">Login-Seite</a> weitergeleitet. '.
212         'Bitte melden Sie sich dort an.</p>';
213       $zlist['redirect'] = 'login.html';
214     }
215   return $ret;
216 }
217
218 function process_account_new()
219 {
220   global $cfg;
221   global $_POST;
222
223   if (($try = check_account_data()) !== true)
224     return $try;
225
226   if (($try = check_passwd()) !== true)
227     return $try;
228
229   db_query('BEGIN TRANSACTION');
230
231   $query = sprintf('INSERT INTO users (nickname,email,status,passwd,register_date%s%s)' .
232                    "VALUES ('%s','%s',0,'%s',now()%s%s)",
233                    strlen($_POST['realname'])?',realname':'',
234                    strlen($_POST['url'])?',url':'',
235                    $_POST['nickname'],
236                    $_POST['email'],
237                    passwd($_POST['nickname'], $_POST['passwd']),
238                    strlen($_POST['realname'])?",'".pg_escape_string($_POST['realname'])."'":'',
239                    strlen($_POST['url'])?",'".pg_escape_string($_POST['url'])."'":'');
240
241   $sth = db_query($query);
242
243   if ($sth === false) {
244     db_query('ROLLBACK');
245     return 'Es ist ein Datenbankfehler aufgetreten.';
246   }
247
248   $uid = db_last_id('users', 'id');
249
250   $code = md5(pwgen(8));
251
252   $query = sprintf('INSERT INTO activation (code,uid,register_date) '.
253                    "VALUES ('%s',%d,now())", $code, $uid);
254
255   $sth = db_query($query);
256
257   if ($sth === false) {
258     db_query('ROLLBACK');
259     return 'Es ist ein Datenbankfehler aufgetreten.';
260   }
261
262   if (send_activation($_POST['email'],
263                       strlen($_POST['realname'])?$_POST['realname']:$_POST['nickname'],
264                       $code) === false) {
265     db_query('ROLLBACK');
266     return 'Fehler beim Versenden der Aktivierungsmail.';
267   }
268
269   db_query('COMMIT');
270
271   return true;
272 }
273
274 function update_account()
275 {
276   global $_POST;
277   global $_SESSION;
278   global $zlist;
279
280   $query = sprintf("UPDATE users SET nickname='%s',realname='%s',email='%s',url='%s' ".
281                    'WHERE id = %d',
282                    $_POST['nickname'],
283                    pg_escape_string($_POST['realname']),
284                    $_POST['email'],
285                    pg_escape_string($_POST['url']),
286                    $_SESSION['uid']);
287
288   db_query($query);
289
290   if ($_SESSION['nickname'] != $_POST['nickname'])
291     $zlist['newpass'] = true;
292
293   $_SESSION['nickname'] = $_POST['nickname'];
294   $_SESSION['email'] = $_POST['email'];
295   $_SESSION['homepage'] = $_POST['url'];
296   $_SESSION['realname'] = $_POST['realname'];
297 }
298
299 function update_passwd()
300 {
301   global $_POST;
302   global $_SESSION;
303
304   $hash = passwd($_SESSION['nickname'], $_POST['passwd']);
305
306   $query = sprintf("UPDATE users SET passwd='%s' WHERE id = %d",
307                    $hash, $_SESSION['uid']);
308
309   db_query($query);
310 }
311
312 function checkpass($nickname, $passwd)
313 {
314   global $cfg;
315
316   if (empty($nickname) || empty($passwd))
317     return false;
318
319   $query = sprintf("SELECT passwd,nickname FROM users ".
320                    "WHERE lower(nickname) = lower('%s') AND status = 1",
321                    pg_escape_string($nickname));
322
323   $sth = db_query($query);
324
325   if ($sth === false)
326     return false;
327
328   if (pg_NumRows($sth) == 0)
329     return false;
330
331   $row = pg_fetch_array($sth, 0);
332
333   $hash = passwd($row['nickname'], $passwd);
334
335   if (strcmp($row[0], $hash) != 0)
336     return false;
337
338   return true;
339 }
340
341 function login_user($nickname, $passwd)
342 {
343   global $cfg;
344   global $_SERVER;
345   global $_POST;
346   global $_SESSION;
347
348   $query = sprintf("SELECT id,nickname,realname,email,url,passwd FROM users " .
349                    "WHERE lower(nickname) = lower('%s') AND status = 1",
350                    pg_escape_string($nickname));
351
352   $sth = db_query($query);
353
354   if ($sth === false)
355     return false;
356
357   if (pg_NumRows($sth) == 0)
358     return false;
359
360   $row = pg_fetch_array($sth, 0);
361
362   $hash = passwd($row['nickname'], $passwd);
363
364   if (strcmp($row['passwd'], $hash) != 0)
365     return false;
366
367   session_name($cfg['session']);
368   session_start();
369
370   $_SESSION['REMOTE_ADDR'] = $_SERVER["REMOTE_ADDR"];
371   $_SESSION['uid'] = $row['id'];
372   $_SESSION['nickname'] = $row['nickname'];
373   $_SESSION['javascript'] = $_POST['js']=='1'?true:false;
374   $_SESSION['email'] = $row['email'];
375   $_SESSION['homepage'] = $row['url'];
376   $_SESSION['realname'] = $row['realname'];
377
378   $query = sprintf('UPDATE users SET last_login = now() WHERE id = %d', $_SESSION['uid']);
379   db_query($query);
380
381   $query = sprintf('INSERT INTO online (uid,activity) VALUES (%d,now())', $_SESSION['uid']);
382   db_query($query);
383
384   session_update();
385   $_SESSION["lastupdate"] = time();
386
387   return true;
388 }
389
390 function login_sendnew($nickname)
391 {
392   global $cfg;
393
394   if (!strlen($nickname))
395     return 'Kein Username angegeben!';
396
397   $query = 'SELECT id,email,nickname,realname FROM users WHERE status = 1 AND ';
398   if (strstr($nickname, '@') === false)
399     $query .= sprintf("lower(nickname) = lower('%s')", pg_escape_string($nickname));
400   else
401     $query .= sprintf("lower(email) = lower('%s')", pg_escape_string($nickname));
402
403   $sth = db_query($query);
404
405   if ($sth === false)
406     return 'Es ist ein Datenbankfehler aufgetreten.';
407
408   if (pg_NumRows($sth) == 0)
409     return 'Der angegebene Username ist im System nicht bekannt.';
410
411   $row = pg_fetch_array($sth, 0);
412
413   $passwd = pwgen(8);
414
415   $query = sprintf("UPDATE users SET passwd = '%s' WHERE id = %d",
416                    passwd($row['nickname'], $passwd), $row['id']);
417
418   $sth = db_query($query);
419
420   if ($sth === false)
421     return 'Es ist ein Datenbankfehler aufgetreten.';
422
423   $header = array();
424   $header[] = 'From: ' . $cfg['from'];
425   $header[] = sprintf('To: %s <%s>',
426                       strlen($row['realname'])?$row['realname']:$row['nickname'],
427                       $row['email']);
428   $header[] = 'MIME-Version: 1.0';
429   $header[] = 'Content-type: text/plain; charset=utf-8';
430
431   $subject = 'Zeitungsliste: Ihr Neues Passwort';
432
433   $body = sprintf('Moin %s!
434
435 Sie oder jemand anderes hat ein neues Passwort für Ihren Account auf
436 <%s> angefordert.
437
438 Das alte Passwort verliert damit seine Gültigkeit.  Das System hat
439 ein neues Passwort für Sie berechnet und eingetragen.
440
441 Das neue Passwort lautet %s
442
443 Bitte ändern Sie es beim nächsten Einloggen.
444 ',
445                   strlen($row['realname'])?$row['realname']:$row['nickname'],
446                   $cfg['home'], $passwd);
447
448   $sig = load_template('signature', array());
449   if (strlen($sig))
450     $body .= $sig;
451
452   if (mail ($row['email'], $subject, $body, implode("\n", $header)) === false)
453     return 'Es ist ein Fehler beim Versand der Mail aufgetreten.';
454
455   return true;
456 }
457
458 function process_login_request()
459 {
460   global $_POST;
461
462   if (isset($_POST['new'])) {
463     $try = process_account_new();
464     if ($try === true)
465       return information('Ihr neuer Account wurde angelegt.  Die Aktivierungsmail ist unterwegs.');
466     else {
467       $body = warning($try);
468       $replace = array('nickname' => $_POST['nickname'],
469                        'realname' => $_POST['realname'],
470                        'email' => $_POST['email'],
471                        'url' => $_POST['url']);
472       $body .= load_template('login_new.html', $replace);
473       return $body;
474     }
475   } elseif (isset($_POST['sendnew'])) {
476     $try = login_sendnew($_POST['nickname']);
477     if ($try === true) {
478       $ret = information('Ein neues Passwort wird Ihnen per Mail zugeschickt');
479       $ret .= '<p>Bitte melden Sie sich mit diesem an und ändern Sie das Passwort '.
480         'über den Menüpunkt "Einstellungen".</p>';
481       return $ret;
482     } else {
483       $body = warning($try);
484       $body .= load_template('login.html');
485       return $body;
486     }
487   } else {
488     if (empty($_POST['nickname'])) {
489       $body = warning('Sie haben keinen Usernamen angegeben!');
490     } elseif (empty($_POST['passwd'])) {
491       $body = warning('Sie haben kein Passwort angegeben!');
492     } else {
493       $try = login_user($_POST['nickname'], $_POST['passwd']);
494       if ($try === true)
495         return true;
496       else
497         $body = warning('Das angegebene Passwort ist ungültig!');
498     }
499
500     $replace = array('nickname' => $_POST['nickname']);
501     $body .= load_template('login.html', $replace);
502     return $body;
503   }
504 }
505
506 function last_activity()
507 {
508   global $cfg;
509   global $_SESSION;
510
511   $query = sprintf("SELECT DISTINCT topics.id,topics.topic FROM article " .
512                    "JOIN topics ON article.topic=topics.id " .
513                    "WHERE uid = %d AND article.created > now() - interval'7 days'",
514                    $_SESSION['uid']);
515
516   $sth = db_query($query);
517
518   if ($sth === false || pg_NumRows($sth) == 0)
519     return false;
520
521   $ret = '<h3>Sie haben an folgenden Diskussionen teilgenommen</h3>';
522   $ret .= '<p><ul class="gold">';
523
524   for ($i=0; $i < pg_NumRows($sth); $i++) {
525     $row = pg_fetch_array($sth, $i);
526
527     $ret .= sprintf('<li><a href="%stopic/%d.html">%s</a></li>',
528                     $cfg['basedir'],
529                     $row['id'], $row['topic']);
530   }
531   $ret .= '</ul></p>';
532
533   return $ret;
534 }
535
536 function process_login()
537 {
538   global $_GET;
539   global $_SERVER;
540   global $_SESSION;
541
542   if (isset($_GET['from'])) {
543     if ($_GET['from'] == 'article')
544       $ret .= warning('Um an einer Diskussion teilzunehmen, müssen Sie angemeldet sein.');
545     elseif ($_GET['from'] == 'zeitung')
546       $ret .= warning('Um eine neue Diskussion zu beginnen, müssen Sie angemeldet sein.');
547     elseif ($_GET['from'] == 'new')
548       $ret .= warning('Um eine Zeitung hinzuzufügen, müssen Sie angemeldet sein.');
549     elseif ($_GET['from'] == 'tags')
550       $ret .= warning('Um Tags zu einer Zeitung zu verwalten, müssen Sie angemeldet sein.');
551     elseif ($_GET['from'] == 'edit')
552       $ret .= warning('Um eine Zeitung zu bearbeiten, müssen Sie angemeldet sein.');
553     elseif ($_GET['from'] == 'session')
554       $ret .= warning('Ihre Session ist abgelaufen.  Bitte melden Sie sich erneut an.');
555     $ret .= load_template('login.html');
556   } elseif (isset($_GET['new'])) {
557     $ret = load_template('login_new.html');
558   } elseif ($_SERVER['REQUEST_METHOD'] == 'GET') {
559     $ret = load_template('login.html');
560   } else {
561     $try = process_login_request();
562     if ($try === true) {
563       $ret = information('Hallo '.$_SESSION['nickname'].'! Sie sind jetzt angemeldet.');
564       $try = last_activity();
565       if ($try !== false)
566         $ret .= $try;
567       else
568         $ret .= load_template('main.html');
569     } else
570       $ret = $try;
571   }
572
573   return $ret;
574 }
575
576 function process_passwd()
577 {
578   global $_SESSION;
579   global $_SERVER;
580   global $zlist;
581   global $cfg;
582
583   if (!isset($_SESSION['uid'])) {
584     $ret = warning('Sie sind nicht angemeldet.  Sie werden gleich zur Anmeldung weitergeleitet.');
585     $zlist['redirect'] = 'login.html';
586   } else {
587     if ($_SERVER['REQUEST_METHOD'] == 'GET') {
588       $ret .= load_javascript('passwd.js') . load_template('passwd.html', array('nickname' => $_SESSION['nickname']));
589     } elseif ($_SERVER['REQUEST_METHOD'] == 'POST') {
590       if (($try = check_passwd()) !== true) {
591         $ret = warning($try);
592         $ret .= load_javascript('passwd.js') . load_template('passwd.html', array('nickname' => $_SESSION['nickname']));
593       } else {
594         update_passwd();
595         $ret .= information('Ihr neues Passwort ist eingetragen.  Sie werden zur Hauptseite weitergeleitet.');
596         $zlist['redirect'] = '';
597       }
598     }
599   }
600
601   return $ret;
602 }
603
604 function logout()
605 {
606   session_invalidate();
607 }
608
609 function process_options()
610 {
611   global $_SESSION;
612   global $_SERVER;
613   global $_POST;
614   global $zlist;
615   global $cfg;
616
617   $ok = false;
618   $ret = '';
619   if (!isset($_SESSION['uid'])) {
620     $ret .= warning('Sie sind nicht angemeldet.  Sie werden gleich zur Anmeldung weitergeleitet.');
621     $zlist['redirect'] = 'login.html';
622   } else {
623     if ($_SERVER['REQUEST_METHOD'] == 'POST') {
624       if (($try = check_account_data()) !== true)
625         $ret .= warning($try);
626       else {
627         update_account();
628         $ret .= information('Ihre persönlichen Daten wurden geändert.  Sie werden zur Hauptseite weitergeleitet.');
629         $ok = true;
630         $zlist['redirect'] = '';
631         if ($zlist['newpass']) {
632           $try = login_sendnew($_POST['nickname']);
633           if ($try === true) {
634             $ret .= '<p>Aus technischen Gründen ist es erforderlich Ihnen ein neues Passwort zu geben. '.
635               'Dieses wird Ihnen per Mail an die im System gespeicherte Mail-Adresse geschickt. '.
636               'Diese Session ist von dieser Änderung nicht betroffen, Sie können daher Ihr Passwort '.
637               'direkt über den Menüpunkt "Passwort" ändern und die Mail ignorieren. '.
638               'Ansonsten melden Sie sich bitte beim nächsten Mal mit dem neuen Passwort an und ändern Sie es.</p>';
639           }
640         }
641       }
642     } else
643       $zlist['replace'] = array('nickname' => $_SESSION['nickname'],
644                                 'realname' => $_SESSION['realname'],
645                                 'email' => $_SESSION['email'],
646                                 'url' => $_SESSION['url']);
647     if (!$ok)
648       $ret .= load_javascript('options.js') . load_template('options.html', $zlist['replace']);
649   }
650
651   return $ret;
652 }
653
654 function ajax_passwd_check()
655 {
656   global $_POST;
657
658   return checkpass($_POST['nickname'], $_POST['passwd']);
659 }
660
661 function ajax_nickname_check()
662 {
663   global $_POST;
664
665   return account_exists($_POST['nickname']);
666 }
667
668 ?>