we want to create SIP application on Android 2.3.3 and have some issues with android.sip stack (default sip stack). Our mobile app sends register sip packet, but
1.) by default OpenIMS core responds 400 Bad request P-Visited-Network-ID Header missing
2.) in the case that we set port number to 4060 -PCSCF /builder.setPort(4060)/ OpenIMS core sends this request from 4060 to 4060 (same port, same IP, same CSCF, same packet) and this is cykling until OpenIMS core send respond to mobile app - 504 Server Time-out.
We also tried SipDemo, CSipSimple and we had same problems.
When we tried Monster Communicator or IMSDroid, then it works!
There is one difference between working and problematic applications - working apps send register packet also with Authorization field.
Part of the code:
public SipManager mSipManager = null;
public SipProfile mSipProfile = null;
SipProfile.Builder builder = new SipProfile.Builder(username, domain);
builder.setPassword(password);
builder.setDisplayName(username);
builder.setProfileName(username + "#" + domain);
port = Integer.parseInt(4060);
builder.setProtocol(protocol);
mSipProfile = builder.build();
...
try { mSipManager.open(mSipProfile);} catch (SipException e) { ...}
try {
mSipManager.register(mSipProfile, 30, new SipRegistrationListener(){
public void onRegistering(String localProfileUri) {
}
public void onRegistrationDone(String localProfileUri, long expiryTime) {
}
public void onRegistrationFailed(String localProfileUri, int errorCode, String errorMessage) {
}
});
} catch (SipException e) {
....
}
How to give authorization field to register packet in classic SIP stack?
We also tried J-SIP but it display error: Conversion to dalvik format failed with error 1.
Every answer would be very appreciated.
Your problem is not related to missing Authorization header.
Registration is done in the following matter:
the client send Register request without "Authorization" header.
server response with 401 response code which includes an header named "WWW-Authnticate", that header hold parameters as realm, opaque, qop and hashing algorithm type.
using these parameters with the username and passord an Authorication header is generated automatically by SIP stacks. and a second Register request is sent which includes the "Authorication" header.
the if the request is send in the correct manner the server return 200 OK response code which means that you are now registered.
Your problem is something else, you don't even get to step 3 (Authorization step), you fail in step 1, for your initial Register request you receive 400 Bad Request response code - which almost always mean that you have a syntax error in your request.
Related
I've been trying to send a POST request to a php script in order to update the value of a table in a MySQL database. I've been using RetroFit of late and have used it to send quite a few POST request for POST based operations. I don't know if my problem occurs because I am violating HTTP best practices which would recommend to use PUT for update operations but I have also tried sending a PUT request from the Client although it had the same outcome. The database values were not changed when I checked them. Here is the code where I send the HTTP Post Request from RetroFit.
final JsonDataAPI API = RetroFitClient.getInstance().getAPI();
Map<String, String> voteParams = new HashMap<String, String>();
voteParams.put("pollID", String.valueOf(pollID));
voteParams.put("optText", rb1.getTag().toString());
Call <ArrayList<PollOpt>> voteCall = API.makeVote(voteParams);
voteCall.enqueue(new Callback<ArrayList<PollOpt>>() {
#Override
public void onResponse(Call<ArrayList<PollOpt>> call, Response<ArrayList<PollOpt>> response) {
if(!response.isSuccessful()){
Toast.makeText(PollPage.this, "Vote Unsuccessful for vote 2",
Toast.LENGTH_SHORT).show();
} else{
Toast.makeText(PollPage.this, "Vote Successful for vote 2",
Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<ArrayList<PollOpt>> call, Throwable t) {
Log.d("TAG", "Vote Failure: " + t.getMessage());
Log.d("TAG", "FailureURL rb2: " + call.request().url().toString());
}
});
The URL of the requests is just fine it even prints the request in the onFailure function which fires as there is no response, although I don't expect a response.
This is the part of the PHP API code the request is getting sent too Vote.php:
'
method = $_SERVER['REQUEST_METHOD'];
switch($method){
case "POST":
if(isset($_POST["optText"]) && isset($_POST["pollID"])){
$optText = $_POST["optText"];
$pollID = (int) $_POST["pollID"];
$query = "UPDATE polloption SET frequency = frequency + 1 WHERE pollID = " . $pollID . " AND optText = '". $optText ."'";
$conn = new mysqli($host, $user, $pass, $database);
if ($conn->connect_error)
die($conn->connect_error);
if(mysqli_query($conn, $query)){
echo "Updated Poll Frequency";
} else{
echo "Failed to Update Poll Frequency";
}
}
break;'
I've tested the PHP code out using AJAX scripts seeing as you can see phps echo data in the AJAX Requests response whereas in Android you are basically working in the dark when sending HTTP Requests with methods other than GET and determining errors without having to individually test each file with an AJAX Request. After testing the server with AJAX scripts multiple times with different parameters, I can't see why the frequency value doesn't update when sending POST requests through RetroFit.
Any advice on this would be much appreciated!
Dont use isset($_POST["optText"]) && isset($_POST["pollID"]) to get Values from Json based body requests just use
$post_body = file_get_contents('php://input');
for php
I am trying to connect signalR from the android client. I have already setup signalR hub and its working properly with javascript client on the browser. javascript client able to sent bearer-token and on the server side, I am able to get user identity.
But android java client is not able to send bearer token on. I am using https://github.com/SignalR/java-client library (As I am not using SIgnalR-core so not using latest SIgnalR core library)
connection = new HubConnection(serverUrl);
connection.getHeaders().put("Authorization","Bearer XYZ");
proxy = connection.createHubProxy(hubName);
When I run this code, I got an error
java.lang.InterruptedException: Operation was canceled
But when I don't send AUthorization header with the request then on server-side SIgnalR OnConnected() method called successfully.
The issue seems to be with sending Authorization header with the request.
For reference, the following is code to show how token authentication is implemented on the server-side
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
map.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
Provider = new QueryStringOAuthBearerProvider()
});
var hubConfiguration = new HubConfiguration
{
Resolver = GlobalHost.DependencyResolver,
};
map.RunSignalR(hubConfiguration);
});
ConfigureAuth(app);
I have tried calling it by removing authorization from the server. Then it called successfully. But not works when called with Authorization header.
When I tried connection without Authorization then on server-side OnCOnnected method called but Context. Identity is null.
android Java code for connecting to SignalR client
Platform.loadPlatformComponent(new AndroidPlatformComponent());
// Create Connection
connection = new HubConnection(serverUrl);
connection.getHeaders().put("Authorization","Bearer XYZ");
// Create Proxy
proxy = connection.createHubProxy(hubName);
// Establish Connection
ClientTransport clientTransport = new
ServerSentEventsTransport(connection.getLogger());
SignalRFuture<Void> signalRFuture = connection.start(clientTransport);
try {
signalRFuture.get();
} catch (InterruptedException e) {
return false;
} catch (ExecutionException e) {
return false;
}
return true;
If you are using Websocket, try this
https://github.com/doctorcareanywhere/java-client
build signalr-client-sdk and import the jar to your project
eg.
implementation files('libs/signalr-client-sdk.jar')
I making a android chat app. Im use the api level 23, the Xmpp Smack lib version 4.1.8, and on server I use ejabberd, version 16.06.
I use this code to build a XMPP connection.
(TIMEOUT = 5000)
private void buildConnection() {
XMPPTCPConnectionConfiguration.Builder builder = XMPPTCPConnectionConfiguration.builder()
.setServiceName(mServiceName)
.setHost(mServiceName)
.setConnectTimeout(TIMEOUT)
.setResource("Smack")
.setUsernameAndPassword(mUsername, mPassword)
.setSendPresence(true)
.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled);
mConnection = new XMPPTCPConnection(builder.build());
mConnection.setPacketReplyTimeout(TIMEOUT);
chatManager = ChatManager.getInstanceFor(mConnection);
chatManager.addChatListener(this);
PingManager pingManager = PingManager.getInstanceFor(mConnection);
pingManager.setPingInterval(5);
pingManager.registerPingFailedListener(this);
ReconnectionManager reconnectionManager = ReconnectionManager.getInstanceFor(mConnection);
reconnectionManager.enableAutomaticReconnection();
mConnection.addConnectionListener(this);
if (mConnection != null) {
DeliveryReceiptManager.getInstanceFor(mConnection).addReceiptReceivedListener(new ReceiptReceivedListener() {
#Override
public void onReceiptReceived(String fromJid, String toJid, String receiptId, Stanza receipt) {
updateMessageStatusReceived(receipt);
Log.i(TAG, "Confirmation stanza Got: " + receipt);
}
});
}
}
(the updateMessageStatusReceived(receipt) method is only to refresh the view).
after that, I connect and authenticate.
Everthing is normal, but when the connection is down, I need to resend the failed messages when the connection is restored. I didn't find any solution or event in Smack to detected if a message fails. Sometimes the delay of confirmation server stanza is high, and my app assumes that message fails but don't, and the other side receive many times the same message.
I build a Message object and send using this code:
try {
Chat chat = chatManager.createChat(chatMessage.getJid());
chat.sendMessage(message);
} catch (SmackException.NotConnectedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
When the connection is lost and I send message, no one exception is throw.
my question is: How I know the message realy fail, to try again?
Thanks, and sorry about the english.
I am trying to connect to FCM using the smack library:
Here's what I've tried. It works, but I get an exception when the connection tries to login.
new Thread(new Runnable(){
XMPPTCPConnectionConfiguration.Builder configBuilder = XMPPTCPConnectionConfiguration.builder();
private Handler umm;
#Override
public void run() {
configBuilder.setSecurityMode(XMPPTCPConnectionConfiguration.SecurityMode.disabled );
configBuilder.setServiceName("fcm-xmpp.googleapis.com");
configBuilder.setHost("fcm-xmpp.googleapis.com");//MAYBE PROBLEM HERE??
configBuilder.setPort(5236);
configBuilder.setCompressionEnabled(false);
configBuilder.setSendPresence(true);
configBuilder.setSocketFactory(SSLSocketFactory.getDefault());
InterfaceClass.FCMconnection = new XMPPTCPConnection(configBuilder.build());
umm = yes;
try {
InterfaceClass.FCMconnection.connect();
Log.v("pony", "white horse");
//InterfaceClass.FCMloggin.start();
android.os.Message y4 = android.os.Message.obtain();
y4.what = LOGINTOFCM;
umm.sendMessage(y4);
//the rest of the thread is just exception handling in catch clauses
Once my handler receives the message I attempt to login with the connection
like this:
try { FCMconnection.login("senderId#gcm.googleapis.com","SERVER_KEY");
Log.d("black","r2d2");
} catch (XMPPException e) {//exception thrown here
e.printStackTrace();
Log.d("black","maity "+e);
I get the following excecption:
"smack.sasl.SASLErrorException: SASLError using X-OAUTH2: incorrect- encoding"
Now from the documentation it says clearly to implement SASL plain mechanism,
but I don't know how? Here's what the documentation says:
"The connection has two important requirements:
You must initiate a Transport Layer Security (TLS) connection. Note that CCS doesn't currently support the STARTTLS extension.
CCS requires a SASL PLAIN authentication mechanism using #gcm.googleapis.com (FCM sender ID) and the Server key as the password, where the sender ID and Server key are the values you gathered when configuring your client app. See the client documentation for your platform for information on obtaining these credentials."
Does anybody have any idea what could be causing this exception? How should I connect to FCM with the smack library?
Thank you for any advice.
As per documentation connecting to FCM over XMPP protocol needs:
1) TLS connection in transport layer, to achieve this create SSLContext using TLS protocol extension
2) Plain SASL protocol, make sure "smack-sasl-javax-4.1.8.jar" is integrated into your build setup. This took me lot of time to figure out
3) Host, service name and port number are correct(refer code snippet below)
Below code snippet works perfectly for me:
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, null, null);
} catch (Exception e) {
//Failed to get default ssl context with TLS enabled... something can't proceed further
}
XMPPTCPConnectionConfiguration.Builder config = XMPPTCPConnectionConfiguration.builder();
config.setConnectTimeout(CONNECTION_TIMEOUT);
config.setSendPresence(true);
config.setCustomSSLContext(sslContext);
config.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled);
config.setServiceName("gcm.googleapis.com");
config.setHost("fcm-xmpp.googleapis.com");
config.setPort(5236);//not production server
config.setDebuggerEnabled(true);
config.setCompressionEnabled(true);
config.setSocketFactory(sslContext.getSocketFactory());
(mConnection = new XMPPTCPConnection(config.build())).addConnectionListener(ConnectionSession.this);
mConnection.setPacketReplyTimeout(REPLY_TIMEOUT);
mConnection.connect();
mConnection.login(userID, password); //use your app server credential here
Smack version 4.8.1 implemented and tested from openfire setup.
Hope this help someone!!
i'm trying to integrate 'Log in with Google' in app that have an android and web component. Everything in the web component is working fine with the following steps:
1. Rendering the view with an anti-forgery token, client id and app name.
$state = md5(rand());
Session::set('state', $state);
$this->view->render('login', array(
'CLIENT_ID' => 'my_web_client_id',
'STATE' => $state,
'APPLICATION_NAME' => 'my_app_name'));
2. When user clicks on the Google's SignIn button, obtain the one-time code from Google's servers and send it to my server.
3. After my server receives the one-time code using https://github.com/google/google-api-php-client to authenticate the user with that code.
if ($_SESSION['state'] != $_POST['state']) { // Where state is the anti-forgery token
return 'some error';
}
$code = $_POST['code'];
$client = new Google_Client();
$client->setApplicationName("my_app_name");
$client->setClientId('my_web_client_id');
$client->setClientSecret('client_secret');
$client->setRedirectUri('postmessage');
$client->addScope("https://www.googleapis.com/auth/urlshortener");
$client->authenticate($code);
$token = json_decode($client->getAccessToken());
// Verify the token
$reqUrl = 'https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=' . $token->access_token;
$req = new Google_Http_Request($reqUrl);
$tokenInfo = json_decode($client->getAuth()->authenticatedRequest($req)->getResponseBody());
// If there was an error in the token info, abort.
if ($tokenInfo->error) {
return 'some error';
}
// Make sure the token we got is for our app.
if ($tokenInfo->audience != "my_web_client_id") {
return 'some error';
}
// Saving user in db
...
// Load the app view
Now, for android client should be something similar, right? Following these tutorials:https://developers.google.com/+/mobile/android/sign-in and http://www.androidhive.info/2014/02/android-login-with-google-plus-account-1/
Executing async task in onConnected method
class CreateToken extends AsyncTask<Void, Void, String> {
#Override
protected String doInBackground(Void... voids) {
oneTimeCode = getOneTimeCode();
String email = getUserGPlusEmail();
try {
// Opens connection and sends the one-time code and email to the server with 'POST' request
googleLogin(oneTimeCode, email);
} catch (IOException e) {
e.printStackTrace();
}
return oneTimeCode;
}
}
private String getOneTimeCode() {
String scopes = "oauth2:server:client_id:" + SERVER_CLIENT_ID + ":api_scope:" + SCOPE_EMAIL;
String code = null;
try {
code = GoogleAuthUtil.getToken(
LoginActivity.this, // Context context
Plus.AccountApi.getAccountName(mGoogleApiClient), // String accountName
scopes // String scope
);
} catch (IOException transientEx) {
Log.e(Constants.TAG, "IOException");
transientEx.printStackTrace();
// network or server error, the call is expected to succeed if you try again later.
// Don't attempt to call again immediately - the request is likely to
// fail, you'll hit quotas or back-off.
} catch (UserRecoverableAuthException e) {
Log.e(Constants.TAG, "UserRecoverableAuthException");
e.printStackTrace();
// Requesting an authorization code will always throw
// UserRecoverableAuthException on the first call to GoogleAuthUtil.getToken
// because the user must consent to offline access to their data. After
// consent is granted control is returned to your activity in onActivityResult
// and the second call to GoogleAuthUtil.getToken will succeed.
startActivityForResult(e.getIntent(), AUTH_CODE_REQUEST_CODE);
} catch (GoogleAuthException authEx) {
// Failure. The call is not expected to ever succeed so it should not be
// retried.
Log.e(Constants.TAG, "GoogleAuthException");
authEx.printStackTrace();
} catch (Exception e) {
throw new RuntimeException(e);
}
Log.e(Constants.TAG, "ONE TIME CODE: " + code);
return code;
}
After obtaining the code successfully, send it to my server for authentication.
And here's the code on the server:
$code = $_POST['code'];
$client = new Google_Client();
$client->setApplicationName("my_app_name");
$client->setClientId('my_web_client_id'); // Web component's client id
$client->setClientSecret('client_secret'); // Web component's secret
$client->addScope("email");
$client->setAccessType("offline");
$client->authenticate($code);
...
And the problem is that authentication works only once every 10-15 minutes. When trying to obtain the one-time code more than once in 10-15 minutes, i get the same code as the last one(Clearly there is something wrong. This happens only with the android client and i'm getting this error: Error fetching OAuth2 access token, message: 'invalid_grant: i'). Couldn't find anyone with the same problem here in SO. Probably i'm doing something wrong, but can't figure out what is it...Any help would be appreciated.
You shouldn't be sending the code each time. On the web this is kind of OK as when you first consent you'll get a code that gives you offline access (you'll see a refresh token in the response when you exchange it) but in future cases you wont. On Android, you get a code that gives you a refresh token every time, which means you'll need to show the consent every time, and you're likely to run into per-user limits or cache issues (as you seem to be).
The magic extra component you need is a thing called an ID token. This you can get easily on both platforms and tells you who the person is. Take a look at this blog post for more: http://www.riskcompletefailure.com/2013/11/client-server-authentication-with-id.html
The limitation with an ID token is that you can't use it to call Google APIs. All it does is give you the Google user ID, the client ID of the app being used and (if email scope is used) the email address. The nice thing is that you can get one really easily on all platforms with less user interaction, and they're cryptographically signed so most of the time you can use them without making any further network calls on the server. If you don't need to make Google API calls (because you're just using it for auth) this is the best thing to use by far - given that you're just getting the email, I would be inclined to stop here.
If you need to make Google API calls from your server though, then you should use the code - but just once. When you exchange it, you store the refresh token in a database keyed against the user ID. Then, when the user comes back you look up the refresh token and use it to generate a new access token. So the flow would be:
First time:
Android -> Server: id token
Server -> I have no refresh token!
Android -> Server: code
Other times:
Android -> Server: id token
Server - I have a code, and can make calls.
For the web, you can use the same flow or carry on sending the code each time, but you should still keep the refresh token in the database if the response contains one.