diff options
Diffstat (limited to 'protocols/FacebookRM/src')
40 files changed, 9474 insertions, 0 deletions
diff --git a/protocols/FacebookRM/src/JSON_CAJUN/Readme.txt b/protocols/FacebookRM/src/JSON_CAJUN/Readme.txt new file mode 100644 index 0000000000..8b9505cebf --- /dev/null +++ b/protocols/FacebookRM/src/JSON_CAJUN/Readme.txt @@ -0,0 +1,14 @@ +CAJUN* is a C++ API for the JSON object interchange format. JSON is like XML, except it doesn't suck**. It is specifically designed for representing (in plain text format) structures familiar to software engineers: booleans, numerics, strings, arrays, and objects (i.e. name/value pairs, associative array, etc.); it humbly leaves text markup to XML. It is ideal for storing persistent application data, such as configuration or user data files.
+
+Too many JSON parsers I've seen suffer from overly complex designs and confusing interfaces, so in true software engineer form, I thought I could do better. The goal of JSON was to create an simple, "minimalist" interface while sacrificing absolutely no power or flexibility. The STL containers, while not without their violations of that spirit, served as an inspiration. The end result is (IMHO) an interface that should be immediately intuitive to anyone familiar with C++ and the Standard Library containers. It can best be described as working with an "element", where an element may consist of:
+* String (mimics std::string)
+* Numeric (double)
+* Boolean (bool)
+* Array (std::vector<UnknownElement>)
+* Object (unsorted std::map<std::string, UnknownElement>)
+* UnknownElement - like boost::any, but restricted to types below. Used to aggregate elements within Objects & Arrays, and for reading documents of unknown structure
+
+As with any design, sacrifices were made with CAJUN. Most situations I've encountered where JSON is well-suited (reading & writing application configuration and data files) are not typically performance bottlenecks, so simplicity, safety & flexibility were favored over raw speed. The end result is a library with simple, typesafe classes, no memory-management burden on the user, and exception-based error reporting.
+
+* C++ API for JSON. A pint on me for who ever comes up with a good meaning for "UN".
+** To be fair, XML doesn't suck intentionally, it is just often used inappropriately.
\ No newline at end of file diff --git a/protocols/FacebookRM/src/JSON_CAJUN/ReleaseNotes.txt b/protocols/FacebookRM/src/JSON_CAJUN/ReleaseNotes.txt new file mode 100644 index 0000000000..8e43b6f928 --- /dev/null +++ b/protocols/FacebookRM/src/JSON_CAJUN/ReleaseNotes.txt @@ -0,0 +1,35 @@ +2.0.1 (11/17/2009)
+* A couple of Reader functions not inlined, sometimes resulting in linker duplicate symbols. Oops.
+
+
+2.0.0 (11/14/2009)
+* Redesign/simplicification of the element class relationships:
+ * Element/Element_T base class or *Imp classes eliminated. Originally necessary for aggregation by Object/Array, but now unnecessary with UnknownElement type
+ * json_cast<> functions eliminated. Static type safety relaxed, allowing more concise document data extraction code (dynamic type safety still completely maintained).
+ * Quick-Interpreter/-Builder classes eliminated. Equivalent functionality now in "UnknownElement", but now more accessible
+ In summary, simpler design, less code in library, less code necessary to utilize library. See test app for many more new examples.
+* Entire library is now inlined. Bound to be controversial, but...
+ * Modern compilers should eliminate redundant object code
+ * Fixes problems associated with different runtime libraries, library debug information, security & debug iterator compile-time options under MSVC++, among other things.
+ * Simply include the appropriate file & go - no linker settings to mess with.
+* Added 64-bit build targets for MSVC 8/9 test app, just because.
+* Scan/Parse exceptions moved into Reader class scope, and Parse exceptions fixed to always include bad token string
+* A few more random bug fixes
+* Tested under:
+ * MSVC++ 2005
+ * MSVC++ 2008
+ * GCC 4.4.0
+
+1.1.0 (08/30/2009)
+* Implemented operator == for all element types
+* Added makefile for building with g++ (thanks George Morgan).
+* Fixed a few compiler errors on non-Visual Studio compilers (my C++ wasn't as ANSI as I thought...)
+* Support for (non-standard) comments REMOVED
+* Support for Visual Studio 7.1 (2003) REMOVED
+* Fixed the "Unexpected token..." exception string (was gibberish)
+* Improvements to the QuickInterpreter & QuickBuilder interfaces
+* Elements now sanity-check themselves during operations and throw an exception accordingly, for example if an Object gets tricked into thinking it's an Array (reinterpret_cast, reading a document with an Array root element into an Object, etc)
+* Other random minor bug fixes & general cleanup
+
+1.0.0 (01/31/2009)
+* Initial release! Remaining work: better documentation, better test/sample app, yada yada
diff --git a/protocols/FacebookRM/src/JSON_CAJUN/elements.h b/protocols/FacebookRM/src/JSON_CAJUN/elements.h new file mode 100644 index 0000000000..f255dcd9a5 --- /dev/null +++ b/protocols/FacebookRM/src/JSON_CAJUN/elements.h @@ -0,0 +1,272 @@ +/**********************************************
+
+License: BSD
+Project Webpage: http://cajun-jsonapi.sourceforge.net/
+Author: Terry Caton
+
+***********************************************/
+
+#pragma once
+
+#include <deque>
+#include <list>
+#include <string>
+#include <stdexcept>
+
+/*
+
+TODO:
+* better documentation (doxygen?)
+* Unicode support
+* parent element accessors
+
+*/
+
+namespace json
+{
+
+
+/////////////////////////////////////////////////
+// forward declarations (more info further below)
+
+
+class Visitor;
+class ConstVisitor;
+
+template <typename ValueTypeT>
+class TrivialType_T;
+
+typedef TrivialType_T<double> Number;
+typedef TrivialType_T<bool> Boolean;
+typedef TrivialType_T<std::string> String;
+
+class Object;
+class Array;
+class Null;
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// Exception - base class for all JSON-related runtime errors
+
+class Exception : public std::runtime_error
+{
+public:
+ Exception(const std::string& sMessage);
+};
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// UnknownElement - provides a typesafe surrogate for any of the JSON-
+// sanctioned element types. This class allows the Array and Object
+// class to effectively contain a heterogeneous set of child elements.
+// The cast operators provide convenient implicit downcasting, while
+// preserving dynamic type safety by throwing an exception during a
+// a bad cast.
+// The object & array element index operators (operators [std::string]
+// and [size_t]) provide convenient, quick access to child elements.
+// They are a logical extension of the cast operators. These child
+// element accesses can be chained together, allowing the following
+// (when document structure is well-known):
+// String str = objInvoices[1]["Customer"]["Company"];
+
+
+class UnknownElement
+{
+public:
+ UnknownElement();
+ UnknownElement(const UnknownElement& unknown);
+ UnknownElement(const Object& object);
+ UnknownElement(const Array& array);
+ UnknownElement(const Number& number);
+ UnknownElement(const Boolean& boolean);
+ UnknownElement(const String& string);
+ UnknownElement(const Null& null);
+
+ ~UnknownElement();
+
+ UnknownElement& operator = (const UnknownElement& unknown);
+
+ // implicit cast to actual element type. throws on failure
+ operator const Object& () const;
+ operator const Array& () const;
+ operator const Number& () const;
+ operator const Boolean& () const;
+ operator const String& () const;
+ operator const Null& () const;
+
+ // implicit cast to actual element type. *converts* on failure, and always returns success
+ operator Object& ();
+ operator Array& ();
+ operator Number& ();
+ operator Boolean& ();
+ operator String& ();
+ operator Null& ();
+
+ // provides quick access to children when real element type is object
+ UnknownElement& operator[] (const std::string& key);
+ const UnknownElement& operator[] (const std::string& key) const;
+
+ // provides quick access to children when real element type is array
+ UnknownElement& operator[] (size_t index);
+ const UnknownElement& operator[] (size_t index) const;
+
+ // implements visitor pattern
+ void Accept(ConstVisitor& visitor) const;
+ void Accept(Visitor& visitor);
+
+ // tests equality. first checks type, then value if possible
+ bool operator == (const UnknownElement& element) const;
+
+private:
+ class Imp;
+
+ template <typename ElementTypeT>
+ class Imp_T;
+
+ class CastVisitor;
+ class ConstCastVisitor;
+
+ template <typename ElementTypeT>
+ class CastVisitor_T;
+
+ template <typename ElementTypeT>
+ class ConstCastVisitor_T;
+
+ template <typename ElementTypeT>
+ const ElementTypeT& CastTo() const;
+
+ template <typename ElementTypeT>
+ ElementTypeT& ConvertTo();
+
+ Imp* m_pImp;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////////
+// Array - mimics std::deque<UnknownElement>. The array contents are effectively
+// heterogeneous thanks to the ElementUnknown class. push_back has been replaced
+// by more generic insert functions.
+
+class Array
+{
+public:
+ typedef std::deque<UnknownElement> Elements;
+ typedef Elements::iterator iterator;
+ typedef Elements::const_iterator const_iterator;
+
+ iterator Begin();
+ iterator End();
+ const_iterator Begin() const;
+ const_iterator End() const;
+
+ iterator Insert(const UnknownElement& element, iterator itWhere);
+ iterator Insert(const UnknownElement& element);
+ iterator Erase(iterator itWhere);
+ void Resize(size_t newSize);
+ void Clear();
+
+ size_t Size() const;
+ bool Empty() const;
+
+ UnknownElement& operator[] (size_t index);
+ const UnknownElement& operator[] (size_t index) const;
+
+ bool operator == (const Array& array) const;
+
+private:
+ Elements m_Elements;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////////
+// Object - mimics std::map<std::string, UnknownElement>. The member value
+// contents are effectively heterogeneous thanks to the UnknownElement class
+
+class Object
+{
+public:
+ struct Member {
+ Member(const std::string& nameIn = std::string(), const UnknownElement& elementIn = UnknownElement());
+
+ bool operator == (const Member& member) const;
+
+ std::string name;
+ UnknownElement element;
+ };
+
+ typedef std::list<Member> Members; // map faster, but does not preserve order
+ typedef Members::iterator iterator;
+ typedef Members::const_iterator const_iterator;
+
+ bool operator == (const Object& object) const;
+
+ iterator Begin();
+ iterator End();
+ const_iterator Begin() const;
+ const_iterator End() const;
+
+ size_t Size() const;
+ bool Empty() const;
+
+ iterator Find(const std::string& name);
+ const_iterator Find(const std::string& name) const;
+
+ iterator Insert(const Member& member);
+ iterator Insert(const Member& member, iterator itWhere);
+ iterator Erase(iterator itWhere);
+ void Clear();
+
+ UnknownElement& operator [](const std::string& name);
+ const UnknownElement& operator [](const std::string& name) const;
+
+private:
+ class Finder;
+
+ Members m_Members;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////////
+// TrivialType_T - class template for encapsulates a simple data type, such as
+// a string, number, or boolean. Provides implicit const & noncost cast operators
+// for that type, allowing "DataTypeT type = trivialType;"
+
+
+template <typename DataTypeT>
+class TrivialType_T
+{
+public:
+ TrivialType_T(const DataTypeT& t = DataTypeT());
+
+ operator DataTypeT&();
+ operator const DataTypeT&() const;
+
+ DataTypeT& Value();
+ const DataTypeT& Value() const;
+
+ bool operator == (const TrivialType_T<DataTypeT>& trivial) const;
+
+private:
+ DataTypeT m_tValue;
+};
+
+
+
+/////////////////////////////////////////////////////////////////////////////////
+// Null - doesn't do much of anything but satisfy the JSON spec. It is the default
+// element type of UnknownElement
+
+class Null
+{
+public:
+ bool operator == (const Null& trivial) const;
+};
+
+
+} // End namespace
+
+
+#include "elements.inl"
diff --git a/protocols/FacebookRM/src/JSON_CAJUN/elements.inl b/protocols/FacebookRM/src/JSON_CAJUN/elements.inl new file mode 100644 index 0000000000..7febabc3a2 --- /dev/null +++ b/protocols/FacebookRM/src/JSON_CAJUN/elements.inl @@ -0,0 +1,412 @@ +/**********************************************
+
+License: BSD
+Project Webpage: http://cajun-jsonapi.sourceforge.net/
+Author: Terry Caton
+
+***********************************************/
+
+#include "visitor.h"
+#include "reader.h"
+#include <cassert>
+#include <algorithm>
+#include <map>
+
+/*
+
+TODO:
+* better documentation
+
+*/
+
+namespace json
+{
+
+
+inline Exception::Exception(const std::string& sMessage) :
+ std::runtime_error(sMessage) {}
+
+
+/////////////////////////
+// UnknownElement members
+
+class UnknownElement::Imp
+{
+public:
+ virtual ~Imp() {}
+ virtual Imp* Clone() const = 0;
+
+ virtual bool Compare(const Imp& imp) const = 0;
+
+ virtual void Accept(ConstVisitor& visitor) const = 0;
+ virtual void Accept(Visitor& visitor) = 0;
+};
+
+
+template <typename ElementTypeT>
+class UnknownElement::Imp_T : public UnknownElement::Imp
+{
+public:
+ Imp_T(const ElementTypeT& element) : m_Element(element) {}
+ virtual Imp* Clone() const { return new Imp_T<ElementTypeT>(*this); }
+
+ virtual void Accept(ConstVisitor& visitor) const { visitor.Visit(m_Element); }
+ virtual void Accept(Visitor& visitor) { visitor.Visit(m_Element); }
+
+ virtual bool Compare(const Imp& imp) const
+ {
+ ConstCastVisitor_T<ElementTypeT> castVisitor;
+ imp.Accept(castVisitor);
+ return castVisitor.m_pElement &&
+ m_Element == *castVisitor.m_pElement;
+ }
+
+private:
+ ElementTypeT m_Element;
+};
+
+
+class UnknownElement::ConstCastVisitor : public ConstVisitor
+{
+ virtual void Visit(const Array& array) {}
+ virtual void Visit(const Object& object) {}
+ virtual void Visit(const Number& number) {}
+ virtual void Visit(const String& string) {}
+ virtual void Visit(const Boolean& boolean) {}
+ virtual void Visit(const Null& null) {}
+};
+
+template <typename ElementTypeT>
+class UnknownElement::ConstCastVisitor_T : public ConstCastVisitor
+{
+public:
+ ConstCastVisitor_T() : m_pElement(0) {}
+ virtual void Visit(const ElementTypeT& element) { m_pElement = &element; } // we don't know what this is, but it overrides one of the base's no-op functions
+ const ElementTypeT* m_pElement;
+};
+
+
+class UnknownElement::CastVisitor : public Visitor
+{
+ virtual void Visit(Array& array) {}
+ virtual void Visit(Object& object) {}
+ virtual void Visit(Number& number) {}
+ virtual void Visit(String& string) {}
+ virtual void Visit(Boolean& boolean) {}
+ virtual void Visit(Null& null) {}
+};
+
+template <typename ElementTypeT>
+class UnknownElement::CastVisitor_T : public CastVisitor
+{
+public:
+ CastVisitor_T() : m_pElement(0) {}
+ virtual void Visit(ElementTypeT& element) { m_pElement = &element; } // we don't know what this is, but it overrides one of the base's no-op functions
+ ElementTypeT* m_pElement;
+};
+
+
+
+
+inline UnknownElement::UnknownElement() : m_pImp( new Imp_T<Null>( Null()) ) {}
+inline UnknownElement::UnknownElement(const UnknownElement& unknown) : m_pImp( unknown.m_pImp->Clone()) {}
+inline UnknownElement::UnknownElement(const Object& object) : m_pImp( new Imp_T<Object>(object)) {}
+inline UnknownElement::UnknownElement(const Array& array) : m_pImp( new Imp_T<Array>(array)) {}
+inline UnknownElement::UnknownElement(const Number& number) : m_pImp( new Imp_T<Number>(number)) {}
+inline UnknownElement::UnknownElement(const Boolean& boolean) : m_pImp( new Imp_T<Boolean>(boolean)) {}
+inline UnknownElement::UnknownElement(const String& string) : m_pImp( new Imp_T<String>(string)) {}
+inline UnknownElement::UnknownElement(const Null& null) : m_pImp( new Imp_T<Null>(null)) {}
+
+inline UnknownElement::~UnknownElement() { delete m_pImp; }
+
+inline UnknownElement::operator const Object& () const { return CastTo<Object>(); }
+inline UnknownElement::operator const Array& () const { return CastTo<Array>(); }
+inline UnknownElement::operator const Number& () const { return CastTo<Number>(); }
+inline UnknownElement::operator const Boolean& () const { return CastTo<Boolean>(); }
+inline UnknownElement::operator const String& () const { return CastTo<String>(); }
+inline UnknownElement::operator const Null& () const { return CastTo<Null>(); }
+
+inline UnknownElement::operator Object& () { return ConvertTo<Object>(); }
+inline UnknownElement::operator Array& () { return ConvertTo<Array>(); }
+inline UnknownElement::operator Number& () { return ConvertTo<Number>(); }
+inline UnknownElement::operator Boolean& () { return ConvertTo<Boolean>(); }
+inline UnknownElement::operator String& () { return ConvertTo<String>(); }
+inline UnknownElement::operator Null& () { return ConvertTo<Null>(); }
+
+inline UnknownElement& UnknownElement::operator = (const UnknownElement& unknown)
+{
+ delete m_pImp;
+ m_pImp = unknown.m_pImp->Clone();
+ return *this;
+}
+
+inline UnknownElement& UnknownElement::operator[] (const std::string& key)
+{
+ // the people want an object. make us one if we aren't already
+ Object& object = ConvertTo<Object>();
+ return object[key];
+}
+
+inline const UnknownElement& UnknownElement::operator[] (const std::string& key) const
+{
+ // throws if we aren't an object
+ const Object& object = CastTo<Object>();
+ return object[key];
+}
+
+inline UnknownElement& UnknownElement::operator[] (size_t index)
+{
+ // the people want an array. make us one if we aren't already
+ Array& array = ConvertTo<Array>();
+ return array[index];
+}
+
+inline const UnknownElement& UnknownElement::operator[] (size_t index) const
+{
+ // throws if we aren't an array
+ const Array& array = CastTo<Array>();
+ return array[index];
+}
+
+
+template <typename ElementTypeT>
+const ElementTypeT& UnknownElement::CastTo() const
+{
+ ConstCastVisitor_T<ElementTypeT> castVisitor;
+ m_pImp->Accept(castVisitor);
+ if (castVisitor.m_pElement == 0)
+ throw Exception("Bad cast");
+ return *castVisitor.m_pElement;
+}
+
+
+
+template <typename ElementTypeT>
+ElementTypeT& UnknownElement::ConvertTo()
+{
+ CastVisitor_T<ElementTypeT> castVisitor;
+ m_pImp->Accept(castVisitor);
+ if (castVisitor.m_pElement == 0)
+ {
+ // we're not the right type. fix it & try again
+ *this = ElementTypeT();
+ m_pImp->Accept(castVisitor);
+ }
+
+ return *castVisitor.m_pElement;
+}
+
+
+inline void UnknownElement::Accept(ConstVisitor& visitor) const { m_pImp->Accept(visitor); }
+inline void UnknownElement::Accept(Visitor& visitor) { m_pImp->Accept(visitor); }
+
+
+inline bool UnknownElement::operator == (const UnknownElement& element) const
+{
+ return m_pImp->Compare(*element.m_pImp);
+}
+
+
+
+//////////////////
+// Object members
+
+
+inline Object::Member::Member(const std::string& nameIn, const UnknownElement& elementIn) :
+ name(nameIn), element(elementIn) {}
+
+inline bool Object::Member::operator == (const Member& member) const
+{
+ return name == member.name &&
+ element == member.element;
+}
+
+class Object::Finder : public std::unary_function<Object::Member, bool>
+{
+public:
+ Finder(const std::string& name) : m_name(name) {}
+ bool operator () (const Object::Member& member) {
+ return member.name == m_name;
+ }
+
+private:
+ std::string m_name;
+};
+
+
+
+inline Object::iterator Object::Begin() { return m_Members.begin(); }
+inline Object::iterator Object::End() { return m_Members.end(); }
+inline Object::const_iterator Object::Begin() const { return m_Members.begin(); }
+inline Object::const_iterator Object::End() const { return m_Members.end(); }
+
+inline size_t Object::Size() const { return m_Members.size(); }
+inline bool Object::Empty() const { return m_Members.empty(); }
+
+inline Object::iterator Object::Find(const std::string& name)
+{
+ return std::find_if(m_Members.begin(), m_Members.end(), Finder(name));
+}
+
+inline Object::const_iterator Object::Find(const std::string& name) const
+{
+ return std::find_if(m_Members.begin(), m_Members.end(), Finder(name));
+}
+
+inline Object::iterator Object::Insert(const Member& member)
+{
+ return Insert(member, End());
+}
+
+inline Object::iterator Object::Insert(const Member& member, iterator itWhere)
+{
+ iterator it = Find(member.name);
+ if (it != m_Members.end())
+ throw Exception("Object member already exists: " + member.name);
+
+ it = m_Members.insert(itWhere, member);
+ return it;
+}
+
+inline Object::iterator Object::Erase(iterator itWhere)
+{
+ return m_Members.erase(itWhere);
+}
+
+inline UnknownElement& Object::operator [](const std::string& name)
+{
+
+ iterator it = Find(name);
+ if (it == m_Members.end())
+ {
+ Member member(name);
+ it = Insert(member, End());
+ }
+ return it->element;
+}
+
+inline const UnknownElement& Object::operator [](const std::string& name) const
+{
+ const_iterator it = Find(name);
+ if (it == End())
+ throw Exception("Object member not found: " + name);
+ return it->element;
+}
+
+inline void Object::Clear()
+{
+ m_Members.clear();
+}
+
+inline bool Object::operator == (const Object& object) const
+{
+ return m_Members == object.m_Members;
+}
+
+
+/////////////////
+// Array members
+
+inline Array::iterator Array::Begin() { return m_Elements.begin(); }
+inline Array::iterator Array::End() { return m_Elements.end(); }
+inline Array::const_iterator Array::Begin() const { return m_Elements.begin(); }
+inline Array::const_iterator Array::End() const { return m_Elements.end(); }
+
+inline Array::iterator Array::Insert(const UnknownElement& element, iterator itWhere)
+{
+ return m_Elements.insert(itWhere, element);
+}
+
+inline Array::iterator Array::Insert(const UnknownElement& element)
+{
+ return Insert(element, End());
+}
+
+inline Array::iterator Array::Erase(iterator itWhere)
+{
+ return m_Elements.erase(itWhere);
+}
+
+inline void Array::Resize(size_t newSize)
+{
+ m_Elements.resize(newSize);
+}
+
+inline size_t Array::Size() const { return m_Elements.size(); }
+inline bool Array::Empty() const { return m_Elements.empty(); }
+
+inline UnknownElement& Array::operator[] (size_t index)
+{
+ size_t nMinSize = index + 1; // zero indexed
+ if (m_Elements.size() < nMinSize)
+ m_Elements.resize(nMinSize);
+ return m_Elements[index];
+}
+
+inline const UnknownElement& Array::operator[] (size_t index) const
+{
+ if (index >= m_Elements.size())
+ throw Exception("Array out of bounds");
+ return m_Elements[index];
+}
+
+inline void Array::Clear() {
+ m_Elements.clear();
+}
+
+inline bool Array::operator == (const Array& array) const
+{
+ return m_Elements == array.m_Elements;
+}
+
+
+////////////////////////
+// TrivialType_T members
+
+template <typename DataTypeT>
+TrivialType_T<DataTypeT>::TrivialType_T(const DataTypeT& t) :
+ m_tValue(t) {}
+
+template <typename DataTypeT>
+TrivialType_T<DataTypeT>::operator DataTypeT&()
+{
+ return Value();
+}
+
+template <typename DataTypeT>
+TrivialType_T<DataTypeT>::operator const DataTypeT&() const
+{
+ return Value();
+}
+
+template <typename DataTypeT>
+DataTypeT& TrivialType_T<DataTypeT>::Value()
+{
+ return m_tValue;
+}
+
+template <typename DataTypeT>
+const DataTypeT& TrivialType_T<DataTypeT>::Value() const
+{
+ return m_tValue;
+}
+
+template <typename DataTypeT>
+bool TrivialType_T<DataTypeT>::operator == (const TrivialType_T<DataTypeT>& trivial) const
+{
+ return m_tValue == trivial.m_tValue;
+}
+
+
+
+//////////////////
+// Null members
+
+inline bool Null::operator == (const Null& trivial) const
+{
+ return true;
+}
+
+
+
+} // End namespace
diff --git a/protocols/FacebookRM/src/JSON_CAJUN/reader.h b/protocols/FacebookRM/src/JSON_CAJUN/reader.h new file mode 100644 index 0000000000..2f576c752e --- /dev/null +++ b/protocols/FacebookRM/src/JSON_CAJUN/reader.h @@ -0,0 +1,125 @@ +/**********************************************
+
+License: BSD
+Project Webpage: http://cajun-jsonapi.sourceforge.net/
+Author: Terry Caton
+
+***********************************************/
+
+#pragma once
+
+#include "elements.h"
+#include <iostream>
+#include <vector>
+
+namespace json
+{
+
+class Reader
+{
+public:
+ // this structure will be reported in one of the exceptions defined below
+ struct Location
+ {
+ Location();
+
+ unsigned int m_nLine; // document line, zero-indexed
+ unsigned int m_nLineOffset; // character offset from beginning of line, zero indexed
+ unsigned int m_nDocOffset; // character offset from entire document, zero indexed
+ };
+
+ // thrown during the first phase of reading. generally catches low-level problems such
+ // as errant characters or corrupt/incomplete documents
+ class ScanException : public Exception
+ {
+ public:
+ ScanException(const std::string& sMessage, const Reader::Location& locError) :
+ Exception(sMessage),
+ m_locError(locError) {}
+
+ Reader::Location m_locError;
+ };
+
+ // thrown during the second phase of reading. generally catches higher-level problems such
+ // as missing commas or brackets
+ class ParseException : public Exception
+ {
+ public:
+ ParseException(const std::string& sMessage, const Reader::Location& locTokenBegin, const Reader::Location& locTokenEnd) :
+ Exception(sMessage),
+ m_locTokenBegin(locTokenBegin),
+ m_locTokenEnd(locTokenEnd) {}
+
+ Reader::Location m_locTokenBegin;
+ Reader::Location m_locTokenEnd;
+ };
+
+
+ // if you know what the document looks like, call one of these...
+ static void Read(Object& object, std::istream& istr);
+ static void Read(Array& array, std::istream& istr);
+ static void Read(String& string, std::istream& istr);
+ static void Read(Number& number, std::istream& istr);
+ static void Read(Boolean& boolean, std::istream& istr);
+ static void Read(Null& null, std::istream& istr);
+
+ // ...otherwise, if you don't know, call this & visit it
+ static void Read(UnknownElement& elementRoot, std::istream& istr);
+
+private:
+ struct Token
+ {
+ enum Type
+ {
+ TOKEN_OBJECT_BEGIN, // {
+ TOKEN_OBJECT_END, // }
+ TOKEN_ARRAY_BEGIN, // [
+ TOKEN_ARRAY_END, // ]
+ TOKEN_NEXT_ELEMENT, // ,
+ TOKEN_MEMBER_ASSIGN, // :
+ TOKEN_STRING, // "xxx"
+ TOKEN_NUMBER, // [+/-]000.000[e[+/-]000]
+ TOKEN_BOOLEAN, // true -or- false
+ TOKEN_NULL // null
+ };
+
+ Type nType;
+ std::string sValue;
+
+ // for malformed file debugging
+ Reader::Location locBegin;
+ Reader::Location locEnd;
+ };
+
+ class InputStream;
+ class TokenStream;
+ typedef std::vector<Token> Tokens;
+
+ template <typename ElementTypeT>
+ static void Read_i(ElementTypeT& element, std::istream& istr);
+
+ // scanning istream into token sequence
+ void Scan(Tokens& tokens, InputStream& inputStream);
+
+ void EatWhiteSpace(InputStream& inputStream);
+ void MatchString(std::string& sValue, InputStream& inputStream);
+ void MatchNumber(std::string& sNumber, InputStream& inputStream);
+ void MatchExpectedString(const std::string& sExpected, InputStream& inputStream);
+
+ // parsing token sequence into element structure
+ void Parse(UnknownElement& element, TokenStream& tokenStream);
+ void Parse(Object& object, TokenStream& tokenStream);
+ void Parse(Array& array, TokenStream& tokenStream);
+ void Parse(String& string, TokenStream& tokenStream);
+ void Parse(Number& number, TokenStream& tokenStream);
+ void Parse(Boolean& boolean, TokenStream& tokenStream);
+ void Parse(Null& null, TokenStream& tokenStream);
+
+ const std::string& MatchExpectedToken(Token::Type nExpected, TokenStream& tokenStream);
+};
+
+
+} // End namespace
+
+
+#include "reader.inl"
\ No newline at end of file diff --git a/protocols/FacebookRM/src/JSON_CAJUN/reader.inl b/protocols/FacebookRM/src/JSON_CAJUN/reader.inl new file mode 100644 index 0000000000..26339497a0 --- /dev/null +++ b/protocols/FacebookRM/src/JSON_CAJUN/reader.inl @@ -0,0 +1,524 @@ +/**********************************************
+
+License: BSD
+Project Webpage: http://cajun-jsonapi.sourceforge.net/
+Author: Terry Caton
+
+***********************************************/
+
+#include <cassert>
+#include <set>
+#include <sstream>
+
+/*
+
+TODO:
+* better documentation
+* unicode character decoding
+
+*/
+
+namespace json
+{
+
+
+ inline std::istream& operator >> (std::istream& istr, UnknownElement& elementRoot) {
+ Reader::Read(elementRoot, istr);
+ return istr;
+}
+
+inline Reader::Location::Location() :
+ m_nLine(0),
+ m_nLineOffset(0),
+ m_nDocOffset(0)
+{}
+
+
+//////////////////////
+// Reader::InputStream
+
+class Reader::InputStream // would be cool if we could inherit from std::istream & override "get"
+{
+public:
+ InputStream(std::istream& iStr) :
+ m_iStr(iStr) {}
+
+ // protect access to the input stream, so we can keeep track of document/line offsets
+ char Get(); // big, define outside
+ char Peek() {
+ assert(m_iStr.eof() == false); // enforce reading of only valid stream data
+ return m_iStr.peek();
+ }
+
+ bool EOS() {
+ m_iStr.peek(); // apparently eof flag isn't set until a character read is attempted. whatever.
+ return m_iStr.eof();
+ }
+
+ const Location& GetLocation() const { return m_Location; }
+
+private:
+ std::istream& m_iStr;
+ Location m_Location;
+};
+
+
+inline char Reader::InputStream::Get()
+{
+ assert(m_iStr.eof() == false); // enforce reading of only valid stream data
+ char c = m_iStr.get();
+
+ ++m_Location.m_nDocOffset;
+ if (c == '\n') {
+ ++m_Location.m_nLine;
+ m_Location.m_nLineOffset = 0;
+ }
+ else {
+ ++m_Location.m_nLineOffset;
+ }
+
+ return c;
+}
+
+
+
+//////////////////////
+// Reader::TokenStream
+
+class Reader::TokenStream
+{
+public:
+ TokenStream(const Tokens& tokens);
+
+ const Token& Peek();
+ const Token& Get();
+
+ bool EOS() const;
+
+private:
+ const Tokens& m_Tokens;
+ Tokens::const_iterator m_itCurrent;
+};
+
+
+inline Reader::TokenStream::TokenStream(const Tokens& tokens) :
+ m_Tokens(tokens),
+ m_itCurrent(tokens.begin())
+{}
+
+inline const Reader::Token& Reader::TokenStream::Peek() {
+ assert(m_itCurrent != m_Tokens.end());
+ return *(m_itCurrent);
+}
+
+inline const Reader::Token& Reader::TokenStream::Get() {
+ assert(m_itCurrent != m_Tokens.end());
+ return *(m_itCurrent++);
+}
+
+inline bool Reader::TokenStream::EOS() const {
+ return m_itCurrent == m_Tokens.end();
+}
+
+///////////////////
+// Reader (finally)
+
+
+inline void Reader::Read(Object& object, std::istream& istr) { Read_i(object, istr); }
+inline void Reader::Read(Array& array, std::istream& istr) { Read_i(array, istr); }
+inline void Reader::Read(String& string, std::istream& istr) { Read_i(string, istr); }
+inline void Reader::Read(Number& number, std::istream& istr) { Read_i(number, istr); }
+inline void Reader::Read(Boolean& boolean, std::istream& istr) { Read_i(boolean, istr); }
+inline void Reader::Read(Null& null, std::istream& istr) { Read_i(null, istr); }
+inline void Reader::Read(UnknownElement& unknown, std::istream& istr) { Read_i(unknown, istr); }
+
+
+template <typename ElementTypeT>
+void Reader::Read_i(ElementTypeT& element, std::istream& istr)
+{
+ Reader reader;
+
+ Tokens tokens;
+ InputStream inputStream(istr);
+ reader.Scan(tokens, inputStream);
+
+ TokenStream tokenStream(tokens);
+ reader.Parse(element, tokenStream);
+
+ if (tokenStream.EOS() == false)
+ {
+ const Token& token = tokenStream.Peek();
+ std::string sMessage = "Expected End of token stream; found " + token.sValue;
+ throw ParseException(sMessage, token.locBegin, token.locEnd);
+ }
+}
+
+
+inline void Reader::Scan(Tokens& tokens, InputStream& inputStream)
+{
+ while (EatWhiteSpace(inputStream), // ignore any leading white space...
+ inputStream.EOS() == false) // ...before checking for EOS
+ {
+ // if all goes well, we'll create a token each pass
+ Token token;
+ token.locBegin = inputStream.GetLocation();
+
+ // gives us null-terminated string
+ std::string sChar;
+ sChar.push_back(inputStream.Peek());
+
+ switch (sChar[0])
+ {
+ case '{':
+ token.sValue = sChar[0];
+ MatchExpectedString(sChar, inputStream);
+ token.nType = Token::TOKEN_OBJECT_BEGIN;
+ break;
+
+ case '}':
+ token.sValue = sChar[0];
+ MatchExpectedString(sChar, inputStream);
+ token.nType = Token::TOKEN_OBJECT_END;
+ break;
+
+ case '[':
+ token.sValue = sChar[0];
+ MatchExpectedString(sChar, inputStream);
+ token.nType = Token::TOKEN_ARRAY_BEGIN;
+ break;
+
+ case ']':
+ token.sValue = sChar[0];
+ MatchExpectedString(sChar, inputStream);
+ token.nType = Token::TOKEN_ARRAY_END;
+ break;
+
+ case ',':
+ token.sValue = sChar[0];
+ MatchExpectedString(sChar, inputStream);
+ token.nType = Token::TOKEN_NEXT_ELEMENT;
+ break;
+
+ case ':':
+ token.sValue = sChar[0];
+ MatchExpectedString(sChar, inputStream);
+ token.nType = Token::TOKEN_MEMBER_ASSIGN;
+ break;
+
+ case '"':
+ MatchString(token.sValue, inputStream);
+ token.nType = Token::TOKEN_STRING;
+ break;
+
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ MatchNumber(token.sValue, inputStream);
+ token.nType = Token::TOKEN_NUMBER;
+ break;
+
+ case 't':
+ token.sValue = "true";
+ MatchExpectedString(token.sValue, inputStream);
+ token.nType = Token::TOKEN_BOOLEAN;
+ break;
+
+ case 'f':
+ token.sValue = "false";
+ MatchExpectedString(token.sValue, inputStream);
+ token.nType = Token::TOKEN_BOOLEAN;
+ break;
+
+ case 'n':
+ token.sValue = "null";
+ MatchExpectedString(token.sValue, inputStream);
+ token.nType = Token::TOKEN_NULL;
+ break;
+
+ default: {
+ std::string sErrorMessage = "Unexpected character in stream: " + sChar;
+ throw ScanException(sErrorMessage, inputStream.GetLocation());
+ }
+ }
+
+ token.locEnd = inputStream.GetLocation();
+ tokens.push_back(token);
+ }
+}
+
+
+inline void Reader::EatWhiteSpace(InputStream& inputStream)
+{
+ while (inputStream.EOS() == false &&
+ ::isspace(inputStream.Peek()))
+ inputStream.Get();
+}
+
+inline void Reader::MatchExpectedString(const std::string& sExpected, InputStream& inputStream)
+{
+ std::string::const_iterator it(sExpected.begin()),
+ itEnd(sExpected.end());
+ for ( ; it != itEnd; ++it) {
+ if (inputStream.EOS() || // did we reach the end before finding what we're looking for...
+ inputStream.Get() != *it) // ...or did we find something different?
+ {
+ std::string sMessage = "Expected string: " + sExpected;
+ throw ScanException(sMessage, inputStream.GetLocation());
+ }
+ }
+
+ // all's well if we made it here, return quietly
+}
+
+
+inline void Reader::MatchString(std::string& string, InputStream& inputStream)
+{
+ MatchExpectedString("\"", inputStream);
+
+ while (inputStream.EOS() == false &&
+ inputStream.Peek() != '"')
+ {
+ char c = inputStream.Get();
+
+ // escape?
+ if (c == '\\' &&
+ inputStream.EOS() == false) // shouldn't have reached the end yet
+ {
+ c = inputStream.Get();
+ switch (c) {
+ case '/': string.push_back('/'); break;
+ case '"': string.push_back('"'); break;
+ case '\\': string.push_back('\\'); break;
+ case 'b': string.push_back('\b'); break;
+ case 'f': string.push_back('\f'); break;
+ case 'n': string.push_back('\n'); break;
+ case 'r': string.push_back('\r'); break;
+ case 't': string.push_back('\t'); break;
+//
+// misacek: Modification due to need
+//
+// case 'u': // TODO: what do we do with this?
+ case 'u': string.append("\\u"); break;
+//
+ default: {
+ std::string sMessage = "Unrecognized escape sequence found in string: \\" + c;
+ throw ScanException(sMessage, inputStream.GetLocation());
+ }
+ }
+ }
+ else {
+ string.push_back(c);
+ }
+ }
+
+ // eat the last '"' that we just peeked
+ MatchExpectedString("\"", inputStream);
+}
+
+
+inline void Reader::MatchNumber(std::string& sNumber, InputStream& inputStream)
+{
+ const char sNumericChars[] = "0123456789.eE-+";
+ std::set<char> numericChars;
+ numericChars.insert(sNumericChars, sNumericChars + sizeof(sNumericChars));
+
+ while (inputStream.EOS() == false &&
+ numericChars.find(inputStream.Peek()) != numericChars.end())
+ {
+ sNumber.push_back(inputStream.Get());
+ }
+}
+
+
+inline void Reader::Parse(UnknownElement& element, Reader::TokenStream& tokenStream)
+{
+ if (tokenStream.EOS()) {
+ std::string sMessage = "Unexpected end of token stream";
+ throw ParseException(sMessage, Location(), Location()); // nowhere to point to
+ }
+
+ const Token& token = tokenStream.Peek();
+ switch (token.nType) {
+ case Token::TOKEN_OBJECT_BEGIN:
+ {
+ // implicit non-const cast will perform conversion for us (if necessary)
+ Object& object = element;
+ Parse(object, tokenStream);
+ break;
+ }
+
+ case Token::TOKEN_ARRAY_BEGIN:
+ {
+ Array& array = element;
+ Parse(array, tokenStream);
+ break;
+ }
+
+ case Token::TOKEN_STRING:
+ {
+ String& string = element;
+ Parse(string, tokenStream);
+ break;
+ }
+
+ case Token::TOKEN_NUMBER:
+ {
+ Number& number = element;
+ Parse(number, tokenStream);
+ break;
+ }
+
+ case Token::TOKEN_BOOLEAN:
+ {
+ Boolean& boolean = element;
+ Parse(boolean, tokenStream);
+ break;
+ }
+
+ case Token::TOKEN_NULL:
+ {
+ Null& null = element;
+ Parse(null, tokenStream);
+ break;
+ }
+
+ default:
+ {
+ std::string sMessage = "Unexpected token: " + token.sValue;
+ throw ParseException(sMessage, token.locBegin, token.locEnd);
+ }
+ }
+}
+
+
+inline void Reader::Parse(Object& object, Reader::TokenStream& tokenStream)
+{
+ MatchExpectedToken(Token::TOKEN_OBJECT_BEGIN, tokenStream);
+
+ bool bContinue = (tokenStream.EOS() == false &&
+ tokenStream.Peek().nType != Token::TOKEN_OBJECT_END);
+ while (bContinue)
+ {
+ Object::Member member;
+
+ // first the member name. save the token in case we have to throw an exception
+ const Token& tokenName = tokenStream.Peek();
+ member.name = MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
+
+ // ...then the key/value separator...
+ MatchExpectedToken(Token::TOKEN_MEMBER_ASSIGN, tokenStream);
+
+ // ...then the value itself (can be anything).
+ Parse(member.element, tokenStream);
+
+ // try adding it to the object (this could throw)
+ try
+ {
+ object.Insert(member);
+ }
+ catch (Exception&)
+ {
+ // must be a duplicate name
+ std::string sMessage = "Duplicate object member token: " + member.name;
+ throw ParseException(sMessage, tokenName.locBegin, tokenName.locEnd);
+ }
+
+ bContinue = (tokenStream.EOS() == false &&
+ tokenStream.Peek().nType == Token::TOKEN_NEXT_ELEMENT);
+ if (bContinue)
+ MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream);
+ }
+
+ MatchExpectedToken(Token::TOKEN_OBJECT_END, tokenStream);
+}
+
+
+inline void Reader::Parse(Array& array, Reader::TokenStream& tokenStream)
+{
+ MatchExpectedToken(Token::TOKEN_ARRAY_BEGIN, tokenStream);
+
+ bool bContinue = (tokenStream.EOS() == false &&
+ tokenStream.Peek().nType != Token::TOKEN_ARRAY_END);
+ while (bContinue)
+ {
+ // ...what's next? could be anything
+ Array::iterator itElement = array.Insert(UnknownElement());
+ UnknownElement& element = *itElement;
+ Parse(element, tokenStream);
+
+ bContinue = (tokenStream.EOS() == false &&
+ tokenStream.Peek().nType == Token::TOKEN_NEXT_ELEMENT);
+ if (bContinue)
+ MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream);
+ }
+
+ MatchExpectedToken(Token::TOKEN_ARRAY_END, tokenStream);
+}
+
+
+inline void Reader::Parse(String& string, Reader::TokenStream& tokenStream)
+{
+ string = MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
+}
+
+
+inline void Reader::Parse(Number& number, Reader::TokenStream& tokenStream)
+{
+ const Token& currentToken = tokenStream.Peek(); // might need this later for throwing exception
+ const std::string& sValue = MatchExpectedToken(Token::TOKEN_NUMBER, tokenStream);
+
+ std::istringstream iStr(sValue);
+ double dValue;
+ iStr >> dValue;
+
+ // did we consume all characters in the token?
+ if (iStr.eof() == false)
+ {
+ std::string sMessage = "Unexpected character in NUMBER token: " + iStr.peek();
+ throw ParseException(sMessage, currentToken.locBegin, currentToken.locEnd);
+ }
+
+ number = dValue;
+}
+
+
+inline void Reader::Parse(Boolean& boolean, Reader::TokenStream& tokenStream)
+{
+ const std::string& sValue = MatchExpectedToken(Token::TOKEN_BOOLEAN, tokenStream);
+ boolean = (sValue == "true" ? true : false);
+}
+
+
+inline void Reader::Parse(Null&, Reader::TokenStream& tokenStream)
+{
+ MatchExpectedToken(Token::TOKEN_NULL, tokenStream);
+}
+
+
+inline const std::string& Reader::MatchExpectedToken(Token::Type nExpected, Reader::TokenStream& tokenStream)
+{
+ if (tokenStream.EOS())
+ {
+ std::string sMessage = "Unexpected End of token stream";
+ throw ParseException(sMessage, Location(), Location()); // nowhere to point to
+ }
+
+ const Token& token = tokenStream.Get();
+ if (token.nType != nExpected)
+ {
+ std::string sMessage = "Unexpected token: " + token.sValue;
+ throw ParseException(sMessage, token.locBegin, token.locEnd);
+ }
+
+ return token.sValue;
+}
+
+} // End namespace
diff --git a/protocols/FacebookRM/src/JSON_CAJUN/visitor.h b/protocols/FacebookRM/src/JSON_CAJUN/visitor.h new file mode 100644 index 0000000000..8a28d7a64a --- /dev/null +++ b/protocols/FacebookRM/src/JSON_CAJUN/visitor.h @@ -0,0 +1,44 @@ +/**********************************************
+
+License: BSD
+Project Webpage: http://cajun-jsonapi.sourceforge.net/
+Author: Terry Caton
+
+***********************************************/
+
+#pragma once
+
+#include "elements.h"
+
+namespace json
+{
+
+
+class Visitor
+{
+public:
+ virtual ~Visitor() {}
+
+ virtual void Visit(Array& array) = 0;
+ virtual void Visit(Object& object) = 0;
+ virtual void Visit(Number& number) = 0;
+ virtual void Visit(String& string) = 0;
+ virtual void Visit(Boolean& boolean) = 0;
+ virtual void Visit(Null& null) = 0;
+};
+
+class ConstVisitor
+{
+public:
+ virtual ~ConstVisitor() {}
+
+ virtual void Visit(const Array& array) = 0;
+ virtual void Visit(const Object& object) = 0;
+ virtual void Visit(const Number& number) = 0;
+ virtual void Visit(const String& string) = 0;
+ virtual void Visit(const Boolean& boolean) = 0;
+ virtual void Visit(const Null& null) = 0;
+};
+
+
+} // End namespace
diff --git a/protocols/FacebookRM/src/JSON_CAJUN/writer.h b/protocols/FacebookRM/src/JSON_CAJUN/writer.h new file mode 100644 index 0000000000..c364bcb289 --- /dev/null +++ b/protocols/FacebookRM/src/JSON_CAJUN/writer.h @@ -0,0 +1,57 @@ +/**********************************************
+
+License: BSD
+Project Webpage: http://cajun-jsonapi.sourceforge.net/
+Author: Terry Caton
+
+***********************************************/
+
+#pragma once
+
+#include "elements.h"
+#include "visitor.h"
+
+namespace json
+{
+
+class Writer : private ConstVisitor
+{
+public:
+ static void Write(const Object& object, std::ostream& ostr);
+ static void Write(const Array& array, std::ostream& ostr);
+ static void Write(const String& string, std::ostream& ostr);
+ static void Write(const Number& number, std::ostream& ostr);
+ static void Write(const Boolean& boolean, std::ostream& ostr);
+ static void Write(const Null& null, std::ostream& ostr);
+ static void Write(const UnknownElement& elementRoot, std::ostream& ostr);
+
+private:
+ Writer(std::ostream& ostr);
+
+ template <typename ElementTypeT>
+ static void Write_i(const ElementTypeT& element, std::ostream& ostr);
+
+ void Write_i(const Object& object);
+ void Write_i(const Array& array);
+ void Write_i(const String& string);
+ void Write_i(const Number& number);
+ void Write_i(const Boolean& boolean);
+ void Write_i(const Null& null);
+ void Write_i(const UnknownElement& unknown);
+
+ virtual void Visit(const Array& array);
+ virtual void Visit(const Object& object);
+ virtual void Visit(const Number& number);
+ virtual void Visit(const String& string);
+ virtual void Visit(const Boolean& boolean);
+ virtual void Visit(const Null& null);
+
+ std::ostream& m_ostr;
+ int m_nTabDepth;
+};
+
+
+} // End namespace
+
+
+#include "writer.inl"
\ No newline at end of file diff --git a/protocols/FacebookRM/src/JSON_CAJUN/writer.inl b/protocols/FacebookRM/src/JSON_CAJUN/writer.inl new file mode 100644 index 0000000000..eeb3db94b9 --- /dev/null +++ b/protocols/FacebookRM/src/JSON_CAJUN/writer.inl @@ -0,0 +1,153 @@ +/**********************************************
+
+License: BSD
+Project Webpage: http://cajun-jsonapi.sourceforge.net/
+Author: Terry Caton
+
+***********************************************/
+
+#include "writer.h"
+#include <iostream>
+#include <iomanip>
+
+/*
+
+TODO:
+* better documentation
+* unicode character encoding
+
+*/
+
+namespace json
+{
+
+
+inline void Writer::Write(const UnknownElement& elementRoot, std::ostream& ostr) { Write_i(elementRoot, ostr); }
+inline void Writer::Write(const Object& object, std::ostream& ostr) { Write_i(object, ostr); }
+inline void Writer::Write(const Array& array, std::ostream& ostr) { Write_i(array, ostr); }
+inline void Writer::Write(const Number& number, std::ostream& ostr) { Write_i(number, ostr); }
+inline void Writer::Write(const String& string, std::ostream& ostr) { Write_i(string, ostr); }
+inline void Writer::Write(const Boolean& boolean, std::ostream& ostr) { Write_i(boolean, ostr); }
+inline void Writer::Write(const Null& null, std::ostream& ostr) { Write_i(null, ostr); }
+
+
+inline Writer::Writer(std::ostream& ostr) :
+ m_ostr(ostr),
+ m_nTabDepth(0)
+{}
+
+template <typename ElementTypeT>
+void Writer::Write_i(const ElementTypeT& element, std::ostream& ostr)
+{
+ Writer writer(ostr);
+ writer.Write_i(element);
+ ostr.flush(); // all done
+}
+
+inline void Writer::Write_i(const Array& array)
+{
+ if (array.Empty())
+ m_ostr << "[]";
+ else
+ {
+ m_ostr << '[' << std::endl;
+ ++m_nTabDepth;
+
+ Array::const_iterator it(array.Begin()),
+ itEnd(array.End());
+ while (it != itEnd) {
+ m_ostr << std::string(m_nTabDepth, '\t');
+
+ Write_i(*it);
+
+ if (++it != itEnd)
+ m_ostr << ',';
+ m_ostr << std::endl;
+ }
+
+ --m_nTabDepth;
+ m_ostr << std::string(m_nTabDepth, '\t') << ']';
+ }
+}
+
+inline void Writer::Write_i(const Object& object)
+{
+ if (object.Empty())
+ m_ostr << "{}";
+ else
+ {
+ m_ostr << '{' << std::endl;
+ ++m_nTabDepth;
+
+ Object::const_iterator it(object.Begin()),
+ itEnd(object.End());
+ while (it != itEnd) {
+ m_ostr << std::string(m_nTabDepth, '\t') << '"' << it->name << "\" : ";
+ Write_i(it->element);
+
+ if (++it != itEnd)
+ m_ostr << ',';
+ m_ostr << std::endl;
+ }
+
+ --m_nTabDepth;
+ m_ostr << std::string(m_nTabDepth, '\t') << '}';
+ }
+}
+
+inline void Writer::Write_i(const Number& numberElement)
+{
+ m_ostr << std::setprecision(20) << numberElement.Value();
+}
+
+inline void Writer::Write_i(const Boolean& booleanElement)
+{
+ m_ostr << (booleanElement.Value() ? "true" : "false");
+}
+
+inline void Writer::Write_i(const String& stringElement)
+{
+ m_ostr << '"';
+
+ const std::string& s = stringElement.Value();
+ std::string::const_iterator it(s.begin()),
+ itEnd(s.end());
+ for (; it != itEnd; ++it)
+ {
+ switch (*it)
+ {
+ case '"': m_ostr << "\\\""; break;
+ case '\\': m_ostr << "\\\\"; break;
+ case '\b': m_ostr << "\\b"; break;
+ case '\f': m_ostr << "\\f"; break;
+ case '\n': m_ostr << "\\n"; break;
+ case '\r': m_ostr << "\\r"; break;
+ case '\t': m_ostr << "\\t"; break;
+ //case '\u': m_ostr << ""; break; ??
+ default: m_ostr << *it; break;
+ }
+ }
+
+ m_ostr << '"';
+}
+
+inline void Writer::Write_i(const Null& )
+{
+ m_ostr << "null";
+}
+
+inline void Writer::Write_i(const UnknownElement& unknown)
+{
+ unknown.Accept(*this);
+}
+
+inline void Writer::Visit(const Array& array) { Write_i(array); }
+inline void Writer::Visit(const Object& object) { Write_i(object); }
+inline void Writer::Visit(const Number& number) { Write_i(number); }
+inline void Writer::Visit(const String& string) { Write_i(string); }
+inline void Writer::Visit(const Boolean& boolean) { Write_i(boolean); }
+inline void Writer::Visit(const Null& null) { Write_i(null); }
+
+
+
+} // End namespace
diff --git a/protocols/FacebookRM/src/avatars.cpp b/protocols/FacebookRM/src/avatars.cpp new file mode 100644 index 0000000000..2239843ef6 --- /dev/null +++ b/protocols/FacebookRM/src/avatars.cpp @@ -0,0 +1,240 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+File name : $HeadURL: http://eternityplugins.googlecode.com/svn/trunk/facebook/avatars.cpp $
+Revision : $Revision: 91 $
+Last change by : $Author: n3weRm0re.ewer $
+Last change on : $Date: 2011-01-08 11:10:34 +0100 (so, 08 1 2011) $
+
+*/
+
+#include "common.h"
+
+bool FacebookProto::GetDbAvatarInfo(PROTO_AVATAR_INFORMATIONT &ai, std::string *url)
+{
+ DBVARIANT dbv;
+ if (!DBGetContactSettingString(ai.hContact, m_szModuleName, FACEBOOK_KEY_AV_URL, &dbv)) {
+ std::string new_url = dbv.pszVal;
+ DBFreeVariant(&dbv);
+
+ if (new_url.empty())
+ return false;
+
+ if (url)
+ *url = new_url;
+
+ if (!DBGetContactSettingTString(ai.hContact, m_szModuleName, FACEBOOK_KEY_ID, &dbv)) {
+ std::string ext = new_url.substr(new_url.rfind('.'));
+ std::tstring filename = GetAvatarFolder() + L'\\' + dbv.ptszVal + (TCHAR*)_A2T(ext.c_str());
+ DBFreeVariant(&dbv);
+
+ ai.hContact = ai.hContact;
+ ai.format = ext_to_format(ext);
+ _tcsncpy(ai.filename, filename.c_str(), SIZEOF(ai.filename));
+ ai.filename[SIZEOF(ai.filename)-1] = 0;
+
+ return true;
+ }
+ }
+ return false;
+}
+
+void FacebookProto::CheckAvatarChange(HANDLE hContact, std::string image_url)
+{
+ // Facebook contacts always have some avatar - keep avatar in database even if we have loaded empty one (e.g. for 'On Mobile' contacts)
+ if (image_url.empty())
+ return;
+
+ if (DBGetContactSettingByte(NULL, m_szModuleName, FACEBOOK_KEY_BIG_AVATARS, DEFAULT_BIG_AVATARS))
+ {
+ std::tstring::size_type pos = image_url.rfind( "_q." );
+ if (pos != std::wstring::npos)
+ image_url = image_url.replace( pos, 3, "_s." );
+ }
+
+ DBVARIANT dbv;
+ bool update_required = true;
+ if (!DBGetContactSettingString(hContact, m_szModuleName, FACEBOOK_KEY_AV_URL, &dbv))
+ {
+ update_required = image_url != dbv.pszVal;
+ DBFreeVariant(&dbv);
+ }
+ if (update_required || !hContact)
+ {
+ DBWriteContactSettingString(hContact, m_szModuleName, FACEBOOK_KEY_AV_URL, image_url.c_str());
+ if (hContact)
+ {
+ DBWriteContactSettingByte(hContact, "ContactPhoto", "NeedUpdate", 1);
+ ProtoBroadcastAck(m_szModuleName, hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, NULL, 0);
+ }
+ else
+ {
+ PROTO_AVATAR_INFORMATIONT ai = {sizeof(ai)};
+ if (GetAvatarInfo(update_required ? GAIF_FORCE : 0, (LPARAM)&ai) != GAIR_WAITFOR)
+ CallService(MS_AV_REPORTMYAVATARCHANGED, (WPARAM)m_szModuleName, 0);
+ }
+ }
+}
+
+void FacebookProto::UpdateAvatarWorker(void *)
+{
+ HANDLE nlc = NULL;
+
+ LOG("***** UpdateAvatarWorker");
+
+ for (;;)
+ {
+ std::string url;
+ PROTO_AVATAR_INFORMATIONT ai = {sizeof(ai)};
+ ai.hContact = avatar_queue[0];
+
+ if (Miranda_Terminated())
+ {
+ LOG("***** Terminating avatar update early: %s", url.c_str());
+ break;
+ }
+
+ if (GetDbAvatarInfo(ai, &url))
+ {
+ LOG("***** Updating avatar: %s", url.c_str());
+ bool success = facy.save_url(url, ai.filename, nlc);
+
+ if (ai.hContact)
+ ProtoBroadcastAck(m_szModuleName, ai.hContact, ACKTYPE_AVATAR, success ? ACKRESULT_SUCCESS : ACKRESULT_FAILED, (HANDLE)&ai, 0);
+ else if (success)
+ CallService(MS_AV_REPORTMYAVATARCHANGED, (WPARAM)m_szModuleName, 0);
+ }
+
+ ScopedLock s(avatar_lock_);
+ avatar_queue.erase(avatar_queue.begin());
+ if (avatar_queue.empty())
+ break;
+ }
+ Netlib_CloseHandle(nlc);
+}
+
+std::tstring FacebookProto::GetAvatarFolder()
+{
+ TCHAR path[MAX_PATH];
+ if ( hAvatarFolder_ && FoldersGetCustomPathT(hAvatarFolder_, path, SIZEOF(path), _T("")) == 0 )
+ return path;
+ else
+ return def_avatar_folder_;
+}
+
+int FacebookProto::GetAvatarCaps(WPARAM wParam, LPARAM lParam)
+{
+ int res = 0;
+
+ switch (wParam)
+ {
+ case AF_MAXSIZE:
+ ((POINT*)lParam)->x = -1;
+ ((POINT*)lParam)->y = -1;
+ break;
+
+ case AF_MAXFILESIZE:
+ res = 0;
+ break;
+
+ case AF_PROPORTION:
+ res = PIP_NONE;
+ break;
+
+ case AF_FORMATSUPPORTED:
+ res = (lParam == PA_FORMAT_JPEG || lParam == PA_FORMAT_GIF);
+ break;
+
+ case AF_DELAYAFTERFAIL:
+ res = 60 * 1000;
+ break;
+
+ case AF_ENABLED:
+ case AF_DONTNEEDDELAYS:
+ case AF_FETCHALWAYS:
+ res = 1;
+ break;
+ }
+
+ return res;
+}
+
+int FacebookProto::GetAvatarInfo(WPARAM wParam, LPARAM lParam)
+{
+ if (!lParam)
+ return GAIR_NOAVATAR;
+
+ PROTO_AVATAR_INFORMATIONT* AI = (PROTO_AVATAR_INFORMATIONT*)lParam;
+ if (GetDbAvatarInfo(*AI, NULL))
+ {
+ bool fileExist = _taccess(AI->filename, 0) == 0;
+
+ bool needLoad;
+ if (AI->hContact)
+ needLoad = (wParam & GAIF_FORCE) && (!fileExist || DBGetContactSettingByte(AI->hContact, "ContactPhoto", "NeedUpdate", 0));
+ else
+ needLoad = (wParam & GAIF_FORCE) || !fileExist;
+
+ if (needLoad)
+ {
+ LOG("***** Starting avatar request thread for %s", AI->filename);
+ ScopedLock s( avatar_lock_ );
+
+ if (std::find(avatar_queue.begin(), avatar_queue.end(), AI->hContact) == avatar_queue.end())
+ {
+ bool is_empty = avatar_queue.empty();
+ avatar_queue.push_back(AI->hContact);
+ if (is_empty)
+ ForkThread(&FacebookProto::UpdateAvatarWorker, this, NULL);
+ }
+ return GAIR_WAITFOR;
+ }
+ else if (fileExist)
+ return GAIR_SUCCESS;
+
+ }
+ return GAIR_NOAVATAR;
+}
+
+int FacebookProto::GetMyAvatar(WPARAM wParam, LPARAM lParam)
+{
+ LOG("***** GetMyAvatar");
+
+ if (!wParam || !lParam)
+ return -3;
+
+ TCHAR* buf = ( TCHAR* )wParam;
+ int size = ( int )lParam;
+
+ PROTO_AVATAR_INFORMATIONT ai = {sizeof(ai)};
+ switch (GetAvatarInfo(0, (LPARAM)&ai))
+ {
+ case GAIR_SUCCESS:
+ _tcsncpy(buf, ai.filename, size);
+ buf[size-1] = 0;
+ return 0;
+
+ case GAIR_WAITFOR:
+ return -1;
+
+ default:
+ return -2;
+ }
+}
diff --git a/protocols/FacebookRM/src/avatars.h b/protocols/FacebookRM/src/avatars.h new file mode 100644 index 0000000000..18f8e1c100 --- /dev/null +++ b/protocols/FacebookRM/src/avatars.h @@ -0,0 +1,32 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+struct update_avatar
+{
+ update_avatar(HANDLE hContact,const std::string &url,const std::string &file)
+ : hContact(hContact),url(url),file(file) {}
+
+ HANDLE hContact;
+ std::string url, file;
+};
diff --git a/protocols/FacebookRM/src/chat.cpp b/protocols/FacebookRM/src/chat.cpp new file mode 100644 index 0000000000..db50207076 --- /dev/null +++ b/protocols/FacebookRM/src/chat.cpp @@ -0,0 +1,317 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "common.h"
+
+void FacebookProto::UpdateChat(const char *chat_id, const char *id, const char *name, const char *message, DWORD timestamp)
+{
+ GCDEST gcd = { m_szModuleName };
+ gcd.ptszID = mir_a2t(chat_id);
+
+ GCEVENT gce = {sizeof(gce)};
+ gce.pDest = &gcd;
+ gce.ptszText = mir_a2t_cp(message,CP_UTF8);
+ gce.time = timestamp ? timestamp : ::time(NULL);
+ gce.dwFlags = GC_TCHAR;
+ gcd.iType = GC_EVENT_MESSAGE;
+ gce.bIsMe = !strcmp(id,facy.self_.user_id.c_str());
+ gce.dwFlags |= GCEF_ADDTOLOG;
+
+ gce.ptszNick = mir_a2t_cp(name,CP_UTF8);
+ gce.ptszUID = mir_a2t(id);
+
+ CallServiceSync(MS_GC_EVENT,0,reinterpret_cast<LPARAM>(&gce));
+
+ mir_free(const_cast<TCHAR*>(gce.ptszUID));
+ mir_free(const_cast<TCHAR*>(gce.ptszNick));
+ mir_free(const_cast<TCHAR*>(gce.ptszText));
+ mir_free(const_cast<TCHAR*>(gcd.ptszID));
+
+
+ // Close chat window, if set
+ ForkThread( &FacebookProto::MessagingWorker, this, new send_messaging(chat_id, FACEBOOK_SEND_MESSAGE ));
+}
+
+int FacebookProto::OnChatOutgoing(WPARAM wParam,LPARAM lParam)
+{
+ GCHOOK *hook = reinterpret_cast<GCHOOK*>(lParam);
+ char *text;
+ char *id;
+
+ if (strcmp(hook->pDest->pszModule,m_szModuleName))
+ return 0;
+
+ switch(hook->pDest->iType)
+ {
+ case GC_USER_MESSAGE:
+ {
+ text = mir_t2a_cp(hook->ptszText,CP_UTF8);
+ std::string msg = text;
+
+ id = mir_t2a_cp(hook->pDest->ptszID,CP_UTF8);
+ std::string chat_id = id;
+
+ mir_free(text);
+ mir_free(id);
+
+ if (isOnline()) {
+ LOG("**Chat - Outgoing message: %s", text);
+ ForkThread(&FacebookProto::SendChatMsgWorker, this, new send_chat(chat_id, msg));
+ }
+
+ break;
+ }
+
+ case GC_USER_LEAVE:
+ case GC_SESSION_TERMINATE:
+ {
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void FacebookProto::AddChatContact(const char *chat_id, const char *id, const char *name)
+{
+ GCDEST gcd = { m_szModuleName };
+ gcd.ptszID = mir_a2t(chat_id);
+ gcd.iType = GC_EVENT_JOIN;
+
+ GCEVENT gce = {sizeof(gce)};
+ gce.pDest = &gcd;
+ gce.dwFlags = GC_TCHAR | GCEF_ADDTOLOG;
+ gce.ptszNick = mir_a2t_cp(name, CP_UTF8);
+ gce.ptszUID = mir_a2t(id);
+ gce.time = ::time(NULL);
+ gce.bIsMe = !strcmp(id, facy.self_.user_id.c_str());
+
+ if (gce.bIsMe)
+ gce.ptszStatus = _T("Admin");
+ else
+ gce.ptszStatus = _T("Normal");
+
+ CallServiceSync(MS_GC_EVENT,0,reinterpret_cast<LPARAM>(&gce));
+
+ mir_free(const_cast<TCHAR*>(gce.ptszNick));
+ mir_free(const_cast<TCHAR*>(gce.ptszUID));
+ mir_free(const_cast<TCHAR*>(gcd.ptszID));
+}
+
+
+void FacebookProto::RemoveChatContact(const char *chat_id, const char *id)
+{
+ // We dont want to remove our self-contact from chat. Ever.
+ if (!strcmp(id, facy.self_.user_id.c_str()))
+ return;
+
+ GCDEST gcd = { m_szModuleName };
+ gcd.ptszID = mir_a2t(chat_id);
+ gcd.iType = GC_EVENT_PART;
+
+ GCEVENT gce = {sizeof(gce)};
+ gce.pDest = &gcd;
+ gce.dwFlags = GC_TCHAR | GCEF_ADDTOLOG;
+ //gce.ptszNick = mir_a2t_cp(name, CP_UTF8);
+ gce.ptszUID = mir_a2t(id);
+ gce.ptszNick = gce.ptszUID;
+ gce.time = ::time(NULL);
+ gce.bIsMe = false;//!strcmp(id, facy.self_.user_id.c_str());
+
+ CallServiceSync(MS_GC_EVENT,0,reinterpret_cast<LPARAM>(&gce));
+
+ mir_free(const_cast<TCHAR*>(gcd.ptszID));
+ mir_free(const_cast<TCHAR*>(gce.ptszNick));
+ mir_free(const_cast<TCHAR*>(gce.ptszUID));
+}
+
+char *FacebookProto::GetChatUsers(const char *chat_id)
+{
+ GC_INFO gci = {0};
+ gci.Flags = USERS;
+ gci.pszModule = m_szModuleName;
+ gci.pszID = mir_a2t(chat_id);
+ CallService(MS_GC_GETINFO, 0, (LPARAM)(GC_INFO *) &gci);
+
+ LOG("**Chat - Users in chat %s: %s", chat_id, gci.pszUsers);
+
+ mir_free(gci.pszID);
+
+ // mir_free(gci.pszUsers);
+ return gci.pszUsers;
+}
+
+bool FacebookProto::IsChatContact(const char *chat_id, const char *id)
+{
+ char *users = GetChatUsers(chat_id);
+ bool found = false;
+
+ if (users != NULL && strstr(users, id) != NULL)
+ found = true;
+
+ mir_free(users);
+ return found;
+}
+
+void FacebookProto::AddChat(const char *id, const char *name)
+{
+ GCSESSION gcw = {sizeof(gcw)};
+
+ // Create the group chat session
+ gcw.dwFlags = GC_TCHAR;
+ gcw.iType = GCW_CHATROOM;
+ gcw.pszModule = m_szModuleName;
+ gcw.ptszName = mir_a2t_cp(name, CP_UTF8);
+ gcw.ptszID = mir_a2t(id);
+ CallServiceSync(MS_GC_NEWSESSION, 0, (LPARAM)&gcw);
+
+ mir_free(const_cast<TCHAR*>(gcw.ptszName));
+ mir_free(const_cast<TCHAR*>(gcw.ptszID));
+
+ // Send setting events
+ GCDEST gcd = { m_szModuleName };
+ gcd.ptszID = mir_a2t(id);
+
+ GCEVENT gce = {sizeof(gce)};
+ gce.pDest = &gcd;
+ gce.dwFlags = GC_TCHAR;
+
+ // Create a user statuses
+ gcd.iType = GC_EVENT_ADDGROUP;
+ gce.ptszStatus = _T("Admin");
+ CallServiceSync( MS_GC_EVENT, NULL, reinterpret_cast<LPARAM>(&gce));
+ gce.ptszStatus = _T("Normal");
+ CallServiceSync( MS_GC_EVENT, NULL, reinterpret_cast<LPARAM>(&gce));
+
+ // Finish initialization
+ gcd.iType = GC_EVENT_CONTROL;
+ gce.time = ::time(NULL);
+ gce.pDest = &gcd;
+
+ // Add self contact
+ AddChatContact(id, facy.self_.user_id.c_str(), facy.self_.real_name.c_str());
+ CallServiceSync(MS_GC_EVENT,SESSION_INITDONE,reinterpret_cast<LPARAM>(&gce));
+ CallServiceSync(MS_GC_EVENT,SESSION_ONLINE, reinterpret_cast<LPARAM>(&gce));
+
+ mir_free(const_cast<TCHAR*>(gcd.ptszID));
+}
+
+/*void FacebookProto::SetTopic(const char *topic)
+{
+ GCDEST gcd = { m_szModuleName };
+ gcd.ptszID = const_cast<TCHAR*>(m_tszUserName);
+ gcd.iType = GC_EVENT_TOPIC;
+
+ GCEVENT gce = {sizeof(gce)};
+ gce.pDest = &gcd;
+ gce.dwFlags = GC_TCHAR;
+ gce.time = ::time(NULL);
+
+ std::string top = Translate(topic);
+ gce.ptszText = mir_a2t(top.c_str());
+ CallServiceSync(MS_GC_EVENT,0, reinterpret_cast<LPARAM>(&gce));
+}
+*/
+
+int FacebookProto::OnJoinChat(WPARAM,LPARAM suppress)
+{
+/* GCSESSION gcw = {sizeof(gcw)};
+
+ // Create the group chat session
+ gcw.dwFlags = GC_TCHAR;
+ gcw.iType = GCW_CHATROOM;
+ gcw.pszModule = m_szModuleName;
+ gcw.ptszName = m_tszUserName;
+ gcw.ptszID = m_tszUserName;
+ CallServiceSync(MS_GC_NEWSESSION, 0, (LPARAM)&gcw);
+
+ if(m_iStatus != ID_STATUS_ONLINE)
+ return 0;
+
+ // Create a group
+ GCDEST gcd = { m_szModuleName };
+ gcd.ptszID = const_cast<TCHAR*>(m_tszUserName);
+
+ GCEVENT gce = {sizeof(gce)};
+ gce.pDest = &gcd;
+ gce.dwFlags = GC_TCHAR;
+
+ gcd.iType = GC_EVENT_ADDGROUP;
+
+ gce.ptszStatus = _T("Admin");
+ CallServiceSync( MS_GC_EVENT, NULL, reinterpret_cast<LPARAM>(&gce));
+
+ gce.ptszStatus = _T("Normal");
+ CallServiceSync( MS_GC_EVENT, NULL, reinterpret_cast<LPARAM>(&gce));
+
+ SetTopic("Omegle is a great way of meeting new friends!");
+
+ // Note: Initialization will finish up in SetChatStatus, called separately
+ if (!suppress)
+ SetChatStatus(m_iStatus);
+*/
+ return 0;
+}
+
+int FacebookProto::OnLeaveChat(WPARAM,LPARAM)
+{
+ GCDEST gcd = { m_szModuleName };
+ gcd.ptszID = NULL;
+ gcd.iType = GC_EVENT_CONTROL;
+
+ GCEVENT gce = {sizeof(gce)};
+ gce.dwFlags = GC_TCHAR;
+ gce.time = ::time(NULL);
+ gce.pDest = &gcd;
+
+ CallServiceSync(MS_GC_EVENT,SESSION_OFFLINE, reinterpret_cast<LPARAM>(&gce));
+ CallServiceSync(MS_GC_EVENT,SESSION_TERMINATE,reinterpret_cast<LPARAM>(&gce));
+
+ return 0;
+}
+
+/*
+void FacebookProto::SetChatStatus(int status)
+{
+ GCDEST gcd = { m_szModuleName };
+ gcd.ptszID = const_cast<TCHAR*>(m_tszUserName);
+ gcd.iType = GC_EVENT_CONTROL;
+
+ GCEVENT gce = {sizeof(gce)};
+ gce.dwFlags = GC_TCHAR;
+ gce.time = ::time(NULL);
+ gce.pDest = &gcd;
+
+ if(status == ID_STATUS_ONLINE)
+ {
+ // Add self contact
+ AddChatContact(facy.nick_.c_str());
+
+ CallServiceSync(MS_GC_EVENT,SESSION_INITDONE,reinterpret_cast<LPARAM>(&gce));
+ CallServiceSync(MS_GC_EVENT,SESSION_ONLINE, reinterpret_cast<LPARAM>(&gce));
+ }
+ else
+ {
+ CallServiceSync(MS_GC_EVENT,SESSION_OFFLINE,reinterpret_cast<LPARAM>(&gce));
+ }
+}
+*/
\ No newline at end of file diff --git a/protocols/FacebookRM/src/client.h b/protocols/FacebookRM/src/client.h new file mode 100644 index 0000000000..d11cf0ab55 --- /dev/null +++ b/protocols/FacebookRM/src/client.h @@ -0,0 +1,189 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+#define FORCE_DISCONNECT true
+
+class facebook_client
+{
+public:
+
+ ////////////////////////////////////////////////////////////
+
+ // Client definition
+
+ facebook_client( )
+ {
+ username_ = password_ = \
+ post_form_id_ = dtsg_ = \
+ chat_sequence_num_ = chat_channel_host_ = chat_channel_partition_ = chat_channel_jslogger_ = \
+ logout_hash_ = "";
+
+ msgid_ = error_count_ = last_feeds_update_ = last_notification_time_ = 0;
+
+ https_ = is_idle_ = invisible_ = is_typing_ = false;
+
+ buddies_lock_ = send_message_lock_ = NULL;
+ hMsgCon = NULL;
+ hFcbCon = NULL;
+ fcb_conn_lock_ = NULL;
+ }
+
+ HANDLE hMsgCon;
+ HANDLE hFcbCon;
+ HANDLE fcb_conn_lock_;
+
+ // Parent handle
+
+ FacebookProto* parent;
+
+ // User data
+
+ facebook_user self_;
+
+ std::string username_;
+ std::string password_;
+
+ std::string post_form_id_;
+ std::string dtsg_;
+ std::string logout_hash_;
+ std::string chat_channel_host_;
+ std::string chat_channel_jslogger_;
+ std::string chat_channel_partition_;
+ std::string chat_sequence_num_;
+ std::string chat_reconnect_reason_;
+ bool invisible_;
+ bool is_typing_;
+ bool is_idle_;
+ bool https_;
+ time_t last_feeds_update_;
+ unsigned __int64 last_notification_time_;
+ int msgid_;
+
+ ////////////////////////////////////////////////////////////
+
+ // Client vs protocol communication
+
+ void client_notify( TCHAR* message );
+
+ ////////////////////////////////////////////////////////////
+
+ // Cookies, Data storage
+
+ HANDLE cookies_lock_;
+
+ std::map< std::string, std::string > cookies;
+
+ std::string get_newsfeed_type( );
+
+ char* load_cookies( );
+ void store_headers( http::response* resp, NETLIBHTTPHEADER* headers, int headers_count );
+ void clear_cookies( );
+
+ ////////////////////////////////////////////////////////////
+
+ // Connection handling
+
+ unsigned int error_count_;
+
+ bool validate_response( http::response* );
+
+ bool handle_entry( std::string method );
+ bool handle_success( std::string method );
+ bool handle_error( std::string method, bool force_disconnect = false );
+
+ void __inline increment_error( ) { this->error_count_++; }
+ void __inline decrement_error( ) { if ( error_count_ > 0 ) error_count_--; }
+ void __inline reset_error( ) { error_count_ = 0; }
+
+ ////////////////////////////////////////////////////////////
+
+ // Login handling
+
+ bool login( const std::string &username, const std::string &password );
+ bool logout( );
+
+ const std::string & get_username() const;
+
+ ////////////////////////////////////////////////////////////
+
+ // Session handling
+
+ bool home( );
+ bool reconnect( );
+ bool chat_state( bool online = true );
+
+ ////////////////////////////////////////////////////////////
+
+ // Updates handling
+
+ List::List< facebook_user > buddies;
+ HANDLE buddies_lock_;
+ HANDLE send_message_lock_;
+
+ bool buddy_list( );
+ bool load_friends( );
+ bool feeds( );
+
+ ////////////////////////////////////////////////////////////
+
+ // Messages handling
+
+ bool channel( );
+ bool send_message( std::string message_recipient, std::string message_text, std::string *error_text, bool use_inbox = false, bool is_tid = false );
+ void close_chat( std::string message_recipient );
+ void chat_mark_read( std::string message_recipient );
+
+ ////////////////////////////////////////////////////////////
+
+ // Status handling
+
+ bool set_status(const std::string &text);
+
+ ////////////////////////////////////////////////////////////
+
+ // HTTP communication
+
+ http::response flap( const int request_type, std::string* request_data = NULL, std::string* request_get_data = NULL, int method = 0 );
+ bool save_url(const std::string &url,const std::tstring &filename, HANDLE &nlc);
+
+ DWORD choose_security_level( int );
+ int choose_method( int );
+ std::string choose_proto( int );
+ std::string choose_server( int, std::string* data = NULL, std::string* get_data = NULL );
+ std::string choose_action( int, std::string* data = NULL, std::string* get_data = NULL );
+ std::string choose_request_url( int, std::string* data = NULL, std::string* get_data = NULL );
+
+ NETLIBHTTPHEADER* get_request_headers( int request_type, int* headers_count );
+
+ ////////////////////////////////////////////////////////////
+
+ // Netlib handle
+
+ HANDLE handle_;
+
+ void set_handle(HANDLE h)
+ {
+ handle_ = h;
+ }
+};
diff --git a/protocols/FacebookRM/src/common.h b/protocols/FacebookRM/src/common.h new file mode 100644 index 0000000000..3df0e046d0 --- /dev/null +++ b/protocols/FacebookRM/src/common.h @@ -0,0 +1,104 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+//#pragma warning(push)
+//#pragma warning(disable:4312)
+#pragma warning(disable:4996)
+
+#define MIRANDA_VER 0x0A00
+#define _WIN32_WINNT 0x0500
+#define _WIN32_WINDOWS 0x0500
+
+#include <m_stdhdr.h>
+
+#include <string>
+#include <sstream>
+#include <fstream>
+#include <list>
+#include <map>
+#include <vector>
+#include <algorithm>
+
+#include <stdarg.h>
+#include <time.h>
+#include <assert.h>
+#include <io.h>
+
+#include <windows.h>
+#include <win2k.h>
+#include <commctrl.h>
+
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_system_cpp.h>
+#include <m_avatars.h>
+#include <m_button.h>
+#include <m_chat.h>
+#include <m_clc.h>
+#include <m_clist.h>
+#include <m_clistint.h>
+#include <m_clui.h>
+#include <m_database.h>
+#include <m_history.h>
+#include <m_idle.h>
+#include <m_ignore.h>
+#include <m_langpack.h>
+#include <m_message.h>
+#include <m_netlib.h>
+#include <m_options.h>
+#include <m_popup.h>
+#include <m_protocols.h>
+#include <m_protosvc.h>
+#include <m_protoint.h>
+#include <m_protomod.h>
+#include <m_skin.h>
+#include <statusmodes.h>
+#include <m_userinfo.h>
+#include <m_addcontact.h>
+#include <m_icolib.h>
+#include <m_utils.h>
+#include <m_hotkeys.h>
+
+#include <m_folders.h>
+
+class FacebookProto;
+
+#include "definitions.h"
+#include "entities.h"
+#include "avatars.h"
+#include "http.h"
+#include "list.hpp"
+#include "utils.h"
+#include "client.h"
+#include "proto.h"
+#include "json.h"
+#include "db.h"
+#include "constants.h"
+#include "dialogs.h"
+#include "theme.h"
+#include "resource.h"
+
+extern HINSTANCE g_hInstance;
+extern std::string g_strUserAgent;
+extern DWORD g_mirandaVersion;
\ No newline at end of file diff --git a/protocols/FacebookRM/src/communication.cpp b/protocols/FacebookRM/src/communication.cpp new file mode 100644 index 0000000000..7aa116d653 --- /dev/null +++ b/protocols/FacebookRM/src/communication.cpp @@ -0,0 +1,1276 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "common.h"
+
+void facebook_client::client_notify( TCHAR* message )
+{
+ parent->NotifyEvent( parent->m_tszUserName, message, NULL, FACEBOOK_EVENT_CLIENT );
+}
+
+http::response facebook_client::flap( const int request_type, std::string* request_data, std::string* request_get_data, int method )
+{
+ NETLIBHTTPREQUEST nlhr = {sizeof( NETLIBHTTPREQUEST )};
+ nlhr.requestType = !method ? choose_method( request_type ) : method;
+ std::string url = choose_request_url( request_type, request_data, request_get_data );
+ nlhr.szUrl = (char*)url.c_str( );
+ nlhr.flags = NLHRF_HTTP11 | NLHRF_NODUMP | choose_security_level( request_type );
+ nlhr.headers = get_request_headers( nlhr.requestType, &nlhr.headersCount );
+
+ switch (request_type)
+ {
+ case FACEBOOK_REQUEST_MESSAGES_RECEIVE:
+ nlhr.timeout = 1000 * 65; break;
+ case FACEBOOK_REQUEST_MESSAGE_SEND:
+ nlhr.timeout = 1000 * 10; break;
+ default:
+ nlhr.timeout = 1000 * 15; break;
+ }
+
+ if ( request_data != NULL )
+ {
+ nlhr.pData = (char*)(*request_data).c_str();
+ nlhr.dataLength = (int)request_data->length( );
+ }
+
+ parent->Log("@@@@@ Sending request to '%s'", nlhr.szUrl);
+
+ switch ( request_type )
+ {
+ case FACEBOOK_REQUEST_LOGIN:
+ nlhr.nlc = NULL;
+ break;
+
+ case FACEBOOK_REQUEST_MESSAGES_RECEIVE:
+ nlhr.nlc = hMsgCon;
+ nlhr.flags |= NLHRF_PERSISTENT;
+ break;
+
+ default:
+ WaitForSingleObject(fcb_conn_lock_, INFINITE);
+ nlhr.nlc = hFcbCon;
+ nlhr.flags |= NLHRF_PERSISTENT;
+ break;
+ }
+
+ NETLIBHTTPREQUEST* pnlhr = ( NETLIBHTTPREQUEST* )CallService( MS_NETLIB_HTTPTRANSACTION, (WPARAM)handle_, (LPARAM)&nlhr );
+
+ utils::mem::detract(nlhr.headers[3].szValue);
+ utils::mem::detract(nlhr.headers);
+
+ http::response resp;
+
+ switch ( request_type )
+ {
+ case FACEBOOK_REQUEST_LOGIN:
+ case FACEBOOK_REQUEST_SETUP_MACHINE:
+ break;
+
+ case FACEBOOK_REQUEST_MESSAGES_RECEIVE:
+ hMsgCon = pnlhr ? pnlhr->nlc : NULL;
+ break;
+
+ default:
+ ReleaseMutex(fcb_conn_lock_);
+ hFcbCon = pnlhr ? pnlhr->nlc : NULL;
+ break;
+ }
+
+ if ( pnlhr != NULL )
+ {
+ parent->Log("@@@@@ Got response with code %d", pnlhr->resultCode);
+ store_headers( &resp, pnlhr->headers, pnlhr->headersCount );
+ resp.code = pnlhr->resultCode;
+ resp.data = pnlhr->pData ? pnlhr->pData : "";
+
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)pnlhr);
+ } else {
+ parent->Log("!!!!! No response from server (time-out)");
+ resp.code = HTTP_CODE_FAKE_DISCONNECTED;
+ // Better to have something set explicitely as this value
+ // is compaired in all communication requests
+ }
+
+ if (DBGetContactSettingByte( NULL, parent->m_szModuleName, FACEBOOK_KEY_VALIDATE_RESPONSE, 0 ) == 1)
+ validate_response(&resp);
+
+ return resp;
+}
+
+bool facebook_client::validate_response( http::response* resp )
+{
+ if ( resp->code == HTTP_CODE_FAKE_DISCONNECTED )
+ {
+ parent->Log(" ! ! Request has timed out, connection or server error");
+ return false;
+ }
+
+ if (DBGetContactSettingByte( NULL, parent->m_szModuleName, FACEBOOK_KEY_VALIDATE_RESPONSE, 0 ) == 2) {
+ return true;
+ }
+
+/*
+ // TODO: Is this from jarvis? Or me? Add it?
+ std::string cookie = utils::text::source_get_value(&resp->data, 2, "setCookie(\\\"", ");");
+ if (!cookie.empty()) {
+ std::string cookie_name = utils::text::source_get_value(&cookie, 1, "\\\"");
+ std::string cookie_value = utils::text::source_get_value(&cookie, 3, "\\\"", "\\\"", "\\\"");
+
+ parent->Log(" New cookie from response '%s': %s", cookie_name.c_str(), cookie_value.c_str());
+ this->cookies[cookie_name] = cookie_value;
+ }
+*/
+ std::string::size_type pos = resp->data.find( "\"error\":" );
+ if ( pos != std::string::npos )
+ try
+ {
+ pos += 8;
+ int error_num = atoi( resp->data.substr( pos, resp->data.find( ",", pos ) - pos ).c_str());
+ if ( error_num != 0 )
+ {
+ std::string error = "";
+ pos = resp->data.find( "\"errorDescription\":\"", pos );
+ if (pos != std::string::npos ) {
+ pos += 20;
+ error = resp->data.substr( pos, resp->data.find( "\"", pos ) - pos );
+ error = utils::text::trim(
+ utils::text::special_expressions_decode(
+ utils::text::slashu_to_utf8( error )) );
+
+ }
+
+ resp->error_number = error_num;
+ resp->error_text = error;
+ parent->Log(" ! ! Received Facebook error: %d -- %s", error_num, error.c_str());
+ // client_notify( ... );
+ resp->code = HTTP_CODE_FAKE_ERROR;
+ return false;
+ }
+ } catch (const std::exception &e) {
+ parent->Log(" @ @ validate_response: Exception: %s",e.what());
+ return false;
+ }
+
+ return true;
+}
+
+bool facebook_client::handle_entry( std::string method )
+{
+ parent->Log(" >> Entering %s()", method.c_str());
+ return true;
+}
+
+bool facebook_client::handle_success( std::string method )
+{
+ parent->Log(" << Quitting %s()", method.c_str());
+ reset_error();
+ return true;
+}
+
+bool facebook_client::handle_error( std::string method, bool force_disconnect )
+{
+ bool result;
+ increment_error();
+ parent->Log("!!!!! %s(): Something with Facebook went wrong", method.c_str());
+
+ if ( force_disconnect )
+ result = false;
+ else if ( error_count_ <= (UINT)DBGetContactSettingByte(NULL,parent->m_szModuleName,FACEBOOK_KEY_TIMEOUTS_LIMIT,FACEBOOK_TIMEOUTS_LIMIT))
+ result = true;
+ else
+ result = false;
+
+ if ( result == false )
+ {
+ reset_error();
+ parent->SetStatus(ID_STATUS_OFFLINE);
+ }
+
+ return result;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+DWORD facebook_client::choose_security_level( int request_type )
+{
+ if (this->https_)
+ {
+ if ( request_type != FACEBOOK_REQUEST_MESSAGES_RECEIVE
+ || DBGetContactSettingByte( NULL, parent->m_szModuleName, FACEBOOK_KEY_FORCE_HTTPS_CHANNEL, DEFAULT_FORCE_HTTPS_CHANNEL ))
+ return NLHRF_SSL;
+ }
+
+ switch ( request_type )
+ {
+ case FACEBOOK_REQUEST_LOGIN:
+ case FACEBOOK_REQUEST_SETUP_MACHINE:
+ return NLHRF_SSL;
+
+// case FACEBOOK_REQUEST_LOGOUT:
+// case FACEBOOK_REQUEST_HOME:
+// case FACEBOOK_REQUEST_BUDDY_LIST:
+// case FACEBOOK_REQUEST_LOAD_FRIENDS:
+// case FACEBOOK_REQUEST_LOAD_REQUESTS:
+// case FACEBOOK_REQUEST_SEARCH:
+// case FACEBOOK_REQUEST_DELETE_FRIEND:
+// case FACEBOOK_REQUEST_REQUEST_FRIEND:
+// case FACEBOOK_REQUEST_APPROVE_FRIEND:
+// case FACEBOOK_REQUEST_CANCEL_REQUEST:
+// case FACEBOOK_REQUEST_FEEDS:
+// case FACEBOOK_REQUEST_NOTIFICATIONS:
+// case FACEBOOK_REQUEST_RECONNECT:
+// case FACEBOOK_REQUEST_STATUS_SET:
+// case FACEBOOK_REQUEST_MESSAGE_SEND:
+// case FACEBOOK_REQUEST_THREAD_INFO:
+// case FACEBOOK_REQUEST_MESSAGES_RECEIVE:
+// case FACEBOOK_REQUEST_VISIBILITY:
+// case FACEBOOK_REQUEST_TABS:
+// case FACEBOOK_REQUEST_ASYNC:
+// case FACEBOOK_REQUEST_TYPING_SEND:
+ default:
+ return ( DWORD )0;
+ }
+}
+
+int facebook_client::choose_method( int request_type )
+{
+ switch ( request_type )
+ {
+ case FACEBOOK_REQUEST_LOGIN:
+ case FACEBOOK_REQUEST_SETUP_MACHINE:
+ case FACEBOOK_REQUEST_BUDDY_LIST:
+ case FACEBOOK_REQUEST_STATUS_SET:
+ case FACEBOOK_REQUEST_MESSAGE_SEND:
+ case FACEBOOK_REQUEST_THREAD_INFO:
+ case FACEBOOK_REQUEST_VISIBILITY:
+ case FACEBOOK_REQUEST_TABS:
+ case FACEBOOK_REQUEST_ASYNC:
+ case FACEBOOK_REQUEST_TYPING_SEND:
+ case FACEBOOK_REQUEST_LOGOUT:
+ case FACEBOOK_REQUEST_DELETE_FRIEND:
+ case FACEBOOK_REQUEST_REQUEST_FRIEND:
+ case FACEBOOK_REQUEST_APPROVE_FRIEND:
+ case FACEBOOK_REQUEST_CANCEL_REQUEST:
+ return REQUEST_POST;
+
+// case FACEBOOK_REQUEST_HOME:
+// case FACEBOOK_REQUEST_MESSAGES_RECEIVE:
+// case FACEBOOK_REQUEST_FEEDS:
+// case FACEBOOK_REQUEST_NOTIFICATIONS:
+// case FACEBOOK_REQUEST_RECONNECT:
+// case FACEBOOK_REQUEST_LOAD_FRIENDS:
+// case FACEBOOK_REQUEST_LOAD_REQUESTS:
+// case FACEBOOK_REQUEST_SEARCH:
+ default:
+ return REQUEST_GET;
+ }
+}
+
+std::string facebook_client::choose_proto( int request_type )
+{
+ if (choose_security_level(request_type) == NLHRF_SSL)
+ return HTTP_PROTO_SECURE;
+ else
+ return HTTP_PROTO_REGULAR;
+}
+
+std::string facebook_client::choose_server( int request_type, std::string* data, std::string* get_data )
+{
+ switch ( request_type )
+ {
+ case FACEBOOK_REQUEST_LOGIN:
+ return FACEBOOK_SERVER_LOGIN;
+
+ case FACEBOOK_REQUEST_MESSAGES_RECEIVE:
+ {
+ std::string server = FACEBOOK_SERVER_CHAT;
+ if (!this->chat_channel_jslogger_.empty())
+ server = FACEBOOK_SERVER_CHAT2;
+
+ utils::text::replace_first( &server, "%s", "0" );
+ utils::text::replace_first( &server, "%s", this->chat_channel_host_ );
+ return server;
+ }
+
+ case FACEBOOK_REQUEST_APPROVE_FRIEND:
+ case FACEBOOK_REQUEST_LOAD_REQUESTS:
+ case FACEBOOK_REQUEST_SEARCH:
+ return FACEBOOK_SERVER_MOBILE;
+
+// case FACEBOOK_REQUEST_LOGOUT:
+// case FACEBOOK_REQUEST_HOME:
+// case FACEBOOK_REQUEST_BUDDY_LIST:
+// case FACEBOOK_REQUEST_LOAD_FRIENDS:
+// case FACEBOOK_REQUEST_FEEDS:
+// case FACEBOOK_REQUEST_NOTIFICATIONS:
+// case FACEBOOK_REQUEST_RECONNECT:
+// case FACEBOOK_REQUEST_STATUS_SET:
+// case FACEBOOK_REQUEST_MESSAGE_SEND:
+// case FACEBOOK_REQUEST_THREAD_INFO:
+// case FACEBOOK_REQUEST_VISIBILITY:
+// case FACEBOOK_REQUEST_TABS:
+// case FACEBOOK_REQUEST_ASYNC:
+// case FACEBOOK_REQUEST_TYPING_SEND:
+// case FACEBOOK_REQUEST_SETUP_MACHINE:
+// case FACEBOOK_REQUEST_DELETE_FRIEND:
+// case FACEBOOK_REQUEST_REQUEST_FRIEND:
+// case FACEBOOK_REQUEST_CANCEL_REQUEST:
+ default:
+ return FACEBOOK_SERVER_REGULAR;
+ }
+}
+
+std::string facebook_client::choose_action( int request_type, std::string* data, std::string* get_data )
+{
+ switch ( request_type )
+ {
+ case FACEBOOK_REQUEST_LOGIN:
+ return "/login.php?login_attempt=1";
+
+ case FACEBOOK_REQUEST_SETUP_MACHINE:
+ return "/checkpoint/";
+
+ case FACEBOOK_REQUEST_LOGOUT:
+ return "/logout.php";
+
+ case FACEBOOK_REQUEST_HOME:
+ return "/home.php?_fb_noscript=1";
+
+ case FACEBOOK_REQUEST_BUDDY_LIST:
+ return "/ajax/chat/buddy_list.php?__a=1";
+
+ case FACEBOOK_REQUEST_LOAD_FRIENDS:
+ {
+ std::string action = "/ajax/chat/user_info_all.php?__a=1&viewer=%s&__user=%s";
+ utils::text::replace_all( &action, "%s", self_.user_id );
+ return action;
+ }
+
+ case FACEBOOK_REQUEST_LOAD_REQUESTS:
+ {
+ return "/friends/";
+ }
+
+ case FACEBOOK_REQUEST_SEARCH:
+ {
+ std::string action = "/search/?search=people&query=";
+ if (get_data != NULL) {
+ action += *get_data;
+ }
+ return action;
+ }
+
+ case FACEBOOK_REQUEST_DELETE_FRIEND:
+ {
+ return "/ajax/profile/removefriend.php?__a=1";
+ }
+
+ case FACEBOOK_REQUEST_REQUEST_FRIEND:
+ {
+ return "/ajax/add_friend/action.php?__a=1";
+ }
+
+ case FACEBOOK_REQUEST_APPROVE_FRIEND:
+ {
+ std::string action = "/a/notifications.php?__a=1";
+ if (get_data != NULL) {
+ action += "&" + (*get_data);
+ }
+ return action;
+ }
+
+ case FACEBOOK_REQUEST_CANCEL_REQUEST:
+ {
+ return "/ajax/friends/requests/cancel.php?__a=1";
+ }
+
+ case FACEBOOK_REQUEST_FEEDS:
+ {
+ std::string action = "/ajax/intent.php?filter=";
+ action += get_newsfeed_type();
+ action += "&request_type=4&__a=1&newest=%s&ignore_self=true&load_newer=true&__user=%s";
+ std::string newest = utils::conversion::to_string((void*)&this->last_feeds_update_, UTILS_CONV_TIME_T);
+ utils::text::replace_first( &action, "%s", newest );
+ utils::text::replace_first( &action, "%s", self_.user_id );
+ return action;
+ }
+
+ case FACEBOOK_REQUEST_NOTIFICATIONS:
+ {
+ std::string action = "/ajax/notifications/get.php?__a=1&user=%s&time=0&version=2&__user=%s";
+ utils::text::replace_all( &action, "%s", self_.user_id );
+ return action;
+ }
+
+ case FACEBOOK_REQUEST_RECONNECT:
+ {
+ std::string action = "/ajax/presence/reconnect.php?__a=1&reason=%s&fb_dtsg=%s&post_form_id=%s&__user=%s";
+
+ if (this->chat_reconnect_reason_.empty())
+ this->chat_reconnect_reason_ = "6";
+
+ utils::text::replace_first( &action, "%s", this->chat_reconnect_reason_ );
+ utils::text::replace_first( &action, "%s", this->dtsg_ );
+ utils::text::replace_first( &action, "%s", this->post_form_id_ );
+ utils::text::replace_first( &action, "%s", this->self_.user_id );
+ return action;
+ }
+
+ case FACEBOOK_REQUEST_STATUS_SET:
+ return "/ajax/updatestatus.php?__a=1";
+
+ case FACEBOOK_REQUEST_MESSAGE_SEND:
+ return "/ajax/mercury/send_messages.php?__a=1";
+
+ case FACEBOOK_REQUEST_THREAD_INFO:
+ return "/ajax/mercury/thread_info.php?__a=1";
+
+ case FACEBOOK_REQUEST_MESSAGES_RECEIVE:
+ {
+ std::string action = "/x/%s/0/true/p_%s=%s";
+ if (!this->chat_channel_jslogger_.empty()) {
+ action = "/pull?clientid=&channel=p_%s&seq=%s&cb=&state=active";
+ //utils::text::replace_first( &action, "%s", dtsg_ );
+ } else {
+ utils::text::replace_first( &action, "%s", utils::time::unix_timestamp());
+ }
+
+ utils::text::replace_first( &action, "%s", self_.user_id );
+ utils::text::replace_first( &action, "%s", chat_sequence_num_.empty() ? "0" : chat_sequence_num_ );
+ return action;
+ }
+
+ case FACEBOOK_REQUEST_VISIBILITY:
+ return "/ajax/chat/privacy/visibility.php?__a=1";
+
+ case FACEBOOK_REQUEST_TABS:
+ return "/ajax/chat/tabs.php?__a=1";
+
+ case FACEBOOK_REQUEST_ASYNC:
+ {
+ std::string action = "/ajax/messaging/async.php?__a=1";
+ if (get_data != NULL) {
+ action += "&" + (*get_data);
+ }
+ return action;
+ }
+
+ case FACEBOOK_REQUEST_TYPING_SEND:
+ return "/ajax/messaging/typ.php?__a=1";
+
+ default:
+ return "/?_fb_noscript=1";
+ }
+}
+
+std::string facebook_client::choose_request_url( int request_type, std::string* data, std::string* get_data )
+{
+ std::string url = choose_proto( request_type );
+ url.append( choose_server( request_type, data, get_data ));
+ url.append( choose_action( request_type, data, get_data ));
+ return url;
+}
+
+NETLIBHTTPHEADER* facebook_client::get_request_headers( int request_type, int* headers_count )
+{
+ if (request_type == REQUEST_POST)
+ *headers_count = 5;
+ else
+ *headers_count = 4;
+
+ NETLIBHTTPHEADER* headers = ( NETLIBHTTPHEADER* )utils::mem::allocate( sizeof( NETLIBHTTPHEADER )*( *headers_count ));
+
+ if (request_type == REQUEST_POST)
+ {
+ headers[4].szName = "Content-Type";
+ headers[4].szValue = "application/x-www-form-urlencoded; charset=utf-8";
+ }
+
+ headers[3].szName = "Cookie";
+ headers[3].szValue = load_cookies( );
+ headers[2].szName = "User-Agent";
+ headers[2].szValue = (char *)g_strUserAgent.c_str( );
+ headers[1].szName = "Accept";
+ headers[1].szValue = "*/*";
+ headers[0].szName = "Accept-Language";
+ headers[0].szValue = "en,en-US;q=0.9";
+
+ return headers;
+}
+
+std::string facebook_client::get_newsfeed_type( )
+{
+ BYTE feed_type = DBGetContactSettingByte(NULL, parent->m_szModuleName, FACEBOOK_KEY_FEED_TYPE, 0);
+ if (feed_type < 0 || feed_type >= SIZEOF(feed_types))
+ feed_type = 0;
+ return feed_types[feed_type].id;
+}
+
+char* facebook_client::load_cookies( )
+{
+ ScopedLock s( cookies_lock_ );
+
+ std::string cookieString = "isfbe=false;";
+
+ if ( !cookies.empty( ))
+ for ( std::map< std::string, std::string >::iterator iter = cookies.begin(); iter != cookies.end(); ++iter )
+ {
+ cookieString.append( iter->first );
+ cookieString.append( 1, '=' );
+ cookieString.append( iter->second );
+ cookieString.append( 1, ';' );
+ }
+
+ return mir_strdup(cookieString.c_str());
+}
+
+void facebook_client::store_headers( http::response* resp, NETLIBHTTPHEADER* headers, int headersCount )
+{
+ ScopedLock c( cookies_lock_ );
+
+ for ( int i = 0; i < headersCount; i++ )
+ {
+ std::string header_name = headers[i].szName; // TODO: Casting?
+ std::string header_value = headers[i].szValue; // TODO: Casting?
+
+ if ( header_name == "Set-Cookie" )
+ {
+ std::string cookie_name = header_value.substr( 0, header_value.find( "=" ));
+ std::string cookie_value = header_value.substr( header_value.find( "=" ) + 1, header_value.find( ";" ) - header_value.find( "=" ) - 1 );
+ if ( cookie_value == "deleted" )
+ {
+ parent->Log(" Deleted cookie '%s'", cookie_name.c_str());
+ cookies.erase( cookie_name );
+ } else {
+ parent->Log(" New cookie '%s': %s", cookie_name.c_str(), cookie_value.c_str());
+ cookies[cookie_name] = cookie_value;
+ }
+ }
+ else
+ { // TODO RM: (un)comment
+ //parent->Log("----- Got header '%s': %s", header_name.c_str(), header_value.c_str());
+ resp->headers[header_name] = header_value;
+ }
+ }
+}
+
+void facebook_client::clear_cookies( )
+{
+ ScopedLock s( cookies_lock_ );
+
+ if ( !cookies.empty( ))
+ cookies.clear( );
+}
+
+bool facebook_client::login(const std::string &username,const std::string &password)
+{
+ handle_entry( "login" );
+
+ username_ = username;
+ password_ = password;
+
+ // Access homepage to get initial cookies
+ flap( FACEBOOK_REQUEST_HOME, NULL );
+
+ // Prepare login data
+ std::string data = "charset_test=%e2%82%ac%2c%c2%b4%2c%e2%82%ac%2c%c2%b4%2c%e6%b0%b4%2c%d0%94%2c%d0%84&locale=en&pass_placeHolder=Password&login=Login&persistent=1";
+ data += "&email=" + utils::url::encode( username );
+ data += "&pass=" + utils::url::encode( password );
+
+ // Send validation
+ http::response resp = flap( FACEBOOK_REQUEST_LOGIN, &data );
+
+ // Process result data
+ validate_response(&resp);
+
+ if ( resp.code == HTTP_CODE_FOUND && resp.headers.find("Location") != resp.headers.end())
+ {
+ // Check whether some Facebook things are required
+ if ( resp.headers["Location"].find("help.php") != std::string::npos )
+ {
+ client_notify( TranslateT("Login error: Some Facebook things are required."));
+ parent->Log(" ! ! Login error: Some Facebook things are required.");
+ // return handle_error( "login", FORCE_DISCONNECT );
+ }
+
+ // Check whether setting Machine name is required
+ if ( resp.headers["Location"].find("/checkpoint/") != std::string::npos )
+ {
+ resp = flap( FACEBOOK_REQUEST_SETUP_MACHINE, NULL, NULL, REQUEST_GET );
+
+ std::string inner_data;
+ if (resp.data.find("name=\"submit[Continue]\"") != std::string::npos) {
+ // Multi step with approving last unrecognized device
+ // 1) Continue
+ inner_data = "submit[Continue]=Continue";
+ inner_data += "&nh=" + utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\"" );
+ inner_data += "&fb_dtsg=" + utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"" );
+ resp = flap( FACEBOOK_REQUEST_SETUP_MACHINE, &inner_data );
+
+ // 2) Approve last unknown login
+ // inner_data = "submit[I%20don't%20recognize]=I%20don't%20recognize"; // Don't recognize - this will force to change account password
+ inner_data = "submit[This%20is%20Okay]=This%20is%20Okay"; // Recognize
+ inner_data += "&nh=" + utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\"" );
+ inner_data += "&fb_dtsg=" + utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"" );
+ resp = flap( FACEBOOK_REQUEST_SETUP_MACHINE, &inner_data );
+ }
+
+ // Save actual machine name
+ // inner_data = "machine_name=Miranda%20NG&submit[Don't%20Save]=Don't%20Save"; // Don't save
+ inner_data = "machine_name=Miranda%20NG&submit[Save%20Device]=Save%20Device"; // Save
+ inner_data += "&lsd=" + utils::text::source_get_value(&resp.data, 3, "name=\"lsd\"", "value=\"", "\"" );
+ inner_data += "&nh=" + utils::text::source_get_value(&resp.data, 3, "name=\"nh\"", "value=\"", "\"" );
+ inner_data += "&fb_dtsg=" + utils::text::source_get_value(&resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"" );
+
+ resp = flap( FACEBOOK_REQUEST_SETUP_MACHINE, &inner_data );
+ validate_response(&resp);
+ }
+ }
+
+ if ( resp.code == HTTP_CODE_FOUND && resp.headers.find("Location") != resp.headers.end())
+ {
+ // Check whether HTTPS connection is required and we don't have enabled it
+ if (!this->https_)
+ {
+ if ( resp.headers["Location"].find("https://") != std::string::npos )
+ {
+ client_notify(TranslateT("Your account requires HTTPS connection. Activating."));
+ DBWriteContactSettingByte(NULL, parent->m_szModuleName, FACEBOOK_KEY_FORCE_HTTPS, 1);
+ this->https_ = true;
+ }
+ }
+
+ }
+
+ // Check for Device ID
+ if ( cookies["datr"].length())
+ DBWriteContactSettingString( NULL, parent->m_szModuleName, FACEBOOK_KEY_DEVICE_ID, cookies["datr"].c_str());
+
+ switch ( resp.code )
+ {
+ case HTTP_CODE_FAKE_DISCONNECTED:
+ {
+ // When is error only because timeout, try login once more
+ if ( handle_error( "login" ))
+ return login(username, password);
+ else
+ return false;
+ }
+
+ case HTTP_CODE_OK: // OK page returned, but that is regular login page we don't want in fact
+ {
+ // Check whether captcha code is required
+ if ( resp.data.find("id=\"captcha\"") != std::string::npos )
+ {
+ client_notify( TranslateT("Login error: Captcha code is required. Bad login credentials?"));
+ parent->Log(" ! ! Login error: Captcha code is required.");
+ return handle_error( "login", FORCE_DISCONNECT );
+ }
+
+ // Get error message
+ std::string error_str = utils::text::trim(
+ utils::text::special_expressions_decode(
+ utils::text::remove_html(
+ utils::text::edit_html(
+ utils::text::source_get_value( &resp.data, 2, "id=\"standard_error\">", "</h2>" )) )) );
+
+ if ( !error_str.length())
+ error_str = Translate("Unknown login error");
+ parent->Log(" ! ! Login error: %s", error_str.c_str());
+
+ std::string message = Translate("Login error: ") + error_str;
+ TCHAR* tmessage = mir_a2t(message.c_str());
+ client_notify( tmessage );
+ mir_free( tmessage );
+ }
+ case HTTP_CODE_FORBIDDEN: // Forbidden
+ case HTTP_CODE_NOT_FOUND: // Not Found
+ default:
+ return handle_error( "login", FORCE_DISCONNECT );
+
+ case HTTP_CODE_FOUND: // Found and redirected to Home, Logged in, everything is OK
+ if ( cookies.find("c_user") != cookies.end())
+ {
+ this->self_.user_id = cookies.find("c_user")->second;
+ DBWriteContactSettingString(NULL,parent->m_szModuleName,FACEBOOK_KEY_ID,this->self_.user_id.c_str());
+ parent->Log(" Got self user id: %s", this->self_.user_id.c_str());
+ return handle_success( "login" );
+ } else {
+ client_notify(TranslateT("Login error, probably bad login credentials."));
+ parent->Log(" ! ! Login error, probably bad login credentials.");
+ return handle_error( "login", FORCE_DISCONNECT );
+ }
+ }
+}
+
+bool facebook_client::logout( )
+{
+ if ( DBGetContactSettingByte(NULL, parent->m_szModuleName, FACEBOOK_KEY_DISABLE_LOGOUT, 0))
+ return true;
+
+ handle_entry( "logout" );
+
+ std::string data = "post_form_id=" + (this->post_form_id_.length() ? this->post_form_id_ : "0");
+ data += "&fb_dtsg=" + (this->dtsg_.length() ? this->dtsg_ : "0");
+ data += "&ref=mb&h=" + this->logout_hash_;
+
+ http::response resp = flap( FACEBOOK_REQUEST_LOGOUT, &data );
+
+ if (hFcbCon)
+ Netlib_CloseHandle(hFcbCon);
+ hFcbCon = NULL;
+
+ // Process result
+ username_ = password_ = self_.user_id = "";
+
+ switch ( resp.code )
+ {
+ case HTTP_CODE_OK:
+ case HTTP_CODE_FOUND:
+ return handle_success( "logout" );
+
+ default:
+ return false; // Logout not finished properly, but..okay, who cares :P
+ }
+}
+
+bool facebook_client::home( )
+{
+ handle_entry( "home" );
+
+ http::response resp = flap( FACEBOOK_REQUEST_HOME );
+
+ // Process result data
+ validate_response(&resp);
+
+ switch ( resp.code )
+ {
+ case HTTP_CODE_OK:
+ {
+ if ( resp.data.find( "id=\"navAccountName\"" ) != std::string::npos )
+ { // Backup for old fb version
+ // Get real_name
+ this->self_.real_name = utils::text::remove_html( utils::text::special_expressions_decode( utils::text::source_get_value( &resp.data, 2, " id=\"navAccountName\">", "</a" )) );
+ DBWriteContactSettingUTF8String(NULL,parent->m_szModuleName,FACEBOOK_KEY_NAME,this->self_.real_name.c_str());
+ DBWriteContactSettingUTF8String(NULL,parent->m_szModuleName,FACEBOOK_KEY_NICK,this->self_.real_name.c_str());
+ parent->Log(" Got self real name: %s", this->self_.real_name.c_str());
+ } else if ( resp.data.find("id=\"pageNav\"") != std::string::npos ) {
+ // Get real_name
+ this->self_.real_name = utils::text::remove_html( utils::text::special_expressions_decode( utils::text::source_get_value( &resp.data, 3, " class=\"headerTinymanName\"", ">", "</a" )) );
+ DBWriteContactSettingUTF8String(NULL,parent->m_szModuleName,FACEBOOK_KEY_NAME,this->self_.real_name.c_str());
+ DBWriteContactSettingUTF8String(NULL,parent->m_szModuleName,FACEBOOK_KEY_NICK,this->self_.real_name.c_str());
+ parent->Log(" Got self real name: %s", this->self_.real_name.c_str());
+ } else {
+ client_notify(TranslateT("Something happened to Facebook. Maybe there was some major update so you should wait for an update."));
+ return handle_error( "home", FORCE_DISCONNECT );
+ }
+
+ // Get avatar
+ std::string avatar = utils::text::source_get_value( &resp.data, 4, "fbxWelcomeBoxImg", "src=", "\"", "\"" );
+ if (avatar[avatar.length()-1] == '\\')
+ avatar = avatar.substr(0, avatar.length()-1);
+
+ this->self_.image_url = utils::text::trim( utils::text::special_expressions_decode( avatar ));
+ parent->Log(" Got self avatar: %s", this->self_.image_url.c_str());
+ parent->CheckAvatarChange(NULL, this->self_.image_url);
+
+ // Get post_form_id
+ this->post_form_id_ = utils::text::source_get_value( &resp.data, 3, "name=\"post_form_id\"", "value=\"", "\"" );
+ parent->Log(" Got self post form id: %s", this->post_form_id_.c_str());
+
+ // Get dtsg
+ this->dtsg_ = utils::text::source_get_value( &resp.data, 3, "name=\"fb_dtsg\"", "value=\"", "\"" );
+ parent->Log(" Got self dtsg: %s", this->dtsg_.c_str());
+
+ // Get logout hash
+ this->logout_hash_ = utils::text::source_get_value( &resp.data, 2, "<input type=\"hidden\" autocomplete=\"off\" name=\"h\" value=\"", "\"" );
+ parent->Log(" Got self logout hash: %s", this->logout_hash_.c_str());
+
+ std::string str_count;
+
+ if (!DBGetContactSettingByte(NULL,parent->m_szModuleName,FACEBOOK_KEY_PARSE_MESSAGES, DEFAULT_PARSE_MESSAGES))
+ {
+ str_count = utils::text::source_get_value( &resp.data, 2, "<span id=\"messagesCountValue\">", "</span>" );
+ if ( str_count.length() && str_count != std::string( "0" ))
+ {
+ std::string message = Translate("Got new messages: ") + str_count;
+
+ TCHAR* tmessage = mir_a2t(message.c_str());
+ parent->NotifyEvent( parent->m_tszUserName, tmessage, NULL, FACEBOOK_EVENT_OTHER, TEXT(FACEBOOK_URL_MESSAGES));
+ mir_free( tmessage );
+ }
+ }
+
+ str_count = utils::text::source_get_value( &resp.data, 2, "<span id=\"notificationsCountValue\">", "</span>" );
+ if ( str_count.length() && str_count != std::string( "0" ))
+ {
+ // Parse notifications directly to popups
+ ForkThread( &FacebookProto::ProcessNotifications, this->parent, NULL );
+ }
+
+ if (DBGetContactSettingByte(NULL, parent->m_szModuleName, FACEBOOK_KEY_ENABLE_GROUPCHATS, DEFAULT_ENABLE_GROUPCHATS)) {
+ // Get group chats
+ std::string favorites = utils::text::source_get_value( &resp.data, 2, "<div id=\"leftCol\"", "<div id=\"contentCol\"" );
+
+ std::string::size_type pos = 0;
+ while ((pos = favorites.find("href=\"/groups/",pos)) != std::string::npos) {
+ pos += 14;
+ std::string item = favorites.substr(pos, favorites.find("</a>", pos) - pos);
+ std::string id = item.substr(0, item.find("/"));
+
+ if (!id.empty()) {
+ std::string name = utils::text::source_get_value( &item, 3, "class=\"linkWrap", ">", "</div>" );
+ name = utils::text::special_expressions_decode(utils::text::slashu_to_utf8( name ));
+ parent->Log(" Got new group chat: %s (id: %s)", name.c_str(), id.c_str());
+ if (!name.empty())
+ parent->AddChat(id.c_str(), name.c_str());
+ }
+ }
+ }
+
+ return handle_success( "home" );
+
+ }
+ case HTTP_CODE_FOUND:
+ // Work-around for replica_down, f**king hell what's that?
+ parent->Log(" REPLICA_DOWN is back in force!");
+ return this->home();
+
+ default:
+ return handle_error( "home", FORCE_DISCONNECT );
+ }
+}
+
+bool facebook_client::chat_state( bool online )
+{
+ handle_entry( "chat_state" );
+
+ std::string data = "visibility=";
+ data += ( online ) ? "1" : "0";
+ data += "&window_id=0&post_form_id=" + (post_form_id_.length() ? post_form_id_ : "0");
+ data += "&post_form_id_source=AsyncRequest&fb_dtsg=" + this->dtsg_;
+ data += "&lsd=&phstamp=0&__user=" + self_.user_id;
+ http::response resp = flap( FACEBOOK_REQUEST_VISIBILITY, &data );
+
+ return handle_success( "chat_state" );
+}
+
+bool facebook_client::reconnect( )
+{
+ handle_entry( "reconnect" );
+
+ // Request reconnect
+ http::response resp = flap( FACEBOOK_REQUEST_RECONNECT );
+
+ // Process result data
+ validate_response(&resp);
+
+ switch ( resp.code )
+ {
+ case HTTP_CODE_OK:
+ {
+ this->chat_channel_jslogger_ = utils::text::source_get_value( &resp.data, 2, "\"jslogger_suffix\":\"", "\"" );
+ parent->Log(" Got self channel jslogger: %s", this->chat_channel_jslogger_.c_str());
+
+ this->chat_channel_partition_ = utils::text::source_get_value2( &resp.data, "\"partition\":", ",}" );
+ parent->Log(" Got self channel partition: %s", this->chat_channel_partition_.c_str());
+
+ this->chat_channel_host_ = utils::text::source_get_value( &resp.data, 2, "\"host\":\"", "\"" );
+ parent->Log(" Got self channel host: %s", this->chat_channel_host_.c_str());
+
+ this->chat_sequence_num_ = utils::text::source_get_value2( &resp.data, "\"seq\":", ",}" );
+ parent->Log(" Got self sequence number: %s", this->chat_sequence_num_.c_str());
+
+ if (this->chat_channel_jslogger_.empty()) {
+ if (!atoi(this->chat_channel_host_.substr(0, this->chat_channel_host_.find(".")).c_str())) {
+ this->chat_channel_jslogger_ = "SOMETHING";
+ parent->Log(" Got no jslogger, changed.");
+ }
+ }
+
+ return handle_success( "reconnect" );
+ }
+
+ default:
+ return handle_error( "reconnect", FORCE_DISCONNECT );
+ }
+}
+
+bool facebook_client::buddy_list( )
+{
+ handle_entry( "buddy_list" );
+
+ // Prepare update data
+ std::string data = "user=" + this->self_.user_id + "&fetch_mobile=true&post_form_id=" + this->post_form_id_ + "&fb_dtsg=" + this->dtsg_ + "&lsd=&post_form_id_source=AsyncRequest&__user=" + this->self_.user_id;
+
+ {
+ ScopedLock s(buddies_lock_);
+
+ int counter = 0;
+ for (List::Item< facebook_user >* i = buddies.begin(); i != NULL; i = i->next, counter++ )
+ {
+ data += "&available_user_info_ids[";
+ data += utils::conversion::to_string(&counter, UTILS_CONV_UNSIGNED_NUMBER);
+ data += "]=";
+ data += i->data->user_id;
+ }
+ }
+
+ // Get buddy list
+ http::response resp = flap( FACEBOOK_REQUEST_BUDDY_LIST, &data );
+
+ // Process result data
+ validate_response(&resp);
+
+ switch ( resp.code )
+ {
+ case HTTP_CODE_OK:
+ {
+ std::string* response_data = new std::string( resp.data );
+ ForkThread( &FacebookProto::ProcessBuddyList, this->parent, ( void* )response_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( FACEBOOK_REQUEST_LOAD_FRIENDS );
+
+ // Process result data
+ validate_response(&resp);
+
+ switch ( resp.code )
+ {
+ case HTTP_CODE_OK:
+ {
+ std::string* response_data = new std::string( resp.data );
+ ForkThread( &FacebookProto::ProcessFriendList, this->parent, ( void* )response_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( FACEBOOK_REQUEST_FEEDS );
+
+ // Process result data
+ validate_response(&resp);
+
+ switch ( resp.code )
+ {
+ case HTTP_CODE_OK:
+ if (resp.data.find("\"num_stories\":0") == std::string::npos) {
+ std::string* response_data = new std::string( resp.data );
+ ForkThread( &FacebookProto::ProcessFeeds, this->parent, ( void* )response_data );
+ }
+ return handle_success( "feeds" );
+
+ case HTTP_CODE_FAKE_ERROR:
+ case HTTP_CODE_FAKE_DISCONNECTED:
+ default:
+ return handle_error( "feeds" );
+ }
+}
+
+bool facebook_client::channel( )
+{
+ handle_entry( "channel" );
+
+ // Get update
+ http::response resp = flap( FACEBOOK_REQUEST_MESSAGES_RECEIVE );
+
+ // Process result data
+ validate_response(&resp);
+
+ if ( resp.code != HTTP_CODE_OK )
+ {
+ // Something went wrong
+ }
+ else if ( resp.data.find( "\"t\":\"continue\"" ) != std::string::npos )
+ {
+ // Everything is OK, no new message received
+ }
+ else if ( resp.data.find( "\"t\":\"fullReload\"" ) != std::string::npos )
+ {
+ // Something went wrong (server flooding?)
+
+ parent->Log("! ! ! Requested full reload");
+
+ this->chat_sequence_num_ = utils::text::source_get_value2( &resp.data, "\"seq\":", ",}" );
+ parent->Log(" Got self sequence number: %s", this->chat_sequence_num_.c_str());
+
+ this->chat_reconnect_reason_ = utils::text::source_get_value2( &resp.data, "\"reason\":", ",}" );
+ parent->Log(" Reconnect reason: %s", this->chat_reconnect_reason_.c_str());
+ }
+ else if ( resp.data.find( "\"t\":\"refresh\"" ) != std::string::npos )
+ {
+ // Something went wrong (server flooding?)
+ parent->Log("! ! ! Requested channel refresh");
+
+ this->chat_reconnect_reason_ = utils::text::source_get_value2( &resp.data, "\"reason\":", ",}" );
+ parent->Log(" Reconnect reason: %s", this->chat_reconnect_reason_.c_str());
+
+ this->chat_sequence_num_ = utils::text::source_get_value2( &resp.data, "\"seq\":", ",}" );
+ parent->Log(" Got self sequence number: %s", this->chat_sequence_num_.c_str());
+
+ return this->reconnect( );
+ } else {
+ // Something has been received, throw to new thread to process
+ std::string* response_data = new std::string( resp.data );
+ ForkThread( &FacebookProto::ProcessMessages, this->parent, ( void* )response_data );
+
+ // Increment sequence number
+ this->chat_sequence_num_ = utils::text::source_get_value2( &resp.data, "\"seq\":", ",}" );
+ parent->Log(" Got self sequence number: %s", this->chat_sequence_num_.c_str());
+ }
+
+ // Return
+ switch ( resp.code )
+ {
+ case HTTP_CODE_OK:
+ return handle_success( "channel" );
+
+ case HTTP_CODE_FAKE_DISCONNECTED:
+ case HTTP_CODE_FAKE_ERROR:
+ default:
+ // Testing workaround for channel change
+ if (!this->chat_channel_jslogger_.empty())
+ this->chat_channel_jslogger_ = "_";
+ else
+ this->chat_channel_jslogger_.clear();
+
+ return handle_error( "channel" );
+ }
+}
+
+bool facebook_client::send_message( std::string message_recipient, std::string message_text, std::string *error_text, bool use_inbox, bool is_tid )
+{
+ handle_entry( "send_message" );
+
+ http::response resp;
+
+ if (is_tid)
+ {
+ std::string data = "message_batch[0][action_type]=ma-type:user-generated-message";
+ data += "&message_batch[0][thread_id]=" + message_recipient;
+ data += "&message_batch[0][author]=fbid:" + this->self_.user_id;
+ data += "&message_batch[0][timestamp]=" + utils::time::mili_timestamp();
+ data += "&message_batch[0][timestamp_absolute]=";
+ data += "&message_batch[0][timestamp_relative]=";
+ data += "&message_batch[0][is_unread]=false";
+ data += "&message_batch[0][is_cleared]=false";
+ data += "&message_batch[0][is_forward]=false";
+ data += "&message_batch[0][source]=source:chat:web";
+ data += "&message_batch[0][body]=" + utils::url::encode(message_text);
+ data += "&message_batch[0][has_attachment]=false";
+ data += "&message_batch[0][is_html]=false";
+ data += "&message_batch[0][message_id]=";
+ data += "&fb_dtsg=" + (dtsg_.length() ? dtsg_ : "0");
+ data += "&__user=" + this->self_.user_id;
+ data += "&phstamp=0";
+
+ resp = flap( FACEBOOK_REQUEST_MESSAGE_SEND, &data );
+ } else {
+ std::string data = "action=send&body=";
+ data += utils::url::encode( message_text );
+ data += "&recipients[0]=";
+ data += message_recipient;
+ data += "&lsd=&fb_dtsg=";
+ data += ( dtsg_.length( )) ? dtsg_ : "0";
+ data += "&post_form_id=";
+ data += ( post_form_id_.length( )) ? post_form_id_ : "0";
+
+ resp = flap( FACEBOOK_REQUEST_ASYNC, &data );
+ }
+
+ validate_response(&resp);
+ *error_text = resp.error_text;
+
+ switch ( resp.error_number )
+ {
+ case 0: // Everything is OK
+ break;
+
+ //case 1356002: // You are offline - wtf??
+
+ case 1356003: // Contact is offline
+ {
+ HANDLE hContact = parent->ContactIDToHContact( message_recipient );
+ if (hContact != NULL)
+ DBWriteContactSettingWord(hContact,parent->m_szModuleName,"Status",ID_STATUS_OFFLINE);
+ return false;
+ } break;
+
+ case 1356026: // Contact has alternative client
+ {
+ client_notify(TranslateT("Need confirmation for sending messages to other clients.\nOpen facebook website and try to send message to this contact again!"));
+ /*
+ post na url http://www.facebook.com/ajax/chat/post_application_settings.php?__a=1
+
+ enable_and_send Povolit a odeslat
+ to_send AQCoweMPeszBoKpd4iahcOyhmh0kiTYIhv1b5wCtuBiD0AaPVZIdEp3Pf5JMBmQ-9wf0ju-xdi-VRuk0ERk_I7XzI5dVJCs6-B0FExTZhspD-4-kTZLmZI-_M6fIuF2328yMyT3R3UEUmMV8P9MHcZwu-_pS3mOhsaHf6rIVcQ2rocSqLKi03wLKCfg0m8VsptPADWpOI-UNcIo-xl1PAoC1yVnL2wEXEtnF1qI_xFcmlJZ40AOONfIF_LS_lBrGYA-oCWLUK-GLHtQAHjO8aDeNXDU8Jk7Z_ES-_oAHee2PVLHcG_ACHXpasE7Iu3XFLMrdN2hjM96AjPRIf0Vk8gBZzfW_lUspakZmXxMI7iSNQE8lourK_6B3Z1s4UHxDZCNXYuc9gh70nm_xnaxnF9K1bR00s4MltnFjUT_3ypThzA
+ __d 1
+ post_form_id c73ebd9d94b7449c40e6965410fcdcf6
+ fb_dtsg Tb-T9
+ lsd
+ post_form_id_source AsyncRequest
+ */
+ return false;
+ } break;
+
+ default: // Other error
+ return false;
+ }
+
+ switch ( resp.code )
+ {
+ case HTTP_CODE_OK:
+ return handle_success( "send_message" );
+
+ case HTTP_CODE_FAKE_ERROR:
+ case HTTP_CODE_FAKE_DISCONNECTED:
+ default:
+ *error_text = Translate("Timeout when sending message.");
+
+ handle_error( "send_message" );
+ return false;
+ }
+}
+
+void facebook_client::close_chat( std::string message_recipient )
+{
+ // TODO RM: better optimalization for close_chat
+ // add items to list and then checking every x seconds
+/* if ( (::time(NULL) - parent->facy.last_close_chat_time_) < 8 )
+ return;*/
+ // parent->facy.last_close_chat_time_ = ::time(NULL);
+
+ /* Wait some time before close window, because sometimes facebook
+ can't close it so soon. But maybe this didnt help also. */
+ Sleep(300);
+
+ std::string data = "close_chat=" + message_recipient;
+ data += "&window_id=0&post_form_id=" + (post_form_id_.length() ? post_form_id_ : "0");
+ data += "&post_form_id_source=AsyncRequest&fb_dtsg=" + (this->dtsg_.length() ? this->dtsg_ : "0");
+ data += "&__user=" + self_.user_id;
+
+ http::response resp = flap( FACEBOOK_REQUEST_TABS, &data );
+}
+
+void facebook_client::chat_mark_read( std::string message_recipient )
+{
+ // TODO RM: optimalization?
+
+ std::string data = "action=chatMarkRead";
+ data += "&other_user=" + message_recipient;
+ data += "&post_form_id=" + (post_form_id_.length() ? post_form_id_ : "0");
+ data += "&fb_dtsg=" + (this->dtsg_.length() ? this->dtsg_ : "0");
+ data += "&post_form_id_source=AsyncRequest&lsd=&__user=" + self_.user_id;
+
+ http::response resp = flap( FACEBOOK_REQUEST_ASYNC, &data );
+}
+
+bool facebook_client::set_status(const std::string &status_text)
+{
+ handle_entry( "set_status" );
+
+ std::string data = "post_form_id_source=AsyncRequest";
+ data += "&post_form_id=" + (this->post_form_id_.length() ? this->post_form_id_ : "0");
+ data += "&fb_dtsg=" + (this->dtsg_.length() ? this->dtsg_ : "0");
+ data += "&target_id=" + this->self_.user_id;
+
+ if ( status_text.length( ))
+ {
+ data += "&action=PROFILE_UPDATE&app_id=&hey_kid_im_a_composer=true&display_context=profile&_log_display_context=profile&ajax_log=1";
+ data += "&status=" + utils::url::encode( status_text );
+ data += "&profile_id=" + this->self_.user_id;
+ }
+
+ http::response resp = flap( FACEBOOK_REQUEST_STATUS_SET, &data );
+
+ validate_response(&resp);
+
+ switch ( resp.code )
+ {
+ case HTTP_CODE_OK:
+ return handle_success( "set_status" );
+
+ case HTTP_CODE_FAKE_ERROR:
+ case HTTP_CODE_FAKE_DISCONNECTED:
+ default:
+ return handle_error( "set_status" );
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool facebook_client::save_url(const std::string &url,const std::tstring &filename, HANDLE &nlc)
+{
+ NETLIBHTTPREQUEST req = {sizeof(req)};
+ NETLIBHTTPREQUEST *resp;
+ req.requestType = REQUEST_GET;
+ req.szUrl = const_cast<char*>(url.c_str());
+ req.flags = NLHRF_HTTP11 | NLHRF_REDIRECT | NLHRF_PERSISTENT | NLHRF_NODUMP;
+ req.nlc = nlc;
+
+ resp = reinterpret_cast<NETLIBHTTPREQUEST*>(CallService( MS_NETLIB_HTTPTRANSACTION,
+ reinterpret_cast<WPARAM>(this->parent->m_hNetlibUser), reinterpret_cast<LPARAM>(&req)));
+
+ if ( resp )
+ {
+ nlc = resp->nlc;
+ parent->Log( "@@@@@ Saving avatar URL %s to path %s", url.c_str(), filename.c_str());
+
+ // Create folder if necessary
+ std::tstring dir = filename.substr(0,filename.rfind('\\'));
+ if( _taccess(dir.c_str(),0))
+ CallService(MS_UTILS_CREATEDIRTREET, 0, (LPARAM)dir.c_str());
+
+ // Write to file
+ FILE *f = _tfopen(filename.c_str(), _T("wb"));
+ fwrite(resp->pData,1,resp->dataLength,f);
+ fclose(f);
+
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT,0,(LPARAM)resp);
+ return true;
+ } else {
+ nlc = NULL;
+ return false;
+ }
+}
diff --git a/protocols/FacebookRM/src/connection.cpp b/protocols/FacebookRM/src/connection.cpp new file mode 100644 index 0000000000..d041e0344f --- /dev/null +++ b/protocols/FacebookRM/src/connection.cpp @@ -0,0 +1,247 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+File name : $HeadURL: http://eternityplugins.googlecode.com/svn/trunk/facebook/connection.cpp $
+Revision : $Revision: 91 $
+Last change by : $Author: n3weRm0re.ewer $
+Last change on : $Date: 2011-01-08 11:10:34 +0100 (so, 08 1 2011) $
+
+*/
+
+#include "common.h"
+
+void FacebookProto::ChangeStatus(void*)
+{
+ ScopedLock s(signon_lock_);
+ ScopedLock b(facy.buddies_lock_);
+
+ int new_status = m_iDesiredStatus;
+ int old_status = m_iStatus;
+
+ if ( new_status == ID_STATUS_OFFLINE )
+ { // Logout
+ LOG("##### Beginning SignOff process");
+
+ m_iStatus = facy.self_.status_id = ID_STATUS_OFFLINE;
+ SetEvent(update_loop_lock_);
+ Netlib_Shutdown(facy.hMsgCon);
+
+ if ( getByte(FACEBOOK_KEY_DISCONNECT_CHAT, DEFAULT_DISCONNECT_CHAT))
+ facy.chat_state( false );
+
+ facy.logout( );
+
+ deleteSetting( "LogonTS" );
+
+ facy.clear_cookies( );
+ facy.buddies.clear( );
+
+ ProtoBroadcastAck(m_szModuleName, 0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus);
+
+ OnLeaveChat(NULL, NULL);
+
+ SetAllContactStatuses( ID_STATUS_OFFLINE );
+
+ ToggleStatusMenuItems(false);
+
+ if (facy.hMsgCon)
+ Netlib_CloseHandle(facy.hMsgCon);
+ facy.hMsgCon = NULL;
+
+ LOG("##### SignOff complete");
+
+ return;
+ }
+ else if ( old_status == ID_STATUS_OFFLINE )
+ { // Login
+ SYSTEMTIME t;
+ GetLocalTime( &t );
+ Log("[%d.%d.%d] Using Facebook Protocol RM %s", t.wDay, t.wMonth, t.wYear, __VERSION_STRING);
+
+ LOG("***** Beginning SignOn process");
+
+ m_iStatus = facy.self_.status_id = ID_STATUS_CONNECTING;
+ ProtoBroadcastAck(m_szModuleName,0,ACKTYPE_STATUS,ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus);
+
+ ResetEvent(update_loop_lock_);
+
+ if ( NegotiateConnection( ))
+ {
+ facy.last_feeds_update_ = ::time( NULL );
+
+ facy.home();
+ facy.reconnect();
+
+ facy.load_friends();
+
+ // Process Friends requests
+ ForkThread( &FacebookProto::ProcessFriendRequests, this, NULL );
+
+ if (getByte(FACEBOOK_KEY_PARSE_MESSAGES, DEFAULT_PARSE_MESSAGES))
+ ForkThread( &FacebookProto::ProcessUnreadMessages, this );
+
+ setDword( "LogonTS", (DWORD)time(NULL));
+ ForkThread( &FacebookProto::UpdateLoop, this );
+ ForkThread( &FacebookProto::MessageLoop, this );
+
+ if (getByte(FACEBOOK_KEY_SET_MIRANDA_STATUS, DEFAULT_SET_MIRANDA_STATUS))
+ {
+ ForkThread(&FacebookProto::SetAwayMsgWorker, this, NULL);
+ }
+ } else {
+ ProtoBroadcastAck(m_szModuleName,0,ACKTYPE_STATUS,ACKRESULT_FAILED,
+ (HANDLE)old_status,m_iStatus);
+
+ // Set to offline
+ m_iStatus = m_iDesiredStatus = facy.self_.status_id = ID_STATUS_OFFLINE;
+ ProtoBroadcastAck(m_szModuleName, 0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus);
+
+ LOG("***** SignOn failed");
+
+ return;
+ }
+
+ ToggleStatusMenuItems(true);
+ LOG("***** SignOn complete");
+ }
+ else if ( new_status == ID_STATUS_INVISIBLE )
+ {
+ facy.buddies.clear( );
+ this->SetAllContactStatuses( ID_STATUS_OFFLINE );
+ }
+
+ facy.chat_state( m_iDesiredStatus != ID_STATUS_INVISIBLE );
+ facy.buddy_list( );
+
+ m_iStatus = facy.self_.status_id = m_iDesiredStatus;
+ ProtoBroadcastAck(m_szModuleName, 0, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus);
+
+ LOG("***** ChangeStatus complete");
+}
+
+/** Return true on success, false on error. */
+bool FacebookProto::NegotiateConnection( )
+{
+ LOG("***** Negotiating connection with Facebook");
+
+ bool error;
+ std::string user, pass;
+ DBVARIANT dbv = {0};
+
+ error = true;
+ if ( !DBGetContactSettingString(NULL,m_szModuleName,FACEBOOK_KEY_LOGIN,&dbv))
+ {
+ user = dbv.pszVal;
+ DBFreeVariant(&dbv);
+ error = user.empty();
+ }
+ if (error)
+ {
+ NotifyEvent(m_tszUserName,TranslateT("Please enter a username."),NULL,FACEBOOK_EVENT_CLIENT);
+ return false;
+ }
+
+ error = true;
+ if ( !DBGetContactSettingString(NULL,m_szModuleName,FACEBOOK_KEY_PASS,&dbv))
+ {
+ CallService(MS_DB_CRYPT_DECODESTRING,strlen(dbv.pszVal)+1,
+ reinterpret_cast<LPARAM>(dbv.pszVal));
+ pass = dbv.pszVal;
+ DBFreeVariant(&dbv);
+ error = pass.empty();
+ }
+ if (error)
+ {
+ NotifyEvent(m_tszUserName,TranslateT("Please enter a password."),NULL,FACEBOOK_EVENT_CLIENT);
+ return false;
+ }
+
+ // Load machine name
+ if ( !DBGetContactSettingString(NULL,m_szModuleName,FACEBOOK_KEY_DEVICE_ID,&dbv))
+ {
+ facy.cookies["datr"] = dbv.pszVal;
+ DBFreeVariant(&dbv);
+ }
+
+ // Get info about secured connection
+ facy.https_ = DBGetContactSettingByte(NULL, m_szModuleName, FACEBOOK_KEY_FORCE_HTTPS, DEFAULT_FORCE_HTTPS ) != 0;
+
+ // Create default group for new contacts
+ if (!DBGetContactSettingTString(NULL, m_szModuleName, FACEBOOK_KEY_DEF_GROUP, &dbv) && lstrlen(dbv.ptszVal) > 0)
+ {
+ CallService(MS_CLIST_GROUPCREATE, 0, (LPARAM)dbv.ptszVal);
+ }
+
+ return facy.login( user, pass );
+}
+
+void FacebookProto::UpdateLoop(void *)
+{
+ time_t tim = ::time(NULL);
+ LOG( ">>>>> Entering Facebook::UpdateLoop[%d]", tim );
+
+ for ( int i = -1; !isOffline(); i = ++i % 50 )
+ {
+ if ( i != -1 ) {
+ if ( !facy.invisible_ )
+ if ( !facy.buddy_list( ))
+ break;
+ }
+ if ( i == 2 && getByte( FACEBOOK_KEY_EVENT_FEEDS_ENABLE, DEFAULT_EVENT_FEEDS_ENABLE ))
+ if ( !facy.feeds( ))
+ break;
+
+ if ( i == 49 )
+ ForkThread( &FacebookProto::ProcessFriendRequests, this, NULL );
+
+ LOG( "***** FacebookProto::UpdateLoop[%d] going to sleep...", tim );
+ if ( WaitForSingleObjectEx( update_loop_lock_, GetPollRate( ) * 1000, true ) != WAIT_TIMEOUT )
+ break;
+ LOG( "***** FacebookProto::UpdateLoop[%d] waking up...", tim );
+ }
+
+ ResetEvent(update_loop_lock_);
+ LOG( "<<<<< Exiting FacebookProto::UpdateLoop[%d]", tim );
+}
+
+void FacebookProto::MessageLoop(void *)
+{
+ time_t tim = ::time(NULL);
+ LOG( ">>>>> Entering Facebook::MessageLoop[%d]", tim );
+
+ while ( facy.channel( ))
+ {
+ if ( isOffline())
+ break;
+ LOG( "***** FacebookProto::MessageLoop[%d] refreshing...", tim );
+ }
+
+ LOG( "<<<<< Exiting FacebookProto::MessageLoop[%d]", tim );
+}
+
+BYTE FacebookProto::GetPollRate( )
+{
+ BYTE poll_rate = getByte( FACEBOOK_KEY_POLL_RATE, FACEBOOK_DEFAULT_POLL_RATE );
+
+ return (
+ ( poll_rate >= FACEBOOK_MINIMAL_POLL_RATE &&
+ poll_rate <= FACEBOOK_MAXIMAL_POLL_RATE )
+ ? poll_rate : FACEBOOK_DEFAULT_POLL_RATE );
+}
diff --git a/protocols/FacebookRM/src/constants.h b/protocols/FacebookRM/src/constants.h new file mode 100644 index 0000000000..30f65b9df3 --- /dev/null +++ b/protocols/FacebookRM/src/constants.h @@ -0,0 +1,134 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+// Version management
+#define __VERSION_DWORD PLUGIN_MAKE_VERSION(0, 0, 9, 0)
+#define __VERSION_STRING "0.0.9.0"
+
+// Product management
+#define FACEBOOK_NAME "Facebook"
+#define FACEBOOK_URL_HOMEPAGE "http://www.facebook.com"
+#define FACEBOOK_URL_REQUESTS "http://www.facebook.com/n/?reqs.php"
+#define FACEBOOK_URL_MESSAGES "http://www.facebook.com/n/?inbox"
+#define FACEBOOK_URL_NOTIFICATIONS "http://www.facebook.com/n/?notifications.php"
+#define FACEBOOK_URL_PROFILE "http://www.facebook.com/profile.php?id="
+#define FACEBOOK_URL_GROUP "http://www.facebook.com/n/?home.php&sk=group_"
+
+// Connection
+#define FACEBOOK_SERVER_REGULAR "www.facebook.com"
+#define FACEBOOK_SERVER_MOBILE "m.facebook.com"
+#define FACEBOOK_SERVER_CHAT "%s.%s.facebook.com"
+#define FACEBOOK_SERVER_CHAT2 "%s-%s.facebook.com"
+#define FACEBOOK_SERVER_LOGIN "login.facebook.com"
+#define FACEBOOK_SERVER_APPS "apps.facebook.com"
+
+// Limits
+#define FACEBOOK_MESSAGE_LIMIT 1024
+#define FACEBOOK_MESSAGE_LIMIT_TEXT "1024"
+#define FACEBOOK_MIND_LIMIT 420
+#define FACEBOOK_MIND_LIMIT_TEXT "420"
+#define FACEBOOK_TIMEOUTS_LIMIT 5
+#define FACEBOOK_GROUP_NAME_LIMIT 100
+
+// Defaults
+#define FACEBOOK_MINIMAL_POLL_RATE 10
+#define FACEBOOK_DEFAULT_POLL_RATE 24 // in seconds
+#define FACEBOOK_MAXIMAL_POLL_RATE 60
+
+#define DEFAULT_FORCE_HTTPS 0
+#define DEFAULT_FORCE_HTTPS_CHANNEL 0
+#define DEFAULT_CLOSE_WINDOWS_ENABLE 0
+#define DEFAULT_SET_MIRANDA_STATUS 0
+#define DEFAULT_LOGGING_ENABLE 0
+#define DEFAULT_SYSTRAY_NOTIFY 0
+#define DEFAULT_DISABLE_STATUS_NOTIFY 0
+#define DEFAULT_BIG_AVATARS 0
+#define DEFAULT_DISCONNECT_CHAT 0
+#define DEFAULT_PARSE_MESSAGES 0
+#define DEFAULT_MAP_STATUSES 0
+#define DEFAULT_LOAD_MOBILE 0
+#define DEFAULT_ENABLE_GROUPCHATS 0
+
+#define DEFAULT_EVENT_NOTIFICATIONS_ENABLE 1
+#define DEFAULT_EVENT_FEEDS_ENABLE 1
+#define DEFAULT_EVENT_OTHER_ENABLE 1
+#define DEFAULT_EVENT_CLIENT_ENABLE 1
+#define DEFAULT_EVENT_COLBACK 0x00ffffff
+#define DEFAULT_EVENT_COLTEXT 0x00000000
+#define DEFAULT_EVENT_TIMEOUT_TYPE 0
+#define DEFAULT_EVENT_TIMEOUT 20
+
+// Event flags
+#define FACEBOOK_EVENT_CLIENT 0x10000000 // Facebook error or info message
+#define FACEBOOK_EVENT_NEWSFEED 0x20000000 // Facebook newsfeed (wall) message
+#define FACEBOOK_EVENT_NOTIFICATION 0x40000000 // Facebook new notification
+#define FACEBOOK_EVENT_OTHER 0x80000000 // Facebook other event - friend requests/new messages
+
+// Facebook request types // TODO: Provide MS_ and release in FB plugin API?
+#define FACEBOOK_REQUEST_LOGIN 100 // connecting physically
+#define FACEBOOK_REQUEST_SETUP_MACHINE 102 // setting machine name
+#define FACEBOOK_REQUEST_LOGOUT 106 // disconnecting physically
+#define FACEBOOK_REQUEST_HOME 110 // getting __post_form_id__ + __fb_dtsg__ + ...
+#define FACEBOOK_REQUEST_SEARCH 111 // searching
+#define FACEBOOK_REQUEST_BUDDY_LIST 120 // getting regular updates (friends online, ...)
+#define FACEBOOK_REQUEST_LOAD_FRIENDS 121 // getting list of all friends
+#define FACEBOOK_REQUEST_FEEDS 122 // getting feeds
+#define FACEBOOK_REQUEST_NOTIFICATIONS 123 // getting notifications
+#define FACEBOOK_REQUEST_LOAD_REQUESTS 125 // getting friend requests
+#define FACEBOOK_REQUEST_REQUEST_FRIEND 126 // requesting friends
+#define FACEBOOK_REQUEST_APPROVE_FRIEND 127 // approving friends
+#define FACEBOOK_REQUEST_DELETE_FRIEND 128 // deleting friends
+#define FACEBOOK_REQUEST_CANCEL_REQUEST 129 // cancel friends request
+#define FACEBOOK_REQUEST_RECONNECT 130 // getting __sequence_num__ and __channel_id__
+#define FACEBOOK_REQUEST_STATUS_SET 251 // setting my "What's on my mind?"
+#define FACEBOOK_REQUEST_MESSAGE_SEND 300 // sending message
+#define FACEBOOK_REQUEST_MESSAGES_RECEIVE 301 // receiving messages
+#define FACEBOOK_REQUEST_TYPING_SEND 304 // sending typing notification
+#define FACEBOOK_REQUEST_VISIBILITY 305 // setting chat visibility
+#define FACEBOOK_REQUEST_TABS 306 // closing message window
+#define FACEBOOK_REQUEST_ASYNC 307 // marking messages read and getting other things
+#define FACEBOOK_REQUEST_THREAD_INFO 310 // getting thread info
+
+#define FACEBOOK_RECV_MESSAGE 1
+#define FACEBOOK_SEND_MESSAGE 2
+
+// Contact types
+#define FACEBOOK_CONTACT_FRIEND 1 // contact that IS on our server list
+#define FACEBOOK_CONTACT_NONE 2 // contact that ISN'T on our server list
+#define FACEBOOK_CONTACT_REQUEST 3 // contact that we asked for friendship
+#define FACEBOOK_CONTACT_APPROVE 4 // contact that is asking us for approval of friendship
+
+// News Feed types
+static const struct
+{
+ const char *name;
+ const char *id;
+} feed_types[] = {
+ { "Most Recent", "lf_" }, //h_chr?
+ { "Wall Posts", "app_2915120374" },
+ { "Top News", "h_nor" }, //h
+ { "Photos", "app_2305272732_2392950137" },
+ { "Links", "app_2309869772" },
+ { "Apps and Games", "appsandgames" },
+};
\ No newline at end of file diff --git a/protocols/FacebookRM/src/contacts.cpp b/protocols/FacebookRM/src/contacts.cpp new file mode 100644 index 0000000000..9e6c6abfa0 --- /dev/null +++ b/protocols/FacebookRM/src/contacts.cpp @@ -0,0 +1,335 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "common.h"
+
+bool FacebookProto::IsMyContact(HANDLE hContact, bool include_chat)
+{
+ const char *proto = reinterpret_cast<char*>( CallService(MS_PROTO_GETCONTACTBASEPROTO,
+ reinterpret_cast<WPARAM>(hContact),0));
+
+ if( proto && strcmp(m_szModuleName,proto) == 0 )
+ {
+ if( include_chat )
+ return true;
+ else
+ return DBGetContactSettingByte(hContact,m_szModuleName,"ChatRoom",0) == 0;
+ } else {
+ return false;
+ }
+}
+
+HANDLE FacebookProto::ChatIDToHContact(std::string chat_id)
+{
+ for(HANDLE hContact = db_find_first();
+ hContact;
+ hContact = db_find_next(hContact))
+ {
+ if(!IsMyContact(hContact, true))
+ continue;
+
+ DBVARIANT dbv;
+ if( !DBGetContactSettingString(hContact,m_szModuleName,"ChatRoomID",&dbv))
+ {
+ if( strcmp(chat_id.c_str(),dbv.pszVal) == 0 )
+ {
+ DBFreeVariant(&dbv);
+ return hContact;
+ } else {
+ DBFreeVariant(&dbv);
+ }
+ }
+ }
+
+ return 0;
+}
+
+HANDLE FacebookProto::ContactIDToHContact(std::string user_id)
+{
+ for(HANDLE hContact = db_find_first();
+ hContact;
+ hContact = db_find_next(hContact))
+ {
+ if(!IsMyContact(hContact))
+ continue;
+
+ DBVARIANT dbv;
+ if( !DBGetContactSettingString(hContact,m_szModuleName,FACEBOOK_KEY_ID,&dbv))
+ {
+ if( strcmp(user_id.c_str(),dbv.pszVal) == 0 )
+ {
+ DBFreeVariant(&dbv);
+ return hContact;
+ } else {
+ DBFreeVariant(&dbv);
+ }
+ }
+ }
+
+ return 0;
+}
+
+HANDLE FacebookProto::AddToContactList(facebook_user* fbu, BYTE type, bool dont_check, const char *new_name)
+{
+ HANDLE hContact;
+
+ if (!dont_check) {
+ // First, check if this contact exists
+ hContact = ContactIDToHContact(fbu->user_id);
+ if( hContact )
+ return hContact;
+ }
+
+ // If not, make a new contact!
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_ADD, 0, 0);
+ if( hContact )
+ {
+ if( CallService(MS_PROTO_ADDTOCONTACT,(WPARAM)hContact,(LPARAM)m_szModuleName) == 0 )
+ {
+ DBWriteContactSettingString(hContact,m_szModuleName,FACEBOOK_KEY_ID,fbu->user_id.c_str());
+
+ std::string homepage = FACEBOOK_URL_PROFILE + fbu->user_id;
+ DBWriteContactSettingString(hContact, m_szModuleName,"Homepage", homepage.c_str());
+
+ DBWriteContactSettingString(hContact, m_szModuleName, "MirVer", FACEBOOK_NAME);
+
+ DBDeleteContactSetting(hContact, "CList", "MyHandle");
+
+ DBVARIANT dbv;
+ if( !DBGetContactSettingTString(NULL,m_szModuleName,FACEBOOK_KEY_DEF_GROUP,&dbv))
+ {
+ DBWriteContactSettingTString(hContact,"CList","Group",dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+
+ if (strlen(new_name) > 0)
+ {
+ DBWriteContactSettingUTF8String(hContact, m_szModuleName, FACEBOOK_KEY_NAME, new_name);
+ DBWriteContactSettingUTF8String(hContact, m_szModuleName, FACEBOOK_KEY_NICK, new_name);
+ }
+
+ DBWriteContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, type);
+
+ if (getByte(FACEBOOK_KEY_DISABLE_STATUS_NOTIFY, 0))
+ CallService(MS_IGNORE_IGNORE, (WPARAM)hContact, (LPARAM)IGNOREEVENT_USERONLINE);
+
+ return hContact;
+ } else {
+ CallService(MS_DB_CONTACT_DELETE,(WPARAM)hContact,0);
+ }
+ }
+
+ return 0;
+}
+
+void FacebookProto::SetAllContactStatuses(int status)
+{
+ for (HANDLE hContact = db_find_first();
+ hContact;
+ hContact = db_find_next(hContact))
+ {
+ if (!IsMyContact(hContact))
+ continue;
+
+ if (DBGetContactSettingWord(hContact,m_szModuleName,"Status",ID_STATUS_OFFLINE) == status)
+ continue;
+
+ DBWriteContactSettingWord(hContact,m_szModuleName,"Status",status);
+ }
+}
+
+void FacebookProto::DeleteContactFromServer(void *data)
+{
+ facy.handle_entry( "DeleteContactFromServer" );
+
+ if ( data == NULL )
+ return;
+
+ std::string id = (*(std::string*)data);
+ delete data;
+
+ std::string query = "norefresh=false&post_form_id_source=AsyncRequest&lsd=&fb_dtsg=";
+ query += facy.dtsg_;
+ query += "&post_form_id=";
+ query += facy.post_form_id_;
+ query += "&uid=";
+ query += id;
+ query += "&__user=";
+ query += facy.self_.user_id;
+
+ // Get unread inbox threads
+ http::response resp = facy.flap( FACEBOOK_REQUEST_DELETE_FRIEND, &query );
+
+ // Process result data
+ facy.validate_response(&resp);
+
+ if (resp.data.find("\"payload\":null", 0) != std::string::npos)
+ {
+ facebook_user* fbu = facy.buddies.find( id );
+ if (fbu != NULL)
+ fbu->deleted = true;
+
+ HANDLE hContact = ContactIDToHContact(id);
+
+ // If contact wasn't deleted from database
+ if (hContact != NULL)
+ {
+ DBWriteContactSettingWord(hContact, m_szModuleName, "Status", ID_STATUS_OFFLINE);
+ DBWriteContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, FACEBOOK_CONTACT_NONE);
+ DBWriteContactSettingDword(hContact, m_szModuleName, FACEBOOK_KEY_DELETED, ::time(NULL));
+ }
+
+ NotifyEvent(m_tszUserName, TranslateT("Contact was removed from your server list."), NULL, FACEBOOK_EVENT_OTHER, NULL);
+ } else {
+ facy.client_notify( TranslateT("Error occured when removing contact from server."));
+ }
+
+ if (resp.code != HTTP_CODE_OK)
+ facy.handle_error( "DeleteContactFromServer" );
+}
+
+void FacebookProto::AddContactToServer(void *data)
+{
+ facy.handle_entry( "AddContactToServer" );
+
+ if ( data == NULL )
+ return;
+
+ std::string id = (*(std::string*)data);
+ delete data;
+
+ std::string query = "action=add_friend&how_found=profile_button&ref_param=ts&outgoing_id=&unwanted=&logging_location=&no_flyout_on_click=false&ego_log_data=&post_form_id_source=AsyncRequest&lsd=&fb_dtsg=";
+ query += facy.dtsg_;
+ query += "&post_form_id=";
+ query += facy.post_form_id_;
+ query += "&to_friend=";
+ query += id;
+ query += "&__user=";
+ query += facy.self_.user_id;
+
+ // Get unread inbox threads
+ http::response resp = facy.flap( FACEBOOK_REQUEST_REQUEST_FRIEND, &query );
+
+ // Process result data
+ facy.validate_response(&resp);
+
+ if (resp.data.find("\"success\":true", 0) != std::string::npos)
+ {
+ HANDLE hContact = ContactIDToHContact(id);
+
+ // If contact wasn't deleted from database
+ if (hContact != NULL)
+ {
+ DBWriteContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, FACEBOOK_CONTACT_REQUEST);
+ }
+
+ NotifyEvent(m_tszUserName, TranslateT("Request for friendship was sent."), NULL, FACEBOOK_EVENT_OTHER, NULL);
+ } else {
+ facy.client_notify( TranslateT("Error occured when requesting friendship."));
+ }
+
+ if (resp.code != HTTP_CODE_OK)
+ facy.handle_error( "AddContactToServer" );
+
+}
+
+void FacebookProto::ApproveContactToServer(void *data)
+{
+ facy.handle_entry( "ApproveContactToServer" );
+
+ if ( data == NULL )
+ return;
+
+ HANDLE hContact = (*(HANDLE*)data);
+ delete data;
+
+ std::string post_data = "fb_dtsg=" + facy.dtsg_;
+ post_data += "&charset_test=%e2%82%ac%2c%c2%b4%2c%e2%82%ac%2c%c2%b4%2c%e6%b0%b4%2c%d0%94%2c%d0%84&confirm_button=";
+
+ std::string get_data = "id=";
+
+ DBVARIANT dbv;
+ if (!DBGetContactSettingString(hContact, m_szModuleName, FACEBOOK_KEY_ID, &dbv))
+ {
+ get_data += dbv.pszVal;
+ DBFreeVariant(&dbv);
+ }
+
+ http::response resp = facy.flap( FACEBOOK_REQUEST_APPROVE_FRIEND, &post_data, &get_data );
+
+ // Process result data
+ facy.validate_response(&resp);
+
+ DBWriteContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, FACEBOOK_CONTACT_FRIEND);
+}
+
+void FacebookProto::CancelFriendsRequest(void *data)
+{
+ facy.handle_entry( "CancelFriendsRequest" );
+
+ if ( data == NULL )
+ return;
+
+ HANDLE hContact = (*(HANDLE*)data);
+ delete data;
+
+ std::string query = "phstamp=0&confirmed=1";
+ query += "&fb_dtsg=" + facy.dtsg_;
+ query += "&__user=" + facy.self_.user_id;
+
+ DBVARIANT dbv;
+ if (!DBGetContactSettingString(hContact, m_szModuleName, FACEBOOK_KEY_ID, &dbv))
+ {
+ query += "&friend=" + std::string(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+
+ // Get unread inbox threads
+ http::response resp = facy.flap( FACEBOOK_REQUEST_CANCEL_REQUEST, &query );
+
+ // Process result data
+ facy.validate_response(&resp);
+
+ if (resp.data.find("\"payload\":null", 0) != std::string::npos)
+ {
+ DBWriteContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, FACEBOOK_CONTACT_NONE);
+ NotifyEvent(m_tszUserName, TranslateT("Request for friendship was canceled."), NULL, FACEBOOK_EVENT_OTHER, NULL);
+ } else {
+ facy.client_notify( TranslateT("Error occured when canceling friendship request."));
+ }
+
+ if (resp.code != HTTP_CODE_OK)
+ facy.handle_error( "CancelFriendsRequest" );
+}
+
+
+HANDLE FacebookProto::GetAwayMsg(HANDLE hContact)
+{
+ return 0; // Status messages are disabled
+}
+
+int FacebookProto::OnContactDeleted(WPARAM wParam,LPARAM)
+{
+ CancelFriendship(wParam, 1);
+
+ return 0;
+}
diff --git a/protocols/FacebookRM/src/db.h b/protocols/FacebookRM/src/db.h new file mode 100644 index 0000000000..74233f54f2 --- /dev/null +++ b/protocols/FacebookRM/src/db.h @@ -0,0 +1,97 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+// DB macros
+#define getByte( setting, error ) DBGetContactSettingByte( NULL, m_szModuleName, setting, error )
+#define setByte( setting, value ) DBWriteContactSettingByte( NULL, m_szModuleName, setting, value )
+#define getWord( setting, error ) DBGetContactSettingWord( NULL, m_szModuleName, setting, error )
+#define setWord( setting, value ) DBWriteContactSettingWord( NULL, m_szModuleName, setting, value )
+#define getDword( setting, error ) DBGetContactSettingDword( NULL, m_szModuleName, setting, error )
+#define setDword( setting, value ) DBWriteContactSettingDword( NULL, m_szModuleName, setting, value )
+#define getString( setting, dest ) DBGetContactSettingString( NULL, m_szModuleName, setting, dest )
+#define setString( setting, value ) DBWriteContactSettingString( NULL, m_szModuleName, setting, value )
+#define getTString( setting, dest ) DBGetContactSettingTString( NULL, m_szModuleName, setting, dest )
+#define setTString( setting, value ) DBWriteContactSettingTString( NULL, m_szModuleName, setting, value )
+#define getU8String( setting, dest ) DBGetContactSettingUTF8String( NULL, m_szModuleName, setting, dest )
+#define setU8String( setting, value ) DBWriteContactSettingUTF8String( NULL, m_szModuleName, setting, value )
+#define deleteSetting( setting ) DBDeleteContactSetting( NULL, m_szModuleName, setting )
+
+// DB keys
+#define FACEBOOK_KEY_LOGIN "Email"
+#define FACEBOOK_KEY_ID "ID"
+#define FACEBOOK_KEY_TID "ThreadID"
+#define FACEBOOK_KEY_NAME "RealName"
+#define FACEBOOK_KEY_NICK "Nick"
+#define FACEBOOK_KEY_PASS "Password"
+#define FACEBOOK_KEY_UPD_NAMES "UpdateNames"
+#define FACEBOOK_KEY_DEVICE_ID "DeviceID"
+#define FACEBOOK_KEY_AV_URL "AvatarURL"
+#define FACEBOOK_KEY_DELETED "Deleted"
+#define FACEBOOK_KEY_CONTACT_TYPE "ContactType"
+#define FACEBOOK_KEY_DEF_GROUP "DefaultGroup"
+#define FACEBOOK_KEY_FORCE_HTTPS "ForceHTTPS"
+#define FACEBOOK_KEY_FORCE_HTTPS_CHANNEL "ForceHTTPSChannel"
+#define FACEBOOK_KEY_CLOSE_WINDOWS_ENABLE "CloseChatEnable"
+#define FACEBOOK_KEY_SET_MIRANDA_STATUS "SetMirandaStatus"
+#define FACEBOOK_KEY_LOGGING_ENABLE "LoggingEnable"
+#define FACEBOOK_KEY_SYSTRAY_NOTIFY "UseSystrayNotify"
+#define FACEBOOK_KEY_DISABLE_STATUS_NOTIFY "DisableStatusNotify"
+#define FACEBOOK_KEY_PARSE_MESSAGES "ParseUnreadMessages"
+#define FACEBOOK_KEY_BIG_AVATARS "UseBigAvatars"
+#define FACEBOOK_KEY_DISCONNECT_CHAT "DisconnectChatEnable"
+#define FACEBOOK_KEY_MAP_STATUSES "MapStatuses"
+#define FACEBOOK_KEY_LOAD_MOBILE "LoadMobile"
+#define FACEBOOK_KEY_ENABLE_GROUPCHATS "GroupchatsEnable"
+
+#define FACEBOOK_KEY_POLL_RATE "PollRate" // [HIDDEN]
+#define FACEBOOK_KEY_TIMEOUTS_LIMIT "TimeoutsLimit" // [HIDDEN]
+#define FACEBOOK_KEY_DISABLE_LOGOUT "DisableLogout" // [HIDDEN]
+#define FACEBOOK_KEY_VALIDATE_RESPONSE "ValidateResponse" // [HIDDEN] - 0 = standard, 1 = always, 2 = never
+#define FACEBOOK_KEY_LOCAL_TIMESTAMP "UseLocalTimestamp" // [HIDDEN]
+
+#define FACEBOOK_KEY_EVENT_NOTIFICATIONS_ENABLE "EventNotificationsEnable"
+#define FACEBOOK_KEY_EVENT_FEEDS_ENABLE "EventFeedsEnable"
+#define FACEBOOK_KEY_EVENT_OTHER_ENABLE "EventOtherEnable"
+#define FACEBOOK_KEY_EVENT_CLIENT_ENABLE "EventClientEnable"
+#define FACEBOOK_KEY_FEED_TYPE "EventFeedsType"
+
+#define FACEBOOK_KEY_EVENT_NOTIFICATIONS_COLBACK "PopupNotificationsColorBack"
+#define FACEBOOK_KEY_EVENT_NOTIFICATIONS_COLTEXT "PopupNotificationsColorText"
+#define FACEBOOK_KEY_EVENT_NOTIFICATIONS_TIMEOUT "PopupNotificationsTimeout"
+#define FACEBOOK_KEY_EVENT_NOTIFICATIONS_DEFAULT "PopupNotificationsColorDefault"
+
+#define FACEBOOK_KEY_EVENT_FEEDS_COLBACK "PopupFeedsColorBack"
+#define FACEBOOK_KEY_EVENT_FEEDS_COLTEXT "PopupFeedsColorText"
+#define FACEBOOK_KEY_EVENT_FEEDS_TIMEOUT "PopupFeedsTimeout"
+#define FACEBOOK_KEY_EVENT_FEEDS_DEFAULT "PopupFeedsColorDefault"
+
+#define FACEBOOK_KEY_EVENT_OTHER_COLBACK "PopupOtherColorBack"
+#define FACEBOOK_KEY_EVENT_OTHER_COLTEXT "PopupOtherColorText"
+#define FACEBOOK_KEY_EVENT_OTHER_TIMEOUT "PopupOtherTimeout"
+#define FACEBOOK_KEY_EVENT_OTHER_DEFAULT "PopupOtherColorDefault"
+
+#define FACEBOOK_KEY_EVENT_CLIENT_COLBACK "PopupClientColorBack"
+#define FACEBOOK_KEY_EVENT_CLIENT_COLTEXT "PopupClientColorText"
+#define FACEBOOK_KEY_EVENT_CLIENT_TIMEOUT "PopupClientTimeout"
+#define FACEBOOK_KEY_EVENT_CLIENT_DEFAULT "PopupClientColorDefault"
diff --git a/protocols/FacebookRM/src/definitions.h b/protocols/FacebookRM/src/definitions.h new file mode 100644 index 0000000000..94efdb128b --- /dev/null +++ b/protocols/FacebookRM/src/definitions.h @@ -0,0 +1,45 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+#define CODE_BLOCK_BEGIN {
+#define CODE_BLOCK_TRY try {
+#define CODE_BLOCK_CATCH } catch(const std::exception &e) {
+#define CODE_BLOCK_INFINITE while( true ) {
+#define CODE_BLOCK_END }
+
+#define FLAG_CONTAINS(x,y) ( ( x & y ) == y )
+#define REMOVE_FLAG(x,y) ( x = ( x & ~y ))
+
+#define LOG Log
+
+#define LOG_NOTIFY 0
+#define LOG_WARNING 1
+#define LOG_ALERT 2
+#define LOG_FAILURE 3
+#define LOG_CRITICAL 4
+
+
+#define NIIF_INTERN_TCHAR NIIF_INTERN_UNICODE // m_clist.h
+#define mir_tstrdup mir_wstrdup // m_system.h
+
diff --git a/protocols/FacebookRM/src/dialogs.cpp b/protocols/FacebookRM/src/dialogs.cpp new file mode 100644 index 0000000000..935005ab2c --- /dev/null +++ b/protocols/FacebookRM/src/dialogs.cpp @@ -0,0 +1,489 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "common.h"
+
+static BOOL LoadDBCheckState(FacebookProto* ppro, HWND hwnd, int idCtrl, const char* szSetting, BYTE bDef)
+{
+ BOOL state = DBGetContactSettingByte(NULL, ppro->m_szModuleName, szSetting, bDef);
+ CheckDlgButton(hwnd, idCtrl, state);
+ return state;
+}
+
+static BOOL StoreDBCheckState(FacebookProto* ppro, HWND hwnd, int idCtrl, const char* szSetting)
+{
+ BOOL state = IsDlgButtonChecked(hwnd, idCtrl);
+ DBWriteContactSettingByte(NULL, ppro->m_szModuleName, szSetting, (BYTE)state);
+ return state;
+}
+
+INT_PTR CALLBACK FBAccountProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam )
+{
+ FacebookProto *proto;
+
+ switch ( message )
+ {
+
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwnd);
+
+ proto = reinterpret_cast<FacebookProto*>(lparam);
+ SetWindowLongPtr(hwnd,GWLP_USERDATA,lparam);
+
+ DBVARIANT dbv;
+ if ( !DBGetContactSettingString(0,proto->ModuleName(),FACEBOOK_KEY_LOGIN,&dbv))
+ {
+ SetDlgItemTextA(hwnd,IDC_UN,dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+
+ if ( !DBGetContactSettingString(0,proto->ModuleName(),FACEBOOK_KEY_PASS,&dbv))
+ {
+ CallService(MS_DB_CRYPT_DECODESTRING,strlen(dbv.pszVal)+1,
+ reinterpret_cast<LPARAM>(dbv.pszVal));
+ SetDlgItemTextA(hwnd,IDC_PW,dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+
+ if (!proto->isOffline()) {
+ SendMessage(GetDlgItem(hwnd,IDC_UN),EM_SETREADONLY,1,0);
+ SendMessage(GetDlgItem(hwnd,IDC_PW),EM_SETREADONLY,1,0); }
+
+ return TRUE;
+
+ case WM_COMMAND:
+ if ( LOWORD( wparam ) == IDC_NEWACCOUNTLINK )
+ {
+ CallService(MS_UTILS_OPENURL,1,reinterpret_cast<LPARAM>
+ ( FACEBOOK_URL_HOMEPAGE ));
+ return TRUE;
+ }
+
+ if ( HIWORD( wparam ) == EN_CHANGE && reinterpret_cast<HWND>(lparam) == GetFocus())
+ {
+ switch(LOWORD(wparam))
+ {
+ case IDC_UN:
+ case IDC_PW:
+ SendMessage(GetParent(hwnd),PSM_CHANGED,0,0);
+ }
+ }
+ break;
+
+ case WM_NOTIFY:
+ if ( reinterpret_cast<NMHDR*>(lparam)->code == PSN_APPLY )
+ {
+ proto = reinterpret_cast<FacebookProto*>(GetWindowLongPtr(hwnd,GWLP_USERDATA));
+ char str[128];
+
+ GetDlgItemTextA(hwnd,IDC_UN,str,sizeof(str));
+ DBWriteContactSettingString(0,proto->ModuleName(),FACEBOOK_KEY_LOGIN,str);
+
+ GetDlgItemTextA(hwnd,IDC_PW,str,sizeof(str));
+ CallService(MS_DB_CRYPT_ENCODESTRING,sizeof(str),reinterpret_cast<LPARAM>(str));
+ DBWriteContactSettingString(0,proto->ModuleName(),FACEBOOK_KEY_PASS,str);
+
+ return TRUE;
+ }
+ break;
+
+ }
+
+ return FALSE;
+}
+
+INT_PTR CALLBACK FBMindProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam )
+{
+ FacebookProto *proto;
+
+ switch(message)
+ {
+
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwnd);
+
+ proto = reinterpret_cast<FacebookProto*>(lparam);
+ SetWindowLongPtr(hwnd,GWLP_USERDATA,lparam);
+ SendDlgItemMessage(hwnd,IDC_MINDMSG,EM_LIMITTEXT,FACEBOOK_MIND_LIMIT,0);
+
+ DBVARIANT dbv = { DBVT_TCHAR };
+
+ if (!DBGetContactSettingTString(NULL,proto->m_szModuleName,FACEBOOK_KEY_NAME,&dbv))
+ {
+ SetWindowText( hwnd, dbv.ptszVal );
+ DBFreeVariant( &dbv );
+ }
+ }
+
+ EnableWindow(GetDlgItem( hwnd, IDOK ), FALSE);
+ return TRUE;
+
+ case WM_COMMAND:
+ if ( LOWORD( wparam ) == IDC_MINDMSG && HIWORD( wparam ) == EN_CHANGE )
+ {
+ size_t len = SendDlgItemMessage(hwnd,IDC_MINDMSG,WM_GETTEXTLENGTH,0,0);
+ TCHAR str[4];
+ _sntprintf( str, 4, TEXT( "%d" ), FACEBOOK_MIND_LIMIT-len );
+ SetDlgItemText(hwnd,IDC_CHARACTERS,str);
+
+ EnableWindow(GetDlgItem( hwnd, IDOK ), len > 0);
+
+ return TRUE;
+ }
+ else if ( LOWORD( wparam ) == IDOK )
+ {
+ TCHAR mindMessage[FACEBOOK_MIND_LIMIT+1];
+ proto = reinterpret_cast<FacebookProto*>(GetWindowLongPtr(hwnd,GWLP_USERDATA));
+
+ GetDlgItemText(hwnd,IDC_MINDMSG,mindMessage,SIZEOF(mindMessage));
+ ShowWindow(hwnd,SW_HIDE);
+
+ char *narrow = mir_utf8encodeT(mindMessage);
+ if (proto->last_status_msg_ != narrow)
+ proto->last_status_msg_ = narrow;
+ utils::mem::detract(narrow);
+
+ //char *narrow = mir_t2a_cp(mindMessage,CP_UTF8);
+ ForkThread(&FacebookProto::SetAwayMsgWorker, proto, NULL);
+
+ EndDialog(hwnd, wparam);
+ return TRUE;
+ }
+ else if ( LOWORD( wparam ) == IDCANCEL )
+ {
+ EndDialog(hwnd, wparam);
+ return TRUE;
+ }
+ break;
+
+ }
+
+ return FALSE;
+}
+
+INT_PTR CALLBACK FBOptionsProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam )
+{
+ FacebookProto *proto = reinterpret_cast<FacebookProto*>(GetWindowLongPtr(hwnd,GWLP_USERDATA));
+
+ switch ( message )
+ {
+
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwnd);
+
+ proto = reinterpret_cast<FacebookProto*>(lparam);
+ SetWindowLongPtr(hwnd,GWLP_USERDATA,lparam);
+
+ DBVARIANT dbv;
+ if ( !DBGetContactSettingString(0,proto->ModuleName(),FACEBOOK_KEY_LOGIN,&dbv))
+ {
+ SetDlgItemTextA(hwnd,IDC_UN,dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+
+ if ( !DBGetContactSettingString(0,proto->ModuleName(),FACEBOOK_KEY_PASS,&dbv))
+ {
+ CallService(MS_DB_CRYPT_DECODESTRING,strlen(dbv.pszVal)+1,reinterpret_cast<LPARAM>(dbv.pszVal));
+ SetDlgItemTextA(hwnd,IDC_PW,dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+
+ if (!proto->isOffline())
+ {
+ SendMessage(GetDlgItem(hwnd,IDC_UN),EM_SETREADONLY,TRUE,0);
+ SendMessage(GetDlgItem(hwnd,IDC_PW),EM_SETREADONLY,TRUE,0);
+ }
+
+ SendDlgItemMessage(hwnd, IDC_GROUP, EM_LIMITTEXT, FACEBOOK_GROUP_NAME_LIMIT, 0);
+
+ if ( !DBGetContactSettingTString(0,proto->ModuleName(),FACEBOOK_KEY_DEF_GROUP,&dbv))
+ {
+ SetDlgItemText(hwnd,IDC_GROUP,dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+
+ LoadDBCheckState(proto, hwnd, IDC_SET_IGNORE_STATUS, FACEBOOK_KEY_DISABLE_STATUS_NOTIFY, DEFAULT_DISABLE_STATUS_NOTIFY);
+ LoadDBCheckState(proto, hwnd, IDC_BIGGER_AVATARS, FACEBOOK_KEY_BIG_AVATARS, DEFAULT_BIG_AVATARS);
+ LoadDBCheckState(proto, hwnd, IDC_LOAD_MOBILE, FACEBOOK_KEY_LOAD_MOBILE, DEFAULT_LOAD_MOBILE);
+
+ } return TRUE;
+
+ case WM_COMMAND: {
+ if ( LOWORD( wparam ) == IDC_NEWACCOUNTLINK )
+ {
+ CallService(MS_UTILS_OPENURL,1,reinterpret_cast<LPARAM>
+ ( FACEBOOK_URL_HOMEPAGE ));
+ return TRUE;
+ }
+
+ if ( LOWORD( wparam ) == IDC_SECURE ) {
+ EnableWindow(GetDlgItem(hwnd, IDC_SECURE_CHANNEL), IsDlgButtonChecked(hwnd, IDC_SECURE));
+ }
+
+ if ((LOWORD(wparam)==IDC_UN || LOWORD(wparam)==IDC_PW || LOWORD(wparam)==IDC_GROUP) &&
+ (HIWORD(wparam)!=EN_CHANGE || (HWND)lparam!=GetFocus()))
+ return 0;
+ else
+ SendMessage(GetParent(hwnd),PSM_CHANGED,0,0);
+
+ } break;
+
+ case WM_NOTIFY:
+ if ( reinterpret_cast<NMHDR*>(lparam)->code == PSN_APPLY )
+ {
+ char str[128]; TCHAR tstr[128];
+
+ GetDlgItemTextA(hwnd,IDC_UN,str,sizeof(str));
+ DBWriteContactSettingString(0,proto->ModuleName(),FACEBOOK_KEY_LOGIN,str);
+
+ GetDlgItemTextA(hwnd,IDC_PW,str,sizeof(str));
+ CallService(MS_DB_CRYPT_ENCODESTRING,sizeof(str),reinterpret_cast<LPARAM>(str));
+ DBWriteContactSettingString(NULL,proto->m_szModuleName,FACEBOOK_KEY_PASS,str);
+
+ GetDlgItemText(hwnd,IDC_GROUP,tstr,sizeof(tstr));
+ if ( lstrlen( tstr ) > 0 )
+ {
+ DBWriteContactSettingTString(NULL,proto->m_szModuleName,FACEBOOK_KEY_DEF_GROUP,tstr);
+ CallService( MS_CLIST_GROUPCREATE, 0, (LPARAM)tstr );
+ }
+ else
+ DBDeleteContactSetting(NULL,proto->m_szModuleName,FACEBOOK_KEY_DEF_GROUP);
+
+ StoreDBCheckState(proto, hwnd, IDC_SET_IGNORE_STATUS, FACEBOOK_KEY_DISABLE_STATUS_NOTIFY);
+ StoreDBCheckState(proto, hwnd, IDC_BIGGER_AVATARS, FACEBOOK_KEY_BIG_AVATARS);
+ StoreDBCheckState(proto, hwnd, IDC_LOAD_MOBILE, FACEBOOK_KEY_LOAD_MOBILE);
+
+ return TRUE;
+ }
+ break;
+
+ }
+
+ return FALSE;
+}
+
+INT_PTR CALLBACK FBOptionsAdvancedProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam )
+{
+ FacebookProto *proto = reinterpret_cast<FacebookProto*>(GetWindowLongPtr(hwnd,GWLP_USERDATA));
+
+ switch ( message )
+ {
+
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwnd);
+
+ proto = reinterpret_cast<FacebookProto*>(lparam);
+ SetWindowLongPtr(hwnd,GWLP_USERDATA,lparam);
+
+ LoadDBCheckState(proto, hwnd, IDC_SECURE, FACEBOOK_KEY_FORCE_HTTPS, DEFAULT_FORCE_HTTPS);
+ LoadDBCheckState(proto, hwnd, IDC_SECURE_CHANNEL, FACEBOOK_KEY_FORCE_HTTPS_CHANNEL, DEFAULT_FORCE_HTTPS_CHANNEL);
+ LoadDBCheckState(proto, hwnd, IDC_DISCONNECT_CHAT, FACEBOOK_KEY_DISCONNECT_CHAT, DEFAULT_DISCONNECT_CHAT);
+ LoadDBCheckState(proto, hwnd, IDC_PARSE_UNREAD, FACEBOOK_KEY_PARSE_MESSAGES, DEFAULT_PARSE_MESSAGES);
+ LoadDBCheckState(proto, hwnd, IDC_CLOSE_WINDOWS, FACEBOOK_KEY_CLOSE_WINDOWS_ENABLE, DEFAULT_CLOSE_WINDOWS_ENABLE);
+ LoadDBCheckState(proto, hwnd, IDC_SET_STATUS, FACEBOOK_KEY_SET_MIRANDA_STATUS, DEFAULT_SET_MIRANDA_STATUS);
+ LoadDBCheckState(proto, hwnd, IDC_LOGGING, FACEBOOK_KEY_LOGGING_ENABLE, DEFAULT_LOGGING_ENABLE);
+ LoadDBCheckState(proto, hwnd, IDC_MAP_STATUSES, FACEBOOK_KEY_MAP_STATUSES, DEFAULT_MAP_STATUSES);
+ LoadDBCheckState(proto, hwnd, IDC_GROUPCHATS, FACEBOOK_KEY_ENABLE_GROUPCHATS, DEFAULT_ENABLE_GROUPCHATS);
+
+ EnableWindow(GetDlgItem(hwnd, IDC_SECURE_CHANNEL), IsDlgButtonChecked(hwnd, IDC_SECURE));
+
+ return TRUE;
+ }
+
+ case WM_COMMAND: {
+ if ( LOWORD( wparam ) == IDC_SECURE ) {
+ EnableWindow(GetDlgItem(hwnd, IDC_SECURE_CHANNEL), IsDlgButtonChecked(hwnd, IDC_SECURE));
+ }
+
+ if (LOWORD(wparam) == IDC_SECURE_CHANNEL && IsDlgButtonChecked(hwnd, IDC_SECURE_CHANNEL))
+ MessageBox( hwnd, TranslateT("Note: Make sure you have disabled 'Validate SSL certificates' option in Network options to work properly."), proto->m_tszUserName, MB_OK );
+
+ SendMessage(GetParent(hwnd),PSM_CHANGED,0,0);
+
+ break;
+ }
+
+ case WM_NOTIFY:
+ if ( reinterpret_cast<NMHDR*>(lparam)->code == PSN_APPLY )
+ {
+ StoreDBCheckState(proto, hwnd, IDC_SECURE, FACEBOOK_KEY_FORCE_HTTPS);
+ StoreDBCheckState(proto, hwnd, IDC_CLOSE_WINDOWS, FACEBOOK_KEY_CLOSE_WINDOWS_ENABLE);
+ StoreDBCheckState(proto, hwnd, IDC_LOGGING, FACEBOOK_KEY_LOGGING_ENABLE);
+ StoreDBCheckState(proto, hwnd, IDC_SECURE_CHANNEL, FACEBOOK_KEY_FORCE_HTTPS_CHANNEL);
+ StoreDBCheckState(proto, hwnd, IDC_DISCONNECT_CHAT, FACEBOOK_KEY_DISCONNECT_CHAT);
+ StoreDBCheckState(proto, hwnd, IDC_PARSE_UNREAD, FACEBOOK_KEY_PARSE_MESSAGES);
+ StoreDBCheckState(proto, hwnd, IDC_MAP_STATUSES, FACEBOOK_KEY_MAP_STATUSES);
+ StoreDBCheckState(proto, hwnd, IDC_GROUPCHATS, FACEBOOK_KEY_ENABLE_GROUPCHATS);
+
+ BOOL setStatus = IsDlgButtonChecked(hwnd, IDC_SET_STATUS);
+ BOOL setStatusOld = DBGetContactSettingByte(NULL, proto->m_szModuleName, FACEBOOK_KEY_SET_MIRANDA_STATUS, DEFAULT_SET_MIRANDA_STATUS);
+ if (setStatus != setStatusOld)
+ {
+ DBWriteContactSettingByte(NULL, proto->m_szModuleName, FACEBOOK_KEY_SET_MIRANDA_STATUS, setStatus);
+ if (setStatus && proto->isOnline())
+ {
+ ForkThread(&FacebookProto::SetAwayMsgWorker, proto, NULL);
+ }
+ }
+
+ return TRUE;
+ }
+
+ break;
+ }
+
+ return FALSE;
+}
+
+
+INT_PTR CALLBACK FBEventsProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam )
+{
+ FacebookProto *proto = reinterpret_cast<FacebookProto*>(GetWindowLongPtr(hwnd,GWLP_USERDATA));
+
+ switch(message)
+ {
+
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwnd);
+
+ proto = reinterpret_cast<FacebookProto*>(lparam);
+ SetWindowLongPtr(hwnd,GWLP_USERDATA,lparam);
+
+ for(size_t i=0; i<SIZEOF(feed_types); i++)
+ {
+ SendDlgItemMessageA(hwnd,IDC_FEED_TYPE,CB_INSERTSTRING,i,
+ reinterpret_cast<LPARAM>(Translate(feed_types[i].name)));
+ }
+ SendDlgItemMessage(hwnd, IDC_FEED_TYPE, CB_SETCURSEL, DBGetContactSettingByte(NULL, proto->m_szModuleName, FACEBOOK_KEY_FEED_TYPE, 0), 0);
+ LoadDBCheckState(proto, hwnd, IDC_SYSTRAY_NOTIFY, FACEBOOK_KEY_SYSTRAY_NOTIFY, DEFAULT_SYSTRAY_NOTIFY);
+
+ LoadDBCheckState(proto, hwnd, IDC_NOTIFICATIONS_ENABLE, FACEBOOK_KEY_EVENT_NOTIFICATIONS_ENABLE, DEFAULT_EVENT_NOTIFICATIONS_ENABLE);
+ LoadDBCheckState(proto, hwnd, IDC_FEEDS_ENABLE, FACEBOOK_KEY_EVENT_FEEDS_ENABLE, DEFAULT_EVENT_FEEDS_ENABLE);
+ LoadDBCheckState(proto, hwnd, IDC_CLIENT_ENABLE, FACEBOOK_KEY_EVENT_CLIENT_ENABLE, DEFAULT_EVENT_CLIENT_ENABLE);
+ LoadDBCheckState(proto, hwnd, IDC_OTHER_ENABLE, FACEBOOK_KEY_EVENT_OTHER_ENABLE, DEFAULT_EVENT_OTHER_ENABLE);
+
+ SendDlgItemMessage(hwnd, IDC_COLBACK, CPM_SETCOLOUR, 0, DBGetContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_NOTIFICATIONS_COLBACK,DEFAULT_EVENT_COLBACK));
+ SendDlgItemMessage(hwnd, IDC_COLTEXT, CPM_SETCOLOUR, 0, DBGetContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_NOTIFICATIONS_COLTEXT,DEFAULT_EVENT_COLTEXT));
+ SetDlgItemInt(hwnd, IDC_TIMEOUT,DBGetContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_NOTIFICATIONS_TIMEOUT, 0),TRUE);
+ SendDlgItemMessage(hwnd, IDC_COLBACK2, CPM_SETCOLOUR, 0, DBGetContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_FEEDS_COLBACK,DEFAULT_EVENT_COLBACK));
+ SendDlgItemMessage(hwnd, IDC_COLTEXT2, CPM_SETCOLOUR, 0, DBGetContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_FEEDS_COLTEXT,DEFAULT_EVENT_COLTEXT));
+ SetDlgItemInt(hwnd, IDC_TIMEOUT2,DBGetContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_FEEDS_TIMEOUT, 0),TRUE);
+ SendDlgItemMessage(hwnd, IDC_COLBACK3, CPM_SETCOLOUR, 0, DBGetContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_OTHER_COLBACK,DEFAULT_EVENT_COLBACK));
+ SendDlgItemMessage(hwnd, IDC_COLTEXT3, CPM_SETCOLOUR, 0, DBGetContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_OTHER_COLTEXT,DEFAULT_EVENT_COLTEXT));
+ SetDlgItemInt(hwnd, IDC_TIMEOUT3,DBGetContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_OTHER_TIMEOUT, 0),TRUE);
+ SendDlgItemMessage(hwnd, IDC_COLBACK4, CPM_SETCOLOUR, 0, DBGetContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_CLIENT_COLBACK,DEFAULT_EVENT_COLBACK));
+ SendDlgItemMessage(hwnd, IDC_COLTEXT4, CPM_SETCOLOUR, 0, DBGetContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_CLIENT_COLTEXT,DEFAULT_EVENT_COLTEXT));
+ SetDlgItemInt(hwnd, IDC_TIMEOUT4,DBGetContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_CLIENT_TIMEOUT, 0),TRUE);
+
+ LoadDBCheckState(proto, hwnd, IDC_NOTIFICATIONS_DEFAULT, FACEBOOK_KEY_EVENT_NOTIFICATIONS_DEFAULT, 0);
+ LoadDBCheckState(proto, hwnd, IDC_FEEDS_DEFAULT, FACEBOOK_KEY_EVENT_FEEDS_DEFAULT, 0);
+ LoadDBCheckState(proto, hwnd, IDC_CLIENT_DEFAULT, FACEBOOK_KEY_EVENT_CLIENT_DEFAULT, 0);
+ LoadDBCheckState(proto, hwnd, IDC_OTHER_DEFAULT, FACEBOOK_KEY_EVENT_OTHER_DEFAULT, 0);
+
+ SendDlgItemMessage(hwnd, IDC_TIMEOUT, EM_LIMITTEXT, 4, 0);
+ SendDlgItemMessage(hwnd, IDC_TIMEOUT_SPIN, UDM_SETRANGE32, -1, 100);
+ SendDlgItemMessage(hwnd, IDC_TIMEOUT2, EM_LIMITTEXT, 4, 0);
+ SendDlgItemMessage(hwnd, IDC_TIMEOUT_SPIN2, UDM_SETRANGE32, -1, 100);
+ SendDlgItemMessage(hwnd, IDC_TIMEOUT3, EM_LIMITTEXT, 4, 0);
+ SendDlgItemMessage(hwnd, IDC_TIMEOUT_SPIN3, UDM_SETRANGE32, -1, 100);
+ SendDlgItemMessage(hwnd, IDC_TIMEOUT4, EM_LIMITTEXT, 4, 0);
+ SendDlgItemMessage(hwnd, IDC_TIMEOUT_SPIN4, UDM_SETRANGE32, -1, 100);
+
+ } return TRUE;
+
+ case WM_COMMAND: {
+ switch ( LOWORD( wparam ))
+ {
+ case IDC_PREVIEW:
+ {
+ TCHAR protoName[255];
+ lstrcpy( protoName, proto->m_tszUserName );
+ proto->NotifyEvent( protoName, TranslateT("Sample event"), NULL, FACEBOOK_EVENT_CLIENT );
+ proto->NotifyEvent( protoName, TranslateT("Sample request"), NULL, FACEBOOK_EVENT_OTHER );
+ proto->NotifyEvent( protoName, TranslateT("Sample newsfeed"), NULL, FACEBOOK_EVENT_NEWSFEED );
+ proto->NotifyEvent( protoName, TranslateT("Sample notification"), NULL, FACEBOOK_EVENT_NOTIFICATION );
+ } break;
+
+ case IDC_COLTEXT:
+ case IDC_COLBACK:
+ case IDC_COLTEXT2:
+ case IDC_COLBACK2:
+ case IDC_COLTEXT3:
+ case IDC_COLBACK3:
+ case IDC_COLTEXT4:
+ case IDC_COLBACK4:
+ SendMessage(GetParent(hwnd),PSM_CHANGED,0,0);
+ // TODO: Required? There's a catching clause below
+ }
+
+ if ((LOWORD(wparam)==IDC_PREVIEW || (HWND)lparam!=GetFocus()))
+ return 0;
+ else
+ SendMessage(GetParent(hwnd),PSM_CHANGED,0,0); }
+
+ return TRUE;
+
+ case WM_NOTIFY:
+ {
+ if ( reinterpret_cast<NMHDR*>(lparam)->code == PSN_APPLY )
+ {
+ DBWriteContactSettingByte(NULL, proto->m_szModuleName, FACEBOOK_KEY_FEED_TYPE, SendDlgItemMessage(hwnd, IDC_FEED_TYPE, CB_GETCURSEL, 0, 0));
+
+ StoreDBCheckState(proto, hwnd, IDC_SYSTRAY_NOTIFY, FACEBOOK_KEY_SYSTRAY_NOTIFY);
+
+ StoreDBCheckState(proto, hwnd, IDC_NOTIFICATIONS_ENABLE, FACEBOOK_KEY_EVENT_NOTIFICATIONS_ENABLE);
+ StoreDBCheckState(proto, hwnd, IDC_FEEDS_ENABLE, FACEBOOK_KEY_EVENT_FEEDS_ENABLE);
+ StoreDBCheckState(proto, hwnd, IDC_OTHER_ENABLE, FACEBOOK_KEY_EVENT_OTHER_ENABLE);
+ StoreDBCheckState(proto, hwnd, IDC_CLIENT_ENABLE, FACEBOOK_KEY_EVENT_CLIENT_ENABLE);
+
+ StoreDBCheckState(proto, hwnd, IDC_NOTIFICATIONS_DEFAULT, FACEBOOK_KEY_EVENT_NOTIFICATIONS_DEFAULT);
+ StoreDBCheckState(proto, hwnd, IDC_FEEDS_DEFAULT, FACEBOOK_KEY_EVENT_FEEDS_DEFAULT);
+ StoreDBCheckState(proto, hwnd, IDC_OTHER_DEFAULT, FACEBOOK_KEY_EVENT_OTHER_DEFAULT);
+ StoreDBCheckState(proto, hwnd, IDC_CLIENT_DEFAULT, FACEBOOK_KEY_EVENT_CLIENT_DEFAULT);
+
+ DBWriteContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_NOTIFICATIONS_COLBACK, SendDlgItemMessage(hwnd,IDC_COLBACK,CPM_GETCOLOUR,0,0));
+ DBWriteContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_NOTIFICATIONS_COLTEXT, SendDlgItemMessage(hwnd,IDC_COLTEXT,CPM_GETCOLOUR,0,0));
+ DBWriteContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_NOTIFICATIONS_TIMEOUT, GetDlgItemInt(hwnd,IDC_TIMEOUT,NULL,TRUE));
+
+ DBWriteContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_FEEDS_COLBACK, SendDlgItemMessage(hwnd,IDC_COLBACK2,CPM_GETCOLOUR,0,0));
+ DBWriteContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_FEEDS_COLTEXT, SendDlgItemMessage(hwnd,IDC_COLTEXT2,CPM_GETCOLOUR,0,0));
+ DBWriteContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_FEEDS_TIMEOUT, GetDlgItemInt(hwnd,IDC_TIMEOUT2,NULL,TRUE));
+
+ DBWriteContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_OTHER_COLBACK, SendDlgItemMessage(hwnd,IDC_COLBACK3,CPM_GETCOLOUR,0,0));
+ DBWriteContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_OTHER_COLTEXT, SendDlgItemMessage(hwnd,IDC_COLTEXT3,CPM_GETCOLOUR,0,0));
+ DBWriteContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_OTHER_TIMEOUT, GetDlgItemInt(hwnd,IDC_TIMEOUT3,NULL,TRUE));
+
+ DBWriteContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_CLIENT_COLBACK, SendDlgItemMessage(hwnd,IDC_COLBACK4,CPM_GETCOLOUR,0,0));
+ DBWriteContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_CLIENT_COLTEXT, SendDlgItemMessage(hwnd,IDC_COLTEXT4,CPM_GETCOLOUR,0,0));
+ DBWriteContactSettingDword(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_CLIENT_TIMEOUT, GetDlgItemInt(hwnd,IDC_TIMEOUT4,NULL,TRUE));
+ }
+ }
+ return TRUE;
+
+ }
+
+ return FALSE;
+}
diff --git a/protocols/FacebookRM/src/dialogs.h b/protocols/FacebookRM/src/dialogs.h new file mode 100644 index 0000000000..1917027546 --- /dev/null +++ b/protocols/FacebookRM/src/dialogs.h @@ -0,0 +1,29 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+INT_PTR CALLBACK FBAccountProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam );
+INT_PTR CALLBACK FBMindProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam );
+INT_PTR CALLBACK FBOptionsProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam );
+INT_PTR CALLBACK FBOptionsAdvancedProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam );
+INT_PTR CALLBACK FBEventsProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam );
diff --git a/protocols/FacebookRM/src/entities.h b/protocols/FacebookRM/src/entities.h new file mode 100644 index 0000000000..c551e1ac53 --- /dev/null +++ b/protocols/FacebookRM/src/entities.h @@ -0,0 +1,136 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+struct facebook_user
+{
+ HANDLE handle;
+
+ std::string user_id;
+ std::string real_name;
+
+ unsigned int status_id;
+ unsigned int gender;
+
+ std::string image_url;
+
+ bool deleted;
+
+ facebook_user( )
+ {
+ this->handle = NULL;
+ this->user_id = this->real_name = this->image_url = "";
+ this->status_id = ID_STATUS_OFFLINE;
+ this->gender = 0;
+ this->deleted = false;
+ }
+
+ facebook_user( facebook_user* fu )
+ {
+ this->handle = fu->handle;
+ this->image_url = fu->image_url;
+ this->real_name = fu->real_name;
+ this->status_id = fu->status_id;
+ this->user_id = fu->user_id;
+ this->gender = fu->gender;
+ this->deleted = fu->deleted;
+ }
+};
+
+struct facebook_message
+{
+ std::string user_id;
+ std::string message_text;
+ std::string sender_name;
+ DWORD time;
+
+ facebook_message( )
+ {
+ this->user_id = this->message_text = this->sender_name = "";
+ this->time = 0;
+ }
+
+ facebook_message( const facebook_message& msg )
+ {
+ this->user_id = msg.user_id;
+ this->message_text = msg.message_text;
+ this->sender_name = msg.sender_name;
+ this->time = msg.time;
+ }
+};
+
+struct facebook_notification
+{
+ std::string user_id;
+ std::string text;
+ std::string link;
+
+ facebook_notification( )
+ {
+ this->user_id = this->text = this->link = "";
+ }
+};
+
+struct facebook_newsfeed
+{
+ std::string user_id;
+ std::string title;
+ std::string text;
+ std::string link;
+
+ facebook_newsfeed( )
+ {
+ this->user_id = this->title = this->text = this->link = "";
+ }
+};
+
+
+struct send_chat
+{
+ send_chat(const std::string &chat_id,const std::string &msg) : chat_id(chat_id), msg(msg) {}
+ std::string chat_id;
+ std::string msg;
+};
+
+
+struct send_direct
+{
+ send_direct(HANDLE hContact,const std::string &msg, HANDLE msgid) : hContact(hContact), msg(msg), msgid(msgid) {}
+ HANDLE hContact;
+ std::string msg;
+ HANDLE msgid;
+};
+
+struct send_typing
+{
+ send_typing(HANDLE hContact,const int status) : hContact(hContact), status(status) {}
+ HANDLE hContact;
+ int status;
+};
+
+struct send_messaging
+{
+ send_messaging(const std::string &user_id, const int type) : user_id(user_id), type(type) {}
+ std::string user_id;
+ int type;
+};
diff --git a/protocols/FacebookRM/src/events.cpp b/protocols/FacebookRM/src/events.cpp new file mode 100644 index 0000000000..5d01df88df --- /dev/null +++ b/protocols/FacebookRM/src/events.cpp @@ -0,0 +1,186 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "common.h"
+
+int FacebookProto::Log(const char *fmt,...)
+{
+ if ( !getByte( FACEBOOK_KEY_LOGGING_ENABLE, 0 ))
+ return EXIT_SUCCESS;
+
+ va_list va;
+ char text[65535];
+ ScopedLock s(log_lock_);
+
+ va_start(va,fmt);
+ mir_vsnprintf(text,sizeof(text),fmt,va);
+ va_end(va);
+
+ return utils::debug::log( m_szModuleName, text );
+}
+
+LRESULT CALLBACK PopupDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch(message)
+ {
+ case WM_COMMAND:
+ {
+ //Get the plugin data (we need the PopUp service to do it)
+ TCHAR* data = (TCHAR*)PUGetPluginData(hwnd);
+ if (data != NULL)
+ {
+ std::string url = mir_t2a_cp(data,CP_UTF8);
+ if ( url.substr(0,4) != "http" )
+ url = FACEBOOK_URL_HOMEPAGE + url; // make absolute url
+
+ CallService(MS_UTILS_OPENURL, (WPARAM) 1, (LPARAM) url.c_str());
+ }
+
+ // After a click, destroy popup
+ PUDeletePopUp(hwnd);
+ } break;
+
+ case WM_CONTEXTMENU:
+ PUDeletePopUp(hwnd);
+ break;
+
+ case UM_FREEPLUGINDATA:
+ {
+ // After close, free
+ TCHAR* url = (TCHAR*)PUGetPluginData(hwnd);
+ if (url != NULL)
+ mir_free(url);
+ } return FALSE;
+
+ default:
+ break;
+ }
+
+ return DefWindowProc(hwnd, message, wParam, lParam);
+};
+
+void FacebookProto::NotifyEvent(TCHAR* title, TCHAR* info, HANDLE contact, DWORD flags, TCHAR* szUrl)
+{
+ int ret; int timeout; COLORREF colorBack = 0; COLORREF colorText = 0;
+
+ switch ( flags )
+ {
+ case FACEBOOK_EVENT_CLIENT:
+ if ( !getByte( FACEBOOK_KEY_EVENT_CLIENT_ENABLE, DEFAULT_EVENT_CLIENT_ENABLE ))
+ goto exit;
+ if ( !getByte( FACEBOOK_KEY_EVENT_CLIENT_DEFAULT, 0 ))
+ {
+ colorBack = getDword( FACEBOOK_KEY_EVENT_CLIENT_COLBACK, DEFAULT_EVENT_COLBACK );
+ colorText = getDword( FACEBOOK_KEY_EVENT_CLIENT_COLTEXT, DEFAULT_EVENT_COLTEXT );
+ }
+ timeout = getDword( FACEBOOK_KEY_EVENT_CLIENT_TIMEOUT, 0 );
+ flags |= NIIF_WARNING;
+ break;
+
+ case FACEBOOK_EVENT_NEWSFEED:
+ if ( !getByte( FACEBOOK_KEY_EVENT_FEEDS_ENABLE, DEFAULT_EVENT_FEEDS_ENABLE ))
+ goto exit;
+ if ( !getByte( FACEBOOK_KEY_EVENT_FEEDS_DEFAULT, 0 ))
+ {
+ colorBack = getDword( FACEBOOK_KEY_EVENT_FEEDS_COLBACK, DEFAULT_EVENT_COLBACK );
+ colorText = getDword( FACEBOOK_KEY_EVENT_FEEDS_COLTEXT, DEFAULT_EVENT_COLTEXT );
+ }
+ timeout = getDword( FACEBOOK_KEY_EVENT_FEEDS_TIMEOUT, 0 );
+ SkinPlaySound( "NewsFeed" );
+ flags |= NIIF_INFO;
+ break;
+
+ case FACEBOOK_EVENT_NOTIFICATION:
+ if ( !getByte( FACEBOOK_KEY_EVENT_NOTIFICATIONS_ENABLE, DEFAULT_EVENT_NOTIFICATIONS_ENABLE ))
+ goto exit;
+ if ( !getByte( FACEBOOK_KEY_EVENT_NOTIFICATIONS_DEFAULT, 0 ))
+ {
+ colorBack = getDword( FACEBOOK_KEY_EVENT_NOTIFICATIONS_COLBACK, DEFAULT_EVENT_COLBACK );
+ colorText = getDword( FACEBOOK_KEY_EVENT_NOTIFICATIONS_COLTEXT, DEFAULT_EVENT_COLTEXT );
+ }
+ timeout = getDword( FACEBOOK_KEY_EVENT_NOTIFICATIONS_TIMEOUT, 0 );
+ SkinPlaySound( "Notification" );
+ flags |= NIIF_INFO;
+ break;
+
+ case FACEBOOK_EVENT_OTHER:
+ if ( !getByte( FACEBOOK_KEY_EVENT_OTHER_ENABLE, DEFAULT_EVENT_OTHER_ENABLE ))
+ goto exit;
+ if ( !getByte( FACEBOOK_KEY_EVENT_OTHER_DEFAULT, 0 ))
+ {
+ colorBack = getDword( FACEBOOK_KEY_EVENT_OTHER_COLBACK, DEFAULT_EVENT_COLBACK );
+ colorText = getDword( FACEBOOK_KEY_EVENT_OTHER_COLTEXT, DEFAULT_EVENT_COLTEXT );
+ }
+ timeout = getDword( FACEBOOK_KEY_EVENT_OTHER_TIMEOUT, 0 );
+ SkinPlaySound( "OtherEvent" );
+ flags |= NIIF_INFO;
+ break;
+ }
+
+ if ( !getByte(FACEBOOK_KEY_SYSTRAY_NOTIFY,DEFAULT_SYSTRAY_NOTIFY))
+ {
+ if (ServiceExists(MS_POPUP_ADDPOPUP))
+ {
+ POPUPDATAT pd;
+ pd.colorBack = colorBack;
+ pd.colorText = colorText;
+ pd.iSeconds = timeout;
+ pd.lchContact = contact;
+ pd.lchIcon = GetIcon(1); // TODO: Icon test
+ pd.PluginData = szUrl;
+ pd.PluginWindowProc = (WNDPROC)PopupDlgProc;
+ lstrcpy(pd.lptzContactName, title);
+ lstrcpy(pd.lptzText, info);
+ ret = PUAddPopUpT(&pd);
+
+ if (ret == 0)
+ return;
+ }
+ } else {
+ if (ServiceExists(MS_CLIST_SYSTRAY_NOTIFY))
+ {
+ MIRANDASYSTRAYNOTIFY err;
+ int niif_flags = flags;
+ REMOVE_FLAG( niif_flags, FACEBOOK_EVENT_CLIENT |
+ FACEBOOK_EVENT_NEWSFEED |
+ FACEBOOK_EVENT_NOTIFICATION |
+ FACEBOOK_EVENT_OTHER );
+ err.szProto = m_szModuleName;
+ err.cbSize = sizeof(err);
+ err.dwInfoFlags = NIIF_INTERN_TCHAR | niif_flags;
+ err.tszInfoTitle = title;
+ err.tszInfo = info;
+ err.uTimeout = 1000 * timeout;
+ ret = CallService(MS_CLIST_SYSTRAY_NOTIFY, 0, (LPARAM) & err);
+
+ if (ret == 0)
+ goto exit;
+ }
+ }
+
+ if (FLAG_CONTAINS(flags, FACEBOOK_EVENT_CLIENT))
+ MessageBox(NULL, info, title, MB_OK | MB_ICONINFORMATION);
+
+exit:
+ if (szUrl != NULL)
+ mir_free(szUrl);
+}
diff --git a/protocols/FacebookRM/src/http.cpp b/protocols/FacebookRM/src/http.cpp new file mode 100644 index 0000000000..853e6b8c2f --- /dev/null +++ b/protocols/FacebookRM/src/http.cpp @@ -0,0 +1,23 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "common.h"
diff --git a/protocols/FacebookRM/src/http.h b/protocols/FacebookRM/src/http.h new file mode 100644 index 0000000000..1dd4174549 --- /dev/null +++ b/protocols/FacebookRM/src/http.h @@ -0,0 +1,102 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+#define HTTP_PROTO_REGULAR "http://"
+#define HTTP_PROTO_SECURE "https://"
+
+#define HTTP_CODE_CONTINUE 100
+#define HTTP_CODE_SWITCHING_PROTOCOLS 101
+#define HTTP_CODE_PROCESSING 102
+#define HTTP_CODE_OK 200
+#define HTTP_CODE_CREATED 201
+#define HTTP_CODE_ACCEPTED 202
+#define HTTP_CODE_NON_AUTHORITATIVE_INFORMATION 203
+#define HTTP_CODE_NO_CONTENT 204
+#define HTTP_CODE_RESET_CONTENT 205
+#define HTTP_CODE_PARTIAL_CONTENT 206
+#define HTTP_CODE_MULTI_STATUS 207
+#define HTTP_CODE_MULTIPLE_CHOICES 300
+#define HTTP_CODE_MOVED_PERMANENTLY 301
+#define HTTP_CODE_FOUND 302
+#define HTTP_CODE_SEE_OTHER 303
+#define HTTP_CODE_NOT_MODIFIED 304
+#define HTTP_CODE_USE_PROXY 305
+#define HTTP_CODE_SWITCH_PROXY 306
+#define HTTP_CODE_TEMPORARY_REDIRECT 307
+#define HTTP_CODE_BAD_REQUEST 400
+#define HTTP_CODE_UNAUTHORIZED 401
+#define HTTP_CODE_PAYMENT_REQUIRED 402
+#define HTTP_CODE_FORBIDDEN 403
+#define HTTP_CODE_NOT_FOUND 404
+#define HTTP_CODE_METHOD_NOT_ALLOWED 405
+#define HTTP_CODE_NOT_ACCEPTABLE 406
+#define HTTP_CODE_PROXY_AUTHENTICATION_REQUIRED 407
+#define HTTP_CODE_REQUEST_TIMEOUT 408
+#define HTTP_CODE_CONFLICT 409
+#define HTTP_CODE_GONE 410
+#define HTTP_CODE_LENGTH_REQUIRED 411
+#define HTTP_CODE_PRECONDITION_REQUIRED 412
+#define HTTP_CODE_REQUEST_ENTITY_TOO_LARGE 413
+#define HTTP_CODE_REQUEST_URI_TOO_LONG 414
+#define HTTP_CODE_UNSUPPORTED_MEDIA_TYPE 415
+#define HTTP_CODE_REQUESTED_RANGE_NOT_SATISFIABLE 416
+#define HTTP_CODE_EXPECTATION_FAILED 417
+#define HTTP_CODE_UNPROCESSABLE_ENTITY 422
+#define HTTP_CODE_LOCKED 423
+#define HTTP_CODE_FAILED_DEPENDENCY 424
+#define HTTP_CODE_UNORDERED_COLLECTION 425
+#define HTTP_CODE_UPGRADE_REQUIRED 426
+#define HTTP_CODE_RETRY_WITH 449
+#define HTTP_CODE_INTERNAL_SERVER_ERROR 500
+#define HTTP_CODE_NOT_IMPLEMENTED 501
+#define HTTP_CODE_BAD_GATEWAY 502
+#define HTTP_CODE_SERVICE_UNAVAILABLE 503
+#define HTTP_CODE_GATEWAY_TIMEOUT 504
+#define HTTP_CODE_HTTP_VERSION_NOT_SUPPORTED 505
+#define HTTP_CODE_VARIANT_ALSO_NEGOTIATES 506
+#define HTTP_CODE_INSUFFICIENT_STORAGE 507
+#define HTTP_CODE_BANDWIDTH_LIMIT_EXCEEDED 509
+#define HTTP_CODE_NOT_EXTENDED 510
+
+#define HTTP_CODE_FAKE_DISCONNECTED 0
+#define HTTP_CODE_FAKE_ERROR 1
+
+namespace http
+{
+ enum method
+ {
+ get,
+ post
+ };
+
+ struct response
+ {
+ response() : code(0), error_number(0) {}
+ int code;
+ unsigned int error_number;
+ std::string error_text;
+ std::map< std::string, std::string > headers;
+ std::string data;
+ };
+}
diff --git a/protocols/FacebookRM/src/json.cpp b/protocols/FacebookRM/src/json.cpp new file mode 100644 index 0000000000..e79c6966cd --- /dev/null +++ b/protocols/FacebookRM/src/json.cpp @@ -0,0 +1,544 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "common.h"
+#include "JSON_CAJUN/reader.h"
+#include "JSON_CAJUN/writer.h"
+#include "JSON_CAJUN/elements.h"
+
+int facebook_json_parser::parse_buddy_list( void* data, List::List< facebook_user >* buddy_list )
+{
+ using namespace json;
+
+ try
+ {
+ facebook_user* current = NULL;
+ std::string buddyData = static_cast< std::string* >( data )->substr( 9 );
+ std::istringstream sDocument( buddyData );
+ Object objDocument;
+ Reader::Read(objDocument, sDocument);
+
+ const Object& objRoot = objDocument;
+/* const Array& wasAvailableIDs = objRoot["payload"]["buddy_list"]["wasAvailableIDs"];
+
+ for ( Array::const_iterator itWasAvailable( wasAvailableIDs.Begin());
+ itWasAvailable != wasAvailableIDs.End(); ++itWasAvailable)
+ {
+ const Number& member = *itWasAvailable;
+ char was_id[32];
+ lltoa( member.Value(), was_id, 10 );
+
+ current = buddy_list->find( std::string( was_id ));
+ if ( current != NULL )
+ current->status_id = ID_STATUS_OFFLINE;
+ }*/ // Facebook removed support for "wasAvailableIDs"
+
+ // Set all contacts in map to offline
+ for ( List::Item< facebook_user >* i = buddy_list->begin( ); i != NULL; i = i->next ) {
+ i->data->status_id = ID_STATUS_OFFLINE;
+ }
+
+ // Find mobile friends
+ if (DBGetContactSettingByte(NULL,proto->m_szModuleName,FACEBOOK_KEY_LOAD_MOBILE, DEFAULT_LOAD_MOBILE)) {
+ const Array& mobileFriends = objRoot["payload"]["buddy_list"]["mobile_friends"];
+
+ for ( Array::const_iterator buddy( mobileFriends.Begin()); buddy != mobileFriends.End(); ++buddy) {
+ const Number& member = *buddy;
+ char was_id[32];
+ lltoa( member.Value(), was_id, 10 );
+
+ std::string id = was_id;
+ if (!id.empty()) {
+ current = buddy_list->find( id );
+
+ if ( current == NULL) {
+ buddy_list->insert( std::make_pair( id, new facebook_user( )) );
+ current = buddy_list->find( id );
+ current->user_id = id;
+ }
+
+ current->status_id = ID_STATUS_ONTHEPHONE;
+ }
+ }
+ }
+
+ const Object& nowAvailableList = objRoot["payload"]["buddy_list"]["nowAvailableList"];
+ // Find now awailable contacts
+ for (Object::const_iterator itAvailable(nowAvailableList.Begin());
+ itAvailable != nowAvailableList.End(); ++itAvailable)
+ {
+ const Object::Member& member = *itAvailable;
+ const Object& objMember = member.element;
+ const Boolean idle = objMember["i"]; // In new version of Facebook "i" means "offline"
+
+ current = buddy_list->find( member.name );
+ if ( current == NULL) {
+ if (idle) continue; // Just little optimalization
+
+ buddy_list->insert( std::make_pair( member.name, new facebook_user( )) );
+ current = buddy_list->find( member.name );
+ current->user_id = current->real_name = member.name;
+ }
+
+ current->status_id = (idle ? ID_STATUS_OFFLINE : ID_STATUS_ONLINE);
+ }
+
+ const Object& userInfosList = objRoot["payload"]["buddy_list"]["userInfos"];
+ // Get aditional informations about contacts (if available)
+ for (Object::const_iterator itUserInfo(userInfosList.Begin());
+ itUserInfo != userInfosList.End(); ++itUserInfo)
+ {
+ const Object::Member& member = *itUserInfo;
+
+ current = buddy_list->find( member.name );
+ if ( current == NULL )
+ continue;
+
+ const Object& objMember = member.element;
+ const String& realName = objMember["name"];
+ const String& imageUrl = objMember["thumbSrc"];
+
+ current->real_name = utils::text::slashu_to_utf8(
+ utils::text::special_expressions_decode( realName.Value( )) );
+ current->image_url = utils::text::slashu_to_utf8(
+ utils::text::special_expressions_decode( imageUrl.Value( )) );
+ }
+ }
+ catch (Reader::ParseException& e)
+ {
+ proto->Log( "!!!!! Caught json::ParseException: %s", e.what());
+ proto->Log( " Line/offset: %d/%d", e.m_locTokenBegin.m_nLine + 1, e.m_locTokenBegin.m_nLineOffset + 1 );
+ }
+ catch (const Exception& e)
+ {
+ proto->Log( "!!!!! Caught json::Exception: %s", e.what());
+ }
+ catch (const std::exception& e)
+ {
+ proto->Log( "!!!!! Caught std::exception: %s", e.what());
+ }
+
+ return EXIT_SUCCESS;
+}
+
+int facebook_json_parser::parse_friends( void* data, std::map< std::string, facebook_user* >* friends )
+{
+ using namespace json;
+
+ try
+ {
+ std::string buddyData = static_cast< std::string* >( data )->substr( 9 );
+ std::istringstream sDocument( buddyData );
+ Object objDocument;
+ Reader::Read(objDocument, sDocument);
+
+ const Object& objRoot = objDocument;
+ const Object& payload = objRoot["payload"];
+
+ for ( Object::const_iterator payload_item( payload.Begin()); payload_item != payload.End(); ++payload_item)
+ {
+ const Object::Member& member = *payload_item;
+
+ const Object& objMember = member.element;
+
+ const String& realName = objMember["name"];
+ const String& imageUrl = objMember["thumbSrc"];
+ //const String& vanity = objMember["vanity"];
+ const Number& gender = objMember["gender"];
+
+ facebook_user *fbu = new facebook_user();
+
+ fbu->user_id = member.name;
+ fbu->real_name = utils::text::slashu_to_utf8(
+ utils::text::special_expressions_decode( realName.Value()) );
+ fbu->image_url = utils::text::slashu_to_utf8(
+ utils::text::special_expressions_decode( imageUrl.Value()) );
+
+ if (gender.Value() == 1) {
+ fbu->gender = 70; // female
+ } else if (gender.Value() == 2) {
+ fbu->gender = 77; // male
+ }
+
+ friends->insert( std::make_pair( member.name, fbu ));
+ }
+ }
+ catch (Reader::ParseException& e)
+ {
+ proto->Log( "!!!!! Caught json::ParseException: %s", e.what());
+ proto->Log( " Line/offset: %d/%d", e.m_locTokenBegin.m_nLine + 1, e.m_locTokenBegin.m_nLineOffset + 1 );
+ }
+ catch (const Exception& e)
+ {
+ proto->Log( "!!!!! Caught json::Exception: %s", e.what());
+ }
+ catch (const std::exception& e)
+ {
+ proto->Log( "!!!!! Caught std::exception: %s", e.what());
+ }
+
+ return EXIT_SUCCESS;
+}
+
+
+int facebook_json_parser::parse_notifications( void *data, std::vector< facebook_notification* > *notifications )
+{
+ using namespace json;
+
+ try
+ {
+ std::string notificationsData = static_cast< std::string* >( data )->substr( 9 );
+ std::istringstream sDocument( notificationsData );
+ Object objDocument;
+ Reader::Read(objDocument, sDocument);
+
+ const Object& objRoot = objDocument;
+ const Object& payload = objRoot["payload"]["notifications"];
+
+ for ( Object::const_iterator payload_item( payload.Begin()); payload_item != payload.End(); ++payload_item)
+ {
+ const Object::Member& member = *payload_item;
+
+ const Object& objMember = member.element;
+
+ const String& content = objMember["markup"];
+ const Number& unread = objMember["unread"];
+
+ if (unread.Value() == 0) // ignore old notifications
+ continue;
+
+ std::string text = utils::text::slashu_to_utf8(
+ utils::text::special_expressions_decode( content.Value()) );
+
+ facebook_notification* notification = new facebook_notification( );
+
+ notification->text = utils::text::remove_html( utils::text::source_get_value(&text, 1, "<abbr"));
+ notification->link = utils::text::source_get_value(&text, 3, "<a ", "href=\"", "\"");
+
+ notifications->push_back( notification );
+ }
+
+ }
+ catch (Reader::ParseException& e)
+ {
+ proto->Log( "!!!!! Caught json::ParseException: %s", e.what());
+ proto->Log( " Line/offset: %d/%d", e.m_locTokenBegin.m_nLine + 1, e.m_locTokenBegin.m_nLineOffset + 1 );
+ }
+ catch (const Exception& e)
+ {
+ proto->Log( "!!!!! Caught json::Exception: %s", e.what());
+ }
+ catch (const std::exception& e)
+ {
+ proto->Log( "!!!!! Caught std::exception: %s", e.what());
+ }
+
+ return EXIT_SUCCESS;
+}
+
+int facebook_json_parser::parse_messages( void* data, std::vector< facebook_message* >* messages, std::vector< facebook_notification* >* notifications )
+{
+ using namespace json;
+
+ try
+ {
+ std::string messageData = static_cast< std::string* >( data )->substr( 9 );
+ std::istringstream sDocument( messageData );
+ Object objDocument;
+ Reader::Read(objDocument, sDocument);
+
+ const Object& objRoot = objDocument;
+ const Array& messagesArray = objRoot["ms"];
+
+ std::string last_msg = "";
+
+ for (Array::const_iterator itMessage(messagesArray.Begin());
+ itMessage != messagesArray.End(); ++itMessage)
+ {
+ const Object& objMember = *itMessage;
+
+ const String& type = objMember["type"];
+
+ if ( type.Value( ) == "msg" || type.Value() == "offline_msg" ) // direct message
+ {
+ const Number& from = objMember["from"];
+ char was_id[32];
+ lltoa( from.Value(), was_id, 10 );
+
+ const Object& messageContent = objMember["msg"];
+ const String& text = messageContent["text"];
+ //"tab_type":"friend", objMember["tab_type"]
+
+ const Number& time_sent = messageContent["time"];
+// proto->Log("????? Checking time %15.2f > %15.2f", time_sent.Value(), proto->facy.last_message_time_);
+
+ if ((messageContent.Find("truncated") != messageContent.End())
+ && (((const Number &)messageContent["truncated"]).Value() == 1)) {
+ // If we got truncated message, we can ignore it, because we should get it again as "messaging" type
+ std::string msg = "????? We got truncated message so we ignore it\n";
+ msg += utils::text::special_expressions_decode(utils::text::slashu_to_utf8(text.Value()));
+ proto->Log(msg.c_str());
+ } else if (last_msg != text.Value()) {
+ last_msg = text.Value();
+ facebook_message* message = new facebook_message( );
+ message->message_text = utils::text::special_expressions_decode(
+ utils::text::slashu_to_utf8( text.Value( )) );
+ message->time = utils::time::fix_timestamp( time_sent.Value());
+ message->user_id = was_id;
+
+ messages->push_back( message );
+ } else {
+ std::string msg = "????? Got duplicit message?\n";
+ msg += utils::text::special_expressions_decode(utils::text::slashu_to_utf8(text.Value()));
+ proto->Log(msg.c_str());
+ }
+ }
+ else if ( type.Value( ) == "messaging" ) // inbox message (multiuser or direct)
+ {
+ const String& type = objMember["event"];
+
+ if (type.Value() == "deliver") {
+ const Object& messageContent = objMember["message"];
+
+ const Number& from = messageContent["sender_fbid"];
+ char was_id[32];
+ lltoa( from.Value(), was_id, 10 );
+
+
+ // Ignore if message is from self user
+ if (was_id == proto->facy.self_.user_id)
+ continue;
+
+
+ const String& text = messageContent["body"];
+ //std::string tid = ((const String&)messageContent["tid"]).Value();
+
+ const String& sender_name = messageContent["sender_name"];
+
+ std::string row = ((const String &)objMember["thread_row"]).Value();
+
+ const Number& time_sent = messageContent["timestamp"];
+ //proto->Log("????? Checking time %15.2f > %15.2f", time_sent.Value(), proto->facy.last_message_time_);
+
+ if (last_msg != text.Value()) {
+ last_msg = text.Value();
+
+ facebook_message* message = new facebook_message( );
+ message->message_text = utils::text::special_expressions_decode(
+ utils::text::slashu_to_utf8( text.Value( )) );
+
+ message->sender_name = utils::text::special_expressions_decode(
+ utils::text::slashu_to_utf8( sender_name.Value( )) );
+
+ message->time = utils::time::fix_timestamp( time_sent.Value());
+ message->user_id = was_id; // TODO: Check if we have contact with this ID in friendlist and then do something different?
+
+ if (row.find("uiSplitPic",0) != std::string::npos) {
+ // This is multiuser message
+
+ std::string authors = utils::text::special_expressions_decode(
+ utils::text::slashu_to_utf8( row ));
+ authors = utils::text::source_get_value(&authors, 2, "<strong class=\"authors\">", "<");
+
+ const String& to_id = messageContent["tid"];
+
+ std::string popup_text = message->sender_name;
+ popup_text += ": ";
+ popup_text += message->message_text;
+
+ std::string title = Translate("Multichat");
+ title += ": ";
+ title += authors;
+
+ std::string url = "/?action=read&sk=inbox&page&query&tid=";
+ url += to_id.Value();
+
+ proto->Log(" Got multichat message");
+
+ TCHAR* szTitle = mir_a2t_cp(title.c_str(), CP_UTF8);
+ TCHAR* szText = mir_a2t_cp(popup_text.c_str(), CP_UTF8);
+ TCHAR* szUrl = mir_a2t_cp(url.c_str(), CP_UTF8);
+ proto->NotifyEvent(szTitle,szText,NULL,FACEBOOK_EVENT_OTHER, szUrl);
+ mir_free(szTitle);
+ mir_free(szText);
+
+ } else {
+ messages->push_back( message );
+ }
+ } else {
+ std::string msg = "????? Got duplicit inbox message?\n";
+ msg += utils::text::special_expressions_decode(utils::text::slashu_to_utf8(text.Value()));
+ proto->Log(msg.c_str());
+ }
+ }
+ }
+ else if ( type.Value( ) == "group_msg" ) // chat message
+ {
+ if (!DBGetContactSettingByte(NULL,proto->m_szModuleName,FACEBOOK_KEY_ENABLE_GROUPCHATS, DEFAULT_ENABLE_GROUPCHATS))
+ continue;
+
+ const String& from_name = objMember["from_name"];
+
+ const Number& to = objMember["to"];
+ char group_id[32];
+ lltoa( to.Value(), group_id, 10 );
+
+ const Number& from = objMember["from"];
+ char was_id[32];
+ lltoa( from.Value(), was_id, 10 );
+
+ const Object& messageContent = objMember["msg"];
+ const String& text = messageContent["text"];
+
+ std::string msg = utils::text::special_expressions_decode(
+ utils::text::slashu_to_utf8( text.Value( )) );
+
+ std::string name = utils::text::special_expressions_decode(
+ utils::text::slashu_to_utf8( from_name.Value( )) );
+
+ // Add contact into chat, if isn't there already
+ if (!proto->IsChatContact(group_id, was_id))
+ proto->AddChatContact(group_id, was_id, name.c_str());
+
+ const Number& time_sent = messageContent["time"];
+ DWORD timestamp = utils::time::fix_timestamp( time_sent.Value());
+
+ // Add message into chat
+ proto->UpdateChat(group_id, was_id, name.c_str(), msg.c_str(), timestamp);
+ }
+ else if ( type.Value( ) == "thread_msg" ) // multiuser message
+ {
+ const String& from_name = objMember["from_name"];
+ const String& to_name = objMember["to_name"]["__html"];
+ const String& to_id = objMember["to"];
+
+ const Number& from = objMember["from"];
+ char was_id[32];
+ lltoa( from.Value(), was_id, 10 );
+
+ // Ignore if message is from self user
+ if (was_id == proto->facy.self_.user_id)
+ continue;
+
+ const Object& messageContent = objMember["msg"];
+ const String& text = messageContent["text"];
+
+
+ last_msg = text.Value();
+
+
+ std::string popup_text = utils::text::special_expressions_decode(
+ utils::text::slashu_to_utf8( from_name.Value( )) );
+ popup_text += ": ";
+ popup_text += utils::text::special_expressions_decode(
+ utils::text::slashu_to_utf8( text.Value( )) );
+
+ std::string title = Translate("Multichat");
+ title += ": ";
+ title += utils::text::special_expressions_decode(
+ utils::text::slashu_to_utf8( to_name.Value( )) );
+
+ std::string url = "/?action=read&sk=inbox&page&query&tid=";
+ url += to_id.Value();
+
+ proto->Log(" Got multichat message");
+
+ TCHAR* szTitle = mir_a2t_cp(title.c_str(), CP_UTF8);
+ TCHAR* szText = mir_a2t_cp(popup_text.c_str(), CP_UTF8);
+ TCHAR* szUrl = mir_a2t_cp(url.c_str(), CP_UTF8);
+ proto->NotifyEvent(szTitle,szText,NULL,FACEBOOK_EVENT_OTHER, szUrl);
+ mir_free(szTitle);
+ mir_free(szText);
+ }
+ else if ( type.Value( ) == "app_msg" ) // event notification
+ {
+ if (!DBGetContactSettingByte(NULL, proto->m_szModuleName, FACEBOOK_KEY_EVENT_NOTIFICATIONS_ENABLE, DEFAULT_EVENT_NOTIFICATIONS_ENABLE))
+ continue;
+
+ const String& text = objMember["response"]["payload"]["title"];
+ const String& link = objMember["response"]["payload"]["link"];
+ // TODO RM: include additional text of notification if exits? (e.g. comment text)
+ //const String& text2 = objMember["response"]["payload"]["alert"]["text"];
+
+ const Number& time_sent = objMember["response"]["payload"]["alert"]["time_sent"];
+ if (time_sent.Value() > proto->facy.last_notification_time_) // Check agains duplicit notifications
+ {
+ proto->facy.last_notification_time_ = time_sent.Value();
+
+ facebook_notification* notification = new facebook_notification( );
+ notification->text = utils::text::remove_html(
+ utils::text::special_expressions_decode(
+ utils::text::slashu_to_utf8( text.Value( )) ));
+
+ notification->link = utils::text::special_expressions_decode( link.Value( ));
+
+ notifications->push_back( notification );
+ }
+ }
+ else if ( type.Value( ) == "typ" ) // chat typing notification
+ {
+ const Number& from = objMember["from"];
+ char user_id[32];
+ lltoa( from.Value(), user_id, 10 );
+
+ facebook_user fbu;
+ fbu.user_id = user_id;
+
+ HANDLE hContact = proto->AddToContactList(&fbu, FACEBOOK_CONTACT_FRIEND);
+
+ if ( DBGetContactSettingWord(hContact,proto->m_szModuleName,"Status", 0) == ID_STATUS_OFFLINE )
+ DBWriteContactSettingWord(hContact,proto->m_szModuleName,"Status",ID_STATUS_ONLINE);
+
+ const Number& state = objMember["st"];
+ if (state.Value() == 1)
+ CallService(MS_PROTO_CONTACTISTYPING, (WPARAM)hContact, (LPARAM)60);
+ else
+ CallService(MS_PROTO_CONTACTISTYPING, (WPARAM)hContact, (LPARAM)PROTOTYPE_CONTACTTYPING_OFF);
+ }
+ else if ( type.Value( ) == "privacy_changed")
+ {
+ const String& event_type = objMember["event"];
+ const Object& event_data = objMember["data"];
+
+ if ( event_type.Value( ) == "visibility_update" )
+ { // change of chat status
+ const Boolean visibility = event_data["visibility"];
+ proto->Log(" Requested chat switch to %s", visibility ? "Online" : "Offline");
+ proto->SetStatus( visibility ? ID_STATUS_ONLINE : ID_STATUS_INVISIBLE );
+ }
+ }
+ else
+ continue;
+ }
+ }
+ catch (Reader::ParseException& e)
+ {
+ proto->Log( "!!!!! Caught json::ParseException: %s", e.what());
+ proto->Log( " Line/offset: %d/%d", e.m_locTokenBegin.m_nLine + 1, e.m_locTokenBegin.m_nLineOffset + 1 );
+ }
+ catch (const Exception& e)
+ {
+ proto->Log ( "!!!!! Caught json::Exception: %s", e.what());
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/protocols/FacebookRM/src/json.h b/protocols/FacebookRM/src/json.h new file mode 100644 index 0000000000..c83e1ef4f6 --- /dev/null +++ b/protocols/FacebookRM/src/json.h @@ -0,0 +1,42 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+// Parser front-end
+
+#define lltoa _i64toa
+
+class facebook_json_parser
+{
+public:
+ FacebookProto* proto;
+ int parse_buddy_list( void*, List::List< facebook_user >* );
+ int parse_friends( void*, std::map< std::string, facebook_user* >* );
+ int parse_notifications( void*, std::vector< facebook_notification* >* );
+ int parse_messages( void*, std::vector< facebook_message* >*, std::vector< facebook_notification* >* );
+
+ facebook_json_parser( FacebookProto* proto )
+ {
+ this->proto = proto;
+ }
+};
diff --git a/protocols/FacebookRM/src/list.hpp b/protocols/FacebookRM/src/list.hpp new file mode 100644 index 0000000000..7a26aec1ec --- /dev/null +++ b/protocols/FacebookRM/src/list.hpp @@ -0,0 +1,195 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+namespace List
+{
+ template< typename T > class Item
+ {
+ public:
+ std::string key;
+ T* data;
+ Item< T >* prev;
+ Item< T >* next;
+
+ Item( )
+ {
+ this->data = NULL;
+ this->prev = NULL;
+ this->next = NULL;
+ }
+
+ ~Item( )
+ {
+ delete this->data;
+ }
+ };
+
+ template< typename T > class List
+ {
+ private:
+ Item< T >* first;
+ Item< T >* last;
+ unsigned int count;
+
+ public:
+ List( )
+ {
+ this->first = this->last = NULL;
+ this->count = 0;
+ }
+
+ ~List( )
+ {
+ this->clear( );
+ }
+
+ Item< T >* begin( )
+ {
+ return first;
+ }
+
+ Item< T >* end( )
+ {
+ return last;
+ }
+
+ unsigned int size( )
+ {
+ return count;
+ }
+
+ bool empty( )
+ {
+ return ( this->first == NULL );
+ }
+
+ void insert( Item< T >* item )
+ {
+ if ( this->empty( ))
+ {
+ this->first = this->last = item;
+ this->count = 1;
+ } else { // TODO: key sorting/comparation
+ item->next = this->first;
+ this->first->prev = item;
+ this->first = item;
+ this->count++;
+ }
+ }
+
+ void insert( std::pair< std::string, T* > item )
+ {
+ Item<T>* ins = new Item<T>;
+ ins->key = item.first;
+ ins->data = item.second;
+ this->insert( ins );
+ }
+ void erase( std::string key )
+ {
+ Item< T >* help = this->first;
+ while ( help != NULL )
+ {
+ if ( help->key.compare( key ) != 0 )
+ help = help->next;
+ else
+ {
+ if ( help == this->first )
+ {
+ this->first = help->next;
+ if ( this->first != NULL )
+ this->first->prev = NULL;
+ else
+ this->last = NULL;
+ }
+ else if ( help == this->last )
+ {
+ this->last = help->prev;
+ if ( this->last != NULL )
+ this->last->next = NULL;
+ else
+ this->first = NULL;
+ }
+ else
+ {
+ help->prev->next = help->next;
+ help->next->prev = help->prev;
+ }
+ if (help != NULL)
+ {
+ this->count--;
+ delete help;
+ }
+ break;
+ }
+ }
+ }
+
+ void erase( Item< T >* item )
+ {
+ if (item != NULL)
+ erase( item->key );
+ }
+
+ T* find( std::string key )
+ {
+ Item< T >* help = this->begin( );
+ while ( help != NULL )
+ {
+ if ( help->key.compare( key ) != 0 )
+ help = help->next;
+ else
+ return help->data;
+ }
+ return NULL;
+ }
+
+ T* at( const unsigned int item )
+ {
+ if (item >= this->count)
+ return NULL;
+ Item< T >* help = this->begin( );
+ for ( unsigned int i = 0; i < item; i++ )
+ help = help->next;
+ return help->item;
+ }
+
+ T* operator[]( const unsigned int item )
+ {
+ return at( item );
+ }
+
+ void clear( )
+ {
+ Item< T >* help;
+ while ( this->first != NULL )
+ {
+ help = this->first;
+ this->first = this->first->next;
+ delete help;
+ }
+ this->last = NULL;
+ this->count = 0;
+ }
+ };
+};
diff --git a/protocols/FacebookRM/src/main.cpp b/protocols/FacebookRM/src/main.cpp new file mode 100644 index 0000000000..5928b85499 --- /dev/null +++ b/protocols/FacebookRM/src/main.cpp @@ -0,0 +1,148 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "common.h"
+
+// TODO: Make following as "globals" structure?
+
+CLIST_INTERFACE* pcli;
+int hLangpack;
+
+HINSTANCE g_hInstance;
+std::string g_strUserAgent;
+DWORD g_mirandaVersion;
+
+PLUGININFOEX pluginInfo = {
+ sizeof(PLUGININFOEX),
+ "Facebook Protocol RM",
+ __VERSION_DWORD,
+ "Provides basic support for Facebook Chat protocol.",
+ "Michal Zelinka, Robert Posel",
+ "robyer@seznam.cz",
+ "(c) 2009-11 Michal Zelinka, 2011-12 Robert Posel",
+ "http://miranda-ng.org/",
+ UNICODE_AWARE,
+ // {8432B009-FF32-4727-AAE6-A9035038FD58}
+ { 0x8432b009, 0xff32, 0x4727, { 0xaa, 0xe6, 0xa9, 0x3, 0x50, 0x38, 0xfd, 0x58 } }
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// Protocol instances
+static int compare_protos(const FacebookProto *p1, const FacebookProto *p2)
+{
+ return _tcscmp(p1->m_tszUserName, p2->m_tszUserName);
+}
+
+OBJLIST<FacebookProto> g_Instances(1, compare_protos);
+
+DWORD WINAPI DllMain(HINSTANCE hInstance,DWORD,LPVOID)
+{
+ g_hInstance = hInstance;
+ return TRUE;
+}
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ g_mirandaVersion = mirandaVersion;
+ return &pluginInfo;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Interface information
+
+extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = {MIID_PROTOCOL, MIID_LAST};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Load
+
+static PROTO_INTERFACE* protoInit(const char *proto_name,const TCHAR *username )
+{
+ FacebookProto *proto = new FacebookProto(proto_name,username);
+ g_Instances.insert(proto);
+ return proto;
+}
+
+static int protoUninit(PROTO_INTERFACE* proto)
+{
+ g_Instances.remove(( FacebookProto* )proto);
+ return EXIT_SUCCESS;
+}
+
+static HANDLE g_hEvents[1];
+
+extern "C" int __declspec(dllexport) Load(void)
+{
+
+ mir_getLP(&pluginInfo);
+
+ pcli = reinterpret_cast<CLIST_INTERFACE*>( CallService(
+ MS_CLIST_RETRIEVE_INTERFACE,0,reinterpret_cast<LPARAM>(g_hInstance)));
+
+ PROTOCOLDESCRIPTOR pd = { 0 };
+ pd.cbSize = sizeof(pd);
+ pd.szName = "Facebook";
+ pd.type = PROTOTYPE_PROTOCOL;
+ pd.fnInit = protoInit;
+ pd.fnUninit = protoUninit;
+ CallService(MS_PROTO_REGISTERMODULE,0,reinterpret_cast<LPARAM>(&pd));
+
+
+ InitIcons();
+ InitContactMenus();
+
+ // Init native User-Agent
+ {
+ std::stringstream agent;
+// DWORD mir_ver = ( DWORD )CallService( MS_SYSTEM_GETVERSION, NULL, NULL );
+ agent << "MirandaNG/";
+ agent << (( g_mirandaVersion >> 24) & 0xFF);
+ agent << ".";
+ agent << (( g_mirandaVersion >> 16) & 0xFF);
+ agent << ".";
+ agent << (( g_mirandaVersion >> 8) & 0xFF);
+ agent << ".";
+ agent << (( g_mirandaVersion ) & 0xFF);
+ #ifdef _WIN64
+ agent << " Facebook Protocol RM x64/";
+ #else
+ agent << " Facebook Protocol RM/";
+ #endif
+ agent << __VERSION_STRING;
+ g_strUserAgent = agent.str( );
+ }
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Unload
+
+extern "C" int __declspec(dllexport) Unload(void)
+{
+ UninitContactMenus();
+ for(size_t i=0; i<SIZEOF(g_hEvents); i++)
+ UnhookEvent(g_hEvents[i]);
+
+ g_Instances.destroy();
+
+ return 0;
+}
diff --git a/protocols/FacebookRM/src/messages.cpp b/protocols/FacebookRM/src/messages.cpp new file mode 100644 index 0000000000..8b27b4721f --- /dev/null +++ b/protocols/FacebookRM/src/messages.cpp @@ -0,0 +1,190 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "common.h"
+
+int FacebookProto::RecvMsg(HANDLE hContact, PROTORECVEVENT *pre)
+{
+ DBVARIANT dbv;
+
+ if ( !DBGetContactSettingString(hContact,m_szModuleName,FACEBOOK_KEY_ID,&dbv))
+ {
+ ForkThread( &FacebookProto::MessagingWorker, this, new send_messaging(dbv.pszVal, FACEBOOK_RECV_MESSAGE ));
+ DBFreeVariant(&dbv);
+ }
+
+ CallService(MS_PROTO_CONTACTISTYPING, (WPARAM)hContact, (LPARAM)PROTOTYPE_CONTACTTYPING_OFF);
+
+ return Proto_RecvMessage(hContact, pre);
+}
+
+void FacebookProto::SendMsgWorker(void *p)
+{
+ if(p == NULL)
+ return;
+
+// ScopedLock s( facy.send_message_lock_, 500 );
+
+ send_direct *data = static_cast<send_direct*>(p);
+
+ DBVARIANT dbv;
+
+ if ( !isOnline( ))
+ {
+ ProtoBroadcastAck(m_szModuleName, data->hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, data->msgid, (LPARAM)Translate("You cannot send messages when you are offline."));
+ }
+ else if ( !DBGetContactSettingString(data->hContact,m_szModuleName,FACEBOOK_KEY_ID,&dbv))
+ {
+ int retries = 5;
+ std::string error_text = "";
+ bool result = false;
+ while (!result && retries > 0) {
+ result = facy.send_message(dbv.pszVal, data->msg, &error_text, retries % 2 == 0 );
+ retries--;
+ }
+ if (result) {
+ ProtoBroadcastAck(m_szModuleName,data->hContact,ACKTYPE_MESSAGE,ACKRESULT_SUCCESS, data->msgid,0);
+ MessagingWorker( new send_messaging(dbv.pszVal, FACEBOOK_SEND_MESSAGE ));
+ } else {
+ char *err = mir_utf8decodeA(error_text.c_str());
+ ProtoBroadcastAck(m_szModuleName,data->hContact,ACKTYPE_MESSAGE,ACKRESULT_FAILED, data->msgid,(LPARAM)err);
+ }
+ DBFreeVariant(&dbv);
+ }
+
+ delete data;
+}
+
+void FacebookProto::SendChatMsgWorker(void *p)
+{
+ if(p == NULL)
+ return;
+
+ send_chat *data = static_cast<send_chat*>(p);
+ std::string err_message = "";
+
+ HANDLE hContact = ChatIDToHContact(data->chat_id);
+ if (hContact) {
+ std::string tid;
+ DBVARIANT dbv;
+ if (!DBGetContactSettingString(hContact, m_szModuleName, FACEBOOK_KEY_TID, &dbv)) {
+ tid = dbv.pszVal;
+ DBFreeVariant(&dbv);
+ } else {
+ std::string post_data = "threads[group_ids][0]=" + data->chat_id;
+ post_data += "&fb_dtsg=" + (facy.dtsg_.length() ? facy.dtsg_ : "0");
+ post_data += "&__user=" + facy.self_.user_id;
+ post_data += "&phstamp=0";
+
+ http::response resp = facy.flap( FACEBOOK_REQUEST_THREAD_INFO, &post_data );
+ facy.validate_response(&resp);
+
+ tid = utils::text::source_get_value(&resp.data, 2, "\"thread_id\":\"", "\"");
+ DBWriteContactSettingString(hContact, m_szModuleName, FACEBOOK_KEY_TID, tid.c_str());
+ Log(" Got thread info: %s = %s", data->chat_id.c_str(), tid.c_str());
+ }
+
+ if (!tid.empty())
+ facy.send_message(tid, data->msg, &err_message, false, true );
+ }
+
+ delete data;
+}
+
+int FacebookProto::SendMsg(HANDLE hContact, int flags, const char *msg)
+{
+ // TODO: msg comes as Unicode (retyped wchar_t*), why should we convert it as ANSI to UTF-8? o_O
+ if ( flags & PREF_UNICODE )
+ msg = mir_utf8encode(msg);
+
+ facy.msgid_ = (facy.msgid_ % 1024)+1;
+ ForkThread( &FacebookProto::SendMsgWorker, this,new send_direct(hContact,msg,(HANDLE)facy.msgid_));
+ return facy.msgid_;
+}
+
+int FacebookProto::UserIsTyping(HANDLE hContact,int type)
+{
+ if (hContact && isOnline())
+ ForkThread(&FacebookProto::SendTypingWorker, this,new send_typing(hContact,type));
+
+ return 0;
+}
+
+void FacebookProto::SendTypingWorker(void *p)
+{
+ if(p == NULL)
+ return;
+
+ send_typing *typing = static_cast<send_typing*>(p);
+
+ // Dont send typing notifications to contacts, that are offline or not friends
+ if ( DBGetContactSettingWord(typing->hContact,m_szModuleName,"Status", 0) == ID_STATUS_OFFLINE
+ || DBGetContactSettingByte(typing->hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, 0) != FACEBOOK_CONTACT_FRIEND)
+ return;
+
+ // TODO RM: maybe better send typing optimalization
+ facy.is_typing_ = (typing->status == PROTOTYPE_SELFTYPING_ON);
+ SleepEx( 2000, true );
+
+ if ( !facy.is_typing_ == (typing->status == PROTOTYPE_SELFTYPING_ON))
+ {
+ delete typing;
+ return;
+ }
+
+ DBVARIANT dbv;
+ if ( !DBGetContactSettingString(typing->hContact,m_szModuleName,FACEBOOK_KEY_ID,&dbv))
+ {
+ std::string data = "typ=";
+ data += ( typing->status == PROTOTYPE_SELFTYPING_ON ) ? "1" : "0"; // PROTOTYPE_SELFTYPING_OFF
+ data += "&to=";
+ data += dbv.pszVal;
+ data += "&source=mercury-chat";
+ data += "&fb_dtsg=" + facy.dtsg_;
+ data += "&post_form_id=";
+ data += ( facy.post_form_id_.length( )) ? facy.post_form_id_ : "0";
+ data += "&post_form_id_source=AsyncRequest&lsd=&phstamp=0&__user=";
+ data += facy.self_.user_id;
+
+ http::response resp = facy.flap( FACEBOOK_REQUEST_TYPING_SEND, &data );
+
+ DBFreeVariant(&dbv);
+ }
+
+ delete typing;
+}
+
+void FacebookProto::MessagingWorker(void *p)
+{
+ if (p == NULL)
+ return;
+
+ send_messaging *data = static_cast<send_messaging*>(p);
+
+ if (data->type == FACEBOOK_RECV_MESSAGE)
+ facy.chat_mark_read( data->user_id );
+
+// if ( DBGetContactSettingByte(NULL, m_szModuleName, FACEBOOK_KEY_CLOSE_WINDOWS_ENABLE, DEFAULT_CLOSE_WINDOWS_ENABLE ))
+// facy.close_chat( data->user_id );
+
+ delete data;
+}
diff --git a/protocols/FacebookRM/src/process.cpp b/protocols/FacebookRM/src/process.cpp new file mode 100644 index 0000000000..0acff0f36f --- /dev/null +++ b/protocols/FacebookRM/src/process.cpp @@ -0,0 +1,832 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "common.h"
+
+void FacebookProto::ProcessBuddyList( void* data )
+{
+ if ( data == NULL )
+ return;
+
+ ScopedLock s( facy.buddies_lock_ );
+
+ std::string* resp = (std::string*)data;
+
+ if ( isOffline())
+ goto exit;
+
+ LOG("***** Starting processing buddy list");
+
+ CODE_BLOCK_TRY
+
+ facebook_json_parser* p = new facebook_json_parser( this );
+ p->parse_buddy_list( data, &facy.buddies );
+ delete p;
+
+ for ( List::Item< facebook_user >* i = facy.buddies.begin( ); i != NULL; )
+ {
+ LOG(" Now %s: %s", (i->data->status_id == ID_STATUS_OFFLINE ? "offline" : "online"), i->data->real_name.c_str());
+
+ facebook_user* fbu;
+
+ if ( i->data->status_id == ID_STATUS_OFFLINE || i->data->deleted )
+ {
+ fbu = i->data;
+
+ if (fbu->handle)
+ DBWriteContactSettingWord(fbu->handle, m_szModuleName, "Status", ID_STATUS_OFFLINE);
+
+ std::string to_delete( i->key );
+ i = i->next;
+ facy.buddies.erase( to_delete );
+ } else {
+ fbu = i->data;
+ i = i->next;
+
+ if (!fbu->handle) { // just been added
+ fbu->handle = AddToContactList(fbu, FACEBOOK_CONTACT_FRIEND);
+
+ if (!fbu->real_name.empty()) {
+ DBWriteContactSettingUTF8String(fbu->handle,m_szModuleName,FACEBOOK_KEY_NAME,fbu->real_name.c_str());
+ DBWriteContactSettingUTF8String(fbu->handle,m_szModuleName,FACEBOOK_KEY_NICK,fbu->real_name.c_str());
+ }
+ }
+
+ if (DBGetContactSettingWord(fbu->handle,m_szModuleName,"Status", 0) != fbu->status_id ) {
+ DBWriteContactSettingWord(fbu->handle,m_szModuleName,"Status", fbu->status_id );
+ }
+
+ if (DBGetContactSettingByte(fbu->handle, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, 0) != FACEBOOK_CONTACT_FRIEND) {
+ DBWriteContactSettingByte(fbu->handle, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, FACEBOOK_CONTACT_FRIEND);
+ // TODO: remove that popup and use "Contact added you" event?
+ }
+
+ // Wasn't contact removed from "server-list" someday?
+ if ( DBGetContactSettingDword(fbu->handle, m_szModuleName, FACEBOOK_KEY_DELETED, 0)) {
+ DBDeleteContactSetting(fbu->handle, m_szModuleName, FACEBOOK_KEY_DELETED);
+
+ std::string url = FACEBOOK_URL_PROFILE + fbu->user_id;
+
+ TCHAR* szTitle = mir_a2t_cp(fbu->real_name.c_str(), CP_UTF8);
+ TCHAR* szUrl = mir_a2t_cp(url.c_str(), CP_UTF8);
+ NotifyEvent(szTitle, TranslateT("Contact is back on server-list."), fbu->handle, FACEBOOK_EVENT_OTHER, szUrl);
+ mir_free( szTitle );
+ // mir_free( szUrl ); // url is free'd in popup procedure
+ }
+
+ // Check avatar change
+ CheckAvatarChange(fbu->handle, fbu->image_url);
+ }
+ }
+
+ LOG("***** Buddy list processed");
+
+ CODE_BLOCK_CATCH
+
+ LOG("***** Error processing buddy list: %s", e.what());
+
+ CODE_BLOCK_END
+
+exit:
+ delete resp;
+}
+
+void FacebookProto::ProcessFriendList( void* data )
+{
+ if ( data == NULL )
+ return;
+
+ std::string* resp = (std::string*)data;
+
+ LOG("***** Starting processing friend list");
+
+ CODE_BLOCK_TRY
+
+ std::map<std::string, facebook_user*> friends;
+
+ facebook_json_parser* p = new facebook_json_parser( this );
+ p->parse_friends( data, &friends );
+ delete p;
+
+
+ // Check and update old contacts
+ for(HANDLE hContact = db_find_first();
+ hContact;
+ hContact = db_find_next(hContact))
+ {
+ if (!IsMyContact(hContact))
+ continue;
+
+ DBVARIANT dbv;
+ facebook_user *fbu;
+ if ( !DBGetContactSettingString(hContact,m_szModuleName,FACEBOOK_KEY_ID,&dbv)) {
+ std::string id = dbv.pszVal;
+ DBFreeVariant(&dbv);
+
+ std::map< std::string, facebook_user* >::iterator iter;
+
+ if ((iter = friends.find(id)) != friends.end()) {
+ // Found contact, update it and remove from map
+ fbu = iter->second;
+
+ DBVARIANT dbv;
+ bool update_required = true;
+
+ // TODO RM: remove, because contacts cant change it, so its only for "first run"
+ // - but what with contacts, that was added after logon?
+ // Update gender
+ if ( DBGetContactSettingByte(hContact, m_szModuleName, "Gender", 0) != fbu->gender )
+ DBWriteContactSettingByte(hContact, m_szModuleName, "Gender", fbu->gender);
+
+ // Update real name
+ if ( !DBGetContactSettingUTF8String(hContact, m_szModuleName, FACEBOOK_KEY_NAME, &dbv))
+ {
+ update_required = strcmp( dbv.pszVal, fbu->real_name.c_str()) != 0;
+ DBFreeVariant(&dbv);
+ }
+ if ( update_required )
+ {
+ DBWriteContactSettingUTF8String(hContact, m_szModuleName, FACEBOOK_KEY_NAME, fbu->real_name.c_str());
+ DBWriteContactSettingUTF8String(hContact, m_szModuleName, FACEBOOK_KEY_NICK, fbu->real_name.c_str());
+ }
+
+ if (DBGetContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, 0) != FACEBOOK_CONTACT_FRIEND) {
+ DBWriteContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, FACEBOOK_CONTACT_FRIEND);
+ // TODO: remove that popup and use "Contact added you" event?
+ }
+
+ // Wasn't contact removed from "server-list" someday?
+ if ( DBGetContactSettingDword(hContact, m_szModuleName, FACEBOOK_KEY_DELETED, 0)) {
+ DBDeleteContactSetting(hContact, m_szModuleName, FACEBOOK_KEY_DELETED);
+
+ std::string url = FACEBOOK_URL_PROFILE + fbu->user_id;
+
+ TCHAR* szTitle = mir_a2t_cp(fbu->real_name.c_str(), CP_UTF8);
+ TCHAR* szUrl = mir_a2t_cp(url.c_str(), CP_UTF8);
+ NotifyEvent(szTitle, TranslateT("Contact is back on server-list."), hContact, FACEBOOK_EVENT_OTHER, szUrl);
+ mir_free( szTitle );
+ // mir_free( szUrl ); // url is free'd in popup procedure
+ }
+
+ // Check avatar change
+ CheckAvatarChange(hContact, fbu->image_url);
+
+ delete fbu;
+ friends.erase(iter);
+ } else {
+ // Contact was removed from "server-list", notify it
+
+ // Wasnt we already been notified about this contact? And was this real friend?
+ if (!DBGetContactSettingDword(hContact, m_szModuleName, FACEBOOK_KEY_DELETED, 0)
+ && DBGetContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, 0) == FACEBOOK_CONTACT_FRIEND)
+ {
+
+ DBWriteContactSettingDword(hContact, m_szModuleName, FACEBOOK_KEY_DELETED, ::time(NULL));
+ DBWriteContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, FACEBOOK_CONTACT_NONE);
+
+ std::string contactname = id;
+ if ( !DBGetContactSettingUTF8String(hContact, m_szModuleName, FACEBOOK_KEY_NAME, &dbv)) {
+ contactname = dbv.pszVal;
+ DBFreeVariant(&dbv);
+ }
+
+ std::string url = FACEBOOK_URL_PROFILE + id;
+
+ TCHAR* szTitle = mir_a2t_cp(contactname.c_str(), CP_UTF8);
+ TCHAR* szUrl = mir_a2t_cp(url.c_str(), CP_UTF8);
+ NotifyEvent(szTitle, TranslateT("Contact is no longer on server-list."), hContact, FACEBOOK_EVENT_OTHER, szUrl);
+ mir_free( szTitle );
+ // mir_free( szUrl ); // url is free'd in popup procedure
+ }
+ }
+ }
+ }
+
+ // Check remain contacts in map and add it to contact list
+ for ( std::map< std::string, facebook_user* >::iterator iter = friends.begin(); iter != friends.end(); ++iter )
+ {
+ facebook_user *fbu = iter->second;
+
+ HANDLE hContact = AddToContactList(fbu, FACEBOOK_CONTACT_FRIEND, true); // This contact is surely new
+
+ DBWriteContactSettingByte(hContact, m_szModuleName, "Gender", fbu->gender );
+ DBWriteContactSettingUTF8String(hContact, m_szModuleName, FACEBOOK_KEY_NAME, fbu->real_name.c_str());
+ DBWriteContactSettingUTF8String(hContact, m_szModuleName, FACEBOOK_KEY_NICK, fbu->real_name.c_str());
+ DBWriteContactSettingString(hContact, m_szModuleName, FACEBOOK_KEY_AV_URL, fbu->image_url.c_str());
+// DBWriteContactSettingWord(hContact, m_szModuleName, "Status", ID_STATUS_OFFLINE );
+ }
+
+ LOG("***** Friend list processed");
+
+ CODE_BLOCK_CATCH
+
+ LOG("***** Error processing friend list: %s", e.what());
+
+ CODE_BLOCK_END
+
+ delete resp;
+}
+
+void FacebookProto::ProcessUnreadMessages( void* )
+{
+ facy.handle_entry( "messages" );
+
+ std::string get_data = "sk=inbox&query=is%3Aunread";
+
+ std::string data = "post_form_id=";
+ data += ( facy.post_form_id_.length( )) ? facy.post_form_id_ : "0";
+ data += "&fb_dtsg=" + facy.dtsg_;
+ data += "&post_form_id_source=AsyncRequest&lsd=&phstamp=";
+ data += utils::time::mili_timestamp();
+ data += "&__user=";
+ data += facy.self_.user_id;
+
+ // Get unread inbox threads
+ http::response resp = facy.flap( FACEBOOK_REQUEST_ASYNC, &data, &get_data );
+
+ // sk=inbox, sk=other
+
+ // Process result data
+ facy.validate_response(&resp);
+
+ if (resp.code != HTTP_CODE_OK) {
+ facy.handle_error( "messages" );
+ return;
+ }
+
+ std::string threadlist = utils::text::slashu_to_utf8(resp.data);
+
+ std::string::size_type pos = 0;
+
+ while ( ( pos = threadlist.find( "<li class=\\\"threadRow noDraft unread", pos )) != std::string::npos )
+ {
+ std::string::size_type pos2 = threadlist.find( "/li>", pos );
+ std::string thread_content = threadlist.substr( pos, pos2 - pos );
+
+ pos = pos2;
+
+ get_data = "sk=inbox&query=is%3Aunread&thread_query=is%3Aunread&action=read&tid=";
+ get_data += utils::text::source_get_value( &thread_content, 2, "id=\\\"", "\\\"" );
+
+ resp = facy.flap( FACEBOOK_REQUEST_ASYNC, &data, &get_data );
+ // TODO: move this to new thread...
+
+ facy.validate_response(&resp);
+
+ if (resp.code != HTTP_CODE_OK) {
+ LOG(" !! !! Error when getting messages list");
+ continue;
+ }
+
+ std::string messageslist = utils::text::slashu_to_utf8(resp.data);
+
+ std::string user_id = utils::text::source_get_value( &messageslist, 2, "single_thread_id\":", "," );
+ if (user_id.empty()) {
+ LOG(" !! !! Thread id is empty - this is groupchat message."); // TODO: remove as this is not such 'error'
+ continue;
+ }
+
+ facebook_user fbu;
+ fbu.user_id = user_id;
+
+ HANDLE hContact = AddToContactList(&fbu, FACEBOOK_CONTACT_NONE);
+ // TODO: if contact is newly added, get his user info
+ // TODO: maybe create new "receiveMsg" function and use it for offline and channel messages?
+
+ pos2 = 0;
+ while ( ( pos2 = messageslist.find( "class=\\\"MessagingMessage ", pos2 )) != std::string::npos ) {
+ pos2 += 8;
+ std::string strclass = messageslist.substr(pos2, messageslist.find("\\\"", pos2) - pos2);
+
+ if (strclass.find("MessagingMessageUnread") == std::string::npos)
+ continue; // ignoring old messages
+
+ //std::string::size_type pos3 = messageslist.find( "/li>", pos2 ); // TODO: ne proti tomuhle li, protože i pÅ™Ãlohy majà li...
+ std::string::size_type pos3 = messageslist.find( "class=\\\"MessagingMessage ", pos2 );
+ std::string messagesgroup = messageslist.substr( pos2, pos3 - pos2 );
+
+ DWORD timestamp = utils::conversion::to_timestamp(
+ utils::text::source_get_value( &messagesgroup, 2, "data-utime=\\\"", "\\\"" ));
+
+ pos3 = 0;
+ while ( ( pos3 = messagesgroup.find( "class=\\\"content noh", pos3 )) != std::string::npos )
+ {
+
+ std::string message_attachments = "";
+ std::string::size_type pos4 = 0;
+ if ((pos4 = messagesgroup.find( "class=\\\"attachments\\\"", pos4)) != std::string::npos) {
+ std::string attachments = messagesgroup.substr( pos4, messagesgroup.find("<\\/ul", pos4) - pos4 );
+
+ pos4 = 0;
+ while ( ( pos4 = attachments.find("<li", pos4)) != std::string::npos ) {
+ std::string attachment = attachments.substr( pos4, attachments.find("<\\/li>", pos4) - pos4 );
+ std::string link = utils::text::source_get_value( &attachment, 4, "<a class=", "attachment", "href=\\\"", "\\\"" );
+
+ link = utils::text::trim(
+ utils::text::special_expressions_decode( link ));
+
+ // or first: std::string name = utils::text::source_get_value( &attachment, 4, "<a class=", "attachment", ">", "<\\/a>" );
+ std::string name = utils::text::trim(
+ utils::text::special_expressions_decode(
+ utils::text::remove_html( attachment )) );
+
+ if (link.find("/ajax/messaging/attachments/photo/dialog.php?uri=") != std::string::npos) {
+ link = link.substr(49);
+ link = utils::url::decode(link);
+ }
+
+ message_attachments += "< " + name + " > " + FACEBOOK_URL_HOMEPAGE;
+ message_attachments += link + "\r\n";
+
+ pos4++;
+ }
+
+ }
+
+ std::string message_text = messagesgroup.substr(pos3, messagesgroup.find( "<\\/div", pos3 ) + 6 - pos3);
+ message_text = utils::text::source_get_value( &message_text, 2, "\\\">", "<\\/div" );
+ message_text = utils::text::trim(
+ utils::text::special_expressions_decode(
+ utils::text::remove_html( message_text )) );
+
+ if (!message_attachments.empty()) {
+ if (!message_text.empty())
+ message_text += "\r\n\r\n";
+
+ message_text += Translate("Attachments:");
+ message_text += "\r\n" + message_attachments;
+ }
+
+ PROTORECVEVENT recv = {0};
+ CCSDATA ccs = {0};
+
+ recv.flags = PREF_UTF;
+ recv.szMessage = const_cast<char*>(message_text.c_str());
+ recv.timestamp = timestamp;
+
+ ccs.hContact = hContact;
+ ccs.szProtoService = PSR_MESSAGE;
+ ccs.lParam = reinterpret_cast<LPARAM>(&recv);
+ CallService(MS_PROTO_CHAINRECV,0,reinterpret_cast<LPARAM>(&ccs));
+
+ pos3++;
+ }
+
+ }
+
+ }
+
+}
+
+void FacebookProto::ProcessMessages( void* data )
+{
+ if ( data == NULL )
+ return;
+
+ std::string* resp = (std::string*)data;
+
+ if ( isOffline())
+ goto exit;
+
+ LOG("***** Starting processing messages");
+
+ CODE_BLOCK_TRY
+
+ std::vector< facebook_message* > messages;
+ std::vector< facebook_notification* > notifications;
+
+ facebook_json_parser* p = new facebook_json_parser( this );
+ p->parse_messages( data, &messages, ¬ifications );
+ delete p;
+
+ bool local_timestamp = getByte(FACEBOOK_KEY_LOCAL_TIMESTAMP, 0) != 0;
+
+ for(std::vector<facebook_message*>::size_type i=0; i<messages.size( ); i++)
+ {
+ if ( messages[i]->user_id != facy.self_.user_id )
+ {
+ LOG(" Got message: %s", messages[i]->message_text.c_str());
+ facebook_user fbu;
+ fbu.user_id = messages[i]->user_id;
+
+ HANDLE hContact = AddToContactList(&fbu, FACEBOOK_CONTACT_NONE, false, messages[i]->sender_name.c_str());
+
+ // TODO: if contact is newly added, get his user info
+ // TODO: maybe create new "receiveMsg" function and use it for offline and channel messages?
+
+ PROTORECVEVENT recv = {0};
+ CCSDATA ccs = {0};
+
+ recv.flags = PREF_UTF;
+ recv.szMessage = const_cast<char*>(messages[i]->message_text.c_str());
+ recv.timestamp = local_timestamp ? ::time(NULL) : messages[i]->time;
+
+ ccs.hContact = hContact;
+ ccs.szProtoService = PSR_MESSAGE;
+ ccs.lParam = reinterpret_cast<LPARAM>(&recv);
+ CallService(MS_PROTO_CHAINRECV,0,reinterpret_cast<LPARAM>(&ccs));
+ }
+ delete messages[i];
+ }
+ messages.clear();
+
+ for(std::vector<facebook_notification*>::size_type i=0; i<notifications.size( ); i++)
+ {
+ LOG(" Got notification: %s", notifications[i]->text.c_str());
+ TCHAR* szTitle = mir_a2t_cp(this->m_szModuleName, CP_UTF8);
+ TCHAR* szText = mir_a2t_cp(notifications[i]->text.c_str(), CP_UTF8);
+ TCHAR* szUrl = mir_a2t_cp(notifications[i]->link.c_str(), CP_UTF8);
+ NotifyEvent( szTitle, szText, ContactIDToHContact(notifications[i]->user_id), FACEBOOK_EVENT_NOTIFICATION, szUrl );
+ mir_free( szTitle );
+ mir_free( szText );
+// mir_free( szUrl ); // URL is free'd in popup procedure
+
+ delete notifications[i];
+ }
+ notifications.clear();
+
+ LOG("***** Messages processed");
+
+ CODE_BLOCK_CATCH
+
+ LOG("***** Error processing messages: %s", e.what());
+
+ CODE_BLOCK_END
+
+exit:
+ delete resp;
+}
+
+void FacebookProto::ProcessNotifications( void* )
+{
+ if ( isOffline())
+ return;
+
+ if (!getByte( FACEBOOK_KEY_EVENT_NOTIFICATIONS_ENABLE, DEFAULT_EVENT_NOTIFICATIONS_ENABLE ))
+ return;
+
+ facy.handle_entry( "notifications" );
+
+ // Get notifications
+ http::response resp = facy.flap( FACEBOOK_REQUEST_NOTIFICATIONS );
+
+ // Process result data
+ facy.validate_response(&resp);
+
+ if (resp.code != HTTP_CODE_OK) {
+ facy.handle_error( "notifications" );
+ return;
+ }
+
+
+ // Process notifications
+ LOG("***** Starting processing notifications");
+
+ CODE_BLOCK_TRY
+
+ std::vector< facebook_notification* > notifications;
+
+ facebook_json_parser* p = new facebook_json_parser( this );
+ p->parse_notifications( &(resp.data), ¬ifications );
+ delete p;
+
+ for(std::vector<facebook_notification*>::size_type i=0; i<notifications.size( ); i++)
+ {
+ LOG(" Got notification: %s", notifications[i]->text.c_str());
+ TCHAR* szTitle = mir_a2t_cp(this->m_szModuleName, CP_UTF8);
+ TCHAR* szText = mir_a2t_cp(notifications[i]->text.c_str(), CP_UTF8);
+ TCHAR* szUrl = mir_a2t_cp(notifications[i]->link.c_str(), CP_UTF8);
+ NotifyEvent( szTitle, szText, ContactIDToHContact(notifications[i]->user_id), FACEBOOK_EVENT_NOTIFICATION, szUrl );
+ mir_free( szTitle );
+ mir_free( szText );
+// mir_free( szUrl ); // URL is free'd in popup procedure
+
+ delete notifications[i];
+ }
+ notifications.clear();
+
+ LOG("***** Notifications processed");
+
+ CODE_BLOCK_CATCH
+
+ LOG("***** Error processing notifications: %s", e.what());
+
+ CODE_BLOCK_END
+}
+
+void FacebookProto::ProcessFriendRequests( void* )
+{
+ facy.handle_entry( "friendRequests" );
+
+ // Get notifications
+ http::response resp = facy.flap( FACEBOOK_REQUEST_LOAD_REQUESTS );
+
+ // Process result data
+ facy.validate_response(&resp);
+
+ if (resp.code != HTTP_CODE_OK) {
+ facy.handle_error( "friendRequests" );
+ return;
+ }
+
+ // Parse it
+ std::string reqs = utils::text::source_get_value(&resp.data, 2, "<div class=\"mRequestItem", "<div class=\"al aps\">");
+
+ std::string::size_type pos = 0;
+ std::string::size_type pos2 = 0;
+ bool last = false;
+
+ while (!last && !reqs.empty()) {
+ std::string req;
+ if ((pos2 = reqs.find("<div class=\"mRequestItem", pos)) != std::string::npos) {
+ req = reqs.substr(pos, pos2 - pos);
+ pos = pos2 + 24;
+ } else {
+ req = reqs.substr(pos);
+ last = true;
+ }
+
+ std::string get = utils::text::source_get_value(&req, 3, "<form", "action=\"", "\">");
+ std::string time = utils::text::source_get_value2(&get, "seenrequesttime=", "&\"");
+
+ facebook_user *fbu = new facebook_user();
+ fbu->real_name = utils::text::source_get_value(&req, 2, "class=\"actor\">", "</");
+ fbu->user_id = utils::text::source_get_value(&get, 2, "id=", "&");
+
+ if (fbu->user_id.length() && fbu->real_name.length())
+ {
+ HANDLE hContact = AddToContactList(fbu, FACEBOOK_CONTACT_APPROVE, false, fbu->real_name.c_str());
+ DBWriteContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, FACEBOOK_CONTACT_APPROVE);
+
+ bool seen = false;
+
+ DBVARIANT dbv;
+ if (!DBGetContactSettingString(hContact, m_szModuleName, "RequestTime", &dbv)) {
+ seen = !strcmp(dbv.pszVal, time.c_str());
+ DBFreeVariant(&dbv);
+ }
+
+ if (!seen) {
+ // This is new request
+ DBWriteContactSettingString(hContact, m_szModuleName, "RequestTime", time.c_str());
+
+ //blob is: uin( DWORD ), hContact( HANDLE ), nick( ASCIIZ ), first( ASCIIZ ), last( ASCIIZ ), email( ASCIIZ ), reason( ASCIIZ )
+ //blob is: 0( DWORD ), hContact( HANDLE ), nick( ASCIIZ ), ""( ASCIIZ ), ""( ASCIIZ ), ""( ASCIIZ ), ""( ASCIIZ )
+ DBEVENTINFO dbei = {0};
+ dbei.cbSize = sizeof( DBEVENTINFO );
+ dbei.szModule = m_szModuleName;
+ dbei.timestamp = ::time( NULL );
+ dbei.flags = DBEF_UTF;
+ dbei.eventType = EVENTTYPE_AUTHREQUEST;
+ dbei.cbBlob = (DWORD)(sizeof( DWORD )*2 + fbu->real_name.length() + 5);
+
+ PBYTE pCurBlob = dbei.pBlob = ( PBYTE ) mir_alloc( dbei.cbBlob );
+ *(PDWORD)pCurBlob = 0; pCurBlob += sizeof(DWORD); // UID
+ *(PDWORD)pCurBlob = (DWORD)hContact; pCurBlob += sizeof(DWORD); // Contact Handle
+ strcpy((char*)pCurBlob, fbu->real_name.data()); pCurBlob += fbu->real_name.length()+1; // Nickname
+ *pCurBlob = '\0'; pCurBlob++; // First Name
+ *pCurBlob = '\0'; pCurBlob++; // Last Name
+ *pCurBlob = '\0'; pCurBlob++; // E-mail
+ *pCurBlob = '\0'; // Reason
+
+ CallService(MS_DB_EVENT_ADD, (WPARAM)NULL, (LPARAM)&dbei);
+
+ LOG(" (New) Friendship request from: %s (%s) [%s]", fbu->real_name.c_str(), fbu->user_id.c_str(), time.c_str());
+ } else {
+ LOG(" (Old) Friendship request from: %s (%s) [%s]", fbu->real_name.c_str(), fbu->user_id.c_str(), time.c_str());
+ }
+ } else {
+ LOG(" !!! Wrong friendship request");
+ LOG(req.c_str());
+ }
+ }
+
+ facy.handle_success( "friendRequests" );
+}
+
+void FacebookProto::ProcessFeeds( void* data )
+{
+ if ( data == NULL )
+ return;
+
+ std::string* resp = (std::string*)data;
+
+ if (!isOnline())
+ goto exit;
+
+ CODE_BLOCK_TRY
+
+ LOG("***** Starting processing feeds");
+
+ std::vector< facebook_newsfeed* > news;
+
+ 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\":\"", ">\"");
+
+ while ( ( pos = resp->find( "<div class=\\\"mainWrapper\\\"", pos )) != std::string::npos && limit <= 25 )
+ {
+ std::string::size_type pos2 = resp->find( "<div class=\\\"mainWrapper\\\"", pos+5 );
+ if (pos2 == std::string::npos)
+ pos2 = resp->length();
+
+ std::string post = resp->substr( pos, pos2 - pos );
+ pos += 5;
+
+ std::string post_header = utils::text::source_get_value(&post, 4, "<h6 class=", "uiStreamHeadline", ">", "<\\/h6>");
+ std::string post_message = utils::text::source_get_value(&post, 3, "<h6 class=\\\"uiStreamMessage\\\"", ">", "<\\/h6>");
+ std::string post_link = utils::text::source_get_value(&post, 3, "<span class=\\\"uiStreamSource\\\"", ">", "<\\/span>");
+ std::string post_attach = utils::text::source_get_value(&post, 4, "<div class=", "uiStreamAttachments", ">", "<form");
+
+ // in title keep only name, end of events like "X shared link" put into message
+ pos2 = post_header.find("<\\/a>") + 5;
+ post_message = post_header.substr(pos2, post_header.length() - pos2) + post_message;
+ post_header = post_header.substr(0, pos2);
+
+ // append attachement to message (if any)
+ post_message += utils::text::trim( post_attach );
+
+ facebook_newsfeed* nf = new facebook_newsfeed;
+
+ nf->title = utils::text::trim(
+ utils::text::special_expressions_decode(
+ utils::text::remove_html( post_header )) );
+
+ nf->user_id = utils::text::source_get_value( &post_header, 2, "user.php?id=", "\\\"" );
+
+ nf->link = utils::text::special_expressions_decode(
+ utils::text::source_get_value( &post_link, 2, "href=\\\"", "\\\">" ));
+
+ nf->text = utils::text::trim(
+ utils::text::special_expressions_decode(
+ utils::text::remove_html(
+ utils::text::edit_html( post_message )) ));
+
+ if ( !nf->title.length() || !nf->text.length())
+ {
+ delete nf;
+ continue;
+ }
+
+ if (nf->text.length() > 500)
+ {
+ nf->text = nf->text.substr(0, 500);
+ nf->text += "...";
+ }
+
+ news.push_back( nf );
+ pos++;
+ limit++;
+ }
+
+ for(std::vector<facebook_newsfeed*>::size_type i=0; i<news.size( ); i++)
+ {
+ LOG(" Got newsfeed: %s %s", news[i]->title.c_str(), news[i]->text.c_str());
+ TCHAR* szTitle = mir_a2t_cp(news[i]->title.c_str(), CP_UTF8);
+ TCHAR* szText = mir_a2t_cp(news[i]->text.c_str(), CP_UTF8);
+ TCHAR* szUrl = mir_a2t_cp(news[i]->link.c_str(), CP_UTF8);
+ NotifyEvent(szTitle,szText,this->ContactIDToHContact(news[i]->user_id),FACEBOOK_EVENT_NEWSFEED, szUrl);
+ mir_free(szTitle);
+ mir_free(szText);
+// mir_free(szUrl); // URL is free'd in popup procedure
+ delete news[i];
+ }
+ news.clear();
+
+ this->facy.last_feeds_update_ = ::time(NULL);
+
+ LOG("***** Feeds processed");
+
+ CODE_BLOCK_CATCH
+
+ LOG("***** Error processing feeds: %s", e.what());
+
+ CODE_BLOCK_END
+
+exit:
+ delete resp;
+}
+
+void FacebookProto::SearchAckThread(void *targ)
+{
+ facy.handle_entry( "searchAckThread" );
+
+ int count = 0;
+
+ char *arg = mir_utf8encodeT((TCHAR*)targ);
+ std::string search = utils::url::encode( arg );
+ std::string ssid;
+
+ while (count < 50 && !isOffline())
+ {
+ std::string get_data = search + "&s=" + utils::conversion::to_string(&count, UTILS_CONV_UNSIGNED_NUMBER);
+ if (!ssid.empty())
+ get_data += "&ssid=" + ssid;
+
+ // Get notifications
+ http::response resp = facy.flap( FACEBOOK_REQUEST_SEARCH, NULL, &get_data );
+
+ // Process result data
+ facy.validate_response(&resp);
+
+ if (resp.code == HTTP_CODE_OK)
+ {
+ std::string items = utils::text::source_get_value(&resp.data, 3, "<body", "<div class=\"c\">", "</body>");
+
+ std::string::size_type pos = 0;
+ std::string::size_type pos2 = 0;
+ bool last = false;
+
+ while (!last) {
+ std::string item;
+ if ((pos2 = items.find("<div class=\"c\">", pos)) != std::string::npos) {
+ item = items.substr(pos, pos2 - pos);
+ pos = pos2 + 14;
+ } else {
+ item = items.substr(pos);
+ last = true;
+ }
+
+ count++;
+
+ std::string id = utils::text::source_get_value2(&item, "?id=", "&\"");
+ if (id.empty())
+ id = utils::text::source_get_value2(&item, "?slog=", "&\"");
+
+ std::string name = utils::text::source_get_value(&item, 2, "<span>", "</span>");
+ std::string surname;
+ std::string nick;
+ std::string common = utils::text::source_get_value(&item, 2, "<span class=\"fcg\">", "</span>");
+
+ if ((pos2 = name.find(" <span class=\"alternate_name\">")) != std::string::npos) {
+ nick = name.substr(pos2 + 31, name.length() - pos2 - 32); // also remove brackets around nickname
+ name = name.substr(0, pos2);
+ }
+
+ if ((pos2 = name.find(" ")) != std::string::npos) {
+ surname = name.substr(pos2 + 1, name.length() - pos2 - 1);
+ name = name.substr(0, pos2);
+ }
+
+ // ignore self contact and empty ids
+ if (id.empty() || id == facy.self_.user_id)
+ continue;
+
+ TCHAR* tid = mir_a2t_cp(id.c_str(), CP_UTF8);
+ TCHAR* tname = mir_a2t_cp(name.c_str(), CP_UTF8);
+ TCHAR* tsurname = mir_a2t_cp(surname.c_str(), CP_UTF8);
+ TCHAR* tnick = mir_a2t_cp(nick.c_str(), CP_UTF8);
+ TCHAR* tcommon = mir_a2t_cp(common.c_str(), CP_UTF8);
+
+ PROTOSEARCHRESULT isr = {0};
+ isr.cbSize = sizeof(isr);
+ isr.flags = PSR_TCHAR;
+ isr.id = tid;
+ isr.nick = tnick;
+ isr.firstName = tname;
+ isr.lastName = tsurname;
+ isr.email = tcommon;
+
+ ProtoBroadcastAck(m_szModuleName, NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, targ, (LPARAM)&isr);
+
+ mir_free(tid);
+ mir_free(tnick);
+ mir_free(tname);
+ mir_free(tsurname);
+ mir_free(tcommon);
+ }
+
+ ssid = utils::text::source_get_value(&items, 3, "id=\"more_objects\"", "ssid=", "&");
+ if (ssid.empty())
+ break; // No more results
+ }
+ }
+
+ ProtoBroadcastAck(m_szModuleName, NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, targ, 0);
+
+ facy.handle_success( "searchAckThread" );
+
+ mir_free(targ);
+ mir_free(arg);
+}
diff --git a/protocols/FacebookRM/src/proto.cpp b/protocols/FacebookRM/src/proto.cpp new file mode 100644 index 0000000000..c2d7a39151 --- /dev/null +++ b/protocols/FacebookRM/src/proto.cpp @@ -0,0 +1,584 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "common.h"
+
+FacebookProto::FacebookProto(const char* proto_name,const TCHAR* username)
+{
+ m_iVersion = 2;
+ m_szProtoName = mir_strdup( proto_name );
+ m_szModuleName = mir_strdup( proto_name );
+ m_tszUserName = mir_tstrdup( username );
+
+ facy.parent = this;
+
+ signon_lock_ = CreateMutex( NULL, FALSE, NULL );
+ avatar_lock_ = CreateMutex( NULL, FALSE, NULL );
+ log_lock_ = CreateMutex( NULL, FALSE, NULL );
+ update_loop_lock_ = CreateEvent( NULL, FALSE, FALSE, NULL);
+ facy.buddies_lock_ = CreateMutex( NULL, FALSE, NULL );
+ facy.send_message_lock_ = CreateMutex( NULL, FALSE, NULL );
+ facy.fcb_conn_lock_ = CreateMutex( NULL, FALSE, NULL );
+
+ CreateProtoService(m_szModuleName, PS_CREATEACCMGRUI, &FacebookProto::SvcCreateAccMgrUI, this);
+ CreateProtoService(m_szModuleName, PS_GETMYAWAYMSG, &FacebookProto::GetMyAwayMsg, this);
+ CreateProtoService(m_szModuleName, PS_GETMYAVATART, &FacebookProto::GetMyAvatar, this);
+ CreateProtoService(m_szModuleName, PS_GETAVATARINFOT, &FacebookProto::GetAvatarInfo, this);
+ CreateProtoService(m_szModuleName, PS_GETAVATARCAPS, &FacebookProto::GetAvatarCaps, this);
+
+ CreateProtoService(m_szModuleName, PS_JOINCHAT, &FacebookProto::OnJoinChat, this);
+ CreateProtoService(m_szModuleName, PS_LEAVECHAT, &FacebookProto::OnLeaveChat, this);
+
+ HookProtoEvent(ME_CLIST_PREBUILDSTATUSMENU, &FacebookProto::OnBuildStatusMenu, this);
+ HookProtoEvent(ME_OPT_INITIALISE, &FacebookProto::OnOptionsInit, this);
+ HookProtoEvent(ME_GC_EVENT, &FacebookProto::OnChatOutgoing, this);
+ HookProtoEvent(ME_IDLE_CHANGED, &FacebookProto::OnIdleChanged, this);
+
+ // Create standard network connection
+ TCHAR descr[512];
+ NETLIBUSER nlu = {sizeof(nlu)};
+ nlu.flags = NUF_INCOMING | NUF_OUTGOING | NUF_HTTPCONNS | NUF_TCHAR;
+ nlu.szSettingsModule = m_szModuleName;
+ char module[512];
+ mir_snprintf(module,SIZEOF(module),"%sAv",m_szModuleName);
+ nlu.szSettingsModule = module;
+ mir_sntprintf(descr,SIZEOF(descr),TranslateT("%s server connection"),m_tszUserName);
+ nlu.ptszDescriptiveName = descr;
+ m_hNetlibUser = (HANDLE)CallService(MS_NETLIB_REGISTERUSER,0,(LPARAM)&nlu);
+ if (m_hNetlibUser == NULL)
+ MessageBox(NULL,TranslateT("Unable to get Netlib connection for Facebook"),m_tszUserName,MB_OK);
+
+ facy.set_handle(m_hNetlibUser);
+
+ SkinAddNewSoundExT( "Notification", m_tszUserName, LPGENT( "Notification" ));
+ SkinAddNewSoundExT( "NewsFeed", m_tszUserName, LPGENT( "News Feed" ));
+ SkinAddNewSoundExT( "OtherEvent", m_tszUserName, LPGENT( "Other Event" ));
+
+ TCHAR *profile = Utils_ReplaceVarsT( _T("%miranda_avatarcache%"));
+ def_avatar_folder_ = std::tstring(profile) + _T("\\") + m_tszUserName;
+ mir_free(profile);
+ hAvatarFolder_ = FoldersRegisterCustomPathT(m_szModuleName, "Avatars", def_avatar_folder_.c_str());
+
+ // Set all contacts offline -- in case we crashed
+ SetAllContactStatuses( ID_STATUS_OFFLINE );
+}
+
+FacebookProto::~FacebookProto( )
+{
+ Netlib_CloseHandle( m_hNetlibUser );
+
+ WaitForSingleObject( signon_lock_, IGNORE );
+ WaitForSingleObject( avatar_lock_, IGNORE );
+ WaitForSingleObject( log_lock_, IGNORE );
+ WaitForSingleObject( facy.buddies_lock_, IGNORE );
+ WaitForSingleObject( facy.send_message_lock_, IGNORE );
+
+ CloseHandle( signon_lock_ );
+ CloseHandle( avatar_lock_ );
+ CloseHandle( log_lock_ );
+ CloseHandle( update_loop_lock_ );
+ CloseHandle( facy.buddies_lock_ );
+ CloseHandle( facy.send_message_lock_ );
+ CloseHandle( facy.fcb_conn_lock_ );
+
+ mir_free( m_tszUserName );
+ mir_free( m_szModuleName );
+ mir_free( m_szProtoName );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+DWORD_PTR FacebookProto::GetCaps( int type, HANDLE hContact )
+{
+ switch(type)
+ {
+ case PFLAGNUM_1:
+ {
+ DWORD_PTR flags = PF1_IM | PF1_CHAT | PF1_SERVERCLIST | PF1_AUTHREQ | /*PF1_ADDED |*/ PF1_BASICSEARCH | PF1_USERIDISEMAIL | PF1_SEARCHBYEMAIL | PF1_SEARCHBYNAME | PF1_ADDSEARCHRES; // | PF1_VISLIST | PF1_INVISLIST;
+
+ if ( getByte( FACEBOOK_KEY_SET_MIRANDA_STATUS, 0 ))
+ return flags |= PF1_MODEMSG;
+ else
+ return flags |= PF1_MODEMSGRECV;
+ }
+ case PFLAGNUM_2:
+ return PF2_ONLINE | PF2_INVISIBLE | PF2_ONTHEPHONE | PF2_IDLE; // | PF2_SHORTAWAY;
+ case PFLAGNUM_3:
+ if ( getByte( FACEBOOK_KEY_SET_MIRANDA_STATUS, 0 ))
+ return PF2_ONLINE; // | PF2_SHORTAWAY;
+ else
+ return 0;
+ case PFLAGNUM_4:
+ return PF4_NOCUSTOMAUTH | PF4_FORCEADDED | PF4_IMSENDUTF | PF4_AVATARS | PF4_SUPPORTTYPING | PF4_NOAUTHDENYREASON | PF4_IMSENDOFFLINE;
+ case PFLAGNUM_5:
+ return PF2_ONTHEPHONE;
+ case PFLAG_MAXLENOFMESSAGE:
+ return FACEBOOK_MESSAGE_LIMIT;
+ case PFLAG_UNIQUEIDTEXT:
+ return (int) "Facebook ID";
+ case PFLAG_UNIQUEIDSETTING:
+ return (int) FACEBOOK_KEY_ID;
+ }
+ return 0;
+}
+
+HICON FacebookProto::GetIcon(int index)
+{
+ if (LOWORD(index) == PLI_PROTOCOL)
+ {
+ HICON ico = (HICON)CallService(MS_SKIN2_GETICON,0,(LPARAM)"Facebook_facebook");
+ return CopyIcon(ico);
+ } else {
+ return 0;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int FacebookProto::SetStatus( int new_status )
+{
+ LOG("===== Beginning SetStatus process");
+
+ // Routing statuses not supported by Facebook
+ switch ( new_status )
+ {
+ case ID_STATUS_INVISIBLE:
+ case ID_STATUS_OFFLINE:
+ m_iDesiredStatus = new_status;
+ break;
+
+ // TODO RM: needed/useful?
+ case ID_STATUS_CONNECTING:
+ m_iDesiredStatus = ID_STATUS_OFFLINE;
+ break;
+
+ case ID_STATUS_IDLE:
+ default:
+ m_iDesiredStatus = ID_STATUS_INVISIBLE;
+ if (DBGetContactSettingByte(NULL,m_szModuleName,FACEBOOK_KEY_MAP_STATUSES, DEFAULT_MAP_STATUSES))
+ break;
+ case ID_STATUS_ONLINE:
+ case ID_STATUS_FREECHAT:
+ m_iDesiredStatus = ID_STATUS_ONLINE;
+ break;
+ }
+
+ if ( m_iStatus == ID_STATUS_CONNECTING )
+ {
+ LOG("===== Status is connecting, no change");
+ return 0;
+ }
+
+ if ( m_iStatus == m_iDesiredStatus)
+ {
+ LOG("===== Statuses are same, no change");
+ return 0;
+ }
+
+ facy.invisible_ = ( new_status == ID_STATUS_INVISIBLE );
+
+ ForkThread( &FacebookProto::ChangeStatus, this );
+
+ return 0;
+}
+
+int FacebookProto::SetAwayMsg( int status, const PROTOCHAR *msg )
+{
+ if (!msg)
+ {
+ last_status_msg_.clear();
+ return 0;
+ }
+
+ char *narrow = mir_utf8encodeT(msg);
+ if (last_status_msg_ != narrow) last_status_msg_ = narrow;
+ utils::mem::detract(narrow);
+
+ if (isOnline() && getByte(FACEBOOK_KEY_SET_MIRANDA_STATUS, DEFAULT_SET_MIRANDA_STATUS))
+ {
+ ForkThread(&FacebookProto::SetAwayMsgWorker, this, NULL);
+ }
+ return 0;
+}
+
+void FacebookProto::SetAwayMsgWorker(void *)
+{
+ if ( !last_status_msg_.empty())
+ facy.set_status( last_status_msg_ );
+}
+
+HANDLE FacebookProto::SearchBasic( const PROTOCHAR* id )
+{
+ if (isOffline())
+ return 0;
+
+ TCHAR* email = mir_tstrdup(id);
+ ForkThread(&FacebookProto::SearchAckThread, this, (void*)email);
+
+ return email;
+}
+
+HANDLE FacebookProto::SearchByEmail( const PROTOCHAR* email )
+{
+ return SearchBasic(email);
+}
+
+HANDLE FacebookProto::SearchByName( const PROTOCHAR* nick, const PROTOCHAR* firstName, const PROTOCHAR* lastName )
+{
+ TCHAR arg[200];
+ _sntprintf (arg, SIZEOF(arg), _T("%s %s %s"), nick, firstName, lastName);
+ return SearchBasic(arg);
+}
+
+HANDLE FacebookProto::AddToList(int flags, PROTOSEARCHRESULT* psr)
+{
+ char *id = mir_t2a_cp(psr->id, CP_UTF8);
+ char *name = mir_t2a_cp(psr->firstName, CP_UTF8);
+ char *surname = mir_t2a_cp(psr->lastName, CP_UTF8);
+
+ facebook_user fbu;
+ fbu.user_id = id;
+ fbu.real_name = name;
+ fbu.real_name += " ";
+ fbu.real_name += surname;
+
+ HANDLE hContact = AddToContactList(&fbu, FACEBOOK_CONTACT_NONE, false, fbu.real_name.c_str());
+ if (hContact) {
+ if (flags & PALF_TEMPORARY)
+ {
+ DBWriteContactSettingByte(hContact, "Clist", "Hidden", 1);
+ DBWriteContactSettingByte(hContact, "Clist", "NotOnList", 1);
+ }
+ else if (DBGetContactSettingByte(hContact, "CList", "NotOnList", 0))
+ {
+ DBDeleteContactSetting(hContact, "CList", "Hidden");
+ DBDeleteContactSetting(hContact, "CList", "NotOnList");
+ }
+ }
+
+ mir_free(id);
+ mir_free(name);
+ mir_free(surname);
+
+ return hContact;
+}
+
+int FacebookProto::AuthRequest(HANDLE hContact,const PROTOCHAR *message)
+{
+ return RequestFriendship((WPARAM)hContact, NULL);
+}
+
+int FacebookProto::Authorize(HANDLE hDbEvent)
+{
+ if (!isOffline() && hDbEvent)
+ {
+ HANDLE hContact = HContactFromAuthEvent( hDbEvent );
+ if (hContact == INVALID_HANDLE_VALUE)
+ return 1;
+
+ return ApproveFriendship((WPARAM)hContact, NULL);
+ }
+
+ return 1;
+}
+
+int FacebookProto::AuthDeny(HANDLE hDbEvent, const PROTOCHAR *reason)
+{
+
+ if (!isOffline() && hDbEvent)
+ {
+ HANDLE hContact = HContactFromAuthEvent(hDbEvent);
+ if (hContact == INVALID_HANDLE_VALUE)
+ return 1;
+
+ // TODO: hide from facebook requests list
+
+ if (DBGetContactSettingByte(hContact, "CList", "NotOnList", 0))
+ CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact, 0);
+
+ return 0;
+ }
+
+ return 1;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// SERVICES
+
+int FacebookProto::GetMyAwayMsg( WPARAM wParam, LPARAM lParam )
+{
+ DBVARIANT dbv = { DBVT_TCHAR };
+ if ( !getTString( "StatusMsg", &dbv ) && lstrlen( dbv.ptszVal ) != 0 )
+ {
+ int res = (lParam & SGMA_UNICODE) ? (INT_PTR)mir_t2u(dbv.ptszVal) : (INT_PTR)mir_t2a(dbv.ptszVal);
+ DBFreeVariant( &dbv );
+ return res;
+ } else {
+ return 0;
+ }
+}
+
+int FacebookProto::OnIdleChanged( WPARAM wParam, LPARAM lParam )
+{
+ if (m_iStatus == ID_STATUS_INVISIBLE || m_iStatus <= ID_STATUS_OFFLINE)
+ return 0;
+
+ bool bIdle = (lParam & IDF_ISIDLE) != 0;
+ bool bPrivacy = (lParam & IDF_PRIVACY) != 0;
+
+ if (facy.is_idle_ && !bIdle)
+ {
+ facy.is_idle_ = false;
+ SetStatus(m_iDesiredStatus);
+ }
+ else if (!facy.is_idle_ && bIdle && !bPrivacy && m_iDesiredStatus != ID_STATUS_INVISIBLE)
+ {
+ facy.is_idle_ = true;
+ SetStatus(ID_STATUS_IDLE);
+ }
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+int FacebookProto::OnEvent(PROTOEVENTTYPE event,WPARAM wParam,LPARAM lParam)
+{
+ switch(event)
+ {
+ case EV_PROTO_ONLOAD:
+ return OnModulesLoaded(wParam,lParam);
+
+ case EV_PROTO_ONEXIT:
+ return OnPreShutdown(wParam,lParam);
+
+ case EV_PROTO_ONOPTIONS:
+ return OnOptionsInit(wParam,lParam);
+
+ case EV_PROTO_ONCONTACTDELETED:
+ return OnContactDeleted(wParam,lParam);
+ }
+
+ return 1;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// EVENTS
+
+int FacebookProto::SvcCreateAccMgrUI(WPARAM wParam,LPARAM lParam)
+{
+ return (int)CreateDialogParam(g_hInstance,MAKEINTRESOURCE(IDD_FACEBOOKACCOUNT),
+ (HWND)lParam, FBAccountProc, (LPARAM)this );
+}
+
+int FacebookProto::OnModulesLoaded(WPARAM wParam,LPARAM lParam)
+{
+ // Register group chat
+ GCREGISTER gcr = {sizeof(gcr)};
+ gcr.dwFlags = 0; //GC_ACKMSG;
+ gcr.pszModule = m_szModuleName;
+ gcr.pszModuleDispName = m_szModuleName;
+ gcr.iMaxText = FACEBOOK_MESSAGE_LIMIT;
+ gcr.nColors = 0;
+ gcr.pColors = NULL;
+ CallService(MS_GC_REGISTER,0,reinterpret_cast<LPARAM>(&gcr));
+
+ return 0;
+}
+
+int FacebookProto::OnPreShutdown(WPARAM wParam,LPARAM lParam)
+{
+ SetStatus( ID_STATUS_OFFLINE );
+ return 0;
+}
+
+int FacebookProto::OnOptionsInit(WPARAM wParam,LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp = {sizeof(odp)};
+ odp.hInstance = g_hInstance;
+ odp.ptszTitle = m_tszUserName;
+ odp.dwInitParam = LPARAM(this);
+ odp.flags = ODPF_BOLDGROUPS | ODPF_TCHAR | ODPF_DONTTRANSLATE;
+
+ odp.position = 271828;
+ odp.ptszGroup = LPGENT("Network");
+ odp.ptszTab = LPGENT("Account");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS);
+ odp.pfnDlgProc = FBOptionsProc;
+ Options_AddPage(wParam, &odp);
+
+ odp.position = 271829;
+ odp.ptszTab = LPGENT("Advanced");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_ADVANCED);
+ odp.pfnDlgProc = FBOptionsAdvancedProc;
+ Options_AddPage(wParam, &odp);
+
+ odp.position = 271830;
+ if (ServiceExists(MS_POPUP_ADDPOPUPT))
+ odp.ptszGroup = LPGENT("Popups");
+ odp.ptszTab = LPGENT("Events");
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS_EVENTS);
+ odp.pfnDlgProc = FBEventsProc;
+ Options_AddPage(wParam, &odp);
+
+ return 0;
+}
+
+int FacebookProto::OnMind(WPARAM,LPARAM)
+{
+ HWND hDlg = CreateDialogParam( g_hInstance, MAKEINTRESOURCE( IDD_MIND ),
+ ( HWND )0, FBMindProc, reinterpret_cast<LPARAM>( this ));
+ ShowWindow( hDlg, SW_SHOW );
+ return FALSE;
+}
+
+int FacebookProto::VisitProfile(WPARAM wParam,LPARAM lParam)
+{
+ HANDLE hContact = reinterpret_cast<HANDLE>(wParam);
+
+ DBVARIANT dbv;
+ if ( wParam != 0 && !DBGetContactSettingString(hContact,m_szModuleName,"Homepage",&dbv))
+ {
+ CallService(MS_UTILS_OPENURL,1,reinterpret_cast<LPARAM>(dbv.pszVal));
+ DBFreeVariant(&dbv);
+ }
+ else if (DBGetContactSettingByte(hContact,m_szModuleName,"ChatRoom",0))
+ {
+ std::string url = FACEBOOK_URL_GROUP;
+ if (!DBGetContactSettingString(hContact,m_szModuleName,"ChatRoomID",&dbv)) {
+ url += dbv.pszVal;
+ DBFreeVariant(&dbv);
+ }
+ CallService(MS_UTILS_OPENURL,1,reinterpret_cast<LPARAM>(url.c_str()));
+ } else {
+ // TODO: why isn't wParam == 0 when is status menu moved to main menu?
+ CallService(MS_UTILS_OPENURL,1,reinterpret_cast<LPARAM>(FACEBOOK_URL_PROFILE));
+ }
+
+ return 0;
+}
+
+int FacebookProto::CancelFriendship(WPARAM wParam,LPARAM lParam)
+{
+ if (wParam == NULL || isOffline())
+ return 0;
+
+ bool deleting = (lParam == 1);
+
+ HANDLE hContact = reinterpret_cast<HANDLE>(wParam);
+
+ // Ignore groupchats and, if deleting, also not-friends
+ if (DBGetContactSettingByte(hContact, m_szModuleName, "ChatRoom", 0)
+ || (deleting && DBGetContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, 0) != FACEBOOK_CONTACT_FRIEND))
+ return 0;
+
+ DBVARIANT dbv;
+ TCHAR tstr[256];
+
+ if ( !DBGetContactSettingTString(hContact, m_szModuleName, FACEBOOK_KEY_NAME, &dbv)) {
+ mir_sntprintf(tstr,SIZEOF(tstr),TranslateT("Do you want to cancel your friendship with '%s'?"), dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ } else if ( !DBGetContactSettingTString(hContact,m_szModuleName,FACEBOOK_KEY_ID,&dbv)) {
+ mir_sntprintf(tstr,SIZEOF(tstr),TranslateT("Do you want to cancel your friendship with '%s'?"), dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+
+ if (MessageBox( 0, tstr, m_tszUserName, MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2 ) == IDYES) {
+
+ if ( !DBGetContactSettingString(hContact,m_szModuleName,FACEBOOK_KEY_ID,&dbv))
+ {
+ std::string* id = new std::string(dbv.pszVal);
+
+ if (deleting) {
+ facebook_user* fbu = facy.buddies.find( (*id));
+ if (fbu != NULL) {
+ fbu->handle = NULL;
+ }
+ }
+
+ ForkThread( &FacebookProto::DeleteContactFromServer, this, ( void* )id );
+ DBFreeVariant(&dbv);
+ }
+
+ }
+
+ return 0;
+}
+
+int FacebookProto::RequestFriendship(WPARAM wParam,LPARAM lParam)
+{
+ if (wParam == NULL || isOffline())
+ return 0;
+
+ HANDLE hContact = reinterpret_cast<HANDLE>(wParam);
+
+ DBVARIANT dbv;
+ if ( !DBGetContactSettingString(hContact,m_szModuleName,FACEBOOK_KEY_ID,&dbv))
+ {
+ std::string* id = new std::string(dbv.pszVal);
+ ForkThread( &FacebookProto::AddContactToServer, this, ( void* )id );
+ DBFreeVariant(&dbv);
+ }
+
+ return 0;
+}
+
+int FacebookProto::ApproveFriendship(WPARAM wParam,LPARAM lParam)
+{
+ if (wParam == NULL || isOffline())
+ return 0;
+
+ HANDLE *hContact = new HANDLE(reinterpret_cast<HANDLE>(wParam));
+ ForkThread( &FacebookProto::ApproveContactToServer, this, ( void* )hContact );
+
+ return 0;
+}
+
+int FacebookProto::OnCancelFriendshipRequest(WPARAM wParam,LPARAM lParam)
+{
+ if (wParam == NULL || isOffline())
+ return 0;
+
+ HANDLE *hContact = new HANDLE(reinterpret_cast<HANDLE>(wParam));
+ ForkThread( &FacebookProto::CancelFriendsRequest, this, ( void* )hContact );
+
+ return 0;
+}
+
+HANDLE FacebookProto::HContactFromAuthEvent(HANDLE hEvent)
+{
+ DWORD body[2];
+ DBEVENTINFO dbei = { sizeof(dbei) };
+ dbei.cbBlob = sizeof(DWORD)*2;
+ dbei.pBlob = (PBYTE)&body;
+
+ if (CallService(MS_DB_EVENT_GET, (WPARAM)hEvent, (LPARAM)&dbei))
+ return INVALID_HANDLE_VALUE;
+
+ if (dbei.eventType != EVENTTYPE_AUTHREQUEST)
+ return INVALID_HANDLE_VALUE;
+
+ if (strcmp(dbei.szModule, m_szModuleName))
+ return INVALID_HANDLE_VALUE;
+
+ return DbGetAuthEventContact(&dbei);
+}
diff --git a/protocols/FacebookRM/src/proto.h b/protocols/FacebookRM/src/proto.h new file mode 100644 index 0000000000..cdeeaf03f6 --- /dev/null +++ b/protocols/FacebookRM/src/proto.h @@ -0,0 +1,206 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+class FacebookProto : public PROTO_INTERFACE, public MZeroedObject
+{
+public:
+ FacebookProto( const char *proto_name, const TCHAR *username );
+ ~FacebookProto( );
+
+ inline const char* ModuleName( ) const
+ {
+ return m_szModuleName;
+ }
+
+ inline bool isOnline( )
+ {
+ return ( m_iStatus != ID_STATUS_OFFLINE && m_iStatus != ID_STATUS_CONNECTING );
+ }
+
+ inline bool isOffline( )
+ {
+ return ( m_iStatus == ID_STATUS_OFFLINE );
+ }
+
+ inline bool isInvisible( )
+ {
+ return ( m_iStatus == ID_STATUS_INVISIBLE );
+ }
+
+ //PROTO_INTERFACE
+
+ virtual HANDLE __cdecl AddToList( int flags, PROTOSEARCHRESULT* psr );
+ virtual HANDLE __cdecl AddToListByEvent( int flags, int iContact, HANDLE hDbEvent );
+
+ virtual int __cdecl Authorize( HANDLE hDbEvent );
+ virtual int __cdecl AuthDeny( HANDLE hDbEvent, const PROTOCHAR* szReason );
+ virtual int __cdecl AuthRecv( HANDLE hContact, PROTORECVEVENT* );
+ virtual int __cdecl AuthRequest( HANDLE hContact, const PROTOCHAR* szMessage );
+
+ virtual HANDLE __cdecl ChangeInfo( int iInfoType, void* pInfoData );
+
+ virtual HANDLE __cdecl FileAllow( HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szPath );
+ virtual int __cdecl FileCancel( HANDLE hContact, HANDLE hTransfer );
+ virtual int __cdecl FileDeny( HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szReason );
+ virtual int __cdecl FileResume( HANDLE hTransfer, int* action, const PROTOCHAR** szFilename );
+
+ virtual DWORD_PTR __cdecl GetCaps( int type, HANDLE hContact = NULL );
+ virtual HICON __cdecl GetIcon( int iconIndex );
+ virtual int __cdecl GetInfo( HANDLE hContact, int infoType );
+
+ virtual HANDLE __cdecl SearchBasic( const PROTOCHAR* id );
+ virtual HANDLE __cdecl SearchByEmail( const PROTOCHAR* email );
+ virtual HANDLE __cdecl SearchByName( const PROTOCHAR* nick, const PROTOCHAR* firstName, const PROTOCHAR* lastName );
+ virtual HWND __cdecl SearchAdvanced( HWND owner );
+ virtual HWND __cdecl CreateExtendedSearchUI( HWND owner );
+
+ virtual int __cdecl RecvContacts( HANDLE hContact, PROTORECVEVENT* );
+ virtual int __cdecl RecvFile( HANDLE hContact, PROTOFILEEVENT* );
+ virtual int __cdecl RecvMsg( HANDLE hContact, PROTORECVEVENT* );
+ virtual int __cdecl RecvUrl( HANDLE hContact, PROTORECVEVENT* );
+
+ virtual int __cdecl SendContacts( HANDLE hContact, int flags, int nContacts, HANDLE* hContactsList );
+ virtual HANDLE __cdecl SendFile( HANDLE hContact, const PROTOCHAR* szDescription, PROTOCHAR** ppszFiles );
+ virtual int __cdecl SendMsg( HANDLE hContact, int flags, const char* msg );
+ virtual int __cdecl SendUrl( HANDLE hContact, int flags, const char* url );
+
+ virtual int __cdecl SetApparentMode( HANDLE hContact, int mode );
+ virtual int __cdecl SetStatus( int iNewStatus );
+
+ virtual HANDLE __cdecl GetAwayMsg( HANDLE hContact );
+ virtual int __cdecl RecvAwayMsg( HANDLE hContact, int mode, PROTORECVEVENT* evt );
+ virtual int __cdecl SendAwayMsg( HANDLE hContact, HANDLE hProcess, const char* msg );
+ virtual int __cdecl SetAwayMsg( int iStatus, const PROTOCHAR* msg );
+
+ virtual int __cdecl UserIsTyping( HANDLE hContact, int type );
+
+ virtual int __cdecl OnEvent( PROTOEVENTTYPE iEventType, WPARAM wParam, LPARAM lParam );
+
+ ////////////////////////
+
+ // Services
+ int __cdecl GetMyAwayMsg( WPARAM, LPARAM );
+ int __cdecl SetMyAwayMsg( WPARAM, LPARAM );
+ int __cdecl SvcCreateAccMgrUI( WPARAM, LPARAM );
+ int __cdecl GetMyAvatar(WPARAM, LPARAM );
+ int __cdecl GetAvatarInfo(WPARAM, LPARAM );
+ int __cdecl GetAvatarCaps(WPARAM, LPARAM );
+ int __cdecl VisitProfile(WPARAM, LPARAM );
+ int __cdecl CancelFriendship(WPARAM, LPARAM );
+ int __cdecl RequestFriendship(WPARAM, LPARAM );
+ int __cdecl ApproveFriendship(WPARAM, LPARAM );
+ int __cdecl OnCancelFriendshipRequest(WPARAM, LPARAM );
+
+ // Events
+ int __cdecl OnModulesLoaded(WPARAM, LPARAM);
+ int __cdecl OnOptionsInit(WPARAM, LPARAM);
+ int __cdecl OnBuildStatusMenu(WPARAM,LPARAM);
+ int __cdecl OnContactDeleted(WPARAM,LPARAM);
+ int __cdecl OnMind(WPARAM,LPARAM);
+ int __cdecl OnPreShutdown(WPARAM,LPARAM);
+ int __cdecl OnPrebuildContactMenu(WPARAM,LPARAM);
+ int __cdecl OnIdleChanged(WPARAM,LPARAM);
+ int __cdecl OnChatOutgoing(WPARAM,LPARAM);
+ int __cdecl OnJoinChat(WPARAM,LPARAM);
+ int __cdecl OnLeaveChat(WPARAM,LPARAM);
+
+ // Loops
+ bool NegotiateConnection();
+ BYTE GetPollRate();
+ void __cdecl MessageLoop(void*);
+ void __cdecl UpdateLoop(void*);
+
+ // Processing threads
+ void __cdecl ProcessBuddyList(void*);
+ void __cdecl ProcessFriendList(void*);
+ void __cdecl ProcessMessages(void*);
+ void __cdecl ProcessUnreadMessages(void*);
+ void __cdecl ProcessFeeds(void*);
+ void __cdecl ProcessNotifications(void*);
+ void __cdecl ProcessFriendRequests(void*);
+ void __cdecl SearchAckThread(void*);
+
+ // Worker threads
+ void __cdecl SignOn(void*);
+ void __cdecl ChangeStatus(void*);
+ void __cdecl SignOff(void*);
+ void __cdecl SetAwayMsgWorker(void*);
+ void __cdecl UpdateAvatarWorker(void*);
+ void __cdecl SendMsgWorker(void*);
+ void __cdecl SendChatMsgWorker(void*);
+ void __cdecl SendTypingWorker(void*);
+ void __cdecl MessagingWorker(void*);
+ void __cdecl DeleteContactFromServer(void*);
+ void __cdecl AddContactToServer(void*);
+ void __cdecl ApproveContactToServer(void*);
+ void __cdecl CancelFriendsRequest(void*);
+
+ // Contacts handling
+ bool IsMyContact(HANDLE, bool include_chat = false);
+ HANDLE ContactIDToHContact(std::string);
+ HANDLE ChatIDToHContact(std::string);
+ HANDLE AddToContactList(facebook_user*, BYTE type, bool dont_check = false, const char *new_name = "");
+ void SetAllContactStatuses(int);
+ HANDLE HContactFromAuthEvent(HANDLE hEvent);
+
+ // Chats handling
+ void AddChat(const char *id, const char *name);
+ void UpdateChat(const char *chat_id, const char *id, const char *name, const char *message, DWORD timestamp = 0);
+ bool IsChatContact(const char *chat_id, const char *id);
+ void AddChatContact(const char *chat_id, const char *id, const char *name);
+ void RemoveChatContact(const char *chat_id, const char *id);
+ void SetChatStatus(const char *chat_id, int status);
+ char *GetChatUsers(const char *chat_id);
+
+ // Connection client
+ facebook_client facy; // TODO: Refactor to "client" and make dynamic
+
+ // Helpers
+ std::tstring GetAvatarFolder();
+ bool GetDbAvatarInfo(PROTO_AVATAR_INFORMATIONT &ai, std::string *url);
+ void CheckAvatarChange(HANDLE hContact, std::string image_url);
+ void ToggleStatusMenuItems( BOOL bEnable );
+
+ // Handles, Locks
+ HGENMENU m_hMenuRoot;
+ HANDLE m_hStatusMind;
+
+ HANDLE signon_lock_;
+ HANDLE avatar_lock_;
+ HANDLE log_lock_;
+ HANDLE update_loop_lock_;
+
+ HANDLE m_hNetlibUser;
+
+ std::string last_status_msg_;
+ std::tstring def_avatar_folder_;
+ HANDLE hAvatarFolder_;
+ std::vector<HANDLE> avatar_queue;
+
+ static void CALLBACK APC_callback(ULONG_PTR p);
+
+ // Information providing
+ int Log(const char *fmt,...);
+ void NotifyEvent(TCHAR* title, TCHAR* info, HANDLE contact, DWORD flags, TCHAR* url=NULL);
+};
diff --git a/protocols/FacebookRM/src/resource.h b/protocols/FacebookRM/src/resource.h new file mode 100644 index 0000000000..887119bd4b --- /dev/null +++ b/protocols/FacebookRM/src/resource.h @@ -0,0 +1,70 @@ +//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by facebook.rc
+//
+#define IDI_FACEBOOK 101
+#define IDI_MIND 102
+#define IDI_AUTH_GRANT 103
+#define IDI_AUTH_ASK 104
+#define IDI_AUTH_REVOKE 105
+#define IDD_FACEBOOKACCOUNT 111
+#define IDD_MIND 112
+#define IDD_OPTIONS 113
+#define IDD_OPTIONS_EVENTS 114
+#define IDD_OPTIONS_ADVANCED 115
+#define IDC_UN 1001
+#define IDC_PW 1002
+#define IDC_NEWACCOUNTLINK 1003
+#define IDC_MINDMSG 1012
+#define IDC_CHARACTERS 1013
+#define IDC_GROUP 1021
+#define IDC_SECURE 1024
+#define IDC_SET_IGNORE_STATUS 1025
+#define IDC_SECURE_CHANNEL 1026
+#define IDC_LOGGING 1027
+#define IDC_DISCONNECT_CHAT 1028
+#define IDC_PARSE_UNREAD 1029
+#define IDC_BIGGER_AVATARS 1030
+#define IDC_CLOSE_WINDOWS 1031
+#define IDC_MAP_STATUSES 1032
+#define IDC_LOAD_MOBILE 1033
+#define IDC_GROUPCHATS 1034
+#define IDC_NOTIFICATIONS_ENABLE 1041
+#define IDC_FEEDS_ENABLE 1042
+#define IDC_OTHER_ENABLE 1043
+#define IDC_CLIENT_ENABLE 1044
+#define IDC_COLBACK 1051
+#define IDC_COLTEXT 1052
+#define IDC_COLBACK2 1053
+#define IDC_COLTEXT2 1054
+#define IDC_COLBACK3 1055
+#define IDC_COLTEXT3 1056
+#define IDC_COLBACK4 1057
+#define IDC_COLTEXT4 1058
+#define IDC_NOTIFICATIONS_DEFAULT 1071
+#define IDC_FEEDS_DEFAULT 1072
+#define IDC_OTHER_DEFAULT 1073
+#define IDC_CLIENT_DEFAULT 1074
+#define IDC_TIMEOUT 1081
+#define IDC_TIMEOUT_SPIN 1082
+#define IDC_TIMEOUT2 1083
+#define IDC_TIMEOUT_SPIN2 1084
+#define IDC_TIMEOUT3 1085
+#define IDC_TIMEOUT_SPIN3 1086
+#define IDC_TIMEOUT4 1087
+#define IDC_TIMEOUT_SPIN4 1088
+#define IDC_SYSTRAY_NOTIFY 1098
+#define IDC_PREVIEW 1099
+#define IDC_SET_STATUS 1126
+#define IDC_FEED_TYPE 1201
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 126
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1202
+#define _APS_NEXT_SYMED_VALUE 131
+#endif
+#endif
diff --git a/protocols/FacebookRM/src/stubs.cpp b/protocols/FacebookRM/src/stubs.cpp new file mode 100644 index 0000000000..25d3f45252 --- /dev/null +++ b/protocols/FacebookRM/src/stubs.cpp @@ -0,0 +1,120 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "common.h"
+
+HANDLE FacebookProto::AddToListByEvent(int flags,int iContact,HANDLE hDbEvent)
+{
+ return NULL;
+}
+
+int FacebookProto::AuthRecv(HANDLE hContact,PROTORECVEVENT *)
+{
+ return 1;
+}
+
+HANDLE FacebookProto::ChangeInfo(int type,void *info_data)
+{
+ MessageBoxA(0,"ChangeInfo","",0);
+ return NULL;
+}
+
+HANDLE FacebookProto::FileAllow(HANDLE hContact,HANDLE hTransfer,const PROTOCHAR *path)
+{
+ return NULL;
+}
+
+int FacebookProto::FileCancel(HANDLE hContact,HANDLE hTransfer)
+{
+ return 1;
+}
+
+int FacebookProto::FileDeny(HANDLE hContact,HANDLE hTransfer,const PROTOCHAR *reason)
+{
+ return 1;
+}
+
+int FacebookProto::FileResume(HANDLE hTransfer,int *action,const PROTOCHAR **filename)
+{
+ return 1;
+}
+
+int FacebookProto::GetInfo( HANDLE hContact, int infoType )
+{
+ // TODO: Most probably some ProtoAck should be here instead
+ return 1;
+}
+
+HWND FacebookProto::SearchAdvanced(HWND owner)
+{
+ return NULL;
+}
+
+HWND FacebookProto::CreateExtendedSearchUI(HWND owner)
+{
+ return NULL;
+}
+
+int FacebookProto::RecvContacts(HANDLE hContact,PROTORECVEVENT *)
+{
+ return 1;
+}
+
+int FacebookProto::RecvFile(HANDLE hContact,PROTORECVFILET *)
+{
+ return 1;
+}
+
+int FacebookProto::RecvUrl(HANDLE hContact,PROTORECVEVENT *)
+{
+ return 1;
+}
+
+int FacebookProto::SendContacts(HANDLE hContact,int flags,int nContacts,HANDLE *hContactsList)
+{
+ return 1;
+}
+
+HANDLE FacebookProto::SendFile(HANDLE hContact,const PROTOCHAR *desc, PROTOCHAR **files)
+{
+ return NULL;
+}
+
+int FacebookProto::SendUrl(HANDLE hContact,int flags,const char *url)
+{
+ return 1;
+}
+
+int FacebookProto::SetApparentMode(HANDLE hContact,int mode)
+{
+ return 1;
+}
+
+int FacebookProto::RecvAwayMsg(HANDLE hContact,int mode,PROTORECVEVENT *evt)
+{
+ return 1;
+}
+
+int FacebookProto::SendAwayMsg(HANDLE hContact,HANDLE hProcess,const char *msg)
+{
+ return 1;
+}
diff --git a/protocols/FacebookRM/src/theme.cpp b/protocols/FacebookRM/src/theme.cpp new file mode 100644 index 0000000000..1fdbeace0b --- /dev/null +++ b/protocols/FacebookRM/src/theme.cpp @@ -0,0 +1,289 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "common.h"
+
+extern OBJLIST<FacebookProto> g_Instances;
+
+struct
+{
+ const char* name;
+ char* descr;
+ int defIconID;
+ const char* section;
+}
+static const icons[] =
+{
+ { "facebook", LPGEN("Facebook Icon"), IDI_FACEBOOK },
+ { "mind", LPGEN("Mind"), IDI_MIND },
+
+ { "authRevoke", LPGEN("Cancel friendship"), IDI_AUTH_REVOKE },
+ { "authRevokeReq", LPGEN("Cancel friendship request"), IDI_AUTH_REVOKE },
+ { "authAsk", LPGEN("Request friendship"), IDI_AUTH_ASK },
+ { "authGrant", LPGEN("Approve friendship"), IDI_AUTH_GRANT },
+
+ { "homepage", LPGEN("Visit Profile"), 0, "core_main_2" },
+};
+
+static HANDLE hIconLibItem[SIZEOF(icons)];
+
+// TODO: uninit
+void InitIcons(void)
+{
+ TCHAR szFile[MAX_PATH];
+ GetModuleFileName(g_hInstance, szFile, SIZEOF(szFile));
+
+ char setting_name[100];
+ char section_name[100];
+
+ SKINICONDESC sid = {0};
+ sid.cbSize = sizeof(SKINICONDESC);
+ sid.ptszDefaultFile = szFile;
+ sid.cx = sid.cy = 16;
+ sid.pszName = setting_name;
+ sid.pszSection = section_name;
+ sid.flags = SIDF_PATH_TCHAR;
+
+ for (int i=0; i<SIZEOF(icons); i++)
+ {
+ if(icons[i].defIconID)
+ {
+ mir_snprintf(setting_name,sizeof(setting_name),"%s_%s","Facebook",icons[i].name);
+
+ if (icons[i].section)
+ {
+ mir_snprintf(section_name,sizeof(section_name),"%s/%s/%s",LPGEN("Protocols"),
+ LPGEN("Facebook"), icons[i].section);
+ } else {
+ mir_snprintf(section_name,sizeof(section_name),"%s/%s",LPGEN("Protocols"),
+ LPGEN("Facebook"));
+ }
+
+ sid.pszDescription = (char*)icons[i].descr;
+ sid.iDefaultIndex = -icons[i].defIconID;
+ hIconLibItem[i] = Skin_AddIcon(&sid);
+ } else { // External icons
+ hIconLibItem[i] = (HANDLE)CallService(MS_SKIN2_GETICONHANDLE,0,
+ (LPARAM)icons[i].section);
+ }
+ }
+}
+
+HANDLE GetIconHandle(const char* name)
+{
+ for(size_t i=0; i<SIZEOF(icons); i++)
+ {
+ if(strcmp(icons[i].name,name) == 0)
+ return hIconLibItem[i];
+ }
+ return 0;
+}
+
+char *GetIconDescription(const char* name)
+{
+ for(size_t i=0; i<SIZEOF(icons); i++)
+ {
+ if(strcmp(icons[i].name,name) == 0)
+ return icons[i].descr;
+ }
+ return "";
+}
+
+// Contact List menu stuff
+HANDLE hHookPreBuildMenu;
+HANDLE g_hContactMenuItems[CMITEMS_COUNT];
+HANDLE g_hContactMenuSvc[CMITEMS_COUNT];
+
+// Helper functions
+static FacebookProto * GetInstanceByHContact(HANDLE hContact)
+{
+ char *proto = reinterpret_cast<char*>( CallService(MS_PROTO_GETCONTACTBASEPROTO,
+ reinterpret_cast<WPARAM>(hContact),0));
+ if(!proto)
+ return 0;
+
+ for(int i=0; i<g_Instances.getCount(); i++)
+ if(!strcmp(proto,g_Instances[i].m_szModuleName))
+ return &g_Instances[i];
+
+ return 0;
+}
+
+template<int (__cdecl FacebookProto::*Fcn)(WPARAM,LPARAM)>
+INT_PTR GlobalService(WPARAM wParam,LPARAM lParam)
+{
+ FacebookProto *proto = GetInstanceByHContact(reinterpret_cast<HANDLE>(wParam));
+ return proto ? (proto->*Fcn)(wParam,lParam) : 0;
+}
+
+static int PrebuildContactMenu(WPARAM wParam,LPARAM lParam)
+{
+ for (size_t i=0; i<SIZEOF(g_hContactMenuItems); i++)
+ {
+ EnableMenuItem(g_hContactMenuItems[i], false);
+ }
+
+ FacebookProto *proto = GetInstanceByHContact(reinterpret_cast<HANDLE>(wParam));
+ return proto ? proto->OnPrebuildContactMenu(wParam,lParam) : 0;
+}
+
+void InitContactMenus()
+{
+ hHookPreBuildMenu = HookEvent(ME_CLIST_PREBUILDCONTACTMENU,PrebuildContactMenu);
+
+ CLISTMENUITEM mi = {sizeof(mi)};
+ mi.flags = CMIF_ICONFROMICOLIB;
+
+ mi.position=-2000006000;
+ mi.icolibItem = GetIconHandle("homepage");
+ mi.pszName = GetIconDescription("homepage");
+ mi.pszService = "FacebookProto/VisitProfile";
+ g_hContactMenuSvc[CMI_VISIT_PROFILE] = CreateServiceFunction(mi.pszService,GlobalService<&FacebookProto::VisitProfile>);
+ g_hContactMenuItems[CMI_VISIT_PROFILE] = Menu_AddContactMenuItem(&mi);
+
+ mi.position=-2000006001;
+ mi.icolibItem = GetIconHandle("authRevoke");
+ mi.pszName = GetIconDescription("authRevoke");
+ mi.pszService = "FacebookProto/CancelFriendship";
+ g_hContactMenuSvc[CMI_AUTH_REVOKE] = CreateServiceFunction(mi.pszService,GlobalService<&FacebookProto::CancelFriendship>);
+ g_hContactMenuItems[CMI_AUTH_REVOKE] = Menu_AddContactMenuItem(&mi);
+
+ mi.position=-2000006001;
+ mi.icolibItem = GetIconHandle("authRevokeReq");
+ mi.pszName = GetIconDescription("authRevokeReq");
+ mi.pszService = "FacebookProto/CancelFriendshipRequest";
+ g_hContactMenuSvc[CMI_AUTH_CANCEL] = CreateServiceFunction(mi.pszService,GlobalService<&FacebookProto::OnCancelFriendshipRequest>);
+ g_hContactMenuItems[CMI_AUTH_CANCEL] = Menu_AddContactMenuItem(&mi);
+
+ mi.position=-2000006002;
+ mi.icolibItem = GetIconHandle("authAsk");
+ mi.pszName = GetIconDescription("authAsk");
+ mi.pszService = "FacebookProto/RequestFriendship";
+ g_hContactMenuSvc[CMI_AUTH_ASK] = CreateServiceFunction(mi.pszService,GlobalService<&FacebookProto::RequestFriendship>);
+ g_hContactMenuItems[CMI_AUTH_ASK] = Menu_AddContactMenuItem(&mi);
+
+ mi.position=-2000006003;
+ mi.icolibItem = GetIconHandle("authGrant");
+ mi.pszName = GetIconDescription("authGrant");
+ mi.pszService = "FacebookProto/ApproveFriendship";
+ g_hContactMenuSvc[CMI_AUTH_GRANT] = CreateServiceFunction(mi.pszService,GlobalService<&FacebookProto::ApproveFriendship>);
+ g_hContactMenuItems[CMI_AUTH_GRANT] = Menu_AddContactMenuItem(&mi);
+}
+
+void UninitContactMenus()
+{
+ for(size_t i=0; i<SIZEOF(g_hContactMenuItems); i++)
+ CallService(MS_CLIST_REMOVECONTACTMENUITEM,(WPARAM)g_hContactMenuItems[i],0);
+
+ for(size_t i=0; i<SIZEOF(g_hContactMenuSvc); i++)
+ DestroyServiceFunction(g_hContactMenuSvc[i]);
+
+ UnhookEvent(hHookPreBuildMenu);
+}
+
+void EnableMenuItem(HANDLE hMenuItem, bool enable)
+{
+ CLISTMENUITEM clmi = {0};
+ clmi.cbSize = sizeof(CLISTMENUITEM);
+ clmi.flags = CMIM_FLAGS;
+ if (!enable)
+ clmi.flags |= CMIF_HIDDEN;
+
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuItem, (LPARAM)&clmi);
+}
+
+int FacebookProto::OnPrebuildContactMenu(WPARAM wParam,LPARAM lParam)
+{
+ HANDLE hContact = reinterpret_cast<HANDLE>(wParam);
+
+ EnableMenuItem(g_hContactMenuItems[CMI_VISIT_PROFILE], true);
+
+ if (!isOffline() && !DBGetContactSettingByte(hContact, m_szModuleName, "ChatRoom", 0))
+ {
+ bool ctrlPressed = (GetKeyState(VK_CONTROL) & 0x8000) != 0;
+
+ BYTE type = DBGetContactSettingByte(hContact, m_szModuleName, FACEBOOK_KEY_CONTACT_TYPE, 0);
+
+ EnableMenuItem(g_hContactMenuItems[CMI_AUTH_ASK], ctrlPressed || type == FACEBOOK_CONTACT_NONE || !type);
+ EnableMenuItem(g_hContactMenuItems[CMI_AUTH_GRANT], ctrlPressed || type == FACEBOOK_CONTACT_APPROVE);
+ EnableMenuItem(g_hContactMenuItems[CMI_AUTH_REVOKE], ctrlPressed || type == FACEBOOK_CONTACT_FRIEND);
+ EnableMenuItem(g_hContactMenuItems[CMI_AUTH_CANCEL], ctrlPressed || type == FACEBOOK_CONTACT_REQUEST);
+ }
+
+ return 0;
+}
+
+int FacebookProto::OnBuildStatusMenu(WPARAM wParam,LPARAM lParam)
+{
+ char text[200];
+ strcpy(text,m_szModuleName);
+ char *tDest = text+strlen(text);
+
+ HGENMENU hRoot;
+ CLISTMENUITEM mi = {sizeof(mi)};
+ mi.pszService = text;
+
+ hRoot = MO_GetProtoRootMenu(m_szModuleName);
+ if (hRoot == NULL)
+ {
+ mi.popupPosition = 500085000;
+ mi.hParentMenu = HGENMENU_ROOT;
+ mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTPOPUP | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED | ( this->isOnline() ? 0 : CMIF_GRAYED );
+ mi.icolibItem = GetIconHandle( "facebook" );
+ mi.ptszName = m_tszUserName;
+ hRoot = m_hMenuRoot = Menu_AddProtoMenuItem(&mi);
+ } else {
+ if ( m_hMenuRoot )
+ CallService( MS_CLIST_REMOVEMAINMENUITEM, ( WPARAM )m_hMenuRoot, 0 );
+ m_hMenuRoot = NULL;
+ }
+
+ mi.flags = CMIF_ICONFROMICOLIB | CMIF_CHILDPOPUP | ( this->isOnline() ? 0 : CMIF_GRAYED );
+ mi.position = 201001;
+
+ CreateProtoService(m_szModuleName,"/Mind",&FacebookProto::OnMind,this);
+ strcpy(tDest,"/Mind");
+ mi.hParentMenu = hRoot;
+ mi.pszName = LPGEN("Mind...");
+ mi.icolibItem = GetIconHandle("mind");
+ m_hStatusMind = Menu_AddProtoMenuItem(&mi);
+
+ CreateProtoService(m_szModuleName,"/VisitProfile",&FacebookProto::VisitProfile,this);
+ strcpy(tDest,"/VisitProfile");
+ mi.flags = CMIF_ICONFROMICOLIB | CMIF_CHILDPOPUP;
+ mi.pszName = LPGEN("Visit Profile");
+ mi.icolibItem = GetIconHandle("homepage");
+ // TODO RM: remember and properly free in destructor?
+ /*m_hStatusMind = */Menu_AddProtoMenuItem(&mi);
+
+ return 0;
+}
+
+void FacebookProto::ToggleStatusMenuItems( BOOL bEnable )
+{
+ CLISTMENUITEM clmi = { 0 };
+ clmi.cbSize = sizeof( CLISTMENUITEM );
+ clmi.flags = CMIM_FLAGS | (( bEnable ) ? 0 : CMIF_GRAYED);
+
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_hMenuRoot, ( LPARAM )&clmi );
+ CallService( MS_CLIST_MODIFYMENUITEM, ( WPARAM )m_hStatusMind, ( LPARAM )&clmi );
+}
diff --git a/protocols/FacebookRM/src/theme.h b/protocols/FacebookRM/src/theme.h new file mode 100644 index 0000000000..4071fc0669 --- /dev/null +++ b/protocols/FacebookRM/src/theme.h @@ -0,0 +1,39 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+void InitIcons(void);
+HANDLE GetIconHandle(const char *name);
+
+void InitContactMenus(void);
+void UninitContactMenus(void);
+void EnableMenuItem(HANDLE hMenuItem, bool enable);
+
+/* Contact menu item indexes */
+#define CMI_VISIT_PROFILE 0
+#define CMI_AUTH_REVOKE 1
+#define CMI_AUTH_ASK 2
+#define CMI_AUTH_GRANT 3
+#define CMI_AUTH_CANCEL 4
+
+#define CMITEMS_COUNT 5
\ No newline at end of file diff --git a/protocols/FacebookRM/src/utils.cpp b/protocols/FacebookRM/src/utils.cpp new file mode 100644 index 0000000000..8e33b26e01 --- /dev/null +++ b/protocols/FacebookRM/src/utils.cpp @@ -0,0 +1,449 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "common.h"
+
+std::string utils::url::encode(const std::string &s)
+{
+ char *encoded = reinterpret_cast<char*>(CallService( MS_NETLIB_URLENCODE,
+ 0,reinterpret_cast<LPARAM>(s.c_str())));
+ std::string ret = encoded;
+ HeapFree(GetProcessHeap(),0,encoded);
+
+ return ret;
+}
+
+std::string utils::url::decode(std::string data)
+{
+ // TODO: Better and universal method?
+ utils::text::replace_all( &data, "%2F", "/" );
+ utils::text::replace_all( &data, "%3F", "?" );
+ utils::text::replace_all( &data, "%3D", "=" );
+ utils::text::replace_all( &data, "%26", "&" );
+
+ return data;
+}
+
+std::string utils::time::unix_timestamp( )
+{
+ time_t in = ::time( NULL );
+ return utils::conversion::to_string( ( void* )&in, UTILS_CONV_TIME_T );
+}
+
+std::string utils::time::mili_timestamp( )
+{
+ SYSTEMTIME st;
+ std::string timestamp = utils::time::unix_timestamp();
+ GetSystemTime(&st);
+ timestamp.append(utils::conversion::to_string( ( void* )&st.wMilliseconds, UTILS_CONV_UNSIGNED_NUMBER ));
+ return timestamp;
+}
+
+DWORD utils::time::fix_timestamp( double mili_timestamp )
+{
+ // If it is really mili_timestamp
+ if (mili_timestamp > 100000000000) {
+ return (DWORD) (mili_timestamp / 1000);
+ }
+ return (DWORD) mili_timestamp;
+}
+
+DWORD utils::conversion::to_timestamp( std::string data )
+{
+ DWORD timestamp = NULL;
+ if (!utils::conversion::from_string<DWORD>(timestamp, data, std::dec)) {
+ timestamp = static_cast<DWORD>(::time(NULL));
+ }
+ return timestamp;
+}
+
+std::string utils::conversion::to_string( void* data, WORD type )
+{
+ std::stringstream out;
+
+ switch ( type )
+ {
+ case UTILS_CONV_BOOLEAN:
+ out << (data ? "true" : "false");
+
+ case UTILS_CONV_TIME_T:
+ out << (*( time_t* )data);
+ break;
+
+ case UTILS_CONV_SIGNED_NUMBER:
+ out << (*( signed int* )data);
+ break;
+
+ case UTILS_CONV_UNSIGNED_NUMBER:
+ out << (*( unsigned int* )data);
+ break;
+ }
+
+ return out.str( );
+}
+
+void utils::text::replace_first( std::string* data, std::string from, std::string to )
+{
+ std::string::size_type position = data->find(from);
+ if ( position != std::string::npos )
+ {
+ data->replace( position, from.size(), to );
+ }
+}
+
+void utils::text::replace_all( std::string* data, std::string from, std::string to )
+{
+ std::string::size_type position = 0;
+
+ while ( ( position = data->find( from, position )) != std::string::npos )
+ {
+ data->replace( position, from.size(), to );
+ position++;
+ }
+}
+
+unsigned int utils::text::count_all( std::string* data, std::string term )
+{
+ unsigned int count = 0;
+ std::string::size_type position = 0;
+
+ while ( ( position = data->find( term, position )) != std::string::npos )
+ {
+ count++;
+ position++;
+ }
+
+ return count;
+}
+
+std::string utils::text::special_expressions_decode( std::string data )
+{
+ utils::text::replace_all( &data, "&", "&" );
+ utils::text::replace_all( &data, """, "\"" );
+ utils::text::replace_all( &data, "'", "'" );
+ utils::text::replace_all( &data, "@", "@" );
+ utils::text::replace_all( &data, "<", "<" );
+ utils::text::replace_all( &data, ">", ">" );
+
+ utils::text::replace_all( &data, "♥", "\xE2\x99\xA5" ); // direct byte replacement
+// utils::text::replace_all( &data, "♥", "\\u2665" ); // indirect slashu replacement
+
+ utils::text::replace_all( &data, "\\/", "/" );
+ utils::text::replace_all( &data, "\\\\", "\\" );
+
+ // TODO: Add more to comply general usage
+ // http://www.utexas.edu/learn/html/spchar.html
+ // http://www.webmonkey.com/reference/Special_Characters
+ // http://www.degraeve.com/reference/specialcharacters.php
+ // http://www.chami.com/tips/internet/050798i.html
+ // http://www.w3schools.com/tags/ref_entities.asp
+ // http://www.natural-innovations.com/wa/doc-charset.html
+ // http://webdesign.about.com/library/bl_htmlcodes.htm
+
+ return data;
+}
+
+std::string utils::text::edit_html( std::string data )
+{
+ std::string::size_type end = 0;
+ std::string::size_type start = 0;
+ std::string new_string = "";
+
+ while ( end != std::string::npos )
+ {
+ end = data.find( "<span class=\\\"text_exposed_hide", start );
+ if ( end != std::string::npos )
+ {
+ new_string += data.substr( start, end - start );
+ start = data.find( "<\\/span", end );
+ } else {
+ new_string += data.substr( start, data.length() - start );
+ }
+ }
+
+ start = end = 0;
+ data = new_string;
+ new_string = "";
+
+ while ( end != std::string::npos )
+ {
+ end = data.find( "<span class=\\\"uiTooltipText", start );
+ if ( end != std::string::npos )
+ {
+ new_string += data.substr( start, end - start );
+ start = data.find( "<\\/span", end );
+ } else {
+ new_string += data.substr( start, data.length() - start );
+ }
+ }
+
+ // Remove "Translate" link
+ start = end = 0;
+ data = new_string;
+ new_string = "";
+ while ( end != std::string::npos )
+ {
+ end = data.find( "translate_story_link\\\">", start );
+ if ( end != std::string::npos )
+ {
+ new_string += data.substr( start, end - start );
+ start = data.find( "<\\/div", end );
+ } else {
+ new_string += data.substr( start, data.length() - start );
+ }
+ }
+
+ // Append newline after attachement title
+ start = new_string.find( "class=\\\"uiAttachmentTitle", 0 );
+ if ( start != std::string::npos )
+ {
+ data = new_string.substr( 0, start );
+ data = utils::text::trim( data );
+
+ start = new_string.find( ">", start );
+ if ( start != std::string::npos )
+ new_string.insert(start+1, "\n\n");
+
+ start = new_string.find( "<\\/div>", start );
+ if ( start != std::string::npos )
+ new_string.insert(start, "\n");
+ }
+
+ // Append newline between attachement link and description
+ start = new_string.find( "uiAttachmentDesc", 0 );
+ if ( start != std::string::npos )
+ {
+ start = new_string.find( ">", start );
+ if ( start != std::string::npos )
+ new_string.insert(start+1, "\n");
+
+ start = new_string.find( "<\\/div>", start );
+ if ( start != std::string::npos )
+ new_string.insert(start, "\n");
+ }
+
+ utils::text::replace_all( &new_string, "<br \\/>", "\n" );
+ utils::text::replace_all( &new_string, "\n\n\n", "\n\n" );
+ //utils::text::replace_all( &new_string, "\\t", "" );
+ //utils::text::replace_all( &new_string, "\\n", "" );
+ return new_string;
+}
+
+
+std::string utils::text::remove_html( std::string data )
+{
+ std::string new_string = "";
+
+ for ( std::string::size_type i = 0; i < data.length( ); i++ )
+ {
+ if ( data.at(i) == '<' && data.at(i+1) != ' ' )
+ {
+ i = data.find( ">", i );
+ if (i == std::string::npos)
+ break;
+
+ continue;
+ }
+
+ new_string += data.at(i);
+ }
+
+ return new_string;
+}
+
+std::string utils::text::slashu_to_utf8( std::string data )
+{
+ std::string new_string = "";
+
+ for ( std::string::size_type i = 0; i < data.length( ); i++ )
+ {
+ if ( data.at(i) == '\\' && (i+1) < data.length( ) && data.at(i+1) == 'u' )
+ {
+ unsigned int udn = strtol( data.substr( i + 2, 4 ).c_str(), NULL, 16 );
+
+ if ( udn >= 128 && udn <= 2047 )
+ { // U+0080 .. U+07FF
+ new_string += ( char )( 192 + ( udn / 64 ));
+ new_string += ( char )( 128 + ( udn % 64 ));
+ }
+ else if ( udn >= 2048 && udn <= 65535 )
+ { // U+0800 .. U+FFFF
+ new_string += ( char )( 224 + ( udn / 4096 ));
+ new_string += ( char )( 128 + ( ( udn / 64 ) % 64 ));
+ new_string += ( char )( 128 + ( udn % 64 ));
+ }
+ else if ( udn <= 127 )
+ { // U+0000 .. U+007F (should not appear)
+ new_string += ( char )udn;
+ }
+
+ i += 5;
+ continue;
+ }
+
+ new_string += data.at(i);
+ }
+
+ return new_string;
+}
+
+std::string utils::text::trim( std::string data )
+{
+ std::string spaces = " \t\r\n"; // TODO: include "nbsp"?
+ std::string::size_type begin = data.find_first_not_of( spaces );
+ std::string::size_type end = data.find_last_not_of( spaces ) + 1;
+
+ return (begin != std::string::npos) ? data.substr( begin, end - begin ) : "";
+}
+
+void utils::text::explode(std::string str, std::string separator, std::vector<std::string>* results)
+{
+ std::string::size_type pos;
+ pos = str.find_first_of(separator);
+ while (pos != std::string::npos) {
+ if (pos > 0) {
+ results->push_back(str.substr(0,pos));
+ }
+ str = str.substr(pos+1);
+ pos = str.find_first_of(separator);
+ }
+ if (str.length() > 0) {
+ results->push_back(str);
+ }
+}
+
+std::string utils::text::source_get_value( std::string* data, unsigned int argument_count, ... )
+{
+ va_list arg;
+ std::string ret;
+ std::string::size_type start = 0, end = 0;
+
+ va_start( arg, argument_count );
+
+ for ( unsigned int i = argument_count; i > 0; i-- )
+ {
+ if ( i == 1 )
+ {
+ end = data->find( va_arg( arg, char* ), start );
+ if ( start == std::string::npos || end == std::string::npos )
+ break;
+ ret = data->substr( start, end - start );
+ } else {
+ std::string term = va_arg( arg, char* );
+ start = data->find( term, start );
+ if ( start == std::string::npos )
+ break;
+ start += term.length();
+ }
+ }
+
+ va_end( arg );
+ return ret;
+}
+
+std::string utils::text::source_get_value2( std::string* data, const char *term, const char *endings)
+{
+ std::string::size_type start = 0, end = 0;
+ std::string ret;
+
+ start = data->find(term);
+ if (start != std::string::npos) {
+ start += strlen(term);
+
+ end = data->find_first_of(endings, start);
+ if (end != std::string::npos) {
+ ret = data->substr( start, end - start );
+ }
+ }
+
+ return ret;
+}
+
+int utils::number::random( )
+{
+ srand( ::time( NULL ));
+ return rand( );
+}
+
+int utils::debug::log(std::string file_name, std::string text)
+{
+ char szFile[MAX_PATH];
+ GetModuleFileNameA(g_hInstance, szFile, SIZEOF(szFile));
+ std::string path = szFile;
+ path = path.substr( 0, path.rfind( "\\" ));
+ path = path.substr( 0, path.rfind( "\\" ) + 1 );
+ path = path + file_name.c_str() + ".txt";
+
+ SYSTEMTIME time;
+ GetLocalTime( &time );
+
+ std::ofstream out( path.c_str(), std::ios_base::out | std::ios_base::app | std::ios_base::ate );
+ out << "[" << (time.wHour < 10 ? "0" : "") << time.wHour << ":" << (time.wMinute < 10 ? "0" : "") << time.wMinute << ":" << (time.wSecond < 10 ? "0" : "") << time.wSecond << "] " << text << std::endl;
+ out.close( );
+
+ return EXIT_SUCCESS;
+}
+
+void __fastcall utils::mem::detract(char** str )
+{
+ utils::mem::detract( ( void** )str );
+}
+
+void __fastcall utils::mem::detract(void** p)
+{
+ utils::mem::detract((void*)(*p));
+}
+
+void __fastcall utils::mem::detract(void* p)
+{
+ mir_free(p);
+}
+
+void* __fastcall utils::mem::allocate(size_t size)
+{
+ return mir_calloc(size);
+}
+
+struct
+{
+ char *ext;
+ int fmt;
+}
+static formats[] = {
+ { ".png", PA_FORMAT_PNG },
+ { ".jpg", PA_FORMAT_JPEG },
+ { ".jpeg", PA_FORMAT_JPEG },
+ { ".ico", PA_FORMAT_ICON },
+ { ".bmp", PA_FORMAT_BMP },
+ { ".gif", PA_FORMAT_GIF },
+};
+
+int ext_to_format(const std::string &ext)
+{
+ for(size_t i=0; i<SIZEOF(formats); i++)
+ {
+ if(ext == formats[i].ext)
+ return formats[i].fmt;
+ }
+
+ return PA_FORMAT_UNKNOWN;
+}
diff --git a/protocols/FacebookRM/src/utils.h b/protocols/FacebookRM/src/utils.h new file mode 100644 index 0000000000..75712d6a2d --- /dev/null +++ b/protocols/FacebookRM/src/utils.h @@ -0,0 +1,149 @@ +/*
+
+Facebook plugin for Miranda Instant Messenger
+_____________________________________________
+
+Copyright © 2009-11 Michal Zelinka, 2011-12 Robert Pösel
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program 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 General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#pragma once
+
+// C++ bool type
+#define UTILS_CONV_BOOLEAN 0x0001 // true | false
+// signed regular numbers
+#define UTILS_CONV_SIGNED_NUMBER 0x0010 // 1234 | -1234
+// unsigned regular numbers
+#define UTILS_CONV_UNSIGNED_NUMBER 0x0020 // 1234
+// miscellaneous
+#define UTILS_CONV_TIME_T 0x0040 // 1234567890
+
+template<typename T>
+void CreateProtoService(const char *module,const char *service,
+ int (__cdecl T::*serviceProc)(WPARAM,LPARAM),T *self)
+{
+ char temp[MAX_PATH*2];
+
+ mir_snprintf(temp,sizeof(temp),"%s%s",module,service);
+ CreateServiceFunctionObj(temp,( MIRANDASERVICEOBJ )*(void**)&serviceProc, self );
+}
+
+template<typename T>
+void HookProtoEvent(const char* evt, int (__cdecl T::*eventProc)(WPARAM,LPARAM), T *self)
+{
+ ::HookEventObj(evt,(MIRANDAHOOKOBJ)*(void**)&eventProc,self);
+}
+
+template<typename T>
+HANDLE ForkThreadEx(void (__cdecl T::*thread)(void*),T *self,void *data = 0)
+{
+ return reinterpret_cast<HANDLE>( mir_forkthreadowner(
+ (pThreadFuncOwner)*(void**)&thread,self,data,0));
+}
+
+template<typename T>
+void ForkThread(void (__cdecl T::*thread)(void*),T *self,void *data = 0)
+{
+ CloseHandle(ForkThreadEx(thread,self,data));
+}
+
+namespace utils
+{
+ namespace url
+ {
+ std::string encode(const std::string &s);
+ std::string decode(std::string data);
+ };
+
+ namespace time
+ {
+ std::string unix_timestamp( );
+ std::string mili_timestamp( );
+ DWORD fix_timestamp( double );
+ };
+
+ namespace number
+ {
+ int random( );
+ };
+
+ namespace text
+ {
+ void replace_first( std::string* data, std::string from, std::string to );
+ void replace_all( std::string* data, std::string from, std::string to );
+ unsigned int count_all( std::string* data, std::string term );
+ std::string special_expressions_decode( std::string data );
+ std::string edit_html( std::string data );
+ std::string remove_html( std::string data );
+ std::string slashu_to_utf8( std::string data );
+ std::string trim( std::string data );
+ std::string source_get_value( std::string* data, unsigned int argument_count, ... );
+ std::string source_get_value2( std::string* data, const char *term, const char *endings);
+ void explode(std::string str, std::string separator, std::vector<std::string>* results);
+ };
+
+ namespace conversion
+ {
+ DWORD to_timestamp( std::string data );
+ std::string to_string( void*, WORD type );
+
+ template <class T>
+ bool from_string(T& t, const std::string& s, std::ios_base& (*f)(std::ios_base&)) {
+ std::istringstream iss(s);
+ return !(iss >> f >> t).fail();
+ }
+ };
+
+ namespace debug
+ {
+ int log(std::string file_name, std::string text);
+ };
+
+ namespace mem
+ {
+ void __fastcall detract(char** str );
+ void __fastcall detract(void** p);
+ void __fastcall detract(void* p);
+ void* __fastcall allocate(size_t size);
+ };
+};
+
+class ScopedLock
+{
+public:
+ ScopedLock(HANDLE h, int t = INFINITE) : handle_(h), timeout_(t)
+ {
+ WaitForSingleObject(handle_,timeout_);
+ }
+ ~ScopedLock()
+ {
+ if(handle_)
+ ReleaseMutex(handle_);
+ }
+ void Unlock()
+ {
+ ReleaseMutex(handle_);
+ handle_ = 0;
+ }
+private:
+ HANDLE handle_;
+ int timeout_;
+};
+
+int ext_to_format(const std::string &ext);
+
+void MB( const char* m );
+void MBI( int a );
|