diff --git a/README b/README index 9538dbe7a1..f35b8c0cf9 100644 --- a/README +++ b/README @@ -1,18 +1,24 @@ - 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 -where your privacy is always under your control? + http://friendika.com -What if this social network could scale to encompass the entire internet, and -*not* require a central organisation to provide servers (in exchange for +Did you ever wonder what might happen if you ever left Facebook? + +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)? Look no further. - + Friendika is a social network without boundaries. Friendika installations can link together into a global social network which is free from central control. Besides the Friendika network [which is privacy enhanced]; you can @@ -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 (straight-forward and simple). Whether you're communicating with drinking 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 several thousand) people using commodity hosting hardware. Each of these diff --git a/boot.php b/boot.php index 4625f64f0f..1546429c35 100644 --- a/boot.php +++ b/boot.php @@ -2,7 +2,7 @@ set_time_limit(0); -define ( 'BUILD_ID', 1022 ); +define ( 'BUILD_ID', 1023 ); define ( 'DFRN_PROTOCOL_VERSION', '2.0' ); define ( 'EOL', "
\r\n" ); @@ -157,6 +157,7 @@ if(! class_exists('App')) { class App { public $module_loaded = false; + public $query_string; public $config; public $page; public $profile; @@ -189,6 +190,8 @@ class App { $this->page = array(); $this->pager= array(); + $this->query_string = ''; + $this->scheme = ((isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'])) ? 'https' : 'http' ); if(x($_SERVER,'SERVER_NAME')) @@ -197,7 +200,7 @@ class App { set_include_path("include/$this->hostname" . PATH_SEPARATOR . 'include' . PATH_SEPARATOR . '.' ); 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')) $this->cmd = trim($_GET['q'],'/'); @@ -881,7 +884,7 @@ function hex2bin($s) { if(! function_exists('paginate')) { function paginate(&$a) { $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 = trim($stripped,'/'); $url = $a->get_baseurl() . '/' . $stripped; @@ -1040,6 +1043,96 @@ function set_config($family,$key,$value) { 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] === '!!') { + 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] = '!!'; + } + 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 // 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)); }} + +/** + * + * Function: linkify + * + * Replace naked text hyperlink with HTML formatted hyperlink + * + */ + if(! function_exists('linkify')) { function linkify($s) { $s = preg_replace("/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\.\=\_\~\#\'\%]*)/", ' $1', $s); return($s); }} + +/** + * + * Function: smilies + * + * Description: + * Replaces text emoticons with graphical images + * + * @Parameter: string $s + * + * Returns string + */ + if(! function_exists('smilies')) { function smilies($s) { $a = get_app(); @@ -1739,14 +1854,95 @@ function profile_load(&$a, $nickname, $profile = 0) { $a->profile = $r[0]; - $a->page['template'] = 'profile'; $a->page['title'] = $a->profile['name']; $_SESSION['theme'] = $a->profile['theme']; if(! (x($a->page,'aside'))) $a->page['aside'] = ''; + + $a->page['aside'] .= profile_sidebar($a->profile); $a->page['aside'] .= contact_block(); 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 = '
' . $profile['name'] . '
'; + + $tabs = ''; + + $photo = '
' . $profile['name'] . '
'; + + $connect = (($profile['uid'] != local_user()) ? '
  • ' . t('Connect') . '
  • ' : ''); + + 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 .= '
    ' . t('Location:') . '
    '; + $location .= ((x($profile,'address') == 1) ? '
    ' . $profile['address'] . '
    ' : ''); + $location .= (((x($profile,'locality') == 1) || (x($profile,'region') == 1) || (x($profile,'postal-code') == 1)) + ? '' . $profile['locality'] . '' + . ((x($profile['locality']) == 1) ? t(', ') : '') + . '' . $profile['region'] . '' + . ' ' . $profile['postal-code'] . '' : ''); + $location .= ((x($profile,'country-name') == 1) ? ' ' . $profile['country-name'] . '' : ''); + $location .= '
    '; + + } + + $gender = ((x($profile,'gender') == 1) ? '
    ' . t('Gender:') . ' ' . $profile['gender'] . '
    ' : ''); + + $pubkey = ((x($profile,'key') == 1) ? '' : ''); + + $marital = ((x($profile,'marital') == 1) ? '
    ' . t('Status:') . ' ' . $profile['marital'] . '
    ' : ''); + + $homepage = ((x($profile,'homepage') == 1) ? '
    ' . t('Homepage:') . ' ' . linkify($profile['homepage']) . '
    ' : ''); + + $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; +}} \ No newline at end of file diff --git a/database.sql b/database.sql index 0b322e9e02..148a8d0d85 100644 --- a/database.sql +++ b/database.sql @@ -419,3 +419,13 @@ CREATE TABLE IF NOT EXISTS `queue` ( `last` DATETIME NOT NULL , `content` MEDIUMTEXT NOT NULL ) 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; + + diff --git a/include/nav.php b/include/nav.php index 77cffc3ab1..f7c5c4e01d 100644 --- a/include/nav.php +++ b/include/nav.php @@ -55,7 +55,7 @@ if($banner === false) - $banner .= 'logoFriendika'; + $banner .= 'logoFriendika'; $a->page['nav'] .= ''; diff --git a/index.php b/index.php index e91603a30a..2f56d54143 100644 --- a/index.php +++ b/index.php @@ -131,6 +131,7 @@ if(strlen($a->module)) { $a->module_loaded = true; } else { + logger('index.php: page not found: ' . $_SERVER['REQUEST_URI'] . ' QUERY: ' . $_SERVER['QUERY_STRING'], LOGGER_DEBUG); header($_SERVER["SERVER_PROTOCOL"] . ' 404 ' . t('Not Found')); notice( t('Page not found.' ) . EOL); } diff --git a/mod/dfrn_request.php b/mod/dfrn_request.php index e1370dfd82..65a2c771ed 100644 --- a/mod/dfrn_request.php +++ b/mod/dfrn_request.php @@ -1,5 +1,14 @@ argv[1]; profile_load($a,$which); - 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')) { function dfrn_request_post(&$a) { @@ -24,24 +48,34 @@ 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)) { - // 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'))) { - $dfrn_url = notags(trim($_POST['dfrn_url'])); - $aes_allow = (((x($_POST,'aes_allow')) && ($_POST['aes_allow'] == 1)) ? 1 : 0); + $dfrn_url = notags(trim($_POST['dfrn_url'])); + $aes_allow = (((x($_POST,'aes_allow')) && ($_POST['aes_allow'] == 1)) ? 1 : 0); $confirm_key = ((x($_POST,'confirm_key')) ? $_POST['confirm_key'] : ""); $contact_record = null; 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", intval(local_user()), @@ -50,6 +84,11 @@ function dfrn_request_post(&$a) { if(count($r)) { 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 ); return; } @@ -65,9 +104,12 @@ function dfrn_request_post(&$a) { } else { + /** + * 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); if(! count($parms)) { @@ -88,12 +130,17 @@ function dfrn_request_post(&$a) { } } - - $dfrn_request = $parms['dfrn-request']; + /********* Escape the entire array ********/ + 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`, `request`, `confirm`, `notify`, `poll`, `aes_allow`) @@ -117,14 +164,18 @@ function dfrn_request_post(&$a) { notice( t("Introduction complete.") . EOL); } - // Allow the blocked remote notification to complete + /** + * Allow the blocked remote notification to complete + */ if(is_array($contact_record)) $dfrn_request = $contact_record['request']; if(strlen($dfrn_request) && strlen($confirm_key)) $s = fetch_url($dfrn_request . '?confirm_key=' . $confirm_key); - // ignore reply + + // (ignore reply, nothing we can do it failed) + goaway($dfrn_url); return; // NOTREACHED @@ -139,23 +190,27 @@ function dfrn_request_post(&$a) { return; // NOTREACHED } - // 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 - // figure out how to contact their cell. - - // 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. - // 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.. - - // 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. + /** + * Otherwise: + * + * Scenario 1: + * 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. + * + * 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. + * 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.. + * + * 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. + * + */ if(! (is_array($a->profile) && count($a->profile))) { notice( t('Profile unavailable.') . EOL); @@ -343,14 +398,14 @@ function dfrn_request_post(&$a) { elseif($network === 'stat') { /** - * - * OStatus network - * Check contact existence - * 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 - * Send the subscriber home to subscribe - * - **/ + * + * OStatus network + * Check contact existence + * 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 + * Send the subscriber home to subscribe + * + */ $url = str_replace('{uri}', $a->get_baseurl() . '/dfrn_poll/' . $nickname, $url); goaway($url); diff --git a/update.php b/update.php index 92b2407984..b3fab6720b 100644 --- a/update.php +++ b/update.php @@ -228,3 +228,12 @@ function update_1021() { 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 "); +} diff --git a/view/profile_vcard.tpl b/view/profile_vcard.tpl new file mode 100644 index 0000000000..81659af6a0 --- /dev/null +++ b/view/profile_vcard.tpl @@ -0,0 +1,26 @@ +
    + + $fullname + + $tabs + + $photo + + + + $location + + $gender + + $pubkey + +
    + +$marital + +$homepage + diff --git a/view/theme/default/style.css b/view/theme/default/style.css index f20341dd69..491486794f 100644 --- a/view/theme/default/style.css +++ b/view/theme/default/style.css @@ -188,7 +188,7 @@ img.photo { .heart { color: #FF0000; - font-size: 120%; + font-size: 100%; } aside { @@ -1893,4 +1893,21 @@ a.mail-list-link { #search-box { margin-bottom: 25px; -} \ No newline at end of file +} + +.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; +}