public function testEmailParsing() { $email = new PhutilEmailAddress('Abraham Lincoln <*****@*****.**>'); $this->assertEqual('Abraham Lincoln', $email->getDisplayName()); $this->assertEqual('alincoln', $email->getLocalPart()); $this->assertEqual('logcabin.com', $email->getDomainName()); $this->assertEqual('*****@*****.**', $email->getAddress()); $email = new PhutilEmailAddress('*****@*****.**'); $this->assertEqual(null, $email->getDisplayName()); $this->assertEqual('alincoln', $email->getLocalPart()); $this->assertEqual('logcabin.com', $email->getDomainName()); $this->assertEqual('*****@*****.**', $email->getAddress()); $email = new PhutilEmailAddress('"Abraham" <*****@*****.**>'); $this->assertEqual('Abraham', $email->getDisplayName()); $this->assertEqual('alincoln', $email->getLocalPart()); $this->assertEqual('logcabin.com', $email->getDomainName()); $this->assertEqual('*****@*****.**', $email->getAddress()); $email = new PhutilEmailAddress(' alincoln@logcabin.com '); $this->assertEqual(null, $email->getDisplayName()); $this->assertEqual('alincoln', $email->getLocalPart()); $this->assertEqual('logcabin.com', $email->getDomainName()); $this->assertEqual('*****@*****.**', $email->getAddress()); $email = new PhutilEmailAddress('alincoln'); $this->assertEqual(null, $email->getDisplayName()); $this->assertEqual('alincoln', $email->getLocalPart()); $this->assertEqual(null, $email->getDomainName()); $this->assertEqual('alincoln', $email->getAddress()); $email = new PhutilEmailAddress('alincoln <alincoln at logcabin dot com>'); $this->assertEqual('alincoln', $email->getDisplayName()); $this->assertEqual('alincoln at logcabin dot com', $email->getLocalPart()); $this->assertEqual(null, $email->getDomainName()); $this->assertEqual('alincoln at logcabin dot com', $email->getAddress()); }
/** * Try to link a commit name to a Phabricator account. Basically we throw it * at the wall and see if something sticks. */ public function resolveUserPHID($user_name) { if (!strlen($user_name)) { return null; } $phid = $this->findUserByUserName($user_name); if ($phid) { return $phid; } $phid = $this->findUserByEmailAddress($user_name); if ($phid) { return $phid; } $phid = $this->findUserByRealName($user_name); if ($phid) { return $phid; } // No hits yet, try to parse it as an email address. $email = new PhutilEmailAddress($user_name); $phid = $this->findUserByEmailAddress($email->getAddress()); if ($phid) { return $phid; } $display_name = $email->getDisplayName(); if ($display_name) { $phid = $this->findUserByRealName($display_name); if ($phid) { return $phid; } } return null; }
private static function renderName($name) { $email = new PhutilEmailAddress($name); if ($email->getDisplayName() || $email->getDomainName()) { return phutil_render_tag('span', array('title' => $email->getAddress()), phutil_escape_html($email->getDisplayName())); } return phutil_escape_html($name); }
public static final function renderName($name) { $email = new PhutilEmailAddress($name); if ($email->getDisplayName() && $email->getDomainName()) { Javelin::initBehavior('phabricator-tooltips', array()); require_celerity_resource('aphront-tooltip-css'); return javelin_tag('span', array('sigil' => 'has-tooltip', 'meta' => array('tip' => $email->getAddress(), 'align' => 'E', 'size' => 'auto')), $email->getDisplayName()); } return hsprintf('%s', $name); }
/** * Identifies the sender's user account for a piece of received mail. Note * that this method does not validate that the sender is who they say they * are, just that they've presented some credential which corresponds to a * recognizable user. */ public function loadSender(PhabricatorMetaMTAReceivedMail $mail) { $raw_from = $mail->getHeader('From'); $from = self::getRawAddress($raw_from); $reasons = array(); // Try to find a user with this email address. $user = PhabricatorUser::loadOneWithEmailAddress($from); if ($user) { return $user; } else { $reasons[] = pht('This email was sent from "%s", but that address is not recognized by ' . 'Phabricator and does not correspond to any known user account.', $raw_from); } // If we missed on "From", try "Reply-To" if we're configured for it. $raw_reply_to = $mail->getHeader('Reply-To'); if (strlen($raw_reply_to)) { $reply_to_key = 'metamta.insecure-auth-with-reply-to'; $allow_reply_to = PhabricatorEnv::getEnvConfig($reply_to_key); if ($allow_reply_to) { $reply_to = self::getRawAddress($raw_reply_to); $user = PhabricatorUser::loadOneWithEmailAddress($reply_to); if ($user) { return $user; } else { $reasons[] = pht('Phabricator is configured to authenticate users using the ' . '"Reply-To" header, but the reply address ("%s") on this ' . 'message does not correspond to any known user account.', $raw_reply_to); } } else { $reasons[] = pht('(Phabricator is not configured to authenticate users using the ' . '"Reply-To" header, so it was ignored.)'); } } // If we don't know who this user is, load or create an external user // account for them if we're configured for it. $email_key = 'phabricator.allow-email-users'; $allow_email_users = PhabricatorEnv::getEnvConfig($email_key); if ($allow_email_users) { $from_obj = new PhutilEmailAddress($from); $xuser = id(new PhabricatorExternalAccountQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withAccountTypes(array('email'))->withAccountDomains(array($from_obj->getDomainName(), 'self'))->withAccountIDs(array($from_obj->getAddress()))->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->loadOneOrCreate(); return $xuser->getPhabricatorUser(); } else { $reasons[] = pht('Phabricator is also not configured to allow unknown external users ' . 'to send mail to the system using just an email address.'); $reasons[] = pht('To interact with Phabricator, add this address ("%s") to your ' . 'account.', $raw_from); } $reasons = implode("\n\n", $reasons); throw new PhabricatorMetaMTAReceivedMailProcessingException(MetaMTAReceivedMailStatus::STATUS_UNKNOWN_SENDER, $reasons); }
public function getFullAuthor() { $author_name = $this->getAuthorName(); if ($author_name === null) { return null; } $author_email = $this->getAuthorEmail(); if ($author_email === null) { return null; } $full_author = sprintf('%s <%s>', $author_name, $author_email); // Because git is very picky about the author being in a valid format, // verify that we can parse it. $address = new PhutilEmailAddress($full_author); if (!$address->getDisplayName() || !$address->getAddress()) { return null; } return $full_author; }
public function addRawTos(array $raw_email) { // Strip addresses down to bare emails, since the MailAdapter API currently // requires we pass it just the address (like `alincoln@logcabin.org`), not // a full string like `"Abraham Lincoln" <*****@*****.**>`. foreach ($raw_email as $key => $email) { $object = new PhutilEmailAddress($email); $raw_email[$key] = $object->getAddress(); } $this->setParam('raw-to', $raw_email); return $this; }
public function renderAuthorShortName($handles) { $author_phid = $this->getAuthorPHID(); if ($author_phid && isset($handles[$author_phid])) { return $handles[$author_phid]->getName(); } $data = $this->getCommitData(); $name = $data->getAuthorName(); $parsed = new PhutilEmailAddress($name); return nonempty($parsed->getDisplayName(), $parsed->getAddress()); }
private function sendVerifySignatureEmail(LegalpadDocument $doc, LegalpadDocumentSignature $signature) { $signature_data = $signature->getSignatureData(); $email = new PhutilEmailAddress($signature_data['email']); $doc_name = $doc->getTitle(); $doc_link = PhabricatorEnv::getProductionURI('/' . $doc->getMonogram()); $path = $this->getApplicationURI(sprintf('/verify/%s/', $signature->getSecretKey())); $link = PhabricatorEnv::getProductionURI($path); $name = idx($signature_data, 'name'); $body = pht("%s:\n\n" . "This email address was used to sign a Legalpad document " . "in Phabricator:\n\n" . " %s\n\n" . "Please verify you own this email address and accept the " . "agreement by clicking this link:\n\n" . " %s\n\n" . "Your signature is not valid until you complete this " . "verification step.\n\nYou can review the document here:\n\n" . " %s\n", $name, $doc_name, $link, $doc_link); id(new PhabricatorMetaMTAMail())->addRawTos(array($email->getAddress()))->setSubject(pht('[Legalpad] Signature Verification'))->setForceDelivery(true)->setBody($body)->setRelatedPHID($signature->getDocumentPHID())->saveAndSend(); }
public function run() { $source = $this->getSource(); switch ($source) { case self::SOURCE_LOCAL: $repository_api = $this->getRepositoryAPI(); $parser = new ArcanistDiffParser(); $parser->setRepositoryAPI($repository_api); if ($repository_api instanceof ArcanistGitAPI) { $this->parseBaseCommitArgument($this->getArgument('paths')); $diff = $repository_api->getFullGitDiff($repository_api->getBaseCommit(), $repository_api->getHeadCommit()); $changes = $parser->parseDiff($diff); $authors = $this->getConduit()->callMethodSynchronous('user.query', array('phids' => array($this->getUserPHID()))); $author_dict = reset($authors); list($email) = $repository_api->execxLocal('config user.email'); $author = sprintf('%s <%s>', $author_dict['realName'], $email); } else { if ($repository_api instanceof ArcanistMercurialAPI) { $this->parseBaseCommitArgument($this->getArgument('paths')); $diff = $repository_api->getFullMercurialDiff(); $changes = $parser->parseDiff($diff); $authors = $this->getConduit()->callMethodSynchronous('user.query', array('phids' => array($this->getUserPHID()))); list($author) = $repository_api->execxLocal('showconfig ui.username'); } else { // TODO: paths support $paths = $repository_api->getWorkingCopyStatus(); $changes = $parser->parseSubversionDiff($repository_api, $paths); $author = $this->getUserName(); } } $bundle = ArcanistBundle::newFromChanges($changes); $bundle->setProjectID($this->getWorkingCopy()->getProjectID()); $bundle->setBaseRevision($repository_api->getSourceControlBaseRevision()); // NOTE: we can't get a revision ID for SOURCE_LOCAL $parser = new PhutilEmailAddress($author); $bundle->setAuthorName($parser->getDisplayName()); $bundle->setAuthorEmail($parser->getAddress()); break; case self::SOURCE_REVISION: $bundle = $this->loadRevisionBundleFromConduit($this->getConduit(), $this->getSourceID()); break; case self::SOURCE_DIFF: $bundle = $this->loadDiffBundleFromConduit($this->getConduit(), $this->getSourceID()); break; } $try_encoding = nonempty($this->getArgument('encoding'), null); if (!$try_encoding) { try { $project_info = $this->getConduit()->callMethodSynchronous('arcanist.projectinfo', array('name' => $bundle->getProjectID())); $try_encoding = $project_info['encoding']; } catch (ConduitClientException $e) { $try_encoding = null; } } if ($try_encoding) { $bundle->setEncoding($try_encoding); } $format = $this->getFormat(); switch ($format) { case self::FORMAT_GIT: echo $bundle->toGitPatch(); break; case self::FORMAT_UNIFIED: echo $bundle->toUnifiedDiff(); break; case self::FORMAT_BUNDLE: $path = $this->getArgument('arcbundle'); echo "Writing bundle to '{$path}'...\n"; $bundle->writeToDisk($path); echo "done.\n"; break; } return 0; }
private function splitUserIdentifier($user) { $email = new PhutilEmailAddress($user); if ($email->getDisplayName() || $email->getDomainName()) { $user_name = $email->getDisplayName(); $user_email = $email->getAddress(); } else { $user_name = $email->getAddress(); $user_email = null; } return array($user_name, $user_email); }
/** * Parse the Mercurial author field. * * Not everyone enters their email address as a part of the username * field. Try to make it work when it's obvious. * * @param string $full_author * @return array */ protected function parseFullAuthor($full_author) { if (strpos($full_author, '@') === false) { $author = $full_author; $author_email = null; } else { $email = new PhutilEmailAddress($full_author); $author = $email->getDisplayName(); $author_email = $email->getAddress(); } return array($author, $author_email); }
public static function newActionListFromAddresses(PhabricatorUser $viewer, array $addresses) { $results = array(); foreach ($addresses as $address) { $result = new PhabricatorAuthInviteAction(); $result->rawInput = $address; $email = new PhutilEmailAddress($address); $result->emailAddress = phutil_utf8_strtolower($email->getAddress()); if (!preg_match('/^\\S+@\\S+\\.\\S+\\z/', $result->emailAddress)) { $result->issues[] = self::ISSUE_PARSE; } $results[] = $result; } // Identify duplicates. $address_groups = mgroup($results, 'getEmailAddress'); foreach ($address_groups as $address => $group) { if (count($group) > 1) { foreach ($group as $action) { $action->issues[] = self::ISSUE_DUPLICATE; } } } // Identify addresses which are already in the system. $addresses = mpull($results, 'getEmailAddress'); $email_objects = id(new PhabricatorUserEmail())->loadAllWhere('address IN (%Ls)', $addresses); $email_map = array(); foreach ($email_objects as $email_object) { $address_key = phutil_utf8_strtolower($email_object->getAddress()); $email_map[$address_key] = $email_object; } // Identify outstanding invites. $invites = id(new PhabricatorAuthInviteQuery())->setViewer($viewer)->withEmailAddresses($addresses)->execute(); $invite_map = mpull($invites, null, 'getEmailAddress'); foreach ($results as $action) { $email = idx($email_map, $action->getEmailAddress()); if ($email) { if ($email->getUserPHID()) { $action->userPHID = $email->getUserPHID(); if ($email->getIsVerified()) { $action->issues[] = self::ISSUE_VERIFIED; } else { $action->issues[] = self::ISSUE_UNVERIFIED; } } } $invite = idx($invite_map, $action->getEmailAddress()); if ($invite) { if ($invite->getAcceptedByPHID()) { $action->issues[] = self::ISSUE_ACCEPTED; if (!$action->userPHID) { // This could be different from the user who is currently attached // to the email address if the address was removed or added to a // different account later. Only show it if the address was // removed, since the current status is more up-to-date otherwise. $action->userPHID = $invite->getAcceptedByPHID(); } } else { $action->issues[] = self::ISSUE_INVITED; } } } foreach ($results as $result) { foreach ($result->getIssues() as $issue) { switch ($issue) { case self::ISSUE_PARSE: $result->action = self::ACTION_ERROR; break; case self::ISSUE_ACCEPTED: case self::ISSUE_VERIFIED: $result->action = self::ACTION_IGNORE; break; } } if (!$result->action) { $result->action = self::ACTION_SEND; } } return $results; }
public function getLocalCommitInformation() { if ($this->localCommitInfo === null) { $base_commit = $this->getBaseCommit(); list($info) = $this->execxLocal("log --template %s --rev %s --branch %s --", "{node}{rev}{author}" . "{date|rfc822date}{branch}{tag}{parents}{desc}", hgsprintf('(%s::. - %s)', $base_commit, $base_commit), $this->getBranchName()); $logs = array_filter(explode("", $info)); $last_node = null; $futures = array(); $commits = array(); foreach ($logs as $log) { list($node, $rev, $full_author, $date, $branch, $tag, $parents, $desc) = explode("", $log, 9); $email = new PhutilEmailAddress($full_author); $author = $email->getDisplayName(); $author_email = $email->getAddress(); // NOTE: If a commit has only one parent, {parents} returns empty. // If it has two parents, {parents} returns revs and short hashes, not // full hashes. Try to avoid making calls to "hg parents" because it's // relatively expensive. $commit_parents = null; if (!$parents) { if ($last_node) { $commit_parents = array($last_node); } } if (!$commit_parents) { // We didn't get a cheap hit on previous commit, so do the full-cost // "hg parents" call. We can run these in parallel, at least. $futures[$node] = $this->execFutureLocal("parents --template='{node}\\n' --rev %s", $node); } $commits[$node] = array('author' => $author, 'time' => strtotime($date), 'branch' => $branch, 'tag' => $tag, 'commit' => $node, 'rev' => $node, 'local' => $rev, 'parents' => $commit_parents, 'summary' => head(explode("\n", $desc)), 'message' => $desc, 'authorEmail' => $author_email); $last_node = $node; } foreach (Futures($futures)->limit(4) as $node => $future) { list($parents) = $future->resolvex(); $parents = array_filter(explode("\n", $parents)); $commits[$node]['parents'] = $parents; } // Put commits in newest-first order, to be consistent with Git and the // expected order of "hg log" and "git log" under normal circumstances. // The order of ancestors() is oldest-first. $commits = array_reverse($commits); $this->localCommitInfo = $commits; } return $this->localCommitInfo; }