diff options
Diffstat (limited to 'protocols/Slack/src')
24 files changed, 1557 insertions, 0 deletions
diff --git a/protocols/Slack/src/api/api_users.h b/protocols/Slack/src/api/api_users.h new file mode 100644 index 0000000000..5037e97374 --- /dev/null +++ b/protocols/Slack/src/api/api_users.h @@ -0,0 +1,18 @@ +#ifndef _SLACK_API_USERS_H_ +#define _SLACK_API_USERS_H_ + +class GetUserListRequest : public HttpRequest +{ +public: +	GetUserListRequest(const char *token) : +		HttpRequest(HttpMethod::HttpPost, SLACK_API_URL "/users.list") +	{ +		Headers +			<< CHAR_VALUE("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); +		Content +			<< CHAR_VALUE("token", token) +			<< CHAR_VALUE("presence", "true"); +	} +}; + +#endif //_SLACK_API_USERS_H_
\ No newline at end of file diff --git a/protocols/Slack/src/http_request.h b/protocols/Slack/src/http_request.h new file mode 100644 index 0000000000..03f5894c6f --- /dev/null +++ b/protocols/Slack/src/http_request.h @@ -0,0 +1,347 @@ +#ifndef _HTTP_REQUEST_H_ +#define _HTTP_REQUEST_H_ + +typedef void (CSlackProto::*HttpCallback)(NETLIBHTTPREQUEST*, struct AsyncHttpRequest*); + +class HttpRequest; +class HttpResponse; + +struct VALUE +{ +	LPCSTR szName; +	__forceinline VALUE(LPCSTR _name) : szName(_name) { } +}; + +struct INT_VALUE : public VALUE +{ +	int iValue; +	__forceinline INT_VALUE(LPCSTR _name, int _value) +		: VALUE(_name), iValue(_value) { } +}; + +struct LONG_VALUE : public VALUE +{ +	LONGLONG llValue; +	__forceinline LONG_VALUE(LPCSTR _name, LONGLONG _value) +		: VALUE(_name), llValue(_value) { } +}; + +struct CHAR_VALUE : public VALUE +{ +	LPCSTR szValue; +	__forceinline CHAR_VALUE(LPCSTR _name, LPCSTR _value) +		: VALUE(_name), szValue(_value) { } +}; + +struct ENCODED_VALUE : public VALUE +{ +	LPSTR szValue; +	__forceinline ENCODED_VALUE(LPCSTR _name, LPCSTR _value) +		: VALUE(_name) { szValue = mir_urlEncode(_value); } +	__forceinline ~ENCODED_VALUE() { mir_free(szValue); } +}; + +class HttpUri +{ +	friend class HttpRequest; + +private: +	CMStringA m_uri; +	NETLIBHTTPREQUEST *m_request; + +	HttpUri(NETLIBHTTPREQUEST *request, const char *uri) +		: m_request(request), m_uri(uri) +	{ +		if (m_request) +			m_request->szUrl = m_uri.GetBuffer(); +	} + +	HttpUri(NETLIBHTTPREQUEST *request, const char *urlFormat, va_list args) +		: m_request(request) +	{ +		m_uri.AppendFormatV(urlFormat, args); +		if (m_request) +			m_request->szUrl = m_uri.GetBuffer(); +	} + +	void Add(const char *fmt, ...) +	{ +		va_list args; +		va_start(args, fmt); +		m_uri += (m_uri.Find('?') == -1) ? '?' : '&'; +		m_uri.AppendFormatV(fmt, args); +		va_end(args); +		 +		if (m_request) +			m_request->szUrl = m_uri.GetBuffer(); +	} + +	~HttpUri() +	{ +		if (m_request) +			m_request->szUrl = NULL; +	} + +public: +	HttpUri& operator=(const HttpUri&); // to prevent copying; + +	operator const char*() +	{ +		return m_request +			? m_request->szUrl +			: NULL; +	} + +	HttpUri &operator<<(const VALUE ¶m) +	{ +		Add(param.szName); +		return *this; +	} + +	HttpUri &operator<<(const INT_VALUE ¶m) +	{ +		Add("%s=%i", param.szName, param.iValue); +		return *this; +	} + +	HttpUri &operator<<(const LONG_VALUE ¶m) +	{ +		Add("%s=%lld", param.szName, param.llValue); +		return *this; +	} + +	HttpUri &operator<<(const CHAR_VALUE ¶m) +	{ +		Add("%s=%s", param.szName, param.szValue); +		return *this; +	} + +	HttpUri &operator<<(const ENCODED_VALUE ¶m) +	{ +		Add("%s=%s", param.szName, param.szValue); +		return *this; +	} +}; + +class HttpHeaders +{ +	friend class HttpRequest; +	friend class HttpResponse; + +private: +	NETLIBHTTPREQUEST *m_request; + +	HttpHeaders(NETLIBHTTPREQUEST *request) +		: m_request(request) +	{ +	} + +	void Add(LPCSTR szName) +	{ +		Add(szName, ""); +	} + +	void Add(LPCSTR szName, LPCSTR szValue) +	{ +		if (!m_request) +			return; + +		m_request->headers = (NETLIBHTTPHEADER*)mir_realloc(m_request->headers, +			sizeof(NETLIBHTTPHEADER)*(m_request->headersCount + 1)); +		m_request->headers[m_request->headersCount].szName = mir_strdup(szName); +		m_request->headers[m_request->headersCount].szValue = mir_strdup(szValue); +		m_request->headersCount++; +	} + +public: +	HttpHeaders& operator=(const HttpHeaders&); // to prevent copying; + +	const NETLIBHTTPHEADER* operator[](size_t idx) +	{ +		return m_request +			? &m_request->headers[idx] +			: NULL; +	} + +	HttpHeaders& operator<<(const VALUE ¶m) +	{ +		Add(param.szName); +		return *this; +	} + +	HttpHeaders& operator<<(const CHAR_VALUE ¶m) +	{ +		Add(param.szName, param.szValue); +		return *this; +	} + +	HttpHeaders& operator<<(const ENCODED_VALUE ¶m) +	{ +		Add(param.szName, param.szValue); +		return *this; +	} +}; + +class HttpContent +{ +	friend class HttpRequest; +	friend class HttpResponse; + +protected: +	CMStringA m_content; +	NETLIBHTTPREQUEST *m_request; + +	HttpContent(NETLIBHTTPREQUEST *request) +		: m_request(request) +	{ +	} + +	~HttpContent() +	{ +		if (m_request) +		{ +			m_request->pData = NULL; +			m_request->dataLength = 0; +		} +	} + +	void Add(const char *fmt, ...) +	{ +		va_list args; +		va_start(args, fmt); +		if (!m_content.IsEmpty()) +			m_content += '&'; +		m_content.AppendFormatV(fmt, args); +		va_end(args); + +		if (m_request) +		{ +			m_request->pData = m_content.GetBuffer(); +			m_request->dataLength = m_content.GetLength(); +		} +	} + +public: +	HttpContent& operator=(const HttpContent&); // to prevent copying; + +	bool operator!() const +	{ +		return !m_request || !m_request->pData || !m_request->dataLength; +	} + +	operator const char*() +	{ +		return m_request +			? m_request->pData +			: NULL; +	} + +	virtual size_t GetSize() const +	{ +		return m_request +			? m_request->dataLength +			: 0; +	} + +	HttpContent & operator<<(const VALUE ¶m) +	{ +		Add(param.szName); +		return *this; +	} + +	HttpContent & operator<<(const INT_VALUE ¶m) +	{ +		Add("%s=%i", param.szName, param.iValue); +		return *this; +	} + +	HttpContent & operator<<(const LONG_VALUE ¶m) +	{ +		Add("%s=%lld", param.szName, param.llValue); +		return *this; +	} + +	HttpContent & operator<<(const CHAR_VALUE ¶m) +	{ +		Add("%s=%s", param.szName, param.szValue); +		return *this; +	} + +	HttpContent &operator<<(const ENCODED_VALUE ¶m) +	{ +		Add("%s=%s", param.szName, param.szValue); +		return *this; +	} +}; + +enum HttpMethod +{ +	HttpGet = 1, +	HttpPost +}; + +class HttpResponse +{ +	friend class HttpRequest; + +private: +	NETLIBHTTPREQUEST *m_response; + +public: +	HttpRequest *Request; +	HttpHeaders Headers; +	HttpContent Content; + +	HttpResponse(HttpRequest *request, NETLIBHTTPREQUEST *response) +		: Request(request), m_response(response), +		Headers(response), Content(response) +	{ +	} + +	~HttpResponse() +	{ +		Netlib_FreeHttpRequest(m_response); +	} + +	bool IsSuccess() const +	{ +		return m_response->resultCode >= HTTP_CODE_OK && +			m_response->resultCode <= HTTP_CODE_MULTI_STATUS; +	} + +	int GetStatusCode() const +	{ +		return m_response->resultCode; +	} +}; + +class HttpRequest : protected NETLIBHTTPREQUEST, public MZeroedObject +{ +	friend class HttpUri; +	friend class HttpHeaders; +	friend class HttpContent; +	friend class FormContent; + +public: +	HttpUri Uri; +	HttpHeaders Headers; +	HttpContent Content; + +	HttpRequest(HttpMethod method, const char *url) +		: Uri(this, url), Headers(this), Content(this) +	{ +		cbSize = sizeof(NETLIBHTTPREQUEST); +		requestType = method; +	} + +	~HttpRequest() +	{ +	} + +	operator NETLIBHTTPREQUEST*() +	{ +		return this; +	} +}; + +#endif //_HTTP_REQUEST_H_
\ No newline at end of file diff --git a/protocols/Slack/src/main.cpp b/protocols/Slack/src/main.cpp new file mode 100644 index 0000000000..f74403708b --- /dev/null +++ b/protocols/Slack/src/main.cpp @@ -0,0 +1,54 @@ +#include "stdafx.h" + +int hLangpack; +HINSTANCE g_hInstance; + +PLUGININFOEX pluginInfo = +{ +	sizeof(PLUGININFOEX), +	__PLUGIN_NAME, +	PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), +	__DESCRIPTION, +	__AUTHOR, +	__AUTHOREMAIL, +	__COPYRIGHT, +	__AUTHORWEB, +	UNICODE_AWARE, +	// {5487475F-00C2-4D88-AE41-C5969260D455} +	{ 0x5487475f, 0xc2, 0x4d88, {0xae, 0x41, 0xc5, 0x96, 0x92, 0x60, 0xd4, 0x55 }} +}; + +DWORD WINAPI DllMain(HINSTANCE hInstance, DWORD, LPVOID) +{ +	g_hInstance = hInstance; + +	return TRUE; +} + +extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD) +{ +	return &pluginInfo; +} + +extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_PROTOCOL, MIID_LAST }; + +extern "C" int __declspec(dllexport) Load(void) +{ +	mir_getLP(&pluginInfo); + +	PROTOCOLDESCRIPTOR pd = { sizeof(pd) }; +	pd.szName = "SLACK"; +	pd.type = PROTOTYPE_PROTOCOL; +	pd.fnInit = (pfnInitProto)CSlackProto::InitAccount; +	pd.fnUninit = (pfnUninitProto)CSlackProto::UninitAccount; +	Proto_RegisterModule(&pd); + +	HookEvent(ME_SYSTEM_MODULESLOADED, &CSlackProto::OnModulesLoaded); + +	return 0; +} + +extern "C" int __declspec(dllexport) Unload(void) +{ +	return 0; +} diff --git a/protocols/Slack/src/resource.h b/protocols/Slack/src/resource.h new file mode 100644 index 0000000000..ecd6bd1008 --- /dev/null +++ b/protocols/Slack/src/resource.h @@ -0,0 +1,19 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by E:\Projects\C++\miranda-ng\protocols\Slack\res\resource.rc +// +#define IDI_SLACK                       100 +#define IDD_OAUTH                       120 +#define IDC_OAUTH_CODE                  1082 +#define IDC_OAUTH_AUTHORIZE             1200 + +// Next default values for new objects +//  +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE        121 +#define _APS_NEXT_COMMAND_VALUE         40001 +#define _APS_NEXT_CONTROL_VALUE         1028 +#define _APS_NEXT_SYMED_VALUE           101 +#endif +#endif diff --git a/protocols/Slack/src/slack_accounts.cpp b/protocols/Slack/src/slack_accounts.cpp new file mode 100644 index 0000000000..1887c9fc95 --- /dev/null +++ b/protocols/Slack/src/slack_accounts.cpp @@ -0,0 +1,43 @@ +#include "stdafx.h" + +LIST<CSlackProto> CSlackProto::Accounts(1, CSlackProto::CompareAccounts); + +int CSlackProto::CompareAccounts(const CSlackProto *p1, const CSlackProto *p2) +{ +	return mir_wstrcmp(p1->m_tszUserName, p2->m_tszUserName); +} + +CSlackProto* CSlackProto::InitAccount(const char *protoName, const wchar_t *userName) +{ +	CSlackProto *proto = new CSlackProto(protoName, userName); +	Accounts.insert(proto); +	return proto; +} + +int CSlackProto::UninitAccount(CSlackProto *proto) +{ +	Accounts.remove(proto); +	delete proto; +	return 0; +} + +CSlackProto* CSlackProto::GetContactAccount(MCONTACT hContact) +{ +	for (int i = 0; i < Accounts.getCount(); i++) +		if (mir_strcmpi(GetContactProto(hContact), Accounts[i]->m_szModuleName) == 0) +			return Accounts[i]; +	return NULL; +} + +int CSlackProto::OnAccountLoaded(WPARAM, LPARAM) +{ +	HookProtoEvent(ME_OPT_INITIALISE, &CSlackProto::OnOptionsInit); +	HookProtoEvent(ME_MSG_PRECREATEEVENT, &CSlackProto::OnPreCreateMessage); + +	return 0; +} + +INT_PTR CSlackProto::OnAccountManagerInit(WPARAM, LPARAM lParam) +{ +	return (INT_PTR)(CSlackOptionsMain::CreateAccountManagerPage(this, (HWND)lParam))->GetHwnd(); +}
\ No newline at end of file diff --git a/protocols/Slack/src/slack_connection.cpp b/protocols/Slack/src/slack_connection.cpp new file mode 100644 index 0000000000..b8b0652618 --- /dev/null +++ b/protocols/Slack/src/slack_connection.cpp @@ -0,0 +1,75 @@ +#include "stdafx.h" + +void CSlackProto::Login() +{ +	ptrA token(getStringA("TokenSecret")); +	if (mir_strlen(token)) +	{ +		PushRequest(new GetUserListRequest(token), &CSlackProto::OnGotUserList); +		ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)ID_STATUS_CONNECTING, m_iStatus = m_iDesiredStatus);		 +		return; +	} + +	CSlackOAuth oauth(this); +	if (!oauth.DoModal()) +	{ +		SetStatus(ID_STATUS_OFFLINE); +		return; +	} + +	HttpRequest *request = new HttpRequest(HttpMethod::HttpPost, SLACK_API_URL "/oauth.access"); +	request->Headers +		<< CHAR_VALUE("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); +	request->Content +		<< CHAR_VALUE("client_id", SLACK_CLIENT_ID) +		<< CHAR_VALUE("client_secret", SLACK_CLIENT_SECRET) +		<< ENCODED_VALUE("code", oauth.GetAuthCode()) +		<< ENCODED_VALUE("redirect_uri", SLACK_REDIRECT_URL); + +	PushRequest(request, &CSlackProto::OnAuthorize); +} + +void CSlackProto::OnAuthorize(JSONNode &root) +{ +	if (!root) +	{ +		SetStatus(ID_STATUS_OFFLINE); +		ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, NULL); +		return; +	} + +	bool isOk = root["ok"].as_bool(); +	if (!isOk) +	{ +		SetStatus(ID_STATUS_OFFLINE); +		json_string error = root["error"].as_string(); +		debugLogA(__FUNCTION__": %s", error); +		ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_BADUSERID); +		return; +	} + +	json_string token = root["access_token"].as_string(); +	setString("TokenSecret", token.c_str()); + +	json_string userId = root["user_id"].as_string(); +	setString("UserId", userId.c_str()); + +	CMStringW teamName = root["team_name"].as_mstring(); +	setWString("TeamName", teamName); +	if (!teamName.IsEmpty() > 0 && !Clist_GroupExists(teamName)) +		Clist_GroupCreate(0, teamName); + +	json_string teamId = root["team_id"].as_string(); +	setString("TeamId", userId.c_str()); + +	PushRequest(new GetUserListRequest(token.c_str()), &CSlackProto::OnGotUserList); + +	ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)ID_STATUS_CONNECTING, m_iStatus = m_iDesiredStatus); +} + +void CSlackProto::LogOut() +{ +	isTerminated = true; +	if (hRequestQueueThread) +		SetEvent(hRequestsQueueEvent); +}
\ No newline at end of file diff --git a/protocols/Slack/src/slack_contacts.cpp b/protocols/Slack/src/slack_contacts.cpp new file mode 100644 index 0000000000..185d5110b8 --- /dev/null +++ b/protocols/Slack/src/slack_contacts.cpp @@ -0,0 +1,165 @@ +#include "stdafx.h" + +WORD CSlackProto::GetContactStatus(MCONTACT hContact) +{ +	return getWord(hContact, "Status", ID_STATUS_OFFLINE); +} + +void CSlackProto::SetContactStatus(MCONTACT hContact, WORD status) +{ +	WORD oldStatus = GetContactStatus(hContact); +	if (oldStatus != status) +		setWord(hContact, "Status", status); +} + +void CSlackProto::SetAllContactsStatus(WORD status) +{ +	for (MCONTACT hContact = db_find_first(m_szModuleName); hContact; hContact = db_find_next(hContact, m_szModuleName)) +		SetContactStatus(hContact, status); +} + +MCONTACT CSlackProto::GetContact(const char *userId) +{ +	MCONTACT hContact = NULL; +	for (hContact = db_find_first(m_szModuleName); hContact; hContact = db_find_next(hContact, m_szModuleName)) +	{ +		ptrA contactPhone(getStringA(hContact, "UserId")); +		if (mir_strcmp(userId, contactPhone) == 0) +			break; +	} +	return hContact; +} + +MCONTACT CSlackProto::AddContact(const char *userId, const char *nick, bool isTemporary) +{ +	MCONTACT hContact = GetContact(userId); +	if (!hContact) +	{ +		hContact = db_add_contact(); +		Proto_AddToContact(hContact, m_szModuleName); + +		setString(hContact, "UserId", userId); + +		if (mir_strlen(nick)) +			setWString(hContact, "Nick", ptrW(mir_utf8decodeW(nick))); + +		DBVARIANT dbv; +		if (!getWString("TeamName", &dbv) && Clist_GroupExists(dbv.ptszVal)) +		{ +			db_set_ws(hContact, "CList", "Group", dbv.ptszVal); +			db_free(&dbv); +		} + +		setByte(hContact, "Auth", 1); +		setByte(hContact, "Grant", 1); + +		if (isTemporary) +		{ +			db_set_b(hContact, "CList", "NotOnList", 1); +		} +	} +	return hContact; +} + +void CSlackProto::OnGotUserProfile(JSONNode &root) +{ +	if (!root) +	{ +		debugLogA(__FUNCTION__": failed to load user profile"); +		return; +	} + +	bool isOk = root["ok"].as_bool(); +	if (!isOk) +	{ +		debugLogA(__FUNCTION__": failed to load users profile"); +		return; +	} + +	JSONNode profile = root["profile"].as_node(); +} + +void CSlackProto::OnGotUserProfile(MCONTACT hContact, JSONNode &root) +{ +	if (!root) +	{ +		debugLogA(__FUNCTION__": failed to read users profile"); +		return; +	} + +	CMStringW firstName = root["first_name"].as_mstring(); +	setWString(hContact, "FirstName", firstName); + +	CMStringW lastName = root["last_name"].as_mstring(); +	setWString(hContact, "LastName", lastName); +} + +void CSlackProto::OnGotUserList(JSONNode &root) +{ +	if (!root) +	{ +		debugLogA(__FUNCTION__": failed to load users list"); +		return; +	} + +	bool isOk = root["ok"].as_bool(); +	if (!isOk) +	{ +		debugLogA(__FUNCTION__": failed to load users list"); +		return; +	} + +	JSONNode users = root["members"].as_array(); +	for (size_t i = 0; i < users.size(); i++) +	{ +		JSONNode user = users[i]; + +		json_string userId = user["id"].as_string(); +		json_string nick = user["name"].as_string(); +		bool isDeleted = user["deleted"].as_bool(); +		MCONTACT hContact = AddContact(userId.c_str(), nick.c_str(), isDeleted); +		if (hContact) +		{ +			json_string teamId = user["team_id"].as_string(); +			setString(hContact, "TeamId", teamId.c_str()); + +			CMStringW status = root["status"].as_mstring(); +			if (!status.IsEmpty()) +				setWString(hContact, "StatusMsg", status); + +			JSONNode profile = root["profile"].as_node(); +			OnGotUserProfile(hContact, profile); +		} +	} +} + +INT_PTR CSlackProto::OnRequestAuth(WPARAM hContact, LPARAM lParam) +{ +	if (!IsOnline()) +	{ +		return -1; // ??? +	} +	return 0; +} + +INT_PTR CSlackProto::OnGrantAuth(WPARAM hContact, LPARAM) +{ +	if (!IsOnline()) +	{ +		// TODO: warn +		return 0; +	} + +	return 0; +} + +int CSlackProto::OnContactDeleted(MCONTACT hContact, LPARAM) +{ +	if (!IsOnline()) +	{ +		// TODO: warn +		return 0; +	} + +	return 0; +}
\ No newline at end of file diff --git a/protocols/Slack/src/slack_dialogs.cpp b/protocols/Slack/src/slack_dialogs.cpp new file mode 100644 index 0000000000..b495af842e --- /dev/null +++ b/protocols/Slack/src/slack_dialogs.cpp @@ -0,0 +1,36 @@ +#include "stdafx.h" + +CSlackOAuth::CSlackOAuth(CSlackProto *proto) +	: CSuper(proto, IDD_OAUTH, false), +	m_authorize(this, IDC_OAUTH_AUTHORIZE, SLACK_URL "/oauth/authorize?scope=identify+read+post&redirect_uri=" SLACK_REDIRECT_URL "&client_id=" SLACK_CLIENT_ID), +	m_code(this, IDC_OAUTH_CODE), m_ok(this, IDOK) +{ +	m_ok.OnClick = Callback(this, &CSlackOAuth::OnOk); + +	Utils_RestoreWindowPosition(m_hwnd, NULL, m_proto->m_szModuleName, "OAuthWindow"); +} + +void CSlackOAuth::OnInitDialog() +{ +	CSuper::OnInitDialog(); + +	Window_SetIcon_IcoLib(m_hwnd, m_proto->GetIconHandle(IDI_SLACK)); + +	SendMessage(m_code.GetHwnd(), EM_LIMITTEXT, 40, 0); +} + +void CSlackOAuth::OnOk(CCtrlButton*) +{ +	mir_strncpy(m_authCode, ptrA(m_code.GetTextA()), _countof(m_authCode)); +	EndDialog(m_hwnd, 1); +} + +void CSlackOAuth::OnClose() +{ +	Utils_SaveWindowPosition(m_hwnd, NULL, m_proto->m_szModuleName, "OAuthWindow"); +} + +const char* CSlackOAuth::GetAuthCode() +{ +	return m_authCode; +}
\ No newline at end of file diff --git a/protocols/Slack/src/slack_dialogs.h b/protocols/Slack/src/slack_dialogs.h new file mode 100644 index 0000000000..cd7f78226d --- /dev/null +++ b/protocols/Slack/src/slack_dialogs.h @@ -0,0 +1,26 @@ +#ifndef _SLACK_DIALOGS_H_ +#define _SLACK_DIALOGS_H_ + +class CSlackOAuth : public CProtoDlgBase<CSlackProto> +{ +	typedef CProtoDlgBase<CSlackProto> CSuper; + +private: +	char m_authCode[40]; + +	CCtrlHyperlink m_authorize; +	CCtrlEdit m_code; +	CCtrlButton m_ok; + +protected: +	void OnInitDialog(); +	void OnOk(CCtrlButton*); +	void OnClose(); + +public: +	CSlackOAuth(CSlackProto *proto); + +	const char *GetAuthCode(); +}; + +#endif //_SLACK_DIALOGS_H_ diff --git a/protocols/Slack/src/slack_events.cpp b/protocols/Slack/src/slack_events.cpp new file mode 100644 index 0000000000..233b662594 --- /dev/null +++ b/protocols/Slack/src/slack_events.cpp @@ -0,0 +1,9 @@ +#include "stdafx.h" + +int CSlackProto::OnModulesLoaded(WPARAM, LPARAM) +{ +	CSlackProto::InitIcons(); +	//CSlackProto::InitMenus(); + +	return 0; +}
\ No newline at end of file diff --git a/protocols/Slack/src/slack_icons.cpp b/protocols/Slack/src/slack_icons.cpp new file mode 100644 index 0000000000..2bb652e444 --- /dev/null +++ b/protocols/Slack/src/slack_icons.cpp @@ -0,0 +1,20 @@ +#include "stdafx.h" + +IconItemT CSlackProto::Icons[] = +{ +	{ LPGENW("Protocol icon"),			"main",				IDI_SLACK	}, +}; + +void CSlackProto::InitIcons() +{ +	Icon_RegisterT(g_hInstance, LPGENW("Protocols") L"/" _A2W(MODULE), Icons, _countof(Icons), MODULE); +} + +HANDLE CSlackProto::GetIconHandle(int iconId) +{ +	for (size_t i = 0; i < _countof(Icons); i++) +		if (Icons[i].defIconID == iconId) +			return Icons[i].hIcolib; + +	return NULL; +}
\ No newline at end of file diff --git a/protocols/Slack/src/slack_menus.cpp b/protocols/Slack/src/slack_menus.cpp new file mode 100644 index 0000000000..183971237f --- /dev/null +++ b/protocols/Slack/src/slack_menus.cpp @@ -0,0 +1,57 @@ +#include "stdafx.h" + +HGENMENU CSlackProto::ContactMenuItems[CMI_MAX]; + +int CSlackProto::OnPrebuildContactMenu(WPARAM hContact, LPARAM) +{ +	if (!hContact) +		return 0; + +	if (!this->IsOnline()) +		return 0; + +	if (this->isChatRoom(hContact)) +		return 0; + +	bool isCtrlPressed = (GetKeyState(VK_CONTROL) & 0x8000) != 0; + +	bool isAuthNeed = getByte(hContact, "Auth", 0) > 0; +	Menu_ShowItem(ContactMenuItems[CMI_AUTH_REQUEST], isCtrlPressed || isAuthNeed); + +	bool isGrantNeed = getByte(hContact, "Grant", 0) > 0; +	Menu_ShowItem(ContactMenuItems[CMI_AUTH_GRANT], isCtrlPressed || isGrantNeed); + +	return 0; +} + +int CSlackProto::PrebuildContactMenu(WPARAM hContact, LPARAM lParam) +{ +	for (int i = 0; i < _countof(ContactMenuItems); i++) +		Menu_ShowItem(ContactMenuItems[i], FALSE); +	CSlackProto *proto = CSlackProto::GetContactAccount(hContact); +	return proto ? proto->OnPrebuildContactMenu(hContact, lParam) : 0; +} + +void CSlackProto::InitMenus() +{ +	HookEvent(ME_CLIST_PREBUILDCONTACTMENU, &CSlackProto::PrebuildContactMenu); + +	CMenuItem mi; +	mi.flags = CMIF_UNICODE; +	/* +	// Request authorization +	mi.pszService = MODULE"/RequestAuth"; +	mi.ptszName = LPGENT("Request authorization"); +	mi.position = CMI_POSITION + CMI_AUTH_REQUEST; +	mi.icolibItem = LoadSkinnedIconHandle(SKINICON_AUTH_REQUEST); +	ContactMenuItems[CMI_AUTH_REQUEST] = Menu_AddContactMenuItem(&mi); +	CreateServiceFunction(mi.pszService, GlobalService<&CSlackProto::OnRequestAuth>); + +	// Grant authorization +	mi.pszService = MODULE"/GrantAuth"; +	mi.ptszName = LPGENT("Grant authorization"); +	mi.position = CMI_POSITION + CMI_AUTH_GRANT; +	mi.icolibItem = LoadSkinnedIconHandle(SKINICON_AUTH_GRANT); +	ContactMenuItems[CMI_AUTH_GRANT] = Menu_AddContactMenuItem(&mi); +	CreateServiceFunction(mi.pszService, GlobalService<&CSlackProto::OnGrantAuth>);*/ +} diff --git a/protocols/Slack/src/slack_menus.h b/protocols/Slack/src/slack_menus.h new file mode 100644 index 0000000000..7270a6d6db --- /dev/null +++ b/protocols/Slack/src/slack_menus.h @@ -0,0 +1,20 @@ +#ifndef _SLACK_MENUS_H_ +#define _SLACK_MENUS_H_ + +#define CMI_POSITION -201001000 + +enum CMI_MENU_ITEMS +{ +	CMI_AUTH_REQUEST, +	CMI_AUTH_GRANT, +	CMI_MAX // this item shall be the last one +}; + +#define SMI_POSITION 200000 + +enum SMI_MENU_ITEMS +{ +	SMI_MAX // this item shall be the last one +}; + +#endif //_SLACK_MENUS_H_ diff --git a/protocols/Slack/src/slack_messages.cpp b/protocols/Slack/src/slack_messages.cpp new file mode 100644 index 0000000000..f78ba3e9db --- /dev/null +++ b/protocols/Slack/src/slack_messages.cpp @@ -0,0 +1,49 @@ +#include "stdafx.h" + +/* MESSAGE RECEIVING */ + +// incoming message flow + + +// writing message/even into db +int CSlackProto::OnReceiveMessage(MCONTACT hContact, PROTORECVEVENT *pre) +{ +	//return Proto_RecvMessage(hContact, pre); +	if (pre->szMessage == NULL) +		return NULL; + +	DBEVENTINFO dbei = {}; +	dbei.szModule = GetContactProto(hContact); +	dbei.timestamp = pre->timestamp; +	dbei.flags = 0; +	dbei.eventType = pre->lParam; +	dbei.cbBlob = (DWORD)mir_strlen(pre->szMessage) + 1; +	dbei.pBlob = (PBYTE)pre->szMessage; + +	return (INT_PTR)db_event_add(hContact, &dbei); +} + +/* MESSAGE SENDING */ + +// outcoming message flow +int CSlackProto::OnSendMessage(MCONTACT hContact, int flags, const char *szMessage) +{ +	return 0; +} + +// message is received by the other side + +// preparing message/action to writing into db +int CSlackProto::OnPreCreateMessage(WPARAM, LPARAM lParam) +{ +	MessageWindowEvent *evt = (MessageWindowEvent*)lParam; +	if (mir_strcmp(GetContactProto(evt->hContact), m_szModuleName)) +		return 0; +} + +/* TYPING */ + +int CSlackProto::OnUserIsTyping(MCONTACT hContact, int type) +{ +	return 0; +}
\ No newline at end of file diff --git a/protocols/Slack/src/slack_netlib.cpp b/protocols/Slack/src/slack_netlib.cpp new file mode 100644 index 0000000000..85b832e152 --- /dev/null +++ b/protocols/Slack/src/slack_netlib.cpp @@ -0,0 +1,21 @@ +#include "stdafx.h" + +void CSlackProto::InitNetlib() +{ +	wchar_t name[128]; +	mir_snwprintf(name, TranslateT("%s connection"), m_tszUserName); + +	NETLIBUSER nlu = {}; +	nlu.flags = NUF_OUTGOING | NUF_INCOMING | NUF_HTTPCONNS | NUF_UNICODE; +	nlu.szDescriptiveName.w = name; +	nlu.szSettingsModule = m_szModuleName; +	m_hNetlibUser = Netlib_RegisterUser(&nlu); + +	debugLogA(__FUNCTION__":Setting protocol / module name to '%s'", m_szModuleName); +} + +void CSlackProto::UninitNetlib() +{ +	Netlib_CloseHandle(hNetlib); +	hNetlib = NULL; +} diff --git a/protocols/Slack/src/slack_options.cpp b/protocols/Slack/src/slack_options.cpp new file mode 100644 index 0000000000..49544935c1 --- /dev/null +++ b/protocols/Slack/src/slack_options.cpp @@ -0,0 +1,43 @@ +#include "stdafx.h" + +CSlackOptionsMain::CSlackOptionsMain(CSlackProto *proto, int idDialog) +	: CSuper(proto, idDialog, false), +	m_team(this, IDC_TEAM), m_email(this, IDC_EMAIL), +	m_password(this, IDC_PASSWORD), m_group(this, IDC_GROUP) +{ + +	CreateLink(m_team, "Team", L""); +	CreateLink(m_email, "Email", L""); +	CreateLink(m_password, "Password", L""); +	CreateLink(m_group, "DefaultGroup", _A2W(MODULE)); +} + +void CSlackOptionsMain::OnInitDialog() +{ +	CSuper::OnInitDialog(); + +	SendMessage(m_team.GetHwnd(), EM_LIMITTEXT, 21, 0); +	SendMessage(m_email.GetHwnd(), EM_LIMITTEXT, 40, 0); +	SendMessage(m_password.GetHwnd(), EM_LIMITTEXT, 40, 0); +	SendMessage(m_group.GetHwnd(), EM_LIMITTEXT, 64, 0); +} + +void CSlackOptionsMain::OnApply() +{ +} + +///////////////////////////////////////////////////////////////////////////////// + +int CSlackProto::OnOptionsInit(WPARAM wParam, LPARAM) +{ +	OPTIONSDIALOGPAGE odp = { 0 }; +	odp.szTitle.w = m_tszUserName; +	odp.flags = ODPF_BOLDGROUPS | ODPF_UNICODE | ODPF_DONTTRANSLATE; +	odp.szGroup.w = LPGENW("Network"); + +	odp.szTab.w = LPGENW("Account"); +	odp.pDialog = CSlackOptionsMain::CreateOptionsPage(this); +	//Options_AddPage(wParam, &odp); + +	return 0; +} diff --git a/protocols/Slack/src/slack_options.h b/protocols/Slack/src/slack_options.h new file mode 100644 index 0000000000..e0eeda70a3 --- /dev/null +++ b/protocols/Slack/src/slack_options.h @@ -0,0 +1,32 @@ +#ifndef _SLACK_OPTIONS_H_ +#define _SLACK_OPTIONS_H_ + +class CSlackOptionsMain : public CProtoDlgBase<CSlackProto> +{ +	typedef CProtoDlgBase<CSlackProto> CSuper; + +private: +	CCtrlEdit m_team; +	CCtrlEdit m_email; +	CCtrlEdit m_password; +	CCtrlEdit m_group; + +protected: +	void OnInitDialog(); +	void OnApply(); + +public: +	CSlackOptionsMain(CSlackProto *proto, int idDialog); + +	static CDlgBase *CreateAccountManagerPage(void *param, HWND owner) +	{ +		CSlackOptionsMain *page = new CSlackOptionsMain((CSlackProto*)param, IDD_ACCOUNT_MANAGER); +		page->SetParent(owner); +		page->Show(); +		return page; +	} + +	static CDlgBase *CreateOptionsPage(void *param) { return new CSlackOptionsMain((CSlackProto*)param, IDD_OPTIONS_MAIN); } +}; + +#endif //_SLACK_OPTIONS_H_ diff --git a/protocols/Slack/src/slack_proto.cpp b/protocols/Slack/src/slack_proto.cpp new file mode 100644 index 0000000000..8a37838c28 --- /dev/null +++ b/protocols/Slack/src/slack_proto.cpp @@ -0,0 +1,147 @@ +#include "stdafx.h" + +CSlackProto::CSlackProto(const char* protoName, const TCHAR* userName) : +	PROTO<CSlackProto>(protoName, userName), requestQueue(1) +{ +	InitNetlib(); + +	//CreateProtoService(PS_CREATEACCMGRUI, &CSlackProto::OnAccountManagerInit); + +	SetAllContactsStatus(ID_STATUS_OFFLINE); +} + +CSlackProto::~CSlackProto() +{ +	UninitNetlib(); +} + +DWORD_PTR CSlackProto::GetCaps(int type, MCONTACT) +{ +	switch (type) +	{ +	case PFLAGNUM_1: +		return PF1_IM; +	case PFLAGNUM_2: +		return PF2_ONLINE | PF2_LONGAWAY; +	case PFLAGNUM_3: +		return PF2_ONLINE | PF2_LONGAWAY; +	case PFLAG_UNIQUEIDTEXT: +		return (INT_PTR)"User Id"; +	case PFLAG_UNIQUEIDSETTING: +		return (DWORD_PTR)"UserId"; +	} + +	return 0; +} + +MCONTACT CSlackProto::AddToList(int flags, PROTOSEARCHRESULT *psr) +{ +	return NULL; +} + +int CSlackProto::AuthRecv(MCONTACT, PROTORECVEVENT* pre) +{ +	return Proto_AuthRecv(m_szModuleName, pre); +} + +int CSlackProto::AuthRequest(MCONTACT hContact, const wchar_t *szMessage) +{ +	ptrA reason(mir_utf8encodeW(szMessage)); +	return OnRequestAuth(hContact, (LPARAM)reason); +} + +int CSlackProto::RecvMsg(MCONTACT hContact, PROTORECVEVENT *pre) +{ +	return OnReceiveMessage(hContact, pre); +} + +int CSlackProto::SendMsg(MCONTACT hContact, int flags, const char *msg) +{ +	return OnSendMessage(hContact, flags, msg); +} + +int CSlackProto::SetStatus(int iNewStatus) +{ +	if (iNewStatus == m_iDesiredStatus) +		return 0; + +	switch (iNewStatus) +	{ +	case ID_STATUS_FREECHAT: +	case ID_STATUS_ONTHEPHONE: +		iNewStatus = ID_STATUS_ONLINE; +		break; + +	case ID_STATUS_NA: +	case ID_STATUS_OUTTOLUNCH: +		iNewStatus = ID_STATUS_AWAY; +		break; + +	case ID_STATUS_DND: +	case ID_STATUS_INVISIBLE: +		iNewStatus = ID_STATUS_OCCUPIED; +		break; +	} + +	debugLogA(__FUNCTION__ ": changing status from %i to %i", m_iStatus, iNewStatus); + +	int old_status = m_iStatus; +	m_iDesiredStatus = iNewStatus; + +	if (iNewStatus == ID_STATUS_OFFLINE) +	{ +		// logout +		LogOut(); + +		if (!Miranda_IsTerminated()) +		{ +			SetAllContactsStatus(ID_STATUS_OFFLINE); +		} + +		m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE; +	} +	else +	{ +		if (old_status == ID_STATUS_CONNECTING) +		{ +			return 0; +		} + +		if (old_status == ID_STATUS_OFFLINE && !IsOnline()) +		{ +			// login +			m_iStatus = ID_STATUS_CONNECTING; + +			isTerminated = false; + +			hRequestQueueThread = ForkThreadEx(&CSlackProto::RequestQueueThread, NULL, NULL); +		} +		else +		{ +			// set tox status +			m_iStatus = iNewStatus; +		} +	} + +	ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, m_iStatus); +	return 0; +} + +int CSlackProto::UserIsTyping(MCONTACT hContact, int type) +{ +	return OnUserIsTyping(hContact, type); +} + +int CSlackProto::OnEvent(PROTOEVENTTYPE iEventType, WPARAM wParam, LPARAM lParam) +{ +	switch (iEventType) +	{ +	case EV_PROTO_ONLOAD: +		return OnAccountLoaded(wParam, lParam); + +	case EV_PROTO_ONCONTACTDELETED: +		return OnContactDeleted(wParam, lParam); +	} + +	return 1; +} diff --git a/protocols/Slack/src/slack_proto.h b/protocols/Slack/src/slack_proto.h new file mode 100644 index 0000000000..f47c70f946 --- /dev/null +++ b/protocols/Slack/src/slack_proto.h @@ -0,0 +1,146 @@ +#ifndef _SLACK_PROTO_H_ +#define _SLACK_PROTO_H_ + +struct CSlackProto : public PROTO<CSlackProto> +{ +	friend class CSlackOptionsMain; +	friend class CSlackOAuth; + +	typedef void(CSlackProto::*HttpCallback)(HttpResponse&); +	typedef void(CSlackProto::*JsonCallback)(JSONNode&); +	struct RequestQueueItem +	{ +		HttpRequest *request; +		HttpCallback httpCallback; +		JsonCallback jsonCallback; +	}; + +public: +	////////////////////////////////////////////////////////////////////////////////////// +	//Ctors + +	CSlackProto(const char *protoName, const wchar_t *userName); +	~CSlackProto(); + +	////////////////////////////////////////////////////////////////////////////////////// +	// Virtual functions + +	virtual	MCONTACT __cdecl AddToList(int flags, PROTOSEARCHRESULT* psr); + +	virtual	int      __cdecl AuthRecv(MCONTACT hContact, PROTORECVEVENT*); +	virtual	int      __cdecl AuthRequest(MCONTACT hContact, const wchar_t* szMessage); + +	virtual	DWORD_PTR __cdecl GetCaps(int type, MCONTACT hContact = NULL); + +	virtual	int       __cdecl RecvMsg(MCONTACT hContact, PROTORECVEVENT*); +	virtual	int       __cdecl SendMsg(MCONTACT hContact, int flags, const char* msg); + +	virtual	int       __cdecl SetStatus(int iNewStatus); + +	virtual	int       __cdecl UserIsTyping(MCONTACT hContact, int type); + +	virtual	int       __cdecl OnEvent(PROTOEVENTTYPE iEventType, WPARAM wParam, LPARAM lParam); + +	// accounts +	static CSlackProto*       InitAccount(const char *protoName, const TCHAR *userName); +	static int                UninitAccount(CSlackProto *proto); + +	// icons +	static void InitIcons(); + +	// menus +	static void InitMenus(); + +	static int OnModulesLoaded(WPARAM, LPARAM); + +private: +	HANDLE hNetlib; +	bool isTerminated, isConnected; +	mir_cs requestQueueLock; +	HANDLE hRequestsQueueEvent; +	HANDLE hRequestQueueThread; +	LIST<RequestQueueItem> requestQueue; + +	// requests +	void SendRequest(HttpRequest *request); +	void SendRequest(HttpRequest *request, HttpCallback callback); +	void SendRequest(HttpRequest *request, JsonCallback callback); +	void PushRequest(HttpRequest *request); +	void PushRequest(HttpRequest *request, HttpCallback callback); +	void PushRequest(HttpRequest *request, JsonCallback callback); +	void __cdecl RequestQueueThread(void*); +	 +	// network +	bool IsOnline(); + +	// accounts +	static LIST<CSlackProto> Accounts; +	static int CompareAccounts(const CSlackProto *p1, const CSlackProto *p2); + +	static CSlackProto* GetContactAccount(MCONTACT hContact); + +	int __cdecl OnAccountLoaded(WPARAM, LPARAM); + +	INT_PTR __cdecl OnAccountManagerInit(WPARAM, LPARAM); + +	// netlib +	void InitNetlib(); +	void UninitNetlib(); + +	// login +	void Login(); +	void LogOut(); +	void OnAuthorize(JSONNode &root); + +	// icons +	static IconItemT Icons[]; +	static HANDLE GetIconHandle(int iconId); + +	// menus +	static HGENMENU ContactMenuItems[CMI_MAX]; +	int OnPrebuildContactMenu(WPARAM hContact, LPARAM); +	static int PrebuildContactMenu(WPARAM hContact, LPARAM lParam); + +	// options +	int __cdecl OnOptionsInit(WPARAM wParam, LPARAM lParam); + +	// contacts +	WORD GetContactStatus(MCONTACT hContact); +	void SetContactStatus(MCONTACT hContact, WORD status); +	void SetAllContactsStatus(WORD status); + +	MCONTACT GetContact(const char *userId); +	MCONTACT AddContact(const char *userId, const char *nick, bool isTemporary = false); + +	void OnGotUserProfile(JSONNode &root); +	void OnGotUserProfile(MCONTACT hContact, JSONNode &root); +	void OnGotUserList(JSONNode &root); + +	INT_PTR __cdecl OnRequestAuth(WPARAM hContact, LPARAM lParam); +	INT_PTR __cdecl OnGrantAuth(WPARAM hContact, LPARAM); + +	int __cdecl OnContactDeleted(MCONTACT, LPARAM); + +	// messages +	int OnReceiveMessage(MCONTACT hContact, PROTORECVEVENT *pre); +	int OnSendMessage(MCONTACT hContact, int flags, const char *message); + +	int OnUserIsTyping(MCONTACT hContact, int type); + +	int __cdecl OnPreCreateMessage(WPARAM wParam, LPARAM lParam); + +	// utils +	static void ShowNotification(const TCHAR *message, int flags = 0, MCONTACT hContact = NULL); +	static void ShowNotification(const TCHAR *caption, const TCHAR *message, int flags = 0, MCONTACT hContact = NULL); + +	MEVENT AddEventToDb(MCONTACT hContact, WORD type, DWORD timestamp, DWORD flags, PBYTE pBlob, DWORD cbBlob); + +	template<INT_PTR(__cdecl CSlackProto::*Service)(WPARAM, LPARAM)> +	static INT_PTR __cdecl GlobalService(WPARAM wParam, LPARAM lParam) +	{ +		CSlackProto *proto = CSlackProto::GetContactAccount((MCONTACT)wParam); +		return proto ? (proto->*Service)(wParam, lParam) : 0; +	} +}; + +#endif //_SLACK_PROTO_H_ diff --git a/protocols/Slack/src/slack_requests.cpp b/protocols/Slack/src/slack_requests.cpp new file mode 100644 index 0000000000..7fd0ef4805 --- /dev/null +++ b/protocols/Slack/src/slack_requests.cpp @@ -0,0 +1,95 @@ +#include "stdafx.h" + +void CSlackProto::SendRequest(HttpRequest *request) +{ +	NETLIBHTTPREQUEST *pResp = Netlib_HttpTransaction(m_hNetlibUser, (NETLIBHTTPREQUEST*)request); +	HttpResponse response(request, pResp); +	delete request; +} + +void CSlackProto::SendRequest(HttpRequest *request, HttpCallback callback) +{ +	NETLIBHTTPREQUEST *pResp = Netlib_HttpTransaction(m_hNetlibUser, (NETLIBHTTPREQUEST*)request); +	HttpResponse response(request, pResp); +	if (callback) +		(this->*callback)(response); +	delete request; +} + +void CSlackProto::SendRequest(HttpRequest *request, JsonCallback callback) +{ +	NETLIBHTTPREQUEST *pResp = Netlib_HttpTransaction(m_hNetlibUser, (NETLIBHTTPREQUEST*)request); +	HttpResponse response(request, pResp); +	if (callback) +	{ +		JSONNode root = JSONNode::parse(response.Content); +		(this->*callback)(root); +	} +	delete request; +} + +void CSlackProto::PushRequest(HttpRequest *request) +{ +	RequestQueueItem *item = new RequestQueueItem(); +	item->request = request; +	{ +		mir_cslock lock(requestQueueLock); +		requestQueue.insert(item); +	} +	SetEvent(hRequestsQueueEvent); +} + +void CSlackProto::PushRequest(HttpRequest *request, HttpCallback callback) +{ +	RequestQueueItem *item = new RequestQueueItem(); +	item->request = request; +	item->httpCallback = callback; +	{ +		mir_cslock lock(requestQueueLock); +		requestQueue.insert(item); +	} +	SetEvent(hRequestsQueueEvent); +} + +void CSlackProto::PushRequest(HttpRequest *request, JsonCallback callback) +{ +	RequestQueueItem *item = new RequestQueueItem(); +	item->request = request; +	item->jsonCallback = callback; +	{ +		mir_cslock lock(requestQueueLock); +		requestQueue.insert(item); +	} +	SetEvent(hRequestsQueueEvent); +} + +void CSlackProto::RequestQueueThread(void*) +{ +	Login(); + +	do +	{ +		RequestQueueItem *item; +		while (true) +		{ +			{ +				mir_cslock lock(requestQueueLock); +				if (!requestQueue.getCount()) +					break; + +				item = requestQueue[0]; +				requestQueue.remove(0); +			} +			if (item->httpCallback) +				SendRequest(item->request, item->httpCallback); +			else if (item->jsonCallback) +				SendRequest(item->request, item->jsonCallback); +			else +				SendRequest(item->request); +			delete item; +		} +		WaitForSingleObject(hRequestsQueueEvent, 1000); +	} while (!isTerminated); + +	hRequestQueueThread = NULL; +}
\ No newline at end of file diff --git a/protocols/Slack/src/slack_utils.cpp b/protocols/Slack/src/slack_utils.cpp new file mode 100644 index 0000000000..bd40f58ee7 --- /dev/null +++ b/protocols/Slack/src/slack_utils.cpp @@ -0,0 +1,45 @@ +#include "stdafx.h" + +bool CSlackProto::IsOnline() +{ +	return false; +} + +void CSlackProto::ShowNotification(const TCHAR *caption, const TCHAR *message, int flags, MCONTACT hContact) +{ +	if (Miranda_IsTerminated()) +	{ +		return; +	} + +	if (ServiceExists(MS_POPUP_ADDPOPUPT) && db_get_b(NULL, "Popup", "ModuleIsEnabled", 1)) +	{ +		POPUPDATAT ppd = { 0 }; +		ppd.lchContact = hContact; +		wcsncpy(ppd.lpwzContactName, caption, MAX_CONTACTNAME); +		wcsncpy(ppd.lpwzText, message, MAX_SECONDLINE); +		ppd.lchIcon = IcoLib_GetIcon("Slack_main"); + +		if (!PUAddPopupT(&ppd)) +			return; +	} + +	MessageBox(NULL, message, caption, MB_OK | flags); +} + +void CSlackProto::ShowNotification(const TCHAR *message, int flags, MCONTACT hContact) +{ +	ShowNotification(_A2W(MODULE), message, flags, hContact); +} + +MEVENT CSlackProto::AddEventToDb(MCONTACT hContact, WORD type, DWORD timestamp, DWORD flags, PBYTE pBlob, DWORD cbBlob) +{ +	DBEVENTINFO dbei = {}; +	dbei.szModule = this->m_szModuleName; +	dbei.timestamp = timestamp; +	dbei.eventType = type; +	dbei.cbBlob = cbBlob; +	dbei.pBlob = pBlob; +	dbei.flags = flags; +	return db_event_add(hContact, &dbei); +}
\ No newline at end of file diff --git a/protocols/Slack/src/stdafx.cxx b/protocols/Slack/src/stdafx.cxx new file mode 100644 index 0000000000..e6f635a0de --- /dev/null +++ b/protocols/Slack/src/stdafx.cxx @@ -0,0 +1,18 @@ +/* +Copyright (C) 2012-15 Miranda NG project (http://miranda-ng.org) + +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 version 2 +of the License. + +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 "stdafx.h"
\ No newline at end of file diff --git a/protocols/Slack/src/stdafx.h b/protocols/Slack/src/stdafx.h new file mode 100644 index 0000000000..ce5ebf1a80 --- /dev/null +++ b/protocols/Slack/src/stdafx.h @@ -0,0 +1,58 @@ +#ifndef _COMMON_H_ +#define _COMMON_H_ + +#include <windows.h> +#include <commctrl.h> +#include <time.h> + +#include <newpluginapi.h> + +#include <m_protoint.h> +#include <m_protosvc.h> + +#include <m_system.h> +#include <m_database.h> +#include <m_langpack.h> +#include <m_options.h> +#include <m_netlib.h> +#include <m_popup.h> +#include <m_icolib.h> +#include <m_userinfo.h> +#include <m_addcontact.h> +#include <m_message.h> +#include <m_avatars.h> +#include <m_skin.h> +#include <m_chat.h> +#include <m_genmenu.h> +#include <m_clc.h> +#include <m_clist.h> +#include <m_clistint.h> +#include <m_gui.h> +#include <m_http.h> +#include <m_json.h> + +struct CSlackProto; + +#define MODULE "Slack" + +#define SLACK_URL "https://slack.com" +#define SLACK_API_URL SLACK_URL "/api" +#define SLACK_REDIRECT_URL "http://www.miranda-ng.org/slack" + +#define SLACK_CLIENT_ID "149178180673.150539976630" +#include "../../../miranda-private-keys/Slack/client_secret.h" + +#include "http_request.h" + +#include "version.h" +#include "resource.h" +#include "slack_menus.h" +#include "slack_dialogs.h" +#include "slack_options.h" +#include "slack_proto.h" + +#include "api\api_users.h" + +extern HINSTANCE g_hInstance; + +#endif //_COMMON_H_
\ No newline at end of file diff --git a/protocols/Slack/src/version.h b/protocols/Slack/src/version.h new file mode 100644 index 0000000000..e76c9c223d --- /dev/null +++ b/protocols/Slack/src/version.h @@ -0,0 +1,14 @@ +#define __MAJOR_VERSION            0 +#define __MINOR_VERSION            11 +#define __RELEASE_NUM              0 +#define __BUILD_NUM                0 + +#include <stdver.h> + +#define __PLUGIN_NAME              "Slack" +#define __FILENAME                 "Slack.dll" +#define __DESCRIPTION              "Slack for Miranda NG." +#define __AUTHOR                   "Miranda NG Team" +#define __AUTHOREMAIL              "" +#define __AUTHORWEB                "http://miranda-ng.org/p/Slack/" +#define __COPYRIGHT                "© 2017 Miranda NG Team"  | 
