| bridges/facebook/configure.ac |
| 1 | AC_INIT([tracker-bridge-facebook], [0.1.0], [http://bugzilla.gnome.org/enter_bug.cgi?product=tracker], [tracker-bridge-facebook]) |
| 2 | AC_CONFIG_SRCDIR([src/facebook.vala]) |
| 3 | AC_CONFIG_HEADERS(config.h) |
| 4 | AM_INIT_AUTOMAKE([dist-bzip2]) |
| 5 | |
| 6 | AC_SUBST(PACKAGE_URL, [http://www.tracker-project.org]) |
| 7 | |
| 8 | AC_PROG_CC |
| 9 | AM_PROG_CC_C_O |
| 10 | AC_DISABLE_STATIC |
| 11 | AC_PROG_LIBTOOL |
| 12 | |
| 13 | AC_PATH_PROG(VALAC, valac, valac) |
| 14 | AC_SUBST(VALAC) |
| 15 | |
| 16 | AH_TEMPLATE([GETTEXT_PACKAGE], [Package name for gettext]) |
| 17 | GETTEXT_PACKAGE=tracker-bridge-facebook |
| 18 | AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE") |
| 19 | AC_SUBST(GETTEXT_PACKAGE) |
| 20 | AM_GLIB_GNU_GETTEXT |
| 21 | IT_PROG_INTLTOOL([0.35.0]) |
| 22 | AM_MAINTAINER_MODE |
| 23 | |
| 24 | AC_SUBST(CFLAGS) |
| 25 | AC_SUBST(CPPFLAGS) |
| 26 | AC_SUBST(LDFLAGS) |
| 27 | |
| 28 | GLIB_REQUIRED=2.12.0 |
| 29 | DBUS_REQUIRED=0.60 |
| 30 | TRACKERBRIDGE_REQUIRED=0.1 |
| 31 | GNOMEKEYRING_REQUIRED=2.26 |
| 32 | REST_REQUIRED=0.4 |
| 33 | UUID_REQUIRED=1.41 |
| 34 | |
| 35 | PKG_CHECK_MODULES(GLIB2, glib-2.0 >= $GLIB_REQUIRED) |
| 36 | AC_SUBST(GLIB2_CFLAGS) |
| 37 | AC_SUBST(GLIB2_LIBS) |
| 38 | |
| 39 | PKG_CHECK_MODULES(GOBJECT, gobject-2.0 >= $GLIB_REQUIRED) |
| 40 | AC_SUBST(GOBJECT_CFLAGS) |
| 41 | AC_SUBST(GOBJECT_LIBS) |
| 42 | |
| 43 | PKG_CHECK_MODULES(GTHREAD, gthread-2.0 >= $GLIB_REQUIRED) |
| 44 | AC_SUBST(GOBJECT_CFLAGS) |
| 45 | AC_SUBST(GOBJECT_LIBS) |
| 46 | |
| 47 | PKG_CHECK_MODULES(DBUS, [dbus-1 >= $DBUS_REQUIRED dbus-glib-1 >= $DBUS_REQUIRED]) |
| 48 | AC_SUBST(DBUS_CFLAGS) |
| 49 | AC_SUBST(DBUS_LIBS) |
| 50 | |
| 51 | PKG_CHECK_MODULES(TRACKERBRIDGE, tracker-bridge-0.1 >= $TRACKERBRIDGE_REQUIRED) |
| 52 | AC_SUBST(TRACKERBRIDGE_CFLAGS) |
| 53 | AC_SUBST(TRACKERBRIDGE_LIBS) |
| 54 | |
| 55 | PKG_CHECK_MODULES(GNOMEKEYRING, gnome-keyring-1 >= $GNOMEKEYRING_REQUIRED) |
| 56 | AC_SUBST(GNOMEKEYRING_CFLAGS) |
| 57 | AC_SUBST(GNOMEKEYRING_LIBS) |
| 58 | |
| 59 | PKG_CHECK_MODULES(REST, rest >= $REST_REQUIRED) |
| 60 | AC_SUBST(REST_CFLAGS) |
| 61 | AC_SUBST(REST_LIBS) |
| 62 | |
| 63 | PKG_CHECK_MODULES(UUID, uuid >= $UUID_REQUIRED) |
| 64 | AC_SUBST(UUID_CFLAGS) |
| 65 | AC_SUBST(UUID_LIBS) |
| 66 | |
| 67 | AC_CONFIG_FILES([Makefile |
| 68 | src/Makefile |
| 69 | data/Makefile |
| 70 | po/Makefile.in]) |
| 71 | |
| 72 | AC_OUTPUT |
| bridges/facebook/facebook.vala |
| 1 | | using Tracker; |
| 2 | | using Rest; |
| 3 | | |
| 4 | | public class FacebookBridge : Object, Tracker.Bridge { |
| 5 | | private const string SERVICE_NAME = "Facebook"; |
| 6 | | private const string API_KEY = "a07366931355e51525938ade2d0df2fb"; |
| 7 | | private const string SHARED_SECRET = "dd34c9d53460953bfd3b5aa87c09b538"; |
| 8 | | private const string TRACKER_NAME = "org.freedesktop.Tracker"; |
| 9 | | private const string TRACKER_RESOURCES_PATH = "/org/freedesktop/Tracker/Resources"; |
| 10 | | private const string TRACKER_RESOURCES_INTERFACE = "org.freedesktop.Tracker.Resources"; |
| 11 | | private const string FACEBOOK_REST = "https://api.facebook.com/restserver.php"; |
| 12 | | |
| 13 | | private DBus.Connection conn; |
| 14 | | dynamic DBus.Object tracker; |
| 15 | | |
| 16 | | private Proxy rest; |
| 17 | | private string auth_token; |
| 18 | | private string session = null; |
| 19 | | private string secret = SHARED_SECRET; |
| 20 | | |
| 21 | | private string username = null; // Is actually the numeric user ID |
| 22 | | |
| 23 | | private uint bridge_status; |
| 24 | | |
| 25 | | public FacebookBridge () |
| 26 | | { |
| 27 | | rest = new Proxy (FACEBOOK_REST, false); |
| 28 | | |
| 29 | | set_bridge_status (Bridge.Status.NotAuthenticated); |
| 30 | | |
| 31 | | try { |
| 32 | | conn = DBus.Bus.get (DBus.BusType.SESSION); |
| 33 | | } catch (Error e) { |
| 34 | | critical ("Cannot connect to session bus : %s\n", e.message); |
| 35 | | } |
| 36 | | |
| 37 | | tracker = conn.get_object (TRACKER_NAME, TRACKER_RESOURCES_PATH, TRACKER_RESOURCES_INTERFACE); |
| 38 | | |
| 39 | | } |
| 40 | | |
| 41 | | // DBus signals callbacks |
| 42 | | public HashTable<string, string> GetAssociationData () |
| 43 | | { |
| 44 | | var ret = new HashTable<string, string> (str_hash, str_equal); |
| 45 | | ProxyCall c = rest.new_call (); |
| 46 | | c.add_param ("method", "auth.createToken"); |
| 47 | | XmlNode node = runCall (c); |
| 48 | | |
| 49 | | if (node.name != "auth_createToken_response") { |
| 50 | | #if DEBUG |
| 51 | | critical ("Got answer %s\n", c.get_payload ()); |
| 52 | | critical ("Couldn't get authentification token"); |
| 53 | | #endif |
| 54 | | Error (DBusErrorCode.AuthenticationError, ""); |
| 55 | | return ret; |
| 56 | | } |
| 57 | | |
| 58 | | auth_token = node.content; |
| 59 | | string url = "http://www.facebook.com/login.php?api_key=%s&v=1.0&auth_token=%s".printf (API_KEY, auth_token); |
| 60 | | ret.insert ("url", url); |
| 61 | | |
| 62 | | ret.insert ("post_message", _("A last browser window will now open, which will allow you to grant Tracker permanent access to your Facebook account. You're not obliged to do so, but if you choose not to grant Tracker a permanent access, you'll need to login again every 24 hours.")); |
| 63 | | ret.insert ("post_url", "http://www.facebook.com/authorize.php?api_key=%s&v=1.0&ext_perm=offline_access".printf (API_KEY)); |
| 64 | | return ret; |
| 65 | | } |
| 66 | | |
| 67 | | // This supposes we have a valid auth_token. Else, well, it'll just fail... |
| 68 | | public void FinishAssociation (HashTable<string, string> data) |
| 69 | | { |
| 70 | | ProxyCall c = rest.new_call (); |
| 71 | | c.add_param ("method", "auth.getSession"); |
| 72 | | c.add_param ("auth_token", auth_token); |
| 73 | | |
| 74 | | { |
| 75 | | XmlNode node = runCall (c); |
| 76 | | |
| 77 | | if (node.name != "auth_getSession_response") { |
| 78 | | #if DEBUG |
| 79 | | critical ("Got answer %s\n", c.get_payload ()); |
| 80 | | critical ("Couldn't get session token"); |
| 81 | | #endif |
| 82 | | Error (DBusErrorCode.AuthenticationError, ""); |
| 83 | | return; |
| 84 | | } |
| 85 | | |
| 86 | | username = node.find ("uid").content; |
| 87 | | secret = node.find ("secret").content; |
| 88 | | session = node.find ("session_key").content; |
| 89 | | } |
| 90 | | |
| 91 | | var pwdData = new HashTable<string, string> (str_hash, str_equal); |
| 92 | | pwdData.insert ("session", session); |
| 93 | | Tracker.passwordProvider.save (username, SERVICE_NAME, secret, "Authentication token for Facebook", pwdData); |
| 94 | | |
| 95 | | c = rest.new_call (); |
| 96 | | c.add_params ("method", "users.getInfo", |
| 97 | | "uids", username, |
| 98 | | "fields", "last_name,first_name", |
| 99 | | null); |
| 100 | | XmlNode node = runCall (c); |
| 101 | | |
| 102 | | if (node.name != "users_getInfo_response") { |
| 103 | | #if DEBUG |
| 104 | | critical ("Got answer %s\n", c.get_payload ()); |
| 105 | | critical ("Couldn't get user info"); |
| 106 | | #endif |
| 107 | | Error (DBusErrorCode.AuthenticationError, _("Couldn't get user information")); |
| 108 | | return; |
| 109 | | } |
| 110 | | |
| 111 | | string realname = node.find ("first_name").content + " " + node.find ("last_name").content; |
| 112 | | |
| 113 | | set_bridge_status (Bridge.Status.Authenticated, realname); |
| 114 | | } |
| 115 | | |
| 116 | | public void Authenticate () |
| 117 | | { |
| 118 | | var pwdData = Tracker.passwordProvider.search (null, SERVICE_NAME); |
| 119 | | |
| 120 | | if (pwdData != null) { |
| 121 | | username = pwdData.lookup ("username"); |
| 122 | | session = pwdData.lookup ("session"); |
| 123 | | secret = pwdData.lookup ("secret"); |
| 124 | | |
| 125 | | ProxyCall c = rest.new_call (); |
| 126 | | c.add_params ("method", "users.getInfo", |
| 127 | | "uids", username, |
| 128 | | "fields", "last_name,first_name", |
| 129 | | null); |
| 130 | | XmlNode node = runCall (c); |
| 131 | | |
| 132 | | if (node.name != "users_getInfo_response") { |
| 133 | | #if DEBUG |
| 134 | | critical ("Couldn't get user info\nGot answer %s\n", c.get_payload ()); |
| 135 | | #endif |
| 136 | | Error (DBusErrorCode.AuthenticationError, _("Couldn't get user information")); |
| 137 | | set_bridge_status (Bridge.Status.WrongToken); |
| 138 | | if (node.find ("error_code").content == "102") { // Session key invalid or no longer valid |
| 139 | | session = null; |
| 140 | | secret = SHARED_SECRET; |
| 141 | | } |
| 142 | | return; |
| 143 | | } |
| 144 | | |
| 145 | | string realname = node.find ("first_name").content + " " + node.find ("last_name").content; |
| 146 | | set_bridge_status (Bridge.Status.Authenticated, realname); |
| 147 | | |
| 148 | | } else { |
| 149 | | set_bridge_status (Bridge.Status.NotAuthenticated); |
| 150 | | return; |
| 151 | | } |
| 152 | | } |
| 153 | | |
| 154 | | public uint GetStatus () |
| 155 | | { |
| 156 | | return bridge_status; |
| 157 | | } |
| 158 | | |
| 159 | | public void Pull () |
| 160 | | { |
| 161 | | |
| 162 | | set_bridge_status (Bridge.Status.Working); |
| 163 | | |
| 164 | | var c = rest.new_call (); |
| 165 | | c.add_params ("method", "fql.query", |
| 166 | | "query", "SELECT pid,src_big,caption FROM photo WHERE aid IN (SELECT aid FROM album WHERE owner='%s')".printf (username)); |
| 167 | | XmlNode root = runCall (c); |
| 168 | | |
| 169 | | XmlNode current = root.find ("photo"); |
| 170 | | while (current != null) { |
| 171 | | if (current.find ("src_big") == null) continue; // FIXME not always wanted |
| 172 | | try { |
| 173 | | string urn = "urn:uuid:%s".printf (uuid_generate_string ()); |
| 174 | | string uri = current.find ("src_big").content; |
| 175 | | |
| 176 | | tracker.SparqlUpdate ("insert {<%s> a nmm:Photo}".printf (urn)); |
| 177 | | tracker.SparqlUpdate ("insert {<%s> a nfo:RemoteDataObject}".printf (uri)); |
| 178 | | |
| 179 | | string caption = current.find ("caption").content; |
| 180 | | if (caption != null) |
| 181 | | tracker.SparqlUpdate ("insert {<%s> rdfs:label '%s'}".printf (urn, caption)); |
| 182 | | } catch (Error e) { |
| 183 | | critical ("Error while inserting data into Tracker : %s", e.message); |
| 184 | | } |
| 185 | | current = current.next; |
| 186 | | } |
| 187 | | } |
| 188 | | |
| 189 | | public void Shutdown () |
| 190 | | { |
| 191 | | #if DEBUG |
| 192 | | stdout.printf ("Shutting down...\n"); |
| 193 | | #endif |
| 194 | | loop.quit (); |
| 195 | | } |
| 196 | | |
| 197 | | // Private functions |
| 198 | | private void set_bridge_status (Tracker.Bridge.Status s, string message = "") |
| 199 | | { |
| 200 | | bridge_status = s; |
| 201 | | StatusChanged ((uint)s, message); |
| 202 | | } |
| 203 | | |
| 204 | | private XmlNode? runCall (ProxyCall c) |
| 205 | | { |
| 206 | | signCall (c); |
| 207 | | |
| 208 | | try { |
| 209 | | c.run (null); |
| 210 | | } catch (Error e) { |
| 211 | | critical ("Error in REST call : %s\n", e.message); |
| 212 | | return null; |
| 213 | | } |
| 214 | | |
| 215 | | var parser = new XmlParser (); |
| 216 | | weak XmlNode ret = parser.parse_from_data (c.get_payload (), c.get_payload_length ()); |
| 217 | | return ret; |
| 218 | | } |
| 219 | | |
| 220 | | // Add version, api_key, call_id and sig |
| 221 | | private void signCall (ProxyCall c) |
| 222 | | { |
| 223 | | var time = TimeVal (); |
| 224 | | var callid = "%ld".printf(1000000*(time.tv_sec) + time.tv_usec); |
| 225 | | c.add_params ("v", "1.0", |
| 226 | | "api_key", API_KEY, |
| 227 | | "call_id", callid, |
| 228 | | null); |
| 229 | | |
| 230 | | if (session != null) |
| 231 | | c.add_param ("session_key", session); |
| 232 | | |
| 233 | | string sig = ""; |
| 234 | | var par = c.get_params (); |
| 235 | | List<weak string> keys = par.get_keys ().copy (); |
| 236 | | keys.sort ((CompareFunc)strcmp); |
| 237 | | |
| 238 | | for (int i = 0 ; i < keys.length () ; ++i) { |
| 239 | | sig += keys.nth_data (i) + "=" + par.lookup (keys.nth_data (i)); |
| 240 | | } |
| 241 | | sig += secret; |
| 242 | | |
| 243 | | sig = Checksum.compute_for_string (ChecksumType.MD5, sig); |
| 244 | | |
| 245 | | c.add_param ("sig", sig); |
| 246 | | } |
| 247 | | } |
| 248 | | |
| 249 | | MainLoop loop; |
| 250 | | |
| 251 | | void main () |
| 252 | | { |
| 253 | | loop = new MainLoop (null, false); |
| 254 | | |
| 255 | | Environment.set_application_name ("FacebookBrige"); |
| 256 | | |
| 257 | | try { |
| 258 | | var conn = DBus.Bus.get (DBus.BusType.SESSION); |
| 259 | | dynamic DBus.Object bus = conn.get_object ("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus"); |
| 260 | | |
| 261 | | uint request_name_result = bus.request_name ("org.freedesktop.Tracker.FacebookBridge", (uint)0); |
| 262 | | if (request_name_result != DBus.RequestNameReply.PRIMARY_OWNER) { |
| 263 | | critical ("Somebody already got our name on the bus !"); |
| 264 | | return; |
| 265 | | } |
| 266 | | |
| 267 | | #if DEBUG |
| 268 | | stdout.printf ("Creating the bridge...\n"); |
| 269 | | #endif |
| 270 | | |
| 271 | | var bridge = new FacebookBridge (); |
| 272 | | conn.register_object ("/org/freedesktop/Tracker/Bridge", bridge); |
| 273 | | |
| 274 | | |
| 275 | | loop.run (); |
| 276 | | } catch (Error e) { |
| 277 | | critical ("Cannot start the bridge : %s", e.message); |
| 278 | | } |
| 279 | | } |
| bridges/facebook/src/facebook.vala |
| 1 | using Tracker; |
| 2 | using Rest; |
| 3 | |
| 4 | public class FacebookBridge : Object, Tracker.Bridge { |
| 5 | private const string SERVICE_NAME = "Facebook"; |
| 6 | private const string API_KEY = "a07366931355e51525938ade2d0df2fb"; |
| 7 | private const string SHARED_SECRET = "dd34c9d53460953bfd3b5aa87c09b538"; |
| 8 | private const string TRACKER_NAME = "org.freedesktop.Tracker"; |
| 9 | private const string TRACKER_RESOURCES_PATH = "/org/freedesktop/Tracker/Resources"; |
| 10 | private const string TRACKER_RESOURCES_INTERFACE = "org.freedesktop.Tracker.Resources"; |
| 11 | private const string FACEBOOK_REST = "https://api.facebook.com/restserver.php"; |
| 12 | |
| 13 | private DBus.Connection conn; |
| 14 | dynamic DBus.Object tracker; |
| 15 | |
| 16 | private Proxy rest; |
| 17 | private string auth_token; |
| 18 | private string session = null; |
| 19 | private string secret = SHARED_SECRET; |
| 20 | |
| 21 | private string username = null; // Is actually the numeric user ID |
| 22 | |
| 23 | private uint bridge_status; |
| 24 | |
| 25 | public FacebookBridge () |
| 26 | { |
| 27 | rest = new Proxy (FACEBOOK_REST, false); |
| 28 | |
| 29 | set_bridge_status (Bridge.Status.NotAuthenticated); |
| 30 | |
| 31 | try { |
| 32 | conn = DBus.Bus.get (DBus.BusType.SESSION); |
| 33 | } catch (Error e) { |
| 34 | critical ("Cannot connect to session bus : %s\n", e.message); |
| 35 | } |
| 36 | |
| 37 | tracker = conn.get_object (TRACKER_NAME, TRACKER_RESOURCES_PATH, TRACKER_RESOURCES_INTERFACE); |
| 38 | |
| 39 | } |
| 40 | |
| 41 | // DBus signals callbacks |
| 42 | public HashTable<string, string> GetAssociationData () |
| 43 | { |
| 44 | var ret = new HashTable<string, string> (str_hash, str_equal); |
| 45 | ProxyCall c = rest.new_call (); |
| 46 | c.add_param ("method", "auth.createToken"); |
| 47 | XmlNode node = runCall (c); |
| 48 | |
| 49 | if (node.name != "auth_createToken_response") { |
| 50 | #if DEBUG |
| 51 | critical ("Got answer %s\n", c.get_payload ()); |
| 52 | critical ("Couldn't get authentification token"); |
| 53 | #endif |
| 54 | Error (DBusErrorCode.AuthenticationError, ""); |
| 55 | return ret; |
| 56 | } |
| 57 | |
| 58 | auth_token = node.content; |
| 59 | string url = "http://www.facebook.com/login.php?api_key=%s&v=1.0&auth_token=%s".printf (API_KEY, auth_token); |
| 60 | ret.insert ("url", url); |
| 61 | |
| 62 | ret.insert ("post_message", _("A last browser window will now open, which will allow you to grant Tracker permanent access to your Facebook account. You're not obliged to do so, but if you choose not to grant Tracker a permanent access, you'll need to login again every 24 hours.")); |
| 63 | ret.insert ("post_url", "http://www.facebook.com/authorize.php?api_key=%s&v=1.0&ext_perm=offline_access".printf (API_KEY)); |
| 64 | return ret; |
| 65 | } |
| 66 | |
| 67 | // This supposes we have a valid auth_token. Else, well, it'll just fail... |
| 68 | public void FinishAssociation (HashTable<string, string> data) |
| 69 | { |
| 70 | ProxyCall c = rest.new_call (); |
| 71 | c.add_param ("method", "auth.getSession"); |
| 72 | c.add_param ("auth_token", auth_token); |
| 73 | |
| 74 | { |
| 75 | XmlNode node = runCall (c); |
| 76 | |
| 77 | if (node.name != "auth_getSession_response") { |
| 78 | #if DEBUG |
| 79 | critical ("Got answer %s\n", c.get_payload ()); |
| 80 | critical ("Couldn't get session token"); |
| 81 | #endif |
| 82 | Error (DBusErrorCode.AuthenticationError, ""); |
| 83 | return; |
| 84 | } |
| 85 | |
| 86 | username = node.find ("uid").content; |
| 87 | secret = node.find ("secret").content; |
| 88 | session = node.find ("session_key").content; |
| 89 | } |
| 90 | |
| 91 | var pwdData = new HashTable<string, string> (str_hash, str_equal); |
| 92 | pwdData.insert ("session", session); |
| 93 | Tracker.passwordProvider.save (username, SERVICE_NAME, secret, "Authentication token for Facebook", pwdData); |
| 94 | |
| 95 | c = rest.new_call (); |
| 96 | c.add_params ("method", "users.getInfo", |
| 97 | "uids", username, |
| 98 | "fields", "last_name,first_name", |
| 99 | null); |
| 100 | XmlNode node = runCall (c); |
| 101 | |
| 102 | if (node.name != "users_getInfo_response") { |
| 103 | #if DEBUG |
| 104 | critical ("Got answer %s\n", c.get_payload ()); |
| 105 | critical ("Couldn't get user info"); |
| 106 | #endif |
| 107 | Error (DBusErrorCode.AuthenticationError, _("Couldn't get user information")); |
| 108 | return; |
| 109 | } |
| 110 | |
| 111 | string realname = node.find ("first_name").content + " " + node.find ("last_name").content; |
| 112 | |
| 113 | set_bridge_status (Bridge.Status.Authenticated, realname); |
| 114 | } |
| 115 | |
| 116 | public void Authenticate () |
| 117 | { |
| 118 | var pwdData = Tracker.passwordProvider.search (null, SERVICE_NAME); |
| 119 | |
| 120 | if (pwdData != null) { |
| 121 | username = pwdData.lookup ("username"); |
| 122 | session = pwdData.lookup ("session"); |
| 123 | secret = pwdData.lookup ("secret"); |
| 124 | |
| 125 | ProxyCall c = rest.new_call (); |
| 126 | c.add_params ("method", "users.getInfo", |
| 127 | "uids", username, |
| 128 | "fields", "last_name,first_name", |
| 129 | null); |
| 130 | XmlNode node = runCall (c); |
| 131 | |
| 132 | if (node.name != "users_getInfo_response") { |
| 133 | #if DEBUG |
| 134 | critical ("Couldn't get user info\nGot answer %s\n", c.get_payload ()); |
| 135 | #endif |
| 136 | Error (DBusErrorCode.AuthenticationError, _("Couldn't get user information")); |
| 137 | set_bridge_status (Bridge.Status.WrongToken); |
| 138 | if (node.find ("error_code").content == "102") { // Session key invalid or no longer valid |
| 139 | session = null; |
| 140 | secret = SHARED_SECRET; |
| 141 | } |
| 142 | return; |
| 143 | } |
| 144 | |
| 145 | string realname = node.find ("first_name").content + " " + node.find ("last_name").content; |
| 146 | set_bridge_status (Bridge.Status.Authenticated, realname); |
| 147 | |
| 148 | } else { |
| 149 | set_bridge_status (Bridge.Status.NotAuthenticated); |
| 150 | return; |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | public uint GetStatus () |
| 155 | { |
| 156 | return bridge_status; |
| 157 | } |
| 158 | |
| 159 | public void Pull () |
| 160 | { |
| 161 | |
| 162 | set_bridge_status (Bridge.Status.Working); |
| 163 | |
| 164 | var c = rest.new_call (); |
| 165 | c.add_params ("method", "fql.query", |
| 166 | "query", "SELECT pid,src_big,caption FROM photo WHERE aid IN (SELECT aid FROM album WHERE owner='%s')".printf (username)); |
| 167 | XmlNode root = runCall (c); |
| 168 | |
| 169 | XmlNode current = root.find ("photo"); |
| 170 | while (current != null) { |
| 171 | if (current.find ("src_big") == null) continue; // FIXME not always wanted |
| 172 | try { |
| 173 | string urn = "urn:uuid:%s".printf (uuid_generate_string ()); |
| 174 | string uri = current.find ("src_big").content; |
| 175 | |
| 176 | tracker.SparqlUpdate ("insert {<%s> a nmm:Photo}".printf (urn)); |
| 177 | tracker.SparqlUpdate ("insert {<%s> a nfo:RemoteDataObject}".printf (uri)); |
| 178 | |
| 179 | string caption = current.find ("caption").content; |
| 180 | if (caption != null) |
| 181 | tracker.SparqlUpdate ("insert {<%s> rdfs:label '%s'}".printf (urn, caption)); |
| 182 | } catch (Error e) { |
| 183 | critical ("Error while inserting data into Tracker : %s", e.message); |
| 184 | } |
| 185 | current = current.next; |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | public void Shutdown () |
| 190 | { |
| 191 | #if DEBUG |
| 192 | stdout.printf ("Shutting down...\n"); |
| 193 | #endif |
| 194 | loop.quit (); |
| 195 | } |
| 196 | |
| 197 | // Private functions |
| 198 | private void set_bridge_status (Tracker.Bridge.Status s, string message = "") |
| 199 | { |
| 200 | bridge_status = s; |
| 201 | StatusChanged ((uint)s, message); |
| 202 | } |
| 203 | |
| 204 | private XmlNode? runCall (ProxyCall c) |
| 205 | { |
| 206 | signCall (c); |
| 207 | |
| 208 | try { |
| 209 | c.run (null); |
| 210 | } catch (Error e) { |
| 211 | critical ("Error in REST call : %s\n", e.message); |
| 212 | return null; |
| 213 | } |
| 214 | |
| 215 | var parser = new XmlParser (); |
| 216 | weak XmlNode ret = parser.parse_from_data (c.get_payload (), c.get_payload_length ()); |
| 217 | return ret; |
| 218 | } |
| 219 | |
| 220 | // Add version, api_key, call_id and sig |
| 221 | private void signCall (ProxyCall c) |
| 222 | { |
| 223 | var time = TimeVal (); |
| 224 | var callid = "%ld".printf(1000000*(time.tv_sec) + time.tv_usec); |
| 225 | c.add_params ("v", "1.0", |
| 226 | "api_key", API_KEY, |
| 227 | "call_id", callid, |
| 228 | null); |
| 229 | |
| 230 | if (session != null) |
| 231 | c.add_param ("session_key", session); |
| 232 | |
| 233 | string sig = ""; |
| 234 | var par = c.get_params (); |
| 235 | List<weak string> keys = par.get_keys ().copy (); |
| 236 | keys.sort ((CompareFunc)strcmp); |
| 237 | |
| 238 | for (int i = 0 ; i < keys.length () ; ++i) { |
| 239 | sig += keys.nth_data (i) + "=" + par.lookup (keys.nth_data (i)); |
| 240 | } |
| 241 | sig += secret; |
| 242 | |
| 243 | sig = Checksum.compute_for_string (ChecksumType.MD5, sig); |
| 244 | |
| 245 | c.add_param ("sig", sig); |
| 246 | } |
| 247 | } |
| 248 | |
| 249 | MainLoop loop; |
| 250 | |
| 251 | void main () |
| 252 | { |
| 253 | loop = new MainLoop (null, false); |
| 254 | |
| 255 | Environment.set_application_name ("FacebookBrige"); |
| 256 | |
| 257 | try { |
| 258 | var conn = DBus.Bus.get (DBus.BusType.SESSION); |
| 259 | dynamic DBus.Object bus = conn.get_object ("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus"); |
| 260 | |
| 261 | uint request_name_result = bus.request_name ("org.freedesktop.Tracker.FacebookBridge", (uint)0); |
| 262 | if (request_name_result != DBus.RequestNameReply.PRIMARY_OWNER) { |
| 263 | critical ("Somebody already got our name on the bus !"); |
| 264 | return; |
| 265 | } |
| 266 | |
| 267 | #if DEBUG |
| 268 | stdout.printf ("Creating the bridge...\n"); |
| 269 | #endif |
| 270 | |
| 271 | var bridge = new FacebookBridge (); |
| 272 | conn.register_object ("/org/freedesktop/Tracker/Bridge", bridge); |
| 273 | |
| 274 | |
| 275 | loop.run (); |
| 276 | } catch (Error e) { |
| 277 | critical ("Cannot start the bridge : %s", e.message); |
| 278 | } |
| 279 | } |