File Manager
Current Path : /usr/share/crm114/ |
|
Current File : //usr/share/crm114/maillib.crm |
#! /usr/bin/crm
#
# maillib.crm - handy library for mail whacking
# Copyright 2009 William S. Yerazunis.
# This file is under GPLv3, as described in COPYING.
##########################################################
#
# :load_cf_file:
#
# Calling Sequence:
# call /:load_cf_file:/ [filename]
# Returns:
# nothing. This routine ONLY sets up variables from the .cf file
#
###########################################################
#
# Load in the specified .cf file. The .cf file needs
# to be in the format
# :varname: /value/
#
# Blank lines and lines starting with a # are ignored
#
# Note that because this happens during the run, stuff set in
# a .cf file will _override_ any comand line arguments that get set.
# This could (and should) probably be changed.
#
:load_cf_file: (:cf_filename:)
{
isolate (:option_txt:)
isolate (:ev:)
isolate (:verbose_startup:)
#
# Part 1 - read in the options/configuration file
#
{
{
match <absent> [:cf_filename:] /.+/
return
}
input [:*:cf_filename:] (:option_txt:)
}
#
#
# reset loop for matching to start of :option_txt:
match [:option_txt:] //
# and loop till there are no more options.
{
# find a line that looks like a parameter setting...
match < fromend nomultiline > (:line: :name: :value:) \
[:option_txt:] /^[ ]*(:[[:graph:]]+:)[ \t]+\/(.*)\//
{
# don't execute the assign if there's
# a # at the start of the line.
match <absent> [:name:] /^\x23/
{
# Verbose startup?
match [:verbose_startup:] /SET/
output / :*:name:\n :*:value:\n/
}
isolate (:*:name:) /:*:value:/
}
liaf
}
}
# All done, return.
return
###########################################################
#
# :mail_preprocess:
#
# Calling Sequence:
# call /:mail_preprocess:/ [text to be preprocessed]
#
# Returns:
# the processed text
#
###########################################################
# Preprocess a piece of mail to whatever we've specified in the
# loaded .cf file setup.
:mail_preprocess: (:mutilation_input:)
#
# We take the input and create a mutilated, annotated copy
# of the incoming text. The mutilations are defined by
# whatever the .cf file has set up.
# will become an annotated _copy_ of the incoming text,
# with whatever changes we think will help us classify better.
#
# We clip m_text to be the first :decision_length: characters of
# the incoming text
#
match (:m_text:) [:mutilation_input: 0 :*:decision_length:] /.*/
isolate (:m_text:)
#
# :b_text: is the text with base64's expanded.
isolate (:b_text:) /:*:m_text:/
#
# :i_text: is the text with Hypertextus Interruptus removed.
isolate (:i_text:) /:*:m_text:/
#
isolate (:commentbin:) //
#
#
# do we do any expansions?
{
# expansion 1: - do we perform base64 expansions?
{
match [:do_base64:] /yes/
{
# yes, expand base64's if there are any
#
# Note: some spams don't even bother to use
# a 'Content-Transfer-Encoding marker,
# and even fewer use Content-Type: text/whatever
# so we have to sort of wing it, when to expand
# what _might_ be base64 and when to ignore it.
# For now, if it says it's a base64, it gets
# expanded, no matter what the type. Maybe
# someday someone will put in a lockout for
# things like .jpg files, .doc files, etc.
#
match [:m_text:] <nocase> (:a: :h: :b:) \
/(Content-Transfer-Encoding): base64(.*)/
match (:c:) [:b:] \
/([a-zA-Z0-9+=!\/]+:*:_nl:){2,200}/
isolate (:exp_text:) //
syscall (:*:c:) (:exp_text:) /:*:mime_decoder: /
# and stuff the result back into m_text for
# classification right in context.
alter (:c:) /:*:exp_text:/
# and mark this piece of mime as "prior".
alter (:h:) /Content-Transfer-Prior-Encoding/
# repeat till no more Mime base64 encodings
liaf
}
}
# Expansion 2 - fetch and insert URLs into the stream for further
# analysis. BUG NOTE: as originally written, this was fully recursive
# without limit, and might concieveably spider the entire web. The
# EVAL statement limits total fetched length to not more than
# one fetch more than :decision_length:
{
match [:expand_urls:] /yes/
{
match [:m_text:] (:url:) /http:\/\/[[:graph:]]+/
isolate (:wget_output:) //
syscall /:*:url_fetch_cmd: :*:url: | :*:url_trim_cmd: / \
() (:wget_output:)
alter (:url:) /:*:url:\n :*:wget_output: \n/
eval /:@: :#:m_text: < (:*:decision_length: \/ 4) :/
liaf
}
}
# expansion 3 : do we bust HTML comments ( a.k.a.
# hypertextus interruptus) out?
{
match [:undo_interruptus:] /yes/
{
match [:m_text:] (:comment:) /<!--([^-]|-[^-]|--[^>])*-->/
alter (:commentbin:) /:*:commentbin: :*:comment:/
alter ( :comment: ) //
liaf
}
# if we had at least 80 characters worth of comments, then
# it's worth using the decommented text, else not.
# (this my personal judgement call)
{
{
match [:commentbin:] /(.){80,}/
}
alius
{
alter (:commentbin:) //
}
}
}
}
# and reassemble the mucked-over text into the :m_text: var, always
# with the base64's expanded, then the extacted comments
#
{
alter (:m_text:) \
/:*:m_text: \n :*:commentbin: \n\n/
}
#########################################################
#
# Do we want to do any rewrites before running?
#
{
match [:rewrites_enabled:] /yes/
#
# NOTE CHANGE THIS ONE TO ISOLATE AND THE PROGRAM FAILS!
isolate (:rewrites:) //
isolate (:fileprefix:) <default> //
input (:rewrites:) [:*:fileprefix:rewrites.mfp]
# reset matching on rewrites to start of string - if no string, no more
# processing of rewrites !!
match [:rewrites:] //
#
#
{
# Grab the next regex; turn the one-per-line patterns into a
# regex and a replacement string.
# First, do the line-spanning regexes.
match <fromend nomultiline> (:ch: :fr1: :to:) [:rewrites:] /(.+)>-->(.*)/
# see if the "fr" regex matches anywhere
match [:m_text:] //
{
match [:m_text:] <fromend> (:place:) /:*:fr1:/
# Yep, it matched... alter it and do it again
#
alter (:place:) /:*:to:/
liaf
}
# Nope, didn't match... grab the next regex and try again,
liaf
}
#
# reset back to the start of the rewrites.
#
match [:rewrites:] //
#
# and do it again for non-line-spanners
{
# Go through and do it again, except this time do it for
# the non-line-spanning regexes.
match <fromend nomultiline> (:ch: :fr2: :to:) [:rewrites:] /(.+)>->(.*)/
# see if the "fr" regex matches anywhere
{
match [:m_text:] <nomultiline> (:place:) /:*:fr2:/
# Yep, it matched... alter it and do it again
#
alter (:place:) /:*:to:/
liaf
}
# Nope, didn't match... grab the next regex and try again,
liaf
}
} # done with rewrites.
# all done; m_text now has the fully mutilated text.
return /:*:m_text:/
###############################################################
#
# This is Mungmail - these are the replacement routines for
# formail(), to remove dependency on formail() being in every
# distribution (because formail() isn't _in_ every distribution)
#
#
# Add a new header
:mungmail_add: (:new_header:)
{
# Grab the current headers
call /:mungmail_grab_current_headers:/
alter (:current_headers:) /:*:current_headers::*:new_header:\n/
return
}
#
# extract a header (first of them found)
#
:mungmail_extract: (:header_name:)
{
# Extract the header with the given field type, and
# return that. Note that we add the colon here; don't
# put it into the desired_header string.
#
call /:mungmail_grab_current_headers:/
{
match [:current_headers:] <nocase> (:: :desired_header:) \
/(?:^|\n)(:*:header_name: *: ([^\n]|\n[[:space:]])*)/
return /:*:desired_header:/
}
return //
}
#
# delete all current headers of this type
#
:mungmail_delete: (:new_header:)
{
call /:mungmail_grab_current_headers:/
{
match (:new_header_type:) [:new_header:] /[[:graph:]]+/
}
#
# a LIAF-loop to delete any header (including continuations) that
# has a type that matches the new_header_type.
{
match [:current_headers:] (:kill_this_line:) \
/:*:new_header_type: ([^\n]|\n[[:space:]])*\n/
alter (:kill_this_line:) //
liaf
}
}
return
# delete all current headers of this type, insert ours instead.
#
:mungmail_unique: (:new_header:)
{
call /:mungmail_grab_current_headers:/
{
match (:new_header_type:) [:new_header:] /[[:graph:]]+/
}
call /:mungmail_delete:/ [:*:new_header:]
call /:mungmail_add:/ [:*:new_header:]
}
return
#
# Helper routine to get the current mail headers of :_dw:
#
:mungmail_grab_current_headers:
{
{
# Grab everything before the first \n\n
match (:: :current_headers:) /(([^\n]+\n)+)\n/
# output /-A-->:*:current_headers:<---\n/
return
}
# if we got here, it wasn't a real message (void body, and/or no
# doubled newline) but it might still have useful text anyway.
# Is there a final newline?
{
match (:current_headers:) /^.*\n$/
# output /-B-->:*:current_headers:<---\n/
return
}
# if we got to here, then there wasn't even a final newline.
# That's a violation of RFC, we'll add it.
{
alter (:_dw:) /:*:_dw:\n/
match (:current_headers:) /.+/
# output /-C-->:*:current_headers:<---\n/
return
}
fault / Couldn't manage to find the headers, though I tried really hard\n/
}
return
#
# find header arg1, append comment arg2-n. If no
# such header, create it, and add the comment. Note that
# neither the header name nor the comment can contain spaces.
#
:mungmail_add_comment: (:ac_args:)
{
# parse input args to this routine
match [:ac_args:] (:: :header: :comment:) /([[:graph:]]+) ([[:graph:]]+)/
{
# find the header if it exists
match (:found: :hd: :tail:) <nomultiline nocase> /^(:*:header:) (.*)/
alter (:tail:) /:*:tail: (:*:comment:)/
}
# Commented out by Milan Zamazal <pdm@debian.org> on 2007-01-28 as suggested on
# the crm114 mailing list. If this is wrong please let me know.
# alius
# {
# # no such header. make one.
# call /:mungmail_add:/ [:*:header: (:*:comment:)]
# }
}
return
#
# change_subject_line
#
:mungmail_mung_subject: (:new_tag:)
# get the Subject: line. If none, make one.
{
{
match (:subject_line: :subj_text:) <nocase nomultiline> \
/^Subject: (.*)/
}
alius
{
match (:end_of_headers:) /\n\n/
alter (:end_of_headers:) /\nSubject: ( none supplied in original message )\n\n/
match (:subject_line: :subj_text:) <nomultiline> /^Subject: (.*)/
}
}
{
#
# If we are re-sending this, we want to de-fang the
# subject, otherwise we don't.
match [:reject_address:] /[a-zA-Z0-9]/
# Paolo P. suggests this alteration to avoid subversion
# by enclosing an alternate target in "marks". We always
# have to do this.
{
match (:dq:) [:subj_text:] /\$/
alter (:dq:) /USD/
liaf
}
#
# and translate away anything that might be a shell subterfuge
translate (:subj_text:) [:subj_text:] /^-a-zA-Z0-9!., /
}
#
# If the user asked for a spam-flagging string, put the flagging
# string into the subject.
#
{
match [:new_tag:] /./
alter (:subj_text:) \
/:*:new_tag: :*:subj_text:/
}
return
#
# Mark a piece of mail with Reaver IDs. Hopefully one or the
# other of these will survive your local mailer.
#
:mungmail_add_cache_info: (:cid:)
{
call /:mungmail_unique:/ [X-CRM114-CacheID: sfid-:*:cid: ]
call /:mungmail_add_comment:/ [Message-Id: sfid-:*:cid:]
}
###############################################################
#
# Reaver Cache routines
#
# Assumptions= the var :text_cache: contains the name of
# the cache directory
#
# Assure that the text cache exists
:reavercache_init:
{
match [:text_cache:] /./
{
### If the text_cache dir isn't there, create it
# and it's subdirectories.
#
isolate (:tmp:) //
syscall () (:tmp:) /ls :*:text_cache: 2>&1 /
match [:tmp:] <absent> /texts/
syscall () () /mkdir :*:text_cache: /
syscall () () /mkdir :*:text_cache:\/texts /
syscall () () /mkdir :*:text_cache:\/prob_good /
syscall () () /mkdir :*:text_cache:\/prob_spam /
syscall () () /mkdir :*:text_cache:\/known_good /
syscall () () /mkdir :*:text_cache:\/known_spam /
syscall () () /mkdir :*:text_cache:\/empty /
}
}
return
#
# Put some text into the cache;
# side effect:
# variable :reaver_cacheid: to the filename (no directory)
# variable :long_cacheid: is set to the file alone (no directory) name
#
:reavercache_store: (:text:)
{
match [:text_cache:] /./ # Don't store it if no reavercache desired
isolate (:system_time: :msg_hash:) //
syscall () (:system_time:) /date +%Y%m%d_%H%M%S_%N /
match [:system_time:] ( :: :cacheid: ) /([[:graph:]]+)..../
hash (:msg_hash:) /:*:text:/
isolate (:cacheid:) /:*:cacheid:/
# It's unclear if the following increases security at all.
isolate (:cacheid:) /:*:cacheid:_:*:msg_hash:/
isolate (:long_cacheid:) /:*:text_cache:\/texts\/:*:cacheid:/
output [:*:long_cacheid:] /:*:text:/
}
return
#
# And the mother of all traps...
#
#
trap (:broken_program_message:) /.*/
{
accept
output /:*:_nl: Aw, crud. maillib.crm broke. Here's the error: :*:_nl:/
output /:*:broken_program_message:/
output [stderr] /:*:_nl: ERROR: maillib.crm broke. Here's the error\: :*:_nl:/
output [stderr] /ERROR: :*:broken_program_message:/
}
exit /:*:program_fault_exit_code:/
File Manager Version 1.0, Coded By Lucas
Email: hehe@yahoo.com