Add Cristian Greco as uploader
[debian/pino.git] / src / rest_api_abstract.vala
index 1d7ae45..67dd551 100644 (file)
@@ -23,13 +23,16 @@ using Gee;
 using Soup;
 using Auth;
 using TimeUtils;
 using Soup;
 using Auth;
 using TimeUtils;
+using Xml;
 
 namespace RestAPI {
 
 public class Status : Object {
 
 namespace RestAPI {
 
 public class Status : Object {
+
        public string id;
        public string text;
        public Time created_at = Time();
        public string id;
        public string text;
        public Time created_at = Time();
+       public string created_at_s = "";
        public string user_name;
        public string user_screen_name;
        public string user_avatar;
        public string user_name;
        public string user_screen_name;
        public string user_avatar;
@@ -43,16 +46,39 @@ public class Status : Object {
        public string to_status_id = "";
        
        public bool is_retweet = false;
        public string to_status_id = "";
        
        public bool is_retweet = false;
+       public bool is_favorite = false;
+       
+       public bool tmp = false;
+}
+
+public class FullStatus : Status {
+       
+       public string followers = "";
+       public string friends = "";
+       public string statuses = "";
+       public string url = "";
+       public string desc = "";
+       public bool following = false;
+       
+       public bool done = false;
+       
 }
 
 public struct AuthData {
 }
 
 public struct AuthData {
-       public string login = "";
-       public string password = "";
-       public string service = "";
+       public string login;
+       public string password;
+       public string service;
 } 
 
 } 
 
+public static enum ServiceType {
+       TWITTER,
+       IDENTICA,
+       UNKNOWN
+}
+
 errordomain RestError {
 errordomain RestError {
-       CODE
+       CODE,
+       CODE_404
 }
 
 errordomain ParseError {
 }
 
 errordomain ParseError {
@@ -61,28 +87,62 @@ errordomain ParseError {
 
 public static enum TimelineType {
        HOME,
 
 public static enum TimelineType {
        HOME,
-       MENTIONS
+       MENTIONS,
+       USER,
+       FAVORITES
 }
 
 public abstract class RestAPIAbstract : Object {
        
 }
 
 public abstract class RestAPIAbstract : Object {
        
-       protected IRestUrls urls;
+       protected RestUrls urls;
        public Account? account;
        
        public Account? account;
        
+       private SessionAsync session_async;
+       private SessionSync session_sync;
+       
        public RestAPIAbstract(Account? _account) {
        public RestAPIAbstract(Account? _account) {
+               urls = new RestUrls(ServiceType.UNKNOWN);
                set_auth(_account);
                set_auth(_account);
+
+               session_async = new SessionAsync();
+               session_sync = new SessionSync();
+
+               session_async.timeout = 30; //seconds
+               session_sync.timeout = 30; //seconds
+
+               //Basic HTTP authorization
+        session_async.authenticate.connect(http_auth);
+        session_sync.authenticate.connect(http_auth);
+       }
+
+       private void http_auth(Message msg, Soup.Auth auth, bool retrying) {
+               if(retrying)
+                               return;
+
+               auth.authenticate(account.login, account.password);
        }
        
        }
        
-       private IRestUrls select_urls(string service) {
-               switch(service) {
+       private void select_urls() {
+               switch(account.service) {
                        case "twitter.com":
                        case "twitter.com":
-                               return new TwitterUrls();
+                               urls.set_prefix(ServiceType.TWITTER);
+                               break;
                        
                        case "identi.ca":
                        
                        case "identi.ca":
-                               return new IdenticaUrls();
+                               urls.set_prefix(ServiceType.IDENTICA);
+                               break;
+                       
+                       case "other":
+                               string proxy = "http://api.twitter.com/";
+                               if(account.proxy != "")
+                                       proxy = account.proxy;
+                               
+                               urls.set_prefix(ServiceType.UNKNOWN, proxy);
+                               break;
                        
                        default:
                        
                        default:
-                               return new TwitterUrls();
+                               urls.set_prefix(ServiceType.TWITTER);
+                               break;
                }
        }
        
                }
        }
        
@@ -90,13 +150,13 @@ public abstract class RestAPIAbstract : Object {
                account = _account;
                
                if(account != null)
                account = _account;
                
                if(account != null)
-                       urls = select_urls(account.service);
+                       select_urls();
        }
        
        public signal void request(string req);
        
        }
        
        public signal void request(string req);
        
-       public virtual ArrayList<Status>? get_timeline(int count = 0,
-               string since_id = "", string max_id = "") throws RestError, ParseError {
+       public virtual ArrayList<Status>? get_timeline(int count = 0, FullStatus? fstatus = null,
+               string since_id = "", string max_id = "", bool sync = true) throws RestError, ParseError {
                
                return null;
        }
                
                return null;
        }
@@ -105,6 +165,12 @@ public abstract class RestAPIAbstract : Object {
                return new Status();
        }
        
                return new Status();
        }
        
+       public virtual void favorite_create(string id) throws RestError {}
+       public virtual void favorite_destroy(string id) throws RestError {}
+       
+       public virtual void follow_create(string screen_name) throws RestError {}
+       public virtual void follow_destroy(string screen_name) throws RestError {}
+       
        public virtual void destroy_status(string id) throws RestError {}
        
        protected void reply_tracking(int status_code) throws RestError {
        public virtual void destroy_status(string id) throws RestError {}
        
        protected void reply_tracking(int status_code) throws RestError {
@@ -112,6 +178,12 @@ public abstract class RestAPIAbstract : Object {
                        case 2:
                                throw new RestError.CODE("Connection problems: can't connect to the server.");
                        
                        case 2:
                                throw new RestError.CODE("Connection problems: can't connect to the server.");
                        
+                       case 4:
+                               throw new RestError.CODE("Connection problems: timeout.");
+                       
+                       case 400:
+                               throw new RestError.CODE("%d Rate limiting: you have reached the limit requests.".printf(status_code));
+                       
                        case 401:
                                throw new RestError.CODE("%d Unauthorized: the request requires user authentication.".printf(status_code));
                        
                        case 401:
                                throw new RestError.CODE("%d Unauthorized: the request requires user authentication.".printf(status_code));
                        
@@ -119,7 +191,7 @@ public abstract class RestAPIAbstract : Object {
                                throw new RestError.CODE("%d Forbidden: the server understood the request, but is refusing to fulfill it.".printf(status_code));
                        
                        case 404:
                                throw new RestError.CODE("%d Forbidden: the server understood the request, but is refusing to fulfill it.".printf(status_code));
                        
                        case 404:
-                               throw new RestError.CODE("%d Not Found: The server has not found anything matching the Request-URI.".printf(status_code));
+                               throw new RestError.CODE_404("%d Not Found: The server has not found anything matching the Request-URI.".printf(status_code));
                        
                        case 407:
                                throw new RestError.CODE("%d Proxy Authentication Required: the request requires user authentication.".printf(status_code));
                        
                        case 407:
                                throw new RestError.CODE("%d Proxy Authentication Required: the request requires user authentication.".printf(status_code));
@@ -134,78 +206,111 @@ public abstract class RestAPIAbstract : Object {
        }
        
        public string make_request(owned string req_url, string method,
        }
        
        public string make_request(owned string req_url, string method,
-               HashTable<string, string> params = new HashTable<string, string>(null, null),
+               HashTable<string, string> params = new HashTable<string, string>(str_hash, str_equal),
                bool async = true, int retry = 3) throws RestError {
                
                if(account == null)
                        no_account();
                
                bool async = true, int retry = 3) throws RestError {
                
                if(account == null)
                        no_account();
                
-               if(method == "GET") { //set get-parameters
-                       string query = "";
-               
-                       if(params.size() > 0) {
-                               query = "?";
-                               
-                               //Very dirty. HashTable.loockup() doesn't work. Bug?
-                               int tmp_iter = 0;
-                               foreach(string key in params.get_keys()) {
-                                       int tmp_iter2 = 0;
-                                       foreach(string val in params.get_values()) {
-                                               if(tmp_iter2 == tmp_iter) {
-                                                       query += Soup.form_encode(key, val);
-                                                       if(tmp_iter < params.size() - 1)
-                                                               query += "&";
-                                                       break;
-                                               }
-                                               tmp_iter2++;
-                                       }
-                                       tmp_iter++;
-                               }
-                       }
-                       req_url += query;
-               }
-               
                //send signal about all requests
         request("%s: %s".printf(method, req_url));
         
         Session session;
         
                if(async)
                //send signal about all requests
         request("%s: %s".printf(method, req_url));
         
         Session session;
         
                if(async)
-                       session = new SessionAsync();
+                       session = session_async;
                else
                else
-                       session = new SessionSync();
+                       session = session_sync;
                
                
-        Message message = new Message(method, req_url);
-        message.set_http_version (HTTPVersion.1_1);
-        
-        MessageHeaders headers = new MessageHeaders(MessageHeadersType.MULTIPART);
-        headers.append("User-Agent", "%s/%s".printf(Config.APPNAME, Config.APP_VERSION));
-        
-        message.request_headers = headers;
-        
-        if(method != "GET") { //set post/delete-parameters
-               string body = form_encode_hash(params);
-                       message.set_request("application/x-www-form-urlencoded",
-                               MemoryUse.COPY, body, (int)body.size());
-               }
-               
-               //Basic HTTP authorization
-        session.authenticate += (sess, msg, auth, retrying) => {
-                       if (retrying) return;
-                       auth.authenticate(account.login, account.password);
-               };
+               Message message = form_request_new_from_hash(method, req_url, params);
+               message.request_headers.append("User-Agent", "%s/%s".printf(Config.APPNAME, Config.APP_VERSION));
                
                
+               debug("and one more");
                int status_code = 0;
                for(int i = 0; i < retry; i++) {
                int status_code = 0;
                for(int i = 0; i < retry; i++) {
-                       status_code = (int)session.send_message(message);
-                       if(status_code == 200 || status_code == 401)
+                       debug("go into loop");
+                       try {
+                               status_code = (int)session.send_message(message);
+                       } catch(GLib.Error e) {
+                               debug("we got some error: %s", e.message);
+                               break;
+                       }
+                       debug("something recieve %d", status_code);
+                       if(status_code == 200 || status_code == 401 || status_code == 4)
                                break;
                }
                                break;
                }
-               
+               debug("...");
                if(status_code != 200)
                        reply_tracking(status_code);
                if(status_code != 200)
                        reply_tracking(status_code);
+
+               debug("end of make_request");
+               
+               return (string)message.response_body.data;
+       }
+       
+       /* check user for DM availability */
+       public bool check_friendship(string screen_name,
+               bool just_friend_check = false) throws RestError, ParseError {
+               
+               string req_url = urls.friendship();
+               
+               var map = new HashTable<string, string>(str_hash, str_equal);
+               map.insert("source_screen_name", account.login);
+               map.insert("target_screen_name", screen_name);
+               debug(req_url);
+               string data = make_request(req_url, "GET", map);
+
+               Parser.init();
+               bool result = parse_friendship(data, just_friend_check);
+               Parser.cleanup();
+
+               return result;
+       }
+       
+       private bool parse_friendship(string data,
+               bool just_friend_check = false) throws ParseError {
+
+               bool followed_by = false;
+               bool following = false;
+               
+               Xml.Doc* xmlDoc = Xml.Parser.parse_memory(data, (int)data.size());
+               if(xmlDoc == null)
+                       throw new ParseError.CODE("Invalid XML data");
+               
+               Xml.Node* rootNode = xmlDoc->get_root_element();
+               
+               Xml.Node* iter;
+               for(iter = rootNode->children; iter != null; iter = iter->next) {
+                       if (iter->type != ElementType.ELEMENT_NODE)
+                               continue;
+                       
+                       if(iter->name == "target") {
+                               
+                               Xml.Node* iter_in;
+                               for(iter_in = iter->children; iter_in != null; iter_in = iter_in->next) {
+                                       switch(iter_in->name) {
+                                               case "followed_by":
+                                                       followed_by = iter_in->get_content().to_bool();
+                                                       break;
+                                       
+                                               case "following":
+                                                       following = iter_in->get_content().to_bool();
+                                                       break;
+                                       }
+                               }
+                               delete iter_in;
+                               break;
+                       }
+                       
+               } delete iter;
+               
+               if(just_friend_check && followed_by)
+                       return true;
+               
+               if(followed_by && following)
+                       return true;
                
                
-               return (string)message.response_body.flatten().data;
+               return false;
        }
 }
 
        }
 }