Using App inventor app to mail from google app engine - android

Using Shival wolfs WolfWebEmail2 in app inventor to send mail via Google app engine and nothing arriving in recipient email.
need to confirm if my code is correct.
not showing any errors on app engine.
Does this look correct for command to run webapp?
application = webapp.WSGIApplication([('/', MainPage), ('/sendemail', sendemail), ('/attach', attachfile)], debug=True)
def main():
run_wsgi_app(application)
Think i have got a bit of small keyboard large finger syndrome.
Many thanks in advance.
OK Zig. Many thanks. Here it is
class sendemail(webapp.RequestHandler):
def process_email(self, data):
outvalue=""
ValidData = False
logging.info("data: %s" %data)
details=data.split("|||")
data = "\"%s\"" %data
if len(details) == 5 or len(details) == 7:
message = mail.EmailMessage()
message.sender = EmailFrom
NewAuthKey = details[0]
EmailTo = details[1]
EmailSubject = details[2]
EmailBody = details[3]
EmailBody = EmailBody.replace("\\t","\t")
if details[4].lower()=="yes" and len(details) == 7:
filename=details[5];
file_id=details[6];
ValidData = True
if ValidData:
if NewAuthKey == AuthKey:
logging.info("Auth Key Valid")
else:
logging.info("Auth Key does not Match")
outvalue = "Auth Key is Invalid"
ValidData = False
if ValidData:
if mail.is_email_valid(EmailTo):
message.to = EmailTo
else:
logging.info("Email Address for TO Address is Invalid")
outvalue = "Email Address for TO Address is Invalid"
ValidData = False
if ValidData:
if len(EmailBody) > 0 and len(EmailSubject) > 0:
message.subject = EmailSubject
message.body = EmailBody
else:
logging.info("Subject or Body was Empty")
outvalue = "Subject or Body was left Empty"
ValidData = False
if ValidData:
if details[4].lower()=="yes":
try:
filedata = db.GqlQuery("SELECT * FROM emailattach WHERE id = :1 LIMIT 1",file_id).get()
if filedata:
message.attachments = [(filename, filedata.blob)]
except Exception, message:
ValidData = False
logging.info("Could not attach file:\n\n "+str(message))
outvalue = "Could not attach file:\n\n "+str(message)
if ValidData:
try:
message.send()
logging.info("Email Sent")
outvalue = "Email Sent"
if details[4].lower()=="yes": ##delete the file once emailed
key = db.GqlQuery("SELECT __key__ FROM emailattach where id = :1", file_id).get()
if key:
db.run_in_transaction(dbSafeDelete,key)
except Exception, message:
logging.info(message)
outvalue = str(message)
self.response.out.write(outvalue)
I hope thats it! new to this.

You've left out the last part of the boiplerplate:
if __name__ == '__main__':
main()
Without it, the first request to each instance won't be processed.

Can you show us the sendemail function?
EDIT
message.sender = EmailFrom
Where is EmailFrom?
Try removing all the validations and check if the email gets sent.
First try running this-
message = mail.EmailMessage(sender="you#domain.com",
subject="Testing")
message.to = "you#domain.com"
message.body = "This is the body!"
message.send()
Change both email addresses to your email.
If it works then check the validation and other parts one at a time.

Related

Android TV Remote Control API [duplicate]

I have been tasked to create an application for android mobile to control an Android TV, preferably the dashboard/landingpage outside of any apps (settings included).
It doesn't really matter if it's via bluetooth or wifi, although I have found that bluetooth is not possible as the HID profile is needed, and that profile is only available on API 28 (I need to support from API 19 up)
There are some apps on the play store that already have this functionality. Most connect via Wifi to the Android TV, also pairing with it.
By analysing the APK Files I found out some options, i.e.
some use the
connectSDK library
others use what seems to be a native google package that I can't seem to find
import com.google.android.tv.support.remote.Discovery;
import com.google.android.tv.support.remote.core.Client;
import com.google.android.tv.remote.BuildInfo;
I found that a couple of years ago the Anymote Protocol could be used as well, but that one only works with Google TV, not Android TV.
The problems I am facing right now is that the connectSDK library isn't being maintained and does not contain any code for Android TV connections.
The native google package cannot be found anywhere, not sure if it's included in a specific Jar file, or maybe some obscured/hidden dependency?
I could try to create a connection to a specific socket with Android TV, I know for example that the ServiceType is "_androidtvremote._tcp." and that the port number is 6466. But I'm not sure what would be the best way to implement this.
What I'm looking for are some pointers or ideas how I could tackle this problem. Maybe some references as well.
EDIT on December 2021: I created a new documentation for the new protocol v2.
EDIT on September 2021: Google is deploying a new version of the "Android TV Remote Control" (from v4.x to v5), and this version is not compatible with the legacy pairing system. For now it's necessary to keep a version < 5 to make it work.
We spent some time to find how to connect and control an Android/Google TV (by reverse engineering), and I'm sharing here the result of our findings. For a more recent/updated version, you can check this wiki page.
I develop in PHP so I'll share the code in PHP (the Java code can be found by decompiling some Android apps using https://github.com/skylot/jadx)
Thanks to #hubertlejaune for his tremendous help.
The Android TV (aka server in this document) should have 2 open ports: 6466 and 6467.
To know more about the Android TV, we can enter the below Linux command:
openssl s_client -connect SERVER_IP:6467 -prexit -state -debug
Which will return some information, including the server's public certificate.
If you only want the server's public certificate:
openssl s_client -showcerts -connect SERVER_IP:6467 </dev/null 2>/dev/null|openssl x509 -outform PEM > server.pem
Pairing
The pairing protocol will happen on port 6467.
Client's certificate
It's required to generate our own (client) certificate.
In PHP we can do it with the below code:
<?php
// the commande line is: php generate_key.php > client.pem
// certificate details (Distinguished Name)
// (OpenSSL applies defaults to missing fields)
$dn = array(
"commonName" => "atvremote",
"countryName" => "US",
"stateOrProvinceName" => "California",
"localityName" => "Montain View",
"organizationName" => "Google Inc.",
"organizationalUnitName" => "Android",
"emailAddress" => "example#google.com"
);
// create certificate which is valid for ~10 years
$privkey = openssl_pkey_new();
$cert = openssl_csr_new($dn, $privkey);
$cert = openssl_csr_sign($cert, null, $privkey, 3650);
// export public key
openssl_x509_export($cert, $out);
echo $out;
// export private key
$passphrase = null;
openssl_pkey_export($privkey, $out, $passphrase);
echo $out;
It will generate a file called client.pem that contains both the public and the private keys for our client.
Connection to the server
You need to open a TLS/SSL connection to the server using port 6467.
In PHP, you could use https://github.com/reactphp/socket:
<?php
use React\EventLoop\Factory;
use React\Socket\Connector;
use React\Socket\SecureConnector;
use React\Socket\ConnectionInterface;
require __DIR__ . '/./vendor/autoload.php';
$host = 'SERVER_IP';
$loop = Factory::create();
$tcpConnector = new React\Socket\TcpConnector($loop);
$dnsResolverFactory = new React\Dns\Resolver\Factory();
$dns = $dnsResolverFactory->createCached('8.8.8.8', $loop);
$dnsConnector = new React\Socket\DnsConnector($tcpConnector, $dns);
$connector = new SecureConnector($dnsConnector, $loop, array(
'allow_self_signed' => true,
'verify_peer' => false,
'verify_peer_name' => false,
'dns' => false,
'local_cert' => 'client.pem'
));
$connector->connect('tls://' . $host . ':6467')->then(function (ConnectionInterface $connection) use ($host) {
$connection->on('data', function ($data) use ($connection) {
$dataLen = strlen($data);
echo "data recv => ".$data." (".strlen($data).")\n";
// deal with the messages received from the server
});
// below we can send the first message
$connection->write(/* first message here */);
}, 'printf');
$loop->run();
?>
Protocol
⚠️ Attention, each message is sent as a JSON string, but with two components/parts:
(first) we send the length of the message (JSON string) on 4 bytes,
(second) we send the message (JSON string) itself.
PAIRING_REQUEST(10)
As soon as we are connected to the server, we send a PAIRING_REQUEST(10) message (type = 10).
The first message to send is:
{"protocol_version":1,"payload":{"service_name":"androidtvremote","client_name":"CLIENT_NAME"},"type":10,"status":200}
The server returns a PAIRING_REQUEST_ACK(11) message with type is 11 and status is 200:
{"protocol_version":1,"payload":{},"type":11,"status":200}
OPTIONS(20)
Then the client replies with a OPTIONS(20) message (type = 20):
{"protocol_version":1,"payload":{"output_encodings":[{"symbol_length":4,"type":3}],"input_encodings":[{"symbol_length":4,"type":3}],"preferred_role":1},"type":20,"status":200}
The server returns a OPTIONS(20) message with type is 20 and status is 200.
CONFIGURATION(30)
Then the client replies with a CONFIGURATION(30) message (type = 30):
{"protocol_version":1,"payload":{"encoding":{"symbol_length":4,"type":3},"client_role":1},"type":30,"status":200}
The server returns a CONFIGURATION_ACK(31) message with type is 31 and status is 200.
🎉 The code appears on the TV screen!
SECRET(40)
Then the client replies with a SECRET(40) message (type = 40):
{"protocol_version":1,"payload":{"secret":"encodedSecret"},"type":40,"status":200}
At this stage, the TV screen shows a code with 4 characters (e.g. 4D35).
To find the encodedSecret:
we use a SHA-256 hash;
we add the client public key's modulus to the hash;
we add the client public key's exponent to the hash;
we add the server public key's modulus to the hash;
we add the server public key's exponent to the hash;
we add the last 2 characters of the code to the hash (in the example it's 35).
The result of the hash is then encoded in base64.
The server returns a SECRET_ACK(41) message with type is 41 and status is 200, as well as an encoded secret that permits to verify – we didn't try to decode it, but it's probably the first 2 characters of the code:
{"protocol_version":1,"payload":{"secret":"encodedSecretAck"},"type":41,"status":200}
PHP Code
(you can find some Java code that produces pretty much the same)
Here is the related PHP code:
<?php
use React\EventLoop\Factory;
use React\Socket\Connector;
use React\Socket\SecureConnector;
use React\Socket\ConnectionInterface;
require __DIR__ . '/./vendor/autoload.php';
$host = 'SERVER_IP';
$loop = Factory::create();
$tcpConnector = new React\Socket\TcpConnector($loop);
$dnsResolverFactory = new React\Dns\Resolver\Factory();
$dns = $dnsResolverFactory->createCached('8.8.8.8', $loop);
$dnsConnector = new React\Socket\DnsConnector($tcpConnector, $dns);
// get the server's public certificate
exec("openssl s_client -showcerts -connect ".escapeshellcmd($host).":6467 </dev/null 2>/dev/null|openssl x509 -outform PEM > server.pem");
$connector = new SecureConnector($dnsConnector, $loop, array(
'allow_self_signed' => true,
'verify_peer' => false,
'verify_peer_name' => false,
'dns' => false,
'local_cert' => 'client.pem'
));
// return the message's length on 4 bytes
function getLen($len) {
return chr($len>>24 & 0xFF).chr($len>>16 & 0xFF).chr($len>>8 & 0xFF).chr($len & 0xFF);
}
// connect to the server
$connector->connect('tls://' . $host . ':6467')->then(function (ConnectionInterface $connection) use ($host) {
$connection->on('data', function ($data) use ($connection) {
$dataLen = strlen($data);
echo "data recv => ".$data." (".strlen($data).")\n";
// the first response from the server is the message's size on 4 bytes (that looks like a char to convert to decimal) – we can ignore it
// only look at messages longer than 4 bytes
if ($dataLen > 4) {
// decode the JSON string
$res = json_decode($data);
// check the status is 200
if ($res->status === 200) {
// check at which step we are
switch($res->type) {
case 11:{
// message to send:
// {"protocol_version":1,"payload":{"output_encodings":[{"symbol_length":4,"type":3}],"input_encodings":[{"symbol_length":4,"type":3}],"preferred_role":1},"type":20,"status":200}
$json = new stdClass();
$json->protocol_version = 1;
$json->payload = new stdClass();
$json->payload->output_encodings = [];
$encoding = new stdClass();
$encoding->symbol_length = 4;
$encoding->type = 3;
array_push($json->payload->output_encodings, $encoding);
$json->payload->input_encodings = [];
$encoding = new stdClass();
$encoding->symbol_length = 4;
$encoding->type = 3;
array_push($json->payload->input_encodings, $encoding);
$json->payload->preferred_role = 1;
$json->type = 20;
$json->status = 200;
$payload = json_encode($json);
$payloadLen = strlen($payload);
$connection->write(getLen($payloadLen));
$connection->write($payload);
break;
}
case 20:{
// message to send:
// {"protocol_version":1,"payload":{"encoding":{"symbol_length":4,"type":3},"client_role":1},"type":30,"status":200}
$json = new stdClass();
$json->protocol_version = 1;
$json->payload = new stdClass();
$json->payload->encoding = new stdClass();
$json->payload->encoding->symbol_length = 4;
$json->payload->encoding->type = 3;
$json->payload->client_role = 1;
$json->type = 30;
$json->status = 200;
$payload = json_encode($json);
$payloadLen = strlen($payload);
$connection->write(getLen($payloadLen));
$connection->write($payload);
break;
}
case 31:{
// when we arrive here, the TV screen displays a code with 4 characters
// message to send:
// {"protocol_version":1,"payload":{"secret":"encodedSecret"},"type":40,"status":200}
$json = new stdClass();
$json->protocol_version = 1;
$json->payload = new stdClass();
// get the code... here we'll let the user to enter it in the console
$code = readline("Code: ");
// get the client's certificate
$clientPub = openssl_get_publickey(file_get_contents("client.pem"));
$clientPubDetails = openssl_pkey_get_details($clientPub);
// get the server's certificate
$serverPub = openssl_get_publickey(file_get_contents("public.key"));
$serverPubDetails = openssl_pkey_get_details($serverPub);
// get the client's certificate modulus
$clientModulus = $clientPubDetails['rsa']['n'];
// get the client's certificate exponent
$clientExponent = $clientPubDetails['rsa']['e'];
// get the server's certificate modulus
$serverModulus = $serverPubDetails['rsa']['n'];
// get the server's certificate exponent
$serverExponent = $serverPubDetails['rsa']['e'];
// use SHA-256
$ctxHash = hash_init('sha256');
hash_update($ctxHash, $clientModulus);
hash_update($ctxHash, $clientExponent);
hash_update($ctxHash, $serverModulus);
hash_update($ctxHash, $serverExponent);
// only keep the last two characters of the code
$codeBin = hex2bin(substr($code, 2));
hash_update($ctxHash, $codeBin);
$alpha = hash_final($ctxHash, true);
// encode in base64
$json->payload->secret = base64_encode($alpha);
$json->type = 40;
$json->status = 200;
$payload = json_encode($json);
$payloadLen = strlen($payload);
$connection->write(getLen($payloadLen));
$connection->write($payload);
break;
}
}
}
}
});
// send the first message to the server
// {"protocol_version":1,"payload":{"service_name":"androidtvremote","client_name":"TEST"},"type":10,"status":200}
$json = new stdClass();
$json->protocol_version = 1;
$json->payload = new stdClass();
$json->payload->service_name = "androidtvremote";
$json->payload->client_name = "interface Web";
$json->type = 10;
$json->status = 200;
$payload = json_encode($json);
$payloadLen = strlen($payload);
// send the message size
$connection->write(getLen($payloadLen));
// send the message
$connection->write($payload);
}, 'printf');
$loop->run();
?>
Send Commands
Now that the client is paired with the server, we'll use port 6466 to send the commands.
Please, note we'll use an array of bytes for the commands.
Configuration message
An initial message must be sent:
[1,0,0,21,0,0,0,1,0,0,0,1,32,3,0,0,0,0,0,0,4,116,101,115,116]
The server will respond with an array of bytes that should start with [1,7,0
Commands
You must send two messages to execute one command.
The format is:
[1,2,0,{SIZE=16},0,0,0,0,0,0,0, {COUNTER} ,0,0,0, {PRESS=0} ,0,0,0,{KEYCODE}]
[1,2,0,{SIZE=16},0,0,0,0,0,0,0,{COUNTER+1},0,0,0,{RELEASE=1},0,0,0,{KEYCODE}]
The {KEYCODE} can be found on https://developer.android.com/reference/android/view/KeyEvent.
For example, if we want to send a VOLUME_UP:
[1,2,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24]
[1,2,0,16,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,24]
PHP Code
And here some PHP code:
<?php
use React\EventLoop\Factory;
use React\Socket\Connector;
use React\Socket\SecureConnector;
use React\Socket\ConnectionInterface;
require __DIR__ . '/./vendor/autoload.php';
$host = 'SERVER_IP';
$loop = Factory::create();
$tcpConnector = new React\Socket\TcpConnector($loop);
$dnsResolverFactory = new React\Dns\Resolver\Factory();
$dns = $dnsResolverFactory->createCached('8.8.8.8', $loop);
$dnsConnector = new React\Socket\DnsConnector($tcpConnector, $dns);
$connector = new SecureConnector($dnsConnector, $loop, array(
'allow_self_signed' => true,
'verify_peer' => false,
'verify_peer_name' => false,
'dns' => false,
'local_cert' => 'client.pem'
));
// convert the array of bytes
function toMsg($arr) {
$chars = array_map("chr", $arr);
return join($chars);
}
// connect to the server
$connector->connect('tls://' . $host . ':6466')->then(function (ConnectionInterface $connection) use ($host) {
$connection->on('data', function ($data) use ($connection) {
// convert the data received to an array of bytes
$dataLen = strlen($data);
$arr = [];
for ($i=0; $i<$dataLen;$i++) {
$arr[] = ord($data[$i]);
}
$str = "[".implode(",", $arr)."]";
echo "data recv => ".$data." ".$str." (".strlen($data).")\n";
// if we receive [1,20,0,0] it means the server sent a ping
if (strpos($str, "[1,20,0,0]") === 0) {
// we can reply with a PONG [1,21,0,0] if we want
// $connection->write(toMsg([1,21,0,0]));
}
else if (strpos($str, "[1,7,0,") === 0) {
// we can send the command, here it's a VOLUME_UP
$connection->write(toMsg([1,2,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24]));
$connection->write(toMsg([1,2,0,16,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,24]));
}
});
// send the first message (configuration) to the server
$arr = [1,0,0,21,0,0,0,1,0,0,0,1,32,3,0,0,0,0,0,0,4,116,101,115,116];
$connection->write(toMsg($arr));
}, 'printf');
$loop->run();
?>
So, I found the answer what I was looking for.
If you are a Google Partner (and only then), and have an account with those privileges, you can simply download the jar file at this location. Documentation can be found there as well and the SDK exists for Android and iOS.
Not much information is available how to use it. But by looking over the different classes it can become clear.

Pyjnius Autoclass with multiple constructor options

I'm trying to use Stripe payments Android SDK in a Kivy app with pyjnius. When trying to initialize Card.java from https://github.com/stripe/stripe-android/blob/v2.1.0/stripe/src/main/java/com/stripe/android/model/Card.java
I get an error 'jnius.jnius.JavaException: No constructor matching your arguments' I think this has to do with the fact the Card.java has multiple constructor options. Here's my code below. (crashes on card = Card(cardNum,expMon,expYear,cvc))
from jnius import autoclass,PythonJavaClass,cast,java_method
Stripe = autoclass('com.stripe.android.Stripe')
Card = autoclass('com.stripe.android.model.Card')
Token = autoclass('com.stripe.android.model.Token')
TokenCallback = autoclass('com.stripe.android.TokenCallback')
class StripeTokenCallback(PythonJavaClass):
__javainterfaces__ = ('com.stripe.android.TokenCallback',)
#java_method('([Lcom.stripe.android.model.Token;)V')
def onSuccess(self,token):
print 'printing token debug'
print token
Cipher = AESCipher.AESCipher(_key)
msg = '{"client_nonce:"' + token + '"}'
print msg
encMsg = Cipher.encrypt(msg)
rsp = connectToServer(_host, _port, encMsg)
decRsp = Cipher.decrypt(rsp)
pass
#java_method('[Ljava.lang.Exception;)V')
def onError(self,error):
print 'Error - Debug'
print error
pass
class StripeToken():
def __init__(self):
pass
def genToken(self,token,cardNum,expMon,expYear,cvc):
card = Card(cardNum,expMon,expYear,cvc)
if not card.validateCard():
print 'Card Not Valid'
return False
stripe = Stripe("pk_test_6pRNASCoBOKtIshFeQd4XMUh")
token_cb = StripeTokenCallback()
stripe.createToken(card,token_cb)
Fixed by casting the input variables to strings and integers
from jnius import autoclass,PythonJavaClass,cast,java_method
Integer = autoclass('java.lang.Integer')
String = autoclass('java.lang.String')
Stripe = autoclass('com.stripe.android.Stripe')
Card = autoclass('com.stripe.android.model.Card')
Token = autoclass('com.stripe.android.model.Token')
TokenCallback = autoclass('com.stripe.android.TokenCallback')
class StripeTokenCallback(PythonJavaClass):
__javainterfaces__ = ('com.stripe.android.TokenCallback',)
#java_method('([Lcom.stripe.android.model.Token;)V')
def onSuccess(self,token):
print 'printing token debug'
print token
Cipher = AESCipher.AESCipher(_key)
msg = '{"client_nonce:"' + token + '"}'
print msg
encMsg = Cipher.encrypt(msg)
rsp = connectToServer(_host, _port, encMsg)
decRsp = Cipher.decrypt(rsp)
pass
#java_method('[Ljava.lang.Exception;)V')
def onError(self,error):
print 'Error - Debug'
print error
pass
def genToken(token,cardNum,expMon,expYear,cvc):
jcardNum = cast('java.lang.String', String(cardNum))
jexpMon = cast('java.lang.Integer', Integer(expMon))
jexpYear = cast('java.lang.Integer', Integer(expYear))
jcvc = cast('java.lang.String', String(cvc))
card = Card(jcardNum,jexpMon,jexpYear,jcvc)
if not card.validateCard():
print 'Card Not Valid'
return False
stripe = Stripe("pk_test_6pRNASCoBOKtIshFeQd4XMUh")
token_cb = StripeTokenCallback()
stripe.createToken(card,token_cb)

Corona SDK - email doesn't send

I'm trying to send an email using native.showPopup, but the email is never sent, below is the code:
function scene:createScene( event )
function sendMail()
local options =
{
to = "yourname#youremail.com",
subject = "Game Result",
isBodyHtml = true,
body = "<html><body>Play Time: <b>10</b> <br> Score: <b>1</b></body></html>"
}
native.showPopup("mail", options)
end
-- add some button to send mail
submitBtn = widget.newButton{
defaultFile="assets/submit.png",
over="assets/submit.png",
width=display.contentWidth/2, height=display.contentHeight/6,
onPress = sendMail
}
end
scene:addEventListener( "createScene", scene )
return scene
and this is the build.settings:
settings = {
android =
{
versionCode = "11",
usesPermissions =
{
"android.permission.INTERNET",
"android.permission.WRITE_EXTERNAL_STORAGE",
"android.permission.ACCESS_FINE_LOCATION",
"android.permission.ACCESS_COARSE_LOCATION",
}
},
}
I've tried in simulator and on my phone, but nothing is working. Can anyone help me?
i dont know why, but i just add some validation and now its work..
here the revision code i made:
function sendMail()
local options =
{
to = "yourname#youremail.com",
subject = "Game Result",
isBodyHtml = true,
body = "<html><body>Play Time: <b>10</b> <br> Score: <b>1</b></body></html>"
}
-- add some validation
-- and this is revision code
local mailSend = native.showPopup("mail", options)
if not mailSend then
native.showAlert( "Alert!", "Mail cannot be send.", { "OK" })
end
end
thx

Air android/mobile: same sharedobject for different apps?

I have two apps, one is a trial version the other the full version of a game, both made with adobe air. While saving data via the sharedobjects solution is no problem, I would like to use "one" savegame for both appsm, so users can keep their progress when upgrading to the full version. I tried around a little. But code like e.g. ...:
SharedObject.getLocal("myApp","/");
... doesnt work. So the question is, is there a way to have two Air apps using the same shared object? Or maybe if not using, at least "read" the shared object of another Air app?
Thanks in advance,
ANB_Seth
The answer is yes, I actually made a game transfer system for iOS and Android via network connection and 6 digit hash the user has to enter in the newly installed app to fetch the SO from the server. You could do this with a simple file stored locally on the SD card or other local storage device.
/**
* send this user's current save data to the server
*/
public function send():void{
var ba:ByteArray = new ByteArray();
// Main.sv.user - is the registerClassAlias object we write/read locally via SharedObject
ba.writeObject(Main.sv.user);
var name:String = Crypto.hash("Random Secrect Salt - typically user score, name, etc.");
// create 6 digit hash
var key:String = Crypto.hash(name).slice(0, 6).toUpperCase();
var request:URLRequest = new URLRequest ( 'https://sharedobject.com/transfer/save/name/'+name+'/key/'+key );
var loader: URLLoader = new URLLoader();
request.contentType = 'application/octet-stream';
request.method = URLRequestMethod.POST;
request.data = ba;
loader.addEventListener(IOErrorEvent.IO_ERROR, function (evt:Event) {
trace("error - network");
onSaveRestoreEvent(1);
});
loader.addEventListener(Event.COMPLETE, function (evt:Event) {
addChild(new BaseDialog("Save Sent To Server", "Your data has been sent to the server. To get this data back from the server " +
"you will need your secret key. Please write this six digit key down:\n"+name));
});
loader.load( request );
}
/**
* create a GET SO dialog
*/
public function receive():void{
var text:Sprite = new Sprite();
var textInput:TextInput = new TextInput();
textInput.width = Constants.SCREEN_WIDTH-100;
textInput.y = -50;
text.addChild(textInput);
var dialog:BaseDialog = new BaseDialog("Enter Secret Save Key", "Please enter your six digit secret save key in the field below, then press \"Get\".\n\n",
"Get", function():void{
text.removeChildren();
var url:String = "https://sharedobject.com/transfer/get/name/"+textInput.text; //servlet url
var request:URLRequest = new URLRequest(url);
//get rid of the cache issue:
var urlVariables:URLVariables = new URLVariables();
urlVariables.nocache = new Date().getTime();
request.data = urlVariables;
request.method = URLRequestMethod.GET;
var loader:URLLoader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.addEventListener(Event.COMPLETE, function (evt:Event) {
var loader:URLLoader = URLLoader(evt.target);
var bytes:ByteArray = loader.data as ByteArray;
bytes.position = 0;
if(bytes.length <= 10 || !(bytes.readObject() is User)){
onSaveRestoreEvent(2);
}else{
try{
bytes.position = 0;
Main.sv.user = (bytes.readObject() as User);
Main.sv.save();
onSaveRestoreEvent(0);
}
catch( e : EOFError ){
onSaveRestoreEvent(3);
}
}
});
loader.addEventListener(IOErrorEvent.IO_ERROR, function (evt:Event) {
trace("error - network");
onSaveRestoreEvent(1);
});
loader.load(request);
},
"Close", function():void{text.removeChildren();}, null, null, text);
dispatchEvent(new CreateBaseDialogEvent(dialog));
}
/**
* called after the restore save system is done
* #param prompt int [0 = complete][1 = error network][2 = error key][3 = error EOF]
*/
private function onSaveRestoreEvent(prompt:int):void{
var dialog:BaseDialog;
if(prompt == 0){
dialog = new BaseDialog("Restore Complete!", "All save data has been restored.");
}else if(prompt == 1){
dialog = new BaseDialog("Network Error!", "Please seek an internet connection and try again.");
}else if(prompt == 2){
dialog = new BaseDialog("Invalid Secret Key!", "The key you've entered seems to be invalid, or the save data has expired on the server. " +
"Data only lasts on the server for 24 hours.");
}else{
dialog = new BaseDialog("Error!", "There was an issue getting the file from the server. Please try the transfer again.");
}
dispatchEvent(new CreateBaseDialogEvent(dialog));
}

LDAP changing user password on Active Directory

I state that I am a complete beginner to LDAP.
I have to let a user change its own password through an Android device. User has NOT administrative privileges.
Using the UnboudId LDAP SDK for Java I'm able to bind to server and get the user entry using this code:
final SocketFactory _socket_factory;
final SSLUtil _ssl_util = new SSLUtil(new TrustAllTrustManager());
try {
_socket_factory = _ssl_util.createSSLSocketFactory();
}
catch (Exception e) {
Log.e(LOG_TAG, "*** Unable to initialize ssl", e);
}
LDAPConnectionOptions _ldap_connection_options = new LDAPConnectionOptions();
_ldap_connection_options.setAutoReconnect(true);
_ldap_connection_options.setConnectTimeoutMillis(30000);
_ldap_connection_options.setFollowReferrals(false);
_ldap_connection_options.setMaxMessageSize(1024*1024);
LDAPConnection _ldap_connection = new LDAPConnection(_socket_factory, _ldap_connection_options, [host ip], 636, [username], [password]);
Filter _filter = Filter.create("(userPrincipalName=" + [username] + ")");
SearchRequest _search_request = new SearchRequest([base DN], SearchScope.SUB, _filter);
_search_request.setSizeLimit(1000);
_search_request.setTimeLimitSeconds(30);
SearchResult _search_result = _connection.search(_search_request);
This works and I get 1 entry and all the relative attributes. Now my task is to change the password [password] with a new [new password].
My attempts:
PasswordModifyExtendedRequest _password_modify_request = new PasswordModifyExtendedRequest([found entry DN], [password], [new password]);
PasswordModifyExtendedResult _password_modify_result = (PasswordModifyExtendedResult)_ldap_connection.processExtendedOperation(_password_modify_request);
This doesn't work due to LDAPException
LDAPException(resultCode=2 (protocol error), errorMessage='0000203D: LdapErr: DSID-0C090C7D, comment: Unknown extended request OID, data 0, vece��', diagnosticMessage='0000203D: LdapErr: DSID-0C090C7D, comment: Unknown extended request OID, data 0, vece��')
Then I've tryed
final Modification _replace_modification = new Modification(ModificationType.REPLACE, "unicodePwd", _get_quoted_string_bytes([new password]));
LDAPResult _result = _connection.modify([found entry DN], _replace_modification);
This doesn't work due to LDAPException
LDAPException(resultCode=50 (insufficient access rights), errorMessage='00000005: SecErr: DSID-031A0F44, problem 4003 (INSUFF_ACCESS_RIGHTS), data 0)
Finally I've tryed
final Modification _delete_old_modification = new Modification(ModificationType.DELETE, "unicodePwd", _get_quoted_string_bytes([password]));
final Modification _add_new_modification = new Modification(ModificationType.ADD, "unicodePwd", _get_quoted_string_bytes([new password]));
final ArrayList<Modification> _modifications = new ArrayList<Modification>();
_modifications.add(_delete_old_modification);
_modifications.add(_add_new_modification);
LDAPResult _result = _connection.modify([found entry DN], _modifications);
This doesn't work due to LDAPException
LDAPException(resultCode=19 (constraint violation), errorMessage='00000005: AtrErr: DSID-03190F00, #1:0: 00000005: DSID-03190F00, problem 1005 (CONSTRAINT_ATT_TYPE), data 0, Att 9005a (unicodePwd)��', diagnosticMessage='00000005: AtrErr: DSID-03190F00, #1: 0: 00000005: DSID-03190F00, problem 1005 (CONSTRAINT_ATT_TYPE), data 0, Att 9005a (unicodePwd) ��')
And now i have no more ideas... Any help will be appreciated, thanks in advance
final Modification _delete_old_modification = new Modification(ModificationType.DELETE, "unicodePwd", ('"' + oldPassword + '"').getBytes("UTF-16LE"));
final Modification _add_new_modification = new Modification(ModificationType.ADD, "unicodePwd", ('"' + newPassword + '"').getBytes("UTF-16LE"));
Did the trick.
Finally, I was able to resolve CONSTRAINT_ATT_TYPE issue in password change. I had set minimum password age as 4 days, so AD wasn’t allowing me to update the password. AD throws generic error CONSTRAINT_ATT_TYPE for all such violations. After setting minimum password age as 0 (None), everything works fine. AD password history also gets updated.
Refer:
http://www.javaxt.com/Tutorials/Windows/How_to_Authenticate_Users_with_Active_Directory
"Minimum password age" was the source of my problem CONSTRAINT_ATT_TYPE

Categories

Resources