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