Merge branch 'master' of git://github.com/friendika/friendika

This commit is contained in:
root 2010-12-15 12:07:14 +01:00
commit 961a0c13c7
9 changed files with 379 additions and 54 deletions

27
README
View File

@ -1,14 +1,20 @@
Friendika *********
Distributed Social Network Friendika
*********
http://friendika.com Distributed Social Network
What if there was a social network which was free to use, open source - and http://friendika.com
where your privacy is always under your control?
What if this social network could scale to encompass the entire internet, and Did you ever wonder what might happen if you ever left Facebook?
*not* require a central organisation to provide servers (in exchange for
What if there was a social network which provided some of the same interaction
you've grown to love, *and* was free to use, open source - and where your
privacy is always under your control?
And what if this social network could scale to encompass the entire internet,
and *not* require a central organisation to provide servers (in exchange for
selling your private information to advertisers)? selling your private information to advertisers)?
Look no further. Look no further.
@ -36,7 +42,12 @@ Friendika is secure, and as private as you wish it to be. Our privacy settings
are straight-forward and simple, because we know that relationships rarely are are straight-forward and simple, because we know that relationships rarely are
(straight-forward and simple). Whether you're communicating with drinking (straight-forward and simple). Whether you're communicating with drinking
buddies or potential employers, you can rest assured that each is only able to buddies or potential employers, you can rest assured that each is only able to
see the side of you that you wish to present. see the side of you that you wish to present. If you send a private message to
your aunt Mary, we encrypt it with military grade encryption.
Other social network projects talk about privacy and offering a feature-rich
social networking alternative, but all they can deliver is vapour and vague
promises. Friendika delivers the goods. Time and time again.
A single instance of Friendika can easily support hundreds of (and up to A single instance of Friendika can easily support hundreds of (and up to
several thousand) people using commodity hosting hardware. Each of these several thousand) people using commodity hosting hardware. Each of these

204
boot.php
View File

@ -2,7 +2,7 @@
set_time_limit(0); set_time_limit(0);
define ( 'BUILD_ID', 1022 ); define ( 'BUILD_ID', 1023 );
define ( 'DFRN_PROTOCOL_VERSION', '2.0' ); define ( 'DFRN_PROTOCOL_VERSION', '2.0' );
define ( 'EOL', "<br />\r\n" ); define ( 'EOL', "<br />\r\n" );
@ -157,6 +157,7 @@ if(! class_exists('App')) {
class App { class App {
public $module_loaded = false; public $module_loaded = false;
public $query_string;
public $config; public $config;
public $page; public $page;
public $profile; public $profile;
@ -189,6 +190,8 @@ class App {
$this->page = array(); $this->page = array();
$this->pager= array(); $this->pager= array();
$this->query_string = '';
$this->scheme = ((isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'])) ? 'https' : 'http' ); $this->scheme = ((isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'])) ? 'https' : 'http' );
if(x($_SERVER,'SERVER_NAME')) if(x($_SERVER,'SERVER_NAME'))
@ -197,7 +200,7 @@ class App {
set_include_path("include/$this->hostname" . PATH_SEPARATOR . 'include' . PATH_SEPARATOR . '.' ); set_include_path("include/$this->hostname" . PATH_SEPARATOR . 'include' . PATH_SEPARATOR . '.' );
if((x($_SERVER,'QUERY_STRING')) && substr($_SERVER['QUERY_STRING'],0,2) === "q=") if((x($_SERVER,'QUERY_STRING')) && substr($_SERVER['QUERY_STRING'],0,2) === "q=")
$_SERVER['QUERY_STRING'] = substr($_SERVER['QUERY_STRING'],2); $this->query_string = substr($_SERVER['QUERY_STRING'],2);
if(x($_GET,'q')) if(x($_GET,'q'))
$this->cmd = trim($_GET['q'],'/'); $this->cmd = trim($_GET['q'],'/');
@ -881,7 +884,7 @@ function hex2bin($s) {
if(! function_exists('paginate')) { if(! function_exists('paginate')) {
function paginate(&$a) { function paginate(&$a) {
$o = ''; $o = '';
$stripped = preg_replace('/(&page=[0-9]*)/','',$_SERVER['QUERY_STRING']); $stripped = preg_replace('/(&page=[0-9]*)/','',$a->query_string);
$stripped = str_replace('q=','',$stripped); $stripped = str_replace('q=','',$stripped);
$stripped = trim($stripped,'/'); $stripped = trim($stripped,'/');
$url = $a->get_baseurl() . '/' . $stripped; $url = $a->get_baseurl() . '/' . $stripped;
@ -1040,6 +1043,96 @@ function set_config($family,$key,$value) {
return $ret; return $ret;
}} }}
if(! function_exists('get_pconfig')) {
function get_pconfig($uid,$family, $key, $instore = false) {
global $a;
if(! $instore) {
if(isset($a->config[$uid][$family][$key])) {
if($a->config[$uid][$family][$key] === '!<unset>!') {
return false;
}
return $a->config[$uid][$family][$key];
}
}
$ret = q("SELECT `v` FROM `pconfig` WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s' LIMIT 1",
intval($uid),
dbesc($family),
dbesc($key)
);
if(count($ret)) {
$a->config[$uid][$family][$key] = $ret[0]['v'];
return $ret[0]['v'];
}
else {
$a->config[$uid][$family][$key] = '!<unset>!';
}
return false;
}}
if(! function_exists('del_config')) {
function del_config($family,$key) {
global $a;
if(x($a->config[$family],$key))
unset($a->config[$family][$key]);
$ret = q("DELETE FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1",
dbesc($cat),
dbesc($key)
);
return $ret;
}}
// Same as above functions except these are for personal config storage and take an
// additional $uid argument.
if(! function_exists('set_pconfig')) {
function set_pconfig($uid,$family,$key,$value) {
global $a;
$a->config[$uid][$family][$key] = $value;
if(get_pconfig($uid,$family,$key,true) === false) {
$ret = q("INSERT INTO `pconfig` ( `uid`, `cat`, `k`, `v` ) VALUES ( %d, '%s', '%s', '%s' ) ",
intval($uid),
dbesc($family),
dbesc($key),
dbesc($value)
);
if($ret)
return $value;
return $ret;
}
$ret = q("UPDATE `pconfig` SET `v` = '%s' WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s' LIMIT 1",
intval($uid),
dbesc($value),
dbesc($family),
dbesc($key)
);
if($ret)
return $value;
return $ret;
}}
if(! function_exists('del_pconfig')) {
function del_pconfig($uid,$family,$key) {
global $a;
if(x($a->config[$uid][$family],$key))
unset($a->config[$uid][$family][$key]);
$ret = q("DELETE FROM `pconfig` WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s' LIMIT 1",
intval($uid),
dbesc($cat),
dbesc($key)
);
return $ret;
}}
// convert an XML document to a normalised, case-corrected array // convert an XML document to a normalised, case-corrected array
// used by webfinger // used by webfinger
@ -1654,12 +1747,34 @@ function aes_encrypt($val,$ky)
return mcrypt_encrypt($enc, $key, $val, $mode, mcrypt_create_iv( mcrypt_get_iv_size($enc, $mode), MCRYPT_DEV_URANDOM)); return mcrypt_encrypt($enc, $key, $val, $mode, mcrypt_create_iv( mcrypt_get_iv_size($enc, $mode), MCRYPT_DEV_URANDOM));
}} }}
/**
*
* Function: linkify
*
* Replace naked text hyperlink with HTML formatted hyperlink
*
*/
if(! function_exists('linkify')) { if(! function_exists('linkify')) {
function linkify($s) { function linkify($s) {
$s = preg_replace("/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\.\=\_\~\#\'\%]*)/", ' <a href="$1" >$1</a>', $s); $s = preg_replace("/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\.\=\_\~\#\'\%]*)/", ' <a href="$1" >$1</a>', $s);
return($s); return($s);
}} }}
/**
*
* Function: smilies
*
* Description:
* Replaces text emoticons with graphical images
*
* @Parameter: string $s
*
* Returns string
*/
if(! function_exists('smilies')) { if(! function_exists('smilies')) {
function smilies($s) { function smilies($s) {
$a = get_app(); $a = get_app();
@ -1739,14 +1854,95 @@ function profile_load(&$a, $nickname, $profile = 0) {
$a->profile = $r[0]; $a->profile = $r[0];
$a->page['template'] = 'profile';
$a->page['title'] = $a->profile['name']; $a->page['title'] = $a->profile['name'];
$_SESSION['theme'] = $a->profile['theme']; $_SESSION['theme'] = $a->profile['theme'];
if(! (x($a->page,'aside'))) if(! (x($a->page,'aside')))
$a->page['aside'] = ''; $a->page['aside'] = '';
$a->page['aside'] .= profile_sidebar($a->profile);
$a->page['aside'] .= contact_block(); $a->page['aside'] .= contact_block();
return; return;
}} }}
/**
*
* Function: profile_sidebar
*
* Formats a profile for display in the sidebar.
* It is very difficult to templatise the HTML completely
* because of all the conditional logic.
*
* @parameter: array $profile
*
* Returns HTML string stuitable for sidebar inclusion
* Exceptions: Returns empty string if passed $profile is wrong type or not populated
*
*/
if(! function_exists('profile_sidebar')) {
function profile_sidebar($profile) {
$o = '';
$location = '';
$address = false;
if((! is_array($profile)) && (! count($profile)))
return $o;
$fullname = '<div class="fn">' . $profile['name'] . '</div>';
$tabs = '';
$photo = '<div id="profile=photo-wrapper"><img class="photo" src="' . $profile['photo'] . '" alt="' . $profile['name'] . '" /></div>';
$connect = (($profile['uid'] != local_user()) ? '<li><a id="dfrn-request-link" href="dfrn_request/' . $profile['nickname'] . '">' . t('Connect') . '</a></li>' : '');
if((x($profile,'address') == 1)
|| (x($profile,'locality') == 1)
|| (x($profile,'region') == 1)
|| (x($profile,'postal-code') == 1)
|| (x($profile,'country-name') == 1))
$address = true;
if($address) {
$location .= '<div class="location"><span class="location-label">' . t('Location:') . '</span> <div class="adr">';
$location .= ((x($profile,'address') == 1) ? '<div class="street-address">' . $profile['address'] . '</div>' : '');
$location .= (((x($profile,'locality') == 1) || (x($profile,'region') == 1) || (x($profile,'postal-code') == 1))
? '<span class="city-state-zip"><span class="locality">' . $profile['locality'] . '</span>'
. ((x($profile['locality']) == 1) ? t(', ') : '')
. '<span class="region">' . $profile['region'] . '</span>'
. ' <span class="postal-code">' . $profile['postal-code'] . '</span></span>' : '');
$location .= ((x($profile,'country-name') == 1) ? ' <span class="country-name">' . $profile['country-name'] . '</span>' : '');
$location .= '</div></div><div class="profile-clear"></div>';
}
$gender = ((x($profile,'gender') == 1) ? '<div class="mf"><span class="gender-label">' . t('Gender:') . '</span> <span class="x-gender">' . $profile['gender'] . '</span></div><div class="profile-clear"></div>' : '');
$pubkey = ((x($profile,'key') == 1) ? '<div class="key" style="display:none;">' . $profile['pubkey'] . '</div>' : '');
$marital = ((x($profile,'marital') == 1) ? '<div class="marital"><span class="marital-label"><span class="heart">&hearts;</span> ' . t('Status:') . ' </span><span class="marital-text">' . $profile['marital'] . '</span></div></div><div class="profile-clear"></div>' : '');
$homepage = ((x($profile,'homepage') == 1) ? '<div class="homepage"><span class="homepage-label">' . t('Homepage:') . ' </span><span class="homepage-url">' . linkify($profile['homepage']) . '</span></div></div><div class="profile-clear"></div>' : '');
$tpl = load_view_file('view/profile_vcard.tpl');
$o .= replace_macros($tpl, array(
'$fullname' => $fullname,
'$tabs' => $tabs,
'$photo' => $photo,
'$connect' => $connect,
'$location' => $location,
'$gender' => $gender,
'$pubkey' => $pubkey,
'$marital' => $marital,
'$homepage' => $homepage
));
return $o;
}}

View File

@ -419,3 +419,13 @@ CREATE TABLE IF NOT EXISTS `queue` (
`last` DATETIME NOT NULL , `last` DATETIME NOT NULL ,
`content` MEDIUMTEXT NOT NULL `content` MEDIUMTEXT NOT NULL
) ENGINE = MYISAM DEFAULT CHARSET=utf8; ) ENGINE = MYISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `pconfig` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`uid` INT NOT NULL DEFAULT '0',
`cat` CHAR( 255 ) NOT NULL ,
`k` CHAR( 255 ) NOT NULL ,
`v` MEDIUMTEXT NOT NULL
) ENGINE = MYISAM DEFAULT CHARSET=utf8;

View File

@ -55,7 +55,7 @@
if($banner === false) if($banner === false)
$banner .= '<img id="logo-img" src="images/ff-32.jpg" alt="logo" /><span id="logo-text">Friendika</span>'; $banner .= '<a href="http://friendika.com"><img id="logo-img" src="images/ff-32.jpg" alt="logo" /></a><span id="logo-text">Friendika</span>';
$a->page['nav'] .= '<span id="banner">' . $banner . '</span>'; $a->page['nav'] .= '<span id="banner">' . $banner . '</span>';

View File

@ -131,6 +131,7 @@ if(strlen($a->module)) {
$a->module_loaded = true; $a->module_loaded = true;
} }
else { else {
logger('index.php: page not found: ' . $_SERVER['REQUEST_URI'] . ' QUERY: ' . $_SERVER['QUERY_STRING'], LOGGER_DEBUG);
header($_SERVER["SERVER_PROTOCOL"] . ' 404 ' . t('Not Found')); header($_SERVER["SERVER_PROTOCOL"] . ' 404 ' . t('Not Found'));
notice( t('Page not found.' ) . EOL); notice( t('Page not found.' ) . EOL);
} }

View File

@ -1,5 +1,14 @@
<?php <?php
/**
*
* Module: dfrn_request
*
* Purpose: Handles communication associated with the issuance of
* friend requests.
*
*/
if(! function_exists('dfrn_request_init')) { if(! function_exists('dfrn_request_init')) {
function dfrn_request_init(&$a) { function dfrn_request_init(&$a) {
@ -7,11 +16,26 @@ function dfrn_request_init(&$a) {
$which = $a->argv[1]; $which = $a->argv[1];
profile_load($a,$which); profile_load($a,$which);
return; return;
}} }}
/**
* 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.
*
*/
if(! function_exists('dfrn_request_post')) { if(! function_exists('dfrn_request_post')) {
function dfrn_request_post(&$a) { function dfrn_request_post(&$a) {
@ -24,25 +48,35 @@ function dfrn_request_post(&$a) {
} }
// We've introduced ourself to another cell, then have been returned to our own cell /**
// to confirm the request, and then we've clicked submit (perhaps after logging in). *
// That brings us here: * Scenario 2: We've introduced ourself to another cell, then have been returned to our own cell
* to confirm the request, and then we've clicked submit (perhaps after logging in).
* That brings us here:
*
*/
if((x($_POST,'localconfirm')) && ($_POST['localconfirm'] == 1)) { if((x($_POST,'localconfirm')) && ($_POST['localconfirm'] == 1)) {
// Ensure this is a valid request /**
* Ensure this is a valid request
*/
if(local_user() && ($a->user['nickname'] == $a->argv[1]) && (x($_POST,'dfrn_url'))) { if(local_user() && ($a->user['nickname'] == $a->argv[1]) && (x($_POST,'dfrn_url'))) {
$dfrn_url = notags(trim($_POST['dfrn_url'])); $dfrn_url = notags(trim($_POST['dfrn_url']));
$aes_allow = (((x($_POST,'aes_allow')) && ($_POST['aes_allow'] == 1)) ? 1 : 0); $aes_allow = (((x($_POST,'aes_allow')) && ($_POST['aes_allow'] == 1)) ? 1 : 0);
$confirm_key = ((x($_POST,'confirm_key')) ? $_POST['confirm_key'] : ""); $confirm_key = ((x($_POST,'confirm_key')) ? $_POST['confirm_key'] : "");
$contact_record = null; $contact_record = null;
if(x($dfrn_url)) { if(x($dfrn_url)) {
/**
* Lookup the contact based on their URL (which is the only unique thing we have at the moment)
*/
$r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `self` = 0 LIMIT 1", $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `self` = 0 LIMIT 1",
intval(local_user()), intval(local_user()),
dbesc($dfrn_url) dbesc($dfrn_url)
@ -50,6 +84,11 @@ function dfrn_request_post(&$a) {
if(count($r)) { if(count($r)) {
if(strlen($r[0]['dfrn-id'])) { if(strlen($r[0]['dfrn-id'])) {
/**
* We don't need to be here. It has already happened.
*/
notice( t("This introduction has already been accepted.") . EOL ); notice( t("This introduction has already been accepted.") . EOL );
return; return;
} }
@ -65,8 +104,11 @@ function dfrn_request_post(&$a) {
} }
else { else {
require_once('Scrape.php'); /**
* Scrape the other site's profile page to pick up the dfrn links, key, fn, and photo
*/
require_once('Scrape.php');
$parms = scrape_dfrn($dfrn_url); $parms = scrape_dfrn($dfrn_url);
@ -88,12 +130,17 @@ function dfrn_request_post(&$a) {
} }
} }
$dfrn_request = $parms['dfrn-request']; $dfrn_request = $parms['dfrn-request'];
/********* Escape the entire array ********/
dbesc_array($parms); dbesc_array($parms);
/******************************************/
/**
* Create a contact record on our site for the other person
*/
$r = q("INSERT INTO `contact` ( `uid`, `created`,`url`, `name`, `nick`, `photo`, `site-pubkey`, $r = q("INSERT INTO `contact` ( `uid`, `created`,`url`, `name`, `nick`, `photo`, `site-pubkey`,
`request`, `confirm`, `notify`, `poll`, `aes_allow`) `request`, `confirm`, `notify`, `poll`, `aes_allow`)
@ -117,14 +164,18 @@ function dfrn_request_post(&$a) {
notice( t("Introduction complete.") . EOL); notice( t("Introduction complete.") . EOL);
} }
// Allow the blocked remote notification to complete /**
* Allow the blocked remote notification to complete
*/
if(is_array($contact_record)) if(is_array($contact_record))
$dfrn_request = $contact_record['request']; $dfrn_request = $contact_record['request'];
if(strlen($dfrn_request) && strlen($confirm_key)) if(strlen($dfrn_request) && strlen($confirm_key))
$s = fetch_url($dfrn_request . '?confirm_key=' . $confirm_key); $s = fetch_url($dfrn_request . '?confirm_key=' . $confirm_key);
// ignore reply
// (ignore reply, nothing we can do it failed)
goaway($dfrn_url); goaway($dfrn_url);
return; // NOTREACHED return; // NOTREACHED
@ -139,23 +190,27 @@ function dfrn_request_post(&$a) {
return; // NOTREACHED return; // NOTREACHED
} }
// Otherwise: /**
* Otherwise:
// 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 * Scenario 1:
// figure out how to contact their cell. * 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
// Scrape the originating DFRN-URL for everything we need. Create a contact record * figure out how to contact their cell.
// 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. * Scrape the originating DFRN-URL for everything we need. Create a contact record
// If our user (the requestee) later confirms this request, a record of it will need * and an introduction to show our user next time he/she logs in.
// to exist on the requestor's cell in order for the confirmation process to complete.. * Finally redirect back to the requestor so that their site can record the request.
* If our user (the requestee) later confirms this request, a record of it will need
// It's possible that neither the requestor or the requestee are logged in at the moment, * to exist on the requestor's cell in order for the confirmation process to complete..
// and the requestor does not yet have any credentials to the requestee profile. *
* It's possible that neither the requestor or the requestee are logged in at the moment,
// Who is the requestee? We've already loaded their profile which means their nickname should be * and the requestor does not yet have any credentials to the requestee profile.
// in $a->argv[1] and we should have their complete info in $a->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.
*
*/
if(! (is_array($a->profile) && count($a->profile))) { if(! (is_array($a->profile) && count($a->profile))) {
notice( t('Profile unavailable.') . EOL); notice( t('Profile unavailable.') . EOL);
@ -343,14 +398,14 @@ function dfrn_request_post(&$a) {
elseif($network === 'stat') { elseif($network === 'stat') {
/** /**
* *
* OStatus network * OStatus network
* Check contact existence * Check contact existence
* Try and scrape together enough information to create a contact record, with us as REL_VIP * Try and scrape together enough information to create a contact record, with us as REL_VIP
* Substitute our user's feed URL into $url template * Substitute our user's feed URL into $url template
* Send the subscriber home to subscribe * Send the subscriber home to subscribe
* *
**/ */
$url = str_replace('{uri}', $a->get_baseurl() . '/dfrn_poll/' . $nickname, $url); $url = str_replace('{uri}', $a->get_baseurl() . '/dfrn_poll/' . $nickname, $url);
goaway($url); goaway($url);

View File

@ -228,3 +228,12 @@ function update_1021() {
q("ALTER TABLE `item` ADD `private` TINYINT( 1 ) NOT NULL DEFAULT '0' AFTER `deny_gid` "); q("ALTER TABLE `item` ADD `private` TINYINT( 1 ) NOT NULL DEFAULT '0' AFTER `deny_gid` ");
} }
function update_1022() {
q("CREATE TABLE `pconfig` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`uid` INT NOT NULL DEFAULT '0',
`cat` CHAR( 255 ) NOT NULL ,
`k` CHAR( 255 ) NOT NULL ,
`v` MEDIUMTEXT NOT NULL
) ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci ");
}

26
view/profile_vcard.tpl Normal file
View File

@ -0,0 +1,26 @@
<div class="vcard">
$fullname
$tabs
$photo
<div id="profile-extra-links">
<ul>
$connect
</ul>
</div>
$location
$gender
$pubkey
</div>
$marital
$homepage

View File

@ -188,7 +188,7 @@ img.photo {
.heart { .heart {
color: #FF0000; color: #FF0000;
font-size: 120%; font-size: 100%;
} }
aside { aside {
@ -1894,3 +1894,20 @@ a.mail-list-link {
#search-box { #search-box {
margin-bottom: 25px; margin-bottom: 25px;
} }
.location-label, .gender-label, .marital-label, .homepage-label {
float: left;
text-align: right;
display: block;
width: 65px;
}
.adr, .x-gender, .marital-text, .homepage-url {
float: left;
display: block;
margin-left: 8px;
}
.profile-clear {
clear: both;
}