I want to build a message encryption system where users will send message in a encrypted format. I am using GnUPG.I got help from http://www.php.net/manual/en/gnupg.installation.php to install the GnUPG. After install in the server I create the public and private keyring by following code
$GeneratedKey = $gpg->GenKey($name, $comment, $email, $passphrase,$ExpireDate, $KeyType, $KeyLength,$SubkeyType, $SubkeyLength );
function GenKey($RealName, $Comment, $Email, $Passphrase = '', $ExpireDate = 0, $KeyType = 'DSA', $KeyLength = 1024, $SubkeyType = 'ELG-E', $SubkeyLength = 1024)
{
// validates the keytype
if (($KeyType != 'DSA') && ($KeyType != 'RSA')) {
$this->error = 'Invalid Key-Type, the allowed are DSA and RSA';
return false;
}
// validates the subkey
if ((!empty($SubkeyType)) && ($SubkeyType != 'ELG-E')) {
$this->error = 'Invalid Subkey-Type, the allowed is ELG-E';
return false;
}
// validate the expiration date
if (!preg_match('/^(([0-9]+[dwmy]?)|([0-9]{4}-[0-9]{2}-[0-9]{2}))$/', $ExpireDate)) {
$this->error = 'Invalid Expire Date, the allowed values are <iso-date>|(<number>[d|w|m|y])';
return false;
}
// generates the batch configuration script
$batch_script = "Key-Type: $KeyType\n" .
"Key-Length: $KeyLength\n";
if (($KeyType == 'DSA') && ($SubkeyType == 'ELG-E'))
$batch_script .= "Subkey-Type: $SubkeyType\n" .
"Subkey-Length: $SubkeyLength\n";
$batch_script .= "Name-Real: $RealName\n" .
"Name-Comment: $Comment\n" .
"Name-Email: $Email\n" .
"Expire-Date: $ExpireDate\n" .
"Passphrase: $Passphrase\n" .
"%commit\n" .
"%echo done with success\n";
// initialize the output
$contents = '';
// execute the GPG command
if ( $this->_fork_process($this->program_path . ' --homedir ' . $this->home_directory .
' --batch --status-fd 1 --gen-key',
$batch_script, $contents) ) {
$matches = false;
if ( preg_match('/\[GNUPG:\]\sKEY_CREATED\s(\w+)\s(\w+)/', $contents, $matches) )
return $matches[2];
else
return true;
} else
return false;
}
I encrypt by the following code
$gpg = new gnupg();
$gpg->addencryptkey($recipient);
$ciphertext = $gpg->encrypt($plaintext);
Decrypt by the following code
$gpg = new gnupg();
$gpg->adddecryptkey($recipient, $receiver_passphrase);
$plain = $gpg->decrypt($encrypted_text, $plaintext);
BY this I successfully create a folder of a username and generate private and public keyring over there and then send message in a encrypted way and decrypt by the receiver. But my main concern is I don't want to generate user public and private keyring in the server, instead I want to generate public and private keyring in users local computer..
Is it possible to generate public and private key in local computer? Because I don't want user depend on server security. Only receiver will able to decrypt the message.. Not other will able to decrypt..
thanks,
You can create the keys using OpenPGP.js which runs in your client's browser, store the private key somewhere with your client and send nothing but the public one to the server.
// Create new key with RSA encryption (1), 4k length for
// John Doe with password "foobar"
var keys = openpgp.generate_key_pair(1, 4096,
"John Doe john.doe#example.org", "foobar");
keys.privateKeyArmored; // Access private key
keys.publicKeyArmored; // Access public key
Related
I need to upgrade a website I look after to PHP 7.2. In the testing phase i've discovered that one of the plug-in's on the site used the mcrypt library which is no longer available in PHP 7.2.
Essentially this plugin receives PBKDF2 encrypted data from a ticketing system (Tessitura) that returns the user's session key and a time stamp and an encrypted string.
In the control panel I have been given data to use to decrypt this session key such as a Passphrase, Salt, Authentication/HMAC Key , BlockSize, PaddingMode, EncryptionKeyIterations, EncryptionKeyLength and HMACLength
Image of supplied fields
I've been trying to work out how to decrypt the data but I confess i'm struggling. C
an anybody tell me how to use php 7.2 to achieve this? I've found some functions in the openssl suite that look like they may be the correct way to go but they all use different terminology to the information i've been given and I cannot work out where to start, what goes where or what settings to use
Thanks in advance to anybody that can solve this problem!!
As Rob Napier said, PBKDF2 is what the system is using to hash the password being passed into the encryption process. The site is actually using aes-256-cbc encryption. That encryption process can include a password.
After the information is encrypted, that payload is signed with an HMAC key.
You can use the openSSL library to execute all of this in php 7 and higher. Here is some sample code that creates a class to handle the encryption/decryption, for example:
$crypto = new AesCryptoClass('YOUR_PASSPHRASE_HERE',
'YOUR_HMAC_KEY_HERE',
'YOUR_SALT_HERE');
class AesCryptoClass {
// These should not change
private $hmacLength = 32;
private $iterations = 1000;
private $keyLength = 32;
private $blockSize = 16;
private $cipher = 'aes-256-cbc';
function __construct($password,$hmacKey,$salt)
{
$this->password = $password;
$this->hmacKey = $hmacKey;
$this->salt = $salt;
}
function encrypt($plainText)
{
$iv = openssl_random_pseudo_bytes(16);
$encryptedBytes = $this->encryptInner($iv, $plainText);
$encryptedMessage = $iv . $encryptedBytes;
$mac = $this->hashMessage($encryptedMessage);
$secureMessage = $mac . $encryptedMessage;
$encryptedText = base64_encode($secureMessage);
return $encryptedText;
}
function decrypt($encryptedText)
{
$secureMessage = base64_decode($encryptedText);
$mac = substr($secureMessage, 0, $this->hmacLength);
$encryptedMessage = substr($secureMessage, $this->hmacLength);
$newMac = $this->hashMessage($encryptedMessage);
if (strcmp($mac, $newMac) !== 0) {
return "";
}
$iv = substr($encryptedMessage,0, $this->blockSize);
$encryptedBytes = substr($encryptedMessage, $this->blockSize);
$plainText = $this->decryptInner($iv, $encryptedBytes);
return $plainText;
}
function encryptInner($iv, $plainText)
{
$encryptionKey = openssl_pbkdf2($this->password, $this->salt, $this->keyLength, $this->iterations);
return openssl_encrypt($plainText, $this->cipher, $encryptionKey, OPENSSL_RAW_DATA, $iv);
}
function decryptInner($iv, $encryptedBytes)
{
$encryptionKey = openssl_pbkdf2($this->password, $this->salt, $this->keyLength, $this->iterations);
return openssl_decrypt($encryptedBytes, $this->cipher, $encryptionKey, OPENSSL_RAW_DATA, $iv);
}
function hashMessage($encryptedMessage)
{
return pack("H*", hash_hmac("sha256", $encryptedMessage, $this->hmacKey));
}
}
This code and the description of the process are also included here at the bottom of the wiki:
https://bitbucket.org/TN_WebShare/webpro-session-sharing-sample/wiki/Session%20Key%20Encryption%20and%20Decryption
I have configured slim to write logs to log files as the standard way. But this is not effective when we want to search large and all the logs at a given time. So I want to write those logs to a separate sqlite DB.
My question is how can I set the log writer to write the messages (as done in the Zend framework) ?
P S: I know that I can create a PDO object and use the queries. But I don't want to change the existing code. Just prefer to set the writer and let the framework do the job for me.
I managed to do this as follows,
Create the sqlite connection
$sqlite = new PDO('sqlite:./logs/log.db');
Create my own LogWritter similar to the framework
<?php
/**
* Description of LogWritter
*
* #author Ruwantha.Lankathilaka
*/
class LogWritter {
protected $sqliteConnection;
public function __construct($connection) {
$this->sqliteConnection = $connection;
}
/**
* Write function will bypass the slim default LogWriter and will return
* last inserted log id which could be used as a reference
*
* #param type $object will get the error message
* #param type $level will get the error levels of \Slim\Log
* #return mix if successfully logged will return the last insert id, else
* will return false
*/
public function write($object,$level) {
//Determine label
$label = 'DEBUG';
$message = (string) $object;
switch ($level) {
case \Slim\Log::FATAL:
$label = 'FATAL';
break;
case \Slim\Log::ERROR:
$label = 'ERROR';
break;
case \Slim\Log::WARN:
$label = 'WARN';
break;
case \Slim\Log::INFO:
$label = 'INFO';
break;
}
$sqliteQuery = "INSERT INTO logs (lable,message) VALUES (:lable,:message)";
$statement = $this->sqliteConnection->prepare($sqliteQuery);
$result = $statement->execute(array(':lable'=>$label,':message'=>$message));
if(!empty($result)){
return $this->sqliteConnection->lastInsertId();
}else{
return false;
}
}
}
Add the LogWritter to the index
Add the LogWritter to the Slim app
$app = new \Slim\Slim(array(
'log.writer' => $logWriter,
'log.enabled' => true,
'log.level' => \Slim\Log::DEBUG,
'debug' => true
));
now you can get the log from app
$retult = $app->log->error('test error');
$result will have the inserted log id false if the log failed
Hope this will help someone in future.
I would like to run a duplicate content check just before firing off an email whcih uses swiftmailer inside my symph2 app to send me dev ERROR log entries.
this functionality sits right next to my error log to database function, where it too has a duplicate check, although that one is much easier, it uses sql.
for this one, i want to maintain the last mail sent body for atleast the next 10 emails sent, so that if my error log goes out of control, it wont keep firing me duplicate emails of the same error.
should i just collect this body onto an object that holds last 10 email bodies, and attach this to the swift mailer class? or is there an easier way, like using something that is already embedded in swift mailer for this kind of post sending use? Or maybe a session..
Edit, i call swift mailer from a backend helper class, so think i can pretty much do anything there so long as its atleast semi-elegant.
EDIT this is a refined version of the method that calls both the persist and firing of email
<?php
class someWierdClass
{
public function addLogAction(Request $request, $persist = TRUE, $addEmail = TRUE)
{
$responseAdd = array();
if ($this->getRequest()->request->all() !== null) {
$data = $this->getRequest()->request->get('data') ? $this->getRequest()->request->get('data') : 'no_data';
$duplicate = $this->getRequest()->request->get('duplicate', null);
}
if ($addEmail) {
$responseAdd[] = 'firedIt';
$this->fireEmailString('You have an error log here. <br>' . $data);
}
if ($persist)
{
$responseAdd[] = 'persistedIt';
$this->persistLog($data, $duplicate);
}
if ($responseAdd)
{
$body = implode(', ', $responseAdd);
return new Response($body);
}
}
}
Log emails in a table and check that there it isn't a duplicate every time you send an email.
To do this, you should create a helper function that queries the emails table for entries who's body matches the body you would like to send. If the query returns nothing, then you know that isn't a duplicate. You would then send the email and log it the database. Otherwise, if it returned (a) record(s), you would send a dev ERROR log entry.
If you would like to only check against the last 10 emails, you would do this by querying for both $body == $new_body and $id >= ($total_rows-10)
You would then inject this into the container and call it using something like this
$this->container->get('helper')->sendEmail($body, $subject, $recipients);
Ok, thanks Dan for the idea as to using the database to do the dup check. If you notice, per your suggestion, i was already doing the dup check, but it made me think. It helped me connect the dots.
What i have done is return the answer if its a duplicate on the response when it does the updating the database, then using that response as a flag to determine if email fires or not. (in my case, i go further to check the updated stamp is at least +1 hour old, as opposed to the 'last 10 emails content' idea)
Heres the code.. Enjoy..
<?php
namespace Acme\AcmeBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller,
Acme\AcmeBundle\Entity\Log,
Symfony\Component\HttpFoundation\JsonResponse,
Symfony\Component\HttpFoundation\Response,
Symfony\Component\Config\Definition\Exception\Exception,
Symfony\Component\HttpFoundation\Request,
Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
class someWierdClass
{
/**
* #var array
*/
protected $senderArray = array('no-reply#yorudomain.com' => 'Your Website Name');
/**
* #param Request $request
* #param bool $persist
* #param bool $addEmail
* #return Response
*/
public function addLogAction(Request $request, $persist = TRUE, $addEmail = TRUE)
{
$responseAdd = array();
if ($this->getRequest()->request->all() !== null) {
$data = $this->getRequest()->request->get('data') ? $this->getRequest()->request->get('data') : 'no_data';
$type = $this->getRequest()->request->get('type') ? $this->getRequest()->request->get('type') : 'no_type';
$duplicate = $this->getRequest()->request->get('duplicate', null);
}
if ($addEmail) {
$responseAdd[] = 'firedIt';
$this->fireEmailString('You have an error log here. <br>' . $data);
}
if ($persist) {
$responseAdd[] = 'persistedIt';
$persistResponse = $this->persistLog( $type = $data, $duplicate);
if ($persistResponse) {
// a dup check is done here and results of this is on the response. (e.g. $content->passesCutoff)
$content = json_decode($persistResponse->getContent());
}
}
if ( $addEmail && ( isset($content->passesCutoff) && $content->passesCutoff ))
{
//fire off an email also, because its kind of hard to look in teh logs all the time, sometimes we just want an email.
$successEmail = $this->fireEmailString($data);
if( ! $successEmail )
{
$responseAdd[] = 'firedIt';
}
}
if ($responseAdd) {
$body = implode(', ', $responseAdd);
return new Response($body);
}
}
/**
* #param $emailStringData
* #param null $emailSubject
* #param null $emailTo
* #return mixed
*/
protected function fireEmailString($emailStringData, $emailSubject = null, $emailTo=null){
$templateName = 'AcmeBundle:Default:fireEmailString.html.twig';
if( ! $emailSubject )
{
$emailSubject = 'An email is being fired to you!' ;
}
if( ! $emailTo )
{
$emailTo = 'youremail#gmail.com';
}
$renderedView = $this->renderView(
$templateName, array(
'body' => $emailStringData,
));
$mailer = $this->get('mailer');
$message = $mailer->createMessage()
->setSubject( $emailSubject)
->setBody($emailStringData, 'text/plain')
->addPart($renderedView, 'text/html')
->setFrom($this->senderArray)
->setSender($this->senderArray)
->setTo($emailTo);
$results = $mailer->send($message);
return $results;
}
/**
* #param $type
* #param $data
* #param $duplicate
* #return JsonResponse
*/
protected function persistLog($type, $data, $duplicate) {
$em = $this->getDoctrine()->getManager();
$count = null;
$passesCutoff = null;
$mysqlNow = new \DateTime(date('Y-m-d G:i:s'));
//only two conditions can satisy here, strings '1' and 'true'.
if($duplicate !== '1' && $duplicate !== 'true' /*&& $duplicate != TRUE*/)
{
//in order to check if its unique we need to get the repo
//returns an object (findByData() would return an array)
$existingLog = $em->getRepository('AcmeBundle:Log')->findOneByData(
array('type' => $type, 'data' => $data)
);
if($existingLog)
{
$timeUpdatedString = strtotime($existingLog->getTimeupdated()->format('Y-m-d H:i:s'));
$cutoffStamp = strtotime('+1 hour', $timeUpdatedString); //advance 1 hour (customize this to the amount of time you want to go by before you consider this a duplicate. i think 1 hour is good)
$passesCutoff = time() >= $cutoffStamp ? TRUE : FALSE; //1 hour later
$count = $existingLog->getUpdatedcount();
$existingLog->setUpdatedcount($count + 1); // '2014-10-11 03:52:20' // date('Y-m-d G:i:s')
$em->persist($existingLog);
}
else
{
//this record isnt found, must be unique
$newLog = new Log(); //load our entity
//set in new values
$newLog->setType($type);
$newLog->setData($data);
$newLog->setUpdatedcount(0);
$newLog->setTimeupdated($mysqlNow);
$em->persist($newLog);
}
}
else
{
//we dont care if unique or not, we just want a new row
$newLog = new Log(); //load our entity
$newLog->setType($type);
$newLog->setData($data);
//time updated has been set to auto update to current timestamp in the schema, test first, then remove this
$newLog->setUpdatedcount(0);
$newLog->setTimeupdated($mysqlNow);
$em->persist($newLog);
}
$em->flush();
$response = new JsonResponse();
$response->setData(
array(
'data' => 'persistedIt',
'existingLog' => $count,
'passesCutoff' => $passesCutoff,
));
return $response;
}
}
In hindsight, i would have just passed the last update timestamp back on the response from the persist method, then do the cutoff calculation inside the fire email method obviously, but the above code does work as a starting point.. :-)
I am trying to send emails with pdf attached.
I have a Command to send a lot of emails and swiftmailer is configured in file spool but I have this error:
PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 523800 bytes) in .....vendor/tcpdf/tcpdf.php on line 4989
My swiftmailer configuration is:
swiftmailer:
transport: "%mailer_transport%"
host: "%mailer_host%"
username: "%mailer_user%"
password: "%mailer_password%"
spool: { type: file, path: "%kernel.root_dir%/spool" }
and my Command is:
class EnviarJustificanteCommand extends ContainerAwareCommand {
protected function configure()
{
$this
->setName('preinscripciones:enviar')
->setDescription('Enviar Justificantes')
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$em = $this->getContainer()->get('doctrine')->getEntityManager();
$textoMailPreinscripcion = "......";
//find alumnos
$preinscritos = $em->getRepository('BackendAlumnosBundle:Historial')->findAlumnosEnviarJustificante();
foreach ($preinscritos as $key => $alumno) {
if ($alumno->getEmail() != null) {
$message = \Swift_Message::newInstance()
->setSubject('PreinscripciĆ³n realizada')
->setFrom(array($this->container->getParameter('fromemail.contact') => '........'))
->setReplyTo($this->container->getParameter('replyto.email'))
->setTo($alumno->getEmail())
->setBody($textoMailPreinscripcion);
// Create your file contents in the normal way, but don't write them to disk
$data = $this->imprmirJustificantePreinscripcionPDF($escuela, $alumno, true);
// Create the attachment with your data
$attachment = \Swift_Attachment::newInstance($data, 'JustificantePreinscripcion.pdf', 'application/pdf');
// Attach it to the message
$message->attach($attachment);
$this->get('mailer')->send($message);
}
//set flag to 0 as sent
foreach ($alumno->getHistorial() as $key => $historial) {
$historial->setEnviarJustificante(false);
$em->persist($alumno);
}
}
$em->flush();
}
}
I don't know why I have configured swiftmailer as type file the memory is exhausted. Some clue?
Thanks in advance!
Swift_Attachment::newInstance() accepts data either as a String or a Stream that implements Swift_OutputByteStream.
You are using a String, which in your case is too big for memory. (Swift performs a base64 encode, which chews up even more memory)
You need to pass your data as a Stream, which enables incremental reads.
In your case you could do this by writing the data to disk, then getting a file resource handle, and wrapping that in an wrapper class.
An example for your Wrapper class is:
class AttachmentStream implements Swift_OutputByteStream
{
protected $resource;
public function __construct($resource)
{
$this->resource = $resource;
}
public function read($length)
{
$string = fread($this->resource, $length);
if(false === $string){
throw new Swift_IoException('Unable to read from stream');
}
return (0 === strlen($string)) ? false : $string;
}
public function setReadPointer($byteOffset)
{
return 0 === fseek($this->resource,$byteOffset);
}
}
You could then call it like:
...
$fp = fopen($file,$mode);
$stream = new AttachmentStream($fp);
$filename = 'JustificantePreinscripcion.pdf';
$contentType = 'application/pdf';
$attach = Swift_Attachment::newInstance($stream,$filename,$contentType);
Looks like it's dying in TCPD while trying to generate a PDF file. Not too surprising; TCPDF is pretty crufty. Give the http://knpbundles.com/KnpLabs/KnpSnappyBundle bundle a try, it uses wkhtmltopdf (http://wkhtmltopdf.org/). Should be considerably faster and is unlikely to bump up against memory restrictions.
I'm just a new to Drupal but i wanna make my site to be authenticated from external login sources. I mean, not to use the current Drupal database to login. Then use one or more external databases.
To make it a little more clear:
User Login Authentication will not use the existing Drupal database.
Then it will use the external database(s)
External Database will be one or more (If User is not found on one external database, then found on another source again.)
I've been trying to hack the Drupal Login but i can't make it yet as the structure is complicated enough. Any solution or suggestion please?
Which files will be needed to modify?
You shouldn't completely discard Drupal's user system for this, but rather piggy tail on it with your own authentication. The way this works is by 1) collecting user credentials, 2) authenticating against your custom sources and 3) registering/logging in an external user.
Collecting credentials
One way of collecting user credentials is by hijacking the user login form. You do this by implementing hook_form_alter() and add your own validation callback.
function mymodule_form_alter(&$form, &$form_state, $form_id) {
if ($form_id == 'user_login') {
$form['#validate'][] = array('mymodule_user_login_validate');
$form['#validate'][] = array('mymodule_user_login_submit');
}
}
The login form will look exactly the same and but it will send the username and password to your callback instead.
The user_login_name_validate() and user_login_final_validate() callbacks are user.module's default validators. They supply user blocking functionality and brute force protection but feel free to leave them out.
Authentication
When the login form is sent you pick up the credentials and authenticate against your custom sources.
function mymodule_user_login_validate($form, &$form_state) {
mymodule_authenticate(
$form_state['values']['name'],
$form_state['values']['name']
);
}
If the credentials doesn't check out you can just call form_set_error() to inform the user of what went wrong. Doing this will also make sure no further validation or submit callbacks are executed.
External users
So if no error is evoked then Drupal will move on to running your submit callback. Then we can run user_external_login_register() to create our external user as a Drupal user.
function mymodule_user_login_submit($form, &$form_state) {
user_external_login_register($form_state['values']['name'], 'mymodule');
}
As the name implies, this function will also log the user in. And that should be it!
There's a module for that
Chances are there is already a module for what you want to do. Always go search for existing contrib modules before starting your own!
define('DRUPAL_ROOT', 'full/path/to/drupal');
$drupal_url = 'http://site.com';
drupal_external_load($drupal_path, $drupal_url);
if ( !($user->uid > 0) ) {
drupal_external_login('user', 'pass');
} else {
print 'user is already logged';
}
function drupal_external_load($drupal_url) {
global $base_url;
// set drupal base_url (if not set set in settings.php)
// because it's used in session name
$base_url = $drupal_url;
// save current path
$current_path = getcwd();
// move to drupal path, because it uses relative path for its includes
chdir(DRUPAL_ROOT);
require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
// to use the drupal global user var (instead of session hack)
// drupal_bootstrap(DRUPAL_BOOTSTRAP_SESSION);
// return the current path
chdir($current_path);
}
// load a drupal user into the user obj
function drupal_external_userload($user_info = array()) {
// Dynamically compose a SQL query:
$query = array();
$params = array();
if (is_numeric($user_info)) {
$user_info = array('uid' => $user_info);
}
elseif (!is_array($user_info)) {
return FALSE;
}
foreach ($user_info as $key => $value) {
if ($key == 'uid' || $key == 'status') {
$query[] = "$key = %d";
$params[] = $value;
}
else if ($key == 'pass') {
$query[] = "pass = '%s'";
$params[] = md5($value);
}
else {
$query[]= "LOWER($key) = LOWER('%s')";
$params[] = $value;
}
}
$result = db_query('SELECT * FROM {users} u WHERE '. implode(' AND ', $query), $params);
if ($user = db_fetch_object($result)) {
$user = drupal_unpack($user);
$user->roles = array();
if ($user->uid) {
$user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
}
else {
$user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
}
$result = db_query('SELECT r.rid, r.name FROM {role} r INNER JOIN {users_roles} ur ON
ur.rid = r.rid WHERE ur.uid = %d', $user->uid);
while ($role = db_fetch_object($result)) {
$user->roles[$role->rid] = $role->name;
}
//user_module_invoke('load', $user_info, $user);
}
else {
$user = FALSE;
}
return $user;
}
// don't send any headers before calling this
function drupal_external_login($username, $password) {
global $user;
if ( $user->uid > 0 ) {
if ( $user->name != $username ) {
drupal_external_logout();
} else {
return true;
}
}
require DRUPAL_ROOT. '/includes/password.inc' ;
$account = user_load_by_name($username);
if ( user_check_password($password, $account) ) {
$user = $account;
drupal_session_regenerate();
return true;
}
return false;
}
function drupal_external_logout() {
global $user;
session_destroy();
$user = drupal_anonymous_user();
}