diff options
author | Brendan Long <self@brendanlong.com> | 2019-02-20 21:05:48 +0300 |
---|---|---|
committer | Brendan Long <self@brendanlong.com> | 2019-02-20 21:12:04 +0300 |
commit | 898bdfd2a3afcd164a4b0b8b03a0efa97a8fcb47 (patch) | |
tree | ce286260e0f8105754d59f0b6071135b61e5557a /plugins | |
parent | d919c675a3fc221ca289a43476c92f338275c11c (diff) |
Cleanup Vala indentation
This isn't perfect but it's way better than what Uncrustify generated
Diffstat (limited to 'plugins')
66 files changed, 15041 insertions, 15041 deletions
diff --git a/plugins/backend/bazqux/bazquxAPI.vala b/plugins/backend/bazqux/bazquxAPI.vala index a55ee589..e153fb74 100644 --- a/plugins/backend/bazqux/bazquxAPI.vala +++ b/plugins/backend/bazqux/bazquxAPI.vala @@ -14,423 +14,423 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.bazquxAPI : GLib.Object { - -public enum bazquxSubscriptionAction { - EDIT, - SUBSCRIBE, - UNSUBSCRIBE -} - -private bazquxConnection m_connection; -private bazquxUtils m_utils; -private string m_userID; - -public bazquxAPI(bazquxUtils utils) -{ - m_utils = utils; - m_connection = new bazquxConnection(utils); -} - -public LoginResponse login() -{ - if(m_utils.getAccessToken() == "") - { - var result = m_connection.getToken(); - if(getUserID()) + + public enum bazquxSubscriptionAction { + EDIT, + SUBSCRIBE, + UNSUBSCRIBE + } + + private bazquxConnection m_connection; + private bazquxUtils m_utils; + private string m_userID; + + public bazquxAPI(bazquxUtils utils) + { + m_utils = utils; + m_connection = new bazquxConnection(utils); + } + + public LoginResponse login() + { + if(m_utils.getAccessToken() == "") { - return result; + var result = m_connection.getToken(); + if(getUserID()) + { + return result; + } } + else if(getUserID()) + { + return LoginResponse.SUCCESS; + } + + return LoginResponse.UNKNOWN_ERROR; } - else if(getUserID()) - { - return LoginResponse.SUCCESS; - } - - return LoginResponse.UNKNOWN_ERROR; -} - -public bool ping() -{ - return m_connection.ping(); -} - -private bool getUserID() -{ - Logger.debug("getUserID: getting user info"); - var msg = new bazquxMessage(); - msg.add("output", "json"); - var response = m_connection.send_get_request("user-info", msg.get()); - - if(response.status != 200) - { - return false; - } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); - } - catch(Error e) - { - Logger.error("getUserID: Could not load message response"); - Logger.error(e.message); - return false; - } - var root = parser.get_root().get_object(); - if(root.has_member("userId")) - { - m_userID = root.get_string_member("userId"); - m_utils.setUserID(m_userID); - Logger.info("bazqux: userID = " + m_userID); - - return true; - } - - return false; -} - -public bool getFeeds(Gee.List<Feed> feeds) -{ - var msg = new bazquxMessage(); - msg.add("output", "json"); - var response = m_connection.send_get_request("subscription/list", msg.get()); - - if(response.status != 200) - { - return false; - } - - Logger.debug(response.data); - var parser = new Json.Parser(); - try + + public bool ping() { - parser.load_from_data(response.data, -1); + return m_connection.ping(); } - catch(Error e) + + private bool getUserID() { - Logger.error("getFeeds: Could not load message response"); - Logger.error(e.message); + Logger.debug("getUserID: getting user info"); + var msg = new bazquxMessage(); + msg.add("output", "json"); + var response = m_connection.send_get_request("user-info", msg.get()); + + if(response.status != 200) + { + return false; + } + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("getUserID: Could not load message response"); + Logger.error(e.message); + return false; + } + var root = parser.get_root().get_object(); + if(root.has_member("userId")) + { + m_userID = root.get_string_member("userId"); + m_utils.setUserID(m_userID); + Logger.info("bazqux: userID = " + m_userID); + + return true; + } + return false; } - var root = parser.get_root().get_object(); - var array = root.get_array_member("subscriptions"); - uint length = array.get_length(); - - for (uint i = 0; i < length; i++) + + public bool getFeeds(Gee.List<Feed> feeds) { - Json.Object object = array.get_object_element(i); - - string feedID = object.get_string_member("id"); - string url = object.has_member("htmlUrl") ? object.get_string_member("htmlUrl") : object.get_string_member("url"); - string? icon_url = object.get_string_member("iconUrl"); - - uint catCount = object.get_array_member("categories").get_length(); - var categories = new Gee.ArrayList<string>(); - for(uint j = 0; j < catCount; ++j) + var msg = new bazquxMessage(); + msg.add("output", "json"); + var response = m_connection.send_get_request("subscription/list", msg.get()); + + if(response.status != 200) { - categories.add(object.get_array_member("categories").get_object_element(j).get_string_member("id")); + return false; } - feeds.add( - new Feed( - feedID, - object.get_string_member("title"), - url, - 0, - categories, - icon_url, - object.get_string_member("url") + + Logger.debug(response.data); + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("getFeeds: Could not load message response"); + Logger.error(e.message); + return false; + } + var root = parser.get_root().get_object(); + var array = root.get_array_member("subscriptions"); + uint length = array.get_length(); + + for (uint i = 0; i < length; i++) + { + Json.Object object = array.get_object_element(i); + + string feedID = object.get_string_member("id"); + string url = object.has_member("htmlUrl") ? object.get_string_member("htmlUrl") : object.get_string_member("url"); + string? icon_url = object.get_string_member("iconUrl"); + + uint catCount = object.get_array_member("categories").get_length(); + var categories = new Gee.ArrayList<string>(); + for(uint j = 0; j < catCount; ++j) + { + categories.add(object.get_array_member("categories").get_object_element(j).get_string_member("id")); + } + feeds.add( + new Feed( + feedID, + object.get_string_member("title"), + url, + 0, + categories, + icon_url, + object.get_string_member("url") ) ); + } + return true; } - return true; -} - -public bool getCategoriesAndTags(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags) -{ - var msg = new bazquxMessage(); - msg.add("output", "json"); - var response = m_connection.send_get_request("tag/list", msg.get()); - - if(response.status != 200) - { - return false; - } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); - } - catch(Error e) - { - Logger.error("getCategoriesAndTags: Could not load message response"); - Logger.error(e.message); - return false; - } - var root = parser.get_root().get_object(); - var array = root.get_array_member("tags"); - uint length = array.get_length(); - int orderID = 0; - - var db = DataBase.readOnly(); - for (uint i = 0; i < length; i++) + + public bool getCategoriesAndTags(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags) { - Json.Object object = array.get_object_element(i); - string id = object.get_string_member("id"); - int start = id.last_index_of_char('/') + 1; - string title = id.substring(start); - - if(id.contains("/label/")) + var msg = new bazquxMessage(); + msg.add("output", "json"); + var response = m_connection.send_get_request("tag/list", msg.get()); + + if(response.status != 200) + { + return false; + } + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("getCategoriesAndTags: Could not load message response"); + Logger.error(e.message); + return false; + } + var root = parser.get_root().get_object(); + var array = root.get_array_member("tags"); + uint length = array.get_length(); + int orderID = 0; + + var db = DataBase.readOnly(); + for (uint i = 0; i < length; i++) { - if(m_utils.tagIsCat(id, feeds)) + Json.Object object = array.get_object_element(i); + string id = object.get_string_member("id"); + int start = id.last_index_of_char('/') + 1; + string title = id.substring(start); + + if(id.contains("/label/")) { - categories.add( - new Category( - id, - title, - 0, - orderID, - CategoryID.MASTER.to_string(), - 1 + if(m_utils.tagIsCat(id, feeds)) + { + categories.add( + new Category( + id, + title, + 0, + orderID, + CategoryID.MASTER.to_string(), + 1 ) ); - ++orderID; - } - else - { - tags.add( - new Tag( - id, - title, - db.getTagColor() + ++orderID; + } + else + { + tags.add( + new Tag( + id, + title, + db.getTagColor() ) ); + } } } + return true; } - return true; -} - - -public int getTotalUnread() -{ - var msg = new bazquxMessage(); - msg.add("output", "json"); - var response = m_connection.send_get_request("unread-count", msg.get()); - - if(response.status != 200) - { - return 0; - } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); - } - catch(Error e) - { - Logger.error("getTotalUnread: Could not load message response"); - Logger.error(e.message); - } - - var root = parser.get_root().get_object(); - var array = root.get_array_member("unreadcounts"); - uint length = array.get_length(); - int count = 0; - - for (uint i = 0; i < length; i++) + + + public int getTotalUnread() { - Json.Object object = array.get_object_element(i); - if(object.get_string_member("id").has_prefix("feed/")) + var msg = new bazquxMessage(); + msg.add("output", "json"); + var response = m_connection.send_get_request("unread-count", msg.get()); + + if(response.status != 200) { - count += (int)object.get_int_member("count"); + return 0; } - - } - - Logger.debug("getTotalUnread %i".printf(count)); - return count; -} - - -public string? updateArticles(Gee.List<string> ids, int count, string? continuation = null) -{ - var msg = new bazquxMessage(); - msg.add("output", "json"); - msg.add("n", count.to_string()); - msg.add("xt", "user/-/state/com.google/read"); - if(continuation != null) - { - msg.add("c", continuation); - } - - var response = m_connection.send_get_request("stream/items/ids", msg.get()); - - if(response.status != 200) - { - return null; - } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); - } - catch(Error e) - { - Logger.error("updateArticles: Could not load message response"); - Logger.error(e.message); - } - - var root = parser.get_root().get_object(); - var array = root.get_array_member("itemRefs"); - uint length = array.get_length(); - - for (uint i = 0; i < length; i++) - { - Json.Object object = array.get_object_element(i); - ids.add(object.get_string_member("id")); - } - - if(root.has_member("continuation") && root.get_string_member("continuation") != "") - { - return root.get_string_member("continuation"); + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("getTotalUnread: Could not load message response"); + Logger.error(e.message); + } + + var root = parser.get_root().get_object(); + var array = root.get_array_member("unreadcounts"); + uint length = array.get_length(); + int count = 0; + + for (uint i = 0; i < length; i++) + { + Json.Object object = array.get_object_element(i); + if(object.get_string_member("id").has_prefix("feed/")) + { + count += (int)object.get_int_member("count"); + } + + } + + Logger.debug("getTotalUnread %i".printf(count)); + return count; } - - return null; -} - -public string? getArticles(Gee.List<Article> articles, int count, ArticleStatus whatToGet = ArticleStatus.ALL, string? continuation = null, string? tagID = null, string? feed_id = null) -{ - var msg = new bazquxMessage(); - msg.add("output", "json"); - msg.add("n", count.to_string()); - - if(whatToGet == ArticleStatus.UNREAD) + + + public string? updateArticles(Gee.List<string> ids, int count, string? continuation = null) { + var msg = new bazquxMessage(); + msg.add("output", "json"); + msg.add("n", count.to_string()); msg.add("xt", "user/-/state/com.google/read"); - } - if(whatToGet == ArticleStatus.READ) - { - msg.add("s", "user/-/state/com.google/read"); - } - else if(whatToGet == ArticleStatus.MARKED) - { - msg.add("s", "user/-/state/com.google/starred"); - } - - if( continuation != null ) - { - msg.add("c", continuation); - } - - string api_endpoint = "stream/contents"; - if(feed_id != null) - { - api_endpoint += "/" + feed_id; - } - else if(tagID != null) - { - api_endpoint += "/" + tagID; - } - var response = m_connection.send_get_request(api_endpoint, msg.get()); - - if(response.status != 200) - { + if(continuation != null) + { + msg.add("c", continuation); + } + + var response = m_connection.send_get_request("stream/items/ids", msg.get()); + + if(response.status != 200) + { + return null; + } + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("updateArticles: Could not load message response"); + Logger.error(e.message); + } + + var root = parser.get_root().get_object(); + var array = root.get_array_member("itemRefs"); + uint length = array.get_length(); + + for (uint i = 0; i < length; i++) + { + Json.Object object = array.get_object_element(i); + ids.add(object.get_string_member("id")); + } + + if(root.has_member("continuation") && root.get_string_member("continuation") != "") + { + return root.get_string_member("continuation"); + } + return null; } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); - } - catch(Error e) - { - Logger.error("getArticles: Could not load message response"); - Logger.error(e.message); - } - - var root = parser.get_root().get_object(); - var array = root.get_array_member("items"); - uint length = array.get_length(); - - var db = DataBase.readOnly(); - for (uint i = 0; i < length; i++) + + public string? getArticles(Gee.List<Article> articles, int count, ArticleStatus whatToGet = ArticleStatus.ALL, string? continuation = null, string? tagID = null, string? feed_id = null) { - Json.Object object = array.get_object_element(i); - string id = object.get_string_member("id"); - id = id.substring(id.last_index_of_char('/') + 1); - bool marked = false; - bool read = false; - var cats = object.get_array_member("categories"); - uint cat_length = cats.get_length(); - - var tags = new Gee.ArrayList<string>(); - for (uint j = 0; j < cat_length; j++) + var msg = new bazquxMessage(); + msg.add("output", "json"); + msg.add("n", count.to_string()); + + if(whatToGet == ArticleStatus.UNREAD) { - string cat = cats.get_string_element(j); - if(cat.has_suffix("com.google/starred")) - { - marked = true; - } - else if(cat.has_suffix("com.google/read")) - { - read = true; - } - else if(cat.contains("/label/") && db.getTagName(cat) != null) - { - tags.add(cat); - } + msg.add("xt", "user/-/state/com.google/read"); } - - var enclosures = new Gee.ArrayList<Enclosure>(); - if(object.has_member("enclosure")) + if(whatToGet == ArticleStatus.READ) { - var attachments = object.get_array_member("enclosure"); - - uint mediaCount = 0; - if(attachments != null) + msg.add("s", "user/-/state/com.google/read"); + } + else if(whatToGet == ArticleStatus.MARKED) + { + msg.add("s", "user/-/state/com.google/starred"); + } + + if( continuation != null ) + { + msg.add("c", continuation); + } + + string api_endpoint = "stream/contents"; + if(feed_id != null) + { + api_endpoint += "/" + feed_id; + } + else if(tagID != null) + { + api_endpoint += "/" + tagID; + } + var response = m_connection.send_get_request(api_endpoint, msg.get()); + + if(response.status != 200) + { + return null; + } + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("getArticles: Could not load message response"); + Logger.error(e.message); + } + + var root = parser.get_root().get_object(); + var array = root.get_array_member("items"); + uint length = array.get_length(); + + var db = DataBase.readOnly(); + for (uint i = 0; i < length; i++) + { + Json.Object object = array.get_object_element(i); + string id = object.get_string_member("id"); + id = id.substring(id.last_index_of_char('/') + 1); + bool marked = false; + bool read = false; + var cats = object.get_array_member("categories"); + uint cat_length = cats.get_length(); + + var tags = new Gee.ArrayList<string>(); + for (uint j = 0; j < cat_length; j++) { - mediaCount = attachments.get_length(); + string cat = cats.get_string_element(j); + if(cat.has_suffix("com.google/starred")) + { + marked = true; + } + else if(cat.has_suffix("com.google/read")) + { + read = true; + } + else if(cat.contains("/label/") && db.getTagName(cat) != null) + { + tags.add(cat); + } } - - for(int j = 0; j < mediaCount; ++j) + + var enclosures = new Gee.ArrayList<Enclosure>(); + if(object.has_member("enclosure")) { - var attachment = attachments.get_object_element(j); - - enclosures.add( - new Enclosure(id, attachment.get_string_member("href"), - EnclosureType.from_string(attachment.get_string_member("type"))) + var attachments = object.get_array_member("enclosure"); + + uint mediaCount = 0; + if(attachments != null) + { + mediaCount = attachments.get_length(); + } + + for(int j = 0; j < mediaCount; ++j) + { + var attachment = attachments.get_object_element(j); + + enclosures.add( + new Enclosure(id, attachment.get_string_member("href"), + EnclosureType.from_string(attachment.get_string_member("type"))) ); + } } - } - - articles.add(new Article( - id, - object.get_string_member("title"), - object.get_array_member("alternate").get_object_element(0).get_string_member("href"), - object.get_object_member("origin").get_string_member("streamId"), - read ? ArticleStatus.READ : ArticleStatus.UNREAD, - marked ? ArticleStatus.MARKED : ArticleStatus.UNMARKED, - object.get_object_member("summary").get_string_member("content"), - "", - object.get_string_member("author"), - new DateTime.from_unix_local(object.get_int_member("published")), - -1, - tags, - enclosures - ) - ); - } - + + articles.add(new Article( + id, + object.get_string_member("title"), + object.get_array_member("alternate").get_object_element(0).get_string_member("href"), + object.get_object_member("origin").get_string_member("streamId"), + read ? ArticleStatus.READ : ArticleStatus.UNREAD, + marked ? ArticleStatus.MARKED : ArticleStatus.UNMARKED, + object.get_object_member("summary").get_string_member("content"), + "", + object.get_string_member("author"), + new DateTime.from_unix_local(object.get_int_member("published")), + -1, + tags, + enclosures + ) + ); + } + if(root.has_member("continuation") && root.get_string_member("continuation") != "") { return root.get_string_member("continuation"); } - + return null; } @@ -439,7 +439,7 @@ public void edidTag(string articleID, string tagID, bool add = true) { var msg = new bazquxMessage(); msg.add("output", "json"); - + if(add) { msg.add("a", tagID); @@ -448,7 +448,7 @@ public void edidTag(string articleID, string tagID, bool add = true) { msg.add("r", tagID); } - + msg.add("i", "tag:google.com,2005:reader/item/" + articleID); m_connection.send_post_request("edit-tag", msg.get()); } @@ -488,40 +488,40 @@ public bool editSubscription(bazquxSubscriptionAction action, string feedID, str { var msg = new bazquxMessage(); msg.add("output", "json"); - + switch(action) { - case bazquxSubscriptionAction.EDIT: + case bazquxSubscriptionAction.EDIT: msg.add("ac", "edit"); break; - case bazquxSubscriptionAction.SUBSCRIBE: + case bazquxSubscriptionAction.SUBSCRIBE: msg.add("ac", "subscribe"); break; - case bazquxSubscriptionAction.UNSUBSCRIBE: + case bazquxSubscriptionAction.UNSUBSCRIBE: msg.add("ac", "unsubscribe"); break; } - + msg.add("s", feedID); - + if(title != null) { msg.add("t", title); } - + if(add != null) { msg.add("a", add); } - + if(remove != null) { msg.add("r", remove); } - - + + var response = m_connection.send_post_request("subscription/edit", msg.get()); - + return response.status == 200; } } diff --git a/plugins/backend/bazqux/bazquxConnection.vala b/plugins/backend/bazqux/bazquxConnection.vala index 90c5d0aa..16c4f385 100644 --- a/plugins/backend/bazqux/bazquxConnection.vala +++ b/plugins/backend/bazqux/bazquxConnection.vala @@ -14,141 +14,141 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.bazquxConnection { -private string m_username; -private string m_api_code; -private string m_passwd; -private bazquxUtils m_utils; -private Soup.Session m_session; - -public bazquxConnection(bazquxUtils utils) -{ - m_utils = utils; - m_username = m_utils.getUser(); - m_api_code = m_utils.getAccessToken(); - m_passwd = m_utils.getPasswd(); - m_session = new Soup.Session(); - m_session.user_agent = Constants.USER_AGENT; -} - -public LoginResponse getToken() -{ - Logger.debug("bazqux Connection: getToken()"); - - if(m_username == "" && m_passwd == "") + private string m_username; + private string m_api_code; + private string m_passwd; + private bazquxUtils m_utils; + private Soup.Session m_session; + + public bazquxConnection(bazquxUtils utils) { - return LoginResponse.ALL_EMPTY; + m_utils = utils; + m_username = m_utils.getUser(); + m_api_code = m_utils.getAccessToken(); + m_passwd = m_utils.getPasswd(); + m_session = new Soup.Session(); + m_session.user_agent = Constants.USER_AGENT; } - if(m_username == "") + + public LoginResponse getToken() { - return LoginResponse.MISSING_USER; - } - if(m_passwd == "") - { - return LoginResponse.MISSING_PASSWD; - } - - var message = new Soup.Message("POST", "https://bazqux.com/accounts/ClientLogin/"); - string message_string = "Email=" + m_username + "&Passwd=" + m_passwd; - message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string.data); - m_session.send_message(message); - string response = (string)message.response_body.flatten().data; - try{ - - var regex = new Regex(".*\\w\\s.*\\w\\sAuth="); - if(regex.match(response)) + Logger.debug("bazqux Connection: getToken()"); + + if(m_username == "" && m_passwd == "") + { + return LoginResponse.ALL_EMPTY; + } + if(m_username == "") { - Logger.error("Regex bazqux - %s".printf(response)); - string split = regex.replace( response, -1,0,""); - Logger.error("authcode"+split); - m_utils.setAccessToken(split.strip()); - return LoginResponse.SUCCESS; + return LoginResponse.MISSING_USER; + } + if(m_passwd == "") + { + return LoginResponse.MISSING_PASSWD; + } + + var message = new Soup.Message("POST", "https://bazqux.com/accounts/ClientLogin/"); + string message_string = "Email=" + m_username + "&Passwd=" + m_passwd; + message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string.data); + m_session.send_message(message); + string response = (string)message.response_body.flatten().data; + try{ + + var regex = new Regex(".*\\w\\s.*\\w\\sAuth="); + if(regex.match(response)) + { + Logger.error("Regex bazqux - %s".printf(response)); + string split = regex.replace( response, -1,0,""); + Logger.error("authcode"+split); + m_utils.setAccessToken(split.strip()); + return LoginResponse.SUCCESS; + } + else + { + Logger.debug(response); + return LoginResponse.WRONG_LOGIN; + } } - else + catch(Error e) { - Logger.debug(response); - return LoginResponse.WRONG_LOGIN; + Logger.error("bazquxConnection - getToken: Could not load message response"); + Logger.error(e.message); + return LoginResponse.UNKNOWN_ERROR; } } - catch(Error e) + + public Response send_get_request(string path, string? message_string = null) { - Logger.error("bazquxConnection - getToken: Could not load message response"); - Logger.error(e.message); - return LoginResponse.UNKNOWN_ERROR; + return send_request(path, "GET", message_string); } -} - -public Response send_get_request(string path, string? message_string = null) -{ - return send_request(path, "GET", message_string); -} - -public Response send_post_request(string path, string? message_string = null) -{ - return send_request(path, "POST", message_string); -} - -private Response send_request(string path, string type, string? message_string = null) -{ - - var message = new Soup.Message(type, bazquxSecret.base_uri + path); - - string oldauth = "GoogleLogin auth=" + m_utils.getAccessToken(); - message.request_headers.append("Authorization", oldauth); - - if(message_string != null) + + public Response send_post_request(string path, string? message_string = null) { - message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string.data); + return send_request(path, "POST", message_string); } - - m_session.send_message(message); - - return Response() { - status = message.status_code, - data = (string)message.response_body.flatten().data - }; -} - -public bool ping() -{ - var message = new Soup.Message("GET", "https://www.bazqux.com/reader/ping"); - - string oldauth = "GoogleLogin auth=" + m_utils.getAccessToken(); - message.request_headers.append("Authorization", oldauth); - m_session.send_message(message); - - if((string)message.response_body.data == "OK") + + private Response send_request(string path, string type, string? message_string = null) { - return true; + + var message = new Soup.Message(type, bazquxSecret.base_uri + path); + + string oldauth = "GoogleLogin auth=" + m_utils.getAccessToken(); + message.request_headers.append("Authorization", oldauth); + + if(message_string != null) + { + message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string.data); + } + + m_session.send_message(message); + + return Response() { + status = message.status_code, + data = (string)message.response_body.flatten().data + }; } - - return false; -} - + + public bool ping() + { + var message = new Soup.Message("GET", "https://www.bazqux.com/reader/ping"); + + string oldauth = "GoogleLogin auth=" + m_utils.getAccessToken(); + message.request_headers.append("Authorization", oldauth); + m_session.send_message(message); + + if((string)message.response_body.data == "OK") + { + return true; + } + + return false; + } + } public class FeedReader.bazquxMessage { - -string request = ""; - -public bazquxMessage() -{ - -} - -public void add(string parameter, string val) -{ - if(request != "") + + string request = ""; + + public bazquxMessage() { - request += "&"; + + } + + public void add(string parameter, string val) + { + if(request != "") + { + request += "&"; + } + + request += parameter; + request += "="; + request += GLib.Uri.escape_string(val); + } + + public string get() + { + return request; } - - request += parameter; - request += "="; - request += GLib.Uri.escape_string(val); -} - -public string get() -{ - return request; -} } diff --git a/plugins/backend/bazqux/bazquxInterface.vala b/plugins/backend/bazqux/bazquxInterface.vala index ad4dcfca..a9d70c47 100644 --- a/plugins/backend/bazqux/bazquxInterface.vala +++ b/plugins/backend/bazqux/bazquxInterface.vala @@ -14,423 +14,423 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.bazquxInterface : FeedServerInterface { - -private bazquxAPI m_api; -private bazquxUtils m_utils; -private Gtk.Entry m_userEntry; -private Gtk.Entry m_passwordEntry; - -public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) -{ - m_utils = new bazquxUtils(settings_backend, secrets); - m_api = new bazquxAPI(m_utils); -} - -public override string getWebsite() -{ - return "https://bazqux.com/"; -} - -public override BackendFlags getFlags() -{ - return (BackendFlags.HOSTED | BackendFlags.PROPRIETARY | BackendFlags.PAID); -} - -public override string getID() -{ - return "bazqux"; -} - -public override string iconName() -{ - return "feed-service-bazqux"; -} - -public override string serviceName() -{ - return "BazQux"; -} - -public override bool needWebLogin() -{ - return false; -} - -public override Gtk.Box? getWidget() -{ - var user_label = new Gtk.Label(_("Username:")); - var password_label = new Gtk.Label(_("Password:")); - - user_label.set_alignment(1.0f, 0.5f); - password_label.set_alignment(1.0f, 0.5f); - - user_label.set_hexpand(true); - password_label.set_hexpand(true); - - m_userEntry = new Gtk.Entry(); - m_passwordEntry = new Gtk.Entry(); - - m_userEntry.activate.connect(() => { tryLogin(); }); - m_passwordEntry.activate.connect(() => { tryLogin(); }); - - m_passwordEntry.set_invisible_char('*'); - m_passwordEntry.set_visibility(false); - - var grid = new Gtk.Grid(); - grid.set_column_spacing(10); - grid.set_row_spacing(10); - grid.set_valign(Gtk.Align.CENTER); - grid.set_halign(Gtk.Align.CENTER); - - grid.attach(user_label, 0, 0, 1, 1); - grid.attach(m_userEntry, 1, 0, 1, 1); - grid.attach(password_label, 0, 1, 1, 1); - grid.attach(m_passwordEntry, 1, 1, 1, 1); - - var logo = new Gtk.Image.from_icon_name("feed-service-bazqux", Gtk.IconSize.MENU); - - var loginLabel = new Gtk.Label(_("Please log in to FeedHQ and enjoy using FeedReader")); - loginLabel.get_style_context().add_class("h2"); - loginLabel.set_justify(Gtk.Justification.CENTER); - loginLabel.set_lines(3); - - var loginButton = new Gtk.Button.with_label(_("Login")); - loginButton.halign = Gtk.Align.END; - loginButton.set_size_request(80, 30); - loginButton.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); - loginButton.clicked.connect(() => { tryLogin(); }); - - var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10); - box.valign = Gtk.Align.CENTER; - box.halign = Gtk.Align.CENTER; - box.pack_start(loginLabel, false, false, 10); - box.pack_start(logo, false, false, 10); - box.pack_start(grid, true, true, 10); - box.pack_end(loginButton, false, false, 20); - - m_userEntry.set_text(m_utils.getUser()); - m_passwordEntry.set_text(m_utils.getPasswd()); - - return box; -} - -public override void writeData() -{ - m_utils.setUser(m_userEntry.get_text()); - m_utils.setPassword(m_passwordEntry.get_text()); -} - -public override bool supportTags() -{ - return true; -} - -public override bool supportFeedManipulation() -{ - return true; -} - -public override bool doInitSync() -{ - return true; -} - -public override string symbolicIcon() -{ - return "feed-service-bazqux-symbolic"; -} - -public override string accountName() -{ - return m_utils.getUser(); -} - -public override string getServerURL() -{ - return "https://bazqux.com/"; -} - -public override string uncategorizedID() -{ - return ""; -} - -public override bool supportCategories() -{ - return true; -} -public override bool hideCategoryWhenEmpty(string cadID) -{ - return false; -} - -public override bool supportMultiLevelCategories() -{ - return false; -} - -public override bool supportMultiCategoriesPerFeed() -{ - return false; -} - -public override bool syncFeedsAndCategories() -{ - return true; -} - -public override bool tagIDaffectedByNameChange() -{ - return true; -} - -public override void resetAccount() -{ - m_utils.resetAccount(); -} - -public override bool useMaxArticles() -{ - return true; -} - -public override LoginResponse login() -{ - return m_api.login(); -} - -public override void setArticleIsRead(string articleIDs, ArticleStatus read) -{ - if(read == ArticleStatus.READ) + + private bazquxAPI m_api; + private bazquxUtils m_utils; + private Gtk.Entry m_userEntry; + private Gtk.Entry m_passwordEntry; + + public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) { - m_api.edidTag(articleIDs, "user/-/state/com.google/read"); + m_utils = new bazquxUtils(settings_backend, secrets); + m_api = new bazquxAPI(m_utils); } - else + + public override string getWebsite() { - m_api.edidTag(articleIDs, "user/-/state/com.google/read", false); + return "https://bazqux.com/"; } -} - -public override void setArticleIsMarked(string articleID, ArticleStatus marked) -{ - if(marked == ArticleStatus.MARKED) + + public override BackendFlags getFlags() { - m_api.edidTag(articleID, "user/-/state/com.google/starred"); + return (BackendFlags.HOSTED | BackendFlags.PROPRIETARY | BackendFlags.PAID); } - else + + public override string getID() { - m_api.edidTag(articleID, "user/-/state/com.google/starred", false); + return "bazqux"; } -} - -public override bool alwaysSetReadByID() -{ - return false; -} - -public override void setFeedRead(string feedID) -{ - m_api.markAsRead(feedID); -} - -public override void setCategoryRead(string catID) -{ - m_api.markAsRead(catID); -} - -public override void markAllItemsRead() -{ - var db = DataBase.readOnly(); - var categories = db.read_categories(); - foreach(Category cat in categories) + + public override string iconName() { - m_api.markAsRead(cat.getCatID()); + return "feed-service-bazqux"; } - - var feeds = db.read_feeds_without_cat(); - foreach(Feed feed in feeds) + + public override string serviceName() { - m_api.markAsRead(feed.getFeedID()); + return "BazQux"; } - m_api.markAsRead(); -} - -public override void tagArticle(string articleID, string tagID) -{ - m_api.edidTag(articleID, tagID, true); -} - -public override void removeArticleTag(string articleID, string tagID) -{ - m_api.edidTag(articleID, tagID, false); -} - -public override string createTag(string caption) -{ - return m_api.composeTagID(caption); -} - -public override void deleteTag(string tagID) -{ - m_api.deleteTag(tagID); -} - -public override void renameTag(string tagID, string title) -{ - m_api.renameTag(tagID, title); -} - -public override bool serverAvailable() -{ - return m_api.ping(); -} - -public override bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) -{ - feedID = "feed/" + feedURL; - bool success = false; - errmsg = ""; - - if(catID == null && newCatName != null) + + public override bool needWebLogin() { - string newCatID = m_api.composeTagID(newCatName); - success = m_api.editSubscription(bazquxAPI.bazquxSubscriptionAction.SUBSCRIBE, "feed/"+feedURL, null, newCatID); + return false; } - else + + public override Gtk.Box? getWidget() { - success = m_api.editSubscription(bazquxAPI.bazquxSubscriptionAction.SUBSCRIBE, "feed/"+feedURL, null, catID); + var user_label = new Gtk.Label(_("Username:")); + var password_label = new Gtk.Label(_("Password:")); + + user_label.set_alignment(1.0f, 0.5f); + password_label.set_alignment(1.0f, 0.5f); + + user_label.set_hexpand(true); + password_label.set_hexpand(true); + + m_userEntry = new Gtk.Entry(); + m_passwordEntry = new Gtk.Entry(); + + m_userEntry.activate.connect(() => { tryLogin(); }); + m_passwordEntry.activate.connect(() => { tryLogin(); }); + + m_passwordEntry.set_invisible_char('*'); + m_passwordEntry.set_visibility(false); + + var grid = new Gtk.Grid(); + grid.set_column_spacing(10); + grid.set_row_spacing(10); + grid.set_valign(Gtk.Align.CENTER); + grid.set_halign(Gtk.Align.CENTER); + + grid.attach(user_label, 0, 0, 1, 1); + grid.attach(m_userEntry, 1, 0, 1, 1); + grid.attach(password_label, 0, 1, 1, 1); + grid.attach(m_passwordEntry, 1, 1, 1, 1); + + var logo = new Gtk.Image.from_icon_name("feed-service-bazqux", Gtk.IconSize.MENU); + + var loginLabel = new Gtk.Label(_("Please log in to FeedHQ and enjoy using FeedReader")); + loginLabel.get_style_context().add_class("h2"); + loginLabel.set_justify(Gtk.Justification.CENTER); + loginLabel.set_lines(3); + + var loginButton = new Gtk.Button.with_label(_("Login")); + loginButton.halign = Gtk.Align.END; + loginButton.set_size_request(80, 30); + loginButton.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); + loginButton.clicked.connect(() => { tryLogin(); }); + + var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10); + box.valign = Gtk.Align.CENTER; + box.halign = Gtk.Align.CENTER; + box.pack_start(loginLabel, false, false, 10); + box.pack_start(logo, false, false, 10); + box.pack_start(grid, true, true, 10); + box.pack_end(loginButton, false, false, 20); + + m_userEntry.set_text(m_utils.getUser()); + m_passwordEntry.set_text(m_utils.getPasswd()); + + return box; } - - if(!success) + + public override void writeData() { - errmsg = @"bazqux could not subscribe to $feedURL"; + m_utils.setUser(m_userEntry.get_text()); + m_utils.setPassword(m_passwordEntry.get_text()); } - - return success; -} - -public override void removeFeed(string feedID) -{ - m_api.editSubscription(bazquxAPI.bazquxSubscriptionAction.UNSUBSCRIBE, feedID); -} - -public override void renameFeed(string feedID, string title) -{ - m_api.editSubscription(bazquxAPI.bazquxSubscriptionAction.EDIT, feedID, title); -} - -public override void moveFeed(string feedID, string newCatID, string? currentCatID) -{ - m_api.editSubscription(bazquxAPI.bazquxSubscriptionAction.EDIT, feedID, null, newCatID, currentCatID); -} - -public override string createCategory(string title, string? parentID) -{ - return m_api.composeTagID(title); -} - -public override void renameCategory(string catID, string title) -{ - m_api.renameTag(catID, title); -} - -public override void moveCategory(string catID, string newParentID) -{ - return; -} - -public override void deleteCategory(string catID) -{ - m_api.deleteTag(catID); -} - -public override void removeCatFromFeed(string feedID, string catID) -{ - return; -} - -public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) -{ - if(m_api.getFeeds(feeds)) + + public override bool supportTags() + { + return true; + } + + public override bool supportFeedManipulation() + { + return true; + } + + public override bool doInitSync() + { + return true; + } + + public override string symbolicIcon() + { + return "feed-service-bazqux-symbolic"; + } + + public override string accountName() + { + return m_utils.getUser(); + } + + public override string getServerURL() + { + return "https://bazqux.com/"; + } + + public override string uncategorizedID() + { + return ""; + } + + public override bool supportCategories() + { + return true; + } + public override bool hideCategoryWhenEmpty(string cadID) + { + return false; + } + + public override bool supportMultiLevelCategories() + { + return false; + } + + public override bool supportMultiCategoriesPerFeed() + { + return false; + } + + public override bool syncFeedsAndCategories() + { + return true; + } + + public override bool tagIDaffectedByNameChange() + { + return true; + } + + public override void resetAccount() + { + m_utils.resetAccount(); + } + + public override bool useMaxArticles() + { + return true; + } + + public override LoginResponse login() + { + return m_api.login(); + } + + public override void setArticleIsRead(string articleIDs, ArticleStatus read) { - if(cancellable != null && cancellable.is_cancelled()) + if(read == ArticleStatus.READ) { - return false; + m_api.edidTag(articleIDs, "user/-/state/com.google/read"); } - - if(m_api.getCategoriesAndTags(feeds, categories, tags)) + else { - return true; + m_api.edidTag(articleIDs, "user/-/state/com.google/read", false); } } - return false; -} - -public override int getUnreadCount() -{ - return m_api.getTotalUnread(); -} - -public override void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) -{ - if(whatToGet == ArticleStatus.READ) + + public override void setArticleIsMarked(string articleID, ArticleStatus marked) + { + if(marked == ArticleStatus.MARKED) + { + m_api.edidTag(articleID, "user/-/state/com.google/starred"); + } + else + { + m_api.edidTag(articleID, "user/-/state/com.google/starred", false); + } + } + + public override bool alwaysSetReadByID() + { + return false; + } + + public override void setFeedRead(string feedID) + { + m_api.markAsRead(feedID); + } + + public override void setCategoryRead(string catID) + { + m_api.markAsRead(catID); + } + + public override void markAllItemsRead() + { + var db = DataBase.readOnly(); + var categories = db.read_categories(); + foreach(Category cat in categories) + { + m_api.markAsRead(cat.getCatID()); + } + + var feeds = db.read_feeds_without_cat(); + foreach(Feed feed in feeds) + { + m_api.markAsRead(feed.getFeedID()); + } + m_api.markAsRead(); + } + + public override void tagArticle(string articleID, string tagID) + { + m_api.edidTag(articleID, tagID, true); + } + + public override void removeArticleTag(string articleID, string tagID) + { + m_api.edidTag(articleID, tagID, false); + } + + public override string createTag(string caption) + { + return m_api.composeTagID(caption); + } + + public override void deleteTag(string tagID) + { + m_api.deleteTag(tagID); + } + + public override void renameTag(string tagID, string title) + { + m_api.renameTag(tagID, title); + } + + public override bool serverAvailable() + { + return m_api.ping(); + } + + public override bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) + { + feedID = "feed/" + feedURL; + bool success = false; + errmsg = ""; + + if(catID == null && newCatName != null) + { + string newCatID = m_api.composeTagID(newCatName); + success = m_api.editSubscription(bazquxAPI.bazquxSubscriptionAction.SUBSCRIBE, "feed/"+feedURL, null, newCatID); + } + else + { + success = m_api.editSubscription(bazquxAPI.bazquxSubscriptionAction.SUBSCRIBE, "feed/"+feedURL, null, catID); + } + + if(!success) + { + errmsg = @"bazqux could not subscribe to $feedURL"; + } + + return success; + } + + public override void removeFeed(string feedID) + { + m_api.editSubscription(bazquxAPI.bazquxSubscriptionAction.UNSUBSCRIBE, feedID); + } + + public override void renameFeed(string feedID, string title) + { + m_api.editSubscription(bazquxAPI.bazquxSubscriptionAction.EDIT, feedID, title); + } + + public override void moveFeed(string feedID, string newCatID, string? currentCatID) + { + m_api.editSubscription(bazquxAPI.bazquxSubscriptionAction.EDIT, feedID, null, newCatID, currentCatID); + } + + public override string createCategory(string title, string? parentID) + { + return m_api.composeTagID(title); + } + + public override void renameCategory(string catID, string title) + { + m_api.renameTag(catID, title); + } + + public override void moveCategory(string catID, string newParentID) { return; } - else if(whatToGet == ArticleStatus.ALL) + + public override void deleteCategory(string catID) { - var unreadIDs = new Gee.LinkedList<string>(); - string? continuation = null; - int left = 4*count; - - while(left > 0) + m_api.deleteTag(catID); + } + + public override void removeCatFromFeed(string feedID, string catID) + { + return; + } + + public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) + { + if(m_api.getFeeds(feeds)) { if(cancellable != null && cancellable.is_cancelled()) { - return; - } - - if(left > 1000) - { - continuation = m_api.updateArticles(unreadIDs, 1000, continuation); - left -= 1000; + return false; } - else + + if(m_api.getCategoriesAndTags(feeds, categories, tags)) { - m_api.updateArticles(unreadIDs, left, continuation); - left = 0; + return true; } } - DataBase.writeAccess().updateArticlesByID(unreadIDs, "unread"); + return false; } - - var articles = new Gee.LinkedList<Article>(); - string? continuation = null; - int left = count; - string? bazqux_feedID = (isTagID) ? null : feedID; - string? bazqux_tagID = (isTagID) ? feedID : null; - - while(left > 0) + + public override int getUnreadCount() { - if(cancellable != null && cancellable.is_cancelled()) + return m_api.getTotalUnread(); + } + + public override void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) + { + if(whatToGet == ArticleStatus.READ) { return; } - - if(left > 1000) + else if(whatToGet == ArticleStatus.ALL) { - continuation = m_api.getArticles(articles, 1000, whatToGet, continuation, bazqux_tagID, bazqux_feedID); - left -= 1000; + var unreadIDs = new Gee.LinkedList<string>(); + string? continuation = null; + int left = 4*count; + + while(left > 0) + { + if(cancellable != null && cancellable.is_cancelled()) + { + return; + } + + if(left > 1000) + { + continuation = m_api.updateArticles(unreadIDs, 1000, continuation); + left -= 1000; + } + else + { + m_api.updateArticles(unreadIDs, left, continuation); + left = 0; + } + } + DataBase.writeAccess().updateArticlesByID(unreadIDs, "unread"); } - else + + var articles = new Gee.LinkedList<Article>(); + string? continuation = null; + int left = count; + string? bazqux_feedID = (isTagID) ? null : feedID; + string? bazqux_tagID = (isTagID) ? feedID : null; + + while(left > 0) { - continuation = m_api.getArticles(articles, left, whatToGet, continuation, bazqux_tagID, bazqux_feedID); - left = 0; + if(cancellable != null && cancellable.is_cancelled()) + { + return; + } + + if(left > 1000) + { + continuation = m_api.getArticles(articles, 1000, whatToGet, continuation, bazqux_tagID, bazqux_feedID); + left -= 1000; + } + else + { + continuation = m_api.getArticles(articles, left, whatToGet, continuation, bazqux_tagID, bazqux_feedID); + left = 0; + } } + writeArticles(articles); } - writeArticles(articles); -} - + } [ModuleInit] diff --git a/plugins/backend/bazqux/bazquxUtils.vala b/plugins/backend/bazqux/bazquxUtils.vala index f33ef0bf..d7ef39bf 100644 --- a/plugins/backend/bazqux/bazquxUtils.vala +++ b/plugins/backend/bazqux/bazquxUtils.vala @@ -14,91 +14,91 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. namespace FeedReader.bazquxSecret { -const string base_uri = "https://www.bazqux.com/reader/api/0/"; + const string base_uri = "https://www.bazqux.com/reader/api/0/"; } public class FeedReader.bazquxUtils : GLib.Object { - -private GLib.Settings m_settings; -private Password m_password; - -public bazquxUtils(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) -{ - if(settings_backend != null) - { - m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.bazqux", settings_backend); - } - else + + private GLib.Settings m_settings; + private Password m_password; + + public bazquxUtils(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) { - m_settings = new GLib.Settings("org.gnome.feedreader.bazqux"); - } - - var password_schema = new Secret.Schema ("org.gnome.feedreader.bazqux", Secret.SchemaFlags.NONE, - "type", Secret.SchemaAttributeType.STRING, - "Username", Secret.SchemaAttributeType.STRING); - m_password = new Password(secrets, password_schema, "Feedserver login", () => { + if(settings_backend != null) + { + m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.bazqux", settings_backend); + } + else + { + m_settings = new GLib.Settings("org.gnome.feedreader.bazqux"); + } + + var password_schema = new Secret.Schema ("org.gnome.feedreader.bazqux", Secret.SchemaFlags.NONE, + "type", Secret.SchemaAttributeType.STRING, + "Username", Secret.SchemaAttributeType.STRING); + m_password = new Password(secrets, password_schema, "Feedserver login", () => { var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); attributes["type"] = "BazQux"; attributes["Username"] = getUser(); return attributes; }); -} - -public string getUser() -{ - return Utils.gsettingReadString(m_settings, "username"); -} - -public void setUser(string user) -{ - Utils.gsettingWriteString(m_settings, "username", user); -} - -public string getAccessToken() -{ - return Utils.gsettingReadString(m_settings, "access-token"); -} - -public void setAccessToken(string token) -{ - Utils.gsettingWriteString(m_settings, "access-token", token); -} - -public string getUserID() -{ - return Utils.gsettingReadString(m_settings, "user-id"); -} - -public void setUserID(string id) -{ - Utils.gsettingWriteString(m_settings, "user-id", id); -} - -public void resetAccount() -{ - Utils.resetSettings(m_settings); - m_password.delete_password(); -} - -public string getPasswd() -{ - return m_password.get_password(); -} - -public void setPassword(string passwd) -{ - m_password.set_password(passwd); -} - -public bool tagIsCat(string tagID, Gee.List<Feed> feeds) -{ - foreach(Feed feed in feeds) + } + + public string getUser() { - if(feed.hasCat(tagID)) + return Utils.gsettingReadString(m_settings, "username"); + } + + public void setUser(string user) + { + Utils.gsettingWriteString(m_settings, "username", user); + } + + public string getAccessToken() + { + return Utils.gsettingReadString(m_settings, "access-token"); + } + + public void setAccessToken(string token) + { + Utils.gsettingWriteString(m_settings, "access-token", token); + } + + public string getUserID() + { + return Utils.gsettingReadString(m_settings, "user-id"); + } + + public void setUserID(string id) + { + Utils.gsettingWriteString(m_settings, "user-id", id); + } + + public void resetAccount() + { + Utils.resetSettings(m_settings); + m_password.delete_password(); + } + + public string getPasswd() + { + return m_password.get_password(); + } + + public void setPassword(string passwd) + { + m_password.set_password(passwd); + } + + public bool tagIsCat(string tagID, Gee.List<Feed> feeds) + { + foreach(Feed feed in feeds) { - return true; + if(feed.hasCat(tagID)) + { + return true; + } } + return false; } - return false; -} } diff --git a/plugins/backend/decsync/decsyncInterface.vala b/plugins/backend/decsync/decsyncInterface.vala index 55821183..e2f0113e 100644 --- a/plugins/backend/decsync/decsyncInterface.vala +++ b/plugins/backend/decsync/decsyncInterface.vala @@ -14,586 +14,586 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.decsyncInterface : FeedServerInterface { - -internal DecsyncUtils m_utils; -private Soup.Session m_session; -internal Decsync<Unit> m_sync; -private string m_loginDir; -private Gtk.Button loginButton; -private Gtk.Spinner waitingSpinner; -private Gtk.Stack loginStack; - -public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) -{ - m_utils = new DecsyncUtils(settings_backend); - m_session = new Soup.Session(); - m_session.user_agent = Constants.USER_AGENT; - m_session.timeout = 5; -} - -private bool initDecsync() -{ - var decsyncDir = m_utils.getDecsyncDir(); - if (decsyncDir == "") + + internal DecsyncUtils m_utils; + private Soup.Session m_session; + internal Decsync<Unit> m_sync; + private string m_loginDir; + private Gtk.Button loginButton; + private Gtk.Spinner waitingSpinner; + private Gtk.Stack loginStack; + + public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) { - return false; + m_utils = new DecsyncUtils(settings_backend); + m_session = new Soup.Session(); + m_session.user_agent = Constants.USER_AGENT; + m_session.timeout = 5; } - var dir = getDecsyncSubdir(decsyncDir, "rss"); - var ownAppId = getAppId("FeedReader"); - var listeners = new Gee.ArrayList<OnEntryUpdateListener>(); - listeners.add(new DecsyncListeners.ReadMarkListener(true, this)); - listeners.add(new DecsyncListeners.ReadMarkListener(false, this)); - listeners.add(new DecsyncListeners.SubscriptionsListener(this)); - listeners.add(new DecsyncListeners.FeedNamesListener(this)); - listeners.add(new DecsyncListeners.CategoriesListener(this)); - listeners.add(new DecsyncListeners.CategoryNamesListener(this)); - listeners.add(new DecsyncListeners.CategoryParentsListener(this)); - m_sync = new Decsync<Unit>(dir, ownAppId, listeners); - m_sync.syncComplete.connect((extra) => { + + private bool initDecsync() + { + var decsyncDir = m_utils.getDecsyncDir(); + if (decsyncDir == "") + { + return false; + } + var dir = getDecsyncSubdir(decsyncDir, "rss"); + var ownAppId = getAppId("FeedReader"); + var listeners = new Gee.ArrayList<OnEntryUpdateListener>(); + listeners.add(new DecsyncListeners.ReadMarkListener(true, this)); + listeners.add(new DecsyncListeners.ReadMarkListener(false, this)); + listeners.add(new DecsyncListeners.SubscriptionsListener(this)); + listeners.add(new DecsyncListeners.FeedNamesListener(this)); + listeners.add(new DecsyncListeners.CategoriesListener(this)); + listeners.add(new DecsyncListeners.CategoryNamesListener(this)); + listeners.add(new DecsyncListeners.CategoryParentsListener(this)); + m_sync = new Decsync<Unit>(dir, ownAppId, listeners); + m_sync.syncComplete.connect((extra) => { FeedReaderBackend.get_default().updateBadge(); refreshFeedListCounter(); newFeedList(); updateArticleList(); }); - m_sync.initMonitor(new Unit()); - return true; -} - -public override string getWebsite() -{ - return "https://github.com/39aldo39/DecSync"; -} - -public override BackendFlags getFlags() -{ - return (BackendFlags.LOCAL | BackendFlags.FREE_SOFTWARE | BackendFlags.FREE); -} - -public override string getID() -{ - return "decsync"; -} - -public override string iconName() -{ - return "feed-service-decsync"; -} - -public override string serviceName() -{ - return "DecSync"; -} - -public override bool needWebLogin() -{ - return false; -} - -public override Gtk.Box? getWidget() -{ - var doneLabel = new Gtk.Label(_("Done")); - var waitingLabel = new Gtk.Label(_("Adding Feeds")); - waitingSpinner = new Gtk.Spinner(); - var waitingBox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 5); - waitingBox.pack_start(waitingSpinner, false, false, 0); - waitingBox.pack_start(waitingLabel, true, false, 0); - loginStack = new Gtk.Stack(); - loginStack.add_named(doneLabel, "label"); - loginStack.add_named(waitingBox, "waiting"); - var dirLabel = new Gtk.Label(_("DecSync directory:")); - dirLabel.set_alignment(1.0f, 0.5f); - dirLabel.set_hexpand(true); - m_loginDir = m_utils.getDecsyncDir(); - var buttonLabel = m_loginDir; - if (buttonLabel == "") - { - buttonLabel = _("Select..."); - } - var dirButton = new Gtk.Button.with_label(buttonLabel); - dirButton.clicked.connect(() => { + m_sync.initMonitor(new Unit()); + return true; + } + + public override string getWebsite() + { + return "https://github.com/39aldo39/DecSync"; + } + + public override BackendFlags getFlags() + { + return (BackendFlags.LOCAL | BackendFlags.FREE_SOFTWARE | BackendFlags.FREE); + } + + public override string getID() + { + return "decsync"; + } + + public override string iconName() + { + return "feed-service-decsync"; + } + + public override string serviceName() + { + return "DecSync"; + } + + public override bool needWebLogin() + { + return false; + } + + public override Gtk.Box? getWidget() + { + var doneLabel = new Gtk.Label(_("Done")); + var waitingLabel = new Gtk.Label(_("Adding Feeds")); + waitingSpinner = new Gtk.Spinner(); + var waitingBox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 5); + waitingBox.pack_start(waitingSpinner, false, false, 0); + waitingBox.pack_start(waitingLabel, true, false, 0); + loginStack = new Gtk.Stack(); + loginStack.add_named(doneLabel, "label"); + loginStack.add_named(waitingBox, "waiting"); + var dirLabel = new Gtk.Label(_("DecSync directory:")); + dirLabel.set_alignment(1.0f, 0.5f); + dirLabel.set_hexpand(true); + m_loginDir = m_utils.getDecsyncDir(); + var buttonLabel = m_loginDir; + if (buttonLabel == "") + { + buttonLabel = _("Select..."); + } + var dirButton = new Gtk.Button.with_label(buttonLabel); + dirButton.clicked.connect(() => { var chooser = new Gtk.FileChooserDialog("Select Directory", - null, - Gtk.FileChooserAction.SELECT_FOLDER, - _("_Cancel"), - Gtk.ResponseType.CANCEL, - _("_Select"), - Gtk.ResponseType.ACCEPT); + null, + Gtk.FileChooserAction.SELECT_FOLDER, + _("_Cancel"), + Gtk.ResponseType.CANCEL, + _("_Select"), + Gtk.ResponseType.ACCEPT); chooser.set_show_hidden(true); chooser.set_current_folder(m_utils.getDecsyncDir()); if (chooser.run() == Gtk.ResponseType.ACCEPT) { - m_loginDir = chooser.get_filename(); - dirButton.set_label(m_loginDir); + m_loginDir = chooser.get_filename(); + dirButton.set_label(m_loginDir); } chooser.close(); }); - - var grid = new Gtk.Grid(); - grid.set_column_spacing(10); - grid.set_row_spacing(10); - grid.set_valign(Gtk.Align.CENTER); - grid.set_halign(Gtk.Align.CENTER); - - grid.attach(dirLabel, 0, 0, 1, 1); - grid.attach(dirButton, 1, 0, 1, 1); - - //--------------------------------------------------------------------- - - var logo = new Gtk.Image.from_icon_name("feed-service-decsync", Gtk.IconSize.MENU); - - var loginLabel = new Gtk.Label(_("Please select your DecSync directory and enjoy using FeedReader")); - loginLabel.get_style_context().add_class("h2"); - loginLabel.set_justify(Gtk.Justification.CENTER); - loginLabel.set_lines(3); - - loginButton = new Gtk.Button(); - loginButton.add(loginStack); - loginButton.halign = Gtk.Align.END; - loginButton.set_size_request(80, 30); - loginButton.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); - loginButton.clicked.connect(() => { tryLogin(); }); - - var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10); - box.valign = Gtk.Align.CENTER; - box.halign = Gtk.Align.CENTER; - box.pack_start(loginLabel, false, false, 10); - box.pack_start(logo, false, false, 10); - box.pack_start(grid, true, true, 10); - box.pack_end(loginButton, false, false, 20); - - return box; -} - -public override void writeData() -{ - m_utils.setDecsyncDir(m_loginDir); -} - -public override async void postLoginAction() -{ - loginButton.set_sensitive(false); - waitingSpinner.start(); - loginButton.get_style_context().remove_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); - loginStack.set_visible_child_name("waiting"); - SourceFunc callback = postLoginAction.callback; - new Thread<void*>(null, () => { + + var grid = new Gtk.Grid(); + grid.set_column_spacing(10); + grid.set_row_spacing(10); + grid.set_valign(Gtk.Align.CENTER); + grid.set_halign(Gtk.Align.CENTER); + + grid.attach(dirLabel, 0, 0, 1, 1); + grid.attach(dirButton, 1, 0, 1, 1); + + //--------------------------------------------------------------------- + + var logo = new Gtk.Image.from_icon_name("feed-service-decsync", Gtk.IconSize.MENU); + + var loginLabel = new Gtk.Label(_("Please select your DecSync directory and enjoy using FeedReader")); + loginLabel.get_style_context().add_class("h2"); + loginLabel.set_justify(Gtk.Justification.CENTER); + loginLabel.set_lines(3); + + loginButton = new Gtk.Button(); + loginButton.add(loginStack); + loginButton.halign = Gtk.Align.END; + loginButton.set_size_request(80, 30); + loginButton.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); + loginButton.clicked.connect(() => { tryLogin(); }); + + var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10); + box.valign = Gtk.Align.CENTER; + box.halign = Gtk.Align.CENTER; + box.pack_start(loginLabel, false, false, 10); + box.pack_start(logo, false, false, 10); + box.pack_start(grid, true, true, 10); + box.pack_end(loginButton, false, false, 20); + + return box; + } + + public override void writeData() + { + m_utils.setDecsyncDir(m_loginDir); + } + + public override async void postLoginAction() + { + loginButton.set_sensitive(false); + waitingSpinner.start(); + loginButton.get_style_context().remove_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); + loginStack.set_visible_child_name("waiting"); + SourceFunc callback = postLoginAction.callback; + new Thread<void*>(null, () => { m_sync.initStoredEntries(); m_sync.executeStoredEntries({"feeds", "subscriptions"}, new Unit()); Idle.add((owned) callback); return null; }); - yield; -} - -public override bool supportTags() -{ - return false; -} - -public override bool doInitSync() -{ - return false; -} - -public override string symbolicIcon() -{ - return "feed-service-decsync-symbolic"; -} - -public override string accountName() -{ - return "DecSync"; -} - -public override string getServerURL() -{ - return "http://localhost/"; -} - -public override string uncategorizedID() -{ - return "0"; -} - -public override bool hideCategoryWhenEmpty(string catID) -{ - return false; -} - -public override bool supportCategories() -{ - return true; -} - -public override bool supportFeedManipulation() -{ - return true; -} - -public override bool supportMultiLevelCategories() -{ - return true; -} - -public override bool supportMultiCategoriesPerFeed() -{ - return false; -} - -public override bool syncFeedsAndCategories() -{ - return false; -} - -public override bool tagIDaffectedByNameChange() -{ - return false; -} - -public override void resetAccount() -{ - return; -} - -public override bool useMaxArticles() -{ - return true; -} - -public override LoginResponse login() -{ - if (initDecsync()) + yield; + } + + public override bool supportTags() { - return LoginResponse.SUCCESS; + return false; } - else + + public override bool doInitSync() { - return LoginResponse.ALL_EMPTY; + return false; } -} - -public override bool serverAvailable() -{ - return Utils.ping("https://duckduckgo.com/"); -} - -public override void setArticleIsRead(string articleIDs, ArticleStatus readStatus) -{ - var read = readStatus == ArticleStatus.READ; - Logger.debug("Mark " + articleIDs + " as " + (read ? "read" : "unread")); - var entries = new Gee.ArrayList<Decsync.EntryWithPath>(); - var db = DataBase.readOnly(); - foreach (var articleID in articleIDs.split(",")) + + public override string symbolicIcon() + { + return "feed-service-decsync-symbolic"; + } + + public override string accountName() + { + return "DecSync"; + } + + public override string getServerURL() + { + return "http://localhost/"; + } + + public override string uncategorizedID() { - Article? article = db.read_article(articleID); + return "0"; + } + + public override bool hideCategoryWhenEmpty(string catID) + { + return false; + } + + public override bool supportCategories() + { + return true; + } + + public override bool supportFeedManipulation() + { + return true; + } + + public override bool supportMultiLevelCategories() + { + return true; + } + + public override bool supportMultiCategoriesPerFeed() + { + return false; + } + + public override bool syncFeedsAndCategories() + { + return false; + } + + public override bool tagIDaffectedByNameChange() + { + return false; + } + + public override void resetAccount() + { + return; + } + + public override bool useMaxArticles() + { + return true; + } + + public override LoginResponse login() + { + if (initDecsync()) + { + return LoginResponse.SUCCESS; + } + else + { + return LoginResponse.ALL_EMPTY; + } + } + + public override bool serverAvailable() + { + return Utils.ping("https://duckduckgo.com/"); + } + + public override void setArticleIsRead(string articleIDs, ArticleStatus readStatus) + { + var read = readStatus == ArticleStatus.READ; + Logger.debug("Mark " + articleIDs + " as " + (read ? "read" : "unread")); + var entries = new Gee.ArrayList<Decsync.EntryWithPath>(); + var db = DataBase.readOnly(); + foreach (var articleID in articleIDs.split(",")) + { + Article? article = db.read_article(articleID); + if (article != null) + { + var path = articleToPath(article, "read"); + var key = stringToNode(article.getArticleID()); + entries.add(new Decsync.EntryWithPath.now(path, key, boolToNode(read))); + } + } + m_sync.setEntries(entries); + } + + public override void setArticleIsMarked(string articleID, ArticleStatus markedStatus) + { + var marked = markedStatus == ArticleStatus.MARKED; + Logger.debug("Mark " + articleID + " as " + (marked ? "marked" : "unmarked")); + Article? article = DataBase.readOnly().read_article(articleID); if (article != null) { - var path = articleToPath(article, "read"); + var path = articleToPath(article, "marked"); var key = stringToNode(article.getArticleID()); - entries.add(new Decsync.EntryWithPath.now(path, key, boolToNode(read))); + m_sync.setEntry(path, key, boolToNode(marked)); } } - m_sync.setEntries(entries); -} - -public override void setArticleIsMarked(string articleID, ArticleStatus markedStatus) -{ - var marked = markedStatus == ArticleStatus.MARKED; - Logger.debug("Mark " + articleID + " as " + (marked ? "marked" : "unmarked")); - Article? article = DataBase.readOnly().read_article(articleID); - if (article != null) + + public override bool alwaysSetReadByID() { - var path = articleToPath(article, "marked"); - var key = stringToNode(article.getArticleID()); - m_sync.setEntry(path, key, boolToNode(marked)); + return true; } -} - -public override bool alwaysSetReadByID() -{ - return true; -} - -public override void setFeedRead(string feedID) -{ - return; -} - -public override void setCategoryRead(string catID) -{ - return; -} - -public override void markAllItemsRead() -{ - return; -} - -public override void tagArticle(string articleID, string tagID) -{ - return; -} - -public override void removeArticleTag(string articleID, string tagID) -{ - return; -} - -public override string createTag(string caption) -{ - return ""; -} - -public override void deleteTag(string tagID) -{ - return; -} - -public override void renameTag(string tagID, string title) -{ - return; -} - -public override bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) -{ - return addFeedWithDecsync(feedURL, catID, newCatName, out feedID, out errmsg); -} - -public bool addFeedWithDecsync(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg, bool updateDecsync = true) -{ - var db = DataBase.writeAccess(); - var catIDs = new Gee.ArrayList<string>(); - if(catID == null && newCatName != null) + + public override void setFeedRead(string feedID) { - string cID = createCategory(newCatName, null); - var cat = new Category(cID, newCatName, 0, 99, CategoryID.MASTER.to_string(), 1); - db.write_categories(ListUtils.single(cat)); - catIDs.add(cID); + return; } - else if(catID != null && newCatName == null) + + public override void setCategoryRead(string catID) { - catIDs.add(catID); + return; } - else + + public override void markAllItemsRead() { - catIDs.add(uncategorizedID()); + return; } - - feedID = feedURL; - - Logger.info(@"addFeed: ID = $feedID"); - Feed? feed = m_utils.downloadFeed(m_session, feedURL, feedID, catIDs, out errmsg); - - if(feed != null) + + public override void tagArticle(string articleID, string tagID) + { + return; + } + + public override void removeArticleTag(string articleID, string tagID) + { + return; + } + + public override string createTag(string caption) + { + return ""; + } + + public override void deleteTag(string tagID) + { + return; + } + + public override void renameTag(string tagID, string title) + { + return; + } + + public override bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) + { + return addFeedWithDecsync(feedURL, catID, newCatName, out feedID, out errmsg); + } + + public bool addFeedWithDecsync(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg, bool updateDecsync = true) { - if(!db.feed_exists(feed.getURL())) + var db = DataBase.writeAccess(); + var catIDs = new Gee.ArrayList<string>(); + if(catID == null && newCatName != null) { - db.write_feeds(ListUtils.single(feed)); - - if (updateDecsync) + string cID = createCategory(newCatName, null); + var cat = new Category(cID, newCatName, 0, 99, CategoryID.MASTER.to_string(), 1); + db.write_categories(ListUtils.single(cat)); + catIDs.add(cID); + } + else if(catID != null && newCatName == null) + { + catIDs.add(catID); + } + else + { + catIDs.add(uncategorizedID()); + } + + feedID = feedURL; + + Logger.info(@"addFeed: ID = $feedID"); + Feed? feed = m_utils.downloadFeed(m_session, feedURL, feedID, catIDs, out errmsg); + + if(feed != null) + { + if(!db.feed_exists(feed.getURL())) { - m_sync.setEntry({"feeds", "subscriptions"}, stringToNode(feedID), boolToNode(true)); - renameFeed(feedID, feed.getTitle()); - moveFeed(feedID, feed.getCatString(), null); + db.write_feeds(ListUtils.single(feed)); + + if (updateDecsync) + { + m_sync.setEntry({"feeds", "subscriptions"}, stringToNode(feedID), boolToNode(true)); + renameFeed(feedID, feed.getTitle()); + moveFeed(feedID, feed.getCatString(), null); + } + + m_sync.executeStoredEntries({"feeds", "names"}, new Unit(), + stringEquals(feedID) + ); + m_sync.executeStoredEntries({"feeds", "categories"}, new Unit(), + stringEquals(feedID) + ); + return true; } - - m_sync.executeStoredEntries({"feeds", "names"}, new Unit(), - stringEquals(feedID) - ); - m_sync.executeStoredEntries({"feeds", "categories"}, new Unit(), - stringEquals(feedID) - ); - return true; } + + return false; } - - return false; -} - -public override void removeFeed(string feedID) -{ - m_sync.setEntry({"feeds", "subscriptions"}, stringToNode(feedID), boolToNode(false)); -} - -public override void renameFeed(string feedID, string title) -{ - m_sync.setEntry({"feeds", "names"}, stringToNode(feedID), stringToNode(title)); -} - -public override void moveFeed(string feedID, string newCatID, string? currentCatID) -{ - string? value = newCatID == uncategorizedID() ? null : newCatID; - m_sync.setEntry({"feeds", "categories"}, stringToNode(feedID), stringToNode(value)); -} - -public override string createCategory(string title, string? parentID) -{ - var db = DataBase.readOnly(); - string? catID = db.getCategoryID(title); - while (catID == null || db.read_category(catID) != null) + + public override void removeFeed(string feedID) { - catID = "catID%05d".printf(Random.int_range(0, 100000)); + m_sync.setEntry({"feeds", "subscriptions"}, stringToNode(feedID), boolToNode(false)); } - renameCategory(catID, title); - moveCategory(catID, parentID ?? CategoryID.MASTER.to_string()); - Logger.info("createCategory: ID = " + catID); - return catID; -} - -public override void renameCategory(string catID, string title) -{ - m_sync.setEntry({"categories", "names"}, stringToNode(catID), stringToNode(title)); -} - -public override void moveCategory(string catID, string newParentID) -{ - string? value = newParentID == CategoryID.MASTER.to_string() ? null : newParentID; - m_sync.setEntry({"categories", "parents"}, stringToNode(catID), stringToNode(value)); -} - -public override void deleteCategory(string catID) -{ - Logger.info("Delete category " + catID); - var feedIDs = DataBase.readOnly().getFeedIDofCategorie(catID); - foreach (var feedID in feedIDs) + + public override void renameFeed(string feedID, string title) + { + m_sync.setEntry({"feeds", "names"}, stringToNode(feedID), stringToNode(title)); + } + + public override void moveFeed(string feedID, string newCatID, string? currentCatID) + { + string? value = newCatID == uncategorizedID() ? null : newCatID; + m_sync.setEntry({"feeds", "categories"}, stringToNode(feedID), stringToNode(value)); + } + + public override string createCategory(string title, string? parentID) + { + var db = DataBase.readOnly(); + string? catID = db.getCategoryID(title); + while (catID == null || db.read_category(catID) != null) + { + catID = "catID%05d".printf(Random.int_range(0, 100000)); + } + renameCategory(catID, title); + moveCategory(catID, parentID ?? CategoryID.MASTER.to_string()); + Logger.info("createCategory: ID = " + catID); + return catID; + } + + public override void renameCategory(string catID, string title) + { + m_sync.setEntry({"categories", "names"}, stringToNode(catID), stringToNode(title)); + } + + public override void moveCategory(string catID, string newParentID) + { + string? value = newParentID == CategoryID.MASTER.to_string() ? null : newParentID; + m_sync.setEntry({"categories", "parents"}, stringToNode(catID), stringToNode(value)); + } + + public override void deleteCategory(string catID) + { + Logger.info("Delete category " + catID); + var feedIDs = DataBase.readOnly().getFeedIDofCategorie(catID); + foreach (var feedID in feedIDs) + { + moveFeed(feedID, uncategorizedID(), catID); + } + } + + public override void removeCatFromFeed(string feedID, string catID) { moveFeed(feedID, uncategorizedID(), catID); } -} - -public override void removeCatFromFeed(string feedID, string catID) -{ - moveFeed(feedID, uncategorizedID(), catID); -} - -public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) -{ - return true; -} - -public override int getUnreadCount() -{ - return 0; -} - -public override void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) -{ - var feeds = DataBase.readOnly().read_feeds(); - var articles = new Gee.ArrayList<Article>(); - GLib.Mutex mutex = GLib.Mutex(); - var now = new GLib.DateTime.now_local(); - int? weeks = ((DropArticles)Settings.general().get_enum("drop-articles-after")).to_weeks(); - var dropDate = weeks == null ? null : now.add_weeks(-(int)weeks); - - try + + public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) + { + return true; + } + + public override int getUnreadCount() { - var threads = new ThreadPool<Feed>.with_owned_data((feed) => { + return 0; + } + + public override void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) + { + var feeds = DataBase.readOnly().read_feeds(); + var articles = new Gee.ArrayList<Article>(); + GLib.Mutex mutex = GLib.Mutex(); + var now = new GLib.DateTime.now_local(); + int? weeks = ((DropArticles)Settings.general().get_enum("drop-articles-after")).to_weeks(); + var dropDate = weeks == null ? null : now.add_weeks(-(int)weeks); + + try + { + var threads = new ThreadPool<Feed>.with_owned_data((feed) => { if(cancellable != null && cancellable.is_cancelled()) { - return; + return; } - + Logger.debug("getArticles for feed: " + feed.getTitle()); string url = feed.getXmlUrl().escape(""); - + if(url == null || url == "" || GLib.Uri.parse_scheme(url) == null) { - Logger.error("no valid URL"); - return; + Logger.error("no valid URL"); + return; } - + var msg = new Soup.Message("GET", url); var session = new Soup.Session(); session.user_agent = Constants.USER_AGENT; session.timeout = 5; session.send_message(msg); string xml = (string)msg.response_body.flatten().data; - + // parse Rss.Parser parser = new Rss.Parser(); try { - parser.load_from_data(xml, xml.length); + parser.load_from_data(xml, xml.length); } catch(GLib.Error e) { - Logger.error("decsyncInterface.getArticles: %s".printf(e.message)); - return; + Logger.error("decsyncInterface.getArticles: %s".printf(e.message)); + return; } var doc = parser.get_document(); - + string? locale = null; if(doc.encoding != null - && doc.encoding != "") + && doc.encoding != "") { - locale = doc.encoding; + locale = doc.encoding; } - + Logger.debug("Got %u articles".printf(doc.get_items().length())); var newArticles = new Gee.ArrayList<Article>(); var db = DataBase.readOnly(); foreach(Rss.Item item in doc.get_items()) { - string? articleID = item.guid; - - if(articleID == null) - { - if(item.link == null) - { - Logger.warning("no valid id and no valid URL as well? what the hell man? I'm giving up"); - continue; + string? articleID = item.guid; + + if(articleID == null) + { + if(item.link == null) + { + Logger.warning("no valid id and no valid URL as well? what the hell man? I'm giving up"); + continue; } - - articleID = item.link; + + articleID = item.link; } - - if (db.read_article(articleID) != null) - { - continue; + + if (db.read_article(articleID) != null) + { + continue; } - - var date = Rfc822.parseDate(item.pub_date); - if (date != null) - { - Logger.info(@"Parsed $(item.pub_date) as $(date.to_string())"); + + var date = Rfc822.parseDate(item.pub_date); + if (date != null) + { + Logger.info(@"Parsed $(item.pub_date) as $(date.to_string())"); } - else - { - if (item.pub_date != null) - { - Logger.warning(@"RFC 822 date parser failed to parse $(item.pub_date). Falling back to DateTime.now()"); + else + { + if (item.pub_date != null) + { + Logger.warning(@"RFC 822 date parser failed to parse $(item.pub_date). Falling back to DateTime.now()"); } - date = new DateTime.now_local(); + date = new DateTime.now_local(); } - - if (dropDate != null && date.compare(dropDate) == -1) - { - continue; + + if (dropDate != null && date.compare(dropDate) == -1) + { + continue; } - - //Logger.info("Got content: " + item.description); - string? content = m_utils.convert(item.description, locale); - //Logger.info("Converted to: " + item.description); - if(content == null) - { - content = _("Nothing to read here."); + + //Logger.info("Got content: " + item.description); + string? content = m_utils.convert(item.description, locale); + //Logger.info("Converted to: " + item.description); + if(content == null) + { + content = _("Nothing to read here."); } - - var enclosures = new Gee.ArrayList<Enclosure>(); - - if(item.enclosure_url != null) - { - // FIXME: check what type of media we actually got - enclosures.add(new Enclosure(articleID, item.enclosure_url, EnclosureType.FILE)); + + var enclosures = new Gee.ArrayList<Enclosure>(); + + if(item.enclosure_url != null) + { + // FIXME: check what type of media we actually got + enclosures.add(new Enclosure(articleID, item.enclosure_url, EnclosureType.FILE)); } - - string articleURL = item.link; - if(articleURL.has_prefix("/")) - { - articleURL = feed.getURL() + articleURL.substring(1); + + string articleURL = item.link; + if(articleURL.has_prefix("/")) + { + articleURL = feed.getURL() + articleURL.substring(1); } - - var article = new Article( + + var article = new Article( articleID, (item.title != null) ? m_utils.convert(item.title, locale) : null, articleURL, @@ -607,90 +607,90 @@ public override void getArticles(int count, ArticleStatus whatToGet, DateTime? s 0, null, enclosures - ); - - Logger.debug("Got new article: " + article.getTitle()); - - newArticles.add(article); + ); + + Logger.debug("Got new article: " + article.getTitle()); + + newArticles.add(article); } mutex.lock(); articles.add_all(newArticles); mutex.unlock(); }, (int)GLib.get_num_processors(), true); - - foreach(Feed feed in feeds) - { - try + + foreach(Feed feed in feeds) { - threads.add(feed); - } - catch(GLib.Error e) - { - Logger.error("Error creating thread to download Feed %s: %s".printf(feed.getTitle(), e.message)); + try + { + threads.add(feed); + } + catch(GLib.Error e) + { + Logger.error("Error creating thread to download Feed %s: %s".printf(feed.getTitle(), e.message)); + } } + + bool immediate = false; // allow to queue up additional tasks + bool wait = true; // function will block until all tasks are done + ThreadPool.free((owned)threads, immediate, wait); } - - bool immediate = false; // allow to queue up additional tasks - bool wait = true; // function will block until all tasks are done - ThreadPool.free((owned)threads, immediate, wait); - } - catch(Error e) - { - Logger.error("Error creating threads to download Feeds: " + e.message); - } - - articles.sort((a, b) => { + catch(Error e) + { + Logger.error("Error creating threads to download Feeds: " + e.message); + } + + articles.sort((a, b) => { return strcmp(a.getArticleID(), b.getArticleID()); }); - - if(articles.size > 0) - { - DataBase.writeAccess().write_articles(articles); - Logger.debug("decsyncInterface: %i articles written".printf(articles.size)); - - var multiMap = groupBy<Article, Gee.List<string>, Article>( - articles, - article => { return articleToBasePath(article); } + + if(articles.size > 0) + { + DataBase.writeAccess().write_articles(articles); + Logger.debug("decsyncInterface: %i articles written".printf(articles.size)); + + var multiMap = groupBy<Article, Gee.List<string>, Article>( + articles, + article => { return articleToBasePath(article); } ); - multiMap.get_keys().@foreach(basePath => { + multiMap.get_keys().@foreach(basePath => { var articleIDs = multiMap.@get(basePath).map<Json.Node>(article => { return stringToNode(article.getArticleID()); }); foreach (var type in toList({"read","marked"})) { - m_sync.executeStoredEntries(basePathToPath(basePath, type), new Unit(), - key => { return articleIDs.any_match(articleID => { return articleID.equal(key); }); } - ); + m_sync.executeStoredEntries(basePathToPath(basePath, type), new Unit(), + key => { return articleIDs.any_match(articleID => { return articleID.equal(key); }); } + ); } return true; }); + } + + m_sync.executeAllNewEntries(new Unit()); + } + + private string[] articleToPath(Article article, string type) + { + return basePathToPath(articleToBasePath(article), type); + } + + private string[] basePathToPath(Gee.List<string> basePath, string type) + { + var path = new Gee.ArrayList<string>(); + path.add("articles"); + path.add(type); + path.add_all(basePath); + return path.to_array(); + } + + private Gee.List<string> articleToBasePath(Article article) + { + var datetime = article.getDate().to_utc(); + var year = datetime.format("%Y"); + var month = datetime.format("%m"); + var day = datetime.format("%d"); + return toList({year, month, day}); } - - m_sync.executeAllNewEntries(new Unit()); -} - -private string[] articleToPath(Article article, string type) -{ - return basePathToPath(articleToBasePath(article), type); -} - -private string[] basePathToPath(Gee.List<string> basePath, string type) -{ - var path = new Gee.ArrayList<string>(); - path.add("articles"); - path.add(type); - path.add_all(basePath); - return path.to_array(); -} - -private Gee.List<string> articleToBasePath(Article article) -{ - var datetime = article.getDate().to_utc(); - var year = datetime.format("%Y"); - var month = datetime.format("%m"); - var day = datetime.format("%d"); - return toList({year, month, day}); -} } [ModuleInit] diff --git a/plugins/backend/decsync/decsyncListeners.vala b/plugins/backend/decsync/decsyncListeners.vala index d223aab6..8d6b6cf7 100644 --- a/plugins/backend/decsync/decsyncListeners.vala +++ b/plugins/backend/decsync/decsyncListeners.vala @@ -14,275 +14,275 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.DecsyncListeners : GLib.Object { - -public class ReadMarkListener : OnSubdirEntryUpdateListener<Unit> { - -private Gee.List<string> m_subdir; -private bool m_is_read_entry; -private decsyncInterface m_plugin; - -public ReadMarkListener(bool is_read_entry, decsyncInterface plugin) -{ - this.m_subdir = toList({"articles", is_read_entry ? "read" : "marked"}); - this.m_is_read_entry = is_read_entry; - this.m_plugin = plugin; -} - -public override Gee.List<string> subdir() -{ - return m_subdir; -} - -public override void onSubdirEntryUpdate(Gee.List<string> path, Decsync.Entry entry, Unit extra) -{ - var articleID = entry.key.get_string(); - if (articleID == null) - { - Logger.warning("Invalid articleID " + Json.to_string(entry.key, false)); - return; - } - var added = entry.value.get_boolean(); - if (m_is_read_entry) - { - Logger.debug((added ? "read " : "unread ") + articleID); - } - else - { - Logger.debug((added ? "mark " : "unmark ") + articleID); - } - var db = DataBase.writeAccess(); - Article? article = db.read_article(articleID); - if (article == null) - { - Logger.info("Unkown article " + articleID); - return; - } - if (m_is_read_entry) - { - article.setUnread(added ? ArticleStatus.READ : ArticleStatus.UNREAD); - } - else - { - article.setMarked(added ? ArticleStatus.MARKED : ArticleStatus.UNMARKED); - } - db.update_article(article); -} -} - -public class SubscriptionsListener : OnSubfileEntryUpdateListener<Unit> { - -private Gee.List<string> m_subfile; -private decsyncInterface m_plugin; - -public SubscriptionsListener(decsyncInterface plugin) -{ - this.m_subfile = toList({"feeds", "subscriptions"}); - this.m_plugin = plugin; -} - -public override Gee.List<string> subfile() -{ - return m_subfile; -} - -public override void onSubfileEntryUpdate(Decsync.Entry entry, Unit extra) -{ - var feedID = entry.key.get_string(); - if (feedID == null) - { - Logger.warning("Invalid feedID " + Json.to_string(entry.key, false)); - return; - } - var subscribed = entry.value.get_boolean(); - if (subscribed) - { - string outFeedID, errmsg; - m_plugin.addFeedWithDecsync(feedID, null, null, out outFeedID, out errmsg, false); - } - else - { - DataBase.writeAccess().delete_feed(feedID); - } -} -} - -public class FeedNamesListener : OnSubfileEntryUpdateListener<Unit> { - -private Gee.List<string> m_subfile; -private decsyncInterface m_plugin; - -public FeedNamesListener(decsyncInterface plugin) -{ - this.m_subfile = toList({"feeds", "names"}); - this.m_plugin = plugin; -} - -public override Gee.List<string> subfile() -{ - return m_subfile; -} - -public override void onSubfileEntryUpdate(Decsync.Entry entry, Unit extra) -{ - var feedID = entry.key.get_string(); - if (feedID == null) - { - Logger.warning("Invalid feedID " + Json.to_string(entry.key, false)); - return; - } - var name = entry.value.get_string(); - if (name == null) - { - Logger.warning("Invalid name " + Json.to_string(entry.value, false)); - return; - } - DataBase.writeAccess().rename_feed(feedID, name); -} -} - -public class CategoriesListener : OnSubfileEntryUpdateListener<Unit> { - -private Gee.List<string> m_subfile; -private decsyncInterface m_plugin; - -public CategoriesListener(decsyncInterface plugin) -{ - this.m_subfile = toList({"feeds", "categories"}); - this.m_plugin = plugin; -} - -public override Gee.List<string> subfile() -{ - return m_subfile; -} - -public override void onSubfileEntryUpdate(Decsync.Entry entry, Unit extra) -{ - var feedID = entry.key.get_string(); - if (feedID == null) - { - Logger.warning("Invalid feedID " + Json.to_string(entry.key, false)); - return; + + public class ReadMarkListener : OnSubdirEntryUpdateListener<Unit> { + + private Gee.List<string> m_subdir; + private bool m_is_read_entry; + private decsyncInterface m_plugin; + + public ReadMarkListener(bool is_read_entry, decsyncInterface plugin) + { + this.m_subdir = toList({"articles", is_read_entry ? "read" : "marked"}); + this.m_is_read_entry = is_read_entry; + this.m_plugin = plugin; + } + + public override Gee.List<string> subdir() + { + return m_subdir; + } + + public override void onSubdirEntryUpdate(Gee.List<string> path, Decsync.Entry entry, Unit extra) + { + var articleID = entry.key.get_string(); + if (articleID == null) + { + Logger.warning("Invalid articleID " + Json.to_string(entry.key, false)); + return; + } + var added = entry.value.get_boolean(); + if (m_is_read_entry) + { + Logger.debug((added ? "read " : "unread ") + articleID); + } + else + { + Logger.debug((added ? "mark " : "unmark ") + articleID); + } + var db = DataBase.writeAccess(); + Article? article = db.read_article(articleID); + if (article == null) + { + Logger.info("Unkown article " + articleID); + return; + } + if (m_is_read_entry) + { + article.setUnread(added ? ArticleStatus.READ : ArticleStatus.UNREAD); + } + else + { + article.setMarked(added ? ArticleStatus.MARKED : ArticleStatus.UNMARKED); + } + db.update_article(article); + } } - var db = DataBase.writeAccess(); - var feed = db.read_feed(feedID); - if (feed == null) - { - return; + + public class SubscriptionsListener : OnSubfileEntryUpdateListener<Unit> { + + private Gee.List<string> m_subfile; + private decsyncInterface m_plugin; + + public SubscriptionsListener(decsyncInterface plugin) + { + this.m_subfile = toList({"feeds", "subscriptions"}); + this.m_plugin = plugin; + } + + public override Gee.List<string> subfile() + { + return m_subfile; + } + + public override void onSubfileEntryUpdate(Decsync.Entry entry, Unit extra) + { + var feedID = entry.key.get_string(); + if (feedID == null) + { + Logger.warning("Invalid feedID " + Json.to_string(entry.key, false)); + return; + } + var subscribed = entry.value.get_boolean(); + if (subscribed) + { + string outFeedID, errmsg; + m_plugin.addFeedWithDecsync(feedID, null, null, out outFeedID, out errmsg, false); + } + else + { + DataBase.writeAccess().delete_feed(feedID); + } + } } - var currentCatID = feed.getCatString(); - string newCatID; - if (entry.value.is_null()) - { - newCatID = m_plugin.uncategorizedID(); + + public class FeedNamesListener : OnSubfileEntryUpdateListener<Unit> { + + private Gee.List<string> m_subfile; + private decsyncInterface m_plugin; + + public FeedNamesListener(decsyncInterface plugin) + { + this.m_subfile = toList({"feeds", "names"}); + this.m_plugin = plugin; + } + + public override Gee.List<string> subfile() + { + return m_subfile; + } + + public override void onSubfileEntryUpdate(Decsync.Entry entry, Unit extra) + { + var feedID = entry.key.get_string(); + if (feedID == null) + { + Logger.warning("Invalid feedID " + Json.to_string(entry.key, false)); + return; + } + var name = entry.value.get_string(); + if (name == null) + { + Logger.warning("Invalid name " + Json.to_string(entry.value, false)); + return; + } + DataBase.writeAccess().rename_feed(feedID, name); + } } - else - { - newCatID = entry.value.get_string(); + + public class CategoriesListener : OnSubfileEntryUpdateListener<Unit> { + + private Gee.List<string> m_subfile; + private decsyncInterface m_plugin; + + public CategoriesListener(decsyncInterface plugin) + { + this.m_subfile = toList({"feeds", "categories"}); + this.m_plugin = plugin; + } + + public override Gee.List<string> subfile() + { + return m_subfile; + } + + public override void onSubfileEntryUpdate(Decsync.Entry entry, Unit extra) + { + var feedID = entry.key.get_string(); + if (feedID == null) + { + Logger.warning("Invalid feedID " + Json.to_string(entry.key, false)); + return; + } + var db = DataBase.writeAccess(); + var feed = db.read_feed(feedID); + if (feed == null) + { + return; + } + var currentCatID = feed.getCatString(); + string newCatID; + if (entry.value.is_null()) + { + newCatID = m_plugin.uncategorizedID(); + } + else + { + newCatID = entry.value.get_string(); + } + if (newCatID == null) + { + Logger.warning("Invalid catID " + Json.to_string(entry.value, false)); + return; + } + addCategory(m_plugin, newCatID); + db.move_feed(feedID, currentCatID, newCatID); + } } - if (newCatID == null) - { - Logger.warning("Invalid catID " + Json.to_string(entry.value, false)); - return; + + public class CategoryNamesListener : OnSubfileEntryUpdateListener<Unit> { + + private Gee.List<string> m_subfile; + private decsyncInterface m_plugin; + + public CategoryNamesListener(decsyncInterface plugin) + { + this.m_subfile = toList({"categories", "names"}); + this.m_plugin = plugin; + } + + public override Gee.List<string> subfile() + { + return m_subfile; + } + + public override void onSubfileEntryUpdate(Decsync.Entry entry, Unit extra) + { + var catID = entry.key.get_string(); + if (catID == null) + { + Logger.warning("Invalid catID " + Json.to_string(entry.key, false)); + return; + } + var name = entry.value.get_string(); + if (name == null) + { + Logger.warning("Invalid name " + Json.to_string(entry.value, false)); + return; + } + DataBase.writeAccess().rename_category(catID, name); + Logger.debug("Renamed category " + catID + " to " + name); + } } - addCategory(m_plugin, newCatID); - db.move_feed(feedID, currentCatID, newCatID); -} -} - -public class CategoryNamesListener : OnSubfileEntryUpdateListener<Unit> { - -private Gee.List<string> m_subfile; -private decsyncInterface m_plugin; - -public CategoryNamesListener(decsyncInterface plugin) -{ - this.m_subfile = toList({"categories", "names"}); - this.m_plugin = plugin; -} - -public override Gee.List<string> subfile() -{ - return m_subfile; -} - -public override void onSubfileEntryUpdate(Decsync.Entry entry, Unit extra) -{ - var catID = entry.key.get_string(); - if (catID == null) - { - Logger.warning("Invalid catID " + Json.to_string(entry.key, false)); - return; + + public class CategoryParentsListener : OnSubfileEntryUpdateListener<Unit> { + + private Gee.List<string> m_subfile; + private decsyncInterface m_plugin; + + public CategoryParentsListener(decsyncInterface plugin) + { + this.m_subfile = toList({"categories", "parents"}); + this.m_plugin = plugin; + } + + public override Gee.List<string> subfile() + { + return m_subfile; + } + + public override void onSubfileEntryUpdate(Decsync.Entry entry, Unit extra) + { + var catID = entry.key.get_string(); + if (catID == null) + { + Logger.warning("Invalid catID " + Json.to_string(entry.key, false)); + return; + } + string parentID; + if (entry.value.is_null()) + { + parentID = CategoryID.MASTER.to_string(); + } + else + { + parentID = entry.value.get_string(); + } + if (parentID == null) + { + Logger.warning("Invalid parentID " + Json.to_string(entry.value, false)); + return; + } + addCategory(m_plugin, parentID); + DataBase.writeAccess().move_category(catID, parentID); + Logger.debug("Moved category " + catID + " to " + parentID); + } } - var name = entry.value.get_string(); - if (name == null) + + private static void addCategory(decsyncInterface plugin, string catID) { - Logger.warning("Invalid name " + Json.to_string(entry.value, false)); - return; + if (catID == plugin.uncategorizedID() || catID == CategoryID.MASTER.to_string() || DataBase.readOnly().read_category(catID) != null) + { + return; + } + var cat = new Category(catID, catID, 0, 99, CategoryID.MASTER.to_string(), 1); + DataBase.writeAccess().write_categories(ListUtils.single(cat)); + plugin.m_sync.executeStoredEntries({"categories", "names"}, new Unit(), + stringEquals(catID) + ); + plugin.m_sync.executeStoredEntries({"categories", "parents"}, new Unit(), + stringEquals(catID) + ); + Logger.debug("Added category " + catID); } - DataBase.writeAccess().rename_category(catID, name); - Logger.debug("Renamed category " + catID + " to " + name); -} -} - -public class CategoryParentsListener : OnSubfileEntryUpdateListener<Unit> { - -private Gee.List<string> m_subfile; -private decsyncInterface m_plugin; - -public CategoryParentsListener(decsyncInterface plugin) -{ - this.m_subfile = toList({"categories", "parents"}); - this.m_plugin = plugin; -} - -public override Gee.List<string> subfile() -{ - return m_subfile; -} - -public override void onSubfileEntryUpdate(Decsync.Entry entry, Unit extra) -{ - var catID = entry.key.get_string(); - if (catID == null) - { - Logger.warning("Invalid catID " + Json.to_string(entry.key, false)); - return; - } - string parentID; - if (entry.value.is_null()) - { - parentID = CategoryID.MASTER.to_string(); - } - else - { - parentID = entry.value.get_string(); - } - if (parentID == null) - { - Logger.warning("Invalid parentID " + Json.to_string(entry.value, false)); - return; - } - addCategory(m_plugin, parentID); - DataBase.writeAccess().move_category(catID, parentID); - Logger.debug("Moved category " + catID + " to " + parentID); -} -} - -private static void addCategory(decsyncInterface plugin, string catID) -{ - if (catID == plugin.uncategorizedID() || catID == CategoryID.MASTER.to_string() || DataBase.readOnly().read_category(catID) != null) - { - return; - } - var cat = new Category(catID, catID, 0, 99, CategoryID.MASTER.to_string(), 1); - DataBase.writeAccess().write_categories(ListUtils.single(cat)); - plugin.m_sync.executeStoredEntries({"categories", "names"}, new Unit(), - stringEquals(catID) - ); - plugin.m_sync.executeStoredEntries({"categories", "parents"}, new Unit(), - stringEquals(catID) - ); - Logger.debug("Added category " + catID); -} } diff --git a/plugins/backend/decsync/decsyncUtils.vala b/plugins/backend/decsync/decsyncUtils.vala index c0b9061f..5ad2557d 100644 --- a/plugins/backend/decsync/decsyncUtils.vala +++ b/plugins/backend/decsync/decsyncUtils.vala @@ -14,127 +14,127 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.DecsyncUtils : GLib.Object { - -GLib.Settings m_settings; - -public DecsyncUtils(GLib.SettingsBackend? settings_backend) -{ - if(settings_backend != null) - { - m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.decsync", settings_backend); - } - else - { - m_settings = new GLib.Settings("org.gnome.feedreader.decsync"); - } -} - -public string getDecsyncDir() -{ - var dir = Utils.gsettingReadString(m_settings, "decsync-dir"); - if (dir == "") - { - return GLib.Environment.get_variable("DECSYNC_DIR") ?? getDefaultDecsyncBaseDir(); - } - else - { - return dir; - } -} - -public void setDecsyncDir(string decsyncDir) -{ - Utils.gsettingWriteString(m_settings, "decsync-dir", decsyncDir); -} - -public Feed? downloadFeed(Soup.Session session, string feed_url, string feedID, Gee.List<string> catIDs, out string errmsg) -{ - var error = new StringBuilder(_("Failed to add feed")); - error.append_printf(" %s\n", feed_url); - - var msg = new Soup.Message("GET", feed_url); - if (msg == null) - { - error.append(_("Failed to parse URL.")); - errmsg = error.str; - Logger.warning(errmsg); - return null; - } - - uint status = session.send_message(msg); - if(status < 100 || status >= 400) + + GLib.Settings m_settings; + + public DecsyncUtils(GLib.SettingsBackend? settings_backend) { - if(status < 100) + if(settings_backend != null) { - error.append(_("Network error connecting to the server.")); + m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.decsync", settings_backend); } else { - error.append(_("Got HTTP error code")); - error.append_printf(" %u %s", status, Soup.Status.get_phrase(status)); + m_settings = new GLib.Settings("org.gnome.feedreader.decsync"); } - - errmsg = error.str; - Logger.warning(errmsg); - return null; } - string xml = (string)msg.response_body.flatten().data; - string? url = null; - - // parse - Rss.Parser parser = new Rss.Parser(); - try + + public string getDecsyncDir() { - parser.load_from_data(xml, xml.length); + var dir = Utils.gsettingReadString(m_settings, "decsync-dir"); + if (dir == "") + { + return GLib.Environment.get_variable("DECSYNC_DIR") ?? getDefaultDecsyncBaseDir(); + } + else + { + return dir; + } } - catch(Error e) + + public void setDecsyncDir(string decsyncDir) { - error.append(_("Could not parse feed as RSS or ATOM.")); - errmsg = error.str; - Logger.warning(errmsg); - return null; + Utils.gsettingWriteString(m_settings, "decsync-dir", decsyncDir); } - - var doc = parser.get_document(); - - if(doc.link != null - && doc.link != "") + + public Feed? downloadFeed(Soup.Session session, string feed_url, string feedID, Gee.List<string> catIDs, out string errmsg) { - url = doc.link; - } - - errmsg = ""; - return new Feed( - feedID, - doc.title, - url, - 0, - catIDs, - doc.image_url, + var error = new StringBuilder(_("Failed to add feed")); + error.append_printf(" %s\n", feed_url); + + var msg = new Soup.Message("GET", feed_url); + if (msg == null) + { + error.append(_("Failed to parse URL.")); + errmsg = error.str; + Logger.warning(errmsg); + return null; + } + + uint status = session.send_message(msg); + if(status < 100 || status >= 400) + { + if(status < 100) + { + error.append(_("Network error connecting to the server.")); + } + else + { + error.append(_("Got HTTP error code")); + error.append_printf(" %u %s", status, Soup.Status.get_phrase(status)); + } + + errmsg = error.str; + Logger.warning(errmsg); + return null; + } + string xml = (string)msg.response_body.flatten().data; + string? url = null; + + // parse + Rss.Parser parser = new Rss.Parser(); + try + { + parser.load_from_data(xml, xml.length); + } + catch(Error e) + { + error.append(_("Could not parse feed as RSS or ATOM.")); + errmsg = error.str; + Logger.warning(errmsg); + return null; + } + + var doc = parser.get_document(); + + if(doc.link != null + && doc.link != "") + { + url = doc.link; + } + + errmsg = ""; + return new Feed( + feedID, + doc.title, + url, + 0, + catIDs, + doc.image_url, feed_url); -} - -public string? convert(string? text, string? locale) -{ - if(text == null) - { - return null; - } - - if(locale == null) - { - return text; - } - - try - { - return GLib.convert(text, -1, "utf-8", locale); } - catch(ConvertError e) + + public string? convert(string? text, string? locale) { - Logger.error(e.message); + if(text == null) + { + return null; + } + + if(locale == null) + { + return text; + } + + try + { + return GLib.convert(text, -1, "utf-8", locale); + } + catch(ConvertError e) + { + Logger.error(e.message); + } + + return ""; } - - return ""; -} } diff --git a/plugins/backend/decsync/libdecsync/src/Decsync.vala b/plugins/backend/decsync/libdecsync/src/Decsync.vala index d18c3b1e..ba4ca131 100644 --- a/plugins/backend/decsync/libdecsync/src/Decsync.vala +++ b/plugins/backend/decsync/libdecsync/src/Decsync.vala @@ -1,71 +1,71 @@ /** - * libdecsync-vala - Decsync.vala - * - * Copyright (C) 2018 Aldo Gunsing - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see <http://www.gnu.org/licenses/>. - */ +* libdecsync-vala - Decsync.vala +* +* Copyright (C) 2018 Aldo Gunsing +* +* This library is free software; you can redistribute it and/or modify it +* under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation. +* +* This library is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ public class Unit { public Unit() { - } +} } /** - * The `DecSync` class represents an interface to synchronized key-value mappings stored on the file - * system. - * - * The mappings can be synchronized by synchronizing the directory [dir]. The stored mappings are - * stored in a conflict-free way. When the same keys are updated independently, the most recent - * value is taken. This should not cause problems when the individual values contain as little - * information as possible. - * - * Every entry consists of a path, a key and a value. The path is a list of strings which contains - * the location to the used mapping. This can make interacting with the data easier. It is also used - * to construct a path in the file system. All characters are allowed in the path. However, other - * limitations of the file system may apply. For example, there may be a maximum length or the file - * system may be case insensitive. - * - * To update an entry, use the method [setEntry]. When multiple keys in the same path are updated - * simultaneous, it is encouraged to use the more efficient methods [setEntriesForPath] and - * [setEntries]. - * - * To get notified about updated entries, use the method [executeAllNewEntries] to get all updated - * entries and execute the corresponding actions. The method [initObserver] creates a file observer - * which is notified about the updated entries immediately. - * - * Sometimes, updates cannot be execute immediately. For example, if the name of a category is - * updated when the category does not exist yet, the name cannot be changed. In such cases, the - * updates have to be executed retroactively. In the example, the update can be executed when the - * category is created. For such cases, use the method [executeStoredEntries]. - * - * Finally, to initialize the stored entries to the most recent values, use the method - * [initStoredEntries]. This method is almost exclusively used when the application is installed. It - * is almost always followed by a call to [executeStoredEntries]. - * - * @param T the type of the extra data passed to the [listeners] and [syncComplete]. - * @property dir the directory in which the synchronized DecSync files are stored. - * For the default location, use [getDecsyncSubdir]. - * @property ownAppId the unique appId corresponding to the stored data by the application. There - * must not be two simultaneous instances with the same appId. However, if an application is - * reinstalled, it may reuse its old appId. In that case, it has to call [initStoredEntries] and - * [executeStoredEntries]. Even if the old appId is not reused, it is still recommended call these. - * For the default appId, use [getAppId]. - * @property listeners a list of listeners describing the actions to execute on every updated entry. - * When an entry is updated, the method [OnEntryUpdateListener.onEntriesUpdate] is called on the - * listener whose method [OnEntryUpdateListener.matchesPath] returns true. - * @property syncComplete an optional function which is called when a sync is complete. For example, - * it can be used to update the UI. - */ +* The `DecSync` class represents an interface to synchronized key-value mappings stored on the file +* system. +* +* The mappings can be synchronized by synchronizing the directory [dir]. The stored mappings are +* stored in a conflict-free way. When the same keys are updated independently, the most recent +* value is taken. This should not cause problems when the individual values contain as little +* information as possible. +* +* Every entry consists of a path, a key and a value. The path is a list of strings which contains +* the location to the used mapping. This can make interacting with the data easier. It is also used +* to construct a path in the file system. All characters are allowed in the path. However, other +* limitations of the file system may apply. For example, there may be a maximum length or the file +* system may be case insensitive. +* +* To update an entry, use the method [setEntry]. When multiple keys in the same path are updated +* simultaneous, it is encouraged to use the more efficient methods [setEntriesForPath] and +* [setEntries]. +* +* To get notified about updated entries, use the method [executeAllNewEntries] to get all updated +* entries and execute the corresponding actions. The method [initObserver] creates a file observer +* which is notified about the updated entries immediately. +* +* Sometimes, updates cannot be execute immediately. For example, if the name of a category is +* updated when the category does not exist yet, the name cannot be changed. In such cases, the +* updates have to be executed retroactively. In the example, the update can be executed when the +* category is created. For such cases, use the method [executeStoredEntries]. +* +* Finally, to initialize the stored entries to the most recent values, use the method +* [initStoredEntries]. This method is almost exclusively used when the application is installed. It +* is almost always followed by a call to [executeStoredEntries]. +* +* @param T the type of the extra data passed to the [listeners] and [syncComplete]. +* @property dir the directory in which the synchronized DecSync files are stored. +* For the default location, use [getDecsyncSubdir]. +* @property ownAppId the unique appId corresponding to the stored data by the application. There +* must not be two simultaneous instances with the same appId. However, if an application is +* reinstalled, it may reuse its old appId. In that case, it has to call [initStoredEntries] and +* [executeStoredEntries]. Even if the old appId is not reused, it is still recommended call these. +* For the default appId, use [getAppId]. +* @property listeners a list of listeners describing the actions to execute on every updated entry. +* When an entry is updated, the method [OnEntryUpdateListener.onEntriesUpdate] is called on the +* listener whose method [OnEntryUpdateListener.matchesPath] returns true. +* @property syncComplete an optional function which is called when a sync is complete. For example, +* it can be used to update the UI. +*/ public class Decsync<T> : GLib.Object { string dir; @@ -75,8 +75,8 @@ Gee.Iterable<OnEntryUpdateListener<T>> listeners; DirectoryMonitor? monitor = null; /** - * Signal which is called when a sync is complete. For example, it can be used to update the UI. - */ +* Signal which is called when a sync is complete. For example, it can be used to update the UI. +*/ public signal void syncComplete(T extra); public Decsync(string dir, string ownAppId, Gee.Iterable<OnEntryUpdateListener<T>> listeners) @@ -88,115 +88,115 @@ public Decsync(string dir, string ownAppId, Gee.Iterable<OnEntryUpdateListener<T } /** - * Represents an [Entry] with its path. - */ +* Represents an [Entry] with its path. +*/ public class EntryWithPath { -public Gee.List<string> path; -public Entry entry; - -public EntryWithPath(string[] path, Entry entry) -{ - this.path = toList(path); - this.entry = entry; -} - -public EntryWithPath.now(string[] path, Json.Node key, Json.Node value) -{ - this.path = toList(path); - this.entry = new Entry.now(key, value); -} + public Gee.List<string> path; + public Entry entry; + + public EntryWithPath(string[] path, Entry entry) + { + this.path = toList(path); + this.entry = entry; + } + + public EntryWithPath.now(string[] path, Json.Node key, Json.Node value) + { + this.path = toList(path); + this.entry = new Entry.now(key, value); + } } /** - * Represents a key/value pair stored by DecSync. Additionally, it has a datetime property - * indicating the most recent update. It does not store its path, see [EntryWithPath]. - */ +* Represents a key/value pair stored by DecSync. Additionally, it has a datetime property +* indicating the most recent update. It does not store its path, see [EntryWithPath]. +*/ public class Entry { -internal string datetime; -public Json.Node key; -public Json.Node value; - -public Entry(string datetime, Json.Node key, Json.Node value) -{ - this.datetime = datetime; - this.key = key; - this.value = value; -} - -public Entry.now(Json.Node key, Json.Node value) -{ - this.datetime = new GLib.DateTime.now_utc().format("%FT%T"); - this.key = key; - this.value = value; -} - -internal string toLine() -{ - var json = new Json.Node(Json.NodeType.ARRAY); - var array = new Json.Array(); - array.add_string_element(this.datetime); - array.add_element(this.key); - array.add_element(this.value); - json.set_array(array); - return Json.to_string(json, false); -} - -internal static Entry? fromLine(string line) -{ - try { - var json = Json.from_string(line); - var array = json.get_array(); - if (array == null || array.get_length() != 3) - { - Log.w("Invalid entry " + line); - return null; - } - var datetime = array.get_string_element(0); - if (datetime == null) - { - Log.w("Invalid entry " + line); + internal string datetime; + public Json.Node key; + public Json.Node value; + + public Entry(string datetime, Json.Node key, Json.Node value) + { + this.datetime = datetime; + this.key = key; + this.value = value; + } + + public Entry.now(Json.Node key, Json.Node value) + { + this.datetime = new GLib.DateTime.now_utc().format("%FT%T"); + this.key = key; + this.value = value; + } + + internal string toLine() + { + var json = new Json.Node(Json.NodeType.ARRAY); + var array = new Json.Array(); + array.add_string_element(this.datetime); + array.add_element(this.key); + array.add_element(this.value); + json.set_array(array); + return Json.to_string(json, false); + } + + internal static Entry? fromLine(string line) + { + try { + var json = Json.from_string(line); + var array = json.get_array(); + if (array == null || array.get_length() != 3) + { + Log.w("Invalid entry " + line); + return null; + } + var datetime = array.get_string_element(0); + if (datetime == null) + { + Log.w("Invalid entry " + line); + return null; + } + var key = array.get_element(1); + var value = array.get_element(2); + return new Entry(datetime, key, value); + } catch (GLib.Error e) { + Log.w("Invalid JSON: " + line + "\n" + e.message); return null; } - var key = array.get_element(1); - var value = array.get_element(2); - return new Entry(datetime, key, value); - } catch (GLib.Error e) { - Log.w("Invalid JSON: " + line + "\n" + e.message); - return null; } } -} private class EntriesLocation { -public Gee.List<string> path; -public File newEntriesFile; -public File? storedEntriesFile; -public File? readBytesFile; - -public EntriesLocation.getNewEntriesLocation(Decsync decsync, Gee.List<string> path, string appId) -{ - var pathString = FileUtils.pathToString(path); - var appIdEncoded = FileUtils.urlencode(appId); - this.path = path; - this.newEntriesFile = File.new_for_path(decsync.dir + "/new-entries/" + appIdEncoded + "/" + pathString); - this.storedEntriesFile = File.new_for_path(decsync.dir + "/stored-entries/" + decsync.ownAppIdEncoded + "/" + pathString); - this.readBytesFile = File.new_for_path(decsync.dir + "/read-bytes/" + decsync.ownAppIdEncoded + "/" + appIdEncoded + "/" + pathString); -} - -public EntriesLocation.getStoredEntriesLocation(Decsync decsync, Gee.List<string> path) -{ - var pathString = FileUtils.pathToString(path); - this.path = path; - this.newEntriesFile = File.new_for_path(decsync.dir + "/stored-entries/" + decsync.ownAppIdEncoded + "/" + pathString); - this.storedEntriesFile = null; - this.readBytesFile = null; -} + public Gee.List<string> path; + public File newEntriesFile; + public File? storedEntriesFile; + public File? readBytesFile; + + public EntriesLocation.getNewEntriesLocation(Decsync decsync, Gee.List<string> path, string appId) + { + var pathString = FileUtils.pathToString(path); + var appIdEncoded = FileUtils.urlencode(appId); + this.path = path; + this.newEntriesFile = File.new_for_path(decsync.dir + "/new-entries/" + appIdEncoded + "/" + pathString); + this.storedEntriesFile = File.new_for_path(decsync.dir + "/stored-entries/" + decsync.ownAppIdEncoded + "/" + pathString); + this.readBytesFile = File.new_for_path(decsync.dir + "/read-bytes/" + decsync.ownAppIdEncoded + "/" + appIdEncoded + "/" + pathString); + } + + public EntriesLocation.getStoredEntriesLocation(Decsync decsync, Gee.List<string> path) + { + var pathString = FileUtils.pathToString(path); + this.path = path; + this.newEntriesFile = File.new_for_path(decsync.dir + "/stored-entries/" + decsync.ownAppIdEncoded + "/" + pathString); + this.storedEntriesFile = null; + this.readBytesFile = null; + } } /** - * Associates the given [value] with the given [key] in the map corresponding to the given - * [path]. This update is sent to synchronized devices. - */ +* Associates the given [value] with the given [key] in the map corresponding to the given +* [path]. This update is sent to synchronized devices. +*/ public void setEntry(string[] pathArray, Json.Node key, Json.Node value) { var entries = new Gee.ArrayList<Entry>(); @@ -205,36 +205,36 @@ public void setEntry(string[] pathArray, Json.Node key, Json.Node value) } /** - * Like [setEntry], but allows multiple entries to be set. This is more efficient if multiple - * entries share the same path. - * - * @param entriesWithPath entries with path which are inserted. - */ +* Like [setEntry], but allows multiple entries to be set. This is more efficient if multiple +* entries share the same path. +* +* @param entriesWithPath entries with path which are inserted. +*/ public void setEntries(Gee.Collection<EntryWithPath> entriesWithPath) { var multiMap = groupBy<EntryWithPath, Gee.List<string>, Entry>( entriesWithPath, entryWithPath => { return entryWithPath.path; }, entryWithPath => { return entryWithPath.entry; } - ); + ); multiMap.get_keys().@foreach(path => { - setEntriesForPath(path, multiMap.@get(path)); - return true; - }); + setEntriesForPath(path, multiMap.@get(path)); + return true; + }); } /** - * Like [setEntries], but only allows the entries to have the same path. Consequently, it can - * be slightly more convenient since the path has to be specified just once. - * - * @param path path to the map in which the entries are inserted. - * @param entries entries which are inserted. - */ +* Like [setEntries], but only allows the entries to have the same path. Consequently, it can +* be slightly more convenient since the path has to be specified just once. +* +* @param path path to the map in which the entries are inserted. +* @param entries entries which are inserted. +*/ public void setEntriesForPath(Gee.List<string> path, Gee.Collection<Entry> entries) { Log.d("Write to path " + FileUtils.pathToString(path)); var entriesLocation = new EntriesLocation.getNewEntriesLocation(this, path, ownAppId); - + // Write new entries var builder = new StringBuilder(); foreach (var entry in entries) { @@ -245,13 +245,13 @@ public void setEntriesForPath(Gee.List<string> path, Gee.Collection<Entry> entri } catch (Error e) { Log.w(e.message); } - + // Update .decsync-sequence files while (!path.is_empty) { path.remove_at(path.size - 1); var dir = new EntriesLocation.getNewEntriesLocation(this, path, ownAppId).newEntriesFile; var file = dir.get_child(".decsync-sequence"); - + // Get the old version int64 version = 0; if (file.query_exists()) @@ -263,7 +263,7 @@ public void setEntriesForPath(Gee.List<string> path, Gee.Collection<Entry> entri Log.w(e.message); } } - + // Write the new version try { FileUtils.writeFile(file, (version + 1).to_string()); @@ -271,17 +271,17 @@ public void setEntriesForPath(Gee.List<string> path, Gee.Collection<Entry> entri Log.w(e.message); } } - + // Update stored entries updateStoredEntries(entriesLocation, entries); } /** - * Initializes the monitor which watches the filesystem for updated entries and executes the - * corresponding actions. - * - * @param extra extra data passed to the [listeners]. - */ +* Initializes the monitor which watches the filesystem for updated entries and executes the +* corresponding actions. +* +* @param extra extra data passed to the [listeners]. +*/ public void initMonitor(T extra) { try { @@ -293,29 +293,29 @@ public void initMonitor(T extra) } monitor = new DirectoryMonitor(newEntriesDir); monitor.changed.connect(pathString => { - var pathEncoded = new Gee.ArrayList<string>.wrap(pathString.split("/")); - pathEncoded.remove(""); - if (pathEncoded.is_empty || pathEncoded.last()[0] == '.') - { - return; - } - var path = new Gee.ArrayList<string>(); - path.add_all_iterator(pathEncoded.map<string>(part => { return FileUtils.urldecode(part); })); - if (path.any_match(part => { return part == null; })) - { - Log.w("Cannot decode path " + pathString); - return; - } - var appId = path.first(); - path.remove_at(0); - var entriesLocation = new EntriesLocation.getNewEntriesLocation(this, path, appId); - if (appId != ownAppId && entriesLocation.newEntriesFile.query_file_type(FileQueryInfoFlags.NONE) == FileType.REGULAR) - { - executeEntriesLocation(entriesLocation, extra); - Log.d("Sync complete"); - syncComplete(extra); - } - }); + var pathEncoded = new Gee.ArrayList<string>.wrap(pathString.split("/")); + pathEncoded.remove(""); + if (pathEncoded.is_empty || pathEncoded.last()[0] == '.') + { + return; + } + var path = new Gee.ArrayList<string>(); + path.add_all_iterator(pathEncoded.map<string>(part => { return FileUtils.urldecode(part); })); + if (path.any_match(part => { return part == null; })) + { + Log.w("Cannot decode path " + pathString); + return; + } + var appId = path.first(); + path.remove_at(0); + var entriesLocation = new EntriesLocation.getNewEntriesLocation(this, path, appId); + if (appId != ownAppId && entriesLocation.newEntriesFile.query_file_type(FileQueryInfoFlags.NONE) == FileType.REGULAR) + { + executeEntriesLocation(entriesLocation, extra); + Log.d("Sync complete"); + syncComplete(extra); + } + }); Log.d("Initialized folder monitor for " + dir + "/new-entries"); } catch (GLib.Error e) { Log.w(e.message); @@ -323,10 +323,10 @@ public void initMonitor(T extra) } /** - * Gets all updated entries and executes the corresponding actions. - * - * @param extra extra data passed to the [listeners]. - */ +* Gets all updated entries and executes the corresponding actions. +* +* @param extra extra data passed to the [listeners]. +*/ public void executeAllNewEntries(T extra) { Log.d("Execute all new entries in " + dir); @@ -336,9 +336,9 @@ public void executeAllNewEntries(T extra) FileUtils.listFilesRecursiveRelative(newEntriesDir, readBytesDir, pathPred) .map<EntriesLocation>(path => { return new EntriesLocation.getNewEntriesLocation(this, path.slice(1, path.size), path.first()); }) .@foreach (entriesLocation => { - executeEntriesLocation(entriesLocation, extra); - return true; - }); + executeEntriesLocation(entriesLocation, extra); + return true; + }); Log.d("Sync complete"); syncComplete(extra); } @@ -356,7 +356,7 @@ private void executeEntriesLocation(EntriesLocation entriesLocation, T extra, Ge Log.w(e.message); } } - + // Write the new number of read bytes (= size of the entry file) if (entriesLocation.readBytesFile != null) { @@ -371,14 +371,14 @@ private void executeEntriesLocation(EntriesLocation entriesLocation, T extra, Ge Log.w(e.message); } } - + Log.d("Execute entries of " + entriesLocation.newEntriesFile.get_path()); - + // Execute the entries var entriesMap = new Gee.HashMap<Json.Node, Entry>( a => { return a.hash(); }, (a, b) => { return a.equal(b); } - ); + ); try { var stream = new DataInputStream(entriesLocation.newEntriesFile.read()); stream.seek(readBytes, SeekType.SET); @@ -390,7 +390,7 @@ private void executeEntriesLocation(EntriesLocation entriesLocation, T extra, Ge continue; } if ((keyPred == null || keyPred(entryLine.key)) && - (valuePred == null || valuePred(entryLine.value))) + (valuePred == null || valuePred(entryLine.value))) { var key = entryLine.key; var entry = entriesMap.@get(key); @@ -411,14 +411,14 @@ private void executeEntriesLocation(EntriesLocation entriesLocation, T extra, Ge private void executeEntries(EntriesLocation entriesLocation, Gee.Collection<Entry> entries, T extra) { updateStoredEntries(entriesLocation, entries); - + var listener = getListener(entriesLocation.path); if (listener == null) { Log.e("Unknown action for path " + FileUtils.pathToString(entriesLocation.path)); return; } - + listener.onEntriesUpdate(entriesLocation.path, entries, extra); } @@ -428,7 +428,7 @@ private void updateStoredEntries(EntriesLocation entriesLocation, Gee.Collection { return; } - + try { var haveToFilterFile = false; if (entriesLocation.storedEntriesFile.query_exists()) @@ -459,24 +459,24 @@ private void updateStoredEntries(EntriesLocation entriesLocation, Gee.Collection } } } - + if (haveToFilterFile) { FileUtils.filterFile(entriesLocation.storedEntriesFile, line => { - var entryLine = Entry.fromLine(line); - if (entryLine == null) - { - return false; - } - return !entries.any_match(entry => { return entry.key.equal(entryLine.key); }); - }); + var entryLine = Entry.fromLine(line); + if (entryLine == null) + { + return false; + } + return !entries.any_match(entry => { return entry.key.equal(entryLine.key); }); + }); } - + var builder = new StringBuilder(); entries.@foreach(entry => { - builder.append(entry.toLine() + "\n"); - return true; - }); + builder.append(entry.toLine() + "\n"); + return true; + }); FileUtils.writeFile(entriesLocation.storedEntriesFile, builder.str, true); } catch (GLib.Error e) @@ -486,41 +486,41 @@ private void updateStoredEntries(EntriesLocation entriesLocation, Gee.Collection } /** - * Gets all stored entries satisfying the predicates and executes the corresponding actions. - * - * @param executePath path to the entries to executes. This can be either a file or a directory. - * If it specifies a file, the entries in that file are executed. If it specifies a directory, - * all entries in all subfiles are executed. - * @param extra extra data passed to the [listeners]. - * @param keyPred optional predicate on the keys. The key has to satisfy this predicate to be - * executed. - * @param valuePred optional predicate on the values. The value has to satisfy this predicate to - * be executed. - * @param pathPred optional predicate on the subpaths. Each subpath has to satisfy this - * predicate to be executed. This holds for directories as well. Furthermore, the path of - * specified in [executePath] is not part of the argument. - */ +* Gets all stored entries satisfying the predicates and executes the corresponding actions. +* +* @param executePath path to the entries to executes. This can be either a file or a directory. +* If it specifies a file, the entries in that file are executed. If it specifies a directory, +* all entries in all subfiles are executed. +* @param extra extra data passed to the [listeners]. +* @param keyPred optional predicate on the keys. The key has to satisfy this predicate to be +* executed. +* @param valuePred optional predicate on the values. The value has to satisfy this predicate to +* be executed. +* @param pathPred optional predicate on the subpaths. Each subpath has to satisfy this +* predicate to be executed. This holds for directories as well. Furthermore, the path of +* specified in [executePath] is not part of the argument. +*/ public void executeStoredEntries(string[] executePathArray, T extra, - Gee.Predicate<Json.Node>? keyPred = null, - Gee.Predicate<Json.Node>? valuePred = null, - Gee.Predicate<Gee.List<string>>? pathPred = null) + Gee.Predicate<Json.Node>? keyPred = null, + Gee.Predicate<Json.Node>? valuePred = null, +Gee.Predicate<Gee.List<string>>? pathPred = null) { var executePath = toList(executePathArray); var executePathString = FileUtils.pathToString(executePath); var executeDir = File.new_for_path(dir + "/stored-entries/" + ownAppIdEncoded + "/" + executePathString); FileUtils.listFilesRecursiveRelative(executeDir, null, pathPred) .@foreach(path => { - path.insert_all(0, executePath); - var entriesLocation = new EntriesLocation.getStoredEntriesLocation(this, path); - executeEntriesLocation(entriesLocation, extra, keyPred, valuePred); - return true; - }); + path.insert_all(0, executePath); + var entriesLocation = new EntriesLocation.getStoredEntriesLocation(this, path); + executeEntriesLocation(entriesLocation, extra, keyPred, valuePred); + return true; + }); } /** - * Initializes the stored entries. This method does not execute any actions. This is often - * followed with a call to [executeStoredEntries]. - */ +* Initializes the stored entries. This method does not execute any actions. This is often +* followed with a call to [executeStoredEntries]. +*/ public void initStoredEntries() { // Get the most up-to-date appId @@ -529,47 +529,47 @@ public void initStoredEntries() FileUtils.listFilesRecursiveRelative(File.new_for_path(dir + "/stored-entries")) .filter(path => { return !path.is_empty; }) .@foreach(path => { - var pathString = FileUtils.pathToString(path); - try { - var file = File.new_for_path(dir + "/stored-entries/" + pathString); - var stream = new DataInputStream(file.read()); - string line; - while ((line = stream.read_line(null)) != null) { - var entry = Entry.fromLine(line); - if (entry == null) - { - continue; - } - if (maxDatetime == null || entry.datetime > maxDatetime || - path.first() == ownAppId && entry.datetime == maxDatetime) // Prefer own appId - { - maxDatetime = entry.datetime; - appId = path.first(); - } + var pathString = FileUtils.pathToString(path); + try { + var file = File.new_for_path(dir + "/stored-entries/" + pathString); + var stream = new DataInputStream(file.read()); + string line; + while ((line = stream.read_line(null)) != null) { + var entry = Entry.fromLine(line); + if (entry == null) + { + continue; + } + if (maxDatetime == null || entry.datetime > maxDatetime || + path.first() == ownAppId && entry.datetime == maxDatetime) // Prefer own appId + { + maxDatetime = entry.datetime; + appId = path.first(); } - } catch (GLib.Error e) { - Log.w(e.message); } - return true; - }); + } catch (GLib.Error e) { + Log.w(e.message); + } + return true; + }); if (appId == null) { Log.i("No appId found for initialization"); return; } - + // Copy the stored files and update the read bytes if (appId != ownAppId) { var appIdEncoded = FileUtils.urlencode(appId); - + try { FileUtils.@delete(File.new_for_path(dir + "/stored-entries/" + ownAppIdEncoded)); FileUtils.copy(File.new_for_path(dir + "/stored-entries/" + appIdEncoded), File.new_for_path(dir + "/stored-entries/" + ownAppIdEncoded)); } catch (GLib.Error e) { Log.w(e.message); } - + try { FileUtils.@delete(File.new_for_path(dir + "/read-bytes/" + ownAppIdEncoded)); FileUtils.copy(File.new_for_path(dir + "/read-bytes/" + appIdEncoded), File.new_for_path(dir + "/read-bytes/" + ownAppIdEncoded)); @@ -579,26 +579,26 @@ public void initStoredEntries() var newEntriesDir = File.new_for_path(dir + "/new-entries/" + appIdEncoded); var ownReadBytesDir = File.new_for_path(dir + "/read-bytes/" + ownAppIdEncoded + "/" + appIdEncoded); FileUtils.listFilesRecursiveRelative(newEntriesDir, ownReadBytesDir).@foreach(path => { - var pathString = FileUtils.pathToString(path); - try { - var newEntriesFile = File.new_for_path(dir + "/new-entries/" + appIdEncoded + "/" + pathString); - var size = newEntriesFile.query_info("standard::size", FileQueryInfoFlags.NONE).get_size(); - var readBytesFile = File.new_for_path(dir + "/read-bytes/" + ownAppIdEncoded + "/" + appIdEncoded + "/" + pathString); - FileUtils.writeFile(readBytesFile, size.to_string()); - } catch (GLib.Error e) { - Log.w(e.message); - } - return true; - }); + var pathString = FileUtils.pathToString(path); + try { + var newEntriesFile = File.new_for_path(dir + "/new-entries/" + appIdEncoded + "/" + pathString); + var size = newEntriesFile.query_info("standard::size", FileQueryInfoFlags.NONE).get_size(); + var readBytesFile = File.new_for_path(dir + "/read-bytes/" + ownAppIdEncoded + "/" + appIdEncoded + "/" + pathString); + FileUtils.writeFile(readBytesFile, size.to_string()); + } catch (GLib.Error e) { + Log.w(e.message); + } + return true; + }); } } /** - * Returns the value of the given [key] in the map of the given [path], and in the given - * [DecSync directory][decsyncDir] without specifying an appId, or `null` if there is no - * such value. The use of this method is discouraged. It is recommended to use the method - * [executeStoredEntries] when possible. - */ +* Returns the value of the given [key] in the map of the given [path], and in the given +* [DecSync directory][decsyncDir] without specifying an appId, or `null` if there is no +* such value. The use of this method is discouraged. It is recommended to use the method +* [executeStoredEntries] when possible. +*/ public static Json.Node? getStoredStaticValue(string decsyncDir, string[] pathArray, Json.Node key) { Log.d("Get value for key " + Json.to_string(key, false) + " for path " + string.joinv("/", pathArray) + " in " + decsyncDir); @@ -615,14 +615,14 @@ public static Json.Node? getStoredStaticValue(string decsyncDir, string[] pathAr { continue; } - + var appIdEncoded = info.get_name(); var file = File.new_for_path(decsyncDir + "/stored-entries/" + appIdEncoded + "/" + pathString); if (!file.query_exists() || file.query_file_type(FileQueryInfoFlags.NONE) != FileType.REGULAR) { continue; } - + var stream = new DataInputStream(file.read()); string line; while ((line = stream.read_line(null)) != null) { @@ -641,7 +641,7 @@ public static Json.Node? getStoredStaticValue(string decsyncDir, string[] pathAr } catch (GLib.Error e) { Log.w(e.message); } - + return result; } @@ -658,87 +658,87 @@ private OnEntryUpdateListener<T>? getListener(Gee.List<string> path) } /** - * Returns the path to the DecSync subdirectory in a [decsyncBaseDir] for a [syncType] and - * optionally with a [collection]. - * - * @param decsyncBaseDir the path to the main DecSync directory, or null for the default one. - * @param syncType the type of data to sync. For example, "rss", "contacts" or "calendars". - * @param collection an optional collection identifier when multiple instances of the [syncType] are - * supported. For example, this is the case for "contacts" and "calendars", but not for "rss". - */ +* Returns the path to the DecSync subdirectory in a [decsyncBaseDir] for a [syncType] and +* optionally with a [collection]. +* +* @param decsyncBaseDir the path to the main DecSync directory, or null for the default one. +* @param syncType the type of data to sync. For example, "rss", "contacts" or "calendars". +* @param collection an optional collection identifier when multiple instances of the [syncType] are +* supported. For example, this is the case for "contacts" and "calendars", but not for "rss". +*/ public string getDecsyncSubdir(string? decsyncBaseDir, string syncType, string? collection = null) { - string dir = decsyncBaseDir ?? getDefaultDecsyncBaseDir(); - dir += "/" + FileUtils.urlencode(syncType); - if (collection != null) - { - dir += "/" + FileUtils.urlencode(collection); - } - return dir; +string dir = decsyncBaseDir ?? getDefaultDecsyncBaseDir(); +dir += "/" + FileUtils.urlencode(syncType); +if (collection != null) +{ + dir += "/" + FileUtils.urlencode(collection); +} +return dir; } /** - * Returns the default DecSync directory. This is the "decsync" subdirectory on the user data dir - * ("~/.local/share" by default). - */ +* Returns the default DecSync directory. This is the "decsync" subdirectory on the user data dir +* ("~/.local/share" by default). +*/ public string getDefaultDecsyncBaseDir() { - return GLib.Environment.get_user_data_dir() + "/decsync"; +return GLib.Environment.get_user_data_dir() + "/decsync"; } /** - * Returns a list of DecSync collections inside a [decsyncBaseDir] for a [syncType]. This function - * does not apply for sync types with single instances. - * - * @param decsyncBaseDir the path to the main DecSync directory, or null for the default one. - * @param syncType the type of data to sync. For example, "contacts" or "calendars". - * @param ignoreDeleted `true` to ignore deleted collections. A collection is considered deleted if - * the most recent value of the key "deleted" with the path ["info"] is set to `true`. - */ +* Returns a list of DecSync collections inside a [decsyncBaseDir] for a [syncType]. This function +* does not apply for sync types with single instances. +* +* @param decsyncBaseDir the path to the main DecSync directory, or null for the default one. +* @param syncType the type of data to sync. For example, "contacts" or "calendars". +* @param ignoreDeleted `true` to ignore deleted collections. A collection is considered deleted if +* the most recent value of the key "deleted" with the path ["info"] is set to `true`. +*/ public Gee.ArrayList<string> listDecsyncCollections(string? decsyncBaseDir, string syncType, bool ignoreDeleted = true) throws GLib.Error { - var decsyncSubdir = File.new_for_path(getDecsyncSubdir(decsyncBaseDir, syncType)); - var enumerator = decsyncSubdir.enumerate_children("standard::*", FileQueryInfoFlags.NONE); - FileInfo info; - Gee.ArrayList<string> result = new Gee.ArrayList<string>(); - while ((info = enumerator.next_file(null)) != null) { - if (info.get_file_type() != FileType.DIRECTORY || info.get_name()[0] == '.') +var decsyncSubdir = File.new_for_path(getDecsyncSubdir(decsyncBaseDir, syncType)); +var enumerator = decsyncSubdir.enumerate_children("standard::*", FileQueryInfoFlags.NONE); +FileInfo info; +Gee.ArrayList<string> result = new Gee.ArrayList<string>(); +while ((info = enumerator.next_file(null)) != null) { + if (info.get_file_type() != FileType.DIRECTORY || info.get_name()[0] == '.') + { + continue; + } + if (ignoreDeleted) + { + var deleted = Decsync.getStoredStaticValue(decsyncSubdir.get_child(info.get_name()).get_path(), {"info"}, stringToNode("deleted")); + if (deleted != null && deleted.get_boolean()) { continue; } - if (ignoreDeleted) - { - var deleted = Decsync.getStoredStaticValue(decsyncSubdir.get_child(info.get_name()).get_path(), {"info"}, stringToNode("deleted")); - if (deleted != null && deleted.get_boolean()) - { - continue; - } - } - var collection = FileUtils.urldecode(info.get_name()); - if (collection != null) - { - result.add(collection); - } } - return result; + var collection = FileUtils.urldecode(info.get_name()); + if (collection != null) + { + result.add(collection); + } +} +return result; } /** - * Returns the appId of the current device and application combination. - * - * @param appName the name of the application. - * @param id an optional integer (between 0 and 100000 exclusive) to distinguish different instances - * on the same device and application. - */ +* Returns the appId of the current device and application combination. +* +* @param appName the name of the application. +* @param id an optional integer (between 0 and 100000 exclusive) to distinguish different instances +* on the same device and application. +*/ public string getAppId(string appName, int? id = null) { - string appId = GLib.Environment.get_host_name() + "-" + appName; - if (id == null) - { - return appId; - } - else - { - return appId + "-" + "%05d".printf(id); - } +string appId = GLib.Environment.get_host_name() + "-" + appName; +if (id == null) +{ + return appId; +} +else +{ + return appId + "-" + "%05d".printf(id); +} } diff --git a/plugins/backend/decsync/libdecsync/src/DirectoryMonitor.vala b/plugins/backend/decsync/libdecsync/src/DirectoryMonitor.vala index 8a3bc44a..4342bd61 100644 --- a/plugins/backend/decsync/libdecsync/src/DirectoryMonitor.vala +++ b/plugins/backend/decsync/libdecsync/src/DirectoryMonitor.vala @@ -1,96 +1,96 @@ /** - * libdecsync-vala - DirectoryMonitor.vala - * - * Copyright (C) 2018 Aldo Gunsing - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see <http://www.gnu.org/licenses/>. - */ +* libdecsync-vala - DirectoryMonitor.vala +* +* Copyright (C) 2018 Aldo Gunsing +* +* This library is free software; you can redistribute it and/or modify it +* under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation. +* +* This library is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ public class DirectoryMonitor : GLib.Object { - -private File mDir; -private string mPath; -private FileMonitor mMonitor; -private Gee.ArrayList<DirectoryMonitor> mChilds = new Gee.ArrayList<DirectoryMonitor>(); - -public signal void changed(string path); - -public DirectoryMonitor(File dir) throws GLib.Error -{ - this.withPath(dir, ""); -} - -private DirectoryMonitor.withPath(File dir, string path) throws GLib.Error -{ - mDir = dir; - mPath = path; - var currentDir = File.new_for_path(dir.get_path() + path); - mMonitor = currentDir.monitor_directory(FileMonitorFlags.NONE); - mMonitor.changed.connect((file, otherFile, event) => { + + private File mDir; + private string mPath; + private FileMonitor mMonitor; + private Gee.ArrayList<DirectoryMonitor> mChilds = new Gee.ArrayList<DirectoryMonitor>(); + + public signal void changed(string path); + + public DirectoryMonitor(File dir) throws GLib.Error + { + this.withPath(dir, ""); + } + + private DirectoryMonitor.withPath(File dir, string path) throws GLib.Error + { + mDir = dir; + mPath = path; + var currentDir = File.new_for_path(dir.get_path() + path); + mMonitor = currentDir.monitor_directory(FileMonitorFlags.NONE); + mMonitor.changed.connect((file, otherFile, event) => { if (file.get_path() != mDir.get_path() + path) { - onEvent(path + "/" + file.get_basename(), event); + onEvent(path + "/" + file.get_basename(), event); } }); - Log.d("Monitor created for " + currentDir.get_path() + " (folder " + dir.get_path() + ")"); - - var enumerator = currentDir.enumerate_children("standard::*", FileQueryInfoFlags.NONE); - FileInfo info = null; - while (((info = enumerator.next_file(null)) != null)) { - if (info.get_file_type() == FileType.DIRECTORY) - { - var childMonitor = new DirectoryMonitor.withPath(mDir, path + "/" + info.get_name()); - childMonitor.changed.connect((path) => { + Log.d("Monitor created for " + currentDir.get_path() + " (folder " + dir.get_path() + ")"); + + var enumerator = currentDir.enumerate_children("standard::*", FileQueryInfoFlags.NONE); + FileInfo info = null; + while (((info = enumerator.next_file(null)) != null)) { + if (info.get_file_type() == FileType.DIRECTORY) + { + var childMonitor = new DirectoryMonitor.withPath(mDir, path + "/" + info.get_name()); + childMonitor.changed.connect((path) => { changed(path); }); - mChilds.add(childMonitor); + mChilds.add(childMonitor); + } } } -} - -private void onEvent(string path, FileMonitorEvent event) -{ - Log.d("Received inotify event " + event.to_string() + " at " + mDir.get_path() + "/" + path); - switch (event) { - case FileMonitorEvent.DELETED: - foreach (var c in mChilds) { - if (c.mPath == path) - { - mChilds.remove(c); - break; + + private void onEvent(string path, FileMonitorEvent event) + { + Log.d("Received inotify event " + event.to_string() + " at " + mDir.get_path() + "/" + path); + switch (event) { + case FileMonitorEvent.DELETED: + foreach (var c in mChilds) { + if (c.mPath == path) + { + mChilds.remove(c); + break; + } } - } - break; - case FileMonitorEvent.CREATED: - case FileMonitorEvent.CHANGED: - var file = File.new_for_path(mDir.get_path() + path); - if (file.query_file_type(FileQueryInfoFlags.NONE) == FileType.DIRECTORY) - { - try { - var childMonitor = new DirectoryMonitor.withPath(mDir, path); - childMonitor.changed.connect((path) => { + break; + case FileMonitorEvent.CREATED: + case FileMonitorEvent.CHANGED: + var file = File.new_for_path(mDir.get_path() + path); + if (file.query_file_type(FileQueryInfoFlags.NONE) == FileType.DIRECTORY) + { + try { + var childMonitor = new DirectoryMonitor.withPath(mDir, path); + childMonitor.changed.connect((path) => { changed(path); }); - mChilds.add(childMonitor); - } catch (GLib.Error e) { - Log.w(e.message); + mChilds.add(childMonitor); + } catch (GLib.Error e) { + Log.w(e.message); + } } + else + { + changed(path); + } + break; } - else - { - changed(path); - } - break; } } -} diff --git a/plugins/backend/decsync/libdecsync/src/FileUtils.vala b/plugins/backend/decsync/libdecsync/src/FileUtils.vala index 20008264..c447e523 100644 --- a/plugins/backend/decsync/libdecsync/src/FileUtils.vala +++ b/plugins/backend/decsync/libdecsync/src/FileUtils.vala @@ -1,250 +1,250 @@ /** - * libdecsync-vala - FileUtils.vala - * - * Copyright (C) 2018 Aldo Gunsing - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see <http://www.gnu.org/licenses/>. - */ +* libdecsync-vala - FileUtils.vala +* +* Copyright (C) 2018 Aldo Gunsing +* +* This library is free software; you can redistribute it and/or modify it +* under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation. +* +* This library is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ public class FileUtils : GLib.Object { - -public static void writeFile(File file, string content, bool append = false) throws GLib.Error -{ - var parent = file.get_parent(); - if (!parent.query_exists()) - { - parent.make_directory_with_parents(); - } - - GLib.FileOutputStream stream; - if (append) - { - stream = file.append_to(FileCreateFlags.NONE); - } - else - { - if (file.query_exists()) - { - file.@delete(); - } - stream = file.create(FileCreateFlags.REPLACE_DESTINATION); - } - stream.write(content.data); -} - -public static void @delete(File src) throws GLib.Error -{ - if (!src.query_exists()) - { - return; - } - if (src.query_file_type(FileQueryInfoFlags.NONE) == FileType.DIRECTORY) + + public static void writeFile(File file, string content, bool append = false) throws GLib.Error { - var enumerator = src.enumerate_children("standard::name", FileQueryInfoFlags.NONE); - FileInfo info; - while ((info = enumerator.next_file(null)) != null) { - var name = info.get_name(); - @delete(src.get_child(name)); - } - } - src.@delete(); -} - -public static void copy(File src, File dst, bool overwrite = false) throws GLib.Error -{ - switch (src.query_file_type(FileQueryInfoFlags.NONE)) { - case FileType.REGULAR: - var parent = dst.get_parent(); + var parent = file.get_parent(); if (!parent.query_exists()) { parent.make_directory_with_parents(); } - src.copy(dst, overwrite ? FileCopyFlags.OVERWRITE : FileCopyFlags.NONE); - return; - case FileType.DIRECTORY: - dst.make_directory_with_parents(); - var enumerator = src.enumerate_children("standard::name", FileQueryInfoFlags.NONE); - FileInfo info; - while ((info = enumerator.next_file(null)) != null) { - var name = info.get_name(); - copy(src.get_child(name), dst.get_child(name), overwrite); + + GLib.FileOutputStream stream; + if (append) + { + stream = file.append_to(FileCreateFlags.NONE); } - return; - } -} - -public static void filterFile(File file, Gee.Predicate<string> linePred) throws GLib.Error -{ - var tempFile = File.new_for_path(file.get_parent().get_path() + "." + file.get_basename() + ".tmp"); - var instream = new DataInputStream(file.read()); - var outstream = new DataOutputStream(tempFile.create(FileCreateFlags.NONE)); - string line; - while ((line = instream.read_line(null)) != null) { - if (linePred(line)) + else { - outstream.put_string(line + "\n"); + if (file.query_exists()) + { + file.@delete(); + } + stream = file.create(FileCreateFlags.REPLACE_DESTINATION); } + stream.write(content.data); } - tempFile.move(file, FileCopyFlags.OVERWRITE); -} - -public static Gee.ArrayList<Gee.ArrayList<string>> listFilesRecursiveRelative(File src, File? readBytesSrc = null, Gee.Predicate<Gee.List<string>>? pathPred = null) -{ - if (src.get_basename()[0] == '.') + + public static void @delete(File src) throws GLib.Error { - return new Gee.ArrayList<Gee.ArrayList<string>>(); + if (!src.query_exists()) + { + return; + } + if (src.query_file_type(FileQueryInfoFlags.NONE) == FileType.DIRECTORY) + { + var enumerator = src.enumerate_children("standard::name", FileQueryInfoFlags.NONE); + FileInfo info; + while ((info = enumerator.next_file(null)) != null) { + var name = info.get_name(); + @delete(src.get_child(name)); + } + } + src.@delete(); } - if (pathPred != null && !pathPred(new Gee.ArrayList<string>())) + + public static void copy(File src, File dst, bool overwrite = false) throws GLib.Error { - return new Gee.ArrayList<Gee.ArrayList<string>>(); - } - - switch (src.query_file_type(FileQueryInfoFlags.NONE)) { - case FileType.REGULAR: - var result = new Gee.ArrayList<Gee.ArrayList<string>>(); - result.add(new Gee.ArrayList<string>()); - return result; - case FileType.DIRECTORY: - // Skip same versions - if (readBytesSrc != null) - { - var file = src.get_child(".decsync-sequence"); - string? version = null; - if (file.query_exists()) + switch (src.query_file_type(FileQueryInfoFlags.NONE)) { + case FileType.REGULAR: + var parent = dst.get_parent(); + if (!parent.query_exists()) { - try { - version = new DataInputStream(file.read()).read_line(); - } catch (GLib.Error e) { - Log.w(e.message); - } + parent.make_directory_with_parents(); + } + src.copy(dst, overwrite ? FileCopyFlags.OVERWRITE : FileCopyFlags.NONE); + return; + case FileType.DIRECTORY: + dst.make_directory_with_parents(); + var enumerator = src.enumerate_children("standard::name", FileQueryInfoFlags.NONE); + FileInfo info; + while ((info = enumerator.next_file(null)) != null) { + var name = info.get_name(); + copy(src.get_child(name), dst.get_child(name), overwrite); } - var readBytesFile = readBytesSrc.get_child(".decsync-sequence"); - string? readBytesVersion = null; - if (readBytesFile.query_exists()) + return; + } + } + + public static void filterFile(File file, Gee.Predicate<string> linePred) throws GLib.Error + { + var tempFile = File.new_for_path(file.get_parent().get_path() + "." + file.get_basename() + ".tmp"); + var instream = new DataInputStream(file.read()); + var outstream = new DataOutputStream(tempFile.create(FileCreateFlags.NONE)); + string line; + while ((line = instream.read_line(null)) != null) { + if (linePred(line)) { - try { - readBytesVersion = new DataInputStream(readBytesFile.read()).read_line(); - } catch (GLib.Error e) { - Log.w(e.message); - } + outstream.put_string(line + "\n"); } - if (version != null) + } + tempFile.move(file, FileCopyFlags.OVERWRITE); + } + + public static Gee.ArrayList<Gee.ArrayList<string>> listFilesRecursiveRelative(File src, File? readBytesSrc = null, Gee.Predicate<Gee.List<string>>? pathPred = null) + { + if (src.get_basename()[0] == '.') + { + return new Gee.ArrayList<Gee.ArrayList<string>>(); + } + if (pathPred != null && !pathPred(new Gee.ArrayList<string>())) + { + return new Gee.ArrayList<Gee.ArrayList<string>>(); + } + + switch (src.query_file_type(FileQueryInfoFlags.NONE)) { + case FileType.REGULAR: + var result = new Gee.ArrayList<Gee.ArrayList<string>>(); + result.add(new Gee.ArrayList<string>()); + return result; + case FileType.DIRECTORY: + // Skip same versions + if (readBytesSrc != null) { - if (version == readBytesVersion) - { - return new Gee.ArrayList<Gee.ArrayList<string>>(); - } - else + var file = src.get_child(".decsync-sequence"); + string? version = null; + if (file.query_exists()) { try { - copy(file, readBytesFile, true); + version = new DataInputStream(file.read()).read_line(); } catch (GLib.Error e) { Log.w(e.message); } } - } - } - - var result = new Gee.ArrayList<Gee.ArrayList<string>>(); - try { - var enumerator = src.enumerate_children("standard::name", FileQueryInfoFlags.NONE); - FileInfo info; - while ((info = enumerator.next_file(null)) != null) { - string name = info.get_name(); - string? nameDecoded = urldecode(name); - if (nameDecoded == null) + var readBytesFile = readBytesSrc.get_child(".decsync-sequence"); + string? readBytesVersion = null; + if (readBytesFile.query_exists()) { - Log.w("Cannot decode name " + name); - continue; + try { + readBytesVersion = new DataInputStream(readBytesFile.read()).read_line(); + } catch (GLib.Error e) { + Log.w(e.message); + } } - - var newReadBytesSrc = readBytesSrc == null ? null : readBytesSrc.get_child(name); - Gee.Predicate<Gee.List<string>>? newPathPred = null; - if (pathPred != null) + if (version != null) { - newPathPred = path => { path.insert(0, nameDecoded); return pathPred(path); }; + if (version == readBytesVersion) + { + return new Gee.ArrayList<Gee.ArrayList<string>>(); + } + else + { + try { + copy(file, readBytesFile, true); + } catch (GLib.Error e) { + Log.w(e.message); + } + } } - var paths = listFilesRecursiveRelative(src.get_child(name), newReadBytesSrc, newPathPred); - foreach (var path in paths) { - path.insert(0, nameDecoded); + } + + var result = new Gee.ArrayList<Gee.ArrayList<string>>(); + try { + var enumerator = src.enumerate_children("standard::name", FileQueryInfoFlags.NONE); + FileInfo info; + while ((info = enumerator.next_file(null)) != null) { + string name = info.get_name(); + string? nameDecoded = urldecode(name); + if (nameDecoded == null) + { + Log.w("Cannot decode name " + name); + continue; + } + + var newReadBytesSrc = readBytesSrc == null ? null : readBytesSrc.get_child(name); + Gee.Predicate<Gee.List<string>>? newPathPred = null; + if (pathPred != null) + { + newPathPred = path => { path.insert(0, nameDecoded); return pathPred(path); }; + } + var paths = listFilesRecursiveRelative(src.get_child(name), newReadBytesSrc, newPathPred); + foreach (var path in paths) { + path.insert(0, nameDecoded); + } + result.add_all(paths); } - result.add_all(paths); + } catch (GLib.Error e) { + Log.w(e.message); } - } catch (GLib.Error e) { - Log.w(e.message); + return result; + default: + return new Gee.ArrayList<Gee.ArrayList<string>>(); } - return result; - default: - return new Gee.ArrayList<Gee.ArrayList<string>>(); } -} - -public static string pathToString(Gee.List<string> path) -{ - var encodedPath = new Gee.ArrayList<string>(); - encodedPath.add_all_iterator(path.map<string>(part => { return urlencode(part); })); - return string.joinv("/", encodedPath.to_array()); -} - -public static string urlencode(string input) -{ - var builder = new StringBuilder(); - for (int i = 0; i < input.length; i++) { - char byte = input[i]; - if (byte.isalnum() || "-_.~".contains(byte.to_string())) - { - builder.append_c(byte); + + public static string pathToString(Gee.List<string> path) + { + var encodedPath = new Gee.ArrayList<string>(); + encodedPath.add_all_iterator(path.map<string>(part => { return urlencode(part); })); + return string.joinv("/", encodedPath.to_array()); + } + + public static string urlencode(string input) + { + var builder = new StringBuilder(); + for (int i = 0; i < input.length; i++) { + char byte = input[i]; + if (byte.isalnum() || "-_.~".contains(byte.to_string())) + { + builder.append_c(byte); + } + else + { + builder.append("%%%2X".printf(byte)); + } } - else + var output = builder.str; + + if (output != "" && output[0] == '.') { - builder.append("%%%2X".printf(byte)); + output = "%2E" + output.substring(1); } + + return output; } - var output = builder.str; - - if (output != "" && output[0] == '.') + + public static string? urldecode(string input) { - output = "%2E" + output.substring(1); - } - - return output; -} - -public static string? urldecode(string input) -{ - var builder = new StringBuilder(); - for (int i = 0; i < input.length; i++) { - char byte = input[i]; - if (byte != '%') - { - builder.append_c(byte); - } - else - { - if (i + 2 >= input.length) + var builder = new StringBuilder(); + for (int i = 0; i < input.length; i++) { + char byte = input[i]; + if (byte != '%') { - return null; + builder.append_c(byte); } - if (!input[i+1].isxdigit() || !input[i+2].isxdigit()) + else { - return null; + if (i + 2 >= input.length) + { + return null; + } + if (!input[i+1].isxdigit() || !input[i+2].isxdigit()) + { + return null; + } + char value1 = (char)input[i+1].xdigit_value(); + char value2 = (char)input[i+2].xdigit_value(); + builder.append_c(16 * value1 + value2); + i += 2; } - char value1 = (char)input[i+1].xdigit_value(); - char value2 = (char)input[i+2].xdigit_value(); - builder.append_c(16 * value1 + value2); - i += 2; } + return builder.str; } - return builder.str; -} } diff --git a/plugins/backend/decsync/libdecsync/src/Log.vala b/plugins/backend/decsync/libdecsync/src/Log.vala index 46c935ad..67106597 100644 --- a/plugins/backend/decsync/libdecsync/src/Log.vala +++ b/plugins/backend/decsync/libdecsync/src/Log.vala @@ -1,46 +1,46 @@ /** - * libdecsync-vala - Log.vala - * - * Copyright (C) 2018 Aldo Gunsing - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see <http://www.gnu.org/licenses/>. - */ +* libdecsync-vala - Log.vala +* +* Copyright (C) 2018 Aldo Gunsing +* +* This library is free software; you can redistribute it and/or modify it +* under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation. +* +* This library is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ public class Log : GLib.Object { -const string TAG = "DecSync"; - -private static void log(LogLevelFlags level, string message) -{ - GLib.log_structured(TAG, level, "MESSAGE", "%s", message); -} - -public static void e(string message) -{ - log(GLib.LogLevelFlags.LEVEL_CRITICAL, message); -} - -public static void w(string message) -{ - log(GLib.LogLevelFlags.LEVEL_WARNING, message); -} - -public static void i(string message) -{ - log(GLib.LogLevelFlags.LEVEL_INFO, message); -} - -public static void d(string message) -{ - log(GLib.LogLevelFlags.LEVEL_DEBUG, message); -} + const string TAG = "DecSync"; + + private static void log(LogLevelFlags level, string message) + { + GLib.log_structured(TAG, level, "MESSAGE", "%s", message); + } + + public static void e(string message) + { + log(GLib.LogLevelFlags.LEVEL_CRITICAL, message); + } + + public static void w(string message) + { + log(GLib.LogLevelFlags.LEVEL_WARNING, message); + } + + public static void i(string message) + { + log(GLib.LogLevelFlags.LEVEL_INFO, message); + } + + public static void d(string message) + { + log(GLib.LogLevelFlags.LEVEL_DEBUG, message); + } } diff --git a/plugins/backend/decsync/libdecsync/src/OnEntryUpdateListener.vala b/plugins/backend/decsync/libdecsync/src/OnEntryUpdateListener.vala index 6057ca64..15706e9e 100644 --- a/plugins/backend/decsync/libdecsync/src/OnEntryUpdateListener.vala +++ b/plugins/backend/decsync/libdecsync/src/OnEntryUpdateListener.vala @@ -1,65 +1,65 @@ /** - * libdecsync-vala - Subpath.vala - * - * Copyright (C) 2018 Aldo Gunsing - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see <http://www.gnu.org/licenses/>. - */ +* libdecsync-vala - Subpath.vala +* +* Copyright (C) 2018 Aldo Gunsing +* +* This library is free software; you can redistribute it and/or modify it +* under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation. +* +* This library is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ public interface OnEntryUpdateListener<T> : GLib.Object { -public abstract bool matchesPath(Gee.List<string> path); -public abstract void onEntriesUpdate(Gee.List<string> path, Gee.Collection<Decsync.Entry> entries, T extra); + public abstract bool matchesPath(Gee.List<string> path); + public abstract void onEntriesUpdate(Gee.List<string> path, Gee.Collection<Decsync.Entry> entries, T extra); } public abstract class OnSubdirEntryUpdateListener<T> : GLib.Object, OnEntryUpdateListener<T> { - -public abstract Gee.List<string> subdir(); -public abstract void onSubdirEntryUpdate(Gee.List<string> path, Decsync.Entry entry, T extra); - -public bool matchesPath(Gee.List<string> path) -{ - return path.size >= subdir().size && pathEquals(path.slice(0, subdir().size), subdir()); -} - -public void onEntriesUpdate(Gee.List<string> path, Gee.Collection<Decsync.Entry> entries, T extra) -{ - foreach (var entry in entries) { - onSubdirEntryUpdate(convertPath(path), entry, extra); + + public abstract Gee.List<string> subdir(); + public abstract void onSubdirEntryUpdate(Gee.List<string> path, Decsync.Entry entry, T extra); + + public bool matchesPath(Gee.List<string> path) + { + return path.size >= subdir().size && pathEquals(path.slice(0, subdir().size), subdir()); + } + + public void onEntriesUpdate(Gee.List<string> path, Gee.Collection<Decsync.Entry> entries, T extra) + { + foreach (var entry in entries) { + onSubdirEntryUpdate(convertPath(path), entry, extra); + } + } + + private Gee.List<string> convertPath(Gee.List<string> path) + { + return path.slice(subdir().size, path.size); } -} - -private Gee.List<string> convertPath(Gee.List<string> path) -{ - return path.slice(subdir().size, path.size); -} } public abstract class OnSubfileEntryUpdateListener<T> : GLib.Object, OnEntryUpdateListener<T> { - -public abstract Gee.List<string> subfile(); -public abstract void onSubfileEntryUpdate(Decsync.Entry entry, T extra); - -public bool matchesPath(Gee.List<string> path) -{ - return pathEquals(path, subfile()); -} - -public void onEntriesUpdate(Gee.List<string> path, Gee.Collection<Decsync.Entry> entries, T extra) -{ - foreach (var entry in entries) { - onSubfileEntryUpdate(entry, extra); + + public abstract Gee.List<string> subfile(); + public abstract void onSubfileEntryUpdate(Decsync.Entry entry, T extra); + + public bool matchesPath(Gee.List<string> path) + { + return pathEquals(path, subfile()); + } + + public void onEntriesUpdate(Gee.List<string> path, Gee.Collection<Decsync.Entry> entries, T extra) + { + foreach (var entry in entries) { + onSubfileEntryUpdate(entry, extra); + } } -} } private bool pathEquals(Gee.List<string> path1, Gee.List<string> path2) diff --git a/plugins/backend/decsync/libdecsync/src/Utils.vala b/plugins/backend/decsync/libdecsync/src/Utils.vala index 603d5fca..1946b999 100644 --- a/plugins/backend/decsync/libdecsync/src/Utils.vala +++ b/plugins/backend/decsync/libdecsync/src/Utils.vala @@ -1,20 +1,20 @@ /** - * libdecsync-vala - Utils.vala - * - * Copyright (C) 2018 Aldo Gunsing - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see <http://www.gnu.org/licenses/>. - */ +* libdecsync-vala - Utils.vala +* +* Copyright (C) 2018 Aldo Gunsing +* +* This library is free software; you can redistribute it and/or modify it +* under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation. +* +* This library is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +* for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ public Gee.List<string> toList(string[] input) { @@ -24,7 +24,7 @@ public Gee.List<string> toList(string[] input) public Gee.Predicate<Json.Node> stringEquals(string input) { return json => { - return json.get_string() == input; + return json.get_string() == input; }; } @@ -66,6 +66,6 @@ public Gee.MultiMap<K, V> groupBy<T, K, V>(Gee.Collection<T> inputs, Gee.MapFunc var value = f == null ? input : f(input); resultsMap.@set(key, value); } - + return resultsMap; } diff --git a/plugins/backend/demo/demoInterface.vala b/plugins/backend/demo/demoInterface.vala index b96372d8..40554c1a 100644 --- a/plugins/backend/demo/demoInterface.vala +++ b/plugins/backend/demo/demoInterface.vala @@ -5,592 +5,592 @@ //-------------------------------------------------------------------------------------- public class FeedReader.demoInterface : Peas.ExtensionBase, FeedServerInterface { - -//-------------------------------------------------------------------------------------- -// This method gets executed right after the plugin is loaded. Do everything -// you need to set up the plugin here. -//-------------------------------------------------------------------------------------- -public void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) -{ - -} - -//-------------------------------------------------------------------------------------- -// Return the the website/homepage of the project -//-------------------------------------------------------------------------------------- -public string getWebsite() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Return an unique id for the backend. Basically a short form of the name: -// Tiny Tiny RSS -> "ttrss" -// Local Backend -> "local" -//-------------------------------------------------------------------------------------- -public string getID() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Return flags describing the type of Service -// - LOCAL -// - HOSTED -// - SELF_HOSTED -// - FREE_SOFTWARE -// - PROPRIETARY -// - FREE -// - PAID_PREMIUM -// - PAID -//-------------------------------------------------------------------------------------- -public BackendFlags getFlags() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Return the login UI inside a Gtk.Box (username- and password-entries) -// Return 'null' if use web-login -//-------------------------------------------------------------------------------------- -public Gtk.Box? getWidget() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Return the name of the service-icon (non-symbolic). -//-------------------------------------------------------------------------------------- -public string iconName() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Return the name of the service as displayed to the user -//-------------------------------------------------------------------------------------- -public string serviceName() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Return wheather the plugin needs a webview to log in via oauth. -//-------------------------------------------------------------------------------------- -public bool needWebLogin() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Only important for self-hosted services. -// If the server is secured by htaccess and a second username and password -// is required, show the UI to enter those in this methode. -// If htaccess won't be needed do nothing here. -//-------------------------------------------------------------------------------------- -public void showHtAccess() -{ - -} - -//-------------------------------------------------------------------------------------- -// Methode gets executed before logging in. Write all the data gathered -// into gsettings (password, username, access-key). -//-------------------------------------------------------------------------------------- -public void writeData() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Do stuff after a successful login -//-------------------------------------------------------------------------------------- -public async void postLoginAction() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Only needed if "needWebLogin()" retruned true. Return URL that should be -// loaded to log in via website. -//-------------------------------------------------------------------------------------- -public string buildLoginURL() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Extract access-key from redirect-URL from webview after loggin in with -// the webview. -// Return "true" if extracted sucessfuly, "false" otherwise. -//-------------------------------------------------------------------------------------- -public bool extractCode(string redirectURL) -{ - -} - - -//-------------------------------------------------------------------------------------- -// Does the service you are implementing support tags? -// If so return "true", otherwise return "false". -//-------------------------------------------------------------------------------------- -public bool supportTags() -{ - -} - - -//-------------------------------------------------------------------------------------- -// If the daemon should to an initial sync after logging in. -// For all online services: true -// Only for local backend: false -//-------------------------------------------------------------------------------------- -public bool doInitSync() -{ - -} - - -//-------------------------------------------------------------------------------------- -// What is the symbolic icon-name of the service-logo? -// Return a string with the name, not the complete path. -// For example: "feed-service-demo-symbolic" -//-------------------------------------------------------------------------------------- -public string symbolicIcon() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Return a name the account of the user can be identified with. -// This can be the real name of the user, the email-address -// or any other personal information that identifies the account. -//-------------------------------------------------------------------------------------- -public string accountName() -{ - -} - - -//-------------------------------------------------------------------------------------- -// If the service can be self-hosted or has multiple providers -// you can return the URL of the server here. Preferably without "http://www." -//-------------------------------------------------------------------------------------- -public string getServerURL() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Many services have different ways of telling if a feed is uncategorized. -// OwnCloud-News and Tiny Tiny RSS use the id "0", while feedly and InoReader -// use an empty string (""). -// Return what this service uses to indicate that the feed does not belong -// to any category. -//-------------------------------------------------------------------------------------- -public string uncategorizedID() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Sone services have special categories that should not be visible when empty -// e.g. feedly has a category called "Must Read". -// Argument: ID of a category -// Return: wheather the category should be visible when empty -//-------------------------------------------------------------------------------------- -public bool hideCategoryWhenEmpty(string catID) -{ - -} - -//-------------------------------------------------------------------------------------- -// Does the service support categories at all? (feedbin is weird :P) -//-------------------------------------------------------------------------------------- -public bool supportCategories() -{ - -} - -//-------------------------------------------------------------------------------------- -// Does the service support add/remove/rename of categories and feeds? -//-------------------------------------------------------------------------------------- -public bool supportFeedManipulation() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Does the service allow categories as children of other categories? -// If so return "true", otherwise return "false". -//-------------------------------------------------------------------------------------- -public bool supportMultiLevelCategories() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Can one feed be part of more than one category? -// If so return "true", otherwise return "false". -//-------------------------------------------------------------------------------------- -public bool supportMultiCategoriesPerFeed() -{ - -} - -public bool syncFeedsAndCategories() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Does changing the name of a tag also change it's ID? -// InoReader tagID's for example look like this: -// "user/1005921515/label/tagName" -// So if the name changes the ID changes accordingly. This needs special treatment. -// Return "true" if this is the case, otherwise return "false". -//-------------------------------------------------------------------------------------- -public bool tagIDaffectedByNameChange() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Delete all passwords, keys and user-information. -// Do not delete feeds or articles from the data-base. -//-------------------------------------------------------------------------------------- -public void resetAccount() -{ - -} - - -//-------------------------------------------------------------------------------------- -// State wheater the service syncs articles based on a maximum count -// or uses something else (OwnCloud uses the last synced articleID) -//-------------------------------------------------------------------------------------- -public bool useMaxArticles() -{ - -} - -//-------------------------------------------------------------------------------------- -// Log in to the account of the service. If there is no need or API to sign in, -// check all passwords or keys and make sure the service is reachable and works. -// Possible return values are: -// - SUCCESS -// - MISSING_USER -// - MISSING_PASSWD -// - MISSING_URL -// - ALL_EMPTY -// - UNKNOWN_ERROR -// - FIRST_TRY -// - NO_BACKEND -// - WRONG_LOGIN -// - NO_CONNECTION -// - NO_API_ACCESS -// - UNAUTHORIZED -// - CA_ERROR -// - PLUGIN_NEEDED -//-------------------------------------------------------------------------------------- -public LoginResponse login() -{ - -} - - -//-------------------------------------------------------------------------------------- -// If it is possible to log out of the account of the service, do so here. -// If not, do nothing and return "true". -//-------------------------------------------------------------------------------------- -public bool logout() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Check if the service is reachable. -// You can use the method Utils.ping() if the service doesn't provide anything. -//-------------------------------------------------------------------------------------- -public bool serverAvailable() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Method to set the state of articles to read or unread -// "articleIDs": comma separated string of articleIDs e.g. "id1,id2,id3" -// "read": the state to apply. ArticleStatus.READ or ArticleStatus.UNREAD -//-------------------------------------------------------------------------------------- -public void setArticleIsRead(string articleIDs, ArticleStatus read) -{ - -} - - -//-------------------------------------------------------------------------------------- -// Method to set the state of articles to marked or unmarked -// "articleID": single articleID -// "read": the state to apply. ArticleStatus.MARKED or ArticleStatus.UNMARKED -//-------------------------------------------------------------------------------------- -public void setArticleIsMarked(string articleID, ArticleStatus marked) -{ - -} - -//-------------------------------------------------------------------------------------- -// Should setArticleIsRead always be used instead of setFeedRead, setCategoryRead or -// markAllItemsRead? -// By using IDs as identifier, the articles are known, but it may be less efficient. -// If "true", the methods setFeedRead/setCategoryRead/markAllItemsRead never get called. -//-------------------------------------------------------------------------------------- -public bool alwaysSetReadByID() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Mark all articles of the feed as read -//-------------------------------------------------------------------------------------- -public void setFeedRead(string feedID) -{ - -} - - -//-------------------------------------------------------------------------------------- -// Mark all articles of the feeds that are part of the category as read -//-------------------------------------------------------------------------------------- -public void setCategoryRead(string catID) -{ - -} - - -//-------------------------------------------------------------------------------------- -// Mark ALL articles as read -//-------------------------------------------------------------------------------------- -public void markAllItemsRead() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Add an existing tag to the article -//-------------------------------------------------------------------------------------- -public void tagArticle(string articleID, string tagID) -{ - -} - - -//-------------------------------------------------------------------------------------- -// Remove an existing tag from the article -//-------------------------------------------------------------------------------------- -public void removeArticleTag(string articleID, string tagID) -{ - -} - - -//-------------------------------------------------------------------------------------- -// Create a new tag with the title of "caption" and return the id of the -// newly added tag. -// Hint: some services don't have API to create tags, but instead create them -// on the fly when tagging articles. In this case just compose the tagID -// following the schema tha service uses and return it. -//-------------------------------------------------------------------------------------- -public string createTag(string caption) -{ - -} - - -//-------------------------------------------------------------------------------------- -// Delete a tag completely -//-------------------------------------------------------------------------------------- -public void deleteTag(string tagID) -{ - -} - - -//-------------------------------------------------------------------------------------- -// Rename the tag with the id "tagID" to the new name "title" -//-------------------------------------------------------------------------------------- -public void renameTag(string tagID, string title) -{ - -} - - -//-------------------------------------------------------------------------------------- -// Subscribe to the URL "feedURL" -// "catID": the category the feed should be placed into, "null" otherwise -// "newCatName": the name of a new category the feed should be put in, "null" otherwise -//-------------------------------------------------------------------------------------- -public bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) -{ - -} - - -//-------------------------------------------------------------------------------------- -// Remove the feed with the id "feedID" completely -//-------------------------------------------------------------------------------------- -public void removeFeed(string feedID) -{ - -} - - -//-------------------------------------------------------------------------------------- -// Rename the feed with the id "feedID" to "title" -//-------------------------------------------------------------------------------------- -public void renameFeed(string feedID, string title) -{ - -} - - -//-------------------------------------------------------------------------------------- -// Move the feed with the id "feedID" from its current category -// to any other category. "currentCatID" is only needed if the -// feed can be part of multiple categories at once. -//-------------------------------------------------------------------------------------- -public void moveFeed(string feedID, string newCatID, string? currentCatID) -{ - -} - - -//-------------------------------------------------------------------------------------- -// Create a new category -// "title": title of the new category -// "parentID": only needed if multi-level-categories are supported -// Hint: some services don't have API to create categories, but instead create them -// on the fly when movin feeds over to them. In this case just compose the categoryID -// following the schema tha service uses and return it. -//-------------------------------------------------------------------------------------- -public string createCategory(string title, string? parentID) -{ - -} - - -//-------------------------------------------------------------------------------------- -// Rename the category with the id "catID" to "title" -//-------------------------------------------------------------------------------------- -public void renameCategory(string catID, string title) -{ - -} - - -//-------------------------------------------------------------------------------------- -// Move the category with the id "catID" into another category -// with the id "newParentID" -// This method is only used if multi-level-categories are supported -//-------------------------------------------------------------------------------------- -public void moveCategory(string catID, string newParentID) -{ - -} - - -//-------------------------------------------------------------------------------------- -// Delete the category with the id "catID" -//-------------------------------------------------------------------------------------- -public void deleteCategory(string catID) -{ - -} - - -//-------------------------------------------------------------------------------------- -// Rename the feed with the id "feedID" from the category with the id "catID" -// Don't delete the feed entirely, just remove it from the category. -// Only useful if feed can be part of multiple categories. -//-------------------------------------------------------------------------------------- -public void removeCatFromFeed(string feedID, string catID) -{ - -} - - -//-------------------------------------------------------------------------------------- -// Import the content of "opml" -// If the service doesn't provide API to import OPML you can use the -// OPMLparser-class -//-------------------------------------------------------------------------------------- -public void importOPML(string opml) -{ - -} - - -//-------------------------------------------------------------------------------------- -// Get all feeds, categories and tags from the service -// Fill up the emtpy LinkedList's that are provided with instances of the -// model-classes category, feed and article -//-------------------------------------------------------------------------------------- -public bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) -{ - -} - - -//-------------------------------------------------------------------------------------- -// Return the total count of unread articles on the server -//-------------------------------------------------------------------------------------- -public int getUnreadCount() -{ - -} - - -//-------------------------------------------------------------------------------------- -// Get the requested articles and write them to the data-base -// -// "count": the number of articles to get -// "whatToGet": the kind of articles to get (all/unread/marked/etc.) -// "since": how far back to sync articles (null = no limit) -// "feedID": get only articles of a secific feed or tag -// "isTagID": false if "feedID" is a feed-ID, true if "feedID" is a tag-ID -// -// It is recommended after getting the articles from the server to use the signal -// "writeArticles(Gee.List<Article> articles)" -// to automatically process them in the content-grabber, write them to the -// data-base and send all the signals to the UI to update accordingly. -// But if the API suggests a different approach you can everything on your -// own (see ttrss-backend). -//-------------------------------------------------------------------------------------- -public void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) -{ - -} - + + //-------------------------------------------------------------------------------------- + // This method gets executed right after the plugin is loaded. Do everything + // you need to set up the plugin here. + //-------------------------------------------------------------------------------------- + public void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) + { + + } + + //-------------------------------------------------------------------------------------- + // Return the the website/homepage of the project + //-------------------------------------------------------------------------------------- + public string getWebsite() + { + + } + + + //-------------------------------------------------------------------------------------- + // Return an unique id for the backend. Basically a short form of the name: + // Tiny Tiny RSS -> "ttrss" + // Local Backend -> "local" + //-------------------------------------------------------------------------------------- + public string getID() + { + + } + + + //-------------------------------------------------------------------------------------- + // Return flags describing the type of Service + // - LOCAL + // - HOSTED + // - SELF_HOSTED + // - FREE_SOFTWARE + // - PROPRIETARY + // - FREE + // - PAID_PREMIUM + // - PAID + //-------------------------------------------------------------------------------------- + public BackendFlags getFlags() + { + + } + + + //-------------------------------------------------------------------------------------- + // Return the login UI inside a Gtk.Box (username- and password-entries) + // Return 'null' if use web-login + //-------------------------------------------------------------------------------------- + public Gtk.Box? getWidget() + { + + } + + + //-------------------------------------------------------------------------------------- + // Return the name of the service-icon (non-symbolic). + //-------------------------------------------------------------------------------------- + public string iconName() + { + + } + + + //-------------------------------------------------------------------------------------- + // Return the name of the service as displayed to the user + //-------------------------------------------------------------------------------------- + public string serviceName() + { + + } + + + //-------------------------------------------------------------------------------------- + // Return wheather the plugin needs a webview to log in via oauth. + //-------------------------------------------------------------------------------------- + public bool needWebLogin() + { + + } + + + //-------------------------------------------------------------------------------------- + // Only important for self-hosted services. + // If the server is secured by htaccess and a second username and password + // is required, show the UI to enter those in this methode. + // If htaccess won't be needed do nothing here. + //-------------------------------------------------------------------------------------- + public void showHtAccess() + { + + } + + //-------------------------------------------------------------------------------------- + // Methode gets executed before logging in. Write all the data gathered + // into gsettings (password, username, access-key). + //-------------------------------------------------------------------------------------- + public void writeData() + { + + } + + + //-------------------------------------------------------------------------------------- + // Do stuff after a successful login + //-------------------------------------------------------------------------------------- + public async void postLoginAction() + { + + } + + + //-------------------------------------------------------------------------------------- + // Only needed if "needWebLogin()" retruned true. Return URL that should be + // loaded to log in via website. + //-------------------------------------------------------------------------------------- + public string buildLoginURL() + { + + } + + + //-------------------------------------------------------------------------------------- + // Extract access-key from redirect-URL from webview after loggin in with + // the webview. + // Return "true" if extracted sucessfuly, "false" otherwise. + //-------------------------------------------------------------------------------------- + public bool extractCode(string redirectURL) + { + + } + + + //-------------------------------------------------------------------------------------- + // Does the service you are implementing support tags? + // If so return "true", otherwise return "false". + //-------------------------------------------------------------------------------------- + public bool supportTags() + { + + } + + + //-------------------------------------------------------------------------------------- + // If the daemon should to an initial sync after logging in. + // For all online services: true + // Only for local backend: false + //-------------------------------------------------------------------------------------- + public bool doInitSync() + { + + } + + + //-------------------------------------------------------------------------------------- + // What is the symbolic icon-name of the service-logo? + // Return a string with the name, not the complete path. + // For example: "feed-service-demo-symbolic" + //-------------------------------------------------------------------------------------- + public string symbolicIcon() + { + + } + + + //-------------------------------------------------------------------------------------- + // Return a name the account of the user can be identified with. + // This can be the real name of the user, the email-address + // or any other personal information that identifies the account. + //-------------------------------------------------------------------------------------- + public string accountName() + { + + } + + + //-------------------------------------------------------------------------------------- + // If the service can be self-hosted or has multiple providers + // you can return the URL of the server here. Preferably without "http://www." + //-------------------------------------------------------------------------------------- + public string getServerURL() + { + + } + + + //-------------------------------------------------------------------------------------- + // Many services have different ways of telling if a feed is uncategorized. + // OwnCloud-News and Tiny Tiny RSS use the id "0", while feedly and InoReader + // use an empty string (""). + // Return what this service uses to indicate that the feed does not belong + // to any category. + //-------------------------------------------------------------------------------------- + public string uncategorizedID() + { + + } + + + //-------------------------------------------------------------------------------------- + // Sone services have special categories that should not be visible when empty + // e.g. feedly has a category called "Must Read". + // Argument: ID of a category + // Return: wheather the category should be visible when empty + //-------------------------------------------------------------------------------------- + public bool hideCategoryWhenEmpty(string catID) + { + + } + + //-------------------------------------------------------------------------------------- + // Does the service support categories at all? (feedbin is weird :P) + //-------------------------------------------------------------------------------------- + public bool supportCategories() + { + + } + + //-------------------------------------------------------------------------------------- + // Does the service support add/remove/rename of categories and feeds? + //-------------------------------------------------------------------------------------- + public bool supportFeedManipulation() + { + + } + + + //-------------------------------------------------------------------------------------- + // Does the service allow categories as children of other categories? + // If so return "true", otherwise return "false". + //-------------------------------------------------------------------------------------- + public bool supportMultiLevelCategories() + { + + } + + + //-------------------------------------------------------------------------------------- + // Can one feed be part of more than one category? + // If so return "true", otherwise return "false". + //-------------------------------------------------------------------------------------- + public bool supportMultiCategoriesPerFeed() + { + + } + + public bool syncFeedsAndCategories() + { + + } + + + //-------------------------------------------------------------------------------------- + // Does changing the name of a tag also change it's ID? + // InoReader tagID's for example look like this: + // "user/1005921515/label/tagName" + // So if the name changes the ID changes accordingly. This needs special treatment. + // Return "true" if this is the case, otherwise return "false". + //-------------------------------------------------------------------------------------- + public bool tagIDaffectedByNameChange() + { + + } + + + //-------------------------------------------------------------------------------------- + // Delete all passwords, keys and user-information. + // Do not delete feeds or articles from the data-base. + //-------------------------------------------------------------------------------------- + public void resetAccount() + { + + } + + + //-------------------------------------------------------------------------------------- + // State wheater the service syncs articles based on a maximum count + // or uses something else (OwnCloud uses the last synced articleID) + //-------------------------------------------------------------------------------------- + public bool useMaxArticles() + { + + } + + //-------------------------------------------------------------------------------------- + // Log in to the account of the service. If there is no need or API to sign in, + // check all passwords or keys and make sure the service is reachable and works. + // Possible return values are: + // - SUCCESS + // - MISSING_USER + // - MISSING_PASSWD + // - MISSING_URL + // - ALL_EMPTY + // - UNKNOWN_ERROR + // - FIRST_TRY + // - NO_BACKEND + // - WRONG_LOGIN + // - NO_CONNECTION + // - NO_API_ACCESS + // - UNAUTHORIZED + // - CA_ERROR + // - PLUGIN_NEEDED + //-------------------------------------------------------------------------------------- + public LoginResponse login() + { + + } + + + //-------------------------------------------------------------------------------------- + // If it is possible to log out of the account of the service, do so here. + // If not, do nothing and return "true". + //-------------------------------------------------------------------------------------- + public bool logout() + { + + } + + + //-------------------------------------------------------------------------------------- + // Check if the service is reachable. + // You can use the method Utils.ping() if the service doesn't provide anything. + //-------------------------------------------------------------------------------------- + public bool serverAvailable() + { + + } + + + //-------------------------------------------------------------------------------------- + // Method to set the state of articles to read or unread + // "articleIDs": comma separated string of articleIDs e.g. "id1,id2,id3" + // "read": the state to apply. ArticleStatus.READ or ArticleStatus.UNREAD + //-------------------------------------------------------------------------------------- + public void setArticleIsRead(string articleIDs, ArticleStatus read) + { + + } + + + //-------------------------------------------------------------------------------------- + // Method to set the state of articles to marked or unmarked + // "articleID": single articleID + // "read": the state to apply. ArticleStatus.MARKED or ArticleStatus.UNMARKED + //-------------------------------------------------------------------------------------- + public void setArticleIsMarked(string articleID, ArticleStatus marked) + { + + } + + //-------------------------------------------------------------------------------------- + // Should setArticleIsRead always be used instead of setFeedRead, setCategoryRead or + // markAllItemsRead? + // By using IDs as identifier, the articles are known, but it may be less efficient. + // If "true", the methods setFeedRead/setCategoryRead/markAllItemsRead never get called. + //-------------------------------------------------------------------------------------- + public bool alwaysSetReadByID() + { + + } + + + //-------------------------------------------------------------------------------------- + // Mark all articles of the feed as read + //-------------------------------------------------------------------------------------- + public void setFeedRead(string feedID) + { + + } + + + //-------------------------------------------------------------------------------------- + // Mark all articles of the feeds that are part of the category as read + //-------------------------------------------------------------------------------------- + public void setCategoryRead(string catID) + { + + } + + + //-------------------------------------------------------------------------------------- + // Mark ALL articles as read + //-------------------------------------------------------------------------------------- + public void markAllItemsRead() + { + + } + + + //-------------------------------------------------------------------------------------- + // Add an existing tag to the article + //-------------------------------------------------------------------------------------- + public void tagArticle(string articleID, string tagID) + { + + } + + + //-------------------------------------------------------------------------------------- + // Remove an existing tag from the article + //-------------------------------------------------------------------------------------- + public void removeArticleTag(string articleID, string tagID) + { + + } + + + //-------------------------------------------------------------------------------------- + // Create a new tag with the title of "caption" and return the id of the + // newly added tag. + // Hint: some services don't have API to create tags, but instead create them + // on the fly when tagging articles. In this case just compose the tagID + // following the schema tha service uses and return it. + //-------------------------------------------------------------------------------------- + public string createTag(string caption) + { + + } + + + //-------------------------------------------------------------------------------------- + // Delete a tag completely + //-------------------------------------------------------------------------------------- + public void deleteTag(string tagID) + { + + } + + + //-------------------------------------------------------------------------------------- + // Rename the tag with the id "tagID" to the new name "title" + //-------------------------------------------------------------------------------------- + public void renameTag(string tagID, string title) + { + + } + + + //-------------------------------------------------------------------------------------- + // Subscribe to the URL "feedURL" + // "catID": the category the feed should be placed into, "null" otherwise + // "newCatName": the name of a new category the feed should be put in, "null" otherwise + //-------------------------------------------------------------------------------------- + public bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) + { + + } + + + //-------------------------------------------------------------------------------------- + // Remove the feed with the id "feedID" completely + //-------------------------------------------------------------------------------------- + public void removeFeed(string feedID) + { + + } + + + //-------------------------------------------------------------------------------------- + // Rename the feed with the id "feedID" to "title" + //-------------------------------------------------------------------------------------- + public void renameFeed(string feedID, string title) + { + + } + + + //-------------------------------------------------------------------------------------- + // Move the feed with the id "feedID" from its current category + // to any other category. "currentCatID" is only needed if the + // feed can be part of multiple categories at once. + //-------------------------------------------------------------------------------------- + public void moveFeed(string feedID, string newCatID, string? currentCatID) + { + + } + + + //-------------------------------------------------------------------------------------- + // Create a new category + // "title": title of the new category + // "parentID": only needed if multi-level-categories are supported + // Hint: some services don't have API to create categories, but instead create them + // on the fly when movin feeds over to them. In this case just compose the categoryID + // following the schema tha service uses and return it. + //-------------------------------------------------------------------------------------- + public string createCategory(string title, string? parentID) + { + + } + + + //-------------------------------------------------------------------------------------- + // Rename the category with the id "catID" to "title" + //-------------------------------------------------------------------------------------- + public void renameCategory(string catID, string title) + { + + } + + + //-------------------------------------------------------------------------------------- + // Move the category with the id "catID" into another category + // with the id "newParentID" + // This method is only used if multi-level-categories are supported + //-------------------------------------------------------------------------------------- + public void moveCategory(string catID, string newParentID) + { + + } + + + //-------------------------------------------------------------------------------------- + // Delete the category with the id "catID" + //-------------------------------------------------------------------------------------- + public void deleteCategory(string catID) + { + + } + + + //-------------------------------------------------------------------------------------- + // Rename the feed with the id "feedID" from the category with the id "catID" + // Don't delete the feed entirely, just remove it from the category. + // Only useful if feed can be part of multiple categories. + //-------------------------------------------------------------------------------------- + public void removeCatFromFeed(string feedID, string catID) + { + + } + + + //-------------------------------------------------------------------------------------- + // Import the content of "opml" + // If the service doesn't provide API to import OPML you can use the + // OPMLparser-class + //-------------------------------------------------------------------------------------- + public void importOPML(string opml) + { + + } + + + //-------------------------------------------------------------------------------------- + // Get all feeds, categories and tags from the service + // Fill up the emtpy LinkedList's that are provided with instances of the + // model-classes category, feed and article + //-------------------------------------------------------------------------------------- + public bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) + { + + } + + + //-------------------------------------------------------------------------------------- + // Return the total count of unread articles on the server + //-------------------------------------------------------------------------------------- + public int getUnreadCount() + { + + } + + + //-------------------------------------------------------------------------------------- + // Get the requested articles and write them to the data-base + // + // "count": the number of articles to get + // "whatToGet": the kind of articles to get (all/unread/marked/etc.) + // "since": how far back to sync articles (null = no limit) + // "feedID": get only articles of a secific feed or tag + // "isTagID": false if "feedID" is a feed-ID, true if "feedID" is a tag-ID + // + // It is recommended after getting the articles from the server to use the signal + // "writeArticles(Gee.List<Article> articles)" + // to automatically process them in the content-grabber, write them to the + // data-base and send all the signals to the UI to update accordingly. + // But if the API suggests a different approach you can everything on your + // own (see ttrss-backend). + //-------------------------------------------------------------------------------------- + public void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) + { + + } + } diff --git a/plugins/backend/feedbin/TestFeedbin.vala b/plugins/backend/feedbin/TestFeedbin.vala index 87f5e83f..5ff779ca 100644 --- a/plugins/backend/feedbin/TestFeedbin.vala +++ b/plugins/backend/feedbin/TestFeedbin.vala @@ -24,201 +24,201 @@ void add_login_tests(string host) { return; } - + // Stick a random number at the end of Feed URL's to ensure that they're // unique, even if we run two tests against the same account uint nonce = Random.next_int(); - + Test.add_data_func ("/feedbinapi/login", () => { - + var api = new FeedbinAPI(username, password, null, host); assert(api.login()); - + api = new FeedbinAPI("wrong", "password", null, host); assert(!api.login()); - + api.username = username; assert(!api.login()); - + api.password = password; assert(api.login()); }); - + Test.add_data_func ("/feedbinapi/subscription", () => { if(username == null || password == null) { - Test.skip(@"Need $user_env and $password_env set to run Feedbin tests"); - return; + Test.skip(@"Need $user_env and $password_env set to run Feedbin tests"); + return; } - + var api = new FeedbinAPI(username, password, null, host); - + var url = "https://www.brendanlong.com/feeds/all.atom.xml?feedreader-test-subscribe-$(nonce)"; delete_subscription(api, url); - + var subscription = api.add_subscription(url); assert(subscription.id != 0); - + { - var got_subscription = api.get_subscription(subscription.id); - assert(got_subscription.id == subscription.id); + var got_subscription = api.get_subscription(subscription.id); + assert(got_subscription.id == subscription.id); } - + bool found_subscription = false; foreach(var got_subscription in api.get_subscriptions()) { - if(got_subscription.id == subscription.id) - { - assert(got_subscription.feed_id == subscription.feed_id); - assert(got_subscription.feed_url == subscription.feed_url); - assert(got_subscription.site_url == subscription.site_url); - assert(got_subscription.title == subscription.title); - found_subscription = true; + if(got_subscription.id == subscription.id) + { + assert(got_subscription.feed_id == subscription.feed_id); + assert(got_subscription.feed_url == subscription.feed_url); + assert(got_subscription.site_url == subscription.site_url); + assert(got_subscription.title == subscription.title); + found_subscription = true; } } assert(found_subscription); - + string title = "Rename test"; api.rename_subscription(subscription.id, title); var renamed_subscription = api.get_subscription(subscription.id); assert(renamed_subscription.title == title); - + api.delete_subscription(subscription.id); foreach(var got_subscription in api.get_subscriptions()) { - assert(got_subscription.id != subscription.id); - assert(got_subscription.feed_url != url); + assert(got_subscription.id != subscription.id); + assert(got_subscription.feed_url != url); } }); - + Test.add_data_func ("/feedbinapi/taggings", () => { if(username == null || password == null) { - Test.skip(@"Need $user_env and $password_env set to run Feedbin tests"); - return; + Test.skip(@"Need $user_env and $password_env set to run Feedbin tests"); + return; } - + var api = new FeedbinAPI(username, password, null, host); - + var url = @"https://www.brendanlong.com/feeds/all.atom.xml?feedreader-test-taggings-$(nonce)"; delete_subscription(api, url); - + var subscription = api.add_subscription(url); - + // The subscription is new so it shouldn't have any taggings var taggings = api.get_taggings(); foreach(var tagging in taggings) { - assert(tagging.feed_id != subscription.feed_id); + assert(tagging.feed_id != subscription.feed_id); } - + string category = "Taggings Test"; api.add_tagging(subscription.feed_id, category); - + // Check taggings int64? tagging_id = null; foreach(var tagging in api.get_taggings()) { - if(tagging.feed_id == subscription.feed_id) - { - assert(tagging.name == category); - tagging_id = tagging.id; - break; + if(tagging.feed_id == subscription.feed_id) + { + assert(tagging.name == category); + tagging_id = tagging.id; + break; } } assert(tagging_id != null); - + // Delete the tag and verify that it's gone api.delete_tagging(tagging_id); foreach(var tagging in api.get_taggings()) { - assert(tagging.feed_id != subscription.feed_id); + assert(tagging.feed_id != subscription.feed_id); } - + // cleanup api.delete_subscription(subscription.id); }); - + Test.add_data_func ("/feedbinapi/entries", () => { if(username == null || password == null) { - Test.skip(@"Need $user_env and $password_env set to run Feedbin tests"); - return; + Test.skip(@"Need $user_env and $password_env set to run Feedbin tests"); + return; } - + var api = new FeedbinAPI(username, password, null, host); - + // Note: This one shouldn't be deleted or recreated, since we want the entries to be available var url = "https://www.brendanlong.com/feeds/all.atom.xml?feed-reader-test-entries"; - + var subscription = api.add_subscription(url); - + /* FIXME: Figure out why this next line is failing - var entries = api.get_entries(1, false, null, subscription.feed_id); - foreach(var entry in entries) - { - assert(entry.feed_id == subscription.feed_id); - } - - assert(entries.size > 0); - int i = Random.int_range(0, entries.size); - var entry = entries.to_array()[i]; - var entry_ids = new Gee.ArrayList<int64?>(); - entry_ids.add(entry.id); - - // read status - api.set_entries_read(entry_ids, true); - var unread_entries = api.get_unread_entries(); - assert(!unread_entries.contains(entry.id)); - - api.set_entries_read(entry_ids, false); - unread_entries = api.get_unread_entries(); - assert(unread_entries.contains(entry.id)); - - api.set_entries_read(entry_ids, true); - unread_entries = api.get_unread_entries(); - assert(!unread_entries.contains(entry.id)); - - // starred status - api.set_entries_starred(entry_ids, true); - var starred_entries = api.get_starred_entries(); - assert(starred_entries.contains(entry.id)); - - api.set_entries_starred(entry_ids, false); - starred_entries = api.get_starred_entries(); - assert(!starred_entries.contains(entry.id)); - - api.set_entries_starred(entry_ids, true); - starred_entries = api.get_starred_entries(); - assert(starred_entries.contains(entry.id)); - */ + var entries = api.get_entries(1, false, null, subscription.feed_id); + foreach(var entry in entries) + { + assert(entry.feed_id == subscription.feed_id); + } + + assert(entries.size > 0); + int i = Random.int_range(0, entries.size); + var entry = entries.to_array()[i]; + var entry_ids = new Gee.ArrayList<int64?>(); + entry_ids.add(entry.id); + + // read status + api.set_entries_read(entry_ids, true); + var unread_entries = api.get_unread_entries(); + assert(!unread_entries.contains(entry.id)); + + api.set_entries_read(entry_ids, false); + unread_entries = api.get_unread_entries(); + assert(unread_entries.contains(entry.id)); + + api.set_entries_read(entry_ids, true); + unread_entries = api.get_unread_entries(); + assert(!unread_entries.contains(entry.id)); + + // starred status + api.set_entries_starred(entry_ids, true); + var starred_entries = api.get_starred_entries(); + assert(starred_entries.contains(entry.id)); + + api.set_entries_starred(entry_ids, false); + starred_entries = api.get_starred_entries(); + assert(!starred_entries.contains(entry.id)); + + api.set_entries_starred(entry_ids, true); + starred_entries = api.get_starred_entries(); + assert(starred_entries.contains(entry.id)); + */ }); - + Test.add_data_func ("/feedbinapi/favicons", () => { if(username == null || password == null) { - Test.skip(@"Need $user_env and $password_env set to run Feedbin tests"); - return; + Test.skip(@"Need $user_env and $password_env set to run Feedbin tests"); + return; } - + var api = new FeedbinAPI(username, password, null, host); - + // Note: This one shouldn't be deleted or recreated, since we want the entries to be available var url = "https://www.brendanlong.com/feeds/all.atom.xml?feed-reader-test-favicons"; - + var subscription = api.add_subscription(url); var favicons = api.get_favicons(); bool found_favicon = false; foreach(var i in favicons.entries) { - if(i.key != "www.brendanlong.com") - { - continue; + if(i.key != "www.brendanlong.com") + { + continue; } - // Don't check the contents of the Favicon because Feedbin doesn't - // seem to neccessarily return the exact icon we gave it - found_favicon = true; - break; + // Don't check the contents of the Favicon because Feedbin doesn't + // seem to neccessarily return the exact icon we gave it + found_favicon = true; + break; } // FIXME: We don't download icons on the test server because favicon downloading // is handled by a different service @@ -229,26 +229,26 @@ void add_login_tests(string host) void main(string[] args) { Test.init(ref args); - + string? host = Environment.get_variable(host_env); if(host == null) { host = "https://api.feedbin.com"; } - + // Tests that don't need a login Test.add_data_func ("/feedbinapi/construct", () => { var api = new FeedbinAPI("user", "password", null, host); assert(api != null); }); - + Test.add_data_func ("/feedbinapi/bad login", () => { var api = new FeedbinAPI("user", "password", null, host); - + assert(!api.login()); }); - + add_login_tests(host); - + Test.run (); } diff --git a/plugins/backend/feedbin/feedbinAPI.vala b/plugins/backend/feedbin/feedbinAPI.vala index 27603b79..ecb94892 100644 --- a/plugins/backend/feedbin/feedbinAPI.vala +++ b/plugins/backend/feedbin/feedbinAPI.vala @@ -24,447 +24,447 @@ public errordomain FeedbinError { } public class FeedbinAPI : Object { -private const string BASE_URI_FORMAT = "%s/v2/"; - -private Soup.Session m_session; -private string m_base_uri; -public string username { get; set; } -public string password { get; set; } - -public FeedbinAPI(string username, string password, string? user_agent = null, string? host = "https://api.feedbin.com") -{ - this.username = username; - this.password = password; - m_base_uri = BASE_URI_FORMAT.printf(host); - m_session = new Soup.Session(); - - if(user_agent != null) + private const string BASE_URI_FORMAT = "%s/v2/"; + + private Soup.Session m_session; + private string m_base_uri; + public string username { get; set; } + public string password { get; set; } + + public FeedbinAPI(string username, string password, string? user_agent = null, string? host = "https://api.feedbin.com") { - m_session.user_agent = user_agent; - } - - m_session.authenticate.connect(authenticate); -} - -~FeedbinAPI() -{ - m_session.authenticate.disconnect(authenticate); -} - -private void authenticate(Soup.Message msg, Soup.Auth auth, bool retrying) -{ - if(!retrying) - { - auth.authenticate(this.username, this.password); + this.username = username; + this.password = password; + m_base_uri = BASE_URI_FORMAT.printf(host); + m_session = new Soup.Session(); + + if(user_agent != null) + { + m_session.user_agent = user_agent; + } + + m_session.authenticate.connect(authenticate); } -} - -private Soup.Message request(string method, string last_part, string? input = null) throws FeedbinError -requires (method == "DELETE" || method == "GET" || method == "POST") -requires (input == null || method != "GET") -ensures (result.status_code >= 200) -ensures (result.status_code < 400) -{ - var path = m_base_uri + last_part; - var message = new Soup.Message(method, path); - - if(method == "POST") + + ~FeedbinAPI() { - message.request_headers.append("Content-Type", "application/json; charset=utf-8"); + m_session.authenticate.disconnect(authenticate); } - - if(input != null) + + private void authenticate(Soup.Message msg, Soup.Auth auth, bool retrying) { - message.request_body.append_take(input.data); + if(!retrying) + { + auth.authenticate(this.username, this.password); + } } - - m_session.send_message(message); - var status = message.status_code; - if(status < 200 || status >= 400) + + private Soup.Message request(string method, string last_part, string? input = null) throws FeedbinError + requires (method == "DELETE" || method == "GET" || method == "POST") + requires (input == null || method != "GET") + ensures (result.status_code >= 200) + ensures (result.status_code < 400) { - switch(status) + var path = m_base_uri + last_part; + var message = new Soup.Message(method, path); + + if(method == "POST") + { + message.request_headers.append("Content-Type", "application/json; charset=utf-8"); + } + + if(input != null) { - case Soup.Status.CANT_RESOLVE: - case Soup.Status.CANT_RESOLVE_PROXY: - case Soup.Status.CANT_CONNECT: - case Soup.Status.CANT_CONNECT_PROXY: - throw new FeedbinError.NO_CONNECTION(@"Connection to $m_base_uri failed"); - case Soup.Status.UNAUTHORIZED: - throw new FeedbinError.NOT_AUTHORIZED(@"Not authorized to $method $path"); - case Soup.Status.NOT_FOUND: - throw new FeedbinError.NOT_FOUND(@"$method $path not found"); + message.request_body.append_take(input.data); } - string phrase = Soup.Status.get_phrase(status); - throw new FeedbinError.UNKNOWN_ERROR(@"Unexpected status $status ($phrase) for $method $path"); + + m_session.send_message(message); + var status = message.status_code; + if(status < 200 || status >= 400) + { + switch(status) + { + case Soup.Status.CANT_RESOLVE: + case Soup.Status.CANT_RESOLVE_PROXY: + case Soup.Status.CANT_CONNECT: + case Soup.Status.CANT_CONNECT_PROXY: + throw new FeedbinError.NO_CONNECTION(@"Connection to $m_base_uri failed"); + case Soup.Status.UNAUTHORIZED: + throw new FeedbinError.NOT_AUTHORIZED(@"Not authorized to $method $path"); + case Soup.Status.NOT_FOUND: + throw new FeedbinError.NOT_FOUND(@"$method $path not found"); + } + string phrase = Soup.Status.get_phrase(status); + throw new FeedbinError.UNKNOWN_ERROR(@"Unexpected status $status ($phrase) for $method $path"); + } + return message; } - return message; -} - -// TODO: Move to DateUtils -private static DateTime string_to_datetime(string s) throws FeedbinError -{ - var time = TimeVal(); - if(!time.from_iso8601(s)) + + // TODO: Move to DateUtils + private static DateTime string_to_datetime(string s) throws FeedbinError { - throw new FeedbinError.INVALID_FORMAT(@"Expected date but got $s"); + var time = TimeVal(); + if(!time.from_iso8601(s)) + { + throw new FeedbinError.INVALID_FORMAT(@"Expected date but got $s"); + } + return new DateTime.from_timeval_utc(time); } - return new DateTime.from_timeval_utc(time); -} - -// TODO: JSON utils? -private static DateTime get_datetime_member(Json.Object obj, string name) throws FeedbinError -requires (name != "") -{ - var s = obj.get_string_member(name); - return string_to_datetime(s); -} - -private Soup.Message post_request(string path, string input) throws FeedbinError -requires (input != "") -{ - return request("POST", path, input); -} - -private Soup.Message delete_request(string path) throws FeedbinError -{ - return request("DELETE", path); -} - -private Soup.Message get_request(string path) throws FeedbinError -{ - return request("GET", path); -} - -private static Json.Node parse_json(Soup.Message response) throws FeedbinError -{ - var method = response.method; - var uri = response.uri.to_string(false); - string content = (string)response.response_body.flatten().data; - if(content == null) + + // TODO: JSON utils? + private static DateTime get_datetime_member(Json.Object obj, string name) throws FeedbinError + requires (name != "") { - throw new FeedbinError.INVALID_FORMAT(@"$method $uri returned no content but expected JSON"); + var s = obj.get_string_member(name); + return string_to_datetime(s); } - - var parser = new Json.Parser(); - try + + private Soup.Message post_request(string path, string input) throws FeedbinError + requires (input != "") { - parser.load_from_data(content, -1); + return request("POST", path, input); } - catch (Error e) + + private Soup.Message delete_request(string path) throws FeedbinError { - throw new FeedbinError.INVALID_FORMAT(@"$method $uri returned invalid JSON: " + e.message + "\nContent is: $content"); + return request("DELETE", path); } - return parser.get_root(); -} - -private Json.Node get_json(string path) throws FeedbinError -requires (path != "") -{ - var response = get_request(path); - return parse_json(response); -} - -private Soup.Message post_json_object(string path, Json.Object obj) throws FeedbinError -{ - var root = new Json.Node(Json.NodeType.OBJECT); - root.set_object(obj); - - var gen = new Json.Generator(); - gen.set_root(root); - var data = gen.to_data(null); - - return post_request(path, data); -} - -public bool login() throws FeedbinError -{ - try + + private Soup.Message get_request(string path) throws FeedbinError { - var res = get_request("authentication.json"); - return res.status_code == Soup.Status.OK; + return request("GET", path); } - catch(FeedbinError.NOT_AUTHORIZED e) + + private static Json.Node parse_json(Soup.Message response) throws FeedbinError { - return false; + var method = response.method; + var uri = response.uri.to_string(false); + string content = (string)response.response_body.flatten().data; + if(content == null) + { + throw new FeedbinError.INVALID_FORMAT(@"$method $uri returned no content but expected JSON"); + } + + var parser = new Json.Parser(); + try + { + parser.load_from_data(content, -1); + } + catch (Error e) + { + throw new FeedbinError.INVALID_FORMAT(@"$method $uri returned invalid JSON: " + e.message + "\nContent is: $content"); + } + return parser.get_root(); } -} - -public struct Subscription { - int64 id; - DateTime created_at; - int64 feed_id; - string? title; - string? feed_url; - string? site_url; - - public Subscription.from_json(Json.Object object) throws FeedbinError + + private Json.Node get_json(string path) throws FeedbinError + requires (path != "") { - id = object.get_int_member("id"); - created_at = get_datetime_member(object, "created_at"); - feed_id = object.get_int_member("feed_id"); - title = object.get_string_member("title"); - feed_url = object.get_string_member("feed_url"); - site_url = object.get_string_member("site_url"); + var response = get_request(path); + return parse_json(response); } -} - -public Subscription get_subscription(int64 subscription_id) throws FeedbinError -{ - var root = get_json(@"subscriptions/$subscription_id.json"); - return Subscription.from_json(root.get_object()); -} - -public Gee.List<Subscription?> get_subscriptions() throws FeedbinError -ensures (!result.contains(null)) -{ - var root = get_json("subscriptions.json"); - var subscriptions = new Gee.ArrayList<Subscription?>(); - var array = root.get_array(); - for(var i = 0; i < array.get_length(); ++i) + + private Soup.Message post_json_object(string path, Json.Object obj) throws FeedbinError { - var node = array.get_object_element(i); - subscriptions.add(Subscription.from_json(node)); + var root = new Json.Node(Json.NodeType.OBJECT); + root.set_object(obj); + + var gen = new Json.Generator(); + gen.set_root(root); + var data = gen.to_data(null); + + return post_request(path, data); } - return subscriptions; -} - -public void delete_subscription(int64 subscription_id) throws FeedbinError -{ - delete_request(@"subscriptions/$subscription_id.json"); -} - -public Subscription? add_subscription(string url) throws FeedbinError -{ - Json.Object object = new Json.Object(); - object.set_string_member("feed_url", url); - - try + + public bool login() throws FeedbinError { - var response = post_json_object("subscriptions.json", object); - if(response.status_code == 300) + try { - throw new FeedbinError.MULTIPLE_CHOICES("Site $url has multiple feeds to subscribe to"); + var res = get_request("authentication.json"); + return res.status_code == Soup.Status.OK; + } + catch(FeedbinError.NOT_AUTHORIZED e) + { + return false; } - - var root = parse_json(response); - return Subscription.from_json(root.get_object()); } - catch (FeedbinError.NOT_FOUND e) - { - return null; + + public struct Subscription { + int64 id; + DateTime created_at; + int64 feed_id; + string? title; + string? feed_url; + string? site_url; + + public Subscription.from_json(Json.Object object) throws FeedbinError + { + id = object.get_int_member("id"); + created_at = get_datetime_member(object, "created_at"); + feed_id = object.get_int_member("feed_id"); + title = object.get_string_member("title"); + feed_url = object.get_string_member("feed_url"); + site_url = object.get_string_member("site_url"); + } } -} - -public void rename_subscription(int64 subscription_id, string title) throws FeedbinError -{ - Json.Object object = new Json.Object(); - object.set_string_member("title", title); - post_json_object(@"subscriptions/$subscription_id/update.json", object); -} - -public struct Tagging -{ - int64 id; - int64 feed_id; - string name; - - public Tagging.from_json(Json.Object object) + + public Subscription get_subscription(int64 subscription_id) throws FeedbinError { - id = object.get_int_member("id"); - feed_id = object.get_int_member("feed_id"); - name = object.get_string_member("name"); + var root = get_json(@"subscriptions/$subscription_id.json"); + return Subscription.from_json(root.get_object()); } -} - -public void add_tagging(int64 feed_id, string tag_name) throws FeedbinError -{ - Json.Object object = new Json.Object(); - object.set_int_member("feed_id", feed_id); - object.set_string_member("name", tag_name); - - post_json_object("taggings.json", object); - // TODO: Return id -} - -public void delete_tagging(int64 tagging_id) throws FeedbinError -{ - delete_request(@"taggings/$tagging_id.json"); -} - -public Gee.List<Tagging?> get_taggings() throws FeedbinError -ensures (!result.contains(null)) -{ - var root = get_json("taggings.json"); - var taggings = new Gee.ArrayList<Tagging?>(); - var array = root.get_array(); - for(var i = 0; i < array.get_length(); ++i) + + public Gee.List<Subscription?> get_subscriptions() throws FeedbinError + ensures (!result.contains(null)) { - var object = array.get_object_element(i); - taggings.add(Tagging.from_json(object)); + var root = get_json("subscriptions.json"); + var subscriptions = new Gee.ArrayList<Subscription?>(); + var array = root.get_array(); + for(var i = 0; i < array.get_length(); ++i) + { + var node = array.get_object_element(i); + subscriptions.add(Subscription.from_json(node)); + } + return subscriptions; } - return taggings; -} - -public struct Entry -{ - int64 id; - int64 feed_id; - string? title; - string? url; - string? author; - string? content; - string? summary; - DateTime published; - DateTime created_at; - - public Entry.from_json(Json.Object object) throws FeedbinError + + public void delete_subscription(int64 subscription_id) throws FeedbinError { - id = object.get_int_member("id"); - feed_id = object.get_int_member("feed_id"); - title = object.get_string_member("title"); - url = object.get_string_member("url"); - author = object.get_string_member("author"); - content = object.get_string_member("content"); - summary = object.get_string_member("summary"); - published = get_datetime_member(object, "published"); - created_at = get_datetime_member(object, "created_at"); + delete_request(@"subscriptions/$subscription_id.json"); } -} - -public Gee.List<Entry?> get_entries(int page, bool only_starred, DateTime? since, int64? feed_id = null) throws FeedbinError -requires (page >= 0) -ensures (!result.contains(null)) -{ - string starred = only_starred ? "true" : "false"; - string path = @"entries.json?per_page=100&page=$page&starred=$starred&include_enclosure=true"; - if(since != null) + + public Subscription? add_subscription(string url) throws FeedbinError { - var t = GLib.TimeVal(); - if(since.to_timeval(out t)) + Json.Object object = new Json.Object(); + object.set_string_member("feed_url", url); + + try { - path += "&since=" + t.to_iso8601(); + var response = post_json_object("subscriptions.json", object); + if(response.status_code == 300) + { + throw new FeedbinError.MULTIPLE_CHOICES("Site $url has multiple feeds to subscribe to"); + } + + var root = parse_json(response); + return Subscription.from_json(root.get_object()); + } + catch (FeedbinError.NOT_FOUND e) + { + return null; } } - - if(feed_id != null) + + public void rename_subscription(int64 subscription_id, string title) throws FeedbinError { - path = @"feeds/$feed_id/$path"; + Json.Object object = new Json.Object(); + object.set_string_member("title", title); + post_json_object(@"subscriptions/$subscription_id/update.json", object); } - - Json.Node root; - try + + public struct Tagging { - root = get_json(path); + int64 id; + int64 feed_id; + string name; + + public Tagging.from_json(Json.Object object) + { + id = object.get_int_member("id"); + feed_id = object.get_int_member("feed_id"); + name = object.get_string_member("name"); + } } - catch(FeedbinError.NOT_FOUND e) + + public void add_tagging(int64 feed_id, string tag_name) throws FeedbinError { - return Gee.List.empty<Entry?>(); + Json.Object object = new Json.Object(); + object.set_int_member("feed_id", feed_id); + object.set_string_member("name", tag_name); + + post_json_object("taggings.json", object); + // TODO: Return id } - - var entries = new Gee.ArrayList<Entry?>(); - var array = root.get_array(); - for(var i = 0; i < array.get_length(); ++i) + + public void delete_tagging(int64 tagging_id) throws FeedbinError { - var object = array.get_object_element(i); - entries.add(Entry.from_json(object)); + delete_request(@"taggings/$tagging_id.json"); } - return entries; -} - -private Gee.Set<int64?> get_x_entries(string path) throws FeedbinError -{ - var root = get_json(path); - var array = root.get_array(); - // We have to set the hash function here manually or contains() won't - // work right -- presumably because it's trying to do pointer comparisons? - var ids = new Gee.HashSet<int64?>( - (n) => { return int64_hash(n); }, - (a, b) => { return int64_equal(a, b); }); - for(var i = 0; i < array.get_length(); ++i) + + public Gee.List<Tagging?> get_taggings() throws FeedbinError + ensures (!result.contains(null)) { - ids.add(array.get_int_element(i)); + var root = get_json("taggings.json"); + var taggings = new Gee.ArrayList<Tagging?>(); + var array = root.get_array(); + for(var i = 0; i < array.get_length(); ++i) + { + var object = array.get_object_element(i); + taggings.add(Tagging.from_json(object)); + } + return taggings; } - return ids; -} - -public Gee.Set<int64?> get_unread_entries() throws FeedbinError -{ - return get_x_entries("unread_entries.json"); -} - -public Gee.Set<int64?> get_starred_entries() throws FeedbinError -{ - return get_x_entries("starred_entries.json"); -} - -private void set_entries_status(string type, Gee.Collection<int64?> entry_ids, bool create) throws FeedbinError -requires (!entry_ids.contains(null)) -{ - Json.Array array = new Json.Array(); - foreach(var id in entry_ids) + + public struct Entry { - array.add_int_element(id); + int64 id; + int64 feed_id; + string? title; + string? url; + string? author; + string? content; + string? summary; + DateTime published; + DateTime created_at; + + public Entry.from_json(Json.Object object) throws FeedbinError + { + id = object.get_int_member("id"); + feed_id = object.get_int_member("feed_id"); + title = object.get_string_member("title"); + url = object.get_string_member("url"); + author = object.get_string_member("author"); + content = object.get_string_member("content"); + summary = object.get_string_member("summary"); + published = get_datetime_member(object, "published"); + created_at = get_datetime_member(object, "created_at"); + } } - - Json.Object object = new Json.Object(); - object.set_array_member(type, array); - - string path = create ? @"$type.json" : @"$type/delete.json"; - post_json_object(path, object); -} - -public void set_entries_read(Gee.Collection<int64?> entry_ids, bool read) throws FeedbinError -requires (!entry_ids.contains(null)) -{ - set_entries_status("unread_entries", entry_ids, !read); -} - -public void set_entries_starred(Gee.Collection<int64?> entry_ids, bool starred) throws FeedbinError -requires (!entry_ids.contains(null)) -{ - set_entries_status("starred_entries", entry_ids, starred); -} - -public Gee.Map<string, Bytes?> get_favicons() throws FeedbinError -{ - // The favicon API isn't public right now; make sure to handle it - // suddenly changing or disappearing - try + + public Gee.List<Entry?> get_entries(int page, bool only_starred, DateTime? since, int64? feed_id = null) throws FeedbinError + requires (page >= 0) + ensures (!result.contains(null)) { - var root = get_json("favicons.json"); - if(root == null) + string starred = only_starred ? "true" : "false"; + string path = @"entries.json?per_page=100&page=$page&starred=$starred&include_enclosure=true"; + if(since != null) { - return Gee.Map.empty<string, Bytes?>(); + var t = GLib.TimeVal(); + if(since.to_timeval(out t)) + { + path += "&since=" + t.to_iso8601(); + } } - + + if(feed_id != null) + { + path = @"feeds/$feed_id/$path"; + } + + Json.Node root; + try + { + root = get_json(path); + } + catch(FeedbinError.NOT_FOUND e) + { + return Gee.List.empty<Entry?>(); + } + + var entries = new Gee.ArrayList<Entry?>(); var array = root.get_array(); - if(array == null) + for(var i = 0; i < array.get_length(); ++i) { - return Gee.Map.empty<string, Bytes?>(); + var object = array.get_object_element(i); + entries.add(Entry.from_json(object)); } - - var favicons = new Gee.HashMap<string, Bytes?>(); + return entries; + } + + private Gee.Set<int64?> get_x_entries(string path) throws FeedbinError + { + var root = get_json(path); + var array = root.get_array(); + // We have to set the hash function here manually or contains() won't + // work right -- presumably because it's trying to do pointer comparisons? + var ids = new Gee.HashSet<int64?>( + (n) => { return int64_hash(n); }, + (a, b) => { return int64_equal(a, b); }); for(var i = 0; i < array.get_length(); ++i) { - var obj = array.get_object_element(i); - string host = obj.get_string_member("host"); - if(host == null) + ids.add(array.get_int_element(i)); + } + return ids; + } + + public Gee.Set<int64?> get_unread_entries() throws FeedbinError + { + return get_x_entries("unread_entries.json"); + } + + public Gee.Set<int64?> get_starred_entries() throws FeedbinError + { + return get_x_entries("starred_entries.json"); + } + + private void set_entries_status(string type, Gee.Collection<int64?> entry_ids, bool create) throws FeedbinError + requires (!entry_ids.contains(null)) + { + Json.Array array = new Json.Array(); + foreach(var id in entry_ids) + { + array.add_int_element(id); + } + + Json.Object object = new Json.Object(); + object.set_array_member(type, array); + + string path = create ? @"$type.json" : @"$type/delete.json"; + post_json_object(path, object); + } + + public void set_entries_read(Gee.Collection<int64?> entry_ids, bool read) throws FeedbinError + requires (!entry_ids.contains(null)) + { + set_entries_status("unread_entries", entry_ids, !read); + } + + public void set_entries_starred(Gee.Collection<int64?> entry_ids, bool starred) throws FeedbinError + requires (!entry_ids.contains(null)) + { + set_entries_status("starred_entries", entry_ids, starred); + } + + public Gee.Map<string, Bytes?> get_favicons() throws FeedbinError + { + // The favicon API isn't public right now; make sure to handle it + // suddenly changing or disappearing + try + { + var root = get_json("favicons.json"); + if(root == null) { - continue; + return Gee.Map.empty<string, Bytes?>(); } - var favicon_encoded = obj.get_string_member("favicon"); - if(favicon_encoded == null) + + var array = root.get_array(); + if(array == null) { - continue; + return Gee.Map.empty<string, Bytes?>(); } - var favicon = new Bytes.take(Base64.decode(favicon_encoded)); - favicons.set(host, favicon); + + var favicons = new Gee.HashMap<string, Bytes?>(); + for(var i = 0; i < array.get_length(); ++i) + { + var obj = array.get_object_element(i); + string host = obj.get_string_member("host"); + if(host == null) + { + continue; + } + var favicon_encoded = obj.get_string_member("favicon"); + if(favicon_encoded == null) + { + continue; + } + var favicon = new Bytes.take(Base64.decode(favicon_encoded)); + favicons.set(host, favicon); + } + return favicons; + } + catch(Error e) + { + return Gee.Map.empty<string, Bytes?>(); } - return favicons; - } - catch(Error e) - { - return Gee.Map.empty<string, Bytes?>(); } } -} diff --git a/plugins/backend/feedbin/feedbinInterface.vala b/plugins/backend/feedbin/feedbinInterface.vala index 3c46de9a..86c3d5a3 100644 --- a/plugins/backend/feedbin/feedbinInterface.vala +++ b/plugins/backend/feedbin/feedbinInterface.vala @@ -14,745 +14,745 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.FeedbinInterface : FeedServerInterface { - -private FeedbinAPI m_api; -private FeedbinUtils m_utils; -private Gtk.Entry m_userEntry; -private Gtk.Entry m_passwordEntry; - -public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) -{ - m_utils = new FeedbinUtils(settings_backend, secrets); - m_api = new FeedbinAPI(m_utils.getUser(), m_utils.getPassword(), Constants.USER_AGENT); -} - -public override string getWebsite() -{ - return "https://feedbin.com/"; -} - -public override BackendFlags getFlags() -{ - return (BackendFlags.HOSTED | BackendFlags.FREE_SOFTWARE | BackendFlags.PAID); -} - -public override string getID() -{ - return "feedbin"; -} - -public override string iconName() -{ - return "feed-service-feedbin"; -} - -public override string serviceName() -{ - return "Feedbin"; -} - -public override bool needWebLogin() -{ - return false; -} - -public override Gtk.Box? getWidget() -ensures (result != null) -{ - var user_label = new Gtk.Label(_("Username:")); - var password_label = new Gtk.Label(_("Password:")); - - user_label.set_alignment(1.0f, 0.5f); - password_label.set_alignment(1.0f, 0.5f); - - user_label.set_hexpand(true); - password_label.set_hexpand(true); - - m_userEntry = new Gtk.Entry(); - m_passwordEntry = new Gtk.Entry(); - var loginButton = new Gtk.Button.with_label(_("Login")); - - m_userEntry.activate.connect(() => { + + private FeedbinAPI m_api; + private FeedbinUtils m_utils; + private Gtk.Entry m_userEntry; + private Gtk.Entry m_passwordEntry; + + public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) + { + m_utils = new FeedbinUtils(settings_backend, secrets); + m_api = new FeedbinAPI(m_utils.getUser(), m_utils.getPassword(), Constants.USER_AGENT); + } + + public override string getWebsite() + { + return "https://feedbin.com/"; + } + + public override BackendFlags getFlags() + { + return (BackendFlags.HOSTED | BackendFlags.FREE_SOFTWARE | BackendFlags.PAID); + } + + public override string getID() + { + return "feedbin"; + } + + public override string iconName() + { + return "feed-service-feedbin"; + } + + public override string serviceName() + { + return "Feedbin"; + } + + public override bool needWebLogin() + { + return false; + } + + public override Gtk.Box? getWidget() + ensures (result != null) + { + var user_label = new Gtk.Label(_("Username:")); + var password_label = new Gtk.Label(_("Password:")); + + user_label.set_alignment(1.0f, 0.5f); + password_label.set_alignment(1.0f, 0.5f); + + user_label.set_hexpand(true); + password_label.set_hexpand(true); + + m_userEntry = new Gtk.Entry(); + m_passwordEntry = new Gtk.Entry(); + var loginButton = new Gtk.Button.with_label(_("Login")); + + m_userEntry.activate.connect(() => { loginButton.activate(); }); - m_passwordEntry.activate.connect(() => { + m_passwordEntry.activate.connect(() => { loginButton.activate(); }); - - m_passwordEntry.set_input_purpose(Gtk.InputPurpose.PASSWORD); - m_passwordEntry.set_visibility(false); - - var grid = new Gtk.Grid(); - grid.set_column_spacing(10); - grid.set_row_spacing(10); - grid.set_valign(Gtk.Align.CENTER); - grid.set_halign(Gtk.Align.CENTER); - - grid.attach(user_label, 0, 0, 1, 1); - grid.attach(m_userEntry, 1, 0, 1, 1); - grid.attach(password_label, 0, 1, 1, 1); - grid.attach(m_passwordEntry, 1, 1, 1, 1); - - var logo = new Gtk.Image.from_icon_name("feed-service-feedbin", Gtk.IconSize.MENU); - - var loginLabel = new Gtk.Label(_("Please log in to Feedbin to enjoy using FeedReader")); - loginLabel.get_style_context().add_class("h2"); - loginLabel.set_justify(Gtk.Justification.CENTER); - loginLabel.set_lines(3); - - loginButton.halign = Gtk.Align.END; - loginButton.set_size_request(80, 30); - loginButton.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); - loginButton.clicked.connect(() => { + + m_passwordEntry.set_input_purpose(Gtk.InputPurpose.PASSWORD); + m_passwordEntry.set_visibility(false); + + var grid = new Gtk.Grid(); + grid.set_column_spacing(10); + grid.set_row_spacing(10); + grid.set_valign(Gtk.Align.CENTER); + grid.set_halign(Gtk.Align.CENTER); + + grid.attach(user_label, 0, 0, 1, 1); + grid.attach(m_userEntry, 1, 0, 1, 1); + grid.attach(password_label, 0, 1, 1, 1); + grid.attach(m_passwordEntry, 1, 1, 1, 1); + + var logo = new Gtk.Image.from_icon_name("feed-service-feedbin", Gtk.IconSize.MENU); + + var loginLabel = new Gtk.Label(_("Please log in to Feedbin to enjoy using FeedReader")); + loginLabel.get_style_context().add_class("h2"); + loginLabel.set_justify(Gtk.Justification.CENTER); + loginLabel.set_lines(3); + + loginButton.halign = Gtk.Align.END; + loginButton.set_size_request(80, 30); + loginButton.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); + loginButton.clicked.connect(() => { tryLogin(); }); - - var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10); - box.valign = Gtk.Align.CENTER; - box.halign = Gtk.Align.CENTER; - box.pack_start(loginLabel, false, false, 10); - box.pack_start(logo, false, false, 10); - box.pack_start(grid, true, true, 10); - box.pack_end(loginButton, false, false, 20); - - m_userEntry.set_text(m_utils.getUser()); - m_passwordEntry.set_text(m_utils.getPassword()); - - return box; -} - -public override void writeData() -{ - m_api.username = m_userEntry.get_text().strip(); - m_utils.setUser(m_api.username); - - m_api.password = m_passwordEntry.get_text().strip(); - m_utils.setPassword(m_api.password); -} - -public override bool supportTags() -{ - return false; -} - -public override bool doInitSync() -{ - return true; -} - -public override string symbolicIcon() -{ - return "feed-service-feedbin-symbolic"; -} - -public override string accountName() -{ - return m_utils.getUser(); -} - -public override string getServerURL() -{ - return "https://feedbin.com/"; -} - -public override string uncategorizedID() -{ - return "0"; -} - -public override bool supportCategories() -{ - return true; -} - -public override bool supportFeedManipulation() -{ - return true; -} - -public override bool hideCategoryWhenEmpty(string catID) -{ - return false; -} - -public override bool supportMultiLevelCategories() -{ - return false; -} - -public override bool supportMultiCategoriesPerFeed() -{ - return false; -} - -public override bool syncFeedsAndCategories() -{ - return true; -} - -public override bool tagIDaffectedByNameChange() -{ - return true; -} - -public override void resetAccount() -{ - m_utils.resetAccount(); -} - -public override bool useMaxArticles() -{ - return true; -} - -public override LoginResponse login() -{ - try + + var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10); + box.valign = Gtk.Align.CENTER; + box.halign = Gtk.Align.CENTER; + box.pack_start(loginLabel, false, false, 10); + box.pack_start(logo, false, false, 10); + box.pack_start(grid, true, true, 10); + box.pack_end(loginButton, false, false, 20); + + m_userEntry.set_text(m_utils.getUser()); + m_passwordEntry.set_text(m_utils.getPassword()); + + return box; + } + + public override void writeData() { - if(m_api.login()) - { - return LoginResponse.SUCCESS; - } - else - { - return LoginResponse.WRONG_LOGIN; - } + m_api.username = m_userEntry.get_text().strip(); + m_utils.setUser(m_api.username); + + m_api.password = m_passwordEntry.get_text().strip(); + m_utils.setPassword(m_api.password); } - catch(FeedbinError.NO_CONNECTION e) + + public override bool supportTags() { - return LoginResponse.NO_CONNECTION; + return false; } - catch(Error e) + + public override bool doInitSync() { - Logger.error("Feedbin login: " + e.message); - return LoginResponse.UNKNOWN_ERROR; + return true; } -} - -public override bool serverAvailable() -{ - return login() != LoginResponse.NO_CONNECTION; -} - -public override void setArticleIsRead(string article_id, ArticleStatus status) -{ - var entry_id = int64.parse(article_id); - var entry_ids = ListUtils.single<int64?>(entry_id); - try + + public override string symbolicIcon() { - m_api.set_entries_read(entry_ids, status == ArticleStatus.READ); + return "feed-service-feedbin-symbolic"; } - catch(Error e) + + public override string accountName() { - Logger.error(@"FeedbinInterface.setArticleIsRead: " + e.message); + return m_utils.getUser(); } -} - -public override void setArticleIsMarked(string article_id, ArticleStatus status) -{ - var entry_id = int64.parse(article_id); - var entry_ids = ListUtils.single<int64?>(entry_id); - try + + public override string getServerURL() { - m_api.set_entries_starred(entry_ids, status == ArticleStatus.MARKED); + return "https://feedbin.com/"; } - catch(Error e) + + public override string uncategorizedID() { - Logger.error(@"FeedbinInterface.setArticleIsMarked: " + e.message); + return "0"; } -} - -private void setRead(string id, FeedListType type) -{ - const int count = 1000; - int num_articles = 1; // set to any value > 0 - var db = DataBase.readOnly(); - for(var offset = 0; num_articles > 0; offset += count) - { - var articles = db.read_articles(id, type, ArticleListState.ALL, "", count, offset); - var entry_ids = new Gee.ArrayList<int64?>(); - foreach(var article in articles) + + public override bool supportCategories() + { + return true; + } + + public override bool supportFeedManipulation() + { + return true; + } + + public override bool hideCategoryWhenEmpty(string catID) + { + return false; + } + + public override bool supportMultiLevelCategories() + { + return false; + } + + public override bool supportMultiCategoriesPerFeed() + { + return false; + } + + public override bool syncFeedsAndCategories() + { + return true; + } + + public override bool tagIDaffectedByNameChange() + { + return true; + } + + public override void resetAccount() + { + m_utils.resetAccount(); + } + + public override bool useMaxArticles() + { + return true; + } + + public override LoginResponse login() + { + try { - entry_ids.add(int64.parse(article.getArticleID())); + if(m_api.login()) + { + return LoginResponse.SUCCESS; + } + else + { + return LoginResponse.WRONG_LOGIN; + } } - try + catch(FeedbinError.NO_CONNECTION e) { - m_api.set_entries_read(entry_ids, true); + return LoginResponse.NO_CONNECTION; } catch(Error e) { - Logger.error(@"FeedbinInterface.setRead: " + e.message); - break; + Logger.error("Feedbin login: " + e.message); + return LoginResponse.UNKNOWN_ERROR; } } -} - -public override bool alwaysSetReadByID() -{ - return false; -} - -public override void setFeedRead(string feed_id) -{ - setRead(feed_id, FeedListType.FEED); -} - -public override void setCategoryRead(string category_id) -{ - setRead(category_id, FeedListType.CATEGORY); -} - -public override void markAllItemsRead() -{ - setRead(FeedID.ALL.to_string(), FeedListType.FEED); -} - -public override void tagArticle(string article_id, string tag_id) -{ - return; -} - -public override void removeArticleTag(string article_id, string tag_id) -{ - return; -} - -public override string createTag(string caption) -{ - return ""; -} - -public override void deleteTag(string tag_id) -{ - return; -} - -public override void renameTag(string tagID, string title) -{ - return; -} - -public override bool addFeed(string feed_url, string? cat_id, string? category_name, out string feed_id, out string errmsg) -{ - feed_id = ""; - try + + public override bool serverAvailable() + { + return login() != LoginResponse.NO_CONNECTION; + } + + public override void setArticleIsRead(string article_id, ArticleStatus status) { - var subscription = m_api.add_subscription(feed_url); - if (subscription == null) + var entry_id = int64.parse(article_id); + var entry_ids = ListUtils.single<int64?>(entry_id); + try { - errmsg = @"Feedbin could not find a feed at $(feed_url)"; - return false; + m_api.set_entries_read(entry_ids, status == ArticleStatus.READ); } - feed_id = subscription.feed_id.to_string(); - - if(category_name != null) + catch(Error e) { - m_api.add_tagging(subscription.feed_id, category_name); + Logger.error(@"FeedbinInterface.setArticleIsRead: " + e.message); } - - errmsg = ""; - return true; } - catch(Error e) + + public override void setArticleIsMarked(string article_id, ArticleStatus status) { - errmsg = e.message; - Logger.error(@"FeedbinInterface.addFeed: $errmsg"); - return false; + var entry_id = int64.parse(article_id); + var entry_ids = ListUtils.single<int64?>(entry_id); + try + { + m_api.set_entries_starred(entry_ids, status == ArticleStatus.MARKED); + } + catch(Error e) + { + Logger.error(@"FeedbinInterface.setArticleIsMarked: " + e.message); + } } -} - -private FeedbinAPI.Subscription subscription_for_feed(string feed_id_str) throws FeedbinError -{ - var feed_id = int64.parse(feed_id_str); - var subscriptions = m_api.get_subscriptions(); - foreach(var subscription in subscriptions) + + private void setRead(string id, FeedListType type) { - if(subscription.feed_id == feed_id) + const int count = 1000; + int num_articles = 1; // set to any value > 0 + var db = DataBase.readOnly(); + for(var offset = 0; num_articles > 0; offset += count) { - return subscription; + var articles = db.read_articles(id, type, ArticleListState.ALL, "", count, offset); + var entry_ids = new Gee.ArrayList<int64?>(); + foreach(var article in articles) + { + entry_ids.add(int64.parse(article.getArticleID())); + } + try + { + m_api.set_entries_read(entry_ids, true); + } + catch(Error e) + { + Logger.error(@"FeedbinInterface.setRead: " + e.message); + break; + } } } - throw new FeedbinError.NOT_FOUND("No subscription found for feed $feed_id"); -} - -public override void removeFeed(string feed_id_str) -{ - try + + public override bool alwaysSetReadByID() { - var subscription = subscription_for_feed(feed_id_str); - m_api.delete_subscription(subscription.id); + return false; } - catch(Error e) + + public override void setFeedRead(string feed_id) { - Logger.error(@"FeedbinInterface.removeFeed: " + e.message); + setRead(feed_id, FeedListType.FEED); } -} - -public override void renameFeed(string feed_id_str, string title) -{ - try + + public override void setCategoryRead(string category_id) { - var subscription = subscription_for_feed(feed_id_str); - m_api.rename_subscription(subscription.id, title); + setRead(category_id, FeedListType.CATEGORY); } - catch(Error e) + + public override void markAllItemsRead() { - Logger.error(@"FeedbinInterface.renameFeed: " + e.message); + setRead(FeedID.ALL.to_string(), FeedListType.FEED); } -} - -public override void moveFeed(string feed_id_str, string new_category, string? old_category) -{ - Logger.debug(@"moveFeed: $feed_id_str from $old_category to $new_category"); - try + + public override void tagArticle(string article_id, string tag_id) { - var subscription = subscription_for_feed(feed_id_str); - var feed_id = subscription.feed_id; - if(old_category != null) - { - var taggings = m_api.get_taggings(); - foreach(var tagging in taggings) - { - if(tagging.name != old_category || tagging.feed_id != feed_id) - { - continue; - } - Logger.debug(@"moveFeed: Deleting tag $old_category from $feed_id"); - m_api.delete_tagging(tagging.id); - break; - } - } - Logger.debug(@"moveFeed: Adding tag $new_category to $feed_id"); - m_api.add_tagging(feed_id, new_category); + return; } - catch(Error e) + + public override void removeArticleTag(string article_id, string tag_id) { - Logger.error(@"FeedbinInterface.moveFeed: " + e.message); + return; } -} - -public override void renameCategory(string old_category, string new_category) -{ - Logger.debug(@"renameCategory: From $old_category to $new_category"); - try + + public override string createTag(string caption) { - var taggings = m_api.get_taggings(); - foreach(var tagging in taggings) - { - if(tagging.name != old_category) - { - continue; - } - var feed_id = tagging.feed_id; - Logger.debug(@"renameCategory: Tagging $feed_id with $new_category"); - m_api.delete_tagging(tagging.id); - m_api.add_tagging(feed_id, new_category); - } + return ""; } - catch(Error e) + + public override void deleteTag(string tag_id) { - Logger.error(@"FeedbinInterface.renameCategory: " + e.message); + return; } -} - -public override void moveCategory(string category_id, string new_parent_id) -{ - // Feedbin doesn't have multi-level categories -} - -public override string createCategory(string title, string? parent_id) -ensures (result == title) -{ - // Categories are created and destroyed based on feeds having them. - // There are no empty categories in Feedbin - return title; -} - -public override void deleteCategory(string category) -{ - Logger.debug(@"deleteCategory: $category"); - try + + public override void renameTag(string tagID, string title) { - var taggings = m_api.get_taggings(); - foreach(var tagging in taggings) + return; + } + + public override bool addFeed(string feed_url, string? cat_id, string? category_name, out string feed_id, out string errmsg) + { + feed_id = ""; + try { - if(tagging.name != category) + var subscription = m_api.add_subscription(feed_url); + if (subscription == null) + { + errmsg = @"Feedbin could not find a feed at $(feed_url)"; + return false; + } + feed_id = subscription.feed_id.to_string(); + + if(category_name != null) { - continue; + m_api.add_tagging(subscription.feed_id, category_name); } - var feed_id = tagging.feed_id; - Logger.debug(@"deleteCategory: Deleting category $category from feed $feed_id"); - m_api.delete_tagging(tagging.id); + + errmsg = ""; + return true; + } + catch(Error e) + { + errmsg = e.message; + Logger.error(@"FeedbinInterface.addFeed: $errmsg"); + return false; } } - catch(Error e) - { - Logger.error(@"FeedbinInterface.deleteCategory: " + e.message); - } -} - -public override void removeCatFromFeed(string feed_id_str, string category) -{ - Logger.debug(@"removeCatFromFeed: Feed $feed_id_str, category $category"); - try + + private FeedbinAPI.Subscription subscription_for_feed(string feed_id_str) throws FeedbinError { var feed_id = int64.parse(feed_id_str); - var taggings = m_api.get_taggings(); - foreach(var tagging in taggings) + var subscriptions = m_api.get_subscriptions(); + foreach(var subscription in subscriptions) { - if(tagging.feed_id != feed_id || tagging.name != category) + if(subscription.feed_id == feed_id) { - continue; + return subscription; } - - Logger.debug(@"removeCatFromFeed: Deleting category $category from feed $feed_id"); - m_api.delete_tagging(tagging.id); - break; } + throw new FeedbinError.NOT_FOUND("No subscription found for feed $feed_id"); } - catch(Error e) - { - Logger.error(@"FeedbinInterface.removeCatFromFeed: " + e.message); - } -} - -public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) -{ - try + + public override void removeFeed(string feed_id_str) { - var taggings = m_api.get_taggings(); - if(cancellable != null && cancellable.is_cancelled()) - { - return false; - } - - var favicons = m_api.get_favicons(); - if(cancellable != null && cancellable.is_cancelled()) + try { - return false; + var subscription = subscription_for_feed(feed_id_str); + m_api.delete_subscription(subscription.id); } - - // It's easier to rebuild the category list than to update it - var category_names = new Gee.HashSet<string>(); - foreach(var tagging in taggings) + catch(Error e) { - category_names.add(tagging.name); + Logger.error(@"FeedbinInterface.removeFeed: " + e.message); } - Logger.debug("getFeedsAndCats: Got %d categories: %s".printf(category_names.size, StringUtils.join(category_names, ", "))); - - categories.clear(); - var top_category = CategoryID.MASTER.to_string(); - foreach(string name in category_names) + } + + public override void renameFeed(string feed_id_str, string title) + { + try { - // Note: Feedbin categories *are* case sensitive, so we don't need - // to change the case here. "articles" and "Articles" are different - // tags. - categories.add( - new Category ( - name, - name, - 0, - 0, - top_category, - 1 - ) - ); + var subscription = subscription_for_feed(feed_id_str); + m_api.rename_subscription(subscription.id, title); } - - var tag_map = new Gee.HashMultiMap<string, string>(); - foreach(var tagging in taggings) + catch(Error e) { - tag_map.set(tagging.feed_id.to_string(), tagging.name); + Logger.error(@"FeedbinInterface.renameFeed: " + e.message); } - - var subscriptions = m_api.get_subscriptions(); - feeds.clear(); - - foreach(var subscription in subscriptions) + } + + public override void moveFeed(string feed_id_str, string new_category, string? old_category) + { + Logger.debug(@"moveFeed: $feed_id_str from $old_category to $new_category"); + try { - var feed_id = subscription.feed_id.to_string(); - Gee.List<string> feed_categories = new Gee.ArrayList<string>(); - - if(tag_map.contains(feed_id)) + var subscription = subscription_for_feed(feed_id_str); + var feed_id = subscription.feed_id; + if(old_category != null) { - feed_categories.add_all(tag_map.get(feed_id)); - } - else - { - feed_categories.add(uncategorizedID()); - } - - string? favicon_uri = null; - if(subscription.site_url != null) - { - var uri = new Soup.URI(subscription.site_url); - if(uri != null) + var taggings = m_api.get_taggings(); + foreach(var tagging in taggings) { - var favicon = favicons.get(uri.host); - if(favicon != null) + if(tagging.name != old_category || tagging.feed_id != feed_id) { - string base64 = Base64.encode(favicon.get_data()); - favicon_uri = @"data:application/octet-stream;base64,$base64"; + continue; } + Logger.debug(@"moveFeed: Deleting tag $old_category from $feed_id"); + m_api.delete_tagging(tagging.id); + break; } } - - feeds.add( - new Feed( - feed_id, - subscription.title, - subscription.site_url, - 0, - feed_categories, - favicon_uri, - subscription.feed_url) - ); + Logger.debug(@"moveFeed: Adding tag $new_category to $feed_id"); + m_api.add_tagging(feed_id, new_category); + } + catch(Error e) + { + Logger.error(@"FeedbinInterface.moveFeed: " + e.message); } } - catch(Error e) + + public override void renameCategory(string old_category, string new_category) { - Logger.error(@"FeedbinInterface.getFeedsAndCats: " + e.message); - return false; + Logger.debug(@"renameCategory: From $old_category to $new_category"); + try + { + var taggings = m_api.get_taggings(); + foreach(var tagging in taggings) + { + if(tagging.name != old_category) + { + continue; + } + var feed_id = tagging.feed_id; + Logger.debug(@"renameCategory: Tagging $feed_id with $new_category"); + m_api.delete_tagging(tagging.id); + m_api.add_tagging(feed_id, new_category); + } + } + catch(Error e) + { + Logger.error(@"FeedbinInterface.renameCategory: " + e.message); + } } - return true; -} - -public override int getUnreadCount() -ensures (result >= 0) -{ - try + + public override void moveCategory(string category_id, string new_parent_id) { - return m_api.get_unread_entries().size; + // Feedbin doesn't have multi-level categories } - catch(Error e) + + public override string createCategory(string title, string? parent_id) + ensures (result == title) { - Logger.error(@"FeedbinInterface.getUnreadCount: " + e.message); - return 0; + // Categories are created and destroyed based on feeds having them. + // There are no empty categories in Feedbin + return title; } -} - -public override void getArticles(int count, ArticleStatus what_to_get, DateTime? since, string? feed_id_str, bool is_tag_id, GLib.Cancellable? cancellable = null) -requires (count >= 0) -{ - try + + public override void deleteCategory(string category) { - var db = DataBase.readOnly(); - int64? feed_id = null; - if(!is_tag_id && feed_id_str != null) + Logger.debug(@"deleteCategory: $category"); + try { - feed_id = int64.parse(feed_id_str); + var taggings = m_api.get_taggings(); + foreach(var tagging in taggings) + { + if(tagging.name != category) + { + continue; + } + var feed_id = tagging.feed_id; + Logger.debug(@"deleteCategory: Deleting category $category from feed $feed_id"); + m_api.delete_tagging(tagging.id); + } } - bool only_starred = what_to_get == ArticleStatus.MARKED; - - if(cancellable != null && cancellable.is_cancelled()) + catch(Error e) { - return; + Logger.error(@"FeedbinInterface.deleteCategory: " + e.message); } - - // The Feedbin API doesn't include read/unread/starred status in the entries.json - // so look them up. - var unread_ids = m_api.get_unread_entries(); - if(cancellable != null && cancellable.is_cancelled()) + } + + public override void removeCatFromFeed(string feed_id_str, string category) + { + Logger.debug(@"removeCatFromFeed: Feed $feed_id_str, category $category"); + try { - return; + var feed_id = int64.parse(feed_id_str); + var taggings = m_api.get_taggings(); + foreach(var tagging in taggings) + { + if(tagging.feed_id != feed_id || tagging.name != category) + { + continue; + } + + Logger.debug(@"removeCatFromFeed: Deleting category $category from feed $feed_id"); + m_api.delete_tagging(tagging.id); + break; + } } - - var starred_ids = m_api.get_starred_entries(); - + catch(Error e) { - // Update read/unread status of existing entries - string search_feed_id; - FeedListType search_type; - if(feed_id == null) + Logger.error(@"FeedbinInterface.removeCatFromFeed: " + e.message); + } + } + + public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) + { + try + { + var taggings = m_api.get_taggings(); + if(cancellable != null && cancellable.is_cancelled()) { - search_feed_id = FeedID.ALL.to_string(); - search_type = FeedListType.ALL_FEEDS; + return false; } - else if(is_tag_id) + + var favicons = m_api.get_favicons(); + if(cancellable != null && cancellable.is_cancelled()) { - search_feed_id = feed_id_str; - search_type = FeedListType.TAG; + return false; } - else + + // It's easier to rebuild the category list than to update it + var category_names = new Gee.HashSet<string>(); + foreach(var tagging in taggings) { - search_feed_id = feed_id_str; - search_type = FeedListType.FEED; + category_names.add(tagging.name); } - - Logger.debug(@"Checking if any articles in $search_type $search_feed_id changed state"); - for(var offset = 0, c = 1000; ; offset += c) + Logger.debug("getFeedsAndCats: Got %d categories: %s".printf(category_names.size, StringUtils.join(category_names, ", "))); + + categories.clear(); + var top_category = CategoryID.MASTER.to_string(); + foreach(string name in category_names) { - var articles = new Gee.ArrayList<Article>(); - var existing_articles = db.read_articles(search_feed_id, search_type, ArticleListState.ALL, "", c, offset); - if(existing_articles.size == 0) + // Note: Feedbin categories *are* case sensitive, so we don't need + // to change the case here. "articles" and "Articles" are different + // tags. + categories.add( + new Category ( + name, + name, + 0, + 0, + top_category, + 1 + ) + ); + } + + var tag_map = new Gee.HashMultiMap<string, string>(); + foreach(var tagging in taggings) + { + tag_map.set(tagging.feed_id.to_string(), tagging.name); + } + + var subscriptions = m_api.get_subscriptions(); + feeds.clear(); + + foreach(var subscription in subscriptions) + { + var feed_id = subscription.feed_id.to_string(); + Gee.List<string> feed_categories = new Gee.ArrayList<string>(); + + if(tag_map.contains(feed_id)) { - break; + feed_categories.add_all(tag_map.get(feed_id)); } - - foreach(var article in existing_articles) + else { - var id = int64.parse(article.getArticleID()); - var marked = starred_ids.contains(id) ? ArticleStatus.MARKED : ArticleStatus.UNMARKED; - var unread = unread_ids.contains(id) ? ArticleStatus.UNREAD : ArticleStatus.READ; - var changed = false; - if(article.getMarked() != marked) - { - article.setMarked(marked); - changed = true; - } - if(article.getUnread() != unread) + feed_categories.add(uncategorizedID()); + } + + string? favicon_uri = null; + if(subscription.site_url != null) + { + var uri = new Soup.URI(subscription.site_url); + if(uri != null) { - article.setUnread(unread); - changed = true; + var favicon = favicons.get(uri.host); + if(favicon != null) + { + string base64 = Base64.encode(favicon.get_data()); + favicon_uri = @"data:application/octet-stream;base64,$base64"; + } } - articles.add(article); } - writeArticles(articles); + + feeds.add( + new Feed( + feed_id, + subscription.title, + subscription.site_url, + 0, + feed_categories, + favicon_uri, + subscription.feed_url) + ); } } - - // Add new articles - for(int page = 1; ; ++page) + catch(Error e) + { + Logger.error(@"FeedbinInterface.getFeedsAndCats: " + e.message); + return false; + } + return true; + } + + public override int getUnreadCount() + ensures (result >= 0) + { + try + { + return m_api.get_unread_entries().size; + } + catch(Error e) { + Logger.error(@"FeedbinInterface.getUnreadCount: " + e.message); + return 0; + } + } + + public override void getArticles(int count, ArticleStatus what_to_get, DateTime? since, string? feed_id_str, bool is_tag_id, GLib.Cancellable? cancellable = null) + requires (count >= 0) + { + try + { + var db = DataBase.readOnly(); + int64? feed_id = null; + if(!is_tag_id && feed_id_str != null) + { + feed_id = int64.parse(feed_id_str); + } + bool only_starred = what_to_get == ArticleStatus.MARKED; + if(cancellable != null && cancellable.is_cancelled()) { return; } - - var entries = m_api.get_entries(page, only_starred, since, feed_id); - if(entries.size == 0) + + // The Feedbin API doesn't include read/unread/starred status in the entries.json + // so look them up. + var unread_ids = m_api.get_unread_entries(); + if(cancellable != null && cancellable.is_cancelled()) { - break; + return; } - - var articles = new Gee.ArrayList<Article>(); - foreach(var entry in entries) + + var starred_ids = m_api.get_starred_entries(); + { - articles.add( - new Article( - entry.id.to_string(), - entry.title, - entry.url, - entry.feed_id.to_string(), - unread_ids.contains(entry.id) ? ArticleStatus.UNREAD : ArticleStatus.READ, - starred_ids.contains(entry.id) ? ArticleStatus.MARKED : ArticleStatus.UNMARKED, - entry.content, - entry.summary, - entry.author, - entry.published != null ? entry.published : entry.created_at, - -1, - null, + // Update read/unread status of existing entries + string search_feed_id; + FeedListType search_type; + if(feed_id == null) + { + search_feed_id = FeedID.ALL.to_string(); + search_type = FeedListType.ALL_FEEDS; + } + else if(is_tag_id) + { + search_feed_id = feed_id_str; + search_type = FeedListType.TAG; + } + else + { + search_feed_id = feed_id_str; + search_type = FeedListType.FEED; + } + + Logger.debug(@"Checking if any articles in $search_type $search_feed_id changed state"); + for(var offset = 0, c = 1000; ; offset += c) + { + var articles = new Gee.ArrayList<Article>(); + var existing_articles = db.read_articles(search_feed_id, search_type, ArticleListState.ALL, "", c, offset); + if(existing_articles.size == 0) + { + break; + } + + foreach(var article in existing_articles) + { + var id = int64.parse(article.getArticleID()); + var marked = starred_ids.contains(id) ? ArticleStatus.MARKED : ArticleStatus.UNMARKED; + var unread = unread_ids.contains(id) ? ArticleStatus.UNREAD : ArticleStatus.READ; + var changed = false; + if(article.getMarked() != marked) + { + article.setMarked(marked); + changed = true; + } + if(article.getUnread() != unread) + { + article.setUnread(unread); + changed = true; + } + articles.add(article); + } + writeArticles(articles); + } + } + + // Add new articles + for(int page = 1; ; ++page) + { + if(cancellable != null && cancellable.is_cancelled()) + { + return; + } + + var entries = m_api.get_entries(page, only_starred, since, feed_id); + if(entries.size == 0) + { + break; + } + + var articles = new Gee.ArrayList<Article>(); + foreach(var entry in entries) + { + articles.add( + new Article( + entry.id.to_string(), + entry.title, + entry.url, + entry.feed_id.to_string(), + unread_ids.contains(entry.id) ? ArticleStatus.UNREAD : ArticleStatus.READ, + starred_ids.contains(entry.id) ? ArticleStatus.MARKED : ArticleStatus.UNMARKED, + entry.content, + entry.summary, + entry.author, + entry.published != null ? entry.published : entry.created_at, + -1, + null, null) ); + } + writeArticles(articles); } - writeArticles(articles); + } + catch(Error e) + { + Logger.error(@"FeedbinInterface.getArticles: " + e.message); } } - catch(Error e) - { - Logger.error(@"FeedbinInterface.getArticles: " + e.message); - } -} } [ModuleInit] diff --git a/plugins/backend/feedbin/feedbinUtils.vala b/plugins/backend/feedbin/feedbinUtils.vala index 599fe14d..3803e503 100644 --- a/plugins/backend/feedbin/feedbinUtils.vala +++ b/plugins/backend/feedbin/feedbinUtils.vala @@ -14,56 +14,56 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.FeedbinUtils : GLib.Object { - -GLib.Settings m_settings; -Password m_password; - -public FeedbinUtils(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) -{ - if(settings_backend != null) - { - m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.feedbin", settings_backend); - } - else + + GLib.Settings m_settings; + Password m_password; + + public FeedbinUtils(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) { - m_settings = new GLib.Settings("org.gnome.feedreader.feedbin"); - } - - var password_schema = + if(settings_backend != null) + { + m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.feedbin", settings_backend); + } + else + { + m_settings = new GLib.Settings("org.gnome.feedreader.feedbin"); + } + + var password_schema = new Secret.Schema("org.gnome.feedreader.password", Secret.SchemaFlags.NONE, - "URL", Secret.SchemaAttributeType.STRING, - "Username", Secret.SchemaAttributeType.STRING); - m_password = new Password(secrets, password_schema, "FeedReader: feedbin login", () => { + "URL", Secret.SchemaAttributeType.STRING, + "Username", Secret.SchemaAttributeType.STRING); + m_password = new Password(secrets, password_schema, "FeedReader: feedbin login", () => { var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); attributes["URL"] = "feedbin.com"; attributes["Username"] = getUser(); return attributes; }); -} - -public string getUser() -{ - return Utils.gsettingReadString(m_settings, "username"); -} - -public void setUser(string user) -{ - Utils.gsettingWriteString(m_settings, "username", user); -} - -public string getPassword(Cancellable? cancellable = null) -{ - return m_password.get_password(cancellable); -} - -public void setPassword(string password, Cancellable? cancellable = null) -{ - m_password.set_password(password, cancellable); -} - -public void resetAccount(Cancellable? cancellable = null) -{ - Utils.resetSettings(m_settings); - m_password.delete_password(cancellable); -} + } + + public string getUser() + { + return Utils.gsettingReadString(m_settings, "username"); + } + + public void setUser(string user) + { + Utils.gsettingWriteString(m_settings, "username", user); + } + + public string getPassword(Cancellable? cancellable = null) + { + return m_password.get_password(cancellable); + } + + public void setPassword(string password, Cancellable? cancellable = null) + { + m_password.set_password(password, cancellable); + } + + public void resetAccount(Cancellable? cancellable = null) + { + Utils.resetSettings(m_settings); + m_password.delete_password(cancellable); + } } diff --git a/plugins/backend/feedhq/feedhqAPI.vala b/plugins/backend/feedhq/feedhqAPI.vala index 6005a3e1..0ecb3ceb 100644 --- a/plugins/backend/feedhq/feedhqAPI.vala +++ b/plugins/backend/feedhq/feedhqAPI.vala @@ -14,412 +14,412 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.FeedHQAPI : GLib.Object { - -public enum FeedHQSubscriptionAction { - EDIT, - SUBSCRIBE, - UNSUBSCRIBE -} - -private FeedHQConnection m_connection; -private FeedHQUtils m_utils; -private string m_userID; - -public FeedHQAPI (FeedHQUtils utils) -{ - m_utils = utils; - m_connection = new FeedHQConnection(m_utils); -} - - -public LoginResponse login() -{ - Logger.debug("FeedHQ Login"); - - if(m_utils.getAccessToken() == "") - { - var result = m_connection.getToken(); - if(m_connection.postToken() && getUserID()) + + public enum FeedHQSubscriptionAction { + EDIT, + SUBSCRIBE, + UNSUBSCRIBE + } + + private FeedHQConnection m_connection; + private FeedHQUtils m_utils; + private string m_userID; + + public FeedHQAPI (FeedHQUtils utils) + { + m_utils = utils; + m_connection = new FeedHQConnection(m_utils); + } + + + public LoginResponse login() + { + Logger.debug("FeedHQ Login"); + + if(m_utils.getAccessToken() == "") { - return result; + var result = m_connection.getToken(); + if(m_connection.postToken() && getUserID()) + { + return result; + } } - } - else if(getUserID()) - { - return LoginResponse.SUCCESS; - } - - return LoginResponse.UNKNOWN_ERROR; -} - -public bool ping() -{ - return Utils.ping("https://feedhq.org"); -} - -private bool getUserID() -{ - var msg = new feedhqMessage(); - msg.add("output", "json"); - var response = m_connection.send_get_request("user-info?" + msg.get()); - - if(response.status != 200) - { - return false; - } - - var parser = new Json.Parser(); - try{ - parser.load_from_data(response.data, -1); - } - catch (Error e) { - Logger.error("getUserID: Could not load message response"); - Logger.error(e.message); - return false; - } - var root = parser.get_root().get_object(); - - if(root.has_member("userId")) - { - m_userID = root.get_string_member("userId"); - m_utils.setUserID(m_userID); - Logger.info("FeedHQ: userID = " + m_userID); - - if(root.has_member("userName")) + else if(getUserID()) { - m_utils.setUser(root.get_string_member("userName")); + return LoginResponse.SUCCESS; } - return true; + + return LoginResponse.UNKNOWN_ERROR; } - - return false; -} - -public bool getFeeds(Gee.List<Feed> feeds) -{ - var msg = new feedhqMessage(); - msg.add("output", "json"); - var response = m_connection.send_get_request("subscription/list?" + msg.get()); - - if(response.status != 200) + + public bool ping() { - return false; - } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); + return Utils.ping("https://feedhq.org"); } - catch(Error e) + + private bool getUserID() { - Logger.error("getFeeds: Could not load message response"); - Logger.error(e.message); + var msg = new feedhqMessage(); + msg.add("output", "json"); + var response = m_connection.send_get_request("user-info?" + msg.get()); + + if(response.status != 200) + { + return false; + } + + var parser = new Json.Parser(); + try{ + parser.load_from_data(response.data, -1); + } + catch (Error e) { + Logger.error("getUserID: Could not load message response"); + Logger.error(e.message); + return false; + } + var root = parser.get_root().get_object(); + + if(root.has_member("userId")) + { + m_userID = root.get_string_member("userId"); + m_utils.setUserID(m_userID); + Logger.info("FeedHQ: userID = " + m_userID); + + if(root.has_member("userName")) + { + m_utils.setUser(root.get_string_member("userName")); + } + return true; + } + return false; } - var root = parser.get_root().get_object(); - var array = root.get_array_member("subscriptions"); - uint length = array.get_length(); - - for (uint i = 0; i < length; i++) + + public bool getFeeds(Gee.List<Feed> feeds) { - Json.Object object = array.get_object_element(i); - - string feedID = object.get_string_member("id"); - string url = object.has_member("htmlUrl") ? object.get_string_member("htmlUrl") : object.get_string_member("url"); - string? icon_url = object.has_member("iconUrl") ? "https:" + object.get_string_member("iconUrl") : null; - - uint catCount = object.get_array_member("categories").get_length(); - - var categories = new Gee.ArrayList<string>(); - for(uint j = 0; j < catCount; ++j) + var msg = new feedhqMessage(); + msg.add("output", "json"); + var response = m_connection.send_get_request("subscription/list?" + msg.get()); + + if(response.status != 200) { - categories.add(object.get_array_member("categories").get_object_element(j).get_string_member("id")); + return false; } - feeds.add( - new Feed( - feedID, - object.get_string_member("title"), - url, - 0, - categories, - icon_url, - object.get_string_member("url") + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("getFeeds: Could not load message response"); + Logger.error(e.message); + return false; + } + var root = parser.get_root().get_object(); + var array = root.get_array_member("subscriptions"); + uint length = array.get_length(); + + for (uint i = 0; i < length; i++) + { + Json.Object object = array.get_object_element(i); + + string feedID = object.get_string_member("id"); + string url = object.has_member("htmlUrl") ? object.get_string_member("htmlUrl") : object.get_string_member("url"); + string? icon_url = object.has_member("iconUrl") ? "https:" + object.get_string_member("iconUrl") : null; + + uint catCount = object.get_array_member("categories").get_length(); + + var categories = new Gee.ArrayList<string>(); + for(uint j = 0; j < catCount; ++j) + { + categories.add(object.get_array_member("categories").get_object_element(j).get_string_member("id")); + } + feeds.add( + new Feed( + feedID, + object.get_string_member("title"), + url, + 0, + categories, + icon_url, + object.get_string_member("url") ) ); + } + return true; } - return true; -} - -public bool getCategoriesAndTags(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags) -{ - var msg = new feedhqMessage(); - msg.add("output", "json"); - var response = m_connection.send_get_request("tag/list?" + msg.get()); - - if(response.status != 200) - { - return false; - } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); - } - catch(Error e) - { - Logger.error("getCategoriesAndTags: Could not load message response"); - Logger.error(e.message); - return false; - } - var root = parser.get_root().get_object(); - var array = root.get_array_member("tags"); - uint length = array.get_length(); - int orderID = 0; - - for (uint i = 0; i < length; i++) + + public bool getCategoriesAndTags(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags) { - Json.Object object = array.get_object_element(i); - string id = object.get_string_member("id"); - int start = id.last_index_of_char('/') + 1; - string title = id.substring(start); - - if(id.contains("/label/")) + var msg = new feedhqMessage(); + msg.add("output", "json"); + var response = m_connection.send_get_request("tag/list?" + msg.get()); + + if(response.status != 200) { - categories.add( - new Category( - id, - title, - 0, - orderID, - CategoryID.MASTER.to_string(), - 1 + return false; + } + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("getCategoriesAndTags: Could not load message response"); + Logger.error(e.message); + return false; + } + var root = parser.get_root().get_object(); + var array = root.get_array_member("tags"); + uint length = array.get_length(); + int orderID = 0; + + for (uint i = 0; i < length; i++) + { + Json.Object object = array.get_object_element(i); + string id = object.get_string_member("id"); + int start = id.last_index_of_char('/') + 1; + string title = id.substring(start); + + if(id.contains("/label/")) + { + categories.add( + new Category( + id, + title, + 0, + orderID, + CategoryID.MASTER.to_string(), + 1 ) ); - ++orderID; + ++orderID; + } } + return true; } - return true; -} - - -public int getTotalUnread() -{ - var msg = new feedhqMessage(); - msg.add("output", "json"); - var response = m_connection.send_get_request("unread-count?" + msg.get()); - - var parser = new Json.Parser(); - try{ - parser.load_from_data(response.data, -1); - } - catch (Error e) { - Logger.error("getTotalUnread: Could not load message response"); - Logger.error(e.message); - } - - var root = parser.get_root().get_object(); - var array = root.get_array_member("unreadcounts"); - uint length = array.get_length(); - int count = 0; - - for (uint i = 0; i < length; i++) - { - Json.Object object = array.get_object_element(i); - if(object.get_string_member("id").has_prefix("feed/")) + + + public int getTotalUnread() + { + var msg = new feedhqMessage(); + msg.add("output", "json"); + var response = m_connection.send_get_request("unread-count?" + msg.get()); + + var parser = new Json.Parser(); + try{ + parser.load_from_data(response.data, -1); + } + catch (Error e) { + Logger.error("getTotalUnread: Could not load message response"); + Logger.error(e.message); + } + + var root = parser.get_root().get_object(); + var array = root.get_array_member("unreadcounts"); + uint length = array.get_length(); + int count = 0; + + for (uint i = 0; i < length; i++) { - count += (int)object.get_int_member("count"); + Json.Object object = array.get_object_element(i); + if(object.get_string_member("id").has_prefix("feed/")) + { + count += (int)object.get_int_member("count"); + } + } - - } - - Logger.debug("getTotalUnread %i".printf(count)); - return count; -} - - -public string? updateArticles(Gee.List<string> ids, int count, string? continuation = null) -{ - var msg = new feedhqMessage(); - msg.add("output", "json"); - msg.add("n", count.to_string()); - msg.add("s", "user/-/state/com.google/read"); - if(continuation != null) - { - msg.add("c", continuation); - } - - var response = m_connection.send_get_request("stream/items/ids?" + msg.get()); - - if(response.status != 200) - { - return null; - } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); - } - catch(Error e) - { - Logger.error("updateArticles: Could not load message response"); - Logger.error(e.message); - } - - var root = parser.get_root().get_object(); - - if(!root.has_member("itemRefs")) - { - Logger.error("updateArticles: wrong response?"); - return null; - } - - var array = root.get_array_member("itemRefs"); - uint length = array.get_length(); - - for(uint i = 0; i < length; i++) - { - Json.Object object = array.get_object_element(i); - ids.add(object.get_string_member("id")); - } - - if(root.has_member("continuation") && root.get_string_member("continuation") != "") - { - return root.get_string_member("continuation"); + + Logger.debug("getTotalUnread %i".printf(count)); + return count; } - - return null; -} - -public string? getArticles(Gee.List<Article> articles, int count, ArticleStatus whatToGet = ArticleStatus.ALL, string? continuation = null, string? tagID = null, string? feed_id = null) -{ - var msg = new feedhqMessage(); - msg.add("output", "json"); - msg.add("n", count.to_string()); - - if(whatToGet == ArticleStatus.UNREAD) - { - msg.add("xt", "user/-/state/com.google/read"); - } - if(whatToGet == ArticleStatus.READ) + + + public string? updateArticles(Gee.List<string> ids, int count, string? continuation = null) { + var msg = new feedhqMessage(); + msg.add("output", "json"); + msg.add("n", count.to_string()); msg.add("s", "user/-/state/com.google/read"); - } - else if(whatToGet == ArticleStatus.MARKED) - { - msg.add("s", "user/-/state/com.google/starred"); - } - - if(continuation != null) - { - msg.add("c", continuation); - } - - string api_endpoint = "stream/contents"; - if(feed_id != null) - { - api_endpoint += "/" + feed_id; - } - else if(tagID != null) - { - api_endpoint += "/" + tagID; - } - var response = m_connection.send_get_request(api_endpoint + "?" + msg.get()); - - if(response.status != 200) - { + if(continuation != null) + { + msg.add("c", continuation); + } + + var response = m_connection.send_get_request("stream/items/ids?" + msg.get()); + + if(response.status != 200) + { + return null; + } + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("updateArticles: Could not load message response"); + Logger.error(e.message); + } + + var root = parser.get_root().get_object(); + + if(!root.has_member("itemRefs")) + { + Logger.error("updateArticles: wrong response?"); + return null; + } + + var array = root.get_array_member("itemRefs"); + uint length = array.get_length(); + + for(uint i = 0; i < length; i++) + { + Json.Object object = array.get_object_element(i); + ids.add(object.get_string_member("id")); + } + + if(root.has_member("continuation") && root.get_string_member("continuation") != "") + { + return root.get_string_member("continuation"); + } + return null; } - - Logger.debug(api_endpoint + "?" + msg.get()); - - var parser = new Json.Parser(); - try{ - parser.load_from_data(response.data, -1); - } - catch (Error e) { - Logger.error("getCategoriesAndTags: Could not load message response"); - Logger.error(e.message); - } - - var root = parser.get_root().get_object(); - var array = root.get_array_member("items"); - uint length = array.get_length(); - - for (uint i = 0; i < length; i++) + + public string? getArticles(Gee.List<Article> articles, int count, ArticleStatus whatToGet = ArticleStatus.ALL, string? continuation = null, string? tagID = null, string? feed_id = null) { - - Json.Object object = array.get_object_element(i); - string id = object.get_string_member("id"); - bool marked = false; - bool read = false; - var cats = object.get_array_member("categories"); - uint cat_length = cats.get_length(); - - var tags = new Gee.ArrayList<string>(); - for (uint j = 0; j < cat_length; j++) + var msg = new feedhqMessage(); + msg.add("output", "json"); + msg.add("n", count.to_string()); + + if(whatToGet == ArticleStatus.UNREAD) { - string cat = cats.get_string_element(j); - if(cat.has_suffix("com.google/starred")) - { - marked = true; - } - else if(cat.has_suffix("com.google/read")) - { - read = true; - } - else if(cat.contains("/label/")) - { - tags.add(cat); - } + msg.add("xt", "user/-/state/com.google/read"); } - - var enclosures = new Gee.ArrayList<Enclosure>(); - if(object.has_member("enclosure")) + if(whatToGet == ArticleStatus.READ) { - var attachments = object.get_array_member("enclosure"); - - uint mediaCount = 0; - if(attachments != null) + msg.add("s", "user/-/state/com.google/read"); + } + else if(whatToGet == ArticleStatus.MARKED) + { + msg.add("s", "user/-/state/com.google/starred"); + } + + if(continuation != null) + { + msg.add("c", continuation); + } + + string api_endpoint = "stream/contents"; + if(feed_id != null) + { + api_endpoint += "/" + feed_id; + } + else if(tagID != null) + { + api_endpoint += "/" + tagID; + } + var response = m_connection.send_get_request(api_endpoint + "?" + msg.get()); + + if(response.status != 200) + { + return null; + } + + Logger.debug(api_endpoint + "?" + msg.get()); + + var parser = new Json.Parser(); + try{ + parser.load_from_data(response.data, -1); + } + catch (Error e) { + Logger.error("getCategoriesAndTags: Could not load message response"); + Logger.error(e.message); + } + + var root = parser.get_root().get_object(); + var array = root.get_array_member("items"); + uint length = array.get_length(); + + for (uint i = 0; i < length; i++) + { + + Json.Object object = array.get_object_element(i); + string id = object.get_string_member("id"); + bool marked = false; + bool read = false; + var cats = object.get_array_member("categories"); + uint cat_length = cats.get_length(); + + var tags = new Gee.ArrayList<string>(); + for (uint j = 0; j < cat_length; j++) { - mediaCount = attachments.get_length(); + string cat = cats.get_string_element(j); + if(cat.has_suffix("com.google/starred")) + { + marked = true; + } + else if(cat.has_suffix("com.google/read")) + { + read = true; + } + else if(cat.contains("/label/")) + { + tags.add(cat); + } } - - for(int j = 0; j < mediaCount; ++j) + + var enclosures = new Gee.ArrayList<Enclosure>(); + if(object.has_member("enclosure")) { - var attachment = attachments.get_object_element(j); - enclosures.add( - new Enclosure(id, attachment.get_string_member("href"), - EnclosureType.from_string(attachment.get_string_member("type"))) + var attachments = object.get_array_member("enclosure"); + + uint mediaCount = 0; + if(attachments != null) + { + mediaCount = attachments.get_length(); + } + + for(int j = 0; j < mediaCount; ++j) + { + var attachment = attachments.get_object_element(j); + enclosures.add( + new Enclosure(id, attachment.get_string_member("href"), + EnclosureType.from_string(attachment.get_string_member("type"))) ); + } } - } - - articles.add(new Article( - id, - object.get_string_member("title"), - object.get_array_member("alternate").get_object_element(0).get_string_member("href"), - object.get_object_member("origin").get_string_member("streamId"), - read ? ArticleStatus.READ : ArticleStatus.UNREAD, - marked ? ArticleStatus.MARKED : ArticleStatus.UNMARKED, - null, - null, - null, - new DateTime.from_unix_local(object.get_int_member("published")), - -1, - tags, - enclosures - ) - ); - } - + + articles.add(new Article( + id, + object.get_string_member("title"), + object.get_array_member("alternate").get_object_element(0).get_string_member("href"), + object.get_object_member("origin").get_string_member("streamId"), + read ? ArticleStatus.READ : ArticleStatus.UNREAD, + marked ? ArticleStatus.MARKED : ArticleStatus.UNMARKED, + null, + null, + null, + new DateTime.from_unix_local(object.get_int_member("published")), + -1, + tags, + enclosures + ) + ); + } + if(root.has_member("continuation") && root.get_string_member("continuation") != "") { return root.get_string_member("continuation"); } - + return null; } @@ -428,7 +428,7 @@ public void edidTag(string articleID, string tagID, bool add = true) { var msg = new feedhqMessage(); msg.add("output", "json"); - + if(add) { msg.add("a", tagID); @@ -437,7 +437,7 @@ public void edidTag(string articleID, string tagID, bool add = true) { msg.add("r", tagID); } - + msg.add("i", articleID); m_connection.send_post_request("edit-tag", msg.get()); } @@ -478,43 +478,43 @@ public bool editSubscription(FeedHQSubscriptionAction action, string[] feedID, s { var msg = new feedhqMessage(); msg.add("output", "json"); - + switch(action) { - case FeedHQSubscriptionAction.EDIT: + case FeedHQSubscriptionAction.EDIT: msg.add("ac", "edit"); break; - case FeedHQSubscriptionAction.SUBSCRIBE: + case FeedHQSubscriptionAction.SUBSCRIBE: msg.add("ac", "subscribe"); break; - case FeedHQSubscriptionAction.UNSUBSCRIBE: + case FeedHQSubscriptionAction.UNSUBSCRIBE: msg.add("ac", "unsubscribe"); break; } - + foreach(string s in feedID) { msg.add("s", s); } - + if(title != null) { msg.add("t", title); } - + if(add != null && add != "") { msg.add("a", add); } - - + + if(remove != null && remove != "") { msg.add("r", remove); } - + Logger.debug(msg.get()); var response = m_connection.send_post_request("subscription/edit", msg.get()); - + return response.status == 200; } diff --git a/plugins/backend/feedhq/feedhqConnection.vala b/plugins/backend/feedhq/feedhqConnection.vala index 1f6fe043..9050f15c 100644 --- a/plugins/backend/feedhq/feedhqConnection.vala +++ b/plugins/backend/feedhq/feedhqConnection.vala @@ -14,162 +14,162 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.FeedHQConnection { -private string m_username; -private string m_api_code; -private string m_passwd; -private FeedHQUtils m_utils; -private Soup.Session m_session; - -public FeedHQConnection(FeedHQUtils utils) -{ - m_utils = utils; - m_username = m_utils.getUser(); - m_api_code = m_utils.getAccessToken(); - m_passwd = m_utils.getPasswd(); - m_session = new Soup.Session(); - m_session.user_agent = Constants.USER_AGENT; -} - -public LoginResponse getToken() -{ - Logger.debug("FeedHQ Connection: getToken()"); - - if(m_username == "" && m_passwd == "") + private string m_username; + private string m_api_code; + private string m_passwd; + private FeedHQUtils m_utils; + private Soup.Session m_session; + + public FeedHQConnection(FeedHQUtils utils) { - return LoginResponse.ALL_EMPTY; + m_utils = utils; + m_username = m_utils.getUser(); + m_api_code = m_utils.getAccessToken(); + m_passwd = m_utils.getPasswd(); + m_session = new Soup.Session(); + m_session.user_agent = Constants.USER_AGENT; } - if(m_username == "") + + public LoginResponse getToken() { - return LoginResponse.MISSING_USER; - } - if(m_passwd == "") - { - return LoginResponse.MISSING_PASSWD; - } - - var message = new Soup.Message("POST", "https://feedhq.org/accounts/ClientLogin"); - string message_string = "Email=" + m_username + "&Passwd=" + m_passwd; - message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string.data); - m_session.send_message(message); - string response = (string)message.response_body.flatten().data; - try{ - - var regex = new Regex(".*\\w\\s.*\\w\\sAuth="); - if(regex.match(response)) + Logger.debug("FeedHQ Connection: getToken()"); + + if(m_username == "" && m_passwd == "") + { + return LoginResponse.ALL_EMPTY; + } + if(m_username == "") + { + return LoginResponse.MISSING_USER; + } + if(m_passwd == "") { - string split = regex.replace(response, -1,0,""); - Logger.debug("FeedHQ Authcode : " + split); - m_utils.setAccessToken(split.strip()); - return LoginResponse.SUCCESS; + return LoginResponse.MISSING_PASSWD; + } + + var message = new Soup.Message("POST", "https://feedhq.org/accounts/ClientLogin"); + string message_string = "Email=" + m_username + "&Passwd=" + m_passwd; + message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string.data); + m_session.send_message(message); + string response = (string)message.response_body.flatten().data; + try{ + + var regex = new Regex(".*\\w\\s.*\\w\\sAuth="); + if(regex.match(response)) + { + string split = regex.replace(response, -1,0,""); + Logger.debug("FeedHQ Authcode : " + split); + m_utils.setAccessToken(split.strip()); + return LoginResponse.SUCCESS; + } + else + { + Logger.debug(response); + return LoginResponse.WRONG_LOGIN; + } } - else + catch(Error e) { - Logger.debug(response); - return LoginResponse.WRONG_LOGIN; + Logger.error("FeedHQConnection - getToken: Could not load message response"); + Logger.error(e.message); + return LoginResponse.UNKNOWN_ERROR; } } - catch(Error e) + + + public bool postToken() { - Logger.error("FeedHQConnection - getToken: Could not load message response"); - Logger.error(e.message); - return LoginResponse.UNKNOWN_ERROR; - } -} - - -public bool postToken() -{ - Logger.debug("FeedHQ Connection: postToken()"); - - var message = new Soup.Message("GET", FeedHQSecret.base_uri + "token?output=json"); - - string oldauth = "GoogleLogin auth=" + m_utils.getAccessToken(); - message.request_headers.append("Authorization", oldauth); - m_session.send_message(message); - - if(message.status_code != 200) - { - Logger.debug("FeedHQ post token failed"); - return false; + Logger.debug("FeedHQ Connection: postToken()"); + + var message = new Soup.Message("GET", FeedHQSecret.base_uri + "token?output=json"); + + string oldauth = "GoogleLogin auth=" + m_utils.getAccessToken(); + message.request_headers.append("Authorization", oldauth); + m_session.send_message(message); + + if(message.status_code != 200) + { + Logger.debug("FeedHQ post token failed"); + return false; + } + + string response = (string)message.response_body.data; + Logger.debug("FeedHQ post token : " + response); + m_utils.setPostToken(response); + + return true; + } - - string response = (string)message.response_body.data; - Logger.debug("FeedHQ post token : " + response); - m_utils.setPostToken(response); - - return true; - -} -public Response send_get_request(string path, string? message_string = null) -{ - return send_request(path, "GET", message_string); -} - -public Response send_post_request(string path, string? message_string = null) -{ - return send_request(path, "POST", message_string); -} - - - -private Response send_request(string path, string type, string? message_string = null) -{ - var message = new Soup.Message(type, FeedHQSecret.base_uri + path); - - string oldauth = "GoogleLogin auth=" + m_utils.getAccessToken(); - message.request_headers.append("Authorization", oldauth); - var message_string_post = message_string + "&T=" + m_utils.getPostToken(); - if(message_string != null) + public Response send_get_request(string path, string? message_string = null) { - message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string_post.data); + return send_request(path, "GET", message_string); } - - m_session.send_message(message); - - if(message.status_code != 200) + + public Response send_post_request(string path, string? message_string = null) { - Logger.warning(@"feedHQConnection: message unexpected response - $message_string"); + return send_request(path, "POST", message_string); } - - if((uint)message.status_code == 401) + + + + private Response send_request(string path, string type, string? message_string = null) { - Logger.debug("FeedHQ Post Token Expired"); - postToken(); - return send_request(path, type, message_string); + var message = new Soup.Message(type, FeedHQSecret.base_uri + path); + + string oldauth = "GoogleLogin auth=" + m_utils.getAccessToken(); + message.request_headers.append("Authorization", oldauth); + var message_string_post = message_string + "&T=" + m_utils.getPostToken(); + if(message_string != null) + { + message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string_post.data); + } + + m_session.send_message(message); + + if(message.status_code != 200) + { + Logger.warning(@"feedHQConnection: message unexpected response - $message_string"); + } + + if((uint)message.status_code == 401) + { + Logger.debug("FeedHQ Post Token Expired"); + postToken(); + return send_request(path, type, message_string); + } + + return Response() { + status = message.status_code, + data = (string)message.response_body.flatten().data + }; } - - return Response() { - status = message.status_code, - data = (string)message.response_body.flatten().data - }; -} - + } public class FeedReader.feedhqMessage { - -string request = ""; - -public feedhqMessage() -{ - -} - -public void add(string parameter, string val) -{ - if(request != "") + + string request = ""; + + public feedhqMessage() { - request += "&"; + + } + + public void add(string parameter, string val) + { + if(request != "") + { + request += "&"; + } + + request += parameter; + request += "="; + request += GLib.Uri.escape_string(val); + } + + public string get() + { + return request; } - - request += parameter; - request += "="; - request += GLib.Uri.escape_string(val); -} - -public string get() -{ - return request; -} } diff --git a/plugins/backend/feedhq/feedhqInterface.vala b/plugins/backend/feedhq/feedhqInterface.vala index 519a173d..e5866abe 100644 --- a/plugins/backend/feedhq/feedhqInterface.vala +++ b/plugins/backend/feedhq/feedhqInterface.vala @@ -14,421 +14,421 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.FeedHQInterface : FeedServerInterface { - -private FeedHQAPI m_api; -private FeedHQUtils m_utils; -private Gtk.Entry m_userEntry; -private Gtk.Entry m_passwordEntry; - -public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) -{ - m_utils = new FeedHQUtils(settings_backend, secrets); - m_api = new FeedHQAPI(m_utils); -} - -public override string getWebsite() -{ - return "https://feedhq.org/"; -} - -public override BackendFlags getFlags() -{ - return (BackendFlags.HOSTED | BackendFlags.PROPRIETARY | BackendFlags.PAID); -} - -public override string getID() -{ - return "feedhq"; -} - -public override string iconName() -{ - return "feed-service-feedhq"; -} - -public override string serviceName() -{ - return "FeedHQ"; -} - -public override bool needWebLogin() -{ - return false; -} - -public override Gtk.Box? getWidget() -{ - var user_label = new Gtk.Label(_("Username:")); - var password_label = new Gtk.Label(_("Password:")); - - user_label.set_alignment(1.0f, 0.5f); - password_label.set_alignment(1.0f, 0.5f); - - user_label.set_hexpand(true); - password_label.set_hexpand(true); - - m_userEntry = new Gtk.Entry(); - m_passwordEntry = new Gtk.Entry(); - - m_userEntry.activate.connect(() => { tryLogin(); }); - m_passwordEntry.activate.connect(() => { tryLogin(); }); - - m_passwordEntry.set_invisible_char('*'); - m_passwordEntry.set_visibility(false); - - var grid = new Gtk.Grid(); - grid.set_column_spacing(10); - grid.set_row_spacing(10); - grid.set_valign(Gtk.Align.CENTER); - grid.set_halign(Gtk.Align.CENTER); - - grid.attach(user_label, 0, 0, 1, 1); - grid.attach(m_userEntry, 1, 0, 1, 1); - grid.attach(password_label, 0, 1, 1, 1); - grid.attach(m_passwordEntry, 1, 1, 1, 1); - - var logo = new Gtk.Image.from_icon_name("feed-service-feedhq", Gtk.IconSize.MENU); - - var loginLabel = new Gtk.Label(_("Please log in to FeedHQ and enjoy using FeedReader")); - loginLabel.get_style_context().add_class("h2"); - loginLabel.set_justify(Gtk.Justification.CENTER); - loginLabel.set_lines(3); - - var loginButton = new Gtk.Button.with_label(_("Login")); - loginButton.halign = Gtk.Align.END; - loginButton.set_size_request(80, 30); - loginButton.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); - loginButton.clicked.connect(() => { tryLogin(); }); - - var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10); - box.valign = Gtk.Align.CENTER; - box.halign = Gtk.Align.CENTER; - box.pack_start(loginLabel, false, false, 10); - box.pack_start(logo, false, false, 10); - box.pack_start(grid, true, true, 10); - box.pack_end(loginButton, false, false, 20); - - m_userEntry.set_text(m_utils.getUser()); - m_passwordEntry.set_text(m_utils.getPasswd()); - - return box; -} - -public override void writeData() -{ - m_utils.setUser(m_userEntry.get_text()); - m_utils.setPassword(m_passwordEntry.get_text()); -} - -public override bool supportTags() -{ - return false; -} - -public override bool supportFeedManipulation() -{ - return true; -} - -public override bool doInitSync() -{ - return true; -} - -public override string symbolicIcon() -{ - return "feed-service-feedhq-symbolic"; -} - -public override string accountName() -{ - return m_utils.getUser(); -} - -public override string getServerURL() -{ - return "FeedHQ.org"; -} - -public override string uncategorizedID() -{ - return ""; -} - -public override bool supportCategories() -{ - return true; -} - -public override bool hideCategoryWhenEmpty(string cadID) -{ - return false; -} - -public override bool supportMultiLevelCategories() -{ - return false; -} - -public override bool supportMultiCategoriesPerFeed() -{ - return false; -} - -public override bool syncFeedsAndCategories() -{ - return true; -} - -public override bool tagIDaffectedByNameChange() -{ - return true; -} - -public override void resetAccount() -{ - m_utils.resetAccount(); -} - -public override bool useMaxArticles() -{ - return true; -} - -public override LoginResponse login() -{ - return m_api.login(); -} - -public override void setArticleIsRead(string articleIDs, ArticleStatus read) -{ - if(read == ArticleStatus.READ) + + private FeedHQAPI m_api; + private FeedHQUtils m_utils; + private Gtk.Entry m_userEntry; + private Gtk.Entry m_passwordEntry; + + public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) { - m_api.edidTag(articleIDs, "user/-/state/com.google/read"); + m_utils = new FeedHQUtils(settings_backend, secrets); + m_api = new FeedHQAPI(m_utils); } - else + + public override string getWebsite() { - m_api.edidTag(articleIDs, "user/-/state/com.google/read", false); + return "https://feedhq.org/"; } -} - -public override void setArticleIsMarked(string articleID, ArticleStatus marked) -{ - if(marked == ArticleStatus.MARKED) + + public override BackendFlags getFlags() { - m_api.edidTag(articleID, "user/-/state/com.google/starred"); + return (BackendFlags.HOSTED | BackendFlags.PROPRIETARY | BackendFlags.PAID); } - else + + public override string getID() { - m_api.edidTag(articleID, "user/-/state/com.google/starred", false); + return "feedhq"; } -} - -public override bool alwaysSetReadByID() -{ - return false; -} - -public override void setFeedRead(string feedID) -{ - m_api.markAsRead(feedID); -} - -public override void setCategoryRead(string catID) -{ - m_api.markAsRead(catID); -} - -public override void markAllItemsRead() -{ - var db = DataBase.readOnly(); - var categories = db.read_categories(); - foreach(Category cat in categories) + + public override string iconName() { - m_api.markAsRead(cat.getCatID()); + return "feed-service-feedhq"; } - - var feeds = db.read_feeds_without_cat(); - foreach(Feed feed in feeds) + + public override string serviceName() { - m_api.markAsRead(feed.getFeedID()); + return "FeedHQ"; } -} - -public override void tagArticle(string articleID, string tagID) -{ - return; -} - -public override void removeArticleTag(string articleID, string tagID) -{ - return; -} - -public override string createTag(string caption) -{ - return ":("; -} - -public override void deleteTag(string tagID) -{ - return; -} - -public override void renameTag(string tagID, string title) -{ - return; -} - -public override bool serverAvailable() -{ - return m_api.ping(); -} - -public override bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) -{ - feedID = "feed/" + feedURL; - bool success = false; - - if(catID == null && newCatName != null) + + public override bool needWebLogin() { - string newCatID = m_api.composeTagID(newCatName); - Logger.debug(newCatID); - success = m_api.editSubscription(FeedHQAPI.FeedHQSubscriptionAction.SUBSCRIBE, {"feed/"+feedURL}, null, newCatID); + return false; } - else + + public override Gtk.Box? getWidget() { - success = m_api.editSubscription(FeedHQAPI.FeedHQSubscriptionAction.SUBSCRIBE, {"feed/"+feedURL}, null, catID); + var user_label = new Gtk.Label(_("Username:")); + var password_label = new Gtk.Label(_("Password:")); + + user_label.set_alignment(1.0f, 0.5f); + password_label.set_alignment(1.0f, 0.5f); + + user_label.set_hexpand(true); + password_label.set_hexpand(true); + + m_userEntry = new Gtk.Entry(); + m_passwordEntry = new Gtk.Entry(); + + m_userEntry.activate.connect(() => { tryLogin(); }); + m_passwordEntry.activate.connect(() => { tryLogin(); }); + + m_passwordEntry.set_invisible_char('*'); + m_passwordEntry.set_visibility(false); + + var grid = new Gtk.Grid(); + grid.set_column_spacing(10); + grid.set_row_spacing(10); + grid.set_valign(Gtk.Align.CENTER); + grid.set_halign(Gtk.Align.CENTER); + + grid.attach(user_label, 0, 0, 1, 1); + grid.attach(m_userEntry, 1, 0, 1, 1); + grid.attach(password_label, 0, 1, 1, 1); + grid.attach(m_passwordEntry, 1, 1, 1, 1); + + var logo = new Gtk.Image.from_icon_name("feed-service-feedhq", Gtk.IconSize.MENU); + + var loginLabel = new Gtk.Label(_("Please log in to FeedHQ and enjoy using FeedReader")); + loginLabel.get_style_context().add_class("h2"); + loginLabel.set_justify(Gtk.Justification.CENTER); + loginLabel.set_lines(3); + + var loginButton = new Gtk.Button.with_label(_("Login")); + loginButton.halign = Gtk.Align.END; + loginButton.set_size_request(80, 30); + loginButton.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); + loginButton.clicked.connect(() => { tryLogin(); }); + + var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10); + box.valign = Gtk.Align.CENTER; + box.halign = Gtk.Align.CENTER; + box.pack_start(loginLabel, false, false, 10); + box.pack_start(logo, false, false, 10); + box.pack_start(grid, true, true, 10); + box.pack_end(loginButton, false, false, 20); + + m_userEntry.set_text(m_utils.getUser()); + m_passwordEntry.set_text(m_utils.getPasswd()); + + return box; } - - if(!success) + + public override void writeData() { - errmsg = @"feedHQ could not subscribe to $feedURL"; + m_utils.setUser(m_userEntry.get_text()); + m_utils.setPassword(m_passwordEntry.get_text()); } - else + + public override bool supportTags() { - errmsg = ""; + return false; } - - return success; -} - -public override void removeFeed(string feedID) -{ - m_api.editSubscription(FeedHQAPI.FeedHQSubscriptionAction.UNSUBSCRIBE, {feedID}); -} - -public override void renameFeed(string feedID, string title) -{ - m_api.editSubscription(FeedHQAPI.FeedHQSubscriptionAction.EDIT, {feedID}, title); -} - -public override void moveFeed(string feedID, string newCatID, string? currentCatID) -{ - m_api.editSubscription(FeedHQAPI.FeedHQSubscriptionAction.EDIT, {feedID}, null, newCatID, currentCatID); -} - -public override string createCategory(string title, string? parentID) -{ - return m_api.composeTagID(title); -} - -public override void renameCategory(string catID, string title) -{ - m_api.renameTag(catID, title); -} - -public override void moveCategory(string catID, string newParentID) -{ - return; -} - -public override void deleteCategory(string catID) -{ - m_api.deleteTag(catID); -} - -public override void removeCatFromFeed(string feedID, string catID) -{ - return; -} - -public override void importOPML(string opml) -{ - m_api.import(opml); -} - -public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) -{ - if(m_api.getFeeds(feeds)) + + public override bool supportFeedManipulation() + { + return true; + } + + public override bool doInitSync() + { + return true; + } + + public override string symbolicIcon() + { + return "feed-service-feedhq-symbolic"; + } + + public override string accountName() + { + return m_utils.getUser(); + } + + public override string getServerURL() + { + return "FeedHQ.org"; + } + + public override string uncategorizedID() + { + return ""; + } + + public override bool supportCategories() + { + return true; + } + + public override bool hideCategoryWhenEmpty(string cadID) + { + return false; + } + + public override bool supportMultiLevelCategories() + { + return false; + } + + public override bool supportMultiCategoriesPerFeed() + { + return false; + } + + public override bool syncFeedsAndCategories() + { + return true; + } + + public override bool tagIDaffectedByNameChange() + { + return true; + } + + public override void resetAccount() + { + m_utils.resetAccount(); + } + + public override bool useMaxArticles() { - if(cancellable != null && cancellable.is_cancelled()) + return true; + } + + public override LoginResponse login() + { + return m_api.login(); + } + + public override void setArticleIsRead(string articleIDs, ArticleStatus read) + { + if(read == ArticleStatus.READ) { - return false; + m_api.edidTag(articleIDs, "user/-/state/com.google/read"); } - - if(m_api.getCategoriesAndTags(feeds, categories, tags)) + else { - return true; + m_api.edidTag(articleIDs, "user/-/state/com.google/read", false); } } - - return false; -} - -public override int getUnreadCount() -{ - return m_api.getTotalUnread(); -} - -public override void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) -{ - if(whatToGet == ArticleStatus.READ) + + public override void setArticleIsMarked(string articleID, ArticleStatus marked) + { + if(marked == ArticleStatus.MARKED) + { + m_api.edidTag(articleID, "user/-/state/com.google/starred"); + } + else + { + m_api.edidTag(articleID, "user/-/state/com.google/starred", false); + } + } + + public override bool alwaysSetReadByID() + { + return false; + } + + public override void setFeedRead(string feedID) + { + m_api.markAsRead(feedID); + } + + public override void setCategoryRead(string catID) + { + m_api.markAsRead(catID); + } + + public override void markAllItemsRead() + { + var db = DataBase.readOnly(); + var categories = db.read_categories(); + foreach(Category cat in categories) + { + m_api.markAsRead(cat.getCatID()); + } + + var feeds = db.read_feeds_without_cat(); + foreach(Feed feed in feeds) + { + m_api.markAsRead(feed.getFeedID()); + } + } + + public override void tagArticle(string articleID, string tagID) { return; } - else if(whatToGet == ArticleStatus.ALL) + + public override void removeArticleTag(string articleID, string tagID) { - var unreadIDs = new Gee.LinkedList<string>(); - string? continuation = null; - - do + return; + } + + public override string createTag(string caption) + { + return ":("; + } + + public override void deleteTag(string tagID) + { + return; + } + + public override void renameTag(string tagID, string title) { - if(cancellable != null && cancellable.is_cancelled()) + return; + } + + public override bool serverAvailable() + { + return m_api.ping(); + } + + public override bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) + { + feedID = "feed/" + feedURL; + bool success = false; + + if(catID == null && newCatName != null) { - return; + string newCatID = m_api.composeTagID(newCatName); + Logger.debug(newCatID); + success = m_api.editSubscription(FeedHQAPI.FeedHQSubscriptionAction.SUBSCRIBE, {"feed/"+feedURL}, null, newCatID); } - - continuation = m_api.updateArticles(unreadIDs, 1000, continuation); + else + { + success = m_api.editSubscription(FeedHQAPI.FeedHQSubscriptionAction.SUBSCRIBE, {"feed/"+feedURL}, null, catID); + } + + if(!success) + { + errmsg = @"feedHQ could not subscribe to $feedURL"; + } + else + { + errmsg = ""; + } + + return success; } - while(continuation != null); - DataBase.writeAccess().updateArticlesByID(unreadIDs, "unread"); - } - - var articles = new Gee.LinkedList<Article>(); - string? continuation = null; - string? FeedHQ_feedID = (isTagID) ? null : feedID; - string? FeedHQ_tagID = (isTagID) ? feedID : null; - - do - { - if(cancellable != null && cancellable.is_cancelled()) + + public override void removeFeed(string feedID) + { + m_api.editSubscription(FeedHQAPI.FeedHQSubscriptionAction.UNSUBSCRIBE, {feedID}); + } + + public override void renameFeed(string feedID, string title) + { + m_api.editSubscription(FeedHQAPI.FeedHQSubscriptionAction.EDIT, {feedID}, title); + } + + public override void moveFeed(string feedID, string newCatID, string? currentCatID) + { + m_api.editSubscription(FeedHQAPI.FeedHQSubscriptionAction.EDIT, {feedID}, null, newCatID, currentCatID); + } + + public override string createCategory(string title, string? parentID) + { + return m_api.composeTagID(title); + } + + public override void renameCategory(string catID, string title) + { + m_api.renameTag(catID, title); + } + + public override void moveCategory(string catID, string newParentID) { return; } - - continuation = m_api.getArticles(articles, count, whatToGet, continuation, FeedHQ_tagID, FeedHQ_feedID); + + public override void deleteCategory(string catID) + { + m_api.deleteTag(catID); + } + + public override void removeCatFromFeed(string feedID, string catID) + { + return; + } + + public override void importOPML(string opml) + { + m_api.import(opml); + } + + public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) + { + if(m_api.getFeeds(feeds)) + { + if(cancellable != null && cancellable.is_cancelled()) + { + return false; + } + + if(m_api.getCategoriesAndTags(feeds, categories, tags)) + { + return true; + } + } + + return false; + } + + public override int getUnreadCount() + { + return m_api.getTotalUnread(); + } + + public override void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) + { + if(whatToGet == ArticleStatus.READ) + { + return; + } + else if(whatToGet == ArticleStatus.ALL) + { + var unreadIDs = new Gee.LinkedList<string>(); + string? continuation = null; + + do + { + if(cancellable != null && cancellable.is_cancelled()) + { + return; + } + + continuation = m_api.updateArticles(unreadIDs, 1000, continuation); + } + while(continuation != null); + DataBase.writeAccess().updateArticlesByID(unreadIDs, "unread"); + } + + var articles = new Gee.LinkedList<Article>(); + string? continuation = null; + string? FeedHQ_feedID = (isTagID) ? null : feedID; + string? FeedHQ_tagID = (isTagID) ? feedID : null; + + do + { + if(cancellable != null && cancellable.is_cancelled()) + { + return; + } + + continuation = m_api.getArticles(articles, count, whatToGet, continuation, FeedHQ_tagID, FeedHQ_feedID); + } + while(continuation != null); + + writeArticles(articles); + } + + } + + [ModuleInit] + public void peas_register_types(GLib.TypeModule module) + { + var objmodule = module as Peas.ObjectModule; + objmodule.register_extension_type(typeof(FeedReader.FeedServerInterface), typeof(FeedReader.FeedHQInterface)); } - while(continuation != null); - - writeArticles(articles); -} - -} - -[ModuleInit] -public void peas_register_types(GLib.TypeModule module) -{ - var objmodule = module as Peas.ObjectModule; - objmodule.register_extension_type(typeof(FeedReader.FeedServerInterface), typeof(FeedReader.FeedHQInterface)); -} diff --git a/plugins/backend/feedhq/feedhqUtils.vala b/plugins/backend/feedhq/feedhqUtils.vala index 983204d3..f108a336 100644 --- a/plugins/backend/feedhq/feedhqUtils.vala +++ b/plugins/backend/feedhq/feedhqUtils.vala @@ -14,110 +14,110 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. namespace FeedReader.FeedHQSecret { -const string base_uri = "https://feedhq.org/reader/api/0/"; + const string base_uri = "https://feedhq.org/reader/api/0/"; } public class FeedReader.FeedHQUtils : GLib.Object { - -private GLib.Settings m_settings; -private Password m_password; - -public FeedHQUtils(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) -{ - if(settings_backend != null) + + private GLib.Settings m_settings; + private Password m_password; + + public FeedHQUtils(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) { - m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.feedhq", settings_backend); - } - else - { - m_settings = new GLib.Settings("org.gnome.feedreader.feedhq"); - } - - var pwSchema = new Secret.Schema ("org.gnome.feedreader.feedhq", Secret.SchemaFlags.NONE, - "type", Secret.SchemaAttributeType.STRING, - "Username", Secret.SchemaAttributeType.STRING); - m_password = new Password(secrets, pwSchema, "Feedserver login", () => { + if(settings_backend != null) + { + m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.feedhq", settings_backend); + } + else + { + m_settings = new GLib.Settings("org.gnome.feedreader.feedhq"); + } + + var pwSchema = new Secret.Schema ("org.gnome.feedreader.feedhq", Secret.SchemaFlags.NONE, + "type", Secret.SchemaAttributeType.STRING, + "Username", Secret.SchemaAttributeType.STRING); + m_password = new Password(secrets, pwSchema, "Feedserver login", () => { var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); attributes["type"] = "FeedHQ"; attributes["Username"] = getUser(); return attributes; }); -} - -public string getUser() -{ - return Utils.gsettingReadString(m_settings, "username"); -} - -public void setUser(string user) -{ - Utils.gsettingWriteString(m_settings, "username", user); -} - -public string getAccessToken() -{ - return Utils.gsettingReadString(m_settings, "access-token"); -} - -public void setAccessToken(string token) -{ - Utils.gsettingWriteString(m_settings, "access-token", token); -} - -public string getPostToken() -{ - return Utils.gsettingReadString(m_settings, "post-token"); -} - -public void setPostToken(string token) -{ - Utils.gsettingWriteString(m_settings, "post-token", token); -} -public string getUserID() -{ - return Utils.gsettingReadString(m_settings, "user-id"); -} - -public void setUserID(string id) -{ - Utils.gsettingWriteString(m_settings, "user-id", id); -} - -public string getEmail() -{ - return Utils.gsettingReadString(m_settings, "user-email"); -} - -public void setEmail(string email) -{ - Utils.gsettingWriteString(m_settings, "user-email", email); -} - -public void resetAccount() -{ - Utils.resetSettings(m_settings); - m_password.delete_password(); -} - -public bool tagIsCat(string tagID, Gee.List<Feed> feeds) -{ - foreach(Feed feed in feeds) + } + + public string getUser() + { + return Utils.gsettingReadString(m_settings, "username"); + } + + public void setUser(string user) + { + Utils.gsettingWriteString(m_settings, "username", user); + } + + public string getAccessToken() + { + return Utils.gsettingReadString(m_settings, "access-token"); + } + + public void setAccessToken(string token) + { + Utils.gsettingWriteString(m_settings, "access-token", token); + } + + public string getPostToken() + { + return Utils.gsettingReadString(m_settings, "post-token"); + } + + public void setPostToken(string token) + { + Utils.gsettingWriteString(m_settings, "post-token", token); + } + public string getUserID() + { + return Utils.gsettingReadString(m_settings, "user-id"); + } + + public void setUserID(string id) + { + Utils.gsettingWriteString(m_settings, "user-id", id); + } + + public string getEmail() + { + return Utils.gsettingReadString(m_settings, "user-email"); + } + + public void setEmail(string email) + { + Utils.gsettingWriteString(m_settings, "user-email", email); + } + + public void resetAccount() + { + Utils.resetSettings(m_settings); + m_password.delete_password(); + } + + public bool tagIsCat(string tagID, Gee.List<Feed> feeds) { - if(feed.hasCat(tagID)) + foreach(Feed feed in feeds) { - return true; + if(feed.hasCat(tagID)) + { + return true; + } } + return false; + } + + public string getPasswd() + { + return m_password.get_password(); + } + + public void setPassword(string passwd) + { + m_password.set_password(passwd); } - return false; -} - -public string getPasswd() -{ - return m_password.get_password(); -} - -public void setPassword(string passwd) -{ - m_password.set_password(passwd); -} } diff --git a/plugins/backend/feedly/feedlyAPI.vala b/plugins/backend/feedly/feedlyAPI.vala index 8c5d3f46..57d151b0 100644 --- a/plugins/backend/feedly/feedlyAPI.vala +++ b/plugins/backend/feedly/feedlyAPI.vala @@ -14,747 +14,747 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.FeedlyAPI : Object { - -private FeedlyConnection m_connection; -private string m_userID; -private Json.Array m_unreadcounts; -private FeedlyUtils m_utils; - -public FeedlyAPI(FeedlyUtils utils) { - m_utils = utils; - m_connection = new FeedlyConnection(m_utils); -} - -public string createCatID(string title) -{ - return "user/%s/category/%s".printf(m_userID, title); -} - -public string getMarkedID() -{ - return "user/" + m_userID + "/tag/global.saved"; -} - -public LoginResponse login() -{ - Logger.debug("feedly backend: login"); - - if(!Utils.ping("http://feedly.com/")) - { - return LoginResponse.NO_CONNECTION; - } - - if(m_utils.getRefreshToken() == "") - { - m_connection.getToken(); - } - - if(tokenStillValid() == ConnectionError.INVALID_SESSIONID) - { - Logger.debug("refresh token"); - m_connection.refreshToken(); - } - - if(getUserID()) - { - Logger.debug("feedly: login success"); - return LoginResponse.SUCCESS; + + private FeedlyConnection m_connection; + private string m_userID; + private Json.Array m_unreadcounts; + private FeedlyUtils m_utils; + + public FeedlyAPI(FeedlyUtils utils) { + m_utils = utils; + m_connection = new FeedlyConnection(m_utils); } - - m_utils.setAccessToken(""); - m_utils.setRefreshToken(""); - m_utils.setApiCode(""); - - return LoginResponse.UNKNOWN_ERROR; -} - -private bool getUserID() -{ - var response = m_connection.send_get_request_to_feedly ("/v3/profile/"); - - if(response.status != 200) + + public string createCatID(string title) { - return false; + return "user/%s/category/%s".printf(m_userID, title); } - - var parser = new Json.Parser(); - try + + public string getMarkedID() { - parser.load_from_data(response.data, -1); + return "user/" + m_userID + "/tag/global.saved"; } - catch(Error e) + + public LoginResponse login() { - Logger.error("getUserID: Could not load message response"); - Logger.error(e.message); - return false; - } - var root = parser.get_root().get_object(); - - if(root.has_member("id")) - { - m_userID = root.get_string_member("id"); - Logger.info("feedly: userID = " + m_userID); - - if(root.has_member("email")) + Logger.debug("feedly backend: login"); + + if(!Utils.ping("http://feedly.com/")) { - m_utils.setEmail(root.get_string_member("email")); + return LoginResponse.NO_CONNECTION; } - else if(root.has_member("givenName")) + + if(m_utils.getRefreshToken() == "") { - m_utils.setEmail(root.get_string_member("givenName")); + m_connection.getToken(); } - else if(root.has_member("fullName")) + + if(tokenStillValid() == ConnectionError.INVALID_SESSIONID) { - m_utils.setEmail(root.get_string_member("fullName")); + Logger.debug("refresh token"); + m_connection.refreshToken(); } - else if(root.has_member("google")) + + if(getUserID()) { - m_utils.setEmail(root.get_string_member("google")); + Logger.debug("feedly: login success"); + return LoginResponse.SUCCESS; } - else if(root.has_member("reader")) + + m_utils.setAccessToken(""); + m_utils.setRefreshToken(""); + m_utils.setApiCode(""); + + return LoginResponse.UNKNOWN_ERROR; + } + + private bool getUserID() + { + var response = m_connection.send_get_request_to_feedly ("/v3/profile/"); + + if(response.status != 200) { - m_utils.setEmail(root.get_string_member("reader")); + return false; } - else if(root.has_member("twitterUserId")) + + var parser = new Json.Parser(); + try { - m_utils.setEmail(root.get_string_member("twitterUserId")); + parser.load_from_data(response.data, -1); } - else if(root.has_member("facebookUserId")) + catch(Error e) { - m_utils.setEmail(root.get_string_member("facebookUserId")); + Logger.error("getUserID: Could not load message response"); + Logger.error(e.message); + return false; } - else if(root.has_member("wordPressId")) + var root = parser.get_root().get_object(); + + if(root.has_member("id")) { - m_utils.setEmail(root.get_string_member("wordPressId")); - } - else if(root.has_member("windowsLiveId")) - { - m_utils.setEmail(root.get_string_member("windowsLiveId")); + m_userID = root.get_string_member("id"); + Logger.info("feedly: userID = " + m_userID); + + if(root.has_member("email")) + { + m_utils.setEmail(root.get_string_member("email")); + } + else if(root.has_member("givenName")) + { + m_utils.setEmail(root.get_string_member("givenName")); + } + else if(root.has_member("fullName")) + { + m_utils.setEmail(root.get_string_member("fullName")); + } + else if(root.has_member("google")) + { + m_utils.setEmail(root.get_string_member("google")); + } + else if(root.has_member("reader")) + { + m_utils.setEmail(root.get_string_member("reader")); + } + else if(root.has_member("twitterUserId")) + { + m_utils.setEmail(root.get_string_member("twitterUserId")); + } + else if(root.has_member("facebookUserId")) + { + m_utils.setEmail(root.get_string_member("facebookUserId")); + } + else if(root.has_member("wordPressId")) + { + m_utils.setEmail(root.get_string_member("wordPressId")); + } + else if(root.has_member("windowsLiveId")) + { + m_utils.setEmail(root.get_string_member("windowsLiveId")); + } + + return true; } - - return true; - } - - return false; -} - -private ConnectionError tokenStillValid() -{ - var response = m_connection.send_get_request_to_feedly ("/v3/profile/"); - - if(response.status != 200) - { - return ConnectionError.NO_RESPONSE; - } - - var parser = new Json.Parser (); - try - { - parser.load_from_data(response.data, -1); - } - catch(Error e) - { - Logger.error("tokenStillValid: Could not load message response"); - Logger.error(e.message); - return ConnectionError.NO_RESPONSE; - } - - var root = parser.get_root().get_object(); - - if(root.has_member("errorId")) - { - return ConnectionError.INVALID_SESSIONID; - } - return ConnectionError.SUCCESS; -} - - -public bool getCategories(Gee.List<Category> categories) -{ - var response = m_connection.send_get_request_to_feedly ("/v3/categories/"); - - if(response.status != 200) - { + return false; } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); - } - catch (Error e) + + private ConnectionError tokenStillValid() { - Logger.error("getCategories: Could not load message response"); - Logger.error(e.message); - return false; + var response = m_connection.send_get_request_to_feedly ("/v3/profile/"); + + if(response.status != 200) + { + return ConnectionError.NO_RESPONSE; + } + + var parser = new Json.Parser (); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("tokenStillValid: Could not load message response"); + Logger.error(e.message); + return ConnectionError.NO_RESPONSE; + } + + var root = parser.get_root().get_object(); + + if(root.has_member("errorId")) + { + return ConnectionError.INVALID_SESSIONID; + } + return ConnectionError.SUCCESS; } - Json.Array array = parser.get_root().get_array(); - - for (int i = 0; i < array.get_length(); i++) + + + public bool getCategories(Gee.List<Category> categories) { - Json.Object object = array.get_object_element(i); - string categorieID = object.get_string_member("id"); - - if(categorieID.has_suffix("global.all") - || categorieID.has_suffix("global.uncategorized")) + var response = m_connection.send_get_request_to_feedly ("/v3/categories/"); + + if(response.status != 200) { - continue; + return false; } - - categories.add( - new Category ( - categorieID, - object.get_string_member("label"), - getUnreadCountforID(categorieID), - i+1, - CategoryID.MASTER.to_string(), - 1 + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch (Error e) + { + Logger.error("getCategories: Could not load message response"); + Logger.error(e.message); + return false; + } + Json.Array array = parser.get_root().get_array(); + + for (int i = 0; i < array.get_length(); i++) + { + Json.Object object = array.get_object_element(i); + string categorieID = object.get_string_member("id"); + + if(categorieID.has_suffix("global.all") + || categorieID.has_suffix("global.uncategorized")) + { + continue; + } + + categories.add( + new Category ( + categorieID, + object.get_string_member("label"), + getUnreadCountforID(categorieID), + i+1, + CategoryID.MASTER.to_string(), + 1 ) ); + } + + return true; } - - return true; -} - - -public bool getFeeds(Gee.List<Feed> feeds) -{ - var response = m_connection.send_get_request_to_feedly("/v3/subscriptions/"); - - if(response.status != 200) - { - return false; - } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); - } - catch(Error e) + + + public bool getFeeds(Gee.List<Feed> feeds) { - Logger.error("getFeeds: Could not load message response"); - Logger.error(e.message); - return false; - } - Json.Array array = parser.get_root().get_array(); - uint length = array.get_length(); - - for (uint i = 0; i < length; i++) { - Json.Object object = array.get_object_element(i); - - string feedID = object.get_string_member("id"); - - string? icon_url = null; - if(object.has_member("iconUrl")) + var response = m_connection.send_get_request_to_feedly("/v3/subscriptions/"); + + if(response.status != 200) { - icon_url = object.get_string_member("iconUrl"); + return false; } - else if(object.has_member("visualUrl")) + + var parser = new Json.Parser(); + try { - icon_url = object.get_string_member("visualUrl"); + parser.load_from_data(response.data, -1); } - - uint catCount = object.get_array_member("categories").get_length(); - - var categories = new Gee.ArrayList<string>(); - for(uint j = 0; j < catCount; ++j) + catch(Error e) { - string categoryID = object.get_array_member("categories").get_object_element(j).get_string_member("id"); - - if(categoryID.has_suffix("global.all") - || categoryID.has_suffix("global.uncategorized")) + Logger.error("getFeeds: Could not load message response"); + Logger.error(e.message); + return false; + } + Json.Array array = parser.get_root().get_array(); + uint length = array.get_length(); + + for (uint i = 0; i < length; i++) { + Json.Object object = array.get_object_element(i); + + string feedID = object.get_string_member("id"); + + string? icon_url = null; + if(object.has_member("iconUrl")) { - continue; + icon_url = object.get_string_member("iconUrl"); } - - categories.add(categoryID); - } - - feeds.add( - new Feed( - feedID, - object.get_string_member("title"), - object.get_string_member("website"), - getUnreadCountforID(object.get_string_member("id")), - categories, - icon_url + else if(object.has_member("visualUrl")) + { + icon_url = object.get_string_member("visualUrl"); + } + + uint catCount = object.get_array_member("categories").get_length(); + + var categories = new Gee.ArrayList<string>(); + for(uint j = 0; j < catCount; ++j) + { + string categoryID = object.get_array_member("categories").get_object_element(j).get_string_member("id"); + + if(categoryID.has_suffix("global.all") + || categoryID.has_suffix("global.uncategorized")) + { + continue; + } + + categories.add(categoryID); + } + + feeds.add( + new Feed( + feedID, + object.get_string_member("title"), + object.get_string_member("website"), + getUnreadCountforID(object.get_string_member("id")), + categories, + icon_url ) ); + } + + return true; } - - return true; -} - - -public bool getTags(Gee.List<Tag> tags) -{ - var response = m_connection.send_get_request_to_feedly("/v3/tags/"); - - if(response.status != 200) + + + public bool getTags(Gee.List<Tag> tags) { - return false; - } - - var parser = new Json.Parser(); - try{ - parser.load_from_data(response.data, -1); - } - catch (Error e) { - Logger.error("getTags: Could not load message response"); - Logger.error(e.message); - return false; - } - Json.Array array = parser.get_root().get_array (); - uint length = array.get_length(); - - var db = DataBase.readOnly(); - for (uint i = 0; i < length; i++) { - Json.Object object = array.get_object_element(i); - - tags.add( - new Tag( - object.get_string_member("id"), - object.has_member("label") ? object.get_string_member("label") : "", - db.getTagColor() + var response = m_connection.send_get_request_to_feedly("/v3/tags/"); + + if(response.status != 200) + { + return false; + } + + var parser = new Json.Parser(); + try{ + parser.load_from_data(response.data, -1); + } + catch (Error e) { + Logger.error("getTags: Could not load message response"); + Logger.error(e.message); + return false; + } + Json.Array array = parser.get_root().get_array (); + uint length = array.get_length(); + + var db = DataBase.readOnly(); + for (uint i = 0; i < length; i++) { + Json.Object object = array.get_object_element(i); + + tags.add( + new Tag( + object.get_string_member("id"), + object.has_member("label") ? object.get_string_member("label") : "", + db.getTagColor() ) ); + } + + return true; } - - return true; -} - - - -public string? getArticles(Gee.List<Article> articles, int count, string? continuation = null, ArticleStatus whatToGet = ArticleStatus.ALL, string tagID = "", string feed_id = "") -{ - string steamID = "user/" + m_userID + "/category/global.all"; - string onlyUnread = "false"; - string marked_tag = "user/" + m_userID + "/tag/global.saved"; - - if(whatToGet == ArticleStatus.MARKED) - { - steamID = marked_tag; - } - else if(whatToGet == ArticleStatus.UNREAD) - { - onlyUnread = "true"; - } - - - if(tagID != "" && whatToGet == ArticleStatus.ALL) - { - steamID = tagID; - } - - if(feed_id != "" && whatToGet == ArticleStatus.ALL) - { - steamID = feed_id; - } - - var parser = new Json.Parser(); - - string streamCall = "/v3/streams/ids?streamId=%s&unreadOnly=%s&count=%i&ranked=newest&continuation=%s".printf(steamID, onlyUnread, count, (continuation == null) ? "" : continuation); - var entry_id_response = m_connection.send_get_request_to_feedly(streamCall); - - if(entry_id_response.status != 200) - { - return null; - } - - try - { - parser.load_from_data(entry_id_response.data, -1); - } - catch(Error e) - { - Logger.error("getArticles: Could not load message response"); - Logger.error(e.message); - } - - var root = parser.get_root().get_object(); - if(!root.has_member("continuation")) - { - return null; - } - - string cont = root.get_string_member("continuation"); - - var response = m_connection.send_post_string_request_to_feedly("/v3/entries/.mget", entry_id_response.data, "application/json"); - - if(response.status != 200) - { - return null; - } - - try - { - parser.load_from_data(response.data, -1); - } - catch(Error e) - { - Logger.error("getArticles: Could not load message response"); - Logger.error(e.message); - } - var array = parser.get_root().get_array(); - - for(int i = 0; i < array.get_length(); i++) + + + + public string? getArticles(Gee.List<Article> articles, int count, string? continuation = null, ArticleStatus whatToGet = ArticleStatus.ALL, string tagID = "", string feed_id = "") { - Json.Object object = array.get_object_element(i); - string id = object.get_string_member("id"); - string title = object.get_string_member("title"); - string? author = object.get_string_member("author"); - string summaryContent = object.has_member("summary") ? object.get_object_member("summary").get_string_member("content") : null; - string content = object.has_member("content") ? object.get_object_member("content").get_string_member("content") : summaryContent; - bool unread = object.get_boolean_member("unread"); - string url = object.has_member("alternate") ? object.get_array_member("alternate").get_object_element(0).get_string_member("href") : null; - string feedID = object.get_object_member("origin").get_string_member("streamId"); - - DateTime date = new DateTime.now_local(); - if(object.has_member("updated") && object.get_int_member("updated") > 0) + string steamID = "user/" + m_userID + "/category/global.all"; + string onlyUnread = "false"; + string marked_tag = "user/" + m_userID + "/tag/global.saved"; + + if(whatToGet == ArticleStatus.MARKED) { - date = new DateTime.from_unix_local(object.get_int_member("updated")/1000); + steamID = marked_tag; } - else if(object.has_member("published") && object.get_int_member("published") > 0) + else if(whatToGet == ArticleStatus.UNREAD) { - date = new DateTime.from_unix_local(object.get_int_member("published")/1000); + onlyUnread = "true"; } - else if(object.has_member("crawled") && object.get_int_member("crawled") > 0) + + + if(tagID != "" && whatToGet == ArticleStatus.ALL) { - date = new DateTime.from_unix_local(object.get_int_member("crawled")/1000); + steamID = tagID; } - - var marked = ArticleStatus.UNMARKED; - - var tags = new Gee.ArrayList<string>(); - if(object.has_member("tags")) + + if(feed_id != "" && whatToGet == ArticleStatus.ALL) { - var tag_array = object.get_array_member("tags"); - uint tagCount = tag_array.get_length(); - - for(int j = 0; j < tagCount; ++j) + steamID = feed_id; + } + + var parser = new Json.Parser(); + + string streamCall = "/v3/streams/ids?streamId=%s&unreadOnly=%s&count=%i&ranked=newest&continuation=%s".printf(steamID, onlyUnread, count, (continuation == null) ? "" : continuation); + var entry_id_response = m_connection.send_get_request_to_feedly(streamCall); + + if(entry_id_response.status != 200) + { + return null; + } + + try + { + parser.load_from_data(entry_id_response.data, -1); + } + catch(Error e) + { + Logger.error("getArticles: Could not load message response"); + Logger.error(e.message); + } + + var root = parser.get_root().get_object(); + if(!root.has_member("continuation")) + { + return null; + } + + string cont = root.get_string_member("continuation"); + + var response = m_connection.send_post_string_request_to_feedly("/v3/entries/.mget", entry_id_response.data, "application/json"); + + if(response.status != 200) + { + return null; + } + + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("getArticles: Could not load message response"); + Logger.error(e.message); + } + var array = parser.get_root().get_array(); + + for(int i = 0; i < array.get_length(); i++) + { + Json.Object object = array.get_object_element(i); + string id = object.get_string_member("id"); + string title = object.get_string_member("title"); + string? author = object.get_string_member("author"); + string summaryContent = object.has_member("summary") ? object.get_object_member("summary").get_string_member("content") : null; + string content = object.has_member("content") ? object.get_object_member("content").get_string_member("content") : summaryContent; + bool unread = object.get_boolean_member("unread"); + string url = object.has_member("alternate") ? object.get_array_member("alternate").get_object_element(0).get_string_member("href") : null; + string feedID = object.get_object_member("origin").get_string_member("streamId"); + + DateTime date = new DateTime.now_local(); + if(object.has_member("updated") && object.get_int_member("updated") > 0) + { + date = new DateTime.from_unix_local(object.get_int_member("updated")/1000); + } + else if(object.has_member("published") && object.get_int_member("published") > 0) + { + date = new DateTime.from_unix_local(object.get_int_member("published")/1000); + } + else if(object.has_member("crawled") && object.get_int_member("crawled") > 0) + { + date = new DateTime.from_unix_local(object.get_int_member("crawled")/1000); + } + + var marked = ArticleStatus.UNMARKED; + + var tags = new Gee.ArrayList<string>(); + if(object.has_member("tags")) { - var tag = tag_array.get_object_element(j).get_string_member("id"); - if(tag == marked_tag) + var tag_array = object.get_array_member("tags"); + uint tagCount = tag_array.get_length(); + + for(int j = 0; j < tagCount; ++j) { - marked = ArticleStatus.MARKED; + var tag = tag_array.get_object_element(j).get_string_member("id"); + if(tag == marked_tag) + { + marked = ArticleStatus.MARKED; + } + else if(tag.contains("global.")) + { + continue; + } + else + { + tags.add(tag); + } } - else if(tag.contains("global.")) + } + + var enclosures = new Gee.ArrayList<Enclosure>(); + if(object.has_member("enclosure")) + { + var attachments = object.get_array_member("enclosure"); + + uint mediaCount = 0; + if(attachments != null) { - continue; + mediaCount = attachments.get_length(); } - else + + for(int j = 0; j < mediaCount; ++j) { - tags.add(tag); + var attachment = attachments.get_object_element(j); + enclosures.add( + new Enclosure(id, attachment.get_string_member("href"), + EnclosureType.from_string(attachment.get_string_member("type"))) + ); } } + + var Article = new Article( + id, + title, + url, + feedID, + unread ? ArticleStatus.UNREAD : ArticleStatus.READ, + marked, + content, + //summaryContent, + null, + author, + date, // timestamp includes msecs so divide by 1000 to get rid of them + -1, + tags, + enclosures + ); + articles.add(Article); } - - var enclosures = new Gee.ArrayList<Enclosure>(); - if(object.has_member("enclosure")) + + return cont; + } + + /** Returns the number of unread articles for an ID (may be a feed, subscription, category or tag */ + public void getUnreadCounts() { - var attachments = object.get_array_member("enclosure"); - - uint mediaCount = 0; - if(attachments != null) + var response = m_connection.send_get_request_to_feedly ("/v3/markers/counts"); + + if(response.status != 200) { - mediaCount = attachments.get_length(); + return; } - - for(int j = 0; j < mediaCount; ++j) + + var parser = new Json.Parser (); + try { - var attachment = attachments.get_object_element(j); - enclosures.add( - new Enclosure(id, attachment.get_string_member("href"), - EnclosureType.from_string(attachment.get_string_member("type"))) - ); + parser.load_from_data(response.data, -1); } + catch(Error e) + { + Logger.error("getUnreadCounts: Could not load message response"); + Logger.error(e.message); + } + + var object = parser.get_root ().get_object (); + + m_unreadcounts = object.get_array_member("unreadcounts"); } - - var Article = new Article( - id, - title, - url, - feedID, - unread ? ArticleStatus.UNREAD : ArticleStatus.READ, - marked, - content, - //summaryContent, - null, - author, - date, // timestamp includes msecs so divide by 1000 to get rid of them - -1, - tags, - enclosures - ); - articles.add(Article); - } - - return cont; -} - -/** Returns the number of unread articles for an ID (may be a feed, subscription, category or tag */ -public void getUnreadCounts() -{ - var response = m_connection.send_get_request_to_feedly ("/v3/markers/counts"); - - if(response.status != 200) - { - return; - } - - var parser = new Json.Parser (); - try - { - parser.load_from_data(response.data, -1); - } - catch(Error e) - { - Logger.error("getUnreadCounts: Could not load message response"); - Logger.error(e.message); - } - - var object = parser.get_root ().get_object (); - - m_unreadcounts = object.get_array_member("unreadcounts"); -} - -private int getUnreadCountforID(string id) -{ - int unread_count = -1; - - for(int i = 0; i < m_unreadcounts.get_length (); i++) - { - var unread = m_unreadcounts.get_object_element(i); - string unread_id = unread.get_string_member("id"); - - if(id == unread_id) + + private int getUnreadCountforID(string id) { - unread_count = (int)unread.get_int_member("count"); - break; + int unread_count = -1; + + for(int i = 0; i < m_unreadcounts.get_length (); i++) + { + var unread = m_unreadcounts.get_object_element(i); + string unread_id = unread.get_string_member("id"); + + if(id == unread_id) + { + unread_count = (int)unread.get_int_member("count"); + break; + } + } + + if(unread_count == -1) + { + Logger.error("Unknown id: %s".printf(id)); + } + + return unread_count; } - } - - if(unread_count == -1) - { - Logger.error("Unknown id: %s".printf(id)); - } - - return unread_count; -} - -public int getTotalUnread() -{ - return getUnreadCountforID("user/" + m_userID + "/category/global.all"); -} - - -public void mark_as_read(string ids_string, string type, ArticleStatus read) -{ - var id_array = ids_string.split(","); - Json.Object object = new Json.Object(); - - if(read == ArticleStatus.READ) - { - object.set_string_member ("action", "markAsRead"); - } - else if(read == ArticleStatus.UNREAD) - { - object.set_string_member ("action", "keepUnread"); - } - object.set_string_member ("type", type); - - Json.Array ids = new Json.Array(); - foreach(string id in id_array) - { - ids.add_string_element(id); - } - - string* type_id_identificator = null; - - if(type == "entries") - { - type_id_identificator = "entryIds"; - } - else if(type == "feeds") - { - type_id_identificator = "feedIds"; - } - else if(type == "categories") - { - type_id_identificator = "categoryIds"; - } - else - { - error ("Unknown type: " + type + " don't know what to do with this."); - } - - object.set_array_member(type_id_identificator, ids); - - if(type == "feeds" - || type == "categories") - { - var now = new DateTime.now_local(); - object.set_int_member("asOf", now.to_unix()*1000); - } - - var root = new Json.Node(Json.NodeType.OBJECT); - root.set_object (object); - - m_connection.send_post_request_to_feedly("/v3/markers", root); -} - -public void addArticleTag(string ids_string, string tagID) -{ - var id_array = ids_string.split(","); - Json.Object object = new Json.Object(); - - Json.Array ids = new Json.Array(); - foreach(string id in id_array) - { - ids.add_string_element(id); - } - - object.set_array_member("entryIds", ids); - var root = new Json.Node(Json.NodeType.OBJECT); - root.set_object(object); - - m_connection.send_put_request_to_feedly("/v3/tags/" + GLib.Uri.escape_string(tagID), root); -} - -public void deleteArticleTag(string ids_string, string tagID) -{ - string command = GLib.Uri.escape_string(tagID) + "/" + GLib.Uri.escape_string(ids_string); - m_connection.send_delete_request_to_feedly("/v3/tags/" + command); -} - -public string createTag(string caption) -{ - string tagID = "user/" + m_userID + "/tag/" + caption; - Json.Object object = new Json.Object(); - object.set_string_member("entryId", ""); - var root = new Json.Node(Json.NodeType.OBJECT); - root.set_object(object); - - m_connection.send_put_request_to_feedly("/v3/tags/" + GLib.Uri.escape_string(tagID), root); - return tagID; -} - -public void deleteTag(string tagID) -{ - m_connection.send_delete_request_to_feedly("/v3/tags/" + GLib.Uri.escape_string(tagID)); -} - - -public bool addSubscription(string feedURL, string? title = null, string? catIDs = null) -{ - Json.Object object = new Json.Object(); - object.set_string_member("id", "feed/" + feedURL); - - if(title != null) - { - object.set_string_member("title", title); - } - - if(catIDs != null) - { - var catArray = catIDs.split(","); - Json.Array cats = new Json.Array(); - - var db = DataBase.readOnly(); - foreach(string catID in catArray) + + public int getTotalUnread() { - string catName = db.getCategoryName(catID); - Json.Object catObject = new Json.Object(); - catObject.set_string_member("id", catID); - catObject.set_string_member("label", catName); - cats.add_object_element(catObject); + return getUnreadCountforID("user/" + m_userID + "/category/global.all"); } - - object.set_array_member("categories", cats); - } - - var root = new Json.Node(Json.NodeType.OBJECT); - root.set_object(object); - - var response = m_connection.send_post_request_to_feedly("/v3/subscriptions", root); - - return response.status == 200; -} - -public void moveSubscription(string feedID, string newCatID, string? oldCatID = null) -{ - var db = DataBase.readOnly(); - var Feed = db.read_feed(feedID); - - Json.Object object = new Json.Object(); - object.set_string_member("id", feedID); - object.set_string_member("title", Feed.getTitle()); - - - var catArray = Feed.getCatIDs(); - Json.Array cats = new Json.Array(); - - foreach(string catID in catArray) - { - if(catID != oldCatID) + + + public void mark_as_read(string ids_string, string type, ArticleStatus read) { - string catName = db.getCategoryName(catID); + var id_array = ids_string.split(","); + Json.Object object = new Json.Object(); + + if(read == ArticleStatus.READ) + { + object.set_string_member ("action", "markAsRead"); + } + else if(read == ArticleStatus.UNREAD) + { + object.set_string_member ("action", "keepUnread"); + } + object.set_string_member ("type", type); + + Json.Array ids = new Json.Array(); + foreach(string id in id_array) + { + ids.add_string_element(id); + } + + string* type_id_identificator = null; + + if(type == "entries") + { + type_id_identificator = "entryIds"; + } + else if(type == "feeds") + { + type_id_identificator = "feedIds"; + } + else if(type == "categories") + { + type_id_identificator = "categoryIds"; + } + else + { + error ("Unknown type: " + type + " don't know what to do with this."); + } + + object.set_array_member(type_id_identificator, ids); + + if(type == "feeds" + || type == "categories") + { + var now = new DateTime.now_local(); + object.set_int_member("asOf", now.to_unix()*1000); + } + + var root = new Json.Node(Json.NodeType.OBJECT); + root.set_object (object); + + m_connection.send_post_request_to_feedly("/v3/markers", root); + } + + public void addArticleTag(string ids_string, string tagID) + { + var id_array = ids_string.split(","); + Json.Object object = new Json.Object(); + + Json.Array ids = new Json.Array(); + foreach(string id in id_array) + { + ids.add_string_element(id); + } + + object.set_array_member("entryIds", ids); + var root = new Json.Node(Json.NodeType.OBJECT); + root.set_object(object); + + m_connection.send_put_request_to_feedly("/v3/tags/" + GLib.Uri.escape_string(tagID), root); + } + + public void deleteArticleTag(string ids_string, string tagID) + { + string command = GLib.Uri.escape_string(tagID) + "/" + GLib.Uri.escape_string(ids_string); + m_connection.send_delete_request_to_feedly("/v3/tags/" + command); + } + + public string createTag(string caption) + { + string tagID = "user/" + m_userID + "/tag/" + caption; + Json.Object object = new Json.Object(); + object.set_string_member("entryId", ""); + var root = new Json.Node(Json.NodeType.OBJECT); + root.set_object(object); + + m_connection.send_put_request_to_feedly("/v3/tags/" + GLib.Uri.escape_string(tagID), root); + return tagID; + } + + public void deleteTag(string tagID) + { + m_connection.send_delete_request_to_feedly("/v3/tags/" + GLib.Uri.escape_string(tagID)); + } + + + public bool addSubscription(string feedURL, string? title = null, string? catIDs = null) + { + Json.Object object = new Json.Object(); + object.set_string_member("id", "feed/" + feedURL); + + if(title != null) + { + object.set_string_member("title", title); + } + + if(catIDs != null) + { + var catArray = catIDs.split(","); + Json.Array cats = new Json.Array(); + + var db = DataBase.readOnly(); + foreach(string catID in catArray) + { + string catName = db.getCategoryName(catID); + Json.Object catObject = new Json.Object(); + catObject.set_string_member("id", catID); + catObject.set_string_member("label", catName); + cats.add_object_element(catObject); + } + + object.set_array_member("categories", cats); + } + + var root = new Json.Node(Json.NodeType.OBJECT); + root.set_object(object); + + var response = m_connection.send_post_request_to_feedly("/v3/subscriptions", root); + + return response.status == 200; + } + + public void moveSubscription(string feedID, string newCatID, string? oldCatID = null) + { + var db = DataBase.readOnly(); + var Feed = db.read_feed(feedID); + + Json.Object object = new Json.Object(); + object.set_string_member("id", feedID); + object.set_string_member("title", Feed.getTitle()); + + + var catArray = Feed.getCatIDs(); + Json.Array cats = new Json.Array(); + + foreach(string catID in catArray) + { + if(catID != oldCatID) + { + string catName = db.getCategoryName(catID); + Json.Object catObject = new Json.Object(); + catObject.set_string_member("id", catID); + catObject.set_string_member("label", catName); + cats.add_object_element(catObject); + } + } + + string newCatName = db.getCategoryName(newCatID); Json.Object catObject = new Json.Object(); - catObject.set_string_member("id", catID); - catObject.set_string_member("label", catName); + catObject.set_string_member("id", newCatID); + catObject.set_string_member("label", newCatName); cats.add_object_element(catObject); + + object.set_array_member("categories", cats); + + var root = new Json.Node(Json.NodeType.OBJECT); + root.set_object(object); + + m_connection.send_post_request_to_feedly("/v3/subscriptions", root); + } + + public void removeSubscription(string feedID) + { + Logger.info(@"Deleting $(feedID)"); + m_connection.send_delete_request_to_feedly("/v3/subscriptions/" + Uri.escape_string(feedID)); + } + + public void renameCategory(string catID, string title) + { + Json.Object object = new Json.Object(); + object.set_string_member("label", title); + var root = new Json.Node(Json.NodeType.OBJECT); + root.set_object(object); + + m_connection.send_post_request_to_feedly("/v3/categories/" + Uri.escape_string(catID), root); + } + + public void renameTag(string tagID, string title) + { + Json.Object object = new Json.Object(); + object.set_string_member("label", title); + var root = new Json.Node(Json.NodeType.OBJECT); + root.set_object(object); + + m_connection.send_post_request_to_feedly("/v3/tags/" + Uri.escape_string(tagID), root); + } + + public void removeCategory(string catID) + { + m_connection.send_delete_request_to_feedly("/v3/categories/" + Uri.escape_string(catID)); + } + + public void importOPML(string opml) + { + m_connection.send_post_string_request_to_feedly("/v3/opml", opml, "text/xml"); } } - - string newCatName = db.getCategoryName(newCatID); - Json.Object catObject = new Json.Object(); - catObject.set_string_member("id", newCatID); - catObject.set_string_member("label", newCatName); - cats.add_object_element(catObject); - - object.set_array_member("categories", cats); - - var root = new Json.Node(Json.NodeType.OBJECT); - root.set_object(object); - - m_connection.send_post_request_to_feedly("/v3/subscriptions", root); -} - -public void removeSubscription(string feedID) -{ - Logger.info(@"Deleting $(feedID)"); - m_connection.send_delete_request_to_feedly("/v3/subscriptions/" + Uri.escape_string(feedID)); -} - -public void renameCategory(string catID, string title) -{ - Json.Object object = new Json.Object(); - object.set_string_member("label", title); - var root = new Json.Node(Json.NodeType.OBJECT); - root.set_object(object); - - m_connection.send_post_request_to_feedly("/v3/categories/" + Uri.escape_string(catID), root); -} - -public void renameTag(string tagID, string title) -{ - Json.Object object = new Json.Object(); - object.set_string_member("label", title); - var root = new Json.Node(Json.NodeType.OBJECT); - root.set_object(object); - - m_connection.send_post_request_to_feedly("/v3/tags/" + Uri.escape_string(tagID), root); -} - -public void removeCategory(string catID) -{ - m_connection.send_delete_request_to_feedly("/v3/categories/" + Uri.escape_string(catID)); -} - -public void importOPML(string opml) -{ - m_connection.send_post_string_request_to_feedly("/v3/opml", opml, "text/xml"); -} -} diff --git a/plugins/backend/feedly/feedlyConnection.vala b/plugins/backend/feedly/feedlyConnection.vala index 94c0581c..615bc8d0 100644 --- a/plugins/backend/feedly/feedlyConnection.vala +++ b/plugins/backend/feedly/feedlyConnection.vala @@ -14,273 +14,273 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.FeedlyConnection { - -private FeedlyUtils m_utils; -private GLib.Settings m_settingsTweaks; -private Soup.Session m_session; - -public FeedlyConnection(FeedlyUtils utils) -{ - m_utils = utils; - m_session = new Soup.Session(); - m_session.user_agent = Constants.USER_AGENT; - m_settingsTweaks = new GLib.Settings("org.gnome.feedreader.tweaks"); -} - -public LoginResponse getToken() -{ - var message = new Soup.Message("POST", FeedlySecret.base_uri+"/v3/auth/token"); - string message_string = "code=" + m_utils.getApiCode() - + "&client_id=" + FeedlySecret.apiClientId - + "&client_secret=" + FeedlySecret.apiClientSecret - + "&redirect_uri=" + FeedlySecret.apiRedirectUri - + "&grant_type=authorization_code" - + "&state=getting_token"; - - message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string.data); - m_session.send_message(message); - - if(message.status_code != 200) + + private FeedlyUtils m_utils; + private GLib.Settings m_settingsTweaks; + private Soup.Session m_session; + + public FeedlyConnection(FeedlyUtils utils) { - return LoginResponse.NO_CONNECTION; + m_utils = utils; + m_session = new Soup.Session(); + m_session.user_agent = Constants.USER_AGENT; + m_settingsTweaks = new GLib.Settings("org.gnome.feedreader.tweaks"); } - - try + + public LoginResponse getToken() { - var parser = new Json.Parser(); - parser.load_from_data ((string)message.response_body.flatten().data); - var root = parser.get_root().get_object(); - - if(root.has_member("access_token")) + var message = new Soup.Message("POST", FeedlySecret.base_uri+"/v3/auth/token"); + string message_string = "code=" + m_utils.getApiCode() + + "&client_id=" + FeedlySecret.apiClientId + + "&client_secret=" + FeedlySecret.apiClientSecret + + "&redirect_uri=" + FeedlySecret.apiRedirectUri + + "&grant_type=authorization_code" + + "&state=getting_token"; + + message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string.data); + m_session.send_message(message); + + if(message.status_code != 200) { - string accessToken = root.get_string_member("access_token"); - int64 expires = (int)root.get_int_member("expires_in"); - string refreshToken = root.get_string_member("refresh_token"); - int64 now = (new DateTime.now_local()).to_unix(); - - Logger.debug("access-token: " + accessToken); - Logger.debug("expires in: " + expires.to_string()); - Logger.debug("refresh-token: " + refreshToken); - Logger.debug("now: " + now.to_string()); - - m_utils.setAccessToken(accessToken); - m_utils.setExpiration((int)(now + expires)); - m_utils.setRefreshToken(refreshToken); - return LoginResponse.SUCCESS; + return LoginResponse.NO_CONNECTION; } - else if(root.has_member("errorCode")) + + try { - Logger.error("Feedly: getToken response - " + root.get_string_member("errorMessage")); - return LoginResponse.UNKNOWN_ERROR; + var parser = new Json.Parser(); + parser.load_from_data ((string)message.response_body.flatten().data); + var root = parser.get_root().get_object(); + + if(root.has_member("access_token")) + { + string accessToken = root.get_string_member("access_token"); + int64 expires = (int)root.get_int_member("expires_in"); + string refreshToken = root.get_string_member("refresh_token"); + int64 now = (new DateTime.now_local()).to_unix(); + + Logger.debug("access-token: " + accessToken); + Logger.debug("expires in: " + expires.to_string()); + Logger.debug("refresh-token: " + refreshToken); + Logger.debug("now: " + now.to_string()); + + m_utils.setAccessToken(accessToken); + m_utils.setExpiration((int)(now + expires)); + m_utils.setRefreshToken(refreshToken); + return LoginResponse.SUCCESS; + } + else if(root.has_member("errorCode")) + { + Logger.error("Feedly: getToken response - " + root.get_string_member("errorMessage")); + return LoginResponse.UNKNOWN_ERROR; + } } + catch(Error e) + { + Logger.error("Could not load response to Message from feedly - %s".printf(e.message)); + } + + return LoginResponse.UNKNOWN_ERROR; } - catch(Error e) - { - Logger.error("Could not load response to Message from feedly - %s".printf(e.message)); - } - - return LoginResponse.UNKNOWN_ERROR; -} - - -public LoginResponse refreshToken() -{ - var message = new Soup.Message("POST", FeedlySecret.base_uri+"/v3/auth/token"); - - if(m_settingsTweaks.get_boolean("do-not-track")) - { - message.request_headers.append("DNT", "1"); - } - - string message_string = "refresh_token=" + m_utils.getRefreshToken() - + "&client_id=" + FeedlySecret.apiClientId - + "&client_secret=" + FeedlySecret.apiClientSecret - + "&grant_type=refresh_token"; - - message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string.data); - m_session.send_message(message); - - if(message.status_code != 200) - { - return LoginResponse.NO_CONNECTION; - } - - try + + + public LoginResponse refreshToken() { - var parser = new Json.Parser(); - parser.load_from_data ((string)message.response_body.flatten().data); - var root = parser.get_root().get_object(); - - if(root.has_member("access_token")) + var message = new Soup.Message("POST", FeedlySecret.base_uri+"/v3/auth/token"); + + if(m_settingsTweaks.get_boolean("do-not-track")) { - string accessToken = root.get_string_member("access_token"); - int64 expires = (int)root.get_int_member("expires_in"); - string refreshToken = root.get_string_member("refresh_token"); - int64 now = (new DateTime.now_local()).to_unix(); - - Logger.debug("access-token: " + accessToken); - Logger.debug("expires in: " + expires.to_string()); - Logger.debug("refresh-token: " + refreshToken); - Logger.debug("now: " + now.to_string()); - - m_utils.setAccessToken(accessToken); - m_utils.setExpiration((int)(now + expires)); - m_utils.setRefreshToken(refreshToken); - return LoginResponse.SUCCESS; + message.request_headers.append("DNT", "1"); } - else if(root.has_member("errorCode")) + + string message_string = "refresh_token=" + m_utils.getRefreshToken() + + "&client_id=" + FeedlySecret.apiClientId + + "&client_secret=" + FeedlySecret.apiClientSecret + + "&grant_type=refresh_token"; + + message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string.data); + m_session.send_message(message); + + if(message.status_code != 200) { - Logger.error("Feedly: refreshToken response - " + root.get_string_member("errorMessage")); - return LoginResponse.UNKNOWN_ERROR; + return LoginResponse.NO_CONNECTION; } + + try + { + var parser = new Json.Parser(); + parser.load_from_data ((string)message.response_body.flatten().data); + var root = parser.get_root().get_object(); + + if(root.has_member("access_token")) + { + string accessToken = root.get_string_member("access_token"); + int64 expires = (int)root.get_int_member("expires_in"); + string refreshToken = root.get_string_member("refresh_token"); + int64 now = (new DateTime.now_local()).to_unix(); + + Logger.debug("access-token: " + accessToken); + Logger.debug("expires in: " + expires.to_string()); + Logger.debug("refresh-token: " + refreshToken); + Logger.debug("now: " + now.to_string()); + + m_utils.setAccessToken(accessToken); + m_utils.setExpiration((int)(now + expires)); + m_utils.setRefreshToken(refreshToken); + return LoginResponse.SUCCESS; + } + else if(root.has_member("errorCode")) + { + Logger.error("Feedly: refreshToken response - " + root.get_string_member("errorMessage")); + return LoginResponse.UNKNOWN_ERROR; + } + } + catch(Error e) + { + Logger.error("Could not load response to Message from feedly - %s".printf(e.message)); + } + + return LoginResponse.UNKNOWN_ERROR; } - catch(Error e) - { - Logger.error("Could not load response to Message from feedly - %s".printf(e.message)); - } - - return LoginResponse.UNKNOWN_ERROR; -} - - -public Response send_get_request_to_feedly(string path) -{ - return send_request(path, "GET"); -} - -public Response send_put_request_to_feedly(string path, Json.Node root) -{ - if(!m_utils.accessTokenValid()) - { - refreshToken(); - } - - var message = new Soup.Message("PUT", FeedlySecret.base_uri+path); - - if(m_settingsTweaks.get_boolean("do-not-track")) - { - message.request_headers.append("DNT", "1"); - } - - var gen = new Json.Generator(); - gen.set_root(root); - message.request_headers.append("Authorization","OAuth %s".printf(m_utils.getAccessToken())); - - size_t length; - string json; - json = gen.to_data(out length); - message.request_body.append_take(json.data); - m_session.send_message(message); - - if(message.status_code != 200) - { - Logger.warning(@"FeedlyConnection: message unexpected response"); - } - - return Response() { - status = message.status_code, - data = (string)message.response_body.flatten().data - }; -} - -public Response send_post_request_to_feedly(string path, Json.Node root) -{ - if(!m_utils.accessTokenValid()) - { - refreshToken(); - } - - var message = new Soup.Message("POST", FeedlySecret.base_uri+path); - - if(m_settingsTweaks.get_boolean("do-not-track")) - { - message.request_headers.append("DNT", "1"); - } - - var gen = new Json.Generator(); - gen.set_root(root); - message.request_headers.append("Authorization","OAuth %s".printf(m_utils.getAccessToken())); - - size_t length; - string json; - json = gen.to_data(out length); - Logger.debug(json); - message.request_body.append_take(json.data); - m_session.send_message(message); - - if(message.status_code != 200) - { - Logger.warning(@"FeedlyConnection: message unexpected response"); - Logger.debug("Status Code: " + message.status_code.to_string()); - } - - return Response() { - status = message.status_code, - data = (string)message.response_body.flatten().data - }; -} - -public Response send_post_string_request_to_feedly(string path, string input, string type) -{ - if(!m_utils.accessTokenValid()) + + + public Response send_get_request_to_feedly(string path) { - refreshToken(); + return send_request(path, "GET"); } - - var message = new Soup.Message("POST", FeedlySecret.base_uri+path); - - if(m_settingsTweaks.get_boolean("do-not-track")) + + public Response send_put_request_to_feedly(string path, Json.Node root) { - message.request_headers.append("DNT", "1"); + if(!m_utils.accessTokenValid()) + { + refreshToken(); + } + + var message = new Soup.Message("PUT", FeedlySecret.base_uri+path); + + if(m_settingsTweaks.get_boolean("do-not-track")) + { + message.request_headers.append("DNT", "1"); + } + + var gen = new Json.Generator(); + gen.set_root(root); + message.request_headers.append("Authorization","OAuth %s".printf(m_utils.getAccessToken())); + + size_t length; + string json; + json = gen.to_data(out length); + message.request_body.append_take(json.data); + m_session.send_message(message); + + if(message.status_code != 200) + { + Logger.warning(@"FeedlyConnection: message unexpected response"); + } + + return Response() { + status = message.status_code, + data = (string)message.response_body.flatten().data + }; } - - message.request_headers.append("Authorization","OAuth %s".printf(m_utils.getAccessToken())); - message.request_headers.append("Content-Type", type); - - message.request_body.append_take(input.data); - m_session.send_message(message); - - if(message.status_code != 200) + + public Response send_post_request_to_feedly(string path, Json.Node root) { - Logger.warning(@"FeedlyConnection: message unexpected response - $input"); + if(!m_utils.accessTokenValid()) + { + refreshToken(); + } + + var message = new Soup.Message("POST", FeedlySecret.base_uri+path); + + if(m_settingsTweaks.get_boolean("do-not-track")) + { + message.request_headers.append("DNT", "1"); + } + + var gen = new Json.Generator(); + gen.set_root(root); + message.request_headers.append("Authorization","OAuth %s".printf(m_utils.getAccessToken())); + + size_t length; + string json; + json = gen.to_data(out length); + Logger.debug(json); + message.request_body.append_take(json.data); + m_session.send_message(message); + + if(message.status_code != 200) + { + Logger.warning(@"FeedlyConnection: message unexpected response"); + Logger.debug("Status Code: " + message.status_code.to_string()); + } + + return Response() { + status = message.status_code, + data = (string)message.response_body.flatten().data + }; } - - return Response() { - status = message.status_code, - data = (string)message.response_body.flatten().data - }; -} - -public Response send_delete_request_to_feedly(string path) -{ - return send_request(path, "DELETE"); -} - -private Response send_request(string path, string type) -{ - if(!m_utils.accessTokenValid()) + + public Response send_post_string_request_to_feedly(string path, string input, string type) { - refreshToken(); + if(!m_utils.accessTokenValid()) + { + refreshToken(); + } + + var message = new Soup.Message("POST", FeedlySecret.base_uri+path); + + if(m_settingsTweaks.get_boolean("do-not-track")) + { + message.request_headers.append("DNT", "1"); + } + + message.request_headers.append("Authorization","OAuth %s".printf(m_utils.getAccessToken())); + message.request_headers.append("Content-Type", type); + + message.request_body.append_take(input.data); + m_session.send_message(message); + + if(message.status_code != 200) + { + Logger.warning(@"FeedlyConnection: message unexpected response - $input"); + } + + return Response() { + status = message.status_code, + data = (string)message.response_body.flatten().data + }; } - - var message = new Soup.Message(type, FeedlySecret.base_uri+path); - message.request_headers.append("Authorization", @"OAuth $(m_utils.getAccessToken())"); - - if(m_settingsTweaks.get_boolean("do-not-track")) + + public Response send_delete_request_to_feedly(string path) { - message.request_headers.append("DNT", "1"); + return send_request(path, "DELETE"); } - - m_session.send_message(message); - - if(message.status_code != 200) + + private Response send_request(string path, string type) { - Logger.warning(@"FeedlyConnection: message unexpected response"); + if(!m_utils.accessTokenValid()) + { + refreshToken(); + } + + var message = new Soup.Message(type, FeedlySecret.base_uri+path); + message.request_headers.append("Authorization", @"OAuth $(m_utils.getAccessToken())"); + + if(m_settingsTweaks.get_boolean("do-not-track")) + { + message.request_headers.append("DNT", "1"); + } + + m_session.send_message(message); + + if(message.status_code != 200) + { + Logger.warning(@"FeedlyConnection: message unexpected response"); + } + + return Response() { + status = message.status_code, + data = (string)message.response_body.flatten().data + }; } - - return Response() { - status = message.status_code, - data = (string)message.response_body.flatten().data - }; -} } diff --git a/plugins/backend/feedly/feedlyInterface.vala b/plugins/backend/feedly/feedlyInterface.vala index 2a2aeef3..f668b38e 100644 --- a/plugins/backend/feedly/feedlyInterface.vala +++ b/plugins/backend/feedly/feedlyInterface.vala @@ -14,385 +14,385 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.feedlyInterface : FeedServerInterface { - -private FeedlyAPI m_api; -private FeedlyUtils m_utils; - -public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) -{ - m_utils = new FeedlyUtils(settings_backend); - m_api = new FeedlyAPI(m_utils); -} - -public override string getWebsite() -{ - return "http://feedly.com/"; -} - -public override BackendFlags getFlags() -{ - return (BackendFlags.HOSTED | BackendFlags.PROPRIETARY | BackendFlags.PAID_PREMIUM); -} - -public override string getID() -{ - return "feedly"; -} - -public override string iconName() -{ - return "feed-service-feedly"; -} - -public override string serviceName() -{ - return "feedly"; -} - -public override bool needWebLogin() -{ - return true; -} - -public override bool extractCode(string redirectURL) -{ - if(redirectURL.has_prefix(FeedlySecret.apiRedirectUri)) - { - int start = redirectURL.index_of("=")+1; - int end = redirectURL.index_of("&"); - string code = redirectURL.substring(start, end-start); - m_utils.setApiCode(code); - Logger.debug("feedlyLoginWidget: set feedly-api-code: " + code); - GLib.Thread.usleep(500000); + + private FeedlyAPI m_api; + private FeedlyUtils m_utils; + + public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) + { + m_utils = new FeedlyUtils(settings_backend); + m_api = new FeedlyAPI(m_utils); + } + + public override string getWebsite() + { + return "http://feedly.com/"; + } + + public override BackendFlags getFlags() + { + return (BackendFlags.HOSTED | BackendFlags.PROPRIETARY | BackendFlags.PAID_PREMIUM); + } + + public override string getID() + { + return "feedly"; + } + + public override string iconName() + { + return "feed-service-feedly"; + } + + public override string serviceName() + { + return "feedly"; + } + + public override bool needWebLogin() + { return true; } - - return false; -} - -public override string buildLoginURL() -{ - return FeedlySecret.base_uri + "/v3/auth/auth" + "?client_secret=" + FeedlySecret.apiClientSecret + "&client_id=" + FeedlySecret.apiClientId - + "&redirect_uri=" + FeedlySecret.apiRedirectUri + "&scope=" + FeedlySecret.apiAuthScope + "&response_type=code&state=getting_code"; -} - -public override bool supportTags() -{ - return true; -} - -public override bool doInitSync() -{ - return true; -} - -public override string symbolicIcon() -{ - return "feed-service-feedly-symbolic"; -} - -public override string accountName() -{ - return m_utils.getEmail(); -} - -public override string getServerURL() -{ - return "http://feedly.com/"; -} - -public override string uncategorizedID() -{ - return ""; -} - -public override bool hideCategoryWhenEmpty(string catID) -{ - return catID.has_suffix("global.must"); -} - -public override bool supportCategories() -{ - return true; -} - -public override bool supportFeedManipulation() -{ - return true; -} - -public override bool supportMultiLevelCategories() -{ - return false; -} - -public override bool supportMultiCategoriesPerFeed() -{ - return true; -} - -public override bool syncFeedsAndCategories() -{ - return true; -} - -public override bool tagIDaffectedByNameChange() -{ - return false; -} - -public override void resetAccount() -{ - m_utils.resetAccount(); -} - -public override bool useMaxArticles() -{ - return true; -} - -public override LoginResponse login() -{ - return m_api.login(); -} - -public override void setArticleIsRead(string articleIDs, ArticleStatus read) -{ - m_api.mark_as_read(articleIDs, "entries", read); -} - -public override void setArticleIsMarked(string articleID, ArticleStatus marked) -{ - if(marked == ArticleStatus.MARKED) + + public override bool extractCode(string redirectURL) { - m_api.addArticleTag(articleID, m_api.getMarkedID()); + if(redirectURL.has_prefix(FeedlySecret.apiRedirectUri)) + { + int start = redirectURL.index_of("=")+1; + int end = redirectURL.index_of("&"); + string code = redirectURL.substring(start, end-start); + m_utils.setApiCode(code); + Logger.debug("feedlyLoginWidget: set feedly-api-code: " + code); + GLib.Thread.usleep(500000); + return true; + } + + return false; } - else if(marked == ArticleStatus.UNMARKED) + + public override string buildLoginURL() { - m_api.deleteArticleTag(articleID, m_api.getMarkedID()); + return FeedlySecret.base_uri + "/v3/auth/auth" + "?client_secret=" + FeedlySecret.apiClientSecret + "&client_id=" + FeedlySecret.apiClientId + + "&redirect_uri=" + FeedlySecret.apiRedirectUri + "&scope=" + FeedlySecret.apiAuthScope + "&response_type=code&state=getting_code"; } -} - -public override bool alwaysSetReadByID() -{ - return false; -} - -public override void setFeedRead(string feedID) -{ - m_api.mark_as_read(feedID, "feeds", ArticleStatus.READ); -} - -public override void setCategoryRead(string catID) -{ - m_api.mark_as_read(catID, "categories", ArticleStatus.READ); -} - -public override void markAllItemsRead() -{ - string catArray = ""; - string feedArray = ""; - - var db = DataBase.readOnly(); - var categories = db.read_categories(); - var feeds = db.read_feeds_without_cat(); - - foreach(Category cat in categories) + + public override bool supportTags() { - catArray += cat.getCatID() + ","; + return true; } - - foreach(Feed feed in feeds) + + public override bool doInitSync() { - feedArray += feed.getFeedID() + ","; + return true; } - - m_api.mark_as_read(catArray.substring(0, catArray.length-1), "categories", ArticleStatus.READ); - m_api.mark_as_read(feedArray.substring(0, feedArray.length-1), "feeds", ArticleStatus.READ); -} - -public override void tagArticle(string articleID, string tagID) -{ - m_api.addArticleTag(articleID, tagID); -} - -public override void removeArticleTag(string articleID, string tagID) -{ - m_api.deleteArticleTag(articleID, tagID); -} - -public override string createTag(string caption) -{ - return m_api.createTag(caption); -} - -public override void deleteTag(string tagID) -{ - m_api.deleteTag(tagID); -} - -public override void renameTag(string tagID, string title) -{ - m_api.renameTag(tagID, title); -} - -public override bool serverAvailable() -{ - return Utils.ping("http://feedly.com/"); -} - -public override bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) -{ - feedID = "feed/" + feedURL; - bool success = false; - errmsg = ""; - - if(catID == null && newCatName != null) + + public override string symbolicIcon() { - string newCatID = m_api.createCatID(newCatName); - success = m_api.addSubscription(feedURL, null, newCatID); + return "feed-service-feedly-symbolic"; } - else + + public override string accountName() { - success = m_api.addSubscription(feedURL, null, catID); + return m_utils.getEmail(); } - - if(!success) + + public override string getServerURL() { - errmsg = @"feedly could not add $feedURL"; + return "http://feedly.com/"; } - - return success; -} - -public override void removeFeed(string feedID) -{ - m_api.removeSubscription(feedID); -} - -public override void renameFeed(string feedID, string title) -{ - var feed = DataBase.readOnly().read_feed(feedID); - m_api.addSubscription(feed.getFeedID(), title, feed.getCatString()); -} - -public override void moveFeed(string feedID, string newCatID, string? currentCatID ) -{ - m_api.moveSubscription(feedID, newCatID, currentCatID); -} - -public override string createCategory(string title, string? parentID) -{ - return m_api.createCatID(title); -} - -public override void renameCategory(string catID, string title) -{ - m_api.renameCategory(catID, title); -} - -public override void moveCategory(string catID, string newParentID) -{ - return; -} - -public override void deleteCategory(string catID) -{ - m_api.removeCategory(catID); -} - -public override void removeCatFromFeed(string feedID, string catID) -{ - var feed = DataBase.readOnly().read_feed(feedID); - m_api.addSubscription(feed.getFeedID(), feed.getTitle(), feed.getCatString().replace(catID + ",", "")); -} - -public override void importOPML(string opml) -{ - m_api.importOPML(opml); -} - -public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) -{ - m_api.getUnreadCounts(); - - if(m_api.getCategories(categories)) + + public override string uncategorizedID() + { + return ""; + } + + public override bool hideCategoryWhenEmpty(string catID) + { + return catID.has_suffix("global.must"); + } + + public override bool supportCategories() + { + return true; + } + + public override bool supportFeedManipulation() + { + return true; + } + + public override bool supportMultiLevelCategories() + { + return false; + } + + public override bool supportMultiCategoriesPerFeed() + { + return true; + } + + public override bool syncFeedsAndCategories() + { + return true; + } + + public override bool tagIDaffectedByNameChange() + { + return false; + } + + public override void resetAccount() + { + m_utils.resetAccount(); + } + + public override bool useMaxArticles() + { + return true; + } + + public override LoginResponse login() + { + return m_api.login(); + } + + public override void setArticleIsRead(string articleIDs, ArticleStatus read) + { + m_api.mark_as_read(articleIDs, "entries", read); + } + + public override void setArticleIsMarked(string articleID, ArticleStatus marked) { - if(cancellable != null && cancellable.is_cancelled()) + if(marked == ArticleStatus.MARKED) { - return false; + m_api.addArticleTag(articleID, m_api.getMarkedID()); } - - if(m_api.getFeeds(feeds)) + else if(marked == ArticleStatus.UNMARKED) { - if(cancellable != null && cancellable.is_cancelled()) - { - return false; - } - - if(m_api.getTags(tags)) - { - return true; - } + m_api.deleteArticleTag(articleID, m_api.getMarkedID()); } } - - return false; -} - -public override int getUnreadCount() -{ - return m_api.getTotalUnread(); -} - -public override void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) -{ - string continuation = null; - string feedly_tagID = ""; - string feedly_feedID = ""; - if(feedID != null) + + public override bool alwaysSetReadByID() + { + return false; + } + + public override void setFeedRead(string feedID) + { + m_api.mark_as_read(feedID, "feeds", ArticleStatus.READ); + } + + public override void setCategoryRead(string catID) + { + m_api.mark_as_read(catID, "categories", ArticleStatus.READ); + } + + public override void markAllItemsRead() { - if(isTagID) + string catArray = ""; + string feedArray = ""; + + var db = DataBase.readOnly(); + var categories = db.read_categories(); + var feeds = db.read_feeds_without_cat(); + + foreach(Category cat in categories) { - feedly_tagID = feedID; + catArray += cat.getCatID() + ","; } - else + + foreach(Feed feed in feeds) { - feedly_feedID = feedID; + feedArray += feed.getFeedID() + ","; } + + m_api.mark_as_read(catArray.substring(0, catArray.length-1), "categories", ArticleStatus.READ); + m_api.mark_as_read(feedArray.substring(0, feedArray.length-1), "feeds", ArticleStatus.READ); } - - int skip = count; - int amount = 200; - var articles = new Gee.LinkedList<Article>(); - - while(skip > 0) + + public override void tagArticle(string articleID, string tagID) + { + m_api.addArticleTag(articleID, tagID); + } + + public override void removeArticleTag(string articleID, string tagID) + { + m_api.deleteArticleTag(articleID, tagID); + } + + public override string createTag(string caption) + { + return m_api.createTag(caption); + } + + public override void deleteTag(string tagID) + { + m_api.deleteTag(tagID); + } + + public override void renameTag(string tagID, string title) + { + m_api.renameTag(tagID, title); + } + + public override bool serverAvailable() + { + return Utils.ping("http://feedly.com/"); + } + + public override bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) { - if(cancellable != null && cancellable.is_cancelled()) + feedID = "feed/" + feedURL; + bool success = false; + errmsg = ""; + + if(catID == null && newCatName != null) { - return; + string newCatID = m_api.createCatID(newCatName); + success = m_api.addSubscription(feedURL, null, newCatID); } - - if(skip >= amount) + else { - skip -= amount; + success = m_api.addSubscription(feedURL, null, catID); } - else + + if(!success) { - amount = skip; - skip = 0; + errmsg = @"feedly could not add $feedURL"; } - - continuation = m_api.getArticles(articles, amount, continuation, whatToGet, feedly_tagID, feedly_feedID); - - if(continuation == null) + + return success; + } + + public override void removeFeed(string feedID) + { + m_api.removeSubscription(feedID); + } + + public override void renameFeed(string feedID, string title) + { + var feed = DataBase.readOnly().read_feed(feedID); + m_api.addSubscription(feed.getFeedID(), title, feed.getCatString()); + } + + public override void moveFeed(string feedID, string newCatID, string? currentCatID ) + { + m_api.moveSubscription(feedID, newCatID, currentCatID); + } + + public override string createCategory(string title, string? parentID) + { + return m_api.createCatID(title); + } + + public override void renameCategory(string catID, string title) + { + m_api.renameCategory(catID, title); + } + + public override void moveCategory(string catID, string newParentID) + { + return; + } + + public override void deleteCategory(string catID) + { + m_api.removeCategory(catID); + } + + public override void removeCatFromFeed(string feedID, string catID) + { + var feed = DataBase.readOnly().read_feed(feedID); + m_api.addSubscription(feed.getFeedID(), feed.getTitle(), feed.getCatString().replace(catID + ",", "")); + } + + public override void importOPML(string opml) + { + m_api.importOPML(opml); + } + + public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) + { + m_api.getUnreadCounts(); + + if(m_api.getCategories(categories)) { - break; + if(cancellable != null && cancellable.is_cancelled()) + { + return false; + } + + if(m_api.getFeeds(feeds)) + { + if(cancellable != null && cancellable.is_cancelled()) + { + return false; + } + + if(m_api.getTags(tags)) + { + return true; + } + } } + + return false; + } + + public override int getUnreadCount() + { + return m_api.getTotalUnread(); + } + + public override void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) + { + string continuation = null; + string feedly_tagID = ""; + string feedly_feedID = ""; + if(feedID != null) + { + if(isTagID) + { + feedly_tagID = feedID; + } + else + { + feedly_feedID = feedID; + } + } + + int skip = count; + int amount = 200; + var articles = new Gee.LinkedList<Article>(); + + while(skip > 0) + { + if(cancellable != null && cancellable.is_cancelled()) + { + return; + } + + if(skip >= amount) + { + skip -= amount; + } + else + { + amount = skip; + skip = 0; + } + + continuation = m_api.getArticles(articles, amount, continuation, whatToGet, feedly_tagID, feedly_feedID); + + if(continuation == null) + { + break; + } + } + + writeArticles(articles); } - - writeArticles(articles); -} } [ModuleInit] diff --git a/plugins/backend/feedly/feedlyUtils.vala b/plugins/backend/feedly/feedlyUtils.vala index 1626eaee..5dc0825d 100644 --- a/plugins/backend/feedly/feedlyUtils.vala +++ b/plugins/backend/feedly/feedlyUtils.vala @@ -14,94 +14,94 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. namespace FeedReader.FeedlySecret { -const string base_uri = "http://cloud.feedly.com"; -const string apiClientId = "boutroue"; -const string apiClientSecret = "FE012EGICU4ZOBDRBEOVAJA1JZYH"; -const string apiRedirectUri = "http://localhost"; -const string apiAuthScope = "https://cloud.feedly.com/subscriptions"; + const string base_uri = "http://cloud.feedly.com"; + const string apiClientId = "boutroue"; + const string apiClientSecret = "FE012EGICU4ZOBDRBEOVAJA1JZYH"; + const string apiRedirectUri = "http://localhost"; + const string apiAuthScope = "https://cloud.feedly.com/subscriptions"; } public class FeedReader.FeedlyUtils : Object { - -private GLib.Settings m_settings; - -public FeedlyUtils(GLib.SettingsBackend? settings_backend) -{ - if(settings_backend != null) + + private GLib.Settings m_settings; + + public FeedlyUtils(GLib.SettingsBackend? settings_backend) { - m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.feedly", settings_backend); + if(settings_backend != null) + { + m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.feedly", settings_backend); + } + else + { + m_settings = new GLib.Settings("org.gnome.feedreader.feedly"); + } } - else + + public string getRefreshToken() { - m_settings = new GLib.Settings("org.gnome.feedreader.feedly"); + return Utils.gsettingReadString(m_settings, "refresh-token"); } -} - -public string getRefreshToken() -{ - return Utils.gsettingReadString(m_settings, "refresh-token"); -} - -public void setRefreshToken(string token) -{ - Utils.gsettingWriteString(m_settings, "refresh-token", token); -} - -public string getAccessToken() -{ - return Utils.gsettingReadString(m_settings, "access-token"); -} - -public void setAccessToken(string token) -{ - Utils.gsettingWriteString(m_settings, "access-token", token); -} - -public string getApiCode() -{ - return Utils.gsettingReadString(m_settings, "api-code"); -} - -public void setApiCode(string code) -{ - Utils.gsettingWriteString(m_settings, "api-code", code); -} - -public string getEmail() -{ - return Utils.gsettingReadString(m_settings, "email"); -} - -public void setEmail(string email) -{ - Utils.gsettingWriteString(m_settings, "email", email); -} - -public int getExpiration() -{ - return m_settings.get_int("access-token-expires"); -} - -public void setExpiration(int seconds) -{ - m_settings.set_int("access-token-expires", seconds); -} - -public void resetAccount() -{ - Utils.resetSettings(m_settings); -} - -public bool accessTokenValid() -{ - var now = new DateTime.now_local(); - - if((int)now.to_unix() > getExpiration()) + + public void setRefreshToken(string token) { - Logger.warning("FeedlyUtils: access token expired"); - return false; + Utils.gsettingWriteString(m_settings, "refresh-token", token); + } + + public string getAccessToken() + { + return Utils.gsettingReadString(m_settings, "access-token"); + } + + public void setAccessToken(string token) + { + Utils.gsettingWriteString(m_settings, "access-token", token); + } + + public string getApiCode() + { + return Utils.gsettingReadString(m_settings, "api-code"); + } + + public void setApiCode(string code) + { + Utils.gsettingWriteString(m_settings, "api-code", code); + } + + public string getEmail() + { + return Utils.gsettingReadString(m_settings, "email"); + } + + public void setEmail(string email) + { + Utils.gsettingWriteString(m_settings, "email", email); + } + + public int getExpiration() + { + return m_settings.get_int("access-token-expires"); + } + + public void setExpiration(int seconds) + { + m_settings.set_int("access-token-expires", seconds); + } + + public void resetAccount() + { + Utils.resetSettings(m_settings); + } + + public bool accessTokenValid() + { + var now = new DateTime.now_local(); + + if((int)now.to_unix() > getExpiration()) + { + Logger.warning("FeedlyUtils: access token expired"); + return false; + } + + return true; } - - return true; -} } diff --git a/plugins/backend/fresh/freshAPI.vala b/plugins/backend/fresh/freshAPI.vala index bbe154b8..a0086982 100644 --- a/plugins/backend/fresh/freshAPI.vala +++ b/plugins/backend/fresh/freshAPI.vala @@ -14,291 +14,291 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.freshAPI : Object { - -private freshConnection m_connection; -private freshUtils m_utils; - -public freshAPI(freshUtils utils) -{ - m_utils = utils; - m_connection = new freshConnection(m_utils); -} - -public LoginResponse login() -{ - Logger.debug("fresh backend: login"); - - if(!Utils.ping(m_utils.getUnmodifiedURL())) + + private freshConnection m_connection; + private freshUtils m_utils; + + public freshAPI(freshUtils utils) { - return LoginResponse.NO_CONNECTION; + m_utils = utils; + m_connection = new freshConnection(m_utils); } - - return m_connection.getSID(); -} - -public bool getSubscriptionList(Gee.List<Feed> feeds) -{ - var response = m_connection.getRequest("reader/api/0/subscription/list?output=json"); - - if(response.status != 200) - { - return false; - } - - var parser = new Json.Parser(); - try + + public LoginResponse login() { - parser.load_from_data(response.data, -1); - } - catch (Error e) - { - Logger.error("getTagList: Could not load message response"); - Logger.error(e.message); - return false; + Logger.debug("fresh backend: login"); + + if(!Utils.ping(m_utils.getUnmodifiedURL())) + { + return LoginResponse.NO_CONNECTION; + } + + return m_connection.getSID(); } - Json.Array array = parser.get_root().get_object().get_array_member("subscriptions"); - - for (int i = 0; i < array.get_length (); i++) + + public bool getSubscriptionList(Gee.List<Feed> feeds) { - Json.Object object = array.get_object_element(i); - - string url = object.get_string_member("htmlUrl"); - string id = object.get_string_member("id"); - string catID = object.get_array_member("categories").get_object_element(0).get_string_member("id"); - string xmlURL = object.get_string_member("url"); - - feeds.add( - new Feed( - id, - object.get_string_member("title"), - url, - 0, - ListUtils.single(catID), - object.get_string_member("iconUrl"), + var response = m_connection.getRequest("reader/api/0/subscription/list?output=json"); + + if(response.status != 200) + { + return false; + } + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch (Error e) + { + Logger.error("getTagList: Could not load message response"); + Logger.error(e.message); + return false; + } + Json.Array array = parser.get_root().get_object().get_array_member("subscriptions"); + + for (int i = 0; i < array.get_length (); i++) + { + Json.Object object = array.get_object_element(i); + + string url = object.get_string_member("htmlUrl"); + string id = object.get_string_member("id"); + string catID = object.get_array_member("categories").get_object_element(0).get_string_member("id"); + string xmlURL = object.get_string_member("url"); + + feeds.add( + new Feed( + id, + object.get_string_member("title"), + url, + 0, + ListUtils.single(catID), + object.get_string_member("iconUrl"), xmlURL) ); + } + + return true; } - - return true; -} - -public bool getTagList(Gee.List<Category> categories) -{ - var response = m_connection.getRequest("reader/api/0/tag/list?output=json"); - string prefix = "user/-/label/"; - - if(response.status != 200) - { - return false; - } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); - } - catch (Error e) - { - Logger.error("getTagList: Could not load message response"); - Logger.error(e.message); - return false; - } - Json.Array array = parser.get_root().get_object().get_array_member("tags"); - - for (int i = 0; i < array.get_length (); i++) + + public bool getTagList(Gee.List<Category> categories) { - Json.Object object = array.get_object_element(i); - string categorieID = object.get_string_member("id"); - - - if(!categorieID.has_prefix(prefix)) + var response = m_connection.getRequest("reader/api/0/tag/list?output=json"); + string prefix = "user/-/label/"; + + if(response.status != 200) { - continue; + return false; } - - categories.add( - new Category ( - categorieID, - categorieID.substring(prefix.length), - 0, - i+1, - CategoryID.MASTER.to_string(), - 1 + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch (Error e) + { + Logger.error("getTagList: Could not load message response"); + Logger.error(e.message); + return false; + } + Json.Array array = parser.get_root().get_object().get_array_member("tags"); + + for (int i = 0; i < array.get_length (); i++) + { + Json.Object object = array.get_object_element(i); + string categorieID = object.get_string_member("id"); + + + if(!categorieID.has_prefix(prefix)) + { + continue; + } + + categories.add( + new Category ( + categorieID, + categorieID.substring(prefix.length), + 0, + i+1, + CategoryID.MASTER.to_string(), + 1 ) ); + } + + return true; } - - return true; -} - -public int getUnreadCounts() -{ - var response = m_connection.getRequest("reader/api/0/unread-count?output=json"); - - if(response.status != 200) - { - return 0; - } - - int count = 0; - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); - } - catch (Error e) + + public int getUnreadCounts() { - Logger.error("getTagList: Could not load message response"); - Logger.error(e.message); - } - Json.Array array = parser.get_root().get_object().get_array_member("unreadcounts"); - - for (int i = 0; i < array.get_length (); i++) - { - Json.Object object = array.get_object_element(i); - if(object.get_string_member("id") == "user/-/state/com.google/reading-list") + var response = m_connection.getRequest("reader/api/0/unread-count?output=json"); + + if(response.status != 200) { - count = (int)object.get_int_member("count"); + return 0; } - } - - return count; -} - -public string? getStreamContents( - Gee.List<Article> articles, - string? feedID = null, - string? labelID = null, - string? exclude = null, - int count = 400, - string order = "d", - string? checkpoint = null - ) -{ - var now = new DateTime.now_local(); - string path = "reader/api/0/stream/contents"; - - if(feedID != null) - { - path += "/" + feedID; - } - else if(labelID != null) - { - path += "/" + labelID; - } - - - var msg = new freshMessage(); - msg.add("output", "json"); - msg.add("r", order); - msg.add("n", count.to_string()); - msg.add("client", "FeedReader"); - msg.add("ck", now.to_unix().to_string()); - - if(exclude != null) - { - msg.add("xt", exclude); - } - - if(checkpoint != null) - { - msg.add("c", checkpoint); - } - - Logger.debug("getStreamContents: %s".printf(msg.get())); - - var response = m_connection.getRequest(path + "?" + msg.get()); - - if(response.status != 200) - { - return null; - } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); - } - catch(Error e) - { - Logger.error("getStreamContents: Could not load message response"); - Logger.error(e.message); - } - - var root = parser.get_root().get_object(); - var array = root.get_array_member("items"); - uint length = array.get_length(); - - for(uint i = 0; i < length; i++) - { - Json.Object object = array.get_object_element(i); - string id = object.get_string_member("id"); - bool marked = false; - bool read = false; - var cats = object.get_array_member("categories"); - uint cat_length = cats.get_length(); - - for(uint j = 0; j < cat_length; j++) + + int count = 0; + + var parser = new Json.Parser(); + try { - string cat = cats.get_string_element(j); - if(cat.has_suffix("com.google/starred")) - { - marked = true; - } - else if(cat.has_suffix("com.google/read")) + parser.load_from_data(response.data, -1); + } + catch (Error e) + { + Logger.error("getTagList: Could not load message response"); + Logger.error(e.message); + } + Json.Array array = parser.get_root().get_object().get_array_member("unreadcounts"); + + for (int i = 0; i < array.get_length (); i++) + { + Json.Object object = array.get_object_element(i); + if(object.get_string_member("id") == "user/-/state/com.google/reading-list") { - read = true; + count = (int)object.get_int_member("count"); } } - - var enclosures = new Gee.ArrayList<Enclosure>(); - if(object.has_member("enclosure")) + + return count; + } + + public string? getStreamContents( + Gee.List<Article> articles, + string? feedID = null, + string? labelID = null, + string? exclude = null, + int count = 400, + string order = "d", + string? checkpoint = null + ) + { + var now = new DateTime.now_local(); + string path = "reader/api/0/stream/contents"; + + if(feedID != null) { - var attachments = object.get_array_member("enclosure"); - - uint mediaCount = 0; - if(attachments != null) + path += "/" + feedID; + } + else if(labelID != null) + { + path += "/" + labelID; + } + + + var msg = new freshMessage(); + msg.add("output", "json"); + msg.add("r", order); + msg.add("n", count.to_string()); + msg.add("client", "FeedReader"); + msg.add("ck", now.to_unix().to_string()); + + if(exclude != null) + { + msg.add("xt", exclude); + } + + if(checkpoint != null) + { + msg.add("c", checkpoint); + } + + Logger.debug("getStreamContents: %s".printf(msg.get())); + + var response = m_connection.getRequest(path + "?" + msg.get()); + + if(response.status != 200) + { + return null; + } + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("getStreamContents: Could not load message response"); + Logger.error(e.message); + } + + var root = parser.get_root().get_object(); + var array = root.get_array_member("items"); + uint length = array.get_length(); + + for(uint i = 0; i < length; i++) + { + Json.Object object = array.get_object_element(i); + string id = object.get_string_member("id"); + bool marked = false; + bool read = false; + var cats = object.get_array_member("categories"); + uint cat_length = cats.get_length(); + + for(uint j = 0; j < cat_length; j++) { - mediaCount = attachments.get_length(); + string cat = cats.get_string_element(j); + if(cat.has_suffix("com.google/starred")) + { + marked = true; + } + else if(cat.has_suffix("com.google/read")) + { + read = true; + } } - - for(int j = 0; j < mediaCount; ++j) + + var enclosures = new Gee.ArrayList<Enclosure>(); + if(object.has_member("enclosure")) { - var attachment = attachments.get_object_element(j); - string type = attachment.has_member("type") ? attachment.get_string_member("type") : ""; - - enclosures.add( - new Enclosure(id, attachment.get_string_member("href"), - EnclosureType.from_string(type)) + var attachments = object.get_array_member("enclosure"); + + uint mediaCount = 0; + if(attachments != null) + { + mediaCount = attachments.get_length(); + } + + for(int j = 0; j < mediaCount; ++j) + { + var attachment = attachments.get_object_element(j); + string type = attachment.has_member("type") ? attachment.get_string_member("type") : ""; + + enclosures.add( + new Enclosure(id, attachment.get_string_member("href"), + EnclosureType.from_string(type)) ); + } } - } - - articles.add(new Article( - id, - object.get_string_member("title"), - object.get_array_member("alternate").get_object_element(0).get_string_member("href"), - object.get_object_member("origin").get_string_member("streamId"), - read ? ArticleStatus.READ : ArticleStatus.UNREAD, - marked ? ArticleStatus.MARKED : ArticleStatus.UNMARKED, - object.get_object_member("summary").get_string_member("content"), - null, - object.get_string_member("author"), - new DateTime.from_unix_local(object.get_int_member("published")), - -1, - null, - enclosures - ) - ); - } - - + + articles.add(new Article( + id, + object.get_string_member("title"), + object.get_array_member("alternate").get_object_element(0).get_string_member("href"), + object.get_object_member("origin").get_string_member("streamId"), + read ? ArticleStatus.READ : ArticleStatus.UNREAD, + marked ? ArticleStatus.MARKED : ArticleStatus.UNMARKED, + object.get_object_member("summary").get_string_member("content"), + null, + object.get_string_member("author"), + new DateTime.from_unix_local(object.get_int_member("published")), + -1, + null, + enclosures + ) + ); + } + + if(root.has_member("continuation") && root.get_string_member("continuation") != "") { return root.get_string_member("continuation"); } - + return null; } @@ -306,27 +306,27 @@ public void editTags(string articleIDs, string? addTag = null, string? removeTag { string path = "reader/api/0/edit-tag"; string[] arrayID = articleIDs.split(","); - + var msg = new freshMessage(); msg.add("T", m_connection.getToken()); - + if(addTag != null) { msg.add("a", addTag); } - + if(removeTag != null) { msg.add("r", removeTag); } - + foreach(string id in arrayID) { msg.add("i", "-/" + id); } - + var response = m_connection.postRequest(path, msg.get(), "application/x-www-form-urlencoded"); - + if(response.status != 200) { Logger.debug(path + " " + msg.get()); @@ -337,14 +337,14 @@ public void editTags(string articleIDs, string? addTag = null, string? removeTag public void markAllAsRead(string streamID) { string path = "reader/api/0/mark-all-as-read"; - + var msg = new freshMessage(); msg.add("T", m_connection.getToken()); msg.add("s", streamID); msg.add("ts", DataBase.readOnly().getNewestArticle()); - + var response = m_connection.postRequest(path, msg.get(), "application/x-www-form-urlencoded"); - + if(response.status != 200) { Logger.debug(path + " " + msg.get()); @@ -358,44 +358,44 @@ public Response editStream( string? title = null, string? add = null, string? remove = null - ) +) { string path = "reader/api/0/subscription/edit"; - + var msg = new freshMessage(); msg.add("T", m_connection.getToken()); msg.add("ac", action); - + if(streamID != null) { foreach(string s in streamID) { msg.add("s", s); } } - + if(title != null) { msg.add("t", title); } - + if(add != null) { msg.add("a", add); } - + if(remove != null) { msg.add("r", remove); } - + var response = m_connection.postRequest(path, msg.get(), "application/x-www-form-urlencoded"); - + if(response.status != 200) { Logger.debug(path + " " + msg.get()); Logger.debug(response.status.to_string()); } - + return response; } @@ -407,15 +407,15 @@ public string composeTagID(string title) public void renameTag(string tagID, string title) { string path = "reader/api/0/rename-tag"; - + var msg = new freshMessage(); msg.add("T", m_connection.getToken()); msg.add("s", tagID); msg.add("dest", composeTagID(title)); - + var response = m_connection.postRequest(path, msg.get(), "application/x-www-form-urlencoded"); - - + + if(response.status != 200) { Logger.debug(path + " " + msg.get()); @@ -426,13 +426,13 @@ public void renameTag(string tagID, string title) public void deleteTag(string tagID) { string path = "reader/api/0/disable-tag"; - + var msg = new freshMessage(); msg.add("T", m_connection.getToken()); msg.add("s", tagID); - + var response = m_connection.postRequest(path, msg.get(), "application/x-www-form-urlencoded"); - + if(response.status != 200) { Logger.debug(path + " " + msg.get()); diff --git a/plugins/backend/fresh/freshConnection.vala b/plugins/backend/fresh/freshConnection.vala index e9e8e692..eaf0c7f7 100644 --- a/plugins/backend/fresh/freshConnection.vala +++ b/plugins/backend/fresh/freshConnection.vala @@ -14,145 +14,145 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.freshConnection { - -private freshUtils m_utils; -private GLib.Settings m_settingsTweaks; -private Soup.Session m_session; - -public freshConnection(freshUtils utils) -{ - m_utils = utils; - m_settingsTweaks = new GLib.Settings("org.gnome.feedreader.tweaks"); - m_session = new Soup.Session(); - m_session.user_agent = Constants.USER_AGENT; - m_session.authenticate.connect((msg, auth, retrying) => { + + private freshUtils m_utils; + private GLib.Settings m_settingsTweaks; + private Soup.Session m_session; + + public freshConnection(freshUtils utils) + { + m_utils = utils; + m_settingsTweaks = new GLib.Settings("org.gnome.feedreader.tweaks"); + m_session = new Soup.Session(); + m_session.user_agent = Constants.USER_AGENT; + m_session.authenticate.connect((msg, auth, retrying) => { if(m_utils.getHtaccessUser() == "") { - Logger.error("fresh Session: need Authentication"); + Logger.error("fresh Session: need Authentication"); } else if(!retrying) { - auth.authenticate(m_utils.getHtaccessUser(), m_utils.getHtaccessPasswd()); + auth.authenticate(m_utils.getHtaccessUser(), m_utils.getHtaccessPasswd()); } }); -} - -public LoginResponse getSID() -{ - var message = new Soup.Message("POST", m_utils.getURL()+"accounts/ClientLogin"); - - var msg = new freshMessage(); - msg.add("Email", m_utils.getUser()); - msg.add("Passwd", m_utils.getPasswd()); - - message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, msg.get().data); - m_session.send_message(message); - - if(message.status_code != 200) - { - Logger.error("No response from freshRSS to message getSID()"); - return LoginResponse.NO_CONNECTION; } - - string response = (string)message.response_body.flatten().data; - - if(!response.has_prefix("SID=")) + + public LoginResponse getSID() { - m_utils.setToken(""); - m_utils.setUser(""); - m_utils.setURL(""); - return LoginResponse.WRONG_LOGIN; + var message = new Soup.Message("POST", m_utils.getURL()+"accounts/ClientLogin"); + + var msg = new freshMessage(); + msg.add("Email", m_utils.getUser()); + msg.add("Passwd", m_utils.getPasswd()); + + message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, msg.get().data); + m_session.send_message(message); + + if(message.status_code != 200) + { + Logger.error("No response from freshRSS to message getSID()"); + return LoginResponse.NO_CONNECTION; + } + + string response = (string)message.response_body.flatten().data; + + if(!response.has_prefix("SID=")) + { + m_utils.setToken(""); + m_utils.setUser(""); + m_utils.setURL(""); + return LoginResponse.WRONG_LOGIN; + } + else + { + int start = response.index_of("=")+1; + int end = response.index_of("\n"); + string token = response.substring(start, end-start); + Logger.debug("Token: " + token); + m_utils.setToken(token); + return LoginResponse.SUCCESS; + } } - else + + public string getToken() { - int start = response.index_of("=")+1; - int end = response.index_of("\n"); - string token = response.substring(start, end-start); - Logger.debug("Token: " + token); - m_utils.setToken(token); - return LoginResponse.SUCCESS; + return getRequest("reader/api/0/token").data.replace("\n", ""); } -} - -public string getToken() -{ - return getRequest("reader/api/0/token").data.replace("\n", ""); -} - -public Response postRequest(string path, string input, string type) -{ - var message = new Soup.Message("POST", m_utils.getURL()+path); - - if(m_settingsTweaks.get_boolean("do-not-track")) + + public Response postRequest(string path, string input, string type) { - message.request_headers.append("DNT", "1"); + var message = new Soup.Message("POST", m_utils.getURL()+path); + + if(m_settingsTweaks.get_boolean("do-not-track")) + { + message.request_headers.append("DNT", "1"); + } + + message.request_headers.append("Authorization","GoogleLogin auth=%s".printf(m_utils.getToken())); + message.request_headers.append("Content-Type", type); + + message.request_body.append_take(input.data); + m_session.send_message(message); + + if(message.status_code != 200) + { + Logger.warning("freshConnection: message unexpected response %u".printf(message.status_code)); + } + + return Response() { + status = message.status_code, + data = (string)message.response_body.flatten().data + }; } - - message.request_headers.append("Authorization","GoogleLogin auth=%s".printf(m_utils.getToken())); - message.request_headers.append("Content-Type", type); - - message.request_body.append_take(input.data); - m_session.send_message(message); - - if(message.status_code != 200) + + public Response getRequest(string path) { - Logger.warning("freshConnection: message unexpected response %u".printf(message.status_code)); + var message = new Soup.Message("GET", m_utils.getURL()+path); + message.request_headers.append("Authorization","GoogleLogin auth=%s".printf(m_utils.getToken())); + + if(m_settingsTweaks.get_boolean("do-not-track")) + { + message.request_headers.append("DNT", "1"); + } + + m_session.send_message(message); + + if(message.status_code != 200) + { + Logger.warning("freshConnection: message unexpected response %u".printf(message.status_code)); + } + + return Response() { + status = message.status_code, + data = (string)message.response_body.flatten().data + }; } - - return Response() { - status = message.status_code, - data = (string)message.response_body.flatten().data - }; } -public Response getRequest(string path) -{ - var message = new Soup.Message("GET", m_utils.getURL()+path); - message.request_headers.append("Authorization","GoogleLogin auth=%s".printf(m_utils.getToken())); - if(m_settingsTweaks.get_boolean("do-not-track")) +public class FeedReader.freshMessage { + + string request = ""; + + public freshMessage() { - message.request_headers.append("DNT", "1"); + } - - m_session.send_message(message); - - if(message.status_code != 200) + + public void add(string parameter, string val) { - Logger.warning("freshConnection: message unexpected response %u".printf(message.status_code)); + if(request != "") + { + request += "&"; + } + + request += parameter; + request += "="; + request += GLib.Uri.escape_string(val, "/"); } - - return Response() { - status = message.status_code, - data = (string)message.response_body.flatten().data - }; -} -} - - -public class FeedReader.freshMessage { - -string request = ""; - -public freshMessage() -{ - -} - -public void add(string parameter, string val) -{ - if(request != "") + + public string get() { - request += "&"; + return request; } - - request += parameter; - request += "="; - request += GLib.Uri.escape_string(val, "/"); -} - -public string get() -{ - return request; -} } diff --git a/plugins/backend/fresh/freshInterface.vala b/plugins/backend/fresh/freshInterface.vala index 47375901..3461a500 100644 --- a/plugins/backend/fresh/freshInterface.vala +++ b/plugins/backend/fresh/freshInterface.vala @@ -14,487 +14,487 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.freshInterface : FeedServerInterface { - -private freshAPI m_api; -private freshUtils m_utils; -private Gtk.Entry m_urlEntry; -private Gtk.Entry m_userEntry; -private Gtk.Entry m_passwordEntry; -private Gtk.Entry m_authPasswordEntry; -private Gtk.Entry m_authUserEntry; -private Gtk.Revealer m_revealer; -private bool m_need_htaccess = false; - -public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) -{ - m_utils = new freshUtils(settings_backend, secrets); - m_api = new freshAPI(m_utils); -} - -public override string getWebsite() -{ - return "https://freshrss.org/"; -} - -public override BackendFlags getFlags() -{ - return (BackendFlags.SELF_HOSTED | BackendFlags.FREE_SOFTWARE | BackendFlags.FREE); -} - -public override string getID() -{ - return "fresh"; -} - -public override string iconName() -{ - return "feed-service-fresh"; -} - -public override string serviceName() -{ - return "freshRSS"; -} - -public override bool needWebLogin() -{ - return false; -} - -public override Gtk.Box? getWidget() -{ - var url_label = new Gtk.Label(_("freshRSS URL:")); - var user_label = new Gtk.Label(_("Username:")); - var password_label = new Gtk.Label(_("Password:")); - - url_label.set_alignment(1.0f, 0.5f); - user_label.set_alignment(1.0f, 0.5f); - password_label.set_alignment(1.0f, 0.5f); - - url_label.set_hexpand(true); - user_label.set_hexpand(true); - password_label.set_hexpand(true); - - m_urlEntry = new Gtk.Entry(); - m_userEntry = new Gtk.Entry(); - m_passwordEntry = new Gtk.Entry(); - - m_urlEntry.activate.connect(() => { tryLogin(); }); - m_userEntry.activate.connect(() => { tryLogin(); }); - m_passwordEntry.activate.connect(() => { tryLogin(); }); - - m_passwordEntry.set_input_purpose(Gtk.InputPurpose.PASSWORD); - m_passwordEntry.set_visibility(false); - - var grid = new Gtk.Grid(); - grid.set_column_spacing(10); - grid.set_row_spacing(10); - grid.set_valign(Gtk.Align.CENTER); - grid.set_halign(Gtk.Align.CENTER); - - grid.attach(url_label, 0, 0, 1, 1); - grid.attach(m_urlEntry, 1, 0, 1, 1); - grid.attach(user_label, 0, 1, 1, 1); - grid.attach(m_userEntry, 1, 1, 1, 1); - grid.attach(password_label, 0, 2, 1, 1); - grid.attach(m_passwordEntry, 1, 2, 1, 1); - - - // http auth stuff ---------------------------------------------------- - var auth_user_label = new Gtk.Label(_("Username:")); - var auth_password_label = new Gtk.Label(_("Password:")); - - auth_user_label.set_alignment(1.0f, 0.5f); - auth_password_label.set_alignment(1.0f, 0.5f); - - auth_user_label.set_hexpand(true); - auth_password_label.set_hexpand(true); - - m_authUserEntry = new Gtk.Entry(); - m_authPasswordEntry = new Gtk.Entry(); - m_authPasswordEntry.set_input_purpose(Gtk.InputPurpose.PASSWORD); - m_authPasswordEntry.set_visibility(false); - - m_authUserEntry.activate.connect(() => { tryLogin(); }); - m_authPasswordEntry.activate.connect(() => { tryLogin(); }); - - var authGrid = new Gtk.Grid(); - authGrid.margin = 10; - authGrid.set_column_spacing(10); - authGrid.set_row_spacing(10); - authGrid.set_valign(Gtk.Align.CENTER); - authGrid.set_halign(Gtk.Align.CENTER); - - authGrid.attach(auth_user_label, 0, 0, 1, 1); - authGrid.attach(m_authUserEntry, 1, 0, 1, 1); - authGrid.attach(auth_password_label, 0, 1, 1, 1); - authGrid.attach(m_authPasswordEntry, 1, 1, 1, 1); - - var frame = new Gtk.Frame(_("HTTP Authorization")); - frame.set_halign(Gtk.Align.CENTER); - frame.add(authGrid); - m_revealer = new Gtk.Revealer(); - m_revealer.add(frame); - //--------------------------------------------------------------------- - - var logo = new Gtk.Image.from_icon_name("feed-service-fresh", Gtk.IconSize.MENU); - - var loginLabel = new Gtk.Label(_("Please log in to your freshRSS server and enjoy using FeedReader")); - loginLabel.get_style_context().add_class("h2"); - loginLabel.set_justify(Gtk.Justification.CENTER); - loginLabel.set_lines(3); - - var loginButton = new Gtk.Button.with_label(_("Login")); - loginButton.halign = Gtk.Align.END; - loginButton.set_size_request(80, 30); - loginButton.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); - loginButton.clicked.connect(() => { tryLogin(); }); - - var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10); - box.valign = Gtk.Align.CENTER; - box.halign = Gtk.Align.CENTER; - box.pack_start(loginLabel, false, false, 10); - box.pack_start(logo, false, false, 10); - box.pack_start(grid, true, true, 10); - box.pack_start(m_revealer, true, true, 10); - box.pack_end(loginButton, false, false, 20); - - m_urlEntry.set_text(m_utils.getUnmodifiedURL()); - m_userEntry.set_text(m_utils.getUser()); - m_passwordEntry.set_text(m_utils.getPasswd()); - - return box; -} - -public override void showHtAccess() -{ - m_revealer.set_reveal_child(true); -} - -public override void writeData() -{ - m_utils.setURL(m_urlEntry.get_text()); - m_utils.setUser(m_userEntry.get_text().strip()); - m_utils.setPassword(m_passwordEntry.get_text().strip()); - if(m_need_htaccess) + + private freshAPI m_api; + private freshUtils m_utils; + private Gtk.Entry m_urlEntry; + private Gtk.Entry m_userEntry; + private Gtk.Entry m_passwordEntry; + private Gtk.Entry m_authPasswordEntry; + private Gtk.Entry m_authUserEntry; + private Gtk.Revealer m_revealer; + private bool m_need_htaccess = false; + + public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) { - m_utils.setHtaccessUser(m_authUserEntry.get_text().strip()); - m_utils.setHtAccessPassword(m_authPasswordEntry.get_text().strip()); + m_utils = new freshUtils(settings_backend, secrets); + m_api = new freshAPI(m_utils); } -} - -public override bool supportTags() -{ - return false; -} - -public override bool doInitSync() -{ - return true; -} - -public override string symbolicIcon() -{ - return "feed-service-fresh-symbolic"; -} - -public override string accountName() -{ - return m_utils.getUser(); -} - -public override string getServerURL() -{ - return m_utils.getUnmodifiedURL(); -} - -public override string uncategorizedID() -{ - return "1"; -} - -public override bool hideCategoryWhenEmpty(string catID) -{ - return false; -} - -public override bool supportCategories() -{ - return true; -} - -public override bool supportFeedManipulation() -{ - return true; -} - -public override bool supportMultiLevelCategories() -{ - return false; -} - -public override bool supportMultiCategoriesPerFeed() -{ - return false; -} - -public override bool syncFeedsAndCategories() -{ - return true; -} - -public override bool tagIDaffectedByNameChange() -{ - return true; -} - -public override void resetAccount() -{ - m_utils.resetAccount(); -} - -public override bool useMaxArticles() -{ - return true; -} - -public override LoginResponse login() -{ - return m_api.login(); -} - -public override bool serverAvailable() -{ - return Utils.ping(m_utils.getUnmodifiedURL()); -} - -public override void setArticleIsRead(string articleIDs, ArticleStatus read) -{ - if(read == ArticleStatus.READ) + + public override string getWebsite() { - m_api.editTags(articleIDs, "user/-/state/com.google/read", null); + return "https://freshrss.org/"; } - else + + public override BackendFlags getFlags() { - m_api.editTags(articleIDs, null, "user/-/state/com.google/read"); + return (BackendFlags.SELF_HOSTED | BackendFlags.FREE_SOFTWARE | BackendFlags.FREE); } -} - -public override void setArticleIsMarked(string articleID, ArticleStatus marked) -{ - if(marked == ArticleStatus.MARKED) + + public override string getID() { - m_api.editTags(articleID, "user/-/state/com.google/starred", null); + return "fresh"; } - else + + public override string iconName() { - m_api.editTags(articleID, null, "user/-/state/com.google/starred"); + return "feed-service-fresh"; } -} - -public override bool alwaysSetReadByID() -{ - return false; -} - -public override void setFeedRead(string feedID) -{ - m_api.markAllAsRead(feedID); -} - -public override void setCategoryRead(string catID) -{ - m_api.markAllAsRead(catID); -} - -public override void markAllItemsRead() -{ - m_api.markAllAsRead("user/-/state/com.google/reading-list"); -} - -public override void tagArticle(string articleID, string tagID) -{ - return; -} - -public override void removeArticleTag(string articleID, string tagID) -{ - return; -} - -public override string createTag(string caption) -{ - return ""; -} - -public override void deleteTag(string tagID) -{ - -} - -public override void renameTag(string tagID, string title) -{ - -} - -public override bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) -{ - string? cat = null; - if(catID != null) + + public override string serviceName() { - cat = catID; + return "freshRSS"; } - else if(newCatName != null) + + public override bool needWebLogin() { - cat = newCatName; + return false; } - - cat = m_api.composeTagID(cat); - - var response = m_api.editStream("subscribe", {"feed/" + feedURL}, null, cat, null); - if(response.status != 200) + + public override Gtk.Box? getWidget() + { + var url_label = new Gtk.Label(_("freshRSS URL:")); + var user_label = new Gtk.Label(_("Username:")); + var password_label = new Gtk.Label(_("Password:")); + + url_label.set_alignment(1.0f, 0.5f); + user_label.set_alignment(1.0f, 0.5f); + password_label.set_alignment(1.0f, 0.5f); + + url_label.set_hexpand(true); + user_label.set_hexpand(true); + password_label.set_hexpand(true); + + m_urlEntry = new Gtk.Entry(); + m_userEntry = new Gtk.Entry(); + m_passwordEntry = new Gtk.Entry(); + + m_urlEntry.activate.connect(() => { tryLogin(); }); + m_userEntry.activate.connect(() => { tryLogin(); }); + m_passwordEntry.activate.connect(() => { tryLogin(); }); + + m_passwordEntry.set_input_purpose(Gtk.InputPurpose.PASSWORD); + m_passwordEntry.set_visibility(false); + + var grid = new Gtk.Grid(); + grid.set_column_spacing(10); + grid.set_row_spacing(10); + grid.set_valign(Gtk.Align.CENTER); + grid.set_halign(Gtk.Align.CENTER); + + grid.attach(url_label, 0, 0, 1, 1); + grid.attach(m_urlEntry, 1, 0, 1, 1); + grid.attach(user_label, 0, 1, 1, 1); + grid.attach(m_userEntry, 1, 1, 1, 1); + grid.attach(password_label, 0, 2, 1, 1); + grid.attach(m_passwordEntry, 1, 2, 1, 1); + + + // http auth stuff ---------------------------------------------------- + var auth_user_label = new Gtk.Label(_("Username:")); + var auth_password_label = new Gtk.Label(_("Password:")); + + auth_user_label.set_alignment(1.0f, 0.5f); + auth_password_label.set_alignment(1.0f, 0.5f); + + auth_user_label.set_hexpand(true); + auth_password_label.set_hexpand(true); + + m_authUserEntry = new Gtk.Entry(); + m_authPasswordEntry = new Gtk.Entry(); + m_authPasswordEntry.set_input_purpose(Gtk.InputPurpose.PASSWORD); + m_authPasswordEntry.set_visibility(false); + + m_authUserEntry.activate.connect(() => { tryLogin(); }); + m_authPasswordEntry.activate.connect(() => { tryLogin(); }); + + var authGrid = new Gtk.Grid(); + authGrid.margin = 10; + authGrid.set_column_spacing(10); + authGrid.set_row_spacing(10); + authGrid.set_valign(Gtk.Align.CENTER); + authGrid.set_halign(Gtk.Align.CENTER); + + authGrid.attach(auth_user_label, 0, 0, 1, 1); + authGrid.attach(m_authUserEntry, 1, 0, 1, 1); + authGrid.attach(auth_password_label, 0, 1, 1, 1); + authGrid.attach(m_authPasswordEntry, 1, 1, 1, 1); + + var frame = new Gtk.Frame(_("HTTP Authorization")); + frame.set_halign(Gtk.Align.CENTER); + frame.add(authGrid); + m_revealer = new Gtk.Revealer(); + m_revealer.add(frame); + //--------------------------------------------------------------------- + + var logo = new Gtk.Image.from_icon_name("feed-service-fresh", Gtk.IconSize.MENU); + + var loginLabel = new Gtk.Label(_("Please log in to your freshRSS server and enjoy using FeedReader")); + loginLabel.get_style_context().add_class("h2"); + loginLabel.set_justify(Gtk.Justification.CENTER); + loginLabel.set_lines(3); + + var loginButton = new Gtk.Button.with_label(_("Login")); + loginButton.halign = Gtk.Align.END; + loginButton.set_size_request(80, 30); + loginButton.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); + loginButton.clicked.connect(() => { tryLogin(); }); + + var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10); + box.valign = Gtk.Align.CENTER; + box.halign = Gtk.Align.CENTER; + box.pack_start(loginLabel, false, false, 10); + box.pack_start(logo, false, false, 10); + box.pack_start(grid, true, true, 10); + box.pack_start(m_revealer, true, true, 10); + box.pack_end(loginButton, false, false, 20); + + m_urlEntry.set_text(m_utils.getUnmodifiedURL()); + m_userEntry.set_text(m_utils.getUser()); + m_passwordEntry.set_text(m_utils.getPasswd()); + + return box; + } + + public override void showHtAccess() + { + m_revealer.set_reveal_child(true); + } + + public override void writeData() + { + m_utils.setURL(m_urlEntry.get_text()); + m_utils.setUser(m_userEntry.get_text().strip()); + m_utils.setPassword(m_passwordEntry.get_text().strip()); + if(m_need_htaccess) + { + m_utils.setHtaccessUser(m_authUserEntry.get_text().strip()); + m_utils.setHtAccessPassword(m_authPasswordEntry.get_text().strip()); + } + } + + public override bool supportTags() { - feedID = ""; - errmsg = response.data; return false; } - - errmsg = ""; - feedID = response.data; - return true; -} - -public override void addFeeds(Gee.List<Feed> feeds) -{ - string cat = ""; - string[] urls = {}; - - foreach(Feed f in feeds) + + public override bool doInitSync() + { + return true; + } + + public override string symbolicIcon() + { + return "feed-service-fresh-symbolic"; + } + + public override string accountName() + { + return m_utils.getUser(); + } + + public override string getServerURL() + { + return m_utils.getUnmodifiedURL(); + } + + public override string uncategorizedID() + { + return "1"; + } + + public override bool hideCategoryWhenEmpty(string catID) + { + return false; + } + + public override bool supportCategories() + { + return true; + } + + public override bool supportFeedManipulation() + { + return true; + } + + public override bool supportMultiLevelCategories() + { + return false; + } + + public override bool supportMultiCategoriesPerFeed() + { + return false; + } + + public override bool syncFeedsAndCategories() + { + return true; + } + + public override bool tagIDaffectedByNameChange() + { + return true; + } + + public override void resetAccount() { - if(f.getCatIDs()[0] != cat) + m_utils.resetAccount(); + } + + public override bool useMaxArticles() + { + return true; + } + + public override LoginResponse login() + { + return m_api.login(); + } + + public override bool serverAvailable() + { + return Utils.ping(m_utils.getUnmodifiedURL()); + } + + public override void setArticleIsRead(string articleIDs, ArticleStatus read) + { + if(read == ArticleStatus.READ) { - m_api.editStream("subscribe", urls, null, cat, null); - urls = {}; - cat = f.getCatIDs()[0]; + m_api.editTags(articleIDs, "user/-/state/com.google/read", null); + } + else + { + m_api.editTags(articleIDs, null, "user/-/state/com.google/read"); } - - urls += "feed/" + f.getXmlUrl(); } - - m_api.editStream("subscribe", urls, null, cat, null); -} - -public override void removeFeed(string feedID) -{ - m_api.editStream("unsubscribe", {feedID}, null, null, null); -} - -public override void renameFeed(string feedID, string title) -{ - m_api.editStream("edit", {feedID}, title, null, null); -} - -public override void moveFeed(string feedID, string newCatID, string? currentCatID) -{ - m_api.editStream("edit", {feedID}, null, newCatID, currentCatID); -} - -public override string createCategory(string title, string? parentID) -{ - return m_api.composeTagID(title); -} - -public override void renameCategory(string catID, string title) -{ - m_api.renameTag(catID, title); -} - -public override void moveCategory(string catID, string newParentID) -{ - return; -} - -public override void deleteCategory(string catID) -{ - m_api.deleteTag(catID); -} - -public override void removeCatFromFeed(string feedID, string catID) -{ - return; -} - -public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) -{ - if(m_api.getSubscriptionList(feeds)) + + public override void setArticleIsMarked(string articleID, ArticleStatus marked) { - if(cancellable != null && cancellable.is_cancelled()) + if(marked == ArticleStatus.MARKED) + { + m_api.editTags(articleID, "user/-/state/com.google/starred", null); + } + else { + m_api.editTags(articleID, null, "user/-/state/com.google/starred"); + } + } + + public override bool alwaysSetReadByID() + { + return false; + } + + public override void setFeedRead(string feedID) + { + m_api.markAllAsRead(feedID); + } + + public override void setCategoryRead(string catID) + { + m_api.markAllAsRead(catID); + } + + public override void markAllItemsRead() + { + m_api.markAllAsRead("user/-/state/com.google/reading-list"); + } + + public override void tagArticle(string articleID, string tagID) + { + return; + } + + public override void removeArticleTag(string articleID, string tagID) + { + return; + } + + public override string createTag(string caption) + { + return ""; + } + + public override void deleteTag(string tagID) + { + + } + + public override void renameTag(string tagID, string title) + { + + } + + public override bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) + { + string? cat = null; + if(catID != null) + { + cat = catID; + } + else if(newCatName != null) + { + cat = newCatName; + } + + cat = m_api.composeTagID(cat); + + var response = m_api.editStream("subscribe", {"feed/" + feedURL}, null, cat, null); + if(response.status != 200) + { + feedID = ""; + errmsg = response.data; return false; } - - if(m_api.getTagList(categories)) + + errmsg = ""; + feedID = response.data; + return true; + } + + public override void addFeeds(Gee.List<Feed> feeds) + { + string cat = ""; + string[] urls = {}; + + foreach(Feed f in feeds) { - return true; + if(f.getCatIDs()[0] != cat) + { + m_api.editStream("subscribe", urls, null, cat, null); + urls = {}; + cat = f.getCatIDs()[0]; + } + + urls += "feed/" + f.getXmlUrl(); } + + m_api.editStream("subscribe", urls, null, cat, null); } - - return false; -} - -public override int getUnreadCount() -{ - return m_api.getUnreadCounts(); -} - -public override void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) -{ - if(whatToGet == ArticleStatus.READ) + + public override void removeFeed(string feedID) + { + m_api.editStream("unsubscribe", {feedID}, null, null, null); + } + + public override void renameFeed(string feedID, string title) + { + m_api.editStream("edit", {feedID}, title, null, null); + } + + public override void moveFeed(string feedID, string newCatID, string? currentCatID) + { + m_api.editStream("edit", {feedID}, null, newCatID, currentCatID); + } + + public override string createCategory(string title, string? parentID) + { + return m_api.composeTagID(title); + } + + public override void renameCategory(string catID, string title) + { + m_api.renameTag(catID, title); + } + + public override void moveCategory(string catID, string newParentID) { return; } - - var articles = new Gee.LinkedList<Article>(); - string? continuation = null; - string? exclude = null; - string? labelID = null; - int left = count; - if(whatToGet == ArticleStatus.ALL) + + public override void deleteCategory(string catID) { - labelID = "user/-/state/com.google/reading-list"; + m_api.deleteTag(catID); } - else if(whatToGet == ArticleStatus.MARKED) + + public override void removeCatFromFeed(string feedID, string catID) { - labelID = "user/-/state/com.google/starred"; + return; } - else if(whatToGet == ArticleStatus.UNREAD) + + public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) { - labelID = "user/-/state/com.google/reading-list"; - exclude = "user/-/state/com.google/read"; + if(m_api.getSubscriptionList(feeds)) + { + if(cancellable != null && cancellable.is_cancelled()) + { + return false; + } + + if(m_api.getTagList(categories)) + { + return true; + } + } + + return false; } - - - while(left > 0) + + public override int getUnreadCount() + { + return m_api.getUnreadCounts(); + } + + public override void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) { - if(cancellable != null && cancellable.is_cancelled()) + if(whatToGet == ArticleStatus.READ) { return; } - - if(left > 1000) + + var articles = new Gee.LinkedList<Article>(); + string? continuation = null; + string? exclude = null; + string? labelID = null; + int left = count; + if(whatToGet == ArticleStatus.ALL) { - continuation = m_api.getStreamContents(articles, null, labelID, exclude, 1000, "d"); - left -= 1000; + labelID = "user/-/state/com.google/reading-list"; } - else + else if(whatToGet == ArticleStatus.MARKED) + { + labelID = "user/-/state/com.google/starred"; + } + else if(whatToGet == ArticleStatus.UNREAD) { - continuation = m_api.getStreamContents(articles, null, labelID, exclude, left, "d"); - left = 0; + labelID = "user/-/state/com.google/reading-list"; + exclude = "user/-/state/com.google/read"; } + + + while(left > 0) + { + if(cancellable != null && cancellable.is_cancelled()) + { + return; + } + + if(left > 1000) + { + continuation = m_api.getStreamContents(articles, null, labelID, exclude, 1000, "d"); + left -= 1000; + } + else + { + continuation = m_api.getStreamContents(articles, null, labelID, exclude, left, "d"); + left = 0; + } + } + writeArticles(articles); } - writeArticles(articles); -} - + } [ModuleInit] diff --git a/plugins/backend/fresh/freshUtils.vala b/plugins/backend/fresh/freshUtils.vala index caca081c..92aa2316 100644 --- a/plugins/backend/fresh/freshUtils.vala +++ b/plugins/backend/fresh/freshUtils.vala @@ -14,133 +14,133 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.freshUtils : GLib.Object { - -GLib.Settings m_settings; -Password m_password; -Password m_htaccess_password; - -public freshUtils(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) -{ - if(settings_backend != null) + + GLib.Settings m_settings; + Password m_password; + Password m_htaccess_password; + + public freshUtils(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) { - m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.fresh", settings_backend); - } - else - { - m_settings = new GLib.Settings("org.gnome.feedreader.fresh"); - } - - var pwSchema = new Secret.Schema ("org.gnome.feedreader.password", Secret.SchemaFlags.NONE, - "URL", Secret.SchemaAttributeType.STRING, - "Username", Secret.SchemaAttributeType.STRING); - m_password = new Password(secrets, pwSchema, "FeedReader: freshRSS login", () => { + if(settings_backend != null) + { + m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.fresh", settings_backend); + } + else + { + m_settings = new GLib.Settings("org.gnome.feedreader.fresh"); + } + + var pwSchema = new Secret.Schema ("org.gnome.feedreader.password", Secret.SchemaFlags.NONE, + "URL", Secret.SchemaAttributeType.STRING, + "Username", Secret.SchemaAttributeType.STRING); + m_password = new Password(secrets, pwSchema, "FeedReader: freshRSS login", () => { var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); attributes["URL"] = getURL(); attributes["Username"] = getUser(); return attributes; }); - - var htAccessSchema = new Secret.Schema ("org.gnome.feedreader.password", Secret.SchemaFlags.NONE, - "URL", Secret.SchemaAttributeType.STRING, - "Username", Secret.SchemaAttributeType.STRING, - "htaccess", Secret.SchemaAttributeType.BOOLEAN); - m_htaccess_password = new Password(secrets, htAccessSchema, "FeedReader: freshRSS htaccess Authentication", () => { + + var htAccessSchema = new Secret.Schema ("org.gnome.feedreader.password", Secret.SchemaFlags.NONE, + "URL", Secret.SchemaAttributeType.STRING, + "Username", Secret.SchemaAttributeType.STRING, + "htaccess", Secret.SchemaAttributeType.BOOLEAN); + m_htaccess_password = new Password(secrets, htAccessSchema, "FeedReader: freshRSS htaccess Authentication", () => { var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); attributes["URL"] = getURL(); attributes["Username"] = getHtaccessUser(); attributes["htaccess"] = "true"; return attributes; }); -} - -public string getURL() -{ - string tmp_url = Utils.gsettingReadString(m_settings, "url"); - if(tmp_url != "") + } + + public string getURL() { - if(!tmp_url.has_suffix("/")) + string tmp_url = Utils.gsettingReadString(m_settings, "url"); + if(tmp_url != "") { - tmp_url = tmp_url + "/"; - } - - if(!tmp_url.has_suffix("/api/greader.php/")) - { - tmp_url = tmp_url + "api/greader.php/"; - } - - if(!tmp_url.has_prefix("http://") && !tmp_url.has_prefix("https://")) - { - tmp_url = "https://" + tmp_url; + if(!tmp_url.has_suffix("/")) + { + tmp_url = tmp_url + "/"; + } + + if(!tmp_url.has_suffix("/api/greader.php/")) + { + tmp_url = tmp_url + "api/greader.php/"; + } + + if(!tmp_url.has_prefix("http://") && !tmp_url.has_prefix("https://")) + { + tmp_url = "https://" + tmp_url; + } } + + return tmp_url; + } + + public void setURL(string url) + { + Utils.gsettingWriteString(m_settings, "url", url); + } + + public string getUser() + { + return Utils.gsettingReadString(m_settings, "username"); + } + + public void setToken(string token) + { + Utils.gsettingWriteString(m_settings, "token", token); + } + + public string getToken() + { + return Utils.gsettingReadString(m_settings, "token"); + } + + public void setUser(string user) + { + Utils.gsettingWriteString(m_settings, "username", user); + } + + public string getHtaccessUser() + { + return Utils.gsettingReadString(m_settings, "htaccess-username"); + } + + public void setHtaccessUser(string ht_user) + { + Utils.gsettingWriteString(m_settings, "htaccess-username", ht_user); + } + + public string getUnmodifiedURL() + { + return Utils.gsettingReadString(m_settings, "url"); + } + + public string getPasswd() + { + return m_password.get_password(); + } + + public void setPassword(string passwd) + { + m_password.set_password(passwd); + } + + public void resetAccount() + { + Utils.resetSettings(m_settings); + m_password.delete_password(); + m_htaccess_password.delete_password(); + } + + public string getHtaccessPasswd() + { + return m_htaccess_password.get_password(); + } + + public void setHtAccessPassword(string passwd) + { + m_htaccess_password.set_password(passwd); } - - return tmp_url; -} - -public void setURL(string url) -{ - Utils.gsettingWriteString(m_settings, "url", url); -} - -public string getUser() -{ - return Utils.gsettingReadString(m_settings, "username"); -} - -public void setToken(string token) -{ - Utils.gsettingWriteString(m_settings, "token", token); -} - -public string getToken() -{ - return Utils.gsettingReadString(m_settings, "token"); -} - -public void setUser(string user) -{ - Utils.gsettingWriteString(m_settings, "username", user); -} - -public string getHtaccessUser() -{ - return Utils.gsettingReadString(m_settings, "htaccess-username"); -} - -public void setHtaccessUser(string ht_user) -{ - Utils.gsettingWriteString(m_settings, "htaccess-username", ht_user); -} - -public string getUnmodifiedURL() -{ - return Utils.gsettingReadString(m_settings, "url"); -} - -public string getPasswd() -{ - return m_password.get_password(); -} - -public void setPassword(string passwd) -{ - m_password.set_password(passwd); -} - -public void resetAccount() -{ - Utils.resetSettings(m_settings); - m_password.delete_password(); - m_htaccess_password.delete_password(); -} - -public string getHtaccessPasswd() -{ - return m_htaccess_password.get_password(); -} - -public void setHtAccessPassword(string passwd) -{ - m_htaccess_password.set_password(passwd); -} } diff --git a/plugins/backend/inoreader/InoReaderAPI.vala b/plugins/backend/inoreader/InoReaderAPI.vala index c40c2102..3c4eadf2 100644 --- a/plugins/backend/inoreader/InoReaderAPI.vala +++ b/plugins/backend/inoreader/InoReaderAPI.vala @@ -14,419 +14,419 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.InoReaderAPI : GLib.Object { - -public enum InoSubscriptionAction { - EDIT, - SUBSCRIBE, - UNSUBSCRIBE -} - -private InoReaderConnection m_connection; -private InoReaderUtils m_utils; -private string m_userID; - -public InoReaderAPI (InoReaderUtils utils) -{ - m_utils = utils; - m_connection = new InoReaderConnection(m_utils); -} - - -public LoginResponse login() -{ - if(m_utils.getAccessToken() == "") - { - m_connection.getToken(); - } - - if(getUserID()) - { - return LoginResponse.SUCCESS; - } - - return LoginResponse.UNKNOWN_ERROR; -} - -public bool ping() { - return Utils.ping("http://www.inoreader.com/"); -} - -private bool getUserID() -{ - var response = m_connection.send_request("user-info"); - - if(response.status != 200) - { - return false; - } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); - } - catch(Error e) - { - Logger.error("getUserID: Could not load message response"); - Logger.error(e.message); - return false; - } - var root = parser.get_root().get_object(); - - if(root.has_member("userId")) - { - m_userID = root.get_string_member("userId"); - m_utils.setUserID(m_userID); - Logger.info("Inoreader: userID = " + m_userID); - - if(root.has_member("userEmail")) + + public enum InoSubscriptionAction { + EDIT, + SUBSCRIBE, + UNSUBSCRIBE + } + + private InoReaderConnection m_connection; + private InoReaderUtils m_utils; + private string m_userID; + + public InoReaderAPI (InoReaderUtils utils) + { + m_utils = utils; + m_connection = new InoReaderConnection(m_utils); + } + + + public LoginResponse login() + { + if(m_utils.getAccessToken() == "") { - m_utils.setEmail(root.get_string_member("userEmail")); + m_connection.getToken(); } - - if(root.has_member("userName")) + + if(getUserID()) { - m_utils.setUser(root.get_string_member("userName")); + return LoginResponse.SUCCESS; } - - return true; - } - - return false; -} - -public bool getFeeds(Gee.List<Feed> feeds) -{ - var response = m_connection.send_request("subscription/list"); - - if(response.status != 200) - { - return false; + + return LoginResponse.UNKNOWN_ERROR; } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); + + public bool ping() { + return Utils.ping("http://www.inoreader.com/"); } - catch(Error e) + + private bool getUserID() { - Logger.error("getFeeds: Could not load message response"); - Logger.error(e.message); + var response = m_connection.send_request("user-info"); + + if(response.status != 200) + { + return false; + } + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("getUserID: Could not load message response"); + Logger.error(e.message); + return false; + } + var root = parser.get_root().get_object(); + + if(root.has_member("userId")) + { + m_userID = root.get_string_member("userId"); + m_utils.setUserID(m_userID); + Logger.info("Inoreader: userID = " + m_userID); + + if(root.has_member("userEmail")) + { + m_utils.setEmail(root.get_string_member("userEmail")); + } + + if(root.has_member("userName")) + { + m_utils.setUser(root.get_string_member("userName")); + } + + return true; + } + return false; } - var root = parser.get_root().get_object(); - var array = root.get_array_member("subscriptions"); - uint length = array.get_length(); - - for (uint i = 0; i < length; i++) + + public bool getFeeds(Gee.List<Feed> feeds) { - Json.Object object = array.get_object_element(i); - - string feedID = object.get_string_member("id"); - string url = object.has_member("htmlUrl") ? object.get_string_member("htmlUrl") : object.get_string_member("url"); - - uint catCount = object.get_array_member("categories").get_length(); - var categories = new Gee.ArrayList<string>(); - - for(uint j = 0; j < catCount; ++j) + var response = m_connection.send_request("subscription/list"); + + if(response.status != 200) { - categories.add(object.get_array_member("categories").get_object_element(j).get_string_member("id")); + return false; } - - feeds.add( - new Feed( - feedID, - object.get_string_member("title"), - url, - 0, - categories, - object.get_string_member("iconUrl"), - object.get_string_member("url") + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("getFeeds: Could not load message response"); + Logger.error(e.message); + return false; + } + var root = parser.get_root().get_object(); + var array = root.get_array_member("subscriptions"); + uint length = array.get_length(); + + for (uint i = 0; i < length; i++) + { + Json.Object object = array.get_object_element(i); + + string feedID = object.get_string_member("id"); + string url = object.has_member("htmlUrl") ? object.get_string_member("htmlUrl") : object.get_string_member("url"); + + uint catCount = object.get_array_member("categories").get_length(); + var categories = new Gee.ArrayList<string>(); + + for(uint j = 0; j < catCount; ++j) + { + categories.add(object.get_array_member("categories").get_object_element(j).get_string_member("id")); + } + + feeds.add( + new Feed( + feedID, + object.get_string_member("title"), + url, + 0, + categories, + object.get_string_member("iconUrl"), + object.get_string_member("url") ) ); + } + + return true; } - - return true; -} - -public bool getCategoriesAndTags(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags) -{ - var response = m_connection.send_request("tag/list"); - - if(response.status != 200) - { - return false; - } - - var parser = new Json.Parser(); - try{ - parser.load_from_data(response.data, -1); - } - catch (Error e) { - Logger.error("getCategoriesAndTags: Could not load message response"); - Logger.error(e.message); - return false; - } - var root = parser.get_root().get_object(); - var array = root.get_array_member("tags"); - uint length = array.get_length(); - int orderID = 0; - - var db = DataBase.readOnly(); - for (uint i = 0; i < length; i++) + + public bool getCategoriesAndTags(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags) { - Json.Object object = array.get_object_element(i); - string id = object.get_string_member("id"); - int start = id.last_index_of_char('/') + 1; - string title = id.substring(start); - - if(id.contains("/label/")) + var response = m_connection.send_request("tag/list"); + + if(response.status != 200) { - if(m_utils.tagIsCat(id, feeds)) + return false; + } + + var parser = new Json.Parser(); + try{ + parser.load_from_data(response.data, -1); + } + catch (Error e) { + Logger.error("getCategoriesAndTags: Could not load message response"); + Logger.error(e.message); + return false; + } + var root = parser.get_root().get_object(); + var array = root.get_array_member("tags"); + uint length = array.get_length(); + int orderID = 0; + + var db = DataBase.readOnly(); + for (uint i = 0; i < length; i++) + { + Json.Object object = array.get_object_element(i); + string id = object.get_string_member("id"); + int start = id.last_index_of_char('/') + 1; + string title = id.substring(start); + + if(id.contains("/label/")) { - categories.add( - new Category( - id, - title, - 0, - orderID, - CategoryID.MASTER.to_string(), - 1 + if(m_utils.tagIsCat(id, feeds)) + { + categories.add( + new Category( + id, + title, + 0, + orderID, + CategoryID.MASTER.to_string(), + 1 ) ); - } - else - { - tags.add( - new Tag( - id, - title, - db.getTagColor() + } + else + { + tags.add( + new Tag( + id, + title, + db.getTagColor() ) ); + } + + ++orderID; } - - ++orderID; } + return true; } - return true; -} - - -public int getTotalUnread() -{ - var response = m_connection.send_request("unread-count"); - - if(response.status != 200) - { - return 0; - } - - var parser = new Json.Parser(); - try{ - parser.load_from_data(response.data, -1); - } - catch (Error e) { - Logger.error("getTotalUnread: Could not load message response"); - Logger.error(e.message); - } - - var root = parser.get_root().get_object(); - var array = root.get_array_member("unreadcounts"); - uint length = array.get_length(); - int count = 0; - - for (uint i = 0; i < length; i++) + + + public int getTotalUnread() { - Json.Object object = array.get_object_element(i); - if(object.get_string_member("id").has_prefix("feed/")) + var response = m_connection.send_request("unread-count"); + + if(response.status != 200) { - count += (int)object.get_int_member("count"); + return 0; } - - } - - Logger.debug("getTotalUnread %i".printf(count)); - return count; -} - - -public string? updateArticles(Gee.List<string> ids, int count, string? continuation = null) -{ - var message_string = "n=" + count.to_string(); - message_string += "&xt=user/-/state/com.google/read"; - if(continuation != null) - { - message_string += "&c=" + continuation; - } - var response = m_connection.send_request("stream/items/ids", message_string); - - if(response.status != 200) - { - return null; - } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); - } - catch(Error e) - { - Logger.error("updateArticles: Could not load message response"); - Logger.error(e.message); - } - - var root = parser.get_root().get_object(); - if(!root.has_member("itemRefs")) - { - return null; - } - var array = root.get_array_member("itemRefs"); - uint length = array.get_length(); - - for (uint i = 0; i < length; i++) - { - Json.Object object = array.get_object_element(i); - ids.add(object.get_string_member("id")); + + var parser = new Json.Parser(); + try{ + parser.load_from_data(response.data, -1); + } + catch (Error e) { + Logger.error("getTotalUnread: Could not load message response"); + Logger.error(e.message); + } + + var root = parser.get_root().get_object(); + var array = root.get_array_member("unreadcounts"); + uint length = array.get_length(); + int count = 0; + + for (uint i = 0; i < length; i++) + { + Json.Object object = array.get_object_element(i); + if(object.get_string_member("id").has_prefix("feed/")) + { + count += (int)object.get_int_member("count"); + } + + } + + Logger.debug("getTotalUnread %i".printf(count)); + return count; } - - if(root.has_member("continuation") && root.get_string_member("continuation") != "") - { - return root.get_string_member("continuation"); - } - - return null; -} - -public string? getArticles(Gee.List<Article> articles, int count, ArticleStatus whatToGet = ArticleStatus.ALL, string? continuation = null, string? tagID = null, string? feed_id = null) -{ - var message_string = "n=" + count.to_string(); - - if(whatToGet == ArticleStatus.UNREAD) + + + public string? updateArticles(Gee.List<string> ids, int count, string? continuation = null) { + var message_string = "n=" + count.to_string(); message_string += "&xt=user/-/state/com.google/read"; - } - if(whatToGet == ArticleStatus.READ) - { - message_string += "&it=user/-/state/com.google/read"; - } - else if(whatToGet == ArticleStatus.MARKED) - { - message_string += "&it=user/-/state/com.google/starred"; - } - - if(continuation != null) - { - message_string += "&c=" + continuation; - } - - - string api_endpoint = "stream/contents"; - if(feed_id != null) - { - api_endpoint += "/" + GLib.Uri.escape_string(feed_id); - } - else if(tagID != null) - { - api_endpoint += "/" + GLib.Uri.escape_string(tagID); - } - var response = m_connection.send_request(api_endpoint, message_string); - - if(response.status != 200) - { + if(continuation != null) + { + message_string += "&c=" + continuation; + } + var response = m_connection.send_request("stream/items/ids", message_string); + + if(response.status != 200) + { + return null; + } + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("updateArticles: Could not load message response"); + Logger.error(e.message); + } + + var root = parser.get_root().get_object(); + if(!root.has_member("itemRefs")) + { + return null; + } + var array = root.get_array_member("itemRefs"); + uint length = array.get_length(); + + for (uint i = 0; i < length; i++) + { + Json.Object object = array.get_object_element(i); + ids.add(object.get_string_member("id")); + } + + if(root.has_member("continuation") && root.get_string_member("continuation") != "") + { + return root.get_string_member("continuation"); + } + return null; } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); - } - catch(Error e) - { - Logger.error("getArticles: Could not load message response"); - Logger.error(e.message); - } - - var root = parser.get_root().get_object(); - var array = root.get_array_member("items"); - uint length = array.get_length(); - - var db = DataBase.readOnly(); - for (uint i = 0; i < length; i++) + + public string? getArticles(Gee.List<Article> articles, int count, ArticleStatus whatToGet = ArticleStatus.ALL, string? continuation = null, string? tagID = null, string? feed_id = null) { - Json.Object object = array.get_object_element(i); - string id = object.get_string_member("id"); - id = id.substring(id.last_index_of_char('/')+1); - var tags = new Gee.ArrayList<string>(); - bool marked = false; - bool read = false; - var cats = object.get_array_member("categories"); - uint cat_length = cats.get_length(); - - for (uint j = 0; j < cat_length; j++) + var message_string = "n=" + count.to_string(); + + if(whatToGet == ArticleStatus.UNREAD) { - string cat = cats.get_string_element(j); - if(cat.has_suffix("com.google/starred")) - { - marked = true; - } - else if(cat.has_suffix("com.google/read")) - { - read = true; - } - else if(cat.contains("/label/") && db.getTagName(cat) != null) - { - tags.add(cat); - } + message_string += "&xt=user/-/state/com.google/read"; } - - var enclosures = new Gee.ArrayList<Enclosure>(); - if(object.has_member("enclosure")) + if(whatToGet == ArticleStatus.READ) { - var attachments = object.get_array_member("enclosure"); - - uint mediaCount = 0; - if(attachments != null) + message_string += "&it=user/-/state/com.google/read"; + } + else if(whatToGet == ArticleStatus.MARKED) + { + message_string += "&it=user/-/state/com.google/starred"; + } + + if(continuation != null) + { + message_string += "&c=" + continuation; + } + + + string api_endpoint = "stream/contents"; + if(feed_id != null) + { + api_endpoint += "/" + GLib.Uri.escape_string(feed_id); + } + else if(tagID != null) + { + api_endpoint += "/" + GLib.Uri.escape_string(tagID); + } + var response = m_connection.send_request(api_endpoint, message_string); + + if(response.status != 200) + { + return null; + } + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("getArticles: Could not load message response"); + Logger.error(e.message); + } + + var root = parser.get_root().get_object(); + var array = root.get_array_member("items"); + uint length = array.get_length(); + + var db = DataBase.readOnly(); + for (uint i = 0; i < length; i++) + { + Json.Object object = array.get_object_element(i); + string id = object.get_string_member("id"); + id = id.substring(id.last_index_of_char('/')+1); + var tags = new Gee.ArrayList<string>(); + bool marked = false; + bool read = false; + var cats = object.get_array_member("categories"); + uint cat_length = cats.get_length(); + + for (uint j = 0; j < cat_length; j++) { - mediaCount = attachments.get_length(); + string cat = cats.get_string_element(j); + if(cat.has_suffix("com.google/starred")) + { + marked = true; + } + else if(cat.has_suffix("com.google/read")) + { + read = true; + } + else if(cat.contains("/label/") && db.getTagName(cat) != null) + { + tags.add(cat); + } } - - for(int j = 0; j < mediaCount; ++j) + + var enclosures = new Gee.ArrayList<Enclosure>(); + if(object.has_member("enclosure")) { - var attachment = attachments.get_object_element(j); - enclosures.add( - new Enclosure(id, attachment.get_string_member("href"), - EnclosureType.from_string(attachment.get_string_member("type"))) + var attachments = object.get_array_member("enclosure"); + + uint mediaCount = 0; + if(attachments != null) + { + mediaCount = attachments.get_length(); + } + + for(int j = 0; j < mediaCount; ++j) + { + var attachment = attachments.get_object_element(j); + enclosures.add( + new Enclosure(id, attachment.get_string_member("href"), + EnclosureType.from_string(attachment.get_string_member("type"))) ); + } } - } - - articles.add(new Article( - id, - object.get_string_member("title"), - object.get_array_member("alternate").get_object_element(0).get_string_member("href"), - object.get_object_member("origin").get_string_member("streamId"), - read ? ArticleStatus.READ : ArticleStatus.UNREAD, - marked ? ArticleStatus.MARKED : ArticleStatus.UNMARKED, - object.get_object_member("summary").get_string_member("content"), - null, - object.get_string_member("author"), - new DateTime.from_unix_local(object.get_int_member("published")), - -1, - tags, - enclosures - ) - ); - } - + + articles.add(new Article( + id, + object.get_string_member("title"), + object.get_array_member("alternate").get_object_element(0).get_string_member("href"), + object.get_object_member("origin").get_string_member("streamId"), + read ? ArticleStatus.READ : ArticleStatus.UNREAD, + marked ? ArticleStatus.MARKED : ArticleStatus.UNMARKED, + object.get_object_member("summary").get_string_member("content"), + null, + object.get_string_member("author"), + new DateTime.from_unix_local(object.get_int_member("published")), + -1, + tags, + enclosures + ) + ); + } + if(root.has_member("continuation") && root.get_string_member("continuation") != "") { return root.get_string_member("continuation"); } - + return null; } @@ -442,9 +442,9 @@ public void edidTag(string articleIDs, string tagID, bool add = true) { message_string += "r="; } - + message_string += tagID; - + var id_array = articleIDs.split(","); foreach(string id in id_array) { @@ -482,40 +482,40 @@ public void renameTag(string tagID, string title) public bool editSubscription(InoSubscriptionAction action, string[] feedID, string? title, string? add, string? remove) { var message_string = "ac="; - + switch(action) { - case InoSubscriptionAction.EDIT: + case InoSubscriptionAction.EDIT: message_string += "edit"; break; - case InoSubscriptionAction.SUBSCRIBE: + case InoSubscriptionAction.SUBSCRIBE: message_string += "subscribe"; break; - case InoSubscriptionAction.UNSUBSCRIBE: + case InoSubscriptionAction.UNSUBSCRIBE: message_string += "unsubscribe"; break; } - + foreach(string s in feedID) { message_string += "&s=" + GLib.Uri.escape_string(s); } - + if(title != null) { message_string += "&t=" + title; } - + if(add != null) { message_string += "&a=" + add; } - + if(remove != null) { message_string += "&r=" + remove; } - - + + return m_connection.send_request("subscription/edit", message_string).status == 200; } } diff --git a/plugins/backend/inoreader/InoReaderConnection.vala b/plugins/backend/inoreader/InoReaderConnection.vala index b2c3f93f..ff321e93 100644 --- a/plugins/backend/inoreader/InoReaderConnection.vala +++ b/plugins/backend/inoreader/InoReaderConnection.vala @@ -14,160 +14,160 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.InoReaderConnection { -private string m_api_username; -private string m_api_code; -private InoReaderUtils m_utils; -private Soup.Session m_session; - -public InoReaderConnection(InoReaderUtils utils) -{ - m_utils = utils; - m_api_username = m_utils.getUser(); - m_api_code = m_utils.getAccessToken(); - m_session = new Soup.Session(); - m_session.user_agent = Constants.USER_AGENT; -} - -public LoginResponse getToken() -{ - Logger.debug("InoReaderConnection: getToken()"); - - var message = new Soup.Message("POST", "https://www.inoreader.com/oauth2/token"); - string message_string = "code=" + m_utils.getApiCode() - + "&redirect_uri=" + InoReaderSecret.apiRedirectUri - + "&client_id=" + InoReaderSecret.apiClientId - + "&client_secret=" + InoReaderSecret.apiClientSecret - + "&scope=" - + "&grant_type=authorization_code"; - message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string.data); - m_session.send_message(message); - - if(message.status_code != 200) - { - return LoginResponse.NO_CONNECTION; - } - - string response = (string)message.response_body.flatten().data; - - try + private string m_api_username; + private string m_api_code; + private InoReaderUtils m_utils; + private Soup.Session m_session; + + public InoReaderConnection(InoReaderUtils utils) { - var parser = new Json.Parser(); - parser.load_from_data(response, -1); - var root = parser.get_root().get_object(); - - string accessToken = root.get_string_member("access_token"); - int64 expires = (int)root.get_int_member("expires_in"); - string refreshToken = root.get_string_member("refresh_token"); - int64 now = (new DateTime.now_local()).to_unix(); - - Logger.debug("access-token: " + accessToken); - Logger.debug("expires in: " + expires.to_string()); - Logger.debug("refresh-token: " + refreshToken); - Logger.debug("now: " + now.to_string()); - - m_utils.setAccessToken(accessToken); - m_utils.setExpiration((int)(now + expires)); - m_utils.setRefreshToken(refreshToken); + m_utils = utils; + m_api_username = m_utils.getUser(); + m_api_code = m_utils.getAccessToken(); + m_session = new Soup.Session(); + m_session.user_agent = Constants.USER_AGENT; } - catch(Error e) + + public LoginResponse getToken() { - Logger.error("InoReaderConnection - getToken: Could not load message response"); - Logger.error(e.message); - return LoginResponse.UNKNOWN_ERROR; - } - - return LoginResponse.SUCCESS; -} - -public LoginResponse refreshToken() -{ - Logger.debug("InoReaderConnection: refreshToken()"); - - var message = new Soup.Message("POST", "https://www.inoreader.com/oauth2/token"); - string message_string = "client_id=" + InoReaderSecret.apiClientId - + "&client_secret=" + InoReaderSecret.apiClientSecret - + "&grant_type=refresh_token" - + "&refresh_token=" + m_utils.getRefreshToken(); - - message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string.data); - m_session.send_message(message); - - if(message.status_code != 200) - { - return LoginResponse.NO_CONNECTION; - } - - string response = (string)message.response_body.flatten().data; - - try - { - var parser = new Json.Parser(); - parser.load_from_data(response, -1); - var root = parser.get_root().get_object(); - - if(!root.has_member("access_token")) + Logger.debug("InoReaderConnection: getToken()"); + + var message = new Soup.Message("POST", "https://www.inoreader.com/oauth2/token"); + string message_string = "code=" + m_utils.getApiCode() + + "&redirect_uri=" + InoReaderSecret.apiRedirectUri + + "&client_id=" + InoReaderSecret.apiClientId + + "&client_secret=" + InoReaderSecret.apiClientSecret + + "&scope=" + + "&grant_type=authorization_code"; + message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string.data); + m_session.send_message(message); + + if(message.status_code != 200) { - return getToken(); + return LoginResponse.NO_CONNECTION; } - - string accessToken = root.get_string_member("access_token"); - int64 expires = (int)root.get_int_member("expires_in"); - string refreshToken = root.get_string_member("refresh_token"); - int64 now = (new DateTime.now_local()).to_unix(); - - Logger.debug("access-token: " + accessToken); - Logger.debug("expires in: " + expires.to_string()); - Logger.debug("refresh-token: " + refreshToken); - Logger.debug("now: " + now.to_string()); - - m_utils.setAccessToken(accessToken); - m_utils.setExpiration((int)(now + expires)); - m_utils.setRefreshToken(refreshToken); - } - catch(Error e) - { - Logger.error("InoReaderConnection - getToken: Could not load message response"); - Logger.error(e.message); - return LoginResponse.UNKNOWN_ERROR; + + string response = (string)message.response_body.flatten().data; + + try + { + var parser = new Json.Parser(); + parser.load_from_data(response, -1); + var root = parser.get_root().get_object(); + + string accessToken = root.get_string_member("access_token"); + int64 expires = (int)root.get_int_member("expires_in"); + string refreshToken = root.get_string_member("refresh_token"); + int64 now = (new DateTime.now_local()).to_unix(); + + Logger.debug("access-token: " + accessToken); + Logger.debug("expires in: " + expires.to_string()); + Logger.debug("refresh-token: " + refreshToken); + Logger.debug("now: " + now.to_string()); + + m_utils.setAccessToken(accessToken); + m_utils.setExpiration((int)(now + expires)); + m_utils.setRefreshToken(refreshToken); + } + catch(Error e) + { + Logger.error("InoReaderConnection - getToken: Could not load message response"); + Logger.error(e.message); + return LoginResponse.UNKNOWN_ERROR; + } + + return LoginResponse.SUCCESS; } - - return LoginResponse.SUCCESS; -} - -public Response send_request(string path, string? message_string = null) -{ - return send_post_request(path, "POST", message_string); -} - -private Response send_post_request(string path, string type, string? message_string = null) -{ - if(!m_utils.accessTokenValid()) + + public LoginResponse refreshToken() { - refreshToken(); + Logger.debug("InoReaderConnection: refreshToken()"); + + var message = new Soup.Message("POST", "https://www.inoreader.com/oauth2/token"); + string message_string = "client_id=" + InoReaderSecret.apiClientId + + "&client_secret=" + InoReaderSecret.apiClientSecret + + "&grant_type=refresh_token" + + "&refresh_token=" + m_utils.getRefreshToken(); + + message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string.data); + m_session.send_message(message); + + if(message.status_code != 200) + { + return LoginResponse.NO_CONNECTION; + } + + string response = (string)message.response_body.flatten().data; + + try + { + var parser = new Json.Parser(); + parser.load_from_data(response, -1); + var root = parser.get_root().get_object(); + + if(!root.has_member("access_token")) + { + return getToken(); + } + + string accessToken = root.get_string_member("access_token"); + int64 expires = (int)root.get_int_member("expires_in"); + string refreshToken = root.get_string_member("refresh_token"); + int64 now = (new DateTime.now_local()).to_unix(); + + Logger.debug("access-token: " + accessToken); + Logger.debug("expires in: " + expires.to_string()); + Logger.debug("refresh-token: " + refreshToken); + Logger.debug("now: " + now.to_string()); + + m_utils.setAccessToken(accessToken); + m_utils.setExpiration((int)(now + expires)); + m_utils.setRefreshToken(refreshToken); + } + catch(Error e) + { + Logger.error("InoReaderConnection - getToken: Could not load message response"); + Logger.error(e.message); + return LoginResponse.UNKNOWN_ERROR; + } + + return LoginResponse.SUCCESS; } - - var message = new Soup.Message(type, InoReaderSecret.base_uri + path); - - string inoauth = "Bearer " + m_utils.getAccessToken(); - message.request_headers.append("Authorization", inoauth); - - if(message_string != null) + + public Response send_request(string path, string? message_string = null) { - message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string.data); + return send_post_request(path, "POST", message_string); } - - m_session.send_message(message); - - if(message.status_code != 200) + + private Response send_post_request(string path, string type, string? message_string = null) { - Logger.warning("InoReaderConnection: unexpected response"); - Logger.debug(message.status_code.to_string()); + if(!m_utils.accessTokenValid()) + { + refreshToken(); + } + + var message = new Soup.Message(type, InoReaderSecret.base_uri + path); + + string inoauth = "Bearer " + m_utils.getAccessToken(); + message.request_headers.append("Authorization", inoauth); + + if(message_string != null) + { + message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string.data); + } + + m_session.send_message(message); + + if(message.status_code != 200) + { + Logger.warning("InoReaderConnection: unexpected response"); + Logger.debug(message.status_code.to_string()); + } + + return Response() { + status = message.status_code, + data = (string)message.response_body.flatten().data + }; } - - return Response() { - status = message.status_code, - data = (string)message.response_body.flatten().data - }; -} - + } diff --git a/plugins/backend/inoreader/InoReaderInterface.vala b/plugins/backend/inoreader/InoReaderInterface.vala index 60b383e5..5dfa2c18 100644 --- a/plugins/backend/inoreader/InoReaderInterface.vala +++ b/plugins/backend/inoreader/InoReaderInterface.vala @@ -14,420 +14,420 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.InoReaderInterface : FeedServerInterface { - -private InoReaderAPI m_api; -private InoReaderUtils m_utils; - -public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) -{ - m_utils = new InoReaderUtils(settings_backend); - m_api = new InoReaderAPI(m_utils); -} - -public override string getWebsite() -{ - return "http://www.inoreader.com/"; -} - -public override BackendFlags getFlags() -{ - return (BackendFlags.HOSTED | BackendFlags.PROPRIETARY | BackendFlags.PAID_PREMIUM); -} - -public override string getID() -{ - return "inoreader"; -} - -public override string iconName() -{ - return "feed-service-inoreader"; -} - -public override string serviceName() -{ - return "InoReader"; -} - -public override bool extractCode(string redirectURL) -{ - if(redirectURL.has_prefix(InoReaderSecret.apiRedirectUri)) + + private InoReaderAPI m_api; + private InoReaderUtils m_utils; + + public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) { - Logger.debug(redirectURL); - int csrf_start = redirectURL.index_of("state=")+6; - string csrf_code = redirectURL.substring(csrf_start); - Logger.debug("InoReaderLoginWidget: csrf_code: " + csrf_code); - - if(csrf_code == InoReaderSecret.csrf_protection) + m_utils = new InoReaderUtils(settings_backend); + m_api = new InoReaderAPI(m_utils); + } + + public override string getWebsite() + { + return "http://www.inoreader.com/"; + } + + public override BackendFlags getFlags() + { + return (BackendFlags.HOSTED | BackendFlags.PROPRIETARY | BackendFlags.PAID_PREMIUM); + } + + public override string getID() + { + return "inoreader"; + } + + public override string iconName() + { + return "feed-service-inoreader"; + } + + public override string serviceName() + { + return "InoReader"; + } + + public override bool extractCode(string redirectURL) + { + if(redirectURL.has_prefix(InoReaderSecret.apiRedirectUri)) + { + Logger.debug(redirectURL); + int csrf_start = redirectURL.index_of("state=")+6; + string csrf_code = redirectURL.substring(csrf_start); + Logger.debug("InoReaderLoginWidget: csrf_code: " + csrf_code); + + if(csrf_code == InoReaderSecret.csrf_protection) + { + int start = redirectURL.index_of("code=")+5; + int end = redirectURL.index_of("&", start); + string code = redirectURL.substring(start, end-start); + m_utils.setApiCode(code); + Logger.debug("InoReaderLoginWidget: set inoreader-api-code: " + code); + GLib.Thread.usleep(500000); + return true; + } + + Logger.error("InoReaderLoginWidget: csrf_code mismatch"); + } + else { - int start = redirectURL.index_of("code=")+5; - int end = redirectURL.index_of("&", start); - string code = redirectURL.substring(start, end-start); - m_utils.setApiCode(code); - Logger.debug("InoReaderLoginWidget: set inoreader-api-code: " + code); - GLib.Thread.usleep(500000); - return true; + Logger.warning("InoReaderLoginWidget: wrong redirect_uri"); } - - Logger.error("InoReaderLoginWidget: csrf_code mismatch"); + + return false; } - else + + public override string buildLoginURL() { - Logger.warning("InoReaderLoginWidget: wrong redirect_uri"); + return "https://www.inoreader.com/oauth2/auth" + + "?client_id=" + InoReaderSecret.apiClientId + + "&redirect_uri=" + InoReaderSecret.apiRedirectUri + + "&response_type=code" + + "&scope=read+write" + + "&state=" + InoReaderSecret.csrf_protection; } - - return false; -} - -public override string buildLoginURL() -{ - return "https://www.inoreader.com/oauth2/auth" - + "?client_id=" + InoReaderSecret.apiClientId - + "&redirect_uri=" + InoReaderSecret.apiRedirectUri - + "&response_type=code" - + "&scope=read+write" - + "&state=" + InoReaderSecret.csrf_protection; -} - -public override bool needWebLogin() -{ - return true; -} - -public override bool supportTags() -{ - return true; -} - -public override bool doInitSync() -{ - return true; -} - -public override string symbolicIcon() -{ - return "feed-service-inoreader-symbolic"; -} - -public override string accountName() -{ - return m_utils.getUser(); -} - -public override string getServerURL() -{ - return "http://www.inoreader.com/"; -} - -public override string uncategorizedID() -{ - return ""; -} - -public override bool hideCategoryWhenEmpty(string cadID) -{ - return false; -} - -public override bool supportCategories() -{ - return true; -} - -public override bool supportFeedManipulation() -{ - return true; -} - -public override bool supportMultiLevelCategories() -{ - return false; -} - -public override bool supportMultiCategoriesPerFeed() -{ - return true; -} - -public override bool syncFeedsAndCategories() -{ - return true; -} - -public override bool tagIDaffectedByNameChange() -{ - return true; -} - -public override void resetAccount() -{ - m_utils.resetAccount(); -} - -public override bool useMaxArticles() -{ - return true; -} - -public override LoginResponse login() -{ - return m_api.login(); -} - -public override void setArticleIsRead(string articleIDs, ArticleStatus read) -{ - if(read == ArticleStatus.READ) + + public override bool needWebLogin() { - m_api.edidTag(articleIDs, "user/-/state/com.google/read"); + return true; } - else + + public override bool supportTags() { - m_api.edidTag(articleIDs, "user/-/state/com.google/read", false); + return true; } -} - -public override void setArticleIsMarked(string articleID, ArticleStatus marked) -{ - if(marked == ArticleStatus.MARKED) + + public override bool doInitSync() { - m_api.edidTag(articleID, "user/-/state/com.google/starred"); + return true; } - else + + public override string symbolicIcon() { - m_api.edidTag(articleID, "user/-/state/com.google/starred", false); + return "feed-service-inoreader-symbolic"; } -} - -public override bool alwaysSetReadByID() -{ - return false; -} - -public override void setFeedRead(string feedID) -{ - m_api.markAsRead(feedID); -} - -public override void setCategoryRead(string catID) -{ - m_api.markAsRead(catID); -} - -public override void markAllItemsRead() -{ - var db = DataBase.readOnly(); - var categories = db.read_categories(); - foreach(Category cat in categories) + + public override string accountName() { - m_api.markAsRead(cat.getCatID()); + return m_utils.getUser(); } - - var feeds = db.read_feeds_without_cat(); - foreach(Feed feed in feeds) + + public override string getServerURL() { - m_api.markAsRead(feed.getFeedID()); + return "http://www.inoreader.com/"; } - m_api.markAsRead(); -} - -public override void tagArticle(string articleID, string tagID) -{ - m_api.edidTag(articleID, tagID, true); -} - -public override void removeArticleTag(string articleID, string tagID) -{ - m_api.edidTag(articleID, tagID, false); -} - -public override string createTag(string caption) -{ - return m_api.composeTagID(caption); -} - -public override void deleteTag(string tagID) -{ - m_api.deleteTag(tagID); -} - -public override void renameTag(string tagID, string title) -{ - m_api.renameTag(tagID, title); -} - -public override bool serverAvailable() -{ - return m_api.ping(); -} - -public override bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) -{ - bool success = false; - feedID = "feed/" + feedURL; - errmsg = ""; - - if(catID == null && newCatName != null) + + public override string uncategorizedID() { - string newCatID = m_api.composeTagID(newCatName); - success = m_api.editSubscription(InoReaderAPI.InoSubscriptionAction.SUBSCRIBE, {"feed/"+feedURL}, null, newCatID, null); + return ""; } - else + + public override bool hideCategoryWhenEmpty(string cadID) { - success = m_api.editSubscription(InoReaderAPI.InoSubscriptionAction.SUBSCRIBE, {"feed/"+feedURL}, null, catID, null); + return false; } - - if(!success) + + public override bool supportCategories() { - errmsg = "Inoreader could not add %s"; + return true; } - - return success; -} - -public override void addFeeds(Gee.List<Feed> feeds) -{ - string cat = ""; - string[] urls = {}; - - foreach(Feed f in feeds) + + public override bool supportFeedManipulation() + { + return true; + } + + public override bool supportMultiLevelCategories() { - if(f.getCatIDs()[0] != cat) + return false; + } + + public override bool supportMultiCategoriesPerFeed() + { + return true; + } + + public override bool syncFeedsAndCategories() + { + return true; + } + + public override bool tagIDaffectedByNameChange() + { + return true; + } + + public override void resetAccount() + { + m_utils.resetAccount(); + } + + public override bool useMaxArticles() + { + return true; + } + + public override LoginResponse login() + { + return m_api.login(); + } + + public override void setArticleIsRead(string articleIDs, ArticleStatus read) + { + if(read == ArticleStatus.READ) { - m_api.editSubscription(InoReaderAPI.InoSubscriptionAction.SUBSCRIBE, urls, null, cat, null); - urls = {}; - cat = f.getCatIDs()[0]; + m_api.edidTag(articleIDs, "user/-/state/com.google/read"); + } + else + { + m_api.edidTag(articleIDs, "user/-/state/com.google/read", false); } - - urls += "feed/" + f.getXmlUrl(); } - - m_api.editSubscription(InoReaderAPI.InoSubscriptionAction.SUBSCRIBE, urls, null, cat, null); -} - -public override void removeFeed(string feedID) -{ - m_api.editSubscription(InoReaderAPI.InoSubscriptionAction.UNSUBSCRIBE, {feedID}, null, null, null); -} - -public override void renameFeed(string feedID, string title) -{ - m_api.editSubscription(InoReaderAPI.InoSubscriptionAction.EDIT, {feedID}, title, null, null); -} - -public override void moveFeed(string feedID, string newCatID, string? currentCatID) -{ - m_api.editSubscription(InoReaderAPI.InoSubscriptionAction.EDIT, {feedID}, null, newCatID, currentCatID); -} - -public override string createCategory(string title, string? parentID) -{ - return m_api.composeTagID(title); -} - -public override void renameCategory(string catID, string title) -{ - m_api.renameTag(catID, title); -} - -public override void moveCategory(string catID, string newParentID) -{ - return; -} - -public override void deleteCategory(string catID) -{ - m_api.deleteTag(catID); -} - -public override void removeCatFromFeed(string feedID, string catID) -{ - return; -} - -public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) -{ - if(m_api.getFeeds(feeds)) + + public override void setArticleIsMarked(string articleID, ArticleStatus marked) { - if(cancellable != null && cancellable.is_cancelled()) + if(marked == ArticleStatus.MARKED) { - return false; + m_api.edidTag(articleID, "user/-/state/com.google/starred"); } - - if(m_api.getCategoriesAndTags(feeds, categories, tags)) + else { - return true; + m_api.edidTag(articleID, "user/-/state/com.google/starred", false); } } - - return false; -} - -public override int getUnreadCount() -{ - return m_api.getTotalUnread(); -} - -public override void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) -{ - if(whatToGet == ArticleStatus.READ) + + public override bool alwaysSetReadByID() { - return; + return false; } - else if(whatToGet == ArticleStatus.ALL) + + public override void setFeedRead(string feedID) { - var unreadIDs = new Gee.LinkedList<string>(); - string? continuation = null; - int left = 4*count; - - while(left > 0) + m_api.markAsRead(feedID); + } + + public override void setCategoryRead(string catID) + { + m_api.markAsRead(catID); + } + + public override void markAllItemsRead() + { + var db = DataBase.readOnly(); + var categories = db.read_categories(); + foreach(Category cat in categories) { - if(cancellable != null && cancellable.is_cancelled()) + m_api.markAsRead(cat.getCatID()); + } + + var feeds = db.read_feeds_without_cat(); + foreach(Feed feed in feeds) + { + m_api.markAsRead(feed.getFeedID()); + } + m_api.markAsRead(); + } + + public override void tagArticle(string articleID, string tagID) + { + m_api.edidTag(articleID, tagID, true); + } + + public override void removeArticleTag(string articleID, string tagID) + { + m_api.edidTag(articleID, tagID, false); + } + + public override string createTag(string caption) + { + return m_api.composeTagID(caption); + } + + public override void deleteTag(string tagID) + { + m_api.deleteTag(tagID); + } + + public override void renameTag(string tagID, string title) + { + m_api.renameTag(tagID, title); + } + + public override bool serverAvailable() + { + return m_api.ping(); + } + + public override bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) + { + bool success = false; + feedID = "feed/" + feedURL; + errmsg = ""; + + if(catID == null && newCatName != null) + { + string newCatID = m_api.composeTagID(newCatName); + success = m_api.editSubscription(InoReaderAPI.InoSubscriptionAction.SUBSCRIBE, {"feed/"+feedURL}, null, newCatID, null); + } + else + { + success = m_api.editSubscription(InoReaderAPI.InoSubscriptionAction.SUBSCRIBE, {"feed/"+feedURL}, null, catID, null); + } + + if(!success) + { + errmsg = "Inoreader could not add %s"; + } + + return success; + } + + public override void addFeeds(Gee.List<Feed> feeds) + { + string cat = ""; + string[] urls = {}; + + foreach(Feed f in feeds) + { + if(f.getCatIDs()[0] != cat) { - return; + m_api.editSubscription(InoReaderAPI.InoSubscriptionAction.SUBSCRIBE, urls, null, cat, null); + urls = {}; + cat = f.getCatIDs()[0]; } - - if(left > 1000) + + urls += "feed/" + f.getXmlUrl(); + } + + m_api.editSubscription(InoReaderAPI.InoSubscriptionAction.SUBSCRIBE, urls, null, cat, null); + } + + public override void removeFeed(string feedID) + { + m_api.editSubscription(InoReaderAPI.InoSubscriptionAction.UNSUBSCRIBE, {feedID}, null, null, null); + } + + public override void renameFeed(string feedID, string title) + { + m_api.editSubscription(InoReaderAPI.InoSubscriptionAction.EDIT, {feedID}, title, null, null); + } + + public override void moveFeed(string feedID, string newCatID, string? currentCatID) + { + m_api.editSubscription(InoReaderAPI.InoSubscriptionAction.EDIT, {feedID}, null, newCatID, currentCatID); + } + + public override string createCategory(string title, string? parentID) + { + return m_api.composeTagID(title); + } + + public override void renameCategory(string catID, string title) + { + m_api.renameTag(catID, title); + } + + public override void moveCategory(string catID, string newParentID) + { + return; + } + + public override void deleteCategory(string catID) + { + m_api.deleteTag(catID); + } + + public override void removeCatFromFeed(string feedID, string catID) + { + return; + } + + public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) + { + if(m_api.getFeeds(feeds)) + { + if(cancellable != null && cancellable.is_cancelled()) { - continuation = m_api.updateArticles(unreadIDs, 1000, continuation); - left -= 1000; + return false; } - else + + if(m_api.getCategoriesAndTags(feeds, categories, tags)) { - m_api.updateArticles(unreadIDs, left, continuation); - left = 0; + return true; } } - DataBase.writeAccess().updateArticlesByID(unreadIDs, "unread"); - updateArticleList(); + + return false; } - - var articles = new Gee.LinkedList<Article>(); - string? continuation = null; - int left = count; - string? inoreader_feedID = (isTagID) ? null : feedID; - string? inoreader_tagID = (isTagID) ? feedID : null; - - while(left > 0) + + public override int getUnreadCount() { - if(cancellable != null && cancellable.is_cancelled()) + return m_api.getTotalUnread(); + } + + public override void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) + { + if(whatToGet == ArticleStatus.READ) { return; } - - if(left > 1000) + else if(whatToGet == ArticleStatus.ALL) { - continuation = m_api.getArticles(articles, 1000, whatToGet, continuation, inoreader_tagID, inoreader_feedID); - left -= 1000; + var unreadIDs = new Gee.LinkedList<string>(); + string? continuation = null; + int left = 4*count; + + while(left > 0) + { + if(cancellable != null && cancellable.is_cancelled()) + { + return; + } + + if(left > 1000) + { + continuation = m_api.updateArticles(unreadIDs, 1000, continuation); + left -= 1000; + } + else + { + m_api.updateArticles(unreadIDs, left, continuation); + left = 0; + } + } + DataBase.writeAccess().updateArticlesByID(unreadIDs, "unread"); + updateArticleList(); } - else + + var articles = new Gee.LinkedList<Article>(); + string? continuation = null; + int left = count; + string? inoreader_feedID = (isTagID) ? null : feedID; + string? inoreader_tagID = (isTagID) ? feedID : null; + + while(left > 0) { - continuation = m_api.getArticles(articles, left, whatToGet, continuation, inoreader_tagID, inoreader_feedID); - left = 0; + if(cancellable != null && cancellable.is_cancelled()) + { + return; + } + + if(left > 1000) + { + continuation = m_api.getArticles(articles, 1000, whatToGet, continuation, inoreader_tagID, inoreader_feedID); + left -= 1000; + } + else + { + continuation = m_api.getArticles(articles, left, whatToGet, continuation, inoreader_tagID, inoreader_feedID); + left = 0; + } } + writeArticles(articles); } - writeArticles(articles); -} - + } [ModuleInit] diff --git a/plugins/backend/inoreader/InoReaderUtils.vala b/plugins/backend/inoreader/InoReaderUtils.vala index 9ab65306..fa7c789d 100644 --- a/plugins/backend/inoreader/InoReaderUtils.vala +++ b/plugins/backend/inoreader/InoReaderUtils.vala @@ -14,126 +14,126 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. namespace FeedReader.InoReaderSecret { -const string base_uri = "https://www.inoreader.com/reader/api/0/"; -const string apiClientId = "1000001384"; -const string apiClientSecret = "3AA9IyNTFL_Mgu77WPpWbawx9loERRdf"; -const string apiRedirectUri = "http://localhost"; -const string csrf_protection = "123456"; + const string base_uri = "https://www.inoreader.com/reader/api/0/"; + const string apiClientId = "1000001384"; + const string apiClientSecret = "3AA9IyNTFL_Mgu77WPpWbawx9loERRdf"; + const string apiRedirectUri = "http://localhost"; + const string csrf_protection = "123456"; } public class FeedReader.InoReaderUtils : GLib.Object { - -private GLib.Settings m_settings; - -public InoReaderUtils(GLib.SettingsBackend? settings_backend) -{ - if(settings_backend != null) + + private GLib.Settings m_settings; + + public InoReaderUtils(GLib.SettingsBackend? settings_backend) { - m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.inoreader", settings_backend); + if(settings_backend != null) + { + m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.inoreader", settings_backend); + } + else + { + m_settings = new GLib.Settings("org.gnome.feedreader.inoreader"); + } } - else + + public string getUser() { - m_settings = new GLib.Settings("org.gnome.feedreader.inoreader"); + return Utils.gsettingReadString(m_settings, "username"); } -} - -public string getUser() -{ - return Utils.gsettingReadString(m_settings, "username"); -} - -public void setUser(string user) -{ - Utils.gsettingWriteString(m_settings, "username", user); -} - -public string getRefreshToken() -{ - return Utils.gsettingReadString(m_settings, "refresh-token"); -} - -public void setRefreshToken(string token) -{ - Utils.gsettingWriteString(m_settings, "refresh-token", token); -} - -public string getAccessToken() -{ - return Utils.gsettingReadString(m_settings, "access-token"); -} - -public void setAccessToken(string token) -{ - Utils.gsettingWriteString(m_settings, "access-token", token); -} - -public string getApiCode() -{ - return Utils.gsettingReadString(m_settings, "api-code"); -} - -public void setApiCode(string code) -{ - Utils.gsettingWriteString(m_settings, "api-code", code); -} - -public int getExpiration() -{ - return m_settings.get_int("access-token-expires"); -} - -public void setExpiration(int seconds) -{ - m_settings.set_int("access-token-expires", seconds); -} - -public string getUserID() -{ - return Utils.gsettingReadString(m_settings, "user-id"); -} - -public void setUserID(string id) -{ - Utils.gsettingWriteString(m_settings, "user-id", id); -} - -public string getEmail() -{ - return Utils.gsettingReadString(m_settings, "user-email"); -} - -public void setEmail(string email) -{ - Utils.gsettingWriteString(m_settings, "user-email", email); -} - -public void resetAccount() -{ - Utils.resetSettings(m_settings); -} - -public bool accessTokenValid() -{ - var now = new DateTime.now_local(); - - if((int)now.to_unix() > getExpiration()) + + public void setUser(string user) { - Logger.warning("InoReaderUtils: access token expired"); - return false; + Utils.gsettingWriteString(m_settings, "username", user); } - - return true; -} - -public bool tagIsCat(string tagID, Gee.List<Feed> feeds) -{ - foreach(Feed feed in feeds) + + public string getRefreshToken() + { + return Utils.gsettingReadString(m_settings, "refresh-token"); + } + + public void setRefreshToken(string token) + { + Utils.gsettingWriteString(m_settings, "refresh-token", token); + } + + public string getAccessToken() + { + return Utils.gsettingReadString(m_settings, "access-token"); + } + + public void setAccessToken(string token) + { + Utils.gsettingWriteString(m_settings, "access-token", token); + } + + public string getApiCode() + { + return Utils.gsettingReadString(m_settings, "api-code"); + } + + public void setApiCode(string code) + { + Utils.gsettingWriteString(m_settings, "api-code", code); + } + + public int getExpiration() + { + return m_settings.get_int("access-token-expires"); + } + + public void setExpiration(int seconds) { - if(feed.hasCat(tagID)) + m_settings.set_int("access-token-expires", seconds); + } + + public string getUserID() + { + return Utils.gsettingReadString(m_settings, "user-id"); + } + + public void setUserID(string id) + { + Utils.gsettingWriteString(m_settings, "user-id", id); + } + + public string getEmail() + { + return Utils.gsettingReadString(m_settings, "user-email"); + } + + public void setEmail(string email) + { + Utils.gsettingWriteString(m_settings, "user-email", email); + } + + public void resetAccount() + { + Utils.resetSettings(m_settings); + } + + public bool accessTokenValid() + { + var now = new DateTime.now_local(); + + if((int)now.to_unix() > getExpiration()) { - return true; + Logger.warning("InoReaderUtils: access token expired"); + return false; } + + return true; + } + + public bool tagIsCat(string tagID, Gee.List<Feed> feeds) + { + foreach(Feed feed in feeds) + { + if(feed.hasCat(tagID)) + { + return true; + } + } + return false; } - return false; -} } diff --git a/plugins/backend/local/SuggestedFeedRow.vala b/plugins/backend/local/SuggestedFeedRow.vala index 9a97e7fc..f1ac26b7 100644 --- a/plugins/backend/local/SuggestedFeedRow.vala +++ b/plugins/backend/local/SuggestedFeedRow.vala @@ -14,95 +14,95 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.SuggestedFeedRow : Gtk.ListBoxRow { - -private string m_name; -private string m_url; -private string m_category; -private string m_desc; -private Gtk.CheckButton m_check; - -public SuggestedFeedRow(string url, string iconURL, string category, string name, string desc, string lang) -{ - m_name = name; - m_url = url; - m_category = category; - m_desc = desc; - - var iconStack = new Gtk.Stack(); - iconStack.set_size_request(24, 24); - iconStack.set_transition_duration(100); - iconStack.set_transition_type(Gtk.StackTransitionType.CROSSFADE); - - var spinner = new Gtk.Spinner(); - iconStack.add_named(spinner, "spinner"); - spinner.start(); - - m_check = new Gtk.CheckButton(); - var label = new Gtk.Label(name); - label.get_style_context().add_class("h3"); - label.set_alignment(0.0f, 0.5f); - - var langLabel = new Gtk.Label(lang); - langLabel.opacity = 0.7; - langLabel.set_alignment(1.0f, 0.5f); - langLabel.get_style_context().add_class("preview"); - - var box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); - box.margin_top = 5; - box.margin_bottom = 5; - box.pack_start(m_check, false, false, 10); - box.pack_start(iconStack, false, false, 10); - box.pack_start(label, true, true, 10); - box.pack_end(langLabel, false, false, 10); - var box2 = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); - box2.pack_start(box); - box2.pack_start(new Gtk.Separator(Gtk.Orientation.HORIZONTAL)); - this.add(box2); - this.set_tooltip_text(m_desc); - show_all(); - - var uri = new Soup.URI(url); - var fakeFeed = new Feed(uri.get_host(), null, null, 0); - load_favicon.begin(iconStack, fakeFeed, iconURL, (obj, res) => { + + private string m_name; + private string m_url; + private string m_category; + private string m_desc; + private Gtk.CheckButton m_check; + + public SuggestedFeedRow(string url, string iconURL, string category, string name, string desc, string lang) + { + m_name = name; + m_url = url; + m_category = category; + m_desc = desc; + + var iconStack = new Gtk.Stack(); + iconStack.set_size_request(24, 24); + iconStack.set_transition_duration(100); + iconStack.set_transition_type(Gtk.StackTransitionType.CROSSFADE); + + var spinner = new Gtk.Spinner(); + iconStack.add_named(spinner, "spinner"); + spinner.start(); + + m_check = new Gtk.CheckButton(); + var label = new Gtk.Label(name); + label.get_style_context().add_class("h3"); + label.set_alignment(0.0f, 0.5f); + + var langLabel = new Gtk.Label(lang); + langLabel.opacity = 0.7; + langLabel.set_alignment(1.0f, 0.5f); + langLabel.get_style_context().add_class("preview"); + + var box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); + box.margin_top = 5; + box.margin_bottom = 5; + box.pack_start(m_check, false, false, 10); + box.pack_start(iconStack, false, false, 10); + box.pack_start(label, true, true, 10); + box.pack_end(langLabel, false, false, 10); + var box2 = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); + box2.pack_start(box); + box2.pack_start(new Gtk.Separator(Gtk.Orientation.HORIZONTAL)); + this.add(box2); + this.set_tooltip_text(m_desc); + show_all(); + + var uri = new Soup.URI(url); + var fakeFeed = new Feed(uri.get_host(), null, null, 0); + load_favicon.begin(iconStack, fakeFeed, iconURL, (obj, res) => { load_favicon.end(res); }); -} - -private async void load_favicon(Gtk.Stack iconStack, Feed feed, string iconURL) -{ - Gtk.Image? icon = null; - var surface = yield FavIcon.for_feed(feed).get_surface(); - if(surface != null) + } + + private async void load_favicon(Gtk.Stack iconStack, Feed feed, string iconURL) { - icon = new Gtk.Image.from_surface(surface); + Gtk.Image? icon = null; + var surface = yield FavIcon.for_feed(feed).get_surface(); + if(surface != null) + { + icon = new Gtk.Image.from_surface(surface); + } + else + { + icon = new Gtk.Image.from_icon_name("feed-rss-symbolic", Gtk.IconSize.LARGE_TOOLBAR); + } + + iconStack.add_named(icon, "icon"); + show_all(); + iconStack.set_visible_child_name("icon"); } - else + + public bool checked() { - icon = new Gtk.Image.from_icon_name("feed-rss-symbolic", Gtk.IconSize.LARGE_TOOLBAR); + return m_check.active; + } + + public string getName() + { + return m_name; + } + + public string getURL() + { + return m_url; + } + + public string getCategory() + { + return m_category; } - - iconStack.add_named(icon, "icon"); - show_all(); - iconStack.set_visible_child_name("icon"); -} - -public bool checked() -{ - return m_check.active; -} - -public string getName() -{ - return m_name; -} - -public string getURL() -{ - return m_url; -} - -public string getCategory() -{ - return m_category; -} } diff --git a/plugins/backend/local/TestLocalRSS.vala b/plugins/backend/local/TestLocalRSS.vala index 60a9d419..4b7dc673 100644 --- a/plugins/backend/local/TestLocalRSS.vala +++ b/plugins/backend/local/TestLocalRSS.vala @@ -3,107 +3,107 @@ using FeedReader; void main(string[] args) { Test.init(ref args); - + Test.add_data_func ("/Rfc822/parseDate/Basic", () => { var date = Rfc822.parseDate("Thu, 09 Feb 2006 23:59:45 +0000"); assert(new DateTime.utc(2006, 2, 9, 23, 59, 45).equal(date)); }); - + Test.add_data_func ("/Rfc822/parseDate/LowerCase", () => { var date = Rfc822.parseDate("thu, 09 feb 2006 16:59:45 mst"); assert(new DateTime.utc(2006, 2, 9, 23, 59, 45).equal(date)); }); - + Test.add_data_func ("/Rfc822/parseDate/UpperCase", () => { var date = Rfc822.parseDate("THU, 09 FEB 2006 16:59:45 MST"); assert(new DateTime.utc(2006, 2, 9, 23, 59, 45).equal(date)); }); - + Test.add_data_func ("/Rfc822/parseDate/LotsOfWhiteSpace", () => { var date = Rfc822.parseDate(" \t\n Thu, \t\n 09 \t\n Feb \t\n 2006 \t\n 23:59:45 \t\n +0000 \t\n"); assert(new DateTime.utc(2006, 2, 9, 23, 59, 45).equal(date)); }); - + Test.add_data_func ("/Rfc822/parseDate/TwoDigitYear2000", () => { var date = Rfc822.parseDate("Thu, 09 Feb 00 23:59:45 +0000"); assert(new DateTime.utc(2000, 2, 9, 23, 59, 45).equal(date)); }); - + Test.add_data_func ("/Rfc822/parseDate/TwoDigitYear2006", () => { var date = Rfc822.parseDate("Thu, 09 Feb 06 23:59:45 +0000"); assert(new DateTime.utc(2006, 2, 9, 23, 59, 45).equal(date)); }); - + Test.add_data_func ("/Rfc822/parseDate/TwoDigitYear2049", () => { var date = Rfc822.parseDate("Thu, 09 Feb 49 23:59:45 +0000"); assert(new DateTime.utc(2049, 2, 9, 23, 59, 45).equal(date)); }); - + Test.add_data_func ("/Rfc822/parseDate/TwoDigitYear1950", () => { var date = Rfc822.parseDate("Thu, 09 Feb 50 23:59:45 +0000"); assert(new DateTime.utc(1950, 2, 9, 23, 59, 45).equal(date)); }); - + Test.add_data_func ("/Rfc822/parseDate/TwoDigitYear1956", () => { var date = Rfc822.parseDate("Thu, 09 Feb 56 23:59:45 +0000"); assert(new DateTime.utc(1956, 2, 9, 23, 59, 45).equal(date)); }); - + Test.add_data_func ("/Rfc822/parseDate/TwoDigitYear1999", () => { var date = Rfc822.parseDate("Thu, 09 Feb 99 23:59:45 +0000"); assert(new DateTime.utc(1999, 2, 9, 23, 59, 45).equal(date)); }); - + // Just in case we get RSS feeds made by Romans Test.add_data_func ("/Rfc822/parseDate/ThreeDigitYear", () => { var date = Rfc822.parseDate("Thu, 09 Feb 156 23:59:45 +0000"); assert(new DateTime.utc(156, 2, 9, 23, 59, 45).equal(date)); }); - + Test.add_data_func ("/Rfc822/parseDate/OnlyRequired", () => { var date = Rfc822.parseDate("09 Feb 2006 23:59 +0000"); assert(new DateTime.utc(2006, 2, 9, 23, 59, 0).equal(date)); }); - + Test.add_data_func ("/Rfc822/parseDate/OneDigitDay", () => { var date = Rfc822.parseDate("9 Feb 2006 23:59 +0000"); assert(new DateTime.utc(2006, 2, 9, 23, 59, 0).equal(date)); }); - + Test.add_data_func ("/Rfc822/parseDate/UnsupportedZone", () => { var date = Rfc822.parseDate("09 Feb 2006 23:59 X"); assert(new DateTime.utc(2006, 2, 9, 23, 59, 0).equal(date)); }); - + Test.add_data_func ("/Rfc822/parseDate/MissingDay", () => { var date = Rfc822.parseDate("Feb 2006 23:59 +0000"); assert(date == null); }); - + Test.add_data_func ("/Rfc822/parseDate/MissingMonth", () => { var date = Rfc822.parseDate("09 2006 23:59 +0000"); assert(date == null); }); - + Test.add_data_func ("/Rfc822/parseDate/MissingYear", () => { var date = Rfc822.parseDate("09 Feb 23:59 +0000"); assert(date == null); }); - + Test.add_data_func ("/Rfc822/parseDate/MissingHour", () => { var date = Rfc822.parseDate("09 Feb 2006 59 +0000"); assert(date == null); }); - + Test.add_data_func ("/Rfc822/parseDate/MissingMinute", () => { var date = Rfc822.parseDate("09 Feb 2006 23 +0000"); assert(date == null); }); - + Test.add_data_func ("/Rfc822/parseDate/MissingZone", () => { var date = Rfc822.parseDate("09 Feb 2006 23:59"); assert(date == null); }); - + Test.run (); } diff --git a/plugins/backend/local/localInterface.vala b/plugins/backend/local/localInterface.vala index c9aabad0..6a2d46d1 100644 --- a/plugins/backend/local/localInterface.vala +++ b/plugins/backend/local/localInterface.vala @@ -14,597 +14,597 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.localInterface : FeedServerInterface { - -private localUtils m_utils; -private Soup.Session m_session; -private Gtk.ListBox m_feedlist; - -public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) -{ - m_utils = new localUtils(); - m_session = new Soup.Session(); - m_session.user_agent = Constants.USER_AGENT; - m_session.timeout = 5; -} - -public override string getWebsite() -{ - return "http://jangernert.github.io/FeedReader/"; -} - -public override BackendFlags getFlags() -{ - return (BackendFlags.LOCAL | BackendFlags.FREE_SOFTWARE | BackendFlags.FREE); -} - -public override string getID() -{ - return "local"; -} - -public override string iconName() -{ - return "feed-service-local"; -} - -public override string serviceName() -{ - return "Local RSS"; -} - -public override bool needWebLogin() -{ - return false; -} - -public override Gtk.Box? getWidget() -{ - var doneLabel = new Gtk.Label(_("Done")); - var waitingLabel = new Gtk.Label(_("Adding Feeds")); - var waitingSpinner = new Gtk.Spinner(); - var waitingBox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 5); - waitingBox.pack_start(waitingSpinner, false, false, 0); - waitingBox.pack_start(waitingLabel, true, false, 0); - var loginStack = new Gtk.Stack(); - loginStack.add_named(doneLabel, "label"); - loginStack.add_named(waitingBox, "waiting"); - var loginButton = new Gtk.Button(); - loginButton.add(loginStack); - loginButton.halign = Gtk.Align.END; - loginButton.set_size_request(80, 30); - loginButton.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); - loginButton.clicked.connect(() => { + + private localUtils m_utils; + private Soup.Session m_session; + private Gtk.ListBox m_feedlist; + + public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) + { + m_utils = new localUtils(); + m_session = new Soup.Session(); + m_session.user_agent = Constants.USER_AGENT; + m_session.timeout = 5; + } + + public override string getWebsite() + { + return "http://jangernert.github.io/FeedReader/"; + } + + public override BackendFlags getFlags() + { + return (BackendFlags.LOCAL | BackendFlags.FREE_SOFTWARE | BackendFlags.FREE); + } + + public override string getID() + { + return "local"; + } + + public override string iconName() + { + return "feed-service-local"; + } + + public override string serviceName() + { + return "Local RSS"; + } + + public override bool needWebLogin() + { + return false; + } + + public override Gtk.Box? getWidget() + { + var doneLabel = new Gtk.Label(_("Done")); + var waitingLabel = new Gtk.Label(_("Adding Feeds")); + var waitingSpinner = new Gtk.Spinner(); + var waitingBox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 5); + waitingBox.pack_start(waitingSpinner, false, false, 0); + waitingBox.pack_start(waitingLabel, true, false, 0); + var loginStack = new Gtk.Stack(); + loginStack.add_named(doneLabel, "label"); + loginStack.add_named(waitingBox, "waiting"); + var loginButton = new Gtk.Button(); + loginButton.add(loginStack); + loginButton.halign = Gtk.Align.END; + loginButton.set_size_request(80, 30); + loginButton.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); + loginButton.clicked.connect(() => { tryLogin(); loginButton.set_sensitive(false); waitingSpinner.start(); loginButton.get_style_context().remove_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); loginStack.set_visible_child_name("waiting"); }); - loginButton.show_all(); - - var headlineLabel = new Gtk.Label("Recommended Feeds:"); - headlineLabel.get_style_context().add_class("h1"); - headlineLabel.set_justify(Gtk.Justification.CENTER); - - var loginLabel = new Gtk.Label("Fill your library with feeds. Here are some recommendations."); - loginLabel.get_style_context().add_class("h2"); - loginLabel.set_justify(Gtk.Justification.CENTER); - loginLabel.set_lines(3); - - m_feedlist = new Gtk.ListBox(); - m_feedlist.set_selection_mode(Gtk.SelectionMode.NONE); - m_feedlist.set_sort_func(sortFunc); - m_feedlist.set_header_func(headerFunc); - - try - { - uint8[] contents; - var file = File.new_for_uri("resource:///org/gnome/FeedReader/recommendedFeeds.json"); - file.load_contents(null, out contents, null); - - var parser = new Json.Parser(); - parser.load_from_data((string)contents); - - Json.Array array = parser.get_root().get_array(); - - for (int i = 0; i < array.get_length (); i++) + loginButton.show_all(); + + var headlineLabel = new Gtk.Label("Recommended Feeds:"); + headlineLabel.get_style_context().add_class("h1"); + headlineLabel.set_justify(Gtk.Justification.CENTER); + + var loginLabel = new Gtk.Label("Fill your library with feeds. Here are some recommendations."); + loginLabel.get_style_context().add_class("h2"); + loginLabel.set_justify(Gtk.Justification.CENTER); + loginLabel.set_lines(3); + + m_feedlist = new Gtk.ListBox(); + m_feedlist.set_selection_mode(Gtk.SelectionMode.NONE); + m_feedlist.set_sort_func(sortFunc); + m_feedlist.set_header_func(headerFunc); + + try { - Json.Object object = array.get_object_element(i); - - m_feedlist.add( - new SuggestedFeedRow( - object.get_string_member("url"), - object.get_string_member("icon"), - object.get_string_member("category"), - object.get_string_member("name"), - object.get_string_member("description"), - object.get_string_member("language") + uint8[] contents; + var file = File.new_for_uri("resource:///org/gnome/FeedReader/recommendedFeeds.json"); + file.load_contents(null, out contents, null); + + var parser = new Json.Parser(); + parser.load_from_data((string)contents); + + Json.Array array = parser.get_root().get_array(); + + for (int i = 0; i < array.get_length (); i++) + { + Json.Object object = array.get_object_element(i); + + m_feedlist.add( + new SuggestedFeedRow( + object.get_string_member("url"), + object.get_string_member("icon"), + object.get_string_member("category"), + object.get_string_member("name"), + object.get_string_member("description"), + object.get_string_member("language") ) ); + } } + catch(GLib.Error e) + { + Logger.error("localLoginWidget: loading json filed"); + Logger.error(e.message); + } + + var scroll = new Gtk.ScrolledWindow(null, null); + scroll.set_size_request(450, 0); + scroll.set_halign(Gtk.Align.CENTER); + scroll.get_style_context().add_class(Gtk.STYLE_CLASS_FRAME); + scroll.add(m_feedlist); + + var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); + box.margin = 50; + box.valign = Gtk.Align.FILL; + box.halign = Gtk.Align.CENTER; + box.pack_start(headlineLabel, false, false, 0); + box.pack_start(loginLabel, false, false, 2); + box.pack_start(scroll, true, true, 20); + box.pack_end(loginButton, false, false, 0); + return box; } - catch(GLib.Error e) + + public override async void postLoginAction() { - Logger.error("localLoginWidget: loading json filed"); - Logger.error(e.message); - } - - var scroll = new Gtk.ScrolledWindow(null, null); - scroll.set_size_request(450, 0); - scroll.set_halign(Gtk.Align.CENTER); - scroll.get_style_context().add_class(Gtk.STYLE_CLASS_FRAME); - scroll.add(m_feedlist); - - var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); - box.margin = 50; - box.valign = Gtk.Align.FILL; - box.halign = Gtk.Align.CENTER; - box.pack_start(headlineLabel, false, false, 0); - box.pack_start(loginLabel, false, false, 2); - box.pack_start(scroll, true, true, 20); - box.pack_end(loginButton, false, false, 0); - return box; -} - -public override async void postLoginAction() -{ - SourceFunc callback = postLoginAction.callback; - new GLib.Thread<void*>(null, () => { + SourceFunc callback = postLoginAction.callback; + new GLib.Thread<void*>(null, () => { var children = m_feedlist.get_children(); foreach(var r in children) { - var row = r as SuggestedFeedRow; - if(row.checked()) - { - FeedReaderBackend.get_default().addFeed(row.getURL(), row.getCategory(), false); + var row = r as SuggestedFeedRow; + if(row.checked()) + { + FeedReaderBackend.get_default().addFeed(row.getURL(), row.getCategory(), false); } } Idle.add((owned) callback); return null; }); - yield; -} - -private int sortFunc(Gtk.ListBoxRow row1, Gtk.ListBoxRow row2) -{ - var r1 = row1 as SuggestedFeedRow; - var r2 = row2 as SuggestedFeedRow; - - string cat1 = r1.getCategory(); - string cat2 = r2.getCategory(); - - string name1 = r1.getName(); - string name2 = r2.getName(); - - if(cat1 != cat2) + yield; + } + + private int sortFunc(Gtk.ListBoxRow row1, Gtk.ListBoxRow row2) + { + var r1 = row1 as SuggestedFeedRow; + var r2 = row2 as SuggestedFeedRow; + + string cat1 = r1.getCategory(); + string cat2 = r2.getCategory(); + + string name1 = r1.getName(); + string name2 = r2.getName(); + + if(cat1 != cat2) + { + return cat1.collate(cat2); + } + + return name1.collate(name2); + } + + private void headerFunc(Gtk.ListBoxRow row, Gtk.ListBoxRow? before) { - return cat1.collate(cat2); + var r1 = row as SuggestedFeedRow; + string cat1 = r1.getCategory(); + + var label = new Gtk.Label(cat1); + label.get_style_context().add_class("bold"); + label.margin_top = 20; + label.margin_bottom = 5; + + var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); + box.pack_start(label, true, true, 0); + box.pack_end(new Gtk.Separator(Gtk.Orientation.HORIZONTAL), false, false, 0); + box.show_all(); + + if(before == null) + { + row.set_header(box); + return; + } + + var r2 = before as SuggestedFeedRow; + string cat2 = r2.getCategory(); + + if(cat1 != cat2) + { + row.set_header(box); + } } - - return name1.collate(name2); -} - -private void headerFunc(Gtk.ListBoxRow row, Gtk.ListBoxRow? before) -{ - var r1 = row as SuggestedFeedRow; - string cat1 = r1.getCategory(); - - var label = new Gtk.Label(cat1); - label.get_style_context().add_class("bold"); - label.margin_top = 20; - label.margin_bottom = 5; - - var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); - box.pack_start(label, true, true, 0); - box.pack_end(new Gtk.Separator(Gtk.Orientation.HORIZONTAL), false, false, 0); - box.show_all(); - - if(before == null) + + + public override bool supportTags() + { + return true; + } + + public override bool doInitSync() + { + return false; + } + + public override string symbolicIcon() + { + return "feed-service-local-symbolic"; + } + + public override string accountName() + { + return "Local RSS"; + } + + public override string getServerURL() + { + return "http://localhost/"; + } + + public override string uncategorizedID() + { + return "0"; + } + + public override bool hideCategoryWhenEmpty(string catID) + { + return false; + } + + public override bool supportCategories() + { + return true; + } + + public override bool supportFeedManipulation() + { + return true; + } + + public override bool supportMultiLevelCategories() + { + return true; + } + + public override bool supportMultiCategoriesPerFeed() + { + return false; + } + + public override bool syncFeedsAndCategories() + { + return false; + } + + public override bool tagIDaffectedByNameChange() + { + return false; + } + + public override void resetAccount() { - row.set_header(box); return; } - - var r2 = before as SuggestedFeedRow; - string cat2 = r2.getCategory(); - - if(cat1 != cat2) + + public override bool useMaxArticles() { - row.set_header(box); + return true; } -} - - -public override bool supportTags() -{ - return true; -} - -public override bool doInitSync() -{ - return false; -} - -public override string symbolicIcon() -{ - return "feed-service-local-symbolic"; -} - -public override string accountName() -{ - return "Local RSS"; -} - -public override string getServerURL() -{ - return "http://localhost/"; -} - -public override string uncategorizedID() -{ - return "0"; -} - -public override bool hideCategoryWhenEmpty(string catID) -{ - return false; -} - -public override bool supportCategories() -{ - return true; -} - -public override bool supportFeedManipulation() -{ - return true; -} - -public override bool supportMultiLevelCategories() -{ - return true; -} - -public override bool supportMultiCategoriesPerFeed() -{ - return false; -} - -public override bool syncFeedsAndCategories() -{ - return false; -} - -public override bool tagIDaffectedByNameChange() -{ - return false; -} - -public override void resetAccount() -{ - return; -} - -public override bool useMaxArticles() -{ - return true; -} - -public override LoginResponse login() -{ - return LoginResponse.SUCCESS; -} - -public override bool serverAvailable() -{ - return Utils.ping("https://duckduckgo.com/"); -} - -public override void setArticleIsRead(string articleIDs, ArticleStatus read) -{ - return; -} - -public override void setArticleIsMarked(string articleID, ArticleStatus marked) -{ - return; -} - -public override bool alwaysSetReadByID() -{ - return false; -} - -public override void setFeedRead(string feedID) -{ - return; -} - -public override void setCategoryRead(string catID) -{ - return; -} - -public override void markAllItemsRead() -{ - return; -} - -public override void tagArticle(string articleID, string tagID) -{ - return; -} - -public override void removeArticleTag(string articleID, string tagID) -{ - return; -} - -public override string createTag(string caption) -{ - string tagID = "1"; - - var db = DataBase.readOnly(); - if(!db.isTableEmpty("tags")) + + public override LoginResponse login() { - tagID = (int.parse(db.getMaxID("tags", "tagID")) + 1).to_string(); + return LoginResponse.SUCCESS; } - - Logger.info("createTag: ID = " + tagID); - return tagID; -} - -public override void deleteTag(string tagID) -{ - return; -} - -public override void renameTag(string tagID, string title) -{ - return; -} - -public override bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) -{ - var catIDs = new Gee.ArrayList<string>(); - var db = DataBase.writeAccess(); - if(catID == null && newCatName != null) + + public override bool serverAvailable() { - string cID = createCategory(newCatName, null); - var cat = new Category(cID, newCatName, 0, 99, CategoryID.MASTER.to_string(), 1); - db.write_categories(ListUtils.single(cat)); - catIDs.add(cID); + return Utils.ping("https://duckduckgo.com/"); } - else if(catID != null && newCatName == null) + + public override void setArticleIsRead(string articleIDs, ArticleStatus read) { - catIDs.add(catID); + return; } - else + + public override void setArticleIsMarked(string articleID, ArticleStatus marked) { - catIDs.add("0"); + return; } - - feedID = Uuid.string_random(); - - Logger.info(@"addFeed: ID = $feedID"); - Feed? feed = m_utils.downloadFeed(m_session, feedURL, feedID, catIDs, out errmsg); - - if(feed != null) + + public override bool alwaysSetReadByID() + { + return false; + } + + public override void setFeedRead(string feedID) + { + return; + } + + public override void setCategoryRead(string catID) + { + return; + } + + public override void markAllItemsRead() { - if(!db.feed_exists(feed.getURL())) + return; + } + + public override void tagArticle(string articleID, string tagID) + { + return; + } + + public override void removeArticleTag(string articleID, string tagID) + { + return; + } + + public override string createTag(string caption) + { + string tagID = "1"; + + var db = DataBase.readOnly(); + if(!db.isTableEmpty("tags")) { - db.write_feeds(ListUtils.single(feed)); - return true; + tagID = (int.parse(db.getMaxID("tags", "tagID")) + 1).to_string(); } + + Logger.info("createTag: ID = " + tagID); + return tagID; } - - return false; -} - -public override void addFeeds(Gee.List<Feed> feeds) -{ - var finishedFeeds = new Gee.ArrayList<Feed>(); - - foreach(Feed f in feeds) + + public override void deleteTag(string tagID) { - string feedID = Uuid.string_random(); - - string url = f.getXmlUrl(); - Logger.info(@"addFeed: url = $url, ID = $feedID"); - string errmsg = ""; - Feed? feed = m_utils.downloadFeed(m_session, url, feedID, f.getCatIDs(), out errmsg); - + return; + } + + public override void renameTag(string tagID, string title) + { + return; + } + + public override bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) + { + var catIDs = new Gee.ArrayList<string>(); + var db = DataBase.writeAccess(); + if(catID == null && newCatName != null) + { + string cID = createCategory(newCatName, null); + var cat = new Category(cID, newCatName, 0, 99, CategoryID.MASTER.to_string(), 1); + db.write_categories(ListUtils.single(cat)); + catIDs.add(cID); + } + else if(catID != null && newCatName == null) + { + catIDs.add(catID); + } + else + { + catIDs.add("0"); + } + + feedID = Uuid.string_random(); + + Logger.info(@"addFeed: ID = $feedID"); + Feed? feed = m_utils.downloadFeed(m_session, feedURL, feedID, catIDs, out errmsg); + if(feed != null) { - if(feed.getTitle() != "No Title") + if(!db.feed_exists(feed.getURL())) { - feed.setTitle(f.getTitle()); + db.write_feeds(ListUtils.single(feed)); + return true; } - - finishedFeeds.add(feed); + } + + return false; + } + + public override void addFeeds(Gee.List<Feed> feeds) + { + var finishedFeeds = new Gee.ArrayList<Feed>(); + + foreach(Feed f in feeds) + { + string feedID = Uuid.string_random(); + + string url = f.getXmlUrl(); + Logger.info(@"addFeed: url = $url, ID = $feedID"); + string errmsg = ""; + Feed? feed = m_utils.downloadFeed(m_session, url, feedID, f.getCatIDs(), out errmsg); + + if(feed != null) + { + if(feed.getTitle() != "No Title") + { + feed.setTitle(f.getTitle()); + } + + finishedFeeds.add(feed); + } + else + { + Logger.error("Couldn't add Feed: " + f.getXmlUrl()); + } + } + + foreach(var feed in finishedFeeds) + { + Logger.debug("finishedFeed: " + feed.getTitle()); + } + + DataBase.writeAccess().write_feeds(finishedFeeds); + } + + public override void removeFeed(string feedID) + { + return; + } + + public override void renameFeed(string feedID, string title) + { + return; + } + + public override void moveFeed(string feedID, string newCatID, string? currentCatID) + { + return; + } + + public override string createCategory(string title, string? parentID) + { + string catID; + + string? id = DataBase.readOnly().getCategoryID(title); + if(id == null) + { + catID = Uuid.string_random(); } else { - Logger.error("Couldn't add Feed: " + f.getXmlUrl()); + catID = id; } + + Logger.info(@"createCategory: title= $title ID = $catID"); + return catID; } - - foreach(var feed in finishedFeeds) + + public override void renameCategory(string catID, string title) { - Logger.debug("finishedFeed: " + feed.getTitle()); + return; } - - DataBase.writeAccess().write_feeds(finishedFeeds); -} - -public override void removeFeed(string feedID) -{ - return; -} - -public override void renameFeed(string feedID, string title) -{ - return; -} - -public override void moveFeed(string feedID, string newCatID, string? currentCatID) -{ - return; -} - -public override string createCategory(string title, string? parentID) -{ - string catID; - - string? id = DataBase.readOnly().getCategoryID(title); - if(id == null) + + public override void moveCategory(string catID, string newParentID) { - catID = Uuid.string_random(); + return; } - else + + public override void deleteCategory(string catID) { - catID = id; + return; } - - Logger.info(@"createCategory: title= $title ID = $catID"); - return catID; -} - -public override void renameCategory(string catID, string title) -{ - return; -} - -public override void moveCategory(string catID, string newParentID) -{ - return; -} - -public override void deleteCategory(string catID) -{ - return; -} - -public override void removeCatFromFeed(string feedID, string catID) -{ - return; -} - -public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) -{ - return true; -} - -public override int getUnreadCount() -{ - return 0; -} - -public override void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) -{ - var db = DataBase.writeAccess(); - var feeds = db.read_feeds(); - var articles = new Gee.ArrayList<Article>(); - GLib.Mutex mutex = GLib.Mutex(); - - try + + public override void removeCatFromFeed(string feedID, string catID) + { + return; + } + + public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) { - var threads = new ThreadPool<Feed>.with_owned_data((feed) => { + return true; + } + + public override int getUnreadCount() + { + return 0; + } + + public override void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) + { + var db = DataBase.writeAccess(); + var feeds = db.read_feeds(); + var articles = new Gee.ArrayList<Article>(); + GLib.Mutex mutex = GLib.Mutex(); + + try + { + var threads = new ThreadPool<Feed>.with_owned_data((feed) => { if(cancellable != null && cancellable.is_cancelled()) { - return; + return; } - + Logger.debug("getArticles for feed: " + feed.getTitle()); string url = feed.getXmlUrl().escape(""); - + if(url == null || url == "" || GLib.Uri.parse_scheme(url) == null) { - Logger.error("no valid URL"); - return; + Logger.error("no valid URL"); + return; } - + var msg = new Soup.Message("GET", url); var session = new Soup.Session(); session.user_agent = Constants.USER_AGENT; session.timeout = 5; session.send_message(msg); string xml = (string)msg.response_body.flatten().data; - + // parse Rss.Parser parser = new Rss.Parser(); try { - parser.load_from_data(xml, xml.length); + parser.load_from_data(xml, xml.length); } catch(GLib.Error e) { - Logger.error("localInterface.getArticles: %s".printf(e.message)); - return; + Logger.error("localInterface.getArticles: %s".printf(e.message)); + return; } var doc = parser.get_document(); - + string? locale = null; if(doc.encoding != null - && doc.encoding != "") + && doc.encoding != "") { - locale = doc.encoding; + locale = doc.encoding; } - + Logger.debug("Got %u articles".printf(doc.get_items().length())); var newArticles = new Gee.ArrayList<Article>(); foreach(Rss.Item item in doc.get_items()) { - string? articleID = item.guid; - - if(articleID == null) - { - if(item.link == null) - { - Logger.warning("no valid id and no valid URL as well? what the hell man? I'm giving up"); - continue; + string? articleID = item.guid; + + if(articleID == null) + { + if(item.link == null) + { + Logger.warning("no valid id and no valid URL as well? what the hell man? I'm giving up"); + continue; } - - articleID = item.link; + + articleID = item.link; } - - var date = Rfc822.parseDate(item.pub_date); - if (date != null) - { - Logger.info(@"Parsed $(item.pub_date) as $(date.to_string())"); + + var date = Rfc822.parseDate(item.pub_date); + if (date != null) + { + Logger.info(@"Parsed $(item.pub_date) as $(date.to_string())"); } - else - { - if (item.pub_date != null) - { - Logger.warning(@"RFC 822 date parser failed to parse $(item.pub_date). Falling back to DateTime.now()"); + else + { + if (item.pub_date != null) + { + Logger.warning(@"RFC 822 date parser failed to parse $(item.pub_date). Falling back to DateTime.now()"); } - date = new DateTime.now_local(); + date = new DateTime.now_local(); } - - //Logger.info("Got content: " + item.description); - string? content = m_utils.convert(item.description, locale); - //Logger.info("Converted to: " + item.description); - if(content == null) - { - content = _("Nothing to read here."); + + //Logger.info("Got content: " + item.description); + string? content = m_utils.convert(item.description, locale); + //Logger.info("Converted to: " + item.description); + if(content == null) + { + content = _("Nothing to read here."); } - - var enclosures = new Gee.ArrayList<Enclosure>(); - - if(item.enclosure_url != null) - { - // FIXME: check what type of media we actually got - enclosures.add(new Enclosure(articleID, item.enclosure_url, EnclosureType.FILE)); + + var enclosures = new Gee.ArrayList<Enclosure>(); + + if(item.enclosure_url != null) + { + // FIXME: check what type of media we actually got + enclosures.add(new Enclosure(articleID, item.enclosure_url, EnclosureType.FILE)); } - - string articleURL = item.link; - if(articleURL.has_prefix("/")) - { - articleURL = feed.getURL() + articleURL.substring(1); + + string articleURL = item.link; + if(articleURL.has_prefix("/")) + { + articleURL = feed.getURL() + articleURL.substring(1); } - - var article = new Article( + + var article = new Article( articleID, (item.title != null) ? m_utils.convert(item.title, locale) : null, articleURL, @@ -618,51 +618,51 @@ public override void getArticles(int count, ArticleStatus whatToGet, DateTime? s 0, null, enclosures - ); - - Logger.debug("Got new article: " + article.getTitle()); - - newArticles.add(article); + ); + + Logger.debug("Got new article: " + article.getTitle()); + + newArticles.add(article); } mutex.lock(); articles.add_all(newArticles); mutex.unlock(); }, (int)GLib.get_num_processors(), true); - - foreach(Feed feed in feeds) - { - try - { - threads.add(feed); - } - catch(GLib.Error e) + + foreach(Feed feed in feeds) { - Logger.error("Error creating thread to download Feed %s: %s".printf(feed.getTitle(), e.message)); + try + { + threads.add(feed); + } + catch(GLib.Error e) + { + Logger.error("Error creating thread to download Feed %s: %s".printf(feed.getTitle(), e.message)); + } } + + bool immediate = false; // allow to queue up additional tasks + bool wait = true; // function will block until all tasks are done + ThreadPool.free((owned)threads, immediate, wait); } - - bool immediate = false; // allow to queue up additional tasks - bool wait = true; // function will block until all tasks are done - ThreadPool.free((owned)threads, immediate, wait); - } - catch(Error e) - { - Logger.error("Error creating threads to download Feeds: " + e.message); - } - - articles.sort((a, b) => { + catch(Error e) + { + Logger.error("Error creating threads to download Feeds: " + e.message); + } + + articles.sort((a, b) => { return strcmp(a.getArticleID(), b.getArticleID()); }); - - if(articles.size > 0) - { - db.write_articles(articles); - Logger.debug("localInterface: %i articles written".printf(articles.size)); - refreshFeedListCounter(); - updateArticleList(); + + if(articles.size > 0) + { + db.write_articles(articles); + Logger.debug("localInterface: %i articles written".printf(articles.size)); + refreshFeedListCounter(); + updateArticleList(); + } } -} - + } [ModuleInit] diff --git a/plugins/backend/local/localUtils.vala b/plugins/backend/local/localUtils.vala index b97e6c44..27a5b688 100644 --- a/plugins/backend/local/localUtils.vala +++ b/plugins/backend/local/localUtils.vala @@ -14,100 +14,100 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.localUtils : GLib.Object { - -public localUtils() -{ - -} - -public Feed? downloadFeed(Soup.Session session, string feed_url, string feedID, Gee.List<string> catIDs, out string errmsg) -{ - var error = new StringBuilder(_("Failed to add feed")); - error.append_printf(" %s\n", feed_url); - - var msg = new Soup.Message("GET", feed_url); - if (msg == null) + + public localUtils() { - error.append(_("Failed to parse URL.")); - errmsg = error.str; - Logger.warning(errmsg); - return null; + } - - uint status = session.send_message(msg); - if(status < 100 || status >= 400) + + public Feed? downloadFeed(Soup.Session session, string feed_url, string feedID, Gee.List<string> catIDs, out string errmsg) { - if(status < 100) + var error = new StringBuilder(_("Failed to add feed")); + error.append_printf(" %s\n", feed_url); + + var msg = new Soup.Message("GET", feed_url); + if (msg == null) { - error.append(_("Network error connecting to the server.")); + error.append(_("Failed to parse URL.")); + errmsg = error.str; + Logger.warning(errmsg); + return null; } - else + + uint status = session.send_message(msg); + if(status < 100 || status >= 400) { - error.append(_("Got HTTP error code")); - error.append_printf(" %u %s", status, Soup.Status.get_phrase(status)); + if(status < 100) + { + error.append(_("Network error connecting to the server.")); + } + else + { + error.append(_("Got HTTP error code")); + error.append_printf(" %u %s", status, Soup.Status.get_phrase(status)); + } + + errmsg = error.str; + Logger.warning(errmsg); + return null; } - - errmsg = error.str; - Logger.warning(errmsg); - return null; - } - string xml = (string)msg.response_body.flatten().data; - string? url = null; - - // parse - Rss.Parser parser = new Rss.Parser(); - try - { - parser.load_from_data(xml, xml.length); - } - catch(Error e) - { - error.append(_("Could not parse feed as RSS or ATOM.")); - errmsg = error.str; - Logger.warning(errmsg); - return null; - } - - var doc = parser.get_document(); - - if(doc.link != null - && doc.link != "") - { - url = doc.link; - } - - errmsg = ""; - return new Feed( - feedID, - doc.title, - url, - 0, - catIDs, - doc.image_url, + string xml = (string)msg.response_body.flatten().data; + string? url = null; + + // parse + Rss.Parser parser = new Rss.Parser(); + try + { + parser.load_from_data(xml, xml.length); + } + catch(Error e) + { + error.append(_("Could not parse feed as RSS or ATOM.")); + errmsg = error.str; + Logger.warning(errmsg); + return null; + } + + var doc = parser.get_document(); + + if(doc.link != null + && doc.link != "") + { + url = doc.link; + } + + errmsg = ""; + return new Feed( + feedID, + doc.title, + url, + 0, + catIDs, + doc.image_url, feed_url); -} - -public string? convert(string? text, string? locale) -{ - if(text == null) - { - return null; - } - - if(locale == null) - { - return text; - } - - try - { - return GLib.convert(text, -1, "utf-8", locale); } - catch(ConvertError e) + + public string? convert(string? text, string? locale) { - Logger.error(e.message); + if(text == null) + { + return null; + } + + if(locale == null) + { + return text; + } + + try + { + return GLib.convert(text, -1, "utf-8", locale); + } + catch(ConvertError e) + { + Logger.error(e.message); + } + + return ""; } - - return ""; -} } diff --git a/plugins/backend/oldreader/oldreaderAPI.vala b/plugins/backend/oldreader/oldreaderAPI.vala index d672f2e8..476caff0 100644 --- a/plugins/backend/oldreader/oldreaderAPI.vala +++ b/plugins/backend/oldreader/oldreaderAPI.vala @@ -14,393 +14,393 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.OldReaderAPI : GLib.Object { - -public enum OldreaderSubscriptionAction { - EDIT, - SUBSCRIBE, - UNSUBSCRIBE -} - -private OldReaderConnection m_connection; -private OldReaderUtils m_utils; -private string m_userID; - -public OldReaderAPI(OldReaderUtils utils) -{ - m_utils = utils; - m_connection = new OldReaderConnection(m_utils); -} - -public LoginResponse login() -{ - if(m_utils.getAccessToken() == "") - { - var response = m_connection.getToken(); - if(response != LoginResponse.SUCCESS) + + public enum OldreaderSubscriptionAction { + EDIT, + SUBSCRIBE, + UNSUBSCRIBE + } + + private OldReaderConnection m_connection; + private OldReaderUtils m_utils; + private string m_userID; + + public OldReaderAPI(OldReaderUtils utils) + { + m_utils = utils; + m_connection = new OldReaderConnection(m_utils); + } + + public LoginResponse login() + { + if(m_utils.getAccessToken() == "") { - return response; + var response = m_connection.getToken(); + if(response != LoginResponse.SUCCESS) + { + return response; + } } + if(getUserID()) + { + return LoginResponse.SUCCESS; + } + + return LoginResponse.UNKNOWN_ERROR; } - if(getUserID()) - { - return LoginResponse.SUCCESS; - } - - return LoginResponse.UNKNOWN_ERROR; -} - -public bool ping() { - return Utils.ping("https://theoldreader.com/"); -} - -private bool getUserID() -{ - Logger.debug("getUserID: getting user info"); - var response = m_connection.send_get_request("user-info?output=json"); - - if(response.status != 200) - { - return false; - } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); - } - catch(Error e) - { - Logger.error("getUserID: Could not load message response"); - Logger.error(e.message); - return false; - } - var root = parser.get_root().get_object(); - if(root.has_member("userId")) - { - m_userID = root.get_string_member("userId"); - m_utils.setUserID(m_userID); - Logger.info("Oldreader: userID = " + m_userID); - - return true; - } - - return false; -} - -public bool getFeeds(Gee.List<Feed> feeds) -{ - - var response = m_connection.send_get_request("subscription/list?output=json"); - - if(response.status != 200) - { - return false; - } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); + + public bool ping() { + return Utils.ping("https://theoldreader.com/"); } - catch(Error e) + + private bool getUserID() { - Logger.error("getFeeds: Could not load message response"); - Logger.error(e.message); + Logger.debug("getUserID: getting user info"); + var response = m_connection.send_get_request("user-info?output=json"); + + if(response.status != 200) + { + return false; + } + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("getUserID: Could not load message response"); + Logger.error(e.message); + return false; + } + var root = parser.get_root().get_object(); + if(root.has_member("userId")) + { + m_userID = root.get_string_member("userId"); + m_utils.setUserID(m_userID); + Logger.info("Oldreader: userID = " + m_userID); + + return true; + } + return false; } - var root = parser.get_root().get_object(); - var array = root.get_array_member("subscriptions"); - uint length = array.get_length(); - - for (uint i = 0; i < length; i++) + + public bool getFeeds(Gee.List<Feed> feeds) { - Json.Object object = array.get_object_element(i); - - string feedID = object.get_string_member("id"); - string url = object.has_member("htmlUrl") ? object.get_string_member("htmlUrl") : object.get_string_member("url"); - - uint catCount = object.get_array_member("categories").get_length(); - var categories = new Gee.ArrayList<string>(); - for(uint j = 0; j < catCount; ++j) + + var response = m_connection.send_get_request("subscription/list?output=json"); + + if(response.status != 200) { - categories.add(object.get_array_member("categories").get_object_element(j).get_string_member("id")); + return false; } - - feeds.add( - new Feed( - feedID, - object.get_string_member("title"), - url, - 0, - categories, - object.get_string_member("iconUrl") + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("getFeeds: Could not load message response"); + Logger.error(e.message); + return false; + } + var root = parser.get_root().get_object(); + var array = root.get_array_member("subscriptions"); + uint length = array.get_length(); + + for (uint i = 0; i < length; i++) + { + Json.Object object = array.get_object_element(i); + + string feedID = object.get_string_member("id"); + string url = object.has_member("htmlUrl") ? object.get_string_member("htmlUrl") : object.get_string_member("url"); + + uint catCount = object.get_array_member("categories").get_length(); + var categories = new Gee.ArrayList<string>(); + for(uint j = 0; j < catCount; ++j) + { + categories.add(object.get_array_member("categories").get_object_element(j).get_string_member("id")); + } + + feeds.add( + new Feed( + feedID, + object.get_string_member("title"), + url, + 0, + categories, + object.get_string_member("iconUrl") ) ); + } + return true; } - return true; -} - -public bool getCategoriesAndTags(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags) -{ - var response = m_connection.send_get_request("tag/list?output=json"); - - if(response.status != 200) - { - return false; - } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); - } - catch(Error e) - { - Logger.error("getCategoriesAndTags: Could not load message response"); - Logger.error(e.message); - return false; - } - var root = parser.get_root().get_object(); - var array = root.get_array_member("tags"); - uint length = array.get_length(); - int orderID = 0; - - for (uint i = 0; i < length; i++) + + public bool getCategoriesAndTags(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags) { - Json.Object object = array.get_object_element(i); - string id = object.get_string_member("id"); - int start = id.last_index_of_char('/') + 1; - string title = id.substring(start); - - if(id.contains("/label/")) + var response = m_connection.send_get_request("tag/list?output=json"); + + if(response.status != 200) { - categories.add( - new Category( - id, - title, - 0, - orderID, - CategoryID.MASTER.to_string(), - 1 + return false; + } + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("getCategoriesAndTags: Could not load message response"); + Logger.error(e.message); + return false; + } + var root = parser.get_root().get_object(); + var array = root.get_array_member("tags"); + uint length = array.get_length(); + int orderID = 0; + + for (uint i = 0; i < length; i++) + { + Json.Object object = array.get_object_element(i); + string id = object.get_string_member("id"); + int start = id.last_index_of_char('/') + 1; + string title = id.substring(start); + + if(id.contains("/label/")) + { + categories.add( + new Category( + id, + title, + 0, + orderID, + CategoryID.MASTER.to_string(), + 1 ) ); - ++orderID; + ++orderID; + } } + return true; } - return true; -} - - -public int getTotalUnread() -{ - var response = m_connection.send_get_request("unread-count?output=json"); - - if(response.status != 200) - { - return 0; - } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); - } - catch(Error e) - { - Logger.error("getTotalUnread: Could not load message response"); - Logger.error(e.message); - } - - var root = parser.get_root().get_object(); - var array = root.get_array_member("unreadcounts"); - uint length = array.get_length(); - int count = 0; - - for (uint i = 0; i < length; i++) + + + public int getTotalUnread() { - Json.Object object = array.get_object_element(i); - if(object.get_string_member("id").has_prefix("feed/")) + var response = m_connection.send_get_request("unread-count?output=json"); + + if(response.status != 200) { - count += (int)object.get_int_member("count"); + return 0; } - - } - - Logger.debug("getTotalUnread %i".printf(count)); - return count; -} - - -public string? updateArticles(Gee.List<string> ids, int count, string? continuation = null) -{ - var message_string = "n=" + count.to_string(); - message_string += "&xt=user/-/state/com.google/read"; - if(continuation != null) - { - message_string += "&c=" + continuation; - } - - var response = m_connection.send_get_request("stream/items/ids?output=json&"+message_string); - - if(response.status != 200) - { - return null; - } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); - } - catch(Error e) - { - Logger.error("updateArticles: Could not load message response"); - Logger.error(e.message); - return null; - } - - var root = parser.get_root().get_object(); - var array = root.get_array_member("itemRefs"); - uint length = array.get_length(); - - for (uint i = 0; i < length; i++) - { - Json.Object object = array.get_object_element(i); - ids.add(object.get_string_member("id")); - } - - if(root.has_member("continuation") && root.get_string_member("continuation") != "") - { - return root.get_string_member("continuation"); + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("getTotalUnread: Could not load message response"); + Logger.error(e.message); + } + + var root = parser.get_root().get_object(); + var array = root.get_array_member("unreadcounts"); + uint length = array.get_length(); + int count = 0; + + for (uint i = 0; i < length; i++) + { + Json.Object object = array.get_object_element(i); + if(object.get_string_member("id").has_prefix("feed/")) + { + count += (int)object.get_int_member("count"); + } + + } + + Logger.debug("getTotalUnread %i".printf(count)); + return count; } - - return null; -} - -public string? getArticles(Gee.List<Article> articles, int count, ArticleStatus whatToGet = ArticleStatus.ALL, string? continuation = null, string? tagID = null, string? feed_id = null) -{ - var message_string = "n=" + count.to_string(); - - if(whatToGet == ArticleStatus.UNREAD) + + + public string? updateArticles(Gee.List<string> ids, int count, string? continuation = null) { + var message_string = "n=" + count.to_string(); message_string += "&xt=user/-/state/com.google/read"; - } - if(whatToGet == ArticleStatus.READ) - { - message_string += "&s=user/-/state/com.google/read"; - } - else if(whatToGet == ArticleStatus.MARKED) - { - message_string += "&s=user/-/state/com.google/starred"; - } - - message_string += "&c=" + continuation; - - - string api_endpoint = "stream/contents"; - if(feed_id != null) - { - api_endpoint += "/" + GLib.Uri.escape_string(feed_id); - } - else if(tagID != null) - { - api_endpoint += "/" + GLib.Uri.escape_string(tagID); - } - var response = m_connection.send_get_request(api_endpoint+"?output=json&"+message_string); - - if(response.status != 200) - { + if(continuation != null) + { + message_string += "&c=" + continuation; + } + + var response = m_connection.send_get_request("stream/items/ids?output=json&"+message_string); + + if(response.status != 200) + { + return null; + } + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("updateArticles: Could not load message response"); + Logger.error(e.message); + return null; + } + + var root = parser.get_root().get_object(); + var array = root.get_array_member("itemRefs"); + uint length = array.get_length(); + + for (uint i = 0; i < length; i++) + { + Json.Object object = array.get_object_element(i); + ids.add(object.get_string_member("id")); + } + + if(root.has_member("continuation") && root.get_string_member("continuation") != "") + { + return root.get_string_member("continuation"); + } + return null; } - - var parser = new Json.Parser(); - try - { - parser.load_from_data(response.data, -1); - } - catch(Error e) - { - Logger.error("getCategoriesAndTags: Could not load message response"); - Logger.error(e.message); - } - - var root = parser.get_root().get_object(); - var array = root.get_array_member("items"); - uint length = array.get_length(); - - var db = DataBase.readOnly(); - for (uint i = 0; i < length; i++) + + public string? getArticles(Gee.List<Article> articles, int count, ArticleStatus whatToGet = ArticleStatus.ALL, string? continuation = null, string? tagID = null, string? feed_id = null) { - Json.Object object = array.get_object_element(i); - string id = object.get_string_member("id"); - id = id.substring(id.last_index_of_char('/') + 1); - bool marked = false; - bool read = false; - var cats = object.get_array_member("categories"); - uint cat_length = cats.get_length(); - - var tags = new Gee.ArrayList<string>(); - for (uint j = 0; j < cat_length; j++) + var message_string = "n=" + count.to_string(); + + if(whatToGet == ArticleStatus.UNREAD) { - string cat = cats.get_string_element(j); - if(cat.has_suffix("com.google/starred")) - { - marked = true; - } - else if(cat.has_suffix("com.google/read")) - { - read = true; - } - else if(cat.contains("/label/") && db.getTagName(cat) != null) - { - tags.add(cat); - } + message_string += "&xt=user/-/state/com.google/read"; } - - var enclosures = new Gee.ArrayList<Enclosure>(); - if(object.has_member("enclosure")) + if(whatToGet == ArticleStatus.READ) { - var attachments = object.get_array_member("enclosure"); - - uint mediaCount = 0; - if(attachments != null) + message_string += "&s=user/-/state/com.google/read"; + } + else if(whatToGet == ArticleStatus.MARKED) + { + message_string += "&s=user/-/state/com.google/starred"; + } + + message_string += "&c=" + continuation; + + + string api_endpoint = "stream/contents"; + if(feed_id != null) + { + api_endpoint += "/" + GLib.Uri.escape_string(feed_id); + } + else if(tagID != null) + { + api_endpoint += "/" + GLib.Uri.escape_string(tagID); + } + var response = m_connection.send_get_request(api_endpoint+"?output=json&"+message_string); + + if(response.status != 200) + { + return null; + } + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response.data, -1); + } + catch(Error e) + { + Logger.error("getCategoriesAndTags: Could not load message response"); + Logger.error(e.message); + } + + var root = parser.get_root().get_object(); + var array = root.get_array_member("items"); + uint length = array.get_length(); + + var db = DataBase.readOnly(); + for (uint i = 0; i < length; i++) + { + Json.Object object = array.get_object_element(i); + string id = object.get_string_member("id"); + id = id.substring(id.last_index_of_char('/') + 1); + bool marked = false; + bool read = false; + var cats = object.get_array_member("categories"); + uint cat_length = cats.get_length(); + + var tags = new Gee.ArrayList<string>(); + for (uint j = 0; j < cat_length; j++) { - mediaCount = attachments.get_length(); + string cat = cats.get_string_element(j); + if(cat.has_suffix("com.google/starred")) + { + marked = true; + } + else if(cat.has_suffix("com.google/read")) + { + read = true; + } + else if(cat.contains("/label/") && db.getTagName(cat) != null) + { + tags.add(cat); + } } - - for(int j = 0; j < mediaCount; ++j) + + var enclosures = new Gee.ArrayList<Enclosure>(); + if(object.has_member("enclosure")) { - var attachment = attachments.get_object_element(j); - enclosures.add( - new Enclosure(id, attachment.get_string_member("href"), - EnclosureType.from_string(attachment.get_string_member("type"))) + var attachments = object.get_array_member("enclosure"); + + uint mediaCount = 0; + if(attachments != null) + { + mediaCount = attachments.get_length(); + } + + for(int j = 0; j < mediaCount; ++j) + { + var attachment = attachments.get_object_element(j); + enclosures.add( + new Enclosure(id, attachment.get_string_member("href"), + EnclosureType.from_string(attachment.get_string_member("type"))) ); + } } - } - - articles.add(new Article( - id, - object.get_string_member("title"), - object.get_array_member("alternate").get_object_element(0).get_string_member("href"), - object.get_object_member("origin").get_string_member("streamId"), - read ? ArticleStatus.READ : ArticleStatus.UNREAD, - marked ? ArticleStatus.MARKED : ArticleStatus.UNMARKED, - object.get_object_member("summary").get_string_member("content"), - null, - object.get_string_member("author"), - new DateTime.from_unix_local(object.get_int_member("published")), - -1, - tags, - enclosures - ) - ); - } - + + articles.add(new Article( + id, + object.get_string_member("title"), + object.get_array_member("alternate").get_object_element(0).get_string_member("href"), + object.get_object_member("origin").get_string_member("streamId"), + read ? ArticleStatus.READ : ArticleStatus.UNREAD, + marked ? ArticleStatus.MARKED : ArticleStatus.UNMARKED, + object.get_object_member("summary").get_string_member("content"), + null, + object.get_string_member("author"), + new DateTime.from_unix_local(object.get_int_member("published")), + -1, + tags, + enclosures + ) + ); + } + if(root.has_member("continuation") && root.get_string_member("continuation") != "") { return root.get_string_member("continuation"); } - + return null; } @@ -416,7 +416,7 @@ public void edidTag(string articleID, string tagID, bool add = true) { message_string += "r="; } - + message_string += tagID; message_string += "&i=" + articleID; m_connection.send_post_request("edit-tag?output=json", message_string); @@ -450,42 +450,42 @@ public void renameTag(string tagID, string title) public bool editSubscription(OldreaderSubscriptionAction action, string[] feedID, string? title = null, string? add = null, string? remove = null) { var message_string = "ac="; - + switch(action) { - case OldreaderSubscriptionAction.EDIT: + case OldreaderSubscriptionAction.EDIT: message_string += "edit"; break; - case OldreaderSubscriptionAction.SUBSCRIBE: + case OldreaderSubscriptionAction.SUBSCRIBE: message_string += "subscribe"; break; - case OldreaderSubscriptionAction.UNSUBSCRIBE: + case OldreaderSubscriptionAction.UNSUBSCRIBE: message_string += "unsubscribe"; break; } - + foreach(string s in feedID) { message_string += "&s=" + s; } - + if(title != null) { message_string += "&t=" + title; } - + if(add != null) { message_string += "&a=" + add; } - + if(remove != null) { message_string += "&r=" + remove; } - - + + var response = m_connection.send_post_request("subscription/edit?output=json", message_string); - + return response.status == 200; } } diff --git a/plugins/backend/oldreader/oldreaderConnection.vala b/plugins/backend/oldreader/oldreaderConnection.vala index 5f039f18..ee203a02 100644 --- a/plugins/backend/oldreader/oldreaderConnection.vala +++ b/plugins/backend/oldreader/oldreaderConnection.vala @@ -14,100 +14,100 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.OldReaderConnection { -private string m_api_username; -private string m_api_code; -private string m_passwd; -private OldReaderUtils m_utils; -private Soup.Session m_session; - -public OldReaderConnection(OldReaderUtils utils) -{ - m_utils = utils; - m_api_username = m_utils.getUser(); - m_api_code = m_utils.getAccessToken(); - m_passwd = m_utils.getPasswd(); - m_session = new Soup.Session(); - m_session.user_agent = Constants.USER_AGENT; -} - -public LoginResponse getToken() -{ - Logger.debug("OldReader Connection: getToken()"); - - var message = new Soup.Message("POST", "https://theoldreader.com/accounts/ClientLogin/"); - string message_string = "Email=" + m_api_username - + "&Passwd=" + m_passwd - + "&service=reader" - + "&accountType=HOSTED_OR_GOOGLE" - + "&client=FeedReader"; - message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string.data); - m_session.send_message(message); - - if(message.status_code != 200) + private string m_api_username; + private string m_api_code; + private string m_passwd; + private OldReaderUtils m_utils; + private Soup.Session m_session; + + public OldReaderConnection(OldReaderUtils utils) { - return LoginResponse.NO_CONNECTION; + m_utils = utils; + m_api_username = m_utils.getUser(); + m_api_code = m_utils.getAccessToken(); + m_passwd = m_utils.getPasswd(); + m_session = new Soup.Session(); + m_session.user_agent = Constants.USER_AGENT; } - - string response = (string)message.response_body.flatten().data; - try + + public LoginResponse getToken() { - var regex = new Regex(".*\\w\\s.*\\w\\sAuth="); - if(regex.match(response)) + Logger.debug("OldReader Connection: getToken()"); + + var message = new Soup.Message("POST", "https://theoldreader.com/accounts/ClientLogin/"); + string message_string = "Email=" + m_api_username + + "&Passwd=" + m_passwd + + "&service=reader" + + "&accountType=HOSTED_OR_GOOGLE" + + "&client=FeedReader"; + message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string.data); + m_session.send_message(message); + + if(message.status_code != 200) { - Logger.debug(@"Regex oldreader - $response"); - string split = regex.replace( response, -1,0,""); - Logger.debug(@"authcode: $split"); - m_utils.setAccessToken(split.strip()); - return LoginResponse.SUCCESS; + return LoginResponse.NO_CONNECTION; } - else + + string response = (string)message.response_body.flatten().data; + try { - Logger.debug(message_string); - Logger.error(response); - return LoginResponse.WRONG_LOGIN; + var regex = new Regex(".*\\w\\s.*\\w\\sAuth="); + if(regex.match(response)) + { + Logger.debug(@"Regex oldreader - $response"); + string split = regex.replace( response, -1,0,""); + Logger.debug(@"authcode: $split"); + m_utils.setAccessToken(split.strip()); + return LoginResponse.SUCCESS; + } + else + { + Logger.debug(message_string); + Logger.error(response); + return LoginResponse.WRONG_LOGIN; + } + } + catch(Error e) + { + Logger.error("OldReaderConnection - getToken: Could not load message response"); + Logger.error(e.message); + return LoginResponse.UNKNOWN_ERROR; } } - catch(Error e) + + public Response send_get_request(string path, string? message_string = null) { - Logger.error("OldReaderConnection - getToken: Could not load message response"); - Logger.error(e.message); - return LoginResponse.UNKNOWN_ERROR; + return send_request(path, "GET", message_string); } -} - -public Response send_get_request(string path, string? message_string = null) -{ - return send_request(path, "GET", message_string); -} - -public Response send_post_request(string path, string? message_string = null) -{ - return send_request(path, "POST", message_string); -} - -private Response send_request(string path, string type, string? message_string = null) -{ - var message = new Soup.Message(type, OldReaderSecret.base_uri + path); - - string oldauth = "GoogleLogin auth=" + m_utils.getAccessToken(); - message.request_headers.append("Authorization", oldauth); - - if(message_string != null) + + public Response send_post_request(string path, string? message_string = null) { - message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string.data); + return send_request(path, "POST", message_string); } - - m_session.send_message(message); - - if(message.status_code != 200) + + private Response send_request(string path, string type, string? message_string = null) { - Logger.warning("OldReaderConnection: unexpected response %u".printf(message.status_code)); + var message = new Soup.Message(type, OldReaderSecret.base_uri + path); + + string oldauth = "GoogleLogin auth=" + m_utils.getAccessToken(); + message.request_headers.append("Authorization", oldauth); + + if(message_string != null) + { + message.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message_string.data); + } + + m_session.send_message(message); + + if(message.status_code != 200) + { + Logger.warning("OldReaderConnection: unexpected response %u".printf(message.status_code)); + } + + return Response() { + status = message.status_code, + data = (string)message.response_body.flatten().data + }; } - - return Response() { - status = message.status_code, - data = (string)message.response_body.flatten().data - }; -} - + } diff --git a/plugins/backend/oldreader/oldreaderInterface.vala b/plugins/backend/oldreader/oldreaderInterface.vala index 22f725b9..9bcfbc0f 100644 --- a/plugins/backend/oldreader/oldreaderInterface.vala +++ b/plugins/backend/oldreader/oldreaderInterface.vala @@ -14,456 +14,456 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.OldReaderInterface : FeedServerInterface { - -private OldReaderAPI m_api; -private OldReaderUtils m_utils; -private Gtk.Entry m_userEntry; -private Gtk.Entry m_passwordEntry; - -public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) -{ - m_utils = new OldReaderUtils(settings_backend, secrets); - m_api = new OldReaderAPI(m_utils); -} - -public override string getWebsite() -{ - return "https://theoldreader.com/"; -} - -public override BackendFlags getFlags() -{ - return (BackendFlags.HOSTED | BackendFlags.PROPRIETARY | BackendFlags.PAID_PREMIUM); -} - -public override string getID() -{ - return "oldreader"; -} - -public override string iconName() -{ - return "feed-service-oldreader"; -} - -public override string serviceName() -{ - return "The Old Reader"; -} - -public override bool needWebLogin() -{ - return false; -} - -public override Gtk.Box? getWidget() -{ - var user_label = new Gtk.Label(_("Username:")); - var password_label = new Gtk.Label(_("Password:")); - - user_label.set_alignment(1.0f, 0.5f); - password_label.set_alignment(1.0f, 0.5f); - - user_label.set_hexpand(true); - password_label.set_hexpand(true); - - m_userEntry = new Gtk.Entry(); - m_passwordEntry = new Gtk.Entry(); - - m_userEntry.activate.connect(() => { tryLogin(); }); - m_passwordEntry.activate.connect(() => { tryLogin(); }); - - m_passwordEntry.set_invisible_char('*'); - m_passwordEntry.set_visibility(false); - - var grid = new Gtk.Grid(); - grid.set_column_spacing(10); - grid.set_row_spacing(10); - grid.set_valign(Gtk.Align.CENTER); - grid.set_halign(Gtk.Align.CENTER); - - grid.attach(user_label, 0, 0, 1, 1); - grid.attach(m_userEntry, 1, 0, 1, 1); - grid.attach(password_label, 0, 1, 1, 1); - grid.attach(m_passwordEntry, 1, 1, 1, 1); - - var logo = new Gtk.Image.from_icon_name("feed-service-oldreader", Gtk.IconSize.MENU); - - var loginLabel = new Gtk.Label(_("Please log in to the Old Reader and enjoy using FeedReader")); - loginLabel.get_style_context().add_class("h2"); - loginLabel.set_justify(Gtk.Justification.CENTER); - loginLabel.set_lines(3); - - var loginButton = new Gtk.Button.with_label(_("Login")); - loginButton.halign = Gtk.Align.END; - loginButton.set_size_request(80, 30); - loginButton.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); - loginButton.clicked.connect(() => { tryLogin(); }); - - var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10); - box.valign = Gtk.Align.CENTER; - box.halign = Gtk.Align.CENTER; - box.pack_start(loginLabel, false, false, 10); - box.pack_start(logo, false, false, 10); - box.pack_start(grid, true, true, 10); - box.pack_end(loginButton, false, false, 20); - - m_userEntry.set_text(m_utils.getUser()); - m_passwordEntry.set_text(m_utils.getPasswd()); - - return box; -} - -public override void writeData() -{ - m_utils.setUser(m_userEntry.get_text().strip()); - m_utils.setPassword(m_passwordEntry.get_text().strip()); -} - -public override string buildLoginURL() -{ - return ""; -} - -public override bool extractCode(string redirectURL) -{ - return false; -} - -public override bool supportTags() -{ - return false; -} - -public override bool doInitSync() -{ - return true; -} - -public override string symbolicIcon() -{ - return "feed-service-oldreader-symbolic"; -} - -public override string accountName() -{ - return m_utils.getUser(); -} - -public override string getServerURL() -{ - return "https://theoldreader.com/"; -} - -public override string uncategorizedID() -{ - return ""; -} - -public override bool hideCategoryWhenEmpty(string cadID) -{ - return false; -} - -public override bool supportCategories() -{ - return true; -} - -public override bool supportFeedManipulation() -{ - return true; -} - -public override bool supportMultiLevelCategories() -{ - return false; -} - -public override bool supportMultiCategoriesPerFeed() -{ - return false; -} - -public override bool syncFeedsAndCategories() -{ - return true; -} - -public override bool tagIDaffectedByNameChange() -{ - return true; -} - -public override void resetAccount() -{ - m_utils.resetAccount(); -} - -public override bool useMaxArticles() -{ - return true; -} - -public override LoginResponse login() -{ - return m_api.login(); -} - -public override void setArticleIsRead(string articleIDs, ArticleStatus read) -{ - if(read == ArticleStatus.READ) + + private OldReaderAPI m_api; + private OldReaderUtils m_utils; + private Gtk.Entry m_userEntry; + private Gtk.Entry m_passwordEntry; + + public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) { - m_api.edidTag(articleIDs, "user/-/state/com.google/read"); + m_utils = new OldReaderUtils(settings_backend, secrets); + m_api = new OldReaderAPI(m_utils); } - else + + public override string getWebsite() { - m_api.edidTag(articleIDs, "user/-/state/com.google/read", false); + return "https://theoldreader.com/"; } -} - -public override void setArticleIsMarked(string articleID, ArticleStatus marked) -{ - if(marked == ArticleStatus.MARKED) + + public override BackendFlags getFlags() { - m_api.edidTag(articleID, "user/-/state/com.google/starred"); + return (BackendFlags.HOSTED | BackendFlags.PROPRIETARY | BackendFlags.PAID_PREMIUM); } - else + + public override string getID() { - m_api.edidTag(articleID, "user/-/state/com.google/starred", false); + return "oldreader"; } -} - -public override bool alwaysSetReadByID() -{ - return false; -} - -public override void setFeedRead(string feedID) -{ - m_api.markAsRead(feedID); -} - -public override void setCategoryRead(string catID) -{ - m_api.markAsRead(catID); -} - -public override void markAllItemsRead() -{ - var db = DataBase.readOnly(); - var categories = db.read_categories(); - foreach(Category cat in categories) + + public override string iconName() { - m_api.markAsRead(cat.getCatID()); + return "feed-service-oldreader"; } - - var feeds = db.read_feeds_without_cat(); - foreach(Feed feed in feeds) + + public override string serviceName() { - m_api.markAsRead(feed.getFeedID()); + return "The Old Reader"; } - m_api.markAsRead(); -} - -public override void tagArticle(string articleID, string tagID) -{ - m_api.edidTag(articleID, tagID, true); -} - -public override void removeArticleTag(string articleID, string tagID) -{ - m_api.edidTag(articleID, tagID, false); -} - -public override string createTag(string caption) -{ - return m_api.composeTagID(caption); -} - -public override void deleteTag(string tagID) -{ - m_api.deleteTag(tagID); -} - -public override void renameTag(string tagID, string title) -{ - m_api.renameTag(tagID, title); -} - -public override bool serverAvailable() -{ - return m_api.ping(); -} - -public override bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) -{ - feedID = "feed/" + feedURL; - errmsg = ""; - bool success = false; - - if(catID == null && newCatName != null) + + public override bool needWebLogin() { - string newCatID = m_api.composeTagID(newCatName); - success = m_api.editSubscription(OldReaderAPI.OldreaderSubscriptionAction.SUBSCRIBE, {"feed/"+feedURL}, null, newCatID); + return false; } - else + + public override Gtk.Box? getWidget() { - success = m_api.editSubscription(OldReaderAPI.OldreaderSubscriptionAction.SUBSCRIBE, {"feed/"+feedURL}, null, catID); + var user_label = new Gtk.Label(_("Username:")); + var password_label = new Gtk.Label(_("Password:")); + + user_label.set_alignment(1.0f, 0.5f); + password_label.set_alignment(1.0f, 0.5f); + + user_label.set_hexpand(true); + password_label.set_hexpand(true); + + m_userEntry = new Gtk.Entry(); + m_passwordEntry = new Gtk.Entry(); + + m_userEntry.activate.connect(() => { tryLogin(); }); + m_passwordEntry.activate.connect(() => { tryLogin(); }); + + m_passwordEntry.set_invisible_char('*'); + m_passwordEntry.set_visibility(false); + + var grid = new Gtk.Grid(); + grid.set_column_spacing(10); + grid.set_row_spacing(10); + grid.set_valign(Gtk.Align.CENTER); + grid.set_halign(Gtk.Align.CENTER); + + grid.attach(user_label, 0, 0, 1, 1); + grid.attach(m_userEntry, 1, 0, 1, 1); + grid.attach(password_label, 0, 1, 1, 1); + grid.attach(m_passwordEntry, 1, 1, 1, 1); + + var logo = new Gtk.Image.from_icon_name("feed-service-oldreader", Gtk.IconSize.MENU); + + var loginLabel = new Gtk.Label(_("Please log in to the Old Reader and enjoy using FeedReader")); + loginLabel.get_style_context().add_class("h2"); + loginLabel.set_justify(Gtk.Justification.CENTER); + loginLabel.set_lines(3); + + var loginButton = new Gtk.Button.with_label(_("Login")); + loginButton.halign = Gtk.Align.END; + loginButton.set_size_request(80, 30); + loginButton.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); + loginButton.clicked.connect(() => { tryLogin(); }); + + var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10); + box.valign = Gtk.Align.CENTER; + box.halign = Gtk.Align.CENTER; + box.pack_start(loginLabel, false, false, 10); + box.pack_start(logo, false, false, 10); + box.pack_start(grid, true, true, 10); + box.pack_end(loginButton, false, false, 20); + + m_userEntry.set_text(m_utils.getUser()); + m_passwordEntry.set_text(m_utils.getPasswd()); + + return box; } - - if(!success) + + public override void writeData() { - errmsg = @"The old reader could not add $feedURL"; + m_utils.setUser(m_userEntry.get_text().strip()); + m_utils.setPassword(m_passwordEntry.get_text().strip()); } - - return success; -} - -public override void addFeeds(Gee.List<Feed> feeds) -{ - string cat = ""; - string[] urls = {}; - - foreach(Feed f in feeds) + + public override string buildLoginURL() + { + return ""; + } + + public override bool extractCode(string redirectURL) + { + return false; + } + + public override bool supportTags() { - if(f.getCatIDs()[0] != cat) + return false; + } + + public override bool doInitSync() + { + return true; + } + + public override string symbolicIcon() + { + return "feed-service-oldreader-symbolic"; + } + + public override string accountName() + { + return m_utils.getUser(); + } + + public override string getServerURL() + { + return "https://theoldreader.com/"; + } + + public override string uncategorizedID() + { + return ""; + } + + public override bool hideCategoryWhenEmpty(string cadID) + { + return false; + } + + public override bool supportCategories() + { + return true; + } + + public override bool supportFeedManipulation() + { + return true; + } + + public override bool supportMultiLevelCategories() + { + return false; + } + + public override bool supportMultiCategoriesPerFeed() + { + return false; + } + + public override bool syncFeedsAndCategories() + { + return true; + } + + public override bool tagIDaffectedByNameChange() + { + return true; + } + + public override void resetAccount() + { + m_utils.resetAccount(); + } + + public override bool useMaxArticles() + { + return true; + } + + public override LoginResponse login() + { + return m_api.login(); + } + + public override void setArticleIsRead(string articleIDs, ArticleStatus read) + { + if(read == ArticleStatus.READ) { - m_api.editSubscription(OldReaderAPI.OldreaderSubscriptionAction.SUBSCRIBE, urls, null, cat); - urls = {}; - cat = f.getCatIDs()[0]; + m_api.edidTag(articleIDs, "user/-/state/com.google/read"); + } + else + { + m_api.edidTag(articleIDs, "user/-/state/com.google/read", false); } - - urls += "feed/" + f.getXmlUrl(); } - - m_api.editSubscription(OldReaderAPI.OldreaderSubscriptionAction.SUBSCRIBE, urls, null, cat); -} - -public override void removeFeed(string feedID) -{ - m_api.editSubscription(OldReaderAPI.OldreaderSubscriptionAction.UNSUBSCRIBE, {feedID}); -} - -public override void renameFeed(string feedID, string title) -{ - m_api.editSubscription(OldReaderAPI.OldreaderSubscriptionAction.EDIT, {feedID}, title); -} - -public override void moveFeed(string feedID, string newCatID, string? currentCatID) -{ - m_api.editSubscription(OldReaderAPI.OldreaderSubscriptionAction.EDIT, {feedID}, null, newCatID, currentCatID); -} - -public override string createCategory(string title, string? parentID) -{ - return m_api.composeTagID(title); -} - -public override void renameCategory(string catID, string title) -{ - m_api.renameTag(catID, title); -} - -public override void moveCategory(string catID, string newParentID) -{ - return; -} - -public override void deleteCategory(string catID) -{ - m_api.deleteTag(catID); -} - -public override void removeCatFromFeed(string feedID, string catID) -{ - return; -} - -public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) -{ - if(m_api.getFeeds(feeds)) + + public override void setArticleIsMarked(string articleID, ArticleStatus marked) { - if(cancellable != null && cancellable.is_cancelled()) + if(marked == ArticleStatus.MARKED) { - return false; + m_api.edidTag(articleID, "user/-/state/com.google/starred"); } - - if(m_api.getCategoriesAndTags(feeds, categories, tags)) + else { - return true; + m_api.edidTag(articleID, "user/-/state/com.google/starred", false); } } - - return false; -} - -public override int getUnreadCount() -{ - return m_api.getTotalUnread(); -} - -public override void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) -{ - if(whatToGet == ArticleStatus.READ) + + public override bool alwaysSetReadByID() { - return; + return false; } - else if(whatToGet == ArticleStatus.ALL) + + public override void setFeedRead(string feedID) { - var unreadIDs = new Gee.LinkedList<string>(); - string? continuation = null; - int left = 4*count; - - while(left > 0) + m_api.markAsRead(feedID); + } + + public override void setCategoryRead(string catID) + { + m_api.markAsRead(catID); + } + + public override void markAllItemsRead() + { + var db = DataBase.readOnly(); + var categories = db.read_categories(); + foreach(Category cat in categories) { - if(cancellable != null && cancellable.is_cancelled()) + m_api.markAsRead(cat.getCatID()); + } + + var feeds = db.read_feeds_without_cat(); + foreach(Feed feed in feeds) + { + m_api.markAsRead(feed.getFeedID()); + } + m_api.markAsRead(); + } + + public override void tagArticle(string articleID, string tagID) + { + m_api.edidTag(articleID, tagID, true); + } + + public override void removeArticleTag(string articleID, string tagID) + { + m_api.edidTag(articleID, tagID, false); + } + + public override string createTag(string caption) + { + return m_api.composeTagID(caption); + } + + public override void deleteTag(string tagID) + { + m_api.deleteTag(tagID); + } + + public override void renameTag(string tagID, string title) + { + m_api.renameTag(tagID, title); + } + + public override bool serverAvailable() + { + return m_api.ping(); + } + + public override bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) + { + feedID = "feed/" + feedURL; + errmsg = ""; + bool success = false; + + if(catID == null && newCatName != null) + { + string newCatID = m_api.composeTagID(newCatName); + success = m_api.editSubscription(OldReaderAPI.OldreaderSubscriptionAction.SUBSCRIBE, {"feed/"+feedURL}, null, newCatID); + } + else + { + success = m_api.editSubscription(OldReaderAPI.OldreaderSubscriptionAction.SUBSCRIBE, {"feed/"+feedURL}, null, catID); + } + + if(!success) + { + errmsg = @"The old reader could not add $feedURL"; + } + + return success; + } + + public override void addFeeds(Gee.List<Feed> feeds) + { + string cat = ""; + string[] urls = {}; + + foreach(Feed f in feeds) + { + if(f.getCatIDs()[0] != cat) { - return; + m_api.editSubscription(OldReaderAPI.OldreaderSubscriptionAction.SUBSCRIBE, urls, null, cat); + urls = {}; + cat = f.getCatIDs()[0]; } - - if(left > 1000) + + urls += "feed/" + f.getXmlUrl(); + } + + m_api.editSubscription(OldReaderAPI.OldreaderSubscriptionAction.SUBSCRIBE, urls, null, cat); + } + + public override void removeFeed(string feedID) + { + m_api.editSubscription(OldReaderAPI.OldreaderSubscriptionAction.UNSUBSCRIBE, {feedID}); + } + + public override void renameFeed(string feedID, string title) + { + m_api.editSubscription(OldReaderAPI.OldreaderSubscriptionAction.EDIT, {feedID}, title); + } + + public override void moveFeed(string feedID, string newCatID, string? currentCatID) + { + m_api.editSubscription(OldReaderAPI.OldreaderSubscriptionAction.EDIT, {feedID}, null, newCatID, currentCatID); + } + + public override string createCategory(string title, string? parentID) + { + return m_api.composeTagID(title); + } + + public override void renameCategory(string catID, string title) + { + m_api.renameTag(catID, title); + } + + public override void moveCategory(string catID, string newParentID) + { + return; + } + + public override void deleteCategory(string catID) + { + m_api.deleteTag(catID); + } + + public override void removeCatFromFeed(string feedID, string catID) + { + return; + } + + public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) + { + if(m_api.getFeeds(feeds)) + { + if(cancellable != null && cancellable.is_cancelled()) { - continuation = m_api.updateArticles(unreadIDs, 1000, continuation); - left -= 1000; + return false; } - else + + if(m_api.getCategoriesAndTags(feeds, categories, tags)) { - m_api.updateArticles(unreadIDs, left, continuation); - left = 0; + return true; } } - DataBase.writeAccess().updateArticlesByID(unreadIDs, "unread"); - updateArticleList(); + + return false; } - - var articles = new Gee.LinkedList<Article>(); - string? continuation = null; - int left = count; - string? OldReader_feedID = (isTagID) ? null : feedID; - string? OldReader_tagID = (isTagID) ? feedID : null; - - while(left > 0) + + public override int getUnreadCount() + { + return m_api.getTotalUnread(); + } + + public override void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) { - if(cancellable != null && cancellable.is_cancelled()) + if(whatToGet == ArticleStatus.READ) { return; } - - if(left > 1000) + else if(whatToGet == ArticleStatus.ALL) { - continuation = m_api.getArticles(articles, 1000, whatToGet, continuation, OldReader_tagID, OldReader_feedID); - left -= 1000; + var unreadIDs = new Gee.LinkedList<string>(); + string? continuation = null; + int left = 4*count; + + while(left > 0) + { + if(cancellable != null && cancellable.is_cancelled()) + { + return; + } + + if(left > 1000) + { + continuation = m_api.updateArticles(unreadIDs, 1000, continuation); + left -= 1000; + } + else + { + m_api.updateArticles(unreadIDs, left, continuation); + left = 0; + } + } + DataBase.writeAccess().updateArticlesByID(unreadIDs, "unread"); + updateArticleList(); } - else + + var articles = new Gee.LinkedList<Article>(); + string? continuation = null; + int left = count; + string? OldReader_feedID = (isTagID) ? null : feedID; + string? OldReader_tagID = (isTagID) ? feedID : null; + + while(left > 0) { - continuation = m_api.getArticles(articles, left, whatToGet, continuation, OldReader_tagID, OldReader_feedID); - left = 0; + if(cancellable != null && cancellable.is_cancelled()) + { + return; + } + + if(left > 1000) + { + continuation = m_api.getArticles(articles, 1000, whatToGet, continuation, OldReader_tagID, OldReader_feedID); + left -= 1000; + } + else + { + continuation = m_api.getArticles(articles, left, whatToGet, continuation, OldReader_tagID, OldReader_feedID); + left = 0; + } } + writeArticles(articles); } - writeArticles(articles); -} - + } [ModuleInit] diff --git a/plugins/backend/oldreader/oldreaderUtils.vala b/plugins/backend/oldreader/oldreaderUtils.vala index 1d58863f..1ae850ec 100644 --- a/plugins/backend/oldreader/oldreaderUtils.vala +++ b/plugins/backend/oldreader/oldreaderUtils.vala @@ -14,78 +14,78 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. namespace FeedReader.OldReaderSecret { -const string base_uri = "https://theoldreader.com/reader/api/0/"; + const string base_uri = "https://theoldreader.com/reader/api/0/"; } public class FeedReader.OldReaderUtils : GLib.Object { - -private GLib.Settings m_settings; -private Password m_password; - -public OldReaderUtils(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) -{ - if(settings_backend != null) - { - m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.oldreader", settings_backend); - } - else + + private GLib.Settings m_settings; + private Password m_password; + + public OldReaderUtils(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) { - m_settings = new GLib.Settings("org.gnome.feedreader.oldreader"); - } - - var pwSchema = new Secret.Schema ("org.gnome.feedreader.oldreader", Secret.SchemaFlags.NONE, - "type", "oldreader", - "Username", Secret.SchemaAttributeType.STRING); - m_password = new Password(secrets, pwSchema, "FeedReader: oldreader login", () => { + if(settings_backend != null) + { + m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.oldreader", settings_backend); + } + else + { + m_settings = new GLib.Settings("org.gnome.feedreader.oldreader"); + } + + var pwSchema = new Secret.Schema ("org.gnome.feedreader.oldreader", Secret.SchemaFlags.NONE, + "type", "oldreader", + "Username", Secret.SchemaAttributeType.STRING); + m_password = new Password(secrets, pwSchema, "FeedReader: oldreader login", () => { var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); attributes["Username"] = getUser(); return attributes; }); -} - -public string getUser() -{ - return Utils.gsettingReadString(m_settings, "username"); -} - -public void setUser(string user) -{ - Utils.gsettingWriteString(m_settings, "username", user); -} - -public string getAccessToken() -{ - return Utils.gsettingReadString(m_settings, "access-token"); -} - -public void setAccessToken(string token) -{ - Utils.gsettingWriteString(m_settings, "access-token", token); -} - -public string getUserID() -{ - return Utils.gsettingReadString(m_settings, "user-id"); -} - -public void setUserID(string id) -{ - Utils.gsettingWriteString(m_settings, "user-id", id); -} - -public void resetAccount() -{ - Utils.resetSettings(m_settings); - m_password.delete_password(); -} - -public string getPasswd() -{ - return m_password.get_password(); -} - -public void setPassword(string passwd) -{ - m_password.set_password(passwd); -} + } + + public string getUser() + { + return Utils.gsettingReadString(m_settings, "username"); + } + + public void setUser(string user) + { + Utils.gsettingWriteString(m_settings, "username", user); + } + + public string getAccessToken() + { + return Utils.gsettingReadString(m_settings, "access-token"); + } + + public void setAccessToken(string token) + { + Utils.gsettingWriteString(m_settings, "access-token", token); + } + + public string getUserID() + { + return Utils.gsettingReadString(m_settings, "user-id"); + } + + public void setUserID(string id) + { + Utils.gsettingWriteString(m_settings, "user-id", id); + } + + public void resetAccount() + { + Utils.resetSettings(m_settings); + m_password.delete_password(); + } + + public string getPasswd() + { + return m_password.get_password(); + } + + public void setPassword(string passwd) + { + m_password.set_password(passwd); + } } diff --git a/plugins/backend/owncloud/OwncloudNewsAPI.vala b/plugins/backend/owncloud/OwncloudNewsAPI.vala index 7993d5f4..fe3fc81e 100644 --- a/plugins/backend/owncloud/OwncloudNewsAPI.vala +++ b/plugins/backend/owncloud/OwncloudNewsAPI.vala @@ -14,578 +14,578 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.OwncloudNewsAPI : GLib.Object { - -public enum OwnCloudType { - FEED, - FOLDER, - STARRED, - ALL -} - -private string m_OwnCloudURL; -private string m_OwnCloudVersion; -private Json.Parser m_parser; -private string m_username; -private string m_password; -private OwncloudNewsUtils m_utils; -private Soup.Session m_session; - -public OwncloudNewsAPI(OwncloudNewsUtils utils) -{ - m_parser = new Json.Parser (); - m_utils = utils; - m_session = new Soup.Session(); - m_session.user_agent = Constants.USER_AGENT; - m_session.ssl_strict = false; - m_session.authenticate.connect((msg, auth, retrying) => { + + public enum OwnCloudType { + FEED, + FOLDER, + STARRED, + ALL + } + + private string m_OwnCloudURL; + private string m_OwnCloudVersion; + private Json.Parser m_parser; + private string m_username; + private string m_password; + private OwncloudNewsUtils m_utils; + private Soup.Session m_session; + + public OwncloudNewsAPI(OwncloudNewsUtils utils) + { + m_parser = new Json.Parser (); + m_utils = utils; + m_session = new Soup.Session(); + m_session.user_agent = Constants.USER_AGENT; + m_session.ssl_strict = false; + m_session.authenticate.connect((msg, auth, retrying) => { if(m_utils.getHtaccessUser() == "") { - Logger.error("Nextcloud Session: need Authentication"); + Logger.error("Nextcloud Session: need Authentication"); } else if(!retrying) { - auth.authenticate(m_utils.getHtaccessUser(), m_utils.getHtaccessPasswd()); + auth.authenticate(m_utils.getHtaccessUser(), m_utils.getHtaccessPasswd()); } }); -} - -public LoginResponse login() -{ - Logger.debug("Nextcloud: login"); - m_username = m_utils.getUser(); - m_password = m_utils.getPasswd(); - m_OwnCloudURL = m_utils.getURL(); - - if(m_OwnCloudURL == "" && m_username == "" && m_password == "") - { - m_OwnCloudURL = "example-host/nextcloud"; - return LoginResponse.ALL_EMPTY; - } - if(m_OwnCloudURL == "") - { - return LoginResponse.MISSING_URL; - } - if(GLib.Uri.parse_scheme(m_OwnCloudURL) == null) - { - return LoginResponse.INVALID_URL; - } - if(m_username == "") - { - return LoginResponse.MISSING_USER; - } - if(m_password == "") - { - return LoginResponse.MISSING_PASSWD; - } - - var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + "status", m_username, m_password, "GET"); - int error = message.send(); - - if(error == ConnectionError.SUCCESS) - { - var response = message.get_response_object(); - m_OwnCloudVersion = response.get_string_member("version"); - Logger.info("Nextcloud version: %s".printf(m_OwnCloudVersion)); - return LoginResponse.SUCCESS; } - else if(error == ConnectionError.API_ERROR) + + public LoginResponse login() { - return LoginResponse.WRONG_LOGIN; - } - else if(error == ConnectionError.NO_RESPONSE) - { - return LoginResponse.NO_CONNECTION; - } - else if(error == ConnectionError.CA_ERROR) - { - return LoginResponse.CA_ERROR; - } - else if(error == ConnectionError.UNAUTHORIZED) - { - return LoginResponse.UNAUTHORIZED; + Logger.debug("Nextcloud: login"); + m_username = m_utils.getUser(); + m_password = m_utils.getPasswd(); + m_OwnCloudURL = m_utils.getURL(); + + if(m_OwnCloudURL == "" && m_username == "" && m_password == "") + { + m_OwnCloudURL = "example-host/nextcloud"; + return LoginResponse.ALL_EMPTY; + } + if(m_OwnCloudURL == "") + { + return LoginResponse.MISSING_URL; + } + if(GLib.Uri.parse_scheme(m_OwnCloudURL) == null) + { + return LoginResponse.INVALID_URL; + } + if(m_username == "") + { + return LoginResponse.MISSING_USER; + } + if(m_password == "") + { + return LoginResponse.MISSING_PASSWD; + } + + var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + "status", m_username, m_password, "GET"); + int error = message.send(); + + if(error == ConnectionError.SUCCESS) + { + var response = message.get_response_object(); + m_OwnCloudVersion = response.get_string_member("version"); + Logger.info("Nextcloud version: %s".printf(m_OwnCloudVersion)); + return LoginResponse.SUCCESS; + } + else if(error == ConnectionError.API_ERROR) + { + return LoginResponse.WRONG_LOGIN; + } + else if(error == ConnectionError.NO_RESPONSE) + { + return LoginResponse.NO_CONNECTION; + } + else if(error == ConnectionError.CA_ERROR) + { + return LoginResponse.CA_ERROR; + } + else if(error == ConnectionError.UNAUTHORIZED) + { + return LoginResponse.UNAUTHORIZED; + } + + return LoginResponse.UNKNOWN_ERROR; } - - return LoginResponse.UNKNOWN_ERROR; -} - - -public bool isloggedin() -{ - var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + "version", m_username, m_password, "GET"); - - if(message.send() == ConnectionError.SUCCESS) + + + public bool isloggedin() { - return true; + var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + "version", m_username, m_password, "GET"); + + if(message.send() == ConnectionError.SUCCESS) + { + return true; + } + + Logger.error("OwncloudNewsAPI.isloggedin: not logged in"); + return false; } - - Logger.error("OwncloudNewsAPI.isloggedin: not logged in"); - return false; -} - -public bool getFeeds(Gee.List<Feed> feeds) -{ - if(isloggedin()) + + public bool getFeeds(Gee.List<Feed> feeds) { - var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + "feeds", m_username, m_password, "GET"); - int error = message.send(); - - if(error == ConnectionError.SUCCESS) + if(isloggedin()) { - var response = message.get_response_object(); - if(response.has_member("feeds")) + var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + "feeds", m_username, m_password, "GET"); + int error = message.send(); + + if(error == ConnectionError.SUCCESS) { - var feed_array = response.get_array_member("feeds"); - var feed_count = feed_array.get_length(); - - for(uint i = 0; i < feed_count; i++) + var response = message.get_response_object(); + if(response.has_member("feeds")) { - var feed_node = feed_array.get_object_element(i); - - feeds.add( - new Feed( - feed_node.get_int_member("id").to_string(), - feed_node.get_string_member("title"), - feed_node.get_string_member("link"), - (int)feed_node.get_int_member("unreadCount"), - ListUtils.single(feed_node.get_int_member("folderId").to_string()), - feed_node.get_string_member("faviconLink") + var feed_array = response.get_array_member("feeds"); + var feed_count = feed_array.get_length(); + + for(uint i = 0; i < feed_count; i++) + { + var feed_node = feed_array.get_object_element(i); + + feeds.add( + new Feed( + feed_node.get_int_member("id").to_string(), + feed_node.get_string_member("title"), + feed_node.get_string_member("link"), + (int)feed_node.get_int_member("unreadCount"), + ListUtils.single(feed_node.get_int_member("folderId").to_string()), + feed_node.get_string_member("faviconLink") ) ); + } + + return true; + } + else + { + Logger.error("OwncloudNewsAPI.getFeeds: no member \"feeds\""); } - - return true; } else { - Logger.error("OwncloudNewsAPI.getFeeds: no member \"feeds\""); + Logger.error("OwncloudNewsAPI.getFeeds"); } } - else - { - Logger.error("OwncloudNewsAPI.getFeeds"); - } + + return false; } - - return false; -} - - -public bool getCategories(Gee.List<Category> categories, Gee.List<Feed> feeds) -{ - if(isloggedin()) + + + public bool getCategories(Gee.List<Category> categories, Gee.List<Feed> feeds) { - var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + "folders", m_username, m_password, "GET"); - int error = message.send(); - int orderID = 0; - - if(error == ConnectionError.SUCCESS) + if(isloggedin()) { - var response = message.get_response_object(); - - if(response.has_member("folders")) + var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + "folders", m_username, m_password, "GET"); + int error = message.send(); + int orderID = 0; + + if(error == ConnectionError.SUCCESS) { - var folder_array = response.get_array_member("folders"); - var folder_count = folder_array.get_length(); - - for(uint i = 0; i < folder_count; i++) + var response = message.get_response_object(); + + if(response.has_member("folders")) { - ++orderID; - var folder_node = folder_array.get_object_element(i); - string id = folder_node.get_int_member("id").to_string(); - - categories.add( - new Category ( - id, - folder_node.get_string_member("name"), - m_utils.countUnread(feeds, id), - orderID, - CategoryID.MASTER.to_string(), - 1 + var folder_array = response.get_array_member("folders"); + var folder_count = folder_array.get_length(); + + for(uint i = 0; i < folder_count; i++) + { + ++orderID; + var folder_node = folder_array.get_object_element(i); + string id = folder_node.get_int_member("id").to_string(); + + categories.add( + new Category ( + id, + folder_node.get_string_member("name"), + m_utils.countUnread(feeds, id), + orderID, + CategoryID.MASTER.to_string(), + 1 ) ); + } + return true; + } + else + { + Logger.error("OwncloudNewsAPI.getCategories: no member \"folders\""); } - return true; } else { - Logger.error("OwncloudNewsAPI.getCategories: no member \"folders\""); + Logger.error("OwncloudNewsAPI.getCategories"); } } - else - { - Logger.error("OwncloudNewsAPI.getCategories"); - } + return false; } - return false; -} - - -public void getNewArticles(Gee.List<Article> articles, int lastModified, OwnCloudType type, int id) -{ - var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + "items/updated", m_username, m_password, "GET"); - message.add_int("lastModified", lastModified); - message.add_int("type", type); - message.add_int("id", id); - int error = message.send(); - - if(error == ConnectionError.SUCCESS) + + + public void getNewArticles(Gee.List<Article> articles, int lastModified, OwnCloudType type, int id) { - var response = message.get_response_object(); - if(response.has_member("items")) + var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + "items/updated", m_username, m_password, "GET"); + message.add_int("lastModified", lastModified); + message.add_int("type", type); + message.add_int("id", id); + int error = message.send(); + + if(error == ConnectionError.SUCCESS) { - var article_array = response.get_array_member("items"); - var article_count = article_array.get_length(); - Logger.debug("getNewArticles: %u articles returned".printf(article_count)); - - for(uint i = 0; i < article_count; i++) + var response = message.get_response_object(); + if(response.has_member("items")) { - var article_node = article_array.get_object_element(i); - //Logger.debug(article_node.get_int_member("id").to_string()); - - ArticleStatus unread = article_node.get_boolean_member("unread") ? ArticleStatus.UNREAD : ArticleStatus.READ; - ArticleStatus marked = article_node.get_boolean_member("starred") ? ArticleStatus.MARKED : ArticleStatus.UNMARKED; - - var enclosures = new Gee.ArrayList<Enclosure>(); - if(article_node.has_member("enclosureLink") && article_node.get_string_member("enclosureLink") != null) + var article_array = response.get_array_member("items"); + var article_count = article_array.get_length(); + Logger.debug("getNewArticles: %u articles returned".printf(article_count)); + + for(uint i = 0; i < article_count; i++) { - if(article_node.has_member("enclosureMime") && article_node.get_string_member("enclosureMime") != null) + var article_node = article_array.get_object_element(i); + //Logger.debug(article_node.get_int_member("id").to_string()); + + ArticleStatus unread = article_node.get_boolean_member("unread") ? ArticleStatus.UNREAD : ArticleStatus.READ; + ArticleStatus marked = article_node.get_boolean_member("starred") ? ArticleStatus.MARKED : ArticleStatus.UNMARKED; + + var enclosures = new Gee.ArrayList<Enclosure>(); + if(article_node.has_member("enclosureLink") && article_node.get_string_member("enclosureLink") != null) { - enclosures.add(new Enclosure( - article_node.get_int_member("id").to_string(), - article_node.get_string_member("enclosureLink"), - EnclosureType.from_string(article_node.get_string_member("enclosureMime")))); + if(article_node.has_member("enclosureMime") && article_node.get_string_member("enclosureMime") != null) + { + enclosures.add(new Enclosure( + article_node.get_int_member("id").to_string(), + article_node.get_string_member("enclosureLink"), + EnclosureType.from_string(article_node.get_string_member("enclosureMime")))); + } } + + var Article = new Article( article_node.get_int_member("id").to_string(), + article_node.get_string_member("title"), + article_node.get_string_member("url"), + article_node.get_int_member("feedId").to_string(), + unread, + marked, + article_node.get_string_member("body"), + null, + article_node.get_string_member("author"), + new DateTime.from_unix_local(article_node.get_int_member("pubDate")), + -1, + null, // tags + enclosures, + article_node.get_string_member("guidHash"), + (int)article_node.get_int_member("lastModified")); + + articles.add(Article); } - - var Article = new Article( article_node.get_int_member("id").to_string(), - article_node.get_string_member("title"), - article_node.get_string_member("url"), - article_node.get_int_member("feedId").to_string(), - unread, - marked, - article_node.get_string_member("body"), - null, - article_node.get_string_member("author"), - new DateTime.from_unix_local(article_node.get_int_member("pubDate")), - -1, - null, // tags - enclosures, - article_node.get_string_member("guidHash"), - (int)article_node.get_int_member("lastModified")); - - articles.add(Article); + } + else + { + Logger.error("OwncloudNewsAPI.getNewArticles: no member \"items\""); } } else { - Logger.error("OwncloudNewsAPI.getNewArticles: no member \"items\""); + Logger.error("OwncloudNewsAPI.getNewArticles"); } } - else - { - Logger.error("OwncloudNewsAPI.getNewArticles"); - } -} - - - -public void getArticles(Gee.List<Article> articles, int skip, int count, bool read, OwnCloudType type, int id) -{ - var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + "items", m_username, m_password, "GET"); - message.add_bool("oldestFirst", false); - message.add_int("type", type); - message.add_bool("getRead", read); - message.add_int("id", id); - message.add_int("offset", skip); - message.add_int("batchSize", count); - int error = message.send(); - - if(error == ConnectionError.SUCCESS) - { - var response = message.get_response_object(); - if(response.has_member("items")) + + + + public void getArticles(Gee.List<Article> articles, int skip, int count, bool read, OwnCloudType type, int id) + { + var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + "items", m_username, m_password, "GET"); + message.add_bool("oldestFirst", false); + message.add_int("type", type); + message.add_bool("getRead", read); + message.add_int("id", id); + message.add_int("offset", skip); + message.add_int("batchSize", count); + int error = message.send(); + + if(error == ConnectionError.SUCCESS) { - var article_array = response.get_array_member("items"); - var article_count = article_array.get_length(); - Logger.debug("getArticles: %u articles returned".printf(article_count)); - - for(uint i = 0; i < article_count; i++) + var response = message.get_response_object(); + if(response.has_member("items")) { - var article_node = article_array.get_object_element(i); - - ArticleStatus unread = article_node.get_boolean_member("unread") ? ArticleStatus.UNREAD : ArticleStatus.READ; - ArticleStatus marked = article_node.get_boolean_member("starred") ? ArticleStatus.MARKED : ArticleStatus.UNMARKED; - - var enclosures = new Gee.ArrayList<Enclosure>(); - if(article_node.has_member("enclosureLink") && article_node.get_string_member("enclosureLink") != null) + var article_array = response.get_array_member("items"); + var article_count = article_array.get_length(); + Logger.debug("getArticles: %u articles returned".printf(article_count)); + + for(uint i = 0; i < article_count; i++) { - if(article_node.has_member("enclosureMime") && article_node.get_string_member("enclosureMime") != null) + var article_node = article_array.get_object_element(i); + + ArticleStatus unread = article_node.get_boolean_member("unread") ? ArticleStatus.UNREAD : ArticleStatus.READ; + ArticleStatus marked = article_node.get_boolean_member("starred") ? ArticleStatus.MARKED : ArticleStatus.UNMARKED; + + var enclosures = new Gee.ArrayList<Enclosure>(); + if(article_node.has_member("enclosureLink") && article_node.get_string_member("enclosureLink") != null) { - enclosures.add(new Enclosure( - article_node.get_int_member("id").to_string(), - article_node.get_string_member("enclosureLink"), - EnclosureType.from_string(article_node.get_string_member("enclosureMime")))); + if(article_node.has_member("enclosureMime") && article_node.get_string_member("enclosureMime") != null) + { + enclosures.add(new Enclosure( + article_node.get_int_member("id").to_string(), + article_node.get_string_member("enclosureLink"), + EnclosureType.from_string(article_node.get_string_member("enclosureMime")))); + } } + + var Article = new Article( article_node.get_int_member("id").to_string(), + article_node.get_string_member("title"), + article_node.get_string_member("url"), + article_node.get_int_member("feedId").to_string(), + unread, + marked, + article_node.get_string_member("body"), + null, + article_node.get_string_member("author"), + new DateTime.from_unix_local(article_node.get_int_member("pubDate")), + -1, + null, // tags + enclosures, + article_node.get_string_member("guidHash"), + (int)article_node.get_int_member("lastModified")); + + articles.add(Article); } - - var Article = new Article( article_node.get_int_member("id").to_string(), - article_node.get_string_member("title"), - article_node.get_string_member("url"), - article_node.get_int_member("feedId").to_string(), - unread, - marked, - article_node.get_string_member("body"), - null, - article_node.get_string_member("author"), - new DateTime.from_unix_local(article_node.get_int_member("pubDate")), - -1, - null, // tags - enclosures, - article_node.get_string_member("guidHash"), - (int)article_node.get_int_member("lastModified")); - - articles.add(Article); + } + else + { + Logger.error("OwncloudNewsAPI.getArticles: no member \"items\""); } } else { - Logger.error("OwncloudNewsAPI.getArticles: no member \"items\""); + Logger.error("OwncloudNewsAPI.getArticles"); } } - else + + + public bool markFeedRead(string feedID, bool isCatID) { - Logger.error("OwncloudNewsAPI.getArticles"); - } -} - - -public bool markFeedRead(string feedID, bool isCatID) -{ - string url = "%s/%s/read".printf((isCatID) ? "folders" : "feeds", feedID); - var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "PUT"); - message.add_int("newestItemId", int.parse(DataBase.readOnly().getNewestArticle())); - int error = message.send(); - - if(error == ConnectionError.SUCCESS) - { - return true; - } - - Logger.error("OwncloudNewsAPI.markFeedRead"); - return false; -} - -public bool markAllItemsRead() -{ - string url = "items/read"; - var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "PUT"); - message.add_int("newestItemId", int.parse(DataBase.readOnly().getNewestArticle())); - int error = message.send(); - - if(error == ConnectionError.SUCCESS) - { - return true; - } - - Logger.error("OwncloudNewsAPI.markAllItemsRead"); - return false; -} - - -public bool updateArticleUnread(string articleIDs, ArticleStatus unread) -{ - string url = ""; - - if(unread == ArticleStatus.UNREAD) - { - url = "items/unread/multiple"; - } - else if(unread == ArticleStatus.READ) - { - url = "items/read/multiple"; - } - - var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "PUT"); - message.add_int_array("items", articleIDs); - int error = message.send(); - - if(error == ConnectionError.SUCCESS) - { - return true; - } - - Logger.error("OwncloudNewsAPI.updateArticleUnread"); - return false; -} - - -public bool updateArticleMarked(string articleID, ArticleStatus marked) -{ - var article = DataBase.readOnly().read_article(articleID); - string url = "items/%s/%s/".printf(article.getFeedID(), article.getHash()); - - if(marked == ArticleStatus.MARKED) - { - url += "star"; + string url = "%s/%s/read".printf((isCatID) ? "folders" : "feeds", feedID); + var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "PUT"); + message.add_int("newestItemId", int.parse(DataBase.readOnly().getNewestArticle())); + int error = message.send(); + + if(error == ConnectionError.SUCCESS) + { + return true; + } + + Logger.error("OwncloudNewsAPI.markFeedRead"); + return false; } - else if(marked == ArticleStatus.UNMARKED) + + public bool markAllItemsRead() { - url += "unstar"; + string url = "items/read"; + var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "PUT"); + message.add_int("newestItemId", int.parse(DataBase.readOnly().getNewestArticle())); + int error = message.send(); + + if(error == ConnectionError.SUCCESS) + { + return true; + } + + Logger.error("OwncloudNewsAPI.markAllItemsRead"); + return false; } - - var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "PUT"); - int error = message.send(); - - if(error == ConnectionError.SUCCESS) + + + public bool updateArticleUnread(string articleIDs, ArticleStatus unread) { - return true; + string url = ""; + + if(unread == ArticleStatus.UNREAD) + { + url = "items/unread/multiple"; + } + else if(unread == ArticleStatus.READ) + { + url = "items/read/multiple"; + } + + var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "PUT"); + message.add_int_array("items", articleIDs); + int error = message.send(); + + if(error == ConnectionError.SUCCESS) + { + return true; + } + + Logger.error("OwncloudNewsAPI.updateArticleUnread"); + return false; } - - Logger.error("OwncloudNewsAPI.updateArticleMarked"); - return false; -} - -public bool addFeed(string feedURL, string? catID, out int64 feedID, out string errmsg) -{ - string url = "feeds"; - var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "POST"); - message.add_string("url", feedURL); - message.add_int("folderId", (catID != null) ? int.parse(catID) : 0); - int error = message.send(); - - if(error == ConnectionError.SUCCESS) + + + public bool updateArticleMarked(string articleID, ArticleStatus marked) { - var response = message.get_response_object(); - if(response.has_member("feeds")) + var article = DataBase.readOnly().read_article(articleID); + string url = "items/%s/%s/".printf(article.getFeedID(), article.getHash()); + + if(marked == ArticleStatus.MARKED) + { + url += "star"; + } + else if(marked == ArticleStatus.UNMARKED) + { + url += "unstar"; + } + + var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "PUT"); + int error = message.send(); + + if(error == ConnectionError.SUCCESS) { - errmsg = ""; - feedID = response.get_array_member("feeds").get_object_element(0).get_int_member("id"); return true; } + + Logger.error("OwncloudNewsAPI.updateArticleMarked"); + return false; } - else + + public bool addFeed(string feedURL, string? catID, out int64 feedID, out string errmsg) { - Logger.error("OwncloudNewsAPI.addFeed"); + string url = "feeds"; + var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "POST"); + message.add_string("url", feedURL); + message.add_int("folderId", (catID != null) ? int.parse(catID) : 0); + int error = message.send(); + + if(error == ConnectionError.SUCCESS) + { + var response = message.get_response_object(); + if(response.has_member("feeds")) + { + errmsg = ""; + feedID = response.get_array_member("feeds").get_object_element(0).get_int_member("id"); + return true; + } + } + else + { + Logger.error("OwncloudNewsAPI.addFeed"); + } + + + errmsg = "Nextcloud could not add the feed"; + feedID = 0; + + switch(message.getStatusCode()) + { + case 409: + errmsg = "Feed already added (409)"; + return true; + case 422: + errmsg = "Nextcloud can't read the feed (422)"; + break; + } + + return false; } - - - errmsg = "Nextcloud could not add the feed"; - feedID = 0; - - switch(message.getStatusCode()) + + public void removeFeed(string feedID) { - case 409: - errmsg = "Feed already added (409)"; - return true; - case 422: - errmsg = "Nextcloud can't read the feed (422)"; - break; + string url = "feeds/%s".printf(feedID); + var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "DELETE"); + int error = message.send(); + + if(error != ConnectionError.SUCCESS) + { + Logger.error("OwncloudNewsAPI.removeFeed"); + } } - - return false; -} - -public void removeFeed(string feedID) -{ - string url = "feeds/%s".printf(feedID); - var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "DELETE"); - int error = message.send(); - - if(error != ConnectionError.SUCCESS) + + public void renameFeed(string feedID, string title) { - Logger.error("OwncloudNewsAPI.removeFeed"); + string url = "feeds/%s/rename".printf(feedID); + var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "PUT"); + message.add_string("feedTitle", title); + int error = message.send(); + + if(error != ConnectionError.SUCCESS) + { + Logger.error("OwncloudNewsAPI.renameFeed"); + } } -} - -public void renameFeed(string feedID, string title) -{ - string url = "feeds/%s/rename".printf(feedID); - var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "PUT"); - message.add_string("feedTitle", title); - int error = message.send(); - - if(error != ConnectionError.SUCCESS) + + public void moveFeed(string feedID, string? newCatID = null) { - Logger.error("OwncloudNewsAPI.renameFeed"); + string url = "feeds/%s/move".printf(feedID); + var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "PUT"); + message.add_int("folderId", (newCatID != null) ? int.parse(newCatID) : 0); + int error = message.send(); + + if(error != ConnectionError.SUCCESS) + { + Logger.error("OwncloudNewsAPI.moveFeed"); + } } -} - -public void moveFeed(string feedID, string? newCatID = null) -{ - string url = "feeds/%s/move".printf(feedID); - var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "PUT"); - message.add_int("folderId", (newCatID != null) ? int.parse(newCatID) : 0); - int error = message.send(); - - if(error != ConnectionError.SUCCESS) + + public int64 addFolder(string title) { - Logger.error("OwncloudNewsAPI.moveFeed"); + string url = "folders"; + var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "POST"); + message.add_string("name", title); + int error = message.send(); + + if(error != ConnectionError.SUCCESS) + { + var response = message.get_response_object(); + if(response.has_member("folders")) + { + return response.get_array_member("folders").get_object_element(0).get_int_member("id"); + } + } + else + { + Logger.error("OwncloudNewsAPI.addFolder"); + } + + return 0; } -} - -public int64 addFolder(string title) -{ - string url = "folders"; - var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "POST"); - message.add_string("name", title); - int error = message.send(); - - if(error != ConnectionError.SUCCESS) + + public bool removeFolder(string catID) { - var response = message.get_response_object(); - if(response.has_member("folders")) + string url = "folders/%s".printf(catID); + + var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "DELETE"); + int error = message.send(); + + if(error == ConnectionError.SUCCESS) { - return response.get_array_member("folders").get_object_element(0).get_int_member("id"); + return true; } + + Logger.error("OwncloudNewsAPI.removeFolder"); + return false; } - else + + public void renameCategory(string catID, string title) { - Logger.error("OwncloudNewsAPI.addFolder"); + string url = "folders/%s".printf(catID); + var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "PUT"); + message.add_string("name", title); + int error = message.send(); + + if(error != ConnectionError.SUCCESS) + { + Logger.error("OwncloudNewsAPI.renameCategory"); + } } - - return 0; -} - -public bool removeFolder(string catID) -{ - string url = "folders/%s".printf(catID); - - var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "DELETE"); - int error = message.send(); - - if(error == ConnectionError.SUCCESS) + + public bool ping() { + var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + "version", m_username, m_password, "GET"); + int error = message.send(true); + + if(error == ConnectionError.NO_RESPONSE) + { + Logger.error("OwncloudNewsAPI.ping: failed"); + return false; + } + return true; } - - Logger.error("OwncloudNewsAPI.removeFolder"); - return false; -} - -public void renameCategory(string catID, string title) -{ - string url = "folders/%s".printf(catID); - var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + url, m_username, m_password, "PUT"); - message.add_string("name", title); - int error = message.send(); - - if(error != ConnectionError.SUCCESS) - { - Logger.error("OwncloudNewsAPI.renameCategory"); - } -} - -public bool ping() -{ - var message = new OwnCloudNewsMessage(m_session, m_OwnCloudURL + "version", m_username, m_password, "GET"); - int error = message.send(true); - - if(error == ConnectionError.NO_RESPONSE) - { - Logger.error("OwncloudNewsAPI.ping: failed"); - return false; - } - - return true; -} } diff --git a/plugins/backend/owncloud/OwncloudNewsInterface.vala b/plugins/backend/owncloud/OwncloudNewsInterface.vala index a75f35cd..6a9df88d 100644 --- a/plugins/backend/owncloud/OwncloudNewsInterface.vala +++ b/plugins/backend/owncloud/OwncloudNewsInterface.vala @@ -14,444 +14,444 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.OwncloudNewsInterface : FeedServerInterface { - -private OwncloudNewsAPI m_api; -private OwncloudNewsUtils m_utils; -private Gtk.Entry m_urlEntry; -private Gtk.Entry m_userEntry; -private Gtk.Entry m_passwordEntry; -private Gtk.Entry m_AuthUserEntry; -private Gtk.Entry m_AuthPasswordEntry; -private Gtk.Revealer m_revealer; -private bool m_need_htaccess = false; - -public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) -{ - m_utils = new OwncloudNewsUtils(settings_backend, secrets); - m_api = new OwncloudNewsAPI(m_utils); -} - -public override string getWebsite() -{ - return "https://github.com/nextcloud/news"; -} - -public override BackendFlags getFlags() -{ - return (BackendFlags.SELF_HOSTED | BackendFlags.FREE_SOFTWARE | BackendFlags.FREE); -} - -public override string getID() -{ - return "owncloud"; -} - -public override string iconName() -{ - return "feed-service-nextcloud"; -} - -public override string serviceName() -{ - return "Nextcloud News"; -} - -public override void writeData() -{ - m_utils.setURL(m_urlEntry.get_text()); - m_utils.setUser(m_userEntry.get_text().strip()); - m_utils.setPassword(m_passwordEntry.get_text().strip()); - if(m_need_htaccess) + + private OwncloudNewsAPI m_api; + private OwncloudNewsUtils m_utils; + private Gtk.Entry m_urlEntry; + private Gtk.Entry m_userEntry; + private Gtk.Entry m_passwordEntry; + private Gtk.Entry m_AuthUserEntry; + private Gtk.Entry m_AuthPasswordEntry; + private Gtk.Revealer m_revealer; + private bool m_need_htaccess = false; + + public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) { - m_utils.setHtaccessUser(m_AuthUserEntry.get_text().strip()); - m_utils.setHtAccessPassword(m_AuthPasswordEntry.get_text().strip()); + m_utils = new OwncloudNewsUtils(settings_backend, secrets); + m_api = new OwncloudNewsAPI(m_utils); } -} - -public override void showHtAccess() -{ - m_revealer.set_reveal_child(true); -} - -public override bool needWebLogin() -{ - return false; -} - -public override Gtk.Box? getWidget() -{ - var urlLabel = new Gtk.Label(_("Nextcloud URL:")); - var userLabel = new Gtk.Label(_("Username:")); - var passwordLabel = new Gtk.Label(_("Password:")); - - urlLabel.set_alignment(1.0f, 0.5f); - userLabel.set_alignment(1.0f, 0.5f); - passwordLabel.set_alignment(1.0f, 0.5f); - - urlLabel.set_hexpand(true); - userLabel.set_hexpand(true); - passwordLabel.set_hexpand(true); - - m_urlEntry = new Gtk.Entry(); - m_userEntry = new Gtk.Entry(); - m_passwordEntry = new Gtk.Entry(); - - m_urlEntry.activate.connect(writeData); - m_userEntry.activate.connect(writeData); - m_passwordEntry.activate.connect(writeData); - - m_passwordEntry.set_input_purpose(Gtk.InputPurpose.PASSWORD); - m_passwordEntry.set_visibility(false); - - var grid = new Gtk.Grid(); - grid.set_column_spacing(10); - grid.set_row_spacing(10); - grid.set_valign(Gtk.Align.CENTER); - grid.set_halign(Gtk.Align.CENTER); - - var logo = new Gtk.Image.from_icon_name("feed-service-nextcloud", Gtk.IconSize.MENU); - - grid.attach(urlLabel, 0, 0, 1, 1); - grid.attach(m_urlEntry, 1, 0, 1, 1); - grid.attach(userLabel, 0, 1, 1, 1); - grid.attach(m_userEntry, 1, 1, 1, 1); - grid.attach(passwordLabel, 0, 2, 1, 1); - grid.attach(m_passwordEntry, 1, 2, 1, 1); - - // http auth stuff ---------------------------------------------------- - var authUserLabel = new Gtk.Label(_("Username:")); - var authPasswordLabel = new Gtk.Label(_("Password:")); - - authUserLabel.set_alignment(1.0f, 0.5f); - authPasswordLabel.set_alignment(1.0f, 0.5f); - - authUserLabel.set_hexpand(true); - authPasswordLabel.set_hexpand(true); - - m_AuthUserEntry = new Gtk.Entry(); - m_AuthPasswordEntry = new Gtk.Entry(); - m_AuthPasswordEntry.set_input_purpose(Gtk.InputPurpose.PASSWORD); - m_AuthPasswordEntry.set_visibility(false); - - m_AuthUserEntry.activate.connect(writeData); - m_AuthPasswordEntry.activate.connect(writeData); - - var authGrid = new Gtk.Grid(); - authGrid.margin = 10; - authGrid.set_column_spacing(10); - authGrid.set_row_spacing(10); - authGrid.set_valign(Gtk.Align.CENTER); - authGrid.set_halign(Gtk.Align.CENTER); - - authGrid.attach(authUserLabel, 0, 0, 1, 1); - authGrid.attach(m_AuthUserEntry, 1, 0, 1, 1); - authGrid.attach(authPasswordLabel, 0, 1, 1, 1); - authGrid.attach(m_AuthPasswordEntry, 1, 1, 1, 1); - - var frame = new Gtk.Frame(_("HTTP Authorization")); - frame.set_halign(Gtk.Align.CENTER); - frame.add(authGrid); - m_revealer = new Gtk.Revealer(); - m_revealer.add(frame); - //--------------------------------------------------------------------- - - var loginLabel = new Gtk.Label(_("Please log in to your Nextcloud News instance and enjoy using FeedReader")); - loginLabel.get_style_context().add_class("h2"); - loginLabel.set_justify(Gtk.Justification.CENTER); - loginLabel.set_lines(3); - - var loginButton = new Gtk.Button.with_label(_("Login")); - loginButton.halign = Gtk.Align.END; - loginButton.set_size_request(80, 30); - loginButton.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); - loginButton.clicked.connect(() => { tryLogin(); }); - - var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10); - box.valign = Gtk.Align.CENTER; - box.halign = Gtk.Align.CENTER; - box.pack_start(loginLabel, false, false, 10); - box.pack_start(logo, false, false, 10); - box.pack_start(grid, true, true, 10); - box.pack_start(m_revealer, true, true, 10); - box.pack_end(loginButton, false, false, 20); - - - m_urlEntry.set_text(m_utils.getUnmodifiedURL()); - m_userEntry.set_text(m_utils.getUser()); - m_passwordEntry.set_text(m_utils.getPasswd()); - - return box; -} - -public override bool supportTags() -{ - return false; -} - -public override bool doInitSync() -{ - return true; -} - -public override string symbolicIcon() -{ - return "feed-service-nextcloud-symbolic"; -} - -public override string accountName() -{ - return m_utils.getUser(); -} - -public override string getServerURL() -{ - return m_utils.getURL(); -} - -public override string uncategorizedID() -{ - return "0"; -} - -public override bool hideCategoryWhenEmpty(string cadID) -{ - return false; -} - -public override bool supportCategories() -{ - return true; -} - -public override bool supportFeedManipulation() -{ - return true; -} - -public override bool supportMultiLevelCategories() -{ - return false; -} - -public override bool supportMultiCategoriesPerFeed() -{ - return false; -} - -public override bool syncFeedsAndCategories() -{ - return true; -} - -public override bool tagIDaffectedByNameChange() -{ - return false; -} - -public override void resetAccount() -{ - m_utils.resetAccount(); -} - -public override bool useMaxArticles() -{ - return false; -} - -public override LoginResponse login() -{ - return m_api.login(); -} - -public override void setArticleIsRead(string articleIDs, ArticleStatus read) -{ - m_api.updateArticleUnread(articleIDs, read); -} - -public override void setArticleIsMarked(string articleID, ArticleStatus marked) -{ - m_api.updateArticleMarked(articleID, marked); -} - -public override bool alwaysSetReadByID() -{ - return false; -} - -public override void setFeedRead(string feedID) -{ - m_api.markFeedRead(feedID, false); -} - -public override void setCategoryRead(string catID) -{ - m_api.markFeedRead(catID, true); -} - -public override void markAllItemsRead() -{ - m_api.markAllItemsRead(); -} - -public override void tagArticle(string articleID, string tagID) -{ - return; -} - -public override void removeArticleTag(string articleID, string tagID) -{ - return; -} - -public override string createTag(string caption) -{ - return ":("; -} - -public override void deleteTag(string tagID) -{ - return; -} - -public override void renameTag(string tagID, string title) -{ - return; -} - -public override bool serverAvailable() -{ - return m_api.ping(); -} - -public override bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) -{ - bool success = false; - int64 id = 0; - if(catID == null && newCatName != null) + + public override string getWebsite() { - string newCatID = m_api.addFolder(newCatName).to_string(); - success = m_api.addFeed(feedURL, newCatID, out id, out errmsg); + return "https://github.com/nextcloud/news"; } - else + + public override BackendFlags getFlags() { - success = m_api.addFeed(feedURL, catID, out id, out errmsg); + return (BackendFlags.SELF_HOSTED | BackendFlags.FREE_SOFTWARE | BackendFlags.FREE); } - - - feedID = id.to_string(); - return success; -} - -public override void removeFeed(string feedID) -{ - m_api.removeFeed(feedID); -} - -public override void renameFeed(string feedID, string title) -{ - m_api.renameFeed(feedID, title); -} - -public override void moveFeed(string feedID, string newCatID, string? currentCatID) -{ - m_api.moveFeed(feedID, newCatID); -} - -public override string createCategory(string title, string? parentID) -{ - return m_api.addFolder(title).to_string(); -} - -public override void renameCategory(string catID, string title) -{ - m_api.renameCategory(catID, title); -} - -public override void moveCategory(string catID, string newParentID) -{ - return; -} - -public override void deleteCategory(string catID) -{ - m_api.removeFolder(catID); -} - -public override void removeCatFromFeed(string feedID, string catID) -{ - return; -} - -public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) -{ - if(m_api.getFeeds(feeds)) + + public override string getID() { - if(cancellable != null && cancellable.is_cancelled()) - { - return false; - } - - if(m_api.getCategories(categories, feeds)) + return "owncloud"; + } + + public override string iconName() + { + return "feed-service-nextcloud"; + } + + public override string serviceName() + { + return "Nextcloud News"; + } + + public override void writeData() + { + m_utils.setURL(m_urlEntry.get_text()); + m_utils.setUser(m_userEntry.get_text().strip()); + m_utils.setPassword(m_passwordEntry.get_text().strip()); + if(m_need_htaccess) { - return true; + m_utils.setHtaccessUser(m_AuthUserEntry.get_text().strip()); + m_utils.setHtAccessPassword(m_AuthPasswordEntry.get_text().strip()); } } - - return false; -} - -public override int getUnreadCount() -{ - return (int)DataBase.readOnly().get_unread_total(); -} - -public override void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) -{ - var type = OwncloudNewsAPI.OwnCloudType.ALL; - bool read = true; - int id = 0; - - switch(whatToGet) + + public override void showHtAccess() { - case ArticleStatus.ALL: - break; - case ArticleStatus.UNREAD: - read = false; - break; - case ArticleStatus.MARKED: - type = OwncloudNewsAPI.OwnCloudType.STARRED; - break; + m_revealer.set_reveal_child(true); } - - if(feedID != null) + + public override bool needWebLogin() + { + return false; + } + + public override Gtk.Box? getWidget() { - if(isTagID == true) + var urlLabel = new Gtk.Label(_("Nextcloud URL:")); + var userLabel = new Gtk.Label(_("Username:")); + var passwordLabel = new Gtk.Label(_("Password:")); + + urlLabel.set_alignment(1.0f, 0.5f); + userLabel.set_alignment(1.0f, 0.5f); + passwordLabel.set_alignment(1.0f, 0.5f); + + urlLabel.set_hexpand(true); + userLabel.set_hexpand(true); + passwordLabel.set_hexpand(true); + + m_urlEntry = new Gtk.Entry(); + m_userEntry = new Gtk.Entry(); + m_passwordEntry = new Gtk.Entry(); + + m_urlEntry.activate.connect(writeData); + m_userEntry.activate.connect(writeData); + m_passwordEntry.activate.connect(writeData); + + m_passwordEntry.set_input_purpose(Gtk.InputPurpose.PASSWORD); + m_passwordEntry.set_visibility(false); + + var grid = new Gtk.Grid(); + grid.set_column_spacing(10); + grid.set_row_spacing(10); + grid.set_valign(Gtk.Align.CENTER); + grid.set_halign(Gtk.Align.CENTER); + + var logo = new Gtk.Image.from_icon_name("feed-service-nextcloud", Gtk.IconSize.MENU); + + grid.attach(urlLabel, 0, 0, 1, 1); + grid.attach(m_urlEntry, 1, 0, 1, 1); + grid.attach(userLabel, 0, 1, 1, 1); + grid.attach(m_userEntry, 1, 1, 1, 1); + grid.attach(passwordLabel, 0, 2, 1, 1); + grid.attach(m_passwordEntry, 1, 2, 1, 1); + + // http auth stuff ---------------------------------------------------- + var authUserLabel = new Gtk.Label(_("Username:")); + var authPasswordLabel = new Gtk.Label(_("Password:")); + + authUserLabel.set_alignment(1.0f, 0.5f); + authPasswordLabel.set_alignment(1.0f, 0.5f); + + authUserLabel.set_hexpand(true); + authPasswordLabel.set_hexpand(true); + + m_AuthUserEntry = new Gtk.Entry(); + m_AuthPasswordEntry = new Gtk.Entry(); + m_AuthPasswordEntry.set_input_purpose(Gtk.InputPurpose.PASSWORD); + m_AuthPasswordEntry.set_visibility(false); + + m_AuthUserEntry.activate.connect(writeData); + m_AuthPasswordEntry.activate.connect(writeData); + + var authGrid = new Gtk.Grid(); + authGrid.margin = 10; + authGrid.set_column_spacing(10); + authGrid.set_row_spacing(10); + authGrid.set_valign(Gtk.Align.CENTER); + authGrid.set_halign(Gtk.Align.CENTER); + + authGrid.attach(authUserLabel, 0, 0, 1, 1); + authGrid.attach(m_AuthUserEntry, 1, 0, 1, 1); + authGrid.attach(authPasswordLabel, 0, 1, 1, 1); + authGrid.attach(m_AuthPasswordEntry, 1, 1, 1, 1); + + var frame = new Gtk.Frame(_("HTTP Authorization")); + frame.set_halign(Gtk.Align.CENTER); + frame.add(authGrid); + m_revealer = new Gtk.Revealer(); + m_revealer.add(frame); + //--------------------------------------------------------------------- + + var loginLabel = new Gtk.Label(_("Please log in to your Nextcloud News instance and enjoy using FeedReader")); + loginLabel.get_style_context().add_class("h2"); + loginLabel.set_justify(Gtk.Justification.CENTER); + loginLabel.set_lines(3); + + var loginButton = new Gtk.Button.with_label(_("Login")); + loginButton.halign = Gtk.Align.END; + loginButton.set_size_request(80, 30); + loginButton.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); + loginButton.clicked.connect(() => { tryLogin(); }); + + var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10); + box.valign = Gtk.Align.CENTER; + box.halign = Gtk.Align.CENTER; + box.pack_start(loginLabel, false, false, 10); + box.pack_start(logo, false, false, 10); + box.pack_start(grid, true, true, 10); + box.pack_start(m_revealer, true, true, 10); + box.pack_end(loginButton, false, false, 20); + + + m_urlEntry.set_text(m_utils.getUnmodifiedURL()); + m_userEntry.set_text(m_utils.getUser()); + m_passwordEntry.set_text(m_utils.getPasswd()); + + return box; + } + + public override bool supportTags() + { + return false; + } + + public override bool doInitSync() + { + return true; + } + + public override string symbolicIcon() + { + return "feed-service-nextcloud-symbolic"; + } + + public override string accountName() + { + return m_utils.getUser(); + } + + public override string getServerURL() + { + return m_utils.getURL(); + } + + public override string uncategorizedID() + { + return "0"; + } + + public override bool hideCategoryWhenEmpty(string cadID) + { + return false; + } + + public override bool supportCategories() + { + return true; + } + + public override bool supportFeedManipulation() + { + return true; + } + + public override bool supportMultiLevelCategories() + { + return false; + } + + public override bool supportMultiCategoriesPerFeed() + { + return false; + } + + public override bool syncFeedsAndCategories() + { + return true; + } + + public override bool tagIDaffectedByNameChange() + { + return false; + } + + public override void resetAccount() + { + m_utils.resetAccount(); + } + + public override bool useMaxArticles() + { + return false; + } + + public override LoginResponse login() + { + return m_api.login(); + } + + public override void setArticleIsRead(string articleIDs, ArticleStatus read) + { + m_api.updateArticleUnread(articleIDs, read); + } + + public override void setArticleIsMarked(string articleID, ArticleStatus marked) + { + m_api.updateArticleMarked(articleID, marked); + } + + public override bool alwaysSetReadByID() + { + return false; + } + + public override void setFeedRead(string feedID) + { + m_api.markFeedRead(feedID, false); + } + + public override void setCategoryRead(string catID) + { + m_api.markFeedRead(catID, true); + } + + public override void markAllItemsRead() + { + m_api.markAllItemsRead(); + } + + public override void tagArticle(string articleID, string tagID) + { + return; + } + + public override void removeArticleTag(string articleID, string tagID) + { + return; + } + + public override string createTag(string caption) + { + return ":("; + } + + public override void deleteTag(string tagID) { return; } - - id = int.parse(feedID); - type = OwncloudNewsAPI.OwnCloudType.FEED; - } - - var articles = new Gee.LinkedList<Article>(); - - if(count == -1) - { - m_api.getNewArticles(articles, DataBase.readOnly().getLastModified(), type, id); + + public override void renameTag(string tagID, string title) + { + return; + } + + public override bool serverAvailable() + { + return m_api.ping(); + } + + public override bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) + { + bool success = false; + int64 id = 0; + if(catID == null && newCatName != null) + { + string newCatID = m_api.addFolder(newCatName).to_string(); + success = m_api.addFeed(feedURL, newCatID, out id, out errmsg); + } + else + { + success = m_api.addFeed(feedURL, catID, out id, out errmsg); + } + + + feedID = id.to_string(); + return success; + } + + public override void removeFeed(string feedID) + { + m_api.removeFeed(feedID); + } + + public override void renameFeed(string feedID, string title) + { + m_api.renameFeed(feedID, title); + } + + public override void moveFeed(string feedID, string newCatID, string? currentCatID) + { + m_api.moveFeed(feedID, newCatID); + } + + public override string createCategory(string title, string? parentID) + { + return m_api.addFolder(title).to_string(); + } + + public override void renameCategory(string catID, string title) + { + m_api.renameCategory(catID, title); + } + + public override void moveCategory(string catID, string newParentID) + { + return; + } + + public override void deleteCategory(string catID) + { + m_api.removeFolder(catID); + } + + public override void removeCatFromFeed(string feedID, string catID) + { + return; + } + + public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) + { + if(m_api.getFeeds(feeds)) + { + if(cancellable != null && cancellable.is_cancelled()) + { + return false; + } + + if(m_api.getCategories(categories, feeds)) + { + return true; + } + } + + return false; + } + + public override int getUnreadCount() + { + return (int)DataBase.readOnly().get_unread_total(); + } + + public override void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) + { + var type = OwncloudNewsAPI.OwnCloudType.ALL; + bool read = true; + int id = 0; + + switch(whatToGet) + { + case ArticleStatus.ALL: + break; + case ArticleStatus.UNREAD: + read = false; + break; + case ArticleStatus.MARKED: + type = OwncloudNewsAPI.OwnCloudType.STARRED; + break; + } + + if(feedID != null) + { + if(isTagID == true) + { + return; + } + + id = int.parse(feedID); + type = OwncloudNewsAPI.OwnCloudType.FEED; + } + + var articles = new Gee.LinkedList<Article>(); + + if(count == -1) + { + m_api.getNewArticles(articles, DataBase.readOnly().getLastModified(), type, id); + } + else + { + m_api.getArticles(articles, 0, -1, read, type, id); + } + + writeArticles(articles); + } } - else + + [ModuleInit] + public void peas_register_types(GLib.TypeModule module) { - m_api.getArticles(articles, 0, -1, read, type, id); + var objmodule = module as Peas.ObjectModule; + objmodule.register_extension_type(typeof(FeedReader.FeedServerInterface), typeof(FeedReader.OwncloudNewsInterface)); } - - writeArticles(articles); -} -} - -[ModuleInit] -public void peas_register_types(GLib.TypeModule module) -{ - var objmodule = module as Peas.ObjectModule; - objmodule.register_extension_type(typeof(FeedReader.FeedServerInterface), typeof(FeedReader.OwncloudNewsInterface)); -} diff --git a/plugins/backend/owncloud/OwncloudNewsMessage.vala b/plugins/backend/owncloud/OwncloudNewsMessage.vala index ff81aab4..8691d392 100644 --- a/plugins/backend/owncloud/OwncloudNewsMessage.vala +++ b/plugins/backend/owncloud/OwncloudNewsMessage.vala @@ -14,192 +14,192 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.OwnCloudNewsMessage : GLib.Object { - -private Soup.Session m_session; -private Soup.Message m_message_soup; -private GLib.StringBuilder m_message_string; -private string m_contenttype; -private Json.Parser m_parser; -private Json.Object m_root_object; -private string m_method; -private string m_destination; - -public OwnCloudNewsMessage(Soup.Session session, string destination, string username, string password, string method) -{ - m_message_string = new GLib.StringBuilder(); - m_method = method; - m_session = session; - m_destination = destination; - - if(method == "GET") - { - m_contenttype = "application/x-www-form-urlencoded"; - } - else - { - m_contenttype = "application/json"; - } - - m_parser = new Json.Parser(); - m_message_soup = new Soup.Message(m_method, m_destination); - - string credentials = username + ":" + password; - string base64 = GLib.Base64.encode(credentials.data); - m_message_soup.request_headers.append("Authorization","Basic %s".printf(base64)); -} - -public void add_int(string type, int val) -{ - if(m_method == "GET") - { - if(m_message_string.len > 0) + + private Soup.Session m_session; + private Soup.Message m_message_soup; + private GLib.StringBuilder m_message_string; + private string m_contenttype; + private Json.Parser m_parser; + private Json.Object m_root_object; + private string m_method; + private string m_destination; + + public OwnCloudNewsMessage(Soup.Session session, string destination, string username, string password, string method) + { + m_message_string = new GLib.StringBuilder(); + m_method = method; + m_session = session; + m_destination = destination; + + if(method == "GET") { - m_message_string.append("&"); + m_contenttype = "application/x-www-form-urlencoded"; } - - m_message_string.append(type + "=" + val.to_string()); - } - else - { - m_message_string.append(",\"" + type + "\":" + val.to_string()); - } -} - -public void add_int_array(string type, string values) -{ - if(m_method == "GET") - { - Logger.warning("OwnCloudNewsMessage.add_int_array: this should not happen"); - } - else - { - m_message_string.append(",\"" + type + "\":[" + values + "]"); - } -} - -public void add_bool(string type, bool val) -{ - if(m_method == "GET") - { - if(m_message_string.len > 0) + else { - m_message_string.append("&"); + m_contenttype = "application/json"; } - - m_message_string.append(type + "=" + (val ? "true" : "false")); - } - else - { - m_message_string.append(",\"" + type + "\":" + (val ? "true" : "false")); - } -} - -public void add_string(string type, string val) -{ - if(m_method == "GET") - { - if(m_message_string.len > 0) + + m_parser = new Json.Parser(); + m_message_soup = new Soup.Message(m_method, m_destination); + + string credentials = username + ":" + password; + string base64 = GLib.Base64.encode(credentials.data); + m_message_soup.request_headers.append("Authorization","Basic %s".printf(base64)); + } + + public void add_int(string type, int val) + { + if(m_method == "GET") { - m_message_string.append("&"); + if(m_message_string.len > 0) + { + m_message_string.append("&"); + } + + m_message_string.append(type + "=" + val.to_string()); + } + else + { + m_message_string.append(",\"" + type + "\":" + val.to_string()); } - - m_message_string.append(type + "=" + val); - } - else - { - m_message_string.append(",\"" + type + "\":\"" + val + "\""); } -} - -public ConnectionError send(bool ping = false) -{ - var settingsTweaks = new GLib.Settings("org.gnome.feedreader.tweaks"); - - if(m_method == "GET") + + public void add_int_array(string type, string values) { - string destination = m_destination; - if(m_message_string.len > 0) + if(m_method == "GET") { - destination += "?" + m_message_string.str; + Logger.warning("OwnCloudNewsMessage.add_int_array: this should not happen"); + } + else + { + m_message_string.append(",\"" + type + "\":[" + values + "]"); } - m_message_soup.set_uri(new Soup.URI(destination)); - Logger.debug(destination); } - else + + public void add_bool(string type, bool val) { - m_message_string.overwrite(0, "{").append("}"); - m_message_soup.set_request(m_contenttype, Soup.MemoryUse.COPY, m_message_string.str.data); + if(m_method == "GET") + { + if(m_message_string.len > 0) + { + m_message_string.append("&"); + } + + m_message_string.append(type + "=" + (val ? "true" : "false")); + } + else + { + m_message_string.append(",\"" + type + "\":" + (val ? "true" : "false")); + } } - - if(settingsTweaks.get_boolean("do-not-track")) + + public void add_string(string type, string val) { - m_message_soup.request_headers.append("DNT", "1"); + if(m_method == "GET") + { + if(m_message_string.len > 0) + { + m_message_string.append("&"); + } + + m_message_string.append(type + "=" + val); + } + else + { + m_message_string.append(",\"" + type + "\":\"" + val + "\""); + } } - - var status = m_session.send_message(m_message_soup); - - if(status == 401) // unauthorized - + + public ConnectionError send(bool ping = false) { - return ConnectionError.UNAUTHORIZED; + var settingsTweaks = new GLib.Settings("org.gnome.feedreader.tweaks"); + + if(m_method == "GET") + { + string destination = m_destination; + if(m_message_string.len > 0) + { + destination += "?" + m_message_string.str; + } + m_message_soup.set_uri(new Soup.URI(destination)); + Logger.debug(destination); + } + else + { + m_message_string.overwrite(0, "{").append("}"); + m_message_soup.set_request(m_contenttype, Soup.MemoryUse.COPY, m_message_string.str.data); + } + + if(settingsTweaks.get_boolean("do-not-track")) + { + m_message_soup.request_headers.append("DNT", "1"); + } + + var status = m_session.send_message(m_message_soup); + + if(status == 401) // unauthorized + + { + return ConnectionError.UNAUTHORIZED; + } + + if(m_message_soup.tls_errors != 0 && !settingsTweaks.get_boolean("ignore-tls-errors")) + { + Logger.info("TLS errors: " + Utils.printTlsCertificateFlags(m_message_soup.tls_errors)); + return ConnectionError.CA_ERROR; + } + + if(m_message_soup.status_code != 200) + { + Logger.error("Nextcloud Message: No response - status code: %u %s".printf(m_message_soup.status_code, Soup.Status.get_phrase(m_message_soup.status_code))); + return ConnectionError.NO_RESPONSE; + } + + if(ping) + { + Logger.debug("Nextcloud Message: ping successful"); + return ConnectionError.SUCCESS; + } + + try + { + m_parser.load_from_data((string)m_message_soup.response_body.flatten().data); + } + catch(Error e) + { + Logger.error("Could not load response from Message to Nextcloud"); + printMessage(); + Logger.error(e.message); + return ConnectionError.UNKNOWN; + } + + m_root_object = m_parser.get_root().get_object(); + return ConnectionError.SUCCESS; } - - if(m_message_soup.tls_errors != 0 && !settingsTweaks.get_boolean("ignore-tls-errors")) + + public uint getStatusCode() { - Logger.info("TLS errors: " + Utils.printTlsCertificateFlags(m_message_soup.tls_errors)); - return ConnectionError.CA_ERROR; + return m_message_soup.status_code; } - - if(m_message_soup.status_code != 200) + + public Json.Object? get_response_object() { - Logger.error("Nextcloud Message: No response - status code: %u %s".printf(m_message_soup.status_code, Soup.Status.get_phrase(m_message_soup.status_code))); - return ConnectionError.NO_RESPONSE; + return m_root_object; } - - if(ping) + + public string getMessage() { - Logger.debug("Nextcloud Message: ping successful"); - return ConnectionError.SUCCESS; + return m_message_string.str; } - - try + + public void printMessage() { - m_parser.load_from_data((string)m_message_soup.response_body.flatten().data); + Logger.debug(m_message_string.str); } - catch(Error e) + + public void printResponse() { - Logger.error("Could not load response from Message to Nextcloud"); - printMessage(); - Logger.error(e.message); - return ConnectionError.UNKNOWN; + Logger.debug((string)m_message_soup.response_body.flatten().data); } - - m_root_object = m_parser.get_root().get_object(); - return ConnectionError.SUCCESS; -} - -public uint getStatusCode() -{ - return m_message_soup.status_code; -} - -public Json.Object? get_response_object() -{ - return m_root_object; -} - -public string getMessage() -{ - return m_message_string.str; -} - -public void printMessage() -{ - Logger.debug(m_message_string.str); -} - -public void printResponse() -{ - Logger.debug((string)m_message_soup.response_body.flatten().data); -} } diff --git a/plugins/backend/owncloud/OwncloudNewsUtils.vala b/plugins/backend/owncloud/OwncloudNewsUtils.vala index f1795455..2d2d213b 100644 --- a/plugins/backend/owncloud/OwncloudNewsUtils.vala +++ b/plugins/backend/owncloud/OwncloudNewsUtils.vala @@ -14,145 +14,145 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.OwncloudNewsUtils : GLib.Object { - -GLib.Settings m_settings; -Password m_password; -Password m_htaccess_password; - -public OwncloudNewsUtils(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) -{ - if(settings_backend != null) + + GLib.Settings m_settings; + Password m_password; + Password m_htaccess_password; + + public OwncloudNewsUtils(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) { - m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.owncloud", settings_backend); - } - else - { - m_settings = new GLib.Settings("org.gnome.feedreader.owncloud"); - } - - var pwSchema = new Secret.Schema ("org.gnome.feedreader.password", Secret.SchemaFlags.NONE, - "URL", Secret.SchemaAttributeType.STRING, - "Username", Secret.SchemaAttributeType.STRING); - m_password = new Password(secrets, pwSchema, "FeedReader: Nextcloud login", () => { + if(settings_backend != null) + { + m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.owncloud", settings_backend); + } + else + { + m_settings = new GLib.Settings("org.gnome.feedreader.owncloud"); + } + + var pwSchema = new Secret.Schema ("org.gnome.feedreader.password", Secret.SchemaFlags.NONE, + "URL", Secret.SchemaAttributeType.STRING, + "Username", Secret.SchemaAttributeType.STRING); + m_password = new Password(secrets, pwSchema, "FeedReader: Nextcloud login", () => { var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); attributes["URL"] = getURL(); attributes["Username"] = getUser(); return attributes; }); - - var htAccessSchema = new Secret.Schema ("org.gnome.feedreader.password", Secret.SchemaFlags.NONE, - "URL", Secret.SchemaAttributeType.STRING, - "Username", Secret.SchemaAttributeType.STRING, - "htaccess", Secret.SchemaAttributeType.BOOLEAN); - m_htaccess_password = new Password(secrets, htAccessSchema, "FeedReader: Nextcloud login", () => { + + var htAccessSchema = new Secret.Schema ("org.gnome.feedreader.password", Secret.SchemaFlags.NONE, + "URL", Secret.SchemaAttributeType.STRING, + "Username", Secret.SchemaAttributeType.STRING, + "htaccess", Secret.SchemaAttributeType.BOOLEAN); + m_htaccess_password = new Password(secrets, htAccessSchema, "FeedReader: Nextcloud login", () => { var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); attributes["URL"] = getURL(); attributes["Username"] = getHtaccessUser(); attributes["htaccess"] = "true"; return attributes; }); -} - -public string getURL() -{ - string tmp_url = Utils.gsettingReadString(m_settings, "url"); - if(tmp_url != "") + } + + public string getURL() { - if(!tmp_url.has_suffix("/")) - { - tmp_url = tmp_url + "/"; - } - - if(!tmp_url.has_suffix("/index.php/apps/news/api/v1-2/")) - { - tmp_url = tmp_url + "index.php/apps/news/api/v1-2/"; - } - - if(!tmp_url.has_prefix("http://") && !tmp_url.has_prefix("https://")) + string tmp_url = Utils.gsettingReadString(m_settings, "url"); + if(tmp_url != "") { - tmp_url = "https://" + tmp_url; + if(!tmp_url.has_suffix("/")) + { + tmp_url = tmp_url + "/"; + } + + if(!tmp_url.has_suffix("/index.php/apps/news/api/v1-2/")) + { + tmp_url = tmp_url + "index.php/apps/news/api/v1-2/"; + } + + if(!tmp_url.has_prefix("http://") && !tmp_url.has_prefix("https://")) + { + tmp_url = "https://" + tmp_url; + } } + + Logger.debug("Nextcloud URL: " + tmp_url); + + return tmp_url; } - - Logger.debug("Nextcloud URL: " + tmp_url); - - return tmp_url; -} - -public void setURL(string url) -{ - Utils.gsettingWriteString(m_settings, "url", url); -} - -public string getUser() -{ - return Utils.gsettingReadString(m_settings, "username"); -} - -public void setUser(string user) -{ - Utils.gsettingWriteString(m_settings, "username", user); -} - -public string getHtaccessUser() -{ - return Utils.gsettingReadString(m_settings, "htaccess-username"); -} - -public void setHtaccessUser(string ht_user) -{ - Utils.gsettingWriteString(m_settings, "htaccess-username", ht_user); -} - -public string getUnmodifiedURL() -{ - return Utils.gsettingReadString(m_settings, "url"); -} - -public string getPasswd() -{ - return m_password.get_password(); -} - -public void setPassword(string passwd) -{ - m_password.set_password(passwd); -} - -public void resetAccount() -{ - Utils.resetSettings(m_settings); - m_password.delete_password(); - m_htaccess_password.delete_password(); -} - -public string getHtaccessPasswd() -{ - return m_htaccess_password.get_password(); -} - -public void setHtAccessPassword(string passwd) -{ - m_htaccess_password.set_password(passwd); -} - -public int countUnread(Gee.List<Feed> feeds, string id) -{ - int unread = 0; - - foreach(Feed feed in feeds) + + public void setURL(string url) + { + Utils.gsettingWriteString(m_settings, "url", url); + } + + public string getUser() + { + return Utils.gsettingReadString(m_settings, "username"); + } + + public void setUser(string user) + { + Utils.gsettingWriteString(m_settings, "username", user); + } + + public string getHtaccessUser() + { + return Utils.gsettingReadString(m_settings, "htaccess-username"); + } + + public void setHtaccessUser(string ht_user) + { + Utils.gsettingWriteString(m_settings, "htaccess-username", ht_user); + } + + public string getUnmodifiedURL() { - var ids = feed.getCatIDs(); - foreach(string ID in ids) + return Utils.gsettingReadString(m_settings, "url"); + } + + public string getPasswd() + { + return m_password.get_password(); + } + + public void setPassword(string passwd) + { + m_password.set_password(passwd); + } + + public void resetAccount() + { + Utils.resetSettings(m_settings); + m_password.delete_password(); + m_htaccess_password.delete_password(); + } + + public string getHtaccessPasswd() + { + return m_htaccess_password.get_password(); + } + + public void setHtAccessPassword(string passwd) + { + m_htaccess_password.set_password(passwd); + } + + public int countUnread(Gee.List<Feed> feeds, string id) + { + int unread = 0; + + foreach(Feed feed in feeds) { - if(ID == id) + var ids = feed.getCatIDs(); + foreach(string ID in ids) { - unread += (int)feed.getUnread(); - break; + if(ID == id) + { + unread += (int)feed.getUnread(); + break; + } } } + + return unread; } - - return unread; -} } diff --git a/plugins/backend/ttrss/UntypedJson.vala b/plugins/backend/ttrss/UntypedJson.vala index 66f5088c..c2e7cd1f 100644 --- a/plugins/backend/ttrss/UntypedJson.vala +++ b/plugins/backend/ttrss/UntypedJson.vala @@ -2,46 +2,46 @@ // dynamically cast everything to the type we expect namespace FeedReader.UntypedJson { - -namespace Object -{ - -public Value? get_value_member(Json.Object obj, string key) -{ - var member = obj.get_member(key); - if(member == null) - { - return null; - } - - return member.get_value(); -} - -public int? get_int_member(Json.Object obj, string key) -{ - var value = get_value_member(obj, key); - if (value == null) + + namespace Object { - return null; + + public Value? get_value_member(Json.Object obj, string key) + { + var member = obj.get_member(key); + if(member == null) + { + return null; + } + + return member.get_value(); + } + + public int? get_int_member(Json.Object obj, string key) + { + var value = get_value_member(obj, key); + if (value == null) + { + return null; + } + + var result = new Value(Type.INT); + value.transform(ref result); + return result.get_int(); + } + + public string? get_string_member(Json.Object obj, string key) + { + var value = get_value_member(obj, key); + if (value == null) + { + return null; + } + + var result = new Value(Type.STRING); + value.transform(ref result); + return result.get_string(); + } + } - - var result = new Value(Type.INT); - value.transform(ref result); - return result.get_int(); -} - -public string? get_string_member(Json.Object obj, string key) -{ - var value = get_value_member(obj, key); - if (value == null) - { - return null; - } - - var result = new Value(Type.STRING); - value.transform(ref result); - return result.get_string(); -} - -} } diff --git a/plugins/backend/ttrss/ttrssAPI.vala b/plugins/backend/ttrss/ttrssAPI.vala index 98c9e356..92ad7420 100644 --- a/plugins/backend/ttrss/ttrssAPI.vala +++ b/plugins/backend/ttrss/ttrssAPI.vala @@ -14,961 +14,961 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.ttrssAPI : GLib.Object { - -public string m_ttrss_url { get; private set; } -private ttrssUtils m_utils; -private string m_ttrss_sessionid; -private string? m_iconDir = null; -private Soup.Session m_session; - -public ttrssAPI (ttrssUtils utils) -{ - m_utils = utils; - m_session = new Soup.Session(); - m_session.user_agent = Constants.USER_AGENT; - m_session.ssl_strict = false; - m_session.authenticate.connect((msg, auth, retrying) => { + + public string m_ttrss_url { get; private set; } + private ttrssUtils m_utils; + private string m_ttrss_sessionid; + private string? m_iconDir = null; + private Soup.Session m_session; + + public ttrssAPI (ttrssUtils utils) + { + m_utils = utils; + m_session = new Soup.Session(); + m_session.user_agent = Constants.USER_AGENT; + m_session.ssl_strict = false; + m_session.authenticate.connect((msg, auth, retrying) => { if(m_utils.getHtaccessUser() == "") { - Logger.error("TTRSS Session: need Authentication"); + Logger.error("TTRSS Session: need Authentication"); } else if(!retrying) { - auth.authenticate(m_utils.getHtaccessUser(), m_utils.getHtaccessPasswd()); + auth.authenticate(m_utils.getHtaccessUser(), m_utils.getHtaccessPasswd()); } }); -} - - -public LoginResponse login() -{ - Logger.debug("TTRSS: login"); - string username = m_utils.getUser(); - string passwd = m_utils.getPasswd(); - m_ttrss_url = m_utils.getURL(); - - if(m_ttrss_url == "" && username == "" && passwd == "") - { - m_ttrss_url = "example-host/tt-rss"; - return LoginResponse.ALL_EMPTY; - } - if(m_ttrss_url == "") - { - return LoginResponse.MISSING_URL; - } - if(GLib.Uri.parse_scheme(m_ttrss_url) == null) - { - return LoginResponse.INVALID_URL; - } - if(passwd == "") - { - return LoginResponse.MISSING_PASSWD; } - - - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("op", "login"); - if(username != "") + + + public LoginResponse login() { - message.add_string("user", username); - } - message.add_string("password", passwd); - int status = message.send(); - - if(status == ConnectionError.SUCCESS) - { - var response = message.get_response_object(); - m_ttrss_sessionid = UntypedJson.Object.get_string_member(response, "session_id"); - var api_level = UntypedJson.Object.get_int_member(response, "api_level"); - Logger.info("TTRSS Session ID: %s".printf(m_ttrss_sessionid)); - Logger.info("TTRSS API Level: %lld".printf(api_level)); - - m_iconDir = m_ttrss_url.replace("api/", getIconDir()); - - if(haveAPIplugin()) + Logger.debug("TTRSS: login"); + string username = m_utils.getUser(); + string passwd = m_utils.getPasswd(); + m_ttrss_url = m_utils.getURL(); + + if(m_ttrss_url == "" && username == "" && passwd == "") { - return LoginResponse.SUCCESS; + m_ttrss_url = "example-host/tt-rss"; + return LoginResponse.ALL_EMPTY; } - - return LoginResponse.PLUGIN_NEEDED; - } - - if(status == ConnectionError.API_ERROR) - { - return LoginResponse.API_ERROR; - } - else if(status == ConnectionError.NO_RESPONSE) - { - return LoginResponse.NO_CONNECTION; - } - else if(status == ConnectionError.API_DISABLED) - { - return LoginResponse.NO_API_ACCESS; - } - else if(status == ConnectionError.CA_ERROR) - { - return LoginResponse.CA_ERROR; - } - else if(status == ConnectionError.UNAUTHORIZED) - { - return LoginResponse.UNAUTHORIZED; - } - - return LoginResponse.UNKNOWN_ERROR; -} - -public bool logout() -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "logout"); - int status = message.send(); - Logger.warning("TTRSS: logout"); - - if(status == ConnectionError.SUCCESS) - { - var response = message.get_response_object(); - m_ttrss_sessionid = ""; - return response.get_boolean_member("status"); - } - - return false; -} - - -public bool isloggedin() -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "isLoggedIn"); - int status = message.send(); - Logger.debug("TTRSS: isloggedin?"); - - if(status == ConnectionError.SUCCESS) - { - var response = message.get_response_object(); - return response.get_boolean_member("status"); - } - - return false; -} - -private bool haveAPIplugin() -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "removeLabel"); - int status = message.send(); - - if(status == ConnectionError.API_ERROR) - { - var response = message.get_response_object(); - if(response.has_member("error")) + if(m_ttrss_url == "") { - if(response.get_string_member("error") == "INCORRECT_USAGE") + return LoginResponse.MISSING_URL; + } + if(GLib.Uri.parse_scheme(m_ttrss_url) == null) + { + return LoginResponse.INVALID_URL; + } + if(passwd == "") + { + return LoginResponse.MISSING_PASSWD; + } + + + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("op", "login"); + if(username != "") + { + message.add_string("user", username); + } + message.add_string("password", passwd); + int status = message.send(); + + if(status == ConnectionError.SUCCESS) + { + var response = message.get_response_object(); + m_ttrss_sessionid = UntypedJson.Object.get_string_member(response, "session_id"); + var api_level = UntypedJson.Object.get_int_member(response, "api_level"); + Logger.info("TTRSS Session ID: %s".printf(m_ttrss_sessionid)); + Logger.info("TTRSS API Level: %lld".printf(api_level)); + + m_iconDir = m_ttrss_url.replace("api/", getIconDir()); + + if(haveAPIplugin()) { - return true; + return LoginResponse.SUCCESS; } + + return LoginResponse.PLUGIN_NEEDED; } - } - - return false; -} - - -public int getUnreadCount() -{ - int unread = 0; - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "getUnread"); - int status = message.send(); - - if(status == ConnectionError.SUCCESS) - { - var response = message.get_response_object(); - int? maybe_unread = UntypedJson.Object.get_int_member(response, "unread"); - if(maybe_unread != null) + + if(status == ConnectionError.API_ERROR) { - unread = maybe_unread; + return LoginResponse.API_ERROR; } - else + else if(status == ConnectionError.NO_RESPONSE) { - Logger.warning("Could not parse unread articles"); + return LoginResponse.NO_CONNECTION; } - } - Logger.info("There are %i unread articles".printf(unread)); - - return unread; -} - - -public bool getFeeds(Gee.List<Feed> feeds, Gee.List<Category> categories) -{ - foreach(var item in categories) - { - if(int.parse(item.getCatID()) > 0) + else if(status == ConnectionError.API_DISABLED) { - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "getFeeds"); - message.add_int("cat_id", int.parse(item.getCatID())); - int status = message.send(); - - if(status == ConnectionError.SUCCESS) + return LoginResponse.NO_API_ACCESS; + } + else if(status == ConnectionError.CA_ERROR) + { + return LoginResponse.CA_ERROR; + } + else if(status == ConnectionError.UNAUTHORIZED) + { + return LoginResponse.UNAUTHORIZED; + } + + return LoginResponse.UNKNOWN_ERROR; + } + + public bool logout() + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "logout"); + int status = message.send(); + Logger.warning("TTRSS: logout"); + + if(status == ConnectionError.SUCCESS) + { + var response = message.get_response_object(); + m_ttrss_sessionid = ""; + return response.get_boolean_member("status"); + } + + return false; + } + + + public bool isloggedin() + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "isLoggedIn"); + int status = message.send(); + Logger.debug("TTRSS: isloggedin?"); + + if(status == ConnectionError.SUCCESS) + { + var response = message.get_response_object(); + return response.get_boolean_member("status"); + } + + return false; + } + + private bool haveAPIplugin() + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "removeLabel"); + int status = message.send(); + + if(status == ConnectionError.API_ERROR) + { + var response = message.get_response_object(); + if(response.has_member("error")) { - var response = message.get_response_array(); - var feed_count = response.get_length(); - - for(uint i = 0; i < feed_count; i++) + if(response.get_string_member("error") == "INCORRECT_USAGE") { - var feed_node = response.get_object_element(i); - string feed_id = UntypedJson.Object.get_string_member(feed_node, "id"); - string? icon_url = feed_node.get_boolean_member("has_icon") ? m_iconDir + feed_id + ".ico" : null; - - feeds.add( - new Feed( - feed_id, - feed_node.get_string_member("title"), - feed_node.get_string_member("feed_url"), - UntypedJson.Object.get_int_member(feed_node, "unread"), - ListUtils.single(UntypedJson.Object.get_string_member(feed_node, "cat_id")), - icon_url, - feed_node.get_string_member("feed_url") - ) - ); + return true; } } + } + + return false; + } + + + public int getUnreadCount() + { + int unread = 0; + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "getUnread"); + int status = message.send(); + + if(status == ConnectionError.SUCCESS) + { + var response = message.get_response_object(); + int? maybe_unread = UntypedJson.Object.get_int_member(response, "unread"); + if(maybe_unread != null) + { + unread = maybe_unread; + } else { - return false; + Logger.warning("Could not parse unread articles"); } } + Logger.info("There are %i unread articles".printf(unread)); + + return unread; } - return true; -} - - -public bool getUncategorizedFeeds(Gee.List<Feed> feeds) -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "getFeeds"); - message.add_int("cat_id", 0); - int status = message.send(); - - if(status == ConnectionError.SUCCESS) + + + public bool getFeeds(Gee.List<Feed> feeds, Gee.List<Category> categories) { - var response = message.get_response_array(); - var feed_count = response.get_length(); - - for(uint i = 0; i < feed_count; i++) + foreach(var item in categories) { - var feed_node = response.get_object_element(i); - string feed_id = UntypedJson.Object.get_string_member(feed_node, "id"); - string? icon_url = feed_node.get_boolean_member("has_icon") ? m_iconDir + feed_id + ".ico" : null; - - feeds.add( - new Feed( - feed_id, - feed_node.get_string_member("title"), - feed_node.get_string_member("feed_url"), - UntypedJson.Object.get_int_member(feed_node, "unread"), - ListUtils.single(UntypedJson.Object.get_string_member(feed_node, "cat_id")), - icon_url, - feed_node.get_string_member("feed_url") - ) - ); + if(int.parse(item.getCatID()) > 0) + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "getFeeds"); + message.add_int("cat_id", int.parse(item.getCatID())); + int status = message.send(); + + if(status == ConnectionError.SUCCESS) + { + var response = message.get_response_array(); + var feed_count = response.get_length(); + + for(uint i = 0; i < feed_count; i++) + { + var feed_node = response.get_object_element(i); + string feed_id = UntypedJson.Object.get_string_member(feed_node, "id"); + string? icon_url = feed_node.get_boolean_member("has_icon") ? m_iconDir + feed_id + ".ico" : null; + + feeds.add( + new Feed( + feed_id, + feed_node.get_string_member("title"), + feed_node.get_string_member("feed_url"), + UntypedJson.Object.get_int_member(feed_node, "unread"), + ListUtils.single(UntypedJson.Object.get_string_member(feed_node, "cat_id")), + icon_url, + feed_node.get_string_member("feed_url") + ) + ); + } + } + else + { + return false; + } + } } return true; } - - return false; -} - -public bool getTags(Gee.List<Tag> tags) -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "getLabels"); - int status = message.send(); - - if(status == ConnectionError.SUCCESS) - { - var response = message.get_response_array(); - var tag_count = response.get_length(); - - var db = DataBase.readOnly(); - for(uint i = 0; i < tag_count; ++i) - { - var tag_node = response.get_object_element(i); - tags.add( - new Tag( - UntypedJson.Object.get_string_member(tag_node, "id"), - tag_node.get_string_member("caption"), - db.getTagColor() + + + public bool getUncategorizedFeeds(Gee.List<Feed> feeds) + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "getFeeds"); + message.add_int("cat_id", 0); + int status = message.send(); + + if(status == ConnectionError.SUCCESS) + { + var response = message.get_response_array(); + var feed_count = response.get_length(); + + for(uint i = 0; i < feed_count; i++) + { + var feed_node = response.get_object_element(i); + string feed_id = UntypedJson.Object.get_string_member(feed_node, "id"); + string? icon_url = feed_node.get_boolean_member("has_icon") ? m_iconDir + feed_id + ".ico" : null; + + feeds.add( + new Feed( + feed_id, + feed_node.get_string_member("title"), + feed_node.get_string_member("feed_url"), + UntypedJson.Object.get_int_member(feed_node, "unread"), + ListUtils.single(UntypedJson.Object.get_string_member(feed_node, "cat_id")), + icon_url, + feed_node.get_string_member("feed_url") ) ); + } + return true; } - - return true; - } - - return false; -} - - -public string? getIconDir() -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "getConfig"); - int status = message.send(); - - if(status == ConnectionError.SUCCESS) - { - var response = message.get_response_object(); - return response.get_string_member("icons_url") + "/"; - } - - return null; -} - - -public bool getCategories(Gee.List<Category> categories) -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "getFeedTree"); - message.add_bool("include_empty", true); - int status = message.send(); - - if(status == ConnectionError.SUCCESS) - { - var response = message.get_response_object(); - if(response.has_member("categories")) + + return false; + } + + public bool getTags(Gee.List<Tag> tags) + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "getLabels"); + int status = message.send(); + + if(status == ConnectionError.SUCCESS) { - var category_object = response.get_object_member("categories"); - getSubCategories(categories, category_object, 0, CategoryID.MASTER.to_string()); + var response = message.get_response_array(); + var tag_count = response.get_length(); + + var db = DataBase.readOnly(); + for(uint i = 0; i < tag_count; ++i) + { + var tag_node = response.get_object_element(i); + tags.add( + new Tag( + UntypedJson.Object.get_string_member(tag_node, "id"), + tag_node.get_string_member("caption"), + db.getTagColor() + ) + ); + } + return true; } + + return false; + } + + + public string? getIconDir() + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "getConfig"); + int status = message.send(); + + if(status == ConnectionError.SUCCESS) + { + var response = message.get_response_object(); + return response.get_string_member("icons_url") + "/"; + } + + return null; } - - return false; -} - - -private void getSubCategories(Gee.List<Category> categories, Json.Object categorie, int level, string parent) -{ - level++; - int orderID = 0; - var subcategorie = categorie.get_array_member("items"); - var items_count = subcategorie.get_length(); - for(uint i = 0; i < items_count; i++) - { - var categorie_node = subcategorie.get_object_element(i); - string catID = UntypedJson.Object.get_string_member(categorie_node, "id"); - if(catID.has_prefix("CAT:")) - { - orderID++; - string categorieID = catID.slice(4, catID.length); - - if(int.parse(categorieID) > 0) + + + public bool getCategories(Gee.List<Category> categories) + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "getFeedTree"); + message.add_bool("include_empty", true); + int status = message.send(); + + if(status == ConnectionError.SUCCESS) + { + var response = message.get_response_object(); + if(response.has_member("categories")) { - string title = categorie_node.get_string_member("name"); - int unread_count = UntypedJson.Object.get_int_member(categorie_node, "unread"); - - if(title == "Uncategorized") - { - unread_count = getUncategorizedUnread(); - } - - categories.add( - new Category ( - categorieID, - title, - unread_count, - orderID, - parent, - level - ) - ); + var category_object = response.get_object_member("categories"); + getSubCategories(categories, category_object, 0, CategoryID.MASTER.to_string()); + return true; } - - getSubCategories(categories, categorie_node, level, categorieID); } - } -} - - -private int getUncategorizedUnread() -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "getCounters"); - message.add_string("output_mode", "c"); - int status = message.send(); - - if(status == ConnectionError.SUCCESS) - { - var response = message.get_response_array(); - var categorie_count = response.get_length(); - - for(int i = 0; i < categorie_count; i++) + + return false; + } + + + private void getSubCategories(Gee.List<Category> categories, Json.Object categorie, int level, string parent) + { + level++; + int orderID = 0; + var subcategorie = categorie.get_array_member("items"); + var items_count = subcategorie.get_length(); + for(uint i = 0; i < items_count; i++) { - var categorie_node = response.get_object_element(i); - if(UntypedJson.Object.get_int_member(categorie_node, "id") == 0) + var categorie_node = subcategorie.get_object_element(i); + string catID = UntypedJson.Object.get_string_member(categorie_node, "id"); + if(catID.has_prefix("CAT:")) { - if(categorie_node.has_member("kind")) + orderID++; + string categorieID = catID.slice(4, catID.length); + + if(int.parse(categorieID) > 0) { - if(categorie_node.get_string_member("kind") == "cat") + string title = categorie_node.get_string_member("name"); + int unread_count = UntypedJson.Object.get_int_member(categorie_node, "unread"); + + if(title == "Uncategorized") { - return UntypedJson.Object.get_int_member(categorie_node, "counter"); + unread_count = getUncategorizedUnread(); } + + categories.add( + new Category ( + categorieID, + title, + unread_count, + orderID, + parent, + level + ) + ); } + + getSubCategories(categories, categorie_node, level, categorieID); } } } - - return 0; -} - - -public void getHeadlines(Gee.List<Article> articles, int skip, int limit, ArticleStatus whatToGet, int feedID) -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "getHeadlines"); - message.add_int("feed_id", feedID); - message.add_int("limit", limit); - message.add_int("skip", skip); - - switch(whatToGet) - { - case ArticleStatus.ALL: - message.add_string("view_mode", "all_articles"); - break; - - case ArticleStatus.UNREAD: - message.add_string("view_mode", "unread"); - break; - - case ArticleStatus.MARKED: - message.add_string("view_mode", "marked"); - break; - } - - int status = message.send(); - - if(status == ConnectionError.SUCCESS) - { - var response = message.get_response_array(); - var headline_count = response.get_length(); - - for(uint i = 0; i < headline_count; i++) + + + private int getUncategorizedUnread() + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "getCounters"); + message.add_string("output_mode", "c"); + int status = message.send(); + + if(status == ConnectionError.SUCCESS) { - var headline_node = response.get_object_element(i); - - Gee.List<string>? tags = null; - if(headline_node.has_member("labels")) + var response = message.get_response_array(); + var categorie_count = response.get_length(); + + for(int i = 0; i < categorie_count; i++) { - var labels = headline_node.get_array_member("labels"); - - uint tag_count = 0; - if(labels != null) - { - tag_count = labels.get_length(); - } - - if(tag_count > 0) + var categorie_node = response.get_object_element(i); + if(UntypedJson.Object.get_int_member(categorie_node, "id") == 0) { - tags = new Gee.ArrayList<string>(); - for(int j = 0; j < tag_count; ++j) + if(categorie_node.has_member("kind")) { - tags.add(labels.get_array_element(j).get_int_element(0).to_string()); + if(categorie_node.get_string_member("kind") == "cat") + { + return UntypedJson.Object.get_int_member(categorie_node, "counter"); + } } } } - - var enclosures = new Gee.ArrayList<Enclosure>(); - if(headline_node.has_member("attachments")) + } + + return 0; + } + + + public void getHeadlines(Gee.List<Article> articles, int skip, int limit, ArticleStatus whatToGet, int feedID) + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "getHeadlines"); + message.add_int("feed_id", feedID); + message.add_int("limit", limit); + message.add_int("skip", skip); + + switch(whatToGet) + { + case ArticleStatus.ALL: + message.add_string("view_mode", "all_articles"); + break; + + case ArticleStatus.UNREAD: + message.add_string("view_mode", "unread"); + break; + + case ArticleStatus.MARKED: + message.add_string("view_mode", "marked"); + break; + } + + int status = message.send(); + + if(status == ConnectionError.SUCCESS) + { + var response = message.get_response_array(); + var headline_count = response.get_length(); + + for(uint i = 0; i < headline_count; i++) { - var attachments = headline_node.get_array_member("attachments"); - - uint mediaCount = 0; - if(attachments != null) + var headline_node = response.get_object_element(i); + + Gee.List<string>? tags = null; + if(headline_node.has_member("labels")) { - mediaCount = attachments.get_length(); + var labels = headline_node.get_array_member("labels"); + + uint tag_count = 0; + if(labels != null) + { + tag_count = labels.get_length(); + } + + if(tag_count > 0) + { + tags = new Gee.ArrayList<string>(); + for(int j = 0; j < tag_count; ++j) + { + tags.add(labels.get_array_element(j).get_int_element(0).to_string()); + } + } } - - for(int j = 0; j < mediaCount; ++j) + + var enclosures = new Gee.ArrayList<Enclosure>(); + if(headline_node.has_member("attachments")) { - var attachment = attachments.get_object_element(j); - enclosures.add(new Enclosure( - UntypedJson.Object.get_string_member(headline_node, "id"), - attachment.get_string_member("content_url"), - EnclosureType.from_string(attachment.get_string_member("content_type")))); + var attachments = headline_node.get_array_member("attachments"); + + uint mediaCount = 0; + if(attachments != null) + { + mediaCount = attachments.get_length(); + } + + for(int j = 0; j < mediaCount; ++j) + { + var attachment = attachments.get_object_element(j); + enclosures.add(new Enclosure( + UntypedJson.Object.get_string_member(headline_node, "id"), + attachment.get_string_member("content_url"), + EnclosureType.from_string(attachment.get_string_member("content_type")))); + } } - } - - var Article = new Article( - UntypedJson.Object.get_string_member(headline_node, "id"), - headline_node.get_string_member("title"), - headline_node.get_string_member("link"), - UntypedJson.Object.get_string_member(headline_node, "feed_id"), - headline_node.get_boolean_member("unread") ? ArticleStatus.UNREAD : ArticleStatus.READ, - headline_node.get_boolean_member("marked") ? ArticleStatus.MARKED : ArticleStatus.UNMARKED, - null, - null, - headline_node.get_string_member("author"), - new DateTime.from_unix_local(UntypedJson.Object.get_int_member(headline_node, "updated")), - -1, - tags, - enclosures + + var Article = new Article( + UntypedJson.Object.get_string_member(headline_node, "id"), + headline_node.get_string_member("title"), + headline_node.get_string_member("link"), + UntypedJson.Object.get_string_member(headline_node, "feed_id"), + headline_node.get_boolean_member("unread") ? ArticleStatus.UNREAD : ArticleStatus.READ, + headline_node.get_boolean_member("marked") ? ArticleStatus.MARKED : ArticleStatus.UNMARKED, + null, + null, + headline_node.get_string_member("author"), + new DateTime.from_unix_local(UntypedJson.Object.get_int_member(headline_node, "updated")), + -1, + tags, + enclosures ); - - articles.add(Article); + + articles.add(Article); + } } } -} - -// tt-rss server needs newsplusplus extention -public Gee.List<string>? NewsPlus(ArticleStatus type, int limit) -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "getCompactHeadlines"); - message.add_int("feed_id", ttrssUtils.TTRSSSpecialID.ALL); - message.add_int("limit", limit); - if(type == ArticleStatus.UNREAD) - { - message.add_string("view_mode", "unread"); - } - else if(type == ArticleStatus.MARKED) - { - message.add_string("view_mode", "marked"); - } - else + + // tt-rss server needs newsplusplus extention + public Gee.List<string>? NewsPlus(ArticleStatus type, int limit) { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "getCompactHeadlines"); + message.add_int("feed_id", ttrssUtils.TTRSSSpecialID.ALL); + message.add_int("limit", limit); + if(type == ArticleStatus.UNREAD) + { + message.add_string("view_mode", "unread"); + } + else if(type == ArticleStatus.MARKED) + { + message.add_string("view_mode", "marked"); + } + else + { + return null; + } + int status = message.send(); + + if(status == ConnectionError.SUCCESS) + { + var response = message.get_response_array(); + var headline_count = response.get_length(); + + var ids = new Gee.LinkedList<string>(); + + for(uint i = 0; i < headline_count; i++) + { + var headline_node = response.get_object_element(i); + ids.add(UntypedJson.Object.get_string_member(headline_node, "id")); + } + return ids; + } return null; } - int status = message.send(); - - if(status == ConnectionError.SUCCESS) + + + public Gee.List<Article> getArticles(Gee.List<int> articleIDs) { - var response = message.get_response_array(); - var headline_count = response.get_length(); - - var ids = new Gee.LinkedList<string>(); - - for(uint i = 0; i < headline_count; i++) + var articles = new Gee.ArrayList<Article>(); + if(articleIDs.is_empty) { - var headline_node = response.get_object_element(i); - ids.add(UntypedJson.Object.get_string_member(headline_node, "id")); + return articles; } - return ids; - } - return null; -} - - -public Gee.List<Article> getArticles(Gee.List<int> articleIDs) -{ - var articles = new Gee.ArrayList<Article>(); - if(articleIDs.is_empty) - { - return articles; - } - - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "getArticle"); - message.add_comma_separated_int_array("article_id", articleIDs); - int status = message.send(); - - if(status == ConnectionError.SUCCESS) - { - var response = message.get_response_array(); - var article_count = response.get_length(); - Logger.debug(@"Got $article_count new articles"); - - for(uint i = 0; i < article_count; i++) + + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "getArticle"); + message.add_comma_separated_int_array("article_id", articleIDs); + int status = message.send(); + + if(status == ConnectionError.SUCCESS) { - var article_node = response.get_object_element(i); - - Gee.List<string>? tags = null; - if(article_node.has_member("labels")) + var response = message.get_response_array(); + var article_count = response.get_length(); + Logger.debug(@"Got $article_count new articles"); + + for(uint i = 0; i < article_count; i++) { - var labels = article_node.get_array_member("labels"); - - uint tag_count = 0; - if(labels != null) - { - tag_count = labels.get_length(); - } - - if(tag_count > 0) + var article_node = response.get_object_element(i); + + Gee.List<string>? tags = null; + if(article_node.has_member("labels")) { - tags = new Gee.ArrayList<string>(); + var labels = article_node.get_array_member("labels"); + + uint tag_count = 0; + if(labels != null) + { + tag_count = labels.get_length(); + } + + if(tag_count > 0) + { + tags = new Gee.ArrayList<string>(); + } + + for(int j = 0; j < tag_count; ++j) + { + tags.add(labels.get_array_element(j).get_int_element(0).to_string()); + } } - - for(int j = 0; j < tag_count; ++j) + + var enclosures = new Gee.ArrayList<Enclosure>(); + if(article_node.has_member("attachments")) { - tags.add(labels.get_array_element(j).get_int_element(0).to_string()); + var attachments = article_node.get_array_member("attachments"); + + uint mediaCount = 0; + if(attachments != null) + { + mediaCount = attachments.get_length(); + } + + for(int j = 0; j < mediaCount; ++j) + { + var attachment = attachments.get_object_element(j); + enclosures.add(new Enclosure( + UntypedJson.Object.get_string_member(article_node, "id"), + attachment.get_string_member("content_url"), + EnclosureType.from_string(attachment.get_string_member("content_type")))); + } } + + var Article = new Article( + UntypedJson.Object.get_string_member(article_node, "id"), + article_node.get_string_member("title"), + article_node.get_string_member("link"), + UntypedJson.Object.get_string_member(article_node, "feed_id"), + article_node.get_boolean_member("unread") ? ArticleStatus.UNREAD : ArticleStatus.READ, + article_node.get_boolean_member("marked") ? ArticleStatus.MARKED : ArticleStatus.UNMARKED, + article_node.get_string_member("content"), + null, + article_node.get_string_member("author"), + new DateTime.from_unix_local(UntypedJson.Object.get_int_member(article_node, "updated")), + -1, + tags, + enclosures + ); + + articles.add(Article); } - - var enclosures = new Gee.ArrayList<Enclosure>(); - if(article_node.has_member("attachments")) + } + return articles; + } + + public bool catchupFeed(int feedID, bool isCatID) + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "catchupFeed"); + message.add_int("feed_id", feedID); + message.add_bool("is_cat", isCatID); + int status = message.send(); + + if(status == ConnectionError.SUCCESS) + { + var response = message.get_response_object(); + if(response.get_string_member("status") == "OK") { - var attachments = article_node.get_array_member("attachments"); - - uint mediaCount = 0; - if(attachments != null) - { - mediaCount = attachments.get_length(); - } - - for(int j = 0; j < mediaCount; ++j) - { - var attachment = attachments.get_object_element(j); - enclosures.add(new Enclosure( - UntypedJson.Object.get_string_member(article_node, "id"), - attachment.get_string_member("content_url"), - EnclosureType.from_string(attachment.get_string_member("content_type")))); - } + return true; } - - var Article = new Article( - UntypedJson.Object.get_string_member(article_node, "id"), - article_node.get_string_member("title"), - article_node.get_string_member("link"), - UntypedJson.Object.get_string_member(article_node, "feed_id"), - article_node.get_boolean_member("unread") ? ArticleStatus.UNREAD : ArticleStatus.READ, - article_node.get_boolean_member("marked") ? ArticleStatus.MARKED : ArticleStatus.UNMARKED, - article_node.get_string_member("content"), - null, - article_node.get_string_member("author"), - new DateTime.from_unix_local(UntypedJson.Object.get_int_member(article_node, "updated")), - -1, - tags, - enclosures - ); - - articles.add(Article); } + + return false; } - return articles; -} - -public bool catchupFeed(int feedID, bool isCatID) -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "catchupFeed"); - message.add_int("feed_id", feedID); - message.add_bool("is_cat", isCatID); - int status = message.send(); - - if(status == ConnectionError.SUCCESS) + + public bool updateArticleUnread(Gee.List<int> articleIDs, ArticleStatus unread) { - var response = message.get_response_object(); - if(response.get_string_member("status") == "OK") + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "updateArticle"); + message.add_comma_separated_int_array("article_ids", articleIDs); + if(unread == ArticleStatus.UNREAD) { - return true; + message.add_int("mode", 1); } - } - - return false; -} - -public bool updateArticleUnread(Gee.List<int> articleIDs, ArticleStatus unread) -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "updateArticle"); - message.add_comma_separated_int_array("article_ids", articleIDs); - if(unread == ArticleStatus.UNREAD) - { - message.add_int("mode", 1); - } - else if(unread == ArticleStatus.READ) - { - message.add_int("mode", 0); - } - message.add_int("field", 2); - int status = message.send(); - - if(status == ConnectionError.SUCCESS) - { - var response = message.get_response_object(); - if(response.get_string_member("status") == "OK") + else if(unread == ArticleStatus.READ) { - return true; + message.add_int("mode", 0); } - } - - return false; -} - - -public bool updateArticleMarked(int articleID, ArticleStatus marked) -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "updateArticle"); - message.add_int("article_ids", articleID); - if(marked == ArticleStatus.MARKED) - { - message.add_int("mode", 1); - } - else if(marked == ArticleStatus.UNMARKED) - { - message.add_int("mode", 0); - } - message.add_int("field", 0); - int status = message.send(); - - if(status == ConnectionError.SUCCESS) - { - var response = message.get_response_object(); - if(response.get_string_member("status") == "OK") + message.add_int("field", 2); + int status = message.send(); + + if(status == ConnectionError.SUCCESS) { - return true; + var response = message.get_response_object(); + if(response.get_string_member("status") == "OK") + { + return true; + } } - } - - return false; -} - -public bool setArticleLabel(int articleID, int tagID, bool add) -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "setArticleLabel"); - message.add_int("article_ids", articleID); - message.add_int("label_id", tagID); - message.add_bool("assign", add); - int status = message.send(); - - if(status == ConnectionError.SUCCESS) - { - var response = message.get_response_object(); - if(response.get_string_member("status") == "OK") + + return false; + } + + + public bool updateArticleMarked(int articleID, ArticleStatus marked) + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "updateArticle"); + message.add_int("article_ids", articleID); + if(marked == ArticleStatus.MARKED) { - return true; + message.add_int("mode", 1); } - } - - return false; -} - -public int64 addLabel(string caption) -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "addLabel"); - message.add_string("caption", caption); - int status = message.send(); - - if(status == ConnectionError.SUCCESS) - { - return message.get_response_int(); - } - - return 0; -} - -public bool removeLabel(int tagID) -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "removeLabel"); - message.add_int("label_id", tagID); - int status = message.send(); - - return status == ConnectionError.SUCCESS; -} - -public bool renameLabel(int tagID, string newName) -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "renameLabel"); - message.add_int("label_id", tagID); - message.add_string("caption", newName); - int status = message.send(); - - return status == ConnectionError.SUCCESS; -} - - -public bool subscribeToFeed(string feedURL, string? catID, string? username, string? password, out string errmsg) -{ - errmsg = ""; - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "subscribeToFeed"); - message.add_string("feed_url", feedURL); - - if(catID != null) - { - message.add_int("category_id", int.parse(catID)); - } - if(username != null && password != null) - { - message.add_string("login", username); - message.add_string("password", password); - } - - int msg_status = message.send(); - - if(msg_status == ConnectionError.SUCCESS) - { - var response = message.get_response_object(); - if(response.has_member("status")) + else if(marked == ArticleStatus.UNMARKED) + { + message.add_int("mode", 0); + } + message.add_int("field", 0); + int status = message.send(); + + if(status == ConnectionError.SUCCESS) + { + var response = message.get_response_object(); + if(response.get_string_member("status") == "OK") + { + return true; + } + } + + return false; + } + + public bool setArticleLabel(int articleID, int tagID, bool add) + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "setArticleLabel"); + message.add_int("article_ids", articleID); + message.add_int("label_id", tagID); + message.add_bool("assign", add); + int status = message.send(); + + if(status == ConnectionError.SUCCESS) + { + var response = message.get_response_object(); + if(response.get_string_member("status") == "OK") + { + return true; + } + } + + return false; + } + + public int64 addLabel(string caption) + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "addLabel"); + message.add_string("caption", caption); + int status = message.send(); + + if(status == ConnectionError.SUCCESS) + { + return message.get_response_int(); + } + + return 0; + } + + public bool removeLabel(int tagID) + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "removeLabel"); + message.add_int("label_id", tagID); + int status = message.send(); + + return status == ConnectionError.SUCCESS; + } + + public bool renameLabel(int tagID, string newName) + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "renameLabel"); + message.add_int("label_id", tagID); + message.add_string("caption", newName); + int status = message.send(); + + return status == ConnectionError.SUCCESS; + } + + + public bool subscribeToFeed(string feedURL, string? catID, string? username, string? password, out string errmsg) + { + errmsg = ""; + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "subscribeToFeed"); + message.add_string("feed_url", feedURL); + + if(catID != null) + { + message.add_int("category_id", int.parse(catID)); + } + if(username != null && password != null) { - var status = response.get_object_member("status"); - if(status.has_member("code")) + message.add_string("login", username); + message.add_string("password", password); + } + + int msg_status = message.send(); + + if(msg_status == ConnectionError.SUCCESS) + { + var response = message.get_response_object(); + if(response.has_member("status")) { - switch(UntypedJson.Object.get_int_member(status, "code")) + var status = response.get_object_member("status"); + if(status.has_member("code")) { - case 0: - case 1: - return true; - case 2: - errmsg = _("Invalid URL"); - return false; - case 3: - errmsg = _("URL content is HTML, no feeds available"); - return false; - case 4: - errmsg = _("URL content is HTML which contains multiple feeds."); - return false; - case 5: - errmsg = _("Couldn't download the URL content."); - return false; - case 6: - errmsg = _("The content is invalid XML."); - return false; - default: - if(status.has_member("message")) - { - errmsg = status.get_string_member("message"); - } - else + switch(UntypedJson.Object.get_int_member(status, "code")) { - errmsg = "ttrss error"; + case 0: + case 1: + return true; + case 2: + errmsg = _("Invalid URL"); + return false; + case 3: + errmsg = _("URL content is HTML, no feeds available"); + return false; + case 4: + errmsg = _("URL content is HTML which contains multiple feeds."); + return false; + case 5: + errmsg = _("Couldn't download the URL content."); + return false; + case 6: + errmsg = _("The content is invalid XML."); + return false; + default: + if(status.has_member("message")) + { + errmsg = status.get_string_member("message"); + } + else + { + errmsg = "ttrss error"; + } + return false; } - return false; } } } + + errmsg = _("Error reaching tt-rss"); + return false; + } + + public bool unsubscribeFeed(int feedID) + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "unsubscribeFeed"); + message.add_int("feed_id", feedID); + int status = message.send(); + + return status == ConnectionError.SUCCESS; + } + + public string? createCategory(string title, int? parentID = null) + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "addCategory"); + message.add_string("caption", title); + if(parentID != null) + { + message.add_int("parent_id", parentID); + } + int status = message.send(); + + if(status == ConnectionError.SUCCESS) + { + return message.get_response_string(); + } + + return null; } - - errmsg = _("Error reaching tt-rss"); - return false; -} - -public bool unsubscribeFeed(int feedID) -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "unsubscribeFeed"); - message.add_int("feed_id", feedID); - int status = message.send(); - - return status == ConnectionError.SUCCESS; -} - -public string? createCategory(string title, int? parentID = null) -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "addCategory"); - message.add_string("caption", title); - if(parentID != null) - { - message.add_int("parent_id", parentID); - } - int status = message.send(); - - if(status == ConnectionError.SUCCESS) - { - return message.get_response_string(); - } - - return null; -} - -public bool removeCategory(int catID) -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "removeCategory"); - message.add_int("category_id", catID); - int status = message.send(); - - return status == ConnectionError.SUCCESS; -} - -public bool moveCategory(int catID, int parentID) -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "moveCategory"); - message.add_int("category_id", catID); - if(parentID != int.parse(CategoryID.MASTER.to_string())) - { - message.add_int("parent_id", parentID); + + public bool removeCategory(int catID) + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "removeCategory"); + message.add_int("category_id", catID); + int status = message.send(); + + return status == ConnectionError.SUCCESS; + } + + public bool moveCategory(int catID, int parentID) + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "moveCategory"); + message.add_int("category_id", catID); + if(parentID != int.parse(CategoryID.MASTER.to_string())) + { + message.add_int("parent_id", parentID); + } + int status = message.send(); + + return status == ConnectionError.SUCCESS; + } + + public bool renameCategory(int catID, string title) + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "renameCategory"); + message.add_int("category_id", catID); + message.add_string("caption", title); + int status = message.send(); + + return status == ConnectionError.SUCCESS; + } + + public bool renameFeed(int feedID, string title) + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "renameFeed"); + message.add_int("feed_id", feedID); + message.add_string("caption", title); + int status = message.send(); + + return status == ConnectionError.SUCCESS; + } + + public bool moveFeed(int feedID, int catID) + { + var message = new ttrssMessage(m_session, m_ttrss_url); + message.add_string("sid", m_ttrss_sessionid); + message.add_string("op", "moveFeed"); + message.add_int("feed_id", feedID); + message.add_int("category_id", catID); + int status = message.send(); + + return status == ConnectionError.SUCCESS; + } + + public bool ping() + { + Logger.debug("TTRSS: ping"); + var message = new ttrssMessage(m_session, m_ttrss_url); + int status = message.send(true); + + return status == ConnectionError.SUCCESS; } - int status = message.send(); - - return status == ConnectionError.SUCCESS; -} - -public bool renameCategory(int catID, string title) -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "renameCategory"); - message.add_int("category_id", catID); - message.add_string("caption", title); - int status = message.send(); - - return status == ConnectionError.SUCCESS; -} - -public bool renameFeed(int feedID, string title) -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "renameFeed"); - message.add_int("feed_id", feedID); - message.add_string("caption", title); - int status = message.send(); - - return status == ConnectionError.SUCCESS; -} - -public bool moveFeed(int feedID, int catID) -{ - var message = new ttrssMessage(m_session, m_ttrss_url); - message.add_string("sid", m_ttrss_sessionid); - message.add_string("op", "moveFeed"); - message.add_int("feed_id", feedID); - message.add_int("category_id", catID); - int status = message.send(); - - return status == ConnectionError.SUCCESS; -} - -public bool ping() -{ - Logger.debug("TTRSS: ping"); - var message = new ttrssMessage(m_session, m_ttrss_url); - int status = message.send(true); - - return status == ConnectionError.SUCCESS; -} } diff --git a/plugins/backend/ttrss/ttrssInterface.vala b/plugins/backend/ttrss/ttrssInterface.vala index 2b507ab2..0b8a505a 100644 --- a/plugins/backend/ttrss/ttrssInterface.vala +++ b/plugins/backend/ttrss/ttrssInterface.vala @@ -14,531 +14,531 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.ttrssInterface : FeedServerInterface { - -private ttrssAPI m_api; -private ttrssUtils m_utils; -private Gtk.Entry m_urlEntry; -private Gtk.Entry m_userEntry; -private Gtk.Entry m_passwordEntry; -private Gtk.Entry m_authPasswordEntry; -private Gtk.Entry m_authUserEntry; -private Gtk.Revealer m_revealer; - -public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) -{ - m_utils = new ttrssUtils(settings_backend, secrets); - m_api = new ttrssAPI(m_utils); -} - -public override string getWebsite() -{ - return "https://tt-rss.org/"; -} - -public override BackendFlags getFlags() -{ - return (BackendFlags.SELF_HOSTED | BackendFlags.FREE_SOFTWARE | BackendFlags.FREE); -} - -public override string getID() -{ - return "ttrss"; -} - -public override string iconName() -{ - return "feed-service-ttrss"; -} - -public override string serviceName() -{ - return "Tiny Tiny RSS"; -} - -public override bool needWebLogin() -{ - return false; -} - -public override Gtk.Box? getWidget() -{ - var url_label = new Gtk.Label(_("Tiny Tiny RSS URL:")); - var user_label = new Gtk.Label(_("Username:")); - var password_label = new Gtk.Label(_("Password:")); - - url_label.set_alignment(1.0f, 0.5f); - user_label.set_alignment(1.0f, 0.5f); - password_label.set_alignment(1.0f, 0.5f); - - url_label.set_hexpand(true); - user_label.set_hexpand(true); - password_label.set_hexpand(true); - - m_urlEntry = new Gtk.Entry(); - m_userEntry = new Gtk.Entry(); - m_passwordEntry = new Gtk.Entry(); - - m_urlEntry.activate.connect(() => { tryLogin(); }); - m_userEntry.activate.connect(() => { tryLogin(); }); - m_passwordEntry.activate.connect(() => { tryLogin(); }); - - m_passwordEntry.set_input_purpose(Gtk.InputPurpose.PASSWORD); - m_passwordEntry.set_visibility(false); - - var grid = new Gtk.Grid(); - grid.set_column_spacing(10); - grid.set_row_spacing(10); - grid.set_valign(Gtk.Align.CENTER); - grid.set_halign(Gtk.Align.CENTER); - - grid.attach(url_label, 0, 0, 1, 1); - grid.attach(m_urlEntry, 1, 0, 1, 1); - grid.attach(user_label, 0, 1, 1, 1); - grid.attach(m_userEntry, 1, 1, 1, 1); - grid.attach(password_label, 0, 2, 1, 1); - grid.attach(m_passwordEntry, 1, 2, 1, 1); - - - // http auth stuff ---------------------------------------------------- - var auth_user_label = new Gtk.Label(_("Username:")); - var auth_password_label = new Gtk.Label(_("Password:")); - - auth_user_label.set_alignment(1.0f, 0.5f); - auth_password_label.set_alignment(1.0f, 0.5f); - - auth_user_label.set_hexpand(true); - auth_password_label.set_hexpand(true); - - m_authUserEntry = new Gtk.Entry(); - m_authPasswordEntry = new Gtk.Entry(); - m_authPasswordEntry.set_input_purpose(Gtk.InputPurpose.PASSWORD); - m_authPasswordEntry.set_visibility(false); - - m_authUserEntry.activate.connect(() => { tryLogin(); }); - m_authPasswordEntry.activate.connect(() => { tryLogin(); }); - - var authGrid = new Gtk.Grid(); - authGrid.margin = 10; - authGrid.set_column_spacing(10); - authGrid.set_row_spacing(10); - authGrid.set_valign(Gtk.Align.CENTER); - authGrid.set_halign(Gtk.Align.CENTER); - - authGrid.attach(auth_user_label, 0, 0, 1, 1); - authGrid.attach(m_authUserEntry, 1, 0, 1, 1); - authGrid.attach(auth_password_label, 0, 1, 1, 1); - authGrid.attach(m_authPasswordEntry, 1, 1, 1, 1); - - var frame = new Gtk.Frame(_("HTTP Authorization")); - frame.set_halign(Gtk.Align.CENTER); - frame.add(authGrid); - m_revealer = new Gtk.Revealer(); - m_revealer.add(frame); - //--------------------------------------------------------------------- - - var logo = new Gtk.Image.from_icon_name("feed-service-ttrss", Gtk.IconSize.MENU); - - var loginLabel = new Gtk.Label(_("Please log in to your Tiny Tiny RSS server and enjoy using FeedReader")); - loginLabel.get_style_context().add_class("h2"); - loginLabel.set_justify(Gtk.Justification.CENTER); - loginLabel.set_lines(3); - - var loginButton = new Gtk.Button.with_label(_("Login")); - loginButton.halign = Gtk.Align.END; - loginButton.set_size_request(80, 30); - loginButton.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); - loginButton.clicked.connect(() => { tryLogin(); }); - - var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10); - box.valign = Gtk.Align.CENTER; - box.halign = Gtk.Align.CENTER; - box.pack_start(loginLabel, false, false, 10); - box.pack_start(logo, false, false, 10); - box.pack_start(grid, true, true, 10); - box.pack_start(m_revealer, true, true, 10); - box.pack_end(loginButton, false, false, 20); - - m_urlEntry.set_text(m_utils.getUnmodifiedURL()); - m_userEntry.set_text(m_utils.getUser()); - m_passwordEntry.set_text(m_utils.getPasswd()); - - return box; -} - -public override void showHtAccess() -{ - m_revealer.set_reveal_child(true); -} - -public override void writeData() -{ - string url = m_urlEntry.get_text(); - if (GLib.Uri.parse_scheme(url) == null) + + private ttrssAPI m_api; + private ttrssUtils m_utils; + private Gtk.Entry m_urlEntry; + private Gtk.Entry m_userEntry; + private Gtk.Entry m_passwordEntry; + private Gtk.Entry m_authPasswordEntry; + private Gtk.Entry m_authUserEntry; + private Gtk.Revealer m_revealer; + + public override void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) { - url = "https://" + url; - m_urlEntry.set_text(url); + m_utils = new ttrssUtils(settings_backend, secrets); + m_api = new ttrssAPI(m_utils); } - m_utils.setURL(url); - m_utils.setUser(m_userEntry.get_text().strip()); - m_utils.setPassword(m_passwordEntry.get_text().strip()); -} - -public override bool supportTags() -{ - return true; -} - -public override bool doInitSync() -{ - return true; -} - -public override string symbolicIcon() -{ - return "feed-service-ttrss-symbolic"; -} - -public override string accountName() -{ - return m_utils.getUser(); -} - -public override string getServerURL() -{ - return m_utils.getURL(); -} - -public override string uncategorizedID() -{ - return "0"; -} - -public override bool hideCategoryWhenEmpty(string catID) -{ - return catID == "0"; -} - -public override bool supportCategories() -{ - return true; -} - -public override bool supportFeedManipulation() -{ - return true; -} - -public override bool supportMultiLevelCategories() -{ - return true; -} - -public override bool supportMultiCategoriesPerFeed() -{ - return false; -} - -public override bool syncFeedsAndCategories() -{ - return true; -} - -public override bool tagIDaffectedByNameChange() -{ - return false; -} - -public override void resetAccount() -{ - m_utils.resetAccount(); -} - -public override bool useMaxArticles() -{ - return true; -} - -public override LoginResponse login() -{ - return m_api.login(); -} - -public override bool logout() -{ - return m_api.logout(); -} - -public override bool serverAvailable() -{ - return m_api.ping(); -} - -public override void setArticleIsRead(string articleIDs, ArticleStatus read) -{ - var ids = new Gee.ArrayList<int>(); - foreach(var id in StringUtils.split(articleIDs, ",")) + + public override string getWebsite() { - ids.add(int.parse(id)); + return "https://tt-rss.org/"; } - m_api.updateArticleUnread(ids, read); -} - -public override void setArticleIsMarked(string articleID, ArticleStatus marked) -{ - m_api.updateArticleMarked(int.parse(articleID), marked); -} - -public override bool alwaysSetReadByID() -{ - return false; -} - -public override void setFeedRead(string feedID) -{ - m_api.catchupFeed(int.parse(feedID), false); -} - -public override void setCategoryRead(string catID) -{ - m_api.catchupFeed(int.parse(catID), true); -} - -public override void markAllItemsRead() -{ - var categories = DataBase.readOnly().read_categories(); - foreach(Category cat in categories) + + public override BackendFlags getFlags() { - m_api.catchupFeed(int.parse(cat.getCatID()), true); + return (BackendFlags.SELF_HOSTED | BackendFlags.FREE_SOFTWARE | BackendFlags.FREE); } -} - -public override void tagArticle(string articleID, string tagID) -{ - m_api.setArticleLabel(int.parse(articleID), int.parse(tagID), true); -} - -public override void removeArticleTag(string articleID, string tagID) -{ - m_api.setArticleLabel(int.parse(articleID), int.parse(tagID), false); -} - -public override string createTag(string caption) -{ - return m_api.addLabel(caption).to_string(); -} - -public override void deleteTag(string tagID) -{ - m_api.removeLabel(int.parse(tagID)); -} - -public override void renameTag(string tagID, string title) -{ - m_api.renameLabel(int.parse(tagID), title); -} - -public override bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) -{ - bool success = false; - if(catID == null && newCatName != null) + + public override string getID() { - var newCatID = m_api.createCategory(newCatName); - success = m_api.subscribeToFeed(feedURL, newCatID, null, null, out errmsg); + return "ttrss"; } - else + + public override string iconName() { - success = m_api.subscribeToFeed(feedURL, catID, null, null, out errmsg); + return "feed-service-ttrss"; } - - if(success) + + public override string serviceName() { - feedID = (int.parse(DataBase.readOnly().getMaxID("feeds", "feed_id")) + 1).to_string(); + return "Tiny Tiny RSS"; } - else + + public override bool needWebLogin() { - feedID = "-98"; + return false; } - - - return success; -} - -public override void removeFeed(string feedID) -{ - m_api.unsubscribeFeed(int.parse(feedID)); -} - -public override void renameFeed(string feedID, string title) -{ - m_api.renameFeed(int.parse(feedID), title); -} - -public override void moveFeed(string feedID, string newCatID, string? currentCatID) -{ - m_api.moveFeed(int.parse(feedID), int.parse(newCatID)); -} - -public override string createCategory(string title, string? parentID) -{ - if(parentID != null) + + public override Gtk.Box? getWidget() { - return m_api.createCategory(title, int.parse(parentID)); + var url_label = new Gtk.Label(_("Tiny Tiny RSS URL:")); + var user_label = new Gtk.Label(_("Username:")); + var password_label = new Gtk.Label(_("Password:")); + + url_label.set_alignment(1.0f, 0.5f); + user_label.set_alignment(1.0f, 0.5f); + password_label.set_alignment(1.0f, 0.5f); + + url_label.set_hexpand(true); + user_label.set_hexpand(true); + password_label.set_hexpand(true); + + m_urlEntry = new Gtk.Entry(); + m_userEntry = new Gtk.Entry(); + m_passwordEntry = new Gtk.Entry(); + + m_urlEntry.activate.connect(() => { tryLogin(); }); + m_userEntry.activate.connect(() => { tryLogin(); }); + m_passwordEntry.activate.connect(() => { tryLogin(); }); + + m_passwordEntry.set_input_purpose(Gtk.InputPurpose.PASSWORD); + m_passwordEntry.set_visibility(false); + + var grid = new Gtk.Grid(); + grid.set_column_spacing(10); + grid.set_row_spacing(10); + grid.set_valign(Gtk.Align.CENTER); + grid.set_halign(Gtk.Align.CENTER); + + grid.attach(url_label, 0, 0, 1, 1); + grid.attach(m_urlEntry, 1, 0, 1, 1); + grid.attach(user_label, 0, 1, 1, 1); + grid.attach(m_userEntry, 1, 1, 1, 1); + grid.attach(password_label, 0, 2, 1, 1); + grid.attach(m_passwordEntry, 1, 2, 1, 1); + + + // http auth stuff ---------------------------------------------------- + var auth_user_label = new Gtk.Label(_("Username:")); + var auth_password_label = new Gtk.Label(_("Password:")); + + auth_user_label.set_alignment(1.0f, 0.5f); + auth_password_label.set_alignment(1.0f, 0.5f); + + auth_user_label.set_hexpand(true); + auth_password_label.set_hexpand(true); + + m_authUserEntry = new Gtk.Entry(); + m_authPasswordEntry = new Gtk.Entry(); + m_authPasswordEntry.set_input_purpose(Gtk.InputPurpose.PASSWORD); + m_authPasswordEntry.set_visibility(false); + + m_authUserEntry.activate.connect(() => { tryLogin(); }); + m_authPasswordEntry.activate.connect(() => { tryLogin(); }); + + var authGrid = new Gtk.Grid(); + authGrid.margin = 10; + authGrid.set_column_spacing(10); + authGrid.set_row_spacing(10); + authGrid.set_valign(Gtk.Align.CENTER); + authGrid.set_halign(Gtk.Align.CENTER); + + authGrid.attach(auth_user_label, 0, 0, 1, 1); + authGrid.attach(m_authUserEntry, 1, 0, 1, 1); + authGrid.attach(auth_password_label, 0, 1, 1, 1); + authGrid.attach(m_authPasswordEntry, 1, 1, 1, 1); + + var frame = new Gtk.Frame(_("HTTP Authorization")); + frame.set_halign(Gtk.Align.CENTER); + frame.add(authGrid); + m_revealer = new Gtk.Revealer(); + m_revealer.add(frame); + //--------------------------------------------------------------------- + + var logo = new Gtk.Image.from_icon_name("feed-service-ttrss", Gtk.IconSize.MENU); + + var loginLabel = new Gtk.Label(_("Please log in to your Tiny Tiny RSS server and enjoy using FeedReader")); + loginLabel.get_style_context().add_class("h2"); + loginLabel.set_justify(Gtk.Justification.CENTER); + loginLabel.set_lines(3); + + var loginButton = new Gtk.Button.with_label(_("Login")); + loginButton.halign = Gtk.Align.END; + loginButton.set_size_request(80, 30); + loginButton.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); + loginButton.clicked.connect(() => { tryLogin(); }); + + var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10); + box.valign = Gtk.Align.CENTER; + box.halign = Gtk.Align.CENTER; + box.pack_start(loginLabel, false, false, 10); + box.pack_start(logo, false, false, 10); + box.pack_start(grid, true, true, 10); + box.pack_start(m_revealer, true, true, 10); + box.pack_end(loginButton, false, false, 20); + + m_urlEntry.set_text(m_utils.getUnmodifiedURL()); + m_userEntry.set_text(m_utils.getUser()); + m_passwordEntry.set_text(m_utils.getPasswd()); + + return box; } - - return m_api.createCategory(title); -} - -public override void renameCategory(string catID, string title) -{ - m_api.renameCategory(int.parse(catID), title); -} - -public override void moveCategory(string catID, string newParentID) -{ - m_api.moveCategory(int.parse(catID), int.parse(newParentID)); -} - -public override void deleteCategory(string catID) -{ - m_api.removeCategory(int.parse(catID)); -} - -public override void removeCatFromFeed(string feedID, string catID) -{ - return; -} - -public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) -{ - if(m_api.getCategories(categories)) + + public override void showHtAccess() { - if(cancellable != null && cancellable.is_cancelled()) + m_revealer.set_reveal_child(true); + } + + public override void writeData() + { + string url = m_urlEntry.get_text(); + if (GLib.Uri.parse_scheme(url) == null) { - return false; + url = "https://" + url; + m_urlEntry.set_text(url); } - - if(m_api.getFeeds(feeds, categories)) + m_utils.setURL(url); + m_utils.setUser(m_userEntry.get_text().strip()); + m_utils.setPassword(m_passwordEntry.get_text().strip()); + } + + public override bool supportTags() + { + return true; + } + + public override bool doInitSync() + { + return true; + } + + public override string symbolicIcon() + { + return "feed-service-ttrss-symbolic"; + } + + public override string accountName() + { + return m_utils.getUser(); + } + + public override string getServerURL() + { + return m_utils.getURL(); + } + + public override string uncategorizedID() + { + return "0"; + } + + public override bool hideCategoryWhenEmpty(string catID) + { + return catID == "0"; + } + + public override bool supportCategories() + { + return true; + } + + public override bool supportFeedManipulation() + { + return true; + } + + public override bool supportMultiLevelCategories() + { + return true; + } + + public override bool supportMultiCategoriesPerFeed() + { + return false; + } + + public override bool syncFeedsAndCategories() + { + return true; + } + + public override bool tagIDaffectedByNameChange() + { + return false; + } + + public override void resetAccount() + { + m_utils.resetAccount(); + } + + public override bool useMaxArticles() + { + return true; + } + + public override LoginResponse login() + { + return m_api.login(); + } + + public override bool logout() + { + return m_api.logout(); + } + + public override bool serverAvailable() + { + return m_api.ping(); + } + + public override void setArticleIsRead(string articleIDs, ArticleStatus read) + { + var ids = new Gee.ArrayList<int>(); + foreach(var id in StringUtils.split(articleIDs, ",")) + { + ids.add(int.parse(id)); + } + m_api.updateArticleUnread(ids, read); + } + + public override void setArticleIsMarked(string articleID, ArticleStatus marked) + { + m_api.updateArticleMarked(int.parse(articleID), marked); + } + + public override bool alwaysSetReadByID() + { + return false; + } + + public override void setFeedRead(string feedID) + { + m_api.catchupFeed(int.parse(feedID), false); + } + + public override void setCategoryRead(string catID) + { + m_api.catchupFeed(int.parse(catID), true); + } + + public override void markAllItemsRead() + { + var categories = DataBase.readOnly().read_categories(); + foreach(Category cat in categories) + { + m_api.catchupFeed(int.parse(cat.getCatID()), true); + } + } + + public override void tagArticle(string articleID, string tagID) + { + m_api.setArticleLabel(int.parse(articleID), int.parse(tagID), true); + } + + public override void removeArticleTag(string articleID, string tagID) + { + m_api.setArticleLabel(int.parse(articleID), int.parse(tagID), false); + } + + public override string createTag(string caption) + { + return m_api.addLabel(caption).to_string(); + } + + public override void deleteTag(string tagID) + { + m_api.removeLabel(int.parse(tagID)); + } + + public override void renameTag(string tagID, string title) + { + m_api.renameLabel(int.parse(tagID), title); + } + + public override bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) + { + bool success = false; + if(catID == null && newCatName != null) + { + var newCatID = m_api.createCategory(newCatName); + success = m_api.subscribeToFeed(feedURL, newCatID, null, null, out errmsg); + } + else + { + success = m_api.subscribeToFeed(feedURL, catID, null, null, out errmsg); + } + + if(success) + { + feedID = (int.parse(DataBase.readOnly().getMaxID("feeds", "feed_id")) + 1).to_string(); + } + else + { + feedID = "-98"; + } + + + return success; + } + + public override void removeFeed(string feedID) + { + m_api.unsubscribeFeed(int.parse(feedID)); + } + + public override void renameFeed(string feedID, string title) + { + m_api.renameFeed(int.parse(feedID), title); + } + + public override void moveFeed(string feedID, string newCatID, string? currentCatID) + { + m_api.moveFeed(int.parse(feedID), int.parse(newCatID)); + } + + public override string createCategory(string title, string? parentID) + { + if(parentID != null) + { + return m_api.createCategory(title, int.parse(parentID)); + } + + return m_api.createCategory(title); + } + + public override void renameCategory(string catID, string title) + { + m_api.renameCategory(int.parse(catID), title); + } + + public override void moveCategory(string catID, string newParentID) + { + m_api.moveCategory(int.parse(catID), int.parse(newParentID)); + } + + public override void deleteCategory(string catID) + { + m_api.removeCategory(int.parse(catID)); + } + + public override void removeCatFromFeed(string feedID, string catID) + { + return; + } + + public override bool getFeedsAndCats(Gee.List<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> tags, GLib.Cancellable? cancellable = null) + { + if(m_api.getCategories(categories)) { if(cancellable != null && cancellable.is_cancelled()) { return false; } - - if(m_api.getUncategorizedFeeds(feeds)) + + if(m_api.getFeeds(feeds, categories)) { if(cancellable != null && cancellable.is_cancelled()) { return false; } - - if(m_api.getTags(tags)) + + if(m_api.getUncategorizedFeeds(feeds)) { - return true; + if(cancellable != null && cancellable.is_cancelled()) + { + return false; + } + + if(m_api.getTags(tags)) + { + return true; + } } } } + + return false; } - - return false; -} - -public override int getUnreadCount() -{ - return m_api.getUnreadCount(); -} - -public override void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) -{ - var settings_general = new GLib.Settings("org.gnome.feedreader"); - - // first use newsPlus plugin to update states of 10x as much articles as we would normaly do - var unreadIDs = m_api.NewsPlus(ArticleStatus.UNREAD, 10*settings_general.get_int("max-articles")); - - if(cancellable != null && cancellable.is_cancelled()) + + public override int getUnreadCount() { - return; + return m_api.getUnreadCount(); } - - var db = DataBase.writeAccess(); - if(unreadIDs != null && whatToGet == ArticleStatus.ALL) - { - Logger.debug("getArticles: newsplus plugin active"); - var markedIDs = m_api.NewsPlus(ArticleStatus.MARKED, settings_general.get_int("max-articles")); - db.updateArticlesByID(unreadIDs, "unread"); - db.updateArticlesByID(markedIDs, "marked"); - //updateArticleList(); - } - - if(cancellable != null && cancellable.is_cancelled()) - { - return; - } - - var articleIDs = new Gee.ArrayList<int>(); - int skip = count; - int amount = 200; - - while(skip > 0) + + public override void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) { + var settings_general = new GLib.Settings("org.gnome.feedreader"); + + // first use newsPlus plugin to update states of 10x as much articles as we would normaly do + var unreadIDs = m_api.NewsPlus(ArticleStatus.UNREAD, 10*settings_general.get_int("max-articles")); + if(cancellable != null && cancellable.is_cancelled()) { return; } - - if(skip >= amount) + + var db = DataBase.writeAccess(); + if(unreadIDs != null && whatToGet == ArticleStatus.ALL) { - skip -= amount; + Logger.debug("getArticles: newsplus plugin active"); + var markedIDs = m_api.NewsPlus(ArticleStatus.MARKED, settings_general.get_int("max-articles")); + db.updateArticlesByID(unreadIDs, "unread"); + db.updateArticlesByID(markedIDs, "marked"); + //updateArticleList(); } - else - { - amount = skip; - skip = 0; - } - - var articles = new Gee.LinkedList<Article>(); - m_api.getHeadlines(articles, skip, amount, whatToGet, (feedID == null) ? ttrssUtils.TTRSSSpecialID.ALL : int.parse(feedID)); - - // only update article states if they haven't been updated by the newsPlus-plugin - if(unreadIDs == null || whatToGet != ArticleStatus.ALL) + + if(cancellable != null && cancellable.is_cancelled()) { - db.update_articles(articles); - updateArticleList(); + return; } - - foreach(Article article in articles) + + var articleIDs = new Gee.ArrayList<int>(); + int skip = count; + int amount = 200; + + while(skip > 0) { - var id = article.getArticleID(); - if(!db.article_exists(id)) + if(cancellable != null && cancellable.is_cancelled()) { - articleIDs.add(int.parse(id)); + return; + } + + if(skip >= amount) + { + skip -= amount; + } + else + { + amount = skip; + skip = 0; + } + + var articles = new Gee.LinkedList<Article>(); + m_api.getHeadlines(articles, skip, amount, whatToGet, (feedID == null) ? ttrssUtils.TTRSSSpecialID.ALL : int.parse(feedID)); + + // only update article states if they haven't been updated by the newsPlus-plugin + if(unreadIDs == null || whatToGet != ArticleStatus.ALL) + { + db.update_articles(articles); + updateArticleList(); + } + + foreach(Article article in articles) + { + var id = article.getArticleID(); + if(!db.article_exists(id)) + { + articleIDs.add(int.parse(id)); + } } } - } - var articles = m_api.getArticles(articleIDs); - var article_id_strings = new Gee.ArrayList<string>(); - foreach(int id in articleIDs) - { - article_id_strings.add(id.to_string()); - } - Logger.info("Getting articles: " + StringUtils.join(article_id_strings, ",")); - - articles.sort((a, b) => { + var articles = m_api.getArticles(articleIDs); + var article_id_strings = new Gee.ArrayList<string>(); + foreach(int id in articleIDs) + { + article_id_strings.add(id.to_string()); + } + Logger.info("Getting articles: " + StringUtils.join(article_id_strings, ",")); + + articles.sort((a, b) => { return strcmp(a.getArticleID(), b.getArticleID()); }); - - if(cancellable != null && cancellable.is_cancelled()) - { - return; - } - - if(articles.size > 0) - { - db.write_articles(articles); - refreshFeedListCounter(); - updateArticleList(); + + if(cancellable != null && cancellable.is_cancelled()) + { + return; + } + + if(articles.size > 0) + { + db.write_articles(articles); + refreshFeedListCounter(); + updateArticleList(); + } } -} - + } [ModuleInit] diff --git a/plugins/backend/ttrss/ttrssMessage.vala b/plugins/backend/ttrss/ttrssMessage.vala index 8a5cf907..98d13b3f 100644 --- a/plugins/backend/ttrss/ttrssMessage.vala +++ b/plugins/backend/ttrss/ttrssMessage.vala @@ -14,235 +14,235 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.ttrssMessage : GLib.Object { - -private Soup.Session m_session; -private Soup.Message m_message_soup; -private Json.Object m_request_object = new Json.Object(); -private const string m_contenttype = "application/x-www-form-urlencoded"; -private Json.Object m_response_object; - -public ttrssMessage(Soup.Session session, string destination) -{ - m_session = session; - - m_message_soup = new Soup.Message("POST", destination); - - if(m_message_soup == null) - { - Logger.error(@"ttrssMessage: can't parse URL $destination"); - } -} - -public void add_int(string type, int val) -{ - m_request_object.set_int_member(type, val); -} - -public void add_comma_separated_int_array(string type, Gee.List<int> values) -{ - var strings = new Gee.ArrayList<string>(); - foreach(int value in values) - { - strings.add(value.to_string()); - } - m_request_object.set_string_member(type, StringUtils.join(strings, ",")); -} - -public void add_bool(string type, bool val) -{ - m_request_object.set_boolean_member(type, val); -} - -public void add_string(string type, string val) -{ - m_request_object.set_string_member(type, val); -} - -private static string object_to_string(Json.Object obj) -{ - var root = new Json.Node(Json.NodeType.OBJECT); - root.set_object(obj); - - var gen = new Json.Generator(); - gen.set_root(root); - return gen.to_data(null); -} - -public ConnectionError send(bool ping = false) -{ - var error = send_impl(ping); - if(error != ConnectionError.SUCCESS) - { - logError("Error response from TT-RSS API"); + + private Soup.Session m_session; + private Soup.Message m_message_soup; + private Json.Object m_request_object = new Json.Object(); + private const string m_contenttype = "application/x-www-form-urlencoded"; + private Json.Object m_response_object; + + public ttrssMessage(Soup.Session session, string destination) + { + m_session = session; + + m_message_soup = new Soup.Message("POST", destination); + + if(m_message_soup == null) + { + Logger.error(@"ttrssMessage: can't parse URL $destination"); + } } - - return error; -} - -public ConnectionError send_impl(bool ping) -{ - if(m_message_soup == null) + + public void add_int(string type, int val) { - Logger.error(@"ttrssMessage: can't send message"); - return ConnectionError.UNKNOWN; + m_request_object.set_int_member(type, val); } - - var settingsTweaks = new GLib.Settings("org.gnome.feedreader.tweaks"); - - var data = object_to_string(m_request_object); - m_message_soup.set_request(m_contenttype, Soup.MemoryUse.COPY, data.data); - - if(settingsTweaks.get_boolean("do-not-track")) + + public void add_comma_separated_int_array(string type, Gee.List<int> values) { - m_message_soup.request_headers.append("DNT", "1"); + var strings = new Gee.ArrayList<string>(); + foreach(int value in values) + { + strings.add(value.to_string()); + } + m_request_object.set_string_member(type, StringUtils.join(strings, ",")); } - - var status_code = m_session.send_message(m_message_soup); - - if(status_code == 401) // unauthorized - + + public void add_bool(string type, bool val) { - return ConnectionError.UNAUTHORIZED; + m_request_object.set_boolean_member(type, val); } - - if(m_message_soup.tls_errors != 0 && !settingsTweaks.get_boolean("ignore-tls-errors")) + + public void add_string(string type, string val) { - Logger.info("TLS errors: " + Utils.printTlsCertificateFlags(m_message_soup.tls_errors)); - return ConnectionError.CA_ERROR; + m_request_object.set_string_member(type, val); } - - if(m_message_soup.status_code != 200) + + private static string object_to_string(Json.Object obj) { - Logger.error("TTRSS Message: No response - status code: %s".printf(Soup.Status.get_phrase(m_message_soup.status_code))); - return ConnectionError.NO_RESPONSE; + var root = new Json.Node(Json.NodeType.OBJECT); + root.set_object(obj); + + var gen = new Json.Generator(); + gen.set_root(root); + return gen.to_data(null); } - - if(ping) + + public ConnectionError send(bool ping = false) { - Logger.debug("TTRSS Message: ping successful"); - return ConnectionError.SUCCESS; + var error = send_impl(ping); + if(error != ConnectionError.SUCCESS) + { + logError("Error response from TT-RSS API"); + } + + return error; } - - var parser = new Json.Parser(); - try + + public ConnectionError send_impl(bool ping) { - parser.load_from_data((string)m_message_soup.response_body.flatten().data); + if(m_message_soup == null) + { + Logger.error(@"ttrssMessage: can't send message"); + return ConnectionError.UNKNOWN; + } + + var settingsTweaks = new GLib.Settings("org.gnome.feedreader.tweaks"); + + var data = object_to_string(m_request_object); + m_message_soup.set_request(m_contenttype, Soup.MemoryUse.COPY, data.data); + + if(settingsTweaks.get_boolean("do-not-track")) + { + m_message_soup.request_headers.append("DNT", "1"); + } + + var status_code = m_session.send_message(m_message_soup); + + if(status_code == 401) // unauthorized + + { + return ConnectionError.UNAUTHORIZED; + } + + if(m_message_soup.tls_errors != 0 && !settingsTweaks.get_boolean("ignore-tls-errors")) + { + Logger.info("TLS errors: " + Utils.printTlsCertificateFlags(m_message_soup.tls_errors)); + return ConnectionError.CA_ERROR; + } + + if(m_message_soup.status_code != 200) + { + Logger.error("TTRSS Message: No response - status code: %s".printf(Soup.Status.get_phrase(m_message_soup.status_code))); + return ConnectionError.NO_RESPONSE; + } + + if(ping) + { + Logger.debug("TTRSS Message: ping successful"); + return ConnectionError.SUCCESS; + } + + var parser = new Json.Parser(); + try + { + parser.load_from_data((string)m_message_soup.response_body.flatten().data); + } + catch(Error e) + { + Logger.error("Could not load response from Message to ttrss"); + Logger.error(e.message); + return ConnectionError.NO_RESPONSE; + } + + m_response_object = parser.get_root().get_object(); + + if(m_response_object.has_member("error")) + { + parseError(m_response_object); + } + + + var status = UntypedJson.Object.get_int_member(m_response_object, "status"); + if (status == 0) + { + return ConnectionError.SUCCESS; + } + else if (status == 1) + { + if(m_response_object.has_member("content")) + { + var content = m_response_object.get_object_member("content"); + if(content.has_member("error")) + { + parseError(content); + } + } + + return apiError(); + } + + logError("unknown error while sending ttrss message"); + return ConnectionError.UNKNOWN; } - catch(Error e) + + public Json.Object? get_response_object() { - Logger.error("Could not load response from Message to ttrss"); - Logger.error(e.message); - return ConnectionError.NO_RESPONSE; + if(m_response_object.has_member("content")) + { + return m_response_object.get_object_member("content"); + } + return null; } - - m_response_object = parser.get_root().get_object(); - - if(m_response_object.has_member("error")) + + public int64? get_response_int() { - parseError(m_response_object); + return UntypedJson.Object.get_int_member(m_response_object, "content"); } - - - var status = UntypedJson.Object.get_int_member(m_response_object, "status"); - if (status == 0) + + public string? get_response_string() { - return ConnectionError.SUCCESS; + return UntypedJson.Object.get_string_member(m_response_object, "content"); } - else if (status == 1) + + public Json.Array? get_response_array() { if(m_response_object.has_member("content")) { - var content = m_response_object.get_object_member("content"); - if(content.has_member("error")) - { - parseError(content); - } + return m_response_object.get_array_member("content"); } - - return apiError(); + return null; } - - logError("unknown error while sending ttrss message"); - return ConnectionError.UNKNOWN; -} - -public Json.Object? get_response_object() -{ - if(m_response_object.has_member("content")) + + public uint getStatusCode() { - return m_response_object.get_object_member("content"); + return m_message_soup.status_code; } - return null; -} - -public int64? get_response_int() -{ - return UntypedJson.Object.get_int_member(m_response_object, "content"); -} - -public string? get_response_string() -{ - return UntypedJson.Object.get_string_member(m_response_object, "content"); -} - -public Json.Array? get_response_array() -{ - if(m_response_object.has_member("content")) + + private void logError(string prefix) { - return m_response_object.get_array_member("content"); - } - return null; -} - -public uint getStatusCode() -{ - return m_message_soup.status_code; -} - -private void logError(string prefix) -{ - var url = m_message_soup.get_uri().to_string(false); - var obj = m_request_object; - if(obj.has_member("password")) - { - obj = new Json.Object(); - m_request_object.foreach_member((_, name, member) => - { - if(name == "password") - { - obj.set_string_member("password", "[redacted]"); - } - else + var url = m_message_soup.get_uri().to_string(false); + var obj = m_request_object; + if(obj.has_member("password")) + { + obj = new Json.Object(); + m_request_object.foreach_member((_, name, member) => { - obj.set_member(name, member); - } - }); - } - var request = object_to_string(obj); - var response = (string)m_message_soup.response_body.flatten().data; - Logger.error(@"$prefix\nURL: $url\nRequest object: $request\nResponse: $response"); -} - -private ConnectionError parseError(Json.Object err) -{ - string error = err.get_string_member("error"); - if(error == "NOT_LOGGED_IN") - { - Logger.error("invalid ttrss session id"); - return ConnectionError.INVALID_SESSIONID; - } - else if(error == "API_DISABLED") - { - Logger.error("ttrss api is disabled: please enable it first"); - return ConnectionError.API_DISABLED; + if(name == "password") + { + obj.set_string_member("password", "[redacted]"); + } + else + { + obj.set_member(name, member); + } + }); + } + var request = object_to_string(obj); + var response = (string)m_message_soup.response_body.flatten().data; + Logger.error(@"$prefix\nURL: $url\nRequest object: $request\nResponse: $response"); + } + + private ConnectionError parseError(Json.Object err) + { + string error = err.get_string_member("error"); + if(error == "NOT_LOGGED_IN") + { + Logger.error("invalid ttrss session id"); + return ConnectionError.INVALID_SESSIONID; + } + else if(error == "API_DISABLED") + { + Logger.error("ttrss api is disabled: please enable it first"); + return ConnectionError.API_DISABLED; + } + + return apiError(); + } + + private ConnectionError apiError() + { + logError("TT-RSS API error"); + return ConnectionError.API_ERROR; + } } - - return apiError(); -} - -private ConnectionError apiError() -{ - logError("TT-RSS API error"); - return ConnectionError.API_ERROR; -} -} diff --git a/plugins/backend/ttrss/ttrssUtils.vala b/plugins/backend/ttrss/ttrssUtils.vala index ae10522d..dabc32b6 100644 --- a/plugins/backend/ttrss/ttrssUtils.vala +++ b/plugins/backend/ttrss/ttrssUtils.vala @@ -14,136 +14,136 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.ttrssUtils : GLib.Object { - -public enum TTRSSSpecialID { - ARCHIVED = 0, - STARRED = -1, - PUBLISHED = -2, - FRESH = -3, - ALL = -4, - RECENTLY_READ = -6 -} - -private GLib.Settings m_settings; -private Password m_password; -private Password m_htaccess_password; - -public ttrssUtils(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) -{ - if(settings_backend != null) - { - m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.ttrss", settings_backend); + + public enum TTRSSSpecialID { + ARCHIVED = 0, + STARRED = -1, + PUBLISHED = -2, + FRESH = -3, + ALL = -4, + RECENTLY_READ = -6 } - else + + private GLib.Settings m_settings; + private Password m_password; + private Password m_htaccess_password; + + public ttrssUtils(GLib.SettingsBackend? settings_backend, Secret.Collection secrets) { - m_settings = new GLib.Settings("org.gnome.feedreader.ttrss"); - } - - var pwSchema = new Secret.Schema ("org.gnome.feedreader.password", Secret.SchemaFlags.NONE, - "URL", Secret.SchemaAttributeType.STRING, - "Username", Secret.SchemaAttributeType.STRING); - m_password = new Password(secrets, pwSchema, "FeedReader: ttrss login", () => { + if(settings_backend != null) + { + m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.ttrss", settings_backend); + } + else + { + m_settings = new GLib.Settings("org.gnome.feedreader.ttrss"); + } + + var pwSchema = new Secret.Schema ("org.gnome.feedreader.password", Secret.SchemaFlags.NONE, + "URL", Secret.SchemaAttributeType.STRING, + "Username", Secret.SchemaAttributeType.STRING); + m_password = new Password(secrets, pwSchema, "FeedReader: ttrss login", () => { var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); attributes["URL"] = getURL(); attributes["Username"] = getUser(); return attributes; }); - - var htAccessSchema = new Secret.Schema ("org.gnome.feedreader.password", Secret.SchemaFlags.NONE, - "URL", Secret.SchemaAttributeType.STRING, - "Username", Secret.SchemaAttributeType.STRING, - "htaccess", Secret.SchemaAttributeType.BOOLEAN); - m_htaccess_password = new Password(secrets, htAccessSchema, "FeedReader: ttrss htaccess Authentication", () => { + + var htAccessSchema = new Secret.Schema ("org.gnome.feedreader.password", Secret.SchemaFlags.NONE, + "URL", Secret.SchemaAttributeType.STRING, + "Username", Secret.SchemaAttributeType.STRING, + "htaccess", Secret.SchemaAttributeType.BOOLEAN); + m_htaccess_password = new Password(secrets, htAccessSchema, "FeedReader: ttrss htaccess Authentication", () => { var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); attributes["URL"] = getURL(); attributes["Username"] = getHtaccessUser(); attributes["htaccess"] = "true"; return attributes; }); -} - -public string getURL() -{ - - string tmp_url = Utils.gsettingReadString(m_settings, "url"); - - if(tmp_url != "") + } + + public string getURL() { - if(!tmp_url.has_suffix("/")) + + string tmp_url = Utils.gsettingReadString(m_settings, "url"); + + if(tmp_url != "") { - tmp_url = tmp_url + "/"; - } - - if(!tmp_url.has_suffix("/api/")) - { - tmp_url = tmp_url + "api/"; - } - - if(!tmp_url.has_prefix("http://") && !tmp_url.has_prefix("https://")) - { - tmp_url = "https://" + tmp_url; + if(!tmp_url.has_suffix("/")) + { + tmp_url = tmp_url + "/"; + } + + if(!tmp_url.has_suffix("/api/")) + { + tmp_url = tmp_url + "api/"; + } + + if(!tmp_url.has_prefix("http://") && !tmp_url.has_prefix("https://")) + { + tmp_url = "https://" + tmp_url; + } } + + Logger.debug("ttrss URL: " + tmp_url); + + return tmp_url; + } + + public void setURL(string url) + { + Utils.gsettingWriteString(m_settings, "url", url); + } + + public string getUser() + { + return Utils.gsettingReadString(m_settings, "username"); + } + + public void setUser(string user) + { + Utils.gsettingWriteString(m_settings, "username", user); + } + + public string getHtaccessUser() + { + return Utils.gsettingReadString(m_settings, "htaccess-username"); + } + + public void setHtaccessUser(string ht_user) + { + Utils.gsettingWriteString(m_settings, "htaccess-username", ht_user); + } + + public string getUnmodifiedURL() + { + return Utils.gsettingReadString(m_settings, "url"); + } + + public string getPasswd() + { + return m_password.get_password(); + } + + public void setPassword(string passwd) + { + m_password.set_password(passwd); + } + + public void resetAccount() + { + Utils.resetSettings(m_settings); + m_password.delete_password(); + m_htaccess_password.delete_password(); + } + + public string getHtaccessPasswd() + { + return m_htaccess_password.get_password(); + } + + public void setHtAccessPassword(string passwd) + { + m_htaccess_password.set_password(passwd); } - - Logger.debug("ttrss URL: " + tmp_url); - - return tmp_url; -} - -public void setURL(string url) -{ - Utils.gsettingWriteString(m_settings, "url", url); -} - -public string getUser() -{ - return Utils.gsettingReadString(m_settings, "username"); -} - -public void setUser(string user) -{ - Utils.gsettingWriteString(m_settings, "username", user); -} - -public string getHtaccessUser() -{ - return Utils.gsettingReadString(m_settings, "htaccess-username"); -} - -public void setHtaccessUser(string ht_user) -{ - Utils.gsettingWriteString(m_settings, "htaccess-username", ht_user); -} - -public string getUnmodifiedURL() -{ - return Utils.gsettingReadString(m_settings, "url"); -} - -public string getPasswd() -{ - return m_password.get_password(); -} - -public void setPassword(string passwd) -{ - m_password.set_password(passwd); -} - -public void resetAccount() -{ - Utils.resetSettings(m_settings); - m_password.delete_password(); - m_htaccess_password.delete_password(); -} - -public string getHtaccessPasswd() -{ - return m_htaccess_password.get_password(); -} - -public void setHtAccessPassword(string passwd) -{ - m_htaccess_password.set_password(passwd); -} } diff --git a/plugins/share/Browser/Browser.vala b/plugins/share/Browser/Browser.vala index 82f5b164..90428f60 100644 --- a/plugins/share/Browser/Browser.vala +++ b/plugins/share/Browser/Browser.vala @@ -15,91 +15,91 @@ public class FeedReader.Browser : ShareAccountInterface, Peas.ExtensionBase { - -public bool addBookmark(string id, string url, bool system) -{ - try + + public bool addBookmark(string id, string url, bool system) + { + try + { + Gtk.show_uri_on_window(MainWindow.get_default(), url, Gdk.CURRENT_TIME); + return true; + } + catch(GLib.Error e) + { + Logger.error("BrowserPlugin: Error opening url: " + e.message); + } + + return false; + } + + public void setupSystemAccounts(Gee.List<ShareAccount> accounts) + { + + } + + public bool logout(string id) + { + return false; + } + + public string getIconName() + { + if(Gtk.IconTheme.get_default().lookup_icon("applications-internet", 0, Gtk.IconLookupFlags.FORCE_SVG) != null) + { + return "applications-internet"; + } + + return "feed-share-browser"; + } + + public string getUsername(string id) + { + return "Browser"; + } + + public bool needSetup() + { + return false; + } + + public bool singleInstance() { - Gtk.show_uri_on_window(MainWindow.get_default(), url, Gdk.CURRENT_TIME); return true; } - catch(GLib.Error e) + + public bool useSystemAccounts() { - Logger.error("BrowserPlugin: Error opening url: " + e.message); + return false; } - - return false; -} - -public void setupSystemAccounts(Gee.List<ShareAccount> accounts) -{ - -} - -public bool logout(string id) -{ - return false; -} - -public string getIconName() -{ - if(Gtk.IconTheme.get_default().lookup_icon("applications-internet", 0, Gtk.IconLookupFlags.FORCE_SVG) != null) + + public string pluginID() { - return "applications-internet"; + return "browser"; + } + + public string pluginName() + { + return _("Open in Browser"); + } + + public ServiceSetup? newSetup_withID(string id, string username) + { + return null; + } + + public ServiceSetup? newSetup() + { + return null; + } + + public ServiceSetup? newSystemAccount(string id, string username) + { + return null; + } + + public ShareForm? shareWidget(string url) + { + return null; } - - return "feed-share-browser"; -} - -public string getUsername(string id) -{ - return "Browser"; -} - -public bool needSetup() -{ - return false; -} - -public bool singleInstance() -{ - return true; -} - -public bool useSystemAccounts() -{ - return false; -} - -public string pluginID() -{ - return "browser"; -} - -public string pluginName() -{ - return _("Open in Browser"); -} - -public ServiceSetup? newSetup_withID(string id, string username) -{ - return null; -} - -public ServiceSetup? newSetup() -{ - return null; -} - -public ServiceSetup? newSystemAccount(string id, string username) -{ - return null; -} - -public ShareForm? shareWidget(string url) -{ - return null; -} } [ModuleInit] diff --git a/plugins/share/Email/Email.vala b/plugins/share/Email/Email.vala index 3dd9c35e..e0bf3b7e 100644 --- a/plugins/share/Email/Email.vala +++ b/plugins/share/Email/Email.vala @@ -15,104 +15,104 @@ public class FeedReader.ShareMail : ShareAccountInterface, Peas.ExtensionBase { - -private string m_body; -private string m_to; - -public bool addBookmark(string id, string url, bool system) -{ - string subject = GLib.Uri.escape_string("Amazing article"); - string body = GLib.Uri.escape_string(m_body.replace("$URL", url)); - string mailto = @"mailto:$m_to?subject=$subject&body=$body"; - Logger.debug(mailto); - - try + + private string m_body; + private string m_to; + + public bool addBookmark(string id, string url, bool system) + { + string subject = GLib.Uri.escape_string("Amazing article"); + string body = GLib.Uri.escape_string(m_body.replace("$URL", url)); + string mailto = @"mailto:$m_to?subject=$subject&body=$body"; + Logger.debug(mailto); + + try + { + Gtk.show_uri_on_window(MainWindow.get_default(), mailto, Gdk.CURRENT_TIME); + return true; + } + catch(GLib.Error e) + { + Logger.error("share via mail failed: %s".printf(e.message)); + } + + return false; + } + + public void setupSystemAccounts(Gee.List<ShareAccount> accounts) + { + + } + + public bool logout(string id) + { + return false; + } + + public string getIconName() + { + if(Gtk.IconTheme.get_default().lookup_icon("mail-send", 0, Gtk.IconLookupFlags.FORCE_SVG) != null) + { + return "mail-send"; + } + + return "feed-share-mail"; + } + + public string getUsername(string id) + { + return "Email"; + } + + public bool needSetup() + { + return false; + } + + public bool singleInstance() { - Gtk.show_uri_on_window(MainWindow.get_default(), mailto, Gdk.CURRENT_TIME); return true; } - catch(GLib.Error e) + + public bool useSystemAccounts() { - Logger.error("share via mail failed: %s".printf(e.message)); + return false; } - - return false; -} - -public void setupSystemAccounts(Gee.List<ShareAccount> accounts) -{ - -} - -public bool logout(string id) -{ - return false; -} - -public string getIconName() -{ - if(Gtk.IconTheme.get_default().lookup_icon("mail-send", 0, Gtk.IconLookupFlags.FORCE_SVG) != null) + + public string pluginID() { - return "mail-send"; + return "mail"; } - - return "feed-share-mail"; -} - -public string getUsername(string id) -{ - return "Email"; -} - -public bool needSetup() -{ - return false; -} - -public bool singleInstance() -{ - return true; -} - -public bool useSystemAccounts() -{ - return false; -} - -public string pluginID() -{ - return "mail"; -} - -public string pluginName() -{ - return "Email"; -} - -public ServiceSetup? newSetup_withID(string id, string username) -{ - return null; -} - -public ServiceSetup? newSetup() -{ - return null; -} - -public ServiceSetup? newSystemAccount(string id, string username) -{ - return null; -} - -public ShareForm? shareWidget(string url) -{ - var widget = new EmailForm(url); - widget.share.connect(() => { + + public string pluginName() + { + return "Email"; + } + + public ServiceSetup? newSetup_withID(string id, string username) + { + return null; + } + + public ServiceSetup? newSetup() + { + return null; + } + + public ServiceSetup? newSystemAccount(string id, string username) + { + return null; + } + + public ShareForm? shareWidget(string url) + { + var widget = new EmailForm(url); + widget.share.connect(() => { m_to = widget.getTo(); m_body = widget.getBody(); }); - return widget; -} + return widget; + } } [ModuleInit] diff --git a/plugins/share/Email/EmailForm.vala b/plugins/share/Email/EmailForm.vala index 31f1cf8e..dec4f6ce 100644 --- a/plugins/share/Email/EmailForm.vala +++ b/plugins/share/Email/EmailForm.vala @@ -15,78 +15,78 @@ public class FeedReader.EmailForm : ShareForm { - -private Gtk.Entry m_entry; -private Gtk.TextView m_textView; - -public EmailForm(string url) -{ - string body = _("Hey,\n\nCheck out this interesting article I used FeedReader to read: $URL"); - string to = "john.doe@domain.com"; - - var labelTo = new Gtk.Label(_("To:")); - labelTo.set_alignment(0.0f, 0.5f); - labelTo.get_style_context().add_class("h3"); - m_entry = new Gtk.Entry(); - m_entry.set_text(to); - var box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 5); - box.pack_start(labelTo, false, false); - box.pack_start(m_entry, true, true); - - m_textView = new Gtk.TextView(); - m_textView.get_style_context().add_class("h3"); - m_textView.set_wrap_mode(Gtk.WrapMode.WORD); - m_textView.buffer.text = body; - m_textView.border_width = 2; - - var scrolled = new Gtk.ScrolledWindow(null, null); - scrolled.get_style_context().add_class(Gtk.STYLE_CLASS_FRAME); - scrolled.add(m_textView); - - int margin = 5; - m_textView.left_margin = margin; - m_textView.right_margin = margin; - m_textView.top_margin = margin; - m_textView.bottom_margin = margin; - - var button = new Gtk.Button.with_label(_("Send")); - button.halign = Gtk.Align.END; - button.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); - button.clicked.connect(() => { share(); }); - - var backButton = new Gtk.Button.from_icon_name("go-previous-symbolic"); - backButton.set_focus_on_click(false); - backButton.set_relief(Gtk.ReliefStyle.NONE); - backButton.halign = Gtk.Align.START; - backButton.clicked.connect(() => { + + private Gtk.Entry m_entry; + private Gtk.TextView m_textView; + + public EmailForm(string url) + { + string body = _("Hey,\n\nCheck out this interesting article I used FeedReader to read: $URL"); + string to = "john.doe@domain.com"; + + var labelTo = new Gtk.Label(_("To:")); + labelTo.set_alignment(0.0f, 0.5f); + labelTo.get_style_context().add_class("h3"); + m_entry = new Gtk.Entry(); + m_entry.set_text(to); + var box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 5); + box.pack_start(labelTo, false, false); + box.pack_start(m_entry, true, true); + + m_textView = new Gtk.TextView(); + m_textView.get_style_context().add_class("h3"); + m_textView.set_wrap_mode(Gtk.WrapMode.WORD); + m_textView.buffer.text = body; + m_textView.border_width = 2; + + var scrolled = new Gtk.ScrolledWindow(null, null); + scrolled.get_style_context().add_class(Gtk.STYLE_CLASS_FRAME); + scrolled.add(m_textView); + + int margin = 5; + m_textView.left_margin = margin; + m_textView.right_margin = margin; + m_textView.top_margin = margin; + m_textView.bottom_margin = margin; + + var button = new Gtk.Button.with_label(_("Send")); + button.halign = Gtk.Align.END; + button.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); + button.clicked.connect(() => { share(); }); + + var backButton = new Gtk.Button.from_icon_name("go-previous-symbolic"); + backButton.set_focus_on_click(false); + backButton.set_relief(Gtk.ReliefStyle.NONE); + backButton.halign = Gtk.Align.START; + backButton.clicked.connect(() => { goBack(); }); - - var headline = new Gtk.Label(_("Write Email")); - headline.get_style_context().add_class("h2"); - headline.set_alignment(0.4f, 0.5f); - var box2 = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); - box2.pack_start(backButton, false, false, 0); - box2.pack_start(headline, true, true, 0); - - this.pack_start(box2, false, false, 0); - this.pack_start(box, false, false); - this.pack_start(scrolled); - this.pack_end(button, false, false); - this.orientation = Gtk.Orientation.VERTICAL; - this.spacing = 5; - this.margin = 10; - this.show_all(); -} - -public string getTo() -{ - return m_entry.get_text(); -} - -public string getBody() -{ - return m_textView.buffer.text; -} - + + var headline = new Gtk.Label(_("Write Email")); + headline.get_style_context().add_class("h2"); + headline.set_alignment(0.4f, 0.5f); + var box2 = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); + box2.pack_start(backButton, false, false, 0); + box2.pack_start(headline, true, true, 0); + + this.pack_start(box2, false, false, 0); + this.pack_start(box, false, false); + this.pack_start(scrolled); + this.pack_end(button, false, false); + this.orientation = Gtk.Orientation.VERTICAL; + this.spacing = 5; + this.margin = 10; + this.show_all(); + } + + public string getTo() + { + return m_entry.get_text(); + } + + public string getBody() + { + return m_textView.buffer.text; + } + } diff --git a/plugins/share/Instapaper/InstapaperAPI.vala b/plugins/share/Instapaper/InstapaperAPI.vala index 526a0b96..5ddc2961 100644 --- a/plugins/share/Instapaper/InstapaperAPI.vala +++ b/plugins/share/Instapaper/InstapaperAPI.vala @@ -14,297 +14,297 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. namespace FeedReader.InstapaperSecrets { -const string base_uri = "https://www.instapaper.com/api/"; -const string oauth_consumer_key = "b7681e07bf554b15813511217054e1b2"; -const string oauth_consumer_secret = "c5307cb359d54685904f6d38aaeede6f"; -const string oauth_callback = "feedreader://instapaper"; + const string base_uri = "https://www.instapaper.com/api/"; + const string oauth_consumer_key = "b7681e07bf554b15813511217054e1b2"; + const string oauth_consumer_secret = "c5307cb359d54685904f6d38aaeede6f"; + const string oauth_callback = "feedreader://instapaper"; } public class FeedReader.InstaAPI : ShareAccountInterface, Peas.ExtensionBase { - -public InstaAPI() -{ - -} - -public void setupSystemAccounts(Gee.List<ShareAccount> accounts) -{ - -} - -public string getRequestToken() -{ - return ""; -} - -public bool getAccessToken(string id, string username, string password) -{ - string userID = ""; - - var oauthObject = new Rest.OAuthProxy ( - InstapaperSecrets.oauth_consumer_key, - InstapaperSecrets.oauth_consumer_secret, - "https://www.instapaper.com/api/1/", - false); - - var call = oauthObject.new_call(); - oauthObject.url_format = "https://www.instapaper.com/api/1/"; - call.set_function ("oauth/access_token"); - call.set_method("POST"); - call.add_param("x_auth_mode", "client_auth"); - call.add_param("x_auth_username", username); - call.add_param("x_auth_password", password); - try + + public InstaAPI() { - call.run(); + } - catch(Error e) + + public void setupSystemAccounts(Gee.List<ShareAccount> accounts) { - Logger.error("instapaper getAccessToken: " + e.message); + } - - string response = call.get_payload(); - int64 status = call.get_status_code(); - - if(status != 200) + + public string getRequestToken() { - return false; + return ""; } - - - int secretStart = response.index_of_char('=')+1; - int secretEnd = response.index_of_char('&', secretStart); - int tokenStart = response.index_of_char('=', secretEnd)+1; - - string accessToken_secret = response.substring(secretStart, secretEnd-secretStart); - string accessToken = response.substring(tokenStart); - - oauthObject.set_token(accessToken); - oauthObject.set_token_secret(accessToken_secret); - - // get userID ------------------------------------------------------------------------------------------------- - var call2 = oauthObject.new_call(); - oauthObject.url_format = "https://www.instapaper.com/api/1/"; - call2.set_function("account/verify_credentials"); - call2.set_method("POST"); - try + + public bool getAccessToken(string id, string username, string password) { - call2.run(); + string userID = ""; + + var oauthObject = new Rest.OAuthProxy ( + InstapaperSecrets.oauth_consumer_key, + InstapaperSecrets.oauth_consumer_secret, + "https://www.instapaper.com/api/1/", + false); + + var call = oauthObject.new_call(); + oauthObject.url_format = "https://www.instapaper.com/api/1/"; + call.set_function ("oauth/access_token"); + call.set_method("POST"); + call.add_param("x_auth_mode", "client_auth"); + call.add_param("x_auth_username", username); + call.add_param("x_auth_password", password); + try + { + call.run(); + } + catch(Error e) + { + Logger.error("instapaper getAccessToken: " + e.message); + } + + string response = call.get_payload(); + int64 status = call.get_status_code(); + + if(status != 200) + { + return false; + } + + + int secretStart = response.index_of_char('=')+1; + int secretEnd = response.index_of_char('&', secretStart); + int tokenStart = response.index_of_char('=', secretEnd)+1; + + string accessToken_secret = response.substring(secretStart, secretEnd-secretStart); + string accessToken = response.substring(tokenStart); + + oauthObject.set_token(accessToken); + oauthObject.set_token_secret(accessToken_secret); + + // get userID ------------------------------------------------------------------------------------------------- + var call2 = oauthObject.new_call(); + oauthObject.url_format = "https://www.instapaper.com/api/1/"; + call2.set_function("account/verify_credentials"); + call2.set_method("POST"); + try + { + call2.run(); + } + catch(Error e) + { + Logger.debug("getUserID: " + e.message); + } + + var parser = new Json.Parser(); + try + { + parser.load_from_data(call2.get_payload()); + } + catch (Error e) + { + Logger.error("Could not load response to Message from instapaper"); + Logger.error(e.message); + } + + var root_node = parser.get_root(); + var userArray = root_node.get_array(); + var root_object = userArray.get_object_element(0); + if(root_object.has_member("user_id")) + { + userID = root_object.get_int_member("user_id").to_string(); + } + else if(root_object.has_member("error")) + { + Logger.error(root_object.get_int_member("error_code").to_string()); + Logger.error(root_object.get_string_member("message")); + } + //------------------------------------------------------------------------------------------------------------- + + + var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/instapaper/%s/".printf(id)); + settings.set_string("oauth-access-token", accessToken); + settings.set_string("oauth-access-token-secret", accessToken_secret); + settings.set_string("username", username); + settings.set_string("user-id", userID); + + var array = Settings.share("instapaper").get_strv("account-ids"); + array += id; + Settings.share("instapaper").set_strv("account-ids", array); + + var pwSchema = new Secret.Schema ("org.gnome.feedreader.instapaper.password", Secret.SchemaFlags.NONE, + "userID", Secret.SchemaAttributeType.STRING); + + var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); + attributes["userID"] = userID; + try + { + Secret.password_storev_sync(pwSchema, attributes, Secret.COLLECTION_DEFAULT, "Feedreader: Instapaper login", password, null); + } + catch(GLib.Error e) + { + Logger.error("InstaAPI - getAccessToken: " + e.message); + } + + return true; } - catch(Error e) + + public bool addBookmark(string id, string url, bool system) { - Logger.debug("getUserID: " + e.message); + var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/instapaper/%s/".printf(id)); + + var pwSchema = new Secret.Schema ("org.gnome.feedreader.instapaper.password", Secret.SchemaFlags.NONE, "userID", Secret.SchemaAttributeType.STRING); + var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); + attributes["userID"] = settings.get_string("user-id"); + + string password = ""; + try + { + password = Secret.password_lookupv_sync(pwSchema, attributes, null); + } + catch(GLib.Error e) + { + Logger.error("InstaAPI addBookmark: " + e.message); + } + + var session = new Soup.Session(); + session.user_agent = Constants.USER_AGENT; + string message = "user_id=" + settings.get_string("user-id") + + "&username=" + settings.get_string("username") + + "&password=" + password + + "&url=" + GLib.Uri.escape_string(url); + + Logger.debug("InstaAPI: " + message); + + var message_soup = new Soup.Message("POST", "https://www.instapaper.com/api/add"); + message_soup.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message.data); + + if(Settings.tweaks().get_boolean("do-not-track")) + { + message_soup.request_headers.append("DNT", "1"); + } + + session.send_message(message_soup); + string response = (string)message_soup.response_body.flatten().data; + + if(response == null || response == "") + { + return false; + } + + Logger.debug("InstaAPI: " + response); + + return true; } - - var parser = new Json.Parser(); - try + + public bool logout(string id) { - parser.load_from_data(call2.get_payload()); + Logger.debug(@"InstaAPI.logout($id)"); + var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", @"/org/gnome/feedreader/share/instapaper/$id/"); + var pwSchema = new Secret.Schema("org.gnome.feedreader.instapaper.password", + Secret.SchemaFlags.NONE, "userID", Secret.SchemaAttributeType.STRING); + + var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); + attributes["userID"] = settings.get_string("user-id"); + bool removed = false; + + Secret.password_clearv.begin(pwSchema, attributes, null, (obj, async_res) => { + try + { + removed = Secret.password_clearv.end(async_res); + if(!removed) + { + Logger.error(@"Could not delete password of InstaAPI account $id"); + } + } + catch(GLib.Error e) + { + Logger.error("InstaAPI.logout: %s".printf(e.message)); + } + }); + + var keys = settings.list_keys(); + foreach(string key in keys) + { + settings.reset(key); + } + + var array = Settings.share("instapaper").get_strv("account-ids"); + + string[] array2 = {}; + foreach(string i in array) + { + if(i != id) + { + array2 += i; + } + } + Settings.share("instapaper").set_strv("account-ids", array2); + deleteAccount(id); + + return true; } - catch (Error e) + + public string getIconName() { - Logger.error("Could not load response to Message from instapaper"); - Logger.error(e.message); + return "feed-share-instapaper"; } - - var root_node = parser.get_root(); - var userArray = root_node.get_array(); - var root_object = userArray.get_object_element(0); - if(root_object.has_member("user_id")) + + public string getUsername(string id) { - userID = root_object.get_int_member("user_id").to_string(); + var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/instapaper/%s/".printf(id)); + return settings.get_string("username"); } - else if(root_object.has_member("error")) + + public bool needSetup() { - Logger.error(root_object.get_int_member("error_code").to_string()); - Logger.error(root_object.get_string_member("message")); + return true; } - //------------------------------------------------------------------------------------------------------------- - - - var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/instapaper/%s/".printf(id)); - settings.set_string("oauth-access-token", accessToken); - settings.set_string("oauth-access-token-secret", accessToken_secret); - settings.set_string("username", username); - settings.set_string("user-id", userID); - - var array = Settings.share("instapaper").get_strv("account-ids"); - array += id; - Settings.share("instapaper").set_strv("account-ids", array); - - var pwSchema = new Secret.Schema ("org.gnome.feedreader.instapaper.password", Secret.SchemaFlags.NONE, - "userID", Secret.SchemaAttributeType.STRING); - - var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); - attributes["userID"] = userID; - try + + public bool singleInstance() { - Secret.password_storev_sync(pwSchema, attributes, Secret.COLLECTION_DEFAULT, "Feedreader: Instapaper login", password, null); + return false; } - catch(GLib.Error e) + + public bool useSystemAccounts() { - Logger.error("InstaAPI - getAccessToken: " + e.message); + return false; } - - return true; -} - -public bool addBookmark(string id, string url, bool system) -{ - var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/instapaper/%s/".printf(id)); - - var pwSchema = new Secret.Schema ("org.gnome.feedreader.instapaper.password", Secret.SchemaFlags.NONE, "userID", Secret.SchemaAttributeType.STRING); - var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); - attributes["userID"] = settings.get_string("user-id"); - - string password = ""; - try + + public string pluginID() { - password = Secret.password_lookupv_sync(pwSchema, attributes, null); + return "instapaper"; } - catch(GLib.Error e) + + public string pluginName() { - Logger.error("InstaAPI addBookmark: " + e.message); + return "Instapaper"; } - - var session = new Soup.Session(); - session.user_agent = Constants.USER_AGENT; - string message = "user_id=" + settings.get_string("user-id") - + "&username=" + settings.get_string("username") - + "&password=" + password - + "&url=" + GLib.Uri.escape_string(url); - - Logger.debug("InstaAPI: " + message); - - var message_soup = new Soup.Message("POST", "https://www.instapaper.com/api/add"); - message_soup.set_request("application/x-www-form-urlencoded", Soup.MemoryUse.COPY, message.data); - - if(Settings.tweaks().get_boolean("do-not-track")) + + public string getURL(string token) { - message_soup.request_headers.append("DNT", "1"); + return ""; } - - session.send_message(message_soup); - string response = (string)message_soup.response_body.flatten().data; - - if(response == null || response == "") + + public ServiceSetup? newSetup_withID(string id, string username) { - return false; + return new InstapaperSetup(id, this, username); } - - Logger.debug("InstaAPI: " + response); - - return true; -} - -public bool logout(string id) -{ - Logger.debug(@"InstaAPI.logout($id)"); - var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", @"/org/gnome/feedreader/share/instapaper/$id/"); - var pwSchema = new Secret.Schema("org.gnome.feedreader.instapaper.password", - Secret.SchemaFlags.NONE, "userID", Secret.SchemaAttributeType.STRING); - - var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); - attributes["userID"] = settings.get_string("user-id"); - bool removed = false; - - Secret.password_clearv.begin(pwSchema, attributes, null, (obj, async_res) => { - try - { - removed = Secret.password_clearv.end(async_res); - if(!removed) - { - Logger.error(@"Could not delete password of InstaAPI account $id"); - } - } - catch(GLib.Error e) - { - Logger.error("InstaAPI.logout: %s".printf(e.message)); - } - }); - - var keys = settings.list_keys(); - foreach(string key in keys) + + public ServiceSetup? newSetup() { - settings.reset(key); + return new InstapaperSetup(null, this); } - - var array = Settings.share("instapaper").get_strv("account-ids"); - - string[] array2 = {}; - foreach(string i in array) + + public ServiceSetup? newSystemAccount(string id, string username) { - if(i != id) - { - array2 += i; - } + return null; + } + + public ShareForm? shareWidget(string url) + { + return null; } - Settings.share("instapaper").set_strv("account-ids", array2); - deleteAccount(id); - - return true; -} - -public string getIconName() -{ - return "feed-share-instapaper"; -} - -public string getUsername(string id) -{ - var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/instapaper/%s/".printf(id)); - return settings.get_string("username"); -} - -public bool needSetup() -{ - return true; -} - -public bool singleInstance() -{ - return false; -} - -public bool useSystemAccounts() -{ - return false; -} - -public string pluginID() -{ - return "instapaper"; -} - -public string pluginName() -{ - return "Instapaper"; -} - -public string getURL(string token) -{ - return ""; -} - -public ServiceSetup? newSetup_withID(string id, string username) -{ - return new InstapaperSetup(id, this, username); -} - -public ServiceSetup? newSetup() -{ - return new InstapaperSetup(null, this); -} - -public ServiceSetup? newSystemAccount(string id, string username) -{ - return null; -} - -public ShareForm? shareWidget(string url) -{ - return null; -} } [ModuleInit] diff --git a/plugins/share/Instapaper/InstapaperSetup.vala b/plugins/share/Instapaper/InstapaperSetup.vala index 5952e4a4..868dee67 100644 --- a/plugins/share/Instapaper/InstapaperSetup.vala +++ b/plugins/share/Instapaper/InstapaperSetup.vala @@ -14,113 +14,113 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.InstapaperSetup : ServiceSetup { - -private Gtk.Entry m_userEntry; -private Gtk.Entry m_passEntry; -private Gtk.Revealer m_login_revealer; -private InstaAPI m_api; - -public InstapaperSetup(string? id, InstaAPI api, string username = "") -{ - bool loggedIN = false; - if(username != "") + + private Gtk.Entry m_userEntry; + private Gtk.Entry m_passEntry; + private Gtk.Revealer m_login_revealer; + private InstaAPI m_api; + + public InstapaperSetup(string? id, InstaAPI api, string username = "") { - loggedIN = true; - } - - base("Instapaper", "feed-share-instapaper", loggedIN, username); - - //------------------------------------------------ - // XAuth revealer - //------------------------------------------------ - var grid = new Gtk.Grid(); - grid.set_column_spacing(10); - grid.set_row_spacing(10); - grid.set_valign(Gtk.Align.CENTER); - grid.set_halign(Gtk.Align.CENTER); - grid.margin_bottom = 10; - grid.margin_top = 5; - - m_userEntry = new Gtk.Entry(); - m_passEntry = new Gtk.Entry(); - m_passEntry.set_input_purpose(Gtk.InputPurpose.PASSWORD); - m_passEntry.set_visibility(false); - - m_userEntry.activate.connect(() => { + bool loggedIN = false; + if(username != "") + { + loggedIN = true; + } + + base("Instapaper", "feed-share-instapaper", loggedIN, username); + + //------------------------------------------------ + // XAuth revealer + //------------------------------------------------ + var grid = new Gtk.Grid(); + grid.set_column_spacing(10); + grid.set_row_spacing(10); + grid.set_valign(Gtk.Align.CENTER); + grid.set_halign(Gtk.Align.CENTER); + grid.margin_bottom = 10; + grid.margin_top = 5; + + m_userEntry = new Gtk.Entry(); + m_passEntry = new Gtk.Entry(); + m_passEntry.set_input_purpose(Gtk.InputPurpose.PASSWORD); + m_passEntry.set_visibility(false); + + m_userEntry.activate.connect(() => { m_passEntry.grab_focus(); }); - - m_passEntry.activate.connect(() => { + + m_passEntry.activate.connect(() => { login(); }); - - grid.attach(new Gtk.Label(_("Username:")), 0, 0, 1, 1); - grid.attach(new Gtk.Label(_("Password:")), 0, 1, 1, 1); - grid.attach(m_userEntry, 1, 0, 1, 1); - grid.attach(m_passEntry, 1, 1, 1, 1); - - m_login_revealer = new Gtk.Revealer(); - m_login_revealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_DOWN); - m_login_revealer.add(grid); - //------------------------------------------------ - - m_seperator_box.pack_start(m_login_revealer, false, false, 0); - - m_api = api; - - if(id != null) - { - m_id = id; + + grid.attach(new Gtk.Label(_("Username:")), 0, 0, 1, 1); + grid.attach(new Gtk.Label(_("Password:")), 0, 1, 1, 1); + grid.attach(m_userEntry, 1, 0, 1, 1); + grid.attach(m_passEntry, 1, 1, 1, 1); + + m_login_revealer = new Gtk.Revealer(); + m_login_revealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_DOWN); + m_login_revealer.add(grid); + //------------------------------------------------ + + m_seperator_box.pack_start(m_login_revealer, false, false, 0); + + m_api = api; + + if(id != null) + { + m_id = id; + } } -} - - -public override void login() -{ - if(m_login_revealer.get_child_revealed()) + + + public override void login() { - m_spinner.start(); - m_iconStack.set_visible_child_name("spinner"); - string id = Share.get_default().generateNewID(); - string username = m_userEntry.get_text(); - string password = m_passEntry.get_text(); - - if(m_api.getAccessToken(id, username, password)) + if(m_login_revealer.get_child_revealed()) { - m_id = id; - m_api.addAccount(id, m_api.pluginID(), username, m_api.getIconName(), m_api.pluginName()); - m_login_button.get_style_context().remove_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); - m_login_revealer.set_reveal_child(false); - m_isLoggedIN = true; - m_iconStack.set_visible_child_name("loggedIN"); - m_spinner.stop(); - m_label.set_label(username); - m_labelStack.set_visible_child_name("loggedIN"); - m_login_button.clicked.disconnect(login); - m_login_button.clicked.connect(logout); + m_spinner.start(); + m_iconStack.set_visible_child_name("spinner"); + string id = Share.get_default().generateNewID(); + string username = m_userEntry.get_text(); + string password = m_passEntry.get_text(); + + if(m_api.getAccessToken(id, username, password)) + { + m_id = id; + m_api.addAccount(id, m_api.pluginID(), username, m_api.getIconName(), m_api.pluginName()); + m_login_button.get_style_context().remove_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); + m_login_revealer.set_reveal_child(false); + m_isLoggedIN = true; + m_iconStack.set_visible_child_name("loggedIN"); + m_spinner.stop(); + m_label.set_label(username); + m_labelStack.set_visible_child_name("loggedIN"); + m_login_button.clicked.disconnect(login); + m_login_button.clicked.connect(logout); + } + else + { + showInfoBar(_("Username or Password incorrect")); + m_iconStack.set_visible_child_full("button", Gtk.StackTransitionType.SLIDE_RIGHT); + } + } else { - showInfoBar(_("Username or Password incorrect")); - m_iconStack.set_visible_child_full("button", Gtk.StackTransitionType.SLIDE_RIGHT); + m_login_revealer.set_reveal_child(true); + m_login_button.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); + m_userEntry.grab_focus(); } - } - else + + public override void logout() { - m_login_revealer.set_reveal_child(true); - m_login_button.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); - m_userEntry.grab_focus(); + Logger.debug("InstapaperSetup.logout()"); + m_isLoggedIN = false; + m_iconStack.set_visible_child_full("button", Gtk.StackTransitionType.SLIDE_RIGHT); + m_labelStack.set_visible_child_name("loggedOUT"); + m_api.logout(m_id); + removeRow(); } } - -public override void logout() -{ - Logger.debug("InstapaperSetup.logout()"); - m_isLoggedIN = false; - m_iconStack.set_visible_child_full("button", Gtk.StackTransitionType.SLIDE_RIGHT); - m_labelStack.set_visible_child_name("loggedOUT"); - m_api.logout(m_id); - removeRow(); -} -} diff --git a/plugins/share/Pocket/PocketAPI.vala b/plugins/share/Pocket/PocketAPI.vala index a2ce4b12..a1cce40b 100644 --- a/plugins/share/Pocket/PocketAPI.vala +++ b/plugins/share/Pocket/PocketAPI.vala @@ -14,288 +14,288 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. namespace FeedReader.PocketSecrets { -const string base_uri = "https://getpocket.com/v3/"; -const string oauth_consumer_key = "43273-30a11c29b5eeabfa905df168"; -const string oauth_callback = "feedreader://pocket"; + const string base_uri = "https://getpocket.com/v3/"; + const string oauth_consumer_key = "43273-30a11c29b5eeabfa905df168"; + const string oauth_callback = "feedreader://pocket"; } public class FeedReader.PocketAPI : ShareAccountInterface, Peas.ExtensionBase { - -public PocketAPI() -{ - -} - -public void setupSystemAccounts(Gee.List<ShareAccount> accounts) -{ - try + + public PocketAPI() + { + + } + + public void setupSystemAccounts(Gee.List<ShareAccount> accounts) { - Goa.Client? client = new Goa.Client.sync(); - if(client != null) + try { - var goaAccounts = client.get_accounts(); - foreach(var object in goaAccounts) + Goa.Client? client = new Goa.Client.sync(); + if(client != null) { - if(object.account.provider_type == "pocket" - && !object.account.read_later_disabled) + var goaAccounts = client.get_accounts(); + foreach(var object in goaAccounts) { - accounts.add( - new ShareAccount( - object.account.id, - pluginID(), - object.account.identity, - getIconName(), - pluginName(), - true + if(object.account.provider_type == "pocket" + && !object.account.read_later_disabled) + { + accounts.add( + new ShareAccount( + object.account.id, + pluginID(), + object.account.identity, + getIconName(), + pluginName(), + true ) ); + } } } + else + { + Logger.error("PocketAPI: goa not available"); + } } - else + catch(GLib.Error e) { - Logger.error("PocketAPI: goa not available"); + Logger.error("PocketAPI.setupSystemAccounts: %s".printf(e.message)); } } - catch(GLib.Error e) + + public string getRequestToken() { - Logger.error("PocketAPI.setupSystemAccounts: %s".printf(e.message)); + Logger.debug("PocketAPI: get request token"); + var session = new Soup.Session(); + session.user_agent = Constants.USER_AGENT; + string message = "consumer_key=" + PocketSecrets.oauth_consumer_key + "&redirect_uri=" + PocketSecrets.oauth_callback; + + var message_soup = new Soup.Message("POST", "https://getpocket.com/v3/oauth/request"); + message_soup.set_request("application/x-www-form-urlencoded; charset=UTF8", Soup.MemoryUse.COPY, message.data); + + if(Settings.tweaks().get_boolean("do-not-track")) + { + message_soup.request_headers.append("DNT", "1"); + } + + session.send_message(message_soup); + + string response = (string)message_soup.response_body.flatten().data; + return response.substring(response.index_of_char('=')+1); } -} - -public string getRequestToken() -{ - Logger.debug("PocketAPI: get request token"); - var session = new Soup.Session(); - session.user_agent = Constants.USER_AGENT; - string message = "consumer_key=" + PocketSecrets.oauth_consumer_key + "&redirect_uri=" + PocketSecrets.oauth_callback; - - var message_soup = new Soup.Message("POST", "https://getpocket.com/v3/oauth/request"); - message_soup.set_request("application/x-www-form-urlencoded; charset=UTF8", Soup.MemoryUse.COPY, message.data); - - if(Settings.tweaks().get_boolean("do-not-track")) + + public bool getAccessToken(string id, string requestToken) { - message_soup.request_headers.append("DNT", "1"); - } - - session.send_message(message_soup); - - string response = (string)message_soup.response_body.flatten().data; - return response.substring(response.index_of_char('=')+1); -} - -public bool getAccessToken(string id, string requestToken) -{ - var session = new Soup.Session(); - session.user_agent = Constants.USER_AGENT; - string message = "consumer_key=" + PocketSecrets.oauth_consumer_key + "&code=" + requestToken; - - var message_soup = new Soup.Message("POST", "https://getpocket.com/v3/oauth/authorize"); - message_soup.set_request("application/x-www-form-urlencoded; charset=UTF8", Soup.MemoryUse.COPY, message.data); - - if(Settings.tweaks().get_boolean("do-not-track")) - { - message_soup.request_headers.append("DNT", "1"); - } - - session.send_message(message_soup); - - if((string)message_soup.response_body.flatten().data == null - || (string)message_soup.response_body.flatten().data == "") - { - return false; + var session = new Soup.Session(); + session.user_agent = Constants.USER_AGENT; + string message = "consumer_key=" + PocketSecrets.oauth_consumer_key + "&code=" + requestToken; + + var message_soup = new Soup.Message("POST", "https://getpocket.com/v3/oauth/authorize"); + message_soup.set_request("application/x-www-form-urlencoded; charset=UTF8", Soup.MemoryUse.COPY, message.data); + + if(Settings.tweaks().get_boolean("do-not-track")) + { + message_soup.request_headers.append("DNT", "1"); + } + + session.send_message(message_soup); + + if((string)message_soup.response_body.flatten().data == null + || (string)message_soup.response_body.flatten().data == "") + { + return false; + } + + string response = (string)message_soup.response_body.flatten().data; + Logger.debug(response); + int tokenStart = response.index_of_char('=')+1; + int tokenEnd = response.index_of_char('&', tokenStart); + int userStart = response.index_of_char('=', tokenEnd)+1; + + string accessToken = response.substring(tokenStart, tokenEnd-tokenStart); + string user = GLib.Uri.unescape_string(response.substring(userStart)); + var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/pocket/%s/".printf(id)); + settings.set_string("oauth-access-token", accessToken); + settings.set_string("username", user); + + var array = Settings.share("pocket").get_strv("account-ids"); + array += id; + Settings.share("pocket").set_strv("account-ids", array); + + return true; } - - string response = (string)message_soup.response_body.flatten().data; - Logger.debug(response); - int tokenStart = response.index_of_char('=')+1; - int tokenEnd = response.index_of_char('&', tokenStart); - int userStart = response.index_of_char('=', tokenEnd)+1; - - string accessToken = response.substring(tokenStart, tokenEnd-tokenStart); - string user = GLib.Uri.unescape_string(response.substring(userStart)); - var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/pocket/%s/".printf(id)); - settings.set_string("oauth-access-token", accessToken); - settings.set_string("username", user); - - var array = Settings.share("pocket").get_strv("account-ids"); - array += id; - Settings.share("pocket").set_strv("account-ids", array); - - return true; -} - - -public bool addBookmark(string id, string url, bool system) -{ - string oauthToken = ""; - - if(system) + + + public bool addBookmark(string id, string url, bool system) { - Logger.debug(@"PocketAPI.addBookmark: $id is system account"); - try + string oauthToken = ""; + + if(system) { - Goa.Client? client = new Goa.Client.sync(); - if(client != null) + Logger.debug(@"PocketAPI.addBookmark: $id is system account"); + try { - var accounts = client.get_accounts(); - foreach(var object in accounts) + Goa.Client? client = new Goa.Client.sync(); + if(client != null) { - if(object.account.provider_type == "pocket" - && object.account.id == id) + var accounts = client.get_accounts(); + foreach(var object in accounts) { - int expires = -1; - object.oauth2_based.call_get_access_token_sync(out oauthToken, out expires); - break; + if(object.account.provider_type == "pocket" + && object.account.id == id) + { + int expires = -1; + object.oauth2_based.call_get_access_token_sync(out oauthToken, out expires); + break; + } } } + else + { + Logger.error("PocketAPI: goa not available"); + } } - else + catch(GLib.Error e) { - Logger.error("PocketAPI: goa not available"); + Logger.error("PocketAPI GOA: %s".printf(e.message)); } } - catch(GLib.Error e) + else { - Logger.error("PocketAPI GOA: %s".printf(e.message)); + var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/pocket/%s/".printf(id)); + oauthToken = settings.get_string("oauth-access-token"); } + + + var session = new Soup.Session(); + session.user_agent = Constants.USER_AGENT; + string message = "url=" + GLib.Uri.escape_string(url) + + "&consumer_key=" + PocketSecrets.oauth_consumer_key + + "&access_token=" + oauthToken; + + Logger.debug("PocketAPI: " + message); + + var message_soup = new Soup.Message("POST", "https://getpocket.com/v3/add"); + message_soup.set_request("application/x-www-form-urlencoded; charset=UTF8", Soup.MemoryUse.COPY, message.data); + + if(Settings.tweaks().get_boolean("do-not-track")) + { + message_soup.request_headers.append("DNT", "1"); + } + + session.send_message(message_soup); + + if((string)message_soup.response_body.flatten().data == null + || (string)message_soup.response_body.flatten().data == "") + { + return false; + } + + return true; } - else + + public bool logout(string id) { + Logger.debug(@"PocketAPI: logout($id)"); var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/pocket/%s/".printf(id)); - oauthToken = settings.get_string("oauth-access-token"); + var keys = settings.list_keys(); + foreach(string key in keys) + { + settings.reset(key); + } + + var array = Settings.share("pocket").get_strv("account-ids"); + string[] array2 = {}; + + foreach(string i in array) + { + if(i != id) + { + array2 += i; + } + } + Settings.share("pocket").set_strv("account-ids", array2); + deleteAccount(id); + + return true; } - - - var session = new Soup.Session(); - session.user_agent = Constants.USER_AGENT; - string message = "url=" + GLib.Uri.escape_string(url) - + "&consumer_key=" + PocketSecrets.oauth_consumer_key - + "&access_token=" + oauthToken; - - Logger.debug("PocketAPI: " + message); - - var message_soup = new Soup.Message("POST", "https://getpocket.com/v3/add"); - message_soup.set_request("application/x-www-form-urlencoded; charset=UTF8", Soup.MemoryUse.COPY, message.data); - - if(Settings.tweaks().get_boolean("do-not-track")) + + public string getURL(string token) { - message_soup.request_headers.append("DNT", "1"); + return "https://getpocket.com/auth/authorize?request_token=" + + token + "&redirect_uri=" + + GLib.Uri.escape_string(PocketSecrets.oauth_callback); } - - session.send_message(message_soup); - - if((string)message_soup.response_body.flatten().data == null - || (string)message_soup.response_body.flatten().data == "") + + public string getIconName() { - return false; + return "feed-share-pocket"; } - - return true; -} - -public bool logout(string id) -{ - Logger.debug(@"PocketAPI: logout($id)"); - var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/pocket/%s/".printf(id)); - var keys = settings.list_keys(); - foreach(string key in keys) + + public string getUsername(string id) { - settings.reset(key); + var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/pocket/%s/".printf(id)); + return settings.get_string("username"); } - - var array = Settings.share("pocket").get_strv("account-ids"); - string[] array2 = {}; - - foreach(string i in array) + + public bool needSetup() { - if(i != id) - { - array2 += i; - } + return true; } - Settings.share("pocket").set_strv("account-ids", array2); - deleteAccount(id); - - return true; -} - -public string getURL(string token) -{ - return "https://getpocket.com/auth/authorize?request_token=" - + token + "&redirect_uri=" - + GLib.Uri.escape_string(PocketSecrets.oauth_callback); -} - -public string getIconName() -{ - return "feed-share-pocket"; -} - -public string getUsername(string id) -{ - var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/pocket/%s/".printf(id)); - return settings.get_string("username"); -} - -public bool needSetup() -{ - return true; -} - -public bool singleInstance() -{ - return false; -} - -public bool useSystemAccounts() -{ - try + + public bool singleInstance() { - Goa.Client? client = new Goa.Client.sync(); - if(client != null) + return false; + } + + public bool useSystemAccounts() + { + try { - return true; + Goa.Client? client = new Goa.Client.sync(); + if(client != null) + { + return true; + } + + return false; + } + catch(GLib.Error e) + { + Logger.debug("PocketAPI.useSystemAccounts(): %s".printf(e.message)); + return false; } - - return false; } - catch(GLib.Error e) + + public string pluginID() { - Logger.debug("PocketAPI.useSystemAccounts(): %s".printf(e.message)); - return false; + return "pocket"; + } + + public string pluginName() + { + return "Pocket"; + } + + public ServiceSetup? newSetup_withID(string id, string username) + { + return new PocketSetup(id, this, username); + } + + public ServiceSetup? newSetup() + { + return new PocketSetup(null, this); + } + + public ServiceSetup? newSystemAccount(string id, string username) + { + return new PocketSetup(id, this, username, true); + } + + public ShareForm? shareWidget(string url) + { + return null; } -} - -public string pluginID() -{ - return "pocket"; -} - -public string pluginName() -{ - return "Pocket"; -} - -public ServiceSetup? newSetup_withID(string id, string username) -{ - return new PocketSetup(id, this, username); -} - -public ServiceSetup? newSetup() -{ - return new PocketSetup(null, this); -} - -public ServiceSetup? newSystemAccount(string id, string username) -{ - return new PocketSetup(id, this, username, true); -} - -public ShareForm? shareWidget(string url) -{ - return null; -} } [ModuleInit] diff --git a/plugins/share/Pocket/PocketSetup.vala b/plugins/share/Pocket/PocketSetup.vala index bbb95006..486028ad 100644 --- a/plugins/share/Pocket/PocketSetup.vala +++ b/plugins/share/Pocket/PocketSetup.vala @@ -14,77 +14,77 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.PocketSetup : ServiceSetup { - -private PocketAPI m_api; - -public PocketSetup(string? id, PocketAPI api, string username = "", bool system = false) -{ - bool loggedIN = false; - if(username != "") - { - loggedIN = true; - } - - base("Pocket", "feed-share-pocket", loggedIN, username, system); - - m_api = api; - - if(id != null) - { - m_id = id; - } -} - - -public override void login() -{ - string id = Share.get_default().generateNewID(); - string requestToken = m_api.getRequestToken(); - string url = m_api.getURL(requestToken); - m_spinner.start(); - m_iconStack.set_visible_child_name("spinner"); - try + + private PocketAPI m_api; + + public PocketSetup(string? id, PocketAPI api, string username = "", bool system = false) { - Gtk.show_uri_on_window(SettingsDialog.get_default(), url, Gdk.CURRENT_TIME); + bool loggedIN = false; + if(username != "") + { + loggedIN = true; + } + + base("Pocket", "feed-share-pocket", loggedIN, username, system); + + m_api = api; + + if(id != null) + { + m_id = id; + } } - catch(GLib.Error e) + + + public override void login() { - - } - - - m_login_button.set_label(_("waiting")); - m_login_button.set_sensitive(false); - FeedReaderApp.get_default().callback.connect((content) => { + string id = Share.get_default().generateNewID(); + string requestToken = m_api.getRequestToken(); + string url = m_api.getURL(requestToken); + m_spinner.start(); + m_iconStack.set_visible_child_name("spinner"); + try + { + Gtk.show_uri_on_window(SettingsDialog.get_default(), url, Gdk.CURRENT_TIME); + } + catch(GLib.Error e) + { + + } + + + m_login_button.set_label(_("waiting")); + m_login_button.set_sensitive(false); + FeedReaderApp.get_default().callback.connect((content) => { if(content == PocketSecrets.oauth_callback) { - if(m_api.getAccessToken(id, requestToken)) - { - m_id = id; - m_api.addAccount(id, m_api.pluginID(), m_api.getUsername(id), m_api.getIconName(), m_api.pluginName()); - m_iconStack.set_visible_child_full("loggedIN", Gtk.StackTransitionType.SLIDE_LEFT); - m_spinner.stop(); - m_isLoggedIN = true; - m_label.set_label(m_api.getUsername(id)); - m_labelStack.set_visible_child_full("loggedIN", Gtk.StackTransitionType.CROSSFADE); - m_login_button.clicked.disconnect(login); - m_login_button.clicked.connect(logout); + if(m_api.getAccessToken(id, requestToken)) + { + m_id = id; + m_api.addAccount(id, m_api.pluginID(), m_api.getUsername(id), m_api.getIconName(), m_api.pluginName()); + m_iconStack.set_visible_child_full("loggedIN", Gtk.StackTransitionType.SLIDE_LEFT); + m_spinner.stop(); + m_isLoggedIN = true; + m_label.set_label(m_api.getUsername(id)); + m_labelStack.set_visible_child_full("loggedIN", Gtk.StackTransitionType.CROSSFADE); + m_login_button.clicked.disconnect(login); + m_login_button.clicked.connect(logout); } - else - { - m_iconStack.set_visible_child_full("button", Gtk.StackTransitionType.SLIDE_RIGHT); + else + { + m_iconStack.set_visible_child_full("button", Gtk.StackTransitionType.SLIDE_RIGHT); } } }); -} - -public override void logout() -{ - m_isLoggedIN = false; - m_iconStack.set_visible_child_full("button", Gtk.StackTransitionType.SLIDE_RIGHT); - m_labelStack.set_visible_child_name("loggedOUT"); - m_api.logout(m_id); - removeRow(); -} - + } + + public override void logout() + { + m_isLoggedIN = false; + m_iconStack.set_visible_child_full("button", Gtk.StackTransitionType.SLIDE_RIGHT); + m_labelStack.set_visible_child_name("loggedOUT"); + m_api.logout(m_id); + removeRow(); + } + } diff --git a/plugins/share/Telegram/Telegram.vala b/plugins/share/Telegram/Telegram.vala index defcf297..6f3e2aba 100644 --- a/plugins/share/Telegram/Telegram.vala +++ b/plugins/share/Telegram/Telegram.vala @@ -15,95 +15,95 @@ public class FeedReader.Telegram : ShareAccountInterface, Peas.ExtensionBase { - -string tg_text; - -public bool addBookmark(string id, string url, bool system) -{ - string tg_msg = @"tg://msg_url?url=$url&text=$tg_text"; - - try + + string tg_text; + + public bool addBookmark(string id, string url, bool system) { - Gtk.show_uri_on_window(MainWindow.get_default(), tg_msg, Gdk.CURRENT_TIME); + string tg_msg = @"tg://msg_url?url=$url&text=$tg_text"; + + try + { + Gtk.show_uri_on_window(MainWindow.get_default(), tg_msg, Gdk.CURRENT_TIME); + return true; + } + catch(GLib.Error e) + { + Logger.error("TelegramPlugin: Error opening url: " + e.message); + } + return false; + } + + public void setupSystemAccounts(Gee.List<ShareAccount> accounts) + { + + } + + public bool logout(string id) + { + Settings.share("telegram").set_boolean("enabled", false); + deleteAccount(id); return true; } - catch(GLib.Error e) + + public string getIconName() { - Logger.error("TelegramPlugin: Error opening url: " + e.message); + return "feed-share-telegram"; } - return false; -} - -public void setupSystemAccounts(Gee.List<ShareAccount> accounts) -{ - -} - -public bool logout(string id) -{ - Settings.share("telegram").set_boolean("enabled", false); - deleteAccount(id); - return true; -} - -public string getIconName() -{ - return "feed-share-telegram"; -} - -public string getUsername(string id) -{ - return "Telegram"; -} - -public bool needSetup() -{ - return true; -} - -public bool singleInstance() -{ - return true; -} - -public bool useSystemAccounts() -{ - return false; -} - -public string pluginID() -{ - return "telegram"; -} - -public string pluginName() -{ - return _("Telegram"); -} - -public ServiceSetup? newSetup_withID(string id, string username) -{ - return new TelegramSetup(id, this, username); -} - -public ServiceSetup? newSetup() -{ - return new TelegramSetup(null, this); -} - -public ServiceSetup? newSystemAccount(string id, string username) -{ - return null; -} - -public ShareForm? shareWidget(string url) -{ - var widget = new TelegramForm(); - widget.share.connect(() => { + + public string getUsername(string id) + { + return "Telegram"; + } + + public bool needSetup() + { + return true; + } + + public bool singleInstance() + { + return true; + } + + public bool useSystemAccounts() + { + return false; + } + + public string pluginID() + { + return "telegram"; + } + + public string pluginName() + { + return _("Telegram"); + } + + public ServiceSetup? newSetup_withID(string id, string username) + { + return new TelegramSetup(id, this, username); + } + + public ServiceSetup? newSetup() + { + return new TelegramSetup(null, this); + } + + public ServiceSetup? newSystemAccount(string id, string username) + { + return null; + } + + public ShareForm? shareWidget(string url) + { + var widget = new TelegramForm(); + widget.share.connect(() => { tg_text = widget.getMessage(); }); - return widget; -} + return widget; + } } [ModuleInit] diff --git a/plugins/share/Telegram/TelegramForm.vala b/plugins/share/Telegram/TelegramForm.vala index 21f2158b..d0d666f4 100644 --- a/plugins/share/Telegram/TelegramForm.vala +++ b/plugins/share/Telegram/TelegramForm.vala @@ -15,63 +15,63 @@ public class FeedReader.TelegramForm : ShareForm { - -private Gtk.TextView m_textView; - -public TelegramForm() -{ - string tg_msg_text = _("Hey, check out this interesting article I used FeedReader to read"); - var box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 5); - - m_textView = new Gtk.TextView(); - m_textView.get_style_context().add_class("h3"); - m_textView.set_wrap_mode(Gtk.WrapMode.WORD); - m_textView.buffer.text = tg_msg_text; - m_textView.border_width = 2; - - var scrolled = new Gtk.ScrolledWindow(null, null); - scrolled.get_style_context().add_class(Gtk.STYLE_CLASS_FRAME); - scrolled.add(m_textView); - - int margin = 5; - m_textView.left_margin = margin; - m_textView.right_margin = margin; - m_textView.top_margin = margin; - m_textView.bottom_margin = margin; - - var button = new Gtk.Button.with_label(_("Send")); - button.halign = Gtk.Align.END; - button.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); - button.clicked.connect(() => { share(); }); - - var backButton = new Gtk.Button.from_icon_name("go-previous-symbolic"); - backButton.set_focus_on_click(false); - backButton.set_relief(Gtk.ReliefStyle.NONE); - backButton.halign = Gtk.Align.START; - backButton.clicked.connect(() => { + + private Gtk.TextView m_textView; + + public TelegramForm() + { + string tg_msg_text = _("Hey, check out this interesting article I used FeedReader to read"); + var box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 5); + + m_textView = new Gtk.TextView(); + m_textView.get_style_context().add_class("h3"); + m_textView.set_wrap_mode(Gtk.WrapMode.WORD); + m_textView.buffer.text = tg_msg_text; + m_textView.border_width = 2; + + var scrolled = new Gtk.ScrolledWindow(null, null); + scrolled.get_style_context().add_class(Gtk.STYLE_CLASS_FRAME); + scrolled.add(m_textView); + + int margin = 5; + m_textView.left_margin = margin; + m_textView.right_margin = margin; + m_textView.top_margin = margin; + m_textView.bottom_margin = margin; + + var button = new Gtk.Button.with_label(_("Send")); + button.halign = Gtk.Align.END; + button.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); + button.clicked.connect(() => { share(); }); + + var backButton = new Gtk.Button.from_icon_name("go-previous-symbolic"); + backButton.set_focus_on_click(false); + backButton.set_relief(Gtk.ReliefStyle.NONE); + backButton.halign = Gtk.Align.START; + backButton.clicked.connect(() => { goBack(); }); - - var headline = new Gtk.Label(_("Send Telegram")); - headline.get_style_context().add_class("h2"); - headline.set_alignment(0.4f, 0.5f); - var box2 = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); - box2.pack_start(backButton, false, false, 0); - box2.pack_start(headline, true, true, 0); - - this.pack_start(box2, false, false, 0); - this.pack_start(box, false, false); - this.pack_start(scrolled); - this.pack_end(button, false, false); - this.orientation = Gtk.Orientation.VERTICAL; - this.spacing = 5; - this.margin = 10; - this.show_all(); -} - -public string getMessage() -{ - return m_textView.buffer.text; -} - + + var headline = new Gtk.Label(_("Send Telegram")); + headline.get_style_context().add_class("h2"); + headline.set_alignment(0.4f, 0.5f); + var box2 = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); + box2.pack_start(backButton, false, false, 0); + box2.pack_start(headline, true, true, 0); + + this.pack_start(box2, false, false, 0); + this.pack_start(box, false, false); + this.pack_start(scrolled); + this.pack_end(button, false, false); + this.orientation = Gtk.Orientation.VERTICAL; + this.spacing = 5; + this.margin = 10; + this.show_all(); + } + + public string getMessage() + { + return m_textView.buffer.text; + } + } diff --git a/plugins/share/Telegram/TelegramSetup.vala b/plugins/share/Telegram/TelegramSetup.vala index 65717510..857e9cfe 100644 --- a/plugins/share/Telegram/TelegramSetup.vala +++ b/plugins/share/Telegram/TelegramSetup.vala @@ -14,48 +14,48 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.TelegramSetup : ServiceSetup { - -private Telegram m_tg; - -public TelegramSetup(string? id, Telegram tg, string username = "") -{ - bool loggedIN = false; - if(username != "") + + private Telegram m_tg; + + public TelegramSetup(string? id, Telegram tg, string username = "") { - loggedIN = true; + bool loggedIN = false; + if(username != "") + { + loggedIN = true; + } + + base("Telegram", "feed-share-telegram", loggedIN, username); + + m_tg = tg; + //no login, so change the labels + m_login_button.set_label(_("Add")); + m_logout_button.set_label(_("Remove")); + m_id = m_tg.pluginID(); } - - base("Telegram", "feed-share-telegram", loggedIN, username); - - m_tg = tg; - //no login, so change the labels - m_login_button.set_label(_("Add")); - m_logout_button.set_label(_("Remove")); - m_id = m_tg.pluginID(); -} - - -public override void login() -{ - showInfoBar(_("Info: Telegram would need to be installed for this plugin to work.")); - m_login_button.set_sensitive(false); - Settings.share("telegram").set_boolean("enabled", true); - m_tg.addAccount(m_id, m_tg.pluginID(), m_tg.getUsername(m_id), m_tg.getIconName(), m_tg.pluginName()); - m_iconStack.set_visible_child_full("loggedIN", Gtk.StackTransitionType.SLIDE_LEFT); - m_isLoggedIN = true; - m_label.set_label(m_tg.getUsername(m_id)); - m_labelStack.set_visible_child_full("loggedIN", Gtk.StackTransitionType.CROSSFADE); - m_login_button.clicked.disconnect(login); - m_login_button.clicked.connect(logout); -} - -public override void logout() -{ - m_isLoggedIN = false; - m_iconStack.set_visible_child_full("button", Gtk.StackTransitionType.SLIDE_RIGHT); - m_labelStack.set_visible_child_name("loggedOUT"); - m_tg.logout(m_id); - removeRow(); -} - + + + public override void login() + { + showInfoBar(_("Info: Telegram would need to be installed for this plugin to work.")); + m_login_button.set_sensitive(false); + Settings.share("telegram").set_boolean("enabled", true); + m_tg.addAccount(m_id, m_tg.pluginID(), m_tg.getUsername(m_id), m_tg.getIconName(), m_tg.pluginName()); + m_iconStack.set_visible_child_full("loggedIN", Gtk.StackTransitionType.SLIDE_LEFT); + m_isLoggedIN = true; + m_label.set_label(m_tg.getUsername(m_id)); + m_labelStack.set_visible_child_full("loggedIN", Gtk.StackTransitionType.CROSSFADE); + m_login_button.clicked.disconnect(login); + m_login_button.clicked.connect(logout); + } + + public override void logout() + { + m_isLoggedIN = false; + m_iconStack.set_visible_child_full("button", Gtk.StackTransitionType.SLIDE_RIGHT); + m_labelStack.set_visible_child_name("loggedOUT"); + m_tg.logout(m_id); + removeRow(); + } + } diff --git a/plugins/share/Twitter/TwitterAPI.vala b/plugins/share/Twitter/TwitterAPI.vala index f8a06777..a9f7747a 100644 --- a/plugins/share/Twitter/TwitterAPI.vala +++ b/plugins/share/Twitter/TwitterAPI.vala @@ -14,287 +14,287 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. namespace FeedReader.TwitterSecrets { -const string base_uri = "https://api.twitter.com/"; -const string key = "hqScCfRLj5ImAtwypRKhbVpXo"; -const string secret = "wydD2zd6mgBUnlrdbqNqS0U0dJCWBJ9X0cqtdErk8Hn7aeperP"; -const string callback = "feedreader://twitter"; + const string base_uri = "https://api.twitter.com/"; + const string key = "hqScCfRLj5ImAtwypRKhbVpXo"; + const string secret = "wydD2zd6mgBUnlrdbqNqS0U0dJCWBJ9X0cqtdErk8Hn7aeperP"; + const string callback = "feedreader://twitter"; } public class FeedReader.TwitterAPI : ShareAccountInterface, Peas.ExtensionBase { - -private Rest.OAuthProxy m_oauthObject; -private string m_tweet; -private int m_urlLength = 0; - -public TwitterAPI() -{ - -} - -public void setupSystemAccounts(Gee.List<ShareAccount> accounts) -{ - -} - -public string getRequestToken() -{ - Logger.debug("TwitterAPI: get request token"); - - m_oauthObject = new Rest.OAuthProxy ( - TwitterSecrets.key, - TwitterSecrets.secret, - "https://api.twitter.com/", - false); - - try + + private Rest.OAuthProxy m_oauthObject; + private string m_tweet; + private int m_urlLength = 0; + + public TwitterAPI() { - m_oauthObject.request_token("oauth/request_token", TwitterSecrets.callback); + } - catch(GLib.Error e) + + public void setupSystemAccounts(Gee.List<ShareAccount> accounts) { - Logger.error("TwitterAPI.getRequestToken: %s".printf(e.message)); + } - - return m_oauthObject.get_token(); -} - -public bool getAccessToken(string id, string verifier) -{ - try + + public string getRequestToken() { - m_oauthObject.access_token("oauth/access_token", verifier); + Logger.debug("TwitterAPI: get request token"); + + m_oauthObject = new Rest.OAuthProxy ( + TwitterSecrets.key, + TwitterSecrets.secret, + "https://api.twitter.com/", + false); + + try + { + m_oauthObject.request_token("oauth/request_token", TwitterSecrets.callback); + } + catch(GLib.Error e) + { + Logger.error("TwitterAPI.getRequestToken: %s".printf(e.message)); + } + + return m_oauthObject.get_token(); } - catch(GLib.Error e) + + public bool getAccessToken(string id, string verifier) { - Logger.error("TwitterAPI.getAccessToken: %s".printf(e.message)); + try + { + m_oauthObject.access_token("oauth/access_token", verifier); + } + catch(GLib.Error e) + { + Logger.error("TwitterAPI.getAccessToken: %s".printf(e.message)); + } + + var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/twitter/%s/".printf(id)); + string token = m_oauthObject.get_token(); + string secret = m_oauthObject.get_token_secret(); + settings.set_string("oauth-access-token", token); + settings.set_string("oauth-access-token-secret", secret); + + var call = m_oauthObject.new_call(); + call.set_function("1.1/account/verify_credentials.json"); + call.set_method("GET"); + call.add_param ("include_entities", "false"); + call.add_param ("skip_status", "true"); + call.add_param ("include_email", "true"); + + try + { + call.run(); + } + catch(Error e) + { + Logger.error(e.message); + } + + var parser = new Json.Parser(); + try + { + parser.load_from_data(call.get_payload()); + } + catch(Error e) + { + Logger.error("Could not load response to Message from twitter"); + Logger.error(e.message); + } + + var root_object = parser.get_root().get_object(); + + if(root_object.has_member("screen_name")) + { + string screenName = "@" + root_object.get_string_member("screen_name"); + settings.set_string("username", screenName); + } + else + { + settings.set_string("username", root_object.get_string_member("name")); + } + + + + var array = Settings.share("twitter").get_strv("account-ids"); + array += id; + Settings.share("twitter").set_strv("account-ids", array); + + return true; } - - var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/twitter/%s/".printf(id)); - string token = m_oauthObject.get_token(); - string secret = m_oauthObject.get_token_secret(); - settings.set_string("oauth-access-token", token); - settings.set_string("oauth-access-token-secret", secret); - - var call = m_oauthObject.new_call(); - call.set_function("1.1/account/verify_credentials.json"); - call.set_method("GET"); - call.add_param ("include_entities", "false"); - call.add_param ("skip_status", "true"); - call.add_param ("include_email", "true"); - - try + + + public bool addBookmark(string id, string url, bool system) { - call.run(); + var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/twitter/%s/".printf(id)); + string token = settings.get_string("oauth-access-token"); + string secret = settings.get_string("oauth-access-token-secret"); + + var oauthObject = new Rest.OAuthProxy.with_token ( + TwitterSecrets.key, + TwitterSecrets.secret, + token, + secret, + "https://api.twitter.com/", + false); + + var call = oauthObject.new_call(); + call.set_function("1.1/statuses/update.json"); + call.set_method("POST"); + call.add_param ("status", m_tweet.replace("$URL", url)); + + try + { + call.run(); + } + catch(Error e) + { + Logger.error(e.message); + return false; + } + + return true; } - catch(Error e) + + public bool logout(string id) { - Logger.error(e.message); + var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/twitter/%s/".printf(id)); + var keys = settings.list_keys(); + foreach(string key in keys) + { + settings.reset(key); + } + + var array = Settings.share("twitter").get_strv("account-ids"); + string[] array2 = {}; + + foreach(string i in array) + { + if(i != id) + { + array2 += i; + } + } + Settings.share("twitter").set_strv("account-ids", array2); + deleteAccount(id); + + return true; } - - var parser = new Json.Parser(); - try + + public string getURL(string token) { - parser.load_from_data(call.get_payload()); + return TwitterSecrets.base_uri + + "oauth/authenticate" + + "?oauth_token=" + token; } - catch(Error e) + + public string getIconName() { - Logger.error("Could not load response to Message from twitter"); - Logger.error(e.message); + return "feed-share-twitter"; } - - var root_object = parser.get_root().get_object(); - - if(root_object.has_member("screen_name")) + + public string getUsername(string id) { - string screenName = "@" + root_object.get_string_member("screen_name"); - settings.set_string("username", screenName); + var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/twitter/%s/".printf(id)); + return settings.get_string("username"); } - else + + public bool needSetup() { - settings.set_string("username", root_object.get_string_member("name")); + return true; } - - - - var array = Settings.share("twitter").get_strv("account-ids"); - array += id; - Settings.share("twitter").set_strv("account-ids", array); - - return true; -} - - -public bool addBookmark(string id, string url, bool system) -{ - var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/twitter/%s/".printf(id)); - string token = settings.get_string("oauth-access-token"); - string secret = settings.get_string("oauth-access-token-secret"); - - var oauthObject = new Rest.OAuthProxy.with_token ( - TwitterSecrets.key, - TwitterSecrets.secret, - token, - secret, - "https://api.twitter.com/", - false); - - var call = oauthObject.new_call(); - call.set_function("1.1/statuses/update.json"); - call.set_method("POST"); - call.add_param ("status", m_tweet.replace("$URL", url)); - - try + + public bool singleInstance() { - call.run(); + return false; } - catch(Error e) + + public bool useSystemAccounts() { - Logger.error(e.message); return false; } - - return true; -} - -public bool logout(string id) -{ - var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/twitter/%s/".printf(id)); - var keys = settings.list_keys(); - foreach(string key in keys) + + public string pluginID() { - settings.reset(key); + return "twitter"; } - - var array = Settings.share("twitter").get_strv("account-ids"); - string[] array2 = {}; - - foreach(string i in array) + + public string pluginName() { - if(i != id) - { - array2 += i; - } + return "Twitter"; } - Settings.share("twitter").set_strv("account-ids", array2); - deleteAccount(id); - - return true; -} - -public string getURL(string token) -{ - return TwitterSecrets.base_uri - + "oauth/authenticate" - + "?oauth_token=" + token; -} - -public string getIconName() -{ - return "feed-share-twitter"; -} - -public string getUsername(string id) -{ - var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/twitter/%s/".printf(id)); - return settings.get_string("username"); -} - -public bool needSetup() -{ - return true; -} - -public bool singleInstance() -{ - return false; -} - -public bool useSystemAccounts() -{ - return false; -} - -public string pluginID() -{ - return "twitter"; -} - -public string pluginName() -{ - return "Twitter"; -} - -public ServiceSetup? newSetup_withID(string id, string username) -{ - return new TwitterSetup(id, this, username); -} - -public ServiceSetup? newSetup() -{ - return new TwitterSetup(null, this); -} - -public ServiceSetup? newSystemAccount(string id, string username) -{ - return null; -} - -public ShareForm? shareWidget(string url) -{ - var widget = new TwitterForm(url); - - widget.setAPI.begin(this, (obj, res) => { + + public ServiceSetup? newSetup_withID(string id, string username) + { + return new TwitterSetup(id, this, username); + } + + public ServiceSetup? newSetup() + { + return new TwitterSetup(null, this); + } + + public ServiceSetup? newSystemAccount(string id, string username) + { + return null; + } + + public ShareForm? shareWidget(string url) + { + var widget = new TwitterForm(url); + + widget.setAPI.begin(this, (obj, res) => { widget.setAPI.end(res); }); - widget.share.connect(() => { + widget.share.connect(() => { m_tweet = widget.getTweet(); }); - return widget; -} - -public int getUrlLength() -{ - if(m_urlLength > 0) - { - return m_urlLength; - } - - var array = Settings.share("twitter").get_strv("account-ids"); - string id = array[0]; - - var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/twitter/%s/".printf(id)); - string token = settings.get_string("oauth-access-token"); - string secret = settings.get_string("oauth-access-token-secret"); - - var oauthObject = new Rest.OAuthProxy.with_token ( - TwitterSecrets.key, - TwitterSecrets.secret, - token, - secret, - "https://api.twitter.com/", - false); - - var call = oauthObject.new_call(); - call.set_function("1.1/help/configuration.json"); - call.set_method("GET"); - - try - { - call.run(); + return widget; } - catch(Error e) {} - - var parser = new Json.Parser(); - try + + public int getUrlLength() { - parser.load_from_data(call.get_payload()); + if(m_urlLength > 0) + { + return m_urlLength; + } + + var array = Settings.share("twitter").get_strv("account-ids"); + string id = array[0]; + + var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/twitter/%s/".printf(id)); + string token = settings.get_string("oauth-access-token"); + string secret = settings.get_string("oauth-access-token-secret"); + + var oauthObject = new Rest.OAuthProxy.with_token ( + TwitterSecrets.key, + TwitterSecrets.secret, + token, + secret, + "https://api.twitter.com/", + false); + + var call = oauthObject.new_call(); + call.set_function("1.1/help/configuration.json"); + call.set_method("GET"); + + try + { + call.run(); + } + catch(Error e) {} + + var parser = new Json.Parser(); + try + { + parser.load_from_data(call.get_payload()); + } + catch(Error e) {} + + var root_object = parser.get_root().get_object(); + m_urlLength = (int)root_object.get_int_member("short_url_length"); + return m_urlLength; } - catch(Error e) {} - - var root_object = parser.get_root().get_object(); - m_urlLength = (int)root_object.get_int_member("short_url_length"); - return m_urlLength; -} } [ModuleInit] diff --git a/plugins/share/Twitter/TwitterForm.vala b/plugins/share/Twitter/TwitterForm.vala index f44424dc..5d87ba25 100644 --- a/plugins/share/Twitter/TwitterForm.vala +++ b/plugins/share/Twitter/TwitterForm.vala @@ -15,127 +15,127 @@ public class FeedReader.TwitterForm : ShareForm { - -private Gtk.TextView m_textView; -private int m_urlLength = 0; -private string m_url; -private Gtk.Stack m_stack; -private Gtk.Label m_countLabel; - -public TwitterForm(string url) -{ - m_url = url; - m_stack = new Gtk.Stack(); - string body = _("Hey,\n\nCheck out this interesting article I just read: $URL"); - - m_textView = new Gtk.TextView(); - m_textView.set_wrap_mode(Gtk.WrapMode.WORD); - m_textView.buffer.text = body; - m_textView.border_width = 2; - m_textView.get_style_context().add_class("h3"); - - var scrolled = new Gtk.ScrolledWindow(null, null); - scrolled.get_style_context().add_class(Gtk.STYLE_CLASS_FRAME); - scrolled.add(m_textView); - - int margin = 5; - m_textView.left_margin = margin; - m_textView.right_margin = margin; - m_textView.top_margin = margin; - m_textView.bottom_margin = margin; - - var limitLabel = new Gtk.Label(_("Limit: ")); - limitLabel.set_alignment(0.0f, 0.5f); - limitLabel.get_style_context().add_class("h3"); - - m_countLabel = new Gtk.Label(""); - m_countLabel.set_alignment(0.0f, 0.5f); - m_countLabel.get_style_context().add_class("h3"); - var spinner = new Gtk.Spinner(); - - m_stack.add_named(m_countLabel, "label"); - m_stack.add_named(spinner, "spinner"); - - m_textView.buffer.changed.connect(() => { + + private Gtk.TextView m_textView; + private int m_urlLength = 0; + private string m_url; + private Gtk.Stack m_stack; + private Gtk.Label m_countLabel; + + public TwitterForm(string url) + { + m_url = url; + m_stack = new Gtk.Stack(); + string body = _("Hey,\n\nCheck out this interesting article I just read: $URL"); + + m_textView = new Gtk.TextView(); + m_textView.set_wrap_mode(Gtk.WrapMode.WORD); + m_textView.buffer.text = body; + m_textView.border_width = 2; + m_textView.get_style_context().add_class("h3"); + + var scrolled = new Gtk.ScrolledWindow(null, null); + scrolled.get_style_context().add_class(Gtk.STYLE_CLASS_FRAME); + scrolled.add(m_textView); + + int margin = 5; + m_textView.left_margin = margin; + m_textView.right_margin = margin; + m_textView.top_margin = margin; + m_textView.bottom_margin = margin; + + var limitLabel = new Gtk.Label(_("Limit: ")); + limitLabel.set_alignment(0.0f, 0.5f); + limitLabel.get_style_context().add_class("h3"); + + m_countLabel = new Gtk.Label(""); + m_countLabel.set_alignment(0.0f, 0.5f); + m_countLabel.get_style_context().add_class("h3"); + var spinner = new Gtk.Spinner(); + + m_stack.add_named(m_countLabel, "label"); + m_stack.add_named(spinner, "spinner"); + + m_textView.buffer.changed.connect(() => { updateCount(); }); - - var button = new Gtk.Button.with_label(_("Tweet")); - button.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); - button.clicked.connect(() => { share(); }); - - var box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); - box.pack_start(limitLabel, false, false, 0); - box.pack_start(m_stack, false, false, 0); - box.pack_end(button, false, false, 0); - - var backButton = new Gtk.Button.from_icon_name("go-previous-symbolic"); - backButton.set_focus_on_click(false); - backButton.set_relief(Gtk.ReliefStyle.NONE); - backButton.halign = Gtk.Align.START; - backButton.clicked.connect(() => { + + var button = new Gtk.Button.with_label(_("Tweet")); + button.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); + button.clicked.connect(() => { share(); }); + + var box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); + box.pack_start(limitLabel, false, false, 0); + box.pack_start(m_stack, false, false, 0); + box.pack_end(button, false, false, 0); + + var backButton = new Gtk.Button.from_icon_name("go-previous-symbolic"); + backButton.set_focus_on_click(false); + backButton.set_relief(Gtk.ReliefStyle.NONE); + backButton.halign = Gtk.Align.START; + backButton.clicked.connect(() => { goBack(); }); - - var headline = new Gtk.Label(_("Tweet to Followers")); - headline.get_style_context().add_class("h2"); - headline.set_alignment(0.4f, 0.5f); - var box2 = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); - box2.pack_start(backButton, false, false, 0); - box2.pack_start(headline, true, true, 0); - - this.pack_start(box2, false, false, 0); - this.pack_start(scrolled); - this.pack_end(box, false, false); - this.orientation = Gtk.Orientation.VERTICAL; - this.spacing = 5; - this.margin = 10; - this.show_all(); - - m_stack.set_visible_child_name("spinner"); - spinner.start(); -} - -public string getTweet() -{ - return m_textView.buffer.text; -} - -public async void setAPI(TwitterAPI api) -{ - SourceFunc callback = setAPI.callback; - - new Thread<void*>(null, () => { + + var headline = new Gtk.Label(_("Tweet to Followers")); + headline.get_style_context().add_class("h2"); + headline.set_alignment(0.4f, 0.5f); + var box2 = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); + box2.pack_start(backButton, false, false, 0); + box2.pack_start(headline, true, true, 0); + + this.pack_start(box2, false, false, 0); + this.pack_start(scrolled); + this.pack_end(box, false, false); + this.orientation = Gtk.Orientation.VERTICAL; + this.spacing = 5; + this.margin = 10; + this.show_all(); + + m_stack.set_visible_child_name("spinner"); + spinner.start(); + } + + public string getTweet() + { + return m_textView.buffer.text; + } + + public async void setAPI(TwitterAPI api) + { + SourceFunc callback = setAPI.callback; + + new Thread<void*>(null, () => { m_urlLength = api.getUrlLength(); Idle.add((owned) callback, GLib.Priority.HIGH_IDLE); return null; }); - - yield; - updateCount(); - m_stack.set_visible_child_name("label"); -} - -private int calcLenght(string text) -{ - if(text.contains("$URL")) + + yield; + updateCount(); + m_stack.set_visible_child_name("label"); + } + + private int calcLenght(string text) { - if(m_url.length >= m_urlLength) + if(text.contains("$URL")) { - return (text.length-3) + m_urlLength; - } - else - { - return (text.length-3) + m_url.length; + if(m_url.length >= m_urlLength) + { + return (text.length-3) + m_urlLength; + } + else + { + return (text.length-3) + m_url.length; + } } + + return text.length; } - - return text.length; -} - -private void updateCount() -{ - m_countLabel.set_text(calcLenght(m_textView.buffer.text).to_string() + "/140"); -} - + + private void updateCount() + { + m_countLabel.set_text(calcLenght(m_textView.buffer.text).to_string() + "/140"); + } + } diff --git a/plugins/share/Twitter/TwitterSetup.vala b/plugins/share/Twitter/TwitterSetup.vala index a1686802..d6549473 100644 --- a/plugins/share/Twitter/TwitterSetup.vala +++ b/plugins/share/Twitter/TwitterSetup.vala @@ -14,88 +14,88 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.TwitterSetup : ServiceSetup { - -private TwitterAPI m_api; - -public TwitterSetup(string? id, TwitterAPI api, string username = "") -{ - bool loggedIN = false; - if(username != "") + + private TwitterAPI m_api; + + public TwitterSetup(string? id, TwitterAPI api, string username = "") { - loggedIN = true; + bool loggedIN = false; + if(username != "") + { + loggedIN = true; + } + + base("Twitter", "feed-share-twitter", loggedIN, username); + + m_api = api; + + if(id != null) + { + m_id = id; + } } - - base("Twitter", "feed-share-twitter", loggedIN, username); - - m_api = api; - - if(id != null) + + + public override void login() { - m_id = id; - } -} - - -public override void login() -{ - string id = Share.get_default().generateNewID(); - string requestToken = m_api.getRequestToken(); - string url = m_api.getURL(requestToken); - m_spinner.start(); - m_iconStack.set_visible_child_name("spinner"); - try - { - Gtk.show_uri_on_window(SettingsDialog.get_default(), url, Gdk.CURRENT_TIME); - } - catch(GLib.Error e) - { - - } - - m_login_button.set_label(_("waiting")); - m_login_button.set_sensitive(false); - FeedReaderApp.get_default().callback.connect((content) => { - + string id = Share.get_default().generateNewID(); + string requestToken = m_api.getRequestToken(); + string url = m_api.getURL(requestToken); + m_spinner.start(); + m_iconStack.set_visible_child_name("spinner"); + try + { + Gtk.show_uri_on_window(SettingsDialog.get_default(), url, Gdk.CURRENT_TIME); + } + catch(GLib.Error e) + { + + } + + m_login_button.set_label(_("waiting")); + m_login_button.set_sensitive(false); + FeedReaderApp.get_default().callback.connect((content) => { + if(content.has_prefix(TwitterSecrets.callback)) { - int token_start = content.index_of("token=")+6; - int token_end = content.index_of("&", token_start); - string token = content.substring(token_start, token_end-token_start); - - int verifier_start = content.index_of("verifier=")+9; - string verifier = content.substring(verifier_start); - - if(token == requestToken) - { - if(m_api.getAccessToken(id, verifier)) - { - m_id = id; - m_api.addAccount(id, m_api.pluginID(), m_api.getUsername(id), m_api.getIconName(), m_api.pluginName()); - m_iconStack.set_visible_child_full("loggedIN", Gtk.StackTransitionType.SLIDE_LEFT); - m_isLoggedIN = true; - m_spinner.stop(); - m_label.set_label(m_api.getUsername(id)); - m_labelStack.set_visible_child_full("loggedIN", Gtk.StackTransitionType.CROSSFADE); - m_login_button.clicked.disconnect(login); - m_login_button.clicked.connect(logout); + int token_start = content.index_of("token=")+6; + int token_end = content.index_of("&", token_start); + string token = content.substring(token_start, token_end-token_start); + + int verifier_start = content.index_of("verifier=")+9; + string verifier = content.substring(verifier_start); + + if(token == requestToken) + { + if(m_api.getAccessToken(id, verifier)) + { + m_id = id; + m_api.addAccount(id, m_api.pluginID(), m_api.getUsername(id), m_api.getIconName(), m_api.pluginName()); + m_iconStack.set_visible_child_full("loggedIN", Gtk.StackTransitionType.SLIDE_LEFT); + m_isLoggedIN = true; + m_spinner.stop(); + m_label.set_label(m_api.getUsername(id)); + m_labelStack.set_visible_child_full("loggedIN", Gtk.StackTransitionType.CROSSFADE); + m_login_button.clicked.disconnect(login); + m_login_button.clicked.connect(logout); } - else - { - m_iconStack.set_visible_child_full("button", Gtk.StackTransitionType.SLIDE_RIGHT); + else + { + m_iconStack.set_visible_child_full("button", Gtk.StackTransitionType.SLIDE_RIGHT); } } - + } }); -} - -public override void logout() -{ - m_isLoggedIN = false; - m_iconStack.set_visible_child_full("button", Gtk.StackTransitionType.SLIDE_RIGHT); - m_labelStack.set_visible_child_name("loggedOUT"); - m_api.logout(m_id); - removeRow(); -} - + } + + public override void logout() + { + m_isLoggedIN = false; + m_iconStack.set_visible_child_full("button", Gtk.StackTransitionType.SLIDE_RIGHT); + m_labelStack.set_visible_child_name("loggedOUT"); + m_api.logout(m_id); + removeRow(); + } + } diff --git a/plugins/share/Wallabag/WallabagAPI.vala b/plugins/share/Wallabag/WallabagAPI.vala index edfc80b0..a1b9e5c7 100644 --- a/plugins/share/Wallabag/WallabagAPI.vala +++ b/plugins/share/Wallabag/WallabagAPI.vala @@ -14,318 +14,318 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.WallabagAPI : ShareAccountInterface, Peas.ExtensionBase { - -public WallabagAPI() -{ - -} - -public void setupSystemAccounts(Gee.List<ShareAccount> accounts) -{ - -} - -public bool getAccessToken(string id, string username, string password, string clientID, string clientSecret, string baseURL) -{ - Logger.debug("WallabagAPI getAccessToken"); - var session = new Soup.Session(); - session.user_agent = Constants.USER_AGENT; - string message = "grant_type=password" - + "&client_id=" + clientID - + "&client_secret=" + clientSecret - + "&username=" + GLib.Uri.escape_string(username) - + "&password=" + GLib.Uri.escape_string(password); - - - string url = baseURL + "oauth/v2/token"; - var message_soup = new Soup.Message("POST", url); - message_soup.set_request("application/x-www-form-urlencoded; charset=UTF8", Soup.MemoryUse.COPY, message.data); - session.send_message(message_soup); - - if((string)message_soup.response_body.flatten().data == null - || (string)message_soup.response_body.flatten().data == "") + + public WallabagAPI() { - Logger.error("WallabagAPI - getAccessToken: no response"); - Logger.error(url); - Logger.error(message); - return false; + } - - string response = (string)message_soup.response_body.flatten().data; - Logger.debug(response); - - var parser = new Json.Parser(); - try + + public void setupSystemAccounts(Gee.List<ShareAccount> accounts) { - parser.load_from_data(response); + } - catch (Error e) + + public bool getAccessToken(string id, string username, string password, string clientID, string clientSecret, string baseURL) { - Logger.error("Could not load response to Message from Wallabag"); - Logger.error(e.message); - return false; + Logger.debug("WallabagAPI getAccessToken"); + var session = new Soup.Session(); + session.user_agent = Constants.USER_AGENT; + string message = "grant_type=password" + + "&client_id=" + clientID + + "&client_secret=" + clientSecret + + "&username=" + GLib.Uri.escape_string(username) + + "&password=" + GLib.Uri.escape_string(password); + + + string url = baseURL + "oauth/v2/token"; + var message_soup = new Soup.Message("POST", url); + message_soup.set_request("application/x-www-form-urlencoded; charset=UTF8", Soup.MemoryUse.COPY, message.data); + session.send_message(message_soup); + + if((string)message_soup.response_body.flatten().data == null + || (string)message_soup.response_body.flatten().data == "") + { + Logger.error("WallabagAPI - getAccessToken: no response"); + Logger.error(url); + Logger.error(message); + return false; + } + + string response = (string)message_soup.response_body.flatten().data; + Logger.debug(response); + + var parser = new Json.Parser(); + try + { + parser.load_from_data(response); + } + catch (Error e) + { + Logger.error("Could not load response to Message from Wallabag"); + Logger.error(e.message); + return false; + } + + var root_node = parser.get_root(); + var root_object = root_node.get_object(); + + if(!root_object.has_member("access_token")) + { + Logger.error("WallabagAPI.getAccessToken: no member access_token in response"); + return false; + } + string accessToken = root_object.get_string_member("access_token"); + + + int64 now = (new DateTime.now_local()).to_unix(); + if(!root_object.has_member("expires_in")) + { + Logger.error("WallabagAPI.getAccessToken: no member expires_in in response"); + return false; + } + int64 expires = root_object.get_int_member("expires_in"); + //string refreshToken = root_object.get_string_member("refresh_token"); + + var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/wallabag/%s/".printf(id)); + settings.set_string("oauth-access-token", accessToken); + settings.set_string("username", username); + settings.set_int("access-token-expires", (int)(now + expires)); + settings.set_string("url", baseURL); + settings.set_string("client-id", clientID); + settings.set_string("client-secret", clientSecret); + + + var array = Settings.share("wallabag").get_strv("account-ids"); + foreach(string i in array) + { + if(i == id) + { + Logger.warning("WallabagAPI - getAccessToken: id already part of array. Returning"); + return true; + } + } + array += id; + Settings.share("wallabag").set_strv("account-ids", array); + + + var pwSchema = new Secret.Schema ("org.gnome.feedreader.wallabag.password", Secret.SchemaFlags.NONE, + "username", Secret.SchemaAttributeType.STRING, + "id", Secret.SchemaAttributeType.STRING); + + var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); + attributes["username"] = username; + attributes["id"] = id; + try + { + Secret.password_storev_sync(pwSchema, attributes, Secret.COLLECTION_DEFAULT, "Feedreader: Wallabag login", password, null); + } + catch(GLib.Error e) + { + Logger.error("WallabagAPI - getAccessToken: " + e.message); + } + + return true; } - - var root_node = parser.get_root(); - var root_object = root_node.get_object(); - - if(!root_object.has_member("access_token")) + + + public bool addBookmark(string id, string url, bool system) { - Logger.error("WallabagAPI.getAccessToken: no member access_token in response"); - return false; + var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/wallabag/%s/".printf(id)); + + Logger.debug("WallabagAPI - addBookmark: " + url); + if(!accessTokenValid(id)) + { + string username = getUsername(id); + string password = getPasswd(id); + string clientID = settings.get_string("client-id"); + string clientSecret = settings.get_string("client-secret"); + string baseURL = settings.get_string("url"); + + getAccessToken(id, username, password, clientID, clientSecret, baseURL); + } + + Logger.debug("WallabagAPI - addBookmark: token still valid"); + + var session = new Soup.Session(); + session.user_agent = Constants.USER_AGENT; + string message = "url=" + GLib.Uri.escape_string(url); + string baseURL = settings.get_string("url"); + + var message_soup = new Soup.Message("POST", baseURL + "api/entries.json"); + message_soup.set_request("application/x-www-form-urlencoded; charset=UTF8", Soup.MemoryUse.COPY, message.data); + message_soup.request_headers.append("Authorization", "Bearer " + settings.get_string("oauth-access-token")); + session.send_message(message_soup); + + if((string)message_soup.response_body.flatten().data == null + || (string)message_soup.response_body.flatten().data == "") + { + Logger.error("WallabagAPI - addBookmark: no response"); + Logger.error(url); + Logger.error(message); + return false; + } + + return true; } - string accessToken = root_object.get_string_member("access_token"); - - - int64 now = (new DateTime.now_local()).to_unix(); - if(!root_object.has_member("expires_in")) + + public bool logout(string id) { - Logger.error("WallabagAPI.getAccessToken: no member expires_in in response"); - return false; + Logger.debug("WallabagAPI - logout"); + deletePassword(id); + + var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/wallabag/%s/".printf(id)); + var keys = settings.list_keys(); + foreach(string key in keys) + { + settings.reset(key); + } + + var array = Settings.share("wallabag").get_strv("account-ids"); + string[] array2 = {}; + + foreach(string i in array) + { + if(i != id) + { + array2 += i; + } + } + Settings.share("wallabag").set_strv("account-ids", array2); + deleteAccount(id); + + return true; } - int64 expires = root_object.get_int_member("expires_in"); - //string refreshToken = root_object.get_string_member("refresh_token"); - - var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/wallabag/%s/".printf(id)); - settings.set_string("oauth-access-token", accessToken); - settings.set_string("username", username); - settings.set_int("access-token-expires", (int)(now + expires)); - settings.set_string("url", baseURL); - settings.set_string("client-id", clientID); - settings.set_string("client-secret", clientSecret); - - - var array = Settings.share("wallabag").get_strv("account-ids"); - foreach(string i in array) + + private bool accessTokenValid(string id) { - if(i == id) + var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/wallabag/%s/".printf(id)); + var now = new DateTime.now_local(); + int expires = settings.get_int("access-token-expires"); + + if((int)now.to_unix() > expires) { - Logger.warning("WallabagAPI - getAccessToken: id already part of array. Returning"); - return true; + Logger.warning("WallabagAPI: access token expired"); + return false; } + + return true; } - array += id; - Settings.share("wallabag").set_strv("account-ids", array); - - - var pwSchema = new Secret.Schema ("org.gnome.feedreader.wallabag.password", Secret.SchemaFlags.NONE, - "username", Secret.SchemaAttributeType.STRING, - "id", Secret.SchemaAttributeType.STRING); - - var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); - attributes["username"] = username; - attributes["id"] = id; - try + + public string getIconName() { - Secret.password_storev_sync(pwSchema, attributes, Secret.COLLECTION_DEFAULT, "Feedreader: Wallabag login", password, null); + return "feed-share-wallabag"; } - catch(GLib.Error e) + + public string getUsername(string id) { - Logger.error("WallabagAPI - getAccessToken: " + e.message); + var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/wallabag/%s/".printf(id)); + return settings.get_string("username"); } - - return true; -} - - -public bool addBookmark(string id, string url, bool system) -{ - var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/wallabag/%s/".printf(id)); - - Logger.debug("WallabagAPI - addBookmark: " + url); - if(!accessTokenValid(id)) + + public string getPasswd(string id) { - string username = getUsername(id); - string password = getPasswd(id); - string clientID = settings.get_string("client-id"); - string clientSecret = settings.get_string("client-secret"); - string baseURL = settings.get_string("url"); - - getAccessToken(id, username, password, clientID, clientSecret, baseURL); + var pwSchema = new Secret.Schema ("org.gnome.feedreader.wallabag.password", Secret.SchemaFlags.NONE, + "username", Secret.SchemaAttributeType.STRING, + "id", Secret.SchemaAttributeType.STRING); + + var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); + attributes["username"] = getUsername(id); + attributes["id"] = id; + + string passwd = ""; + + try + { + passwd = Secret.password_lookupv_sync(pwSchema, attributes, null); + } + catch(GLib.Error e) + { + Logger.error(e.message); + } + + if(passwd == null) + { + return ""; + } + + return passwd; } - - Logger.debug("WallabagAPI - addBookmark: token still valid"); - - var session = new Soup.Session(); - session.user_agent = Constants.USER_AGENT; - string message = "url=" + GLib.Uri.escape_string(url); - string baseURL = settings.get_string("url"); - - var message_soup = new Soup.Message("POST", baseURL + "api/entries.json"); - message_soup.set_request("application/x-www-form-urlencoded; charset=UTF8", Soup.MemoryUse.COPY, message.data); - message_soup.request_headers.append("Authorization", "Bearer " + settings.get_string("oauth-access-token")); - session.send_message(message_soup); - - if((string)message_soup.response_body.flatten().data == null - || (string)message_soup.response_body.flatten().data == "") + + private void deletePassword(string id) { - Logger.error("WallabagAPI - addBookmark: no response"); - Logger.error(url); - Logger.error(message); - return false; + bool removed = false; + var pwSchema = new Secret.Schema ("org.gnome.feedreader.wallabag.password", Secret.SchemaFlags.NONE, + "username", Secret.SchemaAttributeType.STRING, + "id", Secret.SchemaAttributeType.STRING); + + var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); + attributes["username"] = getUsername(id); + attributes["id"] = id; + + Logger.debug(getUsername(id)); + Logger.debug(id); + + Secret.password_clearv.begin(pwSchema, attributes, null, (obj, async_res) => { + try + { + removed = Secret.password_clearv.end(async_res); + + if(!removed) + { + Logger.error(@"WallabagAPI: could not delete password of account $id"); + } + } + catch(GLib.Error e) + { + Logger.error("WallabagAPI.deletePassword: %s".printf(e.message)); + } + }); } - - return true; -} - -public bool logout(string id) -{ - Logger.debug("WallabagAPI - logout"); - deletePassword(id); - - var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/wallabag/%s/".printf(id)); - var keys = settings.list_keys(); - foreach(string key in keys) + + public bool needSetup() { - settings.reset(key); + return true; } - - var array = Settings.share("wallabag").get_strv("account-ids"); - string[] array2 = {}; - - foreach(string i in array) + + public bool singleInstance() { - if(i != id) - { - array2 += i; - } + return false; } - Settings.share("wallabag").set_strv("account-ids", array2); - deleteAccount(id); - - return true; -} - -private bool accessTokenValid(string id) -{ - var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/wallabag/%s/".printf(id)); - var now = new DateTime.now_local(); - int expires = settings.get_int("access-token-expires"); - - if((int)now.to_unix() > expires) + + public bool useSystemAccounts() { - Logger.warning("WallabagAPI: access token expired"); return false; } - - return true; -} - -public string getIconName() -{ - return "feed-share-wallabag"; -} - -public string getUsername(string id) -{ - var settings = new GLib.Settings.with_path("org.gnome.feedreader.share.account", "/org/gnome/feedreader/share/wallabag/%s/".printf(id)); - return settings.get_string("username"); -} - -public string getPasswd(string id) -{ - var pwSchema = new Secret.Schema ("org.gnome.feedreader.wallabag.password", Secret.SchemaFlags.NONE, - "username", Secret.SchemaAttributeType.STRING, - "id", Secret.SchemaAttributeType.STRING); - - var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); - attributes["username"] = getUsername(id); - attributes["id"] = id; - - string passwd = ""; - - try + + public string pluginID() { - passwd = Secret.password_lookupv_sync(pwSchema, attributes, null); + return "wallabag"; } - catch(GLib.Error e) + + public string pluginName() { - Logger.error(e.message); + return "wallabag"; } - - if(passwd == null) + + public ServiceSetup? newSetup_withID(string id, string username) { - return ""; + return new WallabagSetup(id, this, username); + } + + public ServiceSetup? newSetup() + { + return new WallabagSetup(null, this); + } + + public ServiceSetup? newSystemAccount(string id, string username) + { + return null; + } + + public ShareForm? shareWidget(string url) + { + return null; } - - return passwd; -} - -private void deletePassword(string id) -{ - bool removed = false; - var pwSchema = new Secret.Schema ("org.gnome.feedreader.wallabag.password", Secret.SchemaFlags.NONE, - "username", Secret.SchemaAttributeType.STRING, - "id", Secret.SchemaAttributeType.STRING); - - var attributes = new GLib.HashTable<string,string>(str_hash, str_equal); - attributes["username"] = getUsername(id); - attributes["id"] = id; - - Logger.debug(getUsername(id)); - Logger.debug(id); - - Secret.password_clearv.begin(pwSchema, attributes, null, (obj, async_res) => { - try - { - removed = Secret.password_clearv.end(async_res); - - if(!removed) - { - Logger.error(@"WallabagAPI: could not delete password of account $id"); - } - } - catch(GLib.Error e) - { - Logger.error("WallabagAPI.deletePassword: %s".printf(e.message)); - } - }); -} - -public bool needSetup() -{ - return true; -} - -public bool singleInstance() -{ - return false; -} - -public bool useSystemAccounts() -{ - return false; -} - -public string pluginID() -{ - return "wallabag"; -} - -public string pluginName() -{ - return "wallabag"; -} - -public ServiceSetup? newSetup_withID(string id, string username) -{ - return new WallabagSetup(id, this, username); -} - -public ServiceSetup? newSetup() -{ - return new WallabagSetup(null, this); -} - -public ServiceSetup? newSystemAccount(string id, string username) -{ - return null; -} - -public ShareForm? shareWidget(string url) -{ - return null; -} } [ModuleInit] diff --git a/plugins/share/Wallabag/WallabagSetup.vala b/plugins/share/Wallabag/WallabagSetup.vala index caaccddd..2a2d8746 100644 --- a/plugins/share/Wallabag/WallabagSetup.vala +++ b/plugins/share/Wallabag/WallabagSetup.vala @@ -14,201 +14,201 @@ // along with FeedReader. If not, see <http://www.gnu.org/licenses/>. public class FeedReader.WallabagSetup : ServiceSetup { - -private Gtk.Entry m_urlEntry; -private Gtk.Entry m_idEntry; -private Gtk.Entry m_secretEntry; -private Gtk.Entry m_userEntry; -private Gtk.Entry m_passEntry; -private Gtk.Revealer m_login_revealer; -private WallabagAPI m_api; - -public WallabagSetup(string? id, WallabagAPI api, string username = "") -{ - bool loggedIN = false; - if(username != "") + + private Gtk.Entry m_urlEntry; + private Gtk.Entry m_idEntry; + private Gtk.Entry m_secretEntry; + private Gtk.Entry m_userEntry; + private Gtk.Entry m_passEntry; + private Gtk.Revealer m_login_revealer; + private WallabagAPI m_api; + + public WallabagSetup(string? id, WallabagAPI api, string username = "") { - loggedIN = true; - } - - base("wallabag", "feed-share-wallabag", loggedIN, username); - - //------------------------------------------------ - // XAuth revealer - //------------------------------------------------ - var grid = new Gtk.Grid(); - grid.set_column_spacing(10); - grid.set_row_spacing(10); - grid.set_valign(Gtk.Align.CENTER); - grid.set_halign(Gtk.Align.CENTER); - grid.margin_bottom = 10; - grid.margin_top = 5; - - m_urlEntry = new Gtk.Entry(); - m_idEntry = new Gtk.Entry(); - m_secretEntry = new Gtk.Entry(); - m_userEntry = new Gtk.Entry(); - m_passEntry = new Gtk.Entry(); - m_passEntry.set_input_purpose(Gtk.InputPurpose.PASSWORD); - m_passEntry.set_visibility(false); - - m_urlEntry.activate.connect(() => { + bool loggedIN = false; + if(username != "") + { + loggedIN = true; + } + + base("wallabag", "feed-share-wallabag", loggedIN, username); + + //------------------------------------------------ + // XAuth revealer + //------------------------------------------------ + var grid = new Gtk.Grid(); + grid.set_column_spacing(10); + grid.set_row_spacing(10); + grid.set_valign(Gtk.Align.CENTER); + grid.set_halign(Gtk.Align.CENTER); + grid.margin_bottom = 10; + grid.margin_top = 5; + + m_urlEntry = new Gtk.Entry(); + m_idEntry = new Gtk.Entry(); + m_secretEntry = new Gtk.Entry(); + m_userEntry = new Gtk.Entry(); + m_passEntry = new Gtk.Entry(); + m_passEntry.set_input_purpose(Gtk.InputPurpose.PASSWORD); + m_passEntry.set_visibility(false); + + m_urlEntry.activate.connect(() => { m_idEntry.grab_focus(); }); - - m_idEntry.activate.connect(() => { + + m_idEntry.activate.connect(() => { m_secretEntry.grab_focus(); }); - - m_secretEntry.activate.connect(() => { + + m_secretEntry.activate.connect(() => { m_userEntry.grab_focus(); }); - - m_userEntry.activate.connect(() => { + + m_userEntry.activate.connect(() => { m_passEntry.grab_focus(); }); - - m_passEntry.activate.connect(() => { + + m_passEntry.activate.connect(() => { login(); }); - - var urlLabel = new Gtk.Label(_("URL:")); - var idLabel = new Gtk.Label(_("Client ID:")); - var secretLabel = new Gtk.Label(_("Client Secret:")); - var userLabel = new Gtk.Label(_("Username:")); - var pwLabel = new Gtk.Label(_("Password:")); - - urlLabel.set_alignment(1.0f, 0.5f); - idLabel.set_alignment(1.0f, 0.5f); - secretLabel.set_alignment(1.0f, 0.5f); - userLabel.set_alignment(1.0f, 0.5f); - pwLabel.set_alignment(1.0f, 0.5f); - - grid.attach(urlLabel, 0, 0, 1, 1); - grid.attach(idLabel, 0, 1, 1, 1); - grid.attach(secretLabel, 0, 2, 1, 1); - grid.attach(userLabel, 0, 3, 1, 1); - grid.attach(pwLabel, 0, 4, 1, 1); - grid.attach(m_urlEntry, 1, 0, 1, 1); - grid.attach(m_idEntry, 1, 1, 1, 1); - grid.attach(m_secretEntry, 1, 2, 1, 1); - grid.attach(m_userEntry, 1, 3, 1, 1); - grid.attach(m_passEntry, 1, 4, 1, 1); - - m_login_revealer = new Gtk.Revealer(); - m_login_revealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_DOWN); - m_login_revealer.add(grid); - //------------------------------------------------ - - m_seperator_box.pack_start(m_login_revealer, false, false, 0); - - m_api = api; - - if(id != null) - { - m_id = id; - } -} - - -public override void login() -{ - if(m_login_revealer.get_child_revealed()) - { - string id = Share.get_default().generateNewID(); - string username = m_userEntry.get_text(); - string password = m_passEntry.get_text(); - string clientID = m_idEntry.get_text(); - string clientSecret = m_secretEntry.get_text(); - string baseURL = m_urlEntry.get_text(); - - // check each and every value - if(baseURL == null || baseURL == "") - { - showInfoBar(_("Please fill in the URL.")); - m_urlEntry.grab_focus(); - return; - } - else if(GLib.Uri.parse_scheme(baseURL) == null) - { - showInfoBar(_("URL seems to not be valid.")); - m_urlEntry.grab_focus(); - return; - } - - if(!baseURL.has_suffix("/")) - { - baseURL += "/"; - } - - if(clientID == null || clientID == "") - { - showInfoBar(_("Please fill in the clientID.")); - m_idEntry.grab_focus(); - return; - } - - if(clientSecret == null || clientSecret == "") - { - showInfoBar(_("Please fill in the clientSecret.")); - m_secretEntry.grab_focus(); - return; - } - - if(password == null || password == "") + + var urlLabel = new Gtk.Label(_("URL:")); + var idLabel = new Gtk.Label(_("Client ID:")); + var secretLabel = new Gtk.Label(_("Client Secret:")); + var userLabel = new Gtk.Label(_("Username:")); + var pwLabel = new Gtk.Label(_("Password:")); + + urlLabel.set_alignment(1.0f, 0.5f); + idLabel.set_alignment(1.0f, 0.5f); + secretLabel.set_alignment(1.0f, 0.5f); + userLabel.set_alignment(1.0f, 0.5f); + pwLabel.set_alignment(1.0f, 0.5f); + + grid.attach(urlLabel, 0, 0, 1, 1); + grid.attach(idLabel, 0, 1, 1, 1); + grid.attach(secretLabel, 0, 2, 1, 1); + grid.attach(userLabel, 0, 3, 1, 1); + grid.attach(pwLabel, 0, 4, 1, 1); + grid.attach(m_urlEntry, 1, 0, 1, 1); + grid.attach(m_idEntry, 1, 1, 1, 1); + grid.attach(m_secretEntry, 1, 2, 1, 1); + grid.attach(m_userEntry, 1, 3, 1, 1); + grid.attach(m_passEntry, 1, 4, 1, 1); + + m_login_revealer = new Gtk.Revealer(); + m_login_revealer.set_transition_type(Gtk.RevealerTransitionType.SLIDE_DOWN); + m_login_revealer.add(grid); + //------------------------------------------------ + + m_seperator_box.pack_start(m_login_revealer, false, false, 0); + + m_api = api; + + if(id != null) { - showInfoBar(_("Please fill in the password.")); - m_passEntry.grab_focus(); - return; - } - - if(username == null || username == "") - { - showInfoBar(_("Please fill in the username.")); - m_userEntry.grab_focus(); - return; + m_id = id; } - - m_spinner.start(); - m_iconStack.set_visible_child_name("spinner"); - - if(m_api.getAccessToken(id, username, password, clientID, clientSecret, baseURL)) + } + + + public override void login() + { + if(m_login_revealer.get_child_revealed()) { - m_id = id; - m_api.addAccount(id, m_api.pluginID(), username, m_api.getIconName(), m_api.pluginName()); - m_login_button.get_style_context().remove_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); - m_login_revealer.set_reveal_child(false); - m_isLoggedIN = true; - m_iconStack.set_visible_child_name("loggedIN"); - m_spinner.stop(); - m_label.set_label(username); - m_labelStack.set_visible_child_name("loggedIN"); - m_login_button.clicked.disconnect(login); - m_login_button.clicked.connect(logout); + string id = Share.get_default().generateNewID(); + string username = m_userEntry.get_text(); + string password = m_passEntry.get_text(); + string clientID = m_idEntry.get_text(); + string clientSecret = m_secretEntry.get_text(); + string baseURL = m_urlEntry.get_text(); + + // check each and every value + if(baseURL == null || baseURL == "") + { + showInfoBar(_("Please fill in the URL.")); + m_urlEntry.grab_focus(); + return; + } + else if(GLib.Uri.parse_scheme(baseURL) == null) + { + showInfoBar(_("URL seems to not be valid.")); + m_urlEntry.grab_focus(); + return; + } + + if(!baseURL.has_suffix("/")) + { + baseURL += "/"; + } + + if(clientID == null || clientID == "") + { + showInfoBar(_("Please fill in the clientID.")); + m_idEntry.grab_focus(); + return; + } + + if(clientSecret == null || clientSecret == "") + { + showInfoBar(_("Please fill in the clientSecret.")); + m_secretEntry.grab_focus(); + return; + } + + if(password == null || password == "") + { + showInfoBar(_("Please fill in the password.")); + m_passEntry.grab_focus(); + return; + } + + if(username == null || username == "") + { + showInfoBar(_("Please fill in the username.")); + m_userEntry.grab_focus(); + return; + } + + m_spinner.start(); + m_iconStack.set_visible_child_name("spinner"); + + if(m_api.getAccessToken(id, username, password, clientID, clientSecret, baseURL)) + { + m_id = id; + m_api.addAccount(id, m_api.pluginID(), username, m_api.getIconName(), m_api.pluginName()); + m_login_button.get_style_context().remove_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); + m_login_revealer.set_reveal_child(false); + m_isLoggedIN = true; + m_iconStack.set_visible_child_name("loggedIN"); + m_spinner.stop(); + m_label.set_label(username); + m_labelStack.set_visible_child_name("loggedIN"); + m_login_button.clicked.disconnect(login); + m_login_button.clicked.connect(logout); + } + else + { + m_iconStack.set_visible_child_full("button", Gtk.StackTransitionType.SLIDE_RIGHT); + showInfoBar(_("Something went wrong.")); + } + } else { - m_iconStack.set_visible_child_full("button", Gtk.StackTransitionType.SLIDE_RIGHT); - showInfoBar(_("Something went wrong.")); + m_login_revealer.set_reveal_child(true); + m_login_button.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); + m_urlEntry.grab_focus(); } - } - else + + public override void logout() { - m_login_revealer.set_reveal_child(true); - m_login_button.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); - m_urlEntry.grab_focus(); + Logger.debug("WallabagSetup: logout"); + m_isLoggedIN = false; + m_iconStack.set_visible_child_full("button", Gtk.StackTransitionType.SLIDE_RIGHT); + m_labelStack.set_visible_child_name("loggedOUT"); + m_api.logout(m_id); + removeRow(); } -} - -public override void logout() -{ - Logger.debug("WallabagSetup: logout"); - m_isLoggedIN = false; - m_iconStack.set_visible_child_full("button", Gtk.StackTransitionType.SLIDE_RIGHT); - m_labelStack.set_visible_child_name("loggedOUT"); - m_api.logout(m_id); - removeRow(); -} - + } |