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);
 }
예제 #4
0
 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);
 }
예제 #6
0
 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();
 }
예제 #10
0
 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);
 }
예제 #12
0
 /**
  * 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;
 }