From b3b392b354f41ee7989c4c1f3ba8452b68ff67b8 Mon Sep 17 00:00:00 2001 From: grez911 Date: Sat, 4 Feb 2017 14:34:24 +0300 Subject: [PATCH] Working project --- add_list.php | 42 + del_list.php | 40 + delete.svg | 65 + edit_list.php | 82 + error.php | 7 + help.svg | 168 + index.php | 36 + init.php | 23 + login.php | 74 + logout.php | 8 + misc/.htaccess | 1 + misc/TODO.txt | 40 + misc/config.txt | 4 + misc/init.sh | 16 + misc/manage_domains.sh | 42 + misc/move/exim4.conf | 2100 ++++++++++++ misc/move/exim4.filter | 2 + misc/move/foot_filter/Makefile | 14 + misc/move/foot_filter/foot_filter | Bin 0 -> 288824 bytes misc/move/foot_filter/foot_filter.c | 2685 ++++++++++++++++ misc/move/mlmmj-footer-receive | 8 + ...d0026471aaaa2a26be865_0.file.error.tpl.php | 57 + ...bed805054bb093a12538d_0.file.index.tpl.php | 122 + ...30d7da2c9680cee0a_0.file.edit_list.tpl.php | 70 + ...d534fc6fbcdf5d2e8c239_0.file.login.tpl.php | 90 + ...51c7c51b98d4db367f54c_0.file.index.tpl.php | 53 + ...55bf29207ecb92147b506_0.file.login.tpl.php | 40 + ...07c28df0b8fa212af00dd_0.file.index.tpl.php | 118 + ...acc80d159739f4ab1_0.file.edit_list.tpl.php | 254 ++ ...ebde8c12ed050e90ded57_0.file.index.tpl.php | 122 + ...08d01bdc17781ec0b_0.file.edit_list.tpl.php | 254 ++ ...612378cd3ff5f5b6adcae_0.file.error.tpl.php | 59 + ...366b69d3a18e59c5c2b67_0.file.login.tpl.php | 80 + ...2f00f9ee0f411ff19_0.file.edit_list.tpl.php | 236 ++ misc/smarty/templates_en/edit_list.tpl | 213 ++ misc/smarty/templates_en/error.tpl | 33 + misc/smarty/templates_en/index.tpl | 80 + misc/smarty/templates_en/login.tpl | 62 + misc/smarty/templates_ru/edit_list.tpl | 213 ++ misc/smarty/templates_ru/error.tpl | 33 + misc/smarty/templates_ru/index.tpl | 80 + misc/smarty/templates_ru/login.tpl | 62 + misc/smarty_libs/Autoloader.php | 110 + misc/smarty_libs/Smarty.class.php | 1549 +++++++++ misc/smarty_libs/SmartyBC.class.php | 455 +++ misc/smarty_libs/bootstrap.php | 17 + misc/smarty_libs/debug.tpl | 160 + misc/smarty_libs/plugins/block.textformat.php | 114 + misc/smarty_libs/plugins/function.counter.php | 73 + misc/smarty_libs/plugins/function.cycle.php | 105 + misc/smarty_libs/plugins/function.fetch.php | 221 ++ .../plugins/function.html_checkboxes.php | 251 ++ .../plugins/function.html_image.php | 166 + .../plugins/function.html_options.php | 206 ++ .../plugins/function.html_radios.php | 234 ++ .../plugins/function.html_select_date.php | 388 +++ .../plugins/function.html_select_time.php | 363 +++ .../plugins/function.html_table.php | 176 + misc/smarty_libs/plugins/function.mailto.php | 153 + misc/smarty_libs/plugins/function.math.php | 109 + .../plugins/modifier.capitalize.php | 101 + .../plugins/modifier.date_format.php | 67 + .../plugins/modifier.debug_print_var.php | 112 + misc/smarty_libs/plugins/modifier.escape.php | 207 ++ .../plugins/modifier.regex_replace.php | 58 + misc/smarty_libs/plugins/modifier.replace.php | 35 + misc/smarty_libs/plugins/modifier.spacify.php | 27 + .../smarty_libs/plugins/modifier.truncate.php | 66 + .../plugins/modifiercompiler.cat.php | 29 + .../modifiercompiler.count_characters.php | 32 + .../modifiercompiler.count_paragraphs.php | 27 + .../modifiercompiler.count_sentences.php | 27 + .../plugins/modifiercompiler.count_words.php | 32 + .../plugins/modifiercompiler.default.php | 35 + .../plugins/modifiercompiler.escape.php | 115 + .../plugins/modifiercompiler.from_charset.php | 33 + .../plugins/modifiercompiler.indent.php | 33 + .../plugins/modifiercompiler.lower.php | 31 + .../plugins/modifiercompiler.noprint.php | 21 + .../modifiercompiler.string_format.php | 25 + .../plugins/modifiercompiler.strip.php | 33 + .../plugins/modifiercompiler.strip_tags.php | 29 + .../plugins/modifiercompiler.to_charset.php | 33 + .../plugins/modifiercompiler.unescape.php | 50 + .../plugins/modifiercompiler.upper.php | 29 + .../plugins/modifiercompiler.wordwrap.php | 51 + .../plugins/outputfilter.trimwhitespace.php | 89 + .../plugins/shared.escape_special_chars.php | 34 + .../plugins/shared.literal_compiler_param.php | 36 + .../plugins/shared.make_timestamp.php | 45 + .../plugins/shared.mb_str_replace.php | 55 + .../smarty_libs/plugins/shared.mb_unicode.php | 54 + .../plugins/shared.mb_wordwrap.php | 75 + .../variablefilter.htmlspecialchars.php | 19 + .../sysplugins/smarty_cacheresource.php | 220 ++ .../smarty_cacheresource_custom.php | 275 ++ .../smarty_cacheresource_keyvaluestore.php | 507 +++ misc/smarty_libs/sysplugins/smarty_data.php | 68 + .../sysplugins/smarty_internal_block.php | 90 + .../smarty_internal_cacheresource_file.php | 220 ++ .../smarty_internal_compile_append.php | 51 + .../smarty_internal_compile_assign.php | 94 + .../smarty_internal_compile_block.php | 202 ++ .../smarty_internal_compile_block_child.php | 54 + .../smarty_internal_compile_block_parent.php | 73 + .../smarty_internal_compile_break.php | 110 + .../smarty_internal_compile_call.php | 89 + .../smarty_internal_compile_capture.php | 112 + .../smarty_internal_compile_config_load.php | 98 + .../smarty_internal_compile_continue.php | 42 + .../smarty_internal_compile_debug.php | 42 + .../smarty_internal_compile_eval.php | 72 + .../smarty_internal_compile_extends.php | 144 + .../smarty_internal_compile_for.php | 170 + .../smarty_internal_compile_foreach.php | 344 ++ .../smarty_internal_compile_function.php | 216 ++ .../sysplugins/smarty_internal_compile_if.php | 211 ++ .../smarty_internal_compile_include.php | 344 ++ .../smarty_internal_compile_include_php.php | 111 + .../smarty_internal_compile_insert.php | 155 + .../smarty_internal_compile_ldelim.php | 40 + .../smarty_internal_compile_make_nocache.php | 64 + .../smarty_internal_compile_nocache.php | 75 + ..._internal_compile_private_block_plugin.php | 122 + ...nternal_compile_private_foreachsection.php | 221 ++ ...ternal_compile_private_function_plugin.php | 74 + ...arty_internal_compile_private_modifier.php | 157 + ..._compile_private_object_block_function.php | 42 + ...ternal_compile_private_object_function.php | 86 + .../smarty_internal_compile_private_php.php | 216 ++ ...ernal_compile_private_print_expression.php | 162 + ...ernal_compile_private_registered_block.php | 72 + ...al_compile_private_registered_function.php | 82 + ...ernal_compile_private_special_variable.php | 126 + .../smarty_internal_compile_rdelim.php | 40 + .../smarty_internal_compile_section.php | 469 +++ .../smarty_internal_compile_setfilter.php | 70 + ...ty_internal_compile_shared_inheritance.php | 46 + .../smarty_internal_compile_while.php | 100 + .../smarty_internal_compilebase.php | 196 ++ .../smarty_internal_config_file_compiler.php | 197 ++ .../smarty_internal_configfilelexer.php | 732 +++++ .../smarty_internal_configfileparser.php | 1022 ++++++ .../sysplugins/smarty_internal_data.php | 285 ++ .../sysplugins/smarty_internal_debug.php | 426 +++ .../smarty_internal_extension_handler.php | 171 + ...rty_internal_method_addautoloadfilters.php | 52 + ...ty_internal_method_adddefaultmodifiers.php | 42 + .../smarty_internal_method_append.php | 74 + .../smarty_internal_method_appendbyref.php | 50 + .../smarty_internal_method_assignbyref.php | 36 + .../smarty_internal_method_assignglobal.php | 43 + .../smarty_internal_method_clearallassign.php | 37 + .../smarty_internal_method_clearallcache.php | 40 + .../smarty_internal_method_clearassign.php | 44 + .../smarty_internal_method_clearcache.php | 44 + ..._internal_method_clearcompiledtemplate.php | 122 + .../smarty_internal_method_clearconfig.php | 41 + ...marty_internal_method_compileallconfig.php | 33 + ...ty_internal_method_compilealltemplates.php | 119 + .../smarty_internal_method_configload.php | 183 ++ .../smarty_internal_method_createdata.php | 44 + ...rty_internal_method_getautoloadfilters.php | 37 + ...arty_internal_method_getconfigvariable.php | 34 + .../smarty_internal_method_getconfigvars.php | 58 + ...marty_internal_method_getdebugtemplate.php | 35 + ...ty_internal_method_getdefaultmodifiers.php | 35 + .../smarty_internal_method_getglobal.php | 47 + ...ty_internal_method_getregisteredobject.php | 44 + ...arty_internal_method_getstreamvariable.php | 51 + .../smarty_internal_method_gettags.php | 62 + ...smarty_internal_method_gettemplatevars.php | 116 + .../smarty_internal_method_loadfilter.php | 77 + .../smarty_internal_method_loadplugin.php | 111 + .../smarty_internal_method_mustcompile.php | 51 + ..._internal_method_registercacheresource.php | 40 + .../smarty_internal_method_registerclass.php | 46 + ...al_method_registerdefaultconfighandler.php | 42 + ...al_method_registerdefaultpluginhandler.php | 43 + ..._method_registerdefaulttemplatehandler.php | 82 + .../smarty_internal_method_registerfilter.php | 88 + .../smarty_internal_method_registerobject.php | 72 + .../smarty_internal_method_registerplugin.php | 51 + ...marty_internal_method_registerresource.php | 44 + ...rty_internal_method_setautoloadfilters.php | 70 + ...marty_internal_method_setdebugtemplate.php | 41 + ...ty_internal_method_setdefaultmodifiers.php | 38 + .../smarty_internal_method_unloadfilter.php | 42 + ...nternal_method_unregistercacheresource.php | 40 + ...marty_internal_method_unregisterfilter.php | 42 + ...marty_internal_method_unregisterobject.php | 40 + ...marty_internal_method_unregisterplugin.php | 41 + ...rty_internal_method_unregisterresource.php | 40 + .../smarty_internal_nocache_insert.php | 53 + .../sysplugins/smarty_internal_parsetree.php | 54 + .../smarty_internal_parsetree_code.php | 42 + .../smarty_internal_parsetree_dq.php | 95 + .../smarty_internal_parsetree_dqcontent.php | 42 + .../smarty_internal_parsetree_tag.php | 69 + .../smarty_internal_parsetree_template.php | 128 + .../smarty_internal_parsetree_text.php | 40 + .../smarty_internal_resource_eval.php | 95 + .../smarty_internal_resource_extends.php | 125 + .../smarty_internal_resource_file.php | 176 + .../smarty_internal_resource_php.php | 106 + .../smarty_internal_resource_registered.php | 99 + .../smarty_internal_resource_stream.php | 80 + .../smarty_internal_resource_string.php | 107 + .../smarty_internal_runtime_cachemodify.php | 68 + ...rty_internal_runtime_cacheresourcefile.php | 136 + .../smarty_internal_runtime_capture.php | 161 + .../smarty_internal_runtime_codeframe.php | 98 + .../smarty_internal_runtime_filterhandler.php | 69 + .../smarty_internal_runtime_foreach.php | 149 + ...smarty_internal_runtime_getincludepath.php | 182 ++ .../smarty_internal_runtime_inheritance.php | 241 ++ .../smarty_internal_runtime_make_nocache.php | 56 + .../smarty_internal_runtime_tplfunction.php | 171 + .../smarty_internal_runtime_updatecache.php | 165 + .../smarty_internal_runtime_updatescope.php | 115 + .../smarty_internal_runtime_writefile.php | 91 + ...smarty_internal_smartytemplatecompiler.php | 170 + .../sysplugins/smarty_internal_template.php | 692 ++++ .../smarty_internal_templatebase.php | 355 +++ .../smarty_internal_templatecompilerbase.php | 1365 ++++++++ .../smarty_internal_templatelexer.php | 1169 +++++++ .../smarty_internal_templateparser.php | 2831 +++++++++++++++++ .../smarty_internal_testinstall.php | 598 ++++ .../sysplugins/smarty_internal_undefined.php | 68 + .../sysplugins/smarty_resource.php | 254 ++ .../sysplugins/smarty_resource_custom.php | 95 + .../sysplugins/smarty_resource_recompiled.php | 92 + .../sysplugins/smarty_resource_uncompiled.php | 49 + .../sysplugins/smarty_security.php | 730 +++++ .../sysplugins/smarty_template_cached.php | 256 ++ .../sysplugins/smarty_template_compiled.php | 255 ++ .../sysplugins/smarty_template_config.php | 99 + .../smarty_template_resource_base.php | 158 + .../sysplugins/smarty_template_source.php | 210 ++ .../sysplugins/smarty_undefined_variable.php | 37 + .../sysplugins/smarty_variable.php | 48 + .../sysplugins/smartycompilerexception.php | 42 + .../sysplugins/smartyexception.php | 16 + misc/template_en/control/addtohdr | 0 misc/template_en/control/customheaders | 0 misc/template_en/control/delheaders | 3 + misc/template_en/control/footer-html | 2 + misc/template_en/control/footer-text | 2 + misc/template_en/control/listaddress | 1 + misc/template_en/control/moderators | 0 misc/template_en/control/prefix | 0 misc/template_en/index | 0 misc/template_en/lastdigest | 0 misc/template_en/list_type.txt | 1 + misc/template_en/mlmmj-maintd.lastrun.log | 0 misc/template_en/text/confirm | 52 + misc/template_en/text/deny | 76 + misc/template_en/text/deny-post | 63 + misc/template_en/text/digest | 5 + misc/template_en/text/faq | 4 + misc/template_en/text/finish | 55 + misc/template_en/text/finish-sub | 44 + misc/template_en/text/gatekeep-sub | 19 + misc/template_en/text/help | 88 + misc/template_en/text/list | 25 + misc/template_en/text/moderate-post | 41 + misc/template_en/text/notify | 41 + misc/template_en/text/probe | 11 + misc/template_en/text/prologue | 2 + misc/template_en/text/wait-post | 1 + misc/template_en/text/wait-sub | 7 + misc/template_ru/control/addtohdr | 0 misc/template_ru/control/customheaders | 0 misc/template_ru/control/delheaders | 3 + misc/template_ru/control/footer-html | 2 + misc/template_ru/control/footer-text | 2 + misc/template_ru/control/listaddress | 1 + misc/template_ru/control/moderators | 0 misc/template_ru/control/prefix | 0 misc/template_ru/index | 0 misc/template_ru/lastdigest | 0 misc/template_ru/list_type.txt | 1 + misc/template_ru/mlmmj-maintd.lastrun.log | 0 misc/template_ru/text/confirm | 45 + misc/template_ru/text/deny | 35 + misc/template_ru/text/deny-post | 61 + misc/template_ru/text/finish | 18 + misc/template_ru/text/finish-sub | 30 + misc/template_ru/text/moderate-post | 41 + misc/template_ru/text/prologue | 1 + misc/template_ru/text/wait-post | 1 + save_list.php | 218 ++ style.css | 499 +++ 293 files changed, 40557 insertions(+) create mode 100644 add_list.php create mode 100644 del_list.php create mode 100644 delete.svg create mode 100644 edit_list.php create mode 100644 error.php create mode 100644 help.svg create mode 100644 index.php create mode 100644 init.php create mode 100644 login.php create mode 100644 logout.php create mode 100644 misc/.htaccess create mode 100644 misc/TODO.txt create mode 100644 misc/config.txt create mode 100755 misc/init.sh create mode 100755 misc/manage_domains.sh create mode 100644 misc/move/exim4.conf create mode 100644 misc/move/exim4.filter create mode 100644 misc/move/foot_filter/Makefile create mode 100755 misc/move/foot_filter/foot_filter create mode 100644 misc/move/foot_filter/foot_filter.c create mode 100755 misc/move/mlmmj-footer-receive create mode 100644 misc/smarty/templates_c/085c43aa609e2e11c05d0026471aaaa2a26be865_0.file.error.tpl.php create mode 100644 misc/smarty/templates_c/0b36f150fb6660b8982bed805054bb093a12538d_0.file.index.tpl.php create mode 100644 misc/smarty/templates_c/33a0e2a90d50b199feaa83030d7da2c9680cee0a_0.file.edit_list.tpl.php create mode 100644 misc/smarty/templates_c/53d7e28d1426936f3a3d534fc6fbcdf5d2e8c239_0.file.login.tpl.php create mode 100644 misc/smarty/templates_c/569f7c9bad1e95dec6a51c7c51b98d4db367f54c_0.file.index.tpl.php create mode 100644 misc/smarty/templates_c/617b12a6bfb47e737ab55bf29207ecb92147b506_0.file.login.tpl.php create mode 100644 misc/smarty/templates_c/6820e2ed7eda3869d8307c28df0b8fa212af00dd_0.file.index.tpl.php create mode 100644 misc/smarty/templates_c/6a01fa4a4840b59937570aeacc80d159739f4ab1_0.file.edit_list.tpl.php create mode 100644 misc/smarty/templates_c/74de2eb1870f65e47bdebde8c12ed050e90ded57_0.file.index.tpl.php create mode 100644 misc/smarty/templates_c/9f1de615a6b90a928a6f77b08d01bdc17781ec0b_0.file.edit_list.tpl.php create mode 100644 misc/smarty/templates_c/aa00a58a15f019bd5c0612378cd3ff5f5b6adcae_0.file.error.tpl.php create mode 100644 misc/smarty/templates_c/ccdf76791105fd7c77f366b69d3a18e59c5c2b67_0.file.login.tpl.php create mode 100644 misc/smarty/templates_c/fb61d69ef038dcd720aa2782f00f9ee0f411ff19_0.file.edit_list.tpl.php create mode 100644 misc/smarty/templates_en/edit_list.tpl create mode 100644 misc/smarty/templates_en/error.tpl create mode 100644 misc/smarty/templates_en/index.tpl create mode 100644 misc/smarty/templates_en/login.tpl create mode 100644 misc/smarty/templates_ru/edit_list.tpl create mode 100644 misc/smarty/templates_ru/error.tpl create mode 100644 misc/smarty/templates_ru/index.tpl create mode 100644 misc/smarty/templates_ru/login.tpl create mode 100644 misc/smarty_libs/Autoloader.php create mode 100644 misc/smarty_libs/Smarty.class.php create mode 100644 misc/smarty_libs/SmartyBC.class.php create mode 100644 misc/smarty_libs/bootstrap.php create mode 100644 misc/smarty_libs/debug.tpl create mode 100644 misc/smarty_libs/plugins/block.textformat.php create mode 100644 misc/smarty_libs/plugins/function.counter.php create mode 100644 misc/smarty_libs/plugins/function.cycle.php create mode 100644 misc/smarty_libs/plugins/function.fetch.php create mode 100644 misc/smarty_libs/plugins/function.html_checkboxes.php create mode 100644 misc/smarty_libs/plugins/function.html_image.php create mode 100644 misc/smarty_libs/plugins/function.html_options.php create mode 100644 misc/smarty_libs/plugins/function.html_radios.php create mode 100644 misc/smarty_libs/plugins/function.html_select_date.php create mode 100644 misc/smarty_libs/plugins/function.html_select_time.php create mode 100644 misc/smarty_libs/plugins/function.html_table.php create mode 100644 misc/smarty_libs/plugins/function.mailto.php create mode 100644 misc/smarty_libs/plugins/function.math.php create mode 100644 misc/smarty_libs/plugins/modifier.capitalize.php create mode 100644 misc/smarty_libs/plugins/modifier.date_format.php create mode 100644 misc/smarty_libs/plugins/modifier.debug_print_var.php create mode 100644 misc/smarty_libs/plugins/modifier.escape.php create mode 100644 misc/smarty_libs/plugins/modifier.regex_replace.php create mode 100644 misc/smarty_libs/plugins/modifier.replace.php create mode 100644 misc/smarty_libs/plugins/modifier.spacify.php create mode 100644 misc/smarty_libs/plugins/modifier.truncate.php create mode 100644 misc/smarty_libs/plugins/modifiercompiler.cat.php create mode 100644 misc/smarty_libs/plugins/modifiercompiler.count_characters.php create mode 100644 misc/smarty_libs/plugins/modifiercompiler.count_paragraphs.php create mode 100644 misc/smarty_libs/plugins/modifiercompiler.count_sentences.php create mode 100644 misc/smarty_libs/plugins/modifiercompiler.count_words.php create mode 100644 misc/smarty_libs/plugins/modifiercompiler.default.php create mode 100644 misc/smarty_libs/plugins/modifiercompiler.escape.php create mode 100644 misc/smarty_libs/plugins/modifiercompiler.from_charset.php create mode 100644 misc/smarty_libs/plugins/modifiercompiler.indent.php create mode 100644 misc/smarty_libs/plugins/modifiercompiler.lower.php create mode 100644 misc/smarty_libs/plugins/modifiercompiler.noprint.php create mode 100644 misc/smarty_libs/plugins/modifiercompiler.string_format.php create mode 100644 misc/smarty_libs/plugins/modifiercompiler.strip.php create mode 100644 misc/smarty_libs/plugins/modifiercompiler.strip_tags.php create mode 100644 misc/smarty_libs/plugins/modifiercompiler.to_charset.php create mode 100644 misc/smarty_libs/plugins/modifiercompiler.unescape.php create mode 100644 misc/smarty_libs/plugins/modifiercompiler.upper.php create mode 100644 misc/smarty_libs/plugins/modifiercompiler.wordwrap.php create mode 100644 misc/smarty_libs/plugins/outputfilter.trimwhitespace.php create mode 100644 misc/smarty_libs/plugins/shared.escape_special_chars.php create mode 100644 misc/smarty_libs/plugins/shared.literal_compiler_param.php create mode 100644 misc/smarty_libs/plugins/shared.make_timestamp.php create mode 100644 misc/smarty_libs/plugins/shared.mb_str_replace.php create mode 100644 misc/smarty_libs/plugins/shared.mb_unicode.php create mode 100644 misc/smarty_libs/plugins/shared.mb_wordwrap.php create mode 100644 misc/smarty_libs/plugins/variablefilter.htmlspecialchars.php create mode 100644 misc/smarty_libs/sysplugins/smarty_cacheresource.php create mode 100644 misc/smarty_libs/sysplugins/smarty_cacheresource_custom.php create mode 100644 misc/smarty_libs/sysplugins/smarty_cacheresource_keyvaluestore.php create mode 100644 misc/smarty_libs/sysplugins/smarty_data.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_block.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_cacheresource_file.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_append.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_assign.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_block.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_block_child.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_block_parent.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_break.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_call.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_capture.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_config_load.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_continue.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_debug.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_eval.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_extends.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_for.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_foreach.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_function.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_if.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_include.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_include_php.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_insert.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_ldelim.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_make_nocache.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_nocache.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_private_block_plugin.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_private_foreachsection.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_private_function_plugin.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_private_modifier.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_private_object_block_function.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_private_object_function.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_private_php.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_private_print_expression.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_private_registered_block.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_private_registered_function.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_private_special_variable.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_rdelim.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_section.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_setfilter.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_shared_inheritance.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compile_while.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_compilebase.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_config_file_compiler.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_configfilelexer.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_configfileparser.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_data.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_debug.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_extension_handler.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_addautoloadfilters.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_adddefaultmodifiers.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_append.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_appendbyref.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_assignbyref.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_assignglobal.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_clearallassign.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_clearallcache.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_clearassign.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_clearcache.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_clearcompiledtemplate.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_clearconfig.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_compileallconfig.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_compilealltemplates.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_configload.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_createdata.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_getautoloadfilters.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_getconfigvariable.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_getconfigvars.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_getdebugtemplate.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_getdefaultmodifiers.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_getglobal.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_getregisteredobject.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_getstreamvariable.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_gettags.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_gettemplatevars.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_loadfilter.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_loadplugin.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_mustcompile.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_registercacheresource.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_registerclass.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_registerdefaultconfighandler.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_registerdefaultpluginhandler.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_registerdefaulttemplatehandler.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_registerfilter.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_registerobject.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_registerplugin.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_registerresource.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_setautoloadfilters.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_setdebugtemplate.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_setdefaultmodifiers.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_unloadfilter.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_unregistercacheresource.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_unregisterfilter.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_unregisterobject.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_unregisterplugin.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_method_unregisterresource.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_nocache_insert.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_parsetree.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_parsetree_code.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_parsetree_dq.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_parsetree_dqcontent.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_parsetree_tag.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_parsetree_template.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_parsetree_text.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_resource_eval.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_resource_extends.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_resource_file.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_resource_php.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_resource_registered.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_resource_stream.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_resource_string.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_runtime_cachemodify.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_runtime_cacheresourcefile.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_runtime_capture.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_runtime_codeframe.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_runtime_filterhandler.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_runtime_foreach.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_runtime_getincludepath.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_runtime_inheritance.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_runtime_make_nocache.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_runtime_tplfunction.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_runtime_updatecache.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_runtime_updatescope.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_runtime_writefile.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_smartytemplatecompiler.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_template.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_templatebase.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_templatecompilerbase.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_templatelexer.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_templateparser.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_testinstall.php create mode 100644 misc/smarty_libs/sysplugins/smarty_internal_undefined.php create mode 100644 misc/smarty_libs/sysplugins/smarty_resource.php create mode 100644 misc/smarty_libs/sysplugins/smarty_resource_custom.php create mode 100644 misc/smarty_libs/sysplugins/smarty_resource_recompiled.php create mode 100644 misc/smarty_libs/sysplugins/smarty_resource_uncompiled.php create mode 100644 misc/smarty_libs/sysplugins/smarty_security.php create mode 100644 misc/smarty_libs/sysplugins/smarty_template_cached.php create mode 100644 misc/smarty_libs/sysplugins/smarty_template_compiled.php create mode 100644 misc/smarty_libs/sysplugins/smarty_template_config.php create mode 100644 misc/smarty_libs/sysplugins/smarty_template_resource_base.php create mode 100644 misc/smarty_libs/sysplugins/smarty_template_source.php create mode 100644 misc/smarty_libs/sysplugins/smarty_undefined_variable.php create mode 100644 misc/smarty_libs/sysplugins/smarty_variable.php create mode 100644 misc/smarty_libs/sysplugins/smartycompilerexception.php create mode 100644 misc/smarty_libs/sysplugins/smartyexception.php create mode 100644 misc/template_en/control/addtohdr create mode 100644 misc/template_en/control/customheaders create mode 100644 misc/template_en/control/delheaders create mode 100644 misc/template_en/control/footer-html create mode 100644 misc/template_en/control/footer-text create mode 100644 misc/template_en/control/listaddress create mode 100644 misc/template_en/control/moderators create mode 100644 misc/template_en/control/prefix create mode 100644 misc/template_en/index create mode 100644 misc/template_en/lastdigest create mode 100644 misc/template_en/list_type.txt create mode 100644 misc/template_en/mlmmj-maintd.lastrun.log create mode 100644 misc/template_en/text/confirm create mode 100644 misc/template_en/text/deny create mode 100644 misc/template_en/text/deny-post create mode 100644 misc/template_en/text/digest create mode 100644 misc/template_en/text/faq create mode 100644 misc/template_en/text/finish create mode 100644 misc/template_en/text/finish-sub create mode 100644 misc/template_en/text/gatekeep-sub create mode 100644 misc/template_en/text/help create mode 100644 misc/template_en/text/list create mode 100644 misc/template_en/text/moderate-post create mode 100644 misc/template_en/text/notify create mode 100644 misc/template_en/text/probe create mode 100644 misc/template_en/text/prologue create mode 100644 misc/template_en/text/wait-post create mode 100644 misc/template_en/text/wait-sub create mode 100644 misc/template_ru/control/addtohdr create mode 100644 misc/template_ru/control/customheaders create mode 100644 misc/template_ru/control/delheaders create mode 100644 misc/template_ru/control/footer-html create mode 100644 misc/template_ru/control/footer-text create mode 100644 misc/template_ru/control/listaddress create mode 100644 misc/template_ru/control/moderators create mode 100644 misc/template_ru/control/prefix create mode 100644 misc/template_ru/index create mode 100644 misc/template_ru/lastdigest create mode 100644 misc/template_ru/list_type.txt create mode 100644 misc/template_ru/mlmmj-maintd.lastrun.log create mode 100644 misc/template_ru/text/confirm create mode 100644 misc/template_ru/text/deny create mode 100644 misc/template_ru/text/deny-post create mode 100644 misc/template_ru/text/finish create mode 100644 misc/template_ru/text/finish-sub create mode 100644 misc/template_ru/text/moderate-post create mode 100644 misc/template_ru/text/prologue create mode 100644 misc/template_ru/text/wait-post create mode 100644 save_list.php create mode 100644 style.css diff --git a/add_list.php b/add_list.php new file mode 100644 index 0000000..88fcace --- /dev/null +++ b/add_list.php @@ -0,0 +1,42 @@ + 30 ) + { + // List name must not be longer than 30 characters + $_SESSION["error_code"] = 6; + header("Location: error.php"); + exit(); + } + + $domain = $_SESSION["domain"]; + shell_exec("cp -r misc/template_$language $lists_path/$domain/$list_name"); + file_put_contents("$lists_path/$domain/$list_name/control/listaddress", "$list_name@$domain"); + file_put_contents("$lists_path/$domain/$list_name/control/customheaders", "From: $list_name@$domain\nReply-To: $list_name@$domain\n"); + file_put_contents("$lists_path/$domain/$list_name/control/prefix", "[$list_name]"); + shell_exec("sed -i -e 's/_unsub_addr_/$list_name\+unsubscribe@$domain/g' $lists_path/$domain/$list_name/control/footer-*"); +} + +header("Location: index.php"); +exit(); +?> diff --git a/del_list.php b/del_list.php new file mode 100644 index 0000000..9ee43e4 --- /dev/null +++ b/del_list.php @@ -0,0 +1,40 @@ + 30 ) +{ + header("Location: error.php"); + exit(); +} + +// Test list existence +if( !is_dir("$lists_path/$domain/$list_name") ) +{ + header("Location: error.php"); + exit(); +} + +if (!isset($_SESSION["auth"]) || $_SESSION["auth"] != 1) +{ + // If not authenticated, then redirect to login page + header("Location: login.php"); + exit(); +} + +if(!empty($list_name)) +{ + shell_exec("rm -rf $lists_path/$domain/$list_name"); + header("Location: index.php"); + exit(); +} +?> diff --git a/delete.svg b/delete.svg new file mode 100644 index 0000000..18c9a8d --- /dev/null +++ b/delete.svg @@ -0,0 +1,65 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/edit_list.php b/edit_list.php new file mode 100644 index 0000000..7586370 --- /dev/null +++ b/edit_list.php @@ -0,0 +1,82 @@ + 30 ) +{ + header("Location: error.php"); + exit(); +} + +// Test list existence +if( !is_dir("$lists_path/$domain/$list_name") ) +{ + header("Location: error.php"); + exit(); +} + +// Get a subscribers list +$subscribers = shell_exec("/usr/bin/mlmmj-list -L $lists_path/$domain/$list_name"); +// Remove trailing empty symbols +$subscribers = trim($subscribers); + +// Get a list type. There are three types of lists: +// a closed moderated list (0), a newslist (1) and a conference (2) +$list_type = file_get_contents("$lists_path/$domain/$list_name/list_type.txt"); +$list_type = trim($list_type); + +// Select current list in select html elemant +$list_type_selected = ["", "", ""]; +$list_type_selected[$list_type] = "selected"; + +// Get a footer +$footer = file_get_contents("$lists_path/$domain/$list_name/control/footer-text"); +$footer = trim($footer); + +// News list do not has moderators file +if ($list_type !== "2") +{ + // Get a moderators list + $moderators = file_get_contents("$lists_path/$domain/$list_name/control/moderators"); + // Remove trailing empty symbols + $moderators = trim($moderators); +} +else +{ + $moderators = NULL; +} + +// Get a prefix +$prefix = file_get_contents("$lists_path/$domain/$list_name/control/prefix"); +// Remove trailing empty symbols +$prefix = trim($prefix); + +$notmetoo_checked = file_exists("$lists_path/$domain/$list_name/control/notmetoo") ? "checked" : ""; + +// Load page +$smarty->assign("subscribers", $subscribers); +$smarty->assign("list_name", $list_name); +$smarty->assign("domain", $domain); +$smarty->assign("list_type_selected", $list_type_selected); +$smarty->assign("footer", $footer); +$smarty->assign("moderators", $moderators); +$smarty->assign("prefix", $prefix); +$smarty->assign("notmetoo_checked", $notmetoo_checked); +$smarty->display("edit_list.tpl"); +?> diff --git a/error.php b/error.php new file mode 100644 index 0000000..d9b031d --- /dev/null +++ b/error.php @@ -0,0 +1,7 @@ +assign("error_code", $error_code); +$smarty->display("error.tpl"); +?> diff --git a/help.svg b/help.svg new file mode 100644 index 0000000..41d8c34 --- /dev/null +++ b/help.svg @@ -0,0 +1,168 @@ + + + + + + + EPS converted from http://aiga.org + + + symbol + mapsym + + + + + Open Clip Art Library + + + + + Jean-Victor Balin + + + + + Jean-Victor Balin + + + + image/svg+xml + + + en + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/index.php b/index.php new file mode 100644 index 0000000..2cfab9e --- /dev/null +++ b/index.php @@ -0,0 +1,36 @@ +assign("lists", $lists); +$smarty->assign("domain", $domain); +$smarty->display("index.tpl"); +?> diff --git a/init.php b/init.php new file mode 100644 index 0000000..c9613ef --- /dev/null +++ b/init.php @@ -0,0 +1,23 @@ +setTemplateDir("misc/smarty/templates_$language"); +$smarty->setCompileDir("misc/smarty/templates_c"); +$smarty->setCacheDir("misc/smarty/cache"); +$smarty->setConfigDir("misc/smarty/configs"); + +session_start(); +?> diff --git a/login.php b/login.php new file mode 100644 index 0000000..37d7ca4 --- /dev/null +++ b/login.php @@ -0,0 +1,74 @@ +display("login.tpl"); +} +?> diff --git a/logout.php b/logout.php new file mode 100644 index 0000000..0f0f5ac --- /dev/null +++ b/logout.php @@ -0,0 +1,8 @@ + diff --git a/misc/.htaccess b/misc/.htaccess new file mode 100644 index 0000000..b66e808 --- /dev/null +++ b/misc/.htaccess @@ -0,0 +1 @@ +Require all denied diff --git a/misc/TODO.txt b/misc/TODO.txt new file mode 100644 index 0000000..5967939 --- /dev/null +++ b/misc/TODO.txt @@ -0,0 +1,40 @@ +1. dkim, spf + +init.sh: +1. mlmmj-footer-receive and footer compile +2. disable /etc/cron.d/mlmmj +4. Cron: +34 */2 * * * /root/bin/mlmmj.maintaince.sh 1>/dev/null 2>/dev/null +*/15 * * * * /root/bin/remove_frozen_bounces.sh > /dev/null 2>&1 +5. Проверить, что в файлах /etc/mailname и /etc/hostname FQDN +6. Права доступа на templates устанавливать в init.sh, прочие права тоже допилить +8. AllowOverride All in /etc/apache2.conf +9. user mlmmj for apache and exim + +Left this to better times: +1. Automatic installation of Let's Encrypt SSL; +2. Send letter to moderators when a letter has sent to all subscribers; + +--------------------- +testing: +0. moderated list: + - test@list.mlmmj.tk (mod) DISCARDED + - test@list.mlmmj.tk (sub) PASSED TO MODS + - test@list.mlmmj.tk (mod+sub) PASSED TO MODS + - test@list.mlmmj.tk (not sub) DISCARDED + - test+subscribe@list.mlmmj.tk (not sub) DISCARDED + - test+unsubscribe@list.mlmmj.tk (sub) CONFIRM + FAREWELL + +1. news list: + - test@list.mlmmj.tk (mod) DISCARDED + - test@list.mlmmj.tk (sub) DISCARDED + - test@list.mlmmj.tk (mod+sub) PASSED TO MODS + - test@list.mlmmj.tk (not sub) DISCARDED + - test+subscribe@list.mlmmj.tk (not sub) CONFIRM + GREETING + - test+unsubscribe@list.mlmmj.tk (sub) CONFIRM + FAREWELL + +2. conference list: + - test@list.mlmmj.tk (sub) PASSED + - test@list.mlmmj.tk (not sub) DISCARDED + - test+subscribe@list.mlmmj.tk (not sub) DISCARDED + - test+unsubscribe@list.mlmmj.tk (sub) CONFIRM + FAREWELL diff --git a/misc/config.txt b/misc/config.txt new file mode 100644 index 0000000..97d9293 --- /dev/null +++ b/misc/config.txt @@ -0,0 +1,4 @@ +lists_path = /var/spool/mlmmj +web_path = /var/www/html +web_url = http://mlmmj.tk/ +language = ru diff --git a/misc/init.sh b/misc/init.sh new file mode 100755 index 0000000..4022883 --- /dev/null +++ b/misc/init.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +if [ "$#" -ne 0 ]; then + echo "This script prepaire system for mlmmj-light-web installation." + exit 1 +fi + +install mlmmj, apg, apache2, exim, php_mod, altermime + +useradd -r -s /bin/false mlmmj +chown mlmmj:mlmmj -R /var/spool/mlmmj +chmod -R g+s /var/spool/mlmmj +chmod -R g+w /var/spool/mlmmj +#change apache user in /etc/apache2/envvars +#exim config and filter +#/usr/bin/mlmmj-amime-receive diff --git a/misc/manage_domains.sh b/misc/manage_domains.sh new file mode 100755 index 0000000..60a3614 --- /dev/null +++ b/misc/manage_domains.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +function load_config() +{ + lists_path=$(cat config.txt | grep lists_path | sed 's/.*=//g' | sed 's/[[:blank:]]//g') + web_url=$(cat config.txt | grep web_url | sed 's/.*=//g' | sed 's/[[:blank:]]//g') +} + +if [ "$#" -ne 2 ]; then + echo "With this script you can create and delete domains for mailing lists." + echo + echo "Usage:" + echo "$0 add list.example.com" + echo "$0 del list.example.com" + exit 1 +fi + +load_config +operation=$1 +domain=$2 + +if [ "${operation}" = "add" ]; then +# password=$(apg -MCLN -m 12 -a 1 -n 1) + password="123" + hash=$(echo -n $password | sha256sum | head -c 64) + mkdir -p $lists_path/$domain + echo $domain:$hash >> $lists_path/passwords.txt + chown mlmmj:mlmmj -R $lists_path + echo -e "Domain: ${domain} \nPass: ${password}\nURL: ${web_url}" +fi + +if [ "${operation}" = "del" ]; then + find_domain=$(sed -n "/^${domain}:.*/p" $lists_path/passwords.txt) + if [ -z "${find_domain}" ]; then + echo "ERROR: No such domain. Aborting." + exit 1 + else + sed -i -e "/^${domain}:.*/d" $lists_path/passwords.txt + rm -r $lists_path/$domain + echo -e "Domain $domain has deleted." + fi +fi diff --git a/misc/move/exim4.conf b/misc/move/exim4.conf new file mode 100644 index 0000000..5c0be82 --- /dev/null +++ b/misc/move/exim4.conf @@ -0,0 +1,2100 @@ +##################################################### +### main/01_exim4-config_listmacrosdefs +##################################################### +###################################################################### +# Runtime configuration file for Exim 4 (Debian Packaging) # +###################################################################### + +###################################################################### +# /etc/exim4/exim4.conf.template is only used with the non-split +# configuration scheme. +# /etc/exim4/conf.d/main/01_exim4-config_listmacrosdefs is only used +# with the split configuration scheme. +# If you find this comment anywhere else, somebody copied it there. +# Documentation about the Debian exim4 configuration scheme can be +# found in /usr/share/doc/exim4-base/README.Debian.gz. +###################################################################### + +###################################################################### +# MAIN CONFIGURATION SETTINGS # +###################################################################### + +system_filter = /etc/exim4/exim4.filter +MLMMJ_HOME=/var/spool/mlmmj/$domain +domainlist mlmmj_domains = lsearch;/var/spool/mlmmj/passwords.txt +delay_warning_condition = ${if match_domain{$domain}{+mlmmj_domains}{no}{yes}} + +# Just for reference and scripts. +# On Debian systems, the main binary is installed as exim4 to avoid +# conflicts with the exim 3 packages. +exim_path = /usr/sbin/exim4 + +# Macro defining the main configuration directory. +# We do not use absolute paths. +.ifndef CONFDIR +CONFDIR = /etc/exim4 +.endif + +# debconf-driven macro definitions get inserted after this line +UPEX4CmacrosUPEX4C = 1 + +# Create domain and host lists for relay control +# '@' refers to 'the name of the local host' + +# List of domains considered local for exim. Domains not listed here +# need to be deliverable remotely. +domainlist local_domains = MAIN_LOCAL_DOMAINS + +# List of recipient domains to relay _to_. Use this list if you're - +# for example - fallback MX or mail gateway for domains. +domainlist relay_to_domains = MAIN_RELAY_TO_DOMAINS : +mlmmj_domains + +# List of sender networks (IP addresses) to _unconditionally_ relay +# _for_. If you intend to be SMTP AUTH server, you do not need to enter +# anything here. +hostlist relay_from_hosts = localhost + +# Decide which domain to use to add to all unqualified addresses. +# If MAIN_PRIMARY_HOSTNAME_AS_QUALIFY_DOMAIN is defined, the primary +# hostname is used. If not, but MAIN_QUALIFY_DOMAIN is set, the value +# of MAIN_QUALIFY_DOMAIN is used. If both macros are not defined, +# the first line of /etc/mailname is used. +.ifndef MAIN_PRIMARY_HOSTNAME_AS_QUALIFY_DOMAIN +.ifndef MAIN_QUALIFY_DOMAIN +qualify_domain = ETC_MAILNAME +.else +qualify_domain = MAIN_QUALIFY_DOMAIN +.endif +.endif + +# listen on all all interfaces? +.ifdef MAIN_LOCAL_INTERFACES +local_interfaces = MAIN_LOCAL_INTERFACES +.endif + +.ifndef LOCAL_DELIVERY +# The default transport, set in /etc/exim4/update-exim4.conf.conf, +# defaulting to mail_spool. See CONFDIR/conf.d/transport/ for possibilities +LOCAL_DELIVERY=mail_spool +.endif + +# The gecos field in /etc/passwd holds not only the name. see passwd(5). +gecos_pattern = ^([^,:]*) +gecos_name = $1 + +# define macros to be used in acl/30_exim4-config_check_rcpt to check +# recipient local parts for strange characters. + +# This macro definition really should be in +# acl/30_exim4-config_check_rcpt but cannot be there due to +# http://www.exim.org/bugzilla/show_bug.cgi?id=101 as of exim 4.62. + +# These macros are documented in acl/30_exim4-config_check_rcpt, +# can be changed here or overridden by a locally added configuration +# file as described in README.Debian chapter 2.1.2 + +.ifndef CHECK_RCPT_LOCAL_LOCALPARTS +CHECK_RCPT_LOCAL_LOCALPARTS = ^[.] : ^.*[@%!/|`#&?] +.endif + +.ifndef CHECK_RCPT_REMOTE_LOCALPARTS +CHECK_RCPT_REMOTE_LOCALPARTS = ^[./|] : ^.*[@%!`#&?] : ^.*/\\.\\./ +.endif + +# always log tls_peerdn as we use TLS for outgoing connects by default +.ifndef MAIN_LOG_SELECTOR +MAIN_LOG_SELECTOR = +tls_peerdn +.endif +##################################################### +### end main/01_exim4-config_listmacrosdefs +##################################################### +##################################################### +### main/02_exim4-config_options +##################################################### + +### main/02_exim4-config_options +################################# + + +# Defines the access control list that is run when an +# SMTP MAIL command is received. +# +.ifndef MAIN_ACL_CHECK_MAIL +MAIN_ACL_CHECK_MAIL = acl_check_mail +.endif +acl_smtp_mail = MAIN_ACL_CHECK_MAIL + + +# Defines the access control list that is run when an +# SMTP RCPT command is received. +# +.ifndef MAIN_ACL_CHECK_RCPT +MAIN_ACL_CHECK_RCPT = acl_check_rcpt +.endif +acl_smtp_rcpt = MAIN_ACL_CHECK_RCPT + + +# Defines the access control list that is run when an +# SMTP DATA command is received. +# +.ifndef MAIN_ACL_CHECK_DATA +MAIN_ACL_CHECK_DATA = acl_check_data +.endif +acl_smtp_data = MAIN_ACL_CHECK_DATA + + +# Message size limit. The default (used when MESSAGE_SIZE_LIMIT +# is unset) is 50 MB +.ifdef MESSAGE_SIZE_LIMIT +message_size_limit = MESSAGE_SIZE_LIMIT +.endif + + +# If you are running exim4-daemon-heavy or a custom version of Exim that +# was compiled with the content-scanning extension, you can cause incoming +# messages to be automatically scanned for viruses. You have to modify the +# configuration in two places to set this up. The first of them is here, +# where you define the interface to your scanner. This example is typical +# for ClamAV; see the manual for details of what to set for other virus +# scanners. The second modification is in the acl_check_data access +# control list. + +# av_scanner = clamd:/var/run/clamav/clamd.ctl + + +# For spam scanning, there is a similar option that defines the interface to +# SpamAssassin. You do not need to set this if you are using the default, which +# is shown in this commented example. As for virus scanning, you must also +# modify the acl_check_data access control list to enable spam scanning. + +# spamd_address = 127.0.0.1 783 + +# Domain used to qualify unqualified recipient addresses +# If this option is not set, the qualify_domain value is used. +# qualify_recipient = + + +# Allow Exim to recognize addresses of the form "user@[10.11.12.13]", +# where the domain part is a "domain literal" (an IP address) instead +# of a named domain. The RFCs require this facility, but it is disabled +# in the default config since it is seldomly used and frequently abused. +# Domain literal support also needs a special router, which is automatically +# enabled if you use the enable macro MAIN_ALLOW_DOMAIN_LITERALS. +# Additionally, you might want to make your local IP addresses (or @[]) +# local domains. +.ifdef MAIN_ALLOW_DOMAIN_LITERALS +allow_domain_literals +.endif + + +# Do a reverse DNS lookup on all incoming IP calls, in order to get the +# true host name. If you feel this is too expensive, the networks for +# which a lookup is done can be listed here. +.ifndef DC_minimaldns +.ifndef MAIN_HOST_LOOKUP +MAIN_HOST_LOOKUP = * +.endif +host_lookup = MAIN_HOST_LOOKUP +.endif + + +# In a minimaldns setup, update-exim4.conf guesses the hostname and +# dumps it here to avoid DNS lookups being done at Exim run time. +.ifdef MAIN_HARDCODE_PRIMARY_HOSTNAME +primary_hostname = MAIN_HARDCODE_PRIMARY_HOSTNAME +.endif + +# The settings below, which are actually the same as the defaults in the +# code, cause Exim to make RFC 1413 (ident) callbacks for all incoming SMTP +# calls. You can limit the hosts to which these calls are made, and/or change +# the timeout that is used. If you set the timeout to zero, all RFC 1413 calls +# are disabled. RFC 1413 calls are cheap and can provide useful information +# for tracing problem messages, but some hosts and firewalls are +# misconfigured to drop the requests instead of either answering or +# rejecting them. This can result in a timeout instead of an immediate refused +# connection, leading to delays on starting up SMTP sessions. (The default was +# reduced from 30s to 5s for release 4.61.) +# rfc1413_hosts = * +# rfc1413_query_timeout = 5s + +# When using an external relay tester (such as rt.njabl.org and/or the +# currently defunct relay-test.mail-abuse.org, the test may be aborted +# since exim complains about "too many nonmail commands". If you want +# the test to complete, add the host from where "your" relay tester +# connects from to the MAIN_SMTP_ACCEPT_MAX_NOMAIL_HOSTS macro. +# Please note that a non-empty setting may cause extra DNS lookups to +# happen, which is the reason why this option is commented out in the +# default settings. +# MAIN_SMTP_ACCEPT_MAX_NOMAIL_HOSTS = !rt.njabl.org +.ifdef MAIN_SMTP_ACCEPT_MAX_NOMAIL_HOSTS +smtp_accept_max_nonmail_hosts = MAIN_SMTP_ACCEPT_MAX_NOMAIL_HOSTS +.endif + +# By default, exim forces a Sender: header containing the local +# account name at the local host name in all locally submitted messages +# that don't have the local account name at the local host name in the +# From: header, deletes any Sender: header present in the submitted +# message and forces the envelope sender of all locally submitted +# messages to the local account name at the local host name. +# The following settings allow local users to specify their own envelope sender +# in a locally submitted message. Sender: headers existing in a locally +# submitted message are not removed, and no automatic Sender: headers +# are added. These settings are fine for most hosts. +# If you run exim on a classical multi-user systems where all users +# have local mailboxes that can be reached via SMTP from the Internet +# with the local FQDN as the domain part of the address, you might want +# to disable the following three lines for traceability reasons. +.ifndef MAIN_FORCE_SENDER +local_from_check = false +local_sender_retain = true +untrusted_set_sender = * +.endif + + +# By default, Exim expects all envelope addresses to be fully qualified, that +# is, they must contain both a local part and a domain. Configure exim +# to accept unqualified addresses from certain hosts. When this is done, +# unqualified addresses are qualified using the settings of qualify_domain +# and/or qualify_recipient (see above). +# sender_unqualified_hosts = +# recipient_unqualified_hosts = + + +# Configure Exim to support the "percent hack" for certain domains. +# The "percent hack" is the feature by which mail addressed to x%y@z +# (where z is one of the domains listed) is locally rerouted to x@y +# and sent on. If z is not one of the "percent hack" domains, x%y is +# treated as an ordinary local part. The percent hack is rarely needed +# nowadays but frequently abused. You should not enable it unless you +# are sure that you really need it. +# percent_hack_domains = + + +# Bounce handling +.ifndef MAIN_IGNORE_BOUNCE_ERRORS_AFTER +MAIN_IGNORE_BOUNCE_ERRORS_AFTER = 2d +.endif +ignore_bounce_errors_after = MAIN_IGNORE_BOUNCE_ERRORS_AFTER + +.ifndef MAIN_TIMEOUT_FROZEN_AFTER +MAIN_TIMEOUT_FROZEN_AFTER = 7d +.endif +timeout_frozen_after = MAIN_TIMEOUT_FROZEN_AFTER + +.ifndef MAIN_FREEZE_TELL +MAIN_FREEZE_TELL = postmaster +.endif +freeze_tell = MAIN_FREEZE_TELL + + +# Define spool directory +.ifndef SPOOLDIR +SPOOLDIR = /var/spool/exim4 +.endif +spool_directory = SPOOLDIR + + +# trusted users can set envelope-from to arbitrary values +.ifndef MAIN_TRUSTED_USERS +MAIN_TRUSTED_USERS = uucp +.endif +trusted_users = MAIN_TRUSTED_USERS +.ifdef MAIN_TRUSTED_GROUPS +trusted_groups = MAIN_TRUSTED_GROUPS +.endif + + +# users in admin group can do many other things +# admin_groups = + + +# SMTP Banner. The example includes the Debian version in the SMTP dialog +# MAIN_SMTP_BANNER = "${primary_hostname} ESMTP Exim ${version_number} (Debian package MAIN_PACKAGE_VERSION) ${tod_full}" +# smtp_banner = $smtp_active_hostname ESMTP Exim $version_number $tod_full + +.ifdef MAIN_KEEP_ENVIRONMENT +keep_environment = MAIN_KEEP_ENVIRONMENT +.else +# set option to empty value to avoid warning. +keep_environment = +.endif +.ifdef MAIN_ADD_ENVIRONMENT +add_environment = MAIN_ADD_ENVIRONMENT +.endif +##################################################### +### end main/02_exim4-config_options +##################################################### +##################################################### +### main/03_exim4-config_tlsoptions +##################################################### + +### main/03_exim4-config_tlsoptions +################################# + +# TLS/SSL configuration for exim as an SMTP server. +# See /usr/share/doc/exim4-base/README.Debian.gz for explanations. + +.ifdef MAIN_TLS_ENABLE +# Defines what hosts to 'advertise' STARTTLS functionality to. The +# default, *, will advertise to all hosts that connect with EHLO. +.ifndef MAIN_TLS_ADVERTISE_HOSTS +MAIN_TLS_ADVERTISE_HOSTS = * +.endif +tls_advertise_hosts = MAIN_TLS_ADVERTISE_HOSTS + + +# Full paths to Certificate and Private Key. The Private Key file +# must be kept 'secret' and should be owned by root.Debian-exim mode +# 640 (-rw-r-----). exim-gencert takes care of these prerequisites. +# Normally, exim4 looks for certificate and key in different files: +# MAIN_TLS_CERTIFICATE - path to certificate file, +# CONFDIR/exim.crt if unset +# MAIN_TLS_PRIVATEKEY - path to private key file +# CONFDIR/exim.key if unset +# You can also configure exim to look for certificate and key in the +# same file, set MAIN_TLS_CERTKEY to that file to enable. This takes +# precedence over all other settings regarding certificate and key file. +.ifdef MAIN_TLS_CERTKEY +tls_certificate = MAIN_TLS_CERTKEY +.else +.ifndef MAIN_TLS_CERTIFICATE +MAIN_TLS_CERTIFICATE = CONFDIR/exim.crt +.endif +tls_certificate = MAIN_TLS_CERTIFICATE + +.ifndef MAIN_TLS_PRIVATEKEY +MAIN_TLS_PRIVATEKEY = CONFDIR/exim.key +.endif +tls_privatekey = MAIN_TLS_PRIVATEKEY +.endif + +# Pointer to the CA Certificates against which client certificates are +# checked. This is controlled by the `tls_verify_hosts' and +# `tls_try_verify_hosts' lists below. +# If you want to check server certificates, you need to add an +# tls_verify_certificates statement to the smtp transport. +# /etc/ssl/certs/ca-certificates.crt is generated by +# the "ca-certificates" package's update-ca-certificates(8) command. +.ifndef MAIN_TLS_VERIFY_CERTIFICATES +MAIN_TLS_VERIFY_CERTIFICATES = ${if exists{/etc/ssl/certs/ca-certificates.crt}\ + {/etc/ssl/certs/ca-certificates.crt}\ + {/dev/null}} +.endif +tls_verify_certificates = MAIN_TLS_VERIFY_CERTIFICATES + + +# A list of hosts which are constrained by `tls_verify_certificates'. A host +# that matches `tls_verify_host' must present a certificate that is +# verifyable through `tls_verify_certificates' in order to be accepted as an +# SMTP client. If it does not, the connection is aborted. +.ifdef MAIN_TLS_VERIFY_HOSTS +tls_verify_hosts = MAIN_TLS_VERIFY_HOSTS +.endif + +# A weaker form of checking: if a client matches `tls_try_verify_hosts' (but +# not `tls_verify_hosts'), request a certificate and check it against +# `tls_verify_certificates' but do not abort the connection if there is no +# certificate or if the certificate presented does not match. (This +# condition can be tested for in ACLs through `verify = certificate') +# By default, this check is done for all hosts. It is known that some +# clients (including incredimail's version downloadable in February +# 2008) choke on this. To disable, set MAIN_TLS_TRY_VERIFY_HOSTS to an +# empty value. +.ifdef MAIN_TLS_TRY_VERIFY_HOSTS +tls_try_verify_hosts = MAIN_TLS_TRY_VERIFY_HOSTS +.endif + +.endif +##################################################### +### end main/03_exim4-config_tlsoptions +##################################################### +##################################################### +### main/90_exim4-config_log_selector +##################################################### + +### main/90_exim4-config_log_selector +################################# + +# uncomment this for debugging +# MAIN_LOG_SELECTOR == MAIN_LOG_SELECTOR +all -subject -arguments + +.ifdef MAIN_LOG_SELECTOR +log_selector = MAIN_LOG_SELECTOR +.endif +##################################################### +### end main/90_exim4-config_log_selector +##################################################### +##################################################### +### acl/00_exim4-config_header +##################################################### + +###################################################################### +# ACL CONFIGURATION # +# Specifies access control lists for incoming SMTP mail # +###################################################################### +begin acl + + +##################################################### +### end acl/00_exim4-config_header +##################################################### +##################################################### +### acl/20_exim4-config_local_deny_exceptions +##################################################### + +### acl/20_exim4-config_local_deny_exceptions +################################# + +# This is used to determine whitelisted senders and hosts. +# It checks for CONFDIR/host_local_deny_exceptions and +# CONFDIR/sender_local_deny_exceptions. +# +# It is meant to be used from some other acl entry. +# +# See exim4-config_files(5) for details. +# +# If the files do not exist, the white list never matches, which is +# the desired behaviour. +# +# The old file names CONFDIR/local_host_whitelist and +# CONFDIR/local_sender_whitelist will continue to be honored for a +# transition period. Their use is deprecated. + +acl_local_deny_exceptions: + accept + hosts = ${if exists{CONFDIR/host_local_deny_exceptions}\ + {CONFDIR/host_local_deny_exceptions}\ + {}} + accept + senders = ${if exists{CONFDIR/sender_local_deny_exceptions}\ + {CONFDIR/sender_local_deny_exceptions}\ + {}} + accept + hosts = ${if exists{CONFDIR/local_host_whitelist}\ + {CONFDIR/local_host_whitelist}\ + {}} + accept + senders = ${if exists{CONFDIR/local_sender_whitelist}\ + {CONFDIR/local_sender_whitelist}\ + {}} + + # This hook allows you to hook in your own ACLs without having to + # modify this file. If you do it like we suggest, you'll end up with + # a small performance penalty since there is an additional file being + # accessed. This doesn't happen if you leave the macro unset. + .ifdef LOCAL_DENY_EXCEPTIONS_LOCAL_ACL_FILE + .include LOCAL_DENY_EXCEPTIONS_LOCAL_ACL_FILE + .endif + + # this is still supported for a transition period and is deprecated. + .ifdef WHITELIST_LOCAL_DENY_LOCAL_ACL_FILE + .include WHITELIST_LOCAL_DENY_LOCAL_ACL_FILE + .endif +##################################################### +### end acl/20_exim4-config_local_deny_exceptions +##################################################### +##################################################### +### acl/30_exim4-config_check_mail +##################################################### + +### acl/30_exim4-config_check_mail +################################# + +# This access control list is used for every MAIL command in an incoming +# SMTP message. The tests are run in order until the address is either +# accepted or denied. +# +acl_check_mail: + .ifdef CHECK_MAIL_HELO_ISSUED + deny + message = no HELO given before MAIL command + condition = ${if def:sender_helo_name {no}{yes}} + .endif + + accept +##################################################### +### end acl/30_exim4-config_check_mail +##################################################### +##################################################### +### acl/30_exim4-config_check_rcpt +##################################################### + +### acl/30_exim4-config_check_rcpt +################################# + +# This access control list is used for every RCPT command in an incoming +# SMTP message. The tests are run in order until the address is either +# accepted or denied. +# +acl_check_rcpt: + + # Accept if the source is local SMTP (i.e. not over TCP/IP). We do this by + # testing for an empty sending host field. + accept + hosts = : + control = dkim_disable_verify + + # Do not try to verify DKIM signatures of incoming mail if DC_minimaldns + # or DISABLE_DKIM_VERIFY are set. +.ifdef DC_minimaldns + warn + control = dkim_disable_verify +.else +.ifdef DISABLE_DKIM_VERIFY + warn + control = dkim_disable_verify +.endif +.endif + + # The following section of the ACL is concerned with local parts that contain + # certain non-alphanumeric characters. Dots in unusual places are + # handled by this ACL as well. + # + # Non-alphanumeric characters other than dots are rarely found in genuine + # local parts, but are often tried by people looking to circumvent + # relaying restrictions. Therefore, although they are valid in local + # parts, these rules disallow certain non-alphanumeric characters, as + # a precaution. + # + # Empty components (two dots in a row) are not valid in RFC 2822, but Exim + # allows them because they have been encountered. (Consider local parts + # constructed as "firstinitial.secondinitial.familyname" when applied to + # a name without a second initial.) However, a local part starting + # with a dot or containing /../ can cause trouble if it is used as part of a + # file name (e.g. for a mailing list). This is also true for local parts that + # contain slashes. A pipe symbol can also be troublesome if the local part is + # incorporated unthinkingly into a shell command line. + # + # These ACL components will block recipient addresses that are valid + # from an RFC2822 point of view. We chose to have them blocked by + # default for security reasons. + # + # If you feel that your site should have less strict recipient + # checking, please feel free to change the default values of the macros + # defined in main/01_exim4-config_listmacrosdefs or override them from a + # local configuration file. + # + # Two different rules are used. The first one has a quite strict + # default, and is applied to messages that are addressed to one of the + # local domains handled by this host. + + # The default value of CHECK_RCPT_LOCAL_LOCALPARTS is defined in + # main/01_exim4-config_listmacrosdefs: + # CHECK_RCPT_LOCAL_LOCALPARTS = ^[.] : ^.*[@%!/|`#&?] + # This blocks local parts that begin with a dot or contain a quite + # broad range of non-alphanumeric characters. + .ifdef CHECK_RCPT_LOCAL_LOCALPARTS + deny + domains = +local_domains + local_parts = CHECK_RCPT_LOCAL_LOCALPARTS + message = restricted characters in address + .endif + + + # The second rule applies to all other domains, and its default is + # considerably less strict. + + # The default value of CHECK_RCPT_REMOTE_LOCALPARTS is defined in + # main/01_exim4-config_listmacrosdefs: + # CHECK_RCPT_REMOTE_LOCALPARTS = ^[./|] : ^.*[@%!`#&?] : ^.*/\\.\\./ + + # It allows local users to send outgoing messages to sites + # that use slashes and vertical bars in their local parts. It blocks + # local parts that begin with a dot, slash, or vertical bar, but allows + # these characters within the local part. However, the sequence /../ is + # barred. The use of some other non-alphanumeric characters is blocked. + # Single quotes might probably be dangerous as well, but they're + # allowed by the default regexps to avoid rejecting mails to Ireland. + # The motivation here is to prevent local users (or local users' malware) + # from mounting certain kinds of attack on remote sites. + .ifdef CHECK_RCPT_REMOTE_LOCALPARTS + deny + domains = !+local_domains + local_parts = CHECK_RCPT_REMOTE_LOCALPARTS + message = restricted characters in address + .endif + + + # Accept mail to postmaster in any local domain, regardless of the source, + # and without verifying the sender. + # + accept + .ifndef CHECK_RCPT_POSTMASTER + local_parts = postmaster + .else + local_parts = CHECK_RCPT_POSTMASTER + .endif + domains = +local_domains : +relay_to_domains + + + # Deny unless the sender address can be verified. + # + # This is disabled by default so that DNSless systems don't break. If + # your system can do DNS lookups without delay or cost, you might want + # to enable this feature. + # + # This feature does not work in smarthost and satellite setups as + # with these setups all domains pass verification. See spec.txt chapter + # 39.31 with the added information that a smarthost/satellite setup + # routes all non-local e-mail to the smarthost. + .ifdef CHECK_RCPT_VERIFY_SENDER + deny + message = Sender verification failed + !acl = acl_local_deny_exceptions + !verify = sender + .endif + + # Verify senders listed in local_sender_callout with a callout. + # + # In smarthost and satellite setups, this causes the callout to be + # done to the smarthost. Verification will thus only be reliable if the + # smarthost does reject illegal addresses in the SMTP dialog. + deny + !acl = acl_local_deny_exceptions + senders = ${if exists{CONFDIR/local_sender_callout}\ + {CONFDIR/local_sender_callout}\ + {}} + !verify = sender/callout + + + # Accept if the message comes from one of the hosts for which we are an + # outgoing relay. It is assumed that such hosts are most likely to be MUAs, + # so we set control=submission to make Exim treat the message as a + # submission. It will fix up various errors in the message, for example, the + # lack of a Date: header line. If you are actually relaying out out from + # MTAs, you may want to disable this. If you are handling both relaying from + # MTAs and submissions from MUAs you should probably split them into two + # lists, and handle them differently. + + # Recipient verification is omitted here, because in many cases the clients + # are dumb MUAs that don't cope well with SMTP error responses. If you are + # actually relaying out from MTAs, you should probably add recipient + # verification here. + + # Note that, by putting this test before any DNS black list checks, you will + # always accept from these hosts, even if they end up on a black list. The + # assumption is that they are your friends, and if they get onto black + # list, it is a mistake. + accept + hosts = +relay_from_hosts + control = submission/sender_retain + control = dkim_disable_verify + + + # Accept if the message arrived over an authenticated connection, from + # any host. Again, these messages are usually from MUAs, so recipient + # verification is omitted, and submission mode is set. And again, we do this + # check before any black list tests. + accept + authenticated = * + control = submission/sender_retain + control = dkim_disable_verify + + + # Insist that any other recipient address that we accept is either in one of + # our local domains, or is in a domain for which we explicitly allow + # relaying. Any other domain is rejected as being unacceptable for relaying. + require + message = relay not permitted + domains = +local_domains : +relay_to_domains + + + # We also require all accepted addresses to be verifiable. This check will + # do local part verification for local domains, but only check the domain + # for remote domains. + require + verify = recipient + + + # Verify recipients listed in local_rcpt_callout with a callout. + # This is especially handy for forwarding MX hosts (secondary MX or + # mail hubs) of domains that receive a lot of spam to non-existent + # addresses. The only way to check local parts for remote relay + # domains is to use a callout (add /callout), but please read the + # documentation about callouts before doing this. + deny + !acl = acl_local_deny_exceptions + recipients = ${if exists{CONFDIR/local_rcpt_callout}\ + {CONFDIR/local_rcpt_callout}\ + {}} + !verify = recipient/callout + + + # CONFDIR/local_sender_blacklist holds a list of envelope senders that + # should have their access denied to the local host. Incoming messages + # with one of these senders are rejected at RCPT time. + # + # The explicit white lists are honored as well as negative items in + # the black list. See exim4-config_files(5) for details. + deny + message = sender envelope address $sender_address is locally blacklisted here. If you think this is wrong, get in touch with postmaster + !acl = acl_local_deny_exceptions + senders = ${if exists{CONFDIR/local_sender_blacklist}\ + {CONFDIR/local_sender_blacklist}\ + {}} + + + # deny bad sites (IP address) + # CONFDIR/local_host_blacklist holds a list of host names, IP addresses + # and networks (CIDR notation) that should have their access denied to + # The local host. Messages coming in from a listed host will have all + # RCPT statements rejected. + # + # The explicit white lists are honored as well as negative items in + # the black list. See exim4-config_files(5) for details. + deny + message = sender IP address $sender_host_address is locally blacklisted here. If you think this is wrong, get in touch with postmaster + !acl = acl_local_deny_exceptions + hosts = ${if exists{CONFDIR/local_host_blacklist}\ + {CONFDIR/local_host_blacklist}\ + {}} + + + # Warn if the sender host does not have valid reverse DNS. + # + # If your system can do DNS lookups without delay or cost, you might want + # to enable this. + # If sender_host_address is defined, it's a remote call. If + # sender_host_name is not defined, then reverse lookup failed. Use + # this instead of !verify = reverse_host_lookup to catch deferrals + # as well as outright failures. + .ifdef CHECK_RCPT_REVERSE_DNS + warn + condition = ${if and{{def:sender_host_address}{!def:sender_host_name}}\ + {yes}{no}} + add_header = X-Host-Lookup-Failed: Reverse DNS lookup failed for $sender_host_address (${if eq{$host_lookup_failed}{1}{failed}{deferred}}) + .endif + + + # Use spfquery to perform a pair of SPF checks (for details, see + # http://www.openspf.org/) + # + # This is quite costly in terms of DNS lookups (~6 lookups per mail). Do not + # enable if that's an issue. Also note that if you enable this, you must + # install "spf-tools-perl" which provides the spfquery command. + # Missing spf-tools-perl will trigger the "Unexpected error in + # SPF check" warning. + .ifdef CHECK_RCPT_SPF + deny + message = [SPF] $sender_host_address is not allowed to send mail from \ + ${if def:sender_address_domain {$sender_address_domain}{$sender_helo_name}}. \ + Please see \ + http://www.openspf.org/Why?scope=${if def:sender_address_domain \ + {mfrom}{helo}};identity=${if def:sender_address_domain \ + {$sender_address}{$sender_helo_name}};ip=$sender_host_address + log_message = SPF check failed. + !acl = acl_local_deny_exceptions + condition = ${run{/usr/bin/spfquery.mail-spf-perl --ip \ + ${quote:$sender_host_address} --identity \ + ${if def:sender_address_domain \ + {--scope mfrom --identity ${quote:$sender_address}}\ + {--scope helo --identity ${quote:$sender_helo_name}}}}\ + {no}{${if eq {$runrc}{1}{yes}{no}}}} + + defer + message = Temporary DNS error while checking SPF record. Try again later. + !acl = acl_local_deny_exceptions + condition = ${if eq {$runrc}{5}{yes}{no}} + + warn + condition = ${if <={$runrc}{6}{yes}{no}} + add_header = Received-SPF: ${if eq {$runrc}{0}{pass}\ + {${if eq {$runrc}{2}{softfail}\ + {${if eq {$runrc}{3}{neutral}\ + {${if eq {$runrc}{4}{permerror}\ + {${if eq {$runrc}{6}{none}{error}}}}}}}}}\ + } client-ip=$sender_host_address; \ + ${if def:sender_address_domain \ + {envelope-from=${sender_address}; }{}}\ + helo=$sender_helo_name + + warn + log_message = Unexpected error in SPF check. + condition = ${if >{$runrc}{6}{yes}{no}} + .endif + + + # Check against classic DNS "black" lists (DNSBLs) which list + # sender IP addresses + .ifdef CHECK_RCPT_IP_DNSBLS + warn + dnslists = CHECK_RCPT_IP_DNSBLS + add_header = X-Warning: $sender_host_address is listed at $dnslist_domain ($dnslist_value: $dnslist_text) + log_message = $sender_host_address is listed at $dnslist_domain ($dnslist_value: $dnslist_text) + .endif + + + # Check against DNSBLs which list sender domains, with an option to locally + # whitelist certain domains that might be blacklisted. + # + # Note: If you define CHECK_RCPT_DOMAIN_DNSBLS, you must append + # "/$sender_address_domain" after each domain. For example: + # CHECK_RCPT_DOMAIN_DNSBLS = rhsbl.foo.org/$sender_address_domain \ + # : rhsbl.bar.org/$sender_address_domain + .ifdef CHECK_RCPT_DOMAIN_DNSBLS + warn + !senders = ${if exists{CONFDIR/local_domain_dnsbl_whitelist}\ + {CONFDIR/local_domain_dnsbl_whitelist}\ + {}} + dnslists = CHECK_RCPT_DOMAIN_DNSBLS + add_header = X-Warning: $sender_address_domain is listed at $dnslist_domain ($dnslist_value: $dnslist_text) + log_message = $sender_address_domain is listed at $dnslist_domain ($dnslist_value: $dnslist_text) + .endif + + + # This hook allows you to hook in your own ACLs without having to + # modify this file. If you do it like we suggest, you'll end up with + # a small performance penalty since there is an additional file being + # accessed. This doesn't happen if you leave the macro unset. + .ifdef CHECK_RCPT_LOCAL_ACL_FILE + .include CHECK_RCPT_LOCAL_ACL_FILE + .endif + + + ############################################################################# + # This check is commented out because it is recognized that not every + # sysadmin will want to do it. If you enable it, the check performs + # Client SMTP Authorization (csa) checks on the sending host. These checks + # do DNS lookups for SRV records. The CSA proposal is currently (May 2005) + # an Internet draft. You can, of course, add additional conditions to this + # ACL statement to restrict the CSA checks to certain hosts only. + # + # require verify = csa + ############################################################################# + + + # Accept if the address is in a domain for which we are an incoming relay, + # but again, only if the recipient can be verified. + + accept + domains = +relay_to_domains + endpass + verify = recipient + + + # At this point, the address has passed all the checks that have been + # configured, so we accept it unconditionally. + + accept +##################################################### +### end acl/30_exim4-config_check_rcpt +##################################################### +##################################################### +### acl/40_exim4-config_check_data +##################################################### + +### acl/40_exim4-config_check_data +################################# + +# This ACL is used after the contents of a message have been received. This +# is the ACL in which you can test a message's headers or body, and in +# particular, this is where you can invoke external virus or spam scanners. + +acl_check_data: + + # Deny unless the address list headers are syntactically correct. + # + # If you enable this, you might reject legitimate mail. + .ifdef CHECK_DATA_VERIFY_HEADER_SYNTAX + deny + message = Message headers fail syntax check + !acl = acl_local_deny_exceptions + !verify = header_syntax + .endif + + + # require that there is a verifiable sender address in at least + # one of the "Sender:", "Reply-To:", or "From:" header lines. + .ifdef CHECK_DATA_VERIFY_HEADER_SENDER + deny + message = No verifiable sender address in message headers + !acl = acl_local_deny_exceptions + !verify = header_sender + .endif + + + # Deny if the message contains malware. Before enabling this check, you + # must install a virus scanner and set the av_scanner option in the + # main configuration. + # + # exim4-daemon-heavy must be used for this section to work. + # + # deny + # malware = * + # message = This message was detected as possible malware ($malware_name). + + + # Add headers to a message if it is judged to be spam. Before enabling this, + # you must install SpamAssassin. You also need to set the spamd_address + # option in the main configuration. + # + # exim4-daemon-heavy must be used for this section to work. + # + # Please note that this is only suiteable as an example. There are + # multiple issues with this configuration method. For example, if you go + # this way, you'll give your spamassassin daemon write access to the + # entire exim spool which might be a security issue in case of a + # spamassassin exploit. + # + # See the exim docs and the exim wiki for more suitable examples. + # + # warn + # spam = Debian-exim:true + # add_header = X-Spam_score: $spam_score\n\ + # X-Spam_score_int: $spam_score_int\n\ + # X-Spam_bar: $spam_bar\n\ + # X-Spam_report: $spam_report + + + # This hook allows you to hook in your own ACLs without having to + # modify this file. If you do it like we suggest, you'll end up with + # a small performance penalty since there is an additional file being + # accessed. This doesn't happen if you leave the macro unset. + .ifdef CHECK_DATA_LOCAL_ACL_FILE + .include CHECK_DATA_LOCAL_ACL_FILE + .endif + + + # accept otherwise + accept +##################################################### +### end acl/40_exim4-config_check_data +##################################################### +##################################################### +### router/00_exim4-config_header +##################################################### + +###################################################################### +# ROUTERS CONFIGURATION # +# Specifies how addresses are handled # +###################################################################### +# THE ORDER IN WHICH THE ROUTERS ARE DEFINED IS IMPORTANT! # +# An address is passed to each router in turn until it is accepted. # +###################################################################### + +begin routers + +mlmmj_router: + driver = accept + domains = +mlmmj_domains + require_files = MLMMJ_HOME/${lc::$local_part} + # Use this instead, if you don't want to give Exim rx rights to mlmmj spool. + # Exim will then spawn a new process running under the UID of "mlmmj". + #require_files = mlmmj:MLMMJ_HOME/${lc::$local_part} + local_part_suffix = +* + local_part_suffix_optional + headers_remove = Delivered-To + headers_add = Delivered-To: $local_part$local_part_suffix@$domain + transport = mlmmj_transport + +dnslookup: + driver = dnslookup + domains = ! +local_domains + transport = remote_smtp + ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8 + no_more + +##################################################### +### end router/00_exim4-config_header +##################################################### +##################################################### +### router/100_exim4-config_domain_literal +##################################################### + +### router/100_exim4-config_domain_literal +################################# + +# This router handles e-mail addresses in "domain literal" form like +# . The RFCs require this facility, but it is disabled +# in the default config since it is seldomly used and frequently abused. +# Domain literal support also needs to be enabled in the main config, +# which is automatically done if you use the enable macro +# MAIN_ALLOW_DOMAIN_LITERALS. + +.ifdef MAIN_ALLOW_DOMAIN_LITERALS +domain_literal: + debug_print = "R: domain_literal for $local_part@$domain" + driver = ipliteral + domains = ! +local_domains + transport = remote_smtp +.endif +##################################################### +### end router/100_exim4-config_domain_literal +##################################################### +##################################################### +### router/150_exim4-config_hubbed_hosts +##################################################### + +# router/150_exim4-config_hubbed_hosts +################################# + +# route specific domains manually. +# +# see exim4-config_files(5) and spec.txt chapter 20.3 through 20.7 for +# more detailed documentation. + +hubbed_hosts: + debug_print = "R: hubbed_hosts for $domain" + driver = manualroute + domains = "${if exists{CONFDIR/hubbed_hosts}\ + {partial-lsearch;CONFDIR/hubbed_hosts}\ + fail}" + same_domain_copy_routing = yes + route_data = ${lookup{$domain}partial-lsearch{CONFDIR/hubbed_hosts}} + transport = remote_smtp +##################################################### +### end router/150_exim4-config_hubbed_hosts +##################################################### +##################################################### +### router/200_exim4-config_primary +##################################################### + +### router/200_exim4-config_primary +################################# +# This file holds the primary router, responsible for nonlocal mails + +.ifdef DCconfig_internet +# configtype=internet +# +# deliver mail to the recipient if recipient domain is a domain we +# relay for. We do not ignore any target hosts here since delivering to +# a site local or even a link local address might be wanted here, and if +# such an address has found its way into the MX record of such a domain, +# the local admin is probably in a place where that broken MX record +# could be fixed. + +dnslookup_relay_to_domains: + debug_print = "R: dnslookup_relay_to_domains for $local_part@$domain" + driver = dnslookup + domains = ! +local_domains : +relay_to_domains + transport = remote_smtp + same_domain_copy_routing = yes + no_more + +# deliver mail directly to the recipient. This router is only reached +# for domains that we do not relay for. Since we most probably can't +# have broken MX records pointing to site local or link local IP +# addresses fixed, we ignore target hosts pointing to these addresses. + +dnslookup: + debug_print = "R: dnslookup for $local_part@$domain" + driver = dnslookup + domains = !+mlmmj_domains : !+local_domains + transport = remote_smtp + same_domain_copy_routing = yes + # ignore private rfc1918 and APIPA addresses + ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8 : 192.168.0.0/16 :\ + 172.16.0.0/12 : 10.0.0.0/8 : 169.254.0.0/16 :\ + 255.255.255.255 + no_more + +.endif + + +.ifdef DCconfig_local +# configtype=local +# +# Stand-alone system, so generate an error for mail to a non-local domain +nonlocal: + debug_print = "R: nonlocal for $local_part@$domain" + driver = redirect + domains = ! +local_domains + allow_fail + data = :fail: Mailing to remote domains not supported + no_more + +.endif + + +.ifdef DCconfig_smarthost DCconfig_satellite +# configtype=smarthost or configtype=satellite +# +# Send all non-local mail to a single other machine (smarthost). +# +# This means _ALL_ non-local mail goes to the smarthost. This will most +# probably not do what you want for domains that are listed in +# relay_domains. The most typical use for relay_domains is to control +# relaying for incoming e-mail on secondary MX hosts. In that case, +# it doesn't make sense to send the mail to the smarthost since the +# smarthost will probably send the message right back here, causing a +# loop. +# +# If you want to use a smarthost while being secondary MX for some +# domains, you'll need to copy the dnslookup_relay_to_domains router +# here so that mail to relay_domains is handled separately. + +smarthost: + debug_print = "R: smarthost for $local_part@$domain" + driver = manualroute + domains = ! +local_domains + transport = remote_smtp_smarthost + route_list = * DCsmarthost byname + host_find_failed = ignore + same_domain_copy_routing = yes + no_more + +.endif + + +# The "no_more" above means that all later routers are for +# domains in the local_domains list, i.e. just like Exim 3 directors. +##################################################### +### end router/200_exim4-config_primary +##################################################### +##################################################### +### router/300_exim4-config_real_local +##################################################### + +### router/300_exim4-config_real_local +################################# + +# This router allows reaching a local user while avoiding local +# processing. This can be used to inform a user of a broken .forward +# file, for example. The userforward router does this. + +COND_LOCAL_SUBMITTER = "\ + ${if match_ip{$sender_host_address}{:@[]}\ + {1}{0}\ + }" + +real_local: + debug_print = "R: real_local for $local_part@$domain" + driver = accept + domains = +local_domains + condition = COND_LOCAL_SUBMITTER + local_part_prefix = real- + check_local_user + transport = LOCAL_DELIVERY + +##################################################### +### end router/300_exim4-config_real_local +##################################################### +##################################################### +### router/400_exim4-config_system_aliases +##################################################### + +### router/400_exim4-config_system_aliases +################################# + +# This router handles aliasing using a traditional /etc/aliases file. +# +##### NB You must ensure that /etc/aliases exists. It used to be the case +##### NB that every Unix had that file, because it was the Sendmail default. +##### NB These days, there are systems that don't have it. Your aliases +##### NB file should at least contain an alias for "postmaster". +# +# This router handles the local part in a case-insensitive way which +# satisfies the RFCs requirement that postmaster be reachable regardless +# of case. If you decide to handle /etc/aliases in a caseful way, you +# need to make arrangements for a caseless postmaster. +# +# Delivery to arbitrary directories, files, and piping to programs in +# /etc/aliases is disabled per default. +# If that is a problem for you, see +# /usr/share/doc/exim4-base/README.Debian.gz +# for explanation and some workarounds. + +system_aliases: + debug_print = "R: system_aliases for $local_part@$domain" + driver = redirect + domains = +local_domains + allow_fail + allow_defer + data = ${lookup{$local_part}lsearch{/etc/aliases}} + .ifdef SYSTEM_ALIASES_USER + user = SYSTEM_ALIASES_USER + .endif + .ifdef SYSTEM_ALIASES_GROUP + group = SYSTEM_ALIASES_GROUP + .endif + .ifdef SYSTEM_ALIASES_FILE_TRANSPORT + file_transport = SYSTEM_ALIASES_FILE_TRANSPORT + .endif + .ifdef SYSTEM_ALIASES_PIPE_TRANSPORT + pipe_transport = SYSTEM_ALIASES_PIPE_TRANSPORT + .endif + .ifdef SYSTEM_ALIASES_DIRECTORY_TRANSPORT + directory_transport = SYSTEM_ALIASES_DIRECTORY_TRANSPORT + .endif +##################################################### +### end router/400_exim4-config_system_aliases +##################################################### +##################################################### +### router/500_exim4-config_hubuser +##################################################### + +### router/500_exim4-config_hubuser +################################# + +.ifdef DCconfig_satellite +# This router is only used for configtype=satellite. +# It takes care to route all mail targetted to +# to the host where we read our mail +# +hub_user: + debug_print = "R: hub_user for $local_part@$domain" + driver = redirect + domains = +local_domains + data = ${local_part}@DCreadhost + check_local_user + +# Grab the redirected mail and deliver it. +# This is a duplicate of the smarthost router, needed because +# DCreadhost might end up as part of +local_domains +hub_user_smarthost: + debug_print = "R: hub_user_smarthost for $local_part@$domain" + driver = manualroute + domains = DCreadhost + transport = remote_smtp_smarthost + route_list = * DCsmarthost byname + host_find_failed = ignore + same_domain_copy_routing = yes + check_local_user +.endif + + +##################################################### +### end router/500_exim4-config_hubuser +##################################################### +##################################################### +### router/600_exim4-config_userforward +##################################################### + +### router/600_exim4-config_userforward +################################# + +# This router handles forwarding using traditional .forward files in users' +# home directories. It also allows mail filtering with a forward file +# starting with the string "# Exim filter" or "# Sieve filter". +# +# The no_verify setting means that this router is skipped when Exim is +# verifying addresses. Similarly, no_expn means that this router is skipped if +# Exim is processing an EXPN command. +# +# The check_ancestor option means that if the forward file generates an +# address that is an ancestor of the current one, the current one gets +# passed on instead. This covers the case where A is aliased to B and B +# has a .forward file pointing to A. +# +# The four transports specified at the end are those that are used when +# forwarding generates a direct delivery to a directory, or a file, or to a +# pipe, or sets up an auto-reply, respectively. +# +userforward: + debug_print = "R: userforward for $local_part@$domain" + driver = redirect + domains = +local_domains + check_local_user + file = $home/.forward + require_files = $local_part:$home/.forward + no_verify + no_expn + check_ancestor + allow_filter + forbid_smtp_code = true + directory_transport = address_directory + file_transport = address_file + pipe_transport = address_pipe + reply_transport = address_reply + skip_syntax_errors + syntax_errors_to = real-$local_part@$domain + syntax_errors_text = \ + This is an automatically generated message. An error has\n\ + been found in your .forward file. Details of the error are\n\ + reported below. While this error persists, you will receive\n\ + a copy of this message for every message that is addressed\n\ + to you. If your .forward file is a filter file, or if it is\n\ + a non-filter file containing no valid forwarding addresses,\n\ + a copy of each incoming message will be put in your normal\n\ + mailbox. If a non-filter file contains at least one valid\n\ + forwarding address, forwarding to the valid addresses will\n\ + happen, and those will be the only deliveries that occur. + +##################################################### +### end router/600_exim4-config_userforward +##################################################### +##################################################### +### router/700_exim4-config_procmail +##################################################### + +procmail: + debug_print = "R: procmail for $local_part@$domain" + driver = accept + domains = +local_domains + check_local_user + transport = procmail_pipe + # emulate OR with "if exists"-expansion + require_files = ${local_part}:\ + ${if exists{/etc/procmailrc}\ + {/etc/procmailrc}{${home}/.procmailrc}}:\ + +/usr/bin/procmail + no_verify + no_expn + +##################################################### +### end router/700_exim4-config_procmail +##################################################### +##################################################### +### router/800_exim4-config_maildrop +##################################################### + +### router/800_exim4-config_maildrop +################################# + +maildrop: + debug_print = "R: maildrop for $local_part@$domain" + driver = accept + domains = +local_domains + check_local_user + transport = maildrop_pipe + require_files = ${local_part}:${home}/.mailfilter:+/usr/bin/maildrop + no_verify + no_expn + +##################################################### +### end router/800_exim4-config_maildrop +##################################################### +##################################################### +### router/850_exim4-config_lowuid +##################################################### + +### router/850_exim4-config_lowuid +################################# + +.ifndef FIRST_USER_ACCOUNT_UID +FIRST_USER_ACCOUNT_UID = 0 +.endif + +.ifndef DEFAULT_SYSTEM_ACCOUNT_ALIAS +DEFAULT_SYSTEM_ACCOUNT_ALIAS = :fail: no mail to system accounts +.endif + +COND_SYSTEM_USER_AND_REMOTE_SUBMITTER = "\ + ${if and{{! match_ip{$sender_host_address}{:@[]}}\ + {<{$local_user_uid}{FIRST_USER_ACCOUNT_UID}}}\ + {1}{0}\ + }" + +lowuid_aliases: + debug_print = "R: lowuid_aliases for $local_part@$domain (UID $local_user_uid)" + check_local_user + driver = redirect + allow_fail + domains = +local_domains + condition = COND_SYSTEM_USER_AND_REMOTE_SUBMITTER + data = ${if exists{CONFDIR/lowuid-aliases}\ + {${lookup{$local_part}lsearch{CONFDIR/lowuid-aliases}\ + {$value}{DEFAULT_SYSTEM_ACCOUNT_ALIAS}}}\ + {DEFAULT_SYSTEM_ACCOUNT_ALIAS}} +##################################################### +### end router/850_exim4-config_lowuid +##################################################### +##################################################### +### router/900_exim4-config_local_user +##################################################### + +### router/900_exim4-config_local_user +################################# + +# This router matches local user mailboxes. If the router fails, the error +# message is "Unknown user". + +local_user: + debug_print = "R: local_user for $local_part@$domain" + driver = accept + domains = +local_domains + check_local_user + local_parts = ! root + transport = LOCAL_DELIVERY + cannot_route_message = Unknown user +##################################################### +### end router/900_exim4-config_local_user +##################################################### +##################################################### +### router/mmm_mail4root +##################################################### + +### router/mmm_mail4root +################################# +# deliver mail addressed to root to /var/mail/mail as user mail:mail +# if it was not redirected in /etc/aliases or by other means +# Exim cannot deliver as root since 4.24 (FIXED_NEVER_USERS) + +mail4root: + debug_print = "R: mail4root for $local_part@$domain" + driver = redirect + domains = +local_domains + data = /var/mail/mail + file_transport = address_file + local_parts = root + user = mail + group = mail + +##################################################### +### end router/mmm_mail4root +##################################################### +##################################################### +### transport/00_exim4-config_header +##################################################### + +###################################################################### +# TRANSPORTS CONFIGURATION # +###################################################################### +# ORDER DOES NOT MATTER # +# Only one appropriate transport is called for each delivery. # +###################################################################### + +# A transport is used only when referenced from a router that successfully +# handles an address. + +begin transports + +mlmmj_transport: + driver = pipe + return_path_add + user = mlmmj + group = mlmmj + home_directory = MLMMJ_HOME + current_directory = MLMMJ_HOME + command = /usr/bin/mlmmj-footer-receive MLMMJ_HOME ${lc:$local_part} + +##################################################### +### end transport/00_exim4-config_header +##################################################### +##################################################### +### transport/10_exim4-config_transport-macros +##################################################### + +### transport/10_exim4-config_transport-macros +################################# + +.ifdef HIDE_MAILNAME +REMOTE_SMTP_HEADERS_REWRITE=*@+local_domains $1@DCreadhost frs : *@ETC_MAILNAME $1@DCreadhost frs +REMOTE_SMTP_RETURN_PATH=${if match_domain{$sender_address_domain}{+local_domains}{${sender_address_local_part}@DCreadhost}{${if match_domain{$sender_address_domain}{ETC_MAILNAME}{${sender_address_local_part}@DCreadhost}fail}}} +.endif + +.ifdef REMOTE_SMTP_HELO_FROM_DNS +.ifdef REMOTE_SMTP_HELO_DATA +REMOTE_SMTP_HELO_DATA==${lookup dnsdb {ptr=$sending_ip_address}{$value}{$primary_hostname}} +.else +REMOTE_SMTP_HELO_DATA=${lookup dnsdb {ptr=$sending_ip_address}{$value}{$primary_hostname}} +.endif +.endif +##################################################### +### end transport/10_exim4-config_transport-macros +##################################################### +##################################################### +### transport/30_exim4-config_address_file +##################################################### + +# This transport is used for handling deliveries directly to files that are +# generated by aliasing or forwarding. +# +address_file: + debug_print = "T: address_file for $local_part@$domain" + driver = appendfile + delivery_date_add + envelope_to_add + return_path_add + +##################################################### +### end transport/30_exim4-config_address_file +##################################################### +##################################################### +### transport/30_exim4-config_address_pipe +##################################################### + +# This transport is used for handling pipe deliveries generated by +# .forward files. If the commands fails and produces any output on standard +# output or standard error streams, the output is returned to the sender +# of the message as a delivery error. +address_pipe: + debug_print = "T: address_pipe for $local_part@$domain" + driver = pipe + return_fail_output + +##################################################### +### end transport/30_exim4-config_address_pipe +##################################################### +##################################################### +### transport/30_exim4-config_address_reply +##################################################### + +# This transport is used for handling autoreplies generated by the filtering +# option of the userforward router. +# +address_reply: + debug_print = "T: autoreply for $local_part@$domain" + driver = autoreply + +##################################################### +### end transport/30_exim4-config_address_reply +##################################################### +##################################################### +### transport/30_exim4-config_mail_spool +##################################################### + +### transport/30_exim4-config_mail_spool + +# This transport is used for local delivery to user mailboxes in traditional +# BSD mailbox format. +# +mail_spool: + debug_print = "T: appendfile for $local_part@$domain" + driver = appendfile + file = /var/mail/$local_part + delivery_date_add + envelope_to_add + return_path_add + group = mail + mode = 0660 + mode_fail_narrower = false + +##################################################### +### end transport/30_exim4-config_mail_spool +##################################################### +##################################################### +### transport/30_exim4-config_maildir_home +##################################################### + +### transport/30_exim4-config_maildir_home +################################# + +# Use this instead of mail_spool if you want to to deliver to Maildir in +# home-directory - change the definition of LOCAL_DELIVERY +# +maildir_home: + debug_print = "T: maildir_home for $local_part@$domain" + driver = appendfile + .ifdef MAILDIR_HOME_MAILDIR_LOCATION + directory = MAILDIR_HOME_MAILDIR_LOCATION + .else + directory = $home/Maildir + .endif + .ifdef MAILDIR_HOME_CREATE_DIRECTORY + create_directory + .endif + .ifdef MAILDIR_HOME_CREATE_FILE + create_file = MAILDIR_HOME_CREATE_FILE + .endif + delivery_date_add + envelope_to_add + return_path_add + maildir_format + .ifdef MAILDIR_HOME_DIRECTORY_MODE + directory_mode = MAILDIR_HOME_DIRECTORY_MODE + .else + directory_mode = 0700 + .endif + .ifdef MAILDIR_HOME_MODE + mode = MAILDIR_HOME_MODE + .else + mode = 0600 + .endif + mode_fail_narrower = false + # This transport always chdirs to $home before trying to deliver. If + # $home is not accessible, this chdir fails and prevents delivery. + # If you are in a setup where home directories might not be + # accessible, uncomment the current_directory line below. + # current_directory = / +##################################################### +### end transport/30_exim4-config_maildir_home +##################################################### +##################################################### +### transport/30_exim4-config_maildrop_pipe +##################################################### + +maildrop_pipe: + debug_print = "T: maildrop_pipe for $local_part@$domain" + driver = pipe + path = "/bin:/usr/bin:/usr/local/bin" + command = "/usr/bin/maildrop" + message_prefix = + message_suffix = + return_path_add + delivery_date_add + envelope_to_add + +##################################################### +### end transport/30_exim4-config_maildrop_pipe +##################################################### +##################################################### +### transport/30_exim4-config_procmail_pipe +##################################################### + +procmail_pipe: + debug_print = "T: procmail_pipe for $local_part@$domain" + driver = pipe + path = "/bin:/usr/bin:/usr/local/bin" + command = "/usr/bin/procmail" + return_path_add + delivery_date_add + envelope_to_add + +##################################################### +### end transport/30_exim4-config_procmail_pipe +##################################################### +##################################################### +### transport/30_exim4-config_remote_smtp +##################################################### + +### transport/30_exim4-config_remote_smtp +################################# +# This transport is used for delivering messages over SMTP connections. + +remote_smtp: + debug_print = "T: remote_smtp for $local_part@$domain" + driver = smtp +.ifdef REMOTE_SMTP_HOSTS_AVOID_TLS + hosts_avoid_tls = REMOTE_SMTP_HOSTS_AVOID_TLS +.endif +.ifdef REMOTE_SMTP_HEADERS_REWRITE + headers_rewrite = REMOTE_SMTP_HEADERS_REWRITE +.endif +.ifdef REMOTE_SMTP_RETURN_PATH + return_path = REMOTE_SMTP_RETURN_PATH +.endif +.ifdef REMOTE_SMTP_HELO_DATA + helo_data=REMOTE_SMTP_HELO_DATA +.endif +.ifdef DKIM_DOMAIN +dkim_domain = DKIM_DOMAIN +.endif +.ifdef DKIM_SELECTOR +dkim_selector = DKIM_SELECTOR +.endif +.ifdef DKIM_PRIVATE_KEY +dkim_private_key = DKIM_PRIVATE_KEY +.endif +.ifdef DKIM_CANON +dkim_canon = DKIM_CANON +.endif +.ifdef DKIM_STRICT +dkim_strict = DKIM_STRICT +.endif +.ifdef DKIM_SIGN_HEADERS +dkim_sign_headers = DKIM_SIGN_HEADERS +.endif +.ifdef TLS_DH_MIN_BITS +tls_dh_min_bits = TLS_DH_MIN_BITS +.endif +.ifdef REMOTE_SMTP_TLS_CERTIFICATE +tls_certificate = REMOTE_SMTP_TLS_CERTIFICATE +.endif +.ifdef REMOTE_SMTP_PRIVATEKEY +tls_privatekey = REMOTE_SMTP_PRIVATEKEY +.endif +##################################################### +### end transport/30_exim4-config_remote_smtp +##################################################### +##################################################### +### transport/30_exim4-config_remote_smtp_smarthost +##################################################### + +### transport/30_exim4-config_remote_smtp_smarthost +################################# + +# This transport is used for delivering messages over SMTP connections +# to a smarthost. The local host tries to authenticate. +# This transport is used for smarthost and satellite configurations. + +remote_smtp_smarthost: + debug_print = "T: remote_smtp_smarthost for $local_part@$domain" + driver = smtp + hosts_try_auth = <; ${if exists{CONFDIR/passwd.client} \ + {\ + ${lookup{$host}nwildlsearch{CONFDIR/passwd.client}{$host_address}}\ + }\ + {} \ + } +.ifdef REMOTE_SMTP_SMARTHOST_HOSTS_AVOID_TLS + hosts_avoid_tls = REMOTE_SMTP_SMARTHOST_HOSTS_AVOID_TLS +.endif +.ifdef REMOTE_SMTP_HEADERS_REWRITE + headers_rewrite = REMOTE_SMTP_HEADERS_REWRITE +.endif +.ifdef REMOTE_SMTP_RETURN_PATH + return_path = REMOTE_SMTP_RETURN_PATH +.endif +.ifdef REMOTE_SMTP_HELO_DATA + helo_data=REMOTE_SMTP_HELO_DATA +.endif +.ifdef TLS_DH_MIN_BITS +tls_dh_min_bits = TLS_DH_MIN_BITS +.endif +.ifdef REMOTE_SMTP_SMARTHOST_TLS_CERTIFICATE +tls_certificate = REMOTE_SMTP_SMARTHOST_TLS_CERTIFICATE +.endif +.ifdef REMOTE_SMTP_SMARTHOST_PRIVATEKEY +tls_privatekey = REMOTE_SMTP_SMARTHOST_PRIVATEKEY +.endif +##################################################### +### end transport/30_exim4-config_remote_smtp_smarthost +##################################################### +##################################################### +### transport/35_exim4-config_address_directory +##################################################### +# This transport is used for handling file addresses generated by alias +# or .forward files if the path ends in "/", which causes it to be treated +# as a directory name rather than a file name. + +address_directory: + debug_print = "T: address_directory for $local_part@$domain" + driver = appendfile + delivery_date_add + envelope_to_add + return_path_add + check_string = "" + escape_string = "" + maildir_format + +##################################################### +### end transport/35_exim4-config_address_directory +##################################################### +##################################################### +### retry/00_exim4-config_header +##################################################### + +###################################################################### +# RETRY CONFIGURATION # +###################################################################### + +begin retry + +##################################################### +### end retry/00_exim4-config_header +##################################################### +##################################################### +### retry/30_exim4-config +##################################################### + +### retry/30_exim4-config +################################# + +# This single retry rule applies to all domains and all errors. It specifies +# retries every 15 minutes for 2 hours, then increasing retry intervals, +# starting at 1 hour and increasing each time by a factor of 1.5, up to 16 +# hours, then retries every 6 hours until 4 days have passed since the first +# failed delivery. + +# Please note that these rules only limit the frequency of retries, the +# effective retry-time depends on the frequency of queue-running, too. +# See QUEUEINTERVAL in /etc/default/exim4. + +# Address or Domain Error Retries +# ----------------- ----- ------- + +* * F,2h,15m; G,16h,1h,1.5; F,4d,6h + +##################################################### +### end retry/30_exim4-config +##################################################### +##################################################### +### rewrite/00_exim4-config_header +##################################################### + +###################################################################### +# REWRITE CONFIGURATION # +###################################################################### + +begin rewrite + +##################################################### +### end rewrite/00_exim4-config_header +##################################################### +##################################################### +### rewrite/31_exim4-config_rewriting +##################################################### + +### rewrite/31_exim4-config_rewriting +################################# + +# This rewriting rule is particularily useful for dialup users who +# don't have their own domain, but could be useful for anyone. +# It looks up the real address of all local users in a file +.ifndef NO_EAA_REWRITE_REWRITE +*@+local_domains "${lookup{${local_part}}lsearch{/etc/email-addresses}\ + {$value}fail}" Ffrs +# identical rewriting rule for /etc/mailname +*@ETC_MAILNAME "${lookup{${local_part}}lsearch{/etc/email-addresses}\ + {$value}fail}" Ffrs +.endif + + +##################################################### +### end rewrite/31_exim4-config_rewriting +##################################################### +##################################################### +### auth/00_exim4-config_header +##################################################### + +###################################################################### +# AUTHENTICATION CONFIGURATION # +###################################################################### + +begin authenticators + + +##################################################### +### end auth/00_exim4-config_header +##################################################### +##################################################### +### auth/30_exim4-config_examples +##################################################### + +### auth/30_exim4-config_examples +################################# + +# The examples below are for server side authentication, when the +# local exim is SMTP server and clients authenticate to the local exim. + +# They allow two styles of plain-text authentication against an +# CONFDIR/passwd file whose syntax is described in exim4_passwd(5). + +# Hosts that are allowed to use AUTH are defined by the +# auth_advertise_hosts option in the main configuration. The default is +# "*", which allows authentication to all hosts over all kinds of +# connections if there is at least one authenticator defined here. +# Authenticators which rely on unencrypted clear text passwords don't +# advertise on unencrypted connections by default. Thus, it might be +# wise to set up TLS to allow encrypted connections. If TLS cannot be +# used for some reason, you can set AUTH_SERVER_ALLOW_NOTLS_PASSWORDS to +# advertise unencrypted clear text password based authenticators on all +# connections. As this is severely reducing security, using TLS is +# preferred over allowing clear text password based authenticators on +# unencrypted connections. + +# PLAIN authentication has no server prompts. The client sends its +# credentials in one lump, containing an authorization ID (which we do not +# use), an authentication ID, and a password. The latter two appear as +# $auth2 and $auth3 in the configuration and should be checked against a +# valid username and password. In a real configuration you would typically +# use $auth2 as a lookup key, and compare $auth3 against the result of the +# lookup, perhaps using the crypteq{}{} condition. + +# plain_server: +# driver = plaintext +# public_name = PLAIN +# server_condition = "${if crypteq{$auth3}{${extract{1}{:}{${lookup{$auth2}lsearch{CONFDIR/passwd}{$value}{*:*}}}}}{1}{0}}" +# server_set_id = $auth2 +# server_prompts = : +# .ifndef AUTH_SERVER_ALLOW_NOTLS_PASSWORDS +# server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}} +# .endif + +# LOGIN authentication has traditional prompts and responses. There is no +# authorization ID in this mechanism, so unlike PLAIN the username and +# password are $auth1 and $auth2. Apart from that you can use the same +# server_condition setting for both authenticators. + +# login_server: +# driver = plaintext +# public_name = LOGIN +# server_prompts = "Username:: : Password::" +# server_condition = "${if crypteq{$auth2}{${extract{1}{:}{${lookup{$auth1}lsearch{CONFDIR/passwd}{$value}{*:*}}}}}{1}{0}}" +# server_set_id = $auth1 +# .ifndef AUTH_SERVER_ALLOW_NOTLS_PASSWORDS +# server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}} +# .endif +# +# cram_md5_server: +# driver = cram_md5 +# public_name = CRAM-MD5 +# server_secret = ${extract{2}{:}{${lookup{$auth1}lsearch{CONFDIR/passwd}{$value}fail}}} +# server_set_id = $auth1 + +# Here is an example of CRAM-MD5 authentication against PostgreSQL: +# +# psqldb_auth_server: +# driver = cram_md5 +# public_name = CRAM-MD5 +# server_secret = ${lookup pgsql{SELECT pw FROM users WHERE username = '${quote_pgsql:$auth1}'}{$value}fail} +# server_set_id = $auth1 + +# Authenticate against local passwords using sasl2-bin +# Requires exim_uid to be a member of sasl group, see README.Debian.gz +# plain_saslauthd_server: +# driver = plaintext +# public_name = PLAIN +# server_condition = ${if saslauthd{{$auth2}{$auth3}}{1}{0}} +# server_set_id = $auth2 +# server_prompts = : +# .ifndef AUTH_SERVER_ALLOW_NOTLS_PASSWORDS +# server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}} +# .endif +# +# login_saslauthd_server: +# driver = plaintext +# public_name = LOGIN +# server_prompts = "Username:: : Password::" +# # don't send system passwords over unencrypted connections +# server_condition = ${if saslauthd{{$auth1}{$auth2}}{1}{0}} +# server_set_id = $auth1 +# .ifndef AUTH_SERVER_ALLOW_NOTLS_PASSWORDS +# server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}} +# .endif +# +# ntlm_sasl_server: +# driver = cyrus_sasl +# public_name = NTLM +# server_realm = +# server_set_id = $auth1 +# .ifndef AUTH_SERVER_ALLOW_NOTLS_PASSWORDS +# server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}} +# .endif +# +# digest_md5_sasl_server: +# driver = cyrus_sasl +# public_name = DIGEST-MD5 +# server_realm = +# server_set_id = $auth1 +# .ifndef AUTH_SERVER_ALLOW_NOTLS_PASSWORDS +# server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}} +# .endif + +# Authentcate against cyrus-sasl +# This is mainly untested, please report any problems to +# pkg-exim4-users@lists.alioth.debian.org. +# cram_md5_sasl_server: +# driver = cyrus_sasl +# public_name = CRAM-MD5 +# server_realm = +# server_set_id = $auth1 +# +# plain_sasl_server: +# driver = cyrus_sasl +# public_name = PLAIN +# server_realm = +# server_set_id = $auth1 +# .ifndef AUTH_SERVER_ALLOW_NOTLS_PASSWORDS +# server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}} +# .endif +# +# login_sasl_server: +# driver = cyrus_sasl +# public_name = LOGIN +# server_realm = +# server_set_id = $auth1 +# .ifndef AUTH_SERVER_ALLOW_NOTLS_PASSWORDS +# server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}} +# .endif + +# Authenticate against courier authdaemon + +# This is now the (working!) example from +# http://www.exim.org/eximwiki/FAQ/Policy_controls/Q0730 +# Possible pitfall: access rights on /var/run/courier/authdaemon/socket. +# plain_courier_authdaemon: +# driver = plaintext +# public_name = PLAIN +# server_condition = \ +# ${extract {ADDRESS} \ +# {${readsocket{/var/run/courier/authdaemon/socket} \ +# {AUTH ${strlen:exim\nlogin\n$auth2\n$auth3\n}\nexim\nlogin\n$auth2\n$auth3\n} }} \ +# {yes} \ +# fail} +# server_set_id = $auth2 +# .ifndef AUTH_SERVER_ALLOW_NOTLS_PASSWORDS +# server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}} +# .endif + +# login_courier_authdaemon: +# driver = plaintext +# public_name = LOGIN +# server_prompts = Username:: : Password:: +# server_condition = \ +# ${extract {ADDRESS} \ +# {${readsocket{/var/run/courier/authdaemon/socket} \ +# {AUTH ${strlen:exim\nlogin\n$auth1\n$auth2\n}\nexim\nlogin\n$auth1\n$auth2\n} }} \ +# {yes} \ +# fail} +# server_set_id = $auth1 +# .ifndef AUTH_SERVER_ALLOW_NOTLS_PASSWORDS +# server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}} +# .endif + +# This one is a bad hack to support the broken version 4.xx of +# Microsoft Outlook Express which violates the RFCs by demanding +# "250-AUTH=" instead of "250-AUTH ". +# If your list of offered authenticators is other than PLAIN and LOGIN, +# you need to adapt the public_name line manually. +# It has to be the last authenticator to work and has not been tested +# well. Use at your own risk. +# See the thread entry point from +# http://www.exim.org/mail-archives/exim-users/Week-of-Mon-20050214/msg00213.html +# for the related discussion on the exim-users mailing list. +# Thanks to Fred Viles for this great work. + +# support_broken_outlook_express_4_server: +# driver = plaintext +# public_name = "\r\n250-AUTH=PLAIN LOGIN" +# server_prompts = User Name : Password +# server_condition = no +# .ifndef AUTH_SERVER_ALLOW_NOTLS_PASSWORDS +# server_advertise_condition = ${if eq{$tls_in_cipher}{}{}{*}} +# .endif + +############## +# See /usr/share/doc/exim4-base/README.Debian.gz +############## + +# These examples below are the equivalent for client side authentication. +# They get the passwords from CONFDIR/passwd.client, whose format is +# defined in exim4_passwd_client(5) + +# Because AUTH PLAIN and AUTH LOGIN send the password in clear, we +# only allow these mechanisms over encrypted connections by default. +# You can set AUTH_CLIENT_ALLOW_NOTLS_PASSWORDS to allow unencrypted +# clear text password authentication on all connections. + +cram_md5: + driver = cram_md5 + public_name = CRAM-MD5 + client_name = ${extract{1}{:}{${lookup{$host}nwildlsearch{CONFDIR/passwd.client}{$value}fail}}} + client_secret = ${extract{2}{:}{${lookup{$host}nwildlsearch{CONFDIR/passwd.client}{$value}fail}}} + +# this returns the matching line from passwd.client and doubles all ^ +PASSWDLINE=${sg{\ + ${lookup{$host}nwildlsearch{CONFDIR/passwd.client}{$value}fail}\ + }\ + {\\N[\\^]\\N}\ + {^^}\ + } + +plain: + driver = plaintext + public_name = PLAIN +.ifndef AUTH_CLIENT_ALLOW_NOTLS_PASSWORDS + client_send = "<; ${if !eq{$tls_out_cipher}{}\ + {^${extract{1}{:}{PASSWDLINE}}\ + ^${sg{PASSWDLINE}{\\N([^:]+:)(.*)\\N}{\\$2}}\ + }fail}" +.else + client_send = "<; ^${extract{1}{:}{PASSWDLINE}}\ + ^${sg{PASSWDLINE}{\\N([^:]+:)(.*)\\N}{\\$2}}" +.endif + +login: + driver = plaintext + public_name = LOGIN +.ifndef AUTH_CLIENT_ALLOW_NOTLS_PASSWORDS + # Return empty string if not non-TLS AND looking up $host in passwd-file + # yields a non-empty string; fail otherwise. + client_send = "<; ${if and{\ + {!eq{$tls_out_cipher}{}}\ + {!eq{PASSWDLINE}{}}\ + }\ + {}fail}\ + ; ${extract{1}{::}{PASSWDLINE}}\ + ; ${sg{PASSWDLINE}{\\N([^:]+:)(.*)\\N}{\\$2}}" +.else + # Return empty string if looking up $host in passwd-file yields a + # non-empty string; fail otherwise. + client_send = "<; ${if !eq{PASSWDLINE}{}\ + {}fail}\ + ; ${extract{1}{::}{PASSWDLINE}}\ + ; ${sg{PASSWDLINE}{\\N([^:]+:)(.*)\\N}{\\$2}}" +.endif +##################################################### +### end auth/30_exim4-config_examples +##################################################### diff --git a/misc/move/exim4.filter b/misc/move/exim4.filter new file mode 100644 index 0000000..4f87a5b --- /dev/null +++ b/misc/move/exim4.filter @@ -0,0 +1,2 @@ +if $message_body contains "DISCARD_THIS_MAIL" and not error_message +then seen finish endif diff --git a/misc/move/foot_filter/Makefile b/misc/move/foot_filter/Makefile new file mode 100644 index 0000000..eb22bc9 --- /dev/null +++ b/misc/move/foot_filter/Makefile @@ -0,0 +1,14 @@ +all: foot_filter +dev: tags splint foot_filter +.PHONY: splint clean clobber +tags: foot_filter.c + ctags --excmd=number '--regex-c=-/\*[[:blank:]]*tag:[[:blank:]]*([[:alnum:]_]+)-\1-' foot_filter.c +splint: + splint +unixlib -exitarg -initallelements foot_filter.c +foot_filter: foot_filter.c + gcc -Wall -g -o foot_filter foot_filter.c -O3 +clean: + -rm tags +clobber: clean + -rm foot_filter + -rm test diff --git a/misc/move/foot_filter/foot_filter b/misc/move/foot_filter/foot_filter new file mode 100755 index 0000000000000000000000000000000000000000..cfd9fac59134f534c7301a33ddc2b10df4dfe3a2 GIT binary patch literal 288824 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

^wXJPvQ8 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

    (J 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ⅅ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'), '>' quoting characters, various pairs of\n\ +HTML tags (p, blockquote, div, span, font; it's naive, it doesn't check\n\ +tags in between are balanced at all, so in '

    prefix

    suffix

    ' the\n\ +first and last tags are paired), and even horizontal rules when inside\n\ +paired tags (e.g. use '

    footer
    '). If the smart strings are\n\ +found in the footer, they won't be added by the program, so you have the\n\ +necessary control to do this.\n\ +\n\ +New footers are added prior to trailing whitespace and a few closing html\n\ +tags (body, html) as well. You almost certainly want to begin your footer\n\ +with an empty line because of this.\n\ +\n\ +Since these alterations, by their very nature, break signed mail,\n\ +signatures are removed while processing. To keep some value from signatures,\n\ +have the MTA verify them and add a header (or even supply an alternative\n\ +footer to this program), and resign them to authenticate they came from the\n\ +mailing list directly after the signature verification was done and recorded.\n\ +Or don't use these kinds of transformations at all.\n\ +\n\ +-V shows the version and exits.\n\ +\n\ +Program is running now. Send EOF or interrupt to stop it. To avoid this usage\n\ +message if wanting to run without arguments, use '--' as an argument.\n\ +\n"; + +/* + +This is a fairly simple program not expecting much extension. As such, some +liberties have been taken and some fun has been had by the author. Correctness +has been prioritised in design, but speed and efficiency have been taken into +consideration and prioritised above readability and modularity and other such +generally recommended programming practices. If making changes, great care +should be taken to understand how and where (everywhere) globals are used +before making them. Don't try to modify the program without understanding how +the whole thing works together or you will get burnt. You have been warned. + +Relevant RFCs: +http://www.ietf.org/rfc/rfc2015.txt +http://www.ietf.org/rfc/rfc3851.txt +http://www.ietf.org/rfc/rfc2045.txt +http://www.ietf.org/rfc/rfc2046.txt +http://www.ietf.org/rfc/rfc822.txt +http://www.ietf.org/rfc/rfc2183.txt + +For program configuration, see the 'constants' section below. + +Also see code comments throughout. + +Future possibilities: + +- Saving copies of original mail in 'semi-temp' files for debugging. + +- Stripping attachments and save them (e.g. in a location that can become a + 'files uploaded' section on a website). Replace them with links to the + website, even. + +- Making the prefixes, suffixes, replacements, padding, guts, pairs, + configurable at runtime. + +- Attaching signed mail, or wrapping in a multipart rather than removing + signatures; wouldn't be hard if always using MIME footers. + +- Following a script to allow various other header transformations (addition, + removal, etc.), or other transformations. + +- Prologues as well as or instead of footers. + +*/ + +/* tag: includes */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* tag: typedefs */ + +// splint has bools, but C doesn't! +#ifndef S_SPLINT_S +typedef int bool; +#define false (0) +#define true (1) +#endif + +// This is mostly to be able to include splint annotations +typedef /*@null@*//*@observer@*/ const char * const_null_string; +typedef /*@null@*/ char * null_string; +typedef /*@null@*//*@owned@*/ char * owned_null_string; +typedef /*@null@*//*@dependent@*/ char * dependent_null_string; + +// 'Callbacks'; they communicate primarily using globals, see below +typedef bool (*callback_t)(); +typedef void (*function_t)(); + +// For fill() +typedef enum { + echo, + encode, + shunt, + discard, + stop, + fail +} when_full_t; + +// Various places +typedef enum { + unencoded, + quoted_printable, + base64 +} encoding_t; + +// For returning multiple characters, and a request to delete backlog +// when decoding +typedef struct { + int r; + int c1; + int c2; + int c3; +} decode_t; + +/* tag: constants */ + +/* tag: header_constants */ + +// How many MIME Content- headers we expect, maximum, in a mail. If we have +// more than that, we won't be able to process MIME so well, but we won't fail +// catastrophically. +#define mime_headers_max 16 + +/* tag: footer_constants */ + +// Stuff for processing the footer's smart removal and (smart or not) +// insertion + +static const char * plain_prefix = "------~----------"; +static const char * plain_suffix = "------~~---------"; +static const char * plain_replacement = "\r\n\r\n"; +static const_null_string plain_tails[] = { + " ","\t","\r","\n", + NULL +}; +static const_null_string plain_padding[] = { + ">"," ","\t","\r","\n", + NULL +}; +static const_null_string plain_guts[] = { + NULL +}; +static const_null_string plain_pairs[] = { + NULL +}; + +static const char * html_prefix = "------~----------"; +static const char * html_suffix = "------~~---------"; +static const char * html_replacement = "\r\n

    \r\n"; +static const_null_string html_tails[] = { + "","","","", + " "," ","&NBSP;","\t","\r","\n", + "
    ","
    ","
    ","
    ","
    ","
    ", + NULL +}; +static const_null_string html_padding[] = { + ">",">", + " "," ","&NBSP;","\t","\r","\n", + "
    ","
    ","
    ","
    ","
    ","
    ", + NULL +}; +static const_null_string html_guts[] = { + // These are removed in an attempt to make a pair + "
    ","
    ","
    ","
    ","
    ","
    ", + " "," ","&NBSP;","\t","\r","\n", + "
    ","
    ","
    ","
    ","
    ","
    ", + NULL +}; +static const_null_string html_pairs[] = { + // Closing part (or NULL to mark no more), end of opening part, + // start of opening part, NULL + // The search strategy is fairly naive; if it finds the closing part, + // it checks for the end of the opening part; if it finds that, it + // searches back for the first character of each of the opening part + // variants, and if that character is found and is the beginning of the + // whole variant, it removes the pair. + "

    ",">","

    ","

    ",">","

    ","

    ",">","

    ","
    ",">","
    ","
    ",">","
    ","
    ",">","
    ","
    ",">","","",">","","",">","","",">","","0,"unexpected commandline argument"); + // Load footers + if (plain_footer_file!=NULL) + load_footer(&plain_footer,&plain_footer_buffer, + plain_footer_file, + smart_footer?plain_prefix:NULL,smart_footer?plain_suffix:NULL); + if (html_footer_file!=NULL) + load_footer(&html_footer,&html_footer_buffer, + html_footer_file, + smart_footer?html_prefix:NULL,smart_footer?html_suffix:NULL); + if (mime_footer_file!=NULL) + load_footer(&mime_footer,&mime_footer_buffer, + mime_footer_file,NULL,NULL); + // Do the job + process_section(true,true,NULL); + // Finish + if (plain_footer_buffer!=NULL) free(plain_footer_buffer); + if (html_footer_buffer!=NULL) free(html_footer_buffer); + if (mime_footer_buffer!=NULL) free(mime_footer_buffer); + exit(EX_OK); +} + +static void load_footer(/*@out@*//*@shared@*/ char ** footer, + /*@reldef@*/ char ** footer_buffer, + char * file, + /*@unique@*/ const_null_string prefix, + /*@unique@*/ const_null_string suffix) { + FILE * f; + int prefixl=0, footerl=0, suffixl=0; + char * ff; + if (prefix!=NULL&&suffix!=NULL) { + prefixl=(int)strlen(prefix); + suffixl=(int)strlen(suffix); + } + f=fopen(file,"r"); + resort_to_errno(f==NULL,"error opening footer file",EX_NOINPUT); + resort_to_errno(fseek(f,0,SEEK_END)!=0, + "error seeking end of footer file",EX_IOERR); + resort_to_errno((footerl=(int)ftell(f))==-1, + "error finding footer length",EX_IOERR); + resort_to_errno(fseek(f,0,SEEK_SET)!=0, + "error seeking in footer file",EX_IOERR); + // prefix, \n, footer, \n, suffix, \0 + *footer_buffer=alloc_or_exit(sizeof(char)*(prefixl+footerl+suffixl+3)); + *footer=*footer_buffer; + *footer+=prefixl+1; + resort_to_errno(fread(*footer,1,(size_t)footerl,f)<(size_t)footerl, + "error reading footer",EX_IOERR); + // We strip off a single trailing newline to keep them from accumulating + // but to allow the user the option of adding them if desired + if ((*footer)[footerl-1]=='\n') --footerl; + (*footer)[footerl]='\0'; + if (prefix==NULL||suffix==NULL) return; + // Put in the prefix and suffix as necessary + ff=strstr(*footer,prefix); + if (ff!=NULL) { + ff=strstr(ff,suffix); + if (ff!=NULL) return; + (*footer)[footerl]='\n'; + ++footerl; + strcpy(*footer+footerl,suffix); + (*footer)[footerl+suffixl]='\0'; + } else { + ff=strstr(*footer,suffix); + if (ff==NULL) { + (*footer)[footerl]='\n'; + ++footerl; + strcpy(*footer+footerl,suffix); + (*footer)[footerl+suffixl]='\0'; + } + *footer-=prefixl+1; + strcpy(*footer,prefix); + (*footer)[prefixl]='\n'; + } +} + +// Should be called with the boundary for the section as lookahead +// in the buffer, but nothing more, and no lookbehind. +static void process_section(bool add_footer, + bool can_reenvelope, /*@null@*/ bool * parent_needs_footer) { + char * external=NULL; + char * internal=NULL; + char * generated=NULL; + bool reenveloping=false; + bool child_needed_footer=false; + bool needs_footer=false; + bool unsigning=false; + if (parent_needs_footer!=NULL) *parent_needs_footer=false; + // The headers must be read, saved and echoed before making any + // recursive calls, as I'm naughty and using globals. + read_boundary(&external); + read_and_save_mime_headers(); + if (mime_bad) { + // If an error, just resort to echoing + echo_buffer(); // Boundary and headers + // End headers with the extra line break + resort_to_errno(putstr("\r\n")==EOF, + "error echoing string",EX_IOERR); + free_saved_mime_headers(); + // Body + echo_to_boundary(external); + free(external); + return; + } + // Headers determining we skip this section + if (is_signature()) { + skip_buffer(); // Boundary and headers + skip_to_boundary(external); + return; + } + // Header processing + if (is_signed()) unsigning=true; + if (unsigning) change_to_mixed(); + if (add_footer&&mime_footer!=NULL&&( + is_alternative()||(is_multipart(NULL)&&!is_mixed())|| + (is_plain()&&plain_footer==NULL)|| + (is_html()&&html_footer==NULL) + )) { + add_footer=false; + if (can_reenvelope) { + reenveloping=true; + remove_mime_headers(); + } else if (parent_needs_footer!=NULL) *parent_needs_footer=true; + } + // Headers + echo_buffer(); // Boundary and possibly modified headers + if (reenveloping) { + generate_boundary(&generated); + output_mime_mixed_headers(generated); + output_prolog(); + output_boundary(generated); + output_saved_mime_headers(); + } + // End the headers with the extra line break + resort_to_errno(putstr("\r\n")==EOF, + "error echoing string",EX_IOERR); + // Body processing + if (is_multipart(&internal)) { + // This branch frees the MIME headers before recursing. + // Don't include the prolog if it used to be signed; + // it usually says something like 'this message is signed' + if (unsigning) { + skip_to_boundary(internal); + resort_to_errno(putstr("\r\n")==EOF, + "error echoing string",EX_IOERR); + } else { + echo_to_boundary(internal); + } + // The recursive call needs these globals + free_saved_mime_headers(); + while (!at_final_boundary(internal)) { + process_section(add_footer,false,&child_needed_footer); + if (child_needed_footer) needs_footer=true; + } + if (needs_footer) output_mime_footer(internal); + free(internal); + echo_to_boundary(external); + } else { + // This branch frees the MIME headers at the end + if (!is_attachment()&&( + (is_plain()&&plain_footer!=NULL)|| + (is_html()&&html_footer!=NULL))) { + // alternatively + // if (!is_attachment()&&( + // (is_plain()&&((add_footer&&plain_footer!=NULL)||smart_footer))|| + // (is_html()&&((add_footer&&html_footer!=NULL)||smart_footer)))) { + if (is_plain()) { + process_text_section(add_footer,plain_footer, + plain_prefix,plain_suffix,plain_replacement, + plain_tails,plain_padding,plain_guts,plain_pairs,external); + } else { + process_text_section(add_footer,html_footer, + html_prefix,html_suffix,html_replacement, + html_tails,html_padding,html_guts,html_pairs,external); + } + } else { + echo_to_boundary(external); + } + free_saved_mime_headers(); + } + // MIME stuff is freed now; take care not to use it. + /*@-branchstate@*/ + if (reenveloping) { + // We ensure generated is not null in another if(reenveloping) + // conditional above + /*@-nullpass@*/ + output_mime_footer(generated); + output_final_boundary(generated); + free(generated); + /*@=nullpass@*/ + } + /*@=branchstate@*/ + free(external); +} + +/* tag: header_functions */ + +static inline void read_and_save_mime_headers() { + /*@-mustfreeonly@*/ + mime_bad=false; + // Mark current end of buffer + buffer_mark=buffer_read; + buffer_marked=true; + for (;;) { + do { + // Extend current header until beginning of next + callback_bool=false; + (void)fill(until_eol,shunt); + if (buffer_filled==buffer_read) { + // We probably hit EOF; just get out, and the whole + // mail will end up echoed out + warning("unexpected end of input"); + break; + } + (void)look(one_char,buffer_read,false); + if (callback_int==(int)' '||callback_int==(int)'\t') { + // Continuation of previous header; read it + read_buffer(); + continue; + } + // Start of new header; don't read it; process the old one + // (from the mark to the end of the lookbehind) + break; + } while (true); + // Process the old header, if there is one + if (buffer_mark0) { + if (*h=='\0') break; + if (*h=='\\') { + ++h; + if (*h=='\0') break; + } + else if (*h=='(') ++levels; + else if (*h==')') --levels; + ++h; + } + if (!delimiting(*h,ext)&&!delimiting(*(hh-1),ext)) { + // Put in some whitespace if something delimiting isn't + // coming and hasn't just been + *hh=' '; + ++hh; + } + continue; + } else if (*h=='"'||*h=='[') { + if (*h=='[') close=']'; + else close='"'; + *hh=*h; + ++h; ++hh; + hhh=hh; + while (*h!='\0'&&*h!=close) { + if (*h=='\\') { + *hh=*h; + ++hh; ++h; + if (*h=='\0') break; + if (*h=='\r'&&*(h+1)=='\n') { + *hh=*h; ++hh; ++h; + *hh=*h; ++hh; ++h; + if (*h=='\0') break; + ++hh; ++h; + continue; + } + } else if (*h==(char)8) { + --hh; ++h; + if (hh'||c=='@'|| + c==','||c==';'||c==':'||c=='\\'||c=='"'|| + c=='.'||c=='['||c==']'|| + (ext&&(c=='/'||c=='='||c=='?'))); +} +static inline void remove_mime_headers() { + int h; + for (h=0;h0, + "internal error: unexpected data in buffer",EX_SOFTWARE); + set_decoding_type(); + encoding=decoding; + decode_and_read_to_boundary_encoding_when_full(boundary); + if (smart_footer&&footer!=NULL) { + // alternatively + // if (smart_footer) { + for (;;) { + prefix_pos=pos_of(prefix,0,buffer_read); + if (prefix_pos==EOF) break; + suffix_pos=pos_of(suffix,prefix_pos,buffer_read); + if (suffix_pos==EOF) break; + for (;;) { + later_prefix_pos= + pos_of(prefix,prefix_pos+prefixl,suffix_pos-prefixl); + if (later_prefix_pos!=EOF) prefix_pos=later_prefix_pos; + else break; + } + suffix_pos+=suffixl; + pad(padding,guts,pairs,&prefix_pos,&suffix_pos); + replacement_starts[replacements_count]=prefix_pos; + replacement_ends[replacements_count]=suffix_pos; + // We may not want the last replacement so replace + // with nothing first + replacement_strings[replacements_count]=NULL; + ++replacements_count; + // We want the last replacement; encode it now before + // doing any more encoding + if (removed_footers) encode_string(replacement); + encode_replacements(); + removed_footers=true; + } + } + if (*boundary!='\0'&&(decoding==quoted_printable||decoding==unencoded)) { + // If we're not using base64 encoding, and we're in multipart, there + // will be a final CRLF that is part of the input but logically part of + // the boundary, not the text. Removing the footer may have already + // removed it, so we need to check if it's here or not. + if (buffer_read>1) { + callback_compare="\r\n"; + (void)look(comparing_head,buffer_read-2,false); + callback_compare=NULL; + if (callback_bool) boundary_newline=true; + } + } + if (add_footer&&footer!=NULL) { + // This will skip past the boundary newline + mark_tail(tails); + if (removed_footers&&buffer_mark==0) { + // The last replacement coincides with where the footer + // is going to go; don't use the replacement text. + removed_footers=false; + } + } + if (removed_footers) encode_string(replacement); + if (add_footer&&footer!=NULL) { + if (buffer_mark0) (void)empty(until_no_buffer); +} +static inline void echo_lookbehind() { + make_replacements(echoing_one_char,echoing_until_start_marked); + if (buffer_read>0) (void)empty(echoing_until_no_lookbehind); +} +static inline void encode_lookbehind() { + make_replacements(encoding_one_char,encoding_until_start_marked); + if (buffer_read>0) (void)empty(encoding_until_no_lookbehind); +} +static inline void encode_replacements() { + make_replacements(encoding_one_char,encoding_until_start_marked); +} +static inline void make_replacements(callback_t one_char, + callback_t start_marked) { + int r, minr=0; + const char * c; + if (buffer_read==0) return; + buffer_marked=false; + while (replacements_count>0) { + for (r=0;r0) (void)empty(start_marked); + c = replacement_strings[minr]; + if (c!=NULL) { + while (*c!='\0') { + buffer_char=(int)(unsigned int)*c; + (void)(*one_char)(); + ++c; + } + } + buffer_marked=true; + buffer_mark=replacement_ends[minr]; + for (r=0;r0) (void)empty(until_start_marked); + for (r=minr;r0) (void)empty(encoding_until_start_marked); +} +static inline void echo_disk_buffer() { + if (disk_buffer_filled>0) (void)empty(echoing_until_no_disk_buffer); +} +static inline void encode_disk_buffer() { + if (disk_buffer_filled>0) (void)empty(encoding_until_no_disk_buffer); +} +static inline void skip_disk_buffer() { + if (disk_buffer_filled>0) (void)empty(until_no_disk_buffer); +} +static inline void read_boundary(/*@out@*/ char ** boundary) { + int l=0; + if (buffer_filled>buffer_read) { + callback_bool=false; + callback_int=0; + resort_to_exit(!look(counting_until_eol,buffer_read,false), + "internal error: missing eol at section boundary",EX_SOFTWARE); + l=callback_int-2; // remove the CRLF, but keep the leading '--' + } + // Leave room to append a trailing '--' for testing final boundary; + // the CRLF will be written in this space by saving_until_eol too. + *boundary = alloc_or_exit(sizeof(char)*(l+3)); + if (buffer_filled>buffer_read) { + callback_bool=false; + callback_save=*boundary; + (void)look(saving_until_eol,buffer_read,false); + callback_save=NULL; + } + (*boundary)[l]='\0'; + if (buffer_filled>buffer_read) { + callback_bool=false; + (void)look(until_eol,buffer_read,true); + } +} +static inline void echo_to_boundary(const char * boundary) { + do { + echo_buffer(); + } while (!process_one_line_checking_boundary( + echoing_n_chars,NULL,until_eol,echo,boundary)); +} +static inline void skip_to_boundary(const char * boundary) { + do { + skip_buffer(); + } while (!process_one_line_checking_boundary( + n_chars,NULL,until_eol,discard,boundary)); +} +static inline void decode_and_read_to_boundary_encoding_when_full( + const char * boundary) { + do { + read_buffer(); + } while (!process_one_line_checking_boundary( + encoding_n_chars,decode_lookahead, + decoding_until_eol,encode,boundary)); + finish_decoding(); // This just sets state, doesn't change data +} +static inline bool process_one_line_checking_boundary(callback_t n_chars, + /*@null@*/ function_t process, callback_t processing, + when_full_t when_full, const char * boundary) { + bool stopped_by_design; + if (feof(stdin)!=0) { + // We're done! Call it a boundary (even if it isn't--we need to + // get out of loops cleanly and tidy up as best we can). + return true; + } + // Empty until enough space for boundary + if (mem_buffer_size-mem_buffer_filled<80) { + callback_int=80-(mem_buffer_size-mem_buffer_filled); + (void)empty(n_chars); + } + callback_bool=false; + stopped_by_design=fill(until_eol,stop); + if (stopped_by_design||feof(stdin)!=0) { + if (buffer_filled-buffer_read==0) { + return *boundary=='\0'; + } + callback_bool=false; + if (*boundary!='\0') { + // Can only be at a boundary without being at EOF if there + // really is a boundary + /*@-temptrans@*/ + callback_compare=boundary; + /*@=temptrans@*/ + (void)look(comparing_head,buffer_read,false); + callback_compare=NULL; + } + if (!callback_bool&&process!=NULL) (*process)(); + return callback_bool; + } else { + // Line is too long to be a boundary, so must be decoded + if (process!=NULL) (*process)(); + callback_bool=false; + (void)fill(processing,when_full); + return false; + } +} + +// Return the position of text whose start may occur in the buffer +// anywhere between from and (just before) to. Use EOF for from to +// go from current location; use EOF for to to read indefinitely; +// EOF is returned if text is not found. +static int pos_of(const char * text,int from,int to) { + int saved_buffer_read; + int pos=EOF; + if (*text=='\0') return from; + saved_buffer_read=buffer_read; + if (from!=EOF) buffer_read=from; + callback_match=(int)(unsigned int)*text; + for (;;) { + if (to!=EOF) { + callback_int=to-buffer_read; + if (!look(n_chars_until_match,buffer_read,true)) break; + } else { + if (!look(until_match,buffer_read,true)) break; + } + if (!callback_bool) break; + /*@-temptrans@*/ + callback_compare=text+1; + /*@=temptrans@*/ + (void)look(comparing_head,buffer_read,false); + callback_compare=NULL; + if (callback_bool) { + // Include the first character + pos=buffer_read-1; + break; + } + } + buffer_read=saved_buffer_read; + return pos; +} + +// Look at characters in the buffer, starting at offset from, +// 'reading' if so indicated (and looking at that location). +// The callback is called after updating the reading pointer +// and placing the character in the buffer. The character is +// also passed by means of the buffer_char global. +// EOF is sent to the callback when we run out of data. +// There is no automatic attempt to fill the buffer. +// The callback should return a boolean indicating whether +// to continue. This function will return true if the callback +// indicated to stop (including if it so indicated on EOF), or +// false if it stopped for EOF. +// We always call the callback at least once, so don't call +// this function at all unless you definitely want to look +// at something. +static bool look(callback_t callback,int from,bool read) { + int pos=from; + int disk_buffer_pos; + char * mem_buffer_pos; + if (pos=mem_buffer_end) mem_buffer_pos-=mem_buffer_size; + while (pos=disk_buffer_filled) { + mem_buffer_pos=mem_buffer_next_empty+(pos-disk_buffer_filled); + if (mem_buffer_pos>=mem_buffer_end) mem_buffer_pos-=mem_buffer_size; + while (pos>=disk_buffer_filled) { + buffer_char=(int)(unsigned int)*mem_buffer_pos; + if (!(*callback)()) return true; + --mem_buffer_pos; + if (mem_buffer_pos==mem_buffer_start-1) + mem_buffer_pos=mem_buffer_end-1; + if (mark&&pos==buffer_mark) --buffer_mark; + --pos; + } + } + if (pos>=0&&disk_buffer_filled>0) { + disk_buffer_pos=disk_buffer_start+pos; + // Reading backwards in the disk buffer is potentially very nasty; + // hopefully it never actually happens + while (pos>=0) { + /*@-nullpass@*/ + resort_to_errno(fseek(disk_buffer,disk_buffer_pos,SEEK_SET)!=0, + "error seeking in temporary file",EX_IOERR); + disk_buffer_sought=disk_buffer_pos; + buffer_char=getc(disk_buffer); + /*@=nullpass@*/ + resort_to_errno(buffer_char==EOF, + "error reading temporary file",EX_IOERR); + ++disk_buffer_sought; + if (!(*callback)()) return true; + --disk_buffer_pos; + if (mark&&pos==buffer_mark) --buffer_mark; + --pos; + } + } + if (mark&&buffer_mark==-1) { + buffer_mark=0; + buffer_marked=false; + } + // We don't call the callback on EOF when going backwards + // buffer_char=EOF; + // (void)(*callback)(); + return false; +} +// Remove characters from the (beginning of the) buffer. The same +// general principles as for look() apply. The callback is called +// after the character is removed and all accounting has been done, so +// perhaps the only place you can reliably find the character is in +// the buffer_char global. Again the callback gets an EOF call if +// there's nothing more to empty, and no automatic filling is done. +// The callback and function return values are as for look() and +// again, the callback is always called at least once; this means at +// least one character is always removed from the buffer, so only call +// the function if something definitely should be removed. +static bool empty(callback_t callback) { + if (disk_buffer_filled>0) { + if (disk_buffer_sought!=disk_buffer_start) { + /*@-nullpass@*/ + resort_to_errno(fseek(disk_buffer,disk_buffer_start,SEEK_SET)!=0, + "error seeking in temporary file",EX_IOERR); + /*@=nullpass@*/ + disk_buffer_sought=disk_buffer_start; + } + while (disk_buffer_filled>0) { + /*@-nullpass@*/ + buffer_char=getc(disk_buffer); + /*@=nullpass@*/ + resort_to_errno(buffer_char==EOF, + "error reading temporary file",EX_IOERR); + ++disk_buffer_sought; + ++disk_buffer_start; + --disk_buffer_filled; + --buffer_filled; + if (buffer_read>0) --buffer_read; + if (buffer_marked) { + if (buffer_mark>0) --buffer_mark; + else buffer_marked=false; + } + if (!(*callback)()) return true; + } + } + while (mem_buffer_filled>0) { + buffer_char=(int)(unsigned int)*mem_buffer_next_empty; + ++mem_buffer_next_empty; + if (mem_buffer_next_empty==mem_buffer_end) mem_buffer_next_empty=mem_buffer_start; + --mem_buffer_filled; + --buffer_filled; + if (buffer_read>0) --buffer_read; + if (buffer_marked) { + if (buffer_mark>0) --buffer_mark; + else buffer_marked=false; + } + if (!(*callback)()) return true; + } + buffer_char=EOF; + if (!(*callback)()) return true; + return false; +} +// Get more characters into the (end of the) buffer. The same +// general principles as for look() apply. The callback is called +// after the character is added and all accounting has been done, +// gets the character via buffer_char, including an EOF when no more +// input is available (EOF on stdin). It should return whether to get +// more characters, and this function will return whether its exit was +// requested by the callback or not (the callback may signal EOF is +// an appropriate place to stop and we still return true). +// When the buffer is full there are a number of automatic options +// echo old the data to stdout or call encodechar for it one character +// at a time; shunt a block off to disk, keeping mem_buffer_keep in +// memory, discard it a character at a time, stop (and return false; +// no EOF call is made), or fail (exit). Here 'full' is defined as +// less than mem_buffer_margin of space after adding the most recent +// character, so there is always a bit of space for callbacks to do +// input transformations. Again, at least one character is always +// added (if possible), and thus consumed from stdin, so only call this +// if you really want to do that. +static bool fill(callback_t callback, when_full_t when_full) { + if (feof(stdin)!=0) { + buffer_char=EOF; + if (!(*callback)()) return true; + return false; + } + for (;;) { + /*@-infloops@*/ + while (mem_buffer_filled>=mem_buffer_size-mem_buffer_margin) { + switch (when_full) { + case echo: + if (disk_buffer_filled>0) echo_disk_buffer(); + (void)empty(echoing_one_char); + break; + case encode: + if (disk_buffer_filled>0) encode_disk_buffer(); + (void)empty(encoding_one_char); + break; + case discard: + if (disk_buffer_filled>0) skip_disk_buffer(); + (void)empty(one_char); + break; + case shunt: + shunt_to_disk(mem_buffer_filled-mem_buffer_keep); + break; + case stop: + return false; + case fail: default: + resort_to_exit(true,"buffer full",EX_SOFTWARE); + } + } + /*@=infloops@*/ + buffer_char=get(); + if (buffer_char==EOF) { + resort_to_errno(ferror(stdin)!=0,"error reading input",EX_IOERR); + if (!(*callback)()) return true; + return false; + } + *mem_buffer_next_fill=(char)buffer_char; + ++mem_buffer_next_fill; + if (mem_buffer_next_fill==mem_buffer_end) mem_buffer_next_fill=mem_buffer_start; + ++mem_buffer_filled; + ++buffer_filled; + if (!(*callback)()) return true; + } +} + +static inline void create_disk_buffer() { + int fildes; + fildes=mkstemp(disk_buffer_template); + resort_to_errno(fildes==-1, + "cannot create temporary file",EX_CANTCREAT); + disk_buffer=fdopen(fildes,"rw"); + resort_to_errno(disk_buffer==NULL, + "cannot create temporary stream",EX_CANTCREAT); +} +static void remove_disk_buffer() { + if (disk_buffer!=NULL) { + resort_to_warning(fclose(disk_buffer)!=0, + "error closing temporary file"); + disk_buffer=NULL; + resort_to_warning(unlink(disk_buffer_template)!=0, + "error removing temporary file"); + } +} +static inline void shunt_to_disk(int n) { + if (disk_buffer==NULL) create_disk_buffer(); + if (disk_buffer_sought!=disk_buffer_start+disk_buffer_filled) { + disk_buffer_sought=disk_buffer_start+disk_buffer_filled; + /*@-nullpass@*/ + resort_to_errno(fseek(disk_buffer, + disk_buffer_start+disk_buffer_filled,SEEK_SET)!=0, + "cannot seek to end of temporary file",EX_IOERR); + /*@=nullpass@*/ + } + while (n>0) { + resort_to_exit(mem_buffer_filled==0, + "internal error: shunting too much to disk",EX_SOFTWARE); + /*@-nullpass@*/ + resort_to_errno(putc(*mem_buffer_next_empty,disk_buffer)==EOF, + "error writing to temporary file",EX_IOERR); + /*@=nullpass@*/ + ++disk_buffer_sought; + ++disk_buffer_filled; + ++mem_buffer_next_empty; + if (mem_buffer_next_empty==mem_buffer_end) mem_buffer_next_empty=mem_buffer_start; + --mem_buffer_filled; + --n; + } +} + +/* tag: callback_functions */ + +static bool one_char() { + callback_int=buffer_char; + return false; +} +static bool echoing_one_char() { + if (buffer_char!=EOF) { + resort_to_errno(put(buffer_char)==EOF,"error echoing",EX_IOERR); + } + callback_int=buffer_char; + return false; +} +static bool encoding_one_char() { + if (buffer_char!=EOF) encodechar(buffer_char); + callback_int=buffer_char; + return false; +} +// Set up callback_int before using this. +static bool n_chars() { + return --callback_int>0; +} +// Set up callback_int before using this. +static bool echoing_n_chars() { + if (buffer_char!=EOF) { + resort_to_errno(put(buffer_char)==EOF,"error echoing",EX_IOERR); + } + return --callback_int>0; +} +// Set up callback_int before using this. +static bool encoding_n_chars() { + if (buffer_char!=EOF) encodechar(buffer_char); + return --callback_int>0; +} +// Set up callback_int and callback_save before using this. +static bool saving_n_chars() { + if (buffer_char!=EOF) *callback_save++=(char)buffer_char; + // We don't actually need this, though it's a good idea, really! + // *callback_save='\0'; + return --callback_int>0; +} +// Set up callback_int and callback_match before using this. +static bool n_chars_until_match() { + callback_bool=buffer_char==callback_match; + return --callback_int>0&&buffer_char!=callback_match; +} +// Do callback_bool=false before using this. +static bool until_eol() { + if (buffer_char==(int)'\n') return !callback_bool; + callback_bool=buffer_char==(int)'\r'; + return true; +} +// Do callback_bool=false before using this. +/*static bool echoing_until_eol() { + if (buffer_char!=EOF) { + resort_to_errno(put(buffer_char)==EOF,"error echoing",EX_IOERR); + } + if (buffer_char==(int)'\n') return !callback_bool; + callback_bool=buffer_char==(int)'\r'; + return true; +}*/ +// Do callback_bool=false, callback_int=0 before using this. +static bool counting_until_eol() { + if (buffer_char!=EOF) ++callback_int; + if (buffer_char==(int)'\n') return !callback_bool; + callback_bool=buffer_char==(int)'\r'; + return true; +} +// Do callback_bool=false and set up callback_save before using this. +static bool saving_until_eol() { + if (buffer_char!=EOF) *callback_save++=(char)buffer_char; + // We don't actually need this, though it's a good idea, really! + // *callback_save='\0'; + if (buffer_char==(int)'\n') return !callback_bool; + callback_bool=buffer_char==(int)'\r'; + return true; +} +// Do callback_bool=false before using this. +static bool decoding_until_eol() { + // We decode as we fill and work directly in the buffer to make + // the transformation. We are guaranteed enough space to do this by + // mem_buffer_margin. + decode_t decoded; + decoded=decodechar(buffer_char); + // We always remove the latest undecoded character from the + // buffer. + ++decoded.r; + if (decoded.r>mem_buffer_filled) { + // This will only happen for quoted-printable decoding + // whitespace stripping, and we can just live with it + // if we can't get rid of it all; with sensible constants + // something really is disobeying MIME and probably SMTP + // about line length anyway if this happens. + warning("unable to strip all whitespace; not enough in memory"); + decoded.r=mem_buffer_filled; + } + if (buffer_filled-decoded.r(int)'Z'||c2!=c1-(int)'A'+(int)'a')&& + (c2<(int)'A'||c2>(int)'Z'||c1!=c2-(int)'A'+(int)'a')) { + callback_bool=false; + return false; + } + /*@-modobserver@*/ + ++callback_compare; + /*@=modobserver@*/ + if (*callback_compare=='\0') { + callback_bool=true; + return false; + } + return true; + /*@=nullderef@*/ +} + +/* tag: encoding_functions */ + +static inline void encode_string(const char * s) { + while (*s!='\0') { + encodechar((int)(unsigned int)*s); + s++; + } +} +static void encodechar(int c) { + if (encoding==unencoded) { + if (c!=EOF) resort_to_errno(put(c)==EOF,"error encoding",EX_IOERR); + return; + } else if (encoding==quoted_printable) { + if (encoding_echoed>=68) { + // We need a soft line break, or are close enough to needing + // one (76 chars max; unclear whether that counts the CRLF; and + // we may output two 3 character sequences which we don't want + // to follow with an unescaped CRLF). This scheme will probably + // make mail look a bit awful, but that's fairly standard anyway, + // and it shouldn't degrade. + resort_to_errno(putstr("=\r\n")==EOF, + "error encoding string",EX_IOERR); + encoding_echoed=0; + } + if (encoding_filled==1) { + // Whatever happens, we'll deal with this now + encoding_filled=0; + if (encoding_buffer[0]=='\r') { + if (c==(int)'\n') { + // Output them as is and we're done for now + resort_to_errno(putstr("\r\n")==EOF, + "error encoding string",EX_IOERR); + encoding_echoed=0; + return; + } else { + // Must encode the bare CR and continue as normal + resort_to_errno(put((int)'=')==EOF,"error encoding",EX_IOERR); + encode_hex_byte((unsigned int)'\r'); + encoding_echoed+=3; + } + } else { + // encoding_buffer[0] must be whitespace + if (c==EOF||c==(int)'\r') { + // Must encode it + resort_to_errno(put((int)'=')==EOF,"error encoding",EX_IOERR); + encode_hex_byte((unsigned int)encoding_buffer[0]); + encoding_echoed+=3; + } else { + // It is fine to output it now as something else is coming + resort_to_errno(put( + (int)(unsigned int)encoding_buffer[0])==EOF, + "error encoding",EX_IOERR); + encoding_echoed+=1; + } + } + } + if ((c>=33&&c<=60)||(c>=62&&c<=126)) { + resort_to_errno(put(c)==EOF,"error encoding",EX_IOERR); + ++encoding_echoed; + } else if (c==(int)' '||c==(int)'\t') { + if (encoding_echoed>=55) { + // My concession to readability; since it's likely to be + // a big mess with a 68 character width, we might as well + // break a bit earlier on a nice word boundary. And it'll + // in fact look better if we break with roughly equal size + // lines, assuming they come in at close to 76 characters + // wide, so we might as well make a nice skinny column. + // rather than a ragged one that uses the same amount of + // space. Compromising between the two, then, as some + // formats, like HTML, don't have many hard line breaks + // anyway, is what we get. + resort_to_errno(put(c)==EOF,"error encoding",EX_IOERR); + resort_to_errno(putstr("=\r\n")==EOF, + "error encoding string",EX_IOERR); + encoding_echoed=0; + } else { + // Store it; we may need to encode it if it's at end of line + encoding_filled=1; + encoding_buffer[0]=(char)c; + } + } else if (c==(int)'\r') { + // Store it; '\n' may be coming up + encoding_filled=1; + encoding_buffer[0]='\r'; + } else if (c==EOF) { + // No buffer, and we're done! Reset for another run. + encoding_echoed=0; + } else { + // Anything else must be encoded as a sequence. + resort_to_errno(put((int)'=')==EOF,"error encoding",EX_IOERR); + encode_hex_byte((unsigned int)c); + encoding_echoed+=3; + } + } else if (encoding==base64) { + if (c==EOF) { + // Reset for next run; we won't need it here + encoding_echoed=0; + if (encoding_filled==0) return; + encoding_buffer[encoding_filled]='\0'; + } else { + encoding_buffer[encoding_filled++]=(char)c; + } + if (encoding_filled==3||c==EOF) { + encode_64((((unsigned int)encoding_buffer[0]>>2)&0x3f)); + encode_64((((unsigned int)encoding_buffer[0]&0x03)<<4)| + (((unsigned int)encoding_buffer[1]>>4)&0x0f)); + if (encoding_filled==1) { + resort_to_errno(put((int)'=')==EOF,"error encoding",EX_IOERR); + resort_to_errno(put((int)'=')==EOF,"error encoding",EX_IOERR); + // Reset for next run + encoding_filled=0; + return; + } + encode_64((((unsigned int)encoding_buffer[1]&0x0f)<<2)| + (((unsigned int)encoding_buffer[2]>>6)&0x03)); + if (encoding_filled==2) { + resort_to_errno(put((int)'=')==EOF,"error encoding",EX_IOERR); + // Reset for next run + encoding_filled=0; + return; + } + encode_64((((unsigned int)encoding_buffer[2]&0x3f))); + encoding_echoed+=4; + if (encoding_echoed>=72) { + resort_to_errno(putstr("\r\n")==EOF, + "error encoding string",EX_IOERR); + encoding_echoed=0; + } + encoding_filled=0; + } + } else { + resort_to_exit(true,"internal error: unknown encoding",EX_SOFTWARE); + } +} +static inline void finish_encoding() { + encodechar(EOF); +} +// The function takes an input character c and returns up to four output +// characters (a character will be EOF to indicate no further characters +// to store; note that this doesn't mean there will be no more ever; only +// if EOF is returned when EOF was input does it meant this), and a number +// of characters to remove before adding the aforementioned characters. +static decode_t decodechar(int c) { + int h; + unsigned int b1, b2, b3, b4; + decode_t o; + o.r=0; o.c1=EOF; o.c2=EOF; o.c3=EOF; + if (decoding==unencoded) { + o.c1=c; + return o; + } else if (decoding==quoted_printable) { + // decoding_buffer may hold '=' and maybe a hex digit or a CR. + if (decoding_filled==2) { + // Whatever happens, it's all settled now. + decoding_filled=0; + if (decoding_buffer[1]=='\r') { + if (c==(int)'\n') { return o; } + // Invalid; leave as is--will be encoded later. + o.c1=(int)'='; o.c2=(int)'\r'; o.c3=c; + return o; + } + h=decode_hex(c); + if (h==EOF) { + // Invalid; leave as is--will be encoded later. + o.c1=(int)'='; o.c2=(int)(unsigned int)decoding_buffer[1]; o.c3=c; + return o; + } + // We have a full sequence representing a single character. + o.c1=decode_hex((int)(unsigned int)decoding_buffer[1])*16+h; + return o; + } else if (decoding_filled==1) { + if (c==(int)'\r'||decode_hex(c)!=EOF) { + // Valid character after = + decoding_filled=2; + decoding_buffer[1]=(char)c; + return o; + } + // Invalid; leave as is--will be encoded later. + decoding_filled=0; + o.c1=(int)'='; o.c2=c; + return o; + } else if (decoding_filled==0) { + if (c==(int)'=') { + // The first character can only ever be '=' so we + // don't actually bother to store it; just say it's there. + decoding_white=0; + decoding_filled=1; + return o; + } + // Keep track of whitespace. + if (c==(int)' '||c==(int)'\t') ++decoding_white; + else decoding_white=0; + // Remove trailing whitespace. + if (c==EOF||c==(int)'\r') { o.r=decoding_white; decoding_white=0; } + // Otherwise we just keep it. If it's EOF, we're done. + o.c1=c; + return o; + } else { + warning("internal error: decoding buffer too full"); + return o; + } + } else if (decoding==base64) { + if (c==EOF) { + // Just in case it was corrupted, make sure we're reset + decoding_filled=0; + return o; + } + if (c==(int)'='||decode_64(c)!=EOF) + decoding_buffer[decoding_filled++]=(char)c; + if (decoding_filled==4) { + // We empty it whatever happens here + decoding_filled=0; + b1=(unsigned int)decode_64((int)decoding_buffer[0]); + b2=(unsigned int)decode_64((int)decoding_buffer[1]); + o.c1=(int)(((b1&0x3f)<<2)|((b2>>4)&0x03)); + if (decoding_buffer[2]=='=') return o; + b3=(unsigned int)decode_64((int)decoding_buffer[2]); + o.c2=(int)(((b2&0x0f)<<4)|((b3>>2)&0x0f)); + if (decoding_buffer[3]=='=') return o; + b4=(unsigned int)decode_64((int)decoding_buffer[3]); + o.c3=(int)(((b3&0x03)<<6)|(b4&0x3f)); + } + return o; + } else { + resort_to_exit(true,"internal error: unknown encoding",EX_SOFTWARE); + // Never reached + return o; + } +} +static void decode_lookahead() { + // Decoding will always shrink, so this is quite easy + char * c; + char * cc; + decode_t decoded; + int pos=buffer_read; + int decpos=buffer_read; + resort_to_exit(buffer_read=mem_buffer_end) c-=mem_buffer_size; + cc=c; + while (pos0) { + resort_to_exit(decpos-decoded.r=(int)'0'&&c<=(int)'9') return c-(int)'0'; + if (c>=(int)'A'&&c<=(int)'F') return c-(int)'A'+10; + return EOF; +} +static inline int decode_64(int c) { + if (c>=(int)'A'&&c<=(int)'Z') return c-(int)'A'; + if (c>=(int)'a'&&c<=(int)'z') return c-(int)'a'+26; + if (c>=(int)'0'&&c<=(int)'9') return c-(int)'0'+52; + if (c==(int)'+') return 62; + if (c==(int)'/') return 63; + // if (c==(int)'=') return EOF; + return EOF; +} +static inline void encode_hex_byte(unsigned int h) { + int h1=(int)((h>>4)&0x0f); + int h2=(int)(h&0x0f); + if (h1<10) resort_to_errno(put((int)'0'+h1)==EOF,"error encoding",EX_IOERR); + else if (h1<16) + resort_to_errno(put((int)'A'+h1-10)==EOF,"error encoding",EX_IOERR); + else resort_to_exit(true,"internal error: byte too large",EX_SOFTWARE); + if (h2<10) resort_to_errno(put((int)'0'+h2)==EOF,"error encoding",EX_IOERR); + else if (h2<16) + resort_to_errno(put((int)'A'+h2-10)==EOF,"error encoding",EX_IOERR); + else resort_to_exit(true,"internal error: byte too large",EX_SOFTWARE); +} +static inline void encode_64(unsigned int b) { + if (b<26) + resort_to_errno(put((int)'A'+b)==EOF,"error encoding",EX_IOERR); + else if (b<52) + resort_to_errno(put((int)'a'+b-26)==EOF,"error encoding",EX_IOERR); + else if (b<62) + resort_to_errno(put((int)'0'+b-52)==EOF,"error encoding",EX_IOERR); + else if (b==62) + resort_to_errno(put((int)'+')==EOF,"error encoding",EX_IOERR); + else if (b==63) + resort_to_errno(put((int)'/')==EOF,"error encoding",EX_IOERR); + else resort_to_exit(true, + "internal error: base64 value too large",EX_SOFTWARE); +} + +/* tag: error_functions */ + +// Syslog constants: +// level: LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG +// facility: LOG_MAIL, LOG_DAEMON, LOG_USER, LOG_LOCALn(0-7) + +static inline void * alloc_or_exit(size_t s) /*@allocates result@*/ { + void * m; + m=malloc(s); + if (m==NULL) { +#ifdef USE_STDERR + fprintf(stderr,"foot_filter: %s\n","out of memory"); +#endif +#ifdef USE_SYSLOG + syslog(LOG_ERR|LOG_MAIL,"%s\n","out of memory"); +#endif + exit(EX_OSERR); + } + return m; +} +static inline void /*noreturnwhentrue*/ + resort_to_exit(bool when,const char * message,int status) { + if (when) { +#ifdef USE_STDERR + fprintf(stderr,"foot_filter: %s\n",message); +#endif +#ifdef USE_SYSLOG + syslog(LOG_ERR|LOG_MAIL,"%s\n",message); +#endif + exit(status); + } +} +static inline void /*noreturnwhentrue*/ + resort_to_errno(bool when,const char * message,int status) { + if (when) { +#ifdef USE_STDERR + fprintf(stderr,"foot_filter: %s (%s)\n",message,strerror(errno)); +#endif +#ifdef USE_SYSLOG + syslog(LOG_ERR|LOG_MAIL,"%s (%m)\n",message); +#endif + exit(status); + } +} +static inline void resort_to_warning(bool when,const char * message) { + if (when) warning(message); +} +static inline void warning(const char * message) { +#ifdef USE_STDERR + fprintf(stderr,"foot_filter: %s\n",message); +#endif +#ifdef USE_SYSLOG + syslog(LOG_WARNING|LOG_MAIL,"%s\n",message); +#endif +} + +/* tag: helper_functions */ + +// The program was written following all the specs using CRLF for newlines, +// but we get them from Postfix with LF only, so these wrapper functions +// do the translation in such a way that it can easily be disabled if desired. +static inline int get() { + int c; +#ifdef UNIX_EOL + static bool got_nl=false; + if (got_nl) { + got_nl=false; + return 10; + } +#endif + c=getchar(); +#ifdef UNIX_EOL + if (c==10) { + got_nl=true; + return 13; + } +#endif + return c; +} +static inline int put(int c) { +#ifdef UNIX_EOL + if (c==13) return c; +#endif + return putchar(c); +} +static inline int putstr(const char * s) { + while (*s!='\0') if (put((int)(unsigned int)*s++)==EOF) return EOF; + return 0; +} + +static inline bool case_insensitively_heads(const char * head,const char * buffer) { + const char * s1=head; + const char * s2=buffer; + for (;;) { + if (*s1=='\0') return true; /* for equality return *s2=='\0'; */ + else if (*s2=='\0') return false; + if (*s1!=*s2&& + (*s1<'A'||*s1>'Z'||*s2!=*s1-'A'+'a')&& + (*s2<'A'||*s2>'Z'||*s1!=*s2-'A'+'a')) return false; + ++s1; ++s2; + } +} + diff --git a/misc/move/mlmmj-footer-receive b/misc/move/mlmmj-footer-receive new file mode 100755 index 0000000..66308e3 --- /dev/null +++ b/misc/move/mlmmj-footer-receive @@ -0,0 +1,8 @@ +#!/bin/bash +# +# mlmmj-footer-receive +# +# Adds the footer to incoming message +# + +/usr/bin/foot_filter -P /$1/$2/control/footer-text -H /$1/$2/control/footer-html | /usr/bin/mlmmj-receive -F -L /$1/$2/ diff --git a/misc/smarty/templates_c/085c43aa609e2e11c05d0026471aaaa2a26be865_0.file.error.tpl.php b/misc/smarty/templates_c/085c43aa609e2e11c05d0026471aaaa2a26be865_0.file.error.tpl.php new file mode 100644 index 0000000..1e4fb21 --- /dev/null +++ b/misc/smarty/templates_c/085c43aa609e2e11c05d0026471aaaa2a26be865_0.file.error.tpl.php @@ -0,0 +1,57 @@ +_decodeProperties($_smarty_tpl, array ( + 'version' => '3.1.31', + 'unifunc' => 'content_588b2f056f0767_75452529', + 'has_nocache_code' => false, + 'file_dependency' => + array ( + '085c43aa609e2e11c05d0026471aaaa2a26be865' => + array ( + 0 => '/var/www/html/smarty/templates_ru/error.tpl', + 1 => 1485516367, + 2 => 'file', + ), + ), + 'includes' => + array ( + ), +),false)) { +function content_588b2f056f0767_75452529 (Smarty_Internal_Template $_smarty_tpl) { +?> + + + + + + +
    + tpl_vars['error_code']->value == 1) {?> + Домен может содержать только латинские буквы, точки, дефисы и цифры. + tpl_vars['error_code']->value == 2) {?> + Пароль может содержать только латинские буквы и цифры. + tpl_vars['error_code']->value == 3) {?> + Неверный пароль. + tpl_vars['error_code']->value == 4) {?> + Такой домен не зарегистрирован. + tpl_vars['error_code']->value == 5) {?> + Название рассылки может содержать только латинские буквы, цифры и символы нижнего подчёркивания. + tpl_vars['error_code']->value == 6) {?> + Длина названия рассылки не может превышать 30-ти символов. + tpl_vars['error_code']->value == 7) {?> + Длина префикса не может превышать 128-ти символов. + tpl_vars['error_code']->value == 8) {?> + Длина подписи не может превышать 1024-ти символов. + tpl_vars['error_code']->value == 9) {?> + Среди подписчиков есть некорректный e-mail. + + Неизвестная ошибка. + +
    + + +_decodeProperties($_smarty_tpl, array ( + 'version' => '3.1.31', + 'unifunc' => 'content_5890e7cc706e70_54046020', + 'has_nocache_code' => false, + 'file_dependency' => + array ( + '0b36f150fb6660b8982bed805054bb093a12538d' => + array ( + 0 => '/var/www/html/misc/smarty/templates_en/index.tpl', + 1 => 1485850802, + 2 => 'file', + ), + ), + 'includes' => + array ( + ), +),false)) { +function content_5890e7cc706e70_54046020 (Smarty_Internal_Template $_smarty_tpl) { +?> + + + + +> + function validate_form() + { + var name = document.getElementById('add_list_input').value; + var name = name.toLowerCase(); + + if (name == "") + { + return false; + } + + if (name.length > 30) + { + alert("Название списка рассылки должно содержать не более 30-ти символов."); + return false; + } + + if ( name.match(/[^a-z0-9_]/) ) + { + alert("Название списка рассылки может содержать только латинские буквы, цифры и символы нижнего подчёркивания."); + return false; + } + } + + function confirm_delete() + { + return confirm("Вы действительно хотите удалить список рассылки?"); + } + +> + + + + +
    +
    + Все листы рассылок: +   +
    + + + Добавляйте и удаляйте списки рассыки с помощью данной формы. Вы можете редактировать список, кликнув по его названию. + Письмо в рассылку отправляется на адрес example@tpl_vars['domain']->value;?> +, где example – имя рассылки. + +
    +
    + + smarty->ext->_foreach->init($_smarty_tpl, $_smarty_tpl->tpl_vars['lists']->value, 'list'); +if ($_from !== null) { +foreach ($_from as $_smarty_tpl->tpl_vars['list']->value) { +?> + + + + + + smarty->ext->_foreach->restore($_smarty_tpl, 1); +?> + +
    + • + + tpl_vars['list']->value;?> + + + +
    +
    +
    + +   + +
    +
    +
    + + +_decodeProperties($_smarty_tpl, array ( + 'version' => '3.1.31', + 'unifunc' => 'content_5873c743903c86_86143891', + 'has_nocache_code' => false, + 'file_dependency' => + array ( + '33a0e2a90d50b199feaa83030d7da2c9680cee0a' => + array ( + 0 => '/var/www/html/smarty/templates/edit_list.tpl', + 1 => 1483982647, + 2 => 'file', + ), + ), + 'includes' => + array ( + ), +),false)) { +function content_5873c743903c86_86143891 (Smarty_Internal_Template $_smarty_tpl) { +?> + + + +
    +
    + + Список подписчиков: +

    + +

    + + Тип рассылки: + +

    + Подпись: +

    + +

    + Список модераторов: +

    + +

    + Префикс: + +

    + +

    +
    + + +_decodeProperties($_smarty_tpl, array ( + 'version' => '3.1.31', + 'unifunc' => 'content_588e3ba3c46a98_78970628', + 'has_nocache_code' => false, + 'file_dependency' => + array ( + '53d7e28d1426936f3a3d534fc6fbcdf5d2e8c239' => + array ( + 0 => '/var/www/html/misc/smarty/templates_ru/login.tpl', + 1 => 1485716381, + 2 => 'file', + ), + ), + 'includes' => + array ( + ), +),false)) { +function content_588e3ba3c46a98_78970628 (Smarty_Internal_Template $_smarty_tpl) { +?> + + + + +> + function validate_form() + { + var domain = document.getElementById('domain_input').value; + var password = document.getElementById('password_input').value; + + if (domain == "") + { + alert("Введите домен."); + return false; + } + + if (password == "") + { + alert("Введите пароль."); + return false; + } + if ( domain.match(/[^A-Za-z0-9\-\.]/) ) + { + alert("Домен может содержать только латинские буквы, цифры, точки и дефисы."); + return false; + } + if ( password.match(/[^A-Za-z0-9]/) ) + { + alert("Пароль может содержать только латинские буквы и цифры."); + return false; + } + } + +> + + + +
    +
    +
    +
    +
    + Домен: +
    +
    + +
    +
    +
    +
    + Пароль: +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +_decodeProperties($_smarty_tpl, array ( + 'version' => '3.1.31', + 'unifunc' => 'content_5869ce142bdbc4_61874071', + 'has_nocache_code' => false, + 'file_dependency' => + array ( + '569f7c9bad1e95dec6a51c7c51b98d4db367f54c' => + array ( + 0 => '/var/www/html/smarty/templates/index.tpl', + 1 => 1483329013, + 2 => 'file', + ), + ), + 'includes' => + array ( + ), +),false)) { +function content_5869ce142bdbc4_61874071 (Smarty_Internal_Template $_smarty_tpl) { +?> + + + +
    +

    Список рассылок:

    + smarty->ext->_foreach->init($_smarty_tpl, $_smarty_tpl->tpl_vars['lists']->value, 'list'); +if ($_from !== null) { +foreach ($_from as $_smarty_tpl->tpl_vars['list']->value) { +?> + tpl_vars['list']->value;?> + [Удалить]
    + smarty->ext->_foreach->restore($_smarty_tpl, 1); +?> + +
    +
    + Имя рассылки: +
    +
    + + +_decodeProperties($_smarty_tpl, array ( + 'version' => '3.1.31', + 'unifunc' => 'content_5869d7d16e2888_57689057', + 'has_nocache_code' => false, + 'file_dependency' => + array ( + '617b12a6bfb47e737ab55bf29207ecb92147b506' => + array ( + 0 => '/var/www/html/smarty/templates/login.tpl', + 1 => 1483329039, + 2 => 'file', + ), + ), + 'includes' => + array ( + ), +),false)) { +function content_5869d7d16e2888_57689057 (Smarty_Internal_Template $_smarty_tpl) { +?> + + + +
    +
    + Домен: +

    + Пароль: +

    + +

    +
    + + +_decodeProperties($_smarty_tpl, array ( + 'version' => '3.1.31', + 'unifunc' => 'content_5888e7322dafc3_21846058', + 'has_nocache_code' => false, + 'file_dependency' => + array ( + '6820e2ed7eda3869d8307c28df0b8fa212af00dd' => + array ( + 0 => '/var/www/html/smarty/templates_ru/index.tpl', + 1 => 1485367020, + 2 => 'file', + ), + ), + 'includes' => + array ( + ), +),false)) { +function content_5888e7322dafc3_21846058 (Smarty_Internal_Template $_smarty_tpl) { +?> + + + + +> + function validate_form() + { + var name = document.getElementById('add_list_input').value; + var name = name.toLowerCase(); + + if (name == "") + { + return false; + } + + if (name.length > 30) + { + alert("Название списка рассылки должно содержать не более 30-ти символов."); + return false; + } + + if ( name.match(/[^a-z0-9_]/) ) + { + alert("Название списка рассылки может содержать только латинские буквы, цифры и символы нижнего подчёркивания."); + return false; + } + } + +> + + + + +
    +
    + Все листы рассылок: +   +
    + + + Добавить новый лист рассылки можно введя его имя в поле ввода и нажав кнопку "Добавить". + Название может содержать до 30-ти латинских букв, цифр и символов нижнего подчёркивания. + Удаление листа рассылки производится с помощью пиктограммы в виде мусорной корзины. + Вы можете редактировать лист, кликнув по его названию. + +
    +
    + + smarty->ext->_foreach->init($_smarty_tpl, $_smarty_tpl->tpl_vars['lists']->value, 'list'); +if ($_from !== null) { +foreach ($_from as $_smarty_tpl->tpl_vars['list']->value) { +?> + + + + + + smarty->ext->_foreach->restore($_smarty_tpl, 1); +?> + +
    + • + + tpl_vars['list']->value;?> + + + +
    +
    +
    + +   + +
    +
    +
    + + +_decodeProperties($_smarty_tpl, array ( + 'version' => '3.1.31', + 'unifunc' => 'content_5890e7c59eb3e0_65385644', + 'has_nocache_code' => false, + 'file_dependency' => + array ( + '6a01fa4a4840b59937570aeacc80d159739f4ab1' => + array ( + 0 => '/var/www/html/misc/smarty/templates_en/edit_list.tpl', + 1 => 1485891494, + 2 => 'file', + ), + ), + 'includes' => + array ( + ), +),false)) { +function content_5890e7c59eb3e0_65385644 (Smarty_Internal_Template $_smarty_tpl) { +?> + + + + +> + // Do not use Smarty here + function switch_moderators_form() + { + // Get a selected value + var select = document.getElementById("list_type"); + var selected_value = select.options[select.selectedIndex].value; + + // If selected conference list type, then disable moderators form + if (selected_value == "2") + { + document.getElementById("moderators").disabled = true; + document.getElementById("moderators_header").style.color = "#777777"; + } + else + { + document.getElementById("moderators").disabled = false; + document.getElementById("moderators_header").style.color = "#222222"; + } + } + + function validate_form() + { + var prefix = document.getElementById('prefix').value; + var footer = document.getElementById('footer').value; + var subscribers = document.getElementById('subscribers').value; + var moderators = document.getElementById('moderators').value; + + // Regex for a valid e-mail + var re_email = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + + // Transform subscribers and moderators into array + subscribers = subscribers.split("\n"); + moderators = moderators.split("\n"); + + if (prefix.length > 128) + { + alert("A prefix length can not be longer than 128 characters."); + return false; + } + + if (footer.length > 1024) + { + alert("A footer length can not be longer than 1024 characters."); + return false; + } + + for(var i in subscribers) + { + if ( subscribers[i] != "" && !re_email.test(subscribers[i]) ) + { + alert('Subscriber "' + subscribers[i] + '" (line #' + (parseFloat(i)+1) + ') have incorrect email.'); + return false; + } + } + + for(var i in moderators) + { + if ( moderators[i] != "" && !re_email.test(moderators[i]) ) + { + alert('Moderator "' + moderators[i] + '" (line #' + (parseFloat(i)+1) + ') have incorrect email.'); + return false; + } + } + } + // + +> + + + + +
    +
    + +
    +
    + Subscribers:  +
    + + + Please, provide one email per line. Do not forget add moderators if you + want them able to post into mailing list. + +
    +
    +
    + +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + +
    +
    +
    + + + Moderated list: you assign subscribers and moderators. Messages will be + moderated before publishing.

    + News list: everybody can subscribe without moderator confirmation by sending + an empty email to tpl_vars['list_name']->value;?> ++subscribe@tpl_vars['domain']->value;?> +. Messages into mailing list can + post only moderators.

    + Conference: IRC channel analogue. You assign subscribers, every subscriber + can send messages without moderation. +
    +
    +  List type: +
    +
    + +
    +
    +
    + + + Prefix added to the subject field of each message. + +
    +  Prefix: +
    +
    + +" id="prefix"> +
    + + + +
    +
    +
    +
    + + + Sender of a post will be excluded from the distribution list for + that post so people don't receive copies of their own posts. + +
    + tpl_vars['notmetoo_checked']->value;?> +> + Do not send mails to yourself. +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Moderators:  +
    + + + In case of moderated list messages before publishing will be send to these + emails. In case of news list only these emails can post to mailing list. + In case of conference there are no moderators. + +
    +
    +
    + +
    +
    +
    +
    + + +_decodeProperties($_smarty_tpl, array ( + 'version' => '3.1.31', + 'unifunc' => 'content_5890eda331cf62_87676770', + 'has_nocache_code' => false, + 'file_dependency' => + array ( + '74de2eb1870f65e47bdebde8c12ed050e90ded57' => + array ( + 0 => '/var/www/html/misc/smarty/templates_ru/index.tpl', + 1 => 1485892241, + 2 => 'file', + ), + ), + 'includes' => + array ( + ), +),false)) { +function content_5890eda331cf62_87676770 (Smarty_Internal_Template $_smarty_tpl) { +?> + + + + +> + function validate_form() + { + var name = document.getElementById('add_list_input').value; + var name = name.toLowerCase(); + + if (name == "") + { + return false; + } + + if (name.length > 30) + { + alert("Название списка рассылки должно содержать не более 30-ти символов."); + return false; + } + + if ( name.match(/[^a-z0-9_]/) ) + { + alert("Название списка рассылки может содержать только латинские буквы, цифры и символы нижнего подчёркивания."); + return false; + } + } + + function confirm_delete() + { + return confirm("Вы действительно хотите удалить список рассылки?"); + } + +> + + + + +
    +
    + Все листы рассылок: +   +
    + + + Добавляйте и удаляйте списки рассылки с помощью данной формы. Вы можете редактировать список, кликнув по его названию. + Письмо в рассылку отправляется на адрес example@tpl_vars['domain']->value;?> +, где example – имя рассылки. + +
    +
    + + smarty->ext->_foreach->init($_smarty_tpl, $_smarty_tpl->tpl_vars['lists']->value, 'list'); +if ($_from !== null) { +foreach ($_from as $_smarty_tpl->tpl_vars['list']->value) { +?> + + + + + + smarty->ext->_foreach->restore($_smarty_tpl, 1); +?> + +
    + • + + tpl_vars['list']->value;?> + + + +
    +
    +
    + +   + +
    +
    +
    + + +_decodeProperties($_smarty_tpl, array ( + 'version' => '3.1.31', + 'unifunc' => 'content_589041dd3259d1_17665347', + 'has_nocache_code' => false, + 'file_dependency' => + array ( + '9f1de615a6b90a928a6f77b08d01bdc17781ec0b' => + array ( + 0 => '/var/www/html/misc/smarty/templates_ru/edit_list.tpl', + 1 => 1485849048, + 2 => 'file', + ), + ), + 'includes' => + array ( + ), +),false)) { +function content_589041dd3259d1_17665347 (Smarty_Internal_Template $_smarty_tpl) { +?> + + + + +> + // Do not use Smarty here + function switch_moderators_form() + { + // Get a selected value + var select = document.getElementById("list_type"); + var selected_value = select.options[select.selectedIndex].value; + + // If selected conference list type, then disable moderators form + if (selected_value == "2") + { + document.getElementById("moderators").disabled = true; + document.getElementById("moderators_header").style.color = "#777777"; + } + else + { + document.getElementById("moderators").disabled = false; + document.getElementById("moderators_header").style.color = "#222222"; + } + } + + function validate_form() + { + var prefix = document.getElementById('prefix').value; + var footer = document.getElementById('footer').value; + var subscribers = document.getElementById('subscribers').value; + var moderators = document.getElementById('moderators').value; + + // Regex for a valid e-mail + var re_email = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + + // Transform subscribers and moderators into array + subscribers = subscribers.split("\n"); + moderators = moderators.split("\n"); + + if (prefix.length > 128) + { + alert("Длина префикса не должна превышать 128-ми символов."); + return false; + } + + if (footer.length > 1024) + { + alert("Длина подписи не должна превышать 1024-ёх символов."); + return false; + } + + for(var i in subscribers) + { + if ( subscribers[i] != "" && !re_email.test(subscribers[i]) ) + { + alert('Подписчик "' + subscribers[i] + '" (строка №' + (parseFloat(i)+1) + ') является невалидным адресом электронной почты.'); + return false; + } + } + + for(var i in moderators) + { + if ( moderators[i] != "" && !re_email.test(moderators[i]) ) + { + alert('Модератор "' + moderators[i] + '" (строка №' + (parseFloat(i)+1) + ') является невалидным адресом электронной почты.'); + return false; + } + } + } + // + +> + + + + +
    +
    + +
    +
    + Список подписчиков:  +
    + + + Добавляйте по одному почтовому адресу в каждой строке. Не забудьте добавить + модераторов, если хотите, чтобы они также могли писать в рассылку. + +
    +
    +
    + +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + +
    +
    +
    + + + Модерируемая рассылка: подписчиков устанавливаете Вы, сообщения + от обычных участников проходят модерацию.

    + Новостная рассылка: подписаться может кто угодно, отправив пустое письмо + на tpl_vars['list_name']->value;?> ++subscribe@tpl_vars['domain']->value;?> +, при этом подтверждение модератора не требуется. + Письма в рассылку могут отправлять только модераторы.

    + Конференция: аналог канала в IRC. Подписчиков устанавливаете Вы, + сообщения может отправлять любой подписчик без модерации. +
    +
    +  Тип рассылки: +
    +
    + +
    +
    +
    + + + Текст, добавляемый в начало заголовка каждого сообщения рассылки. + +
    +  Префикс: +
    +
    + +" id="prefix"> +
    + + + +
    +
    +
    +
    + + + Отправитель будет исключён из списка рассылки для своего сообщения. + Это означает, что ему не будут приходить копии своих сообщений. + +
    + tpl_vars['notmetoo_checked']->value;?> +> + Не отправлять копию своих сообщений. +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Список модераторов:  +
    + + + Для модерируемой рассылки на эти email будут отправляться письма перед их + опубликованием в рассылку. Для новостной рассылки только эти адреса могут + могут писать в рассылку. Для конференции модераторы не предумотрены. + +
    +
    +
    + +
    +
    +
    +
    + + +_decodeProperties($_smarty_tpl, array ( + 'version' => '3.1.31', + 'unifunc' => 'content_588dcdb0c8d950_81798250', + 'has_nocache_code' => false, + 'file_dependency' => + array ( + 'aa00a58a15f019bd5c0612378cd3ff5f5b6adcae' => + array ( + 0 => '/var/www/html/misc/smarty/templates_ru/error.tpl', + 1 => 1485688232, + 2 => 'file', + ), + ), + 'includes' => + array ( + ), +),false)) { +function content_588dcdb0c8d950_81798250 (Smarty_Internal_Template $_smarty_tpl) { +?> + + + + + + +
    + tpl_vars['error_code']->value == 1) {?> + Домен может содержать только латинские буквы, точки, дефисы и цифры. + tpl_vars['error_code']->value == 2) {?> + Пароль может содержать только латинские буквы и цифры. + tpl_vars['error_code']->value == 3) {?> + Неверный пароль. + tpl_vars['error_code']->value == 4) {?> + Такой домен не зарегистрирован. + tpl_vars['error_code']->value == 5) {?> + Название рассылки может содержать только латинские буквы, цифры и символы нижнего подчёркивания. + tpl_vars['error_code']->value == 6) {?> + Длина названия рассылки не может превышать 30-ти символов. + tpl_vars['error_code']->value == 7) {?> + Длина префикса не может превышать 128-ти символов. + tpl_vars['error_code']->value == 8) {?> + Длина подписи не может превышать 1024-ти символов. + tpl_vars['error_code']->value == 9) {?> + Среди подписчиков есть некорректный e-mail. + tpl_vars['error_code']->value == 10) {?> + Среди модераторов есть некорректный e-mail. + + Неизвестная ошибка. + +
    + + +_decodeProperties($_smarty_tpl, array ( + 'version' => '3.1.31', + 'unifunc' => 'content_58878934506f48_92126530', + 'has_nocache_code' => false, + 'file_dependency' => + array ( + 'ccdf76791105fd7c77f366b69d3a18e59c5c2b67' => + array ( + 0 => '/var/www/html/smarty/templates_ru/login.tpl', + 1 => 1485277488, + 2 => 'file', + ), + ), + 'includes' => + array ( + ), +),false)) { +function content_58878934506f48_92126530 (Smarty_Internal_Template $_smarty_tpl) { +?> + + + + +> + function validate_form() + { + var domain = document.getElementById('domain_input').value; + var password = document.getElementById('password_input').value; + + if (domain == "") + { + alert("Введите домен."); + return false; + } + + if (password == "") + { + alert("Введите пароль."); + return false; + } + } + +> + + + +
    +
    +
    +
    +
    + Домен: +
    +
    + +
    +
    +
    +
    + Пароль: +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + +_decodeProperties($_smarty_tpl, array ( + 'version' => '3.1.31', + 'unifunc' => 'content_588b8aa37593d1_30807545', + 'has_nocache_code' => false, + 'file_dependency' => + array ( + 'fb61d69ef038dcd720aa2782f00f9ee0f411ff19' => + array ( + 0 => '/var/www/html/smarty/templates_ru/edit_list.tpl', + 1 => 1485537120, + 2 => 'file', + ), + ), + 'includes' => + array ( + ), +),false)) { +function content_588b8aa37593d1_30807545 (Smarty_Internal_Template $_smarty_tpl) { +?> + + + + +> + // Do not use Smarty here + function switch_moderators_form() + { + // Get a selected value + var select = document.getElementById("list_type"); + var selected_value = select.options[select.selectedIndex].value; + + // If selected conference list type, then disable moderators form + if (selected_value == "2") + { + document.getElementById("moderators").disabled = true; + document.getElementById("moderators_header").style.color = "#777777"; + } + else + { + document.getElementById("moderators").disabled = false; + document.getElementById("moderators_header").style.color = "#222222"; + } + } + + function validate_form() + { + var prefix = document.getElementById('prefix').value; + var footer = document.getElementById('footer').value; + var subscribers = document.getElementById('subscribers').value; + var moderators = document.getElementById('moderators').value; + + // Regex for a valid e-mail + var re_email = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + + // Transform subscribers and moderators into array + subscribers = subscribers.split("\n"); + moderators = moderators.split("\n"); + + if (prefix.length > 128) + { + alert("Длина префикса не должна превышать 128-ми символов."); + return false; + } + + if (footer.length > 1024) + { + alert("Длина подписи не должна превышать 1024-ёх символов."); + return false; + } + + for(var i in subscribers) + { + if ( subscribers[i] != "" && !re_email.test(subscribers[i]) ) + { + alert('Подписчик "' + subscribers[i] + '" (строка №' + (parseFloat(i)+1) + ') является невалидным адресом электронной почты.'); + return false; + } + } + + for(var i in moderators) + { + if ( moderators[i] != "" && !re_email.test(moderators[i]) ) + { + alert('Модератор "' + moderators[i] + '" (строка №' + (parseFloat(i)+1) + ') является невалидным адресом электронной почты.'); + return false; + } + } + } + // + +> + + + + +
    +
    + +
    +
    + Список подписчиков:  +
    + + + Добавляйте по одному почтовому адресу в каждой строке. Не забудьте добавить + модераторов, если хотите, чтобы они также могли писать в рассылку. + +
    +
    +
    + +
    +
    +
    +
    +
    + + + + + + + + + + + + + +
    +
    +
    + + + Модерируемая рассылка: подписчиков устанавливаете Вы, сообщения + от обычных участников проходят модерацию.

    + Новостная рассылка: подписаться может кто угодно, отправив пустое письмо + на tpl_vars['list_name']->value;?> ++subscribe@tpl_vars['domain']->value;?> +, при этом подтверждение модератора не требуется. + Письма в рассылку могут отправлять только модераторы.

    + Конференция: аналог канала в IRC. Подписчиков устанавливаете Вы, + сообщения может отправлять любой подписчик без модерации. +
    +
    +  Тип рассылки: +
    +
    + +
    +
    +
    + + + Текст, добавляемый в начало заголовка каждого сообщения рассылки. + +
    +  Префикс: +
    +
    + +" id="prefix"> +
    + + + +
    +
    +
    + +
    +
    +
    +
    +
    + Список модераторов:  +
    + + + Для модерируемой рассылки на эти email будут отправляться письма перед их + опубликованием в рассылку. Для новостной рассылки только эти адреса могут + могут писать в рассылку. Для конференции модераторы не предумотрены. + +
    +
    +
    + +
    +
    +
    +
    + + + + + + + + + + +
    +
    + +
    +
    + Subscribers:  +
    + + + Please, provide one email per line. Do not forget add moderators if you + want them able to post into mailing list. + +
    +
    +
    + +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + +
    +
    +
    + + + Moderated list: you assign subscribers and moderators. Messages will be + moderated before publishing.

    + News list: everybody can subscribe without moderator confirmation by sending + an empty email to {$list_name}+subscribe@{$domain}. Messages into mailing list can + post only moderators.

    + Conference: IRC channel analogue. You assign subscribers, every subscriber + can send messages without moderation. +
    +
    +  List type: +
    +
    + +
    +
    +
    + + + Prefix added to the subject field of each message. + +
    +  Prefix: +
    +
    + +
    + + + +
    +
    +
    +
    + + + Sender of a post will be excluded from the distribution list for + that post so people don't receive copies of their own posts. + +
    + + Do not send mails to yourself. +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Moderators:  +
    + + + In case of moderated list messages before publishing will be send to these + emails. In case of news list only these emails can post to mailing list. + In case of conference there are no moderators. + +
    +
    +
    + +
    +
    +
    +
    + + diff --git a/misc/smarty/templates_en/error.tpl b/misc/smarty/templates_en/error.tpl new file mode 100644 index 0000000..bb7020a --- /dev/null +++ b/misc/smarty/templates_en/error.tpl @@ -0,0 +1,33 @@ + + + + + + +
    + {if $error_code == 1} + Domain can contain only english letters, dots, hyphens and digits. + {elseif $error_code == 2} + Password can contain only english letters and digits. + {elseif $error_code == 3} + Incorrect password. + {elseif $error_code == 4} + There is no such domain. + {elseif $error_code == 5} + Mailing list name can contain only english letters, digits and undercores. + {elseif $error_code == 6} + The length of a list name can not exceed 30 characters. + {elseif $error_code == 7} + The length of a prefix can not exceed 128 characters. + {elseif $error_code == 8} + The length of a footer can not exceed 1024 characters. + {elseif $error_code == 9} + There is an incorrect email in the subscribers list. + {elseif $error_code == 10} + There is an incorrect email in the moderators list. + {else} + Unknown error. + {/if} +
    + + diff --git a/misc/smarty/templates_en/index.tpl b/misc/smarty/templates_en/index.tpl new file mode 100644 index 0000000..9c6d841 --- /dev/null +++ b/misc/smarty/templates_en/index.tpl @@ -0,0 +1,80 @@ + + + + + + + + +
    +
    + Mailing lists: +   +
    + + + You can add and delete mailing lists on this page. To edit list click on its name. + To post message into mailing list send mail to example@{$domain}, where "example" is the list name. + +
    +
    + + {foreach $lists as $list} + + + + + + {/foreach} +
    + • + + {$list} + + +
    +
    +
    + +   + +
    +
    +
    + + diff --git a/misc/smarty/templates_en/login.tpl b/misc/smarty/templates_en/login.tpl new file mode 100644 index 0000000..f8acf70 --- /dev/null +++ b/misc/smarty/templates_en/login.tpl @@ -0,0 +1,62 @@ + + + + + + + +
    +
    +
    +
    +
    + Domain: +
    +
    + +
    +
    +
    +
    + Password: +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + diff --git a/misc/smarty/templates_ru/edit_list.tpl b/misc/smarty/templates_ru/edit_list.tpl new file mode 100644 index 0000000..9cd9ea7 --- /dev/null +++ b/misc/smarty/templates_ru/edit_list.tpl @@ -0,0 +1,213 @@ + + + + + + + + +
    +
    + +
    +
    + Список подписчиков:  +
    + + + Добавляйте по одному почтовому адресу в каждой строке. Не забудьте добавить + модераторов, если хотите, чтобы они также могли писать в рассылку. + +
    +
    +
    + +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + +
    +
    +
    + + + Модерируемая рассылка: подписчиков устанавливаете Вы, сообщения + от обычных участников проходят модерацию.

    + Новостная рассылка: подписаться может кто угодно, отправив пустое письмо + на {$list_name}+subscribe@{$domain}, при этом подтверждение модератора не требуется. + Письма в рассылку могут отправлять только модераторы.

    + Конференция: аналог канала в IRC. Подписчиков устанавливаете Вы, + сообщения может отправлять любой подписчик без модерации. +
    +
    +  Тип рассылки: +
    +
    + +
    +
    +
    + + + Текст, добавляемый в начало заголовка каждого сообщения рассылки. + +
    +  Префикс: +
    +
    + +
    + + + +
    +
    +
    +
    + + + Отправитель будет исключён из списка рассылки для своего сообщения. + Это означает, что ему не будут приходить копии своих сообщений. + +
    + + Не отправлять копию своих сообщений. +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + Список модераторов:  +
    + + + Для модерируемой рассылки на эти email будут отправляться письма перед их + опубликованием в рассылку. Для новостной рассылки только эти адреса могут + могут писать в рассылку. Для конференции модераторы не предумотрены. + +
    +
    +
    + +
    +
    +
    +
    + + diff --git a/misc/smarty/templates_ru/error.tpl b/misc/smarty/templates_ru/error.tpl new file mode 100644 index 0000000..a9b3bb2 --- /dev/null +++ b/misc/smarty/templates_ru/error.tpl @@ -0,0 +1,33 @@ + + + + + + +
    + {if $error_code == 1} + Домен может содержать только латинские буквы, точки, дефисы и цифры. + {elseif $error_code == 2} + Пароль может содержать только латинские буквы и цифры. + {elseif $error_code == 3} + Неверный пароль. + {elseif $error_code == 4} + Такой домен не зарегистрирован. + {elseif $error_code == 5} + Название рассылки может содержать только латинские буквы, цифры и символы нижнего подчёркивания. + {elseif $error_code == 6} + Длина названия рассылки не может превышать 30-ти символов. + {elseif $error_code == 7} + Длина префикса не может превышать 128-ти символов. + {elseif $error_code == 8} + Длина подписи не может превышать 1024-ти символов. + {elseif $error_code == 9} + Среди подписчиков есть некорректный e-mail. + {elseif $error_code == 10} + Среди модераторов есть некорректный e-mail. + {else} + Неизвестная ошибка. + {/if} +
    + + diff --git a/misc/smarty/templates_ru/index.tpl b/misc/smarty/templates_ru/index.tpl new file mode 100644 index 0000000..ce4cd01 --- /dev/null +++ b/misc/smarty/templates_ru/index.tpl @@ -0,0 +1,80 @@ + + + + + + + + +
    +
    + Все листы рассылок: +   +
    + + + Добавляйте и удаляйте списки рассылки с помощью данной формы. Вы можете редактировать список, кликнув по его названию. + Письмо в рассылку отправляется на адрес example@{$domain}, где example – имя рассылки. + +
    +
    + + {foreach $lists as $list} + + + + + + {/foreach} +
    + • + + {$list} + + +
    +
    +
    + +   + +
    +
    +
    + + diff --git a/misc/smarty/templates_ru/login.tpl b/misc/smarty/templates_ru/login.tpl new file mode 100644 index 0000000..b2c9613 --- /dev/null +++ b/misc/smarty/templates_ru/login.tpl @@ -0,0 +1,62 @@ + + + + + + + +
    +
    +
    +
    +
    + Домен: +
    +
    + +
    +
    +
    +
    + Пароль: +
    +
    + +
    +
    +
    + +
    +
    +
    +
    + + diff --git a/misc/smarty_libs/Autoloader.php b/misc/smarty_libs/Autoloader.php new file mode 100644 index 0000000..d3b039c --- /dev/null +++ b/misc/smarty_libs/Autoloader.php @@ -0,0 +1,110 @@ + 'Smarty.class.php', 'smartybc' => 'SmartyBC.class.php',); + + /** + * Registers Smarty_Autoloader backward compatible to older installations. + * + * @param bool $prepend Whether to prepend the autoloader or not. + */ + public static function registerBC($prepend = false) + { + /** + * register the class autoloader + */ + if (!defined('SMARTY_SPL_AUTOLOAD')) { + define('SMARTY_SPL_AUTOLOAD', 0); + } + if (SMARTY_SPL_AUTOLOAD && + set_include_path(get_include_path() . PATH_SEPARATOR . SMARTY_SYSPLUGINS_DIR) !== false + ) { + $registeredAutoLoadFunctions = spl_autoload_functions(); + if (!isset($registeredAutoLoadFunctions[ 'spl_autoload' ])) { + spl_autoload_register(); + } + } else { + self::register($prepend); + } + } + + /** + * Registers Smarty_Autoloader as an SPL autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not. + */ + public static function register($prepend = false) + { + self::$SMARTY_DIR = defined('SMARTY_DIR') ? SMARTY_DIR : dirname(__FILE__) . DIRECTORY_SEPARATOR; + self::$SMARTY_SYSPLUGINS_DIR = defined('SMARTY_SYSPLUGINS_DIR') ? SMARTY_SYSPLUGINS_DIR : + self::$SMARTY_DIR . 'sysplugins' . DIRECTORY_SEPARATOR; + if (version_compare(phpversion(), '5.3.0', '>=')) { + spl_autoload_register(array(__CLASS__, 'autoload'), true, $prepend); + } else { + spl_autoload_register(array(__CLASS__, 'autoload')); + } + } + + /** + * Handles auto loading of classes. + * + * @param string $class A class name. + */ + public static function autoload($class) + { + if ($class[ 0 ] !== 'S' && strpos($class, 'Smarty') !== 0) { + return; + } + $_class = strtolower($class); + if (isset(self::$rootClasses[ $_class ])) { + $file = self::$SMARTY_DIR . self::$rootClasses[ $_class ]; + if (is_file($file)) { + include $file; + } + } else { + $file = self::$SMARTY_SYSPLUGINS_DIR . $_class . '.php'; + if (is_file($file)) { + include $file; + } + } + return; + } +} diff --git a/misc/smarty_libs/Smarty.class.php b/misc/smarty_libs/Smarty.class.php new file mode 100644 index 0000000..57eedfb --- /dev/null +++ b/misc/smarty_libs/Smarty.class.php @@ -0,0 +1,1549 @@ + + * @author Uwe Tews + * @author Rodney Rehm + * @package Smarty + * @version 3.1.31 + */ + +/** + * set SMARTY_DIR to absolute path to Smarty library files. + * Sets SMARTY_DIR only if user application has not already defined it. + */ +if (!defined('SMARTY_DIR')) { + define('SMARTY_DIR', dirname(__FILE__) . DIRECTORY_SEPARATOR); +} + +/** + * set SMARTY_SYSPLUGINS_DIR to absolute path to Smarty internal plugins. + * Sets SMARTY_SYSPLUGINS_DIR only if user application has not already defined it. + */ +if (!defined('SMARTY_SYSPLUGINS_DIR')) { + define('SMARTY_SYSPLUGINS_DIR', SMARTY_DIR . 'sysplugins' . DIRECTORY_SEPARATOR); +} +if (!defined('SMARTY_PLUGINS_DIR')) { + define('SMARTY_PLUGINS_DIR', SMARTY_DIR . 'plugins' . DIRECTORY_SEPARATOR); +} +if (!defined('SMARTY_MBSTRING')) { + define('SMARTY_MBSTRING', function_exists('mb_get_info')); +} +if (!defined('SMARTY_RESOURCE_CHAR_SET')) { + // UTF-8 can only be done properly when mbstring is available! + /** + * @deprecated in favor of Smarty::$_CHARSET + */ + define('SMARTY_RESOURCE_CHAR_SET', SMARTY_MBSTRING ? 'UTF-8' : 'ISO-8859-1'); +} +if (!defined('SMARTY_RESOURCE_DATE_FORMAT')) { + /** + * @deprecated in favor of Smarty::$_DATE_FORMAT + */ + define('SMARTY_RESOURCE_DATE_FORMAT', '%b %e, %Y'); +} + +/** + * Load Smarty_Autoloader + */ +if (!class_exists('Smarty_Autoloader')) { + include __DIR__ . '/bootstrap.php'; +} + +/** + * Load always needed external class files + */ +require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_data.php'; +require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_extension_handler.php'; +require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_templatebase.php'; +require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_template.php'; +require_once SMARTY_SYSPLUGINS_DIR . 'smarty_resource.php'; +require_once SMARTY_SYSPLUGINS_DIR . 'smarty_variable.php'; +require_once SMARTY_SYSPLUGINS_DIR . 'smarty_template_source.php'; +require_once SMARTY_SYSPLUGINS_DIR . 'smarty_template_resource_base.php'; +require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_resource_file.php'; + +/** + * This is the main Smarty class + * + * @package Smarty + * + * The following methods will be dynamically loaded by the extension handler when they are called. + * They are located in a corresponding Smarty_Internal_Method_xxxx class + * + * @method int clearAllCache(int $exp_time = null, string $type = null) + * @method int clearCache(string $template_name, string $cache_id = null, string $compile_id = null, int $exp_time = null, string $type = null) + * @method int compileAllTemplates(string $extension = '.tpl', bool $force_compile = false, int $time_limit = 0, int $max_errors = null) + * @method int compileAllConfig(string $extension = '.conf', bool $force_compile = false, int $time_limit = 0, int $max_errors = null) + * @method int clearCompiledTemplate($resource_name = null, $compile_id = null, $exp_time = null) + */ +class Smarty extends Smarty_Internal_TemplateBase +{ + /**#@+ + * constant definitions + */ + + /** + * smarty version + */ + const SMARTY_VERSION = '3.1.31'; + + /** + * define variable scopes + */ + const SCOPE_LOCAL = 1; + + const SCOPE_PARENT = 2; + + const SCOPE_TPL_ROOT = 4; + + const SCOPE_ROOT = 8; + + const SCOPE_SMARTY = 16; + + const SCOPE_GLOBAL = 32; + + /** + * define caching modes + */ + const CACHING_OFF = 0; + + const CACHING_LIFETIME_CURRENT = 1; + + const CACHING_LIFETIME_SAVED = 2; + + /** + * define constant for clearing cache files be saved expiration dates + */ + const CLEAR_EXPIRED = - 1; + + /** + * define compile check modes + */ + const COMPILECHECK_OFF = 0; + + const COMPILECHECK_ON = 1; + + const COMPILECHECK_CACHEMISS = 2; + + /** + * define debug modes + */ + const DEBUG_OFF = 0; + + const DEBUG_ON = 1; + + const DEBUG_INDIVIDUAL = 2; + + /** + * modes for handling of "" tags in templates. + */ + const PHP_PASSTHRU = 0; //-> print tags as plain text + + const PHP_QUOTE = 1; //-> escape tags as entities + + const PHP_REMOVE = 2; //-> escape tags as entities + + const PHP_ALLOW = 3; //-> escape tags as entities + + /** + * filter types + */ + const FILTER_POST = 'post'; + + const FILTER_PRE = 'pre'; + + const FILTER_OUTPUT = 'output'; + + const FILTER_VARIABLE = 'variable'; + + /** + * plugin types + */ + const PLUGIN_FUNCTION = 'function'; + + const PLUGIN_BLOCK = 'block'; + + const PLUGIN_COMPILER = 'compiler'; + + const PLUGIN_MODIFIER = 'modifier'; + + const PLUGIN_MODIFIERCOMPILER = 'modifiercompiler'; + + /** + * Resource caching modes + * (not used since 3.1.30) + */ + const RESOURCE_CACHE_OFF = 0; + + const RESOURCE_CACHE_AUTOMATIC = 1; // cache template objects by rules + + const RESOURCE_CACHE_TEMPLATE = 2; // cache all template objects + + const RESOURCE_CACHE_ON = 4; // cache source and compiled resources + + /**#@-*/ + + /** + * assigned global tpl vars + */ + public static $global_tpl_vars = array(); + + /** + * error handler returned by set_error_handler() in Smarty::muteExpectedErrors() + */ + public static $_previous_error_handler = null; + + /** + * contains directories outside of SMARTY_DIR that are to be muted by muteExpectedErrors() + */ + public static $_muted_directories = array(); + + /** + * Flag denoting if Multibyte String functions are available + */ + public static $_MBSTRING = SMARTY_MBSTRING; + + /** + * The character set to adhere to (e.g. "UTF-8") + */ + public static $_CHARSET = SMARTY_RESOURCE_CHAR_SET; + + /** + * The date format to be used internally + * (accepts date() and strftime()) + */ + public static $_DATE_FORMAT = SMARTY_RESOURCE_DATE_FORMAT; + + /** + * Flag denoting if PCRE should run in UTF-8 mode + */ + public static $_UTF8_MODIFIER = 'u'; + + /** + * Flag denoting if operating system is windows + */ + public static $_IS_WINDOWS = false; + + /**#@+ + * variables + */ + + /** + * auto literal on delimiters with whitespace + * + * @var boolean + */ + public $auto_literal = true; + + /** + * display error on not assigned variables + * + * @var boolean + */ + public $error_unassigned = false; + + /** + * look up relative file path in include_path + * + * @var boolean + */ + public $use_include_path = false; + + /** + * template directory + * + * @var array + */ + protected $template_dir = array('./templates/'); + + /** + * flags for normalized template directory entries + * + * @var array + */ + protected $_processedTemplateDir = array(); + + /** + * flag if template_dir is normalized + * + * @var bool + */ + public $_templateDirNormalized = false; + + /** + * joined template directory string used in cache keys + * + * @var string + */ + public $_joined_template_dir = null; + + /** + * config directory + * + * @var array + */ + protected $config_dir = array('./configs/'); + + /** + * flags for normalized template directory entries + * + * @var array + */ + protected $_processedConfigDir = array(); + + /** + * flag if config_dir is normalized + * + * @var bool + */ + public $_configDirNormalized = false; + + /** + * joined config directory string used in cache keys + * + * @var string + */ + public $_joined_config_dir = null; + + /** + * default template handler + * + * @var callable + */ + public $default_template_handler_func = null; + + /** + * default config handler + * + * @var callable + */ + public $default_config_handler_func = null; + + /** + * default plugin handler + * + * @var callable + */ + public $default_plugin_handler_func = null; + + /** + * compile directory + * + * @var string + */ + protected $compile_dir = './templates_c/'; + + /** + * flag if template_dir is normalized + * + * @var bool + */ + public $_compileDirNormalized = false; + + /** + * plugins directory + * + * @var array + */ + protected $plugins_dir = array(); + + /** + * flag if plugins_dir is normalized + * + * @var bool + */ + public $_pluginsDirNormalized = false; + + /** + * cache directory + * + * @var string + */ + protected $cache_dir = './cache/'; + + /** + * flag if template_dir is normalized + * + * @var bool + */ + public $_cacheDirNormalized = false; + + /** + * force template compiling? + * + * @var boolean + */ + public $force_compile = false; + + /** + * check template for modifications? + * + * @var boolean + */ + public $compile_check = true; + + /** + * use sub dirs for compiled/cached files? + * + * @var boolean + */ + public $use_sub_dirs = false; + + /** + * allow ambiguous resources (that are made unique by the resource handler) + * + * @var boolean + */ + public $allow_ambiguous_resources = false; + + /** + * merge compiled includes + * + * @var boolean + */ + public $merge_compiled_includes = false; + + /* + * flag for behaviour when extends: resource and {extends} tag are used simultaneous + * if false disable execution of {extends} in templates called by extends resource. + * (behaviour as versions < 3.1.28) + * + * @var boolean + */ + public $extends_recursion = true; + + /** + * force cache file creation + * + * @var boolean + */ + public $force_cache = false; + + /** + * template left-delimiter + * + * @var string + */ + public $left_delimiter = "{"; + + /** + * template right-delimiter + * + * @var string + */ + public $right_delimiter = "}"; + + /**#@+ + * security + */ + /** + * class name + * This should be instance of Smarty_Security. + * + * @var string + * @see Smarty_Security + */ + public $security_class = 'Smarty_Security'; + + /** + * implementation of security class + * + * @var Smarty_Security + */ + public $security_policy = null; + + /** + * controls handling of PHP-blocks + * + * @var integer + */ + public $php_handling = self::PHP_PASSTHRU; + + /** + * controls if the php template file resource is allowed + * + * @var bool + */ + public $allow_php_templates = false; + + /**#@-*/ + /** + * debug mode + * Setting this to true enables the debug-console. + * + * @var boolean + */ + public $debugging = false; + + /** + * This determines if debugging is enable-able from the browser. + *
      + *
    • NONE => no debugging control allowed
    • + *
    • URL => enable debugging when SMARTY_DEBUG is found in the URL.
    • + *
    + * + * @var string + */ + public $debugging_ctrl = 'NONE'; + + /** + * Name of debugging URL-param. + * Only used when $debugging_ctrl is set to 'URL'. + * The name of the URL-parameter that activates debugging. + * + * @var string + */ + public $smarty_debug_id = 'SMARTY_DEBUG'; + + /** + * Path of debug template. + * + * @var string + */ + public $debug_tpl = null; + + /** + * When set, smarty uses this value as error_reporting-level. + * + * @var int + */ + public $error_reporting = null; + + /**#@+ + * config var settings + */ + + /** + * Controls whether variables with the same name overwrite each other. + * + * @var boolean + */ + public $config_overwrite = true; + + /** + * Controls whether config values of on/true/yes and off/false/no get converted to boolean. + * + * @var boolean + */ + public $config_booleanize = true; + + /** + * Controls whether hidden config sections/vars are read from the file. + * + * @var boolean + */ + public $config_read_hidden = false; + + /**#@-*/ + + /**#@+ + * resource locking + */ + + /** + * locking concurrent compiles + * + * @var boolean + */ + public $compile_locking = true; + + /** + * Controls whether cache resources should use locking mechanism + * + * @var boolean + */ + public $cache_locking = false; + + /** + * seconds to wait for acquiring a lock before ignoring the write lock + * + * @var float + */ + public $locking_timeout = 10; + + /**#@-*/ + + /** + * resource type used if none given + * Must be an valid key of $registered_resources. + * + * @var string + */ + public $default_resource_type = 'file'; + + /** + * caching type + * Must be an element of $cache_resource_types. + * + * @var string + */ + public $caching_type = 'file'; + + /** + * config type + * + * @var string + */ + public $default_config_type = 'file'; + + /** + * check If-Modified-Since headers + * + * @var boolean + */ + public $cache_modified_check = false; + + /** + * registered plugins + * + * @var array + */ + public $registered_plugins = array(); + + /** + * registered objects + * + * @var array + */ + public $registered_objects = array(); + + /** + * registered classes + * + * @var array + */ + public $registered_classes = array(); + + /** + * registered filters + * + * @var array + */ + public $registered_filters = array(); + + /** + * registered resources + * + * @var array + */ + public $registered_resources = array(); + + /** + * registered cache resources + * + * @var array + */ + public $registered_cache_resources = array(); + + /** + * autoload filter + * + * @var array + */ + public $autoload_filters = array(); + + /** + * default modifier + * + * @var array + */ + public $default_modifiers = array(); + + /** + * autoescape variable output + * + * @var boolean + */ + public $escape_html = false; + + /** + * start time for execution time calculation + * + * @var int + */ + public $start_time = 0; + + /** + * required by the compiler for BC + * + * @var string + */ + public $_current_file = null; + + /** + * internal flag to enable parser debugging + * + * @var bool + */ + public $_parserdebug = false; + + /** + * This object type (Smarty = 1, template = 2, data = 4) + * + * @var int + */ + public $_objType = 1; + + /** + * Debug object + * + * @var Smarty_Internal_Debug + */ + public $_debug = null; + + /** + * Directory separator + * + * @var string + */ + public $ds = DIRECTORY_SEPARATOR; + + /** + * removed properties + * + * @var string[] + */ + private $obsoleteProperties = array('resource_caching', 'template_resource_caching', 'direct_access_security', + '_dir_perms', '_file_perms', 'plugin_search_order', + 'inheritance_merge_compiled_includes', 'resource_cache_mode',); + + /** + * List of private properties which will call getter/setter on a direct access + * + * @var string[] + */ + private $accessMap = array('template_dir' => 'TemplateDir', 'config_dir' => 'ConfigDir', + 'plugins_dir' => 'PluginsDir', 'compile_dir' => 'CompileDir', + 'cache_dir' => 'CacheDir',); + + /**#@-*/ + + /** + * Initialize new Smarty object + */ + public function __construct() + { + $this->_clearTemplateCache(); + parent::__construct(); + if (is_callable('mb_internal_encoding')) { + mb_internal_encoding(Smarty::$_CHARSET); + } + $this->start_time = microtime(true); + + if (isset($_SERVER[ 'SCRIPT_NAME' ])) { + Smarty::$global_tpl_vars[ 'SCRIPT_NAME' ] = new Smarty_Variable($_SERVER[ 'SCRIPT_NAME' ]); + } + + // Check if we're running on windows + Smarty::$_IS_WINDOWS = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'; + // let PCRE (preg_*) treat strings as ISO-8859-1 if we're not dealing with UTF-8 + if (Smarty::$_CHARSET !== 'UTF-8') { + Smarty::$_UTF8_MODIFIER = ''; + } + } + + /** + * Check if a template resource exists + * + * @param string $resource_name template name + * + * @return boolean status + */ + public function templateExists($resource_name) + { + // create source object + $source = Smarty_Template_Source::load(null, $this, $resource_name); + return $source->exists; + } + + /** + * Loads security class and enables security + * + * @param string|Smarty_Security $security_class if a string is used, it must be class-name + * + * @return Smarty current Smarty instance for chaining + * @throws SmartyException when an invalid class name is provided + */ + public function enableSecurity($security_class = null) + { + Smarty_Security::enableSecurity($this, $security_class); + return $this; + } + + /** + * Disable security + * + * @return Smarty current Smarty instance for chaining + */ + public function disableSecurity() + { + $this->security_policy = null; + + return $this; + } + + /** + * Set template directory + * + * @param string|array $template_dir directory(s) of template sources + * @param bool $isConfig true for config_dir + * + * @return \Smarty current Smarty instance for chaining + */ + public function setTemplateDir($template_dir, $isConfig = false) + { + if ($isConfig) { + $this->config_dir = array(); + $this->_processedConfigDir = array(); + } else { + $this->template_dir = array(); + $this->_processedTemplateDir = array(); + } + $this->addTemplateDir($template_dir, null, $isConfig); + return $this; + } + + /** + * Add template directory(s) + * + * @param string|array $template_dir directory(s) of template sources + * @param string $key of the array element to assign the template dir to + * @param bool $isConfig true for config_dir + * + * @return Smarty current Smarty instance for chaining + */ + public function addTemplateDir($template_dir, $key = null, $isConfig = false) + { + if ($isConfig) { + $processed = &$this->_processedConfigDir; + $dir = &$this->config_dir; + $this->_configDirNormalized = false; + } else { + $processed = &$this->_processedTemplateDir; + $dir = &$this->template_dir; + $this->_templateDirNormalized = false; + } + if (is_array($template_dir)) { + foreach ($template_dir as $k => $v) { + if (is_int($k)) { + // indexes are not merged but appended + $dir[] = $v; + } else { + // string indexes are overridden + $dir[ $k ] = $v; + unset($processed[ $key ]); + } + } + } else { + if ($key !== null) { + // override directory at specified index + $dir[ $key ] = $template_dir; + unset($processed[ $key ]); + } else { + // append new directory + $dir[] = $template_dir; + } + } + return $this; + } + + /** + * Get template directories + * + * @param mixed $index index of directory to get, null to get all + * @param bool $isConfig true for config_dir + * + * @return array list of template directories, or directory of $index + */ + public function getTemplateDir($index = null, $isConfig = false) + { + if ($isConfig) { + $dir = &$this->config_dir; + } else { + $dir = &$this->template_dir; + } + if ($isConfig ? !$this->_configDirNormalized : !$this->_templateDirNormalized) { + $this->_nomalizeTemplateConfig($isConfig); + } + if ($index !== null) { + return isset($dir[ $index ]) ? $dir[ $index ] : null; + } + return $dir; + } + + /** + * Set config directory + * + * @param $config_dir + * + * @return Smarty current Smarty instance for chaining + */ + public function setConfigDir($config_dir) + { + return $this->setTemplateDir($config_dir, true); + } + + /** + * Add config directory(s) + * + * @param string|array $config_dir directory(s) of config sources + * @param mixed $key key of the array element to assign the config dir to + * + * @return Smarty current Smarty instance for chaining + */ + public function addConfigDir($config_dir, $key = null) + { + return $this->addTemplateDir($config_dir, $key, true); + } + + /** + * Get config directory + * + * @param mixed $index index of directory to get, null to get all + * + * @return array configuration directory + */ + public function getConfigDir($index = null) + { + return $this->getTemplateDir($index, true); + } + + /** + * Set plugins directory + * + * @param string|array $plugins_dir directory(s) of plugins + * + * @return Smarty current Smarty instance for chaining + */ + public function setPluginsDir($plugins_dir) + { + $this->plugins_dir = (array) $plugins_dir; + $this->_pluginsDirNormalized = false; + return $this; + } + + /** + * Adds directory of plugin files + * + * @param null|array $plugins_dir + * + * @return Smarty current Smarty instance for chaining + */ + public function addPluginsDir($plugins_dir) + { + if (empty($this->plugins_dir)) { + $this->plugins_dir[] = SMARTY_PLUGINS_DIR; + } + $this->plugins_dir = array_merge($this->plugins_dir, (array) $plugins_dir); + $this->_pluginsDirNormalized = false; + return $this; + } + + /** + * Get plugin directories + * + * @return array list of plugin directories + */ + public function getPluginsDir() + { + if (empty($this->plugins_dir)) { + $this->plugins_dir[] = SMARTY_PLUGINS_DIR; + $this->_pluginsDirNormalized = false; + } + if (!$this->_pluginsDirNormalized) { + if (!is_array($this->plugins_dir)) { + $this->plugins_dir = (array) $this->plugins_dir; + } + foreach ($this->plugins_dir as $k => $v) { + $this->plugins_dir[ $k ] = $this->_realpath(rtrim($v, "/\\") . $this->ds, true); + } + $this->_cache[ 'plugin_files' ] = array(); + $this->_pluginsDirNormalized = true; + } + return $this->plugins_dir; + } + + /** + * + * @param string $compile_dir directory to store compiled templates in + * + * @return Smarty current Smarty instance for chaining + */ + public function setCompileDir($compile_dir) + { + $this->_normalizeDir('compile_dir', $compile_dir); + $this->_compileDirNormalized = true; + return $this; + } + + /** + * Get compiled directory + * + * @return string path to compiled templates + */ + public function getCompileDir() + { + if (!$this->_compileDirNormalized) { + $this->_normalizeDir('compile_dir', $this->compile_dir); + $this->_compileDirNormalized = true; + } + return $this->compile_dir; + } + + /** + * Set cache directory + * + * @param string $cache_dir directory to store cached templates in + * + * @return Smarty current Smarty instance for chaining + */ + public function setCacheDir($cache_dir) + { + $this->_normalizeDir('cache_dir', $cache_dir); + $this->_cacheDirNormalized = true; + return $this; + } + + /** + * Get cache directory + * + * @return string path of cache directory + */ + public function getCacheDir() + { + if (!$this->_cacheDirNormalized) { + $this->_normalizeDir('cache_dir', $this->cache_dir); + $this->_cacheDirNormalized = true; + } + return $this->cache_dir; + } + + /** + * Normalize and set directory string + * + * @param string $dirName cache_dir or compile_dir + * @param string $dir filepath of folder + */ + private function _normalizeDir($dirName, $dir) + { + $this->{$dirName} = $this->_realpath(rtrim($dir, "/\\") . $this->ds, true); + if (!isset(Smarty::$_muted_directories[ $this->{$dirName} ])) { + Smarty::$_muted_directories[ $this->{$dirName} ] = null; + } + } + + /** + * Normalize template_dir or config_dir + * + * @param bool $isConfig true for config_dir + * + */ + private function _nomalizeTemplateConfig($isConfig) + { + if ($isConfig) { + $processed = &$this->_processedConfigDir; + $dir = &$this->config_dir; + } else { + $processed = &$this->_processedTemplateDir; + $dir = &$this->template_dir; + } + if (!is_array($dir)) { + $dir = (array) $dir; + } + foreach ($dir as $k => $v) { + if (!isset($processed[ $k ])) { + $dir[ $k ] = $v = $this->_realpath(rtrim($v, "/\\") . $this->ds, true); + $processed[ $k ] = true; + } + } + $isConfig ? $this->_configDirNormalized = true : $this->_templateDirNormalized = true; + $isConfig ? $this->_joined_config_dir = join('#', $this->config_dir) : + $this->_joined_template_dir = join('#', $this->template_dir); + } + + /** + * creates a template object + * + * @param string $template the resource handle of the template file + * @param mixed $cache_id cache id to be used with this template + * @param mixed $compile_id compile id to be used with this template + * @param object $parent next higher level of Smarty variables + * @param boolean $do_clone flag is Smarty object shall be cloned + * + * @return object template object + */ + public function createTemplate($template, $cache_id = null, $compile_id = null, $parent = null, $do_clone = true) + { + if ($cache_id !== null && (is_object($cache_id) || is_array($cache_id))) { + $parent = $cache_id; + $cache_id = null; + } + if ($parent !== null && is_array($parent)) { + $data = $parent; + $parent = null; + } else { + $data = null; + } + if (!$this->_templateDirNormalized) { + $this->_nomalizeTemplateConfig(false); + } + $_templateId = $this->_getTemplateId($template, $cache_id, $compile_id); + $tpl = null; + if ($this->caching && isset(Smarty_Internal_Template::$isCacheTplObj[ $_templateId ])) { + $tpl = $do_clone ? clone Smarty_Internal_Template::$isCacheTplObj[ $_templateId ] : + Smarty_Internal_Template::$isCacheTplObj[ $_templateId ]; + $tpl->inheritance = null; + $tpl->tpl_vars = $tpl->config_vars = array(); + } else if (!$do_clone && isset(Smarty_Internal_Template::$tplObjCache[ $_templateId ])) { + $tpl = clone Smarty_Internal_Template::$tplObjCache[ $_templateId ]; + $tpl->inheritance = null; + $tpl->tpl_vars = $tpl->config_vars = array(); + } else { + /* @var Smarty_Internal_Template $tpl */ + $tpl = new $this->template_class($template, $this, null, $cache_id, $compile_id, null, null); + $tpl->templateId = $_templateId; + } + if ($do_clone) { + $tpl->smarty = clone $tpl->smarty; + } + $tpl->parent = $parent ? $parent : $this; + // fill data if present + if (!empty($data) && is_array($data)) { + // set up variable values + foreach ($data as $_key => $_val) { + $tpl->tpl_vars[ $_key ] = new Smarty_Variable($_val); + } + } + if ($this->debugging || $this->debugging_ctrl == 'URL') { + $tpl->smarty->_debug = new Smarty_Internal_Debug(); + // check URL debugging control + if (!$this->debugging && $this->debugging_ctrl == 'URL') { + $tpl->smarty->_debug->debugUrl($tpl->smarty); + } + } + return $tpl; + } + + /** + * Takes unknown classes and loads plugin files for them + * class name format: Smarty_PluginType_PluginName + * plugin filename format: plugintype.pluginname.php + * + * @param string $plugin_name class plugin name to load + * @param bool $check check if already loaded + * + * @throws SmartyException + * @return string |boolean filepath of loaded file or false + */ + public function loadPlugin($plugin_name, $check = true) + { + return $this->ext->loadPlugin->loadPlugin($this, $plugin_name, $check); + } + + /** + * Get unique template id + * + * @param string $template_name + * @param null|mixed $cache_id + * @param null|mixed $compile_id + * @param null $caching + * @param \Smarty_Internal_Template $template + * + * @return string + */ + public function _getTemplateId($template_name, $cache_id = null, $compile_id = null, $caching = null, + Smarty_Internal_Template $template = null) + { + $template_name = (strpos($template_name, ':') === false) ? "{$this->default_resource_type}:{$template_name}" : + $template_name; + $cache_id = $cache_id === null ? $this->cache_id : $cache_id; + $compile_id = $compile_id === null ? $this->compile_id : $compile_id; + $caching = (int) ($caching === null ? $this->caching : $caching); + + if ((isset($template) && strpos($template_name, ':.') !== false) || $this->allow_ambiguous_resources) { + $_templateId = + Smarty_Resource::getUniqueTemplateName((isset($template) ? $template : $this), $template_name) . + "#{$cache_id}#{$compile_id}#{$caching}"; + } else { + $_templateId = $this->_joined_template_dir . "#{$template_name}#{$cache_id}#{$compile_id}#{$caching}"; + } + if (isset($_templateId[ 150 ])) { + $_templateId = sha1($_templateId); + } + return $_templateId; + } + + /** + * Normalize path + * - remove /./ and /../ + * - make it absolute if required + * + * @param string $path file path + * @param bool $realpath if true - convert to absolute + * false - convert to relative + * null - keep as it is but remove /./ /../ + * + * @return string + */ + public function _realpath($path, $realpath = null) + { + $nds = $this->ds == '/' ? '\\' : '/'; + // normalize $this->ds + $path = str_replace($nds, $this->ds, $path); + preg_match('%^(?(?:[[:alpha:]]:[\\\\]|/|[\\\\]{2}[[:alpha:]]+|[[:print:]]{2,}:[/]{2}|[\\\\])?)(?(?:[[:print:]]*))$%', + $path, $parts); + $path = $parts[ 'path' ]; + if ($parts[ 'root' ] == '\\') { + $parts[ 'root' ] = substr(getcwd(), 0, 2) . $parts[ 'root' ]; + } else { + if ($realpath !== null && !$parts[ 'root' ]) { + $path = getcwd() . $this->ds . $path; + } + } + // remove noop 'DIRECTORY_SEPARATOR DIRECTORY_SEPARATOR' and 'DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR' patterns + $path = preg_replace('#([\\\\/]([.]?[\\\\/])+)#', $this->ds, $path); + // resolve '..DIRECTORY_SEPARATOR' pattern, smallest first + if (strpos($path, '..' . $this->ds) != false && + preg_match_all('#(([.]?[\\\\/])*([.][.])[\\\\/]([.]?[\\\\/])*)+#', $path, $match) + ) { + $counts = array(); + foreach ($match[ 0 ] as $m) { + $counts[] = (int) ((strlen($m) - 1) / 3); + } + sort($counts); + foreach ($counts as $count) { + $path = preg_replace('#(([\\\\/]([.]?[\\\\/])*[^\\\\/.]+){' . $count . + '}[\\\\/]([.]?[\\\\/])*([.][.][\\\\/]([.]?[\\\\/])*){' . $count . '})(?=[^.])#', + $this->ds, $path); + } + } + + return $parts[ 'root' ] . $path; + } + + /** + * Empty template objects cache + */ + public function _clearTemplateCache() + { + Smarty_Internal_Template::$isCacheTplObj = array(); + Smarty_Internal_Template::$tplObjCache = array(); + } + + /** + * Get Smarty object + * + * @return Smarty + */ + public function _getSmartyObj() + { + return $this; + } + + /** + * @param boolean $compile_check + */ + public function setCompileCheck($compile_check) + { + $this->compile_check = $compile_check; + } + + /** + * @param boolean $use_sub_dirs + */ + public function setUseSubDirs($use_sub_dirs) + { + $this->use_sub_dirs = $use_sub_dirs; + } + + /** + * @param int $error_reporting + */ + public function setErrorReporting($error_reporting) + { + $this->error_reporting = $error_reporting; + } + + /** + * @param boolean $escape_html + */ + public function setEscapeHtml($escape_html) + { + $this->escape_html = $escape_html; + } + + /** + * @param boolean $auto_literal + */ + public function setAutoLiteral($auto_literal) + { + $this->auto_literal = $auto_literal; + } + + /** + * @param boolean $force_compile + */ + public function setForceCompile($force_compile) + { + $this->force_compile = $force_compile; + } + + /** + * @param boolean $merge_compiled_includes + */ + public function setMergeCompiledIncludes($merge_compiled_includes) + { + $this->merge_compiled_includes = $merge_compiled_includes; + } + + /** + * @param string $left_delimiter + */ + public function setLeftDelimiter($left_delimiter) + { + $this->left_delimiter = $left_delimiter; + } + + /** + * @param string $right_delimiter + */ + public function setRightDelimiter($right_delimiter) + { + $this->right_delimiter = $right_delimiter; + } + + /** + * @param boolean $debugging + */ + public function setDebugging($debugging) + { + $this->debugging = $debugging; + } + + /** + * @param boolean $config_overwrite + */ + public function setConfigOverwrite($config_overwrite) + { + $this->config_overwrite = $config_overwrite; + } + + /** + * @param boolean $config_booleanize + */ + public function setConfigBooleanize($config_booleanize) + { + $this->config_booleanize = $config_booleanize; + } + + /** + * @param boolean $config_read_hidden + */ + public function setConfigReadHidden($config_read_hidden) + { + $this->config_read_hidden = $config_read_hidden; + } + + /** + * @param boolean $compile_locking + */ + public function setCompileLocking($compile_locking) + { + $this->compile_locking = $compile_locking; + } + + /** + * @param string $default_resource_type + */ + public function setDefaultResourceType($default_resource_type) + { + $this->default_resource_type = $default_resource_type; + } + + /** + * @param string $caching_type + */ + public function setCachingType($caching_type) + { + $this->caching_type = $caching_type; + } + + /** + * Test install + * + * @param null $errors + */ + public function testInstall(&$errors = null) + { + Smarty_Internal_TestInstall::testInstall($this, $errors); + } + + /** + * <> Generic getter. + * Calls the appropriate getter function. + * Issues an E_USER_NOTICE if no valid getter is found. + * + * @param string $name property name + * + * @return mixed + */ + public function __get($name) + { + if (isset($this->accessMap[ $name ])) { + $method = 'get' . $this->accessMap[ $name ]; + return $this->{$method}(); + } elseif (isset($this->_cache[ $name ])) { + return $this->_cache[ $name ]; + } elseif (in_array($name, $this->obsoleteProperties)) { + return null; + } else { + trigger_error('Undefined property: ' . get_class($this) . '::$' . $name, E_USER_NOTICE); + } + return null; + } + + /** + * <> Generic setter. + * Calls the appropriate setter function. + * Issues an E_USER_NOTICE if no valid setter is found. + * + * @param string $name property name + * @param mixed $value parameter passed to setter + */ + public function __set($name, $value) + { + if (isset($this->accessMap[ $name ])) { + $method = 'set' . $this->accessMap[ $name ]; + $this->{$method}($value); + } elseif (in_array($name, $this->obsoleteProperties)) { + return; + } else { + if (is_object($value) && method_exists($value, $name)) { + $this->$name = $value; + } else { + trigger_error('Undefined property: ' . get_class($this) . '::$' . $name, E_USER_NOTICE); + } + } + } + + /** + * Error Handler to mute expected messages + * + * @link http://php.net/set_error_handler + * + * @param integer $errno Error level + * @param $errstr + * @param $errfile + * @param $errline + * @param $errcontext + * + * @return bool|void + */ + public static function mutingErrorHandler($errno, $errstr, $errfile, $errline, $errcontext) + { + $_is_muted_directory = false; + + // add the SMARTY_DIR to the list of muted directories + if (!isset(Smarty::$_muted_directories[ SMARTY_DIR ])) { + $smarty_dir = realpath(SMARTY_DIR); + if ($smarty_dir !== false) { + Smarty::$_muted_directories[ SMARTY_DIR ] = + array('file' => $smarty_dir, 'length' => strlen($smarty_dir),); + } + } + + // walk the muted directories and test against $errfile + foreach (Smarty::$_muted_directories as $key => &$dir) { + if (!$dir) { + // resolve directory and length for speedy comparisons + $file = realpath($key); + if ($file === false) { + // this directory does not exist, remove and skip it + unset(Smarty::$_muted_directories[ $key ]); + continue; + } + $dir = array('file' => $file, 'length' => strlen($file),); + } + if (!strncmp($errfile, $dir[ 'file' ], $dir[ 'length' ])) { + $_is_muted_directory = true; + break; + } + } + // pass to next error handler if this error did not occur inside SMARTY_DIR + // or the error was within smarty but masked to be ignored + if (!$_is_muted_directory || ($errno && $errno & error_reporting())) { + if (Smarty::$_previous_error_handler) { + return call_user_func(Smarty::$_previous_error_handler, $errno, $errstr, $errfile, $errline, + $errcontext); + } else { + return false; + } + } + return; + } + + /** + * Enable error handler to mute expected messages + * + * @return void + */ + public static function muteExpectedErrors() + { + /* + error muting is done because some people implemented custom error_handlers using + http://php.net/set_error_handler and for some reason did not understand the following paragraph: + + It is important to remember that the standard PHP error handler is completely bypassed for the + error types specified by error_types unless the callback function returns FALSE. + error_reporting() settings will have no effect and your error handler will be called regardless - + however you are still able to read the current value of error_reporting and act appropriately. + Of particular note is that this value will be 0 if the statement that caused the error was + prepended by the @ error-control operator. + + Smarty deliberately uses @filemtime() over file_exists() and filemtime() in some places. Reasons include + - @filemtime() is almost twice as fast as using an additional file_exists() + - between file_exists() and filemtime() a possible race condition is opened, + which does not exist using the simple @filemtime() approach. + */ + $error_handler = array('Smarty', 'mutingErrorHandler'); + $previous = set_error_handler($error_handler); + + // avoid dead loops + if ($previous !== $error_handler) { + Smarty::$_previous_error_handler = $previous; + } + } + + /** + * Disable error handler muting expected messages + * + * @return void + */ + public static function unmuteExpectedErrors() + { + restore_error_handler(); + } +} diff --git a/misc/smarty_libs/SmartyBC.class.php b/misc/smarty_libs/SmartyBC.class.php new file mode 100644 index 0000000..3955e4f --- /dev/null +++ b/misc/smarty_libs/SmartyBC.class.php @@ -0,0 +1,455 @@ + + * @author Uwe Tews + * @author Rodney Rehm + * @package Smarty + */ +/** + * @ignore + */ +require_once(dirname(__FILE__) . '/Smarty.class.php'); + +/** + * Smarty Backward Compatibility Wrapper Class + * + * @package Smarty + */ +class SmartyBC extends Smarty +{ + /** + * Smarty 2 BC + * + * @var string + */ + public $_version = self::SMARTY_VERSION; + + /** + * This is an array of directories where trusted php scripts reside. + * + * @var array + */ + public $trusted_dir = array(); + + /** + * Initialize new SmartyBC object + * + */ + public function __construct() + { + parent::__construct(); + } + + /** + * wrapper for assign_by_ref + * + * @param string $tpl_var the template variable name + * @param mixed &$value the referenced value to assign + */ + public function assign_by_ref($tpl_var, &$value) + { + $this->assignByRef($tpl_var, $value); + } + + /** + * wrapper for append_by_ref + * + * @param string $tpl_var the template variable name + * @param mixed &$value the referenced value to append + * @param boolean $merge flag if array elements shall be merged + */ + public function append_by_ref($tpl_var, &$value, $merge = false) + { + $this->appendByRef($tpl_var, $value, $merge); + } + + /** + * clear the given assigned template variable. + * + * @param string $tpl_var the template variable to clear + */ + public function clear_assign($tpl_var) + { + $this->clearAssign($tpl_var); + } + + /** + * Registers custom function to be used in templates + * + * @param string $function the name of the template function + * @param string $function_impl the name of the PHP function to register + * @param bool $cacheable + * @param mixed $cache_attrs + */ + public function register_function($function, $function_impl, $cacheable = true, $cache_attrs = null) + { + $this->registerPlugin('function', $function, $function_impl, $cacheable, $cache_attrs); + } + + /** + * Unregister custom function + * + * @param string $function name of template function + */ + public function unregister_function($function) + { + $this->unregisterPlugin('function', $function); + } + + /** + * Registers object to be used in templates + * + * @param string $object name of template object + * @param object $object_impl the referenced PHP object to register + * @param array $allowed list of allowed methods (empty = all) + * @param boolean $smarty_args smarty argument format, else traditional + * @param array $block_methods list of methods that are block format + * + * @throws SmartyException + * @internal param array $block_functs list of methods that are block format + */ + public function register_object($object, $object_impl, $allowed = array(), $smarty_args = true, + $block_methods = array()) + { + settype($allowed, 'array'); + settype($smarty_args, 'boolean'); + $this->registerObject($object, $object_impl, $allowed, $smarty_args, $block_methods); + } + + /** + * Unregister object + * + * @param string $object name of template object + */ + public function unregister_object($object) + { + $this->unregisterObject($object); + } + + /** + * Registers block function to be used in templates + * + * @param string $block name of template block + * @param string $block_impl PHP function to register + * @param bool $cacheable + * @param mixed $cache_attrs + */ + public function register_block($block, $block_impl, $cacheable = true, $cache_attrs = null) + { + $this->registerPlugin('block', $block, $block_impl, $cacheable, $cache_attrs); + } + + /** + * Unregister block function + * + * @param string $block name of template function + */ + public function unregister_block($block) + { + $this->unregisterPlugin('block', $block); + } + + /** + * Registers compiler function + * + * @param string $function name of template function + * @param string $function_impl name of PHP function to register + * @param bool $cacheable + */ + public function register_compiler_function($function, $function_impl, $cacheable = true) + { + $this->registerPlugin('compiler', $function, $function_impl, $cacheable); + } + + /** + * Unregister compiler function + * + * @param string $function name of template function + */ + public function unregister_compiler_function($function) + { + $this->unregisterPlugin('compiler', $function); + } + + /** + * Registers modifier to be used in templates + * + * @param string $modifier name of template modifier + * @param string $modifier_impl name of PHP function to register + */ + public function register_modifier($modifier, $modifier_impl) + { + $this->registerPlugin('modifier', $modifier, $modifier_impl); + } + + /** + * Unregister modifier + * + * @param string $modifier name of template modifier + */ + public function unregister_modifier($modifier) + { + $this->unregisterPlugin('modifier', $modifier); + } + + /** + * Registers a resource to fetch a template + * + * @param string $type name of resource + * @param array $functions array of functions to handle resource + */ + public function register_resource($type, $functions) + { + $this->registerResource($type, $functions); + } + + /** + * Unregister a resource + * + * @param string $type name of resource + */ + public function unregister_resource($type) + { + $this->unregisterResource($type); + } + + /** + * Registers a prefilter function to apply + * to a template before compiling + * + * @param callable $function + */ + public function register_prefilter($function) + { + $this->registerFilter('pre', $function); + } + + /** + * Unregister a prefilter function + * + * @param callable $function + */ + public function unregister_prefilter($function) + { + $this->unregisterFilter('pre', $function); + } + + /** + * Registers a postfilter function to apply + * to a compiled template after compilation + * + * @param callable $function + */ + public function register_postfilter($function) + { + $this->registerFilter('post', $function); + } + + /** + * Unregister a postfilter function + * + * @param callable $function + */ + public function unregister_postfilter($function) + { + $this->unregisterFilter('post', $function); + } + + /** + * Registers an output filter function to apply + * to a template output + * + * @param callable $function + */ + public function register_outputfilter($function) + { + $this->registerFilter('output', $function); + } + + /** + * Unregister an outputfilter function + * + * @param callable $function + */ + public function unregister_outputfilter($function) + { + $this->unregisterFilter('output', $function); + } + + /** + * load a filter of specified type and name + * + * @param string $type filter type + * @param string $name filter name + */ + public function load_filter($type, $name) + { + $this->loadFilter($type, $name); + } + + /** + * clear cached content for the given template and cache id + * + * @param string $tpl_file name of template file + * @param string $cache_id name of cache_id + * @param string $compile_id name of compile_id + * @param string $exp_time expiration time + * + * @return boolean + */ + public function clear_cache($tpl_file = null, $cache_id = null, $compile_id = null, $exp_time = null) + { + return $this->clearCache($tpl_file, $cache_id, $compile_id, $exp_time); + } + + /** + * clear the entire contents of cache (all templates) + * + * @param string $exp_time expire time + * + * @return boolean + */ + public function clear_all_cache($exp_time = null) + { + return $this->clearCache(null, null, null, $exp_time); + } + + /** + * test to see if valid cache exists for this template + * + * @param string $tpl_file name of template file + * @param string $cache_id + * @param string $compile_id + * + * @return boolean + */ + public function is_cached($tpl_file, $cache_id = null, $compile_id = null) + { + return $this->isCached($tpl_file, $cache_id, $compile_id); + } + + /** + * clear all the assigned template variables. + */ + public function clear_all_assign() + { + $this->clearAllAssign(); + } + + /** + * clears compiled version of specified template resource, + * or all compiled template files if one is not specified. + * This function is for advanced use only, not normally needed. + * + * @param string $tpl_file + * @param string $compile_id + * @param string $exp_time + * + * @return boolean results of {@link smarty_core_rm_auto()} + */ + public function clear_compiled_tpl($tpl_file = null, $compile_id = null, $exp_time = null) + { + return $this->clearCompiledTemplate($tpl_file, $compile_id, $exp_time); + } + + /** + * Checks whether requested template exists. + * + * @param string $tpl_file + * + * @return boolean + */ + public function template_exists($tpl_file) + { + return $this->templateExists($tpl_file); + } + + /** + * Returns an array containing template variables + * + * @param string $name + * + * @return array + */ + public function get_template_vars($name = null) + { + return $this->getTemplateVars($name); + } + + /** + * Returns an array containing config variables + * + * @param string $name + * + * @return array + */ + public function get_config_vars($name = null) + { + return $this->getConfigVars($name); + } + + /** + * load configuration values + * + * @param string $file + * @param string $section + * @param string $scope + */ + public function config_load($file, $section = null, $scope = 'global') + { + $this->ConfigLoad($file, $section, $scope); + } + + /** + * return a reference to a registered object + * + * @param string $name + * + * @return object + */ + public function get_registered_object($name) + { + return $this->getRegisteredObject($name); + } + + /** + * clear configuration values + * + * @param string $var + */ + public function clear_config($var = null) + { + $this->clearConfig($var); + } + + /** + * trigger Smarty error + * + * @param string $error_msg + * @param integer $error_type + */ + public function trigger_error($error_msg, $error_type = E_USER_WARNING) + { + trigger_error("Smarty error: $error_msg", $error_type); + } +} diff --git a/misc/smarty_libs/bootstrap.php b/misc/smarty_libs/bootstrap.php new file mode 100644 index 0000000..3209608 --- /dev/null +++ b/misc/smarty_libs/bootstrap.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/* + * Load and register Smarty Autoloader + */ +if (!class_exists('Smarty_Autoloader')) { + require __DIR__ . '/Autoloader.php'; +} +Smarty_Autoloader::register(); diff --git a/misc/smarty_libs/debug.tpl b/misc/smarty_libs/debug.tpl new file mode 100644 index 0000000..5526cbc --- /dev/null +++ b/misc/smarty_libs/debug.tpl @@ -0,0 +1,160 @@ +{capture name='_smarty_debug' assign=debug_output} + + + + Smarty Debug Console + + + + +

    Smarty {Smarty::SMARTY_VERSION} Debug Console + - {if isset($template_name)}{$template_name|debug_print_var nofilter} {/if}{if !empty($template_data)}Total Time {$execution_time|string_format:"%.5f"}{/if}

    + + {if !empty($template_data)} +

    included templates & config files (load time in seconds)

    +
    + {foreach $template_data as $template} + {$template.name} +
       + (compile {$template['compile_time']|string_format:"%.5f"}) (render {$template['render_time']|string_format:"%.5f"}) (cache {$template['cache_time']|string_format:"%.5f"}) + +
    + {/foreach} +
    + {/if} + +

    assigned template variables

    + + + {foreach $assigned_vars as $vars} + + + + + {/foreach} +

    ${$vars@key}

    + {if isset($vars['nocache'])}Nocache
    {/if} + {if isset($vars['scope'])}Origin: {$vars['scope']|debug_print_var nofilter}{/if} +

    Value

    {$vars['value']|debug_print_var:10:80 nofilter}
    {if isset($vars['attributes'])}

    Attributes

    {$vars['attributes']|debug_print_var nofilter} {/if}
    + +

    assigned config file variables

    + + + {foreach $config_vars as $vars} + + + + + {/foreach} + +

    #{$vars@key}#

    + {if isset($vars['scope'])}Origin: {$vars['scope']|debug_print_var nofilter}{/if} +
    {$vars['value']|debug_print_var:10:80 nofilter}
    + + +{/capture} + diff --git a/misc/smarty_libs/plugins/block.textformat.php b/misc/smarty_libs/plugins/block.textformat.php new file mode 100644 index 0000000..e2c5e3d --- /dev/null +++ b/misc/smarty_libs/plugins/block.textformat.php @@ -0,0 +1,114 @@ + + * Name: textformat
    + * Purpose: format text a certain way with preset styles + * or custom wrap/indent settings
    + * Params: + *
    + * - style         - string (email)
    + * - indent        - integer (0)
    + * - wrap          - integer (80)
    + * - wrap_char     - string ("\n")
    + * - indent_char   - string (" ")
    + * - wrap_boundary - boolean (true)
    + * 
    + * + * @link http://www.smarty.net/manual/en/language.function.textformat.php {textformat} + * (Smarty online manual) + * + * @param array $params parameters + * @param string $content contents of the block + * @param Smarty_Internal_Template $template template object + * @param boolean &$repeat repeat flag + * + * @return string content re-formatted + * @author Monte Ohrt + */ +function smarty_block_textformat($params, $content, $template, &$repeat) +{ + if (is_null($content)) { + return; + } + if (Smarty::$_MBSTRING && !is_callable('smarty_mb_wordwrap')) { + require_once(SMARTY_PLUGINS_DIR . 'shared.mb_wordwrap.php'); + } + + $style = null; + $indent = 0; + $indent_first = 0; + $indent_char = ' '; + $wrap = 80; + $wrap_char = "\n"; + $wrap_cut = false; + $assign = null; + + foreach ($params as $_key => $_val) { + switch ($_key) { + case 'style': + case 'indent_char': + case 'wrap_char': + case 'assign': + $$_key = (string) $_val; + break; + + case 'indent': + case 'indent_first': + case 'wrap': + $$_key = (int) $_val; + break; + + case 'wrap_cut': + $$_key = (bool) $_val; + break; + + default: + trigger_error("textformat: unknown attribute '$_key'"); + } + } + + if ($style == 'email') { + $wrap = 72; + } + // split into paragraphs + $_paragraphs = preg_split('![\r\n]{2}!', $content); + + foreach ($_paragraphs as &$_paragraph) { + if (!$_paragraph) { + continue; + } + // convert mult. spaces & special chars to single space + $_paragraph = + preg_replace(array('!\s+!' . Smarty::$_UTF8_MODIFIER, '!(^\s+)|(\s+$)!' . Smarty::$_UTF8_MODIFIER), + array(' ', ''), $_paragraph); + // indent first line + if ($indent_first > 0) { + $_paragraph = str_repeat($indent_char, $indent_first) . $_paragraph; + } + // wordwrap sentences + if (Smarty::$_MBSTRING) { + $_paragraph = smarty_mb_wordwrap($_paragraph, $wrap - $indent, $wrap_char, $wrap_cut); + } else { + $_paragraph = wordwrap($_paragraph, $wrap - $indent, $wrap_char, $wrap_cut); + } + // indent lines + if ($indent > 0) { + $_paragraph = preg_replace('!^!m', str_repeat($indent_char, $indent), $_paragraph); + } + } + $_output = implode($wrap_char . $wrap_char, $_paragraphs); + + if ($assign) { + $template->assign($assign, $_output); + } else { + return $_output; + } +} diff --git a/misc/smarty_libs/plugins/function.counter.php b/misc/smarty_libs/plugins/function.counter.php new file mode 100644 index 0000000..bcc8f49 --- /dev/null +++ b/misc/smarty_libs/plugins/function.counter.php @@ -0,0 +1,73 @@ + + * Name: counter
    + * Purpose: print out a counter value + * + * @author Monte Ohrt + * @link http://www.smarty.net/manual/en/language.function.counter.php {counter} + * (Smarty online manual) + * + * @param array $params parameters + * @param Smarty_Internal_Template $template template object + * + * @return string|null + */ +function smarty_function_counter($params, $template) +{ + static $counters = array(); + + $name = (isset($params[ 'name' ])) ? $params[ 'name' ] : 'default'; + if (!isset($counters[ $name ])) { + $counters[ $name ] = array('start' => 1, 'skip' => 1, 'direction' => 'up', 'count' => 1); + } + $counter =& $counters[ $name ]; + + if (isset($params[ 'start' ])) { + $counter[ 'start' ] = $counter[ 'count' ] = (int) $params[ 'start' ]; + } + + if (!empty($params[ 'assign' ])) { + $counter[ 'assign' ] = $params[ 'assign' ]; + } + + if (isset($counter[ 'assign' ])) { + $template->assign($counter[ 'assign' ], $counter[ 'count' ]); + } + + if (isset($params[ 'print' ])) { + $print = (bool) $params[ 'print' ]; + } else { + $print = empty($counter[ 'assign' ]); + } + + if ($print) { + $retval = $counter[ 'count' ]; + } else { + $retval = null; + } + + if (isset($params[ 'skip' ])) { + $counter[ 'skip' ] = $params[ 'skip' ]; + } + + if (isset($params[ 'direction' ])) { + $counter[ 'direction' ] = $params[ 'direction' ]; + } + + if ($counter[ 'direction' ] == "down") { + $counter[ 'count' ] -= $counter[ 'skip' ]; + } else { + $counter[ 'count' ] += $counter[ 'skip' ]; + } + + return $retval; +} diff --git a/misc/smarty_libs/plugins/function.cycle.php b/misc/smarty_libs/plugins/function.cycle.php new file mode 100644 index 0000000..a76d49a --- /dev/null +++ b/misc/smarty_libs/plugins/function.cycle.php @@ -0,0 +1,105 @@ + + * Name: cycle
    + * Date: May 3, 2002
    + * Purpose: cycle through given values
    + * Params: + *
    + * - name      - name of cycle (optional)
    + * - values    - comma separated list of values to cycle, or an array of values to cycle
    + *               (this can be left out for subsequent calls)
    + * - reset     - boolean - resets given var to true
    + * - print     - boolean - print var or not. default is true
    + * - advance   - boolean - whether or not to advance the cycle
    + * - delimiter - the value delimiter, default is ","
    + * - assign    - boolean, assigns to template var instead of printed.
    + * 
    + * Examples:
    + *
    + * {cycle values="#eeeeee,#d0d0d0d"}
    + * {cycle name=row values="one,two,three" reset=true}
    + * {cycle name=row}
    + * 
    + * + * @link http://www.smarty.net/manual/en/language.function.cycle.php {cycle} + * (Smarty online manual) + * @author Monte Ohrt + * @author credit to Mark Priatel + * @author credit to Gerard + * @author credit to Jason Sweat + * @version 1.3 + * + * @param array $params parameters + * @param Smarty_Internal_Template $template template object + * + * @return string|null + */ + +function smarty_function_cycle($params, $template) +{ + static $cycle_vars; + + $name = (empty($params[ 'name' ])) ? 'default' : $params[ 'name' ]; + $print = (isset($params[ 'print' ])) ? (bool) $params[ 'print' ] : true; + $advance = (isset($params[ 'advance' ])) ? (bool) $params[ 'advance' ] : true; + $reset = (isset($params[ 'reset' ])) ? (bool) $params[ 'reset' ] : false; + + if (!isset($params[ 'values' ])) { + if (!isset($cycle_vars[ $name ][ 'values' ])) { + trigger_error("cycle: missing 'values' parameter"); + + return; + } + } else { + if (isset($cycle_vars[ $name ][ 'values' ]) && $cycle_vars[ $name ][ 'values' ] != $params[ 'values' ]) { + $cycle_vars[ $name ][ 'index' ] = 0; + } + $cycle_vars[ $name ][ 'values' ] = $params[ 'values' ]; + } + + if (isset($params[ 'delimiter' ])) { + $cycle_vars[ $name ][ 'delimiter' ] = $params[ 'delimiter' ]; + } elseif (!isset($cycle_vars[ $name ][ 'delimiter' ])) { + $cycle_vars[ $name ][ 'delimiter' ] = ','; + } + + if (is_array($cycle_vars[ $name ][ 'values' ])) { + $cycle_array = $cycle_vars[ $name ][ 'values' ]; + } else { + $cycle_array = explode($cycle_vars[ $name ][ 'delimiter' ], $cycle_vars[ $name ][ 'values' ]); + } + + if (!isset($cycle_vars[ $name ][ 'index' ]) || $reset) { + $cycle_vars[ $name ][ 'index' ] = 0; + } + + if (isset($params[ 'assign' ])) { + $print = false; + $template->assign($params[ 'assign' ], $cycle_array[ $cycle_vars[ $name ][ 'index' ] ]); + } + + if ($print) { + $retval = $cycle_array[ $cycle_vars[ $name ][ 'index' ] ]; + } else { + $retval = null; + } + + if ($advance) { + if ($cycle_vars[ $name ][ 'index' ] >= count($cycle_array) - 1) { + $cycle_vars[ $name ][ 'index' ] = 0; + } else { + $cycle_vars[ $name ][ 'index' ] ++; + } + } + + return $retval; +} diff --git a/misc/smarty_libs/plugins/function.fetch.php b/misc/smarty_libs/plugins/function.fetch.php new file mode 100644 index 0000000..cb60dd9 --- /dev/null +++ b/misc/smarty_libs/plugins/function.fetch.php @@ -0,0 +1,221 @@ + + * Name: fetch
    + * Purpose: fetch file, web or ftp data and display results + * + * @link http://www.smarty.net/manual/en/language.function.fetch.php {fetch} + * (Smarty online manual) + * @author Monte Ohrt + * + * @param array $params parameters + * @param Smarty_Internal_Template $template template object + * + * @throws SmartyException + * @return string|null if the assign parameter is passed, Smarty assigns the result to a template variable + */ +function smarty_function_fetch($params, $template) +{ + if (empty($params[ 'file' ])) { + trigger_error("[plugin] fetch parameter 'file' cannot be empty", E_USER_NOTICE); + + return; + } + + // strip file protocol + if (stripos($params[ 'file' ], 'file://') === 0) { + $params[ 'file' ] = substr($params[ 'file' ], 7); + } + + $protocol = strpos($params[ 'file' ], '://'); + if ($protocol !== false) { + $protocol = strtolower(substr($params[ 'file' ], 0, $protocol)); + } + + if (isset($template->smarty->security_policy)) { + if ($protocol) { + // remote resource (or php stream, …) + if (!$template->smarty->security_policy->isTrustedUri($params[ 'file' ])) { + return; + } + } else { + // local file + if (!$template->smarty->security_policy->isTrustedResourceDir($params[ 'file' ])) { + return; + } + } + } + + $content = ''; + if ($protocol == 'http') { + // http fetch + if ($uri_parts = parse_url($params[ 'file' ])) { + // set defaults + $host = $server_name = $uri_parts[ 'host' ]; + $timeout = 30; + $accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*"; + $agent = "Smarty Template Engine " . Smarty::SMARTY_VERSION; + $referer = ""; + $uri = !empty($uri_parts[ 'path' ]) ? $uri_parts[ 'path' ] : '/'; + $uri .= !empty($uri_parts[ 'query' ]) ? '?' . $uri_parts[ 'query' ] : ''; + $_is_proxy = false; + if (empty($uri_parts[ 'port' ])) { + $port = 80; + } else { + $port = $uri_parts[ 'port' ]; + } + if (!empty($uri_parts[ 'user' ])) { + $user = $uri_parts[ 'user' ]; + } + if (!empty($uri_parts[ 'pass' ])) { + $pass = $uri_parts[ 'pass' ]; + } + // loop through parameters, setup headers + foreach ($params as $param_key => $param_value) { + switch ($param_key) { + case "file": + case "assign": + case "assign_headers": + break; + case "user": + if (!empty($param_value)) { + $user = $param_value; + } + break; + case "pass": + if (!empty($param_value)) { + $pass = $param_value; + } + break; + case "accept": + if (!empty($param_value)) { + $accept = $param_value; + } + break; + case "header": + if (!empty($param_value)) { + if (!preg_match('![\w\d-]+: .+!', $param_value)) { + trigger_error("[plugin] invalid header format '" . $param_value . "'", E_USER_NOTICE); + + return; + } else { + $extra_headers[] = $param_value; + } + } + break; + case "proxy_host": + if (!empty($param_value)) { + $proxy_host = $param_value; + } + break; + case "proxy_port": + if (!preg_match('!\D!', $param_value)) { + $proxy_port = (int) $param_value; + } else { + trigger_error("[plugin] invalid value for attribute '" . $param_key . "'", E_USER_NOTICE); + + return; + } + break; + case "agent": + if (!empty($param_value)) { + $agent = $param_value; + } + break; + case "referer": + if (!empty($param_value)) { + $referer = $param_value; + } + break; + case "timeout": + if (!preg_match('!\D!', $param_value)) { + $timeout = (int) $param_value; + } else { + trigger_error("[plugin] invalid value for attribute '" . $param_key . "'", E_USER_NOTICE); + + return; + } + break; + default: + trigger_error("[plugin] unrecognized attribute '" . $param_key . "'", E_USER_NOTICE); + + return; + } + } + if (!empty($proxy_host) && !empty($proxy_port)) { + $_is_proxy = true; + $fp = fsockopen($proxy_host, $proxy_port, $errno, $errstr, $timeout); + } else { + $fp = fsockopen($server_name, $port, $errno, $errstr, $timeout); + } + + if (!$fp) { + trigger_error("[plugin] unable to fetch: $errstr ($errno)", E_USER_NOTICE); + + return; + } else { + if ($_is_proxy) { + fputs($fp, 'GET ' . $params[ 'file' ] . " HTTP/1.0\r\n"); + } else { + fputs($fp, "GET $uri HTTP/1.0\r\n"); + } + if (!empty($host)) { + fputs($fp, "Host: $host\r\n"); + } + if (!empty($accept)) { + fputs($fp, "Accept: $accept\r\n"); + } + if (!empty($agent)) { + fputs($fp, "User-Agent: $agent\r\n"); + } + if (!empty($referer)) { + fputs($fp, "Referer: $referer\r\n"); + } + if (isset($extra_headers) && is_array($extra_headers)) { + foreach ($extra_headers as $curr_header) { + fputs($fp, $curr_header . "\r\n"); + } + } + if (!empty($user) && !empty($pass)) { + fputs($fp, "Authorization: BASIC " . base64_encode("$user:$pass") . "\r\n"); + } + + fputs($fp, "\r\n"); + while (!feof($fp)) { + $content .= fgets($fp, 4096); + } + fclose($fp); + $csplit = preg_split("!\r\n\r\n!", $content, 2); + + $content = $csplit[ 1 ]; + + if (!empty($params[ 'assign_headers' ])) { + $template->assign($params[ 'assign_headers' ], preg_split("!\r\n!", $csplit[ 0 ])); + } + } + } else { + trigger_error("[plugin fetch] unable to parse URL, check syntax", E_USER_NOTICE); + + return; + } + } else { + $content = @file_get_contents($params[ 'file' ]); + if ($content === false) { + throw new SmartyException("{fetch} cannot read resource '" . $params[ 'file' ] . "'"); + } + } + + if (!empty($params[ 'assign' ])) { + $template->assign($params[ 'assign' ], $content); + } else { + return $content; + } +} diff --git a/misc/smarty_libs/plugins/function.html_checkboxes.php b/misc/smarty_libs/plugins/function.html_checkboxes.php new file mode 100644 index 0000000..04ce457 --- /dev/null +++ b/misc/smarty_libs/plugins/function.html_checkboxes.php @@ -0,0 +1,251 @@ + + * Type: function
    + * Name: html_checkboxes
    + * Date: 24.Feb.2003
    + * Purpose: Prints out a list of checkbox input types
    + * Examples: + *
    + * {html_checkboxes values=$ids output=$names}
    + * {html_checkboxes values=$ids name='box' separator='
    ' output=$names} + * {html_checkboxes values=$ids checked=$checked separator='
    ' output=$names} + *
    + * Params: + *
    + * - name       (optional) - string default "checkbox"
    + * - values     (required) - array
    + * - options    (optional) - associative array
    + * - checked    (optional) - array default not set
    + * - separator  (optional) - ie 
    or   + * - output (optional) - the output next to each checkbox + * - assign (optional) - assign the output as an array to this variable + * - escape (optional) - escape the content (not value), defaults to true + *
    + * + * @link http://www.smarty.net/manual/en/language.function.html.checkboxes.php {html_checkboxes} + * (Smarty online manual) + * @author Christopher Kvarme + * @author credits to Monte Ohrt + * @version 1.0 + * + * @param array $params parameters + * @param object $template template object + * + * @return string + * @uses smarty_function_escape_special_chars() + */ +function smarty_function_html_checkboxes($params, $template) +{ + if (!is_callable('smarty_function_escape_special_chars')) { + require_once(SMARTY_PLUGINS_DIR . 'shared.escape_special_chars.php'); + } + + $name = 'checkbox'; + $values = null; + $options = null; + $selected = array(); + $separator = ''; + $escape = true; + $labels = true; + $label_ids = false; + $output = null; + + $extra = ''; + + foreach ($params as $_key => $_val) { + switch ($_key) { + case 'name': + case 'separator': + $$_key = (string) $_val; + break; + + case 'escape': + case 'labels': + case 'label_ids': + $$_key = (bool) $_val; + break; + + case 'options': + $$_key = (array) $_val; + break; + + case 'values': + case 'output': + $$_key = array_values((array) $_val); + break; + + case 'checked': + case 'selected': + if (is_array($_val)) { + $selected = array(); + foreach ($_val as $_sel) { + if (is_object($_sel)) { + if (method_exists($_sel, "__toString")) { + $_sel = smarty_function_escape_special_chars((string) $_sel->__toString()); + } else { + trigger_error("html_checkboxes: selected attribute contains an object of class '" . + get_class($_sel) . "' without __toString() method", E_USER_NOTICE); + continue; + } + } else { + $_sel = smarty_function_escape_special_chars((string) $_sel); + } + $selected[ $_sel ] = true; + } + } elseif (is_object($_val)) { + if (method_exists($_val, "__toString")) { + $selected = smarty_function_escape_special_chars((string) $_val->__toString()); + } else { + trigger_error("html_checkboxes: selected attribute is an object of class '" . get_class($_val) . + "' without __toString() method", E_USER_NOTICE); + } + } else { + $selected = smarty_function_escape_special_chars((string) $_val); + } + break; + + case 'checkboxes': + trigger_error('html_checkboxes: the use of the "checkboxes" attribute is deprecated, use "options" instead', + E_USER_WARNING); + $options = (array) $_val; + break; + + case 'assign': + break; + + case 'strict': + break; + + case 'disabled': + case 'readonly': + if (!empty($params[ 'strict' ])) { + if (!is_scalar($_val)) { + trigger_error("html_options: $_key attribute must be a scalar, only boolean true or string '$_key' will actually add the attribute", + E_USER_NOTICE); + } + + if ($_val === true || $_val === $_key) { + $extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_key) . '"'; + } + + break; + } + // omit break; to fall through! + + default: + if (!is_array($_val)) { + $extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_val) . '"'; + } else { + trigger_error("html_checkboxes: extra attribute '$_key' cannot be an array", E_USER_NOTICE); + } + break; + } + } + + if (!isset($options) && !isset($values)) { + return ''; + } /* raise error here? */ + + $_html_result = array(); + + if (isset($options)) { + foreach ($options as $_key => $_val) { + $_html_result[] = + smarty_function_html_checkboxes_output($name, $_key, $_val, $selected, $extra, $separator, $labels, + $label_ids, $escape); + } + } else { + foreach ($values as $_i => $_key) { + $_val = isset($output[ $_i ]) ? $output[ $_i ] : ''; + $_html_result[] = + smarty_function_html_checkboxes_output($name, $_key, $_val, $selected, $extra, $separator, $labels, + $label_ids, $escape); + } + } + + if (!empty($params[ 'assign' ])) { + $template->assign($params[ 'assign' ], $_html_result); + } else { + return implode("\n", $_html_result); + } +} + +function smarty_function_html_checkboxes_output($name, $value, $output, $selected, $extra, $separator, $labels, + $label_ids, $escape = true) +{ + $_output = ''; + + if (is_object($value)) { + if (method_exists($value, "__toString")) { + $value = (string) $value->__toString(); + } else { + trigger_error("html_options: value is an object of class '" . get_class($value) . + "' without __toString() method", E_USER_NOTICE); + + return ''; + } + } else { + $value = (string) $value; + } + + if (is_object($output)) { + if (method_exists($output, "__toString")) { + $output = (string) $output->__toString(); + } else { + trigger_error("html_options: output is an object of class '" . get_class($output) . + "' without __toString() method", E_USER_NOTICE); + + return ''; + } + } else { + $output = (string) $output; + } + + if ($labels) { + if ($label_ids) { + $_id = smarty_function_escape_special_chars(preg_replace('![^\w\-\.]!' . Smarty::$_UTF8_MODIFIER, '_', + $name . '_' . $value)); + $_output .= '