Message& Message::reset() noexcept { return clear_headers().clear_body(); }
int local_scan(int fd, uschar **return_text) { PyObject *user_dict; PyObject *user_func; PyObject *result; PyObject *header_tuple; PyObject *original_recipients; PyObject *working_recipients; if (!expy_enabled) return LOCAL_SCAN_ACCEPT; if (!Py_IsInitialized()) /* local_scan() may have already been run */ { Py_Initialize(); ExPy_Header_Line.ob_type = &PyType_Type; } if (!expy_exim_dict) { PyObject *module = Py_InitModule(expy_exim_module, expy_exim_methods); /* Borrowed reference */ Py_INCREF(module); /* convert to New reference */ expy_exim_dict = PyModule_GetDict(module); /* Borrowed reference */ Py_INCREF(expy_exim_dict); /* convert to New reference */ } if (!expy_user_module) { if (expy_path_add) { PyObject *sys_module; PyObject *sys_dict; PyObject *sys_path; PyObject *add_value; sys_module = PyImport_ImportModule("sys"); /* New Reference */ if (!sys_module) { PyErr_Clear(); *return_text = "Internal error, can't import Python sys module"; log_write(0, LOG_REJECT, "Couldn't import Python 'sys' module"); /* FIXME: write out an exception traceback if possible to Exim log */ return PYTHON_FAILURE_RETURN; } sys_dict = PyModule_GetDict(sys_module); /* Borrowed Reference, never fails */ sys_path = PyMapping_GetItemString(sys_dict, "path"); /* New reference */ if (!sys_path || (!PyList_Check(sys_path))) { PyErr_Clear(); /* in case sys_path was NULL, harmless otherwise */ *return_text = "Internal error, sys.path doesn't exist or isn't a list"; log_write(0, LOG_REJECT, "expy: Python sys.path doesn't exist or isn't a list"); /* FIXME: write out an exception traceback if possible to Exim log */ return PYTHON_FAILURE_RETURN; } add_value = PyString_FromString(expy_path_add); /* New reference */ if (!add_value) { PyErr_Clear(); log_write(0, LOG_PANIC, "expy: Failed to create Python string from [%s]", expy_path_add); return PYTHON_FAILURE_RETURN; } if (PyList_Append(sys_path, add_value)) { PyErr_Clear(); log_write(0, LOG_PANIC, "expy: Failed to append [%s] to Python sys.path", expy_path_add); } Py_DECREF(add_value); Py_DECREF(sys_path); Py_DECREF(sys_module); } expy_user_module = PyImport_ImportModule(expy_scan_module); /* New Reference */ if (!expy_user_module) { PyErr_Clear(); *return_text = "Internal error, can't import Python local_scan module"; log_write(0, LOG_REJECT, "Couldn't import Python '%s' module", expy_scan_module); return PYTHON_FAILURE_RETURN; } } user_dict = PyModule_GetDict(expy_user_module); /* Borrowed Reference, never fails */ user_func = PyMapping_GetItemString(user_dict, expy_scan_function); /* New reference */ if (!user_func) { PyErr_Clear(); *return_text = "Internal error, module doesn't have local_scan function"; log_write(0, LOG_REJECT, "Python %s module doesn't have a %s function", expy_scan_module, expy_scan_function); return PYTHON_FAILURE_RETURN; } /* so far so good, prepare to run function */ /* Copy exim variables */ expy_dict_int("debug_selector", debug_selector); expy_dict_int("host_checking", host_checking); expy_dict_string("interface_address", interface_address); expy_dict_int("interface_port", interface_port); expy_dict_string("message_id", message_id); expy_dict_string("received_protocol", received_protocol); expy_dict_string("sender_address", sender_address); expy_dict_string("sender_host_address", sender_host_address); expy_dict_string("sender_host_authenticated", sender_host_authenticated); expy_dict_string("sender_host_name", sender_host_name); expy_dict_int("sender_host_port", sender_host_port); expy_dict_int("fd", fd); /* copy some constants */ expy_dict_int("LOG_MAIN", LOG_MAIN); expy_dict_int("LOG_PANIC", LOG_PANIC); expy_dict_int("LOG_REJECT", LOG_REJECT); expy_dict_int("LOCAL_SCAN_ACCEPT", LOCAL_SCAN_ACCEPT); expy_dict_int("LOCAL_SCAN_ACCEPT_FREEZE", LOCAL_SCAN_ACCEPT_FREEZE); expy_dict_int("LOCAL_SCAN_ACCEPT_QUEUE", LOCAL_SCAN_ACCEPT_QUEUE); expy_dict_int("LOCAL_SCAN_REJECT", LOCAL_SCAN_REJECT); expy_dict_int("LOCAL_SCAN_REJECT_NOLOGHDR", LOCAL_SCAN_REJECT_NOLOGHDR); expy_dict_int("LOCAL_SCAN_TEMPREJECT", LOCAL_SCAN_TEMPREJECT); expy_dict_int("LOCAL_SCAN_TEMPREJECT_NOLOGHDR", LOCAL_SCAN_TEMPREJECT_NOLOGHDR); expy_dict_int("MESSAGE_ID_LENGTH", MESSAGE_ID_LENGTH); expy_dict_int("SPOOL_DATA_START_OFFSET", SPOOL_DATA_START_OFFSET); expy_dict_int("D_v", D_v); expy_dict_int("D_local_scan", D_local_scan); /* set the headers */ header_tuple = get_headers(); PyDict_SetItemString(expy_exim_dict, "headers", header_tuple); /* * make list of recipients, give module a copy to work with in * List format, but keep original tuple to compare against later */ original_recipients = get_recipients(); /* New reference */ working_recipients = PySequence_List(original_recipients); /* New reference */ PyDict_SetItemString(expy_exim_dict, "recipients", working_recipients); Py_DECREF(working_recipients); /* Try calling our function */ result = PyObject_CallFunction(user_func, NULL); /* New reference */ Py_DECREF(user_func); /* Don't need ref to function anymore */ /* Check for Python exception */ if (!result) { PyErr_Clear(); *return_text = "Internal error, local_scan function failed"; Py_DECREF(original_recipients); clear_headers(header_tuple); Py_DECREF(header_tuple); return PYTHON_FAILURE_RETURN; // FIXME: should write exception to exim log somehow } /* User code may have replaced recipient list, so re-get ref */ working_recipients = PyDict_GetItemString(expy_exim_dict, "recipients"); /* Borrowed reference */ Py_XINCREF(working_recipients); /* convert to New reference */ /* * reconcile original recipient list with what's present after * Python code is done */ if ((!working_recipients) || (!PySequence_Check(working_recipients)) || (PySequence_Size(working_recipients) == 0)) /* Python code either deleted exim.recipients alltogether, or replaced it with a non-list, or emptied out the list */ recipients_count = 0; else { int i; /* remove original recipients not on the working list, reverse order important! */ for (i = recipients_count - 1; i >= 0; i--) { PyObject *addr = PyTuple_GET_ITEM(original_recipients, i); /* borrowed ref */ if (!PySequence_Contains(working_recipients, addr)) expy_remove_recipient(i); } /* add new recipients not in the original list */ for (i = PySequence_Size(working_recipients) - 1; i >= 0; i--) { PyObject *addr = PySequence_GetItem(working_recipients, i); if (!PySequence_Contains(original_recipients, addr)) receive_add_recipient(PyString_AsString(addr), -1); Py_DECREF(addr); } } Py_XDECREF(working_recipients); /* No longer needed */ Py_DECREF(original_recipients); /* No longer needed */ clear_headers(header_tuple); Py_DECREF(header_tuple); /* No longer needed */ /* Deal with the return value, first see if python returned a non-empty sequence */ if (PySequence_Check(result) && (PySequence_Size(result) > 0)) { /* save first item */ PyObject *rc = PySequence_GetItem(result, 0); /* if more than one item, convert 2nd item to string and use as return text */ if (PySequence_Size(result) > 1) { PyObject *str; PyObject *obj = PySequence_GetItem(result, 1); /* New reference */ str = PyObject_Str(obj); /* New reference */ *return_text = string_copy(PyString_AsString(str)); Py_DECREF(obj); Py_DECREF(str); } /* drop the sequence, and focus on the first item we saved */ Py_DECREF(result); result = rc; } /* If we have an integer, return that to Exim */ if (PyInt_Check(result)) { int rc = PyInt_AsLong(result); Py_DECREF(result); return rc; } /* didn't return anything usable */ Py_DECREF(result); *return_text = "Internal error, bad return code"; log_write(0, LOG_REJECT, "Python %s.%s function didn't return integer", expy_scan_module, expy_scan_function); return PYTHON_FAILURE_RETURN; }