function parseHeader($subj, $from, $date, $messageid, $rsaKeys)
 {
     # Initialize an empty array, we create a basic template in a few
     $spot = array();
     /*
      * The "From" header is created using the following system:
      *
      *   From: [Nickname] <[RANDOM or PUBLICKEY]@[CAT][KEY-ID][SUBCAT].[SIZE].[RANDOM].[DATE].[CUSTOM-ID].[CUSTOM-VALUE].[SIGNATURE]>
      *		or
      *   From: [Nickname] <[PUBLICKEY-MODULO.USERSIGNATURE]@[CAT][KEY-ID][SUBCAT].[SIZE].[RANDOM].[DATE].[CUSTOM-ID].[CUSTOM-VALUE].[SIGNATURE]>
      *
      *
      * First we want to extract everything after the @ but because a nickname could contain an @, we have to mangle it a bit
      */
     $fromInfoPos = strpos($from, '<');
     if ($fromInfoPos === false) {
         return false;
     } else {
         # Remove the posters' name and the <> characters
         $fromAddress = explode('@', substr($from, $fromInfoPos + 1, -1));
         if (count($fromAddress) < 2) {
             return false;
         }
         # if
         $spot['header'] = $fromAddress[1];
         /*
          * It is possible the part before the @ contains both the 
          * users' signature as the spots signature as signed by the user
          */
         $headerSignatureTemp = explode('.', $fromAddress[0]);
         $spot['selfsignedpubkey'] = $this->_util->spotUnprepareBase64($headerSignatureTemp[0]);
         if (isset($headerSignatureTemp[1])) {
             $spot['user-signature'] = $this->_util->spotUnprepareBase64($headerSignatureTemp[1]);
         }
         # if
     }
     # if
     /*
      * Initialize some basic variables. We set 'verified' to false so we  can
      * exit this function at any time and the gathered data for this spot up til
      * then is stil ignored.
      */
     $spot['verified'] = false;
     $spot['filesize'] = 0;
     $spot['messageid'] = $messageid;
     $spot['stamp'] = strtotime($date);
     /*
      * Split the .-delimited fields into an array so we can mangle it. We require
      * atleast six fields, if any less we can safely assume the spot is invalid
      */
     $fields = explode('.', $spot['header']);
     if (count($fields) < 6) {
         return false;
     }
     # if
     /*
      * Extract the fixed fields from the header
      */
     $spot['poster'] = substr($from, 0, $fromInfoPos - 1);
     $spot['category'] = substr($fields[0], 0, 1) - 1.0;
     $spot['keyid'] = (int) substr($fields[0], 1, 1);
     $spot['filesize'] = $fields[1];
     $spot['subcata'] = '';
     $spot['subcatb'] = '';
     $spot['subcatc'] = '';
     $spot['subcatd'] = '';
     $spot['subcatz'] = '';
     $spot['wassigned'] = false;
     $spot['spotterid'] = '';
     $isRecentKey = $spot['keyid'] != 1;
     /* 
      * If the keyid is invalid, abort trying to parse it
      */
     if ($spot['keyid'] < 0) {
         return false;
     }
     # if
     /*
      * Listings of subcategories is dependent on the age of the spot.
      *
      * FTD spots just list all subcategories like: a9b4c0d5d15d11
      * Newer spots always use three characters for each subcategory like: a09b04c00d05d15d11.
      *
      * We really do not care for this, we just parse them using the same code as the
      * first one.
      *
      * We pad $strCatList with an extra set of tokes so we always parse te last category,
      * we make sure any sanitycheck is passed by adding 3 tokens.
      */
     $strCatList = strtolower(substr($fields[0], 2)) . '!!!';
     $strCatListLen = strlen($strCatList);
     /*
      * Initialize some basic variables to use for sanitychecking (eg: valid subcats)
      */
     $validSubcats = array('a' => true, 'b' => true, 'c' => true, 'd' => true, 'z' => true);
     $tmpCatBuild = '';
     /* And just try to extract all given subcategories */
     for ($i = 0; $i < $strCatListLen; $i++) {
         /*
          * If the current character is not an number, we found the next
          * subcategory. Add the current one to the list, and start
          * parsing the new one
          */
         if (!is_numeric($strCatList[$i]) && !empty($tmpCatBuild)) {
             if (isset($validSubcats[$tmpCatBuild[0]])) {
                 $spot['subcat' . $tmpCatBuild[0]] .= $tmpCatBuild[0] . (int) substr($tmpCatBuild, 1) . '|';
             }
             # if
             $tmpCatBuild = '';
         }
         # if
         $tmpCatBuild .= $strCatList[$i];
     }
     # for
     /*
      * subcatz is a subcategory introduced in later Spotnet formats, we prefer to
      * always have this subcategory so we just fake it if it's not listed.
      */
     if (empty($spot['subcatz'])) {
         $spot['subcatz'] = SpotCategories::createSubcatz($spot['category'], $spot['subcata'] . $spot['subcatb'] . $spot['subcatd']);
     }
     # if
     # map deprecated genre categories to their new genre category
     $spot['subcatd'] = SpotCategories::mapDeprecatedGenreSubCategories($spot['category'], $spot['subcatd'], $spot['subcatz']);
     $spot['subcatc'] = SpotCategories::mapLanguageSubCategories($spot['category'], $spot['subcatc'], $spot['subcatz']);
     if (strpos($subj, '=?') !== false && strpos($subj, '?=') !== false) {
         # This is an old format to parse, instantiate the legacy parsing
         $legacyParser = new Services_Format_ParsingLegacy();
         # Make sure its as simple as possible
         $subj = str_replace('?= =?', '?==?', $subj);
         $subj = str_replace('\\r', '', trim($legacyParser->oldEncodingParse($subj)));
         $subj = str_replace('\\n', '', $subj);
     }
     # if
     if ($isRecentKey) {
         $tmp = explode('|', $subj);
         $spot['title'] = trim($tmp[0]);
         if (count($tmp) > 1) {
             $spot['tag'] = trim($tmp[1]);
         } else {
             $spot['tag'] = '';
         }
         # else
     } else {
         $tmp = explode('|', $subj);
         if (count($tmp) <= 1) {
             $tmp = array($subj);
         }
         # if
         $spot['tag'] = trim($tmp[count($tmp) - 1]);
         # remove the tags from the array
         array_pop($tmp);
         array_pop($tmp);
         $spot['title'] = trim(implode('|', $tmp));
         if (strpos($spot['title'], chr(0xc2)) !== false | strpos($spot['title'], chr(0xc3)) !== false) {
             # This is an old format to parse, instantiate the legacy parsing
             $legacyParser = new Services_Format_ParsingLegacy();
             $spot['title'] = trim($legacyParser->oldEncodingParse($spot['title']));
         }
         # if
     }
     # if recentKey
     # Title and poster fields are mandatory, we require it to validate the signature
     if (strlen($spot['title']) == 0 || strlen($spot['poster']) == 0) {
         return $spot;
     }
     # if
     /* 
      * For any recentkey ( >1) or spots created after year-2010, we require the spot
      * to be signed
      */
     $mustbeSigned = $isRecentKey | $spot['stamp'] > 1293870080;
     if ($mustbeSigned) {
         $spot['headersign'] = $fields[count($fields) - 1];
         $spot['wassigned'] = strlen($spot['headersign']) != 0;
     } else {
         $spot['verified'] = true;
         $spot['wassigned'] = false;
     }
     # if doesnt need to be signed, pretend that it is
     /*
      * Don't verify spots which are already verified
      */
     if ($spot['wassigned']) {
         /*
          * There are currently two known methods to which Spots are signed,
          * each having different charachteristics, making it a bit difficult
          * to work with this.
          *
          * The oldest method uses a secret private key and a signing server, we
          * name this method SPOTSIGN_V1. The users' public key is only available
          * in the XML header, not in the From header. This is the preferred method.
          *
          * The second method uses a so-called "self signed" spot (the spotter signs
          * the spots, posts the public key in the header and a hashcash is used to
          * prevent spamming). This method is called SPOTSIGN_V2.
          *
          */
         if ($spot['keyid'] == 7) {
             /*
              * KeyID 7 has a special meaning, it defines a self-signed spot and
              * requires a hashcash
              */
             $signingMethod = 2;
         } else {
             $signingMethod = 1;
         }
         # else
         switch ($signingMethod) {
             case 1:
                 # the signature this header is signed with
                 $signature = $this->_util->spotUnprepareBase64($spot['headersign']);
                 /*
                  * Make sure the key specified is an actual known key 
                  */
                 if (isset($rsaKeys[$spot['keyid']])) {
                     if ($spot['keyid'] == 2 && ($spot['filesize'] = 999 && strlen($spot['selfsignedpubkey']) > 50)) {
                         /* Check personal dispose message */
                         $signature = $this->_util->spotUnprepareBase64($spot['headersign']);
                         $userSignedHash = sha1('<' . $spot['messageid'] . '>', false);
                         $spot['verified'] = substr($userSignedHash, 0, 4) === '0000';
                         if ($spot['verified']) {
                             $userRsaKey = array(2 => array('modulo' => $spot['selfsignedpubkey'], 'exponent' => 'AQAB'));
                             if ($this->_spotSigning->verifySpotHeader($spot, $signature, $userRsaKey)) {
                                 $spot['spotterid'] = $this->_util->calculateSpotterId($spot['selfsignedpubkey']);
                             }
                             # if
                         }
                         # if
                     } else {
                         $spot['verified'] = $this->_spotSigning->verifySpotHeader($spot, $signature, $rsaKeys);
                     }
                 }
                 # if
                 break;
                 # SPOTSIGN_V1
             # SPOTSIGN_V1
             case 2:
                 # the signature this header is signed with
                 $signature = $this->_util->spotUnprepareBase64($spot['headersign']);
                 $userSignedHash = sha1('<' . $spot['messageid'] . '>', false);
                 $spot['verified'] = substr($userSignedHash, 0, 4) === '0000';
                 /*
                  * Create a fake RSA keyarray so we can validate it using our standard
                  * infrastructure
                  */
                 if ($spot['verified']) {
                     $userRsaKey = array(7 => array('modulo' => $spot['selfsignedpubkey'], 'exponent' => 'AQAB'));
                     /*
                      * We cannot use this as a full measure to check the spot's validness yet, 
                      * because at least one Spotnet client feeds us invalid data for now
                      */
                     if ($this->_spotSigning->verifySpotHeader($spot, $signature, $userRsaKey)) {
                         /*
                          * The users' public key (modulo) is posted in the header, lets
                          * try this.
                          */
                         $spot['spotterid'] = $this->_util->calculateSpotterId($spot['selfsignedpubkey']);
                     }
                     # if
                 }
                 # if
                 break;
                 # SPOTSIGN_V2
         }
         # switch
         /*
          * Even more recent spots, contain the users' full publickey
          * in the header. This allows us to uniquely identify and verify
          * the poster of the spot.
          *
          * Try to extract this information.
          */
         if ($spot['verified'] && !empty($spot['user-signature']) && !empty($spot['selfsignedpubkey'])) {
             /*
              * Extract the public key
              */
             $spot['spotterid'] = $this->_util->calculateSpotterId($spot['selfsignedpubkey']);
             $spot['user-key'] = array('modulo' => $spot['selfsignedpubkey'], 'exponent' => 'AQAB');
             /* 
              * The spot contains the signature in the header of the spot
              */
             $spot['verified'] = $this->_spotSigning->verifyFullSpot($spot);
         }
         # if
     }
     # if was signed
     /*
      * We convert the title and other fields to UTF8, we cannot
      * do this any earlier because it would break the RSA signature
      */
     if ($spot !== false && $spot['verified']) {
         $spot['title'] = utf8_encode($spot['title']);
         $spot['poster'] = utf8_encode($spot['poster']);
         $spot['tag'] = utf8_encode($spot['tag']);
         # If a spot is in the future, fix it
         if (time() < $spot['stamp']) {
             $spot['stamp'] = time();
         }
         # if
     }
     # if
     return $spot;
 }