/lib/services/Nntp/Services_Nntp_SpotPosting.php
PHP | 270 lines | 129 code | 51 blank | 90 comment | 10 complexity | 518fa39ea9ccdbb612445c3bc1cfda19 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0, Apache-2.0, LGPL-3.0
- <?php
- class Services_Nntp_SpotPosting
- {
- private $_nntpEngine;
- private $_spotParseUtil;
- /*
- * constructor
- */
- public function __construct(Services_Nntp_Engine $nntpEngine)
- {
- $this->_spotParseUtil = new Services_Format_Util();
- $this->_nntpEngine = $nntpEngine;
- }
- // ctor
- /*
- * Post plain usenet message
- */
- private function postPlainMessage($newsgroup, $message, $additionalHeaders)
- {
- $header = 'Subject: '.utf8_decode($message['title'])."\r\n";
- $header .= 'Newsgroups: '.$newsgroup."\r\n";
- $header .= 'Message-ID: <'.$message['newmessageid'].">\r\n";
- $header .= 'X-Newsreader: SpotWeb v'.SPOTWEB_VERSION."\r\n";
- $header .= "X-No-Archive: yes\r\n";
- $header .= $additionalHeaders;
- return $this->_nntpEngine->post([$header, $message['body']]);
- }
- // postPlainMessage
- /*
- * Post a signed usenet message, we allow for additional headers
- * so this function can be used by anything
- */
- private function postSignedMessage($user, $serverPrivKey, $newsgroup, $message, $additionalHeaders)
- {
- // instantiate necessary objects
- $spotSigning = Services_Signing_Base::factory();
- // also by the SpotWeb server
- $server_signature = $spotSigning->signMessage($serverPrivKey, '<'.$message['newmessageid'].'>');
- $addHeaders = '';
- // Only add the user-signature header if there is none set yet
- if (stripos($additionalHeaders, 'X-User-Signature: ') === false) {
- // sign the messageid
- $user_signature = $spotSigning->signMessage($user['privatekey'], '<'.$message['newmessageid'].'>');
- $addHeaders .= 'X-User-Signature: '.$this->_spotParseUtil->spotPrepareBase64($user_signature['signature'])."\r\n";
- $addHeaders .= 'X-User-Key: '.$spotSigning->pubkeyToXml($user_signature['publickey'])."\r\n";
- } // if
- $addHeaders .= 'X-Server-Signature: '.$this->_spotParseUtil->spotPrepareBase64($server_signature['signature'])."\r\n";
- $addHeaders .= 'X-Server-Key: '.$spotSigning->pubkeyToXml($server_signature['publickey'])."\r\n";
- $addHeaders .= $additionalHeaders;
- return $this->postPlainMessage($newsgroup, $message, $addHeaders);
- }
- // postSignedMessage
- /*
- * Post a binary usenet message
- */
- public function postBinaryMessage($user, $newsgroup, $body, $additionalHeaders)
- {
- $chunkLen = (1024 * 1024);
- $segmentList = [];
- $spotSigning = Services_Signing_Base::factory();
- /*
- * Now start posting chunks of the binary files
- */
- while (strlen($body) > 0) {
- $message = [];
- /*
- * Cut of the first piece of the binary file, and remove it
- * from the source string
- */
- $chunk = substr($body, 0, $chunkLen - 1);
- $body = substr($body, $chunkLen - 1);
- /*
- * Split the body in parts of 900 characters
- */
- $message['body'] = $this->safe_chunk($this->_spotParseUtil->specialZipstr($chunk), 900);
- /*
- * Create an unique messageid and store it so we can return it
- * for the actual Spot creation
- */
- $message['newmessageid'] = $spotSigning->makeRandomStr(32).'@spot.net';
- $message['title'] = md5($message['body']);
- $addHeaders = 'From: '.$user['username'].' <'.trim($user['username']).'@spot.net>'."\r\n";
- $addHeaders .= 'Content-Type: text/plain; charset=ISO-8859-1'."\r\n";
- $addHeaders .= 'Content-Transfer-Encoding: 8bit'."\r\n";
- $addHeaders .= $additionalHeaders;
- /*
- * Actually post the image
- */
- $this->postPlainMessage($newsgroup, $message, $addHeaders);
- $segmentList[] = $message['newmessageid'];
- } // if
- return $segmentList;
- }
- // postBinaryMessage
- /*
- * Post a comment to a spot
- */
- public function postComment($user, $serverPrivKey, $newsgroup, $comment)
- {
- /*
- * Create the comment specific headers
- */
- $addHeaders = 'From: '.$user['username'].' <'.trim($user['username']).'@spot.net>'."\r\n";
- $addHeaders .= 'References: <'.$comment['inreplyto'].">\r\n";
- $addHeaders .= 'X-User-Rating: '.(int) $comment['rating']."\r\n";
- /*
- * And add the X-User-Avatar header if user has an avatar specified
- */
- if (!empty($user['avatar'])) {
- $tmpAvatar = explode("\r\n", $this->safe_chunk($user['avatar'], 900));
- foreach ($tmpAvatar as $avatarChunk) {
- if (strlen(trim($avatarChunk)) > 0) {
- $addHeaders .= 'X-User-Avatar: '.$avatarChunk."\r\n";
- } // if
- } // foreach
- } // if
- return $this->postSignedMessage($user, $serverPrivKey, $newsgroup, $comment, $addHeaders);
- }
- // postComment
- /*
- * Posts a spot file
- */
- public function postFullSpot($user, $serverPrivKey, $newsgroup, $spot)
- {
- // instantiate the necessary objects
- $spotSigning = Services_Signing_Base::factory();
- /*
- * Create the spotnet from header part accrdoing to the following structure:
- * From: [Nickname] <[PUBLICKEY-MODULO.USERSIGNATURE]@[CAT][KEY-ID][SUBCAT].[SIZE].[RANDOM].[DATE].[CUSTOM-ID].[CUSTOM-VALUE].[SIGNATURE]>
- */
- $spotHeader = ($spot['category'] + 1).$spot['key']; // Append the category and keyid
- // Process each subcategory and add them to the from header
- foreach ($spot['subcatlist'] as $subcat) {
- $spotHeader .= $subcat[0].str_pad(substr($subcat, 1), 2, '0', STR_PAD_LEFT);
- } // foreach
- $spotHeader .= '.'.$spot['filesize'];
- $spotHeader .= '.'. 10; // some kind of magic number?
- $spotHeader .= '.'.time();
- $spotHeader .= '.'.$spotSigning->makeRandomStr(4);
- $spotHeader .= '.'.$spotSigning->makeRandomStr(3);
- // If a tag is given, add it to the subject
- if (strlen(trim($spot['tag'])) > 0) {
- $spot['title'] = $spot['title'].' | '.$spot['tag'];
- } // if
- // Create the user-signature
- $user_signature = $spotSigning->signMessage($user['privatekey'], '<'.$spot['newmessageid'].'>');
- $header = 'X-User-Signature: '.$this->_spotParseUtil->spotPrepareBase64($user_signature['signature'])."\r\n";
- $header .= 'X-User-Key: '.$spotSigning->pubkeyToXml($user_signature['publickey'])."\r\n";
- // sign the header by using the users' key
- $header_signature = $spotSigning->signMessage($user['privatekey'], $spot['title'].$spotHeader.$spot['poster']);
- // sign the XML with the users' key
- $xml_signature = $spotSigning->signMessage($user['privatekey'], $spot['spotxml']);
- // Extract the users' publickey
- $userPubKey = $spotSigning->getPublicKey($user['privatekey']);
- // Create the From header
- $spotnetFrom = $user['username'].' <'.
- $this->_spotParseUtil->spotPrepareBase64($userPubKey['modulo']).
- '.'.
- $this->_spotParseUtil->spotPrepareBase64($user_signature['signature']).'@';
- $header = 'From: '.$spotnetFrom.$spotHeader.'.'.$this->_spotParseUtil->spotPrepareBase64($header_signature['signature']).">\r\n";
- // Add the Spotnet XML file, but split it in chunks of 900 characters
- $tmpXml = explode("\r\n", $this->safe_chunk($spot['spotxml'], 900));
- foreach ($tmpXml as $xmlChunk) {
- if (strlen(trim($xmlChunk)) > 0) {
- $header .= 'X-XML: '.$xmlChunk."\r\n";
- } // if
- } // foreach
- $header .= 'X-XML-Signature: '.$this->_spotParseUtil->spotPrepareBase64($xml_signature['signature'])."\r\n";
- // post the message
- return $this->postSignedMessage($user, $serverPrivKey, $newsgroup, $spot, $header);
- }
- // postFullSpot
- /*
- * Report a post as spam
- */
- public function reportSpotAsSpam($user, $serverPrivKey, $newsgroup, $report)
- {
- /*
- * Create the comment specific headers
- */
- $addHeaders = 'From: '.$user['username'].' <'.trim($user['username']).'@spot.net>'."\r\n";
- $addHeaders .= 'References: <'.$report['inreplyto'].">\r\n";
- return $this->postSignedMessage($user, $serverPrivKey, $newsgroup, $report, $addHeaders);
- }
- // reportSpotAsSpam
- /**
- * Function which mirrors chunk_split() of PHP, but tries to avoid
- * putting a whitespace character at the end of the line, because
- * some usenet servers discard that.
- *
- * @param $data
- * @param $maxLen
- * @param string $end
- *
- * @return string
- */
- private function safe_chunk($data, $maxLen, $end = "\r\n")
- {
- /*
- * We have to protect ourselves against having
- * only spaces in the stream, so we start with
- * the half of $maxLen, and work ourway up
- */
- $minLength = ceil($maxLen / 2);
- $totalChunk = '';
- while (strlen($data) > 0) {
- $sChunk = substr($data, 0, $minLength);
- $eChunk = substr($data, $minLength, $minLength);
- $eChunkLen = strlen($eChunk);
- while ((substr($eChunk, $eChunkLen - 1, 1) == ' ') && ($eChunkLen > 0)) {
- $eChunkLen--;
- } // while
- $totalChunk .= $sChunk.substr($eChunk, 0, $eChunkLen).$end;
- $data = substr($data, strlen($sChunk.substr($eChunk, 0, $eChunkLen)));
- } // while
- return $totalChunk;
- }
- // safe_chunk
- } // Services_Nntp_SpotPosting