123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- /*
- * Copyright 2005 - 2016 Zarafa and its licensors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- #include <Python.h>
- #include <kopano/platform.h>
- #include <memory>
- #include <new>
- #include <mapi.h>
- #include <mapix.h>
- #include <mapiutil.h>
- #include <mapidefs.h>
- #include <kopano/mapiext.h>
- #include <mapiguid.h>
- #include "PythonSWIGRuntime.h"
- #include "PyMapiPlugin.h"
- #include <kopano/stringutil.h>
- #include "frameobject.h"
- #define NEW_SWIG_INTERFACE_POINTER_OBJ(pyswigobj, objpointer, typeobj) {\
- if (objpointer) {\
- pyswigobj.reset(SWIG_NewPointerObj((void *)objpointer, typeobj, SWIG_POINTER_OWN | 0)); \
- PY_HANDLE_ERROR(m_lpLogger, pyswigobj) \
- \
- objpointer->AddRef();\
- } else {\
- pyswigobj.reset(Py_None); \
- Py_INCREF(Py_None);\
- }\
- }
- #define BUILD_SWIG_TYPE(pyswigobj, type) {\
- pyswigobj = SWIG_TypeQuery(type); \
- if (!pyswigobj) {\
- assert(false);\
- return S_FALSE;\
- }\
- }
- class kcpy_decref {
- public:
- void operator()(PyObject *obj) { Py_DECREF(obj); }
- };
- typedef KCHL::memory_ptr<PyObject, kcpy_decref> PyObjectAPtr;
- class PyMapiPlugin _kc_final : public pym_plugin_intf {
- public:
- PyMapiPlugin(void) = default;
- virtual ~PyMapiPlugin(void);
- HRESULT Init(ECLogger *lpLogger, PyObject *lpModMapiPlugin, const char* lpPluginManagerClassName, const char *lpPluginPath);
- virtual HRESULT MessageProcessing(const char *func, IMAPISession *, IAddrBook *, IMsgStore *, IMAPIFolder *, IMessage *, ULONG *result);
- virtual HRESULT RulesProcessing(const char *func, IMAPISession *, IAddrBook *, IMsgStore *, IExchangeModifyTable *emt_rules, ULONG *result);
- virtual HRESULT RequestCallExecution(const char *func, IMAPISession *, IAddrBook *, IMsgStore *, IMAPIFolder *, IMessage *, ULONG *do_callexe, ULONG *result);
- swig_type_info *type_p_ECLogger = nullptr, *type_p_IAddrBook = nullptr;
- swig_type_info *type_p_IMAPIFolder = nullptr;
- swig_type_info *type_p_IMAPISession = nullptr;
- swig_type_info *type_p_IMsgStore = nullptr;
- swig_type_info *type_p_IMessage = nullptr;
- swig_type_info *type_p_IExchangeModifyTable = nullptr;
- private:
- PyObjectAPtr m_ptrMapiPluginManager{nullptr};
- ECLogger *m_lpLogger = nullptr;
- /* Inhibit (accidental) copying */
- PyMapiPlugin(const PyMapiPlugin &) = delete;
- PyMapiPlugin &operator=(const PyMapiPlugin &) = delete;
- };
- struct pym_factory_priv {
- PyObjectAPtr m_ptrModMapiPlugin{nullptr};
- };
- /**
- * Handle the python errors
- *
- * note: The traceback doesn't work very well
- */
- static HRESULT PyHandleError(ECLogger *lpLogger, PyObject *pyobj)
- {
- HRESULT hr = hrSuccess;
- if (!pyobj)
- {
- PyObject *lpErr = PyErr_Occurred();
- if(lpErr) {
- PyObjectAPtr ptype, pvalue, ptraceback;
- PyErr_Fetch(&~ptype, &~pvalue, &~ptraceback);
- auto traceback = reinterpret_cast<PyTracebackObject *>(ptraceback.get());
- const char *pStrErrorMessage = "Unknown";
- const char *pStrType = "Unknown";
- if (pvalue != nullptr)
- pStrErrorMessage = PyString_AsString(pvalue.get());
- if (ptype != nullptr)
- pStrType = PyString_AsString(ptype.get());
- if (lpLogger)
- {
- lpLogger->Log(EC_LOGLEVEL_ERROR, " Python type: %s", pStrType);
- lpLogger->Log(EC_LOGLEVEL_ERROR, " Python error: %s", pStrErrorMessage);
-
- while (traceback && traceback->tb_next != NULL) {
- auto frame = traceback->tb_frame;
- if (frame) {
- int line = frame->f_lineno;
- const char *filename = PyString_AsString(frame->f_code->co_filename);
- const char *funcname = PyString_AsString(frame->f_code->co_name);
-
- lpLogger->Log(EC_LOGLEVEL_ERROR, " Python trace: %s(%d) %s", filename, line, funcname);
- } else {
- lpLogger->Log(EC_LOGLEVEL_ERROR, " Python trace: Unknown");
- }
-
- traceback = traceback->tb_next;
- }
- }
- PyErr_Clear();
- }
- assert(false);
- hr = S_FALSE;
- }
- return hr;
- }
- #define PY_HANDLE_ERROR(logger, pyobj) { \
- hr = PyHandleError(logger, pyobj);\
- if (hr != hrSuccess) \
- return hr; \
- }
- #define PY_CALL_METHOD(pluginmanager, functionname, returnmacro, format, ...) {\
- PyObjectAPtr ptrResult;\
- {\
- ptrResult.reset(PyObject_CallMethod(pluginmanager, const_cast<char *>(functionname), const_cast<char *>(format), __VA_ARGS__)); \
- PY_HANDLE_ERROR(m_lpLogger, ptrResult)\
- \
- returnmacro\
- }\
- }
- /**
- * Helper macro to parse the python return values which work together
- * with the macro PY_CALL_METHOD.
- *
- */
- #define PY_PARSE_TUPLE_HELPER(format, ...) {\
- if(!PyArg_ParseTuple(ptrResult, format, __VA_ARGS__)) { \
- m_lpLogger->Log(EC_LOGLEVEL_ERROR, " Wrong return value from the pluginmanager or plugin"); \
- PY_HANDLE_ERROR(m_lpLogger, NULL) \
- } \
- }
- PyMapiPlugin::~PyMapiPlugin(void)
- {
- if (m_lpLogger != nullptr)
- m_lpLogger->Release();
- }
- /**
- * Initialize the PyMapiPlugin.
- *
- * @param[in] lpConfig Pointer to the configuration class
- * @param[in] lpLogger Pointer to the logger
- * @param[in] lpPluginManagerClassName The class name of the plugin handler
- *
- * @return Standard mapi errorcodes
- */
- HRESULT PyMapiPlugin::Init(ECLogger *lpLogger, PyObject *lpModMapiPlugin, const char* lpPluginManagerClassName, const char *lpPluginPath)
- {
- HRESULT hr = S_OK;
- PyObjectAPtr ptrPyLogger;
- PyObjectAPtr ptrClass;
- PyObjectAPtr ptrArgs;
- std::string strEnvPython;
- if (!lpModMapiPlugin)
- return S_OK;
- m_lpLogger = lpLogger;
- if (m_lpLogger)
- m_lpLogger->AddRef();
- // Init MAPI-swig types
- BUILD_SWIG_TYPE(type_p_IMessage, "_p_IMessage");
- BUILD_SWIG_TYPE(type_p_IMAPISession, "_p_IMAPISession");
- BUILD_SWIG_TYPE(type_p_IMsgStore, "_p_IMsgStore");
- BUILD_SWIG_TYPE(type_p_IAddrBook, "_p_IAddrBook");
- BUILD_SWIG_TYPE(type_p_IMAPIFolder, "_p_IMAPIFolder");
- BUILD_SWIG_TYPE(type_p_ECLogger, "_p_ECLogger");
- BUILD_SWIG_TYPE(type_p_IExchangeModifyTable, "_p_IExchangeModifyTable");
- // Init logger swig object
- NEW_SWIG_INTERFACE_POINTER_OBJ(ptrPyLogger, m_lpLogger, type_p_ECLogger);
- // Init plugin class
- ptrClass.reset(PyObject_GetAttrString(lpModMapiPlugin, /*char* */lpPluginManagerClassName));
- PY_HANDLE_ERROR(m_lpLogger, ptrClass);
- ptrArgs.reset(Py_BuildValue("(sO)", lpPluginPath, ptrPyLogger.get()));
- PY_HANDLE_ERROR(m_lpLogger, ptrArgs);
- m_ptrMapiPluginManager.reset(PyObject_CallObject(ptrClass, ptrArgs));
- PY_HANDLE_ERROR(m_lpLogger, m_ptrMapiPluginManager);
- return hr;
- }
- /**
- * Plugin python call between MAPI and python.
- *
- * @param[in] lpFunctionName Python function name to call in the plugin framework.
- * The function must be exist the lpPluginManagerClassName defined in the init function.
- * @param[in] lpMapiSession Pointer to a mapi session. Not NULL.
- * @param[in] lpAdrBook Pointer to a mapi Addressbook. Not NULL.
- * @param[in] lpMsgStore Pointer to a mapi mailbox. Can be NULL.
- * @param[in] lpInbox
- * @param[in] lpMessage Pointer to a mapi message.
- *
- * @return Default mapi error codes
- *
- * @todo something with exit codes
- */
- HRESULT PyMapiPlugin::MessageProcessing(const char *lpFunctionName, IMAPISession *lpMapiSession, IAddrBook *lpAdrBook, IMsgStore *lpMsgStore, IMAPIFolder *lpInbox, IMessage *lpMessage, ULONG *lpulResult)
- {
- HRESULT hr = hrSuccess;
- PyObjectAPtr ptrPySession, ptrPyAddrBook, ptrPyStore, ptrPyMessage, ptrPyFolderInbox;
- if (!m_ptrMapiPluginManager)
- return hrSuccess;
- if (!lpFunctionName || !lpMapiSession || !lpAdrBook)
- return MAPI_E_INVALID_PARAMETER;
- if (!m_ptrMapiPluginManager)
- return MAPI_E_CALL_FAILED;
- NEW_SWIG_INTERFACE_POINTER_OBJ(ptrPySession, lpMapiSession, type_p_IMAPISession)
- NEW_SWIG_INTERFACE_POINTER_OBJ(ptrPyAddrBook, lpAdrBook, type_p_IAddrBook)
- NEW_SWIG_INTERFACE_POINTER_OBJ(ptrPyStore, lpMsgStore, type_p_IMsgStore)
- NEW_SWIG_INTERFACE_POINTER_OBJ(ptrPyFolderInbox, lpInbox, type_p_IMAPIFolder)
- NEW_SWIG_INTERFACE_POINTER_OBJ(ptrPyMessage, lpMessage, type_p_IMessage)
- // Call the python function and get the (hr) return code back
- PY_CALL_METHOD(m_ptrMapiPluginManager, const_cast<char *>(lpFunctionName),
- PY_PARSE_TUPLE_HELPER("I", lpulResult), "OOOOO",
- ptrPySession.get(), ptrPyAddrBook.get(), ptrPyStore.get(),
- ptrPyFolderInbox.get(), ptrPyMessage.get());
- return hr;
- }
- /**
- * Hook for change the rules.
- *
- * @param[in] lpFunctionName Python function name to hook the rules in the plugin framework.
- * The function must be exist the lpPluginManagerClassName defined in the init function.
- * @param[in] lpEMTRules Pointer to a mapi IExchangeModifyTable object
- */
- HRESULT PyMapiPlugin::RulesProcessing(const char *lpFunctionName, IMAPISession *lpMapiSession, IAddrBook *lpAdrBook, IMsgStore *lpMsgStore, IExchangeModifyTable *lpEMTRules, ULONG *lpulResult)
- {
- HRESULT hr = hrSuccess;
- PyObjectAPtr ptrPySession, ptrPyAddrBook, ptrPyStore, ptrEMTIn;
- if (!m_ptrMapiPluginManager)
- return hrSuccess;
- if (!lpFunctionName || !lpMapiSession || !lpAdrBook || !lpEMTRules)
- return MAPI_E_INVALID_PARAMETER;
- if (!m_ptrMapiPluginManager)
- return MAPI_E_CALL_FAILED;
-
- NEW_SWIG_INTERFACE_POINTER_OBJ(ptrPySession, lpMapiSession, type_p_IMAPISession)
- NEW_SWIG_INTERFACE_POINTER_OBJ(ptrPyAddrBook, lpAdrBook, type_p_IAddrBook)
- NEW_SWIG_INTERFACE_POINTER_OBJ(ptrPyStore, lpMsgStore, type_p_IMsgStore)
- NEW_SWIG_INTERFACE_POINTER_OBJ(ptrEMTIn, lpEMTRules, type_p_IExchangeModifyTable)
- PY_CALL_METHOD(m_ptrMapiPluginManager, const_cast<char *>(lpFunctionName),
- PY_PARSE_TUPLE_HELPER("I", lpulResult), "OOOO",
- ptrPySession.get(), ptrPyAddrBook.get(), ptrPyStore.get(),
- ptrEMTIn.get());
- return hr;
- }
- HRESULT PyMapiPlugin::RequestCallExecution(const char *lpFunctionName, IMAPISession *lpMapiSession, IAddrBook *lpAdrBook, IMsgStore *lpMsgStore, IMAPIFolder *lpFolder, IMessage *lpMessage, ULONG *lpulDoCallexe, ULONG *lpulResult)
- {
- HRESULT hr = hrSuccess;
- PyObjectAPtr ptrPySession, ptrPyAddrBook, ptrPyStore, ptrFolder, ptrMessage;
- if (!m_ptrMapiPluginManager)
- return hrSuccess;
- if (!lpFunctionName || !lpMapiSession || !lpAdrBook || !lpulDoCallexe)
- return MAPI_E_INVALID_PARAMETER;
- if (!m_ptrMapiPluginManager)
- return MAPI_E_CALL_FAILED;
- NEW_SWIG_INTERFACE_POINTER_OBJ(ptrPySession, lpMapiSession, type_p_IMAPISession)
- NEW_SWIG_INTERFACE_POINTER_OBJ(ptrPyAddrBook, lpAdrBook, type_p_IAddrBook)
- NEW_SWIG_INTERFACE_POINTER_OBJ(ptrPyStore, lpMsgStore, type_p_IMsgStore)
- NEW_SWIG_INTERFACE_POINTER_OBJ(ptrFolder, lpFolder, type_p_IMAPIFolder)
- NEW_SWIG_INTERFACE_POINTER_OBJ(ptrMessage, lpMessage, type_p_IMessage)
- PY_CALL_METHOD(m_ptrMapiPluginManager, const_cast<char *>(lpFunctionName),
- PY_PARSE_TUPLE_HELPER("I|I", lpulResult, lpulDoCallexe), "OOOOO",
- ptrPySession.get(), ptrPyAddrBook.get(), ptrPyStore.get(),
- ptrFolder.get(), ptrMessage.get());
- return hr;
- }
- PyMapiPluginFactory::PyMapiPluginFactory() :
- m_priv(new struct pym_factory_priv)
- {
- }
- PyMapiPluginFactory::~PyMapiPluginFactory()
- {
- if (m_priv != nullptr && m_priv->m_ptrModMapiPlugin != nullptr) {
- m_priv->m_ptrModMapiPlugin = nullptr;
- Py_Finalize();
- }
- if (m_lpLogger != nullptr)
- m_lpLogger->Release();
- delete m_priv;
- }
- HRESULT PyMapiPluginFactory::create_plugin(ECConfig *lpConfig,
- ECLogger *lpLogger, const char *lpPluginManagerClassName,
- pym_plugin_intf **lppPlugin)
- {
- HRESULT hr = S_OK;
- std::string strEnvPython;
- char *lpEnvPython = NULL;
- PyObjectAPtr ptrName;
- PyObjectAPtr ptrModule;
- m_lpLogger = lpLogger;
- if (m_lpLogger)
- m_lpLogger->AddRef();
- m_bEnablePlugin = parseBool(lpConfig->GetSetting("plugin_enabled", NULL, "no"));
- if (m_bEnablePlugin) {
- m_strPluginPath = lpConfig->GetSetting("plugin_path");
- strEnvPython = lpConfig->GetSetting("plugin_manager_path");
- lpEnvPython = getenv("PYTHONPATH");
- if (lpEnvPython)
- strEnvPython += std::string(":") + lpEnvPython;
- setenv("PYTHONPATH", strEnvPython.c_str(), 1);
- lpLogger->Log(EC_LOGLEVEL_DEBUG, "PYTHONPATH = %s", strEnvPython.c_str());
- Py_Initialize();
- ptrModule.reset(PyImport_ImportModule("MAPI"));
- PY_HANDLE_ERROR(m_lpLogger, ptrModule);
- // Import python plugin framework
- // @todo error unable to find file xxx
- ptrName.reset(PyString_FromString("mapiplugin"));
- m_priv->m_ptrModMapiPlugin.reset(PyImport_Import(ptrName));
- PY_HANDLE_ERROR(m_lpLogger, m_priv->m_ptrModMapiPlugin);
- }
- std::unique_ptr<PyMapiPlugin> lpPlugin(new(std::nothrow) PyMapiPlugin);
- if (lpPlugin == nullptr)
- return MAPI_E_NOT_ENOUGH_MEMORY;
- hr = lpPlugin->Init(m_lpLogger, m_priv->m_ptrModMapiPlugin, lpPluginManagerClassName, m_strPluginPath.c_str());
- if (hr != S_OK)
- return hr;
- *lppPlugin = lpPlugin.release();
- return S_OK;
- }
|