3 // You may need to uncomment this to add the directory of the OpenID Auth and Services
4 // libraries if they are not in a standard PHP include place on your server
6 // set_include_path(get_include_path() . PATH_SEPARATOR . "/if needed/replace this with/the path to your/OpenID library includes/");
8 require_once(dirname(dirname(dirname(__FILE__
))).'/includes.php');
9 require_once(dirname(dirname(dirname(__FILE__
))).'/lib/validateurlsyntax.php');
11 really_require_once('Auth/OpenID/Consumer.php');
12 really_require_once('Auth/OpenID/MySQLStore.php');
13 really_require_once('Auth/OpenID/DatabaseConnection.php');
15 $activate_confirmation1 = __gettext("A confirmation message has been sent to ");
16 $activate_confirmation2 = __gettext(" Please click on the link in that message to activate your account. You will then be able to login using the OpenID you have supplied.");
18 $change_confirmation1 = __gettext("Your email address has changed. A confirmation message has been sent to your new address at ");
20 $change_confirmation2 = __gettext(". Please click on the link in that message to confirm this new email address. ");
22 $created_external_msg = __gettext("Created external account, transferred email, name from OpenID server:");
24 $email_in_use_msg = __gettext("Cannot change your email address because the new address is already in use.");
26 $invalid_code_msg = __gettext("Your form code appears to be invalid. Codes only last for seven days; it's possible that yours is older.");
29 * This function replaces require_once and attempts to fix a PHP bug
30 * that interferes with require_once behavior when it is used inside a
31 * function. require_once, when used inside a function, only puts
32 * functions and classes into the global namespace; global variables
33 * in the required file are put into the local scope of the function
34 * call, which is not what is desired. Because this module is
35 * required in the scope of another function, the bug occurs and we
36 * have to put things into the global namespace manually. Whee!
39 function really_require_once($path) {
41 foreach (get_defined_vars() as $k => $v) {
46 if (!function_exists('fnmatch')) {
47 function fnmatch($pattern, $string) {
48 for ($op = 0, $npattern = '', $n = 0, $l = strlen($pattern); $n < $l; $n++
) {
49 switch ($c = $pattern[$n]) {
51 $npattern .= '\\' . @$pattern[++
$n];
53 case '.': case '+': case '^': case '$': case '(': case ')': case '{': case '}': case '=': case '!': case '<': case '>': case '|':
54 $npattern .= '\\' . $c;
57 $npattern .= '.' . $c;
59 case '[': case ']': default:
63 } else if ($c == ']') {
64 if ($op == 0) return false;
71 if ($op != 0) return false;
73 return preg_match('/' . $npattern . '/i', $string);
79 * Emulates a PEAR database connection which is what the Auth_OpenID
80 * library expects to use for its database storage. This just wraps
81 * Elgg's database abstraction calls.
83 class Elgg_OpenID_DBConnection
extends Auth_OpenID_DatabaseConnection
{
85 function setFetchMode($mode = 0) {
87 global $ADODB_FETCH_ASSOC;
90 $mode = $ADODB_FETCH_ASSOC;
93 return $db->SetFetchMode($mode);
96 function autoCommit($mode) {
100 function query($sql, $params = array()) {
102 $mode = $this->setFetchMode();
103 $result = $db->Execute($sql, $params);
104 $this->setFetchMode($mode);
108 function getOne($sql, $params = array()) {
110 $mode = $this->setFetchMode();
111 $result = $db->GetOne($sql, $params);
112 $this->setFetchMode($mode);
116 function getRow($sql, $params = array()) {
118 $mode = $this->setFetchMode();
119 $result = $db->GetRow($sql, $params);
120 $this->setFetchMode($mode);
124 function getAll($sql, $params = array()) {
126 $mode = $this->setFetchMode();
127 $result = $db->GetAll($sql, $params);
128 $this->setFetchMode($mode);
135 * An Elgg-compatible store implementation that uses the
136 * Elgg_OpenID_DBConnection.
138 class OpenID_ElggStore
extends Auth_OpenID_MySQLStore
{
140 function OpenID_ElggStore()
143 $this->nosync_table_name
= $CFG->prefix
."openid_client_nosync";
144 $conn = new Elgg_OpenID_DBConnection();
145 parent
::Auth_OpenID_MySQLStore($conn,
146 $CFG->prefix
.'openid_client_consumer_settings',
147 $CFG->prefix
.'openid_client_consumer_associations',
148 $CFG->prefix
.'openid_client_consumer_nonces');
149 $this->createTables();
152 function createTables() {
154 parent
::createTables();
155 $this->create_nosync_table();
161 $replacements = array(
162 'value' => $this->nosync_table_name
,
163 'keys' => array('nosync_table',
168 foreach ($replacements['keys'] as $key) {
169 $this->sql
[$key] = sprintf($this->sql
[$key],$replacements['value']);
174 * If the data provided by an OpenID server differs from the data
175 * stored on the client site, users may be asked if they want to
176 * update their client data each time they log on to the client.
177 * The nosync table lists the idents of users who have asked to turn
181 function create_nosync_table() {
183 if (!$this->tableExists($this->nosync_table_name
)) {
184 $r = $this->connection
->query($this->sql
['nosync_table']);
185 return $this->resultToBool($r);
190 function getNoSyncStatus($ident) {
192 $r = $this->connection
->getOne($this->sql
['get_nosync'],
194 return $this->resultToBool($r);
197 function addNoSyncStatus($ident) {
198 return $this->connection
->query($this->sql
['add_nosync'],
202 function removeNoSyncStatus($ident) {
203 return $this->connection
->query($this->sql
['remove_nosync'],
208 * Returns true if the specified value is considered an error
209 * value. Values returned from database calls will be passed to
210 * this function to make decisions.
212 function isError($value)
214 return $value === false;
218 * This function is responsible for encoding binary data to make
219 * it safe for use in SQL.
221 function blobEncode($blob)
227 * Given encoded binary data, this function is responsible for
228 * decoding it from its encoded representation. Some backends
229 * will not return encoded data, like this one, so no conversion
232 function blobDecode($blob)
238 * This is called by the SQLStore constructor and gives the
239 * implementor a place to specify SQL that is used to store and
240 * retrieve specific data. Here we call the parent class's setSQL
241 * to get the basic SQL data for a MySQL store and overwrite a
242 * couple of pieces of SQL code that use the unsupported "!"
243 * placeholder in the default MySQL store. We also add the SQL
244 * to support the nosync table used by the Elgg OpenID client to
245 * support OpenID client/server data syncronisation.
252 $this->sql
['create_auth'] =
253 "INSERT INTO %s VALUES ('auth_key', ?)";
255 $this->sql
['set_assoc'] =
256 "REPLACE INTO %s VALUES (?, ?, ?, ?, ?, ?)";
258 $this->sql
['nosync_table'] =
259 "CREATE TABLE %s (ident INT(10) UNIQUE PRIMARY KEY) TYPE=InnoDB";
261 $this->sql
['get_nosync'] =
262 "SELECT ident FROM %s WHERE ident = ?";
264 $this->sql
['add_nosync'] =
265 "INSERT INTO %s (ident) VALUES (?)";
267 $this->sql
['remove_nosync'] =
268 "DELETE FROM %s WHERE ident = ?";
273 function openid_client_generate_i_code($prefix,$username,$ident,$email,$fullname) {
274 $invite = new StdClass
;
275 $invite->name
= $fullname;
276 $invite->email
= $email;
277 $invite->code
= $prefix . substr(base_convert(md5(time() . $username), 16, 36), 0, 7);
278 $invite->added
= time();
279 $invite->owner
= $ident;
280 insert_record('invitations',$invite);
284 function openid_client_send_activate_confirmation_message($i_code) {
287 $greetingstext = sprintf(__gettext("Thank you for registering with %s."),$CFG->sitename
);
288 $subjectline = sprintf(__gettext("%s account verification"),$CFG->sitename
);
289 $url = $CFG->wwwroot
. "mod/openid_client/confirm.php?code=" . $i_code->code
;
290 $emailmessage = sprintf(__gettext("Dear %s,\n\n%s\n\nTo complete your registration, visit the following URL:\n\n\t%s\n\nwithin seven days.\n\nRegards,\n\nThe %s team.")
291 ,$i_code->name
,$greetingstext,$url, $CFG->sitename
);
292 $emailmessage = wordwrap($emailmessage);
293 email_to_user($i_code,null,$subjectline,$emailmessage);
296 function openid_client_send_change_confirmation_message($i_code) {
299 $greetingstext = sprintf(__gettext("We have received a request to change your email address registered with %s."),$CFG->sitename
);
300 $subjectline = sprintf(__gettext("%s email change"),$CFG->sitename
);
301 $url = $CFG->wwwroot
. "mod/openid_client/confirm.php?code=" . $i_code->code
;
302 $emailmessage = sprintf(__gettext("Dear %s,\n\n%s\n\nTo change your email address to {$i_code->email}, visit the following URL:\n\n\t%s\n\nwithin seven days.\n\nRegards,\n\nThe %s team.")
303 ,$i_code->name
,$greetingstext,$url, $CFG->sitename
);
304 $emailmessage = wordwrap($emailmessage);
305 email_to_user($i_code,null,$subjectline,$emailmessage);
308 $emailLabel = __gettext("Email:");
309 $nameLabel = __gettext("Name:");
310 $submitLabel = __gettext("Submit");
311 $cancelLabel = __gettext("Cancel");
313 function openid_client_generate_sync_form($new_email,$new_name, $user, $email_confirmation) {
314 global $emailLabel, $nameLabel, $submitLabel, $cancelLabel;
316 $old_email = $user->email
;
317 $old_name = $user->name
;
319 $noSyncLabel = __gettext("Do not notify me again if the data on this system is not the same as the data on my OpenID server.");
320 $instructions = __gettext("The information on your Open ID server is not the same as on this system. Tick the check boxes next to the information you would like to update (if any) and press submit.");
321 if ($new_email && $new_email != $old_email) {
322 $change_fields .= '<table><tr><td><label for="emailchange"><input type="checkbox" id="emailchange" name="emailchange" value="yes" />'." $emailLabel</label></td><td>$old_email => $new_email</td></tr></table>\n";
323 if (!$email_confirmation) {
324 // the email address is from a green server, so we can change the email without a confirmation message
325 // add an invitation code however to prevent this form from being forged
326 // the user ident and new email address can then securely be stored in the database invitation table
327 // rather than the form
328 $i_code = openid_client_generate_i_code('c',$user->username
,$user->ident
,$new_email,$new_name);
329 $form_stuff = '<input type="hidden" name="i_code" value="'.$i_code.'" />';
331 // the email will be confirmed anyway so it is safe to put it in the form
332 $form_stuff .= <<< END
333 <input type
="hidden" name
="new_email" value
="$new_email" />
338 if ($new_name && $new_name != $old_name) {
339 $change_fields .= '<table><tr><td><label for="namechange"><input type="checkbox" id="namechange" name="namechange" value="yes" />'."$nameLabel</label></td><td>$old_name => $new_name</td></tr></table>\n";
343 <form action
="sync.php" method
="post">
348 <label
for="nosync"><input type
="checkbox" id
="nosync" name
="nosync" value
="yes" />$noSyncLabel</label
>
351 <input type
="hidden" name
="new_name" value
="$new_name" />
352 <input type
="submit" name
="submit" value
="$submitLabel" />
353 <input type
="submit" name
="cancel" value
="$cancelLabel" />
363 function openid_client_generate_missing_data_form($openid_url,$email = '',$fullname = '',$email_confirmation,$code) {
364 global $emailLabel, $nameLabel, $submitLabel, $cancelLabel;
366 $missing_email = __gettext("a valid email address");
367 $missing_name = __gettext("your full name");
368 $and = __gettext("and");
369 $email_form = "<table><tr><td>$emailLabel</td><td><input type=".'"text" size="50" name="email" value=""></td></tr></table>';
370 $name_form = "<table><tr><td>$nameLabel</td><td><input type=".'"text" size="50" name="name" value=""></td></tr></table>';
371 $email_hidden = '<input type="hidden" name="email" value="'.$email.'" />'."\n";
372 $name_hidden = '<input type="hidden" name="name" value="'.$fullname.'" />'."\n";
374 if (!$email && !$fullname) {
375 $missing_fields = $missing_email.' '.$and.' '.$missing_name;
376 $visible_fields = $email_form.'<br />'.$name_form;
379 $missing_fields = $missing_email;
380 $visible_fields = $email_form;
381 $hidden_fields = $name_hidden;
382 } elseif (!$fullname) {
383 $missing_fields = $missing_name;
384 $visible_fields = $name_form;
385 $hidden_fields = $email_hidden;
388 $hidden_fields .= '<input type="hidden" name="code" value="'.$code->code
.'" />'."\n";
390 $instructions = __gettext("In order to create an account on this site you need to supply $missing_fields. Please enter this information below.");
394 <form action
="missing.php" method
="post">
400 <input type
="submit" name
="submit" value
="$submitLabel" />
401 <input type
="submit" name
="cancel" value
="$cancelLabel" />
410 function openid_client_check_email_confirmation($openid_url) {
414 $email_confirmation = false;
416 if ($CFG->openid_client_greenlist
) {
417 foreach (explode("\n",$CFG->openid_client_greenlist
) as $entry ) {
418 if (fnmatch($entry,$openid_url)) {
419 $email_confirmation = false;
425 if (!$done && $CFG->openid_client_yellowlist
) {
426 foreach (explode("\n",$CFG->openid_client_yellowlist
) as $entry ) {
427 if (fnmatch($entry,$openid_url)) {
428 $email_confirmation = true;
433 return $email_confirmation;
436 function openid_client_create_external_user($openid_url,$email, $fullname, $email_confirmation) {
440 if ($email && record_exists('users','email',$email)) {
441 $messages[] = __gettext("Cannot create an account with the email address $email because it is already in use.");
446 $u->name
= $fullname;
448 $u->alias
= $openid_url;
449 $u->user_type
= 'external';
450 if ($email_confirmation) {
457 $id = insert_record('users',$u);
460 $u->username
= "ext".$id;
463 update_record('users',$u);