#!/usr/bin/python """ $Id: sxaa,v 1.6 2006/03/29 02:32:54 dkelly Exp $ """ import commands import os,sys import getopt import re from types import * # # Miscellaneous globals # gDQuote = '"' gDebugMode = False gB = ("s", "") gGS = chr(29) gUserDir = "" gWildcard = "%" # # Prefices for Scalix commands # gScalixBin = "/opt/scalix/bin/" gScalixDiag = "/opt/scalix/diag/" # # Autoaction file # gAAFile = "000003d" gAAForwardDL = "000003e" gAAForwardTxt = "000003f" gAAReplyTxt = "000003g" gForwardDLFile = "/tmp/sxaa.dl." + str(os.getpid()) dl_template = [ ['HEADER', '(DN)', '1', '0', '2', '1000', '0x0', '0x0', '0'], ['DL_COUNTS', '(DN)', '0x0', '1', '0', '1', '0', '0', '0', '0'], ['TO', '(0x1000 + DN)', '70/1/1 00:00.00', '0x0', ('s', '_USER_NAME_'), gB, gB, '0x2'] ] header_template = ['FILTER_STRING','()','0x0','0x0','39','1','0','0',('s','_HEADER_'), gB, gB] sender_template = ['FILTER_NAME','()','0x0','0x8000','3','1','0','0',('s','_SENDER_')] subject_template = ['FILTER_STRING','()','0x0','0x0','2','1','0','0',('s','_SUBJECT_'), gB, gB] item_class_template = ['FILTER_STRING','(DN)','0x0','0x0','14','1','0','0',('s','_ITEM_CLASS'), gB, gB] gAttrTable = [ "", "Message Type", "Subject", "Sender", "Priority", "Importance", "Sensitivity", "Create Date", "Expiry Date", "Delivery Date", "Present Date", "Entry Status", "Acks Requested", "Item Type", "Item Class", "Item Size", "Hidden", "Recipient Category", "MAPI attach flag", "DL Name", "Text Body", "Unread", "IMAP Flagged", "IMAP Draft", "IMAP Deleted", "IMAP UID", "IMAP Answered", "MAPI Flags1", "MAPI Flags2", "MAPI Rel Flags", "No AutoForward", "", "Attach Date", "", "", "", "", "Message ID", "", "Arpa Header", "Auto Forward", "Auto Reply" ] gOpTable = [ "present", "=", "<", "<=", ">", ">=", "bit" ] def RunCommand(cmd): global gDebugMode results = [] errors = [] status = 0 if gDebugMode: print cmd temp_stderr = "/tmp/sxaa.stderr." + str(os.getpid()) pipe = os.popen(cmd + " 2>" + temp_stderr) results = pipe.readlines() status = pipe.close() stderr = open(temp_stderr) errors = stderr.readlines() stderr.close() os.unlink(temp_stderr) if status is None: status = 0 if gDebugMode: print "Status : " + str(status) print "Results: ", results print "Errors : ", errors return status, results, errors def WhitespaceParse(line): global gDebugMode result = [] i = 0 quoted = False escaped = False current_item = "" date_value = False first_space = True while i < len(line): if line[i] == '\\': escaped = True elif line[i] == gDQuote: if escaped: current_item = current_item + line[i] elif quoted: result.append(('s', current_item)) current_item = "" quoted = False first_space = True else: quoted = True string_value = True escaped = False elif line[i] == '/' and not quoted: date_value = True current_item = current_item + line[i] else: if line[i] == " ": if quoted or date_value: current_item = current_item + line[i] date_value = False else: if first_space: if len(current_item) > 0: result.append(current_item) first_space = False current_item = "" else: current_item = current_item + line[i] first_space = 1 escaped = False i += 1 if len(current_item) > 0: result.append(current_item) return result def checkCurrent(): global gDebugMode currentSet = False cmd = gScalixBin + "omrealpath '~/'" status,results,errors = RunCommand(cmd) return status==0 def listInstances(): global gDebugMode cmd = gScalixBin + "omcheckgc -l" status,results,errors = RunCommand(cmd) if status == 0: return results else: return {} def GetUserDir(username): global gDebugMode global gUserDir gUserDir = "" cmd = gScalixBin + "omshowu -n \"" + username + "\" -f" status,results,errors = RunCommand(cmd) if status == 0: for line in results: items = line[:-1].split(":") if items[0].startswith("User Folder"): gUserDir = items[1] break last_dirsep = gUserDir.rfind("/") gUserDir = gUserDir[:last_dirsep + 1].replace("~","~scalix") cmd = gScalixBin + "omrealpath " + gUserDir status, results,errors = RunCommand(cmd) if status == 0: gUserDir = results[0][:-1] else: gUserDir = "" else: for line in errors: print line return gUserDir def LoadTF(filename): global gDebugMode global gWildcard tf_file = [] if os.path.exists(filename): cmd = gScalixDiag + "tfbrowse -w '" + gWildcard + "' -c -i " + filename status, results,errors = RunCommand(cmd) if status == 0: for line in results: tf_file.append(WhitespaceParse(line[:-1])) return tf_file def SaveTF(filename, data): global gDebugMode global gWildcard char_match = re.compile(r'\D') temp_file = "/tmp/sxaa." + str(os.getpid()) fp = open(temp_file, "w+") for line in data: i = 0 for item in line: if isinstance(item, TupleType): value = item[1] if item[0] == 's': write_item = value.replace('"','\\"') write_item = '"' + write_item + '"' else: write_item = value else: write_item = item print >>fp, write_item," ", i += 1 print >>fp fp.close() if os.path.exists(filename): os.rename(filename, filename + ".bak") cmd = gScalixDiag + "tfbrowse -w '" + gWildcard + "' -g -i " + temp_file + " -o " + filename status,results,errors = RunCommand(cmd) if status == 0: os.unlink(temp_file) else: # # tfbrowse doesn't differentiate between errors and warnings # errorCount = 0 for stderr_line in errors: line = stderr_line[:-1].strip() if line.startswith("**") and not line.startswith("** Warning -"): errorCount += 1 if errorCount == 0: os.unlink(temp_file) else: for line in errors: print line def ListActions(aa_file, reference): global gDebugMode if reference == -1: print "OOF FLT OLK %-4s %-10s\t%-30s\t%-10s" % ("AANO","ACTION","TITLE","STATE") print "--- --- --- %-4s %-10s\t%-30s\t%-10s" % ("----","----------","------------------------------","--------") for line in aa_file: if line[0] == "AA_NO": aa_filter = ' ' aa_no = line[3] if int(line[2], 16) & 0x01: state = "disabled" else: state = "enabled" if int(line[2], 16) & 0x04: olk = 'Y' else: olk = ' ' aa_title = line[8][1] elif line[0].startswith("FILTER_"): aa_filter = 'Y' else: if line[0].startswith("AA_"): aa_action = line[0].split("_")[-1] if aa_action.startswith("REPLY") and int(line[3],16) & 0x08: aa_oof = 'Y' else: aa_oof = ' ' print " %c %c %c %-4s %-10s\t%-30s\t%-10s" % (aa_oof, aa_filter, olk, aa_no,aa_action,aa_title,state) else: print "Looking for auto action [ " + str(reference) + "]" def MakeActionActive(aa_file, aa_reference, aa_action): global gDebugMode aa_file_copy = [] for line in aa_file: if line[0] == "AA_NO": aa_no = int(line[3]) if aa_no == aa_reference: if aa_action == "DISABLE": line[2] = hex(int(line[2], 16) | 0x01) else: line[2] = hex(int(line[2], 16) & ~0x01) aa_file_copy.append(line) return aa_file_copy def ToggleRedirectRetain(aa_file, aa_reference): global gDebugMode aa_file_copy = [] aa_found = False for line in aa_file: if line[0] == "AA_NO": aa_no = int(line[3]) if aa_no == aa_reference: aa_found = True else: aa_found = False elif line[0].startswith("AA_") and aa_found: aa_action = line[0].split("_")[-1] if aa_action.startswith("REDIRECT"): if int(line[3], 16) & 0x01: line[3] = hex(int(line[3], 16) & ~0x01) else: line[3] = hex(int(line[3], 16) | 0x01) aa_file_copy.append(line) return aa_file_copy def MakeActionOOF(aa_file, aa_reference, aa_action): global gDebugMode aa_file_copy = [] match_found = False for line in aa_file: if line[0] == "AA_NO": aa_no = int(line[3]) if aa_no == aa_reference: match_found = True else: aa_type = line[0].split("_")[-1] if aa_type.startswith("REPLY") and match_found: if aa_action == "OOF_Y": line[3] = hex(int(line[3], 16) | 0x08) else: line[3] = hex(int(line[3], 16) & ~0x08) match_found = False aa_file_copy.append(line) return aa_file_copy # # Get the default internet route # def GetDefaultRoute(routeOption="-m"): global gDebugMode cmd = gScalixBin + "omshowux " + routeOption status,results,errors = RunCommand(cmd) if not status == 0: default_route = "OU1=internet/" else: i = 1 default_route = "" for item in results[0][:-1].split(","): default_route = default_route + "OU" + str(i) + "=" + item + "/" i += 1 return default_route # # Find the next available AA_NO # def GetNextAANo(aa_file): max_aa_no = 500 for line in aa_file: if line[0] == "AA_NO": aa_no = int(line[3]) if aa_no >= max_aa_no: max_aa_no = aa_no + 1 return max_aa_no def display_indent(indent_level): i = 0 while i < indent_level: print " ", i=i+1 # # Display the auto action details # def showInfo(aa_file, aa_reference): filter = [] aa_action = "" action_found = False action_capture = False filter_found = False filter_capture = False info_line = "" for line in aa_file: if line[0] == "AA_NO": aa_no = int(line[3]) if aa_no == aa_reference: action_found = True filter_capture = True action_capture = True else: filter_capture = False elif line[0].startswith("AA_") and action_capture: aa_action = line[0].split("_")[-1] info_line = line action_capture = False elif line[0].startswith("FILTER_") and filter_capture: filter_found = True filter.append(line) if filter_found: print "Conditions" print "----------" indent_level = 0 for line in filter: output_line = "" if line[0] == "FILTER_START": if int(line[4]) == 1: output_line = output_line + "OR " else: output_line = output_line + "AND " if int(line[2], 16) & 0x01: output_line = output_line + " NOT " output_line = output_line + "( " display_indent(indent_level) print output_line indent_level=indent_level + 2 elif line[0] == "FILTER_END": indent_level = indent_level - 2 if indent_level < 0: indent_level = 0 display_indent(indent_level) output_line = output_line + ")" print output_line elif line[0].startswith("FILTER_"): not_condition = int(line[2], 16) & 0x01 if not_condition: not_value = "not " else: not_value = "" attr_id = int(line[4]) op_id = int(line[5]) if isinstance(line[8], TupleType): value = '"' + line[8][1] + '"' else: value = line[8] output_line= output_line + gAttrTable[attr_id] + " " + not_value + gOpTable[op_id] + " " + value display_indent(indent_level) print output_line if action_found: print "Action" print "------" if aa_action == "REDIRECT": print aa_action,"to",info_line[6][1] if int(info_line[3],16) & 0x01: print "Retain intray copy" if aa_action == "FILE": subfolder = int(info_line[3], 16) & 0x02 folder_name = info_line[6][1] if subfolder > 0: folder_name = "/INBOX/" + folder_name print aa_action,"in",folder_name if aa_action == "REPLY": print aa_action reply_file = gUserDir + gAAReplyTxt+"."+MakeExt(aa_reference) if os.path.exists(reply_file): print "Text" print "----" reply_fp = open(reply_file) reply_contents = reply_fp.read() reply_fp.close() print reply_contents if aa_action == "FORWARD": print aa_action forward_file = gUserDir + gAAForwardDL+"."+MakeExt(aa_reference) if os.path.exists(forward_file): forward_dl = LoadTF(forward_file) for line in forward_dl: if line[0] in ("TO", "CC", "BCC"): print line[0],":",line[6][1] forward_file = gUserDir + gAAForwardTxt+"."+MakeExt(aa_reference) if os.path.exists(forward_file): print "Text" print "----" forward_fp = open(forward_file) forward_contents = forward_fp.read() forward_fp.close() print forward_contents # # Build the autoaction extension # def MakeExt(reference): ref = "" if reference < 1000: ref = ref + str(reference) else: ref = ref + hex(reference / 1000)[2:] + str(reference % 1000) return ref def MakeAddress(action_address): address = "" default_route = GetDefaultRoute() if not action_address.count("@") == 0: name = action_address.split("@")[0] address = "S=" + name + "/" + default_route + "DDT1=RFC-822/DDV1=" + action_address + "/CN=" + name + "/IA=" + action_address else: address = action_address return address def MakeFolderName(folder_string): return_folder = folder_string is_subfolder = False # # Is this a subfolder of the inbox ? # if return_folder.upper().startswith("/INBOX/"): is_subfolder = True return_folder = return_folder[7:] # # Replace all '/' characters with GS_CHAR as the folder separator # return_folder = return_folder.replace("/",gGS) return is_subfolder, return_folder def AddAction(aa_file, action_type, action_address, action_text, retain_intray, filter_type, filter_value, filter_not, action_title): global gDebugMode aux_files = [] if len(aa_file) == 0: aa_file.append(["HEADER","(DN)","1","0","2","1002","0x0","0x0","0","0x0","0x0","0x0","0x0","0x0",gB]) next_aa = GetNextAANo(aa_file) if action_title == "": action_title = "SXAA " + action_type +":" + str(next_aa) if action_type in ("FORWARD", "REPLY", "REDIRECT","FILE"): aa_file.append(["AA_NO","()","0x0", str(next_aa), "0", "0", "0", "0", ('s',action_title),gB, gB, gB, gB]) # # Add any filter that has been supplied # if filter_type == "SUBJECT": subject_template[8] = ("s",gWildcard + filter_value + gWildcard) if filter_not: subject_template[2] = "0x01" aa_file.append(subject_template) if filter_type == "SENDER": sender_template[8] = ("s", "S="+ gWildcard + filter_value + gWildcard) if filter_not: sender_template[2] = "0x01" aa_file.append(sender_template) if filter_type == "HEADER": header_template[8] = ("s",gWildcard + filter_value + gWildcard) if filter_not: header_template[2] = "0x01" aa_file.append(header_template) if filter_type == "ITEM_CLASS": item_class_template[8] = ("s",gWildcard + filter_value + gWildcard) if filter_not: item_class_template[2] = "0x01" aa_file.append(item_class_template) if action_type == "REDIRECT": if retain_intray == "Y": retain_flag = "0x01" else: retain_flag = "0x00" aa_file.append(["AA_REDIRECT","()","0x0", retain_flag,"0","0",("s", MakeAddress(action_address))]) if action_type == "REPLY": aa_file.append(["AA_SIMPLE_REPLY","()","0x0","0x8","1167","0","0",gB,("s", "ISO8859_1"),gB,gB,gB,gB,gB]) aux_files.append([action_text, gAAReplyTxt +"."+ MakeExt(next_aa), "COPY"]) if action_type == "FORWARD": if len(action_text) > 0: aux_files.append([action_text, gAAForwardTxt +"."+ MakeExt(next_aa), "COPY"]) flags = "0x01" else: flags = "0x00" aa_file.append(["AA_SIMPLE_FORWARD","()","0x0",flags,"1167","0","0",gB,("s","ISO8859_1"),gB,gB,gB,gB,gB,gB,gB,gB]) dl_template[2][4] = ("s", MakeAddress(action_address)) SaveTF(gForwardDLFile, dl_template) aux_files.append([gForwardDLFile, gAAForwardDL +"."+ MakeExt(next_aa), "MOVE"]) if action_type == "FILE": subfolder, folder_name = MakeFolderName(action_address) if subfolder: flags = "0x03" else: flags = "0x01" aa_file.append(["AA_FILE", "()", "0x00", flags, "0x00", "0x00", ("s", folder_name), gB, gB]) return aa_file,aux_files def DelAction(aa_file, action_reference): global gDebugMode ignore_line = False aa_file_copy = [] aa_aux_files = [] aa_action = "" for line in aa_file: if line[0] == "AA_NO": aa_no = int(line[3]) if aa_no == action_reference: ignore_line = True else: ignore_line = False aa_file_copy.append(line) else: if not ignore_line: aa_file_copy.append(line) elif line[0].startswith("AA_"): aa_action = line[0].split("_")[-1] ignore_line = False if aa_action == "REPLY": aa_aux_files.append("*." + MakeExt(action_reference)) if aa_action == "FORWARD": aa_aux_files.append(gAAForwardDL + "." + MakeExt(action_reference)) aa_aux_files.append(gAAForwardTxt + "." + MakeExt(action_reference)) return aa_file_copy, aa_aux_files def display_usage(): print "" print "Usage:" print " sxaa --user user" print "" print " sxaa --user user --forward [address@domain | S=Last/G=First/OU1=mailnode] [--text file.txt] [FILTER] [--title ruletitle]" print " sxaa --user user --reply file.txt [FILTER] [--title ruletitle]" print " sxaa --user user --redirect [address@domain | S=Last/G=First/OU1=mailnode] [--retain] [FILTER] [--title ruletitle]" print " sxaa --user user --file foldername [FILTER] [--title ruletitle]" print "" print " where FILTER can be one of:" print " --subject, --sender, --itemclass or --header" print " --not to reverse the meaning of the filter" print "" print " sxaa --user user --del reference" print "" print " sxaa --user user --on reference" print " sxaa --user user --off reference" print "" print " sxaa --user user --oof y --ref reference" print "" print " sxaa --user user --info reference" print "" print " sxaa --user user --toggleretain reference" print "" print " sxaa --help" def main(): global gDebugMode global gWildcard username = "" action = "LIST" action_title = "" action_type = "" action_address = "" action_text = "" action_reference = -1 retain_intray = "" action_filter_type = "" action_filter_value = "" action_filter_not = False # # Check to see if the user has set OMCURRENT correctly # if not checkCurrent(): print "Please set OMCURRENT to one of the following:" instances = listInstances() for instance in instances: print "\t" + instance[:-1] sys.exit(2) # # Get the options from the command line # try: opts, args = getopt.getopt(sys.argv[1:], "d:f:r:R:u:n:YNIxho:S:O:H:F:i:w:t:Nc:T:", ["del=", "forward=", "reply=", "redirect=", "user=","ref=","on=","off=","retain", "debug", "help", "oof=","subject=","sender=","header=","file=","info=","wildcard=","toggleretain=","not","itemclass=","title="]) except getopt.GetoptError: display_usage() sys.exit(2) for opt, arg in opts: if opt in ("-h" , "--help"): display_usage() sys.exit(0) if opt in ("-d", "--del"): action = "DEL" action_reference = int(arg) if opt in ("-f", "--forward"): action = "ADD" action_type = "FORWARD" action_address = arg if opt in ("-r", "--reply"): action = "ADD" action_type = "REPLY" action_text = arg if opt in ("-R", "--redirect"): action = "ADD" action_type = "REDIRECT" action_address = arg if opt in ("-F", "--file"): action = "ADD" action_type = "FILE" action_address = arg if opt in ("-u", "--user"): username = arg if opt in ("-n", "--ref"): action_reference = int(arg) if opt in ("-Y", "--on"): action = "ENABLE" action_reference = int(arg) if opt in ("-N", "--off"): action = "DISABLE" action_reference = int(arg) if opt in ("-o", "--oof"): if arg in ("Y","y"): action = "OOF_Y" else: action = "OOF_N" if opt in ("-I", "--retain"): retain_intray = "Y" if opt in ("-S", "--subject"): action_filter_type = "SUBJECT" action_filter_value = arg if opt in ("-O", "--sender"): action_filter_type = "SENDER" action_filter_value = arg if opt in ("-H", "--header"): action_filter_type = "HEADER" action_filter_value = arg if opt in ("-c", "--itemclass"): action_filter_type = "ITEM_CLASS" action_filter_value = arg if opt in ("-T", "--title"): action_title = arg if opt in ("-i", "--info"): action = "INFO" action_reference = int(arg) if opt in ("-t", "--toggleretain"): action = "TOGGLERETAIN" action_reference = int(arg) if opt in ("-w", "--wildcard"): gWildcard = arg if opt in ("-x", "--debug"): gDebugMode = True if opt in ("-N", "--not"): action_filter_not = True if gDebugMode: print "UserName : " + username print "Action : " + action print "Type : " + action_type print "Text File: " + action_text print "Ref : " + str(action_reference) print "Retain : " + retain_intray print "Address : " + action_address error_status = 0 error_message = "" # # Check that all the correct options have been supplied # if username == "": error_status += 1 error_message = error_message + "No user name has been supplied\n" if action == "ADD" and action_type == "": error_status += 1 error_message = error_message + "No auto action has been supplied to add\n" if not action_type == "" and not action == "ADD": error_status += 1 error_message = error_message + "Action type must be one of --forward, --reply, --file or --redirect\n" if action_type in ("FORWARD", "REDIRECT") and action_address == "": error_status += 1 error_message = error_message + "No address has been supplied for the forward/redirect\n" if action_type == "FILE" and action_address == "": error_status += 1 error_message = error_message + "No folder name has been supplied\n" if action_type == "REPLY" and action_text == "": error_status += 1 error_message = error_message + "No reply text has been supplied\n" if action == "DEL" and action_reference == -1: error_status += 1 error_message = error_message + "No action reference has been supplied to delete\n" if action in ("ENABLE", "DISABLE") and action_reference == -1: error_status += 1 error_message = error_message + "No action reference has been supplied to enable or disable\n" if action in ("OOF_Y", "OOF_N") and action_reference == -1: error_status += 1 error_message = error_message + "No action reference has been supplied to enable/disable out of office\n" if action == "INFO" and action_reference == -1: error_status += 1 error_message = error_message +"No action reference has been supplied to show information for\n" if gDebugMode: print "Error Status: " + str(error_status) if error_status > 0: print "Error: " + error_message display_usage(); sys.exit(error_status) userdir = GetUserDir(username) if userdir == "": print "Unable to determine user's home directory" return aa_file = LoadTF(userdir + gAAFile) # # Start processing # if action == "LIST": if len(aa_file) > 0: ListActions(aa_file, action_reference) else: print "No autoactions defined" if action == "ADD": new_aa_file,aux_files = AddAction(aa_file, action_type, action_address, action_text, retain_intray, action_filter_type, action_filter_value, action_filter_not, action_title) for line in aux_files: if line[2] == "COPY": cmd = "cp " elif line[2] == "MOVE": cmd = "mv " else: cmd = "echo " cmd = cmd + " '"+ line[0] + "' '" + userdir + line[1] + "'" status, results, errors = RunCommand(cmd) if not status == 0: for error in errors: print error cmd = "chown scalix:scalix '" + userdir + line[1] + "'" status, results, errors = RunCommand(cmd) if not status == 0: for error in errors: print error SaveTF(userdir + gAAFile, new_aa_file) if action == "DEL": if len(aa_file) > 0: new_aa_file,aux_files = DelAction(aa_file, action_reference) SaveTF(userdir + gAAFile, new_aa_file) for line in aux_files: cmd = "rm -f " + userdir + line RunCommand(cmd) else: print "No autoactions defined" if action in ("ENABLE", "DISABLE"): new_aa_file = MakeActionActive(aa_file, action_reference, action) SaveTF(userdir + gAAFile, new_aa_file) if action in ("OOF_Y", "OOF_N"): new_aa_file = MakeActionOOF(aa_file, action_reference, action) SaveTF(userdir + gAAFile, new_aa_file) if action == "INFO": if len(aa_file) > 0: showInfo(aa_file, action_reference) if action == "TOGGLERETAIN": if len(aa_file) > 0: new_aa_file = ToggleRedirectRetain(aa_file, action_reference) SaveTF(userdir +gAAFile, new_aa_file) if __name__ == "__main__": main()