Monday, October 31, 2016

Susurri - The Secret Message Service



Susurri is a working prototype that I had build for TADHack 2016 Kuala Lumpur. By utilizing the Cisco Tropo service to create a platform to let people leave a message with password protected. Then others can retrieve and listen to the message as long as they have the password.



 Below is the one-page slide that I had shared in the TADHack presentation.



Tropo can support multiple programming languages, from Groovy, JavaScript, PHP, python to Ruby. For this prototype, PHP is used and since is using demo account, SIP is used for testing and demo. A gentle reminder on Tropo service, it support wide range of country but not all, Malaysia is one of the country which Tropo not supported.

Hereby I will share the code for Susurri, hope you enjoy your hacking on Tropo!

Tropo Script

<?php
/*********************************
 * Configuration
 *********************************/
$GETSESSIONEP="http://FQDN/v3/getsession.php";
$UDPATERECORDEP="http://FQDN/v3/updateRecord.php";
$SEARCHRECORDEP="http://FQDN/v3/searchRecord.php";
$RECORDFTPBASEURI="ftp://FQDN/";
$RECORDUSER="records@FQDN";
$RECORDPASSWORD="secret";
$RECORDHTTPBASEURI="http://FQDN/records/";

/*********************************
 * Variable
 *********************************/
$revision=48;
$menuResult = 0;
$numberOfRetry = 3;
$passcode = 0;
$recordId = 0;
$recordFileName="";
$callerId=$currentCall->callerID." [".$currentCall->callerName."]";


/*********************************
 * Logic
 *********************************/

// Asking user for action, create a message or listen to message
$menuResult = ask("Welcome to susurri ".$revision."! May I help you? Press 1 for creating a secret message, press 2 for listening to the secret message.", 
    array("choices" => "1,2", 
        "timeout"=>5.0, 
        "mode"=>"dtmf", 
        "attempts"=> $numberOfRetry, 
        "onBadChoice" => "badChoiceFCN")
);


if ($menuResult->value==1) { //Create message

    // Create an record in DB
    $getSessionRespJson = myCurl($GETSESSIONEP);
    $getSessionRespArry = json_decode($getSessionRespJson,true);
    
    // Check for error
    if ($getSessionRespArry['id'] != 'error') {
        $recordId = $getSessionRespArry['id'];
        $recordFileName = $recordId.".wav";
        
        say("Please start to speak your message after the beep sound.");
        
        // Record user voice 
        record("Please press the hash key to stop recording.", array(
            "beep"=>true,
            "terminator"=>"#",
            "asyncUpload"=>true,
            "maxTime"=>60,
            "recordURI"=>$RECORDFTPBASEURI. $recordFileName,
            "recordPassword"=>$RECORDPASSWORD,
            "recordUser"=>$RECORDUSER
       ));
       
       // Ask for password  
       $passcodeResult = ask("Please provide 5 digits passcode for your message.", 
            array("choices" => "[5 DIGITS]", 
                "timeout"=>5.0, 
                "mode"=>"dtmf", 
                "attempts"=> $numberOfRetry, 
                "onBadChoice" => "badChoiceFCN")
        );    
        
        $passcode = $passcodeResult->value;

        // Update DB record
        $getUpdateRespJson = myCurl($UDPATERECORDEP."?id=".urlencode($recordId).
            "&passcode=".urlencode($passcode)."&soundfilename=".urlencode($recordFileName).
            "&callerid=".urlencode($callerId));
            
        $getUpdateRespArry = json_decode($getUpdateRespJson,true);
        
        // Check for update status
        if ($getUpdateRespArry['result'] == 'success') {
            say("Your record ID is ".$recordId. " and passcode is ");
            // Speak by digit by digit rather as whole numeric
            say_as($passcode,"digits");
        } else {
            say("Internal error on case 4");
        }
        
    } else { // If error
        say("Internal error on case 3");
    }
    
} else if ($menuResult->value==2) { // Listen to a message

    // Asking user for passcode
    $listenPasscodeResult = ask("You have choose to listen a secret message, please enter 5 digits passcode.", 
        array("choices" => "[5 DIGITS]", 
            "timeout"=>5.0, 
            "mode"=>"dtmf", 
            "attempts"=> $numberOfRetry, 
            "onBadChoice" => "badChoiceFCN")
    );    
    $listenPasscode = $listenPasscodeResult->value;
    
    say("You have provide the passcode as ");
    // Speak by digit by digit rather as whole numeric
    say_as($listenPasscode,"digits");
    
    
    // Search for recording from database
    $searchRecordRespJson = myCurl($SEARCHRECORDEP."?passcode=".urlencode($listenPasscode));
    $searchRecordRespArry = json_decode($searchRecordRespJson,true);  
    
    if ($searchRecordRespArry['result'] == 'found') { // If found, playback the recording
        say("Message will start play in 2 seconds.");
        wait(2000);
        say($RECORDHTTPBASEURI.$searchRecordRespArry['soundFile']);
    } else if ($searchRecordRespArry['result'] == 'notfound') {
        say("Message with provided passcode not found.");
    } else {
        say("Internal error on case 5");
    }
    
} else {
    say("Internal error on case 2");
}

say("Thank you for using Susurri.");
wait(1000); 

/*********************************
 * UTILS
 *********************************/

// Call back function if user input is not recognize
function badChoiceFCN($event) {
    say("Sorry, your input is not recognize.");
}

// Speak by digit by digit rather as whole numeric
function say_as($value, $type) {
    $ssml_start = "<?xml version='1.0'?><speak>";
    $ssml_end="</say-as></speak>";
    $ssml ="<say-as interpret-as=\"vxml:$type\">$value";
    $complete_string = $ssml_start . $ssml . $ssml_end;
    say($complete_string);
}

// CURL
function myCurl($url) {
    //  Initiate curl
    $ch = curl_init();
    // Disable SSL verification
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    // Will return the response, if false it print the response
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    // Set the url
    curl_setopt($ch, CURLOPT_URL,$url);
    // Execute
    $response=curl_exec($ch);
    // Closing
    curl_close($ch);
    
    return $response;
}
?>;

Server Side Script

base.php - Establish DB connection

<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "susurri";

// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn-&gt;connect_error) {
    die("Connection failed: " . $conn-&gt;connect_error);
} 

?>;

getsession.php - Create a record in database and return id

<?php
include 'base.php';

$sql = "INSERT INTO records (createdBy)
VALUES ('admin')";

if ($conn->query($sql) === TRUE) {
    echo "{\"id\":\"". $conn->insert_id."\"}";
} else {
    echo "{\"id\":\"error\"}";
}

$conn->close();

?>;

searchrecord.php - Search record in database

<?php
include 'base.php';

$passcode=$_REQUEST['passcode'];

$sql = "SELECT soundFile FROM records WHERE passcode=".$passcode;

$result =$conn->query($sql);


if ($result->num_rows > 0) {
 while($row = $result->fetch_assoc()) {
  echo "{\"result\":\"found\", \"soundFile\":\"".$row["soundFile"]."\"}";
  break;
 }
} else {
 echo "{\"result\":\"notfound\"}";
}


$conn->close();
?>;

updaterecord.php - Update record in database

<?php
include 'base.php';

$id=$_REQUEST['id'];
$passcode=$_REQUEST['passcode'];
$soundFilename=$_REQUEST['soundfilename'];
$callerId=$_REQUEST['callerid'];


$sql = "UPDATE records SET passcode=\"".$passcode."\", soundFile=\"".$soundFilename."\", callerId=\"".$callerId."\"  WHERE id=".$id;

if ($conn->query($sql) === TRUE) {
    echo "{\"result\":\"success\"}";
} else {
    echo "{\"result\":\"fail\"}";
}

$conn->close();
?>;

schema.sql - Database schema

CREATE TABLE IF NOT EXISTS `records` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `createdAt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `createdBy` varchar(255) DEFAULT NULL,
  `passcode` varchar(20) DEFAULT NULL,
  `soundFile` varchar(255) DEFAULT NULL,
  `callerId` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `passcod` (`passcode`)
) ENGINE=MyISAM  

1 comment: