be kind, this is my first question and my english is not very good.
I have no problem to deal with standard messages from Nexmo API and I want to receive long SMS just like I do with standard ones (i.e. in one block).
Exemple of data received from Nexmo for a standard SMS:
$_GET['msisdn'] ==> "33612345678" // "from"
$_GET['to'] ==> "33687654321"
$_GET['messageId'] ==> "02000000478EBE09"
$_GET['text'] ==> "Hello world!"
$_GET['type'] ==> "unicode"
$_GET['keyword'] ==> "HELLO"
$_GET['message-timestamp'] ==> "2014-11-25 14:06:58"
Long one: (Nexmo sent it piece by piece)
$_GET['msisdn'] ==> "33612345678" // "from"
$_GET['to'] ==> "33687654321"
$_GET['messageId'] ==> "02000000478EBE09"
$_GET['text'] ==> "the first part of a too long text..."
$_GET['type'] ==> "unicode"
$_GET['keyword'] ==> "THE"
$_GET['message-timestamp'] ==> "2014-11-25 12:06:58"
$_GET['concat'] ==> "true"
$_GET['concat-ref'] ==> "108" // unique identifier for long SMS text
$_GET['concat-total'] ==> "4" // or more, or less...
$_GET['concat-part'] ==> "1" // index of the part, start at 1
See more on the Nexmo documetation: Here
So, I started from a library found on github (Nexmo-PHP-lib) and did this:
(pretty ugly but it's for test purpose)
public function inboundText( $data=null ){
if(!$data) $data = $_GET;
if(!isset($data['text'], $data['msisdn'], $data['to']))
return false;
if(isset($data['concat']) && $data['concat'])
{
session_start();
if ($data['concat-part'] > 1) // first part ?
{
if ($data['concat-total'] == $data['concat-part']) // last part ?
{
// last part ! stock the data in the text and unset now useless $_SESSION entry!
$data['text'] = $_SESSION[(string)$data['concat-ref']] . $data['text'];
unset($_SESSION[(string)$data['concat-ref']]);
}
else // not the first or the last, so concat !
{
// concat the new part in the entry named after the concat-ref
$_SESSION[(string)$data['concat-ref']] .= $data['text'];
return false;
}
}
else // first part ! so creat a $_SESSION entry for that! (named after concat-ref)
{
$_SESSION[(string)$data['concat-ref']] = $data['text'];
return false;
}
}
// Get the relevant data
$this->to = $data['to'];
$this->from = $data['msisdn'];
$this->text = $data['text'];
$this->network = (isset($data['network-code'])) ? $data['network-code'] : '';
$this->message_id = $data['messageId'];
// Flag that we have an inbound message
$this->inbound_message = true;
return true;
}
It works perfectly with local test but not when it's hosted on my heroku server, the $_SESSION array seems to be reset at each part of the sms...
So, have you any idea of how to handle it properly? (and without an ugly temporary SQL table).
How can I retreive the previous part of the message until I receive it completely?
Using a session to store the temporary sms parts would only work if there is an exchange of keys between the client and server with each HTTP request which identifies the session.
In PHP, when you create a session and store a value inside $_SESSION a file is created on the server to store this data (unless you are using a DB session handler, in which case the session data is stored in a database). This data is referenced with an identifier (e.g., PHPSESSID=66dda288eb1947843c2341b4e470fa28) which is normally provided to the client as a cookie. When the client returns at the next HTTP request, the identifier is returned to the server as a cookie value, and the server uses this to reference the same session data.
While your server may be providing the cookie to the Nexmo client when it connects to your endpoint URL, Nexmo is probably not storing the cookie and returning it with its next request. (This is an assumption on my part, but I think it is a safe one—although I can't explain why it works on your local machine but not Heroku. Anyways, it is easy to test—just check if the Nexmo client provides any $_COOKIE value during subsequent requests).
Bottom line: if the Nexmo client doesn't use cookies to save state between requests, you cannot use sessions to save temporary sms parts.
A better option, in any case (because the temporary saved message parts would persist in case of a server reboot), would be to save each temporary part to a small database table. (Don't worry, it's a beautiful temporary SQL table. ;) Here's an example using MySQL:
CREATE TABLE `response_concat_tbl` (
`concat-ref` SMALLINT NOT NULL DEFAULT '0',
`concat-total` TINYINT UNSIGNED NOT NULL DEFAULT '0',
`concat-part` TINYINT UNSIGNED NOT NULL DEFAULT '0',
`text` VARCHAR(160) NOT NULL DEFAULT '',
`added_datetime` DATETIME DEFAULT NULL,
UNIQUE KEY `concat-ref` (`concat-ref`, `concat-part`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='A place to temporarily store multipart SMS messages before they are combined and saved as a whole response.';
Here I've defined a UNIQUE KEY so we can avoid duplicate message parts with a REPLACE INTO clause (in case the Nexmo client tries to submit the same part twice). The added_datetime can be used to clean up orphaned messages in the future (in case the final part of a message is never received).
Now let's insert some sample data:
REPLACE INTO response_concat_tbl (`concat-ref`,`concat-total`,`concat-part`,text,added_datetime) VALUES ('101','4','1','This is',NOW());
REPLACE INTO response_concat_tbl (`concat-ref`,`concat-total`,`concat-part`,text,added_datetime) VALUES ('101','4','2','a multipart',NOW());
REPLACE INTO response_concat_tbl (`concat-ref`,`concat-total`,`concat-part`,text,added_datetime) VALUES ('101','4','4','for you!',NOW());
REPLACE INTO response_concat_tbl (`concat-ref`,`concat-total`,`concat-part`,text,added_datetime) VALUES ('101','4','3','sms message',NOW());
Now we can use MySQL's GROUP_CONCAT function to grab all the parts in one query.
SET SESSION group_concat_max_len = 1000000;
SELECT GROUP_CONCAT(text ORDER BY `concat-part` ASC SEPARATOR ' ') AS text
FROM response_concat_tbl
WHERE `concat-ref` = '101'
GROUP BY `concat-ref`;
We set the group_concat_max_len setting so the total string length may be longer than the default 1024 characters (although that's already lots of message). Here's the result:
+-------------------------------------------------------------+
| GROUP_CONCAT(text ORDER BY `concat-part` ASC SEPARATOR ' ') |
+-------------------------------------------------------------+
| This is a multipart sms message for you! |
+-------------------------------------------------------------+
If you're not using MySQL, you might need to do a bit more work (some duplication check and then a loop) without REPLACE INTO and GROUP_CONCAT.
Here is a full working example using this technique:
class SMS
{
static public function processMultipart($sms)
{
$db =& DB::getInstance();
if (isset($sms['concat']) && $sms['concat']) {
// This sms is part of a multipart message, save it temporarily to the db.
$sms = array_map('trim', $sms);
$db->query("
REPLACE INTO response_concat_tbl (
`concat-ref`,
`concat-total`,
`concat-part`,
`text`,
`added_datetime`
) VALUES (
'" . $db->escapeString($sms['concat-ref']) . "',
'" . $db->escapeString($sms['concat-total']) . "',
'" . $db->escapeString($sms['concat-part']) . "',
'" . $db->escapeString($sms['text']) . "',
NOW()
)
");
if ($sms['concat-total'] > $sms['concat-part']) {
// Not all the parts have been received; return false to signal the fact we don't have a complete message yet.
return false;
}
// Otherwise, it means the last part has just been received. Concatonate all the parts and return it.
// Increase the max length returned by MySQL's GROUP_CONCAT function.
$db->query("SET SESSION group_concat_max_len = 32000");
// Group the sms responses by concat-ref and return them as a concatonated string.
$qid = $db->query("
SELECT GROUP_CONCAT(text ORDER BY `concat-part` ASC SEPARATOR ' ')
FROM response_concat_tbl
WHERE `concat-ref` = '" . $db->escapeString($sms['concat-ref']) . "'
GROUP BY `concat-ref`
");
list($sms['text']) = $db->fetch_row($qid);
// Delete the temporary records.
$db->query("
DELETE FROM response_concat_tbl
WHERE `concat-ref` = '" . $db->escapeString($sms['concat-ref']) . "'
");
}
// If this is not a multipart message, the original sms data is returned. If it is a multipart message, we're returning the fully-concatonated message.
return $sms;
}
}
And here's how to use this function:
// False is returned here if we need to wait for additional parts to arrive. Otherwise, $sms is populated with the final, usable data.
if (false === $sms = SMS::processMultipart($_GET)) {
header($_SERVER['SERVER_PROTOCOL'] . ' 200 OK', true, 200);
die('Waiting for additional message parts');
}
// Do something with $sms['text'], e.g.,
SMSResponse::save($sms['text']);
Related
Assume a MySQL database that has two fields: name and email. Also, assume that both fields need to be securely encrypted at rest due to privacy concerns. And assume that a third field contains a GUID that is the key used to encrypt the name/amail fields. (The third field value is used along with another value as the encryption key to reduce the possibility of having a 'master' key for all encryption. )
The application uses PHP (Version 7.4+) and MySQL.
How would you search the database for a plaintext value (name or email) when the data is encrypted at rest?
Added
Data will be encrypted with openssl_encrypt(). (And changed the above to MySQL.)
How would you search the database for a plaintext value (name or email) when the data is encrypted at rest?
You can't do this in general with arbitrarily encrypted data, but a library like CipherSweet make this really easy.
The documentation should help you get the library installed.
To use it, you'll want to first setup a KeyProvider, then create an EncryptedRow object like so:
<?php
use ParagonIE\CipherSweet\BlindIndex;
use ParagonIE\CipherSweet\CipherSweet;
use ParagonIE\CipherSweet\CompoundIndex;
use ParagonIE\CipherSweet\EncryptedRow;
use ParagonIE\CipherSweet\Backend\FIPSCrypto;
use ParagonIE\CipherSweet\KeyProvider\StringProvider;
// Example key provider
$keyProvider = new StringProvider('4e1c44f87b4cdf21808762970b356891db180a9dd9850e7baf2a79ff3ab8a2fc');
// Setup the engine with a key provider. FIPSCrypto uses OpenSSL.
$engine = new CipherSweet($provider, new FIPSCrypto());
// EncryptedRow object setup:
$rowProcessor = (new EncryptedRow($engine, 'your_table_name_here'))
->addTextField('name')
->addTextField('email');
$rowProcessor->addBlindIndex(
'name',
new BlindIndex(
'my_table__name_literal',
16 /* See below */
)
);
$rowProcessor->addBlindIndex(
'email',
new BlindIndex(
'my_table__email_literal',
16 /* See below */
)
);
In this example, I added two blind indexes with a size of 16 bits each. This is a fudged number; you'll want to look at blind index planning for which values to use for guidance here.
Now you need to update your code when you read/write data into your MySQL database, using $rowProcessor to transparently encrypt/decrypt your data on-the-fly (in diff format):
/** #var array<string, mixed> $inputValues */
- $db->insert('your_table_name_here', $inputValues);
+ [$inputValuesSomeEncrypted, $indices] = $rowProcessor->prepareForStorage($inputValues);
+ // If you wish to store the blind indexes in the same table:
+ $inputValuesSomeEncrypted['name_idx'] = $indices['my_table__name_literal'];
+ $inputValuesSomeEncrypted['email_idx'] = $indices['my_table__email_literal'];
+ $db->insert('your_table_name_here', $inputValuesSomeEncrypted);
Next, you're going to need to tweak for your lookup logic.
- $rows = $db->lookup("name = ? OR email = ?", [$row['name'], $row['email']]);
+ $index1 = $rowProcessor->getBlindIndex('my_table__name_literal', $row);
+ $index2 = $rowProcessor->getBlindIndex('my_table__email_literal', $row);
+ $rowsWithCiphertext = $db->lookup("name_idx = ? OR email_idx = ?", [$index1, $index2]);
+
+ /* We need to post-process to eliminate coincidences in the blind index */
+ $rows = [];
+ foreach ($rowsWithCiphertext as $rowC) {
+ $decrypted = $rowProcessor->decryptRow($rowC);
+ if (!hash_equals($decrypted['name'], $row['name']) && !hash_equals($decrypted['email'], $row['email'])) {
+ continue;
+ }
+ $rows[] = $decrypted;
+ }
The exact code changes necessary will look different based on whatever your original code looks like. I'm just trying to demonstrate the structure here.
This will allow you to store encrypted data in your database yet still use the provided name or email parameters in the WHERE clause of your SQL queries (with a layer of abstraction).
I' creating solr document via solarium plugin in php.
But the all document is stored text_general data type except id field. text_general is the default datatype in solr system.
My doubt is why id field is only to stored string type as default.
And If any possible to add document with string type using solarium plugin.
My code part is here,
public function updateQuery() {
$update = $this->client2->createUpdate();
// create a new document for the data
$doc1 = $update->createDocument();
// $doc1->id = 123;
$doc1->name = 'value123';
$doc1->price = 364;
// and a second one
$doc2 = $update->createDocument();
// $doc2->id = 124;
$doc2->name = 'value124';
$doc2->price = 340;
// add the documents and a commit command to the update query
$update->addDocuments(array($doc1, $doc2));
$update->addCommit();
// this executes the query and returns the result
$result = $this->client2->update($update);
echo '<b>Update query executed</b><br/>';
echo 'Query status: ' . $result->getStatus(). '<br/>';
echo 'Query time: ' . $result->getQueryTime();
}
The result document for the above code is here,
{
"responseHeader":{
"status":0,
"QTime":2,
"params":{
"q":"*:*",
"_":"1562736411330"}},
"response":{"numFound":2,"start":0,"docs":[
{
"name":["value123"],
"price":[364],
"id":"873dfec0-4f9b-4d16-9579-a4d5be8fee85",
"_version_":1638647891775979520},
{
"name":["value124"],
"price":[340],
"id":"7228e92d-5ee6-4a09-bf12-78e24bdfa52a",
"_version_":1638647892102086656}]
}}
This depends on the field type defined in the schema for your Solr installation. It does not have anything to do with how you're sending data through Solarium.
In the schemaless mode, the id field is always set as a string, since a unique field can't be tokenized (well, it can, but it'll give weird, non-obvious errors).
In your case i'd suggest defining the price field as an integer/long field (if it's integers all the way) and the name field as a string field. Be aware that string fields only generate hits on exact matches, so in your case you'd have to search for value124 with exact casing to get a hit.
You can also adjust the multiValued property of the field when you define the fields explicitly. That way you get only the string back in the JSON structure instead of an array containing the string.
I have the following SQLite table
CREATE TABLE keywords
(
id INTEGER PRIMARY KEY,
lang INTEGER NOT NULL,
kwd TEXT NOT NULL,
count INTEGER NOT NULL DEFAULT 0,
locs TEXT NOT NULL DEFAULT '{}'
);
CREATE UNIQUE INDEX kwd ON keywords(lang,kwd);
Working in PHP I typically need to insert keywords in this table, or update the row count if the keyword already exists. Take an example
$langs = array(0,1,2,3,4,5);
$kwds = array('noel,canard,foie gras','','','','','');
I now these data run through the following code
$len = count($langs);
$klen = count($kwds);
$klen = ($klen < $len)?$klen:$len;
$sqlite = new SQLite3('/path/to/keywords.sqlite');
$iStmt = $sqlite->prepare("INSERT OR IGNORE INTO keywords (lang,kwd)
VALUES(:lang,:kwd)");
$sStmt = $sqlite->prepare("SELECT rowid FROM keywords WHERE lang = :lang
AND kwd = :kwd");
if (!$iStmt || !$sStmt) return;
for($i=0;$i < $klen;$i++)
{
$keywords = $kwds[$i];
if (0 === strlen($keywords)) continue;
$lang = intval($langs[$i]);
$keywords = explode(',',$keywords);
for($j=0;$j < count($keywords);$j++)
{
$keyword = $keywords[$j];
if (0 === strlen($keyword)) continue;
$iStmt->bindValue(':kwd',$keyword,SQLITE3_TEXT);
$iStmt->bindValue(':lang',$lang,SQLITE3_INTEGER);
$sStmt->bindValue(':lang',$lang,SQLITE3_INTEGER);
$sStmt->bindValue(':kwd',$keyword,SQLITE3_TEXT);
trigger_error($keyword);
$iStmt->execute();
$sqlite->exec("UPDATE keywords SET count = count + 1 WHERE lang =
'{$lang}' AND kwd = '{$keyword}';");
$rslt = $sStmt->execute();
trigger_error($sqlite->lastErrorMsg());
trigger_error(json_encode($rslt->fetchArray()));
}
}
which generates the following trigger_error output
Keyword: noel
Last error: not an error
SELECT Result: {"0":1,"id":1}
Keyword: canard
Last Error: not an error
SELECT Reult:false
Keyword:foiegras
Last Error: not an error
SELECT Result: false
From the SQLite command line I see that the three row entries are present and correct in the table with the id/rowid columns set to 1, 2 and 3 respectively. lastErrorMsg does not report an error and yet two of the three $rslt->fetchArray() statements are returning false as opposed to an array with rowid/id attributes. So what am I doing wrong here?
I investigated this a bit more and found the underlying case. In my original code the result from the first SQLite3::execute - $iStmt-execute() - was not being assigned to anything. I did not see any particular reason for fetching and interpreting that result. When I changed that line of code to read $rslt = $iStmt->execute() I got the expected result - the rowid/id of the three rows that get inserted was correctly reported.
It is as though internally the PHP SQLite3 extension buffers the result from SQLiteStatement::execute function calls. When I was skipping the assignment my next effort at running such a statement, $sStmt->execute() was in effect fetching the previous result. This is my interpretation without knowing the inner workings of the PHP SQLite3 extension. Perhaps someone who understands the extension better would like to comment.
Add $rslt = NONE; right after trigger_error(json_encode($rslt->fetchArray())); and the correct results appear.
FetchArray can only be called once and somehow php is not detecting that the variable has changed. I also played with changing bindValue to bindParam and moving that before the loop but that is unrelated to the main issue.
It is my opinion that my solution should not work unless there is a bug in php. I am too new at the language to feel confident in that opinion and would like help verifying it. Okay, not a bug, but a violation of the least surprise principle. The object still exists in memory so without finalizing it or resetting the variable, fetch array isn't triggering.
I was working in php
I did the work and sottoiscrizione
facebook api is v.2.2
but now there is a problem
how do I read the updates of the feeds I get ?
The code is :
<?php
//file of program
require_once('LoginFb.php');
require_once('FbClass.php');
require_once('dbClass.php');
require_once('FacebookClass.php');
//receive a Real Time Update
$method = $_SERVER['REQUEST_METHOD'];
// In PHP, dots and spaces in query parameter names are converted to
// underscores automatically. So we need to check "hub_mode" instead
// of "hub.mode".
if ($method == 'GET' && $_GET['hub_mode'] == 'subscribe' &&
$_GET['hub_verify_token'] == 'thisisaverifystring') {
echo $_GET['hub_challenge']; //print the code on the page that Facebook expects to read for confirmation
} else if ($method == 'POST') {
$updates = json_decode(file_get_contents("php://input"), true);
// Replace with your own code here to handle the update
// Note the request must complete within 15 seconds.
// Otherwise Facebook server will consider it a timeout and
// resend the push notification again.
$testo=json_decode($updates["entry"]);
$var=fopen("nome_file.txt","a+");
fwrite($var, "ciao");
fwrite($var, $updates );
fclose($var);
error_log('updates = ' . print_r($updates, true));
}
?>
In the above file "$update" contains an updated feed, but how to extract?
Note: subscription WORKS and updates arrived on my server.
Help me please :)
According to the Facebook documentation [link]:
Note that real-time updates only indicate that a particular field has changed, they do not include the value of those fields. They should be used only to indicate when a new Graph API request to that field needs to be made.
So, you don't get the updated data instead you get the updated feild name. On receiving an update, you should extract the changed field (I have explained this below) and make a new Graph API request to that field. Finally, you will get the updated field data.
How to extract the user name and changed field?
You receive this:
{"entry":[{"id":"****","uid":"****","time":1332940650,"changed_fields":{"status"]}],"object":"user"}
where "id" is my pageId and "changed_fields" is an array of changed fields.
You can extract these as following:
$entry = json_decode($updates["entry"]); <br>
$page = json_decode($entry["uid"]); <br>
$fields = json_decode($entry["changed_fields"]);
Hope it helps! :)
No function -> the respose contain a array of array the correct code is:
$json = json_decode($updates["entry"][0]["uid"], true);
I’m trying to send the results of Vb script to a PHP variable,
The code get the CN name in AD.
My Vb code is below:
Set objADSysInfo = CreateObject("ADSystemInfo")
strUser = objADSysInfo.UserName
Set objUser = GetObject("LDAP://" & strUser)
objUser.CN
How can I send the code to my PHP file as a variable once the webpage is loaded?
You can use an ADO Recordset as a wrapper
Set objADSysInfo = CreateObject("ADSystemInfo")
strUser = objADSysInfo.UserName
Set objUser = GetObject("LDAP://" & strUser)
objUser.CN
ret = send2PHP("yourpage.php?data=", strUser)
function send2PHP(phpURL, data)
on error resume next ' if your php page returns a valid recorset you can omit 'on error
set rsPHP = wscript.createobject("adodb.recordset")
'here you must serialize "data" to be a valid uri value (not done here) '
rsPHP.open phpURL & data ,,,, 256 ' this statment (and the following ones) '
if not rsPHP.eof then send2PHP = rsPHP(0) ' fails if you do not return a valid recordset '
rsPHP.close ' form php script, but at least you '
set rsPHP = nothing ' have the "data" within the php anyway '
end function