Gettint 401 Error in Server Side gcm PHP Script [duplicate] - php

I am using GCM services to push information from server. If I use browser key it shows the sucess mesaage as : {"multicast_id":4849013215736515938,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1348742583011905%2adac3a0f9fd7ecd"}]}
but i did not get any notifications on device.
And if I use server key it shows Unauthorized Error 401. My code is shown below:
$apiKey = "xxxxx";
$registrationIDs = array("xxxxxxxx");
$message = "testing Process";
$url = 'https://android.googleapis.com/gcm/send';
$fields = array(
'registration_ids' => $registrationIDs,
'data' => array("message"=>$message),
);
$headers = array(
'Authorization: key=' . $apiKey,
'Content-Type: application/json'
);
$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_POST, true );
curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt ($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode($fields) );
$result = curl_exec($ch);
if(curl_errno($ch)){ echo 'Curl error: ' . curl_error($ch); }
curl_close($ch);
echo $result;
Please help me for this issue. Thanks in advance.

Did you whitelist the IP of your server? This is not necessary by default for the browser key, but it is for the server key.
You can check it here:
https://code.google.com/apis/console/#project:[YOUR PROJECT NUMBER]:access

Another answer already suggested whitelisting the IP address, which seems kind of obvious. What fixed it for me was whitelisting my IPv6 address. That was the key! I hope this helps someone else.

I tried everything in this thread, and still no luck.
So I checked enabled APIs (APIs and Auth -> APIs, Enabled APIs), and realised that I had enabled Google Cloud Messaging for Chrome not Google Cloud Messaging for Android. As soon as I enabled the latter, it worked immediately.
Double check you have the right API enabled!

As many people wrote, you have to whitelist your server IPV4 and IPV6. If you want only IPV4, add this to your curl php init:
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );

use browser key using option create new browser key and use this key in your code.
https://code.google.com/apis/console/?pli=1#project:42053154xxxx:access

I used to put to the
$apiKey = "xxxxx"
like project number (project id) which i put to Android Client app, but i have been wrong in server have to be appkey from google cloud console, (where you activate Google Cloud Messaging for Android)
ProjectXXX -> APIs & auth -> Registered Apps -> Web App -> Server Key
-> Api key
in my case i had there default project 'Service Account-project' on platform: 'Web' but there was just Hosted Application section and no evidence about any api key.
but when i create new Web Apps called 'My app' which contained 4 sections OAuth 2.0 Client ID, Certificate, Server Key, Browser Key i finaly find the api key;-)
may just me who don't know it but i believe this answer could also helps to some one

for me the problem was that you have to enable the api. Having an API set up is not enough. Check that Google Cloud Messaging for Android appears on the enabled APIs in APIs-> enabled APIs.
If its not click the API library tab in APIS and enable it.
\

If I am not mistake your APIKEY needs to be base64 encoded.
Also try var_dump($results) to see if you get information then.

Looking at the code, I recognise this is from a php-gcm sample posted somewhere online. Its quite nice and I can assure you that both browser keys and whitelisted ip's (not IPV6 or bas64 apikey) work.
The reason why no message is shown is because the send notification function you're using 'data' => array("message"=>$message)
targets a key of "message", this is what you must pass to your pending intent i.e
notification.setLatestEventInfo(context, title, message, intent);
This will enable the intent to read the message contained under this key..the message itself is obtained from the GCMIntentService method pasted below:
#Override
protected void onMessage(Context context, Intent intent) {
Log.i(TAG, "Received message");
String message = intent.getExtras().getString("message");
Log.d(TAG, "The intent contains: " + intent.getExtras());
displayMessage(context, message);
// notifies user
generateNotification(context, message);
}

Here is the problem: I was using 2 servers, 1 staging and the other the production server. For my staging server, which was hosted using digital ocean, I entered the inet address in the Key for server applications allowed IP addresses, and it worked just fine.
However, it failed when I changed to my inet address from my production server. Instead, I had to use
inet6 addr: /64 Scope:Global for this to work. To get the value if you encounter the same issue, just enter ifconfig, and find the above entry. Enter that value in the allowed IP addresses and it will work fine.

You must have different project than the default one. Create a project & then create a key. Don't use default project.

Had this trouble, I was using GCM (Google cloud messaging). But after Sep 2016, the server key on the GCM will not work, you have to use FCM (firebase cloud messaging). Create new server keys only in the Firebase Console using the Cloud Messaging tab of the Settings panel. I went over to the firebase console console.firebase.google.com (I had not used it yet) and it asked me to import my project. Suddenly there was this new server key back on the GCM console. If using GCM, use the "legacy key" you see listed there.

Make sure you enabled your API Survery Key!

We are looking for solutions in days. More specifically, add this curl option: curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );

Related

Purging Cloudflare cache through their API in php

I came across this problem with a friend's website. It's a Wordpress installation with several plugins. One of those plugins is used to update several images (gathering them from remote locations and storing them locally to save bandwidth). But when running the plugin, the website seemingly refused to display the updated images and continuously gave me the old version which were definetly no longer present on the server.
Browser cache was ruled out quickly as the cause. Wordpress can be a bit tricky, so I checked all other plugins, drop-ins and whether any form of object cache was active. After also ruling that out, it came to me that the hosting provider must be the issue. I didn't know and had to find out that they use Cloudflare as DNS provider to have a valid SSL certificate for their website. However by default Cloudflare also comes with caching which can be quite aggressive.
Since they liked the caching and wanted to keep it turned on, I told my friend to manually purge the cache at Cloudflare. Ta-Da - the updated images were showing just as they should.
So in order to avoid the process of logging into Cloudflare everytime the plugin is called, I was looking for a way to use their API to solve this in a convenient way. I needed some php-code (to integrate into the Wordpress-Plugin)...
I wrote a small and surely improveable php-script which serves exactly this purpose. It uses the given credentials (user-email and API key) to connect to Cloudflare's API. To retrieve the API key:
Login to the Cloudflare account.
Go to My Profile.
Scroll down to API Keys and locate Global API Key.
Click API Key to see your API identifier.
In the first step the script queries the so called Zone-ID which is a unique identifier for the domain you want to control. Since Cloudflare to date gives no option to view this ID in their backend it can only be obtained through an API request.
In the second step we again connect to Cloudflare's API, this time instructing to purge the entire cache for that Zone.
Here's my solution (I put this on the bottom of my plugin updater-script, to run after everything else has finished):
<?php
//Credentials for Cloudflare
$cust_email = ''; //user#domain.tld
$cust_xauth = ''; //retrieved from the backend after loggin in
$cust_domain = ''; //domain.tld, the domain you want to control
if($cust_email == "" || $cust_xauth == "" || $cust_domain == "") return;
//Get the Zone-ID from Cloudflare since they don't provide that in the Backend
$ch_query = curl_init();
curl_setopt($ch_query, CURLOPT_URL, "https://api.cloudflare.com/client/v4/zones?name=".$cust_domain."&status=active&page=1&per_page=5&order=status&direction=desc&match=all");
curl_setopt($ch_query, CURLOPT_RETURNTRANSFER, 1);
$qheaders = array(
'X-Auth-Email: '.$cust_email.'',
'X-Auth-Key: '.$cust_xauth.'',
'Content-Type: application/json'
);
curl_setopt($ch_query, CURLOPT_HTTPHEADER, $qheaders);
$qresult = json_decode(curl_exec($ch_query),true);
curl_close($ch_query);
$cust_zone = $qresult['result'][0]['id'];
//Purge the entire cache via API
$ch_purge = curl_init();
curl_setopt($ch_purge, CURLOPT_URL, "https://api.cloudflare.com/client/v4/zones/".$cust_zone."/purge_cache");
curl_setopt($ch_purge, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_setopt($ch_purge, CURLOPT_RETURNTRANSFER, 1);
$headers = [
'X-Auth-Email: '.$cust_email,
'X-Auth-Key: '.$cust_xauth,
'Content-Type: application/json'
];
$data = json_encode(array("purge_everything" => true));
curl_setopt($ch_purge, CURLOPT_POST, true);
curl_setopt($ch_purge, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch_purge, CURLOPT_HTTPHEADER, $headers);
$result = json_decode(curl_exec($ch_purge),true);
curl_close($ch_purge);
//Tell the user if it worked
if($result['success']==1) echo "Cloudflare Cache successfully purged! Changes should be visible right away.<br>If not try clearing your Browser Cache by pressing \"Ctrl+F5\"";
else echo "Error purging Cloudflare Cache. Please log into Cloudflare and purge manually!";
?>

Slack Slash commands - Not working in private channels or direct messages

I have programmed several slash commands that show a response in public channels without any problems, but they don't show any response in private channels or direct messages.
As shown below, I am using the in_channel response type. Is there any other response type I can use or a workaround so that it works everywhere?
$data = array(
"username" => "My_user",
"channel" => $channel_id,
"response_type" => "in_channel",
"text" => $text,
"mrkdwn" => true,
"icon_url" => $icon_url
);
$json_string = json_encode($data);
$slack_call = curl_init($slack_webhook_url);
curl_setopt($slack_call, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($slack_call, CURLOPT_POSTFIELDS, $json_string);
curl_setopt($slack_call, CURLOPT_CRLF, true);
curl_setopt($slack_call, CURLOPT_RETURNTRANSFER, true);
curl_setopt($slack_call, CURLOPT_HTTPHEADER, array(
"Content-Type: application/json",
"Content-Length: " . strlen($json_string))
);
$result = curl_exec($slack_call);
curl_close($slack_call);
Thanks in advance!
I talked to the Slack team, which was extremely helpful, and we figured out what the problem was. I am sharing it here in case anybody else runs into the same problem.
The problem was not about the commands being used in public or private channels. The person who created the webhook set it so that it would work on a private channel (our testing channel), so it would only work in that channel or in any channels that she was a part of (so, all the public channels). As soon as I added her to a private channel, it would work.
The solution was for the creator of the webhook to edit it (not the code, just the webhook) and set it by default to a public channel (any) instead of a private channel. This made it work in every channel, even direct messages.
This way, I was able to use my original code, which also allows me to change the user icon dynamically, instead of sending a message back.
I hope that helps other people as well!
That does not look like the right approach.
Responding to slash commands
For responding to a slash command you must not send a new message back (e.g. via webhook as in your code example). Instead just respond to the request from Slack with the content of your message.
Example
$message = array (
'response_type' => 'in_channel',
'text' => $text
);
header ('content-type: application/json');
echo json_encode ($message);
that is all you need.
response_type defines if the response can be seen by all members of a channel "in_channel" or only by the issuer of the slash command "ephemeral"
Please see the official documentation for more details and options.
Sending additional messages
You can of course also send a message from your script in response to the slash command. However, if you want to send a message to a private channel please note that the slash command request from Slack will not include the correct channel ID if it is used in a non-public channel. I don't think there is currently any solution or workaround for this.
You can however always send a direct message to the user by using the ID of the user as channel ID.

How to fix InvalidSignatureException in my Amazon SES SendRawEmail PHP code

I read the Amazon SES documentation several times and came up with the below PHP code to send an email via Amazon SES using what they called as HTTPS Query API approach to send emails without using the Amazon AWS SDK for PHP. However I am stuck with InvalidSignatureException and am unable to get past it. It seems like I am making a mistake somewhere probably in (1) creating the request signature or in (2) selecting right data to generate the request signature from. Any help to fix my code is greatly appreciated. My code is available below. It is important that I do this approach without including the Amazon AWS SDK into my PHP code. Thank you.
define("EMAIL_TO", <Amazon SES configured & verified from email id>);
define("EMAIL_FROM", <Amazon SES configured & verified from email id>);
define("SMTP_USERNAME", <AWS ACCESS KEY>);
define("SMTP_PASSWORD", <AWS SECRET ACCESS KEY>);
function amazonsesrawmailcurlpost($strToEmailId, $strEmailSubject, $strEmailBody)
{
$strDateRFC2822 = date("r", time());
$strRawEmailData = "From: ".EMAIL_FROM."\n";
$strRawEmailData.= "Subject: $strEmailSubject\n";
$strRawEmailData.= "\n";
$strRawEmailData.= $strEmailBody;
$strRequestContentPart = "Action=SendRawEmail";
$strRequestContentPart.= "&Destinations.member.1=$strToEmailId";
$strRequestContentPart.= "&RawMessage.Data=".base64_encode($strRawEmailData);
$strRequestContentPart = urlencode($strRequestContentPart);
$nRequestContentPartLength = strlen($strRequestContentPart);
$strSignature = base64_encode(hash_hmac(
"sha256",
$strDateRFC2822,
SMTP_PASSWORD,
TRUE
));
$rCurlToMailer = curl_init();
curl_setopt($rCurlToMailer, CURLOPT_URL, "https://email.us-west-2.amazonaws.com/");
curl_setopt($rCurlToMailer, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($rCurlToMailer,
CURLOPT_HTTPHEADER,
array(
"POST / HTTP/1.1",
"Host: email.us-west-2.amazonaws.com",
"Date: $strDateRFC2822",
"Content-Type: application/x-www-form-urlencoded",
"Content-Length: $nRequestContentPartLength",
"X-Amzn-Authorization: AWS3-HTTPS AWSAccessKeyId=".SMTP_USERNAME.", Algorithm=HmacSHA256, Signature=$strSignature"
)
);
curl_setopt($rCurlToMailer, CURLOPT_POST, TRUE);
curl_setopt($rCurlToMailer, CURLOPT_POSTFIELDS, $strRequestContentPart);
$strMailerResponse = curl_exec($rCurlToMailer);
echo $strMailerResponse;
curl_close($rCurlToMailer);
}
amazonsesrawmailcurlpost(EMAIL_TO, "amazonses test mail", date("Y-m-d H:i:s"));
Signature Version 3 with HTTPS, used by SES, is starkly different than V2 or V4 commonly seen with other AWS services, in that the only thing you sign is the date value.
To create the string to sign, calculate an RFC 2104-compliant HMAC hash with the Date header value, your secret access key as the key, and SHA256 or SHA1 as the hash algorithm.
— http://docs.aws.amazon.com/ses/latest/DeveloperGuide/query-interface-authentication.html
Note also that all the keys and values in a POST with Content-Type: application/x-www-form-urlencoded need to actually be... urlencoded. I don't see this in your code. It isn't going to be related to the error but may cause additional issues later.

How to send in Deals using a web form?

I'm trying to add a new Deal with the Pipedrive API.
To do so I've followed this tutorial: http://support.pipedrive.com/customer/portal/articles/1271064-how-to-send-in-deals-using-a-web-form
But there's something I didn't understand:
"Email API gives your company a special email address you can use to
automate lead generation and adding of new contacts and
organizations."
Where can I get this email address, there's no other mention of it at the tutorial?
Since I'm unable to follow the tutorial I'm trying to add a new deal with cURL, this is the code:
<?php
$deal = array("item_type" => "deal","stage_id" => 1,"title" => "Atendimento Web Site","organization" => "Company","owner" => "johndoe#company.com.br","visible_to" => 2,"person" => array("name" => $nome,"email" => $email,"organization" => $empresa,"phone" => $tel));
$deal_string = json_encode($deal);
$ch = curl_init('https://api.pipedrive.com/v1/deals?api_token=TOKEN');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $deal_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json, charset=UTF-8',
'Content-Length: ' .strlen($deal_string))
);
echo $deal_string;
echo curl_exec($ch);
?>
And this is what I get:
iten sent -> {"item_type":"deal","stage_id":1,"title":"Atendimento","organization":"Company","owner":"owner#mail.com.br","visible_to":2,"person":{"name":"Jo\u00e3o Neto","email":"mail#mail.com.br","organization":"Company 2","phone":"7112345678"}}
return from api -> {"success":false,"error":"Deal title must be given.","data":null,"additional_data":null}
Where's the error?
About the email support it's true that you are mixing two thing, although it was also happened to me the first time. I admit it would seem strange, an API in which you can use emails.
Anyway, I was working on a simple integration between Pipedrive and another platform and I used the full REST API.
I noticed every time you have an error creating a Deal or you make a mistake in the Json (even if title is ok), you always get the same answer "error":"Deal title must be given.". Of courses it won't help you too much.
So, I recommend you to use some tools like RESTClient for Firefox to simplify the problem at the beginning or even Firebug to sniff it from https://developers.pipedrive.com/v1 making use of their tools to understand the request a little bit better. After that, you can do it more complex.
I am putting you a screenshot in which you can see the simplest example. I hope it will be useful for anyone
I'd receive an email from Pipedrive Support with a full anwser.
*Hi,
Thanks for reaching out!
I'm sorry to hear about the trouble!
So you're mixing up two completely separate things. You're sending in the JSON object needed for the Email API into the REST API.
You have 2 options.
You could go full on with the email API. To do this you need to log into your Pipedrive account, navigate to the Settings, Features page and enable the Email API feature. Then click through to the email API page and get the email address you need to send the object to. And then alter your PHP code to send in that object to that email address as a plain text email. No curl or API token needed for that.
You could clean up the data object you're sending in with the REST API. But you need to understand that the REST API works a little different from the Email API. So you can't just send in the person object along with the deal. You would first need to POST in the person with all the details to the persons endpoint and get back the ID. You can then use the person ID in the deals POST.
I hope this helps
Martin Henk | Co-Founder, Head of Customer Support
Pipedrive*

Google GCM server returns Unauthorized Error 401

I am using GCM services to push information from server. If I use browser key it shows the sucess mesaage as : {"multicast_id":4849013215736515938,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1348742583011905%2adac3a0f9fd7ecd"}]}
but i did not get any notifications on device.
And if I use server key it shows Unauthorized Error 401. My code is shown below:
$apiKey = "xxxxx";
$registrationIDs = array("xxxxxxxx");
$message = "testing Process";
$url = 'https://android.googleapis.com/gcm/send';
$fields = array(
'registration_ids' => $registrationIDs,
'data' => array("message"=>$message),
);
$headers = array(
'Authorization: key=' . $apiKey,
'Content-Type: application/json'
);
$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_POST, true );
curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt ($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt( $ch, CURLOPT_POSTFIELDS, json_encode($fields) );
$result = curl_exec($ch);
if(curl_errno($ch)){ echo 'Curl error: ' . curl_error($ch); }
curl_close($ch);
echo $result;
Please help me for this issue. Thanks in advance.
Did you whitelist the IP of your server? This is not necessary by default for the browser key, but it is for the server key.
You can check it here:
https://code.google.com/apis/console/#project:[YOUR PROJECT NUMBER]:access
Another answer already suggested whitelisting the IP address, which seems kind of obvious. What fixed it for me was whitelisting my IPv6 address. That was the key! I hope this helps someone else.
I tried everything in this thread, and still no luck.
So I checked enabled APIs (APIs and Auth -> APIs, Enabled APIs), and realised that I had enabled Google Cloud Messaging for Chrome not Google Cloud Messaging for Android. As soon as I enabled the latter, it worked immediately.
Double check you have the right API enabled!
As many people wrote, you have to whitelist your server IPV4 and IPV6. If you want only IPV4, add this to your curl php init:
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );
use browser key using option create new browser key and use this key in your code.
https://code.google.com/apis/console/?pli=1#project:42053154xxxx:access
I used to put to the
$apiKey = "xxxxx"
like project number (project id) which i put to Android Client app, but i have been wrong in server have to be appkey from google cloud console, (where you activate Google Cloud Messaging for Android)
ProjectXXX -> APIs & auth -> Registered Apps -> Web App -> Server Key
-> Api key
in my case i had there default project 'Service Account-project' on platform: 'Web' but there was just Hosted Application section and no evidence about any api key.
but when i create new Web Apps called 'My app' which contained 4 sections OAuth 2.0 Client ID, Certificate, Server Key, Browser Key i finaly find the api key;-)
may just me who don't know it but i believe this answer could also helps to some one
for me the problem was that you have to enable the api. Having an API set up is not enough. Check that Google Cloud Messaging for Android appears on the enabled APIs in APIs-> enabled APIs.
If its not click the API library tab in APIS and enable it.
\
If I am not mistake your APIKEY needs to be base64 encoded.
Also try var_dump($results) to see if you get information then.
Looking at the code, I recognise this is from a php-gcm sample posted somewhere online. Its quite nice and I can assure you that both browser keys and whitelisted ip's (not IPV6 or bas64 apikey) work.
The reason why no message is shown is because the send notification function you're using 'data' => array("message"=>$message)
targets a key of "message", this is what you must pass to your pending intent i.e
notification.setLatestEventInfo(context, title, message, intent);
This will enable the intent to read the message contained under this key..the message itself is obtained from the GCMIntentService method pasted below:
#Override
protected void onMessage(Context context, Intent intent) {
Log.i(TAG, "Received message");
String message = intent.getExtras().getString("message");
Log.d(TAG, "The intent contains: " + intent.getExtras());
displayMessage(context, message);
// notifies user
generateNotification(context, message);
}
Here is the problem: I was using 2 servers, 1 staging and the other the production server. For my staging server, which was hosted using digital ocean, I entered the inet address in the Key for server applications allowed IP addresses, and it worked just fine.
However, it failed when I changed to my inet address from my production server. Instead, I had to use
inet6 addr: /64 Scope:Global for this to work. To get the value if you encounter the same issue, just enter ifconfig, and find the above entry. Enter that value in the allowed IP addresses and it will work fine.
You must have different project than the default one. Create a project & then create a key. Don't use default project.
Had this trouble, I was using GCM (Google cloud messaging). But after Sep 2016, the server key on the GCM will not work, you have to use FCM (firebase cloud messaging). Create new server keys only in the Firebase Console using the Cloud Messaging tab of the Settings panel. I went over to the firebase console console.firebase.google.com (I had not used it yet) and it asked me to import my project. Suddenly there was this new server key back on the GCM console. If using GCM, use the "legacy key" you see listed there.
Make sure you enabled your API Survery Key!
We are looking for solutions in days. More specifically, add this curl option: curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );

Categories