summaryrefslogtreecommitdiff
path: root/protocols/FacebookRM/src
diff options
context:
space:
mode:
authorRobert Pösel <robyer@seznam.cz>2014-05-18 09:47:19 +0000
committerRobert Pösel <robyer@seznam.cz>2014-05-18 09:47:19 +0000
commite63ffcd3b3c8598d28ee899bd2537b02c02d8d39 (patch)
tree12eeef452ab23f5b60022f91b04ba049c5787789 /protocols/FacebookRM/src
parent40b63e431bc8c68c91a32ca7a62337b97da30b91 (diff)
Facebook: Refactor various working threads and updating contacts procedure; Version bump
This should eliminate creation of duplicite contacts, maybe made little slower or faster logging in... git-svn-id: http://svn.miranda-ng.org/main/trunk@9217 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'protocols/FacebookRM/src')
-rw-r--r--protocols/FacebookRM/src/client.h5
-rw-r--r--protocols/FacebookRM/src/communication.cpp124
-rw-r--r--protocols/FacebookRM/src/connection.cpp27
-rw-r--r--protocols/FacebookRM/src/contacts.cpp55
-rw-r--r--protocols/FacebookRM/src/process.cpp163
-rw-r--r--protocols/FacebookRM/src/proto.cpp4
-rw-r--r--protocols/FacebookRM/src/proto.h3
-rw-r--r--protocols/FacebookRM/src/version.h4
8 files changed, 162 insertions, 223 deletions
diff --git a/protocols/FacebookRM/src/client.h b/protocols/FacebookRM/src/client.h
index 3e6993eb9a..b17cc8b202 100644
--- a/protocols/FacebookRM/src/client.h
+++ b/protocols/FacebookRM/src/client.h
@@ -148,11 +148,6 @@ public:
HANDLE buddies_lock_;
HANDLE send_message_lock_;
- bool buddy_list();
- bool load_friends();
- bool load_pages();
- bool feeds();
-
////////////////////////////////////////////////////////////
// Messages handling
diff --git a/protocols/FacebookRM/src/communication.cpp b/protocols/FacebookRM/src/communication.cpp
index 6eb267b1e0..4caf79d536 100644
--- a/protocols/FacebookRM/src/communication.cpp
+++ b/protocols/FacebookRM/src/communication.cpp
@@ -1009,130 +1009,6 @@ bool facebook_client::reconnect()
}
}
-bool facebook_client::buddy_list()
-{
- handle_entry("buddy_list");
-
- // Prepare update data
- std::string data = "user=" + this->self_.user_id + "&fetch_mobile=true&phstamp=0&fb_dtsg=" + this->dtsg_ + "&__user=" + this->self_.user_id;
-
- {
- ScopedLock s(buddies_lock_);
-
- data += "&cached_user_info_ids=";
- int counter = 0;
- for (List::Item< facebook_user >* i = buddies.begin(); i != NULL; i = i->next, counter++)
- {
- data += i->data->user_id + "%2C";
- }
- }
-
- // Get buddy list
- http::response resp = flap(REQUEST_BUDDY_LIST, &data);
-
- switch (resp.code) {
- case HTTP_CODE_OK:
- parent->ForkThread(&FacebookProto::ProcessBuddyList, new std::string(resp.data));
- return handle_success("buddy_list");
-
- case HTTP_CODE_FAKE_ERROR:
- case HTTP_CODE_FAKE_DISCONNECTED:
- default:
- return handle_error("buddy_list");
- }
-}
-
-bool facebook_client::load_friends()
-{
- handle_entry("load_friends");
-
- // Get buddy list
- http::response resp = flap(REQUEST_LOAD_FRIENDS);
-
- switch (resp.code) {
- case HTTP_CODE_OK:
- parent->ForkThread(&FacebookProto::ProcessFriendList, new std::string(resp.data));
- return handle_success("load_friends");
-
- case HTTP_CODE_FAKE_ERROR:
- case HTTP_CODE_FAKE_DISCONNECTED:
- default:
- return handle_error("load_friends");
- }
-}
-
-bool facebook_client::feeds()
-{
- handle_entry("feeds");
-
- // Get feeds
- http::response resp = flap(REQUEST_FEEDS);
-
- switch (resp.code) {
- case HTTP_CODE_OK:
- if (resp.data.find("\"num_stories\":0") == std::string::npos)
- parent->ForkThread(&FacebookProto::ProcessFeeds, new std::string(resp.data));
-
- return handle_success("feeds");
-
- case HTTP_CODE_FAKE_ERROR:
- case HTTP_CODE_FAKE_DISCONNECTED:
- default:
- return handle_error("feeds");
- }
-}
-
-bool facebook_client::load_pages()
-{
- if (!parent->getByte(FACEBOOK_KEY_LOAD_PAGES, DEFAULT_LOAD_PAGES))
- return true;
-
- handle_entry("load_pages");
-
- // Get feeds
- http::response resp = flap(REQUEST_PAGES);
-
- switch (resp.code) {
- case HTTP_CODE_OK:
- {
- std::string content = utils::text::source_get_value(&resp.data, 2, "id=\"bookmarksSeeAllSection\"", "</code>");
-
- std::string::size_type start, end;
- start = content.find("<li", 0);
- while (start != std::string::npos) {
- end = content.find("<li", start+1);
- if (end == std::string::npos)
- end = content.length();
-
- std::string item = content.substr(start, end - start);
- //item = utils::text::source_get_value(&item, 2, "data-gt=", ">");
-
- start = content.find("<li", start+1);
-
- std::string id = utils::text::source_get_value(&item, 3, "data-gt=", "bmid&quot;:&quot;", "&quot;");
- std::string title = utils::text::special_expressions_decode(utils::text::slashu_to_utf8(utils::text::source_get_value(&item, 3, "data-gt=", "title=\"", "\"")));
- std::string href = utils::text::source_get_value(&item, 3, "data-gt=", "href=\"", "\"");
-
- // Ignore pages channel
- if (href.find("/pages/feed") != std::string::npos)
- continue;
-
- if (id.empty() || title.empty())
- continue;
-
- parent->debugLogA(" Got page: %s (%s)", title.c_str(), id.c_str());
- pages[id] = title;
- }
-
- return handle_success("load_pages");
- }
- case HTTP_CODE_FAKE_ERROR:
- case HTTP_CODE_FAKE_DISCONNECTED:
- default:
- return handle_error("load_pages");
- }
-}
-
bool facebook_client::channel()
{
handle_entry("channel");
diff --git a/protocols/FacebookRM/src/connection.cpp b/protocols/FacebookRM/src/connection.cpp
index 4ff500a31b..48c290dfba 100644
--- a/protocols/FacebookRM/src/connection.cpp
+++ b/protocols/FacebookRM/src/connection.cpp
@@ -77,13 +77,12 @@ void FacebookProto::ChangeStatus(void*)
ResetEvent(update_loop_lock_);
- if (NegotiateConnection() && facy.home())
+ if (NegotiateConnection() && facy.home() && facy.reconnect())
{
- facy.reconnect();
- facy.load_friends();
- facy.load_pages();
+ // Load all friends
+ ProcessFriendList(NULL);
- // Process Friends requests
+ // Process friendship requests
ForkThread(&FacebookProto::ProcessFriendRequests, NULL);
// Get unread messages
@@ -92,6 +91,9 @@ void FacebookProto::ChangeStatus(void*)
// Get notifications
ForkThread(&FacebookProto::ProcessNotifications, NULL);
+ // Load pages for post status dialog
+ ForkThread(&FacebookProto::ProcessPages, NULL);
+
setDword("LogonTS", (DWORD)time(NULL));
ForkThread(&FacebookProto::UpdateLoop, NULL);
ForkThread(&FacebookProto::MessageLoop, NULL);
@@ -122,7 +124,8 @@ void FacebookProto::ChangeStatus(void*)
}
facy.chat_state(m_iDesiredStatus != ID_STATUS_INVISIBLE);
- facy.buddy_list();
+
+ ForkThread(&FacebookProto::ProcessBuddyList, NULL);
m_iStatus = facy.self_.status_id = m_iDesiredStatus;
ProtoBroadcastAck(0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus);
@@ -175,16 +178,14 @@ void FacebookProto::UpdateLoop(void *)
for (int i = -1; !isOffline(); i = ++i % 50)
{
- if (i != -1) {
- if (!facy.buddy_list())
- break;
- }
+ if (i != -1)
+ ProcessBuddyList(NULL);
+
if (i == 2 && getByte(FACEBOOK_KEY_EVENT_FEEDS_ENABLE, DEFAULT_EVENT_FEEDS_ENABLE))
- if (!facy.feeds())
- break;
+ ProcessFeeds(NULL);
if (i == 49)
- ForkThread(&FacebookProto::ProcessFriendRequests, NULL);
+ ProcessFriendRequests(NULL);
debugLogA("***** FacebookProto::UpdateLoop[%d] going to sleep...", tim);
if (WaitForSingleObjectEx(update_loop_lock_, GetPollRate() * 1000, true) != WAIT_TIMEOUT)
diff --git a/protocols/FacebookRM/src/contacts.cpp b/protocols/FacebookRM/src/contacts.cpp
index 5c82419ac1..2fb0d5fb70 100644
--- a/protocols/FacebookRM/src/contacts.cpp
+++ b/protocols/FacebookRM/src/contacts.cpp
@@ -171,50 +171,43 @@ void FacebookProto::LoadContactInfo(facebook_user* fbu)
}
}
-MCONTACT FacebookProto::AddToContactList(facebook_user* fbu, ContactType type, bool force_save)
+MCONTACT FacebookProto::AddToContactList(facebook_user* fbu, ContactType type, bool force_add)
{
- MCONTACT hContact;
+ // First, check if this contact exists (and if does, just return it)
+ if (!force_add) {
+ MCONTACT hContact = ContactIDToHContact(fbu->user_id);
- // First, check if this contact exists
- hContact = ContactIDToHContact(fbu->user_id);
-
- // If we have contact and don't need to force save data, just return it
- if (hContact && !force_save)
- return hContact;
-
- force_save = !hContact;
+ if (hContact)
+ return hContact;
+ }
- // If not, try to make a new contact
- if (!hContact) {
- hContact = (MCONTACT)CallService(MS_DB_CONTACT_ADD, 0, 0);
+ // Try to make a new contact
+ MCONTACT hContact = (MCONTACT)CallService(MS_DB_CONTACT_ADD, 0, 0);
- if (hContact && CallService(MS_PROTO_ADDTOCONTACT, hContact, (LPARAM)m_szModuleName) != 0) {
- CallService(MS_DB_CONTACT_DELETE, hContact, 0);
- hContact = NULL;
- }
+ if (hContact && CallService(MS_PROTO_ADDTOCONTACT, hContact, (LPARAM)m_szModuleName) != 0) {
+ CallService(MS_DB_CONTACT_DELETE, hContact, 0);
+ hContact = NULL;
}
// If we have some contact, we'll save its data
if (hContact) {
- if (force_save) {
- // Save these values only when adding new contact, not when updating existing
- setString(hContact, FACEBOOK_KEY_ID, fbu->user_id.c_str());
+ // Save these values only when adding new contact, not when updating existing
+ setString(hContact, FACEBOOK_KEY_ID, fbu->user_id.c_str());
- std::string homepage = FACEBOOK_URL_PROFILE + fbu->user_id;
- setString(hContact, "Homepage", homepage.c_str());
- setTString(hContact, "MirVer", fbu->getMirVer());
+ std::string homepage = FACEBOOK_URL_PROFILE + fbu->user_id;
+ setString(hContact, "Homepage", homepage.c_str());
+ setTString(hContact, "MirVer", fbu->getMirVer());
- db_unset(hContact, "CList", "MyHandle");
+ db_unset(hContact, "CList", "MyHandle");
- ptrT group(getTStringA(NULL, FACEBOOK_KEY_DEF_GROUP));
- if (group)
- db_set_ts(hContact, "CList", "Group", group);
+ ptrT group(getTStringA(NULL, FACEBOOK_KEY_DEF_GROUP));
+ if (group)
+ db_set_ts(hContact, "CList", "Group", group);
- setByte(hContact, FACEBOOK_KEY_CONTACT_TYPE, type);
+ setByte(hContact, FACEBOOK_KEY_CONTACT_TYPE, type);
- if (getByte(FACEBOOK_KEY_DISABLE_STATUS_NOTIFY, 0))
- CallService(MS_IGNORE_IGNORE, hContact, (LPARAM)IGNOREEVENT_USERONLINE);
- }
+ if (getByte(FACEBOOK_KEY_DISABLE_STATUS_NOTIFY, 0))
+ CallService(MS_IGNORE_IGNORE, hContact, (LPARAM)IGNOREEVENT_USERONLINE);
if (!fbu->real_name.empty())
SaveName(hContact, fbu);
diff --git a/protocols/FacebookRM/src/process.cpp b/protocols/FacebookRM/src/process.cpp
index 8b4778f8b2..639fdee23d 100644
--- a/protocols/FacebookRM/src/process.cpp
+++ b/protocols/FacebookRM/src/process.cpp
@@ -22,24 +22,38 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "common.h"
-void FacebookProto::ProcessBuddyList(void* data)
+void FacebookProto::ProcessBuddyList(void*)
{
- if (data == NULL)
+ ScopedLock s(facy.buddies_lock_);
+
+ if (isOffline())
return;
- ScopedLock s(facy.buddies_lock_);
+ facy.handle_entry("ProcessBuddyList");
- std::string* resp = (std::string*)data;
+ // Prepare update data
+ std::string post_data = "user=" + facy.self_.user_id + "&fetch_mobile=true&phstamp=0&fb_dtsg=" + facy.dtsg_ + "&__user=" + facy.self_.user_id + "&cached_user_info_ids=";
+
+ int counter = 0;
+ for (List::Item< facebook_user >* i = facy.buddies.begin(); i != NULL; i = i->next, counter++)
+ {
+ post_data += i->data->user_id + "%2C";
+ }
- if (isOffline())
- goto exit;
+ // Get buddy list
+ http::response resp = facy.flap(REQUEST_BUDDY_LIST, &post_data);
+
+ if (resp.code != HTTP_CODE_OK) {
+ facy.handle_error("buddy_list");
+ return;
+ }
debugLogA("***** Starting processing buddy list");
CODE_BLOCK_TRY
facebook_json_parser* p = new facebook_json_parser(this);
- p->parse_buddy_list(data, &facy.buddies);
+ p->parse_buddy_list(&resp.data, &facy.buddies);
delete p;
for (List::Item< facebook_user >* i = facy.buddies.begin(); i != NULL;)
@@ -130,17 +144,24 @@ void FacebookProto::ProcessBuddyList(void* data)
debugLogA("***** Error processing buddy list: %s", e.what());
CODE_BLOCK_END
-
-exit:
- delete resp;
}
-void FacebookProto::ProcessFriendList(void* data)
+void FacebookProto::ProcessFriendList(void*)
{
- if (data == NULL)
+ ScopedLock s(facy.buddies_lock_);
+
+ if (isOffline())
return;
- std::string* resp = (std::string*)data;
+ facy.handle_entry("load_friends");
+
+ // Get buddy list
+ http::response resp = facy.flap(REQUEST_LOAD_FRIENDS);
+
+ if (resp.code != HTTP_CODE_OK) {
+ facy.handle_error("load_friends");
+ return;
+ }
debugLogA("***** Starting processing friend list");
@@ -149,7 +170,7 @@ void FacebookProto::ProcessFriendList(void* data)
std::map<std::string, facebook_user*> friends;
facebook_json_parser* p = new facebook_json_parser(this);
- p->parse_friends(data, &friends);
+ p->parse_friends(&resp.data, &friends);
delete p;
@@ -234,7 +255,7 @@ void FacebookProto::ProcessFriendList(void* data)
// Check remaining contacts in map and add them to contact list
for (std::map< std::string, facebook_user* >::iterator it = friends.begin(); it != friends.end(); ) {
if (!it->second->deleted)
- AddToContactList(it->second, CONTACT_FRIEND, true); // there could be race-condition between this thread and channel/buddy_list thread, so this contact might be created already
+ AddToContactList(it->second, CONTACT_FRIEND, true); // we know this contact doesn't exists, so we force add it
delete it->second;
it = friends.erase(it);
@@ -248,8 +269,6 @@ void FacebookProto::ProcessFriendList(void* data)
debugLogA("***** Error processing friend list: %s", e.what());
CODE_BLOCK_END
-
- delete resp;
}
void FacebookProto::ProcessUnreadMessages(void*)
@@ -269,30 +288,30 @@ void FacebookProto::ProcessUnreadMessages(void*)
http::response resp = facy.flap(REQUEST_UNREAD_THREADS, &data);
- if (resp.code == HTTP_CODE_OK) {
-
- CODE_BLOCK_TRY
+ if (resp.code != HTTP_CODE_OK) {
+ facy.handle_error("ProcessUnreadMessages");
+ return;
+ }
+
+ CODE_BLOCK_TRY
- std::vector<std::string> threads;
+ std::vector<std::string> threads;
- facebook_json_parser* p = new facebook_json_parser(this);
- p->parse_unread_threads(&resp.data, &threads, inboxOnly);
- delete p;
+ facebook_json_parser* p = new facebook_json_parser(this);
+ p->parse_unread_threads(&resp.data, &threads, inboxOnly);
+ delete p;
- ForkThread(&FacebookProto::ProcessUnreadMessage, new std::vector<std::string>(threads));
+ ForkThread(&FacebookProto::ProcessUnreadMessage, new std::vector<std::string>(threads));
- debugLogA("***** Unread threads list processed");
+ debugLogA("***** Unread threads list processed");
- CODE_BLOCK_CATCH
+ CODE_BLOCK_CATCH
- debugLogA("***** Error processing unread threads list: %s", e.what());
+ debugLogA("***** Error processing unread threads list: %s", e.what());
- CODE_BLOCK_END
+ CODE_BLOCK_END
- facy.handle_success("ProcessUnreadMessages");
- } else {
- facy.handle_error("ProcessUnreadMessages");
- }
+ facy.handle_success("ProcessUnreadMessages");
}
void FacebookProto::ProcessUnreadMessage(void *p)
@@ -717,13 +736,21 @@ void FacebookProto::ProcessFriendRequests(void*)
void FacebookProto::ProcessFeeds(void* data)
{
- if (data == NULL)
+ if (!isOnline())
return;
- std::string* resp = (std::string*)data;
+ facy.handle_entry("feeds");
- if (!isOnline())
- goto exit;
+ // Get feeds
+ http::response resp = facy.flap(REQUEST_FEEDS);
+
+ if (resp.code != HTTP_CODE_OK) {
+ facy.handle_error("feeds");
+ return;
+ }
+
+ if (resp.data.empty() || resp.data.find("\"num_stories\":0") != std::string::npos)
+ return;
CODE_BLOCK_TRY
@@ -734,16 +761,16 @@ void FacebookProto::ProcessFeeds(void* data)
std::string::size_type pos = 0;
UINT limit = 0;
- *resp = utils::text::slashu_to_utf8(*resp);
- *resp = utils::text::source_get_value(resp, 2, "\"html\":\"", ">\"");
+ resp.data = utils::text::slashu_to_utf8(resp.data);
+ resp.data = utils::text::source_get_value(&resp.data, 2, "\"html\":\"", ">\"");
- while ((pos = resp->find("<div class=\\\"mainWrapper\\\"", pos)) != std::string::npos && limit <= 25)
+ while ((pos = resp.data.find("<div class=\\\"mainWrapper\\\"", pos)) != std::string::npos && limit <= 25)
{
- std::string::size_type pos2 = resp->find("<div class=\\\"mainWrapper\\\"", pos+5);
+ std::string::size_type pos2 = resp.data.find("<div class=\\\"mainWrapper\\\"", pos+5);
if (pos2 == std::string::npos)
- pos2 = resp->length();
+ pos2 = resp.data.length();
- std::string post = resp->substr(pos, pos2 - pos);
+ std::string post = resp.data.substr(pos, pos2 - pos);
pos += 5;
std::string post_header = utils::text::source_get_value(&post, 4, "<h5 class=", "uiStreamHeadline", ">", "<\\/h5>");
@@ -817,8 +844,54 @@ void FacebookProto::ProcessFeeds(void* data)
CODE_BLOCK_END
-exit:
- delete resp;
+ facy.handle_success("feeds");
+}
+
+void FacebookProto::ProcessPages(void *data)
+{
+ if (!getByte(FACEBOOK_KEY_LOAD_PAGES, DEFAULT_LOAD_PAGES))
+ return;
+
+ facy.handle_entry("load_pages");
+
+ // Get feeds
+ http::response resp = facy.flap(REQUEST_PAGES);
+
+ if (resp.code != HTTP_CODE_OK) {
+ facy.handle_error("load_pages");
+ return;
+ }
+
+ std::string content = utils::text::source_get_value(&resp.data, 2, "id=\"bookmarksSeeAllSection\"", "</code>");
+
+ std::string::size_type start, end;
+ start = content.find("<li", 0);
+ while (start != std::string::npos) {
+ end = content.find("<li", start + 1);
+ if (end == std::string::npos)
+ end = content.length();
+
+ std::string item = content.substr(start, end - start);
+ //item = utils::text::source_get_value(&item, 2, "data-gt=", ">");
+
+ start = content.find("<li", start + 1);
+
+ std::string id = utils::text::source_get_value(&item, 3, "data-gt=", "bmid&quot;:&quot;", "&quot;");
+ std::string title = utils::text::special_expressions_decode(utils::text::slashu_to_utf8(utils::text::source_get_value(&item, 3, "data-gt=", "title=\"", "\"")));
+ std::string href = utils::text::source_get_value(&item, 3, "data-gt=", "href=\"", "\"");
+
+ // Ignore pages channel
+ if (href.find("/pages/feed") != std::string::npos)
+ continue;
+
+ if (id.empty() || title.empty())
+ continue;
+
+ debugLogA(" Got page: %s (%s)", title.c_str(), id.c_str());
+ facy.pages[id] = title;
+ }
+
+ facy.handle_success("load_pages");
}
void FacebookProto::SearchAckThread(void *targ)
diff --git a/protocols/FacebookRM/src/proto.cpp b/protocols/FacebookRM/src/proto.cpp
index abd24e7d7d..b94bd1d5db 100644
--- a/protocols/FacebookRM/src/proto.cpp
+++ b/protocols/FacebookRM/src/proto.cpp
@@ -523,7 +523,7 @@ INT_PTR FacebookProto::CheckNewsfeeds(WPARAM, LPARAM)
{
if (!isOffline()) {
facy.client_notify(TranslateT("Loading newsfeeds..."));
- facy.feeds();
+ ForkThread(&FacebookProto::ProcessFeeds, NULL);
}
return 0;
}
@@ -541,7 +541,7 @@ INT_PTR FacebookProto::RefreshBuddyList(WPARAM, LPARAM)
{
if (!isOffline()) {
facy.client_notify(TranslateT("Refreshing buddy list..."));
- facy.buddy_list();
+ ForkThread(&FacebookProto::ProcessBuddyList, NULL);
}
return 0;
}
diff --git a/protocols/FacebookRM/src/proto.h b/protocols/FacebookRM/src/proto.h
index 1cc6cce4f9..716cdd3d62 100644
--- a/protocols/FacebookRM/src/proto.h
+++ b/protocols/FacebookRM/src/proto.h
@@ -168,6 +168,7 @@ public:
void __cdecl ProcessFriendRequests(void*);
void __cdecl SearchAckThread(void*);
void __cdecl SearchIdAckThread(void*);
+ void __cdecl ProcessPages(void*);
// Worker threads
void __cdecl SignOn(void*);
@@ -192,7 +193,7 @@ public:
MCONTACT ChatIDToHContact(std::tstring);
std::string ThreadIDToContactID(std::string thread_id);
void FacebookProto::LoadContactInfo(facebook_user* fbu);
- MCONTACT AddToContactList(facebook_user*, ContactType type, bool force_save = false);
+ MCONTACT AddToContactList(facebook_user*, ContactType type, bool force_add = false);
void SetAllContactStatuses(int status);
MCONTACT HContactFromAuthEvent(HANDLE hEvent);
diff --git a/protocols/FacebookRM/src/version.h b/protocols/FacebookRM/src/version.h
index 12c19d23ed..33e28ff56f 100644
--- a/protocols/FacebookRM/src/version.h
+++ b/protocols/FacebookRM/src/version.h
@@ -1,7 +1,7 @@
#define __MAJOR_VERSION 0
#define __MINOR_VERSION 2
-#define __RELEASE_NUM 4
-#define __BUILD_NUM 1
+#define __RELEASE_NUM 5
+#define __BUILD_NUM 0
#include <stdver.h>