From bdb8aed78f6395481af4e0a311a03a072ab87e62 Mon Sep 17 00:00:00 2001 From: Sarang Bharadwaj Date: Thu, 19 Jul 2012 19:11:48 +0530 Subject: [PATCH] Added code to fetch avatars --- .../twitter/Requests/CreateFriendRequest.cpp | 16 ++-- .../twitter/Requests/CreateFriendRequest.h | 7 +- .../twitter/Requests/DestroyFriendRequest.cpp | 14 ++- .../twitter/Requests/DestroyFriendRequest.h | 6 +- .../twitter/Requests/ProfileImageRequest.cpp | 63 +++++++++++++ .../twitter/Requests/ProfileImageRequest.h | 89 +++++++++++++++++++ backends/twitter/TwitterPlugin.cpp | 66 ++++++++++---- backends/twitter/TwitterPlugin.h | 8 +- backends/twitter/TwitterResponseParser.cpp | 30 ++++++- backends/twitter/TwitterResponseParser.h | 5 ++ 10 files changed, 271 insertions(+), 33 deletions(-) create mode 100644 backends/twitter/Requests/ProfileImageRequest.cpp create mode 100644 backends/twitter/Requests/ProfileImageRequest.h diff --git a/backends/twitter/Requests/CreateFriendRequest.cpp b/backends/twitter/Requests/CreateFriendRequest.cpp index 163daacc..1d8d154a 100644 --- a/backends/twitter/Requests/CreateFriendRequest.cpp +++ b/backends/twitter/Requests/CreateFriendRequest.cpp @@ -3,10 +3,17 @@ DEFINE_LOGGER(logger, "CreateFriendRequest") void CreateFriendRequest::run() { - LOG4CXX_INFO(logger, user << ": Sending follow request for " << frnd) + LOG4CXX_INFO(logger, user << " - Sending follow request for " << frnd) replyMsg = ""; success = twitObj->friendshipCreate(frnd, false); - if(success) twitObj->getLastWebResponse(replyMsg); + if(success) { + twitObj->getLastWebResponse(replyMsg); + + LOG4CXX_INFO(logger, user << replyMsg) + + friendInfo = getUser(replyMsg); + if(friendInfo.getScreenName() == "") LOG4CXX_INFO(logger, user << " - Was unable to fetch user info for " << frnd); + } } void CreateFriendRequest::finalize() @@ -15,15 +22,14 @@ void CreateFriendRequest::finalize() std::string error; twitObj->getLastCurlError(error); LOG4CXX_ERROR(logger, user << " - Curl error: " << error) - callBack(user, frnd, error); + callBack(user, friendInfo, error); } else { std::string error; error = getErrorMessage(replyMsg); if(error.length()) { LOG4CXX_ERROR(logger, user << " - " << error) - //LOG4CXX_ERROR(logger, user << " " << replyMsg) } else LOG4CXX_INFO(logger, user << ": Now following " << frnd) - callBack(user, frnd, error); + callBack(user, friendInfo, error); } } diff --git a/backends/twitter/Requests/CreateFriendRequest.h b/backends/twitter/Requests/CreateFriendRequest.h index f0cf5b79..48248ac0 100644 --- a/backends/twitter/Requests/CreateFriendRequest.h +++ b/backends/twitter/Requests/CreateFriendRequest.h @@ -8,6 +8,7 @@ #include #include #include +#include using namespace Transport; @@ -17,13 +18,13 @@ class CreateFriendRequest : public Thread std::string user; std::string frnd; std::string replyMsg; - boost::function< void (std::string&, std::string&, std::string&) > callBack; - + boost::function< void (std::string&, User&, std::string&) > callBack; + User friendInfo; bool success; public: CreateFriendRequest(twitCurl *obj, const std::string &_user, const std::string & _frnd, - boost::function< void (std::string&, std::string&, std::string&) > cb) { + boost::function< void (std::string&, User&, std::string&) > cb) { twitObj = obj->clone(); user = _user; frnd = _frnd; diff --git a/backends/twitter/Requests/DestroyFriendRequest.cpp b/backends/twitter/Requests/DestroyFriendRequest.cpp index f7a6628f..5468cace 100644 --- a/backends/twitter/Requests/DestroyFriendRequest.cpp +++ b/backends/twitter/Requests/DestroyFriendRequest.cpp @@ -5,7 +5,15 @@ void DestroyFriendRequest::run() { replyMsg = ""; success = twitObj->friendshipDestroy(frnd, false); - if(success) twitObj->getLastWebResponse(replyMsg); + if(success) { + twitObj->getLastWebResponse(replyMsg); + + LOG4CXX_INFO(logger, user << replyMsg) + + + friendInfo = getUser(replyMsg); + if(friendInfo.getScreenName() == "") LOG4CXX_INFO(logger, user << " - Was unable to fetch user info for " << frnd); + } } void DestroyFriendRequest::finalize() @@ -14,11 +22,11 @@ void DestroyFriendRequest::finalize() std::string error; twitObj->getLastCurlError(error); LOG4CXX_ERROR(logger, user << " Curl error: " << error) - callBack(user, frnd, error); + callBack(user, friendInfo, error); } else { std::string error; error = getErrorMessage(replyMsg); if(error.length()) LOG4CXX_ERROR(logger, user << " - " << error) - callBack(user, frnd, error); + callBack(user, friendInfo, error); } } diff --git a/backends/twitter/Requests/DestroyFriendRequest.h b/backends/twitter/Requests/DestroyFriendRequest.h index 0733e455..f1706c4d 100644 --- a/backends/twitter/Requests/DestroyFriendRequest.h +++ b/backends/twitter/Requests/DestroyFriendRequest.h @@ -8,6 +8,7 @@ #include #include #include +#include using namespace Transport; @@ -17,12 +18,13 @@ class DestroyFriendRequest : public Thread std::string user; std::string frnd; std::string replyMsg; - boost::function< void (std::string&, std::string&, std::string&) > callBack; + boost::function< void (std::string&, User&, std::string&) > callBack; + User friendInfo; bool success; public: DestroyFriendRequest(twitCurl *obj, const std::string &_user, const std::string & _frnd, - boost::function< void (std::string&, std::string&, std::string&) > cb) { + boost::function< void (std::string&, User&, std::string&) > cb) { twitObj = obj->clone(); user = _user; frnd = _frnd; diff --git a/backends/twitter/Requests/ProfileImageRequest.cpp b/backends/twitter/Requests/ProfileImageRequest.cpp new file mode 100644 index 00000000..aee8614b --- /dev/null +++ b/backends/twitter/Requests/ProfileImageRequest.cpp @@ -0,0 +1,63 @@ +#include "ProfileImageRequest.h" +DEFINE_LOGGER(logger, "ProfileImageRequest") + +int ProfileImageRequest::curlCallback(char* data, size_t size, size_t nmemb, ProfileImageRequest* obj) +{ + int writtenSize = 0; + if(obj && data) { + obj->callbackdata.append(data, size*nmemb); + writtenSize = (int)(size*nmemb); + } + return writtenSize; +} + +bool ProfileImageRequest::fetchImage() +{ + LOG4CXX_INFO(logger, user << " - Fetching profile image"); + + curl_easy_setopt(curlhandle, CURLOPT_CUSTOMREQUEST, NULL); + curl_easy_setopt(curlhandle, CURLOPT_ENCODING, ""); + + img = ""; + callbackdata = ""; + memset(curl_errorbuffer, 0, 1024); + + curl_easy_setopt(curlhandle, CURLOPT_ERRORBUFFER, curl_errorbuffer); + curl_easy_setopt(curlhandle, CURLOPT_WRITEFUNCTION, curlCallback); + curl_easy_setopt(curlhandle, CURLOPT_WRITEDATA, this); + + /* Set http request and url */ + curl_easy_setopt(curlhandle, CURLOPT_HTTPGET, 1); + curl_easy_setopt(curlhandle, CURLOPT_VERBOSE, 1); + curl_easy_setopt(curlhandle, CURLOPT_URL, url.c_str()); + + /* Send http request and return status*/ + return (CURLE_OK == curl_easy_perform(curlhandle)); +} + + +void ProfileImageRequest::run() +{ + if(curlhandle == NULL) { + error = "Failed to init CURL!"; + success = false; + return; + } + success = fetchImage(); + if(!success) error.assign(curl_errorbuffer); +} + +void ProfileImageRequest::finalize() +{ + if(!success) { + LOG4CXX_ERROR(logger, user << " - " << error) + img = ""; + callBack(user, buddy, img, reqID, error); + } else { + //std::string error = getErrorMessage(replyMsg); + //if(error.length()) LOG4CXX_ERROR(logger, user << " - " << error) + LOG4CXX_INFO(logger, user << " - " << callbackdata); + img = callbackdata; + callBack(user, buddy, img, reqID, error); + } +} diff --git a/backends/twitter/Requests/ProfileImageRequest.h b/backends/twitter/Requests/ProfileImageRequest.h new file mode 100644 index 00000000..1f4e8b4e --- /dev/null +++ b/backends/twitter/Requests/ProfileImageRequest.h @@ -0,0 +1,89 @@ +#ifndef PROFILEIMAGE_H +#define PROFILEIMAGE_H + +#include "../ThreadPool.h" +#include "../libtwitcurl/curl/curl.h" +#include "../TwitterResponseParser.h" +#include "transport/logging.h" +#include "transport/config.h" +#include +#include +#include +#include +#include +#include + +using namespace Transport; +using namespace boost::program_options; + +class ProfileImageRequest : public Thread +{ + std::string user; + std::string buddy; + std::string url; + std::string img; + unsigned int reqID; + boost::function< void (std::string&, std::string&, std::string&, int, std::string&) > callBack; + + + bool success; + CURL *curlhandle; + + std::string error; + std::string callbackdata; + char curl_errorbuffer[1024]; + bool fetchImage(); + static int curlCallback( char* data, size_t size, size_t nmemb, ProfileImageRequest *obj); + + public: + ProfileImageRequest(Config *config, const std::string &_user, const std::string &_buddy, const std::string &_url, unsigned int _reqID, + boost::function< void (std::string&, std::string&, std::string&, int, std::string&) > cb) { + + curlhandle = curl_easy_init(); + curl_easy_setopt(curlhandle, CURLOPT_PROXY, NULL); + curl_easy_setopt(curlhandle, CURLOPT_PROXYUSERPWD, NULL); + curl_easy_setopt(curlhandle, CURLOPT_PROXYAUTH, (long)CURLAUTH_ANY); + + /************ Set proxy if available *****************************************/ + if(CONFIG_HAS_KEY(config,"proxy.server")) { + std::string ip = CONFIG_STRING(config,"proxy.server"); + + std::ostringstream out; + out << CONFIG_INT(config,"proxy.port"); + std::string port = out.str(); + + std::string puser = CONFIG_STRING(config,"proxy.user"); + std::string ppasswd = CONFIG_STRING(config,"proxy.password"); + + if(ip != "localhost" && port != "0") { + /* Set proxy details in cURL */ + std::string proxyIpPort = ip + ":" + port; + curl_easy_setopt(curlhandle, CURLOPT_PROXY, proxyIpPort.c_str()); + + /* Prepare username and password for proxy server */ + if(puser.length() && ppasswd.length()) { + std::string proxyUserPass = puser + ":" + ppasswd; + curl_easy_setopt(curlhandle, CURLOPT_PROXYUSERPWD, proxyUserPass.c_str()); + } + } + } + /*****************************************************************************/ + + user = _user; + buddy = _buddy; + url = _url; + reqID = _reqID; + callBack = cb; + } + + ~ProfileImageRequest() { + if(curlhandle) { + curl_easy_cleanup(curlhandle); + curlhandle = NULL; + } + } + + void run(); + void finalize(); +}; +#endif diff --git a/backends/twitter/TwitterPlugin.cpp b/backends/twitter/TwitterPlugin.cpp index c55132d6..c3556d87 100644 --- a/backends/twitter/TwitterPlugin.cpp +++ b/backends/twitter/TwitterPlugin.cpp @@ -9,6 +9,7 @@ #include "Requests/CreateFriendRequest.h" #include "Requests/DestroyFriendRequest.h" #include "Requests/RetweetRequest.h" +#include "Requests/ProfileImageRequest.h" DEFINE_LOGGER(logger, "Twitter Backend"); @@ -139,7 +140,6 @@ void TwitterPlugin::handleLoginRequest(const std::string &user, const std::strin void TwitterPlugin::handleLogoutRequest(const std::string &user, const std::string &legacyName) { if(onlineUsers.count(user)) { - clearRoster(user); delete sessions[user]; sessions[user] = NULL; connectionState[user] = DISCONNECTED; @@ -291,6 +291,20 @@ void TwitterPlugin::handleBuddyRemovedRequest(const std::string &user, const std boost::bind(&TwitterPlugin::deleteFriendResponse, this, _1, _2, _3))); } +void TwitterPlugin::handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) +{ + LOG4CXX_INFO(logger, user << " - VCardRequest for " << legacyName << ", " << imgURL[legacyName]) + if(connectionState[user] != CONNECTED) { + LOG4CXX_ERROR(logger, user << " is not connected to twitter!") + return; + } + + if(getTwitterMode(user) != SINGLECONTACT && buddies[user].count(legacyName) && imgURL[legacyName].length()) { + LOG4CXX_INFO(logger, user << " - Initiating VCard request for " << legacyName); + tp->runAsThread(new ProfileImageRequest(config, user, legacyName, imgURL[legacyName], id, + boost::bind(&TwitterPlugin::profileImageResponse, this, _1, _2, _3, _4, _5))); + } +} void TwitterPlugin::pollForTweets() { @@ -510,7 +524,7 @@ void TwitterPlugin::clearRoster(const std::string user) if(buddies[user].size() == 0) return; std::set::iterator it = buddies[user].begin(); while(it != buddies[user].end()) { - handleBuddyChanged(user, *it, *it, std::vector(), pbnetwork::STATUS_NONE); + //handleBuddyChanged(user, *it, *it, std::vector(), pbnetwork::STATUS_NONE); handleBuddyRemoved(user, *it); it++; } @@ -524,10 +538,10 @@ void TwitterPlugin::populateRoster(std::string &user, std::vector &friends for(int i=0 ; i(), pbnetwork::STATUS_ONLINE); - else if(twitterMode[user] == CHATROOM) { + else if(twitterMode[user] == CHATROOM) handleParticipantChanged(user, friends[i].getScreenName(), adminChatRoom, 0, pbnetwork::STATUS_ONLINE); - buddies[user].insert(friends[i].getScreenName()); - } + buddies[user].insert(friends[i].getScreenName()); + imgURL[friends[i].getScreenName()] = friends[i].getProfileImgURL(); } } else handleMessage(user, twitterMode[user] == CHATROOM ? adminChatRoom : adminLegacyName, std::string("Error populating roster - ") + errMsg, twitterMode[user] == CHATROOM ? adminNickName : ""); @@ -629,7 +643,7 @@ void TwitterPlugin::directMessageResponse(std::string &user, std::string &userna } } -void TwitterPlugin::createFriendResponse(std::string &user, std::string &frnd, std::string &errMsg) +void TwitterPlugin::createFriendResponse(std::string &user, User &frnd, std::string &errMsg) { if(errMsg.length()) { handleMessage(user, twitterMode[user] == CHATROOM ? adminChatRoom : adminLegacyName, @@ -638,20 +652,22 @@ void TwitterPlugin::createFriendResponse(std::string &user, std::string &frnd, s } handleMessage(user, twitterMode[user] == CHATROOM ? adminChatRoom : adminLegacyName, - std::string("You are now following ") + frnd, twitterMode[user] == CHATROOM ? adminNickName : ""); + std::string("You are now following ") + frnd.getScreenName(), twitterMode[user] == CHATROOM ? adminNickName : ""); - //buddies[user].insert(frnd); + buddies[user].insert(frnd.getScreenName()); + imgURL[frnd.getScreenName()] = frnd.getProfileImgURL(); + LOG4CXX_INFO(logger, user << " - " << frnd.getScreenName() << ", " << imgURL[frnd.getScreenName()]) //handleBuddyChanged(user, frnd, frnd, std::vector(), pbnetwork::STATUS_NONE); if(twitterMode[user] == MULTIPLECONTACT) { - handleBuddyChanged(user, frnd, frnd, std::vector(), pbnetwork::STATUS_ONLINE); + handleBuddyChanged(user, frnd.getScreenName(), frnd.getScreenName(), std::vector(), pbnetwork::STATUS_ONLINE); } else if(twitterMode[user] == CHATROOM) { - buddies[user].insert(frnd); - handleParticipantChanged(user, frnd, adminChatRoom, 0, pbnetwork::STATUS_ONLINE); + //buddies[user].insert(frnd); + handleParticipantChanged(user, frnd.getScreenName(), adminChatRoom, 0, pbnetwork::STATUS_ONLINE); } } -void TwitterPlugin::deleteFriendResponse(std::string &user, std::string &frnd, std::string &errMsg) +void TwitterPlugin::deleteFriendResponse(std::string &user, User &frnd, std::string &errMsg) { if(errMsg.length()) { handleMessage(user, twitterMode[user] == CHATROOM ? adminChatRoom : adminLegacyName, @@ -660,16 +676,21 @@ void TwitterPlugin::deleteFriendResponse(std::string &user, std::string &frnd, s } handleMessage(user, twitterMode[user] == CHATROOM ? adminChatRoom : adminLegacyName, - std::string("You are not following ") + frnd + " anymore", twitterMode[user] == CHATROOM ? adminNickName : ""); + std::string("You are not following ") + frnd.getScreenName() + " anymore", twitterMode[user] == CHATROOM ? adminNickName : ""); if (twitterMode[user] == CHATROOM) { - handleParticipantChanged(user, frnd, adminLegacyName, 0, pbnetwork::STATUS_NONE); - buddies[user].erase(frnd); + handleParticipantChanged(user, frnd.getScreenName(), adminLegacyName, 0, pbnetwork::STATUS_NONE); + //buddies[user].erase(frnd); } - //buddies[user].erase(frnd); + + LOG4CXX_INFO(logger, user << " - " << frnd.getScreenName() << ", " << imgURL[frnd.getScreenName()]) + + buddies[user].erase(frnd.getScreenName()); + imgURL[frnd.getScreenName()] = ""; + //handleBuddyRemoved(user, frnd); if(twitterMode[user] == MULTIPLECONTACT) { - handleBuddyRemoved(user, frnd); + handleBuddyRemoved(user, frnd.getScreenName()); } } @@ -684,3 +705,14 @@ void TwitterPlugin::RetweetResponse(std::string &user, std::string &errMsg) "Retweet successful", twitterMode[user] == CHATROOM ? adminNickName : ""); } } + +void TwitterPlugin::profileImageResponse(std::string &user, std::string &buddy, std::string &img, unsigned int reqID, std::string &errMsg) +{ + if(errMsg.length()) { + handleMessage(user, twitterMode[user] == CHATROOM ? adminChatRoom : adminLegacyName, + errMsg, twitterMode[user] == CHATROOM ? adminNickName : ""); + } else { + LOG4CXX_INFO(logger, user << " - Sending VCard for " << buddy) + handleVCard(user, reqID, buddy, buddy, "", img); + } +} diff --git a/backends/twitter/TwitterPlugin.h b/backends/twitter/TwitterPlugin.h index c334d0e2..95e03c87 100644 --- a/backends/twitter/TwitterPlugin.h +++ b/backends/twitter/TwitterPlugin.h @@ -77,6 +77,8 @@ class TwitterPlugin : public NetworkPlugin { void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector &groups); + void handleVCardRequest(const std::string &/*user*/, const std::string &/*legacyName*/, unsigned int /*id*/); + void pollForTweets(); void pollForDirectMessages(); @@ -112,9 +114,10 @@ class TwitterPlugin : public NetworkPlugin { void displayFriendlist(std::string &user, std::vector &friends, std::string &errMsg); void displayTweets(std::string &user, std::string &userRequested, std::vector &tweets , std::string &errMsg); void directMessageResponse(std::string &user, std::string &username, std::vector &messages, std::string &errMsg); - void createFriendResponse(std::string &user, std::string &frnd, std::string &errMsg); - void deleteFriendResponse(std::string &user, std::string &frnd, std::string &errMsg); + void createFriendResponse(std::string &user, User &frnd, std::string &errMsg); + void deleteFriendResponse(std::string &user, User &frnd, std::string &errMsg); void RetweetResponse(std::string &user, std::string &errMsg); + void profileImageResponse(std::string &user, std::string &buddy, std::string &img, unsigned int reqID, std::string &errMsg); /***********************************************************************************/ private: @@ -143,6 +146,7 @@ class TwitterPlugin : public NetworkPlugin { std::set onlineUsers; std::map nickName; std::map > buddies; + std::map imgURL; std::map twitterMode; }; diff --git a/backends/twitter/TwitterResponseParser.cpp b/backends/twitter/TwitterResponseParser.cpp index f8c83746..33e2f27c 100644 --- a/backends/twitter/TwitterResponseParser.cpp +++ b/backends/twitter/TwitterResponseParser.cpp @@ -1,8 +1,16 @@ #include "TwitterResponseParser.h" #include "transport/logging.h" +#include DEFINE_LOGGER(logger, "TwitterResponseParser") +static std::string tolowercase(std::string inp) +{ + std::string out = inp; + for(int i=0 ; igetChild(TwitterReponseTypes::id, xmlns)->getText() ) ); - user.setScreenName( std::string( element->getChild(TwitterReponseTypes::screen_name, xmlns)->getText() ) ); + user.setScreenName( tolowercase( std::string( element->getChild(TwitterReponseTypes::screen_name, xmlns)->getText() ) ) ); user.setUserName( std::string( element->getChild(TwitterReponseTypes::name, xmlns)->getText() ) ); + user.setProfileImgURL( std::string( element->getChild(TwitterReponseTypes::profile_image_url, xmlns)->getText() ) ); user.setNumberOfTweets( atoi(element->getChild(TwitterReponseTypes::statuses_count, xmlns)->getText().c_str()) ); return user; } @@ -137,6 +146,25 @@ std::vector getUsers(std::string &xml) return users; } +User getUser(std::string &xml) +{ + User user; + Swift::ParserElement::ref rootElement = Swift::StringTreeParser::parse(xml); + + if(rootElement == NULL) { + LOG4CXX_ERROR(logger, "Error while parsing XML") + return user; + } + + if(rootElement->getName() != TwitterReponseTypes::user) { + LOG4CXX_ERROR(logger, "XML doesn't correspond to user object") + return user; + } + + const std::string xmlns = rootElement->getNamespace(); + return user = getUser(rootElement, xmlns); +} + std::vector getIDs(std::string &xml) { std::vector IDs; diff --git a/backends/twitter/TwitterResponseParser.h b/backends/twitter/TwitterResponseParser.h index d98f99bc..c1f1f8fc 100644 --- a/backends/twitter/TwitterResponseParser.h +++ b/backends/twitter/TwitterResponseParser.h @@ -34,6 +34,7 @@ namespace TwitterReponseTypes const std::string recipient_screen_name = "recipient_screen_name"; const std::string sender = "sender"; const std::string recipient = "recipient"; + const std::string profile_image_url = "profile_image_url"; }; //Class holding user data @@ -42,6 +43,7 @@ class User std::string ID; std::string name; std::string screen_name; + std::string profile_image_url; unsigned int statuses_count; public: @@ -50,12 +52,14 @@ class User std::string getUserID() {return ID;} std::string getUserName() {return name;} std::string getScreenName() {return screen_name;} + std::string getProfileImgURL() {return profile_image_url;} unsigned int getNumberOfTweets() {return statuses_count;} void setUserID(std::string _id) {ID = _id;} void setUserName(std::string _name) {name = _name;} void setScreenName(std::string _screen) {screen_name = _screen;} + void setProfileImgURL(std::string _url) {profile_image_url = _url;} void setNumberOfTweets(unsigned int sc) {statuses_count = sc;} }; @@ -145,6 +149,7 @@ std::vector getTimeline(std::string &xml); std::vector getDirectMessages(std::string &xml); std::vector getIDs(std::string &xml); std::vector getUsers(std::string &xml); +User getUser(std::string &xml); std::string getErrorMessage(std::string &xml); Status getStatus(const Swift::ParserElement::ref &element, const std::string xmlns);