function parseXover($subj, $from, $date, $messageid, $rsakeys) { $_CAT = 0; $_FSIZE = 1; // initialiseer wat variabelen $spot = array(); // Eerst splitsen we de header string op in enkel de category info e.d. $fromInfoPos = strpos($from, '<'); if ($fromInfoPos === false) { return false; } else { # Haal de postername en de <>'s weg $fromAddress = explode('@', substr($from, $fromInfoPos + 1, -1)); if (count($fromAddress) < 2) { return false; } # if $spot['header'] = $fromAddress[1]; } # if $spot['verified'] = false; $spot['filesize'] = 0; $spot['messageid'] = substr($messageid, 1, strlen($messageid) - 2); # als de spot in de toekomst ligt, dan corrigeren we dat naar nu if (time() < strtotime($date)) { $spot['stamp'] = time(); } else { $spot['stamp'] = strtotime($date); } # if $fields = explode('.', $spot['header']); if (count($fields) >= 6) { $spot['filesize'] = $fields[$_FSIZE]; $spot['category'] = substr($fields[$_CAT], 0, 1) - 1.0; // extract de posters name $spot['poster'] = substr($from, 0, $fromInfoPos - 1); // key id $spot['keyid'] = (int) substr($fields[$_CAT], 1, 1); if ($spot['keyid'] >= 0) { $expression = ''; $strInput = substr($fields[$_CAT], 2); $recentKey = $spot['keyid'] != 1; if ($recentKey) { if (strlen($strInput) == 0 || strlen($strInput) % 3 != 0) { return; } # if $subcatAr = $this->splitBySizEx($strInput, 3); foreach ($subcatAr as $str) { if (strlen($str) > 0) { $expression .= strtolower(substr($str, 0, 1)) . (int) substr($str, 1) . '|'; } # if } # foeeach } else { $list = array(); for ($i = 0; $i < strlen($strInput); $i++) { if ($strInput[$i] == 0 && !is_numeric($strInput[$i]) && strlen($expression) > 0) { $list[] = $expression; $expression = ''; } # if $expression .= $strInput[$i]; } # for $list[] = $expression; $expression = ''; foreach ($list as $str) { $expression .= strtolower(substr($str, 0, 1)) . substr($str, 1) . '|'; } # foreach } # else if $recentKey # Break up the subcategories per subcat-type if (strlen($expression) > 0) { $subcats = explode('|', $expression); $spot['subcata'] = ''; $spot['subcatb'] = ''; $spot['subcatc'] = ''; $spot['subcatd'] = ''; $spot['subcatz'] = ''; foreach ($subcats as $subcat) { if (in_array(strtolower(substr($subcat, 0, 1)), array('a', 'b', 'c', 'd', 'z')) !== false) { $spot['subcat' . strtolower(substr($subcat, 0, 1))] .= $subcat . '|'; } # if } # foreach # We vullen hier de z categorieen alvast op in het geval er geen Z category gegeven is if (empty($spot['subcatz'])) { $spot['subcatz'] = SpotCategories::createSubcatz($spot['category'], $spot['subcata'] . $spot['subcatb'] . $spot['subcatd']); } # if } # if if (strpos($subj, '=?') !== false && strpos($subj, '?=') !== false) { # Make sure its as simple as possible $subj = str_replace('?= =?', '?==?', $subj); $subj = str_replace('\\r', '', trim($this->oldEncodingParse($subj))); $subj = str_replace('\\n', '', $subj); } # if if ($recentKey) { $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) { $spot['title'] = trim($this->oldEncodingParse($spot['title'])); } # if } # if recentKey if (strlen($spot['title']) != 0 && strlen($spot['poster']) != 0) { # Als er een recentkey is (key <> 1), OF de spot is na 2010 geplaatst, dan moet # de spot gesigned zijn. $mustbeSigned = $recentKey | $spot['stamp'] > 1293870080; if ($mustbeSigned) { $spot['headersign'] = $fields[count($fields) - 1]; if (strlen($spot['headersign']) != 0) { $spot['wassigned'] = true; # KeyID 7 betekent dat alleen een hashcash vereist is if ($spot['keyid'] == 7) { $userSignedHash = sha1('<' . $spot['messageid'] . '>', false); $spot['verified'] = substr($userSignedHash, 0, 3) == '0000'; } else { # the signature this header is signed with $signature = $this->unspecialString($spot['headersign']); $spotSigning = new SpotSigning(); $spot['verified'] = $spotSigning->verifySpotHeader($spot, $signature, $rsakeys); } # else } # if } else { $spot['verified'] = true; $spot['wassigned'] = false; } # if doesnt need to be signed, pretend that it is } # if } # if } # if # Nu zetten we de titel en dergelijke om naar utf8, we kunnen # dat niet eerder doen omdat anders de RSA signature niet meer # klopt. if ($spot !== false && $spot['verified']) { $spot['title'] = utf8_encode($spot['title']); $spot['poster'] = utf8_encode($spot['poster']); $spot['tag'] = utf8_encode($spot['tag']); } # f return $spot; }
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; }
function parseXover($subj, $from, $date, $messageid, $rsaKeys) { // initialiseer wat variabelen $spot = array(); /* * De "From" header is als volgt opgebouwd: * * From: [Nickname] <[RANDOM or PUBLICKEY]@[CAT][KEY-ID][SUBCAT].[SIZE].[RANDOM].[DATE].[CUSTOM-ID].[CUSTOM-VALUE].[SIGNATURE]> * * We willen nu alles extracten wat achter de '@' staat, maar omdat een nickname theoretisch ook een @ kan bevatten * doen we eerst wat meer moeite */ $fromInfoPos = strpos($from, '<'); if ($fromInfoPos === false) { return false; } else { # Haal de postername en de <>'s weg $fromAddress = explode('@', substr($from, $fromInfoPos + 1, -1)); if (count($fromAddress) < 2) { return false; } # if $spot['selfsignedpubkey'] = $this->unSpecialString($fromAddress[0]); $spot['header'] = $fromAddress[1]; } # if /* * Initialiseer wat basis variabelen, doordat we verified op false zetten * zal de spot altijd nog genegeerd worden ook al geven we nu de spot array * terug */ $spot['verified'] = false; $spot['filesize'] = 0; $spot['messageid'] = substr($messageid, 1, strlen($messageid) - 2); $spot['stamp'] = strtotime($date); /* * Breek de .-delimited velden op in een array zodat we er makkelijker wat * mee kunnen doen. We hebben tenminste 6 velden nodig, anders is de spot * sowieso ongeldig. Meer velden kan (zie spec) */ $fields = explode('.', $spot['header']); if (count($fields) < 6) { return false; } # if /* * De velden die voor het oprapen liggen, halen we nu ook op */ $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'] = ''; $isRecentKey = $spot['keyid'] != 1; /* * Als er sowieso geen geldige keyid is, is de spot ook ongeldig */ if ($spot['keyid'] < 0) { return false; } # if /* * De lijst met subcategorieen is opgebouwd afhankelijk van hoe recent de spot is. * * FTD spots zetten alle subcategorieen gewoon achter elkaar, dus bv: a9b4c0d5d15d11 * nieuwere spots reserveren steeds 3 karakters voor elke categorie, dus: a09b04c00d05d15d11. * * Omdat beide feitelijk dezelfde karakteristieken hebben, parseren we die op dezelfde * manier. We voegen aan de $strCatLis een extra token toe zodat de laatste categorie ook geparsd * kan worden. We voegen drie karakters toe zodat een eventuele sanitycheck (strlen() % 3 = 0) nog * zou valideren. */ $strCatList = strtolower(substr($fields[0], 2)) . '!!!'; $strCatListLen = strlen($strCatList); /* * We initialiseren wat tijdelijke variables zodat we hier de sanity checking * kunnen doen */ $validSubcats = array('a' => true, 'b' => true, 'c' => true, 'd' => true, 'z' => true); $tmpCatBuild = ''; /* Probeer nu alle subcategorieen te extracten */ for ($i = 0; $i < $strCatListLen; $i++) { # Als het huidige karakter geen getal is, dan hebben we de volgende # categorie gevonden, voeg die toe aan de lijst met categorieen 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 # We vullen hier de z categorieen alvast op in het geval er geen Z category gegeven is if (empty($spot['subcatz'])) { $spot['subcatz'] = SpotCategories::createSubcatz($spot['category'], $spot['subcata'] . $spot['subcatb'] . $spot['subcatd']); } # if if (strpos($subj, '=?') !== false && strpos($subj, '?=') !== false) { # Make sure its as simple as possible $subj = str_replace('?= =?', '?==?', $subj); $subj = str_replace('\\r', '', trim($this->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) { $spot['title'] = trim($this->oldEncodingParse($spot['title'])); } # if } # if recentKey # Een title en poster zijn verplicht, anders kan de signature niet gechecked worden if (strlen($spot['title']) == 0 || strlen($spot['poster']) == 0) { return $spot; } # if # Als er een recentkey is (key <> 1), OF de spot is na 2010 geplaatst, dan moet # de spot gesigned zijn. $mustbeSigned = $isRecentKey | $spot['stamp'] > 1293870080; if ($mustbeSigned) { $spot['headersign'] = $fields[count($fields) - 1]; if (strlen($spot['headersign']) != 0) { $spot['wassigned'] = true; # KeyID 7 betekent dat een hashcash vereist is if ($spot['keyid'] == 7) { $userSignedHash = sha1('<' . $spot['messageid'] . '>', false); $spot['verified'] = substr($userSignedHash, 0, 3) == '0000'; /* * Create a fake RSA keyarray so we can validate it using our standard * infrastructure */ if ($spot['verified']) { /* Not sure about this $userRsaKey = array(7 => array('modulo' => $spot['selfsignedpubkey'], 'exponent' => 'AQAB')); $spot['verified'] = $this->_spotSigning->verifySpotHeader($spot, $signature, $userRsaKey); */ } # if } else { # the signature this header is signed with $signature = $this->unspecialString($spot['headersign']); $spot['verified'] = $this->_spotSigning->verifySpotHeader($spot, $signature, $rsaKeys); } # else } # if } else { $spot['verified'] = true; $spot['wassigned'] = false; } # if doesnt need to be signed, pretend that it is # Nu zetten we de titel en dergelijke om naar utf8, we kunnen # dat niet eerder doen omdat anders de RSA signature niet meer # klopt. if ($spot !== false && $spot['verified']) { $spot['title'] = utf8_encode($spot['title']); $spot['poster'] = utf8_encode($spot['poster']); $spot['tag'] = utf8_encode($spot['tag']); # als de spot in de toekomst ligt, dan corrigeren we dat naar nu if (time() < $spot['stamp']) { $spot['stamp'] = time(); } # if } # if return $spot; }