diff options
Diffstat (limited to 'plugins/NotifyAnything/src')
-rw-r--r-- | plugins/NotifyAnything/src/common.h | 61 | ||||
-rw-r--r-- | plugins/NotifyAnything/src/main.cpp | 1057 | ||||
-rw-r--r-- | plugins/NotifyAnything/src/options.cpp | 209 | ||||
-rw-r--r-- | plugins/NotifyAnything/src/resource.h | 27 | ||||
-rw-r--r-- | plugins/NotifyAnything/src/stdafx.cpp | 18 |
5 files changed, 1372 insertions, 0 deletions
diff --git a/plugins/NotifyAnything/src/common.h b/plugins/NotifyAnything/src/common.h new file mode 100644 index 0000000000..264b5adee8 --- /dev/null +++ b/plugins/NotifyAnything/src/common.h @@ -0,0 +1,61 @@ +// disable warnings about underscore in stdc functions
+#pragma warning(disable: 4996)
+
+#include <windows.h>
+
+#include <string.h>
+#include <stdio.h>
+#include <winsock.h>
+#include <time.h>
+#include <stdio.h>
+#include <direct.h>
+#include <process.h>
+#include <sys/types.h>
+#include <sys/timeb.h>
+
+#include <string>
+#include <map>
+#include <set>
+#include <vector>
+
+#include "newpluginapi.h"
+#include "m_system_cpp.h"
+#include "m_options.h"
+#include "m_skin.h"
+#include "m_langpack.h"
+#include "m_database.h"
+#include "m_utils.h"
+
+#include "m_popup.h"
+#include "m_LogService.h"
+
+extern HINSTANCE hInst;
+
+const char PlugName[] = "NotifyAnything";
+
+//---------------------------
+//---Internal Hooks (see main.c)
+//---(Workaround till CallServiceSync is available)
+
+struct NASettings {
+ enum sound_t { never, always, request };
+
+ bool local_only, debug_messages, log_to_file, use_pcspeaker, allow_execute;
+ sound_t sound;
+ int port;
+ std::string password, log_filename;
+};
+
+extern NASettings g_settings;
+
+void stop_threads();
+void start_threads();
+
+void save_settings();
+void load_settings();
+
+int OptionsInitialize(WPARAM wParam, LPARAM lParam);
+
+extern std::string g_mirandaDir;
+extern bool IsLogService;
+#define LOG_ID "NotifyAnything"
diff --git a/plugins/NotifyAnything/src/main.cpp b/plugins/NotifyAnything/src/main.cpp new file mode 100644 index 0000000000..7ef581fb24 --- /dev/null +++ b/plugins/NotifyAnything/src/main.cpp @@ -0,0 +1,1057 @@ +#include "common.h"
+
+#define EnterCS(cs) EnterCriticalSection(cs)
+#define LeaveCS(cs) LeaveCriticalSection(cs)
+
+//---------------------------
+//---Internal Hooks
+//---(Workaround till CallServiceSync is available)
+
+/*
+Stolen from NewEventNotify:
+The idea for this is taken from "NewStatusNotify" by Hrk, thx *g*
+This is needed to send a message with safe multithrading.
+We'll create a private hook and we'll call it via NotifyEventHooks, which brings execution
+back to the main thread.
+*/
+
+void cslog(const char *what, const char *file, int line)
+{
+ if (IsLogService){
+ std::string LogString;
+ char buf[10];
+ LogString = what;
+ LogString += ": ";
+ LogString += file;
+ LogString += ":";
+ LogString += itoa(line, buf, 10);
+ logservice_log(LOG_ID, NULL, (TCHAR *) LogString.c_str());
+ }
+ else {
+ if (g_settings.log_to_file) {
+ time_t t_;
+ time(&t_);
+ tm *t = localtime(&t_);
+ //FILE *f = fopen("na.log", "a");
+ FILE *f = fopen(g_settings.log_filename.c_str(), "a");
+ if (f) {
+ //fprintf(f, "%s: %s:%i\n", what, file, line);
+ fprintf(f, "[%04i-%02i-%02i %02i:%02i:%02i cs] %s: %s:%i\n",
+ int(t->tm_year+1900), int(t->tm_mon), int(t->tm_mday),
+ int(t->tm_hour), int(t->tm_min), int(t->tm_sec), what, file, line);
+ fclose(f);
+ }
+ }
+ }
+}
+
+void EnterCSHelper(CRITICAL_SECTION *cs, const char *file, int line)
+{
+ cslog(">enter", file, line);
+ EnterCriticalSection(cs);
+ cslog("<enter", file, line);
+}
+
+void LeaveCSHelper(CRITICAL_SECTION *cs, const char *file, int line)
+{
+ cslog(">leave", file, line);
+ LeaveCriticalSection(cs);
+ cslog("<leave", file, line);
+}
+
+HANDLE g_udp_thread, g_tcp_thread;
+SOCKET g_udp_socket, g_tcp_socket;
+volatile bool g_exit_threads, g_firstrun;
+std::string g_mirandaDir;
+CRITICAL_SECTION g_wsocklock;
+bool IsLogService;
+
+HINSTANCE hInst;
+int hLangpack;
+
+PLUGININFOEX pluginInfo = {
+ sizeof(PLUGININFOEX),
+ "NotifyAnything",
+ PLUGIN_MAKE_VERSION(0, 0, 2, 8),
+ "Displays popups for custom events, triggered by UDP packages.",
+ "Daniel Wesslén, Korney San",
+ "wesslen@users.sourceforge.net, kora@users.sourceforge.net",
+ "Public Domain 2003 Daniel Wesslén",
+ "http://miranda-ng.org",
+ UNICODE_AWARE,
+ // {E92874EC-594A-4A2F-BDED-C0BE8B5A45D1}
+ { 0xe92874ec, 0x594a, 0x4a2f, { 0xbd, 0xed, 0xc0, 0xbe, 0x8b, 0x5a, 0x45, 0xd1 }}
+};
+
+BOOL WINAPI DllMain(HINSTANCE hi, DWORD, LPVOID)
+{
+ hInst = hi;
+ DisableThreadLibraryCalls(hInst);
+ return TRUE;
+}
+
+enum replace_mode_t {
+ xno,
+ xappend,
+ xprepend,
+ xreplace
+};
+
+struct popup_t {
+ std::string id, contact, message, icon, sound, passwd;
+ std::string left, right, opened, closed;
+ COLORREF foreground, background;
+ int delay;
+ bool beep;
+ replace_mode_t replace;
+ HWND hwnd;
+};
+
+typedef std::map<std::string, popup_t *> popups_t;
+typedef std::set<popup_t *> anon_popups_t;
+popups_t g_popups;
+anon_popups_t g_anon_popups;
+CRITICAL_SECTION g_popups_cs;
+
+std::string strip(std::string str)
+{
+ while (!str.empty() && isspace(str[0]))
+ str.erase(0, 1);
+ while (!str.empty() && isspace(*(str.end()-1)))
+ str.erase(str.size()-1);
+ return str;
+}
+
+void dbg_msg(std::string str, int type)
+{
+ str = strip(str);
+
+ if (g_settings.debug_messages)
+ CallServiceSync(MS_POPUP_SHOWMESSAGE, (WPARAM) const_cast<char *>(str.c_str()), (LPARAM) type);
+
+ if (IsLogService){
+ logservice_log(LOG_ID, NULL, (TCHAR *) str.c_str());
+ }
+ else {
+ if (g_settings.log_to_file) {
+ time_t t_;
+ time(&t_);
+ tm *t = localtime(&t_);
+ FILE *f = fopen(g_settings.log_filename.c_str(), "a");
+ if (f) {
+ fprintf(f, "[%04i-%02i-%02i %02i:%02i:%02i dbg_msg] %s\n",
+ int(t->tm_year+1900), int(t->tm_mon), int(t->tm_mday),
+ int(t->tm_hour), int(t->tm_min), int(t->tm_sec), str.c_str());
+ fclose(f);
+ }
+ }
+ }
+}
+
+void showLastError()
+{
+ int err = GetLastError();
+
+ LPVOID lpMsgBuf;
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL);
+
+ dbg_msg((char *) lpMsgBuf, SM_WARNING);
+
+ LocalFree(lpMsgBuf);
+}
+
+struct enum_icons_t {
+ int nr;
+ LPCTSTR name;
+ bool found;
+};
+
+BOOL CALLBACK enum_icons_func(HMODULE, LPCTSTR, LPTSTR name, LONG_PTR data)
+{
+ enum_icons_t *info = (enum_icons_t *) data;
+ if (!--info->nr) {
+ info->found = true;
+ info->name = name;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void registerSound(const std::string &name)
+{
+ static std::set<std::string> sset;
+
+ if (sset.find(name) != sset.end())
+ return;
+ sset.insert(name);
+
+ std::string id = "NotifyAnything_" + name;
+ std::string desc = "NotifyAnything: " + name;
+ std::string file = name + ".wav";
+ SkinAddNewSound(id.c_str(), Translate(desc.c_str()), file.c_str());
+}
+
+HICON getIcon(const std::string &name)
+{
+ static std::map<std::string, HICON> icons;
+ static HICON deficon;
+ static bool init;
+ if (!init) {
+ init = true;
+
+ // windows icons
+ icons["exclamation"] = icons["warning"] = LoadIcon(NULL, IDI_WARNING);
+ deficon = icons["information"] = icons["asterisk"] = LoadIcon(NULL, IDI_ASTERISK);
+ icons["hand"] = icons["error"] = LoadIcon(NULL, IDI_ERROR);
+ icons["question"] = LoadIcon(NULL, IDI_QUESTION);
+ icons["winlogo"] = LoadIcon(NULL, IDI_WINLOGO);
+
+ // miranda icons
+ icons["online"] = LoadSkinnedIcon(SKINICON_STATUS_ONLINE);
+ icons["offline"] = LoadSkinnedIcon(SKINICON_STATUS_OFFLINE);
+ icons["away"] = LoadSkinnedIcon(SKINICON_STATUS_AWAY);
+ icons["na"] = LoadSkinnedIcon(SKINICON_STATUS_NA);
+ icons["occupied"] = LoadSkinnedIcon(SKINICON_STATUS_OCCUPIED);
+ icons["dnd"] = LoadSkinnedIcon(SKINICON_STATUS_DND);
+ icons["free4chat"] = LoadSkinnedIcon(SKINICON_STATUS_FREE4CHAT);
+ icons["invisible"] = LoadSkinnedIcon(SKINICON_STATUS_INVISIBLE);
+ icons["onthephone"] = LoadSkinnedIcon(SKINICON_STATUS_ONTHEPHONE);
+ icons["outtolunch"] = LoadSkinnedIcon(SKINICON_STATUS_OUTTOLUNCH);
+
+ icons["message"] = LoadSkinnedIcon(SKINICON_EVENT_MESSAGE);
+ icons["url"] = LoadSkinnedIcon(SKINICON_EVENT_URL);
+ icons["file"] = LoadSkinnedIcon(SKINICON_EVENT_FILE);
+ }
+
+ std::map<std::string, HICON>::iterator i = icons.find(name);
+ if (i != icons.end())
+ return i->second;
+
+ size_t p = name.rfind(',');
+ if (p == name.npos) {
+ // try to load icon file
+ HANDLE h = LoadImageA(NULL, name.c_str(), IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
+ if (h != NULL)
+ return icons[name] = (HICON) h;
+
+ showLastError();
+ return deficon;
+ }
+
+ std::tstring file((TCHAR*)_A2T(name.c_str()), 0, p);
+
+ std::tstring rname(file.c_str(), p+1);
+ if (rname.empty()) {
+ dbg_msg(Translate("No resource name given."), SM_WARNING);
+ return deficon;
+ }
+
+ HMODULE module = LoadLibraryEx(file.c_str(), NULL, LOAD_LIBRARY_AS_DATAFILE);
+ if (!module) {
+ showLastError();
+ return deficon;
+ }
+
+ LPCTSTR resname = rname.c_str();
+ if (isdigit(rname[0])) {
+ enum_icons_t info;
+ info.found = false;
+ info.nr = _ttoi(rname.c_str());
+ if (info.nr <= 0) {
+ dbg_msg(Translate("Icon indices start at 1."), SM_WARNING);
+ return deficon;
+ }
+
+ BOOL ok = EnumResourceNames(module, RT_GROUP_ICON, enum_icons_func, (LONG_PTR) &info);
+ if (!info.found) {
+ if (!ok) {
+ if (GetLastError()) {
+ showLastError();
+ return deficon;
+ }
+ }
+ dbg_msg(Translate("Could not find the requested icon."), SM_WARNING);
+ return deficon;
+ }
+ resname = info.name;
+ }
+
+ HICON icon = (HICON) LoadImage(module, resname, IMAGE_ICON, 16, 16, 0);
+ FreeLibrary(module);
+
+ if (!icon) {
+ showLastError();
+ return deficon;
+ }
+
+ return icons[name] = (HICON) icon;
+}
+
+bool getNext(std::string &out, std::string &in, char sep)
+{
+ while (!in.empty() && in[0] == ' ')
+ in.erase(0, 1);
+
+ out.erase();
+
+ while (!in.empty()) {
+ if (in[0] == sep) {
+ in.erase(0,1);
+ return true;
+ }
+ if (in[0] == '\"') {
+ in.erase(0,1);
+ size_t p = in.find('\"');
+ if (p == in.npos)
+ throw "Unterminated quote: \"" + in;
+ out += '"';
+ out.append(in, 0, p);
+ out += '"';
+ in.erase(0, p+1);
+ return true;
+ }
+ if (!in.compare(0, 3, "<[[")) {
+ in.erase(0, 3);
+ size_t p = in.find("]]>");
+ if (p == in.npos)
+ throw "Unterminated \"<[[\": <[[" + in;
+ out.append(in, 0, p);
+ in.erase(0, p+3);
+ return true;
+ }
+
+ out += in[0];
+ in.erase(0,1);
+ return true;
+ }
+ return false;
+}
+
+std::string unquote(std::string str)
+{
+ size_t p;
+ while ((p = str.find('\"')) != str.npos)
+ str.erase(p, 1);
+ return str;
+}
+
+void getAll(std::vector<std::string> &out, std::string &in, char sep, bool unquote_)
+{
+ std::string arg;
+ while (getNext(arg, in, sep))
+ if (!arg.empty()) {
+ if (unquote_)
+ arg = unquote(arg);
+ out.push_back(arg);
+ }
+}
+
+const char *decode_se_arg(std::string &str)
+{
+ return (str.empty()) ? 0 : str.c_str();
+}
+
+void processSingleAction(const std::string &what, bool &closeflag)
+{
+ if (!what.compare(0, 7, "system:")) {
+ if (!g_settings.allow_execute) {
+ dbg_msg(Translate("Application launching is disabled."), SM_WARNING);
+ return;
+ }
+
+ std::string argstr(what, 7);
+
+ if (system(argstr.c_str()) == -1)
+ dbg_msg("Failed to execute: " + argstr, SM_WARNING);
+ }
+ else if (!what.compare(0, 4, "cmd:")) {
+ if (!g_settings.allow_execute) {
+ dbg_msg(Translate("Application launching is disabled."), SM_WARNING);
+ return;
+ }
+
+ std::string argstr(what, 4);
+ std::vector<std::string> args;
+
+ getAll(args, argstr, ' ', true);
+
+ if (args.empty())
+ throw "Insufficient arguments: " + what;
+
+ std::vector<const char *> cargs;
+ for (std::vector<std::string>::iterator i=args.begin(), e=args.end(); i!=e; ++i)
+ cargs.push_back(i->c_str());
+ cargs.push_back(0);
+
+ if (_spawnvp(_P_DETACH, cargs[0], &cargs[0]) == -1)
+ dbg_msg("Failed to execute: " + what.substr(4), SM_WARNING);
+
+ }
+ else if (!what.compare(0, 5, "open:")) {
+ if (!g_settings.allow_execute) {
+ dbg_msg(Translate("Application launching is disabled."), SM_WARNING);
+ return;
+ }
+
+ std::string argstr(what, 5);
+
+ std::string file, args;
+ if (!getNext(file, argstr, ' '))
+ throw "No filename provided: " + what;
+ file = strip(file);
+ args = strip(argstr);
+
+ const char *cargs = decode_se_arg(args);
+
+ if ((int) ShellExecuteA(0, "open", file.c_str(), cargs, 0, SW_SHOWNORMAL) <= 32)
+ throw "Failed to open: " + file + " " + args;
+
+ } else if (!what.compare(0, 6, "shell:")) {
+ if (!g_settings.allow_execute) {
+ dbg_msg(Translate("Application launching is disabled."), SM_WARNING);
+ return;
+ }
+
+ std::string argstr(what, 6);
+
+ std::string verb, file, args, dir;
+ if (!getNext(verb, argstr, ':'))
+ throw "No verb provided: " + what;
+ if (!getNext(file, argstr, ','))
+ throw "No filename provided: " + what;
+ getNext(args, argstr, ',');
+ getNext(dir, argstr, ',');
+ verb = unquote(strip(verb));
+ file = /*unquote(*/strip(file)/*)*/;
+ args = strip(args);
+ dir = /*unquote(*/strip(dir)/*)*/;
+
+ if ((int) ShellExecuteA(0, decode_se_arg(verb), decode_se_arg(file),
+ decode_se_arg(args), decode_se_arg(dir), SW_SHOWNORMAL) <= 32)
+ throw "Failed: " + what;
+ } else if (what == "close")
+ closeflag = true;
+ else
+ throw "Action not recognized: " + what;
+}
+
+void processAction(const std::string &what, bool &closeflag)
+{
+ try
+ {
+ std::string argstr = what;
+ std::vector<std::string> actions;
+
+ std::string action;
+ while (getNext(action, argstr, ';'))
+ if (!action.empty())
+ processSingleAction(action, closeflag);
+ }
+ catch (std::string err) {
+ dbg_msg(err, SM_WARNING);
+ }
+}
+
+static int CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ popup_t *pd = 0;
+ pd = (popup_t *) CallService(MS_POPUP_GETPLUGINDATA, (WPARAM) hWnd, (LPARAM) pd);
+ if (!pd)
+ return FALSE;
+
+ switch (message) {
+ case WM_COMMAND:
+ {
+ EnterCS(&g_popups_cs);
+ std::string left;
+ if (pd)
+ left = pd->left;
+ LeaveCS(&g_popups_cs);
+
+ if (left.empty())
+ PUDeletePopUp(hWnd);
+ else {
+ bool closeflag = false;
+ processAction(left, closeflag);
+ if (closeflag)
+ PUDeletePopUp(hWnd);
+ }
+ }
+ return TRUE;
+
+ case WM_CONTEXTMENU:
+ {
+ EnterCS(&g_popups_cs);
+ std::string right;
+ if (pd)
+ right = pd->right;
+ LeaveCS(&g_popups_cs);
+
+ if (right.empty())
+ PUDeletePopUp(hWnd);
+ else {
+ bool closeflag = false;
+ processAction(right, closeflag);
+ if (closeflag)
+ PUDeletePopUp(hWnd);
+ }
+ }
+ return TRUE;
+
+ case UM_INITPOPUP:
+ EnterCS(&g_popups_cs);
+ pd->hwnd = hWnd;
+ LeaveCS(&g_popups_cs);
+ return TRUE;
+
+ case UM_FREEPLUGINDATA:
+ EnterCS(&g_popups_cs);
+ std::string closed;
+ if (pd)
+ closed = pd->closed;
+
+ g_popups.erase(pd->id);
+ g_anon_popups.erase(pd);
+ delete pd;
+ LeaveCS(&g_popups_cs);
+
+ if (!closed.empty()) {
+ bool closeflag = false;
+ processAction(closed, closeflag);
+ }
+
+ return TRUE;
+ }
+ return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+int showMessage(const popup_t &msg)
+{
+ POPUPDATAEX ppd = { 0 };
+ strncpy(ppd.lpzText, strip(msg.message).c_str(), MAX_SECONDLINE);
+ strncpy(ppd.lpzContactName, msg.contact.c_str(), MAX_CONTACTNAME);
+ ppd.colorBack = msg.background;
+ ppd.colorText = msg.foreground;
+ ppd.lchIcon = getIcon(msg.icon);
+ ppd.PluginWindowProc = (WNDPROC) PopupDlgProc;
+ ppd.iSeconds = msg.delay;
+
+ EnterCS(&g_popups_cs);
+
+ popup_t *msgp = new popup_t(msg);
+
+ if (!msg.id.empty())
+ g_popups[msg.id] = msgp;
+ g_anon_popups.insert(msgp);
+ ppd.PluginData = msgp;
+
+ LeaveCS(&g_popups_cs);
+
+ return CallServiceSync(MS_POPUP_ADDPOPUPEX, (WPARAM)&ppd, 0);
+}
+
+void replaceMessage(const popup_t &msg)
+{
+ EnterCS(&g_popups_cs);
+
+ popups_t::iterator i = g_popups.find(msg.id);
+ if (i != g_popups.end()) {
+ if (i->second->hwnd) {
+
+ popup_t &nmsg = *i->second;
+
+ switch (msg.replace) {
+ case xreplace:
+ nmsg.message = msg.message; break;
+ case xappend:
+ nmsg.message += msg.message; break;
+ case xprepend:
+ nmsg.message = msg.message + nmsg.message; break;
+ default:
+ break;
+ }
+
+ if (!msg.left.empty())
+ nmsg.left = msg.left;
+ if (!msg.right.empty())
+ nmsg.right = msg.right;
+ if (!msg.opened.empty())
+ nmsg.opened = msg.opened;
+ if (!msg.closed.empty())
+ nmsg.closed = msg.closed;
+
+ LeaveCS(&g_popups_cs);
+
+ CallServiceSync(MS_POPUP_CHANGETEXT, (WPARAM)i->second->hwnd, (LPARAM)strip(i->second->message).c_str());
+ return;
+ }
+ }
+
+ LeaveCS(&g_popups_cs);
+}
+
+inline int dehex(int c) {
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ else if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ else
+ return 0;
+}
+
+COLORREF parseColor(const std::string &buf, bool &ok)
+{
+ ok = false;
+ for (int i=0; i!=buf.size(); ++i)
+ if (!isxdigit(buf[i]))
+ return RGB(0,0,0);
+ if (buf.size() == 6) {
+ int r = (dehex(buf[0]) << 4) + dehex(buf[1]);
+ int g = (dehex(buf[2]) << 4) + dehex(buf[3]);
+ int b = (dehex(buf[4]) << 4) + dehex(buf[5]);
+ ok = true;
+ return RGB(r,g,b);
+ } else if (buf.size() == 3) {
+ int r = dehex(buf[0])*17;
+ int g = dehex(buf[1])*17;
+ int b = dehex(buf[2])*17;
+ ok = true;
+ return RGB(r,g,b);
+ } else
+ return RGB(0,0,0);
+}
+
+void loadDefaults(popup_t &msg, char ch)
+{
+ msg.hwnd = NULL;
+ msg.replace = xno;
+ msg.delay = 0;
+ switch (ch) {
+ case '%':
+ msg.icon = "message";
+ msg.background = RGB(173,206,247);
+ msg.foreground = RGB(0,0,0);
+ msg.contact = "Message";
+ msg.beep = true;
+ msg.sound = "Message";
+ return;
+ case '!':
+ msg.icon = "exclamation";
+ msg.background = RGB(191,0,0);
+ msg.foreground = RGB(255,245,225);
+ msg.contact = "Error";
+ msg.beep = true;
+ msg.sound = "Error";
+ return;
+ case ' ':
+ default:
+ msg.icon = "information";
+ msg.background = RGB(255,245,225);
+ msg.foreground = RGB(0,0,0);
+ msg.contact = "Notice";
+ msg.beep = true;
+ msg.sound = "Notice";
+ return;
+ }
+}
+
+bool parseSimpleMessage(const std::string &buf, popup_t &msg, char sep)
+{
+ size_t p = buf.find(sep);
+ if (p == buf.npos)
+ msg.message = buf;
+ else {
+ msg.contact.assign(buf, 0, p);
+ msg.message.assign(buf, p+1, buf.npos);
+ }
+ return true;
+}
+
+bool parseComplexMessage(const std::string &buf, popup_t &msg, char sep)
+{
+ const char *p = buf.c_str();
+ const char *npos = strchr(p, sep);
+ bool passok = false;
+
+ while ((p = npos)) {
+ ++p;
+ const char *cpos = strchr(p, ':');
+ npos = strchr(p, sep);
+
+ const char *wend = cpos;
+ if (!wend || npos && npos < wend)
+ wend = npos;
+ if (!wend) {
+ dbg_msg(Translate("Unterminated option."), SM_WARNING);
+ return false;
+ }
+
+ std::string what(p, wend);
+ std::string arg;
+ if (wend == cpos && wend && npos)
+ arg.assign(cpos+1, npos);
+ else if (!cpos)
+ arg.erase();
+ else
+ arg = cpos+1;
+
+ if (!g_settings.password.empty() && !passok) {
+ if (what == "passwd" && arg == g_settings.password) {
+ passok = true;
+ continue;
+ } else
+ return false;
+ }
+
+ if (what == "passwd")
+ ;
+ else if (what == "icon")
+ msg.icon = arg;
+ else if (what == "msg") {
+ if (!cpos) {
+ dbg_msg(Translate("No argument given to msg option."), SM_WARNING);
+ return false;
+ } else if (msg.replace != xno && msg.id.empty()) {
+ dbg_msg(Translate("Id is required for replacement."), SM_WARNING);
+ return false;
+ }
+ msg.message = arg;
+ return true;
+ } else if (what == "replace") {
+ if (arg == "yes")
+ msg.replace = xreplace;
+ else if (arg == "append")
+ msg.replace = xappend;
+ else if (arg == "prepend")
+ msg.replace = xprepend;
+ else if (arg == "no")
+ msg.replace = xno;
+ else
+ dbg_msg(Translate("Invalid argument for replace option: ") + arg, SM_WARNING);
+ } else if (what == "sound") {
+ if (arg.empty())
+ msg.beep = false;
+ else {
+ msg.beep = true;
+ msg.sound = arg;
+ registerSound(arg);
+ }
+ } else if (what == "left") {
+ msg.left = arg;
+ } else if (what == "right") {
+ msg.right = arg;
+ } else if (what == "opened") {
+ msg.opened = arg;
+ } else if (what == "closed") {
+ msg.closed = arg;
+ } else if (what == "delay") {
+ msg.delay = atoi(arg.c_str());
+ } else if (what == "id") {
+ msg.id = arg;
+ } else if (what == "bg") {
+ bool ok;
+ msg.background = parseColor(arg, ok);
+ if (!ok)
+ dbg_msg("Invalid color: " + arg, SM_WARNING);
+ } else if (what == "fg") {
+ bool ok;
+ msg.foreground = parseColor(arg, ok);
+ if (!ok)
+ dbg_msg("Invalid color: " + arg, SM_WARNING);
+ } else if (what == "from")
+ msg.contact = arg;
+ else if (what == "sep") {
+ if (arg.size() == 1)
+ sep = arg[0];
+ else
+ dbg_msg("Invalid argument for sep option: " + arg, SM_WARNING);
+ } else if (what == "beep") {
+ if (arg == "1")
+ msg.beep = true;
+ else if (arg == "0")
+ msg.beep = false;
+ else
+ dbg_msg("Invalid argument for beep option: " + arg, SM_WARNING);
+ } else
+ dbg_msg("Unknown option: " + what, SM_NOTIFY);
+ }
+ return true;
+}
+
+bool parseMessage(const std::string &abuf, popup_t &msg)
+{
+ if (abuf.empty()) {
+ dbg_msg(Translate("Empty message ignored."), SM_NOTIFY);
+ return false;
+ }
+
+ std::string buf = abuf;
+ char sep = '#';
+ if (buf.size() >= 3 && !isalnum(buf[0]) && buf[0] == buf[1] && buf[1] == buf[2]) {
+ sep = buf[0];
+ buf.erase(0, 3);
+ }
+
+ if (strchr("*!%", buf[0]) && sep != buf[0]) {
+ if (buf.size() < 2) return false;
+ loadDefaults(msg, buf[0]);
+ buf.erase(0, 1);
+ } else
+ loadDefaults(msg, ' ');
+
+ if (buf[0] == sep)
+ return parseComplexMessage(buf, msg, sep);
+ else if (g_settings.password.empty())
+ return parseSimpleMessage(buf, msg, sep);
+ else
+ return false;
+}
+
+void processMessage(std::string buf)
+{
+ if (IsLogService){
+ logservice_log(LOG_ID, NULL, (TCHAR *) buf.c_str());
+ }
+ else {
+ if (g_settings.log_to_file) {
+ time_t t_;
+ time(&t_);
+ tm *t = localtime(&t_);
+ FILE *f = fopen(g_settings.log_filename.c_str(), "a");
+ if (f) {
+ bool err = fprintf(f, "[%04i-%02i-%02i %02i:%02i:%02i] %s\n",
+ int(t->tm_year+1900), int(t->tm_mon+1), int(t->tm_mday),
+ int(t->tm_hour), int(t->tm_min), int(t->tm_sec), buf.c_str()) < 0;
+ if (fclose(f) == EOF || err)
+ dbg_msg(Translate("Failed to write to log file."), SM_WARNING);
+ } else
+ dbg_msg(Translate("Failed to open log file."), SM_WARNING);
+ }
+ }
+
+ popup_t msg;
+ if (parseMessage(buf, msg)) {
+
+ if (!msg.opened.empty()) {
+ bool close = false;
+ processAction(msg.opened, close);
+ if (close)
+ return;
+ }
+
+ if (msg.replace) {
+ replaceMessage(msg);
+ return;
+ }
+
+ showMessage(msg);
+
+ if (g_settings.sound == g_settings.always
+ || g_settings.sound == g_settings.request
+ && msg.beep)
+ {
+ if (g_settings.use_pcspeaker)
+ Beep(650, 50);
+ else {
+ std::string sname = "NotifyAnything_"
+ + msg.sound;
+ SkinPlaySound(sname.c_str());
+ }
+ }
+ }
+}
+
+void initWinsock()
+{
+ EnterCS(&g_wsocklock);
+ if (g_firstrun) {
+ // probably not needed, but just in case...
+ // give PopUp a second to sort itself out
+ Sleep(1000);
+ g_firstrun = false;
+
+ WSADATA wsaData;
+ int err = WSAStartup(MAKEWORD(2, 0), &wsaData);
+ if (err)
+ throw "WSAStartup failed";
+ }
+ LeaveCS(&g_wsocklock);
+}
+
+DWORD udptcpThreadFunc(LPVOID useUdp)
+{
+ try
+ {
+ initWinsock();
+
+ SOCKET sock = socket(AF_INET, useUdp ? SOCK_DGRAM : SOCK_STREAM, 0/*IPPROTO_UDP*/);
+ if (sock == INVALID_SOCKET)
+ throw "socket failed";
+
+ if (useUdp)
+ g_udp_socket = sock;
+ else
+ g_tcp_socket = sock;
+
+ SOCKADDR_IN addr;
+ ZeroMemory(&addr, sizeof addr);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(g_settings.port);
+ if (g_settings.local_only) {
+ addr.sin_addr.S_un.S_un_b.s_b1 = 127;
+ addr.sin_addr.S_un.S_un_b.s_b2 = 0;
+ addr.sin_addr.S_un.S_un_b.s_b3 = 0;
+ addr.sin_addr.S_un.S_un_b.s_b4 = 1;
+ }
+
+ if (bind(sock, reinterpret_cast<sockaddr *>(&addr), sizeof addr))
+ throw "bind failed";
+
+ SOCKADDR_IN from;
+ char buf[4097];
+
+ if (useUdp) {
+ while (!g_exit_threads) {
+ int fromSize = sizeof from;
+ int err = recvfrom(sock, buf, sizeof buf - 1, 0, reinterpret_cast<sockaddr *>(&from), &fromSize);
+
+ if (g_exit_threads)
+ return 0;
+
+ if (err == SOCKET_ERROR)
+ throw "socket error";
+
+ buf[err] = '\0';
+
+ if (err > 0)
+ processMessage(buf);
+ }
+ } else {
+ listen(sock, SOMAXCONN);
+ while (!g_exit_threads) {
+ int fromSize = sizeof from;
+ SOCKET msgsock = accept(sock, reinterpret_cast<sockaddr *>(&from), &fromSize);
+
+ if (g_exit_threads)
+ return 0;
+
+ if (msgsock == INVALID_SOCKET)
+ throw "socket error";
+
+ std::string totalbuf;
+ int totallen = 0;
+ while (true) {
+ int err = recv(msgsock, buf, sizeof buf - 1, 0);
+ if (err < 0) {
+ totalbuf.clear();
+ break;
+ } else if (err == 0)
+ break;
+ else {
+ totallen += err;
+ buf[err] = '\0';
+ totalbuf += buf;
+ }
+ }
+ if (!totalbuf.empty())
+ processMessage(buf);
+ }
+ }
+ return 0;
+ }
+ catch (const char *err) {
+ std::string t = err;
+ t += "\nWSAGetLastError: ";
+
+ DWORD ec = WSAGetLastError();
+
+ char buf[4096];
+ strcpy(buf, Translate("N/A: Failed to format error message"));
+
+ DWORD fm = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, ec, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 4096, NULL);
+
+ if (!fm) {
+ t += Translate("N/A: FormatMessage failed, error code was 0x");
+ char tbuf[10];
+ t += itoa(ec, tbuf, 16);
+ } else
+ t += buf;
+
+ MessageBoxA(0, t.c_str(), Translate("Error"), MB_OK);
+ return 1;
+ }
+}
+
+void start_threads()
+{
+ g_exit_threads = false;
+ DWORD id;
+ g_udp_thread = CreateThread(NULL, 0,
+ (LPTHREAD_START_ROUTINE) udptcpThreadFunc, (LPVOID) 1, 0, &id);
+ g_tcp_thread = CreateThread(NULL, 0,
+ (LPTHREAD_START_ROUTINE) udptcpThreadFunc, NULL, 0, &id);
+}
+
+void stop_threads()
+{
+ g_exit_threads = true;
+ shutdown(g_udp_socket, 2);
+ shutdown(g_tcp_socket, 2);
+ closesocket(g_udp_socket);
+ closesocket(g_tcp_socket);
+ WaitForSingleObject(g_udp_thread, INFINITE);
+ WaitForSingleObject(g_tcp_thread, INFINITE);
+}
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ return &pluginInfo;
+}
+
+int ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ IsLogService = ServiceExists(MS_LOGSERVICE_REGISTER) != 0;
+ if (IsLogService)
+ logservice_register(LOG_ID, LPGENT(LOG_ID), NULL, NULL);
+
+ return 0;
+}
+
+extern "C" int __declspec(dllexport) Load()
+{
+ g_firstrun = true;
+ mir_getLP(&pluginInfo);
+
+ char buf[MAX_PATH+1];
+ strcpy(buf, ".");
+ g_mirandaDir = getcwd(buf, MAX_PATH);
+
+ InitializeCriticalSection(&g_popups_cs);
+ InitializeCriticalSection(&g_wsocklock);
+
+ registerSound("Notice");
+ registerSound("Message");
+ registerSound("Error");
+
+ load_settings();
+
+ HookEvent(ME_OPT_INITIALISE, OptionsInitialize);
+ HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+
+ start_threads();
+ return 0;
+}
+
+extern "C" int __declspec(dllexport) Unload(void)
+{
+ stop_threads();
+ WSACleanup();
+
+ DeleteCriticalSection(&g_popups_cs);
+ DeleteCriticalSection(&g_wsocklock);
+ return 0;
+}
diff --git a/plugins/NotifyAnything/src/options.cpp b/plugins/NotifyAnything/src/options.cpp new file mode 100644 index 0000000000..53c06ecf23 --- /dev/null +++ b/plugins/NotifyAnything/src/options.cpp @@ -0,0 +1,209 @@ +
+#include <windows.h>
+
+#include "common.h"
+#include "resource.h"
+
+NASettings g_settings;
+
+INT_PTR CALLBACK DlgProcOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ {
+ char buf[10];
+ CheckDlgButton(hwndDlg, NA_LOCAL_CHECK, g_settings.local_only ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, NA_DEBUG_MSG_CHECK, g_settings.debug_messages ? BST_CHECKED : BST_UNCHECKED);
+ if (IsLogService) {
+ LS_LOGINFO pli;
+ pli.szID = LOG_ID;
+ pli.hContact = NULL;
+ pli.szLogPath = NULL;
+ pli.Flags = 0;
+ pli.cbSize = sizeof(LS_LOGINFO);
+ if (!CallService(MS_LOGSERVICE_GETLOGINFO, (WPARAM)(LS_LOGINFO *) &pli, 0)) {
+ CheckDlgButton(hwndDlg, NA_LOG_CHECK, (pli.Flags && LSLI_LOGENABLED) ? BST_CHECKED : BST_UNCHECKED);
+ }
+ std::tstring InLogService = TranslateT("Services");
+ InLogService += _T(" -> ");
+ InLogService += TranslateT("LogService");
+ InLogService += _T(" -> ");
+ InLogService += TranslateT(LOG_ID);
+ SetDlgItemText(hwndDlg, NA_LOG_FILENAME, InLogService.c_str());
+ }
+ else {
+ CheckDlgButton(hwndDlg, NA_LOG_CHECK, g_settings.log_to_file ? BST_CHECKED : BST_UNCHECKED);
+ SetDlgItemTextA(hwndDlg, NA_LOG_FILENAME, g_settings.log_filename.c_str());
+ }
+ EnableWindow(GetDlgItem(hwndDlg, NA_LOG_CHECK), !IsLogService);
+ EnableWindow(GetDlgItem(hwndDlg, NA_LOG_FILENAME), !IsLogService);
+ EnableWindow(GetDlgItem(hwndDlg, NA_LOG_BROWSE), !IsLogService);
+ EnableWindow(GetDlgItem(hwndDlg, NA_DEBUG_MSG_CHECK), IsDlgButtonChecked(hwndDlg, NA_LOG_CHECK) ? 1 : 0);
+ CheckDlgButton(hwndDlg, NA_PCSPEAKER_CHECK, g_settings.use_pcspeaker ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, NA_ALLOW_EXECUTE, g_settings.allow_execute ? BST_CHECKED : BST_UNCHECKED);
+ SetDlgItemTextA(hwndDlg, NA_PORT, itoa(g_settings.port, buf, 10));
+ SetDlgItemTextA(hwndDlg, NA_PASSWORD, g_settings.password.c_str());
+ UINT state;
+ switch (g_settings.sound) {
+ case g_settings.always:
+ state = BST_CHECKED; break;
+ case g_settings.never:
+ state = BST_UNCHECKED; break;
+ case g_settings.request:
+ default:
+ state = BST_INDETERMINATE; break;
+ }
+ CheckDlgButton(hwndDlg, NA_SOUND_CHECK, state);
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case NA_SOUND_CHECK:
+ case NA_PCSPEAKER_CHECK:
+ case NA_LOCAL_CHECK:
+ case NA_DEBUG_MSG_CHECK:
+ case NA_LOG_CHECK:
+ case NA_PORT:
+ case NA_PASSWORD:
+ case NA_LOG_FILENAME:
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+
+ if (HIWORD(wParam) == BN_CLICKED) {
+ switch (LOWORD(wParam)) {
+ case NA_LOG_BROWSE:
+ TCHAR szTemp[MAX_PATH+1], szTemp1[MAX_PATH+1], szProfileDir[MAX_PATH+1];
+ GetDlgItemText(hwndDlg, NA_LOG_FILENAME, szTemp, MAX_PATH);
+ OPENFILENAME ofn = {0};
+ ofn.lStructSize = sizeof(ofn);
+ ofn.lpstrFile = szTemp;
+ ofn.nMaxFile = MAX_PATH;
+ ofn.hwndOwner = hwndDlg;
+ ofn.lpstrFilter = TranslateT("Log (*.log)\0*.log\0Text (*.txt)\0*.txt\0All Files (*.*)\0*.*\0");
+ ofn.nFilterIndex = 1;
+ // Use profile directory as default, if path is not specified
+ CallService(MS_DB_GETPROFILEPATH, (WPARAM)MAX_PATH, (LPARAM) szProfileDir);
+ ofn.lpstrInitialDir = szProfileDir;
+ ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
+ ofn.lpstrDefExt = _T("log");
+ if ( GetOpenFileName(&ofn)) {
+ CallService(MS_UTILS_PATHTORELATIVE, (WPARAM)(char*) szTemp, (LPARAM)(char*) szTemp1);
+ SetDlgItemText(hwndDlg, NA_LOG_FILENAME, szTemp1);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0);
+ }
+
+ return 0;
+ }
+ }
+ break;
+
+ case WM_NOTIFY:
+ if (((LPNMHDR)lParam)->idFrom != 0)
+ break;
+
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_RESET:
+ return TRUE;
+
+ case PSN_APPLY: {
+ UINT state = IsDlgButtonChecked(hwndDlg, NA_SOUND_CHECK);
+ NASettings s;
+ switch (state) {
+ case BST_CHECKED:
+ s.sound = s.always; break;
+ case BST_INDETERMINATE:
+ s.sound = s.request; break;
+ case BST_UNCHECKED:
+ default:
+ s.sound = s.never; break;
+ }
+ s.local_only = IsDlgButtonChecked(hwndDlg, NA_LOCAL_CHECK) != BST_UNCHECKED;
+ s.debug_messages = IsDlgButtonChecked(hwndDlg, NA_DEBUG_MSG_CHECK) != BST_UNCHECKED;
+ s.log_to_file = IsDlgButtonChecked(hwndDlg, NA_LOG_CHECK) != BST_UNCHECKED;
+ s.use_pcspeaker = IsDlgButtonChecked(hwndDlg, NA_PCSPEAKER_CHECK) != BST_UNCHECKED;
+ s.allow_execute = IsDlgButtonChecked(hwndDlg, NA_ALLOW_EXECUTE) != BST_UNCHECKED;
+
+ TCHAR buf[1000];
+ if (!GetDlgItemText(hwndDlg, NA_PORT, buf, sizeof buf - 1))
+ buf[0] = '\0';
+ int port = _ttoi(buf);
+ if (port <= 0 || port > 65535)
+ MessageBox(0, TranslateT("Invalid port number"), TranslateT("NotifyAnything"), MB_ICONWARNING | MB_OK);
+ else
+ s.port = port;
+
+ if (!GetDlgItemText(hwndDlg, NA_PASSWORD, buf, sizeof buf - 1))
+ buf[0] = '\0';
+ s.password = _T2A(buf);
+
+ if (!IsLogService) {
+ if (!GetDlgItemText(hwndDlg, NA_LOG_FILENAME, buf, sizeof buf - 1))
+ buf[0] = '\0';
+ s.log_filename = _T2A(buf);
+ }
+
+ g_settings = s;
+ save_settings();
+ stop_threads();
+ start_threads();
+ return TRUE;
+ }
+ }
+ break;
+ }
+ return FALSE;
+}
+
+void save_settings()
+{
+ db_set_b(NULL, PlugName, "local_only", g_settings.local_only);
+ db_set_b(NULL, PlugName, "debug_messages", g_settings.debug_messages);
+ db_set_b(NULL, PlugName, "log_to_file", g_settings.log_to_file);
+ db_set_b(NULL, PlugName, "beep", g_settings.sound);
+ db_set_b(NULL, PlugName, "use_pcspeaker", g_settings.use_pcspeaker);
+ db_set_b(NULL, PlugName, "allow_execute", g_settings.allow_execute);
+ db_set_dw(NULL, PlugName, "port", g_settings.port);
+ db_set_s(NULL, PlugName, "password", g_settings.password.c_str());
+ db_set_s(NULL, PlugName, "log_filename", g_settings.log_filename.c_str());
+}
+
+void load_settings()
+{
+ g_settings.local_only = db_get_b(NULL, PlugName, "local_only", 1) != 0;
+ g_settings.debug_messages = db_get_b(NULL, PlugName, "debug_messages", 0) != 0;
+ g_settings.log_to_file = db_get_b(NULL, PlugName, "log_to_file", 0) != 0;
+ g_settings.sound = NASettings::sound_t(db_get_b(NULL, PlugName, "beep", g_settings.request));
+ g_settings.use_pcspeaker = db_get_b(NULL, PlugName, "use_pcspeaker", 0) != 0;
+ g_settings.allow_execute = db_get_b(NULL, PlugName, "allow_execute", 0) != 0;
+ g_settings.port = db_get_dw(NULL, PlugName, "port", 12001);
+
+ DBVARIANT dbv;
+ if(!DBGetContactSetting(NULL, PlugName, "password", &dbv)) {
+ g_settings.password = dbv.pszVal;
+ DBFreeVariant(&dbv);
+ }
+
+ if(!DBGetContactSetting(NULL, PlugName, "log_filename", &dbv)) {
+ g_settings.log_filename = dbv.pszVal;
+ DBFreeVariant(&dbv);
+ }
+ else
+ g_settings.log_filename = g_mirandaDir + "\\"+LOG_ID+".log";
+}
+
+int OptionsInitialize(WPARAM wParam, LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp = { sizeof(odp) };
+ odp.pszTemplate = MAKEINTRESOURCEA(NA_OPTIONS);
+ odp.pfnDlgProc = DlgProcOpts;
+ odp.pszTitle = "Notify Anything";
+ odp.pszGroup = Translate("Plugins");
+ odp.position = 100000000;
+ odp.hInstance = hInst;
+ odp.groupPosition = 910000000;
+ odp.flags = ODPF_BOLDGROUPS;
+ Options_AddPage(wParam, &odp);
+ return 0;
+}
diff --git a/plugins/NotifyAnything/src/resource.h b/plugins/NotifyAnything/src/resource.h new file mode 100644 index 0000000000..8eee335059 --- /dev/null +++ b/plugins/NotifyAnything/src/resource.h @@ -0,0 +1,27 @@ +//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by NotifyAnything.rc
+//
+#define NA_OPTIONS 106
+#define NA_LOCAL_CHECK 1002
+#define NA_SOUND_CHECK 1007
+#define NA_LOG_CHECK 1008
+#define NA_DEBUG_MSG_CHECK 1009
+#define NA_PCSPEAKER_CHECK 1011
+#define NA_PORT 1012
+#define NA_ALLOW_EXECUTE 1013
+#define NA_PASSWORD 1014
+#define NA_LOG_FILENAME 1015
+#define NA_LOG_SELECT 1016
+#define NA_LOG_BROWSE 1017
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 104
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1018
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/plugins/NotifyAnything/src/stdafx.cpp b/plugins/NotifyAnything/src/stdafx.cpp new file mode 100644 index 0000000000..7a516adf58 --- /dev/null +++ b/plugins/NotifyAnything/src/stdafx.cpp @@ -0,0 +1,18 @@ +/*
+Copyright (C) 2012 Miranda NG team (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 "common.h"
\ No newline at end of file |