; zLEc@sdZdklZdkZdkZdkZdkZdkZdkZdk Z dk Z dk Z dk Z dk Z dkZdkZdkZdkZdZdZdZdZdZdZd Zd Zd Zd Zd ZdZdZdZdZ dZ!dZ"dZ#dZ$dZ%e&dZ'e&e&dZ(e&dZ)dZ*dZ+dZ,dZ-dZ.dZ/d Z0d!Z1e2d"Z3e2e2e2d#Z4d$Z5d%Z6d&Z7d'Z8d(Z9d)Z:d*Z;d+Z<d,Z=d-Z>d.Z?dd/Z@d0ZAd1ZBd2ZCd3d4ZDd5ZEe&d6ZFe&e&dd7ZGe&e&dd8ZHe&e&dd9ZIee&e&d:ZJd;fd<YZKd=fd>YZLd?fd@YZMdS(AsGeneral purpose functions.(sStringIONiiiicCsXtiidp#tiidptiid}| oti}n|SdS(s The host namesTMDAHOSTs QMAILHOSTsMAILHOSTN(sossenvironsgetshostnamessocketsgetfqdn(shostname((s./TMDA/Util.pys gethostname2s 8cCstiidp6tiidp#tiidptiid}| oDdk}|itid}|o|idd}qn|SdS( s5The user's personal name. Default is an empty value.sTMDANAMEs QMAILNAMEsNAMEsMAILNAMENis,i(sossenvironsgetsfullnamespwdsgetpwuidsgetuidssplit(sfullnamespwd((s./TMDA/Util.pys getfullname<sK cCstiidp6tiidp#tiidptiid}| o&dk}|itid}n| o d}n|SdS(s The user namesTMDAUSERs QMAILUSERsUSERsLOGNAMENis (sossenvironsgetsusernamespwdsgetpwuidsgetuid(susernamespwd((s./TMDA/Util.pys getusernameJsK  cCsdk}|i|dSdS(s$Return username's numerical user ID.Ni(spwdsgetpwnamsusername(susernamespwd((s./TMDA/Util.pysgetuidXs cCsdk}|i|dSdS(s%Return username's numerical group ID.Ni(spwdsgetpwnamsusername(susernamespwd((s./TMDA/Util.pysgetgid^s cCsdk}|i|dSdS(s&Return the home directory of username.Ni(spwdsgetpwnamsusername(susernamespwd((s./TMDA/Util.pys gethomedirds cCsmdk}gi}|iD])}||djo||dqq~}|idt||SdS(sxRead through the group file and calculate the group access list for the specified user. Return a list of group ids.Niii( sgrpsappends_[1]sgetgrallsisusernamesgidssinsertsgetgid(susernamesgrpsgidssis_[1]((s./TMDA/Util.pys getgrouplistjs  FcCs?ti|}ti|ti}tt|}|SdS(sPReturn the octal number of the bit pattern for the file permissions on path.N( sossstatspathsstatinfosS_IMODEsST_MODEspermbitssintsoctsmode(spathspermbitssmodesstatinfo((s./TMDA/Util.pys getfilemodeus cCsti|}|tiSdS(s=Return the numerical UID of the user owning the file in path.N(sossstatspathsstatinfosST_UID(spathsstatinfo((s./TMDA/Util.pys getfileuid~scCs%ti|}|titi@SdS(s^Return True if the sticky bit is set on path. Generally only appropriate for directories.N(sossstatspathsstatinfosST_MODEsS_ISVTX(spathsstatinfo((s./TMDA/Util.pysisstickysc Csud}tii|oTt|d}|idd\}}|i }|i } x|iD]} d}| ii } | djp| ddjoqen| idd\} }| | jo d}ns| d djo| i| d jo d}nBy+| iddd| jo d}nWntj onX|o |}PqeqeW|in|SdS( Nssrs@iis#s:s.i(s ret_prependsosspathsexistss vdomainsfilesopensfpsaddressssplitsusdslowers ousernamesodomains readlinesslines vdomain_matchsstripsvdomainsprependsfinds IndexErrorsclose( saddresss vdomainsfilesfpsds ret_prependsprepends ousernames vdomain_matchsusodomainslinesvdomain((s./TMDA/Util.pysgetvdomainprepends6     '  cCsFd|||f}ti|}|i}|i |i SdS(s9Return the home directory of a qmail virtual domain user.s%s %s %sN( sscriptsusersdomainscmdsosspopensfpinsreads vuserhomedirsclosesstrip(susersdomainsscripts vuserhomedirsfpinscmd((s./TMDA/Util.pysgetvuserhomedirs   cCs5dk}|i|}|d|d|dfSdS(s+Return a user's home directory, UID, & GID.Niii(spwdsgetpwnamsloginsstats(sloginsstatsspwd((s./TMDA/Util.pys getuserparamss cCsti\}}ti o5ti|ti|dti|d|nti|ti |}|i }|i|SdS(s6Run a program the "hard way" so we don't lose our UID.iiN( sosspipesReadsWritesforksclosesdup2sexecvsArgssfdopens readlinessRetVal(sArgssReadsWritesRetVal((s./TMDA/Util.pysRunTasks    cCsGtid|}| otd|n|i\}}|djo t|dddd}n|djo t|dddd}n|d jo t|dddd }n||d jot|ddd}nS|d jot|dd}n.|d jot|d}n t|}|SdS(s4Translate the defined timeout interval into seconds.s^([0-9]+)([YMwdhms])$sInvalid timeout value: sYi<iimsMiswisdshsmN( sresmatchstimeouts ValueErrorsgroupssnumsunitsintsseconds(stimeoutssecondssnumsmatchsunit((s./TMDA/Util.pyssecondss&          cCs{tid|}| o|Sn|i\}}dk}|d|i|}t|djo|d }n|SdS(s<Return a human readable translation of the timeout interval.s^([0-9]+)([YMwdhms])$Ns ii( sresmatchstimeoutsgroupssnumsunitsDefaultss TIMEOUT_UNITSsint(stimeoutsnumsmatchsDefaultssunit((s./TMDA/Util.pysformat_timeouts cCsx| oti}nti|}ti|d}ti|i}|it |d|di |SdS(sReturn a date string in the format of the UNIX `date' command. e.g, Thu Dec 27 17:54:04 MST 2001 timesecs is optional, and if not given, the current time is used. iis N( stimesecsstimes localtimes timetuplestznamesasctimessplits asctime_listsinsertslensjoin(stimesecsstznames timetuples asctime_list((s./TMDA/Util.pysunixdatescCs| oti}n| odk}|i}ntiidptiidpt}tiidpd}dt ||||f}|SdS(sMReturn an rfc2822 compliant Message-ID: string, composed of seconds since the epoch in UTC + process id + 'TMDA' + FQDN. e.g: <1016659379.10104.TMDA@nightshade.la.mastaler.com> timesecs is optional, and if not given, the current time is used. pid is optional, and if not given, the current process id is used. Ns TMDAIDHOSTs QMAILIDHOSTs TMDAIDTAGsTMDAs <%s.%s.%s@%s>( stimesecsstimespidsDefaultssPIDsossenvironsgets gethostnamesidhostsidtagsintsmsgid(stimesecsspidsmsgidsidtagsDefaultssidhost((s./TMDA/Util.pys make_msgids   /cCs7|tjoti}ntii|dtSdS(sReturn an RFC 2822 compliant Date string relative to the local timezone. e.g, Tue, 02 Mar 2004 15:55:05 +1300 timesecs is optional, and if not given, the current time is used. s localtimeN(stimesecssNonestimesemailsutilss formatdatesTrue(stimesecs((s./TMDA/Util.pys make_date%s cCsx~ti|D]m}|i}|djp|ddjoqq|i}|d}|i}|d}||| Copyright (C) 2001 Charles Cazabon, and licensed under the GNU General Public License version 2. isbufsizeis exited %iss abnormal exits signal %isno exit?scommand "%s" %s %s (%s)s/failure delivering message to command "%s" (%s)N( spopen2s_cleanupsPopen3scommandscmds fromchildstochildschilderrscmdoutscmdinscmderrsstringsssswritesflushsclosesreadsstripserrsoutswaitsrsoss WIFEXITEDs WEXITSTATUSsexitcodes exitsignals WIFSIGNALEDsWTERMSIGsIOErrors Exceptionstxt( scommandsstringsscmdoutstxtserrsexitcodescmdsssrs exitsignalscmdinscmderrsout((s./TMDA/Util.pyspipecmdPs@  !     $  cCsNtii|ot|dn't|d}|i||i dS(s,Simple function to write contents to a file.s already existsswN( sosspathsexistss fullpathnamesIOErrorsopensfileswritescontentssclose(scontentss fullpathnamesfile((s./TMDA/Util.pys writefiles  cCstii|oxti|D]}|ii}|djp|ddjoq#q#|i i ddi}|i i ddi}|i|joti dSq#q#Wnt|d}|i|id|i dS(s<Append a string to a text file if it isn't already in there.sis#sa+s N(sosspathsexistss fullpathnames fileinputsinputslinesstripslowers expandtabsssplitsstrsbaresclosesopensfileswrite(sstrs fullpathnamesfilesbaresline((s./TMDA/Util.pysappend_to_files cCstiid}|tjoLxIddfD]7}tid|i}|djo |}Pq,q,Wnyti|di |Wnt j o dSnXdS(s>Display a string using a UNIX text pager such as less or more.sPAGERslesssmoreswhich sswN( sossenvironsgetspagersNonesprogspopensreadspathswritesstrsIOError(sstrspagersprogspath((s./TMDA/Util.pyspagers    cCs@|idd}|idd}|i}|td SdS(sReturn a normalized version of the given sender address for use in ~/.tmda/responses. - Any / characters are replaced with : to prevent creation of files outside the directory. - Spaces are replaced with underscores. - The address is lowercased. - Truncate sender at 233 chars to insure the full filename (including time, pid, and two dots) fits within the POSIX limit of 255 chars for a filename. s s_s/s:iN(ssendersreplaceslowersPOSIX_NAME_MAX(ssender((s./TMDA/Util.pysnormalize_senders  cCs| o|Snd|jp d|jo|Sndk} |i} |i}| idd\} }di |idd}| i| i d}|idd\}}di |idd} |i| i d}| i} | djo|Sn| djo|| jo|Sqn| djo&||jo ||jo|Sqn| djo&||jo | |jo|Sqnb| d jo||jo|Sqn<| d jo| |jo|Sqn| d jo|Sn|SdS( s xp is an address from the ``X-Primary-Address'' header. rp is the envelope sender address. Compare the two addresses, and return the address appropriate for CONFIRM_APPEND based on the PRIMARY_ADDRESS_MATCH setting. s@Nis.iiiiiii(sxpsrpsDefaultsslowersrplsxplssplitsrplocalsrphostsjoinsrpdomainsRECIPIENT_DELIMITERs rpusernamesxplocalsxphostsxpdomains xpusernamesPRIMARY_ADDRESS_MATCHsmatch(sxpsrpsrphostsxplocalsxpls rpusernames xpusernamesrpdomainsxphostsrplocalsDefaultssxpdomainsrplsmatch((s./TMDA/Util.pysconfirm_append_addresssF                   cCsddkl}|o&dkl}||i|}n#dkl}||i|}|SdS(szRead a file and parse its contents into a Message object model. Replacement for email.message_from_file(). We use the HeaderParser subclass instead of Parser to avoid trying to parse the message body, instead setting the payload to the raw body as a string. This is faster, and also helps us avoid problems trying to parse spam with broken MIME bodies.(sMessage(sParser(s HeaderParserN( s email.messagesMessages fullParses email.parsersParsersparsesfpsmsgs HeaderParser(sfps fullParsesParsers HeaderParsersmsgsMessage((s./TMDA/Util.pys msg_from_files   cCsXdkl}t}|i}||d|d|}|i |d||i SdS(suA more flexible replacement for Message.as_string(). The default is a textual representation of the message where the headers are not wrapped, From is not escaped, and a leading From_ line is not added. msg is an email.message.Message object. maxheaderlen specifies the longest length for a non-continued header. Disabled by default. RFC 2822 recommends 78. mangle_from_ escapes any line in the body that begins with "From" with ">". Useful when writing to Unix mbox files. Default is False. unixfrom forces the printing of the envelope header delimiter. Default is False.(s generators mangle_from_s maxheaderlensunixfromN( semails generatorsStringIOsfps Generatorsgenclasss mangle_from_s maxheaderlensgsflattensmsgsunixfromsgetvalue(smsgs maxheaderlens mangle_from_sunixfromsfpsgenclasss generatorsg((s./TMDA/Util.pys msg_as_strings   cCsdk}|djo d}n|djo#|iddfjo |idjo d}n|idjo,|idd|d |f}t||nZ|id jo6dk }|i }|i ||||i ntid |idS( sSend e-mail via direct SMTP, or by opening a pipe to the sendmail program. msgstr is an rfc2822 message as a string. envrecip is the envelope recipient address. envsender is the envelope sender address. Nss<>spostfixsqmailssendmails-is-fs--ssmtpsInvalid MAIL_TRANSPORT method: (sDefaultss envsendersMAIL_TRANSFER_AGENTsMAIL_TRANSPORTsSENDMAIL_PROGRAMsenvrecipscmdspipecmdsmsgstrsSMTPs ConnectionsserverssendmailsquitsErrorss ConfigError(smsgstrsenvrecips envsenderscmdsSMTPsserversDefaults((s./TMDA/Util.pyssendmail,s    3   cCsy[dkl}g}|i|}x|D]}|i|dq,Wdi|}|SWnti i j o |SnXdS(sgAccept a possibly encoded message header as a string, and return a decoded string if it can be decoded. JRM: email.header has a decode_header method, but it returns a list of decoded pairs, one for each part of the header, which is an awkward interface IMO, especially when the header contains a mix of encoded and non-encoded parts. (sheaderis N( semailsheaderspartss decode_headersstrspairsspairsappendsjoinsdecoded_stringserrorssHeaderParseError(sstrspairssdecoded_stringsheaderspartsspair((s./TMDA/Util.pys decode_header]s cCs?gi}|iD] \}}|d||fq~SdS(sbReturn a list containing the entire set of header lines, in the order in which they were read.s%s: %sN(sappends_[1]smsgsitemssksv(smsgs_[1]sksv((s./TMDA/Util.pysheaders_as_listrscCs+t|}|id}||d SdS(s/Return the headers as a raw (undecoded) string.s iN(s msg_as_stringsmsgsmsgtextsindexsidx(smsgsmsgtextsidx((s./TMDA/Util.pysheaders_as_raw_stringxs cCsyPdigi}|iD]&\}}|d|t|fq~}Wn%t i i j ot |}nX|SdS(sReturn the (decoded) message headers as a string. If the sequence can't be decoded, punt and return a raw (undecoded) string instead.s s%s: %sN( sjoinsappends_[1]smsgsitemssksvs decode_headershdrstrsemailserrorssHeaderParseErrorsheaders_as_raw_string(smsgsksvs_[1]shdrstr((s./TMDA/Util.pysheaders_as_strings PcCs+t|}|id}||dSdS(s,Return the body as a raw (undecoded) string.s iN(s msg_as_stringsmsgsmsgtextsindexsidx(smsgsmsgtextsidx((s./TMDA/Util.pysbody_as_raw_strings cCsz|i|ofxc|iD]T}|di|ijo1|ii|}|d|df|i| Copyright (C) 1998,1999,2000,2001 by the Free Software Foundation, Inc., and licensed under the GNU General Public License version 2. Nis/s~sTMDA_TEMPLATE_DIRsSENDERs@is.sTMDA_RECIPIENTis templatess share/tmdas /etc/tmda/s Can't find sr(.sDefaultssNonesfoundits templatefilesosspaths expandusersexistss searchdirssappendsenvironsgetsTEMPLATE_DIR_MATCH_SENDERs TEMPLATE_DIRslowerssendersjoinssplits domainpartssrangeslensis IndexErrorsTEMPLATE_DIR_MATCH_RECIPIENTs recipients recipparts domainpartsRECIPIENT_DELIMITERs recippartss PARENTDIRssyssprefixsdirsfilenamesIOErrorsopensfpsreadstemplatescloses__dict__scopys localdictsupdatesvardictstext(s templatefilesvardicts domainpartsstexts domainpartsfilenamestemplatesfps recippartssDefaultss searchdirss recipientsfounditssendersis recipparts localdictsdir((s./TMDA/Util.pysmaketextJsv "(  (        c Csdk}dk}|i|i}|i||i||g\}}d|}|GHdt |GH|o dG|GHn|o dG|GHndt |GH|o dG|GHndGHdS(s;Check if the give e-mail addresses match lines in filename.Ns Checking s-sTo:sFrom:sMATCH:sSorry, no matching lines.( sDefaultss FilterParsers DB_CONNECTIONsfiltersreadsfilenames firstmatchsrecipssendersactionss matchlines checking_msgslen( sfilenamesrecipssenders matchlines checking_msgs FilterParsersactionssfiltersDefaults((s./TMDA/Util.pys filter_matchs"       cCsAyt|t||SWn#tj o| odSq=nXdS(Ni(sCanModesfiles MODE_READsuidsgidsIOErrors raiseError(sfilesuidsgids raiseError((s./TMDA/Util.pysCanReads cCsAyt|t||SWn#tj o| odSq=nXdS(Ni(sCanModesfiles MODE_WRITEsuidsgidsIOErrors raiseError(sfilesuidsgids raiseError((s./TMDA/Util.pysCanWrites cCsAyt|t||SWn#tj o| odSq=nXdS(Ni(sCanModesfiles MODE_EXECsuidsgidsIOErrors raiseError(sfilesuidsgids raiseError((s./TMDA/Util.pysCanExecs cCsyti|}Wntd|nX|tjoti}n|tjoti }n|d}|d}|dd@}|djodSn`||@odSnM||d@o ||jodSn)||d@o ||jodSndSdS( Ns'%s' does not existiiiiiii@(sossstatsfilesfstatsIOErrorsuidsNonesgeteuidsgidsgetegidsneeduidsneedgidsfilemodsmode(sfilesmodesuidsgidsneeduidsfilemodsneedgidsfstat((s./TMDA/Util.pysCanModes(      s DevnullOutputcBs#tZdZdZdZRS(NcCsdS(N((sselfsmsg((s./TMDA/Util.pyswritescCsdS(N((sself((s./TMDA/Util.pysflushscCsdSdS(Ns((sself((s./TMDA/Util.pys__repr__s(s__name__s __module__swritesflushs__repr__(((s./TMDA/Util.pys DevnullOutputs  s StringOutputcBs,tZdZdZdZdZRS(NcCs d|_dS(Ns(sselfs_StringOutput__content(sself((s./TMDA/Util.pys__init__scCs(|djo|id|7_ndS(Nss%s(smsgsselfs_StringOutput__content(sselfsmsg((s./TMDA/Util.pyswrites cCs d|_dS(Ns(sselfs_StringOutput__content(sself((s./TMDA/Util.pysflushscCs |iSdS(N(sselfs_StringOutput__content(sself((s./TMDA/Util.pys__repr__s(s__name__s __module__s__init__swritesflushs__repr__(((s./TMDA/Util.pys StringOutputs   s DebugablecBs8tZedZddZddZdZRS(NcCs3||_|itjo d|_n d|_dS(Nii(s outputObjectsselfs DEBUGSTREAMs DevnullOutputslevel(sselfs outputObject((s./TMDA/Util.pys__init__s  icCs%|i|jo|i|IJndS(N(sselfslevels DEBUGSTREAMsmsg(sselfsmsgslevel((s./TMDA/Util.pysdebugscCs |t_dS(N(slevelsself(slevel((s./TMDA/Util.pys set_debugscCs dt_dS(Ni(sselfslevel(((s./TMDA/Util.pys set_nodebugs(s__name__s __module__s DevnullOutputs__init__sdebugs set_debugs set_nodebug(((s./TMDA/Util.pys Debugables  (Ns__doc__s cStringIOsStringIOscPicklesemails email.utilss fileinputsfnmatchsosspopen2sressocketsstatssysstempfilestextwrapstimesErrorss MODE_EXECs MODE_READs MODE_WRITEsPOSIX_NAME_MAXs gethostnames getfullnames getusernamesgetuidsgetgids gethomedirs getgrouplists getfilemodes getfileuidsisstickysgetvdomainprependsgetvuserhomedirs getuserparamssRunTaskssecondssformat_timeoutsNonesunixdates make_msgids make_dates file_to_dicts file_to_listspipecmds writefilesappend_to_filespagersnormalize_sendersconfirm_append_addresssFalses msg_from_files msg_as_stringssendmails decode_headersheaders_as_listsheaders_as_raw_stringsheaders_as_stringsbody_as_raw_stringsrename_headerss add_headerss purge_headerss build_cdbs build_dbmspickleitsunpickles db_inserts findmatchswraptextsmaketexts filter_matchsCanReadsCanWritesCanExecsCanModes DevnullOutputs StringOutputs Debugable(Is getfilemodes getfileuidsErrorssCanModesisstickysunixdates MODE_WRITEspopen2sstatsunpickles MODE_READs getuserparamssgetvdomainprepends file_to_dicts fileinputs StringOutputswraptextsgetgidsheaders_as_lists make_msgidsmaketexts gethostnamestempfilespickleitsformat_timeouts findmatchs filter_matchsresrename_headerssCanWritesconfirm_append_addresssCanReads purge_headerssemails make_dates build_cdbscPicklesheaders_as_raw_strings gethomedirssendmails getgrouplistsnormalize_senderstextwraps file_to_listssyss writefilespipecmdsCanExecs MODE_EXECs decode_headersPOSIX_NAME_MAXs build_dbmspagers getusernamesappend_to_filesgetuidssocketsheaders_as_stringsStringIOs db_insertsgetvuserhomedirs msg_as_stringssecondss msg_from_files add_headerss DevnullOutputs DebugablesRunTasks getfullnamesbody_as_raw_stringstimesfnmatchsos((s./TMDA/Util.pys?s                        %       :    3  1              ]