spider */ use Bitweaver\KernelTools; function mailman_verify_list( $pListName ) { $error = NULL; if( $matches = preg_match( '/[^A-Za-z0-9\-\_]/', $pListName ) ) { $error = KernelTools::tra( "Invalid mailing list name" ).": ".KernelTools::tra( "List names can only contain letters and numbers" ); } else { $lists = mailman_list_lists(); if( !empty( $lists[strtolower($pListName)] ) ) { $error = KernelTools::tra( "Invalid mailing list name" ).": ".KernelTools::tra( "List already exists" ); } } return $error; } function mailman_list_lists() { $ret = []; if( $ret_code = mailman_command( "list_lists", $output) ) { mailman_fatal(KernelTools::tra("Unable to list lists."), $ret_code); } else { foreach( $output as $o ) { if( strpos( $o, "-" ) ) { list( $name, $desc ) = split( "-", $o ); $ret[strtolower( trim( $name ) )] = trim( $desc ); } } } return( $ret ); } function mailman_list_members( $pListName ) { $ret = []; $options = escapeshellarg( $pListName ); if( $ret = mailman_command( "list_members", $output, $options ) ) { // mailman_fatal(KernelTools::tra('Unable to get members for list: ').$pListName, $ret); } return( $output ); } // pParamHash follows naming convention off config_list --help usage instructions function mailman_config_list( $pParamHash ){ $options = " -i ".escapeshellarg(UTIL_PKG_INCLUDE_PATH."mailman.cfg"); $options .= " ".escapeshellarg( $pParamHash["listname"] ); if( $ret = mailman_command( "config_list", $output, $options) ) { return (KernelTools::tra("Unable to configure list: ").$pParamHash["listname"].":".$ret); } } // pParamHash follows naming convention off newlist --help usage instructions function mailman_newlist( $pParamHash ) { $error = NULL; if( !($error = mailman_verify_list( $pParamHash["listname"] )) ) { $options = ""; if( !empty( $pParamHash["listhost"] ) ) { $options .= " -e ".escapeshellarg( $pParamHash["listhost"] ); } $options .= " -q ".escapeshellarg( $pParamHash["listname"] ); $options .= " ".escapeshellarg( $pParamHash["listadmin-addr"] )." "; $options .= " ".escapeshellarg( $pParamHash["admin-password"] )." "; if( $ret = mailman_command( "newlist", $output, $options ) ) { return (KernelTools::tra("Unable to create list: ").$pParamHash["listname"].":".$ret); } $options = " -i ".escapeshellarg(UTIL_PKG_INCLUDE_PATH."mailman.cfg"); $options .= " ".escapeshellarg( $pParamHash["listname"] ); if( $ret = mailman_command( "config_list", $output, $options) ) { // @TODO if this fails we should roll back the newlist created return (KernelTools::tra("Unable to configure list: ").$pParamHash["listname"].":".$ret); } $newList = $pParamHash["listname"]; $mailman = mailman_get_mailman_command(); $newAliases = " ## $newList mailing list $newList: \"|$mailman post $newList\" $newList-admin: \"|$mailman admin $newList\" $newList-bounces: \"|$mailman bounces $newList\" $newList-confirm: \"|$mailman confirm $newList\" $newList-join: \"|$mailman join $newList\" $newList-leave: \"|$mailman leave $newList\" $newList-owner: \"|$mailman owner $newList\" $newList-request: \"|$mailman request $newList\" $newList-subscribe: \"|$mailman subscribe $newList\" $newList-unsubscribe: \"|$mailman unsubscribe $newList\""; // Make sure we unlock the semaphore ignore_user_abort(true); // Get a lock. flock is not reliable (NFS, FAT, etc) // so we use a semaphore instead. $sem_key = ftok(mailman_get_aliases_file(), "m"); if( $sem_key != -1 ) { // Get the semaphore $sem = sem_get($sem_key); if( $sem ) { if( sem_acquire($sem) ) { if( $fh = fopen( mailman_get_aliases_file(), "a" ) ) { fwrite( $fh, $newAliases ); fclose( $fh ); mailman_newalias(); } else { $error = "Could not open /etc/aliases for appending."; } if( !sem_release($sem) ) { $error = "Error releasing a sempahore."; } } else { $error = "Unable to aquire a semaphore."; } } else { $error = "Unable to get a semaphore."; } } else { $error = "Couldn't create semaphore key."; } // Let the user cancel again ignore_user_abort(false); } return $error; } function mailman_remove_member( $pListName, $pEmail ) { $ret = ""; if( $fullCommand = mailman_get_command( "remove_members" ) ) { $cmd = "echo ".escapeshellarg( $pEmail )." | $fullCommand -f - ".escapeshellarg( $pListName ); exec( $cmd, $ret ); } else { bit_error_log( "Groups mailman command failed (remove_members) File not found: ".$fullCommand ); } } function mailman_setmoderated( $pListName, $pModerated = TRUE ) { $ret = FALSE; if( $fullCommand = mailman_get_command( "withlist" ) ) { $cmd = $fullCommand." -q -l -r mailman_lib.setDefaultModerationFlag ".escapeshellarg( $pListName )." ".( $pModerated ? 1 : 0 ); $cmd = '/bin/sh -c "PYTHONPATH='.UTIL_PKG_INCLUDE_PATH." $cmd\""; exec( $cmd, $ret ); } else { bit_error_log( "Groups mailman command failed (withlist) File not found: ".$fullCommand ); } return $ret; } function mailman_setmoderator( $pListName, $pEmail ) { $ret = ""; if( $fullCommand = mailman_get_command( "withlist" ) ) { $cmd = $fullCommand." -q -l -r mailman_lib.setMemberModeratedFlag ".escapeshellarg( $pListName )." ".escapeshellarg( $pEmail ); $cmd = '/bin/sh -c "PYTHONPATH='.UTIL_PKG_INCLUDE_PATH." $cmd\""; exec( $cmd, $ret ); } else { bit_error_log( "Groups mailman command failed (withlist) File not found: ".$fullCommand ); } return $ret; } function mailman_addmember( $pListName, $pEmail, $pType = NULL ) { $ret = ""; if( $fullCommand = mailman_get_command( "add_members" ) ) { switch( $pType){ case "digest": $cmd = "echo ".escapeshellarg( $pEmail )." | $fullCommand -d - ".escapeshellarg( $pListName ); break; case "email": default: $cmd = "echo ".escapeshellarg( $pEmail )." | $fullCommand -r - ".escapeshellarg( $pListName ); break; } exec( $cmd, $ret ); /** * its not enough to rely on the add_members call * add_members is ignored by mailman if the user is already a member * bw relies on mailman_addmember to toggle if a user wants to receive a digest or not * to keep things simple in bw we conveniently handle the toggle here **/ if( !empty( $pType ) ){ mailman_setsubscriptiontype( $pListName, $pEmail, $pType ); } } else { bit_error_log( "Groups mailman command failed (add_members) File not found: ".$fullCommand ); } } function mailman_findmember( $pListName, $pEmail ) { $options = " -l ".escapeshellarg( $pListName )." ".escapeshellarg( $pEmail ); if( $ret = mailman_command( "find_member", $output, $options ) ) { mailman_fatal(KernelTools::tra("Unable to find member in list: ").$pListName, $ret); } return $output; } function mailman_setsubscriptiontype( $pListName, $pEmail, $pType ) { if( $fullCommand = mailman_get_command( "withlist" ) ) { $cmd = $fullCommand." -q -l -r mailman_lib.setSubscriptionType ".escapeshellarg( $pListName )." ".escapeshellarg( $pEmail )." ".( $pType == "digest" ? 1 : 0 ); $cmd = '/bin/sh -c "PYTHONPATH='.UTIL_PKG_INCLUDE_PATH." $cmd\""; exec( $cmd, $ret ); }else{ bit_error_log( "Groups mailman command failed (withlist) File not found: ".$fullCommand ); } return $ret; } function mailman_getsubscriptiontype( $pListName, $pEmail ){ // even though setSubscriptionType returns a string or false we return an array because thats what exec returns if( $fullCommand = mailman_get_command( "withlist" ) ) { $cmd = $fullCommand." -l -r mailman_lib.getSubscriptionType ".escapeshellarg( $pListName )." ".escapeshellarg( $pEmail ); $cmd = '/bin/sh -c "PYTHONPATH='.UTIL_PKG_INCLUDE_PATH." $cmd\""; exec( $cmd, $ret ); }else{ bit_error_log( "Groups mailman command failed (withlist) File not found: ".$fullCommand ); } return $ret; } function mailman_rmlist( $pListName ) { $error = NULL; if( mailman_verify_list( $pListName ) ) { $options = " -a ".escapeshellarg( $pListName ); if( $ret = mailman_command( "rmlist", $output, $options ) ) { mailman_fatal(KernelTools::tra("Unable to remove list: ").$pListName, $ret); } $newList = $pListName; $mailman = mailman_get_mailman_command(); $aliasesLines = [ "## $newList mailing list", "$newList", "$newList-admin", "$newList-bounces", "$newList-confirm", "$newList-join", "$newList-leave", "$newList-owner", "$newList-request", "$newList-subscribe", "$newList-unsubscribe", ]; // Make sure we unlock the semaphore ignore_user_abort(true); // Get a lock. flock is not reliable (NFS, FAT, etc) // so we use a semaphore instead. $sem_key = ftok(mailman_get_aliases_file(), "m"); if( $sem_key != -1 ) { // Get the semaphore $sem = sem_get($sem_key); if( $sem ) { if( sem_acquire($sem) ) { if( $fh = fopen( mailman_get_aliases_file(), "r+" ) ) { // cull out all aliase lines for the mailing list and rewrite the file $newContents = ""; while( $line = fgets( $fh ) ) { @list( $alias, $value ) = split( ":", $line ); $alias = trim( $alias ); if( !in_array($alias, $aliasesLines) ) { $newContents .= $line; } } // Truncate the file if( ftruncate($fh, 0) != 1) { $error = "Unable to truncate /etc/aliases"; } else { if( !rewind($fh) ) { $error = "Unable to seek /etc/aliases"; } else { if( empty( $newContents ) ) { $error = "Empty aliases for /etc/aliases"; } elseif( !fwrite( $fh, $newContents ) ) { $error = "Could not write new /etc/aliases"; } } } fclose( $fh ); mailman_newalias(); } else { $error = "Could not open /etc/aliases for appending."; } if( !sem_release($sem) ) { $error = "Error releasing a sempahore."; } } else { $error = "Unable to aquire a semaphore."; } } else { $error = "Unable to get a semaphore."; } } else { $error = "Couldn't create semaphore key."; } // Let the user cancel again ignore_user_abort(false); } return $error; } function mailman_newalias() { global $gBitSystem; exec( $gBitSystem->getConfig("server_newaliases_cmd", "/usr/bin/newaliases" )); } function mailman_get_aliases_file() { global $gBitSystem; return $gBitSystem->getConfig("server_aliases_file", "/etc/aliases"); } function mailman_command( $pCommand, &$output, $pOptions=NULL ) { $ret_code = NULL; if( $fullCommand = mailman_get_command( $pCommand ) ) { $cmd = $fullCommand." ".$pOptions; if( !defined( "IS_LIVE" ) || !IS_LIVE ) { bit_error_log( "mailman LOG: ".$cmd ); } exec( $cmd, $output, $ret_code ); if( $ret_code ) { bit_error_log("Error running command: ". $cmd . " Command returned: ".$ret_code); } } else { bit_error_log( "Groups mailman command failed (".$pCommand."): File not found: ".$fullCommand ); } return $ret_code; } function mailman_fatal($pMessage, $pRetCode) { global $gBitSystem; $gBitSystem->fatalError($pMessage.KernelTools::tra(" Command returned: ").$pRetCode); die; } function mailman_get_mailman_command() { global $gBitSystem; $mailman = $gBitSystem->getConfig( "server_mailman_cmd", "/usr/lib/mailman/mail/mailman" ); return $mailman; } function mailman_get_command( $pCommand ) { global $gBitSystem; $ret = NULL; // Support for legacy configurations $fullCommand = $gBitSystem->getConfig( "server_mailman_bin" ) ."/".$pCommand; $fullCommand = str_replace( "//", "/", $fullCommand ); if( file_exists( $fullCommand ) ) { $ret = $fullCommand; } return $ret; } ?>