#!/usr/bin/php -q .com' that is easy to remember. * 5. In your vBulletin control panel, add a option for profiles that is hidden from others, but editable by users. Call it something like "Your MMS-email address". In there, users will enter in the email address their MMS's come from. While most are in the form of @.com, some can have custom aliases instead of the phone; to let them still post; it is easier for users to find out the email their MMS's come from by emailing an MMS to their own email and then seeing the email it comes from; that is the one they would put in this field. * 6. Configure the user-variables below to match the setup for your forum * 7. Set a cron for this script for it to be checked every hour or every 10 minutes or whatever. * 8. Enjoy! * * -=NOTES=- * There's good news and there's bad new: * the good news: since this will check for the username as a registered forum member, any spam that comes to the mms email address won't be posted to the thread! :) * the bad news: since I did not have enough time to make the MMS posts go through VB's proper posting/replying procedure; I hacked it up...bad. So it does just a plain old mysql_query() that does and "INSERT" into the vb table that contains posts! Hah! So please if someone knows the internal workings of VB (I don't have time to look through the code) then please update this script to make it proper. :) * As a result, your post-count and all that good stuff will not increase from MMS-posting; sorry! * * -= History =- * Version 0.01 (2006-02) * First release -= Begin Program =- **/ //Load up some usefull libraries, do not change this define("ROOTDIR",dirname(__FILE__)); require_once (ROOTDIR . '/mimedecode.php'); /* USER VARIABLES */ //Change the ones below to your email setup define("MAILSERVER",'your-mailserver'); define("MAILSERVER_PORT",'110'); //or whatever POP port you use for email define("MAILSERVER_LOGIN",'mms-login'); //the login for the mms mail account define("MAILSERVER_PASS",'mms-pass'); //the password for the mms mail account //change the vars below for your vBulletin setup, to find the tables I'm talking about, phpMyAdmin is a great SQL explorer script that is included in popular hosting packages such as cpanelx and plesk $thread_id = "1"; //THIS IS THE ID OF THE THREAD WHERE YOU WANT NEW MMS'S TO GO; YOU CAN FIND THE ONE YOU WANT FROM THE 'vb_thread' TABLE $post_table = "vb_post"; //TABLE NAME WHERE THE POSTS ARE STORED; DEFAULT IS 'vb_post' //MySQL database connection parameters, change these according to your MySQL setup $db_host = "your-vb-db-host"; //usually 'localhost' $db_user = "your-vb-db-login-name"; $db_pass = "your-vb-db-login-password"; $db = "your-vb-database"; //user-info parameters $user_table = "vb_user"; //TABLE WHERE USER INFO IS STORED FOR FORUM MEMBERS; DEFAULT IS 'vb_user' $user_field_table = "vb_userfield"; //TABLE WHERE CUSTOM FIELDS FOR EACH USER ARE STORED; DEFAULT IS: 'vb_userfield' $mms_field = "field5"; //TABLE COLUMN IN THE USERFIELD TABLE THAT CORRESPONDS TO THE CUSTOM MMS-EMAIL FIELD CREATES, field5 in this case //Providers will often insert their shitty little ads to MMS's, if you know the name of images, add them to this array and it will filter them out //the ones pre-loaded below filter out the ads put in by T-Mobile USA $provider_crap = array( 't-mobile1' => 'masthead.jpg', 't-mobile2' => 'audio.gif', 't-mobile3' => 'dottedLine_350.gif', 't-mobile4' => 'dottedLine_600.gif', 't-mobile5' => 'spacer.gif' ); //that's it! leave the below as is! deffine("PHOTOSDIR","/mms-photos/"); //relative directory, must include extra slashes define("FILESDIR","/mms-files/"); //relative directory, must include extra slashes define("RESIZE_LARGE_IMAGES",true); // true/false - if on it will automatically make thumbnails if you have gd installed //If the image is bigger than this a scaled version will be made define("MAX_IMAGE_WIDTH",400); //if you don't want to add a subject in the email then you assign a default define("DEFAULT_TITLE",'Live from mobile'); //What protocol are we using? Just leave it default if you don't know define("INPUT_PROTOCOL","pop3"); //pop3/smtp //Delete mail on successful download? Setting FALSE for debuging //define("DELETE_MAIL_AFTER_PROCESSING",false); define("DELETE_MAIL_AFTER_PROCESSING",true); define("TIME_OFFSET",0); //drop Sigature, Everything after -- is deleted in the text msg. define("DROP_SIGNATURE",true); //Controls if HTML is allowed in the subject of an email define("ALLOW_HTML_IN_SUBJECT",false); //Controls if HTML is allowed in the body of an email define("ALLOW_HTML_IN_BODY",false); define("IMAGE_PLACEHOLDER",'#img%#'); // % - The percent sign is used to represent the number of the image. define("START_IMAGE_COUNT_AT_ZERO",false); // Where to start placeholders. //true means the first image is 0 //false means the first is 1 //For example, #img0# or #img1# (Every occurence of a particular placeholder should show that image.) define ("WORDPRESSMAIL",''); define ("SECONDMAIL",''); define ("IMAGECLASS",'wp-mailimage'); // CSS-Class of inserted Images. You can set .wp-mailimage{border: 1px solid black} for example in your CSS-File. define ("IMAGESTYLE",'border: none;'); //If you dont want to change your CSS-File, you can insert values directly. This replaces IMAGESTYLE in with your favorite CSS-value. define ("JPEGQUALITY",80); //Compression Value for resized JPEG-Images //Choose Value between 0 (high Compression - ugly) and 100 (almost no Compression - pretty). //75 is a good compromise between filesize and look, 85 looks good. Higher values are too much in most cases. define("ISO_8859_1",false); // Is your blog encoded in ISO-8859-1 ? /* END OF USER VARIABLES */ //some variables error_reporting(2037); if (!ConfirmTrailingSlash(PHOTOSDIR)) { die("The PHOTOSDIR directive must have a / at the end\n"); } if (!ConfirmTrailingSlash(FILESDIR)) { die("The FILESDIR directive must have a / at the end\n"); } if (RESIZE_LARGE_IMAGES && !HasGDInstalled()) { die("You have turned on RESIZE_LARGE_IMAGES, but you do not have GD installed properly.\n"); } define("URLPHOTOSDIR", "http://vbtest.ninetoez.net" . PHOTOSDIR); define("REALPHOTOSDIR",ROOTDIR . PHOTOSDIR); define("URLFILESDIR", "http://vbtest.ninetoez.net" . FILESDIR); define("REALFILESDIR",ROOTDIR . FILESDIR); //Retreive emails switch ( strtolower(INPUT_PROTOCOL) ) { case 'smtp': //direct $fd = fopen("php://stdin", "r"); $input = ""; while (!feof($fd)) { $input .= fread($fd, 1024); } fclose($fd); $emails[0] = $input; break; case 'pop3': default: $emails = pop3_retr(); } //loop through messages foreach ($emails as $email) { //sanity check to see if there is any info in the message if ($email == NULL ) { print 'Dang, message is empty!'; die; } //decode the mime $params['include_bodies'] = true; $params['decode_bodies'] = false; $params['decode_headers'] = true; $params['input'] = $email; $structure = Mail_mimeDecode::decode($params); # print_r ($structure); //assign the default title/subject if ( $structure->headers['subject'] == NULL ) { $subject = DEFAULT_TITLE; } else { if (ALLOW_HTML_IN_SUBJECT) { $subject = $structure->headers['subject']; } else { $subject = htmlentities($structure->headers['subject']); } } $from = trim($structure->headers['from']); //work around for users with extra <> in email address if (preg_match('/^[^<>]+<([^<>]+)>$/',$from,$matches)) { $from = $matches[1]; } $IMAGES = array(); $content = get_content($structure,$IMAGES); // Moved by Hey: // Hint: All images without placeholder appear behind the signature. // If this is executed after the insertion of images, images are not visible //remove signature //********if ( DROP_SIGNATURE ) { $content = removesig($content); } //Begin Search and Replace for image placeholders (START_IMAGE_COUNT_AT_ZERO ? $startIndex = 0 :$startIndex = 1); foreach ( $IMAGES as $i => $value ) { // looks for ' #img1# ' etc... and replaces with image $img_placeholder_temp = str_replace("%", intval($startIndex + $i), IMAGE_PLACEHOLDER); if ( stristr($content, $img_placeholder_temp) ) { $content = str_replace($img_placeholder_temp, $IMAGES[$i], $content); } else { $content .= $IMAGES[$i]; } } // End Image Place Holder Search and Replace //date reformating $ddate = trim($structure->headers['date']); //**************** list($post_date,$post_date_gmt) = DeterminePostDate($content,$ddate); //decode ubb //$content = ubb2html($content); //filter new lines $content = filternewlines($content); //Added by HEY: //Do not take $subject as post_name. WP using mod_rewrite will be broken! $postname = subject2postname($subject); // Report // print '

Mail Format: ' . $mailformat . '

' . "\n"; print '

From: ' . $from . '
' . "\n"; print 'Date: ' . $post_date . '
' . "\n"; print 'Date GMT: ' . $post_date_gmt . '
' . "\n"; print 'Category: ' . $post_categories[0] . '
' . "\n"; print 'Subject: ' . $subject . '
' . "\n"; //Added by HEY print 'Postname: ' . $postname . '
' . "\n"; print 'Posted content:

' . $content . ''; $mms_from = $from; $dbc = mysql_connect($db_host, $db_user, $db_pass); mysql_select_db($db, $dbc); $user_id = ""; $username = ""; $time = time(); $mms_title = $subject; $userid_query = mysql_query("SELECT userid from ".$user_field_table." WHERE LOWER(".$mms_field.") = '".strtolower($mms_from)."'", $dbc); if(mysql_num_rows($userid_query) > 0) { while($row = mysql_fetch_object($userid_query)) { $user_id = $row->userid; $username_query = mysql_query("SELECT username from ".$user_table." WHERE userid = '".$user_id."'", $dbc); if(mysql_num_rows($username_query) > 0) { while($row1 = mysql_fetch_object($username_query)) { $username = $row1->username; //echo $username; mysql_query("INSERT INTO ".$post_table." (threadid, username, userid, title, dateline, pagetext) VALUES (".$thread_id.", '".$username."', ".$user_id.", '".$mms_title."', '".$time."', '".$content."')", $dbc) or die(mysql_error($dbc)); } } else { //the user does not echo "user does not exist"; } } } else { //the user does not echo "user does not exist"; continue; } } // end looping over messages /* END PROGRAM */ /** FUNCTIONS **/ //retrieve POP3 mail function pop3_retr ( ) { require_once('class-pop3.php'); $pop3 = new POP3(); if (!$pop3->connect(MAILSERVER, MAILSERVER_PORT)) : echo "Ooops $pop3->ERROR
\n"; exit; endif; //Check to see if there is any mail, if not die $msg_count = $pop3->login(MAILSERVER_LOGIN, MAILSERVER_PASS); if (0 == $msg_count) { $pop3->quit(); die(('There does not seem to be any new mail.')); } // loop through messages for ($i=1; $i <= $msg_count; $i++) { $emails[$i] = implode ('',$pop3->get($i)); #if ( DELETE_MAIL_AFTER_PROCESSING && checkposter($emails[$i]) ) { if ( DELETE_MAIL_AFTER_PROCESSING) { if( !$pop3->delete($i) ) { echo '

Oops '.$pop3->ERROR.'

'; $pop3->reset(); exit; } else { echo "Mission complete, message $i deleted."; } } } //clean up $pop3->quit(); return $emails; } //tear apart the meta part for useful information function get_content ($part,&$IMAGES) { //fixes // Hint by Hey: // I think this should not be done if we have plain text and want to keep it in utf-8. // Leaving it anyway 'cause I don't use utf-8 and can not test ist. global $provider_crap; $part = charsetcheck($part); $part = base64check($part); switch ( strtolower($part->ctype_primary) ) { case 'multipart': $meta_return = ''; foreach ($part->parts as $section) { $meta_return .= get_content($section,$IMAGES); } break; case 'text': // Added by Hey: Convert Quoted-Printable if needed $part = quotedprintablecheck($part); // Added by Hey: Convert to ISO-8859-1 if desired $part = isocheck($part); //go through each sub-section if ($part->ctype_secondary=='enriched') { //convert enriched text to html $meta_return = etf2html($part->body ) . "\n"; $meta_return = ''; } elseif ($part->ctype_secondary=='html') //check for html encoding { //strip excess html //$meta_return = html2html($part->body ) . "\n"; $meta_return = ''; } else { //regular text, so just strip the pgp signature if (ALLOW_HTML_IN_BODY) { $meta_return = $part->body . "\n"; } else { $meta_return = htmlentities( $part->body ) . "\n"; } $meta_return = strip_pgp($meta_return); } break; case 'image': $real_image_name = $part->ctype_parameters['name']; //echo $real_image_name; $break_me = 0; foreach($provider_crap as $provider_crap_filter) { if($real_image_name == $provider_crap_filter) { $break_me++; } } if($break_me > 0) { break; } $file = GenerateImageFileName(REALPHOTOSDIR, $part->ctype_secondary); //This makes sure there is no collision $ctr = 0; while(file_exists($file) && $ctr < 1000) { $file = GenerateImageFileName(REALPHOTOSDIR, $part->ctype_secondary); $ctr++; } if ($ctr >= 1000) { die("Unable to find a name for images that does not collide\n"); } $fileName = basename($file); //echo $fileName; gives filenames in already changed form, timestamp format thing $fp = fopen($file, 'w'); fwrite($fp, $part->body); fclose($fp); @exec ('chmod 755 ' . $file); if (RESIZE_LARGE_IMAGES) { list($thumbImage, $fullImage) = ResizeImage($file,$part->ctype_secondary); if ($thumbImage) { //$IMAGES[] .= '' . $part->ctype_parameters['name'] . '' . "\n"; $IMAGES[] .= '[url=' . URLPHOTOSDIR . $fullImage . '][img]' . URLPHOTOSDIR . $thumbImage .'[/img][/url]' . "\n"; } else { //$IMAGES[] .= '' . $part->ctype_parameters['name'] . '' . "\n"; $IMAGES[] .= '[img]' . URLPHOTOSDIR . $fullImage . '[/img]' . "\n"; } } else { //$IMAGES[] .= '' . $part->ctype_parameters['name'] . '' . "\n"; $IMAGES[] .= '[img]' . URLPHOTOSDIR . $fileName . '[/img]' . "\n"; } break; case 'application': //pgp signature - then forget it if ( $part->ctype_secondary == 'pgp-signature' ) {break;} //other attachments save to FILESDIR $filename = $part->ctype_parameters['name']; $file = REALFILESDIR . $filename; $fp = fopen($file, 'w'); fwrite($fp, $part->body ); fclose($fp); @exec ('chmod 755 ' . $file); $meta_return .= '[url=' . URLFILESDIR . $filename . ']' . $part->ctype_parameters['name'] . '[/url]' . "\n"; break; } return $meta_return; } function ubb2html($text) { // Array of tags with opening and closing $tagArray['img'] = array('open'=>''); $tagArray['b'] = array('open'=>'','close'=>''); $tagArray['i'] = array('open'=>'','close'=>''); $tagArray['u'] = array('open'=>'','close'=>''); $tagArray['url'] = array('open'=>'\\1'); $tagArray['email'] = array('open'=>'\\1'); $tagArray['url=(.*)'] = array('open'=>'\\2'); $tagArray['email=(.*)'] = array('open'=>'\\2'); $tagArray['color=(.*)'] = array('open'=>'\\2'); $tagArray['size=(.*)'] = array('open'=>'\\2'); $tagArray['font=(.*)'] = array('open'=>'\\2'); // Array of tags with only one part $sTagArray['br'] = array('tag'=>'
'); $sTagArray['hr'] = array('tag'=>'
'); //$sTagArray['hr'] = array('tag'=>''); foreach($tagArray as $tagName=>$replace) { $tagEnd=preg_replace('/\W/Ui','',$tagName); $text = preg_replace("|\[$tagName\](.*)\[/$tagEnd\]|Ui","$replace[open]\\1$replace[close]",$text); } foreach($sTagArray as $tagName=>$replace) { $text= preg_replace("|\[$tagName\]|Ui","$replace[tag]",$text); } return $text; } // This function turns Enriched Text into something similar to html // Very basic at the moment, only supports some functionality and dumps the rest // FIXME: fix colours: FFFF,C2FE,0374some text function etf2html ( $content ) { $search = array( '//', '/<\/bold>/', '//', '/<\/underline>/', '//', '/<\/italic>/', '/.*<\/param>/', '/<\/fontfamily>/', '//', '/<\/x-tad-bigger>/', '//', '/', '//', '/<\/color>/', '/.+<\/param>/' ); $replace = array ( '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ); // strip extra line breaks $content = preg_replace($search,$replace,$content); return trim($content); } // This function cleans up HTML in the e-mail function html2html ( $content ) { $search = array( '//', '/<\/html>/', '//', '/<\/title>/', '/<body.*>/', '/<\/body>/', '/<head>/', '/<\/head>/', '/<meta content=.*>/', '/<!DOCTYPE.*>/', '/<img src=".*>/' // '/<img src="cid:(.*)" .*>/' ); $replace = array ( '', '', '', '', '', '', '', '', '', '', '', '' ); // strip extra line breaks $content = preg_replace($search,$replace,trim($content)); return ($content); } // This function tries to create a valid postname function subject2postname ( $content ) { $content = strtolower($content); $search = array( ':-)', ':-(', ':-(', ':-p', ':-D', ':-()', ':)', ':(', ':(', ':p', ':D', ':()', '!', '?', ':', ';', ')', '(' ); $replace = ''; // strip all those forbidden strings $content = str_replace($search,$replace,$content); //The above operation could have led to whitespace at the beginning and end $content = trim($content); // Perhaps we also have doubled whitespaces now? $content = str_replace(' ','',$content); // Now the two most important rules: $content = str_replace(' ','-',$content); $content = str_replace('/','-',$content); return ($content); } // Keeps content until finds a line with '--' or '- --' // This effectively removes signatures function removesig ( $content ) { $arrcontent = explode("\n", $content); $append = TRUE; $i = 0; for ($i = 0; $i<=count($arrcontent); $i++) { $line = $arrcontent[$i]; $nextline = $arrcontent[$i+1]; if ( preg_match('/^--$/',trim($line)) OR preg_match('/^- --$/',$line) ) { $append = FALSE; } if ( $append == TRUE ) { // if ($line != NULL && $nextline == NULL ) //{ $strcontent .= $line ."</br>\n";; //check if there are null lines \n //} elseif ($line == NULL && $nextline == NULL) { $strcontent .= '</br>'; //}else { $strcontent .= $line ."\n"; } } return $strcontent; } //filter content for new lines function filternewlines ( $content ) { $search = array ( '/ (\n|\r\n|\r)/', '/(\n|\r\n|\r)/' ); $replace = array ( ' ', "\n" ); // strip extra line breaks $return = preg_replace($search,$replace,$content); return $return; } //strip pgp stuff function strip_pgp ( $content ) { $search = array ( '/-----BEGIN PGP SIGNED MESSAGE-----/', '/Hash: SHA1/' ); $replace = array ( ' ', '' ); // strip extra line breaks $return = preg_replace($search,$replace,$content); return $return; } // Added by Hey // This function converts to ISO-8859-1 if desired by the user. // Hint: It should only be applied to type="text/*" Other types are already converted // by the initial charsetcheck()-call and will be broken if converted twice. // Hint: Do conversion only if text was encoded in Base64 or Quoted-Printable. // -> Plain texts are coverted by the initial charsetcheck(). // This could be a bug for utf-8 users but it doesn't bother me because I want ISO anyway :-) function isocheck ( $part ) { $charset = $part->ctype_parameters['charset']; if( (ISO_8859_1 == true) && ($charset != 'iso-8859-1')) { $transenc = strtolower( $part->headers['content-transfer-encoding'] ); if( $transenc == 'base64' || $transenc == 'quoted-printable' ) { $part->body = utf8_decode($part->body); } } return $part; } // This function checks if the content is base64 function base64check ( $part ) { // if ($part->headers['content-transfer-encoding'] != 'base64') // { if ( strtolower($part->headers['content-transfer-encoding']) == 'base64' ) { $part->body = base64_decode($part->body); } // } return $part; } function quotedprintablecheck ( $part ) { // Added by HEY: Doing the same as Above for Quoted Pritable if ( strtolower($part->headers['content-transfer-encoding']) == 'quoted-printable' ) { $part->body = quoted_printable_decode($part->body); } return $part; } function charsetcheck ( $part ) { if ( strtolower($part->ctype_parameters['charset'] == 'utf-8') ) { $part->body = utf8_decode($part->body); } return $part; } function GenerateImageFileName($dir,$type) { static $ctr; $ctr++; return($dir . date("Ymd-His-",time()) . $ctr . "." . $type); } function ConfirmTrailingSlash($string) { if (substr($string,strlen($string) - 1,1) == "/") { return(true); } return(false); } function HasGDInstalled() { $function_list = array("getimagesize", "imagecreatefromjpeg", "imagecreatefromgif", "imagecreatefrompng", "imagecreatetruecolor", "imagecreatetruecolor", "imagecopyresized", "imagejpeg", "imagedestroy"); foreach ($function_list as $function) { if (!function_exists($function)) { print("<p>Missing $function"); return(false); } } return(true); } function ResizeImage($file,$type) { $sizeInfo = getimagesize($file); $fileName = basename($file); //echo $fileName; $scaledFileName = ""; if ($sizeInfo[0] > MAX_IMAGE_WIDTH) { $sourceImage = NULL; switch($type) { case "jpeg": $sourceImage = imagecreatefromjpeg($file); break; case "gif": $sourceImage = imagecreatefromgif($file); break; case "png": $sourceImage = imagecreatefrompng($file); break; } if ($sourceImage) { $scale = MAX_IMAGE_WIDTH/$sizeInfo[0]; $scaledH = round($sizeInfo[1] * $scale ); $scaledW = round($sizeInfo[0] * $scale ); $scaledFileName = "thumb.".$fileName; $scaledFile = REALPHOTOSDIR . $scaledFileName; $scaledImage = imagecreatetruecolor($scaledW,$scaledH); imagecopyresized($scaledImage,$sourceImage,0,0,0,0, $scaledW,$scaledH, $sizeInfo[0],$sizeInfo[1]); imagejpeg($scaledImage,$scaledFile,JPEGQUALITY); @exec ('chmod 755 ' . $scaledFile); imagedestroy($scaledImage); imagedestroy($sourceImage); } } return(array($scaledFileName,$fileName)); } function DeterminePostDate($content,$ddate) { $delay = 0; if (eregi("delay:(.[^ ]*)",$content,$matches) && trim($matches[1])) { if (eregi("([0-9]+)d",$matches[1],$dayMatches)) { $days = $dayMatches[1]; } if (eregi("([0-9]+)h",$matches[1],$hourMatches)) { $hours = $hourMatches[1]; } if (eregi("([0-9]+)m",$matches[1],$minuteMatches)) { $minutes = $minuteMatches[1]; } $delay = (($days * 24 + $hours) * 60 + $minutes) * 60; $content = ereg_replace("delay:$matches[1]","",$content); } if (function_exists("strtotime")) { $post_date = date('Y-m-d H:i:s', strtotime($ddate) + (TIME_OFFSET * 3600) + $delay); $post_date_gmt = gmdate('Y-m-d H:i:s', strtotime($ddate) + $delay ); } else { $post_date = date('Y-m-d H:i:s', time() + (TIME_OFFSET * 3600) + $delay); $post_date_gmt = gmdate('Y-m-d H:i:s', time() + $delay ); } return(array($post_date,$post_date_gmt)); } // end of script ?>