Subscribe to The Occasional Programmer        RSS Feed
-----

Getting DIC's RSS Feed & Active Users Through cURLpp

Icon 1 Comments
Was playing around with cURLpp today, so I wrote up some code to grab the RSS feed and the active user list from the XML API. It's certainly not perfect, but it may provide someone with some insight on using cURLpp and libxml2 (which are obviously required).

constants.h
#ifndef DIC_CONSTANTS_H
#define DIC_CONSTANTS_H

#include <string>

namespace Constants
{
    namespace Rss
    {
        const std::string &FeedburnerNsName = "feedburner";
	const std::string &FeedburnerNsUri =
            "http://rssnamespace.org/feedburner/ext/1.0";
	const std::string &ItemXPath = "//item";
        const std::string &DicFeaturedUrl =
            "http://www.dreamincode.net/rss/featured.php";

    }

    namespace Xml
    {
        const std::string &DicForumsUrl =
            "http://www.dreamincode.net/forums/xml.php";
    }

    namespace ActiveUsers
    {
        const std::string &ListXPath = "/ipb/activeusers/users/a";
    }
}

#endif



dic_curl.h
#ifndef DIC_CURL_H
#define DIC_CURL_H

#include <curlpp/cURLpp.hpp>
#include <curlpp/Easy.hpp>
#include <curlpp/Options.hpp>
#include <string>
#include <sstream>

class WriteBuffer
{
public:
    WriteBuffer() {}
    size_t WriteCallback(char *p, size_t size, size_t count) {
        ss.write(p, size * count);
	return size * count;
    }

    std::string getData() const {
        return ss.str();
    }


private:
    std::stringstream ss;
};

class CurlRetriever
{
public:
    static std::string Get(const std::string &url);
private:
};

#endif



dic_curl.cpp
#include "dic_curl.h"
#include <string>

using namespace std;

string CurlRetriever::Get(const string &url) {

    WriteBuffer buffer;

    curlpp::Cleanup cleanup;
    curlpp::Easy request;

    request.setOpt<curlpp::options::FollowLocation>(true);

    curlpp::types::WriteFunctionFunctor writeFunctor(
	&buffer,
        &WriteBuffer::WriteCallback);
    curlpp::options::WriteFunction *writer =
        new curlpp::options::WriteFunction(writeFunctor);
    request.setOpt<curlpp::options::Url>(url);
    request.setOpt(writer);

    request.perform();

    return buffer.getData();
}



dic_rss_entry.h
#ifndef DIC_RSS_ENTRY
#define DIC_RSS_ENTRY

#include <iostream>
#include <string>
#include <libxml/xpath.h>

namespace XPath
{
    const std::string Title = "title";
    const std::string Link = "link";
    const std::string Category = "category";
    const std::string Date = "pubDate";
    const std::string Description = "description";
    const std::string OrigLink = "feedburner:origLink";
}

class DicRssEntry
{
public:
    DicRssEntry(xmlXPathContextPtr ctx);

    std::string getTitle() const { return title_; }
    std::string getLink() const { return link_; }
    std::string getCategory() const { return category_; }
    std::string getDate() const { return date_; }
    std::string getDescription() const { return description_; }
    std::string getOrigLink() const { return origLink_; }

    friend std::ostream &operator<<(
        std::ostream &os, const DicRssEntry &entry);

private:
    DicRssEntry() {}
    void setValue(const std::string &name, std::string &value);

    xmlXPathContextPtr ctx_;

    std::string title_;
    std::string link_;
    std::string category_;
    std::string date_;
    std::string description_;
    std::string origLink_;
};
#endif



dic_rss_entry.cpp
#include "dic_rss_entry.h"

#include <iostream>

using namespace std;

DicRssEntry::DicRssEntry(xmlXPathContextPtr ctx) : ctx_(ctx) {
    setValue(XPath::Title, title_);
    setValue(XPath::Link, link_);
    setValue(XPath::Category, category_);
    setValue(XPath::Date, date_);
    setValue(XPath::Description, description_);
    setValue(XPath::OrigLink, origLink_);
}

void DicRssEntry::setValue(const string &name, string &value) {
    xmlXPathObjectPtr xpo = xmlXPathEval(
	reinterpret_cast<const xmlChar *>(
            name.c_str()), ctx_);
    if (xpo && !xmlXPathNodeSetIsEmpty(xpo->nodesetval))
    {
     	xmlChar *content = xmlNodeGetContent(xmlXPathNodeSetItem(
                                                 xpo->nodesetval, 0));
	if (content)
	{
            value = reinterpret_cast<const char *>(content);
            xmlFree(content);
	}
    }
    xmlXPathFreeObject(xpo);
}

std::ostream &operator<<(std::ostream &os, const DicRssEntry &entry) {
    os << "Title: " << entry.getTitle() << endl;
    os << "Link: " << entry.getLink() << endl;
    os << "Category: " << entry.getCategory() << endl;
    os << "Date: " << entry.getDate() << endl;
    os << "Description: " << entry.getDescription() << endl;
    os << "Original Link: " << entry.getOrigLink() << endl;
    return os;
}



dic_active_user_entry.h
#ifndef DIC_ACTIVE_USER_ENTRY
#define DIC_ACTIVE_USER_ENTRY

#include <iostream>
#include <string>
#include <libxml/xpath.h>

namespace XPath
{
    const std::string Name = "span";
    const std::string ProfileLinkAttr = "href";
    const std::string TimeAttr = "title";
}

class DicActiveUserEntry
{
public:
    DicActiveUserEntry(xmlXPathContextPtr ctx);

    std::string getName() const { return name_; }
    std::string getProfileLink() const { return profileLink_; }
    std::string getTime() const { return time_; }

    friend std::ostream &operator<<(
	std::ostream &os, const DicActiveUserEntry &entry);

private:
    DicActiveUserEntry() {}
    void setValue(const std::string &name, std::string &value);
    void setAttributeValue(const std::string &name, std::string &value);

    xmlXPathContextPtr ctx_;

    std::string name_;
    std::string profileLink_;
    std::string time_;
};
#endif



dic_active_user_entry.cpp
#include "dic_active_user_entry.h"
#include <iostream>

using namespace std;

DicActiveUserEntry::DicActiveUserEntry(xmlXPathContextPtr ctx) : ctx_(ctx) {
    setValue(XPath::Name, name_);
    setAttributeValue(
	XPath::ProfileLinkAttr, profileLink_);
    setAttributeValue(XPath::TimeAttr, time_);
}

void DicActiveUserEntry::setAttributeValue(const string &name, string &value)
{
    xmlChar *prop = xmlGetProp(
	ctx_->node,
	reinterpret_cast<const xmlChar *>(name.c_str()));
    if (prop)
    {
     	value = reinterpret_cast<const char *>(prop);
	xmlFree(prop);
    }
}

void DicActiveUserEntry::setValue(const string &name, string &value)
{
    xmlXPathObjectPtr xpo = xmlXPathEval(
	reinterpret_cast<const xmlChar *>(
            name.c_str()), ctx_);
    if (xpo && !xmlXPathNodeSetIsEmpty(xpo->nodesetval))
    {
     	xmlChar *content = xmlNodeGetContent(xmlXPathNodeSetItem(
                                                 xpo->nodesetval, 0));
	if (content)
	{
            value = reinterpret_cast<const char *>(content);
            xmlFree(content);
	}
    }
    xmlXPathFreeObject(xpo);
}

std::ostream &operator<<(std::ostream &os, const DicActiveUserEntry &entry)
{
    os << "Name: " << entry.getName() << endl;
    os << "Link: " << entry.getProfileLink() << endl;
    os << "Time: " << entry.getTime() << endl;
    return os;
}



dic_reader.cpp
#include "dic_rss_entry.h"
#include "dic_active_user_entry.h"
#include "dic_curl.h"
#include "constants.h"

#include <libxml/parser.h>
#include <libxml/xpathInternals.h>
#include <vector>

#include <curlpp/Exception.hpp>

using namespace std;

static vector<DicActiveUserEntry> getActiveUsers(const string &xmlData)
{
    vector<DicActiveUserEntry> userEntries;

    xmlInitParser();
    xmlDocPtr doc = xmlParseMemory(xmlData.c_str(), xmlData.size());
    if (!doc)
    {
     	cerr << "Failed to parse memory to XML" << endl;
	return userEntries;
    }

    xmlXPathContextPtr ctx = xmlXPathNewContext(doc);
    if (!ctx)
    {
        cerr << "Failed to create new XPath context" << endl;
        xmlFreeDoc(doc);
	return userEntries;
    }

    xmlXPathObjectPtr xpo = xmlXPathEval(
	reinterpret_cast<const xmlChar *>(
            Constants::ActiveUsers::ListXPath.c_str()),
	ctx);
    if (!xpo)
    {
     	cerr << "XPath evaluation failed" << endl;
	xmlXPathFreeContext(ctx);
	xmlFreeDoc(doc);
	return userEntries;
    }

    if (xmlXPathNodeSetIsEmpty(xpo->nodesetval))
    {
     	cout << "No matching node for XPath expression" << endl;
    }
    else
    else
    {
     	cout << "Found "
             << xmlXPathNodeSetGetLength(xpo->nodesetval)
             << " active users" << endl;
	for (int i = 0; i < xmlXPathNodeSetGetLength(xpo->nodesetval); ++i)
	{
            ctx->node = xmlXPathNodeSetItem(xpo->nodesetval, i);
            userEntries.push_back(DicActiveUserEntry(ctx));
        }
    }

    xmlXPathFreeObject(xpo);
    xmlXPathFreeContext(ctx);
    xmlFreeDoc(doc);

    xmlCleanupParser();

    return userEntries;
}

static vector<DicRssEntry> getRssEntries(const string &rssData)
{
    vector<DicRssEntry> rssEntries;

    xmlInitParser();
    xmlDocPtr doc = xmlParseMemory(rssData.c_str(), rssData.size());
    if (!doc)
    {
        cerr << "Failed to parse memory to XML" << endl;
        return rssEntries;
    }

    xmlXPathContextPtr ctx = xmlXPathNewContext(doc);
    if (!ctx)
    {
        cerr << "Failed to create new XPath context" << endl;
        xmlFreeDoc(doc);
        return rssEntries;
    }

    xmlXPathRegisterNs(
        ctx,
        reinterpret_cast<const xmlChar *>(
            Constants::Rss::FeedburnerNsName.c_str()),
        reinterpret_cast<const xmlChar *>(
            Constants::Rss::FeedburnerNsUri.c_str()));

    xmlXPathObjectPtr xpo = xmlXPathEval(
        reinterpret_cast<const xmlChar *>(Constants::Rss::ItemXPath.c_str()),
        ctx);
    if (!xpo)
    {
        cerr << "XPath evaluation failed" << endl;
        xmlXPathFreeContext(ctx);
        xmlFreeDoc(doc);
        return rssEntries;
    }

    if (xmlXPathNodeSetIsEmpty(xpo->nodesetval))
    {
        cout << "No matching node for XPath expression" << endl;
    }
    else
    {
        cout << "Found " << xmlXPathNodeSetGetLength(xpo->nodesetval) << " item\
s" << endl;
        for (int i = 0; i < xmlXPathNodeSetGetLength(xpo->nodesetval); ++i)
        {
            ctx->node = xmlXPathNodeSetItem(xpo->nodesetval, i);
            rssEntries.push_back(DicRssEntry(ctx));
        }
    }

    xmlXPathFreeObject(xpo);
    xmlXPathFreeContext(ctx);
    xmlFreeDoc(doc);

    xmlCleanupParser();

    return rssEntries;
}

int main(int argc, const char *argv[])
{
    try
    {
        string rss = CurlRetriever::Get(Constants::Rss::DicFeaturedUrl);
        if (rss.size() == 0)
        {
            cerr << "No data returned from RSS query" << endl;
            return 1;
        }

	vector<DicRssEntry> rssEntries = getRssEntries(rss);

	for (size_t i = 0; i < rssEntries.size(); ++i)
        {
            cout << rssEntries[i];
        }
    }
    catch (const curlpp::LibcurlRuntimeError &ex)
    {
        cerr << "cURL error: " << ex.what() << " CODE: "
             << ex.whatCode() << endl;
        return 1;
    }
    catch (const curlpp::RuntimeError &ex)
    {
        cerr << "cURL failure: " << ex.what() << endl;
        return 1;
    }
    catch (const curlpp::LogicError &ex)
    {
        cerr << "cURL failure: " << ex.what() << endl;
        return 1;
    }
    catch (const std::exception &ex)
    {
        cerr << "Exception: " << ex.what() << endl;
        return 1;
    }


    try
    {
        string userData = CurlRetriever::Get(Constants::Xml::DicForumsUrl);
        if (userData.size() == 0)
        {
            cerr << "No data returned from XML query" << endl;
            return 1;
        }

	vector<DicActiveUserEntry> userEntries = getActiveUsers(userData);
        for (size_t i = 0; i < userEntries.size(); ++i)
        {
            cout << userEntries[i];
        }
    }
    catch (const curlpp::LibcurlRuntimeError &ex)
    {
        cerr << "cURL error: " << ex.what() << " CODE: "
             << ex.whatCode() << endl;
        return 1;
    }
    catch (const curlpp::RuntimeError &ex)
    {
        cerr << "cURL failure: " << ex.what() << endl;
        return 1;
    }
    catch (const curlpp::LogicError &ex)
    {
        cerr << "cURL failure: " << ex.what() << endl;
        return 1;
    }
    catch (const std::exception &ex)
    {
        cerr << "Exception: " << ex.what() << endl;
        return 1;
    }

    return 0;
}

1 Comments On This Entry

Page 1 of 1

DaneAU Icon

14 May 2011 - 06:46 PM
Pretty cool stuff mate, working on the API at the moment myself.
0
Page 1 of 1

Trackbacks for this entry [ Trackback URL ]

There are no Trackbacks for this entry

July 2014

S M T W T F S
  12345
6789101112
13141516171819
20212223242526
272829 30 31  

Recent Entries

Recent Comments

Search My Blog

0 user(s) viewing

0 Guests
0 member(s)
0 anonymous member(s)