| 23 | rodolico | 1 | <?php
 | 
        
           |  |  | 2 |   | 
        
           |  |  | 3 | /*
 | 
        
           |  |  | 4 |  * getBackupMail.php
 | 
        
           |  |  | 5 |  * 
 | 
        
           |  |  | 6 |  * Author: R. W. Rodolico
 | 
        
           |  |  | 7 |  * Copyright: 20160218, Daily Data, Inc.
 | 
        
           |  |  | 8 |  * 
 | 
        
           |  |  | 9 |  * This program is free software: you can redistribute it and/or modify
 | 
        
           |  |  | 10 |  * it under the terms of the GNU General Public License as published by
 | 
        
           |  |  | 11 |  * the Free Software Foundation, either version 3 of the License, or
 | 
        
           |  |  | 12 |  * (at your option) any later version.
 | 
        
           |  |  | 13 |  *
 | 
        
           |  |  | 14 |  * This program is distributed in the hope that it will be useful,
 | 
        
           |  |  | 15 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
        
           |  |  | 16 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
        
           |  |  | 17 |  * GNU General Public License for more details.
 | 
        
           |  |  | 18 |  *
 | 
        
           |  |  | 19 |  * You should have received a copy of the GNU General Public License
 | 
        
           |  |  | 20 |  * along with this program.  If not, see <http://www.gnu.org/licenses/>
 | 
        
           |  |  | 21 |  * 
 | 
        
           |  |  | 22 |  * Read messages from mail server. If they are YAML or XML sysinfo reports
 | 
        
           |  |  | 23 |  * save them to local drive, then remove them from mail server.
 | 
        
           |  |  | 24 |  * Files will be named with the following values, separated by underscores
 | 
        
           |  |  | 25 |  * report date (YYYY-MM-DD)
 | 
        
           |  |  | 26 |  * report time (HH:MM:SS) (CONTAINS COLONS)
 | 
        
           |  |  | 27 |  * client name (MAY CONTAIN SPACES)
 | 
        
           |  |  | 28 |  * device name
 | 
        
           |  |  | 29 |  * serial number (default to 00000 if it is not in the report)
 | 
        
           |  |  | 30 |  * 
 | 
        
           |  |  | 31 |  * Requires php5-imap
 | 
        
           |  |  | 32 |  * apt-get install php5-imap
 | 
        
           |  |  | 33 |  * 
 | 
        
           |  |  | 34 |  * Good information taken from
 | 
        
           |  |  | 35 |  * http://docstore.mik.ua/orelly/webprog/pcook/ch17_04.htm
 | 
        
           |  |  | 36 |  * 
 | 
        
           |  |  | 37 |  * V0.1 201602018 RWR
 | 
        
           |  |  | 38 |  * copied from getSysinfoMail.php
 | 
        
           |  |  | 39 |  * 
 | 
        
           |  |  | 40 |  * 
 | 
        
           |  |  | 41 |  * 
 | 
        
           |  |  | 42 |  */
 | 
        
           |  |  | 43 |   | 
        
           |  |  | 44 | $VERSION = '0.1';
 | 
        
           | 27 | rodolico | 45 | $configuration['maxemail'] = 10000; // maximum number of e-mails to download at one time
 | 
        
           | 24 | rodolico | 46 | $LOGGING = 1;
 | 
        
           | 23 | rodolico | 47 | $CLI = isset( $_SERVER["TERM"]); // only set if we are not in a cron job
 | 
        
           |  |  | 48 | // where to load the configuration file from
 | 
        
           |  |  | 49 | $confFileName = "rsbackupRead.conf.yaml";
 | 
        
           |  |  | 50 | $searchPaths = array( '/etc/camp', '/opt/camp', '/opt/camp/backups', $_SERVER['SCRIPT_FILENAME'], getcwd() );
 | 
        
           |  |  | 51 |   | 
        
           | 24 | rodolico | 52 |   | 
        
           | 23 | rodolico | 53 | /*
 | 
        
           |  |  | 54 |  * function loadConfig
 | 
        
           |  |  | 55 |  * Parameters $confFileName - the name of the configuration file
 | 
        
           |  |  | 56 |  *            $searchPaths - an array of paths to be searched
 | 
        
           |  |  | 57 |  * Returns    An array containing the configuration 
 | 
        
           |  |  | 58 |  * will search for a configuration file in $searchPaths for $confFileName
 | 
        
           |  |  | 59 |  * in order (so as to give priorities). The configuration file is
 | 
        
           |  |  | 60 |  * assumed to be a YAML file
 | 
        
           |  |  | 61 |  */
 | 
        
           |  |  | 62 |   | 
        
           |  |  | 63 | function loadConfig ( $confFileName, $searchPaths ) {
 | 
        
           |  |  | 64 |   | 
        
           |  |  | 65 |    /*
 | 
        
           |  |  | 66 |     * we don't know where the configuration file will be, so we have a list
 | 
        
           |  |  | 67 |     * below (searchPaths) to look for it. It will load the first one it 
 | 
        
           |  |  | 68 |     * finds, then exit. THUS, you could have multiple configuration files
 | 
        
           |  |  | 69 |     * but only the first one in the list will be used
 | 
        
           |  |  | 70 |     */
 | 
        
           |  |  | 71 |   | 
        
           |  |  | 72 |    $configuration = array();
 | 
        
           |  |  | 73 |   | 
        
           |  |  | 74 |   | 
        
           |  |  | 75 |    foreach ( $searchPaths as $path ) {
 | 
        
           |  |  | 76 |       if ( file_exists( "$path/$confFileName" ) ) {
 | 
        
           |  |  | 77 |          print "Found $path/$confFileName\n";
 | 
        
           |  |  | 78 |          $configuration = yaml_parse_file( "$path/$confFileName" );
 | 
        
           |  |  | 79 |          #require "$path/$confFileName";
 | 
        
           |  |  | 80 |          break; // exit out of the loop; we don't try to load it more than once
 | 
        
           |  |  | 81 |       } // if
 | 
        
           |  |  | 82 |    } // foreach
 | 
        
           |  |  | 83 |    return $configuration;
 | 
        
           |  |  | 84 | }
 | 
        
           |  |  | 85 |   | 
        
           |  |  | 86 | /* 
 | 
        
           |  |  | 87 |  * function    OpenMailbox
 | 
        
           |  |  | 88 |  * Parameters: $username
 | 
        
           |  |  | 89 |  *             $password
 | 
        
           |  |  | 90 |  *             $server
 | 
        
           |  |  | 91 |  *             $ssl=true
 | 
        
           |  |  | 92 |  *             $port=null
 | 
        
           |  |  | 93 |  *             $mailbox='INBOX'
 | 
        
           |  |  | 94 |  * 
 | 
        
           |  |  | 95 |  * Returns:    The IMAP instance handle
 | 
        
           |  |  | 96 |  *             The server string (used by some functions)
 | 
        
           |  |  | 97 |  */
 | 
        
           |  |  | 98 | function OpenMailbox ( $username, $password, $server, $ssl = true, $port=null, $mailbox = 'INBOX' ) {
 | 
        
           |  |  | 99 |    if ( $port == null )
 | 
        
           |  |  | 100 |       if ( $ssl ) $port = 993;  else $port = 143;
 | 
        
           |  |  | 101 |    $server = gethostbyname( $server );
 | 
        
           |  |  | 102 |    $serverString = "$server:$port/imap";
 | 
        
           |  |  | 103 |    if ( $ssl ) $serverString .= '/ssl';
 | 
        
           |  |  | 104 |    $serverString .= '/novalidate-cert';
 | 
        
           |  |  | 105 |    $serverString = '{' . $serverString . '}' . $mailbox;
 | 
        
           |  |  | 106 |    $server = imap_open( $serverString, $username, $password );
 | 
        
           |  |  | 107 |   | 
        
           |  |  | 108 |    return array($server, $serverString);
 | 
        
           |  |  | 109 | }
 | 
        
           |  |  | 110 |   | 
        
           |  |  | 111 |   | 
        
           |  |  | 112 | /*
 | 
        
           |  |  | 113 |  * function  getContents
 | 
        
           |  |  | 114 |  * Paramters $mail - handle to the connection
 | 
        
           |  |  | 115 |  *           $n    - the # of the message to get
 | 
        
           |  |  | 116 |  * Returns   An array containing all attachments
 | 
        
           |  |  | 117 |  * 
 | 
        
           |  |  | 118 |  */
 | 
        
           |  |  | 119 |   | 
        
           |  |  | 120 | function getContents( $mail, $n ) {
 | 
        
           |  |  | 121 |   | 
        
           |  |  | 122 |    $header = imap_header( $mail, $n );
 | 
        
           |  |  | 123 |    $structure = imap_fetchstructure( $mail, $n );
 | 
        
           |  |  | 124 |    $attachments = array();
 | 
        
           |  |  | 125 |    if(isset($structure->parts) && count($structure->parts)) {
 | 
        
           |  |  | 126 |   | 
        
           |  |  | 127 |       for($i = 0; $i < count($structure->parts); $i++) {
 | 
        
           |  |  | 128 |   | 
        
           |  |  | 129 |          $attachments[$i] = array(
 | 
        
           |  |  | 130 |             'is_attachment' => false,
 | 
        
           |  |  | 131 |             'filename' => '',
 | 
        
           |  |  | 132 |             'name' => '',
 | 
        
           |  |  | 133 |             'attachment' => ''
 | 
        
           |  |  | 134 |          );
 | 
        
           |  |  | 135 |   | 
        
           |  |  | 136 |          if($structure->parts[$i]->ifdparameters) {
 | 
        
           |  |  | 137 |             foreach($structure->parts[$i]->dparameters as $object) {
 | 
        
           |  |  | 138 |                if(strtolower($object->attribute) == 'filename') {
 | 
        
           |  |  | 139 |                   $attachments[$i]['is_attachment'] = true;
 | 
        
           |  |  | 140 |                   $attachments[$i]['filename'] = $object->value;
 | 
        
           |  |  | 141 |                }
 | 
        
           |  |  | 142 |             }
 | 
        
           |  |  | 143 |          }
 | 
        
           |  |  | 144 |   | 
        
           |  |  | 145 |          if($structure->parts[$i]->ifparameters) {
 | 
        
           |  |  | 146 |             foreach($structure->parts[$i]->parameters as $object) {
 | 
        
           |  |  | 147 |                if(strtolower($object->attribute) == 'name') {
 | 
        
           |  |  | 148 |                   $attachments[$i]['is_attachment'] = true;
 | 
        
           |  |  | 149 |                   $attachments[$i]['name'] = $object->value;
 | 
        
           |  |  | 150 |                }
 | 
        
           |  |  | 151 |             }
 | 
        
           |  |  | 152 |          }
 | 
        
           |  |  | 153 |   | 
        
           |  |  | 154 |          if($attachments[$i]['is_attachment']) {
 | 
        
           |  |  | 155 |             $attachments[$i]['attachment'] = imap_fetchbody($mail, $n, $i+1);
 | 
        
           |  |  | 156 |             if($structure->parts[$i]->encoding == 3) { // 3 = BASE64
 | 
        
           |  |  | 157 |                $attachments[$i]['attachment'] = base64_decode($attachments[$i]['attachment']);
 | 
        
           |  |  | 158 |             }
 | 
        
           |  |  | 159 |             elseif($structure->parts[$i]->encoding == 4) { // 4 = QUOTED-PRINTABLE
 | 
        
           |  |  | 160 |                $attachments[$i]['attachment'] = quoted_printable_decode($attachments[$i]['attachment']);
 | 
        
           |  |  | 161 |             }
 | 
        
           |  |  | 162 |          } else {
 | 
        
           |  |  | 163 |             $attachments[$i]['attachment'] = imap_fetchbody( $mail, $n, $i+1 );
 | 
        
           |  |  | 164 |          }
 | 
        
           |  |  | 165 |       }
 | 
        
           |  |  | 166 |    }
 | 
        
           |  |  | 167 |    return array( $header, $attachments );
 | 
        
           |  |  | 168 | }
 | 
        
           |  |  | 169 |   | 
        
           |  |  | 170 |   | 
        
           |  |  | 171 | function saveAttachments ( $attachments, $mailDate ) {
 | 
        
           |  |  | 172 |    $body = '';
 | 
        
           | 24 | rodolico | 173 |    $saved = false;
 | 
        
           | 23 | rodolico | 174 |    foreach ( $attachments as $thisOne ) {
 | 
        
           |  |  | 175 |       if ( $thisOne['is_attachment'] ) {
 | 
        
           |  |  | 176 |          saveFile( makeFileName( $thisOne['filename'] ), $thisOne['attachment'], $mailDate );
 | 
        
           | 24 | rodolico | 177 |          $saved = true;
 | 
        
           | 23 | rodolico | 178 |       } else {
 | 
        
           |  |  | 179 |          $body .= $thisOne['attachment'];
 | 
        
           |  |  | 180 |       }
 | 
        
           |  |  | 181 |    }
 | 
        
           | 24 | rodolico | 182 |    return array( $saved, $body );
 | 
        
           | 23 | rodolico | 183 | }
 | 
        
           |  |  | 184 |   | 
        
           |  |  | 185 | /* function makeFileName
 | 
        
           |  |  | 186 |  * Parameter $data
 | 
        
           |  |  | 187 |  *           $type - the type of the file
 | 
        
           |  |  | 188 |  * Returns   properly formatted file name for saving
 | 
        
           |  |  | 189 |  * 
 | 
        
           |  |  | 190 |  * The file name is created as a combination of the report date
 | 
        
           |  |  | 191 |  * client name, hostname and serial number, separated by the underscore
 | 
        
           |  |  | 192 |  * character. The type of the file is appended, and the configuration
 | 
        
           |  |  | 193 |  * value for outpath is prepended
 | 
        
           |  |  | 194 |  */
 | 
        
           |  |  | 195 | function makeFileName ( $body ) {
 | 
        
           |  |  | 196 |    global $configuration;
 | 
        
           |  |  | 197 |    $outpath = $configuration['outpath'];
 | 
        
           |  |  | 198 |    return $outpath . '/' . $body;
 | 
        
           |  |  | 199 | } 
 | 
        
           |  |  | 200 |   | 
        
           |  |  | 201 | /*
 | 
        
           |  |  | 202 |  * function saveFile
 | 
        
           |  |  | 203 |  * parameter $filename
 | 
        
           |  |  | 204 |  *           $contents
 | 
        
           |  |  | 205 |  *           $timestamp
 | 
        
           |  |  | 206 |  * returns   Number of bytes written
 | 
        
           |  |  | 207 |  * 
 | 
        
           |  |  | 208 |  * Saves $contents to file $filename, then sets the timestamp to
 | 
        
           |  |  | 209 |  * $timestamp. It is assumed $filename was created by makeFileName
 | 
        
           |  |  | 210 |  * and $contents is the content of a report. $timestamp is the actual
 | 
        
           |  |  | 211 |  * report timestamp
 | 
        
           |  |  | 212 |  */
 | 
        
           |  |  | 213 | function saveFile( $filename, $contents, $timestamp ) {
 | 
        
           |  |  | 214 |    $bytesWritten = file_put_contents( $filename, $contents );
 | 
        
           |  |  | 215 |    if ( $bytesWritten ) {
 | 
        
           |  |  | 216 |       if ( ! touch( $filename, $timestamp ) ) logError( "could not set timestamp of $filename to $timestamp" );
 | 
        
           |  |  | 217 |    }
 | 
        
           |  |  | 218 |    return $bytesWritten;
 | 
        
           |  |  | 219 | }
 | 
        
           |  |  | 220 |   | 
        
           |  |  | 221 | function logError ( $message ) {
 | 
        
           |  |  | 222 |    global $configuration;
 | 
        
           |  |  | 223 |    error_log( "$message\n", 3, $configuration['logFile'] );
 | 
        
           |  |  | 224 | }
 | 
        
           |  |  | 225 |   | 
        
           |  |  | 226 | /**************************************************************************************************************
 | 
        
           |  |  | 227 |  * Begin Main Program
 | 
        
           |  |  | 228 |  **************************************************************************************************************/
 | 
        
           |  |  | 229 |   | 
        
           |  |  | 230 | // get the configuration
 | 
        
           |  |  | 231 | $configuration = loadConfig( $confFileName, $searchPaths );
 | 
        
           |  |  | 232 | #print_r( $configuration ) ; die;
 | 
        
           |  |  | 233 |   | 
        
           |  |  | 234 | // ensure the path we want to write to exists
 | 
        
           |  |  | 235 | if ( ! is_dir( $configuration['outpath'] ) ) {
 | 
        
           |  |  | 236 |    if ( ! mkdir( $configuration['outpath'], 0777, true ) ) {
 | 
        
           |  |  | 237 |       die( "Could not create path " . $configuration['outpath'] . "\n" );
 | 
        
           |  |  | 238 |    }
 | 
        
           |  |  | 239 | }
 | 
        
           |  |  | 240 |   | 
        
           |  |  | 241 | $listMailboxes =  isset( $argv[1] ) ; // anything passed on cli results in a list of mailboxes instead of the job
 | 
        
           |  |  | 242 |   | 
        
           | 24 | rodolico | 243 |   | 
        
           | 23 | rodolico | 244 | foreach ( $configuration['servers'] as $thisServer ) {
 | 
        
           | 27 | rodolico | 245 |    if ($configuration['maxemail'] <= 0 ) break;
 | 
        
           | 23 | rodolico | 246 |    if ( ! $thisServer['enabled'] ) continue; // ignore anything that is not enabled
 | 
        
           |  |  | 247 |    print "Working on " . $thisServer['servername'] . "\n";
 | 
        
           |  |  | 248 |    $messagesToDelete = array(); // trap the UID's of messages to delete, more accurate than using message number
 | 
        
           |  |  | 249 |    print "Opening " . $thisServer['servername'] . "\n";
 | 
        
           |  |  | 250 |   | 
        
           |  |  | 251 |   | 
        
           |  |  | 252 |    // open the mail server connection. NOTE: the $connectString is used in other functions, so we need to preserve it
 | 
        
           |  |  | 253 |    list( $server, $connectString ) = OpenMailbox(  $thisServer['username'], 
 | 
        
           |  |  | 254 |                                                    $thisServer['password'], 
 | 
        
           |  |  | 255 |                                                    $thisServer['servername'] , 
 | 
        
           |  |  | 256 |                                                    $thisServer['ssl'],
 | 
        
           |  |  | 257 |                                                    $thisServer['port'], 
 | 
        
           |  |  | 258 |                                                    $listMailboxes ? '' : $thisServer['mailbox'] 
 | 
        
           |  |  | 259 |                                                 );
 | 
        
           |  |  | 260 |    print "Success with $connectString\n";
 | 
        
           |  |  | 261 |    if ( $listMailboxes ) {
 | 
        
           |  |  | 262 |       $list = imap_list($server, $connectString, "*");
 | 
        
           |  |  | 263 |       print_r( $list ); print "\n";
 | 
        
           |  |  | 264 |       continue;
 | 
        
           |  |  | 265 |    }
 | 
        
           |  |  | 266 |    $count = 0;
 | 
        
           | 24 | rodolico | 267 |    $lookedAt = 0;
 | 
        
           | 23 | rodolico | 268 |    if ( $server ) {
 | 
        
           |  |  | 269 |       $folderCount = imap_num_msg( $server );
 | 
        
           | 27 | rodolico | 270 |       for ( $num = 1; ($num <= $folderCount) && $count < $configuration['maxemail']; $num++ ) {
 | 
        
           | 23 | rodolico | 271 |          list( $header, $attachments ) = getContents( $server, $num );
 | 
        
           | 24 | rodolico | 272 |          $lookedAt++;
 | 
        
           | 23 | rodolico | 273 |          # $mailDate = getMailDate( $header );
 | 
        
           | 24 | rodolico | 274 |          list($saved,$body) = saveAttachments( $attachments, strtotime($header->date) );
 | 
        
           |  |  | 275 |          if ( $saved ) {
 | 
        
           |  |  | 276 |             $messagesToDelete[] = imap_uid( $server, $num );
 | 
        
           |  |  | 277 |             $count++;
 | 
        
           |  |  | 278 |          }
 | 
        
           | 23 | rodolico | 279 |          if ( $CLI ) print '.'; // only do this if interactive session
 | 
        
           |  |  | 280 |       } // for
 | 
        
           |  |  | 281 |       if ( $thisServer['deleteProcessed'] ) {
 | 
        
           |  |  | 282 |          foreach ( $messagesToDelete as $uid ) {
 | 
        
           |  |  | 283 |             imap_delete( $server, $uid, FT_UID ) or logError( "Can't delete [$uid]: imap_last_error()" );
 | 
        
           |  |  | 284 |          }
 | 
        
           |  |  | 285 |          imap_expunge( $server );
 | 
        
           |  |  | 286 |       }  // if delete
 | 
        
           |  |  | 287 |       imap_close( $server );
 | 
        
           |  |  | 288 |    } else {
 | 
        
           |  |  | 289 |       print "Could not open server " . $thisServer['servername'] . "\n";
 | 
        
           |  |  | 290 |    } // if..else
 | 
        
           | 24 | rodolico | 291 |    print "\nLooked at $lookedAt messages, Processed $count\n\n";
 | 
        
           | 23 | rodolico | 292 | } // outer for
 | 
        
           |  |  | 293 | ?>
 |