#============================================================================== #- #- erbot.mod - module file for erbot.l # erbot E-mail Robot - execute command via e-mail .forward file. # I wanted to call this erobot but my fingers kept typing # erbot, and I guess they should know. So it's "erbot". # # To modify this program you need the Libero tool. # # Written: 96/01/19 Pieter Hintjens # Revised: 96/02/06 # # Copyright (c) 1996 Pieter Hintjens, may be freely distributed. # # See erbot.doc for a full description of this program. # Implements various NYAIS (not yet an internet standard) protocols: # # RCEP 1.0 Remote Command Entry Protocol # EMFTP 1.0 E-Mail File Transfer Protocol # EMRP 1.0 E-Mail Routing Protocol # #- Skeleton generated by LIBERO 2.10 on 22 Jan, 1996, 14:13. #============================================================================== ########################## INITIALISE THE PROGRAM ######################### function initialise_the_program \ (path) { rcep_version = "1.0" # Protocol version number emftp_version = "1.0" # Protocol version number emrp_version = "1.0" # Protocol version number config_file = "erbot.ini" # Path and name of config file srand () # Build name for response workfile session_key = substr (rand (), 3) # Get random session key "pwd" | getline path # Get current path close ("pwd") # as prefix for work files if (path != "") # If not known, ignore path = path "/" # Name of eventual response file response = path "rsp" session_key # Name of copied incoming message message = path "msg" session_key # Macro for system() call that redirects all output to response file capture = ">>" response " 2>>" response the_next_event = ok_event } # ------------------------------------------------------------------------- # send_reply # # Sends contents of response file to 'reply_to', with specified subject. # Deletes the response file after it is sent. function send_reply (subject) { system (MAIL_COMMAND " \"" subject "\" " reply_to "<" response) system (DEL_COMMAND " " response) } # ------------------------------------------------------------------------- # date_now # # Returns current date/time string "Mon 2 Dec, 1996 13:02:01> " function date_now (datecmd, date) { datecmd = "date +\"%a %e %b, %Y %T> \"" datecmd | getline date close (datecmd) return (date) } # ------------------------------------------------------------------------- # log_msg # # Writes a time-stamped message to the specified log file. Does nothing # if the log file name is empty (i.e. no logging is required). function log_msg (filename, message) { if (filename != "") print date_now() message >> filename } ############################ PARSE CONFIG FILE ############################ function parse_config_file \ (keyword, value) { # Default values for control variables ERBOT_LOGFILE = "erbot.log" # Log file for Erbot errors ERBOT_ADMIN = "" # Erbot administrator address RCEP_PASSWORD = "password" # Required before we allow access RCEP_LOGFILE = "rcep.log" # Log file for RCEP commands EMFTP_ROOT = "pub" # Home location for public files EMFTP_LOGFILE = "emftp.log" # Log file for EMFTP transfers EMRP_ROUTING = "" # EMRP Routine file EMRP_LOGFILE = "emrp.log" # Log file for EMRP messages EMRP_BASKET = "emrp.err" # Unroutable letter basket DIR_COMMAND = "ls -l" # Run ls for permissions DEL_COMMAND = "rm -f" # Remove file with prejudice MAIL_COMMAND = "mail -s" # Mail with subject line RAWMAIL_COMMAND = "sendmail" # Mail a complete message FS = " *= *" # Match "=" maybe with spaces line_number = 0 # Config file line number while (getline < config_file == 1) { line_number++ if ($0 !~ "^#|^$") # Ignore comments and blank lines { sub (/^"/, "", $2) # Remove leading quote sub (/"$/, "", $2) # and trailing quote, if any $1 = toupper ($1) # We want uppercase keyword if ($1 == "ERBOT_LOGFILE") ERBOT_LOGFILE = $2 else if ($1 == "ERBOT_ADMIN") ERBOT_ADMIN = $2 else if ($1 == "RCEP_PASSWORD") RCEP_PASSWORD = $2 else if ($1 == "RCEP_LOGFILE") RCEP_LOGFILE = $2 else if ($1 == "EMFTP_ROOT") EMFTP_ROOT = $2 else if ($1 == "EMFTP_LOGFILE") EMFTP_LOGFILE = $2 else if ($1 == "EMRP_ROUTING") EMRP_ROUTING = $2 else if ($1 == "EMRP_BASKET") EMRP_BASKET = $2 else if ($1 == "EMRP_LOGFILE") EMRP_LOGFILE = $2 else if ($1 == "DIR_COMMAND") DIR_COMMAND = $2 else if ($1 == "DEL_COMMAND") DEL_COMMAND = $2 else if ($1 == "MAIL_COMMAND") MAIL_COMMAND = $2 else if ($1 == "RAWMAIL_COMMAND") RAWMAIL_COMMAND = $2 else { raise_exception(message_event) error_message = \ "Erbot: unrecognised keyword in " config_file \ " at line " line_number ": " $1 } } } close (config_file) FS = " " # Reset field separator } ########################## PARSE AND SAVE MESSAGE ######################### function parse_and_save_message \ () { reply_to = "" # Name of correspondent sent_to = "" # Name of recipient protocol = "" # Protocol, rcep or emftp password = "" # Password, if required command = "" # Command for protocol argument = "" # Argument for command subject = "" # Subject line filetype = "binary" # Default EMFTP filetype # Copy & parse header while (getline == 1) { print $0 >> message $1 = tolower ($1) # Header is case-insensitive if ($0 ~ "^$") break # Blank line = end of header else if ($1 == "from:") { # From: address if ($0 ~ "<.*>") # or From: My Name
{ match ($0, "<.*>"); reply_to = substr ($0, RSTART + 1, RLENGTH - 2) } else reply_to = $2 # Pick-up reply address } else if ($1 == "to:") { # split ($0, words, "@") # Pick-up 'To:' name sent_to = words [1] } else if ($1 == "subject:") { subject = substr ($0, 9) # Save subject line protocol = tolower ($2) # Get protocol name if (protocol == "rcep") # RCEP password command [argument] { password = $3 command = tolower ($4) argument = $5 } else if (protocol == "emftp") # EMFTP command argument [filetype] { command = tolower ($3) argument = $4 if ($5 == "binary" || $5 == "text") filetype = $5 } } } # Copy & parse rest of message print >> message while (getline == 1) print $0 >> message } ######################### CHECK IF ROUTING WANTED ######################### function check_if_routing_wanted \ () { if (EMRP_ROUTING != "") the_next_event = ok_event else the_next_event = none_event } ######################## GET FIRST ROUTING ADDRESS ######################## function get_first_routing_address \ (line_number, fall_through, field) { line_number = 0 fall_through = 0 # When 1, we match next line addresses = 0 # Global: size of routing_address [] log_msg(EMRP_LOGFILE, date_now() "sent_to") while (getline < EMRP_ROUTING == 1) { line_number++ if ($0 !~ "^#|^$") # Ignore comments and blank lines { $1 = tolower ($1) # We want lowercase alias if ($1 == sent_to || $1 == "-" || fall_through) { if ($2 == "") # Fall through if no receipient fall_through = 1 else { # Load addresses from remainder of line for (field = 2; $field != ""; field++) routing_address [++addresses] = tolower ($field) break; # Found - stop reading routing file } } } } close (EMRP_ROUTING) if (addresses == 0) { system ("cat " message ">>" EMRP_BASKET) raise_exception(message_event) error_message = \ "EMRP: no routing address for " sent_to " in " EMRP_ROUTING log_msg(EMRP_LOGFILE, " -- no address") } # Get next address from table cur_address_index = 0 get_next_routing_address() } ######################### GET NEXT ROUTING ADDRESS ######################## function get_next_routing_address \ () { if (++cur_address_index > addresses) the_next_event = finished_event else { cur_address = routing_address [cur_address_index] log_msg(EMRP_LOGFILE, " -> " cur_address) if (cur_address ~ "^&.*") the_next_event = maillist_event else if (cur_address ~ "^\\*") the_next_event = internal_event else the_next_event = external_event } } ########################### PARSE MAILLIST FILE ########################### function parse_maillist_file \ (filename) { # Addresses are in RFC 822 format filename = substr (cur_address, 2) while (getline < filename == 1) { if ($0 ~ "<.*>") # My Name { match ($0, "<.*>"); routing_address [++addresses] = substr($0, RSTART + 1, RLENGTH - 2) } else # name@domain routing_address [++addresses] = $1 } close (filename) } ######################## ROUTE MESSAGE TO RECIPIENT ####################### function route_message_to_recipient \ () { # This does not work (yet) (ever?) return } ####################### CHECK PROTOCOL IS CONSISTENT ###################### function check_protocol_is_consistent \ (check_protocol) { check_protocol = substr (cur_address, 2) if (protocol != check_protocol) { print date_now() "'subject: xxxx' protocol is not correct" >> response send_reply("re: " subject " - error") raise_exception(exception_event) } } ########################### CHECK PROTOCOL TYPE ########################### function check_protocol_type \ () { if (protocol == "rcep") the_next_event = rcep_event else if (protocol == "emftp") the_next_event = emftp_event else the_next_event = other_event } ##################### REPLY UNKNOWN INTERNAL PROTOCOL ##################### function reply_unknown_internal_protocol \ () { print date_now() "unknown protocol" >> response send_reply("re: " subject " - error") } ############################# LOG RCEP COMMAND ############################ function log_rcep_command \ () { log_msg(RCEP_LOGFILE, date_now() reply_to ":" subject) } ######################## BUILD RCEP RESPONSE HEADER ####################### function build_rcep_response_header \ () { print date_now() "Erbot RCEP v" rcep_version > response } ########################### CHECK RCEP PASSWORD ########################### function check_rcep_password \ () { if (password != RCEP_PASSWORD) { # Signal access violation to administrator; don't reply to message raise_exception(message_event) error_message = \ "Erbot: bad password used by " reply_to ":" subject log_msg(RCEP_LOGFILE, " -- bad password") } } ######################### CHECK RCEP COMMAND TYPE ######################### function check_rcep_command_type \ () { if (command == "run") the_next_event = run_event else if (command == "put") the_next_event = put_event else if (command == "get") the_next_event = get_event else if (command == "clone") the_next_event = clone_event else if (command == "version") the_next_event = version_event else if (command == "") the_next_event = empty_event else { print date_now() "bad RCEP command - message rejected" >> response send_reply("re: rcep " command " - error") raise_exception(exception_event) log_msg(RCEP_LOGFILE, " -- bad command") } } ############################ RCEP RUN COMMANDS ############################ function rcep_run_commands \ (command) { # Read through message, skipping header, until we hit a blank line while (getline < message == 1) if ($0 ~ "^$") break while (getline < message == 1) { if ($0 != "") { command = $0 print date_now() command >> response log_msg(RCEP_LOGFILE, command) while (command | getline == 1) { print $0 >> response log_msg(RCEP_LOGFILE, $0) } close (command) } } print date_now() "Erbot RCEP finished" >> response send_reply("re: rcep run - ok") close (message) } ############################# RCEP UPLOAD FILE ############################ function rcep_upload_file \ (output) { output = "" while (getline < message == 1) { $1 = tolower ($1) if ($1 == "begin") # Expect line: { # begin nnn output = $3 # Get name of output file break # and end scan of file } } close (message) if (output == "") { send_reply("re: rcep put - invalid uuencoded file") log_msg(RCEP_LOGFILE, " -- no 'begin' in uuencoded file") } else { system ("uudecode " message capture) # Move uploaded file to destination if required if (argument != "") system ("mv " output " " argument capture) send_reply("re: rcep put " output " - ok") } } ############################ RCEP DOWNLOAD FILE ########################### function rcep_download_file \ () { system ("uuencode " argument " " argument capture) send_reply("re: rcep get " argument " - ok") } ############################ RCEP CLONE SERVER ############################ function rcep_clone_server \ () { system ("cp erbot " response) send_reply("re: rcep clone - ok") } ############################ RCEP SEND VERSION ############################ function rcep_send_version \ () { print "Copyright (c) 1996 Pieter Hintjens" >> response print "Commands:" >> response print " run Execute commands contained in body" >> response print " put [file] Uploads uuencoded file [as file]" >> response print " get file Downloads file as uuencoded message" >> response print " clone Return Erbot program as message" >> response print " version Return version information" >> response send_reply("re: rcep version - v" rcep_version " ok") } ############################ LOG EMFTP COMMAND ############################ function log_emftp_command \ () { log_msg(EMFTP_LOGFILE, date_now() reply_to ":" subject) } ####################### BUILD EMFTP RESPONSE HEADER ####################### function build_emftp_response_header \ () { print date_now() "Erbot EMFTP v" emftp_version > response } ######################### CHECK EMFTP COMMAND TYPE ######################## function check_emftp_command_type \ () { if (command == "get") the_next_event = get_event else { print date_now() "bad EMFTP command - message rejected" >> response send_reply("re: emftp " command " - error") raise_exception(exception_event) log_msg(EMFTP_LOGFILE, " -- bad command") } } ########################### EMFTP DOWNLOAD FILES ########################## function emftp_download_files \ (number_files, filename, count, permissions) { number_files = 0 while ("find " EMFTP_ROOT " -name \"" argument "\" -print" | getline == 1) { count = split ($0, words, "/") filename = words [count] DIR_COMMAND " " $0 | getline permissions close (DIR_COMMAND " " $0) if (substr (permissions, 8, 1) == "r") { if (filetype == "binary") { build_emftp_response_header() system ("uuencode " $1 " " filename capture) } else system ("cp " filename " " response) number_files++ send_reply("re: emftp get " filename " - ok") } } if (number_files == 0) { send_reply("re: emftp get " argument " - not found") log_msg(EMFTP_LOGFILE, " -- no files found") } } ############################# LOG FATAL ERROR ############################# function log_fatal_error \ () { if (ERBOT_ADMIN != "") { reply_to = ERBOT_ADMIN print date_now() error_message >> response send_reply("Erbot reported a fatal error") } log_msg(ERBOT_LOGFILE, error_message) } ############################ GET EXTERNAL EVENT ########################### function get_external_event \ () { return } ########################## TERMINATE THE PROGRAM ######################### function terminate_the_program \ () { system (DEL_COMMAND " " message) the_next_event = terminate_event }