libxml2 sax parser - extracting text nodes - c++

I wanted to extract values from text nodes from an xml input. I have got the following code from web as the official documentation of libxml has many broken links of which sax parser is one. Please help me obtain the value of a text node. in startElementNs when i tried to look for my text node, i get NULL. Appreciate any help here.
My xml looks like this:
<a>
<b>
<c> text values </c>
</b>
</a>
My code looks like this:
#include <stdio.h>
#include <assert.h>
#include <memory.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <string>
class ParseFSM
{
public:
/** SAX2 callback when an element start has been detected by the parser. It provides the namespace informations for the element, as well as the new namespace declarations on the element.
ctx: the user data (XML parser context)
localname: the local name of the element
prefix: the element namespace prefix if available
URI: the element namespace name if available
nb_namespaces: number of namespace definitions on that node
namespaces: pointer to the array of prefix/URI pairs namespace definitions
nb_attributes: the number of attributes on that node
nb_defaulted: the number of defaulted attributes. The defaulted ones are at the end of the array
attributes: pointer to the array of (localname/prefix/URI/value/end) attribute values.
**/
static void startElementNs (void *ctx,
const xmlChar * localname,
const xmlChar * prefix,
const xmlChar * URI,
int nb_namespaces,
const xmlChar ** namespaces,
int nb_attributes,
int nb_defaulted, const xmlChar ** attributes)
{
ParseFSM & fsm = *(static_cast < ParseFSM * >(ctx));
printf ("startElementNs: name = '%s' prefix = '%s' uri = (%p)'%s'\n", localname, prefix, URI, URI);
for (int indexNamespace = 0; indexNamespace < nb_namespaces; ++indexNamespace)
{
const xmlChar *prefix = namespaces[indexNamespace * 2];
const xmlChar *nsURI = namespaces[indexNamespace * 2 + 1];
printf (" namespace: name='%s' uri=(%p)'%s'\n", prefix, nsURI, nsURI);
}
unsigned int index = 0;
for (int indexAttribute = 0; indexAttribute < nb_attributes; ++indexAttribute, index += 5)
{
const xmlChar *localname = attributes[index];
const xmlChar *prefix = attributes[index + 1];
const xmlChar *nsURI = attributes[index + 2];
const xmlChar *valueBegin = attributes[index + 3];
const xmlChar *valueEnd = attributes[index + 4];
std::string value ((const char *) valueBegin, (const char *) valueEnd);
printf (" %sattribute: localname='%s', prefix='%s', uri=(%p)'%s', value='%s'\n", indexAttribute >= (nb_attributes - nb_defaulted) ? "defaulted " : "", localname, prefix, nsURI, nsURI, value.c_str ());
}
}
/** SAX2 callback when an element end has been detected by the parser. It provides the namespace informations for the element.
ctx: the user data (XML parser context)
localname: the local name of the element
prefix: the element namespace prefix if available
URI: the element namespace name if available
**/
static void endElementNs (void *ctx,
const xmlChar * localname,
const xmlChar * prefix, const xmlChar * URI)
{
ParseFSM & fsm = *(static_cast < ParseFSM * >(ctx));
printf ("endElementNs: name = '%s' prefix = '%s' uri = '%s'\n", localname,
prefix, URI);
}
/** Display and format an error messages, callback.
ctx: an XML parser context
msg: the message to display/transmit
...: extra parameters for the message display
*/
static void error (void *ctx, const char *msg, ...)
{
ParseFSM & fsm = *(static_cast < ParseFSM * >(ctx));
va_list args;
va_start (args, msg);
vprintf (msg, args);
va_end (args);
}
/** Display and format a warning messages, callback.
ctx: an XML parser context
msg: the message to display/transmit
...: extra parameters for the message display
*/
static void warning (void *ctx, const char *msg, ...)
{
ParseFSM & fsm = *(static_cast < ParseFSM * >(ctx));
va_list args;
va_start (args, msg);
vprintf (msg, args);
va_end (args);
}
};
int
main (int argc, const char *argv[])
{
std::string xmlIn = "<a><b><c> text values </c> </b> </a>"
/*
* this initialize the library and check potential ABI mismatches
* between the version it was compiled for and the actual shared
* library used.
*/
LIBXML_TEST_VERSION xmlSAXHandler saxHandler; // See http://xmlsoft.org/html/libxml-tree.html#xmlSAXHandler
memset (&saxHandler, 0, sizeof (saxHandler));
// Using xmlSAXVersion( &saxHandler, 2 ) generate crash as it sets plenty of other pointers...
saxHandler.initialized = XML_SAX2_MAGIC; // so we do this to force parsing as SAX2.
saxHandler.startElementNs = &ParseFSM::startElementNs;
saxHandler.endElementNs = &ParseFSM::endElementNs;
saxHandler.warning = &ParseFSM::warning;
saxHandler.error = &ParseFSM::error;
ParseFSM fsm;
int result =
xmlSAXUserParseMemory (&saxHandler, &fsm, xmlIn.c_str (),
int (xmlIn.length ()));
if (result != 0)
{
printf ("Failed to parse document.\n");
return 1;
}
/*
* Cleanup function for the XML library.
*/
xmlCleanupParser ();
/*
* this is to debug memory for regression tests
*/
xmlMemoryDump ();
return 0;
}

You need to use characters callback
void characters( void * user_data,
const xmlChar * ch,
int len);
Strings are not null terminated, u need to use ch,len to determine the string
Another problem with this call back is it can be called multiple times in between start and end element. So u cant blindly assume what you get in call back is string in between the tag. You may need to use string builder or some thing to collect the strings.
In your callback, you will probably want to copy the characters to
some other buffer so that it can be used from the endElement callback.
To optimise this callback a bit, you might adjust the callback so that
it only copies the characters if the parser is in a certain state.
Note that the characters callback may be called more than once between
calls to startElement and endElement.
Hope this answers you, even if its late others might get help

Related

RSA_sign and RSA_verify varied behavior

I have a sample signature generator in C which will create a hash message <count:mac-addr> and generate a signature.
When I use char *message = "120:08:00:27:7c:b6:18";
and sign, the signature is verified successfully.
But when I use
char * generate_hash()
{
xmlDoc *document;
xmlNode *root, *first_child, *node;
char *filename;
char *ap_count;
char *ap_mac_address;
char *message;
filename = "/license.xml";
document = xmlReadFile(filename, NULL, 0);
root = xmlDocGetRootElement(document);
first_child = root->children;
for (node = first_child; node; node = node->next) {
if ( strcmp((char*)node->name, "ap_count") == 0 ) {
ap_count = (char*)xmlNodeGetContent(node);
}
if ( strcmp((char*)node->name, "ap_mac_address") == 0 ){
ap_mac_address = (char*)xmlNodeGetContent(node);
}
}
message = (char *) malloc(strlen(ap_count)+ strlen(ap_mac_address) +1 );
memset(message,0x0,(1 + strlen(ap_count)+ strlen(ap_mac_address)));
strcpy(message,ap_count);
strcat(message,":");
strcat(message,ap_mac_address);
printf(" %d \n", (1 + strlen(ap_count)+ strlen(ap_mac_address)));
return message;
}
--- while verifying,
char* message;
message = generate_hash();
I am using the below function call to generate the signature in both the cases.
if(RSA_sign(NID_sha256, (unsigned char*) message, strlen(message),
signature, &slen, private_key) != 1) {
ERR_print_errors_fp(stdout);
return 1;
}
The signature verification fails with this above procedure. Not sure what I am doing wrong here.
Below is the call I am using to verify the same.
verified = RSA_verify(NID_sha256, (unsigned char*) message,
strlen(message), sign, file_len, public_key);
verified = RSA_verify(NID_sha256, (unsigned char*) message,
strlen(message), sign, file_len, public_key);
The signature could have an embedded NULL. Do not treat it like string data, and don't use strlen on it.
You have to manage a pointer and an explicit length.
The description of RSA_verify tells following:
RSA_verify() verifies that the signature sigbuf of size siglen matches
a given message digest m of size m_len. type denotes the message
digest algorithm that was used to generate the signature. rsa is the
signer's public key.
So, using RSA_verify with original message is not correct: message digest should be used instead.

How to handle type errors when working with blobs in SQLite?

Is there a good way to handle type errors when working with blobs in SQLite? For example, the following code registers two functions create_vector and display_vector. Basically, create_vector stores a std::vector as a blob and display_vector converts this blob into text, so that we can see it:
/* In order to use
sqlite> .load "./blob.so"
sqlite> select display_vector(create_vector());
[ 1.200000, 3.400000, 5.600000, 7.800000, 9.100000 ]
*/
#include <string>
#include <sqlite3ext.h>
SQLITE_EXTENSION_INIT1
extern "C" {
int sqlite3_blob_init(
sqlite3 * db,
char ** err,
sqlite3_api_routines const * const api
);
}
// Cleanup handler that deletes an array
template <typename T>
void array_cleanup(void * v) {
delete [] static_cast <T *> (v);
}
// Creates and returns a std::vector as a blob
static void create_vector(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
// Create a dummy vector
auto * v = new double[5] {1.2,3.4,5.6,7.8,9.10};
// Either cleanup works
sqlite3_result_blob(context,v,sizeof(double[5]),array_cleanup <double>);
}
// Converts a std::vector into text
static void display_vector(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
// Grab the vector. Note, if this is not a vector, then sqlite will
// almost certainly segfault.
auto const * const v =static_cast <double const * const> (
sqlite3_value_blob(argv[0]));
// Assuming we have a vector, convert it into a string
auto s = std::string("[ ");
for(unsigned i=0;i<5;i++) {
// If we're not on the first element, add a comma
if(i>0) s += ", ";
// Add the number
s += std::to_string(v[i]);
}
s += " ]";
// Return the text
sqlite3_result_text(
context,sqlite3_mprintf("%s",s.c_str()),s.size(),sqlite3_free);
}
// Register our blob functions
int sqlite3_blob_init(
sqlite3 *db,
char **err,
sqlite3_api_routines const * const api
){
SQLITE_EXTENSION_INIT2(api)
// Register the create_vector function
if( int ret = sqlite3_create_function(
db, "create_vector", 0, SQLITE_ANY, 0, create_vector, 0, 0)
) {
*err=sqlite3_mprintf("Error registering create_vector: %s",
sqlite3_errmsg(db));
return ret;
}
// Register the display_vector function
if( int ret = sqlite3_create_function(
db, "display_vector", 1, SQLITE_ANY, 0, display_vector, 0, 0)
) {
*err=sqlite3_mprintf("Error registering display_vector: %s",
sqlite3_errmsg(db));
return ret;
}
// If we've made it this far, we should be ok
return SQLITE_OK;
}
We can compile this with:
$ make
g++ -g -std=c++14 blob.cpp -shared -o blob.so -fPIC
Now, if we use these functions as advertised, everything works fine:
sqlite> .load "./blob.so"
sqlite> select display_vector(create_vector());
[ 1.200000, 3.400000, 5.600000, 7.800000, 9.100000 ]
However, if we try to use display_vector on a non-vector, we segfault:
sqlite> .load "./blob.so"
sqlite> select display_vector(NULL);
Segmentation fault
Really, the issue is that the static_cast in display_vector vector is not correct. In any case, is there a good way check the type of the blob or even guarantee that we have a blob? Is there a good way to prevent a segfault when a new extension requires an input of a certain type?
A blob is just a bunch of bytes, and not every value is a blob.
Your function should check the value's type with sqlite3_value_type(), and check the length with sqlite3_value_bytes().

Incompatible pointer type error while setting directives in Apache server

I am trying to set a directive in Apache server using the example code in the section 'The directive handler function' at
http://httpd.apache.org/docs/2.4/developer/modguide.html.
Here's my code:
static const command_rec example_directives[] =
{
AP_INIT_TAKE1("exampleEnabled", example_set_enabled, NULL, ACCESS_CONF, "Enable or disable mod_privet"),
AP_INIT_TAKE1("examplePath", example_set_path, NULL, ACCESS_CONF, "The path to whatever"),
AP_INIT_TAKE2("exampleAction", example_set_action, NULL, ACCESS_CONF, "Special action value!"),
{ NULL }
};
Handler for directives:
/* Handler for the "exampleEnabled" directive */
const char *example_set_enabled(cmd_parms *cmd, void *cfg, const char *arg)
{
if(!strcasecmp(arg, "on")) config.enabled = 1;
else config.enabled = 0;
return NULL;
}
/* Handler for the "examplePath" directive */
const char *example_set_path(cmd_parms *cmd, void *cfg, char *arg)
{
config.path = arg;
return NULL;
}
/* Handler for the "exampleAction" directive */
/* Let's pretend this one takes one argument (file or db), and a second (deny or allow), */
/* and we store it in a bit-wise manner. */
const char *example_set_action(cmd_parms *cmd, void *cfg, const char *arg1, const char *arg2)
{
if(!strcasecmp(arg1, "file")) config.typeOfAction = 0x01;
else config.typeOfAction = 0x02;
if(!strcasecmp(arg2, "deny")) config.typeOfAction += 0x10;
else config.typeOfAction += 0x20;
return NULL;
}
However, when I try to build, I get the following error:
error: initialization from incompatible pointer type [-Werror]
AP_INIT_TAKE1("examplePath", example_set_path, NULL, ACCESS_CONF, "The path to whatever")
Am I missing out on something?
Thanks
Third parameter of example_set_path should be const char *arg
#define AP_INIT_TAKE1 ( directive,
func,
mconfig,
where,
help ) { directive, { .take1=func }, mconfig, where, TAKE1, help }
func is defined as...
const char *(* take1 )(cmd_parms *parms, void *mconfig, const char *w)

AT command list as char * array

,,I need to provide an AT command to a modem which looks like this: AT^SRPN=1,99991,"Download_URL","Image";^SMSO
How can I insert the variable download_url and the variable image into the commands string array? Is the right way to declare the commands array not as const and to use strcpy() to insert the two variables into the commands list?
The function at_send_commands() needs the commands list as const.
Function proto: at_resp_t at_send_commands(TickType ticks_to_wait, const char *commands[]);
at_resp_t at_send_download_url_and_image(const char *download_url, const char *image)
{
static const char *commands[] =
{
"AT^SRPN=1,99991,",
download_url,
",",
image,
";^SMSO\r",
NULL
};
at_resp_t err = at_send_commands(AT_TIMEOUT, commands);
if (err)
return err;
}
Try this:
at_resp_t at_send_download_url_and_image(const char *download_url, const char *image)
{
std::string str("AT^SRPN=1,99991,");
str += download_url;
str += ",";
str += image;
str += ";^SMSO\r";
const char* command = str.c_str();
const char* commands[] =
{
command,
NULL
};
at_resp_t err = at_send_commands(AT_TIMEOUT, commands);
if (err)
return err;
}
In C the simplest way is IMO
void send_command(const char *download_url, const char *image) {
char buf[1000];
sprintf(buf, "AT^SRPN=1,99991,\"%s\",\"%s\";^SMSO",
download_url, image);
...
}
in buf you will end up having the final command to send to the modem.
If this code can be used in an hostile environment then you should also pay attention that no overflow can happen when passed large strings as url/image (e.g. add a check on strlen first or use snprintf instead).

Using string pointers in C++ and Objective-C

I have a sample project here on github where I created a c++ wrapper class for an external C++ library that I want to use in Objective-C.
I don't understand why my returned pointers are sometimes correct and sometimes wrong. Here's sample output:
Test Data = 43343008
In Compress 43343008
Returned Value = 43343008
Casted Value = 43343008
Test Data = 2239023
In Compress 2239023
Returned Value = 2239023
Casted Value = 2239023
Test Data = 29459973
In Compress 29459973
Returned Value = 29459973
Casted Value = l.remote
Test Data = 64019670
In Compress 64019670
Returned Value =
Casted Value = stem.syslog.master
In the above output you can see that the 1st and 2nd click of the button outputs the results I was expecting. In each of the other clicks either the returned value or casted value are invalid. I'm assuming this is because my pointer is pointing to an address I wasn't expecting. when running the app multiple times, any button click could be right or wrong.
I also tried with a single thread but experienced similar results.
The complete code is on github but here are the important bits.
ViewController.m
#import "ViewController.h"
extern const char * CompressCodeData(const char * strToCompress);
#implementation ViewController
...
// IBAction on the button
- (IBAction)testNow:(id)sender
{
[self performSelectorInBackground:#selector(analyze) withObject:nil];
}
- (void)analyze
{
#synchronized(self) {
const char *testData = [[NSString stringWithFormat:#"%d",
(int)(arc4random() % 100000000)] UTF8String];
NSLog(#"Test Data = %s", testData);
const char *compressed = CompressCodeData(testData);
NSLog(#"Returned Value = %s", compressed);
NSString *casted = [NSString stringWithCString:compressed
encoding:NSASCIIStringEncoding];
NSLog(#"Casted Value = %#\n\n", casted);
}
}
#end
SampleWrapper.cpp
#include <iostream>
#include <string.h>
#include <CoreFoundation/CoreFoundation.h>
using namespace std;
extern "C"
{
extern void NSLog(CFStringRef format, ...);
/**
* This function simply wraps a library function so that
* it can be used in objective-c.
*/
const char * CompressCodeData(const char * strToCompress)
{
const string s(strToCompress);
// Omitted call to static method in c++ library
// to simplify this test case.
//const char *result = SomeStaticLibraryFunction(s);
const char *result = s.c_str();
NSLog(CFSTR("In Compress %s"), result);
return result;
}
}
You are returning a pointer to at object that has been deallocated.
const string s(strToCompress);
…
const char *result = s.c_str();
NSLog(CFSTR("In Compress %s"), result);
return result;
s does not exist after CompressCodeData() function is over, so the pointer to it's internal memory is invalid.
You could allocate a chunk of memory to hold the response, but it would be up to the caller to release it.
char *compressed = CompressCodeData(testData);
NSLog(#"Returned Value = %s", compressed);
NSString *casted = [NSString stringWithCString:compressed
encoding:NSASCIIStringEncoding];
free(compressed);
NSLog(#"Casted Value = %#\n\n", casted);
…
const char * CompressCodeData(const char * strToCompress)
…
char *result = strdup(s.c_str());
Another solution is to pass in the memory to store the data into.
char compressed[2048]; // Or whatever!
CompressCodeData(testData, compressed, sizeof(compressed));
NSLog(#"Returned Value = %s", compressed);
NSString *casted = [NSString stringWithCString:compressed
encoding:NSASCIIStringEncoding];
NSLog(#"Casted Value = %#\n\n", casted);
…
void CompressCodeData(const char * strToCompress, char *result, size_t size)
…
s.copy(result, size - 1);
result[s.length() < size ? s.length() : size-1] = '\0';