Access Outlook email in PHP using imap and OAuth - php

Due to Microsoft removing Basic Auth from IMAP connections, we are converting our PHP code to use microsoft's oauth api and the external library: php imap.
We have managed to successfully get the access_token using the api and provide it to the library like so:
It works fine for two consecutive runs of the script, but for some reason, any time i run the same code after that, it doesn't let us connect, it returns an operation timeout error.
Error:
stream_socket_client(): unable to connect to ssl://outlook.office365.com:993 (Operation timed out) in php-imap/src/Connection/Protocols/Protocol.php:204
Code:
$instance = new ClientManager();
$this->client = $instance->make([
'host' => "outlook.office365.com",
'port' => 993,
'encryption' => 'ssl',
'validate_cert' => false,
'username' => $this->mailboxUsername,
'password' => $access_token,
'protocol' => 'imap',
'authentication' => "oauth",
]);
try {
// Connect to the IMAP Server
$this->client->connect();
}catch (Exception $e) {
echo 'Exception : ', $e->getMessage(), "\n";
}
private function getAccessTokenUsingRefreshToken()
{
$CLIENT_ID = $this->azure_settings['client_id'];
$CLIENT_SECRET = $this->azure_settings['secret_value'];
$TENANT = $this->azure_settings['tenant_id'];
$REFRESH_TOKEN = $this->azure_settings['refresh_token'];
$url = "https://login.microsoftonline.com/$TENANT/oauth2/v2.0/token";
$param_post_curl = [
'client_id' => $CLIENT_ID,
'client_secret' => $CLIENT_SECRET,
'refresh_token' => $REFRESH_TOKEN,
'grant_type' => 'refresh_token'
];
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_POSTFIELDS, http_build_query($param_post_curl));
curl_setopt($ch,CURLOPT_POST, 1);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, true);
// ONLY USE CURLOPT_SSL_VERIFYPEER AT FALSE IF YOU ARE IN LOCALHOST !!!
// NOT IN LOCALHOST ? ERASE IT !
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER, false);
$result = curl_exec($ch);
$access_token = json_decode($result)->access_token;
return $access_token;
}

Related

Set proxy in Guzzle

I have a problem with set proxy in guzzle that a blank page was shown while with curl everything works perfect. The code that I used in guzzle and curl came below.
What is wrong with this code:
Guzzle:
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
require_once "vendor/autoload.php";
try {
$client = new Client();
$request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org');
$response = $client->send($request, [
'timeout' => 30,
'curl' => [
'CURLOPT_PROXY' => '*.*.*.*',
'CURLOPT_PROXYPORT' => *,
'CURLOPT_PROXYUSERPWD' => '*:*',
],
]);
echo '</pre>';
echo($response->getBody());
exit;
} catch (RequestException $e) {
echo $e->getRequest();
if ($e->hasResponse()) {
echo $e->getResponse();
}
}
And The code with CURL:
$url = 'http://httpbin.org';
$ch = curl_init($url);
curl_setopt($ch,CURLOPT_USERAGENT,'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13');
curl_setopt($ch, CURLOPT_PROXY, '*.*.*.*');
curl_setopt($ch, CURLOPT_PROXYPORT, *);
curl_setopt($ch, CURLOPT_PROXYUSERPWD, '*:*');
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$page = curl_exec($ch);
echo $page;
Thanks.
As for Guzzle 6.
Guzzle docs give info about setting proxy for a single request
$client->request('GET', '/', ['proxy' => 'tcp://localhost:8125']);
But you can set it to all requests when initializing client
$client = new Client([
'base_uri' => 'http://doma.in/',
'timeout' => 10.0,
'cookie' => true,
'proxy' => 'tcp://12.34.56.78:3128',
]);
UPD. I don't know why, but I face a strange behaviour. One server with guzzle version 6.2.2 works great with config as above, and the other one with the same version receives 400 Bad Request HTTP error from a proxy. It is solved with another config structure (found in docs for guzzle 3)
$client = new Client([
'base_uri' => 'http://doma.in/',
'timeout' => 10.0,
'cookie' => true,
'request.options' => [
'proxy' => 'tcp://12.34.56.78:3128',
],
]);
for a proxy, if you have the username and password, you can use:
$client = new GuzzleHttp\Client();
$res = $client->request("POST", "https://endpoint.com", [
"proxy" => "http://username:password#192.168.16.1:10",
]);
this worked with guzzle in php.
For Guzzle6, I think the best way is to implement a middleware for setting proxy.
From Guzzle6 docs:
request-options.proxy
handlers-and-middleware
We can set proxy as below:
use Psr\Http\Message\RequestInterface;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\Client;
use GuzzleHttp\Middleware;
use Util\Api;
function add_proxy_callback($proxy_callback) {
return function (callable $handler) use ($proxy_callback) {
return function (RequestInterface $request,$options) use ($handler,$proxy_callback) {
$ip = $proxy_callback();
$options['proxy'] = $ip;
return $handler($request,$options);
};
};
}
$stack = new HandlerStack();
$stack->setHandler(new CurlHandler());
$stack->push(add_proxy_callback(function() {
return Api::getIp(); //function return a ip
}));
$client = new Client(['handler'=>$stack]);
$response = $client->request('GET','http://httpbin.org/ip');
var_dump((string)$response->getBody());
Had the same problem right now , and all i needed to do was use curl array keys as constants instead of strings ..
$response = $client->send($request, [
'timeout' => 30,
'curl' => [
CURLOPT_PROXY => '*.*.*.*',
CURLOPT_PROXYPORT => *,
CURLOPT_PROXYUSERPWD => '*:*',
],
]);
See CURL options Keys , they are not strings anymore.
The procedure for psr-7 may be different, but if you're using
the standard way to instantiate a client,
path\to\project\vendor\guzzlehttp\guzzle\src\Client.php, lines 164-170 includes code to read the environment variables to see if HTTP_PROXY and HTTPS_PROXY are set on the current machine, and if yes, Guzzle will use those values.
Additionally, I had to set my HTTPS_PROXY = http://ip:port (not https), because our workplace proxy seems to handle both https and http requests via the http protocol.
The advantage of this configuration is that you don't have to chnge proxy settings in your source code.
$response = \Drupal::httpClient()->post($settings['base_url'] . 'api/search/', [
'verify' => true,
'body' => $post_data,
'headers' => [
'Content-type' => 'application/json',
],
'curl' => [
CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1_2,
CURLOPT_PROXY => 'proxyip:58080'],
]
)->getBody()->getContents();
Set proxy/https in Guzzle and SSL its work perfect.

OAuth 2.0 in php using curl

I need to get my access_token and refresh_token for OAuth 2.0 to Access Google APIs, the php script below should return a json with access_token, refresh_token like this:
{
"access_token" : "####",
"token_type" : "Bearer",
"expires_in" : 3600,
"refresh_token" : "####"
}
but, the php script return me only this error message:
{
"error" : "invalid_request",
"error_description" : "Client must specify either client_id or client_assertion, not both"
}
I tried to remove client_secret/client_id and use only client_id/client_secret, but still get the same error.
PHP script
$client_id = '###.apps.googleusercontent.com';
$redirect_uri = 'http://localhost/phpConnectToDB/csv/refreshFusionTable.php';
$client_secret = '###';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://accounts.google.com/o/oauth2/token");
$code = $_REQUEST['code'];
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
'code' => $code,
'client_id' => $clientID,
'client_secret' => $clientSecret,
'redirect_uri' => $redirect_uri,
'grant_type' => 'authorization_code'
));
$data = curl_exec($ch);
var_dump($data);
Although curl in cmd works and returns me access and refresh token without any errors.
curl --data "code=###&client_id=###.apps.googleusercontent.com&client_secret=###&redirect_uri=http://localhost/phpConnectToDB/csv/refreshFusionTable.php&grant_type=authorization_code" https://accounts.google.com/o/oauth2/token
I don't understand why I get the missing scheme error, although the .php script exists and it's located on the given path. Could you help me please ?
EDIT Problem with "Invalid parameter value for redirect_uri: Missing scheme" solved, I just replaced 'redirect_uri' => urlencode($redirect_uri), with this 'redirect_uri' => $redirect_uri, in CURLOPT_POSTFIELDS.
Wow, stupid mistake, I should have a rest.
The variables names don't match.
I defined:
$client_id = '###.apps.googleusercontent.com';
$client_secret = '###';
But here I used an non-existing clientID and clientSecret :
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
'code' => $code,
'client_id' => $clientID,
'client_secret' => $clientSecret,
'redirect_uri' => $redirect_uri,
'grant_type' => 'authorization_code'
));
Fixed and working PHP script
$client_id = '###.apps.googleusercontent.com';
$redirect_uri = 'http://localhost/phpConnectToDB/csv/refreshFusionTable.php';
$client_secret = '###';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://accounts.google.com/o/oauth2/token");
curl_setopt($ch, CURLOPT_POST, TRUE);
$code = $_REQUEST['code'];
// This option is set to TRUE so that the response
// doesnot get printed and is stored directly in
// the variable
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
'code' => $code,
'client_id' => $client_id,
'client_secret' => $client_secret,
'redirect_uri' => $redirect_uri,
'grant_type' => 'authorization_code'
));
$data = curl_exec($ch);
var_dump($data);
But I have to say that google provides a little misleading error message here, because I hadn't defined client_id nor client_secret and the error message was:
{
"error" : "invalid_request",
"error_description" : "Client must specify either client_id or client_assertion, not both"
}

Facebook Invalid OAuth access token signature trying to post an attachment to group wall from PHP

I am an administrator (manager role) of a Facebook Group. I created an app, and stored its id and secret.
I want my app to be able to post something on the Facebook group's feed. But when I attempt to post, I get the error 190 Invalid OAuth access token signature, even though I able to successfully obtain the access_token with publish_stream and offline_access scopes. It has the form of NNNNNNNNNNNNNNN|XXXXXXXXXXXXXXXXXXXXXXXXXXX, where N is a number (15) and X is a letter or a number (27).
What should I do more to get this accomplished? Here is the code I am using:
public static function postToFB($message, $image, $link) {
//Get App Token
$token = self::getFacebookToken();
// Create FB Object Instance
$facebook = new Facebook(array(
'appId' => self::fb_appid,
'secret' => self::fb_secret,
'cookie' => true
));
//$token = $facebook->getAccessToken();
//Try to Publish on wall or catch the Facebook exception
try {
$attachment = array('access_token' => $token,
'message' => $message,
'picture' => $image,
'link' => $link,
//'name' => '',
//'caption' => '',
'description' => 'More...',
//'actions' => array(array('name' => 'Action Text', 'link' => 'http://apps.facebook.com/xxxxxx/'))
);
$result = $facebook->api('/'.self::fb_groupid.'/feed/', 'post', $attachment);
} catch (FacebookApiException $e) { //If the post is not published, print error details
echo '<pre>';
print_r($e);
echo '</pre>';
}
}
Code which returns the token
//Function to Get Access Token
public static function getFacebookToken($appid = self::fb_appid, $appsecret = self::fb_secret) {
$args = array(
'grant_type' => 'client_credentials',
'client_id' => $appid,
'client_secret' => $appsecret,
'redirect_uri' => 'https://www.facebook.com/connect/login_success.html',
'scope' => 'publish_stream,offline_access'
);
$ch = curl_init();
$url = 'https://graph.facebook.com/oauth/access_token';
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $args);
try {
$data = curl_exec($ch);
} catch (Exception $exc) {
error_log($exc->getMessage());
}
return json_encode($data);
}
If I uncomment $token = $facebook->getAccessToken(); in the posting code, it gives me yet another error (#200) The user hasn't authorized the application to perform this action.
The token I get using
developers.facebook.com/tools/explorer/ is of another form, much longer and with it I am able to post to the group page feed.
How do I do it without copy/paste from Graph API Explorer and how do I post as a group instead of posting as a user?
Thanks.

php - Facebook Video Upload Curl

To upload the video to Facebook using the following lines.
$video = "http://xxx.com/video.mp4";
$data = array('name' => 'file', 'file' => $video,
'access_token' => $access_token, '_title' => $video_title,
'description' => $video_desc);
$post_url = "https://graph-video.facebook.com/" . $page_id . "/videos";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $post_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$res = curl_exec($ch);
I received an error:
"error":{"message":"(#353) You must select a video file to
upload.","type":"OAuthException","code":353}}
If I change curl to form post it works. Any ideas on why is it so?
Use the path to the video on server instead of the url. So:
$video = "uploads/video.mp4";
Then:
$data = array('name' => 'file', 'file' => '#'.realpath($video),
'access_token' => $access_token, '_title' => $video_title,
'description' => $video_desc);
Notice the use of realpath() following the '#' symbol. Haven't tested with your code but I have a similar implementation and works great. Should do the trick!
For FB SDK4: (see the hardcoded video path, and the encoding).
FB requests the video file to be passed encoded as form-data:
https://developers.facebook.com/docs/graph-api/reference/user/videos/
private function postFBVideo($authResponse, $fileObj, $formData)
{
FacebookSession::setDefaultApplication('yourAppkey', 'yourAppSecret');
$ajaxResponse = '';
try {
$session = new FacebookSession($authResponse->accessToken);
} catch (FacebookRequestException $ex) {
// When Facebook returns an error
$ajaxResponse = 'FB Error ->' . json_encode($ex) ;
} catch (\Exception $ex) {
// When validation fails or other local issues
$ajaxResponse = 'FB Validation Error - ' . json_encode($ex) ;
}
if ($session) {
$response = (new FacebookRequest(
$session, 'POST', '/me/videos', array(
'source' => new CURLFile('videos/81JZrD_IMG_4349.MOV', 'video/MOV'),
'message' => $formDataMessage,
)
))->execute();
$ajaxResponse = $response->getGraphObject();
}
return json_encode($ajaxResponse);
}

SSL Error in payment process using CURL

I'm trying to do the payment gateway integration in php. When i'm doing test mode payment from local payment process is working fine. i have successfully redirected to my payment page.i have used CURL to post the datas to payment gateway server.
But after upload it to server i could not do the payment . I got the following Error.
SSL connect error(35)
My code is as follows.
$request_url= "https://mypaymentserver.com"
$url = $request_url;
$successurl = url::site('payment/textpartnerssuccess', 'http');
$processurl = url::site('payment/textpartnersprocess', 'http');
$failurl = url::site('payment/textpartnersfail', 'http');
//Data bind
$invoiceno = commonfunction::randomkey_generator();
$postData = array(
"url_succesfull" => $successurl,
"url_process" => $processurl,
"url_cancel" => $failurl,
"item_id" => $jobid,
"name" => $jobdetails[0]['job_title'],
"currency" => $this->textpartners_currencycode,
"price" => $amount,
"token" => $invoiceno,
"seller_op_id" => time(),
"shipping_cost" => 0
);
$data = http_build_query($postData, NULL, '&');
// Create a new curl instance
$curl = curl_init();
// Set curl options
curl_setopt_array($curl, array(
CURLOPT_URL => $url,
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => $data,
CURLOPT_SSL_VERIFYPEER => FALSE,
CURLOPT_SSL_VERIFYHOST => FALSE,
CURLOPT_RETURNTRANSFER => TRUE,
));
if (($response = curl_exec($curl)) === FALSE)
{
// Get the error code and message
$code = curl_errno($curl);
$error = curl_error($curl);
curl_close($curl);// Close curl
echo $error_msg = 'Payment API request for failed: '.$error.'(' .$code.')'; exit;
Message::error($error_msg);
// Parse the response
parse_str($response, $data);
}
curl_close($curl); // Close curl
// Parse the response
parse_str($response, $data);
Can any one help me? Thanks in advance :)
But after upload it to server i could not do the payment
Error 35 is reported when the client is unable to connect to the SSL server (as a result of a timeout or a protocol error). Check if the server can resolve the name, make an outgoing connection to the named host, and make an outgoing connection across port 443.
Thanks Guys . Finally i have got the solution by adding the following line in curl.
curl_setopt( $ch, CURLOPT_SSL_CIPHER_LIST, 'rsa_rc4_128_sha' );

Categories