|
1 <?php |
|
2 /* |
|
3 Plugin Name: User control panel |
|
4 Plugin URI: http://www.enanocms.org/ |
|
5 Description: Provides the page Special:Preferences. |
|
6 Author: Dan Fuhry |
|
7 Version: 1.0 |
|
8 Author URI: http://www.enanocms.org/ |
|
9 */ |
|
10 |
|
11 /* |
|
12 * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between |
|
13 * Version 1.0 release candidate 2 |
|
14 * Copyright (C) 2006-2007 Dan Fuhry |
|
15 * |
|
16 * This program is Free Software; you can redistribute it and/or modify it under the terms of the GNU General Public License |
|
17 * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. |
|
18 * |
|
19 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied |
|
20 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. |
|
21 */ |
|
22 |
|
23 $userprefs_menu = Array(); |
|
24 $userprefs_menu_links = Array(); |
|
25 function userprefs_menu_add($section, $text, $link) |
|
26 { |
|
27 global $userprefs_menu; |
|
28 if ( is_array($userprefs_menu[$section]) ) |
|
29 { |
|
30 $userprefs_menu[$section][] = Array( |
|
31 'text' => $text, |
|
32 'link' => $link |
|
33 ); |
|
34 } |
|
35 else |
|
36 { |
|
37 $userprefs_menu[$section] = Array(Array( |
|
38 'text' => $text, |
|
39 'link' => $link |
|
40 )); |
|
41 } |
|
42 } |
|
43 |
|
44 function userprefs_menu_html() |
|
45 { |
|
46 global $userprefs_menu; |
|
47 global $userprefs_menu_links; |
|
48 |
|
49 $html = ''; |
|
50 $quot = '"'; |
|
51 |
|
52 foreach ( $userprefs_menu as $section => $buttons ) |
|
53 { |
|
54 $html .= ( isset($userprefs_menu_links[$section]) ) ? "<a href={$quot}{$userprefs_menu_links[$section]}{$quot}>{$section}</a>\n " : "<a>{$section}</a>\n "; |
|
55 $html .= "<ul>\n "; |
|
56 foreach ( $buttons as $button ) |
|
57 { |
|
58 $html .= " <li><a href={$quot}{$button['link']}{$quot}>{$button['text']}</a></li>\n "; |
|
59 } |
|
60 $html .= "</ul>\n "; |
|
61 } |
|
62 |
|
63 return $html; |
|
64 } |
|
65 |
|
66 function userprefs_show_menu() |
|
67 { |
|
68 echo '<div class="menu_nojs"> |
|
69 ' . userprefs_menu_html() . ' |
|
70 <span class="menuclear"></span> |
|
71 </div> |
|
72 <br /> |
|
73 '; |
|
74 } |
|
75 |
|
76 function userprefs_menu_init() |
|
77 { |
|
78 global $db, $session, $paths, $template, $plugins; // Common objects |
|
79 global $userprefs_menu_links; |
|
80 |
|
81 userprefs_menu_add('Profile/membership', 'Edit e-mail address and password', makeUrlNS('Special', 'Preferences/EmailPassword')); |
|
82 userprefs_menu_add('Profile/membership', 'Edit signature', makeUrlNS('Special', 'Preferences/Signature')); |
|
83 userprefs_menu_add('Profile/membership', 'Edit public profile', makeUrlNS('Special', 'Preferences/Profile')); |
|
84 userprefs_menu_add('Private messages', 'Inbox', makeUrlNS('Special', 'PrivateMessages/Folder/Inbox')); |
|
85 userprefs_menu_add('Private messages', 'Outbox', makeUrlNS('Special', 'PrivateMessages/Folder/Outbox')); |
|
86 userprefs_menu_add('Private messages', 'Sent items', makeUrlNS('Special', 'PrivateMessages/Folder/Sent')); |
|
87 userprefs_menu_add('Private messages', 'Drafts', makeUrlNS('Special', 'PrivateMessages/Folder/Drafts')); |
|
88 userprefs_menu_add('Private messages', 'Archive', makeUrlNS('Special', 'PrivateMessages/Folder/Archive')); |
|
89 |
|
90 $userprefs_menu_links['Profile/membership'] = makeUrlNS('Special', 'Preferences'); |
|
91 $userprefs_menu_links['Private messages'] = makeUrlNS('Special', 'PrivateMessages'); |
|
92 |
|
93 $code = $plugins->setHook('userprefs_jbox'); |
|
94 foreach ( $code as $cmd ) |
|
95 { |
|
96 eval($cmd); |
|
97 } |
|
98 } |
|
99 |
|
100 $plugins->attachHook('session_started', 'userprefs_menu_init();'); |
|
101 |
|
102 function page_Special_Preferences() |
|
103 { |
|
104 global $db, $session, $paths, $template, $plugins; // Common objects |
|
105 |
|
106 // We need a login to continue |
|
107 if ( !$session->user_logged_in ) |
|
108 redirect(makeUrlNS('Special', 'Login/' . $paths->page), 'Login required', 'You need to be logged in to access this page. Please wait while you are redirected to the login page.'); |
|
109 |
|
110 // User ID - later this will be specified on the URL, but hardcoded for now |
|
111 $uid = intval($session->user_id); |
|
112 |
|
113 // Instanciate the AES encryptor |
|
114 $aes = new AESCrypt(AES_BITS, AES_BLOCKSIZE); |
|
115 |
|
116 // Basic user info |
|
117 $q = $db->sql_query('SELECT username, password, email, real_name, signature, theme, style FROM '.table_prefix.'users WHERE user_id='.$uid.';'); |
|
118 if ( !$q ) |
|
119 $db->_die(); |
|
120 |
|
121 $row = $db->fetchrow(); |
|
122 $db->free_result(); |
|
123 |
|
124 $section = $paths->getParam(0); |
|
125 if ( !$section ) |
|
126 { |
|
127 $section = 'Home'; |
|
128 } |
|
129 |
|
130 $errors = ''; |
|
131 |
|
132 switch ( $section ) |
|
133 { |
|
134 case 'EmailPassword': |
|
135 // Require elevated privileges (well sortof) |
|
136 if ( $session->auth_level < USER_LEVEL_CHPREF ) |
|
137 { |
|
138 redirect(makeUrlNS('Special', 'Login/' . $paths->fullpage, 'level=' . USER_LEVEL_CHPREF, true), 'Authentication required', 'You need to re-authenticate to access this page.', 0); |
|
139 } |
|
140 |
|
141 if ( isset($_POST['submit']) ) |
|
142 { |
|
143 $email_changed = false; |
|
144 // First do the e-mail address |
|
145 if ( strlen($_POST['newemail']) > 0 ) |
|
146 { |
|
147 switch('foo') // Same reason as in the password code... |
|
148 { |
|
149 case 'foo': |
|
150 if ( $_POST['newemail'] != $_POST['newemail_conf'] ) |
|
151 { |
|
152 $errors .= '<div class="error-box">The e-mail addresses you entered did not match.</div>'; |
|
153 break; |
|
154 } |
|
155 } |
|
156 $q = $db->sql_query('SELECT password FROM '.table_prefix.'users WHERE user_id='.$session->user_id.';'); |
|
157 if ( !$q ) |
|
158 $db->_die(); |
|
159 $row = $db->fetchrow(); |
|
160 $db->free_result(); |
|
161 $old_pass = $aes->decrypt($row['password'], $session->private_key, ENC_HEX); |
|
162 |
|
163 $new_email = $_POST['newemail']; |
|
164 |
|
165 $result = $session->update_user($session->user_id, false, $old_pass, false, $new_email); |
|
166 if ( $result != 'success' ) |
|
167 { |
|
168 die_friendly('Error updating e-mail address', '<p>Session API returned error: ' . $result . '</p>'); |
|
169 } |
|
170 $email_changed = true; |
|
171 } |
|
172 // Obtain password |
|
173 if ( $_POST['use_crypt'] == 'yes' && !empty($_POST['crypt_data']) ) |
|
174 { |
|
175 $key = $session->fetch_public_key($_POST['crypt_key']); |
|
176 if ( !$key ) |
|
177 die('Can\'t lookup key'); |
|
178 $key = hexdecode($key); |
|
179 $newpass = $aes->decrypt($_POST['crypt_data'], $key, ENC_HEX); |
|
180 // At this point we know if we _want_ to change the password... |
|
181 |
|
182 // We can't check the password to see if it matches the confirmation |
|
183 // because the confirmation was destroyed during the encryption. I figured |
|
184 // this wasn't a big deal because if the encryption worked, then either |
|
185 // the Javascript validated it or the user hacked the form. In the latter |
|
186 // case, if he's smart enough to hack the encryption code, he's probably |
|
187 // smart enough to remember his password. |
|
188 |
|
189 if ( strlen($newpass) > 0 ) |
|
190 { |
|
191 // Perform checks |
|
192 if ( strlen($newpass) < 6 ) |
|
193 $errors .= '<div class="error-box">Password must be at least 6 characters. You hacked my script, darn you!</div>'; |
|
194 // Encrypt new password |
|
195 $newpass_enc = $aes->encrypt($newpass, $session->private_key, ENC_HEX); |
|
196 // Perform the swap |
|
197 $q = $db->sql_query('UPDATE '.table_prefix.'users SET password=\'' . $newpass_enc . '\' WHERE user_id=' . $session->user_id . ';'); |
|
198 if ( !$q ) |
|
199 $db->_die(); |
|
200 // Log out and back in |
|
201 $username = $session->username; |
|
202 $session->logout(); |
|
203 if ( $email_changed ) |
|
204 { |
|
205 if ( getConfig('account_activation') == 'user' ) |
|
206 { |
|
207 redirect(makeUrl(getConfig('main_page')), 'Profile changed', 'Your password and e-mail address have been changed. Since e-mail activation is required on this site, you will need to re-activate your account to continue. An e-mail has been sent to the new e-mail address with an activation link. You must click that link in order to log in again.', 19); |
|
208 } |
|
209 else if ( getConfig('account_activation') == 'admin' ) |
|
210 { |
|
211 redirect(makeUrl(getConfig('main_page')), 'Profile changed', 'Your password and e-mail address have been changed. Since administrative activation is requires on this site, a request has been sent to the administrators to activate your account for you. You will not be able to use your account until it is activated by an administrator.', 19); |
|
212 } |
|
213 } |
|
214 $session->login_without_crypto($session->username, $newpass); |
|
215 redirect(makeUrlNS('Special', 'Preferences'), 'Password changed', 'Your password has been changed, and you will now be redirected back to the user control panel.', 4); |
|
216 } |
|
217 } |
|
218 else |
|
219 { |
|
220 switch('foo') // allow breaking out of our section...i can't wait until PHP6 (goto support!) |
|
221 { |
|
222 case 'foo': |
|
223 $pass = $_POST['newpass']; |
|
224 if ( $pass != $_POST['newpass_conf'] ) |
|
225 { |
|
226 $errors .= '<div class="error-box">The passwords you entered did not match</div>'; |
|
227 break; |
|
228 } |
|
229 |
|
230 if ( $email_changed ) |
|
231 { |
|
232 if ( getConfig('account_activation') == 'user' ) |
|
233 { |
|
234 redirect(makeUrl(getConfig('main_page')), 'Profile changed', 'Your e-mail address has been changed. Since e-mail activation is required on this site, you will need to re-activate your account to continue. An e-mail has been sent to the new e-mail address with an activation link. You must click that link in order to log in again.', 19); |
|
235 } |
|
236 else if ( getConfig('account_activation') == 'admin' ) |
|
237 { |
|
238 redirect(makeUrl(getConfig('main_page')), 'Profile changed', 'Your e-mail address has been changed. Since administrative activation is requires on this site, a request has been sent to the administrators to activate your account for you. You will not be able to use your account until it is activated by an administrator.', 19); |
|
239 } |
|
240 else |
|
241 { |
|
242 redirect(makeUrlNS('Special', 'Preferences'), 'Password changed', 'Your e-mail address has been changed, and you will now be redirected back to the user control panel.', 4); |
|
243 } |
|
244 } |
|
245 |
|
246 return; |
|
247 } |
|
248 } |
|
249 } |
|
250 $template->tpl_strings['PAGE_NAME'] = 'Change E-mail Address or Password'; |
|
251 break; |
|
252 case 'Signature': |
|
253 $template->tpl_strings['PAGE_NAME'] = 'Editing signature'; |
|
254 break; |
|
255 case 'Profile': |
|
256 $template->tpl_strings['PAGE_NAME'] = 'Editing public profile'; |
|
257 break; |
|
258 } |
|
259 |
|
260 $template->header(); |
|
261 |
|
262 // Output the menu |
|
263 // This is not templatized because it conforms to the jBox menu standard. |
|
264 |
|
265 userprefs_show_menu(); |
|
266 |
|
267 switch ( $section ) |
|
268 { |
|
269 case 'Home': |
|
270 global $email; |
|
271 $user_page = '<a href="' . makeUrlNS('User', str_replace(' ', '_', $session->username)) . '">user page</a> <sup>(<a href="' . makeUrlNS('User', str_replace(' ', '_', $session->username)) . '#do:comments">comments</a>)</sup>'; |
|
272 $site_admin = $email->encryptEmail(getConfig('contact_email'), '', '', 'administrator'); |
|
273 echo "<h3 style='margin-top: 0;'>$session->username, welcome to your control panel</h3>"; |
|
274 echo "<p>Here you can make changes to your profile, view statistics on yourself on this site, and set your preferences.</p> |
|
275 <p>If you have not already done so, you are encouraged to make a $user_page and tell the other members of this site a little about yourself.</p> |
|
276 <p>Use the menu at the top to navigate around. If you have any questions, you may contact the $site_admin."; |
|
277 break; |
|
278 case 'EmailPassword': |
|
279 |
|
280 echo '<form action="' . makeUrlNS('Special', 'Preferences/EmailPassword') . '" method="post" onsubmit="return runEncryption();" name="empwform" >'; |
|
281 |
|
282 // Password change form |
|
283 $pubkey = $session->rijndael_genkey(); |
|
284 |
|
285 echo '<fieldset> |
|
286 <legend>Change password</legend> |
|
287 Type a new password:<br /> |
|
288 <input type="password" name="newpass" size="30" tabindex="1" /> |
|
289 <br /> |
|
290 <br /> |
|
291 Type the password again to confirm:<br /> |
|
292 <input type="password" name="newpass_conf" size="30" tabindex="2" /> |
|
293 </fieldset><br /> |
|
294 <fieldset> |
|
295 <legend>Change e-mail address</legend> |
|
296 New e-mail address:<br /> |
|
297 <input type="text" name="newemail" size="30" tabindex="3" /> |
|
298 <br /> |
|
299 <br /> |
|
300 Confirm e-mail address:<br /> |
|
301 <input type="text" name="newemail_conf" size="30" tabindex="4" /> |
|
302 </fieldset> |
|
303 <input type="hidden" name="use_crypt" value="no" /> |
|
304 <input type="hidden" name="crypt_key" value="' . $pubkey . '" /> |
|
305 <input type="hidden" name="crypt_data" value="" /> |
|
306 <br /> |
|
307 <div style="text-align: right;"><input type="submit" name="submit" value="Save Changes" tabindex="5" /></div>'; |
|
308 |
|
309 echo '</form>'; |
|
310 |
|
311 // ENCRYPTION CODE |
|
312 ?> |
|
313 <script type="text/javascript"> |
|
314 disableJSONExts(); |
|
315 str = ''; |
|
316 for(i=0;i<keySizeInBits/4;i++) str+='0'; |
|
317 var key = hexToByteArray(str); |
|
318 var pt = hexToByteArray(str); |
|
319 var ct = rijndaelEncrypt(pt, key, "ECB"); |
|
320 var ct = byteArrayToHex(ct); |
|
321 switch(keySizeInBits) |
|
322 { |
|
323 case 128: |
|
324 v = '66e94bd4ef8a2c3b884cfa59ca342b2e'; |
|
325 break; |
|
326 case 192: |
|
327 v = 'aae06992acbf52a3e8f4a96ec9300bd7aae06992acbf52a3e8f4a96ec9300bd7'; |
|
328 break; |
|
329 case 256: |
|
330 v = 'dc95c078a2408989ad48a21492842087dc95c078a2408989ad48a21492842087'; |
|
331 break; |
|
332 } |
|
333 var aes_testpassed = ( ct == v && md5_vm_test() ); |
|
334 function runEncryption() |
|
335 { |
|
336 var frm = document.forms.empwform; |
|
337 if ( frm.newpass.value.length < 1 ) |
|
338 return true; |
|
339 if(aes_testpassed) |
|
340 { |
|
341 frm.use_crypt.value = 'yes'; |
|
342 var cryptkey = frm.crypt_key.value; |
|
343 frm.crypt_key.value = hex_md5(cryptkey); |
|
344 cryptkey = hexToByteArray(cryptkey); |
|
345 if(!cryptkey || ( ( typeof cryptkey == 'string' || typeof cryptkey == 'object' ) ) && cryptkey.length != keySizeInBits / 8 ) |
|
346 { |
|
347 frm.submit.disabled = true; |
|
348 len = ( typeof cryptkey == 'string' || typeof cryptkey == 'object' ) ? '\nLen: '+cryptkey.length : ''; |
|
349 alert('The key is messed up\nType: '+typeof(cryptkey)+len); |
|
350 } |
|
351 } |
|
352 pass1 = frm.newpass.value; |
|
353 pass2 = frm.newpass_conf.value; |
|
354 if ( pass1 != pass2 ) |
|
355 { |
|
356 alert('The passwords you entered do not match.'); |
|
357 return false; |
|
358 } |
|
359 if ( pass1.length < 6 && pass1.length > 0 ) |
|
360 { |
|
361 alert('The new password must be 6 characters or greater in length.'); |
|
362 return false; |
|
363 } |
|
364 if(aes_testpassed) |
|
365 { |
|
366 pass = frm.newpass.value; |
|
367 pass = stringToByteArray(pass); |
|
368 cryptstring = rijndaelEncrypt(pass, cryptkey, 'ECB'); |
|
369 if(!cryptstring) |
|
370 { |
|
371 return false; |
|
372 } |
|
373 cryptstring = byteArrayToHex(cryptstring); |
|
374 frm.crypt_data.value = cryptstring; |
|
375 frm.newpass.value = ""; |
|
376 frm.newpass_conf.value = ""; |
|
377 } |
|
378 return true; |
|
379 } |
|
380 </script> |
|
381 <?php |
|
382 |
|
383 break; |
|
384 case 'Signature': |
|
385 if ( isset($_POST['new_sig']) ) |
|
386 { |
|
387 $sig = $_POST['new_sig']; |
|
388 $sig = RenderMan::preprocess_text($sig, true, false); |
|
389 $sql_sig = $db->escape($sig); |
|
390 $q = $db->sql_query('UPDATE '.table_prefix.'users SET signature=\'' . $sql_sig . '\' WHERE user_id=' . $session->user_id . ';'); |
|
391 if ( !$q ) |
|
392 $db->_die(); |
|
393 $session->signature = $sig; |
|
394 echo '<div class="info-box" style="margin: 0 0 10px 0;">Your signature has been saved.</div>'; |
|
395 } |
|
396 echo '<form action="'.makeUrl($paths->fullpage).'" method="post">'; |
|
397 echo $template->tinymce_textarea('new_sig', $session->signature); |
|
398 echo '<input type="submit" value="Save signature" />'; |
|
399 echo '</form>'; |
|
400 break; |
|
401 case "Profile": |
|
402 if ( isset($_POST['submit']) ) |
|
403 { |
|
404 $real_name = htmlspecialchars($_POST['real_name']); |
|
405 $real_name = $db->escape($real_name); |
|
406 $q = $db->sql_query('UPDATE '.table_prefix."users SET real_name='$real_name' WHERE user_id=$session->user_id;"); |
|
407 if ( !$q ) |
|
408 $db->_die(); |
|
409 |
|
410 echo '<div class="info-box" style="margin: 0 0 10px 0;">Your profile has been updated.</div>'; |
|
411 } |
|
412 echo '<form action="'.makeUrl($paths->fullpage).'" method="post">'; |
|
413 ?> |
|
414 <div class="tblholder"> |
|
415 <table border="0" cellspacing="1" cellpadding="4"> |
|
416 <tr> |
|
417 <th colspan="2">Your public profile</th> |
|
418 </tr> |
|
419 <tr> |
|
420 <td colspan="2" class="row3">Please note that all of the information you enter here will be <b>publicly viewable.</b> All of the fields on this page are optional and may be left blank if you so desire.</td> |
|
421 </tr> |
|
422 <tr> |
|
423 <td class="row2" style="width: 50%;">Real name:</td> |
|
424 <td class="row1" style="width: 50%;"><input type="text" name="real_name" value="<?php echo $session->real_name; ?>" size="30" /></td> |
|
425 </tr> |
|
426 <tr> |
|
427 <td class="row2">Change theme:</td> |
|
428 <td class="row1">If you don't like the look of the site, need a visual break, or are just curious, we might have some different themes for you to try out! <a href="<?php echo makeUrlNS('Special', 'ChangeStyle/' . $paths->page); ?>" onclick="ajaxChangeStyle(); return false;">Change my theme...</a></td> |
|
429 </tr> |
|
430 <tr> |
|
431 <td colspan="2" class="row3"><small>More is coming soon - planned fields include AOL, WLM, Yahoo, and XMPP messenger fields, allow public display of e-mail address, allow private messages from users not on your buddy list, homepage, occupation, and location.</small></td> |
|
432 </tr> |
|
433 <tr> |
|
434 <th class="subhead" colspan="2"> |
|
435 <input type="submit" name="submit" value="Save profile" /> |
|
436 </th> |
|
437 </tr> |
|
438 </table> |
|
439 </div> |
|
440 <?php |
|
441 echo '</form>'; |
|
442 break; |
|
443 default: |
|
444 $good = false; |
|
445 $code = $plugins->setHook('userprefs_body'); |
|
446 foreach ( $code as $cmd ) |
|
447 { |
|
448 if ( eval($code) ) |
|
449 $good = true; |
|
450 } |
|
451 if ( !$good ) |
|
452 { |
|
453 echo '<h3>Invalid module</h3> |
|
454 <p>Userprefs module "'.$section.'" not found.</p>'; |
|
455 } |
|
456 break; |
|
457 } |
|
458 |
|
459 $template->footer(); |
|
460 } |
|
461 |
|
462 ?> |