def doit (self, isPost): (path, query, body) = self.startup (isPost) if path == '/ping': self.ok ('pong') else: if path == '/is_no_systemmail': mode = 0 elif path == '/filter_or_forward': mode = 1 elif path == '/scan_and_unsubscribe': mode = 2 else: mode = -1 if mode != -1: if body: msg = email.message_from_string (body) bav = BAV (msg, mode) if bav.execute (): self.ok (bav.reason) else: self.err (bav.reason) else: agn.log (agn.LV_ERROR, 'doit', 'No body for path %s found' % path) self.err ('%s is only allowed using POST' % path) else: agn.log (agn.LV_WARNING, 'doit', 'Invalid command/path ' + self.path) self.err ('Not (yet) implemented')
def readConfiguration(self): config = None db = eagn.DB() try: if db.isopen(): config = (db.cursor.stream( 'SELECT name, value FROM config_tbl WHERE class = :class', { 'class': 'generate' }).filter(lambda nv: nv[0] is not None).dict()) else: raise agn.error('access to database: %s' % db.lastError()) except Exception as e: agn.log(agn.LV_ERROR, 'sched', 'Failed to read configuration: %s' % e) finally: db.done() if config is not None: self.config = config self.companies = self.configuration( 'companies', convert=lambda v: agn.Stream.ifelse(agn.Range(v))) if self.companies: agn.log( agn.LV_INFO, 'sched', 'Limit operations on these companies: %s' % self.companies)
def moveTo(self, dest): try: shutil.move(self.path, dest) agn.log(agn.LV_DEBUG, "block", "Moved %s to %s" % (self.path, dest)) return True except OSError, e: agn.log(agn.LV_ERROR, "block", "Failed to move %s to %s %s" % (self.path, dest, ` e.args `))
def __init__(self, cfg=None, full=False, prefix='xmlrpc', timeout=None): self.term = False host = '' port = 8080 allowNone = False serverClass = XMLRPC.XMLRPCServer if not cfg is None: host = cfg.get('%s.host' % prefix, host) port = cfg.iget('%s.port' % prefix, port) allowNone = cfg.bget('%s.allow_none' % prefix, allowNone) server = cfg.get('%s.server' % prefix, None) if server is not None: if server == 'forking': serverClass = XMLRPC.XMLRPCServerForking elif server == 'threading': serverClass = XMLRPC.XMLRPCServerThreading else: agn.log( agn.LV_WARNING, 'xmlrpc', 'Requested unknown server type %s, fallback to simple server' % server) if full: self.__setupSignals() self.server = serverClass(port, host, timeout, allowNone) self.server.register_introspection_functions() self.server.register_multicall_functions()
def pendings (self): while self.pending.running: w = self.wait () if w is None: break agn.log (agn.LV_DEBUG, 'par', '%s: process finished' % w.description) while self.pending.queued and len (self.pending.running) < self.processes: prio = agn.Stream.of (self.pending.queued.keys ()).sorted ().first () w = self.pending.queued[prio].pop (0) if not self.pending.queued[prio]: del self.pending.queued[prio] # if w.prepare is not None: try: w.prepare (*w.args, **w.kws) except Exception as e: agn.logexc (agn.LV_ERROR, 'par', '%s: prepare fails: %s' % (w.description, e)) def starter (): self.processtitle (w.description) rc = w.method (*w.args, **w.kws) self.processtitle () return rc w.pid = self.pending.control.spawn (starter) self.pending.running.append (w) agn.log (agn.LV_DEBUG, 'par', '%s: launched' % w.description) return self.pending.running or self.pending.queued
def lock (self, key): try: self.locks[key] += 1 agn.log (agn.LV_DEBUG, 'lock', '%r increased' % key) except KeyError: self.locks[key] = 1 agn.log (agn.LV_DEBUG, 'lock', '%r set' % key)
def main(): global upgrade pid = -1 ips = ['127.0.0.1'] opts = getopt.getopt(sys.argv[1:], 'i:') for opt in opts[0]: if opt[0] == '-i': ips.append(opt[1]) agn.lockpath = '/var/tmp' agn.lock() pid = os.fork() if pid == 0: upgrade = None set_signals(updhandler) upgrade = Upgrade() upgrade.start() else: agn.log(agn.LV_INFO, 'upgrade', 'Starting up') set_signals(webhandler) server = Server(None) while not term: server.handle_request() time.sleep(2) if pid > 0: os.kill(pid, signal.SIGTERM) try: os.unlink(datafile) except OSError, e: agn.log( agn.LV_ERROR, 'upgrade', 'Failed to remove datafile %s: %s' % (datafile, repr(e.args)))
def __init__ (self, msg, mode): global rules, rlock self.msg = msg self.mode = mode self.parm = {} if not self.msg.has_key (BAV.x_agn): if self.msg.has_key ('return-path'): rp = self.msg['return-path'].strip () if rp[0] == '<' and rp[-1] == '>': addr = rp[1:-1].lower () try: fd = open (BAV.configFile) for line in [l for l in fd if len (l) > 0 and l[0] != '#']: parts = line.split (None, 1) if parts[0].lower () == addr: data = agn.chop (parts[1]) if data[:7] == 'accept:': self.msg[BAV.x_agn] = data[7:] break fd.close () except IOError, e: agn.log (agn.LV_WARNING, 'bav', 'Cannot read file %s %s' % (BAV.configFile, `e.args`)) else: agn.log (agn.LV_WARNING, 'bav', 'No %s header, neither Return-Path: found' % BAV.x_agn)
def __cdo(self, collection, query, data, filler, what, cursor=None): #{{{ for (key, companies) in collection.items(): if data: ndata = data.copy() else: ndata = {} if callable(query): basequery = query(key) else: basequery = key filler(ndata, key) if basequery is not None: companies = sorted(companies) while companies: chunk = companies[:20] companies = companies[20:] if len(chunk) > 1: nquery = '%s IN (%s)' % (basequery, ', '.join( [str(_c) for _c in chunk])) else: nquery = '%s = %d' % (basequery, chunk[0]) self.__do( nquery, ndata, '%s for %s' % (what, ', '.join([str(_c) for _c in chunk])), cursor) else: agn.log(agn.LV_INFO, 'cdo', 'Skip execution for key %r' % (key, ))
def writeFile (self): if self.armid: if os.path.isfile (self.fname): try: os.unlink (self.fname) except OSError, e: agn.log (agn.LV_ERROR, 'auto', 'Unable to remove file %s: %s' % (self.fname, str (e)))
def readMailertable (self): self.domains = [] self.mtdom = {} if self.sendmailFree: self.domains = [self.fixdomain] me = socket.getfqdn () if me: self.domains.append (me) db = agn.DBaseID () if not db is None: c = db.cursor () if not c is None: for r in c.query ('SELECT mailloop_domain FROM company_tbl'): if r[0] and not r[0] in self.domains: self.domains.append (r[0]) c.close () db.close () return try: for line in fileReader (mailBase + '/mailertable'): parts = line.split () if len (parts) > 0 and parts[0][0] != '.': self.domains.append (parts[0]) self.mtdom[parts[0]] = 0 except IOError, e: agn.log (agn.LV_ERROR, 'data', 'Unable to read mailertable %s' % `e.args`)
def unsubscribe (self, customerID, mailingID): db = agn.DBaseID () if db is not None: cursor = db.cursor () if cursor is not None: rq = cursor.querys ('SELECT mailinglist_id, company_id FROM mailing_tbl WHERE mailing_id = :mailingID', {'mailingID': mailingID}) if rq is not None: mailinglistID = rq[0] companyID = rq[1] query = cursor.rselect ('UPDATE customer_%d_binding_tbl SET user_status = :userStatus, user_remark = :userRemark, timestamp = %%(sysdate)s, exit_mailing_id = :mailingID WHERE customer_id = :customerID AND mailinglist_id = :mailinglistID' % companyID) data = { 'userStatus': agn.UserStatus.ADMOUT, 'userRemark': 'Opt-out by mandatory CSA link (mailto)', 'mailingID': mailingID, 'customerID': customerID, 'mailinglistID': mailinglistID } cnt = cursor.update (query, data) if cnt == 1: agn.log (agn.LV_INFO, 'unsub', 'Unsubscribed customer %d for company %d on mailinglist %d due to mailing %d' % (customerID, companyID, mailinglistID, mailingID)) else: agn.log (agn.LV_WARNING, 'unsub', 'Failed to unsubscribe customer %d for company %d on mailinglist %d due to mailing %d, matching %d rows (expected one row)' % (customerID, companyID, mailinglistID, mailingID, cnt)) cursor.sync () else: agn.log (agn.LV_WARNING, 'unsub', 'No mailinglist for mailing %d found, do not unsubscribe %d' % (mailingID, customerID)) cursor.close () else: agn.log (agn.LV_ERROR, 'unsub', 'Failed to open database: %s' % db.lastError ()) db.close () else: agn.log (agn.LV_ERROR, 'unsub', 'Failed to setup database interface')
def wait(self, block=False): w = None while self.pending.running and w is None: rc = self.pending.control.join(block=block) if not rc.pid: break # w = (agn.Stream.of( self.pending.running).filter(lambda r: r.pid == rc.pid).first( no=None)) if w is not None: agn.log( agn.LV_DEBUG, 'par', '%s: returned with %s' % (w.description, ('exit with %d' % rc.exitcode) if rc.exitcode is not None else ('died due to signal %d' % rc.signal))) if w.finalize is not None: try: w.finalize(rc, *w.args, **w.kws) except Exception as e: agn.logexc( agn.LV_ERROR, 'per', '%s: finalize fails: %s' % (w.description, e)) self.pending.running.remove(w) self.lockTitle() return w
def removeFile (self): for fname in self.fname, self.limit: if os.path.isfile (fname): try: os.unlink (fname) except OSError, e: agn.log (agn.LV_ERROR, 'auto', 'Unable to remove file %s: %s' % (fname, str (e)))
def main(): global upgrade pid = -1 ips = ['127.0.0.1'] opts = getopt.getopt (sys.argv[1:], 'i:') for opt in opts[0]: if opt[0] == '-i': ips.append (opt[1]) agn.lockpath = '/var/tmp' agn.lock () pid = os.fork () if pid == 0: upgrade = None set_signals(updhandler) upgrade = Upgrade() upgrade.start() else: agn.log (agn.LV_INFO, 'upgrade', 'Starting up') set_signals(webhandler) server = Server(None) while not term: server.handle_request() time.sleep (2) if pid > 0: os.kill(pid, signal.SIGTERM) try: os.unlink(datafile) except OSError, e: agn.log(agn.LV_ERROR, 'upgrade', 'Failed to remove datafile %s: %s' % (datafile, repr(e.args)))
def execute_filter_or_forward (self): match = self.rule.matchHeader (self.msg, 'filter') if not match is None and not match[1].inverse: if not match[1].parm: parm = 'save' else: parm = match[1].parm else: parm = 'sent' self.saveMessage (parm) if parm == 'sent': while self.msg.has_key (BAV.x_agn): del self.msg[BAV.x_agn] if self.parm.has_key ('fwd'): sendMsg = self.msg self.sendmail (sendMsg, self.parm['fwd']) if self.parm.has_key ('ar'): ar = self.parm['ar'] if self.parm.has_key ('from') and not self.headerFrom is None and not self.headerFrom[1]: sender = self.headerFrom[1] ar = Autoresponder (ar, sender) nmsg = ar.createMessage (self.msg, self.parm) if nmsg: agn.log (agn.LV_INFO, 'fof', 'Forward newly generated message to %s' % sender) self.sendmail (nmsg, sender) else: agn.log (agn.LV_INFO, 'fof', 'No sender in original message found') if self.parm.has_key ('sub') and self.parm.has_key ('cid') and self.parm.has_key ('from'): (mlist, form) = self.parm['sub'].split (':', 1) cid = self.parm['cid'] if self.headerFrom and self.headerFrom[1]: self.subscribe (self.headerFrom[1].lower (), self.headerFrom[0], int (cid), int (mlist), int (form)) return True
def updateRules (self, rules): inuse = set () globalRule = None for (rid, rule) in rules.items (): if globalRule is None: try: fd = open (self.ruleFile) globalRule = fd.read () fd.close () if not globalRule.endswith ('\n'): globalRule += '\n' except IOError as e: agn.log (agn.LV_ERROR, 'rule', 'Failed to open "%s" for reading: %s' % (self.ruleFile, str (e))) inuse.add (rid) fname = self.ruleFormat % rid try: fd = open (fname, 'w') if globalRule: fd.write (globalRule) for sect in sorted (rule): fd.write ('[%s]\n' % agn.toutf8 (sect)) for line in rule[sect]: fd.write ('%s\n' % agn.toutf8 (line)) fd.close () except IOError, e: agn.log (agn.LV_ERROR, 'rule', 'Failed to open "%s" for writing: %r' % (fname, e.args))
def __init__ (self): self.fixdomain = 'localhost' self.domains = [] self.prefix = 'ext_' self.last = None self.autoresponder = [] self.mtdom = {} self.sendmailFree = False if agn.iswin: self.sendmailFree = True else: if not smenable is None: sm = smenable.SMCtrl () if sm.valid and not sm.enabled (): self.sendmailFree = True sm.done () self.readMailertable () try: files = os.listdir (arDirectory) for fname in files: if len (fname) > 8 and fname[:3] == 'ar_' and fname[-5:] == '.mail': rid = fname[3:-5] self.autoresponder.append (Autoresponder (rid, 0, None, None, None, None)) except OSError, e: agn.log (agn.LV_ERROR, 'data', 'Unable to read directory %s %s' % (arDirectory, `e.args`))
def isActive(self): rc = False reason = [] proc = eagn.Processtable() be = proc.select(user='******', comm='java', rcmd='org.agnitas.backend.MailoutServerXMLRPC') if len(be) == 1: err = self.__stateCheck(be[0]) if err is None: rc = True else: reason.append('Backend: %s' % err) elif len(be) > 1: reason.append('There are more than one backend running: %s' % ', '.join([str(_t.pid) for _t in be])) else: reason.append('No java process is active') if not rc: agn.log(agn.LV_WARNING, 'active', 'Backend activity error: %s' % ', '.join(reason)) elif reason: agn.log(agn.LV_INFO, 'active', 'Backend is active: %s' % ', '.join(reason)) return rc
def moveTo (self, dest): try: shutil.move (self.path, dest) agn.log (agn.LV_DEBUG, 'block', 'Moved %s to %s' % (self.path, dest)) return True except OSError, e: agn.log (agn.LV_ERROR, 'block', 'Failed to move %s to %s %s' % (self.path, dest, `e.args`))
def main (): global running, reread delay = 180 (opts, param) = getopt.getopt (sys.argv[1:], 'vd:') for opt in opts: if opt[0] == '-v': agn.outlevel = agn.LV_DEBUG agn.outstream = sys.stderr elif opt[0] == '-d': delay = int (opt[1]) signal.signal (signal.SIGINT, handler) signal.signal (signal.SIGTERM, handler) signal.signal (signal.SIGUSR1, handler) signal.signal (signal.SIGHUP, signal.SIG_IGN) signal.signal (signal.SIGPIPE, signal.SIG_IGN) agn.lock () agn.log (agn.LV_INFO, 'main', 'Starting up') data = Data () while running: forcedUpdate = reread reread = False data.update (forcedUpdate) n = delay while n > 0 and running and not reread: time.sleep (1) n -= 1 data.done () agn.log (agn.LV_INFO, 'main', 'Going down') agn.unlock ()
def scan (self): files = sorted (os.listdir (self.incoming)) for fname in [_f for _f in files if _f.endswith ('.final')]: basename = fname.split ('.')[0] if '%s.xml.gz' % basename in files and '%s.stamp' % basename in files: agn.log (agn.LV_INFO, 'scan', 'Found %s to process' % fname) self.doit (basename)
def doit(self, isPost): (path, query, body) = self.startup(isPost) if path == '/ping': self.ok('pong') else: if path == '/is_no_systemmail': mode = 0 elif path == '/filter_or_forward': mode = 1 elif path == '/scan_and_unsubscribe': mode = 2 else: mode = -1 if mode != -1: if body: msg = email.message_from_string(body) bav = BAV(msg, mode) if bav.execute(): self.ok(bav.reason) else: self.err(bav.reason) else: agn.log(agn.LV_ERROR, 'doit', 'No body for path %s found' % path) self.err('%s is only allowed using POST' % path) else: agn.log(agn.LV_WARNING, 'doit', 'Invalid command/path ' + self.path) self.err('Not (yet) implemented')
def __init__ (self, raw, msg, mode, dryrun = False): self.raw = raw self.msg = msg self.mode = mode self.dryrun = dryrun self.parsedEMail = eagn.ParseEMail (raw) self.cinfo = self.parsedEMail.getOrigin () self.parm = {} if not self.msg.has_key (BAV.x_agn): if self.msg.has_key ('return-path'): rp = self.msg['return-path'].strip () if rp[0] == '<' and rp[-1] == '>': addr = rp[1:-1].lower () try: fd = open (BAV.configFile) for line in [l for l in fd if len (l) > 0 and l[0] != '#']: parts = line.split (None, 1) if parts[0].lower () == addr and len (parts) == 2: data = (parts[1]).rstrip ('\r\n') if data[:7] == 'accept:': self.msg[BAV.x_agn] = data[7:] break fd.close () except IOError, e: agn.log (agn.LV_WARNING, 'bav', 'Cannot read file %s %s' % (BAV.configFile, `e.args`)) else: agn.log (agn.LV_WARNING, 'bav', 'No %s header, neither Return-Path: found' % BAV.x_agn)
def createMessage (self, orig, parm, dryrun): global alock fname = Autoresponder.msgPattern % self.aid if not os.access (fname, os.R_OK): agn.log (agn.LV_WARNING, 'ar', 'No autoresponder mail %s for enabled autoresponder %s found' % (fname, self.aid)) return None # if not self.allow (parm, dryrun): return None # try: fd = open (fname) armail = fd.read () fd.close () armsg = email.message_from_string (armail) armsg['To'] = '<%s>' % self.sender if not armsg.has_key ('subject') and orig.has_key ('subject'): subj = orig['subject'] if len (subj) < 4 or subj[:4].lower () != 're: ': subj = 'Re: ' + subj armsg['Subject'] = subj except IOError, e: armsg = None agn.log (agn.LV_ERROR, 'ar', 'Unable to read %s %s' % (armail, `e.args`))
def __db_sanity(self, r): with eagn.DB() as db: key = 'mask-envelope-from' rq = db.querys( 'SELECT count(*) AS cnt ' 'FROM company_info_tbl ' 'WHERE company_id = 0 AND cname = :cname', {'cname': key}) if rq is None or not rq.cnt: count = db.execute( 'INSERT INTO company_info_tbl (' ' company_id, cname, cvalue, description, creation_date, timestamp' ') VALUES (' ' 0, :cname, :cvalue, NULL, current_timestamp, current_timestamp' ')', { 'cname': key, 'cvalue': 'false' }, commit=True) if count == 1: agn.log(agn.LV_INFO, 'sanity', 'Added configuration for envelope address') else: agn.log( agn.LV_ERROR, 'sanity', 'Failed to set configuration for envelope address: %s' % db.lastError())
def __init__(self, msg, mode): global rules, rlock self.msg = msg self.mode = mode self.parm = {} if not self.msg.has_key(BAV.x_agn): if self.msg.has_key('return-path'): rp = self.msg['return-path'].strip() if rp[0] == '<' and rp[-1] == '>': addr = rp[1:-1].lower() try: fd = open(BAV.configFile) for line in [ l for l in fd if len(l) > 0 and l[0] != '#' ]: parts = line.split(None, 1) if parts[0].lower() == addr: data = agn.chop(parts[1]) if data[:7] == 'accept:': self.msg[BAV.x_agn] = data[7:] break fd.close() except IOError, e: agn.log( agn.LV_WARNING, 'bav', 'Cannot read file %s %s' % (BAV.configFile, ` e.args `)) else: agn.log(agn.LV_WARNING, 'bav', 'No %s header, neither Return-Path: found' % BAV.x_agn)
def execute (self, size): global running for path in self.ws: ok = True try: fd = open (path, 'r') body = fd.read (size) fd.close () except IOError, e: agn.log (agn.LV_ERROR, 'child', 'Failed to open %s: %r' % (path, e.args)) ok = False if ok: try: msg = email.message_from_string (body) bav = BAV (body, msg, -1) if bav.execute_is_no_systemmail (): ok = bav.execute_filter_or_forward () else: ok = bav.execute_scan_and_unsubscribe () except Exception as e: agn.logexc (agn.LV_ERROR, 'child', 'Fatal: catched failure: %s' % e) ok = False if ok: self.ws.success (bav.sender) else: self.ws.fail () if not running: break
def writeUpdateLog (self, text): try: fd = open (updateLog, 'a') fd.write ('%d %s\n' % (self.updateCount, text)) fd.close () self.updateCount += 1 except IOError, e: agn.log (agn.LV_ERROR, 'data', 'Unable to write update log %s %s' % (updateLog, `e.args`))
def _encode (self, s): if s and charset != 'UTF-8': temp = StringIO.StringIO (s) convert = codecs.EncodedFile (temp, charset, 'UTF-8') try: s = convert.read () except Exception, e: agn.log (agn.LV_ERROR, 'auto', 'Failed to convert autoresponder text for %s %s' % (self.rid, `e.args`))
def removeOldEntries (self): #{{{ agn.log (agn.LV_INFO, 'expire', 'Remove old entries from softbounce_email_tbl') old = time.localtime (time.time () - 180 * 24 * 60 * 60) query = 'DELETE FROM softbounce_email_tbl WHERE creation_date <= :expire AND company_id' data = {'expire': datetime.datetime (old.tm_year, old.tm_mon, old.tm_mday)} self.__do (query, data, 'Remove old addresses from softbounce_email_tbl') agn.log (agn.LV_INFO, 'expire', 'Removing of old entries from softbounce_email_tbl done')
def readMailFiles (self): rc = '' try: lhost = socket.getfqdn () if lhost: rc += '@%s\taccept:rid=local\n' % lhost except Exception, e: agn.log (agn.LV_ERROR, 'data', 'Unable to find local FQDN %s' % `e.args`)
def saveMessage (self, mid): fname = BAV.savePattern % (mid, self.rid) try: fd = open (fname, 'a') fd.write (self.msg.as_string (True) + '\n') fd.close () except IOError, e: agn.log (agn.LV_ERROR, 'save', 'Unable to save mail copy to %s %s' % (fname, `e.args`))
def __readFile (self): #{{{ try: fd = open (self.path) content = fd.read () fd.close () except IOError, e: agn.log (agn.LV_ERROR, 'prop', 'Failed to read property file "%s": %r' % (self.path, e.args)) content = None
def append(self, line): try: self.entries.append(Entry(line)) except re.error: agn.log( agn.LV_ERROR, 'section', 'Got illegal regular expression "%s" in [%s]' % (line, self.name))
def _encode (self, s): global charset if s and charset != 'UTF-8': try: s = agn.toutf8 (s, charset) except Exception, e: agn.log (agn.LV_ERROR, 'auto', 'Failed to convert autoresponder text for %s %s' % (self.rid, str (e)))
def completed(self, key, value, to_remove): if 'messageID' in value: messageID = value['messageID'] if messageID: to_remove.append((self.SEC_MESSAGEID, messageID)) else: agn.log(agn.LV_WARNING, 'proc/%s' % key, 'Completed record without messageID found, remove')
def updateEnd(self, inst): agn.log( agn.LV_INFO, 'udpBounce', 'Found %d hardbounces, %d softbounces, %d successes, %d ignored in %d lines' % (self.hbcount, self.sbcount, self.sucount, self.igcount, self.lineno)) return True
def __save (self, fname, line): rc = False try: fd = open (fname, 'a') fd.write (line + '\n') fd.close () rc = True except IOError, e: agn.log (agn.LV_ERROR, 'update', 'Failed to write to %s: %s' % (fname, `e.args`))
def update (self, inst): tfname = self.renameToTemp () if tfname is None: return False try: fd = open (tfname, 'r') except IOError, e: agn.log (agn.LV_ERROR, 'update', 'Unable to open %s: %s' % (tfname, `e.args`)) fd = None
def sendmail (self, msg, to): try: mailtext = msg.as_string (False) pp = os.popen ('/usr/sbin/sendmail ' + to, 'w') pp.write (mailtext) pp.close () except Exception, e: agn.log (agn.LV_ERROR, 'sendmail', 'Sending mail to %s failed %s' % (to, `e.args`))
def __scan (self, msg, scan, sects, checkheader): if checkheader: if not scan.section: rc = self.__checkHeader (msg, sects) if rc: (scan.section, scan.entry, scan.reason) = rc self.__scanMID (scan, msg['message-id'], 'header') if not scan.dsn: subj = msg['subject'] if subj: mt = Rule.DSNRE[0].search (self.__decode (subj)) if mt: grps = mt.groups () scan.dsn = grps[0] scan.etext = grps[1] agn.log (agn.LV_VERBOSE, 'dsn', 'Found DSN in Subject: %s' % subj) if not scan.dsn: action = msg['action'] status = msg['status'] if action and status: mt = Rule.DSNRE[2].match (self.__decode (status)) if mt: scan.dsn = mt.groups ()[0] scan.etext = 'Action: ' + action agn.log (agn.LV_VERBOSE, 'dsn', 'Found DSN in Action: %s / Status: %s' % (action, status)) pl = msg.get_payload (decode = True) if not pl: pl = msg.get_payload () if type (pl) in types.StringTypes: for line in pl.split ('\n'): if not scan.minfo: self.__scanMID (scan, line, 'body') if not scan.section: (sec, ent) = self.__match (line, sects) if sec: scan.section = sec scan.entry = ent scan.reason = line agn.log (agn.LV_VERBOSE, 'match', 'Found pattern "%s" in body "%s"' % (scan.entry.pattern, line)) if not scan.dsn: mt = Rule.DSNRE[0].search (line) if mt: grps = mt.groups () scan.dsn = grps[0] scan.etext = grps[1] agn.log (agn.LV_VERBOSE, 'dsn', 'Found DSN %s / Text "%s" in body: %s' % (scan.dsn, scan.etext, line)) else: mt = Rule.DSNRE[1].search (line) if mt: scan.dsn = mt.groups ()[0] scan.etext = line agn.log (agn.LV_VERBOSE, 'dsn', 'Found DSN %s in body: %s' % (scan.dsn, line)) elif type (pl) in [ types.TupleType, types.ListType ]: for p in pl: self.__scan (p, scan, sects, True) if scan.section and scan.dsn and scan.minfo: break
def __readFile(self): #{{{ try: fd = open(self.path) content = fd.read() fd.close() except IOError, e: agn.log( agn.LV_ERROR, 'prop', 'Failed to read property file "%s": %r' % (self.path, e.args)) content = None
def unlock (self, key): try: self.locks[key] -= 1 if self.locks[key] <= 0: del self.locks[key] agn.log (agn.LV_DEBUG, 'lock', '%r removed' % key) else: agn.log (agn.LV_DEBUG, 'lock', '%r decreased' % key) except KeyError: pass
def execute_scan_and_unsubscribe (self): scan = self.rule.scanMessage (self.msg, ['hard', 'soft']) if scan and scan.section and scan.minfo: try: fd = open (BAV.extBouncelog, 'a') fd.write ('%s;0;%d;0;%d;mailloop=%s\n' % (scan.dsn, scan.minfo[0], scan.minfo[1], scan.etext)) fd.close () except IOError, e: agn.log (agn.LV_ERROR, 'log', 'Unable to write %s %s' % (BAV.extBouncelog, `e.args`))
def readMailFiles (self): rc = '' if self.sendmailFree: return rc try: for line in fileReader (mailBase + '/local-host-names'): rc += '@%s\taccept:rid=local\n' % line except IOError, e: agn.log (agn.LV_ERROR, 'data', 'Unable to read local-host-names %s' % `e.args`)
def updateLine (self, inst, line): parts = line.split (';', 5) if len (parts) != 6: agn.log (agn.LV_WARNING, 'updBounce', 'Got invalid line: ' + line) return False try: (dsn, _, mailing, media, customer, info) = (parts[0], int (parts[1]), int (parts[2]), int (parts[3]), int (parts[4]), parts[5]) except ValueError, e: agn.log (agn.LV_WARNING, 'updBounce', 'Unable to parse line: ' + line + ' (' + str (e) + ')') return False
def renameToTemp (self): tfname = self.path + '.%d.%d.%d' % (self.pid, time.time (), self.cur) self.cur += 1 try: os.rename (self.path, tfname) agn.log (agn.LV_INFO, 'update', 'Renamed %s to %s' % (self.path, tfname)) time.sleep (10) except OSError, e: agn.log (agn.LV_ERROR, 'update', 'Unable to rename %s to %s: %s' % (self.path, tfname, `e.args`)) tfname = None
def readLocalFiles (self): rc = '' if agn.iswin: return rc try: for line in fileReader (localFilename): rc += line + '\n' except IOError, e: agn.log (agn.LV_VERBOSE, 'local', 'Unable to read local file %s %s' % (localFilename, `e.args`))
def checkProcmailrc (now, destpath): prc = agn.mkpath (agn.base, '.procmailrc') try: fd = open (prc, 'r') ocontent = fd.read () fd.close () except IOError, e: if e.args[0] != errno.ENOENT: agn.log (agn.LV_WARNING, 'chk', 'Failed to read "%s": %r' % (prc, e.args)) ocontent = ''
def waitForChildren (children): nchildren = [] for child in children: child.wait () if child.active: nchildren.append (child) if len (children) != len (nchildren): count = len (children) - len (nchildren) agn.log (agn.LV_VERBOSE, 'bavd', '%d child%s terminated' % (count, count != 1 and 'ren' or '')) return nchildren
def __todetail(self, dsn, info): (ignore, detail, code, typ, remark) = (False, 0, 0, 2, 'bounce') match = self.dsnparse.match(dsn) if not match is None: grp = match.groups() code = int(grp[0]) * 100 + int(grp[1]) * 10 + int(grp[2]) infos = UpdateInfo(info) stat = infos.get('stat', '').lower() mailloop = infos['mailloop'] # # special cases if infos.get('relay', '').find('yahoo.com') != -1: if code / 100 == 5 and stat.find('service unavailable') != -1: detail = 511 admin = infos['admin'] if not admin is None: typ = 4 remark = admin status = infos['status'] if not status is None: ttyp = self.ustatus.findStatus(status) if not ttyp is None: typ = ttyp if detail == 0: if (code in (511, 571) and (stat.find('user unknown') != -1 or not mailloop is None)) or code == 513: detail = 511 elif code == 512: detail = 512 elif code in (420, 421, 422, 521, 522): detail = 420 elif code in (430, 530, 535): detail = 430 elif code / 100 == 4: if code / 10 in (42, 43, 47) and (stat.find('gray') != -1 or stat.find('grey') != -1): ignore = True detail = 400 elif code in (500, 511, 550, 554): detail = 500 elif code / 100 == 5: detail = 510 elif code == 200: detail = 200 typ = 1 else: agn.log( agn.LV_WARNING, 'updBounce', '%s resulting in %d does not match any rule' % (dsn, code)) return (ignore, detail, code, typ, remark)
def __mapMailingToCompany (self, inst, mailing): if not self.mailingMap.has_key (mailing): rec = inst.querys ('SELECT company_id FROM mailing_tbl WHERE mailing_id = %d' % mailing) if rec is None: agn.log (agn.LV_ERROR, 'updBounce', 'No company_id for mailing %d found' % mailing) return 0 else: self.mailingMap[mailing] = rec[0] return rec[0] else: return self.mailingMap[mailing]
def save (ct): if ct.path is not None and (ct.modified or not os.path.isfile (ct.path)): try: fd = open (ct.path, 'w') if ct.content: fd.write ('\n'.join (['%s\t%s' % _c for _c in ct.content]) + '\n') fd.close () agn.log (agn.LV_INFO, 'data', 'Written %d lines to %s' % (len (ct.content), ct.path)) if ct.hash is not None: make (ct) except OSError, e: agn.log (agn.LV_ERROR, 'data', 'Failed to save %s: %s' % (ct.path, str (e)))
def updateConfigfile (self, new): if new != self.last: temp = configFilename + '.%d' % os.getpid () try: fd = open (temp, 'w') fd.write (new) fd.close () self.renameFile (temp, configFilename) self.last = new except IOError, e: agn.log (agn.LV_ERROR, 'data', 'Unable to write %s %s' % (temp, `e.args`)) raise agn.error ('updateConfigfile.open')
def update (self, forced): try: auto = [] new = self.readMailFiles () new += self.readDatabase (auto) new += self.readLocalFiles () self.updateAutoresponder (auto) self.updateConfigfile (new) updateText = 'success' except agn.error, e: agn.log (agn.LV_ERROR, 'data', 'Update failed: ' + e.msg) updateText = 'failed: ' + e.msg
def __collect (self, path, remove): #{{{ files = os.listdir (path) for fname in [_f for _f in files if not self.pattern.match (_f) is None]: fpath = agn.mkpath (path, fname) if remove: try: os.unlink (fpath) agn.log (agn.LV_DEBUG, 'collect', 'File "%s" removed' % fpath) except OSError, e: agn.log (agn.LV_ERROR, 'collect', 'Failed to remove file "%s": %r' % (fpath, e.args)) elif fname.endswith ('.xml.gz'): self.__parseXML (fpath)
def __todetail (self, dsn, info): (ignore, detail, code, typ, remark) = (False, 0, 0, 2, 'bounce') match = self.dsnparse.match (dsn) if not match is None: grp = match.groups () code = int (grp[0]) * 100 + int (grp[1]) * 10 + int (grp[2]) infos = UpdateInfo (info) stat = infos.get ('stat', '').lower () mailloop = infos['mailloop'] # # special cases relay = infos.get ('relay', None) if relay is not None: if relay.find ('yahoo.com') != -1: if code / 100 == 5 and stat.find ('service unavailable') != -1: detail = 511 admin = infos['admin'] if not admin is None: typ = 4 remark = admin status = infos['status'] if not status is None: ttyp = self.ustatus.findStatus (status) if not ttyp is None: typ = ttyp if detail == 0: if (code in (511, 571) and (stat.find ('user unknown') != -1 or not mailloop is None)) or code == 513: detail = 511 elif code == 512: detail = 512 elif code in (420, 421, 422, 521, 522): detail = 420 elif code in (430, 530, 535): detail = 430 elif code / 100 == 4: if code / 10 in (42, 43, 47) and (stat.find ('gray') != -1 or stat.find ('grey') != -1): ignore = True detail = 400 elif code in (500, 511, 550, 554): detail = 500 elif code / 100 == 5: detail = 510 elif code == 200: detail = 200 typ = 1 elif code / 100 == 1: detail = 100 typ = 1 else: agn.log (agn.LV_WARNING, 'updBounce', '%s resulting in %d does not match any rule' % (dsn, code)) return (ignore, detail, code, typ, remark)
def updateLine (self, inst, line): sql = 'INSERT INTO mailing_account_tbl (' values = 'VALUES (' timestamp = 'now()' sep = '' ignore = False for tok in line.split (): tup = tok.split ('=', 1) if len (tup) == 2: name = None (var, val) = tup quoteit = False if var == 'company': name = 'company_id' elif var == 'mailing': name = 'mailing_id' elif var == 'maildrop': name = 'maildrop_id' elif var == 'status_field': name = 'status_field' quoteit = True elif var in ('mailtype', 'subtype'): name = 'mailtype' elif var == 'count': name = 'no_of_mailings' elif var == 'bytes': name = 'no_of_bytes' elif var == 'block': name = 'blocknr' elif var == 'timestamp': if not self.tscheck.match (val) is None: timestamp = 'str_to_date(\'' + val + '\', \'%Y-%m-%d:%H:%i:%s\')' if quoteit: val = '\'%s\'' % val.replace ('\'', '\'\'') if not name is None: sql += '%s%s' % (sep, name) values += '%s%s' % (sep, val) sep = ', ' sql += '%schange_date) %s, %s)' % (sep, values, timestamp) rc = True if not ignore: try: inst.update (sql, commit = True) self.inserted += 1 except agn.error, e: agn.log (agn.LV_ERROR, 'updAccount', 'Failed to insert %s into database: %s' % (line, e.msg)) rc = False self.failed += 1
def _encode (self, s): if s: try: s = unicode (s, charset).encode ('UTF-8') except Exception, e: agn.log (agn.LV_ERROR, 'auto', 'Failed to convert autoresponder text for %s: %s' % (self.rid, str (e))) ns = [] for ch in s: if ord (ch) < 128: ns.append (ch) else: ns.append ('?') s = ''.join (ns)