/** * Compute an HTTP GET method. * * @param Request $request HTTP request. * @param Response $response HTTP response. * @return bool * @throws Exception\Dav\Exception */ function httpGet(Request $request, Response $response) { if (System\Collection::NAME . '/' . Node::NAME !== $request->getPath()) { return; } $queries = $request->getQueryParameters(); if (isset($queries['test']) && 'mail' === $queries['test'] && isset($queries['payload'])) { $payload = @json_decode($queries['payload']); if (!$payload || !isset($payload->transport) || !isset($payload->username) || !isset($payload->password)) { throw new Exception\Dav\Exception('Payload is corrupted.'); } Mail\Message::setDefaultTransport(new Mail\Transport\Smtp(new Socket\Client('tcp://' . $payload->transport), $payload->username, $payload->password)); $message = new Mail\Message(); $message['from'] = 'sabre/katana <' . $payload->username . '>'; $message['to'] = $payload->username; $message['subject'] = 'Test mail from sabre/katana'; $message->addContent(new Mail\Content\Text('Hey!' . "\n\n" . 'If you receive this email, it means that your ' . 'sabre/katana server is able to send emails! ' . "\n\n" . 'Congratulations :-).')); $message->send(); $response->setHeader('Content-Type', 'application/json'); $response->setBody(json_encode(true)); return false; } $configuration = ['database' => ['dsn' => $this->configuration->database->dsn, 'username' => $this->configuration->database->username], 'mail' => ['address' => '', 'port' => '', 'username' => '', 'password' => '']]; if (isset($this->configuration->mail)) { $socket = new Socket('tcp://' . $this->configuration->mail->transport); $configuration['mail']['address'] = $socket->getAddress(); $configuration['mail']['port'] = $socket->hasPort() ? $socket->getPort() : 587; $configuration['mail']['username'] = $this->configuration->mail->username; $configuration['mail']['password'] = $this->configuration->mail->password; unset($socket); } $response->setHeader('Content-Type', 'application/json'); $response->setBody(json_encode($configuration)); return false; }
/** * Send the IMip message. * * @param ITip\Message $itip ITip message. * @return void */ function schedule(ITip\Message $itip) { // Not sending any emails if the system considers the update // insignificant. if (false === $itip->significantChange) { if (empty($itip->scheduleStatus)) { $itip->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email.'; } return; } $deliveredLocally = '1.2' === $itip->getScheduleStatus(); $senderName = $itip->senderName; $recipientName = $itip->recipientName; // 7 is the length of `mailto:`. $senderEmail = substr($itip->sender, 7); $recipientEmail = substr($itip->recipient, 7); $subject = 'sabre/katana iTIP message'; $summary = (string) $itip->message->VEVENT->SUMMARY; switch (strtoupper($itip->method)) { case 'REPLY': // In the case of a reply, we need to find the `PARTSTAT` from // the user. $partstat = (string) $itip->message->VEVENT->ATTENDEE['PARTSTAT']; switch (strtoupper($partstat)) { case 'DECLINED': $subject = $senderName . ' declined your invitation to "' . $summary . '"'; $action = 'DECLINED'; break; case 'ACCEPTED': $subject = $senderName . ' accepted your invitation to "' . $summary . '"'; $action = 'ACCEPTED'; break; case 'TENTATIVE': $subject = $senderName . ' tentatively accepted your invitation to "' . $summary . '"'; $action = 'TENTATIVE'; break; default: $itip->scheduleStatus = '5.0;Email not deliered. We didn\'t understand this PARTSTAT.'; return; } break; case 'REQUEST': $subject = $senderName . ' invited you to "' . $summary . '"'; $action = 'REQUEST'; break; case 'CANCEL': $subject = '"' . $summary . '" has been canceled.'; $action = 'CANCEL'; break; default: $itip->scheduleStatus = '5.0;Email not delivered. Unsupported METHOD.'; return; } $streamsToClose = []; $katanaLogo = new Mail\Content\Attachment($streamsToClose[] = new File\Read('katana://public/static/image/katana_logo_full.png'), 'Logo_of_sabre_katana.png'); $katanaLogoUrl = $katanaLogo->getIdUrl(); $dateTime = isset($itip->message->VEVENT->DTSTART) ? $itip->message->VEVENT->DTSTART->getDateTime() : new \DateTime('now'); $allDay = isset($itip->message->VEVENT->DTSTART) && false === $itip->message->VEVENT->DTSTART->hasTime(); $attendees = []; if (isset($itip->message->VEVENT->ATTENDEE)) { $_attendees =& $itip->message->VEVENT->ATTENDEE; for ($i = 0, $max = count($_attendees); $i < $max; ++$i) { $attendee = $_attendees[$i]; $attendees[] = ['cn' => isset($attendee['CN']) ? (string) $attendee['CN'] : (string) $attendee['EMAIL'], 'email' => isset($attendee['EMAIL']) ? (string) $attendee['EMAIL'] : null, 'role' => isset($attendee['ROLE']) ? (string) $attendee['ROLE'] : null]; } } usort($attendees, function ($a, $b) { if ('CHAIR' === $a['role']) { return -1; } return 1; }); $notEmpty = function ($property, $else) use($itip) { if (isset($itip->message->VEVENT->{$property})) { $handle = (string) $itip->message->VEVENT->{$property}; if (!empty($handle)) { return $handle; } } return $else; }; $url = $notEmpty('URL', false); $description = $notEmpty('DESCRIPTION', false); $location = $notEmpty('LOCATION', false); $locationImage = null; $locationImageUrl = false; $locationLink = false; if (isset($itip->message->VEVENT->{'X-APPLE-STRUCTURED-LOCATION'})) { $match = preg_match('/^(geo:)?(?<latitude>\\-?\\d+\\.\\d+),(?<longitude>\\-?\\d+\\.\\d+)$/', (string) $itip->message->VEVENT->{'X-APPLE-STRUCTURED-LOCATION'}, $coordinates); if (0 !== $match) { $zoom = 16; $width = 500; $height = 220; $locationImage = new Mail\Content\Attachment($streamsToClose[] = new File\Read('http://api.tiles.mapbox.com/v4' . '/mapbox.streets' . '/pin-m-star+285A98' . '(' . $coordinates['longitude'] . ',' . $coordinates['latitude'] . ')' . '/' . $coordinates['longitude'] . ',' . $coordinates['latitude'] . ',' . $zoom . '/' . $width . 'x' . $height . '.png' . '?access_token=pk.eyJ1IjoiZHRvYnNjaGFsbCIsImEiOiIzMzdhNTRhNGNjOGFjOGQ4MDM5ZTJhNGZjYjNmNmE5OCJ9.7ZQOdfvoZW0XIbvjN54Wrg'), 'Map.png', 'image/png'); $locationImageUrl = $locationImage->getIdUrl(); $locationLink = 'http://www.openstreetmap.org' . '/?mlat=' . $coordinates['latitude'] . '&mlon=' . $coordinates['longitude'] . '#map=' . $zoom . '/' . $coordinates['latitude'] . '/' . $coordinates['longitude']; } } $configuration = $this->getConfiguration()->mail; Mail\Message::setDefaultTransport(new Mail\Transport\Smtp(new Socket\Client('tcp://' . $configuration->transport), $configuration->username, $configuration->password)); $message = new Mail\Message(); $message['from'] = $senderName . ' (via sabre/katana) <' . $configuration->username . '>'; // reply-to $message['to'] = $recipientName . '<' . $recipientEmail . '>'; $message['subject'] = $subject; // return-path $textBody = function () use($senderName, $summary, $action, $dateTime, $allDay, $attendees, $location, $url, $description) { ob_start(); require 'katana://resource/view/caldav_scheduling.txt'; $out = ob_get_contents(); ob_end_clean(); return $out; }; $htmlBody = function () use($senderName, $summary, $action, $katanaLogoUrl, $dateTime, $allDay, $attendees, $location, $locationImageUrl, $locationLink, $url, $description) { ob_start(); require 'katana://resource/view/caldav_scheduling.html'; $out = ob_get_contents(); ob_end_clean(); return $out; }; $relatedContent = [new Mail\Content\Html($htmlBody()), $katanaLogo]; if (null !== $locationImage) { $relatedContent[] = $locationImage; } $message->addContent(new Mail\Content\Alternative([new Mail\Content\Text($textBody()), new Mail\Content\Related($relatedContent)])); if (false === $deliveredLocally) { $bodyAsStream = new Stringbuffer\Read(); $bodyAsStream->initializeWith($itip->message->serialize()); $attachment = new Mail\Content\Attachment($bodyAsStream, 'Event.ics', 'text/calendar; method=' . (string) $itip->method . '; charset=UTF-8'); $message->addContent($attachment); } $message->send(); foreach ($streamsToClose as $stream) { $stream->close(); } if (false === $deliveredLocally) { $itip->scheduleStatus = '1.1;Scheduling message is sent via iMip.'; } }
/** * Send a message. * * @param \Hoa\Mail\Message $message Message. * @return bool */ public function send(Mail\Message $message) { $content = $message->getFormattedContent(); $headers = $message->getHeaders(); $pos = strpos($content, CRLF . CRLF); $_headers = substr($content, 0, $pos); $_body = substr($content, $pos + 4); return mail($headers['to'], $headers['subject'], $_body, $_headers, $message->formatHeaders($this->getParameters())); }
/** * Send a message. * @TODO: Implement the DIGEST-MD5 and GSSAPI auth protocol. Implement SSLv1 * and v2 support. * * @param \Hoa\Mail\Message $message Message. * @return bool */ public function send(Mail\Message $message) { $content = $message->getFormattedContent(); $headers = $message->getHeaders(); $client = $this->getClient(); $client->connect(); $this->ifNot(220, 'Not able to connect to the server'); $domain = $message->getDomain($this->getUsername() ?: $headers['from']); $client->writeAll('EHLO ' . $domain . CRLF); $ehlo = preg_split('#' . CRLF . '250[\\-\\s]+#', $client->read(2048)); if (true === in_array('STARTTLS', $ehlo)) { $client->writeAll('STARTTLS' . CRLF); $this->ifNot(220, 'Cannot start a TLS connection'); if (true !== $client->enableEncryption(true, $client::ENCRYPTION_TLS)) { throw new Mail\Exception\Transport('Cannot enable a TLS connection.', 1); } $client->writeAll('EHLO ' . $domain . CRLF); $ehlo = preg_split('#' . CRLF . '250[\\-\\s]+#', $client->read(2048)); } $matches = null; foreach ($ehlo as $entry) { if (0 !== preg_match('#^AUTH (.+)$#', $entry, $matches)) { break; } } if (empty($matches)) { throw new Mail\Exception\Transport('The server does not support authentication, we cannot ' . 'authenticate.', 2); } $auth = explode(' ', $matches[1]); $username = $this->getUsername(); $password = $this->getPassword(); if (true === in_array('PLAIN', $auth)) { $client->writeAll('AUTH PLAIN' . CRLF); $this->ifNot(334, 'Authentication failed (PLAIN)'); $challenge = base64_encode("" . $username . "" . $password); $client->writeAll($challenge . CRLF); $this->ifNot(235, 'Wrong username or password'); } elseif (true === in_array('LOGIN', $auth)) { $client->writeAll('AUTH LOGIN' . CRLF); $this->ifNot(334, 'Authentication failed (LOGIN)'); $challenge = base64_encode($username); $client->writeAll($challenge . CRLF); $this->ifNot(334, 'Wrong username'); $challenge = base64_encode($password); $client->writeAll($challenge . CRLF); $this->ifNot(235, 'Wrong password'); } elseif (true === in_array('CRAM-MD5', $auth)) { $client->writeAll('AUTH CRAM-MD5' . CRLF); $line = $this->ifNot(334, 'Authentication failed (CRAM-MD5)'); $handle = base64_decode(substr($line, 4)); $challenge = base64_encode($username . ' ' . static::hmac($password, $handle)); $client->writeAll($challenge . CRLF); $this->ifNot(235, 'Wrong username or password'); } else { throw new Mail\Transport('%s does not support authentication algorithms available ' . 'on the server (%s).', 3, [__CLASS__, implode(', ', $auth)]); } $from = $message->getAddress($headers['from']); $client->writeAll('MAIL FROM: <' . $from . '>' . CRLF); $this->ifNot(250, 'Sender ' . $from . ' is wrong'); foreach ($message->getRecipients() as $recipient) { $client->writeAll('RCPT TO: <' . $recipient . '>' . CRLF); $this->ifNot(250, 'Recipient ' . $recipient . ' is wrong'); } $client->writeAll('DATA' . CRLF); $this->ifNot(354, 'Cannot send data'); $client->writeAll($content . CRLF . '.' . CRLF); $this->ifNot(250, 'Something went wrong with data'); $client->writeAll('QUIT' . CRLF); $this->ifNot(221, 'Cannot quit properly'); $client->disconnect(); return true; }
/** * Send a message. * Timeouts are defined as the RFC2821 Section 4.5.3.2 suggests. * * @TODO: Implement the DIGEST-MD5 and GSSAPI auth protocol. Implement SSLv1 * and v2 support. * * @param \Hoa\Mail\Message $message Message. * @return bool */ public function send(Mail\Message $message) { $content = $message->getFormattedContent(); $headers = $message->getHeaders(); $client = $this->getClient(); $client->connect(); $client->setStreamBlocking(true); $client->setStreamTimeout(5 * 60); $this->ifNot(220, 'Not able to connect to the server', 'The server timed out while trying to connect.'); $domain = $message->getDomain($this->getUsername() ?: $headers['from']); $client->writeAll('EHLO ' . $domain . CRLF); $ehlo = $this->ifNot(250, 'Not able to get supported extensions from the server.', 'The server timed out while answering to a `EHLO` command.'); if (true === in_array('STARTTLS', $ehlo)) { $client->writeAll('STARTTLS' . CRLF); $this->ifNot(220, 'Cannot start a TLS connection', 'The server timed out while starting a TLS encryption.'); if (true !== $client->enableEncryption(true, $client::ENCRYPTION_TLS)) { throw new Mail\Exception\Transport('Cannot enable a TLS connection.', 3); } $client->writeAll('EHLO ' . $domain . CRLF); $ehlo = $this->ifNot(250, 'Not able to get supported extensions from the server.', 'The server timed out while answering to a `EHLO` command ' . 'after having established a TLS connection.'); } $matches = null; foreach ($ehlo as $entry) { if (0 !== preg_match('#^AUTH (.+)$#', $entry, $matches)) { break; } } if (empty($matches)) { throw new Mail\Exception\Transport('The server does not support authentication, we cannot ' . 'authenticate.', 4); } $auth = explode(' ', $matches[1]); $username = $this->getUsername(); $password = $this->getPassword(); if (true === in_array('PLAIN', $auth)) { $client->writeAll('AUTH PLAIN' . CRLF); $this->ifNot(334, 'Authentication failed (PLAIN)', 'The server timed out while answering to an `AUTH PLAIN` ' . 'authentication.'); $challenge = base64_encode("" . $username . "" . $password); $client->writeAll($challenge . CRLF); $this->ifNot(235, 'Wrong username or password', 'The server timed out while asserting whether the password ' . 'is correct for an `AUTH PLAIN` authentication.'); } elseif (true === in_array('LOGIN', $auth)) { $client->writeAll('AUTH LOGIN' . CRLF); $this->ifNot(334, 'Authentication failed (LOGIN)', 'The server timed out while answering to an `AUTH LOGIN` ' . 'authentication.'); $challenge = base64_encode($username); $client->writeAll($challenge . CRLF); $this->ifNot(334, 'Wrong username', 'The server timed out while asserting whether the username ' . 'is correct for an `AUTH LOGIN` authentication.'); $challenge = base64_encode($password); $client->writeAll($challenge . CRLF); $this->ifNot(235, 'Wrong password', 'The server timed out while asserting whether the password ' . 'is correct for an `AUTH LOGIN` authentication.'); } elseif (true === in_array('CRAM-MD5', $auth)) { $client->writeAll('AUTH CRAM-MD5' . CRLF); $line = $this->ifNot(334, 'Authentication failed (CRAM-MD5)', 'The server timed out while answering to an `AUTH CRAM-MD5` ' . 'authentication.'); $handle = base64_decode(substr($line, 4)); $challenge = base64_encode($username . ' ' . static::hmac($password, $handle)); $client->writeAll($challenge . CRLF); $this->ifNot(235, 'Wrong username or password', 'The server timed out while asserting whether the username ' . 'and password are correct for an `AUTH CRAM-MD5` authentication.'); } else { throw new Mail\Transport('%s does not support authentication algorithms available ' . 'on the server (%s).', 5, [__CLASS__, implode(', ', $auth)]); } $client->setStreamTimeout(5 * 60); $from = $message->getAddress($headers['from']); $client->writeAll('MAIL FROM: <' . $from . '>' . CRLF); $this->ifNot(250, 'Sender ' . $from . ' is wrong', 'The server timed out while asserting whether the sender\'s ' . 'email is correct.'); $client->setStreamTimeout(5 * 60); foreach ($message->getRecipients() as $recipient) { $client->writeAll('RCPT TO: <' . $recipient . '>' . CRLF); $this->ifNot(250, 'Recipient ' . $recipient . ' is wrong', 'The server timed out while asserting whether the ' . 'recipient\'s emails are correct.'); } $client->setStreamTimeout(5 * 60); $client->writeAll('DATA' . CRLF); $this->ifNot(354, 'Cannot send data', 'The server timed out while answering to a `DATA` command.'); $client->setStreamTimeout(10 * 60); $client->writeAll($content . CRLF . '.' . CRLF); $this->ifNot(250, 'Something went wrong with data', 'The server timed out while asserting all the data have been ' . 'received correctly.'); $client->writeAll('QUIT' . CRLF); $this->ifNot(221, 'Cannot quit properly', 'The server timed out while trying to quit properly.'); $client->disconnect(); return true; }