| 3 | rodolico | 1 | <?php
 | 
        
           |  |  | 2 |   | 
        
           |  |  | 3 | /*
 | 
        
           |  |  | 4 |  * getSysinfoMail.php
 | 
        
           |  |  | 5 |  * 
 | 
        
           |  |  | 6 |  * Author: R. W. Rodolico
 | 
        
           |  |  | 7 |  * Copyright: 20151002, 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 |  * 
 | 
        
           | 5 | rodolico | 37 |  * V1.1 20160203 RWR
 | 
        
           |  |  | 38 |  * Changed to use yaml for the configuration file. Had to take the
 | 
        
           |  |  | 39 |  * anonymous functions which parsed the different file types and convert
 | 
        
           |  |  | 40 |  * them to simpl eval() blocks.
 | 
        
           |  |  | 41 |  * 
 | 
        
           | 3 | rodolico | 42 |  */
 | 
        
           |  |  | 43 |   | 
        
           | 5 | rodolico | 44 | $VERSION = '1.1';
 | 
        
           | 3 | rodolico | 45 | $MAXDOWNLOAD = 5000; // maximum number of e-mails to download at one time
 | 
        
           |  |  | 46 | $CLI = isset( $_SERVER["TERM"]); // only set if we are not in a cron job
 | 
        
           | 6 | rodolico | 47 | // where to load the configuration file from
 | 
        
           |  |  | 48 | $confFileName = "sysinfoRead.conf.yaml";
 | 
        
           |  |  | 49 | $searchPaths = array( '/etc/camp', '/opt/camp', '/opt/camp/sysinfo', $_SERVER['SCRIPT_FILENAME'], getcwd() );
 | 
        
           | 3 | rodolico | 50 |   | 
        
           | 5 | rodolico | 51 | /*
 | 
        
           | 6 | rodolico | 52 |  * function loadConfig
 | 
        
           |  |  | 53 |  * Parameters $confFileName - the name of the configuration file
 | 
        
           |  |  | 54 |  *            $searchPaths - an array of paths to be searched
 | 
        
           |  |  | 55 |  * Returns    An array containing the configuration 
 | 
        
           |  |  | 56 |  * will search for a configuration file in $searchPaths for $confFileName
 | 
        
           |  |  | 57 |  * in order (so as to give priorities). The configuration file is
 | 
        
           |  |  | 58 |  * assumed to be a YAML file
 | 
        
           | 5 | rodolico | 59 |  */
 | 
        
           | 3 | rodolico | 60 |   | 
        
           | 6 | rodolico | 61 | function loadConfig ( $confFileName, $searchPaths ) {
 | 
        
           | 3 | rodolico | 62 |   | 
        
           | 6 | rodolico | 63 |    /*
 | 
        
           |  |  | 64 |     * we don't know where the configuration file will be, so we have a list
 | 
        
           |  |  | 65 |     * below (searchPaths) to look for it. It will load the first one it 
 | 
        
           |  |  | 66 |     * finds, then exit. THUS, you could have multiple configuration files
 | 
        
           |  |  | 67 |     * but only the first one in the list will be used
 | 
        
           |  |  | 68 |     */
 | 
        
           | 3 | rodolico | 69 |   | 
        
           | 6 | rodolico | 70 |    $configuration = array();
 | 
        
           | 5 | rodolico | 71 |   | 
        
           |  |  | 72 |   | 
        
           | 6 | rodolico | 73 |    foreach ( $searchPaths as $path ) {
 | 
        
           |  |  | 74 |       if ( file_exists( "$path/$confFileName" ) ) {
 | 
        
           |  |  | 75 |          print "Found $path/$confFileName\n";
 | 
        
           |  |  | 76 |          $configuration = yaml_parse_file( "$path/$confFileName" );
 | 
        
           |  |  | 77 |          #require "$path/$confFileName";
 | 
        
           |  |  | 78 |          break; // exit out of the loop; we don't try to load it more than once
 | 
        
           |  |  | 79 |       } // if
 | 
        
           |  |  | 80 |    } // foreach
 | 
        
           |  |  | 81 |    return $configuration;
 | 
        
           |  |  | 82 | }
 | 
        
           |  |  | 83 |   | 
        
           | 5 | rodolico | 84 | /* 
 | 
        
           |  |  | 85 |  * function    OpenMailbox
 | 
        
           |  |  | 86 |  * Parameters: $username
 | 
        
           |  |  | 87 |  *             $password
 | 
        
           |  |  | 88 |  *             $server
 | 
        
           |  |  | 89 |  *             $ssl=true
 | 
        
           |  |  | 90 |  *             $port=null
 | 
        
           |  |  | 91 |  *             $mailbox='INBOX'
 | 
        
           |  |  | 92 |  * 
 | 
        
           |  |  | 93 |  * Returns:    The IMAP instance handle
 | 
        
           |  |  | 94 |  *             The server string (used by some functions)
 | 
        
           |  |  | 95 |  */
 | 
        
           | 3 | rodolico | 96 | function OpenMailbox ( $username, $password, $server, $ssl = true, $port=null, $mailbox = 'INBOX' ) {
 | 
        
           |  |  | 97 |    if ( $port == null )
 | 
        
           |  |  | 98 |       if ( $ssl ) $port = 993;  else $port = 143;
 | 
        
           |  |  | 99 |    $server = gethostbyname( $server );
 | 
        
           |  |  | 100 |    $serverString = "$server:$port/imap";
 | 
        
           |  |  | 101 |    if ( $ssl ) $serverString .= '/ssl';
 | 
        
           |  |  | 102 |    $serverString .= '/novalidate-cert';
 | 
        
           |  |  | 103 |    $serverString = '{' . $serverString . '}' . $mailbox;
 | 
        
           |  |  | 104 |    $server = imap_open( $serverString, $username, $password );
 | 
        
           |  |  | 105 |   | 
        
           |  |  | 106 |    return array($server, $serverString);
 | 
        
           |  |  | 107 | }
 | 
        
           |  |  | 108 |   | 
        
           | 5 | rodolico | 109 | /*
 | 
        
           |  |  | 110 |  * function  getContents
 | 
        
           |  |  | 111 |  * Paramters $mail - handle to the connection
 | 
        
           |  |  | 112 |  *           $n    - the # of the message to get
 | 
        
           |  |  | 113 |  * Returns   The type of report (yaml,xml,ini)
 | 
        
           |  |  | 114 |  *           The contents of the above
 | 
        
           |  |  | 115 |  * 
 | 
        
           |  |  | 116 |  * See getBody() below
 | 
        
           |  |  | 117 |  * 
 | 
        
           |  |  | 118 |  * At the very end, calls getBody, which evaluates the type of file
 | 
        
           |  |  | 119 |  * for a valid yaml/xml/ini file
 | 
        
           |  |  | 120 |  */
 | 
        
           | 3 | rodolico | 121 | function getContents ($mail, $n ) {
 | 
        
           |  |  | 122 |    $body = '';
 | 
        
           |  |  | 123 |    $st = imap_fetchstructure($mail, $n);
 | 
        
           |  |  | 124 |    if (!empty($st->parts)) {
 | 
        
           |  |  | 125 |        for ($i = 0, $j = count($st->parts); $i < $j; $i++) {
 | 
        
           |  |  | 126 |            $part = $st->parts[$i];
 | 
        
           |  |  | 127 |            if ($part->subtype == 'PLAIN') {
 | 
        
           |  |  | 128 |                 $body = imap_fetchbody($mail, $n, $i+1);
 | 
        
           |  |  | 129 |            }
 | 
        
           |  |  | 130 |         }
 | 
        
           |  |  | 131 |    } else {
 | 
        
           |  |  | 132 |        $body = imap_body($mail, $n);
 | 
        
           |  |  | 133 |    }
 | 
        
           |  |  | 134 |    return getBody( $body );
 | 
        
           |  |  | 135 | }
 | 
        
           |  |  | 136 |   | 
        
           | 5 | rodolico | 137 | /*
 | 
        
           |  |  | 138 |  * function  getBody
 | 
        
           |  |  | 139 |  * Parameter $body
 | 
        
           |  |  | 140 |  * Returns   type of report (yaml, ini, xml)
 | 
        
           |  |  | 141 |  *           contents of the report
 | 
        
           |  |  | 142 |  * Returns (false,'') if nothing found
 | 
        
           |  |  | 143 |  * 
 | 
        
           |  |  | 144 |  * Removes all cruft from the message, leaving only a yaml, xml
 | 
        
           |  |  | 145 |  * or ini. For example, anything before --- in a yaml file is remove
 | 
        
           |  |  | 146 |  * as is everything after ..., and in this case, the first return
 | 
        
           |  |  | 147 |  * would be the string 'yaml' and the second the actual yaml document
 | 
        
           |  |  | 148 |  */
 | 
        
           | 3 | rodolico | 149 | function getBody ( $body ) {
 | 
        
           | 5 | rodolico | 150 |    global $configuration;
 | 
        
           |  |  | 151 |    foreach ( $configuration['bodyContents'] as $key => $regexes ) {
 | 
        
           | 3 | rodolico | 152 |       $matches;
 | 
        
           |  |  | 153 |       $pattern = $regexes['startTag'] . '.*' . $regexes['endTag'];
 | 
        
           |  |  | 154 |       if ( preg_match( "/$pattern/ms", $body, $matches ) ) 
 | 
        
           |  |  | 155 |          return array( $key, $matches[0] );
 | 
        
           |  |  | 156 |    }
 | 
        
           |  |  | 157 |    return array( false, '' );
 | 
        
           |  |  | 158 | }
 | 
        
           |  |  | 159 |   | 
        
           |  |  | 160 |   | 
        
           | 5 | rodolico | 161 | /* function makeFileName
 | 
        
           |  |  | 162 |  * Parameter $data
 | 
        
           |  |  | 163 |  *           $type - the type of the file
 | 
        
           |  |  | 164 |  * Returns   properly formatted file name for saving
 | 
        
           |  |  | 165 |  * 
 | 
        
           |  |  | 166 |  * The file name is created as a combination of the report date
 | 
        
           |  |  | 167 |  * client name, hostname and serial number, separated by the underscore
 | 
        
           |  |  | 168 |  * character. The type of the file is appended, and the configuration
 | 
        
           |  |  | 169 |  * value for outpath is prepended
 | 
        
           |  |  | 170 |  */
 | 
        
           | 3 | rodolico | 171 | function makeFileName ( $data, $type ) {
 | 
        
           | 5 | rodolico | 172 |    global $configuration;
 | 
        
           |  |  | 173 |    $outpath = $configuration['outpath'];
 | 
        
           | 3 | rodolico | 174 |    // we will create filename as yyyy-mm-dd_HH:mm:ss_client_hostname_serial
 | 
        
           |  |  | 175 |    $date =  empty( $data['report']['date'] ) ? '' : strtotime( $data['report']['date'] ); // store in Unix timestamp
 | 
        
           |  |  | 176 |    $date = strftime( '%F_%T', $date ); // save a copy of the date in SQL format
 | 
        
           |  |  | 177 |   | 
        
           |  |  | 178 |    // add client name
 | 
        
           |  |  | 179 |    $client = empty( $data['report']['client'] ) ? '' : $data['report']['client'];
 | 
        
           |  |  | 180 |   | 
        
           |  |  | 181 |    // add hostname
 | 
        
           |  |  | 182 |    $hostname = empty ( $data['system']['hostname'] ) ? '' : $data['system']['hostname'];
 | 
        
           |  |  | 183 |   | 
        
           |  |  | 184 |    // add serial number if it exists, otherwise use '00000' (5 0's)
 | 
        
           |  |  | 185 |    $serial = empty ( $data['system']['serial'] ) ? '00000' : $data['system']['serial'];
 | 
        
           |  |  | 186 |    return ( $date && $client && $hostname && $serial ) ? "$outpath/$date" . '_' . $client . '_' . $hostname . '_' . "$serial.$type" : false;
 | 
        
           |  |  | 187 |   | 
        
           |  |  | 188 | } 
 | 
        
           |  |  | 189 |   | 
        
           | 5 | rodolico | 190 | /*
 | 
        
           |  |  | 191 |  * function saveFile
 | 
        
           |  |  | 192 |  * parameter $filename
 | 
        
           |  |  | 193 |  *           $contents
 | 
        
           |  |  | 194 |  *           $timestamp
 | 
        
           |  |  | 195 |  * returns   Number of bytes written
 | 
        
           |  |  | 196 |  * 
 | 
        
           |  |  | 197 |  * Saves $contents to file $filename, then sets the timestamp to
 | 
        
           |  |  | 198 |  * $timestamp. It is assumed $filename was created by makeFileName
 | 
        
           |  |  | 199 |  * and $contents is the content of a report. $timestamp is the actual
 | 
        
           |  |  | 200 |  * report timestamp
 | 
        
           |  |  | 201 |  */
 | 
        
           | 3 | rodolico | 202 | function saveFile( $filename, $contents, $timestamp ) {
 | 
        
           |  |  | 203 |    $bytesWritten = file_put_contents( $filename, $contents );
 | 
        
           |  |  | 204 |    if ( $bytesWritten ) {
 | 
        
           |  |  | 205 |       if ( ! touch( $filename, $timestamp ) ) logError( "could not set timestamp of $filename to $timestamp" );
 | 
        
           |  |  | 206 |    }
 | 
        
           |  |  | 207 |    return $bytesWritten;
 | 
        
           |  |  | 208 | }
 | 
        
           |  |  | 209 |   | 
        
           |  |  | 210 | function logError ( $message ) {
 | 
        
           | 5 | rodolico | 211 |    global $configuration;
 | 
        
           |  |  | 212 |    error_log( "$message\n", 3, $configuration['logFile'] );
 | 
        
           | 3 | rodolico | 213 | }
 | 
        
           |  |  | 214 |   | 
        
           |  |  | 215 | /**************************************************************************************************************
 | 
        
           |  |  | 216 |  * Begin Main Program
 | 
        
           |  |  | 217 |  **************************************************************************************************************/
 | 
        
           |  |  | 218 |   | 
        
           | 6 | rodolico | 219 | // get the configuration
 | 
        
           |  |  | 220 | $configuration = loadConfig( $confFileName, $searchPaths );
 | 
        
           | 3 | rodolico | 221 |   | 
        
           |  |  | 222 | // ensure the path we want to write to exists
 | 
        
           | 5 | rodolico | 223 | if ( ! is_dir( $configuration['outpath'] ) ) {
 | 
        
           |  |  | 224 |    if ( ! mkdir( $configuration['outpath'], 0777, true ) ) {
 | 
        
           |  |  | 225 |       die( "Could not create path " . $configuration['outpath'] . "\n" );
 | 
        
           | 3 | rodolico | 226 |    }
 | 
        
           |  |  | 227 | }
 | 
        
           |  |  | 228 |   | 
        
           |  |  | 229 | $listMailboxes =  isset( $argv[1] ) ; // anything passed on cli results in a list of mailboxes instead of the job
 | 
        
           |  |  | 230 |   | 
        
           | 5 | rodolico | 231 | foreach ( $configuration['servers'] as $thisServer ) {
 | 
        
           | 3 | rodolico | 232 |    if ($MAXDOWNLOAD <= 0 ) break;
 | 
        
           |  |  | 233 |    if ( ! $thisServer['enabled'] ) continue; // ignore anything that is not enabled
 | 
        
           |  |  | 234 |    print "Working on " . $thisServer['servername'] . "\n";
 | 
        
           |  |  | 235 |    $messagesToDelete = array(); // trap the UID's of messages to delete, more accurate than using message number
 | 
        
           |  |  | 236 |    print "Opening " . $thisServer['servername'] . "\n";
 | 
        
           |  |  | 237 |   | 
        
           |  |  | 238 |    // open the mail server connection. NOTE: the $connectString is used in other functions, so we need to preserve it
 | 
        
           |  |  | 239 |    list( $server, $connectString ) = OpenMailbox(  $thisServer['username'], 
 | 
        
           |  |  | 240 |                                                    $thisServer['password'], 
 | 
        
           |  |  | 241 |                                                    $thisServer['servername'] , 
 | 
        
           |  |  | 242 |                                                    $thisServer['ssl'],
 | 
        
           |  |  | 243 |                                                    $thisServer['port'], 
 | 
        
           |  |  | 244 |                                                    $listMailboxes ? '' : $thisServer['mailbox'] 
 | 
        
           |  |  | 245 |                                                 );
 | 
        
           |  |  | 246 |    print "Success with $connectString\n";
 | 
        
           |  |  | 247 |    if ( $listMailboxes ) {
 | 
        
           |  |  | 248 |       $list = imap_list($server, $connectString, "*");
 | 
        
           |  |  | 249 |       print_r( $list ); print "\n";
 | 
        
           | 13 | rodolico | 250 |       continue;
 | 
        
           | 3 | rodolico | 251 |    }
 | 
        
           |  |  | 252 |    $count = 0;
 | 
        
           |  |  | 253 |    if ( $server ) {
 | 
        
           |  |  | 254 |       $folderCount = imap_num_msg( $server );
 | 
        
           |  |  | 255 |       for ( $num = 1; ($num <= $folderCount) && $MAXDOWNLOAD; $num++ ) {
 | 
        
           |  |  | 256 |          list( $type,$body ) = getContents( $server, $num );
 | 
        
           |  |  | 257 |          if ( ! $type ) {
 | 
        
           |  |  | 258 |             logError( "Unknown File Type" );
 | 
        
           |  |  | 259 |             continue;
 | 
        
           |  |  | 260 |          }
 | 
        
           | 5 | rodolico | 261 |          $data = eval( $configuration['bodyContents'][$type]['eval'] );
 | 
        
           |  |  | 262 |          if ( ! $data ) {
 | 
        
           | 3 | rodolico | 263 |             logError( "Unable to parse file" );
 | 
        
           |  |  | 264 |             continue;
 | 
        
           |  |  | 265 |          }
 | 
        
           |  |  | 266 |          if ( !(  $filename  = makeFileName( $data, $type ) ) ) {
 | 
        
           |  |  | 267 |             logError( "unable to create a file name" );
 | 
        
           |  |  | 268 |             continue;
 | 
        
           |  |  | 269 |          }
 | 
        
           |  |  | 270 |          if ( !(  $bytesWritten = saveFile( $filename, $body, strtotime( $data['report']['date'] ) ) ) ) {
 | 
        
           |  |  | 271 |             logError( "unable to save file $filename" );
 | 
        
           |  |  | 272 |             continue;
 | 
        
           |  |  | 273 |          }
 | 
        
           |  |  | 274 |          $messagesToDelete[] = imap_uid( $server, $num );
 | 
        
           |  |  | 275 |          $count++;
 | 
        
           |  |  | 276 |          if ( $CLI ) print '.'; // only do this if interactive session
 | 
        
           |  |  | 277 |          $MAXDOWNLOAD--;
 | 
        
           |  |  | 278 |       } // for
 | 
        
           |  |  | 279 |       if ( $thisServer['deleteProcessed'] ) {
 | 
        
           |  |  | 280 |          foreach ( $messagesToDelete as $uid ) {
 | 
        
           |  |  | 281 |             imap_delete( $server, $uid, FT_UID ) or logError( "Can't delete [$uid]: imap_last_error()" );
 | 
        
           |  |  | 282 |          }
 | 
        
           |  |  | 283 |          imap_expunge( $server );
 | 
        
           |  |  | 284 |       }  // if delete
 | 
        
           |  |  | 285 |       imap_close( $server );
 | 
        
           |  |  | 286 |    } else {
 | 
        
           |  |  | 287 |       print "Could not open server " . $thisServer['servername'] . "\n";
 | 
        
           |  |  | 288 |    } // if..else
 | 
        
           |  |  | 289 |    print "\nProcessed $count messages\n\n";
 | 
        
           |  |  | 290 | } // outer for
 | 
        
           |  |  | 291 | ?>
 |