Open ajtel opened 1 year ago
Disclaimer : THIS WILL NOT WORK PROPERLY YET IT IS A ROUGH DRAFT DO NOT USE EXCEPT FOR DEVELOPMENT
I just put it together today and much of the to and from fields still needs properly coding from the template and it has not yet been tested. The aim is to get the community started and then correct as we go. DIDWW needs Base64 in its message content but they change it from in to outbound so this needs testing which is why it is not in a pull request as I don't want it accidentally combined.
<?php
namespace FreePBX\modules\Smsconnector\Provider;
class DIDWW extends providerBase
{
public function __construct()
{
parent::__construct();
$this->name = _('DIDWW');
$this->nameRaw = 'didww';
$this->APIUrlInfo = 'https://doc.didww.com/sms/sms-trunks/technical-data/http-specification.html';
$this->APIVersion = 'v1.0';
$this->configInfo = array(
'trunk_url' => array(
'type' => 'string',
'label' => _('HTTP Trunk URL'),
'help' => _("Enter the DIDWW HTTP Trunk URL for sending SMS."),
'default' => 'https://us.sms-out.didww.com/outbound_messages',
'required' => true,
'placeholder' => _('Enter the DIDWW HTTP Trunk URL'),
),
'api_key' => array(
'type' => 'string',
'label' => _('Username'),
'help' => _("Enter the DIDWW Username"),
'default' => '',
'required' => true,
'placeholder' => _('Enter Username'),
),
'api_secret' => array(
'type' => 'string',
'label' => _('Password'),
'help' => _("Enter the Password"),
'default' => '',
'required' => true,
'class' => 'confidential',
'placeholder' => _('Enter Password'),
),
);
}
// parameters from DIDWW
// Available variables for Path, Headers, and Request Body:
// from https://doc.didww.com/sms/sms-trunks/technical-data/http-specification.html
//
// NOTE DIDWW changes parameters from inbound to outbound.
//
// Inbound Standard Headers
// {SMS_UUID} - The ID of the received message.
// {SMS_SRC_ADDR} - Source number of SMS sender. RFC 3986 encoded.
// {SMS_DST_ADDR} - Destination number to which SMS is received. RFC 3986 encoded.
// {SMS_TEXT} - The SMS Message body raw format.
// {SMS_TEXT_BASE64_ENCODED} - The SMS Message body. Encrypted using Base 64 encoding.
// {SMS_TIME} - Message receive date and time. RFC 1123 encoded.
// Accepted Body types
// RAW - the request body will be sent to customers system without changes.
// JSON - the request body will be sent to customers system in JSON format.
// HTML URL encoded - the request body will be sent to customers system in HTML URL Encoded format.
// HTML Multipart - the request body will be sent to customers system in HTML Multipart format.
// Outbound headers change from inbound headers as follows
// [SMS_SRC_ADDR} changes to {source}
// {SMS_DST_ADDR} chnages to {destination}
// {SMS_TEXT} changes to {content}
// Passing username and password is undertaken as follows
// curl \
// -u username:password \
// -X POST \
// -H "Content-Type: application/vnd.api+json" \
// https://sms-out.didww.com/outbound_messages
// Example curl string
// curl -i -X POST https://sms-out.didww.com/outbound_messages -H "Content-Type: application/vnd.api+json" -H "Authorization: Basic" --data-raw '{"data": {"attributes": {"content": "Hello World!", "destination": "37041654321", "source": "37041123456"}, "type": "outbound_messages"}}'
// Example Outbound Post
// POST /outbound_messages HTTP/1.1
// Host: sms-out.didww.com
// Content-Type: application/vnd.api+json
// Authorization: Basic
//{
// "data": {
// "type": "outbound_messages",
// "attributes": {
// "destination": "37041654321",
// "source": "37041123456",
// "content": "Hello World!"
// }
// }
//}
// Example callback request
// Notification payload is sent by using HTTP POST method:
// {
// "data": {
// "type": "outbound_message_callbacks",
// "id": "550e8400-e29b-41d4-a716-446655440000",
// "attributes": {
// "time_start": "1997-07-16T19:20:30.45Z",
// "end_time": "2001-07-16T19:20:30.45Z",
// "destination": "37041654321",
// "source": "37041123456",
// "status": "Success",
// "code_id": null,
// "fragments_sent": 7,
// "price": 0.001
// }
// }
// }
public function sendMedia($id, $to, $from, $message='')
{
// We have to send media items as base64-encoded form fields
$base64media = array();
$sql = 'SELECT raw FROM sms_media WHERE mid = :mid';
$stmt = $this->Database->prepare($sql);
$stmt->bindParam(':mid', $id, \PDO::PARAM_INT);
$stmt->execute();
$media = $stmt->fetchAll(\PDO::FETCH_ASSOC);
foreach ($media as $media_item)
{
$base64media[] = base64_encode($media_item['raw']);
}
$req = array(
'source' => $from,
'destination' => $to,
'content' => $message,
);
$this->sendDIDWWms($req, $from, $id);
return true;
}
public function sendMessage($id, $to, $from, $message='')
{
$req = array(
'to' => $to,
'text' => $message,
);
if (strlen($message) <= 160)
{
$req['method'] = 'sendSMS';
}
else
{
$req['method'] = 'sendMMS';
}
$this->sendDIDWWms($req, $from, $id);
return true;
}
private function sendDIDWWms($payload, $from, $mid): void
{
if ((strlen($from) == 11) && (strpos($from, '1') === 0))
{
$from = ltrim($from, '1');
}
if ((strlen($payload['to']) == 11) && (strpos($payload['to'], '1') === 0))
{
$payload['to'] = ltrim($payload['to'], '1');
}
$config = $this->getConfig($this->nameRaw);
$fields = array(
'api_username' => $config['api_key'],
'api_password' => $config['api_secret'],
'method' => $payload['method'],
'did' => $from,
'dst' => $payload['to'],
);
if (! empty($payload['text']))
{
$fields['message'] = $payload['text'];
}
if (! empty($payload['media']))
{
$counter = 1;
foreach ($payload['media'] as $media_item)
{
$fields['media'.$counter] = $media_item;
$counter++;
}
}
// build a multipart/form-data request
$crlf = "\r\n";
$mimeBoundary = '----' . md5(time());
$reqbody = '';
foreach ($fields as $key => $value)
{
$reqbody .= '--' . $mimeBoundary . $crlf;
$reqbody .= 'Content-Disposition: form-data; name="' . $key . '"' . $crlf . $crlf;
$reqbody .= $value . $crlf;
}
$reqbody .= '--' . $mimeBoundary . '--' . $crlf . $crlf;
$headers = array("Content-Type" => "multipart/form-data; boundary=$mimeBoundary");
$url = sprintf('https://sms-out.didww.com/outbound_messages', $this->APIVersion);
$session = \FreePBX::Curl()->requests($url);
try
{
$DIDWWmsResponse = $session->post('', $headers, $reqbody, array());
freepbx_log(FPBX_LOG_INFO, sprintf(_("%s responds: HTTP %s, %s"), $this->nameRaw, $DIDWWmsResponse->status_code, $DIDWWmsResponse->body));
if (! $DIDWWmsResponse->success)
{
throw new \Exception(sprintf(_("HTTP %s, %s"), $DIDWWmsResponse->status_code, $DIDWWmsResponse->body));
}
$this->setDelivered($mid);
}
catch (\Exception $e)
{
throw new \Exception(sprintf(_('Unable to send message: %s'), $e->getMessage()));
}
}
public function callPublic($connector)
{
$return_code = 405;
if ($_SERVER['REQUEST_METHOD'] === "GET")
{
if (strstr($_SERVER['QUERY_STRING'], ';') !== FALSE) // using ; as separator
{
$qs = str_replace(';', '&', $_SERVER['QUERY_STRING']);
parse_str($qs, $sms);
} else {
$sms = $_GET;
}
freepbx_log(FPBX_LOG_INFO, sprintf(_("Webhook (%s) in: %s"), $this->nameRaw, print_r($sms, true)));
$to = $sms['to'];
if (preg_match('/^[2-9]\d{2}[2-9]\d{6}$/', $to)) // ten digit NANP
{
$to = '1'.$to;
}
$from = $sms['from'];
if (preg_match('/^[2-9]\d{2}[2-9]\d{6}$/', $from)) // ten digit NANP
{
$from = '1'.$from;
}
$text = $sms['message'] ?? '';
$emid = $sms['id'];
//$date = $sms['date'];
$media = $sms['media'];
try
{
$msgid = $connector->getMessage($to, $from, '', $text, null, null, $emid);
}
catch (\Exception $e)
{
throw new \Exception(sprintf(_('Unable to get message: %s'), $e->getMessage()));
}
if ($media)
{
$img = file_get_contents($media);
$purl = parse_url($media);
$name = $msgid . basename($purl['path']);
try
{
$connector->addMedia($msgid, $name, $img);
}
catch (\Exception $e)
{
throw new \Exception(sprintf(_('Unable to store MMS media: %s'), $e->getMessage()));
}
}
$connector->emitSmsInboundUserEvt($msgid, $to, $from, '', $text, null, 'Smsconnector', $emid);
$return_code = 202;
}
return $return_code;
}
public function getWebHookUrl()
{
return sprintf("%s", parent::getWebHookUrl());
}
}
Please start a branch and post a pull request. We can work on it there. I won't merge until it's ready.
Can you add DIDWW?