2010-07-02 01:48:07 +02:00
< ? php
2010-12-15 01:34:49 +01:00
/**
*
* Module : dfrn_request
*
* Purpose : Handles communication associated with the issuance of
* friend requests .
*
*/
2014-09-06 17:28:46 +02:00
require_once ( 'include/enotify.php' );
2016-01-03 19:15:13 +01:00
require_once ( 'include/Scrape.php' );
2014-09-06 17:28:46 +02:00
2010-07-02 01:48:07 +02:00
if ( ! function_exists ( 'dfrn_request_init' )) {
function dfrn_request_init ( & $a ) {
if ( $a -> argc > 1 )
$which = $a -> argv [ 1 ];
2010-12-12 23:33:04 +01:00
profile_load ( $a , $which );
2010-07-02 01:48:07 +02:00
return ;
}}
2010-12-15 01:34:49 +01:00
/**
* Function : dfrn_request_post
*
* Purpose :
* Handles multiple scenarios .
*
* Scenario 1 :
* Clicking 'submit' on a friend request page .
*
* Scenario 2 :
* Following Scenario 1 , we are brought back to our home site
* in order to link our friend request with our own server cell .
* After logging in , we click 'submit' to approve the linkage .
*
*/
2010-07-02 01:48:07 +02:00
if ( ! function_exists ( 'dfrn_request_post' )) {
function dfrn_request_post ( & $a ) {
2016-03-01 14:42:55 +01:00
if (( $a -> argc != 2 ) || ( ! count ( $a -> profile ))) {
logger ( 'Wrong count of argc or profiles: argc=' . $a -> argc . ',profile()=' . count ( $a -> profile ));
2010-07-02 01:48:07 +02:00
return ;
2016-03-01 14:42:55 +01:00
}
2010-07-02 01:48:07 +02:00
2012-02-28 14:42:12 +01:00
if ( x ( $_POST , 'cancel' )) {
2011-08-02 06:02:25 +02:00
goaway ( z_root ());
2014-09-06 17:28:46 +02:00
}
2010-07-02 01:48:07 +02:00
2010-07-06 06:39:55 +02:00
2010-12-15 01:34:49 +01:00
/**
*
* Scenario 2 : We ' ve introduced ourself to another cell , then have been returned to our own cell
2014-09-06 17:28:46 +02:00
* to confirm the request , and then we ' ve clicked submit ( perhaps after logging in ) .
2010-12-15 01:34:49 +01:00
* That brings us here :
*
*/
2010-07-02 01:48:07 +02:00
2010-07-22 11:13:39 +02:00
if (( x ( $_POST , 'localconfirm' )) && ( $_POST [ 'localconfirm' ] == 1 )) {
2010-07-06 06:39:55 +02:00
2010-12-15 01:34:49 +01:00
/**
* Ensure this is a valid request
*/
2010-07-22 11:13:39 +02:00
if ( local_user () && ( $a -> user [ 'nickname' ] == $a -> argv [ 1 ]) && ( x ( $_POST , 'dfrn_url' ))) {
2010-07-06 06:39:55 +02:00
2010-07-02 01:48:07 +02:00
2010-12-15 01:34:49 +01:00
$dfrn_url = notags ( trim ( $_POST [ 'dfrn_url' ]));
$aes_allow = ((( x ( $_POST , 'aes_allow' )) && ( $_POST [ 'aes_allow' ] == 1 )) ? 1 : 0 );
2010-07-22 11:13:39 +02:00
$confirm_key = (( x ( $_POST , 'confirm_key' )) ? $_POST [ 'confirm_key' ] : " " );
2012-05-30 03:43:56 +02:00
$hidden = (( x ( $_POST , 'hidden-contact' )) ? intval ( $_POST [ 'hidden-contact' ]) : 0 );
2010-07-22 11:13:39 +02:00
$contact_record = null ;
2014-03-11 23:52:32 +01:00
2010-07-22 11:13:39 +02:00
if ( x ( $dfrn_url )) {
2010-12-15 01:34:49 +01:00
/**
* Lookup the contact based on their URL ( which is the only unique thing we have at the moment )
*/
2014-03-11 23:52:32 +01:00
2012-03-15 05:58:54 +01:00
$r = q ( " SELECT * FROM `contact` WHERE `uid` = %d AND (`url` = '%s' OR `nurl` = '%s') AND `self` = 0 LIMIT 1 " ,
2010-10-18 23:34:59 +02:00
intval ( local_user ()),
2012-03-15 05:58:54 +01:00
dbesc ( $dfrn_url ),
dbesc ( normalise_link ( $dfrn_url ))
2010-07-06 06:39:55 +02:00
);
2014-03-11 23:52:32 +01:00
2010-07-22 11:13:39 +02:00
if ( count ( $r )) {
if ( strlen ( $r [ 0 ][ 'dfrn-id' ])) {
2010-12-15 01:34:49 +01:00
/**
* We don ' t need to be here . It has already happened .
*/
2010-07-28 04:27:14 +02:00
notice ( t ( " This introduction has already been accepted. " ) . EOL );
2010-07-22 11:13:39 +02:00
return ;
}
else
$contact_record = $r [ 0 ];
}
2013-12-02 20:30:24 +01:00
2010-07-22 11:13:39 +02:00
if ( is_array ( $contact_record )) {
2013-12-02 20:30:24 +01:00
$r = q ( " UPDATE `contact` SET `ret-aes` = %d, hidden = %d WHERE `id` = %d " ,
2010-07-22 11:13:39 +02:00
intval ( $aes_allow ),
2012-05-30 03:43:56 +02:00
intval ( $hidden ),
2010-07-22 11:13:39 +02:00
intval ( $contact_record [ 'id' ])
);
2010-07-06 06:39:55 +02:00
}
else {
2013-12-02 20:30:24 +01:00
2010-12-15 01:34:49 +01:00
/**
* Scrape the other site ' s profile page to pick up the dfrn links , key , fn , and photo
*/
2010-07-22 11:13:39 +02:00
$parms = scrape_dfrn ( $dfrn_url );
2014-08-21 00:56:21 +02:00
2010-07-22 11:13:39 +02:00
if ( ! count ( $parms )) {
2010-07-28 04:27:14 +02:00
notice ( t ( 'Profile location is not valid or does not contain profile information.' ) . EOL );
2010-07-06 06:39:55 +02:00
return ;
}
2010-07-22 11:13:39 +02:00
else {
if ( ! x ( $parms , 'fn' ))
2010-07-28 04:27:14 +02:00
notice ( t ( 'Warning: profile location has no identifiable owner name.' ) . EOL );
2010-07-22 11:13:39 +02:00
if ( ! x ( $parms , 'photo' ))
2010-07-28 04:27:14 +02:00
notice ( t ( 'Warning: profile location has no profile photo.' ) . EOL );
2014-08-21 00:56:21 +02:00
$invalid = validate_dfrn ( $parms );
2010-07-22 11:13:39 +02:00
if ( $invalid ) {
2011-03-11 00:22:21 +01:00
notice ( sprintf ( tt ( " %d required parameter was not found at the given location " ,
" %d required parameters were not found at the given location " ,
$invalid ), $invalid ) . EOL );
2010-07-22 11:13:39 +02:00
return ;
}
}
2010-07-02 01:48:07 +02:00
2010-07-22 11:13:39 +02:00
$dfrn_request = $parms [ 'dfrn-request' ];
2010-07-06 06:39:55 +02:00
2015-11-25 18:46:02 +01:00
/********* Escape the entire array ********/
2010-12-15 01:34:49 +01:00
2010-07-22 11:13:39 +02:00
dbesc_array ( $parms );
2010-07-06 06:39:55 +02:00
2010-12-15 01:34:49 +01:00
/******************************************/
/**
* Create a contact record on our site for the other person
*/
2010-07-06 06:39:55 +02:00
2015-11-25 18:46:02 +01:00
$r = q ( " INSERT INTO `contact` ( `uid`, `created`,`url`, `nurl`, `addr`, `name`, `nick`, `photo`, `site-pubkey`,
2014-09-06 17:28:46 +02:00
`request` , `confirm` , `notify` , `poll` , `poco` , `network` , `aes_allow` , `hidden` )
2015-11-25 18:46:02 +01:00
VALUES ( % d , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , % d , % d ) " ,
2010-10-18 23:34:59 +02:00
intval ( local_user ()),
2010-07-22 11:13:39 +02:00
datetime_convert (),
dbesc ( $dfrn_url ),
2011-11-02 00:08:07 +01:00
dbesc ( normalise_link ( $dfrn_url )),
2015-11-25 18:46:02 +01:00
$parms [ 'addr' ],
2010-07-22 11:13:39 +02:00
$parms [ 'fn' ],
2010-10-23 10:20:26 +02:00
$parms [ 'nick' ],
2010-07-22 11:13:39 +02:00
$parms [ 'photo' ],
$parms [ 'key' ],
$parms [ 'dfrn-request' ],
$parms [ 'dfrn-confirm' ],
$parms [ 'dfrn-notify' ],
$parms [ 'dfrn-poll' ],
2011-11-01 04:39:04 +01:00
$parms [ 'dfrn-poco' ],
2011-08-19 01:47:45 +02:00
dbesc ( NETWORK_DFRN ),
2012-05-30 03:43:56 +02:00
intval ( $aes_allow ),
intval ( $hidden )
2010-07-22 11:13:39 +02:00
);
}
2010-07-06 06:39:55 +02:00
2010-07-22 11:13:39 +02:00
if ( $r ) {
2011-05-23 11:39:57 +02:00
info ( t ( " Introduction complete. " ) . EOL );
2010-07-22 11:13:39 +02:00
}
2012-06-22 09:25:01 +02:00
$r = q ( " select id from contact where uid = %d and url = '%s' and `site-pubkey` = '%s' limit 1 " ,
2012-06-12 10:13:09 +02:00
intval ( local_user ()),
2012-06-13 01:17:34 +02:00
dbesc ( $dfrn_url ),
2012-06-12 10:13:09 +02:00
$parms [ 'key' ] // this was already escaped
);
if ( count ( $r )) {
$g = q ( " select def_gid from user where uid = %d limit 1 " ,
intval ( local_user ())
);
if ( $g && intval ( $g [ 0 ][ 'def_gid' ])) {
require_once ( 'include/group.php' );
group_add_member ( local_user (), '' , $r [ 0 ][ 'id' ], $g [ 0 ][ 'def_gid' ]);
}
2014-09-01 16:55:16 +02:00
$forwardurl = $a -> get_baseurl () . " /contacts/ " . $r [ 0 ][ 'id' ];
} else
$forwardurl = $a -> get_baseurl () . " /contacts " ;
2012-06-12 10:13:09 +02:00
2010-12-15 01:34:49 +01:00
/**
* Allow the blocked remote notification to complete
*/
2010-07-02 01:48:07 +02:00
2010-07-22 11:13:39 +02:00
if ( is_array ( $contact_record ))
$dfrn_request = $contact_record [ 'request' ];
2010-07-02 01:48:07 +02:00
2010-07-22 11:13:39 +02:00
if ( strlen ( $dfrn_request ) && strlen ( $confirm_key ))
2011-03-31 00:04:18 +02:00
$s = fetch_url ( $dfrn_request . '?confirm_key=' . $confirm_key );
2014-08-21 00:56:21 +02:00
2010-12-15 01:34:49 +01:00
// (ignore reply, nothing we can do it failed)
2014-09-01 16:55:16 +02:00
// Old: goaway(zrl($dfrn_url));
goaway ( $forwardurl );
2010-07-22 11:13:39 +02:00
return ; // NOTREACHED
2010-07-06 06:39:55 +02:00
2010-07-22 11:13:39 +02:00
}
2010-07-06 06:39:55 +02:00
2010-07-02 01:48:07 +02:00
}
2010-07-22 11:13:39 +02:00
// invalid/bogus request
2010-09-14 02:12:54 +02:00
notice ( t ( 'Unrecoverable protocol error.' ) . EOL );
2011-08-02 06:02:25 +02:00
goaway ( z_root ());
2010-07-22 11:13:39 +02:00
return ; // NOTREACHED
2010-07-02 01:48:07 +02:00
}
2010-12-15 01:34:49 +01:00
/**
* Otherwise :
2014-09-06 17:28:46 +02:00
*
2010-12-15 01:34:49 +01:00
* Scenario 1 :
2014-09-06 17:28:46 +02:00
* We are the requestee . A person from a remote cell has made an introduction
* on our profile web page and clicked submit . We will use their DFRN - URL to
* figure out how to contact their cell .
2010-12-15 01:34:49 +01:00
*
* Scrape the originating DFRN - URL for everything we need . Create a contact record
* and an introduction to show our user next time he / she logs in .
* Finally redirect back to the requestor so that their site can record the request .
2014-09-06 17:28:46 +02:00
* If our user ( the requestee ) later confirms this request , a record of it will need
* to exist on the requestor ' s cell in order for the confirmation process to complete ..
2010-12-15 01:34:49 +01:00
*
* It ' s possible that neither the requestor or the requestee are logged in at the moment ,
* and the requestor does not yet have any credentials to the requestee profile .
*
* Who is the requestee ? We ' ve already loaded their profile which means their nickname should be
* in $a -> argv [ 1 ] and we should have their complete info in $a -> profile .
*
*/
2010-07-02 01:48:07 +02:00
2010-07-22 11:13:39 +02:00
if ( ! ( is_array ( $a -> profile ) && count ( $a -> profile ))) {
2010-09-02 02:26:02 +02:00
notice ( t ( 'Profile unavailable.' ) . EOL );
2010-07-22 11:13:39 +02:00
return ;
}
2010-12-20 09:27:00 +01:00
$nickname = $a -> profile [ 'nickname' ];
$notify_flags = $a -> profile [ 'notify-flags' ];
$uid = $a -> profile [ 'uid' ];
$maxreq = intval ( $a -> profile [ 'maxreq' ]);
2010-07-06 06:39:55 +02:00
$contact_record = null ;
2010-12-20 09:27:00 +01:00
$failed = false ;
$parms = null ;
2010-07-02 01:48:07 +02:00
if ( x ( $_POST , 'dfrn_url' )) {
2010-12-20 09:27:00 +01:00
/**
* Block friend request spam
*/
if ( $maxreq ) {
$r = q ( " SELECT * FROM `intro` WHERE `datetime` > '%s' AND `uid` = %d " ,
dbesc ( datetime_convert ( 'UTC' , 'UTC' , 'now - 24 hours' )),
intval ( $uid )
);
if ( count ( $r ) > $maxreq ) {
2011-03-11 00:22:21 +01:00
notice ( sprintf ( t ( '%s has received too many connection requests today.' ), $a -> profile [ 'name' ]) . EOL );
2010-12-20 09:27:00 +01:00
notice ( t ( 'Spam protection measures have been invoked.' ) . EOL );
notice ( t ( 'Friends are advised to please try again in 24 hours.' ) . EOL );
return ;
2014-09-06 17:28:46 +02:00
}
2010-12-20 09:27:00 +01:00
}
2011-01-07 09:24:08 +01:00
/**
*
2014-09-06 17:28:46 +02:00
* Cleanup old introductions that remain blocked .
2011-01-07 09:24:08 +01:00
* Also remove the contact record , but only if there is no existing relationship
2011-12-12 08:33:56 +01:00
* Do not remove email contacts as these may be awaiting email verification
*/
2014-09-06 17:28:46 +02:00
$r = q ( " SELECT `intro`.*, `intro`.`id` AS `iid`, `contact`.`id` AS `cid`, `contact`.`rel`
2011-12-12 08:33:56 +01:00
FROM `intro` LEFT JOIN `contact` on `intro` . `contact-id` = `contact` . `id`
2014-09-06 17:28:46 +02:00
WHERE `intro` . `blocked` = 1 AND `contact` . `self` = 0
2011-12-12 08:33:56 +01:00
AND `contact` . `network` != '%s'
AND `intro` . `datetime` < UTC_TIMESTAMP () - INTERVAL 30 MINUTE " ,
2012-04-11 04:15:52 +02:00
dbesc ( NETWORK_MAIL2 )
2011-12-12 08:33:56 +01:00
);
if ( count ( $r )) {
foreach ( $r as $rr ) {
if ( ! $rr [ 'rel' ]) {
2014-03-11 23:52:32 +01:00
q ( " DELETE FROM `contact` WHERE `id` = %d " ,
2011-12-12 08:33:56 +01:00
intval ( $rr [ 'cid' ])
);
}
2014-03-11 23:52:32 +01:00
q ( " DELETE FROM `intro` WHERE `id` = %d " ,
2011-12-12 08:33:56 +01:00
intval ( $rr [ 'iid' ])
);
}
}
/**
2011-01-07 09:24:08 +01:00
*
2011-12-12 08:33:56 +01:00
* Cleanup any old email intros - which will have a greater lifetime
2011-01-07 09:24:08 +01:00
*/
2014-03-11 23:52:32 +01:00
$r = q ( " SELECT `intro`.*, `intro`.`id` AS `iid`, `contact`.`id` AS `cid`, `contact`.`rel`
2011-01-07 09:24:08 +01:00
FROM `intro` LEFT JOIN `contact` on `intro` . `contact-id` = `contact` . `id`
2014-03-11 23:52:32 +01:00
WHERE `intro` . `blocked` = 1 AND `contact` . `self` = 0
2011-12-12 08:33:56 +01:00
AND `contact` . `network` = '%s'
AND `intro` . `datetime` < UTC_TIMESTAMP () - INTERVAL 3 DAY " ,
2012-04-11 04:15:52 +02:00
dbesc ( NETWORK_MAIL2 )
2011-12-12 08:33:56 +01:00
);
2011-01-07 09:24:08 +01:00
if ( count ( $r )) {
2011-01-07 12:15:52 +01:00
foreach ( $r as $rr ) {
2011-01-07 09:24:08 +01:00
if ( ! $rr [ 'rel' ]) {
2014-03-11 23:52:32 +01:00
q ( " DELETE FROM `contact` WHERE `id` = %d " ,
2011-01-07 09:24:08 +01:00
intval ( $rr [ 'cid' ])
);
}
2014-03-11 23:52:32 +01:00
q ( " DELETE FROM `intro` WHERE `id` = %d " ,
2011-01-07 09:24:08 +01:00
intval ( $rr [ 'iid' ])
);
}
}
2012-04-11 04:15:52 +02:00
$email_follow = ( x ( $_POST , 'email_follow' ) ? intval ( $_POST [ 'email_follow' ]) : 0 );
$real_name = ( x ( $_POST , 'realname' ) ? notags ( trim ( $_POST [ 'realname' ])) : '' );
2011-12-12 08:33:56 +01:00
2010-07-02 01:48:07 +02:00
$url = trim ( $_POST [ 'dfrn_url' ]);
2010-07-06 06:39:55 +02:00
if ( ! strlen ( $url )) {
2010-07-28 04:27:14 +02:00
notice ( t ( " Invalid locator " ) . EOL );
2010-07-06 06:39:55 +02:00
return ;
}
2011-09-07 03:06:19 +02:00
$hcard = '' ;
2010-07-20 04:09:58 +02:00
2012-04-11 04:15:52 +02:00
if ( $email_follow ) {
2012-04-23 14:16:57 +02:00
if ( ! validate_email ( $url )) {
2012-04-11 04:15:52 +02:00
notice ( t ( 'Invalid email address.' ) . EOL );
return ;
}
$addr = $url ;
$name = ( $realname ) ? $realname : $addr ;
$nick = substr ( $addr , 0 , strpos ( $addr , '@' ));
$url = 'http://' . substr ( $addr , strpos ( $addr , '@' ) + 1 );
$nurl = normalise_url ( $host );
$poll = 'email ' . random_string ();
$notify = 'smtp ' . random_string ();
$blocked = 1 ;
$pending = 1 ;
$network = NETWORK_MAIL2 ;
$rel = CONTACT_IS_FOLLOWER ;
$mail_disabled = (( function_exists ( 'imap_open' ) && ( ! get_config ( 'system' , 'imap_disabled' ))) ? 0 : 1 );
if ( get_config ( 'system' , 'dfrn_only' ))
$mail_disabled = 1 ;
if ( ! $mail_disabled ) {
$failed = false ;
$r = q ( " SELECT * FROM `mailacct` WHERE `uid` = %d LIMIT 1 " ,
intval ( $uid )
);
if ( ! count ( $r )) {
2012-12-23 11:47:14 +01:00
2012-04-11 04:15:52 +02:00
notice ( t ( 'This account has not been configured for email. Request failed.' ) . EOL );
return ;
}
}
2012-04-23 14:16:57 +02:00
$r = q ( " insert into contact ( uid, created, addr, name, nick, url, nurl, poll, notify, blocked, pending, network, rel )
values ( % d , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , % d , % d , '%s' , % d ) " ,
intval ( $uid ),
dbesc ( datetime_convert ()),
dbesc ( $addr ),
dbesc ( $name ),
dbesc ( $nick ),
dbesc ( $url ),
dbesc ( $nurl ),
dbesc ( $poll ),
dbesc ( $notify ),
intval ( $blocked ),
intval ( $pending ),
dbesc ( $network ),
intval ( $rel )
);
2012-04-11 04:15:52 +02:00
2012-04-23 14:16:57 +02:00
$r = q ( " select id from contact where poll = '%s' and uid = %d limit 1 " ,
dbesc ( $poll ),
intval ( $uid )
);
if ( count ( $r )) {
$contact_id = $r [ 0 ][ 'id' ];
2012-05-18 07:44:52 +02:00
$g = q ( " select def_gid from user where uid = %d limit 1 " ,
intval ( $uid )
);
if ( $g && intval ( $g [ 0 ][ 'def_gid' ])) {
require_once ( 'include/group.php' );
group_add_member ( $uid , '' , $contact_id , $g [ 0 ][ 'def_gid' ]);
}
2012-04-23 14:16:57 +02:00
$photo = avatar_img ( $addr );
2014-09-06 17:28:46 +02:00
$r = q ( " UPDATE `contact` SET
`photo` = '%s' ,
2012-04-23 14:16:57 +02:00
`thumb` = '%s' ,
2014-09-06 17:28:46 +02:00
`micro` = '%s' ,
`name-date` = '%s' ,
`uri-date` = '%s' ,
`avatar-date` = '%s' ,
2012-04-23 14:16:57 +02:00
`hidden` = 0 ,
2013-12-02 20:30:24 +01:00
WHERE `id` = % d
2012-04-23 14:16:57 +02:00
" ,
dbesc ( $photos [ 0 ]),
dbesc ( $photos [ 1 ]),
dbesc ( $photos [ 2 ]),
dbesc ( datetime_convert ()),
dbesc ( datetime_convert ()),
dbesc ( datetime_convert ()),
intval ( $contact_id )
);
}
2012-04-24 03:28:33 +02:00
// contact is created. Now create an introduction
2012-04-11 04:15:52 +02:00
2012-04-24 03:28:33 +02:00
$hash = random_string ();
2012-04-11 04:15:52 +02:00
2012-04-24 03:28:33 +02:00
$r = q ( " insert into intro ( uid, `contact-id`, knowyou, note, hash, datetime, blocked )
values ( % d , % d , % d , '%s' , '%s' , '%s' , % d ) " ,
intval ( $uid ),
intval ( $contact_id ),
(( x ( $_POST , 'knowyou' ) && ( $_POST [ 'knowyou' ] == 1 )) ? 1 : 0 ),
dbesc ( notags ( trim ( $_POST [ 'dfrn-request-message' ]))),
dbesc ( $hash ),
dbesc ( datetime_convert ()),
1
);
2014-08-21 00:56:21 +02:00
2012-04-24 03:28:33 +02:00
// Next send an email verify form to the requestor.
2012-04-11 04:15:52 +02:00
2016-01-01 17:49:07 +01:00
} else {
2015-12-28 03:14:38 +01:00
// Detect the network
$data = probe_url ( $url );
$network = $data [ " network " ];
2012-04-11 04:15:52 +02:00
2015-12-28 03:14:38 +01:00
// Canonicalise email-style profile locator
2012-04-11 04:15:52 +02:00
$url = webfinger_dfrn ( $url , $hcard );
2016-01-01 17:49:07 +01:00
if ( substr ( $url , 0 , 5 ) === 'stat:' ) {
// Every time we detect the remote subscription we define this as OStatus.
// We do this even if it is not OStatus.
// we only need to pass this through another section of the code.
if ( $network != NETWORK_DIASPORA )
$network = NETWORK_OSTATUS ;
2010-11-10 03:24:35 +01:00
2016-01-01 17:49:07 +01:00
$url = substr ( $url , 5 );
} else
$network = NETWORK_DFRN ;
2010-07-08 16:03:25 +02:00
}
2010-07-06 06:39:55 +02:00
2016-03-01 14:42:55 +01:00
logger ( 'dfrn_request: url: ' . $url . ',network=' . $network , LOGGER_DEBUG );
2010-07-06 06:39:55 +02:00
2011-08-19 01:47:45 +02:00
if ( $network === NETWORK_DFRN ) {
2014-09-06 17:28:46 +02:00
$ret = q ( " SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `self` = 0 LIMIT 1 " ,
2010-10-13 04:32:15 +02:00
intval ( $uid ),
dbesc ( $url )
2010-07-06 06:39:55 +02:00
);
2010-09-14 02:12:54 +02:00
2010-10-13 04:32:15 +02:00
if ( count ( $ret )) {
if ( strlen ( $ret [ 0 ][ 'issued-id' ])) {
notice ( t ( 'You have already introduced yourself here.' ) . EOL );
return ;
}
2011-08-08 01:15:54 +02:00
elseif ( $ret [ 0 ][ 'rel' ] == CONTACT_IS_FRIEND ) {
2011-03-11 00:22:21 +01:00
notice ( sprintf ( t ( 'Apparently you are already friends with %s.' ), $a -> profile [ 'name' ]) . EOL );
2010-10-18 05:04:17 +02:00
return ;
}
2010-10-13 04:32:15 +02:00
else {
$contact_record = $ret [ 0 ];
$parms = array ( 'dfrn-request' => $ret [ 0 ][ 'request' ]);
}
2010-09-14 02:12:54 +02:00
}
2010-10-18 05:04:17 +02:00
2010-10-13 04:32:15 +02:00
$issued_id = random_string ();
if ( is_array ( $contact_record )) {
// There is a contact record but no issued-id, so this
// is a reciprocal introduction from a known contact
2013-12-02 20:30:24 +01:00
$r = q ( " UPDATE `contact` SET `issued-id` = '%s' WHERE `id` = %d " ,
2010-10-13 04:32:15 +02:00
dbesc ( $issued_id ),
intval ( $contact_record [ 'id' ])
);
2010-07-02 01:48:07 +02:00
}
else {
2010-10-13 04:32:15 +02:00
if ( ! validate_url ( $url )) {
notice ( t ( 'Invalid profile URL.' ) . EOL );
goaway ( $a -> get_baseurl () . '/' . $a -> cmd );
return ; // NOTREACHED
}
2010-07-02 01:48:07 +02:00
2010-10-13 04:32:15 +02:00
if ( ! allowed_url ( $url )) {
notice ( t ( 'Disallowed profile URL.' ) . EOL );
goaway ( $a -> get_baseurl () . '/' . $a -> cmd );
return ; // NOTREACHED
2010-07-02 01:48:07 +02:00
}
2014-09-06 17:28:46 +02:00
2010-07-02 01:48:07 +02:00
2012-12-28 22:51:50 +01:00
require_once ( 'include/Scrape.php' );
2010-10-13 04:32:15 +02:00
2011-09-07 03:06:19 +02:00
$parms = scrape_dfrn (( $hcard ) ? $hcard : $url );
2010-10-13 04:32:15 +02:00
if ( ! count ( $parms )) {
notice ( t ( 'Profile location is not valid or does not contain profile information.' ) . EOL );
goaway ( $a -> get_baseurl () . '/' . $a -> cmd );
}
else {
if ( ! x ( $parms , 'fn' ))
notice ( t ( 'Warning: profile location has no identifiable owner name.' ) . EOL );
if ( ! x ( $parms , 'photo' ))
notice ( t ( 'Warning: profile location has no profile photo.' ) . EOL );
2014-09-06 17:28:46 +02:00
$invalid = validate_dfrn ( $parms );
2010-10-13 04:32:15 +02:00
if ( $invalid ) {
2011-03-11 00:22:21 +01:00
notice ( sprintf ( tt ( " %d required parameter was not found at the given location " ,
" %d required parameters were not found at the given location " ,
$invalid ), $invalid ) . EOL );
2014-09-06 17:28:46 +02:00
2010-10-13 04:32:15 +02:00
return ;
}
}
2010-07-02 01:48:07 +02:00
2010-10-13 04:32:15 +02:00
$parms [ 'url' ] = $url ;
$parms [ 'issued-id' ] = $issued_id ;
2010-07-02 01:48:07 +02:00
2010-07-06 06:39:55 +02:00
2010-10-13 04:32:15 +02:00
dbesc_array ( $parms );
2015-11-25 18:46:02 +01:00
$r = q ( " INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `name`, `nick`, `issued-id`, `photo`, `site-pubkey`,
2011-11-01 04:39:04 +01:00
`request` , `confirm` , `notify` , `poll` , `poco` , `network` )
2015-11-25 18:46:02 +01:00
VALUES ( % d , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' , '%s' ) " ,
2010-07-06 06:39:55 +02:00
intval ( $uid ),
2011-11-02 00:08:07 +01:00
dbesc ( datetime_convert ()),
2010-07-06 06:39:55 +02:00
$parms [ 'url' ],
2011-11-02 00:08:07 +01:00
dbesc ( normalise_link ( $parms [ 'url' ])),
2015-11-25 18:46:02 +01:00
$parms [ 'addr' ],
2010-10-13 04:32:15 +02:00
$parms [ 'fn' ],
2010-10-23 10:20:26 +02:00
$parms [ 'nick' ],
2010-10-13 04:32:15 +02:00
$parms [ 'issued-id' ],
$parms [ 'photo' ],
$parms [ 'key' ],
$parms [ 'dfrn-request' ],
$parms [ 'dfrn-confirm' ],
$parms [ 'dfrn-notify' ],
2011-08-19 01:47:45 +02:00
$parms [ 'dfrn-poll' ],
2011-11-01 04:39:04 +01:00
$parms [ 'dfrn-poco' ],
2011-08-19 01:47:45 +02:00
dbesc ( NETWORK_DFRN )
2010-07-06 06:39:55 +02:00
);
2010-07-02 01:48:07 +02:00
2010-10-13 04:32:15 +02:00
// find the contact record we just created
2014-03-11 23:52:32 +01:00
if ( $r ) {
$r = q ( " SELECT `id` FROM `contact`
2010-10-13 04:32:15 +02:00
WHERE `uid` = % d AND `url` = '%s' AND `issued-id` = '%s' LIMIT 1 " ,
intval ( $uid ),
$parms [ 'url' ],
$parms [ 'issued-id' ]
);
2014-03-11 23:52:32 +01:00
if ( count ( $r ))
2010-10-13 04:32:15 +02:00
$contact_record = $r [ 0 ];
}
2013-12-02 20:30:24 +01:00
2010-10-13 04:32:15 +02:00
}
if ( $r === false ) {
notice ( t ( 'Failed to update contact record.' ) . EOL );
return ;
}
2010-07-02 01:48:07 +02:00
2010-10-13 04:32:15 +02:00
$hash = random_string () . ( string ) time (); // Generate a confirm_key
2013-12-02 20:30:24 +01:00
2010-10-13 04:32:15 +02:00
if ( is_array ( $contact_record )) {
$ret = q ( " INSERT INTO `intro` ( `uid`, `contact-id`, `blocked`, `knowyou`, `note`, `hash`, `datetime`)
VALUES ( % d , % d , 1 , % d , '%s' , '%s' , '%s' ) " ,
intval ( $uid ),
intval ( $contact_record [ 'id' ]),
(( x ( $_POST , 'knowyou' ) && ( $_POST [ 'knowyou' ] == 1 )) ? 1 : 0 ),
dbesc ( notags ( trim ( $_POST [ 'dfrn-request-message' ]))),
dbesc ( $hash ),
dbesc ( datetime_convert ())
);
}
2014-08-21 00:56:21 +02:00
2010-10-18 05:04:17 +02:00
// This notice will only be seen by the requestor if the requestor and requestee are on the same server.
2010-07-02 01:48:07 +02:00
2014-09-06 17:28:46 +02:00
if ( ! $failed )
2011-05-23 11:39:57 +02:00
info ( t ( 'Your introduction has been sent.' ) . EOL );
2010-07-02 01:48:07 +02:00
2010-10-13 04:32:15 +02:00
// "Homecoming" - send the requestor back to their site to record the introduction.
2010-07-02 01:48:07 +02:00
2010-10-13 04:32:15 +02:00
$dfrn_url = bin2hex ( $a -> get_baseurl () . '/profile/' . $nickname );
$aes_allow = (( function_exists ( 'openssl_encrypt' )) ? 1 : 0 );
2010-07-02 01:48:07 +02:00
2014-09-06 17:28:46 +02:00
goaway ( $parms [ 'dfrn-request' ] . " ?dfrn_url= $dfrn_url "
. '&dfrn_version=' . DFRN_PROTOCOL_VERSION
. '&confirm_key=' . $hash
2010-10-13 04:32:15 +02:00
. (( $aes_allow ) ? " &aes_allow=1 " : " " )
);
// NOTREACHED
2011-08-19 01:47:45 +02:00
// END $network === NETWORK_DFRN
2015-12-28 03:14:38 +01:00
} elseif (( $network != NETWORK_PHANTOM ) AND ( $url != " " )) {
2014-09-06 17:28:46 +02:00
2010-10-13 04:32:15 +02:00
/**
2010-12-15 01:34:49 +01:00
*
* Substitute our user ' s feed URL into $url template
* Send the subscriber home to subscribe
*
*/
2010-10-13 04:32:15 +02:00
2015-12-28 03:14:38 +01:00
// Diaspora needs the uri in the format user@domain.tld
// Diaspora will support the remote subscription in a future version
if ( $network == NETWORK_DIASPORA ) {
$uri = $nickname . '@' . $a -> get_hostname ();
if ( $a -> get_path ())
$uri .= '/' . $a -> get_path ();
$uri = urlencode ( $uri );
} else
$uri = $a -> get_baseurl () . '/profile/' . $nickname ;
$url = str_replace ( '{uri}' , $uri , $url );
2010-10-13 04:32:15 +02:00
goaway ( $url );
// NOTREACHED
2015-12-28 03:14:38 +01:00
// END $network != NETWORK_PHANTOM
} else {
notice ( t ( " Remote subscription can't be done for your network. Please subscribe directly on your system. " ) . EOL );
return ;
2010-10-13 04:32:15 +02:00
}
2010-07-02 01:48:07 +02:00
2010-10-13 04:32:15 +02:00
} return ;
2010-07-02 01:48:07 +02:00
}}
2010-07-22 11:13:39 +02:00
2010-07-02 01:48:07 +02:00
if ( ! function_exists ( 'dfrn_request_content' )) {
function dfrn_request_content ( & $a ) {
if (( $a -> argc != 2 ) || ( ! count ( $a -> profile )))
return " " ;
// "Homecoming". Make sure we're logged in to this site as the correct user. Then offer a confirm button
// to send us to the post section to record the introduction.
if ( x ( $_GET , 'dfrn_url' )) {
2010-07-06 06:39:55 +02:00
if ( ! local_user ()) {
2011-05-23 11:39:57 +02:00
info ( t ( " Please login to confirm introduction. " ) . EOL );
2011-01-07 08:41:14 +01:00
/* setup the return URL to come back to this page if they use openid */
2014-10-22 09:32:24 +02:00
$_SESSION [ 'return_url' ] = $a -> query_string ;
2010-07-02 01:48:07 +02:00
return login ();
}
2014-09-06 17:28:46 +02:00
// Edge case, but can easily happen in the wild. This person is authenticated,
2010-07-02 01:48:07 +02:00
// but not as the person who needs to deal with this request.
2010-07-22 11:13:39 +02:00
if ( $a -> user [ 'nickname' ] != $a -> argv [ 1 ]) {
2010-07-28 04:27:14 +02:00
notice ( t ( " Incorrect identity currently logged in. Please login to <strong>this</strong> profile. " ) . EOL );
2010-07-02 01:48:07 +02:00
return login ();
}
2010-07-22 11:13:39 +02:00
$dfrn_url = notags ( trim ( hex2bin ( $_GET [ 'dfrn_url' ])));
2010-07-02 01:48:07 +02:00
$aes_allow = ((( x ( $_GET , 'aes_allow' )) && ( $_GET [ 'aes_allow' ] == 1 )) ? 1 : 0 );
$confirm_key = ( x ( $_GET , 'confirm_key' ) ? $_GET [ 'confirm_key' ] : " " );
2015-04-09 00:10:21 +02:00
// Checking fastlane for validity
if ( x ( $_SESSION , " fastlane " ) AND ( normalise_link ( $_SESSION [ " fastlane " ]) == normalise_link ( $dfrn_url ))) {
$_POST [ " dfrn_url " ] = $dfrn_url ;
$_POST [ " confirm_key " ] = $confirm_key ;
$_POST [ " localconfirm " ] = 1 ;
$_POST [ " hidden-contact " ] = 0 ;
$_POST [ " submit " ] = t ( 'Confirm' );
dfrn_request_post ( $a );
killme ();
return ; // NOTREACHED
}
2011-05-11 13:37:13 +02:00
$tpl = get_markup_template ( " dfrn_req_confirm.tpl " );
$o = replace_macros ( $tpl , array (
2010-07-02 01:48:07 +02:00
'$dfrn_url' => $dfrn_url ,
'$aes_allow' => (( $aes_allow ) ? '<input type="hidden" name="aes_allow" value="1" />' : " " ),
2012-05-30 03:43:56 +02:00
'$hidethem' => t ( 'Hide this contact' ),
'$hidechecked' => '' ,
2010-07-02 01:48:07 +02:00
'$confirm_key' => $confirm_key ,
2011-04-07 08:03:54 +02:00
'$welcome' => sprintf ( t ( 'Welcome home %s.' ), $a -> user [ 'username' ]),
'$please' => sprintf ( t ( 'Please confirm your introduction/connection request to %s.' ), $dfrn_url ),
'$submit' => t ( 'Confirm' ),
2010-07-02 01:48:07 +02:00
'$uid' => $_SESSION [ 'uid' ],
2010-07-21 05:48:08 +02:00
'$nickname' => $a -> user [ 'nickname' ],
2010-07-02 01:48:07 +02:00
'dfrn_rawurl' => $_GET [ 'dfrn_url' ]
));
return $o ;
}
2014-09-06 17:28:46 +02:00
elseif (( x ( $_GET , 'confirm_key' )) && strlen ( $_GET [ 'confirm_key' ])) {
2010-07-22 11:13:39 +02:00
// we are the requestee and it is now safe to send our user their introduction,
2014-09-06 17:28:46 +02:00
// We could just unblock it, but first we have to jump through a few hoops to
// send an email, or even to find out if we need to send an email.
2010-07-22 11:13:39 +02:00
$intro = q ( " SELECT * FROM `intro` WHERE `hash` = '%s' LIMIT 1 " ,
dbesc ( $_GET [ 'confirm_key' ])
);
if ( count ( $intro )) {
$r = q ( " SELECT `contact`.*, `user`.* FROM `contact` LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid`
WHERE `contact` . `id` = % d LIMIT 1 " ,
intval ( $intro [ 0 ][ 'contact-id' ])
);
2010-10-18 05:04:17 +02:00
$auto_confirm = false ;
if ( count ( $r )) {
2012-05-30 03:43:56 +02:00
if (( $r [ 0 ][ 'page-flags' ] != PAGE_NORMAL ) && ( $r [ 0 ][ 'page-flags' ] != PAGE_PRVGROUP ))
2014-03-11 23:52:32 +01:00
$auto_confirm = true ;
2011-12-27 00:47:40 +01:00
if ( ! $auto_confirm ) {
2014-09-06 17:28:46 +02:00
2011-12-27 00:47:40 +01:00
notification ( array (
'type' => NOTIFY_INTRO ,
'notify_flags' => $r [ 0 ][ 'notify-flags' ],
'language' => $r [ 0 ][ 'language' ],
'to_name' => $r [ 0 ][ 'username' ],
'to_email' => $r [ 0 ][ 'email' ],
2012-02-18 11:57:42 +01:00
'uid' => $r [ 0 ][ 'uid' ],
2011-12-27 00:47:40 +01:00
'link' => $a -> get_baseurl () . '/notifications/intros' ,
'source_name' => (( strlen ( stripslashes ( $r [ 0 ][ 'name' ]))) ? stripslashes ( $r [ 0 ][ 'name' ]) : t ( '[Name Withheld]' )),
'source_link' => $r [ 0 ][ 'url' ],
2012-01-04 05:26:20 +01:00
'source_photo' => $r [ 0 ][ 'photo' ],
'verb' => ACTIVITY_REQ_FRIEND ,
'otype' => 'intro'
2010-07-22 11:13:39 +02:00
));
}
2011-12-27 00:47:40 +01:00
2010-10-18 05:04:17 +02:00
if ( $auto_confirm ) {
require_once ( 'mod/dfrn_confirm.php' );
$handsfree = array (
'uid' => $r [ 0 ][ 'uid' ],
'node' => $r [ 0 ][ 'nickname' ],
'dfrn_id' => $r [ 0 ][ 'issued-id' ],
'intro_id' => $intro [ 0 ][ 'id' ],
2012-04-30 13:11:42 +02:00
'duplex' => (( $r [ 0 ][ 'page-flags' ] == PAGE_FREELOVE ) ? 1 : 0 ),
'activity' => intval ( get_pconfig ( $r [ 0 ][ 'uid' ], 'system' , 'post_newfriend' ))
2010-10-18 05:04:17 +02:00
);
dfrn_confirm_post ( $a , $handsfree );
}
2010-07-22 11:13:39 +02:00
}
2010-10-18 05:04:17 +02:00
if ( ! $auto_confirm ) {
2010-07-02 01:48:07 +02:00
2010-10-18 05:04:17 +02:00
// If we are auto_confirming, this record will have already been nuked
// in dfrn_confirm_post()
2013-12-02 20:30:24 +01:00
$r = q ( " UPDATE `intro` SET `blocked` = 0 WHERE `hash` = '%s' " ,
2010-10-18 05:04:17 +02:00
dbesc ( $_GET [ 'confirm_key' ])
);
}
2010-07-22 11:13:39 +02:00
}
2011-08-19 01:47:45 +02:00
2010-07-22 11:13:39 +02:00
killme ();
return ; // NOTREACHED
}
else {
2011-01-01 22:12:31 +01:00
/**
* Normal web request . Display our user ' s introduction form .
*/
2014-09-06 17:28:46 +02:00
2011-04-22 04:12:22 +02:00
if (( get_config ( 'system' , 'block_public' )) && ( ! local_user ()) && ( ! remote_user ())) {
2012-09-07 05:17:50 +02:00
if ( ! get_config ( 'system' , 'local_block' )) {
notice ( t ( 'Public access denied.' ) . EOL );
return ;
}
2011-04-22 04:12:22 +02:00
}
2011-01-01 22:12:31 +01:00
/**
* Try to auto - fill the profile address
*/
2014-08-21 00:56:21 +02:00
// At first look if an address was provided
// Otherwise take the local address
if ( x ( $_GET , 'addr' ) AND ( $_GET [ 'addr' ] != " " ))
$myaddr = hex2bin ( $_GET [ 'addr' ]);
elseif ( x ( $_GET , 'address' ) AND ( $_GET [ 'address' ] != " " ))
$myaddr = $_GET [ 'address' ];
elseif ( local_user ()) {
2011-01-01 22:12:31 +01:00
if ( strlen ( $a -> path )) {
$myaddr = $a -> get_baseurl () . '/profile/' . $a -> user [ 'nickname' ];
}
else {
2011-08-02 06:02:25 +02:00
$myaddr = $a -> user [ 'nickname' ] . '@' . substr ( z_root (), strpos ( z_root (), '://' ) + 3 );
2011-01-01 22:12:31 +01:00
}
2014-08-21 00:56:21 +02:00
} else // last, try a zrl
2012-04-28 01:23:25 +02:00
$myaddr = get_my_url ();
2011-09-06 03:34:30 +02:00
$target_addr = $a -> profile [ 'nickname' ] . '@' . substr ( z_root (), strpos ( z_root (), '://' ) + 3 );
2011-01-01 22:12:31 +01:00
/**
*
* The auto_request form only has the profile address
2014-09-06 17:28:46 +02:00
* because nobody is going to read the comments and
2011-01-01 22:12:31 +01:00
* it doesn ' t matter if they know you or not .
*
*/
2010-10-19 01:38:48 +02:00
if ( $a -> profile [ 'page-flags' ] == PAGE_NORMAL )
2011-05-11 13:37:13 +02:00
$tpl = get_markup_template ( 'dfrn_request.tpl' );
2010-10-19 01:38:48 +02:00
else
2011-05-11 13:37:13 +02:00
$tpl = get_markup_template ( 'auto_request.tpl' );
2011-01-01 22:12:31 +01:00
2016-03-01 18:28:06 +01:00
$page_desc = t ( " Please enter your 'Identity Address' from one of the following supported communications networks: " );
2012-03-13 23:40:16 +01:00
2012-03-15 05:58:54 +01:00
// see if we are allowed to have NETWORK_MAIL2 contacts
$mail_disabled = (( function_exists ( 'imap_open' ) && ( ! get_config ( 'system' , 'imap_disabled' ))) ? 0 : 1 );
if ( get_config ( 'system' , 'dfrn_only' ))
$mail_disabled = 1 ;
if ( ! $mail_disabled ) {
$r = q ( " SELECT * FROM `mailacct` WHERE `uid` = %d LIMIT 1 " ,
intval ( $a -> profile [ 'uid' ])
);
if ( ! count ( $r ))
$mail_disabled = 1 ;
}
2014-08-21 00:56:21 +02:00
// "coming soon" is disabled for now
//$emailnet = (($mail_disabled) ? '' : t("<strike>Connect as an email follower</strike> \x28Coming soon\x29"));
$emailnet = " " ;
2012-03-13 23:40:16 +01:00
2015-08-26 19:17:41 +02:00
$invite_desc = sprintf (
t ( 'If you are not yet a member of the free social web, <a href="%s/siteinfo">follow this link to find a public Friendica site and join us today</a>.' ),
get_server ()
);
2011-09-06 03:34:30 +02:00
2016-03-01 18:28:06 +01:00
$o = replace_macros ( $tpl , array (
2010-12-17 04:38:52 +01:00
'$header' => t ( 'Friend/Connection Request' ),
2011-11-01 04:39:04 +01:00
'$desc' => t ( 'Examples: jojo@demo.friendica.com, http://demo.friendica.com/profile/jojo, testuser@identi.ca' ),
2010-12-17 04:38:52 +01:00
'$pls_answer' => t ( 'Please answer the following:' ),
2014-08-21 00:56:21 +02:00
'$does_know_you' => array ( 'knowyou' , sprintf ( t ( 'Does %s know you?' ), $a -> profile [ 'name' ]), false , '' , array ( t ( 'No' ), t ( 'Yes' ))),
/* '$does_know' => sprintf ( t ( 'Does %s know you?' ), $a -> profile [ 'name' ]),
2010-12-17 04:38:52 +01:00
'$yes' => t ( 'Yes' ),
2014-08-21 00:56:21 +02:00
'$no' => t ( 'No' ), */
2010-12-17 04:38:52 +01:00
'$add_note' => t ( 'Add a personal note:' ),
2011-09-06 03:34:30 +02:00
'$page_desc' => $page_desc ,
2012-02-19 19:44:30 +01:00
'$friendica' => t ( 'Friendica' ),
2011-06-09 06:06:02 +02:00
'$statusnet' => t ( 'StatusNet/Federated Social Web' ),
2011-09-06 03:34:30 +02:00
'$diaspora' => t ( 'Diaspora' ),
2012-04-02 01:18:03 +02:00
'$diasnote' => sprintf ( t ( ' - please do not use this form. Instead, enter %s into your Diaspora search bar.' ), $target_addr ),
2011-04-15 05:37:42 +02:00
'$your_address' => t ( 'Your Identity Address:' ),
2012-03-13 23:40:16 +01:00
'$invite_desc' => $invite_desc ,
'$emailnet' => $emailnet ,
2010-12-17 04:38:52 +01:00
'$submit' => t ( 'Submit Request' ),
'$cancel' => t ( 'Cancel' ),
2010-10-26 06:52:30 +02:00
'$nickname' => $a -> argv [ 1 ],
'$name' => $a -> profile [ 'name' ],
'$myaddr' => $myaddr
));
2010-07-22 11:13:39 +02:00
return $o ;
2010-07-02 01:48:07 +02:00
}
2010-07-28 04:27:14 +02:00
2010-07-22 11:13:39 +02:00
return ; // Somebody is fishing.
2011-05-23 11:39:57 +02:00
}}