--[[ @name Content Master @author Michael (nadenj@mail.ru) @version 1.22.7 @description HTML-content editing and blocking engine @min_HC_version 1.0.0.512 @event Init @event Options @event Timer1m @event RequestHeaderReceived @event Destroy @exception \A(?!http)|/ContentMasterLog\.html\z ]] _CM_DIR = hc.script_name:match('.*\\') -- путь к папке СМ local CM_FolderURL = 'http://handycache.ru/cm/' _CM_MAIN_URL = CM_FolderURL .. 'Content_Master.lua' _CM_UPDATE_CFG_URL = CM_FolderURL .. 'cm_update.cfg' -- адрес файла конфигурации обновлений -- if not loadstring then loadstring = load end -- loadstring в Lua 5.2 заменена на load -- if not unpack then unpack = table.unpack end -- unpack в Lua 5.2 заменена на table.unpack function Init() dofile(_CM_DIR .. 'cm_init.lua') end function Options() dofile(_CM_DIR .. 'cm_options.lua') end function Destroy() dofile(_CM_DIR .. 'cm_destroy.lua') end function Timer1m() local MaxCacheSize = 50000 -- максимальное число записей в кэше срабатываний правил СМ local CacheSize = hc.get_global_table_item('CM_URLCache', 'Size') or 0 -- hc.put_msg('Cache size = ', CacheSize) if CacheSize>MaxCacheSize then -- после заполнения MaxCacheSize записей очищаем кэш срабатываний hc.put_msg('Content Master:\r\nURL Cache is emptied by ', CacheSize, ' entries') hc.set_global('CM_URLCache') end local unow = hc.get_global('CM_AutoUpdate') if not unow.period then return end if not unow.last or (os.time()>=unow.last+unow.period*3600) then hc.send_request('GET ' .. _CM_MAIN_URL .. ' HTTP/1.1\r\nUser-Agent: ContentMaster\r\nCMUpdate:\r\nConnection: close\r\n\r\n') unow.last = os.time() hc.set_global('CM_AutoUpdate', unow) end end local function InitiateWorkers() -- инициализирует необходимые движки (raw, html, js, css и т.п.) local content_type = _CM_CURRENT_REQUEST_DATA.ContentType or _CM_CURRENT_REQUEST_DATA.ContentTypeByBody or _CM_CURRENT_REQUEST_DATA.ContentTypeByHeader _CM_RAW_WORKER = _CM_RAW_WORKER or loadstring(hc.get_global_table_item('CM_WORKERS', 'RAW'))():New{} _CM_CURRENT_REQUEST_DATA.RawWorker = _CM_RAW_WORKER:Reinit(content_type, _CM_CURRENT_REQUEST_DATA.Charset or _CM_CURRENT_REQUEST_DATA.CharsetByHeader or 'iso-8859-1') if content_type=='html' then _CM_HTML_WORKER = _CM_HTML_WORKER or loadstring(hc.get_global_table_item('CM_WORKERS', 'HTML'))() _CM_CURRENT_REQUEST_DATA.Worker = _CM_HTML_WORKER:New{ Coding=_CM_CURRENT_REQUEST_DATA.Charset or _CM_CURRENT_REQUEST_DATA.CharsetByHeader or 'iso-8859-1' } elseif content_type=='js' then _CM_JS_WORKER = _CM_JS_WORKER or loadstring(hc.get_global_table_item('CM_WORKERS', 'JS'))() _CM_CURRENT_REQUEST_DATA.Worker = _CM_JS_WORKER:New{} elseif content_type=='css' then _CM_CSS_WORKER = _CM_CSS_WORKER or loadstring(hc.get_global_table_item('CM_WORKERS', 'CSS'))() _CM_CURRENT_REQUEST_DATA.Worker = _CM_CSS_WORKER:New{} end end local function process_update(lst) hc.set_global('CM_UpdateList', lst) hc.set_global('CM_Synchro') if not re.find(lst, [[^(?!(?>[^\r\n]*?\#~\#){3})]]) then local s = re.replace(nil, [[^((?>[^\r\n]*?\#~\#)){3}\s*?(^|\z)|^(?>[^\r\n]*?(?=\#~\#))\K...(?1){2}1$]], '', true) if s:find('%S') then hc.put_msg(10, '--- Content Master ---\r\n', lng['Updated successfully']) hc.reload_extension('Content Master') else hc.put_msg(5, '--- Content Master ---\r\n', lng['Is up to date']) end end end function RequestHeaderReceived() if _CM_USER and not _CM_USER.On then return end -- для этого пользователя СМ отключен local header, req_url = hc.request_header, hc.url -- запоминаем в локальной переменной для ускорения if header:match('^HEAD%s') then return end -- запросы HEAD не обрабатываем -- если это первый запрос в соединении или были изменения в настройках, то (пере)загружаем данные local last_opt_time = hc.get_global_table_item('CM_OPTIONS', 'LAST_OPTIONS_CHANGE_TIME') local connectionWasChanged = _CM_CONNECTION_START_TIME and _CM_CONNECTION_START_TIME < last_opt_time if not _CM_CONNECTION_START_TIME or connectionWasChanged then -- далее задаются настройки, действующие все время жизни СОЕДИНЕНИЯ _CM_CONNECTION_START_TIME = last_opt_time _CM_USERS = loadstring(hc.get_global('CM_USERS'))() -- все пользователи СМ (таблица) _CM_USER = _CM_USERS[hc.user_name] or _CM_USERS.ALL -- пользователь (предполагается, что он один на соединение!!!) if not _CM_USER.On then return end -- для этого пользователя СМ отключен _CM_DATA = _CM_DATA or {} _CM_OPTIONS = hc.get_global('CM_OPTIONS') os.setlocale('', 'all') -- устанавливаем локаль СМ в текущую локаль системы loadstring(hc.get_global('CM_COMMON'))() -- подгружаем библиотеку общих функций lng = _CM_PREPARE_LANGUAGE(_CM_OPTIONS.LanguageID, true) -- создаем переводчик с англ. языка на текущий язык НС _CM_URL_WORKER, _CM_HTML_WORKER, _CM_JS_WORKER, _CM_CSS_WORKER, _CM_RAW_WORKER = nil, nil, nil, nil, nil -- else lng = _CM_PREPARE_LANGUAGE() end -- --------------------------- ОБРАБОТКА ОБНОВЛЕНИЙ СМ ----------------------- _CM_UPDATE = header:match('CMUpdate:[^\r\n%S]*(%S*)') -- признак того, что это запрос на обновление if _CM_UPDATE then -- если это запрос на обновление hc.read_from_cache_on = false hc.white_mask = 'WBSDORU' if req_url==_CM_UPDATE_CFG_URL then hc.call_me_for('BeforeAnswerBodySend') -- регистрируем обработчик тела ответа -- clck1 = 0 else header = header:gsub('\r\n$', 'If-Modified-Since: ' .. hc.systime_to_str(hc.file_last_modified_time(_CM_DIR .. (req_url==_CM_MAIN_URL and 'Content_Master.lua' or re.find(hc.get_global('CM_UpdateList'), [[^(?>(.*?)\#~\#){2}(?>\Q]] .. req_url .. [[\E|\Q]] .. req_url:gsub('^http', 'https', 1) .. [[\E)]], 1))) or 0) .. '\r\n\r\n', 1): gsub('User%-Agent:.-([\r\n])', 'User-Agent: ContentMaster%1', 1) hc.request_header = re.replace(header, [[(*ANYCRLF)^Host:\s*+\K.*?$]], req_url:match('^https?://(.-)[/%?]')) hc.call_me_for('BeforeAnswerHeaderSend') -- регистрируем обработчик заголовка ответа end return end local mindex = hc.monitor_index _CM_DATA[mindex] = { LogOn=_CM_USERS.ALL.Log.On } _CM_CURRENT_REQUEST_DATA = _CM_DATA[mindex] -- сюда будут сохраняться данные, специфичные для запроса, которые потом должны использоваться в обработчиках _CM_CURRENT_REQUEST_DATA.URL, _CM_CURRENT_REQUEST_DATA.Host, _CM_CURRENT_REQUEST_DATA.Domain = _CM_NORMALIZE_URL(req_url) -- --------------------------- ОБРАБОТКА СООБЩЕНИЙ МАСТЕРА ИНТЕРАКТИВНОЙ РАБОТЫ ----------------------- local url = _CM_CURRENT_REQUEST_DATA.URL:match('^http://(.*)/for_CM$') if url then hc.white_mask = hc.white_mask:gsub('[Ss]', '') .. 'S' -- отключаем запись в кэш local action, parameter = header:match('[\r\n]cm%-info:%s*(.-)###(.-)[\r\n]') if action=='AcceptChanges' then local monitor_index, interactive_mode, info = parameter:match('^(.-)###(.-)###(.*)') if info then -- если это служебный запрос клиента, инициированный самим СМ url = _CM_TO_REGEXP(url:gsub('^www%.', '', 1)) local fname = _CM_DIR .. 'rules\\CMAutoRules_' .. _CM_USER.Name .. '.txt' local sfile = _CM_READ_FILE(fname, false) or '' if interactive_mode=='remove' then local document = loadstring(hc.get_global_table_item('CM_DOMS', tostring(monitor_index)))() local function findBody(elem) if elem.tag_name=='body' then return elem end for _,child in ipairs(elem) do local el = findBody(child) if el then return el end end end local body = findBody(document) local function findElement(elem, id) if elem.tag:match(id) then return elem end for _,child in ipairs(elem) do local el = findElement(child, id) if el then return el end end end -- hc.put_to_log('Неизмененный info = ', info) local NonViewedElements = { script=1, style=1, ['!']=1 } info = info:gsub('#(.-)#(.-)#~#', function(id, s2) local element = id=='' and body or findElement(body, '%s[Ii][Dd]%s*=%s*[\'"]?' .. id:gsub('[.%-+()[%]\\$^%%?]', '%%%0') .. '[\'"%s>]') return '#' .. id .. '#' .. s2:gsub('%d+', function(num) local i, j, n = 0, 0, tonumber(num) while n>0 do i = i + 1 if not (element[i].Removed2 or NonViewedElements[element[i].tag_name]) then n = n - 1 end -- учитываем полностью вырезанные элементы if not NonViewedElements[element[i].tag_name] then j = j + 1 end end -- local dd = hc.get_global_table_item('CM_RULES_SOURCE', element[i].Removed) -- hc.put_to_log(element.tag, ' --> ', element[i].tag, '\r\nRemoved = ', tostring(dd), '\r\ni = ', i, '\r\nj=', j, '\r\n', tostring(element[i])) element = element[i] return j end ) .. '#~#' end ) -- hc.put_to_log('info после изменения = ', info) local block = re.find(sfile, [[\#\#\s*+INTERACTIVE(?:(?!\#\#\s*+STYLES).)*+]]) or '## INTERACTIVE' block = block:match('^%s*(.-)%s*$') for path in info:gmatch('(.-)#~#') do block = block .. '\r\n' .. url .. '#~#' .. path end sfile = re.replace(sfile, [[\s*+\#\#\s*+INTERACTIVE(?:(?!\#\#\s*+STYLES).)*+|\z]], '\r\n\r\n' .. block .. '\r\n\r\n') _CM_WRITE_FILE(fname, 'wb', sfile) local current_parser = _CM_USER.Parsers.ContentMaster if current_parser.On and current_parser.Filters['Remove interactively'].On then local Files = current_parser.Files local InteractiveList, short_fname = {}, 'CMAutoRules_' .. _CM_USER.Name .. '.txt' for FileNumber,ff in ipairs(Files) do -- для каждой включенной подписки if ff.On then local f, err = io.open(ff.Name) if f then local StrNumber, CurBlockName = 0 for str in f:lines() do StrNumber = StrNumber + 1 local function modify(argument, code, repl) local hash = _CM_HASH(code .. argument) hc.set_global_table_item('CM_RULES_SOURCE', hash, FileNumber .. code .. StrNumber) if repl then hc.set_global_table_item('CM_RULES_ADDITIONS', hash, repl) end return argument .. '(*:' .. hash .. ')' end if str:match('^%s*##%s*(%S+)') then CurBlockName = str:match('^%s*##%s*(%S+)') elseif str:match('%S') and not str:match('^%s*%-%-') and CurBlockName=='INTERACTIVE' then table.insert(InteractiveList, modify(str:match('^%s*(.-)%s*$'), 'X')) end end f:close() else hc.put_msg('Content Master:\r\n', err) end end end if InteractiveList[1] then hc.set_global_table_item('AdMuncherFiles', _CM_GET_COMPLEX_USER_NAME(_CM_USER) .. '##interactive##', table.concat(InteractiveList)) end end else local CM_Site_Specific_STYLE_List, STYLE_List = {}, {} local block = re.find(sfile, [[\#\#\s*+STYLES(?:(?!\#\#\s*+INTERACTIVE).)*+]]) or '## STYLES' block = block:match('^%s*(.-)%s*$') for style in info:gmatch('(.-)#~#') do block = block .. '\r\n' .. url .. '#~#' .. style .. ' {display:none !important;}' end sfile = re.replace(sfile, [[\s*+\#\#\s*+STYLES(?:(?!\#\#\s*+INTERACTIVE).)*+|\z]], '\r\n\r\n' .. block .. '\r\n\r\n') _CM_WRITE_FILE(fname, 'wb', sfile) local current_parser = _CM_USER.Parsers.ContentMaster if current_parser.On and current_parser.Filters['Remove interactively'].On then local Files = current_parser.Files for FileNumber,ff in ipairs(Files) do -- для каждой включенной подписки if ff.On then local f, err = io.open(ff.Name) if f then local StrNumber, CurBlockName = 0 for str in f:lines() do StrNumber = StrNumber + 1 if str:match('^%s*##%s*(%S+)') then CurBlockName = str:match('^%s*##%s*(%S+)') elseif str:match('%S') and not str:match('^%s*%-%-') and CurBlockName=='STYLES' then local site, css = str:match('^(.-)#~#(.*)') css = css or str site = not (site:match('^^?%.%*?%??$') or site:match('^^$')) and site or nil if site then table.insert(CM_Site_Specific_STYLE_List, str) else table.insert(STYLE_List, css) end end end f:close() else hc.put_msg('Content Master:\r\n', err) end end end if CM_Site_Specific_STYLE_List[1] then hc.set_global_table_item('CMSiteSpecificCSS', _CM_GET_COMPLEX_USER_NAME(_CM_USER), table.concat(CM_Site_Specific_STYLE_List, '~~~~') .. '~~~~') end if STYLE_List[1] then hc.set_global_table_item('AdMuncherFiles', _CM_GET_COMPLEX_USER_NAME(_CM_USER) .. 'helper.css', table.concat(STYLE_List, ' ')) end end end hc.put_msg('Content Master:\r\n' .. lng['Changes saved']) end end hc.answer_header = 'HTTP/1.1 200 Accepted (CM)\r\nContent-Length: 0\r\nCache-Control: no-cache, must-revalidate\r\nPragma: no-cache\r\nConnection: close\r\n\r\n' hc.answer_body = '' return end -- ------------------------- ВСПОМОГАТЕЛЬНЫЕ ФАЙЛЫ ВСТАВКИ JS И CSS -------------------------------- local amfile, extension = _CM_CURRENT_REQUEST_DATA.URL:match('local%.cm/([^/.]+%.(%a+))') if amfile then hc.white_mask = 'WBSDORU' -- disable all HC lists if amfile=='helper_ss.css' then local outer_text = '' local url, user = _CM_CURRENT_REQUEST_DATA.URL:match('%?url=(.-)&user=(.*)') local CMCSS = hc.get_global_table_item('CMSiteSpecificCSS', user) if CMCSS then -- правило "ContentMaster:Вставка CSS" local regexp = CMCSS:gsub('(.-)#~#(.-)~~~~', function (s1, s2) return re.find(url, s1) and (s2 .. '\r\n ') or '' end):match('(.-)%s*$') if regexp~='' then outer_text = outer_text .. '\r\n ' .. regexp end end local ABPCSS = hc.get_global_table_item('ABPSiteSpecificCSS', user) if ABPCSS then -- правило "AdBlockPlus:Скрытие элементов" local request_domain = url:match('^https?://([^:/?]*)') or '' local doms, n = _CM_TO_REGEXP(request_domain):gsub('.-%.', '%0)?') local ss = '' function _CM_ABP_CSS_CALLOUT(_, _, offset_vector, start_match, current_position, _, capture_last) local domains = ABPCSS:sub(offset_vector[2*capture_last]+1, offset_vector[2*capture_last+1]) for domain in domains:gmatch('~([^,]+)') do if request_domain==domain or request_domain:sub(-#domain-1)=='.' .. domain then return 1 end end ss = ss .. '\r\n ' .. ABPCSS:sub(start_match, current_position-1) return 1 end re.set_callout('_CM_ABP_CSS_CALLOUT') -- hc.put_to_log('^(?:' .. string.rep('(?:', n) .. doms .. [[)?(?=[,#]),?+(?>(.*?)\#\#)\K.+?$(*SKIP)(?C1)]]) re.find(ABPCSS, '^(?:' .. string.rep('(?:', n) .. doms .. [[)?(?=[,#]),?+(?>(.*?)\#\#)\K.+?$(*SKIP)(?C1)]]) re.set_callout(nil) outer_text = outer_text .. ss end body = outer_text hc.answer_header = 'HTTP/1.1 200 OK (CM)\r\nConnection: Keep-alive\r\nServer: HandyCache\r\nContent-Type: text/css; charset=utf-8\r\nContent-Length: ' .. #body .. '\r\nCache-Control: no-cache, must-revalidate\r\nPragma: no-cache\r\n\r\n' hc.answer_body = body else local ims = header:match('If%-Modified%-Since:%s*(.-)[\r\n]') if not ims or hc.str_to_systime(ims)\Q]] .. hc.url .. [[\E|\Q]] .. hc.url:gsub('^http', 'https', 1) .. [[\E)\K(?=\s|\z)]], '#~#')) end return end -- --------------------------- ПРИНИМАЕМ РЕШЕНИЕ О НЕОБХОДИМОСТИ РАБОТЫ С ОТВЕТОМ ----------------------- if (AnswerCode~='200' and _CM_USERS.ALL.BypassNon200Answers) or -- ответ сервера не 200 и включено 'Не обрабатывать ответы с кодом, отличным от 200' re.find(header_low, [[\A\S++\s++(?>1\d\d|[23]04)|^Content-Length:\s*+0\s]]) or -- ответ 1хх, 204, 304, а также с Content-Length:0 не имеет тела (_CM_USERS.ALL.BypassHCOwnAnswers and header_low:find('server: handycache', 1, true)) -- собственный ответ НС и включено 'Не обрабатывать ответы, сформированные НС' (вот только это может быть и ответ "внешнего" НС) then return -- выходим без обработки end local mindex = hc.monitor_index _CM_CURRENT_REQUEST_DATA = _CM_DATA[mindex] if _CM_USERS.ALL.BypassNoHits and hc.get_global_table_item('CM_URLCache', '-' .. _CM_CURRENT_REQUEST_DATA.URL) and not _CM_CURRENT_REQUEST_DATA.InteractiveModeOn then hc.monitor_string = hc.monitor_string .. 'CM:0 hits(cached)' return end _CM_CURRENT_REQUEST_DATA.ContentTypeByHeader = header_low:match('content%-type:%s*(%S-)[;%s]') _CM_CURRENT_REQUEST_DATA.ContentTypeByHeader = _CM_OPTIONS.ctypes[_CM_CURRENT_REQUEST_DATA.ContentTypeByHeader] or _CM_CURRENT_REQUEST_DATA.ContentTypeByHeader local charset = header_low:match('charset="?([^%s";]+)') _CM_CURRENT_REQUEST_DATA.CharsetByHeader = charset and _CM_CODINGS_ALIASES[charset] if _CM_CURRENT_REQUEST_DATA.ContentTypeByHeader=='html' then _CM_CURRENT_REQUEST_DATA.Referrer = _CM_CURRENT_REQUEST_DATA.URL else if _CM_USERS.ALL.BypassNonHTML then return end _CM_CURRENT_REQUEST_DATA.Referrer = header_low:match('[\r\n]referer:[^\r\n%S]*(%S+)') or _CM_CURRENT_REQUEST_DATA.URL end -- если тип контента уже определен, загружаем для него правила if _CM_CURRENT_REQUEST_DATA.ContentTypeByHeader~='html' then InitiateWorkers() if not _CM_CURRENT_REQUEST_DATA.RawWorker and not _CM_CURRENT_REQUEST_DATA.Worker then return end end _CM_CURRENT_REQUEST_DATA.FirstChunk = true hc.call_me_for('BeforeAnswerBodySend') -- collectgarbage() -- clck1 = clck1 + os.clock() end function BeforeAnswerBodySend() -- hc.put_to_log('------------ BeforeAnswerBodySend BEGINS -----------\r\n', clck1) -- clck1 = clck1 - os.clock() if _CM_UPDATE then CMBODY = (CMBODY or '') .. hc.answer_body if hc.last_part then if hc.url==_CM_MAIN_URL then local Min_HC_Version = re.find(CMBODY, [[^\s*+@min_HC_version\s*+\K\S++]]) or '0.0.0.0' local HC_Version = hc.version_number if HC_Version(.*?)\#~\#){2}(?>\Q]] .. hc.url .. [[\E|\Q]] .. hc.url:gsub('^http', 'https', 1) .. [[\E)]], 1), 'wb', CMBODY) process_update(re.replace(UpdateList, [[\#~\#(?>\Q]] .. hc.url .. [[\E|\Q]] .. hc.url:gsub('^http', 'https', 1) .. [[\E)\K(?=\s|\z)]], '#~#1')) end CMBODY, CFGBODY = nil end return end local mindex = hc.monitor_index _CM_CURRENT_REQUEST_DATA = _CM_DATA[mindex] if _CM_CURRENT_REQUEST_DATA.UnregisterBeforeAnswerBodySend then return end -- обработчик "отключен" -- clck2 = clck2 - os.clock() local new_body, partial = hc.answer_body, not hc.last_part and 'hard' or nil -- hc.put_to_log('HTML_Worker:Process: ', hc.url, '\r\nпоступил фрагмент длиной ', #new_body, partial and '' or ' (последний)') if _CM_CURRENT_REQUEST_DATA.FirstChunk then -- если это первый чанк ответа -- иногда Content-Type заголовка ответа содержит text/html, однако на самом деле ответ не является HTML -- СМ пытается угадать действительный тип контента. По мере получения примеров список может расширяться if re.find(new_body, [[\A(?:\xEF\xBB\xBF)?+(?>\s++|/\*.*?\*/)*+[;(\s]*+(?>document\.write|function|var\s|{"\w++":)]]) and _CM_CURRENT_REQUEST_DATA.ContentTypeByHeader ~= 'application/json' -- JSON имеет формат JS then _CM_CURRENT_REQUEST_DATA.ContentTypeByBody = 'js' -- это JavaScript (пример: http://www.nix.ru/include/general.js и почти все остальные скрипты, грузимые с http://www.nix.ru/price/price_list.html?section=computers_nix_all elseif _CM_CURRENT_REQUEST_DATA.ContentTypeByHeader=='html' and not new_body:match('<%w') then _CM_CURRENT_REQUEST_DATA.ContentTypeByBody = 'unknown' -- это не HTML (http://gsioutdoors.com/request/pdp_sized_images/65101_h1_f) else -- пытаемся определить тип содержимого по сигнатуре файла -- справочник сигнатур: http://www.garykessler.net/library/file_sigs.html -- справочник MIME-типов: http://svn.apache.org/viewvc/httpd/httpd/branches/2.2.x/docs/conf/mime.types?view=annotate local signatures = { ['\x89\x50\x4E\x47\x0D\x0A\x1A\x0A'] = 'image/png', -- http://morevariantov.ru/bitrix/components/edit_components/catalog/saleimg.php?src=/upload/iblock/53e/53ec3527f8131cf415d925139dc36f23.jpeg&template=/bitrix_personal/templates/morevar ['\xFF\xD8\xFF'] = 'image/jpeg', -- http://zyxel.ru/sites/default/files/catalogue/logo_sky.htm ['\x00\x01\x00\x00\x00'] = 'application/x-font-ttf', -- http://vongomedia.ru/assets/fonts/MtBoCyLI.ttf ['\x77\x4F\x46\x46'] = 'application/x-font-woff ', -- http://vongomedia.ru/assets/fonts/MtBoCyLI.woff ['\x50\x4B\x03\x04'] = 'application/zip', -- http://www.packtpub.com/code_download/11704 ['\x00\x00\x00\x18\x66\x74\x79\x70'] = 'video/mp4', -- http://s5.kinostok.tv/flv/e1b55b8b193c88cb94d91fa5e5ae7a67/uploaded_video/video/30/3054/305409/305409.mp4 } for signature, mime in pairs(signatures) do if new_body:sub(1, #signature)==signature then _CM_CURRENT_REQUEST_DATA.ContentTypeByBody = mime break end end end _CM_CURRENT_REQUEST_DATA.ContentType = _CM_CURRENT_REQUEST_DATA.ContentTypeByBody or _CM_CURRENT_REQUEST_DATA.ContentTypeByHeader if _CM_CURRENT_REQUEST_DATA.ContentType=='html' and not _CM_CURRENT_REQUEST_DATA.CharsetByHeader then -- это html с необъявленным чарсетом. Попытаемся установить его сами. Если не получится, полагаем, что это 'iso-8859-1' -- более крутой алгоритм определения: http://www-archive.mozilla.org/projects/intl/UniversalCharsetDetection.html local charset = new_body:match('
' .. os.date() .. '    ' .. url .. 'Content type: ' .. (hc.answer_header:lower():match('[\r\n]content%-type:%s*([^\r\n]*)') or '') .. '
' .. lng2['User'] .. ': ' .. _CM_USER.Name .. '
' .. lng2['Hits'] .. ': ' .. ReplaceCount .. '
' .. lng2['Monitor index'] .. ': ' .. mindex .. '

' .. lng2['Click on bar to expand it'] .. '
' _CM_WRITE_FILE(_CM_DIR .. 'ContentMasterLog.html', 'ab', _CM_CURRENT_REQUEST_DATA.LogFragment) end if _CM_CURRENT_REQUEST_DATA.Worker and _CM_CURRENT_REQUEST_DATA.ContentType=='html' and _CM_CURRENT_REQUEST_DATA.InteractiveModeOn then -- если интерактивный режим включен, то добавляем на страницу CSS и JS, обеспечивающие его работу new_body = new_body .. '\r\n\r\n' hc.set_global_table_item('CM_DOMS', tostring(mindex), _CM_SAVE_TABLE(_CM_CURRENT_REQUEST_DATA.Worker.document)) end _CM_DATA[mindex], _CM_CURRENT_REQUEST_DATA = nil, nil -- очищаем глобальные переменные -- local cld = clck1 -- clck1 = clck1 + os.clock() -- hc.put_to_log('Content Master: статистика загрузки страницы ', hc.url, string.format( -- '\r\nВремя обработки страницы СМ = %g сек\r\nИз него обработчиком HTML = %g' .. -- '\r\n' .. string.rep('-', 40) .. ' в том числе: ' .. string.rep('-', 40) .. -- '\r\n Замена текста = %g' .. -- '\r\n Построение DOM = %g' .. -- '\r\n Вырез. по прямому указанию пользователя = %g' .. -- '\r\n Вырез. по обрамляющему комментарию = %g' .. -- '\r\n Вырез. по содержанию стартового тэга = %g' .. -- '\r\n Вырез. в CSS = %g' .. -- '\r\n Вырез. в JS = %g' .. -- '\r\n Вырез. по содержащемуся тексту = %g', clck1, clck2, clck6, clck5, clck8, clck7, clck3, clck9, clck10, clck4)) -- if clck1<0 then hc.put_msg(hc.url, '\r\nclck1<0') hc.put_to_log(cld, '\r\n', clck1) end end -- hc.put_to_log('_CM_HTML_WORKER:Process: ', hc.url, '\r\nОТДАН фрагмент длиной ', #new_body, not hc.last_part and '' or ' (последний)', '\r\nНачало = ', new_body:sub(1,200), '\r\nКонец = ', new_body:sub(-200)) hc.answer_body = new_body -- collectgarbage() -- hc.put_to_log('------------ BeforeAnswerBodySend ENDS -----------\r\n', clck1) end