├── README.md ├── application.php ├── class.php ├── config.php ├── mimeDecode.php └── mysql-structure.sql /README.md: -------------------------------------------------------------------------------- 1 | # PHP IMAP Fetcher 2 | PHP IMAP Fetcher is an open source PHP script that can fetch or pipe emails from a POP box, save the message to MySQL (both text-plain and text-html), and save attachments/images locally. 3 | 4 | # Compatibility 5 | The script has been tested on emails sent from Gmail, Outlook, Yahoo, Apple Mail, Mozilla Thunderbird, and others. 6 | 7 | # Installing 8 | Navigate to the folder on your server where you want to run the script. 9 | 10 | 1. Upload the files: 11 | * application.php 12 | * class.php 13 | * config.php 14 | * mimeDecode.php 15 | 16 | 2. Create a folder called "files" and make sure it's writable by the web server. 17 | 18 | 3. In your MySQL database, create the tables "emails" and "files" by importing the file: 19 | * mysql-structure.sql 20 | 21 | 4. Open the file config.php and add your MySQL and POP credentials. 22 | 23 | # Pipe vs Fetch 24 | You can either pipe an email address to the script to process each email as it arrives, or you can fetch emails one-by-one from a mailbox using a cron job (emails are deleted as they're processed.) We recommend using the fetch method. 25 | 26 | The script is set to "fetch" by default but you can change it to "pipe" in config.php 27 | 28 | # IMAP Flags 29 | There are flags set by default in config.php but you can change them by referencing: 30 | 31 | http://php.net/manual/en/function.imap-open.php 32 | 33 | # Running the script 34 | The file to either pipe to or run as a cron job is application.php 35 | 36 | # Referencing attachments/images 37 | When an email is processed, a unique ID will be generated for the MySQL record and if any attachments are present, a folder for that ID will be created in your "files" folder. Attachments will be saved there. 38 | 39 | Reference to images is only saved in the "text-html" part of the message, and not the "text-plain." 40 | 41 | Images are referenced like: 42 | 43 | [filePath]/dkvmbY14NZr4l4eb79Gs1513724817/mypicture.png 44 | 45 | When including the message in your own application you'll simply replace "[filePath]" with the relevant folder path, for example, if the path to your files is: 46 | 47 | [example.com]/application/files/dkvmbY14NZr4l4eb79Gs1513724817/mypicture.png 48 | 49 | then your code will be 50 | 51 | $str = str_replace("[filePath]","/application/files",$str); 52 | -------------------------------------------------------------------------------- /application.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php -q 2 | readEmail(); 28 | } 29 | 30 | /* Fetch */ 31 | if ($grab_type == "fetch") { 32 | 33 | $inbox = @imap_open("{".$imap_host.$imap_flags."}INBOX",$imap_user,$imap_pass); 34 | 35 | if ($inbox) { 36 | $emails = imap_search($inbox,"ALL"); 37 | 38 | if ($emails) { 39 | rsort($emails); 40 | 41 | if ($emails) { 42 | foreach($emails AS $n) { 43 | $source = imap_fetchbody($inbox, $n, ""); 44 | $uniqid = generateId(20).date("U"); 45 | $emailMessage = new EmailObject($mysql,$uniqid,$source,$file_store); 46 | $emailMessage->readEmail(); 47 | imap_delete($inbox, $n); 48 | } 49 | imap_expunge($inbox); 50 | } 51 | } 52 | /* imap_errors() is called to supress PHP errors, such as when a mailbox is empty */ 53 | $errors = imap_errors(); 54 | imap_close($inbox); 55 | } 56 | } 57 | 58 | function generateId($n) { 59 | 60 | mt_srand((double)microtime()*1000000); 61 | 62 | $id = ""; 63 | while(strlen($id)<$n){ 64 | switch(mt_rand(1,3)){ 65 | case 1: $id.=chr(mt_rand(48,57)); break; // 0-9 66 | case 2: $id.=chr(mt_rand(65,90)); break; // A-Z 67 | case 3: $id.=chr(mt_rand(97,122)); break; // a-z 68 | } 69 | } 70 | 71 | return $id; 72 | } 73 | 74 | mysql_close($mysql); 75 | -------------------------------------------------------------------------------- /class.php: -------------------------------------------------------------------------------- 1 | mysql = $mysql; 12 | $this->uniqid = $uniqid; 13 | $this->source = $source; 14 | $this->file_store = $file_store; 15 | } 16 | 17 | function readEmail(){ 18 | 19 | // Decode email message into parts 20 | $decoder = new Mail_mimeDecode($this->source); 21 | 22 | $this->decoded = $decoder->decode( 23 | Array( 24 | "decode_headers" => TRUE, 25 | "include_bodies" => TRUE, 26 | "decode_bodies" => TRUE, 27 | ) 28 | ); 29 | 30 | // Get from name and email 31 | $this->from = $this->decoded->headers["from"]; 32 | 33 | if (preg_match("/.* <.*@.*\..*>/i",$this->from,$matches)) { 34 | $this->name = preg_replace("/ <(.*)>$/", "", $this->from); 35 | $this->email = preg_replace("/.*<(.*)>.*/","$1",$this->from); 36 | } else { 37 | $this->email = $this->from; 38 | } 39 | 40 | // Get subject 41 | $this->subject = trim($this->decoded->headers["subject"]); 42 | 43 | // Get body & attachments (if available) 44 | if (is_array($this->decoded->parts)) { 45 | foreach($this->decoded->parts as $arItem => $body_part){ 46 | $this->decodePart($body_part); 47 | } 48 | } else { 49 | $this->bodyText = $this->decoded->body; 50 | } 51 | 52 | // Save Message to MySQL 53 | $this->saveToDb(); 54 | } 55 | 56 | // Decode body part 57 | private function decodePart($body_part){ 58 | 59 | // Get file and file name 60 | if (isset($body_part->d_parameters["filename"])) { 61 | 62 | // Set file name 63 | $filename = $body_part->d_parameters["filename"]; 64 | 65 | // Save the file 66 | $this->saveFile($filename,$body_part->body); 67 | 68 | // Get content ID for image (as some mailers use CID instead of file name) 69 | if (isset($body_part->headers["content-id"])) { 70 | $cid = $body_part->headers["content-id"]; 71 | $cid = str_replace("<","",$cid); 72 | $cid = str_replace(">","",$cid); 73 | 74 | // Replace the image src reference with the path to saved file in HTML 75 | $this->bodyHtml = preg_replace("/src=\"(cid:)?".$cid."\"/i", "src=\"[filePath]/".$this->uniqid."/".$filename."\"", $this->bodyHtml); 76 | 77 | // Replace the image CID reference in plain text 78 | $this->bodyText = preg_replace("/\[cid:".$cid."\]/i", "", $this->bodyText); 79 | } 80 | 81 | // Replace the image name reference in plain text 82 | $this->bodyText = preg_replace("/\[".$filename."\]/i", "", $this->bodyText); 83 | } 84 | 85 | $mimeType = "{$body_part->ctype_primary}/{$body_part->ctype_secondary}"; 86 | 87 | // Decode sub-parts 88 | if ($body_part->ctype_primary == "multipart") { 89 | if (is_array($body_part->parts)) { 90 | foreach($body_part->parts as $arItem => $sub_part) { 91 | $this->decodePart($sub_part); 92 | } 93 | } 94 | } 95 | 96 | // Get plain text version 97 | if ($mimeType == "text/plain") { 98 | if (!isset($body_part->disposition)) { 99 | $this->bodyText .= $body_part->body; 100 | } 101 | } 102 | 103 | // Get HTML version 104 | if ($mimeType == "text/html") { 105 | if (!isset($body_part->disposition)) { 106 | $this->bodyHtml .= $body_part->body; 107 | } 108 | } 109 | echo "
".$body_part->ctype_primary; 110 | if ($body_part->ctype_primary == "body") 111 | echo $body_part->body; 112 | } 113 | 114 | // Save file 115 | private function saveFile($filename,$contents) { 116 | 117 | // Check if uniqid folder exists 118 | if (!file_exists($this->file_store."/".$this->uniqid)) 119 | mkdir($this->file_store."/".$this->uniqid); 120 | 121 | // Save file 122 | file_put_contents($this->file_store."/".$this->uniqid."/".$filename, $contents); 123 | $this->saved_files[] = $filename; 124 | } 125 | 126 | // Save message & files to MySQL 127 | private function saveToDb() { 128 | 129 | $mysql = $this->mysql; 130 | $uniqid = $this->uniqid; 131 | 132 | if (isset($this->bodyText)) { 133 | $body_text = $this->bodyText; 134 | $body_text = mysql_real_escape_string(mb_convert_encoding(trim($body_text),'UTF-8','UTF-8'), $mysql); 135 | } else { 136 | $body_text = ""; 137 | } 138 | 139 | if (isset($this->bodyHtml)) { 140 | 141 | $body_html = $this->bodyHtml; 142 | 143 | // Strip header tag (some email clients) 144 | $body_html = preg_replace("/(\\r\\n)?/i","",$body_html); 145 | 146 | // Strip HTML tags (Yahoo, Mozilla) 147 | $body_html = preg_replace("/<\/?html(.*?)>(\\r\\n)?/i","",$body_html); 148 | $body_html = preg_replace("/<\/?head(.*?)>(\\r\\n)?/i","",$body_html); 149 | $body_html = preg_replace("/<\/?body(.*?)>(\\r\\n)?/i","",$body_html); 150 | $body_html = preg_replace("/(\\r\\n)?/i","",$body_html); 151 | $body_html = preg_replace("/