I'm going crazy with a little problem with Maildir and PHP.
I need to check the APACHE_RUN_USER's Maildir and parse delivery-status messages.
The problem removing message after reading; i noticed that Zend_Mail_Storage_Maildir->removeMessage() is still a stub.
try {
$mailbox = new Zend_Mail_Storage_Maildir( array('dirname' => '/home/' . $_ENV['APACHE_RUN_USER'] . '/Maildir/') );
foreach ($mailbox as $id => $message) {
// seen flag
if ($message->hasFlag(Zend_Mail_Storage::FLAG_SEEN)) { continue; }
//get the unique id
$uniqueid = $mailbox->getUniqueId($id);
//obtain message headers
$headers = $message->getHeaders();
//check if the original message was sent from this app and is a delivery-status
$result = strpos($message, $id_header);
if($result === false) { echo '1 mail skipped: ' . $uniqueid . '. <br />'; continue; }
$result = strpos($headers['content-type'], 'delivery-status');
//if no skip to the next mail
if($result === false) { echo '1 mail skipped: ' . $uniqueid . '. <br />'; continue; }
// if everything it's ok process it.
// clear results
$data = array();
// foreach line of message
foreach( preg_split('/(\r?\n)/', $message) as $line ){
//clear results
$matches = array();
//perform matches on textlines
if( preg_match('/^(.+)\:\s{0,1}(.+)$/', $line, $matches) ) {
//grab intrested headers
foreach( array('Action', 'Status', 'Remote-MTA', 'Diagnostic-Code', $id_header) as $header) {
if($matches[1] == $header) $data[$header] = $matches[2];
}
}
}
// *** I NEED TO DROP THE MESSAGE HERE ***
// not working code ***
$currentmessageid = $mailbox->getNumberByUniqueId($uniqueid);
$mailbox->removeMessage($currentmessageid);
// *** I NEED TO DROP THE MESSAGE HERE ***
// print out results
echo '<pre class="email">';
print_r( $data );
echo '</pre>';
}
} catch (Exception $e) {
echo $e;
}
How can I remove it by hand? Some workarounds?
Thanks.
Sorry , its not implemented yet !
check out issue tracker http://framework.zend.com/issues/browse/ZF-9574
its open issue till today but some comment might be helpful :
In order to delete an email from a
maildir or mbox storage one must use:
Zend_Mail_Storage_Writable_Maildir or
Zend_Mail_Storage_Writable_Mbox
There are historical reasons for this
and they should be addressed and
standardised. For now the above
classes must be used or an exception
will be thrown with a message that is
a bit misleading.
Please refer to:
http://framework.zend.com/issues/browse/ZF-9574
for more details.
In order of tawfekov answer I solved as follow:
Opening mailbox:
$mailbox = new Zend_Mail_Storage_Writable_Maildir( array('dirname' => '/home/' . $_ENV['APACHE_RUN_USER'] . '/Maildir/') );
Processing mail code:
foreach ($mailbox as $id => $message) {
$uniqueid = $mailbox->getUniqueId($id);
/* ... mail processing code ... */
// mark as read
$currentmessageid = $mailbox->getNumberByUniqueId($uniqueid);
$mailbox->setFlags($currentmessageid, array(Zend_Mail_Storage::FLAG_SEEN));
// or uncomment to delete it
//$mailbox->removeMessage($currentmessageid);
}
Related
We can count unseen messages with:
$unreadMessages = $mail->countMessages([Storage::FLAG_UNSEEN]);
How to iterate only those unseen messages?
When I iterate all messages (as shown in documentation), it's painfully slow.
foreach ($mail as $messageNum => $message) {
if ($message->hasFlag(Storage::FLAG_SEEN) && !$message->hasFlag(Storage::FLAG_UNSEEN) && !$message->hasFlag(Storage::FLAG_RECENT)) {
// echo PHP_EOL . PHP_EOL . "Skipping seen/not-recent e-mail from " . $message->from . PHP_EOL;
continue;
}
}
Thanks.
When you look into how countMessages() function is done, you find out that protocol search function can be used. But protocol is private. So you have to extend the class:
class MyImap extends Laminas\Mail\Storage\Imap
{
public function getProtocol()
{
return $this->protocol;
}
}
$mail = new MyImap(...);
Then you can use this for fast iterating only unseen messages:
$message_nums = $mail->getProtocol()->search(['UNSEEN']);
foreach ($message_nums as $messageNum) {
$message = $mail->getMessage($messageNum);
}
Looking at the following code, when would we ever end up in the else block?
<?php
try {
if ($mail->send()) {
echo 'success';
} else {
echo 'failed to send';
echo $mail->ErrorInfo;
}
} catch (Exception $e) {
echo 'Exception!';
echo $mail->ErrorInfo;
}
?>
From my testing, any issue with the configuration of the email settings (e.g., bad host or port, trying to send from foo#example.com), results in an exception.
So, I'm wondering if there is any reason to test $mail->send(). Or can we just assume that if no exception occurred, $mail->send() will return true?
From the source:
/**
* Create a message and send it.
* Uses the sending method specified by $Mailer.
*
* #throws Exception
*
* #return bool false on error - See the ErrorInfo property for details of the error
*/
public function send()
{
try {
if (!$this->preSend()) {
return false;
}
return $this->postSend();
} catch (Exception $exc) {
$this->mailHeader = '';
$this->setError($exc->getMessage());
if ($this->exceptions) {
throw $exc;
}
return false;
}
}
and the preSend() method:
/**
* Prepare a message for sending.
*
* #throws Exception
*
* #return bool
*/
public function preSend()
{
if ('smtp' == $this->Mailer or
('mail' == $this->Mailer and stripos(PHP_OS, 'WIN') === 0)
) {
//SMTP mandates RFC-compliant line endings
//and it's also used with mail() on Windows
static::setLE("\r\n");
} else {
//Maintain backward compatibility with legacy Linux command line mailers
static::setLE(PHP_EOL);
}
//Check for buggy PHP versions that add a header with an incorrect line break
if (ini_get('mail.add_x_header') == 1
and 'mail' == $this->Mailer
and stripos(PHP_OS, 'WIN') === 0
and ((version_compare(PHP_VERSION, '7.0.0', '>=')
and version_compare(PHP_VERSION, '7.0.17', '<'))
or (version_compare(PHP_VERSION, '7.1.0', '>=')
and version_compare(PHP_VERSION, '7.1.3', '<')))
) {
trigger_error(
'Your version of PHP is affected by a bug that may result in corrupted messages.' .
' To fix it, switch to sending using SMTP, disable the mail.add_x_header option in' .
' your php.ini, switch to MacOS or Linux, or upgrade your PHP to version 7.0.17+ or 7.1.3+.',
E_USER_WARNING
);
}
try {
$this->error_count = 0; // Reset errors
$this->mailHeader = '';
// Dequeue recipient and Reply-To addresses with IDN
foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) {
$params[1] = $this->punyencodeAddress($params[1]);
call_user_func_array([$this, 'addAnAddress'], $params);
}
if (count($this->to) + count($this->cc) + count($this->bcc) < 1) {
throw new Exception($this->lang('provide_address'), self::STOP_CRITICAL);
}
// Validate From, Sender, and ConfirmReadingTo addresses
foreach (['From', 'Sender', 'ConfirmReadingTo'] as $address_kind) {
$this->$address_kind = trim($this->$address_kind);
if (empty($this->$address_kind)) {
continue;
}
$this->$address_kind = $this->punyencodeAddress($this->$address_kind);
if (!static::validateAddress($this->$address_kind)) {
$error_message = sprintf('%s (%s): %s',
$this->lang('invalid_address'),
$address_kind,
$this->$address_kind);
$this->setError($error_message);
$this->edebug($error_message);
if ($this->exceptions) {
throw new Exception($error_message);
}
return false;
}
}
// Set whether the message is multipart/alternative
if ($this->alternativeExists()) {
$this->ContentType = static::CONTENT_TYPE_MULTIPART_ALTERNATIVE;
}
$this->setMessageType();
// Refuse to send an empty message unless we are specifically allowing it
if (!$this->AllowEmpty and empty($this->Body)) {
throw new Exception($this->lang('empty_message'), self::STOP_CRITICAL);
}
//Trim subject consistently
$this->Subject = trim($this->Subject);
// Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
$this->MIMEHeader = '';
$this->MIMEBody = $this->createBody();
// createBody may have added some headers, so retain them
$tempheaders = $this->MIMEHeader;
$this->MIMEHeader = $this->createHeader();
$this->MIMEHeader .= $tempheaders;
// To capture the complete message when using mail(), create
// an extra header list which createHeader() doesn't fold in
if ('mail' == $this->Mailer) {
if (count($this->to) > 0) {
$this->mailHeader .= $this->addrAppend('To', $this->to);
} else {
$this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
}
$this->mailHeader .= $this->headerLine(
'Subject',
$this->encodeHeader($this->secureHeader($this->Subject))
);
}
// Sign with DKIM if enabled
if (!empty($this->DKIM_domain)
and !empty($this->DKIM_selector)
and (!empty($this->DKIM_private_string)
or (!empty($this->DKIM_private)
and static::isPermittedPath($this->DKIM_private)
and file_exists($this->DKIM_private)
)
)
) {
$header_dkim = $this->DKIM_Add(
$this->MIMEHeader . $this->mailHeader,
$this->encodeHeader($this->secureHeader($this->Subject)),
$this->MIMEBody
);
$this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . static::$LE .
static::normalizeBreaks($header_dkim) . static::$LE;
}
return true;
} catch (Exception $exc) {
$this->setError($exc->getMessage());
if ($this->exceptions) {
throw $exc;
}
return false;
}
}
So in short: It returns false, if an exception occurs.
It is always good practice to test $mail->send().
To answer your questions:
You are correct. If there are no exceptions, then it will always return true.
Some Extra Information:
If you want to test emails before sending it to recipients you can do the following:
You can set sendmail_path in php ini to whatever you want. For example, it can be tee -a mail.log so that everything gets logged to a file.
I added a ajax add to cart to the rwd theme, and the action controller is called twice for every http request. Any help with solving the problem or debugging is welcome, I already lost 2 weeks on this. This all works on our development environment, but acts weird on staging. The dev environment is hosted locally on MAMP, staging is hosted on OVH shared hosting.
edit2: removed irrelevant info
found this error message in error.log
FastCGI: comm with server "/..../staging/index.php" aborted: error parsing headers: duplicate header 'Content-Type', referer: http://staging.xxxx.be/product.html
also found a solution here: http://blog.imseo.it/2014/09/08/magento-fastcgi-error-parsing-headers-duplicate-header/
The solution is to replace the function sendHeaders in
app/code/core/Mage/Core/Controller/Response/Http.php
public function sendHeaders()
{
if (!$this->canSendHeaders()) {
Mage::log('HEADERS ALREADY SENT: '.mageDebugBacktrace(true, true, true));
return $this;
}
if (in_array(substr(php_sapi_name(), 0, 3), array('cgi', 'fpm')))
{
// remove duplicate headers
$remove = array('status', 'content-type');
// already sent headers
$sent = array();
foreach (headers_list() as $header)
{
// parse name
if (!$pos = strpos($header, ':'))
continue;
$sent[strtolower(substr($header, 0, $pos))] = true;
}
// raw headers
$headersRaw = array();
foreach ($this->_headersRaw as $i=>$header)
{
// parse name
if (!$pos = strpos($header, ':'))
continue;
$name = strtolower(substr($header, 0, $pos));
if (in_array($name, $remove))
{
// check sent headers
if ($sent[$name])
{
unset($this->_headersRaw[$i]);
continue;
}
// check header
if (!is_null($existing = $headers[$name]))
{
$this->_headersRaw[$existing] = $header;
unset($this->_headersRaw[$i]);
}
else
$headersRaw[$name] = $i;
}
}
// object headers
$headers = array();
foreach ($this->_headers as $i=>$header)
{
$name = strtolower($header['name']);
if (in_array($name, $remove))
{
// check sent headers
if ($sent[$name])
{
unset($this->_headers[$i]);
continue;
}
// check header
if (!is_null($existing = $headers[$name]))
{
$this->_headers[$existing] = $header;
unset($this->_headers[$i]);
}
else
$headers[$name] = $i;
// check raw headers
if (!is_null($existing = $headersRaw[$name]))
unset($this->_headersRaw[$existing]);
}
}
}
parent::sendHeaders();
}
I am currently trying to get the UNSEEN/UNREAD messages from my server. Currently, I have this:
$openmail = imap_open($dns, $email, $password) or die("Cannot Connect " . imap_last_error());
if ($openmail) {
/* grab emails */
$emails = imap_search($openmail, 'UNSEEN');
if ($emails) {
//For every e-mail.
$tot = imap_num_msg($openmail);
for ($i = $tot; $i > 0; $i--) {
$structure = imap_fetchstructure($openmail, $i);
if (isset($structure->parts) && is_array($structure->parts) && isset($structure->parts[1])) {
$part = $structure->parts[1];
$message = imap_fetchbody($openmail, $i, 2, FT_PEEK);
if ($part->encoding == 3) {
$message = imap_base64($message);
} else if ($part->encoding == 1) {
$message = imap_8bit($message);
} else {
$message = imap_qprint($message);
}
}
$header = imap_header($openmail, $i);
$from = imap_utf8($header->fromaddress);
$subject = $header->Subject;
$subject = substr($subject, 0, 150);
$date = $header->Date;
}
/* Print out the Unseen messages in here! */
} else {
/* No unseen messages */
echo "No unseen";
}
}
I've tried sending multiply emails to my mailserver, refreshed the page with the above script. But I keep getting the "No unseen".
I've tried to output the $emails but it's empty. It can't find anything.
If I try to just get ALL the messages (no unseen filter), I can see the emails I've sent to the mailbox, although, they're all marked as read.
Your code $message = imap_fetchbody($openmail, $i, 2, FT_PEEK); uses hardcoded part offsets, i.e. it assumes that a message is always multipart/alternative with text/plain and text/html. That's a very wrong assumption.
You have to look at the output of the BODYSTRUCTURE to see what the MIME structure of your mail is.
With that out of the way, be sure that none of your commands use the BODY fetch operation; you have to use BODY.PEEK. I have no idea how this is accessible within the PHP c-client bindings.
I start saying that I HATE OpenID, because it's poorly implemented/documented.
I'm trying to use "openid-php-openid-2.2.2-24". Here the source code: https://github.com/openid/php-openid
When I try to use the authentication example, it returns to me:
"You have successfully verified https://www.google.com/accounts/o8/id?id=[...] as your identity.
No PAPE response was sent by the provider."
but there's no shadow of email, nickname or fullname of google openid login data.
While reading the file ("/openid/examples/consumer/finish_auth.php"), I note that SREG variables have to be printed between the "You have successfully verified" and "No PAPE response" messages, but they don't:
$success = sprintf('You have successfully verified ' .
'%s as your identity.',
$esc_identity, $esc_identity);
if ($response->endpoint->canonicalID) {
$escaped_canonicalID = escape($response->endpoint->canonicalID);
$success .= ' (XRI CanonicalID: '.$escaped_canonicalID.') ';
}
$sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response);
$sreg = $sreg_resp->contents();
if (#$sreg['email']) {
$success .= " You also returned '".escape($sreg['email']).
"' as your email.";
}
if (#$sreg['nickname']) {
$success .= " Your nickname is '".escape($sreg['nickname']).
"'.";
$_SESSION['nickname'] = escape($sreg['nickname']);
}
if (#$sreg['fullname']) {
$success .= " Your fullname is '".escape($sreg['fullname']).
"'.";
}
$pape_resp = Auth_OpenID_PAPE_Response::fromSuccessResponse($response);
if ($pape_resp) {
[...]
} else {
$success .= "<p>No PAPE response was sent by the provider.</p>";
}
I've tried to print the content of $sreg['email'], $sreg['nickname'] and $sreg['fullname'], but they return all blank contents (null/empty values).
I need to retrieve the email address of the account which users use to login in..
Dante
To get the question off the unanswered list, I post dante's answer here as answer:
I solved my problem.
Example usage of AX in PHP OpenID: Example usage of AX in PHP OpenID
After 2 days of research, I've just now found the answer ("but Google uses AX (attribute exchange) instead of SReg for additional data"). Why Google must always be so different?
However, the code in that stackoverflow answer page doesn't work for me (my hosting server returns 500 internal server error code).
So, I post here "my code" (it's so rough):
oid_ax_common.php
<?php
// Circumnavigate bugs in the GMP math library that can be result in signature
// validation errors
define('Auth_OpenID_BUGGY_GMP', true);
$path_extra = dirname(dirname(dirname(__FILE__)));
$path = ini_get('include_path');
$path = $path_extra . PATH_SEPARATOR . $path;
ini_set('include_path', $path);
function displayError($message) {
$error = $message;
include './index.php';
exit(0);
}
function doIncludes() {
/**
* Require the OpenID consumer code.
*/
require_once "Auth/OpenID/Consumer.php";
/**
* Require the "file store" module, which we'll need to store
* OpenID information.
*/
require_once "Auth/OpenID/FileStore.php";
/**
* Require the Simple Registration extension API.
*/
//require_once "Auth/OpenID/SReg.php";
require_once "Auth/OpenID/AX.php";
/**
* Require the PAPE extension module.
*/
require_once "Auth/OpenID/PAPE.php";
}
doIncludes();
global $pape_policy_uris;
$pape_policy_uris = array(
PAPE_AUTH_MULTI_FACTOR_PHYSICAL,
PAPE_AUTH_MULTI_FACTOR,
PAPE_AUTH_PHISHING_RESISTANT
);
function &getStore() {
/**
* This is where the example will store its OpenID information.
* You should change this path if you want the example store to be
* created elsewhere. After you're done playing with the example
* script, you'll have to remove this directory manually.
*/
$store_path = null;
if (function_exists('sys_get_temp_dir')) {
$store_path = sys_get_temp_dir();
}
else {
if (strpos(PHP_OS, 'WIN') === 0) {
$store_path = $_ENV['TMP'];
if (!isset($store_path)) {
$dir = 'C:\Windows\Temp';
}
}
else {
$store_path = #$_ENV['TMPDIR'];
if (!isset($store_path)) {
$store_path = '/tmp';
}
}
}
$store_path = './tmp/';
$store_path .= DIRECTORY_SEPARATOR . '_php_consumer_test';
if (!file_exists($store_path) &&
!mkdir($store_path)) {
print "Could not create the FileStore directory '$store_path'. ".
" Please check the effective permissions.";
exit(0);
}
$r = new Auth_OpenID_FileStore($store_path);
return $r;
}
function &getConsumer() {
/**
* Create a consumer object using the store object created
* earlier.
*/
$store = getStore();
$r = new Auth_OpenID_Consumer($store);
return $r;
}
function getScheme() {
$scheme = 'http';
if (isset($_SERVER['HTTPS']) and $_SERVER['HTTPS'] == 'on') {
$scheme .= 's';
}
return $scheme;
}
function getReturnTo() {
return sprintf("%s://%s:%s%s/oid_ax_receive.php",
getScheme(), $_SERVER['SERVER_NAME'],
$_SERVER['SERVER_PORT'],
dirname($_SERVER['PHP_SELF']));
}
function getTrustRoot() {
return sprintf("%s://%s:%s%s/",
getScheme(), $_SERVER['SERVER_NAME'],
$_SERVER['SERVER_PORT'],
dirname($_SERVER['PHP_SELF']));
}
?>
oid_ax_send.php
<?php
require_once "oid_ax_common.php";
// Starts session (needed for YADIS)
session_start();
function getOpenIDURL() {
// Render a default page if we got a submission without an openid
// value.
if (empty($_GET['openid_identifier'])) {
$error = "Expected an OpenID URL.";
include './index.php';
exit(0);
}
return $_GET['openid_identifier'];
}
function run() {
// https://www.google.com/accounts/o8/id
// $openid = 'http://openid-provider.appspot.com/';
$openid = 'https://www.google.com/accounts/o8/id';
// $openid .= getOpenIDURL();
$consumer = getConsumer();
// Begin the OpenID authentication process.
$auth_request = $consumer->begin($openid);
// Create attribute request object
// See http://code.google.com/apis/accounts/docs/OpenID.html#Parameters for parameters
// Usage: make($type_uri, $count=1, $required=false, $alias=null)
$attribute[] = Auth_OpenID_AX_AttrInfo::make('http://axschema.org/contact/email',2,1, 'email');
$attribute[] = Auth_OpenID_AX_AttrInfo::make('http://axschema.org/namePerson/first',1,1, 'firstname');
$attribute[] = Auth_OpenID_AX_AttrInfo::make('http://axschema.org/namePerson/last',1,1, 'lastname');
// Create AX fetch request
$ax = new Auth_OpenID_AX_FetchRequest;
// Add attributes to AX fetch request
foreach($attribute as $attr){
$ax->add($attr);
}
// Add AX fetch request to authentication request
$auth_request->addExtension($ax);
// No auth request means we can't begin OpenID.
if (!$auth_request) {
displayError("Authentication error; not a valid OpenID.");
}
/* $sreg_request = Auth_OpenID_SRegRequest::build(
// Required
array('nickname'),
// Optional
array('fullname', 'email'));
if ($sreg_request) {
$auth_request->addExtension($sreg_request);
} */
$policy_uris = null;
if (isset($_GET['policies'])) {
$policy_uris = $_GET['policies'];
}
$pape_request = new Auth_OpenID_PAPE_Request($policy_uris);
if ($pape_request) {
$auth_request->addExtension($pape_request);
}
// Redirect the user to the OpenID server for authentication.
// Store the token for this authentication so we can verify the
// response.
// For OpenID 1, send a redirect. For OpenID 2, use a Javascript
// form to send a POST request to the server.
if ($auth_request->shouldSendRedirect()) {
$redirect_url = $auth_request->redirectURL(getTrustRoot(),
getReturnTo());
// If the redirect URL can't be built, display an error
// message.
if (Auth_OpenID::isFailure($redirect_url)) {
displayError("Could not redirect to server: " . $redirect_url->message);
} else {
// Send redirect.
header("Location: ".$redirect_url);
}
} else {
// Generate form markup and render it.
$form_id = 'openid_message';
$form_html = $auth_request->htmlMarkup(getTrustRoot(), getReturnTo(),
false, array('id' => $form_id));
// Display an error if the form markup couldn't be generated;
// otherwise, render the HTML.
if (Auth_OpenID::isFailure($form_html)) {
displayError("Could not redirect to server: " . $form_html->message);
} else {
print $form_html;
}
}
}
run();
?>
oid_ax_receive.php
<?php
require_once "oid_ax_common.php";
// Starts session (needed for YADIS)
session_start();
function escape($thing) {
return htmlentities($thing);
}
function run() {
$consumer = getConsumer();
// Complete the authentication process using the server's
// response.
$return_to = getReturnTo();
$response = $consumer->complete($return_to);
// Check the response status.
if ($response->status == Auth_OpenID_CANCEL) {
// This means the authentication was cancelled.
$msg = 'Verification cancelled.';
} else if ($response->status == Auth_OpenID_FAILURE) {
// Authentication failed; display the error message.
$msg = "OpenID authentication failed: " . $response->message;
} else if ($response->status == Auth_OpenID_SUCCESS) {
// Get registration informations
$ax = new Auth_OpenID_AX_FetchResponse();
$obj = $ax->fromSuccessResponse($response);
// Print me raw
echo '<pre>';
print_r($obj->data);
echo '</pre>';
exit;
$pape_resp = Auth_OpenID_PAPE_Response::fromSuccessResponse($response);
if ($pape_resp) {
if ($pape_resp->auth_policies) {
$success .= "<p>The following PAPE policies affected the authentication:</p><ul>";
foreach ($pape_resp->auth_policies as $uri) {
$escaped_uri = escape($uri);
$success .= "<li><tt>$escaped_uri</tt></li>";
}
$success .= "</ul>";
} else {
$success .= "<p>No PAPE policies affected the authentication.</p>";
}
if ($pape_resp->auth_age) {
$age = escape($pape_resp->auth_age);
$success .= "<p>The authentication age returned by the " .
"server is: <tt>".$age."</tt></p>";
}
if ($pape_resp->nist_auth_level) {
$auth_level = escape($pape_resp->nist_auth_level);
$success .= "<p>The NIST auth level returned by the " .
"server is: <tt>".$auth_level."</tt></p>";
}
} else {
$success .= "<p>No PAPE response was sent by the provider.</p>";
}
}
include './index.php';
}
run();
?>
Enjoy.
Dante
P.S.: to complete the opera of OpenID, although I solved my problem with user info / login data with Google, I still have one problem with Light OpenID (https://stackoverflow.com/questions/10735708/lightopenid-openid-authurl-does-not-return-any-value).
If you want to help me, we will completely work out and conclude with the OpenID story.