From f1115a103158fc22fd8983cb767f811f64810bc2 Mon Sep 17 00:00:00 2001 From: Friendika Date: Mon, 25 Jul 2011 00:17:46 -0700 Subject: [PATCH 001/123] revup --- boot.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boot.php b/boot.php index d365122951..e8dccd5f30 100644 --- a/boot.php +++ b/boot.php @@ -1,6 +1,6 @@ Date: Mon, 25 Jul 2011 15:43:28 -0700 Subject: [PATCH 002/123] provide ability to use different port --- boot.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/boot.php b/boot.php index e8dccd5f30..a72fbf6734 100644 --- a/boot.php +++ b/boot.php @@ -1,6 +1,6 @@ hostname = $_SERVER['SERVER_NAME']; - + if(x($_SERVER,'SERVER_PORT') && $_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) + $this->hostname .= ':' . $_SERVER['SERVER_PORT']; /** * Figure out if we are running at the top of a domain * or in a sub-directory and adjust accordingly From c0749f18d62e9d9fa53a3a60ba580dadffc0ab1b Mon Sep 17 00:00:00 2001 From: Friendika Date: Mon, 25 Jul 2011 18:41:48 -0700 Subject: [PATCH 003/123] event ownership issues --- include/conversation.php | 4 ++-- include/notifier.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/conversation.php b/include/conversation.php index 82a107c07c..50032cd367 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -347,7 +347,7 @@ function conversation(&$a, $items, $mode, $update) { if(($toplevelpost) && (! $item['self']) && ($mode !== 'profile')) { - if($item['type'] === 'wall') { + if($item['wall']) { // On the network page, I am the owner. On the display page it will be the profile owner. // This will have been stored in $a->page_contact by our calling page. @@ -359,7 +359,7 @@ function conversation(&$a, $items, $mode, $update) { $template = $wallwall; $commentww = 'ww'; } - if(($item['type'] === 'remote') && (strlen($item['owner-link'])) && ($item['owner-link'] != $item['author-link'])) { + if((! $item['wall']) && (strlen($item['owner-link'])) && ($item['owner-link'] != $item['author-link'])) { // Could be anybody. diff --git a/include/notifier.php b/include/notifier.php index 59e5737622..77a37b5eac 100644 --- a/include/notifier.php +++ b/include/notifier.php @@ -145,7 +145,7 @@ function notifier_run($argv, $argc){ $parent = $items[0]; - if($parent['type'] === 'remote' && (! $expire)) { + if($parent['wall'] != 0 && (! $expire)) { // local followup to remote post $followup = true; $notify_hub = false; // not public From ec52010e1662cd37640096b65d60fd26fbe6c172 Mon Sep 17 00:00:00 2001 From: Friendika Date: Mon, 25 Jul 2011 19:57:17 -0700 Subject: [PATCH 004/123] helper functions for Diaspora cert mangling --- include/certfns.php | 52 +++++++++++ library/ASNValue.class.php | 172 +++++++++++++++++++++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100644 include/certfns.php create mode 100644 library/ASNValue.class.php diff --git a/include/certfns.php b/include/certfns.php new file mode 100644 index 0000000000..70d2b54a0e --- /dev/null +++ b/include/certfns.php @@ -0,0 +1,52 @@ +SetIntBuffer($Modulus); + $publicExponent = new ASNValue(ASNValue::TAG_INTEGER); + $publicExponent->SetInt($PublicExponent); + $keySequenceItems = array($modulus, $publicExponent); + $keySequence = new ASNValue(ASNValue::TAG_SEQUENCE); + $keySequence->SetSequence($keySequenceItems); + //Encode bit string + $bitStringValue = $keySequence->Encode(); + $bitStringValue = chr(0x00) . $bitStringValue; //Add unused bits byte + $bitString = new ASNValue(ASNValue::TAG_BITSTRING); + $bitString->Value = $bitStringValue; + //Encode body + $bodyValue = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00" . $bitString->Encode(); + $body = new ASNValue(ASNValue::TAG_SEQUENCE); + $body->Value = $bodyValue; + //Get DER encoded public key: + $PublicDER = $body->Encode(); + return $PublicDER; +} + + +function metopem($m,$e) { + $der = pkcs8_emcode($m,$e); + $key = DerToPem($der,true); + return $key; +} + + diff --git a/library/ASNValue.class.php b/library/ASNValue.class.php new file mode 100644 index 0000000000..8a6aced97d --- /dev/null +++ b/library/ASNValue.class.php @@ -0,0 +1,172 @@ +Tag = $Tag; + $this->Value = $Value; + } + + function Encode() + { + //Write type + $result = chr($this->Tag); + + //Write size + $size = strlen($this->Value); + if ($size < 127) { + //Write size as is + $result .= chr($size); + } + else { + //Prepare length sequence + $sizeBuf = self::IntToBin($size); + + //Write length sequence + $firstByte = 0x80 + strlen($sizeBuf); + $result .= chr($firstByte) . $sizeBuf; + } + + //Write value + $result .= $this->Value; + + return $result; + } + + function Decode(&$Buffer) + { + //Read type + $this->Tag = self::ReadByte($Buffer); + + //Read first byte + $firstByte = self::ReadByte($Buffer); + + if ($firstByte < 127) { + $size = $firstByte; + } + else if ($firstByte > 127) { + $sizeLen = $firstByte - 0x80; + //Read length sequence + $size = self::BinToInt(self::ReadBytes($Buffer, $sizeLen)); + } + else { + throw new Exception("Invalid ASN length value"); + } + + $this->Value = self::ReadBytes($Buffer, $size); + } + + protected static function ReadBytes(&$Buffer, $Length) + { + $result = substr($Buffer, 0, $Length); + $Buffer = substr($Buffer, $Length); + + return $result; + } + + protected static function ReadByte(&$Buffer) + { + return ord(self::ReadBytes($Buffer, 1)); + } + + protected static function BinToInt($Bin) + { + $len = strlen($Bin); + $result = 0; + for ($i=0; $i<$len; $i++) { + $curByte = self::ReadByte($Bin); + $result += $curByte << (($len-$i-1)*8); + } + + return $result; + } + + protected static function IntToBin($Int) + { + $result = ''; + do { + $curByte = $Int % 256; + $result .= chr($curByte); + + $Int = ($Int - $curByte) / 256; + } while ($Int > 0); + + $result = strrev($result); + + return $result; + } + + function SetIntBuffer($Value) + { + if (strlen($Value) > 1) { + $firstByte = ord($Value{0}); + if ($firstByte & 0x80) { //first bit set + $Value = chr(0x00) . $Value; + } + } + + $this->Value = $Value; + } + + function GetIntBuffer() + { + $result = $this->Value; + if (ord($result{0}) == 0x00) { + $result = substr($result, 1); + } + + return $result; + } + + function SetInt($Value) + { + $Value = self::IntToBin($Value); + + $this->SetIntBuffer($Value); + } + + function GetInt() + { + $result = $this->GetIntBuffer(); + $result = self::BinToInt($result); + + return $result; + } + + function SetSequence($Values) + { + $result = ''; + foreach ($Values as $item) { + $result .= $item->Encode(); + } + + $this->Value = $result; + } + + function GetSequence() + { + $result = array(); + $seq = $this->Value; + while (strlen($seq)) { + $val = new ASNValue(); + $val->Decode($seq); + $result[] = $val; + } + + return $result; + } +} + + +?> From 06802dd8feefc5778b73c2e910be3e13e28c59bc Mon Sep 17 00:00:00 2001 From: Friendika Date: Mon, 25 Jul 2011 20:10:21 -0700 Subject: [PATCH 005/123] auto focus jot editor --- view/jot-header.tpl | 1 + 1 file changed, 1 insertion(+) diff --git a/view/jot-header.tpl b/view/jot-header.tpl index 795267ee30..6195c5bbc6 100644 --- a/view/jot-header.tpl +++ b/view/jot-header.tpl @@ -11,6 +11,7 @@ function initEditor(cb){ theme : "advanced", mode : "specific_textareas", editor_selector: /(profile-jot-text|prvmail-text)/, + auto_focus: "profile-jot-text", plugins : "bbcode,paste,autoresize", theme_advanced_buttons1 : "bold,italic,underline,undo,redo,link,unlink,image,forecolor,formatselect,code", theme_advanced_buttons2 : "", From 1441fce04eb0a95756661e54cbc22dff2e275c21 Mon Sep 17 00:00:00 2001 From: Friendika Date: Mon, 25 Jul 2011 20:21:16 -0700 Subject: [PATCH 006/123] ignore any fully numeric hash tag --- boot.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boot.php b/boot.php index a72fbf6734..ae6c1bd090 100644 --- a/boot.php +++ b/boot.php @@ -2075,7 +2075,7 @@ function get_tags($s) { continue; } // ignore strictly numeric tags like #1 - if((strpos($mtch,'#') === 0) && ctype_digit(substr($mtch,1))) + if((strpos($mtch,'#') === 0) && ctype_digit($mtch)) continue; if(substr($mtch,-1,1) === '.') $ret[] = substr($mtch,0,-1); From 454ff3c7f0a4729dbf9a9dd116325630a193f0fb Mon Sep 17 00:00:00 2001 From: Friendika Date: Mon, 25 Jul 2011 20:59:25 -0700 Subject: [PATCH 007/123] configurable format for date input selectors --- include/datetime.php | 107 +++++++++++++++++++++++++++++++------------ mod/events.php | 12 +++-- mod/profiles.php | 5 +- 3 files changed, 90 insertions(+), 34 deletions(-) diff --git a/include/datetime.php b/include/datetime.php index a056eaa60e..3033b88afc 100644 --- a/include/datetime.php +++ b/include/datetime.php @@ -84,12 +84,47 @@ function datetime_convert($from = 'UTC', $to = 'UTC', $s = 'now', $fmt = "Y-m-d function dob($dob) { list($year,$month,$day) = sscanf($dob,'%4d-%2d-%2d'); $y = datetime_convert('UTC',date_default_timezone_get(),'now','Y'); - $o = datesel('',1920,$y,true,$year,$month,$day); + $f = get_config('system','birthday_input_format'); + if(! $f) + $f = 'ymd'; + $o = datesel($f,'',1920,$y,true,$year,$month,$day); + return $o; +} + + +function datesel_format($f) { + + $o = ''; + + if(strlen($f)) { + for($x = 0; $x < strlen($f); $x ++) { + switch($f[$x]) { + case 'y': + if(strlen($o)) + $o .= '-'; + $o .= t('year'); + break; + case 'm': + if(strlen($o)) + $o .= '-'; + $o .= t('month'); + break; + case 'd': + if(strlen($o)) + $o .= '-'; + $o .= t('day'); + break; + default: + break; + } + } + } return $o; } // returns a date selector. +// $f = format string, e.g. 'ymd' or 'mdy' // $pre = prefix (if needed) for HTML name and class fields // $ymin = first year shown in selector dropdown // $ymax = last year shown in selector dropdown @@ -99,40 +134,52 @@ function dob($dob) { // $d = already selected day if(! function_exists('datesel')) { -function datesel($pre,$ymin,$ymax,$allow_blank,$y,$m,$d) { +function datesel($f,$pre,$ymin,$ymax,$allow_blank,$y,$m,$d) { $o = ''; - $o .= ""; + if($allow_blank) { + $sel = (($y == '0000') ? " selected=\"selected\" " : ""); + $o .= ""; + } + + if($ymax > $ymin) { + for($x = $ymax; $x >= $ymin; $x --) { + $sel = (($x == $y) ? " selected=\"selected\" " : ""); + $o .= ""; + } + } + else { + for($x = $ymax; $x <= $ymin; $x ++) { + $sel = (($x == $y) ? " selected=\"selected\" " : ""); + $o .= ""; + } + } + } + elseif($f[$z] == 'm') { - $o .= " "; diff --git a/mod/events.php b/mod/events.php index b0b54601fd..27ca698307 100644 --- a/mod/events.php +++ b/mod/events.php @@ -297,6 +297,12 @@ function events_content(&$a) { $fhour = ((x($orig_event)) ? datetime_convert('UTC', $tz, $fdt, 'H') : 0); $fminute = ((x($orig_event)) ? datetime_convert('UTC', $tz, $fdt, 'i') : 0); + $f = get_config('system','event_input_format'); + if(! $f) + $f = 'ymd'; + + $dateformat = datesel_format($f); + $timeformat = t('hour:minute'); require_once('include/acl_selectors.php'); @@ -306,14 +312,14 @@ function events_content(&$a) { '$cid' => $cid, '$uri' => $uri, '$e_text' => t('Event details'), - '$e_desc' => t('Format is year-month-day hour:minute. Starting date and Description are required.'), + '$e_desc' => sprintf( t('Format is %s %s. Starting date and Description are required.'),$dateformat,$timeformat), '$s_text' => t('Event Starts:') . ' * ', - '$s_dsel' => datesel('start',$syear+5,$syear,false,$syear,$smonth,$sday), + '$s_dsel' => datesel($f,'start',$syear+5,$syear,false,$syear,$smonth,$sday), '$s_tsel' => timesel('start',$shour,$sminute), '$n_text' => t('Finish date/time is not known or not relevant'), '$n_checked' => $n_checked, '$f_text' => t('Event Finishes:'), - '$f_dsel' => datesel('finish',$fyear+5,$fyear,false,$fyear,$fmonth,$fday), + '$f_dsel' => datesel($f,'finish',$fyear+5,$fyear,false,$fyear,$fmonth,$fday), '$f_tsel' => timesel('finish',$fhour,$fminute), '$a_text' => t('Adjust for viewer timezone'), '$a_checked' => $a_checked, diff --git a/mod/profiles.php b/mod/profiles.php index 160ca13bcd..64fab797f6 100644 --- a/mod/profiles.php +++ b/mod/profiles.php @@ -361,6 +361,9 @@ function profiles_content(&$a) { $a->page['htmlhead'] .= replace_macros($tpl, array('$baseurl' => $a->get_baseurl())); $a->page['htmlhead'] .= ""; + $f = get_config('system','birthday_input_format'); + if(! $f) + $f = 'ymd'; $is_default = (($r[0]['is-default']) ? 1 : 0); $tpl = get_markup_template("profile_edit.tpl"); @@ -375,7 +378,7 @@ function profiles_content(&$a) { '$lbl_fullname' => t('Your Full Name:'), '$lbl_title' => t('Title/Description:'), '$lbl_gender' => t('Your Gender:'), - '$lbl_bd' => t("Birthday \x28y/m/d\x29:"), + '$lbl_bd' => sprintf( t("Birthday \x28%s\x29:"),datesel_format($f)), '$lbl_address' => t('Street Address:'), '$lbl_city' => t('Locality/City:'), '$lbl_zip' => t('Postal/Zip Code:'), From 94e027af672cb641c70093e62f54356da0c05ccb Mon Sep 17 00:00:00 2001 From: Friendika Date: Tue, 26 Jul 2011 19:20:29 -0700 Subject: [PATCH 008/123] deal with browser pre-fetch of the ACL img template '{0}' --- boot.php | 2 +- index.php | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/boot.php b/boot.php index ae6c1bd090..4c8c1c4d2f 100644 --- a/boot.php +++ b/boot.php @@ -1,6 +1,6 @@ module)) { */ if(! $a->module_loaded) { + + // Stupid browser tried to pre-fetch our ACL img template. Don't log the event or return anything - just quietly exit. + if((x($_SERVER,'QUERY_STRING')) && strpos($_SERVER['QUERY_STRING'],'{0}') !== false) { + killme(); + } + if((x($_SERVER,'QUERY_STRING')) && ($_SERVER['QUERY_STRING'] === 'q=internal_error.html') && isset($dreamhost_error_hack)) { logger('index.php: dreamhost_error_hack invoked. Original URI =' . $_SERVER['REQUEST_URI']); goaway($a->get_baseurl() . $_SERVER['REQUEST_URI']); From c17890ab6211f9d5095fcecbd0ce439ec5d67916 Mon Sep 17 00:00:00 2001 From: Friendika Date: Tue, 26 Jul 2011 22:40:11 -0700 Subject: [PATCH 009/123] privacy settings on event item not propagated correctly --- include/event.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/event.php b/include/event.php index aab195d245..3100c8ac49 100644 --- a/include/event.php +++ b/include/event.php @@ -341,10 +341,10 @@ function event_store($arr) { $item_arr['author-link'] = $contact['url']; $item_arr['author-avatar'] = $contact['thumb']; $item_arr['title'] = ''; - $item_arr['allow_cid'] = $str_contact_allow; - $item_arr['allow_gid'] = $str_group_allow; - $item_arr['deny_cid'] = $str_contact_deny; - $item_arr['deny_gid'] = $str_group_deny; + $item_arr['allow_cid'] = $arr['allow_cid']; + $item_arr['allow_gid'] = $arr['allow_gid']; + $item_arr['deny_cid'] = $arr['deny_cid']; + $item_arr['deny_gid'] = $arr['deny_gid']; $item_arr['last-child'] = 1; $item_arr['visible'] = 1; $item_arr['verb'] = ACTIVITY_POST; From e6c5afdf4f34e45e4f9b1d9ab1990595851a879b Mon Sep 17 00:00:00 2001 From: Friendika Date: Tue, 26 Jul 2011 22:57:48 -0700 Subject: [PATCH 010/123] privacy issue introduced yesterday --- include/notifier.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/notifier.php b/include/notifier.php index 77a37b5eac..8cc85d3996 100644 --- a/include/notifier.php +++ b/include/notifier.php @@ -145,7 +145,7 @@ function notifier_run($argv, $argc){ $parent = $items[0]; - if($parent['wall'] != 0 && (! $expire)) { + if($parent['wall'] == 0 && (! $expire)) { // local followup to remote post $followup = true; $notify_hub = false; // not public From b721fabc3c1f6c01e4212e804f6ba6293c4983e1 Mon Sep 17 00:00:00 2001 From: Friendika Date: Wed, 27 Jul 2011 02:21:55 -0700 Subject: [PATCH 011/123] show lock icon on private events --- include/event.php | 5 ++++- include/items.php | 1 + mod/dfrn_notify.php | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/event.php b/include/event.php index 3100c8ac49..99f685d0bd 100644 --- a/include/event.php +++ b/include/event.php @@ -197,6 +197,7 @@ function event_store($arr) { $arr['type'] = (($arr['type']) ? $arr['type'] : 'event' ); $arr['cid'] = ((intval($arr['cid'])) ? intval($arr['cid']) : 0); $arr['uri'] = (x($arr,'uri') ? $arr['uri'] : item_new_uri($a->get_hostname(),$arr['uid'])); + $arr['private'] = ((x($arr,'private')) ? intval($arr['private']) : 0); if($arr['cid']) $c = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1", @@ -275,7 +276,7 @@ function event_store($arr) { $object .= '' . "\n"; - q("UPDATE `item` SET `body` = '%s', `object` = '%s', `allow_cid` = '%s', `allow_gid` = '%s', `deny_cid` = '%s', `deny_gid` = '%s', `edited` = '%s' WHERE `id` = %d AND `uid` = %d LIMIT 1", + q("UPDATE `item` SET `body` = '%s', `object` = '%s', `allow_cid` = '%s', `allow_gid` = '%s', `deny_cid` = '%s', `deny_gid` = '%s', `edited` = '%s', `private` = %d WHERE `id` = %d AND `uid` = %d LIMIT 1", dbesc(format_event_bbcode($arr)), dbesc($object), dbesc($arr['allow_cid']), @@ -283,6 +284,7 @@ function event_store($arr) { dbesc($arr['deny_cid']), dbesc($arr['deny_gid']), dbesc($arr['edited']), + intval($arr['private']), intval($r[0]['id']), intval($arr['uid']) ); @@ -345,6 +347,7 @@ function event_store($arr) { $item_arr['allow_gid'] = $arr['allow_gid']; $item_arr['deny_cid'] = $arr['deny_cid']; $item_arr['deny_gid'] = $arr['deny_gid']; + $item_arr['private'] = $arr['private']; $item_arr['last-child'] = 1; $item_arr['visible'] = 1; $item_arr['verb'] = ACTIVITY_POST; diff --git a/include/items.php b/include/items.php index 6593647bac..014d758721 100644 --- a/include/items.php +++ b/include/items.php @@ -1349,6 +1349,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $secure_fee $ev['uid'] = $importer['uid']; $ev['uri'] = $item_id; $ev['edited'] = $datarray['edited']; + $ev['private'] = $datarray['private']; if(is_array($contact)) $ev['cid'] = $contact['id']; diff --git a/mod/dfrn_notify.php b/mod/dfrn_notify.php index 0dc96f8a76..f284b8b389 100644 --- a/mod/dfrn_notify.php +++ b/mod/dfrn_notify.php @@ -704,6 +704,7 @@ function dfrn_notify_post(&$a) { $ev['uid'] = $importer['uid']; $ev['uri'] = $item_id; $ev['edited'] = $datarray['edited']; + $ev['private'] = $datarray['private']; $r = q("SELECT * FROM `event` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($item_id), From 822fea9d9d6ba55fca712c2067616421320e2e9f Mon Sep 17 00:00:00 2001 From: Friendika Date: Wed, 27 Jul 2011 21:48:02 -0700 Subject: [PATCH 012/123] installer text changes --- boot.php | 2 +- mod/install.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/boot.php b/boot.php index 4c8c1c4d2f..7e46d92eb4 100644 --- a/boot.php +++ b/boot.php @@ -1,6 +1,6 @@ t('Friendika Social Network'), '$lbl_02' => t('Installation'), - '$lbl_03' => t('In order to install Friendika we need to know how to contact your database.'), + '$lbl_03' => t('In order to install Friendika we need to know how to connect to your database.'), '$lbl_04' => t('Please contact your hosting provider or site administrator if you have questions about these settings.'), - '$lbl_05' => t('The database you specify below must already exist. If it does not, please create it before continuing.'), + '$lbl_05' => t('The database you specify below should already exist. If it does not, please create it before continuing.'), '$lbl_06' => t('Database Server Name'), '$lbl_07' => t('Database Login Name'), '$lbl_08' => t('Database Login Password'), '$lbl_09' => t('Database Name'), '$lbl_10' => t('Please select a default timezone for your website'), - '$lbl_11' => t('Site administrator email address. Your account email address will need match this.'), + '$lbl_11' => t('Site administrator email address. Your account email address must match this in order to use the web admin panel.'), '$baseurl' => $a->get_baseurl(), '$tzselect' => ((x($_POST,'timezone')) ? select_timezone($_POST['timezone']) : select_timezone()), '$submit' => t('Submit'), From c74f0f0cabc774b1dc1719b2ebc7409179d26ca4 Mon Sep 17 00:00:00 2001 From: Friendika Date: Thu, 28 Jul 2011 01:01:19 -0700 Subject: [PATCH 013/123] show full size photos for album view even when photo has been used as a profile photo --- mod/photos.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mod/photos.php b/mod/photos.php index 4a72bb6807..2941fa32a5 100644 --- a/mod/photos.php +++ b/mod/photos.php @@ -903,7 +903,7 @@ function photos_content(&$a) { $album = hex2bin($datum); $r = q("SELECT `resource-id`, max(`scale`) AS `scale` FROM `photo` WHERE `uid` = %d AND `album` = '%s' - $sql_extra GROUP BY `resource-id`", + AND `scale` < 4 $sql_extra GROUP BY `resource-id`", intval($owner_uid), dbesc($album) ); @@ -913,7 +913,7 @@ function photos_content(&$a) { } $r = q("SELECT `resource-id`, `id`, `filename`, max(`scale`) AS `scale`, `desc` FROM `photo` WHERE `uid` = %d AND `album` = '%s' - $sql_extra GROUP BY `resource-id` ORDER BY `created` DESC LIMIT %d , %d", + AND `scale` < 4 $sql_extra GROUP BY `resource-id` ORDER BY `created` DESC LIMIT %d , %d", intval($owner_uid), dbesc($album), intval($a->pager['start']), From ce36f0bf3dc971d600513ceedac7f73d3701b3ce Mon Sep 17 00:00:00 2001 From: Tobias Diekershoff Date: Thu, 28 Jul 2011 13:25:41 +0200 Subject: [PATCH 014/123] clearing the wordenting for the addon config --- addon/statusnet/statusnet.php | 4 ++-- addon/twitter/twitter.php | 21 +++++++++++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/addon/statusnet/statusnet.php b/addon/statusnet/statusnet.php index 062884b967..bf6cbfbc71 100644 --- a/addon/statusnet/statusnet.php +++ b/addon/statusnet/statusnet.php @@ -1,7 +1,7 @@ */ @@ -276,7 +276,7 @@ function statusnet_settings(&$a,&$s) { $connection = new StatusNetOAuth($api,$ckey,$csecret,$otoken,$osecret); $details = $connection->get('account/verify_credentials'); $s .= '

'. t('Currently connected to: ') .''.$details->screen_name.'
'.$details->description.'

'; - $s .= '

'. t('If enabled all your public postings will be posted to the associated StatusNet account.') .'

'; + $s .= '

'. t('If enabled all your public postings can be posted to the associated StatusNet account. You can choose to do so by default (here) or for every posting separately in the posting options when writing the entry.') .'

'; $s .= '
'; $s .= ''; $s .= ''; diff --git a/addon/twitter/twitter.php b/addon/twitter/twitter.php index 26b324acaa..1c5fe4e4ad 100644 --- a/addon/twitter/twitter.php +++ b/addon/twitter/twitter.php @@ -1,7 +1,7 @@ */ @@ -87,7 +87,8 @@ function twitter_settings_post ($a,$post) { */ del_pconfig( local_user(), 'twitter', 'consumerkey' ); del_pconfig( local_user(), 'twitter', 'consumersecret' ); - del_pconfig( local_user(), 'twitter', 'post' ); + del_pconfig( local_user(), 'twitter', 'post' ); + del_pconfig( local_user(), 'twitter', 'post_by_default' ); } else { if (isset($_POST['twitter-pin'])) { // if the user supplied us with a PIN from Twitter, let the magic of OAuth happen @@ -105,11 +106,13 @@ function twitter_settings_post ($a,$post) { set_pconfig(local_user(),'twitter', 'oauthsecret', $token['oauth_token_secret']); set_pconfig(local_user(),'twitter', 'post', 1); // reload the Addon Settings page, if we don't do it see Bug #42 - header('Location: '.$a->get_baseurl().'/settings/addon'); + goaway($a->get_baseurl().'/settings/addon'); } else { // if no PIN is supplied in the POST variables, the user has changed the setting // to post a tweet for every new __public__ posting to the wall set_pconfig(local_user(),'twitter','post',intval($_POST['twitter-enable'])); + set_pconfig(local_user(),'twitter','post_by_default',intval($_POST['twitter-default'])); + info( t('Twitter settings updated.') . EOL); }} } function twitter_settings(&$a,&$s) { @@ -127,6 +130,9 @@ function twitter_settings(&$a,&$s) { $osecret = get_pconfig(local_user(), 'twitter', 'oauthsecret' ); $enabled = get_pconfig(local_user(), 'twitter', 'post'); $checked = (($enabled) ? ' checked="checked" ' : ''); + $defenabled = get_pconfig(local_user(),'twitter','post_by_default'); + $defchecked = (($defenabled) ? ' checked="checked" ' : ''); + $s .= '
'; $s .= '

'. t('Twitter Posting Settings') .'

'; @@ -172,11 +178,14 @@ function twitter_settings(&$a,&$s) { $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret); $details = $connection->get('account/verify_credentials'); $s .= '

'. t('Currently connected to: ') .''.$details->screen_name.'
'.$details->description.'

'; - $s .= '

'. t('If enabled all your public postings will be posted to the associated Twitter account as well.') .'

'; + $s .= '

'. t('If enabled all your public postings can be posted to the associated Twitter account. You can choose to do so by default (here) or for every posting separately in the posting options when writing the entry.') .'

'; $s .= '
'; - $s .= ''; + $s .= ''; $s .= ''; - $s .= '
'; + $s .= '
'; + $s .= ''; + $s .= ''; + $s .= '
'; $s .= ''; $s .= ''; From ea0d5d34acc75e494374d1dc131e79915c039e06 Mon Sep 17 00:00:00 2001 From: Tobias Diekershoff Date: Thu, 28 Jul 2011 17:34:34 +0200 Subject: [PATCH 015/123] config page formatation issues fixed --- addon/twitter/twitter.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addon/twitter/twitter.php b/addon/twitter/twitter.php index 1c5fe4e4ad..c3fbc3e648 100644 --- a/addon/twitter/twitter.php +++ b/addon/twitter/twitter.php @@ -182,9 +182,10 @@ function twitter_settings(&$a,&$s) { $s .= '
'; $s .= ''; $s .= ''; - $s .= '
'; + $s .= '
'; $s .= ''; $s .= ''; + $s .= '
'; $s .= '
'; $s .= ''; From 04675e3ec3cd5951d651998489a1d04247f1026b Mon Sep 17 00:00:00 2001 From: Friendika Date: Thu, 28 Jul 2011 16:49:43 -0700 Subject: [PATCH 016/123] revup --- boot.php | 2 +- mod/photos.php | 4 ++-- view/theme/duepuntozero/style.css | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/boot.php b/boot.php index 7e46d92eb4..f7a0b50bc5 100644 --- a/boot.php +++ b/boot.php @@ -1,6 +1,6 @@ pager['start']), diff --git a/view/theme/duepuntozero/style.css b/view/theme/duepuntozero/style.css index e2b1a57dc5..28c1ce9a13 100644 --- a/view/theme/duepuntozero/style.css +++ b/view/theme/duepuntozero/style.css @@ -45,7 +45,7 @@ code { border: 1px solid #444; background: #EEE; color: #444; - padding: 0px 10px 10px 10px; + padding: 10px; margin-top: 20px; } /*blockquote:before { From 8042f874f305bdc4a02f9b39efea9c9e0f1a82a7 Mon Sep 17 00:00:00 2001 From: Friendika Date: Thu, 28 Jul 2011 18:16:57 -0700 Subject: [PATCH 017/123] more progress on key conversion functions --- include/certfns.php | 97 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 3 deletions(-) diff --git a/include/certfns.php b/include/certfns.php index 70d2b54a0e..db0e4645ef 100644 --- a/include/certfns.php +++ b/include/certfns.php @@ -19,12 +19,32 @@ function DerToPem($Der, $Private=false) return $result; } +function DerToRsa($Der) +{ + //Encode: + $Der = base64_encode($Der); + //Split lines: + $lines = str_split($Der, 65); + $body = implode("\n", $lines); + //Get title: + $title = 'RSA PUBLIC KEY'; + //Add wrapping: + $result = "-----BEGIN {$title}-----\n"; + $result .= $body . "\n"; + $result .= "-----END {$title}-----\n"; + + return $result; +} + + + + function pkcs8_encode($Modulus,$PublicExponent) { //Encode key sequence $modulus = new ASNValue(ASNValue::TAG_INTEGER); $modulus->SetIntBuffer($Modulus); $publicExponent = new ASNValue(ASNValue::TAG_INTEGER); - $publicExponent->SetInt($PublicExponent); + $publicExponent->SetIntBuffer($PublicExponent); $keySequenceItems = array($modulus, $publicExponent); $keySequence = new ASNValue(ASNValue::TAG_SEQUENCE); $keySequence->SetSequence($keySequenceItems); @@ -43,10 +63,81 @@ function pkcs8_encode($Modulus,$PublicExponent) { } +function pkcs1_encode($Modulus,$PublicExponent) { + //Encode key sequence + $modulus = new ASNValue(ASNValue::TAG_INTEGER); + $modulus->SetIntBuffer($Modulus); + $publicExponent = new ASNValue(ASNValue::TAG_INTEGER); + $publicExponent->SetIntBuffer($PublicExponent); + $keySequenceItems = array($modulus, $publicExponent); + $keySequence = new ASNValue(ASNValue::TAG_SEQUENCE); + $keySequence->SetSequence($keySequenceItems); + //Encode bit string + $bitStringValue = $keySequence->Encode(); + return $bitStringValue; + +// $bitStringValue = chr(0x00) . $bitStringValue; //Add unused bits byte +// $bitString = new ASNValue(ASNValue::TAG_BITSTRING); +// $bitString->Value = $bitStringValue; + //Encode body +// $bodyValue = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00" . $bitString->Encode(); +// $body = new ASNValue(ASNValue::TAG_SEQUENCE); +// $body->Value = $bodyValue; + //Get DER encoded public key: +// $PublicDER = $body->Encode(); +// return $PublicDER; +} + + function metopem($m,$e) { - $der = pkcs8_emcode($m,$e); - $key = DerToPem($der,true); + $der = pkcs8_encode($m,$e); + $key = DerToPem($der,false); return $key; } +function pubrsatome($key,&$m,&$e) { + require_once('library/asn1.php'); + require_once('include/salmon.php'); + + $lines = explode("\n",$key); + unset($lines[0]); + unset($lines[count($lines)]); + $x = base64_decode(implode('',$lines)); + + $r = ASN_BASE::parseASNString($x); + +// print_r($r); + + $m = base64url_decode($r[0]->asnData[0]->asnData); + $e = base64url_decode($r[0]->asnData[1]->asnData); + + +} + + +function rsatopem($key) { + pubrsatome($key,$m,$e); + return(metopem($m,$e)); +} + + +function pemtome($key,&$m,&$e) { + require_once('include/salmon.php'); + $lines = explode("\n",$key); + unset($lines[0]); + unset($lines[count($lines)]); + $x = base64_decode(implode('',$lines)); + + $r = ASN_BASE::parseASNString($x); + + $m = base64url_decode($r[0]->asnData[1]->asnData[0]->asnData[0]->asnData); + $e = base64url_decode($r[0]->asnData[1]->asnData[0]->asnData[1]->asnData); +} + +function metorsa($m,$e) { + $der = pkcs1_encode($m,$e); + $key = DerToRsa($der); + return $key; +} + From a45b94033eaba3bbddec701c3f87310a6d2a18fe Mon Sep 17 00:00:00 2001 From: Friendika Date: Thu, 28 Jul 2011 19:08:38 -0700 Subject: [PATCH 018/123] issue with remote mentions --- include/notifier.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/notifier.php b/include/notifier.php index 8cc85d3996..9f5b271482 100644 --- a/include/notifier.php +++ b/include/notifier.php @@ -504,7 +504,7 @@ function notifier_run($argv, $argc){ // send additional slaps to mentioned remote tags (@foo@example.com) - if($slap && count($url_recipients) && $followup && $notify_hub && (! $expire)) { + if($slap && count($url_recipients) && ($followup || $top_level) && $notify_hub && (! $expire)) { if(! get_config('system','dfrn_only')) { foreach($url_recipients as $url) { if($url) { From 9b2ddb2cc28de56f3f4cbbd1d5ff77da72a01e73 Mon Sep 17 00:00:00 2001 From: Friendika Date: Thu, 28 Jul 2011 19:18:07 -0700 Subject: [PATCH 019/123] auto-friend profiles showing up in directory as community pages --- mod/profile.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/profile.php b/mod/profile.php index f4b6bfaf1c..71912458f1 100644 --- a/mod/profile.php +++ b/mod/profile.php @@ -21,7 +21,7 @@ function profile_init(&$a) { profile_load($a,$which,$profile); - if((x($a->profile,'page-flags')) && ($a->profile['page-flags'] & PAGE_COMMUNITY)) { + if((x($a->profile,'page-flags')) && ($a->profile['page-flags'] == PAGE_COMMUNITY)) { $a->page['htmlhead'] .= ''; } if(x($a->profile,'openidserver')) From 41c7d71e3b8cc73a8c1e7e79e63753eb99ea6b24 Mon Sep 17 00:00:00 2001 From: Friendika Date: Thu, 28 Jul 2011 20:44:35 -0700 Subject: [PATCH 020/123] make profile-jot-desc track lock state changes --- include/acl.js | 4 ++++ view/theme/duepuntozero/style.css | 1 + 2 files changed, 5 insertions(+) diff --git a/include/acl.js b/include/acl.js index a0a1f5dd82..82b631ee94 100644 --- a/include/acl.js +++ b/include/acl.js @@ -153,6 +153,9 @@ ACL.prototype.updateview = function(){ $('#jot-perms-icon').removeClass('lock').addClass('unlock'); $('#jot-public').show(); $('.profile-jot-net input').attr('disabled', false); + if(editor != false) { + $('#profile-jot-desc').html(ispublic); + } } else { that.showall.removeClass("selected"); @@ -160,6 +163,7 @@ ACL.prototype.updateview = function(){ $('#jot-perms-icon').removeClass('unlock').addClass('lock'); $('#jot-public').hide(); $('.profile-jot-net input').attr('disabled', 'disabled'); + $('#profile-jot-desc').html(' '); } $("#acl-list-content .acl-list-item").each(function(){ diff --git a/view/theme/duepuntozero/style.css b/view/theme/duepuntozero/style.css index 28c1ce9a13..3edc603f40 100644 --- a/view/theme/duepuntozero/style.css +++ b/view/theme/duepuntozero/style.css @@ -1487,6 +1487,7 @@ input#dfrn-url { height: 20px; color:#cccccc; border: 1px solid #cccccc; + padding: 3px 0px 0px 5px; } From 8e30f6a2f43d5654a1ecab595be9d801dbc64b3c Mon Sep 17 00:00:00 2001 From: Friendika Date: Thu, 28 Jul 2011 21:05:56 -0700 Subject: [PATCH 021/123] one more try - do not link numeric hash tags --- boot.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boot.php b/boot.php index f7a0b50bc5..956cb2ca64 100644 --- a/boot.php +++ b/boot.php @@ -2075,7 +2075,7 @@ function get_tags($s) { continue; } // ignore strictly numeric tags like #1 - if((strpos($mtch,'#') === 0) && ctype_digit($mtch)) + if((strpos($mtch,'#') === 0) && ctype_digit(substr($mtch,1))) continue; if(substr($mtch,-1,1) === '.') $ret[] = substr($mtch,0,-1); From f47d582736ddf24528dd514850d4bed76783f589 Mon Sep 17 00:00:00 2001 From: Friendika Date: Thu, 28 Jul 2011 21:56:56 -0700 Subject: [PATCH 022/123] api/statusnet/config.xml --- include/api.php | 27 ++++++++++++++++++++++ view/{apiconfig.tpl => api_config_xml.tpl} | 19 ++++++++------- 2 files changed, 36 insertions(+), 10 deletions(-) rename view/{apiconfig.tpl => api_config_xml.tpl} (75%) diff --git a/include/api.php b/include/api.php index 4e5ea43bdb..d7c0124d71 100644 --- a/include/api.php +++ b/include/api.php @@ -550,3 +550,30 @@ } api_register_func('api/account/rate_limit_status','api_account_rate_limit_status',true); + + + function api_statusnet_config(&$a,$type) { + $name = $a->config['sitename']; + $server = $a->get_hostname(); + $logo = $a->get_baseurl() . '/images/friendika-64.png'; + $email = $a->config['admin_email']; + $closed = (($a->config['register_policy'] == REGISTER_CLOSED) ? 'true' : 'false'); + $private = (($a->config['system']['block_public']) ? 'true' : 'false'); + $textlimit = (($a->config['max_import_size']) ? $a->config['max_import_size'] : '200000'); + $ssl = (($a->config['system']['have_ssl']) ? 'true' : 'false'); + $sslserver = (($ssl === 'true') ? str_replace('http:','https:',$a->get_baseurl()) : ''); + + $config = array( + 'site' => array('name' => $name,'server' => $server, 'theme' => 'default', 'path' => '', + 'logo' => $logo, 'fancy' => 'true', 'language' => 'en', 'email' => $email, 'broughtby' => '', + 'broughtbyurl' => '', 'timezone' => 'UTC', 'closed' => $closed, 'inviteonly' => 'false', + 'private' => $private, 'textlimit' => $textlimit, 'sslserver' => $sslserver, 'ssl' => $ssl, + 'shorturllength' => '30' + ), + ); + + return api_apply_template('config', $type, array('$config' => $config)); + + } + api_register_func('api/statusnet/config','api_statusnet_config',true); + diff --git a/view/apiconfig.tpl b/view/api_config_xml.tpl similarity index 75% rename from view/apiconfig.tpl rename to view/api_config_xml.tpl index 71ab241ce0..3281e59dd3 100644 --- a/view/apiconfig.tpl +++ b/view/api_config_xml.tpl @@ -1,25 +1,24 @@ - - $sitedesc - $sitename + $config.site.name + $config.site.server default - $sitelogo + $config.site.logo true en - $adminemail + $config.site.email UTC - $siteclosed + $config.site.closed false - $siteprivate - $textlimit - $has_ssl - $ssl_server + $config.site.private + $config.site.textlimit + $config.site.ssl + $config.site.sslserver 30 From 0111a1231cfa90a2aee5b18fc4b8c3dde9f48b81 Mon Sep 17 00:00:00 2001 From: Fabio Comuni Date: Fri, 29 Jul 2011 17:14:35 +0200 Subject: [PATCH 023/123] re-add pending registrations in ping.php, for admin side menu --- mod/ping.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mod/ping.php b/mod/ping.php index 7c31f00c9c..924f9ff76b 100644 --- a/mod/ping.php +++ b/mod/ping.php @@ -31,11 +31,18 @@ function ping_init(&$a) { intval(local_user()), dbesc($myurl) ); - $mail = $r[0]['total']; + + if ($a->config['register_policy'] == REGISTER_APPROVE && is_site_admin()){ + $r = q("SELECT COUNT(*) AS `total` FROM `register`"); + $register = $r[0]['total']; + } else { + $register = "0"; + } + header("Content-type: text/xml"); - echo "\r\n$intro$mail$network$home\r\n"; + echo "\r\n$intro$mail$network$home$register\r\n"; killme(); } From b1e766dadb1d4a9e727b2d1d5f32442392ac6bd3 Mon Sep 17 00:00:00 2001 From: Fabio Comuni Date: Fri, 29 Jul 2011 17:21:02 +0200 Subject: [PATCH 024/123] allow GET or POST data in statuses/update --- include/api.php | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/include/api.php b/include/api.php index 4e5ea43bdb..5ebf04bb99 100644 --- a/include/api.php +++ b/include/api.php @@ -323,20 +323,31 @@ api_register_func('api/account/verify_credentials','api_account_verify_credentials', true); + /** + * get data from $_POST or $_GET + */ + function requestdata($k){ + if (isset($_POST[$k])){ + return $_POST[$k]; + } + if (isset($_GET[$k])){ + return $_GET[$k]; + } + return null; + } // TODO - media uploads - function api_statuses_update(&$a, $type) { if (local_user()===false) return false; $user_info = api_get_user($a); // convert $_POST array items to the form we use for web posts. - $_POST['body'] = urldecode($_POST['status']); - $_POST['parent'] = $_POST['in_reply_to_status_id']; - if($_POST['lat'] && $_POST['long']) - $_POST['coord'] = sprintf("%s %s",$_POST['lat'],$_POST['long']); + $_POST['body'] = urldecode(requestdata('status')); + $_POST['parent'] = requestdata('in_reply_to_status_id'); + if(requestdata('lat') && requestdata('long')) + $_POST['coord'] = sprintf("%s %s",requestdata('lat'),requestdata('long')); $_POST['profile_uid'] = local_user(); - if($_POST['parent']) + if(requestdata('parent')) $_POST['type'] = 'net-comment'; else $_POST['type'] = 'wall'; From ef47a1cf973a66bb22c1d72dc2a3130db9324e1f Mon Sep 17 00:00:00 2001 From: Friendika Date: Fri, 29 Jul 2011 16:05:16 -0700 Subject: [PATCH 025/123] revup --- boot.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boot.php b/boot.php index 956cb2ca64..68cda3bf2f 100644 --- a/boot.php +++ b/boot.php @@ -1,6 +1,6 @@ Date: Fri, 29 Jul 2011 17:01:57 -0700 Subject: [PATCH 026/123] api/statusnet/config does not require login --- include/api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/api.php b/include/api.php index bc981646d3..df15965e7f 100644 --- a/include/api.php +++ b/include/api.php @@ -586,5 +586,5 @@ return api_apply_template('config', $type, array('$config' => $config)); } - api_register_func('api/statusnet/config','api_statusnet_config',true); + api_register_func('api/statusnet/config','api_statusnet_config',false); From dbc328368187bca4c0365ab56db57437dfda9f38 Mon Sep 17 00:00:00 2001 From: Friendika Date: Fri, 29 Jul 2011 18:21:54 -0700 Subject: [PATCH 027/123] api/statusnet/version --- include/api.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/include/api.php b/include/api.php index df15965e7f..2b1a8b0f5a 100644 --- a/include/api.php +++ b/include/api.php @@ -588,3 +588,20 @@ } api_register_func('api/statusnet/config','api_statusnet_config',false); + + function api_statusnet_version(&$a,$type) { + + // liar + + if($type === 'xml') { + header("Content-type: application/xml"); + echo '' . "\r\n" . '0.9.7' . "\r\n"; + killme(); + } + elseif($type === 'json') { + header("Content-type: application/json"); + echo '"0.9.7"'; + killme(); + } + } + api_register_func('api/statusnet/version','api_statusnet_version',false); From a61ec584d60013f7845d0999deb550a2149f4e76 Mon Sep 17 00:00:00 2001 From: Friendika Date: Sat, 30 Jul 2011 00:31:00 -0700 Subject: [PATCH 028/123] basic diaspora decryption --- boot.php | 17 ++++++- mod/receive.php | 130 +++++++++++++++++++++++++++++++++++++----------- 2 files changed, 117 insertions(+), 30 deletions(-) diff --git a/boot.php b/boot.php index 68cda3bf2f..5d4f547696 100644 --- a/boot.php +++ b/boot.php @@ -2969,4 +2969,19 @@ function generate_guid() { $found = false; } while ($found == true ); return $guid; -} \ No newline at end of file +} + + +function pkcs5_pad ($text, $blocksize) +{ + $pad = $blocksize - (strlen($text) % $blocksize); + return $text . str_repeat(chr($pad), $pad); +} + +function pkcs5_unpad($text) +{ + $pad = ord($text{strlen($text)-1}); + if ($pad > strlen($text)) return false; + if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false; + return substr($text, 0, -1 * $pad); +} diff --git a/mod/receive.php b/mod/receive.php index 6fb68e1545..18f323f422 100644 --- a/mod/receive.php +++ b/mod/receive.php @@ -43,10 +43,68 @@ function receive_post(&$a) { if(! $xml) receive_return(500); - // parse the xml - $dom = simplexml_load_string($xml,'SimpleXMLElement',0,NAMESPACE_SALMON_ME); + $basedom = parse_xml_string($xml); + if($basedom) + logger('parsed dom'); + + $atom = $basedom->children(NAMESPACE_ATOM1); + + logger('atom: ' . count($atom)); + $encrypted_header = json_decode(base64_decode($atom->encrypted_header)); + + print_r($encrypted_header); + + $encrypted_aes_key_bundle = base64_decode($encrypted_header->aes_key); + $ciphertext = base64_decode($encrypted_header->ciphertext); + + logger('encrypted_aes: ' . print_r($encrypted_aes_key_bundle,true)); + logger('ciphertext: ' . print_r($ciphertext,true)); + + $outer_key_bundle = ''; + openssl_private_decrypt($encrypted_aes_key_bundle,$outer_key_bundle,$localprvkey); + + logger('outer_bundle: ' . print_r($outer_key_bundle,true)); + + $j_outer_key_bundle = json_decode($outer_key_bundle); + + $outer_iv = base64_decode($j_outer_key_bundle->iv); + $outer_key = base64_decode($j_outer_key_bundle->key); + + $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $outer_key, $ciphertext, MCRYPT_MODE_CBC, $outer_iv); + + $decrypted = pkcs5_unpad($decrypted); + + logger('decrypted: ' . print_r($decrypted,true)); + + /** + * $decrypted now contains something like + * + * + * 8e+G2+ET8l5BPuW0sVTnQw== + * UvSMb4puPeB14STkcDWq+4QE302Edu15oaprAQSkLKU= + * + * Ryan Hughes + * acct:galaxor@diaspora.pirateship.org + * + * + */ + + $idom = parse_xml_string($decrypted,false); + + print_r($idom); + $inner_iv = base64_decode($idom->iv); + $inner_aes_key = base64_decode($idom->aes_key); + + logger('inner_iv: ' . $inner_iv); + + $dom = $basedom->children(NAMESPACE_SALMON_ME); + + if($dom) + logger('have dom'); + + logger('dom: ' . count($dom)); // figure out where in the DOM tree our data is hiding if($dom->provenance->data) @@ -58,12 +116,22 @@ function receive_post(&$a) { if(! $base) { logger('mod-diaspora: unable to locate salmon data in xml '); - receive_return(400); + dt_return(400); } + // Stash the signature away for now. We have to find their key or it won't be good for anything. $signature = base64url_decode($base->sig); + logger('signature: ' . bin2hex($signature)); + + openssl_public_encrypt('test',$rrr,$ryanpubkey); + logger('rrr: ' . $rrr); + + $pubdecsig = ''; + openssl_public_decrypt($signature,$pubdecsig,$ryanpubkey); + logger('decsig: ' . bin2hex($pubdecsig)); + // unpack the data // strip whitespace so our data element will return to one big base64 blob @@ -76,40 +144,28 @@ function receive_post(&$a) { $encoding = $base->encoding; $alg = $base->alg; - $signed_data = $data . '.' . base64url_encode($type) . '.' . base64url_encode($encoding) . '.' . base64url_encode($alg); + $signed_data = $data . "\n" . '.' . base64url_encode($type) . "\n" . '.' . base64url_encode($encoding) . "\n" . '.' . base64url_encode($alg) . "\n"; + + logger('signed data: ' . $signed_data); // decode the data $data = base64url_decode($data); - // Remove the xml declaration - $data = preg_replace('/\<\?xml[^\?].*\?\>/','',$data); + // Now pull out the inner encrypted blob - // Create a fake feed wrapper so simplepie doesn't choke - $tpl = get_markup_template('fake_feed.tpl'); - - $base = substr($data,strpos($data,''; - logger('mod-diaspora: Processed feed: ' . $feedxml); + $inner_encrypted = base64_decode($data); - // Now parse it like a normal atom feed to scrape out the author URI - - $feed = new SimplePie(); - $feed->set_raw_data($feedxml); - $feed->enable_order_by_date(false); - $feed->init(); + $inner_decrypted = + $inner_decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $inner_aes_key, $inner_encrypted, MCRYPT_MODE_CBC, $inner_iv); + + $inner_decrypted = pkcs5_unpad($inner_decrypted); + + logger('inner_decrypted: ' . $inner_decrypted); - logger('mod-diaspora: Feed parsed.'); - if($feed->get_item_quantity()) { - foreach($feed->get_items() as $item) { - $author = $item->get_author(); - $author_link = unxmlify($author->get_link()); - break; - } - } if(! $author_link) { logger('mod-diaspora: Could not retrieve author URI.'); @@ -117,17 +173,25 @@ function receive_post(&$a) { } // Once we have the author URI, go to the web and try to find their public key + // *** or look it up locally *** - logger('mod-salmon: Fetching key for ' . $author_link ); + logger('mod-diaspora: Fetching key for ' . $author_link ); +// Get diaspora public key (pkcs#1) and convert to pkcs#8 +// $key = get_diaspora_key($author_link); - $key = get_salmon_key($author_link,$keyhash); +// $key = get_salmon_key($author_link,$keyhash); if(! $key) { logger('mod-salmon: Could not retrieve author key.'); receive_return(400); } +// FIXME +// Use non salmon compliant signature + +/* + // Setup RSA stuff to verify the signature set_include_path(get_include_path() . PATH_SEPARATOR . 'library' . PATH_SEPARATOR . 'phpsec'); @@ -155,6 +219,7 @@ function receive_post(&$a) { logger('mod-diaspora: Message did not verify. Discarding.'); receive_return(400); } +*/ logger('mod-diaspora: Message verified.'); @@ -204,7 +269,14 @@ function receive_post(&$a) { $contact_rec = ((count($r)) ? $r[0] : null); - consume_feed($feedxml,$importer,$contact_rec,$hub); + + + +// figure out what kind of diaspora message we have, and process accordingly. + + + + receive_return(200); } From 01703f3be6fae07d9fbb24f1b57b8c181cb59df9 Mon Sep 17 00:00:00 2001 From: Friendika Date: Sat, 30 Jul 2011 00:51:59 -0700 Subject: [PATCH 029/123] get_diaspora_key() --- mod/receive.php | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/mod/receive.php b/mod/receive.php index 18f323f422..86d612dc99 100644 --- a/mod/receive.php +++ b/mod/receive.php @@ -6,7 +6,7 @@ require_once('include/salmon.php'); -require_once('library/simplepie/simplepie.inc'); +require_once('include/certfns.php'); function receive_return($val) { @@ -21,6 +21,30 @@ function receive_return($val) { } + +function get_diaspora_key($uri) { + $key = ''; + + logger('Fetching diaspora key for: ' . $uri); + + $arr = lrdd($uri); + + if(is_array($arr)) { + foreach($arr as $a) { + if($a['@attributes']['rel'] === 'diaspora-public-key') { + $key = base64_decode($a['@attributes']['href']); + } + } + } + else { + return ''; + } + + if($key) + return rsatopem($key); + return ''; +} + function receive_post(&$a) { if($a->argc != 3 || $a->argv[1] !== 'users') @@ -125,12 +149,12 @@ function receive_post(&$a) { logger('signature: ' . bin2hex($signature)); - openssl_public_encrypt('test',$rrr,$ryanpubkey); - logger('rrr: ' . $rrr); +// openssl_public_encrypt('test',$rrr,$rpubkey); +// logger('rrr: ' . $rrr); - $pubdecsig = ''; - openssl_public_decrypt($signature,$pubdecsig,$ryanpubkey); - logger('decsig: ' . bin2hex($pubdecsig)); +// $pubdecsig = ''; +// openssl_public_decrypt($signature,$pubdecsig,$rpubkey); +// logger('decsig: ' . bin2hex($pubdecsig)); // unpack the data @@ -178,9 +202,7 @@ function receive_post(&$a) { logger('mod-diaspora: Fetching key for ' . $author_link ); // Get diaspora public key (pkcs#1) and convert to pkcs#8 -// $key = get_diaspora_key($author_link); - -// $key = get_salmon_key($author_link,$keyhash); + $key = get_diaspora_key($author_link); if(! $key) { logger('mod-salmon: Could not retrieve author key.'); From 60caffcd7776043ee89e5939b81d7bd9d948cd4e Mon Sep 17 00:00:00 2001 From: Friendika Date: Sat, 30 Jul 2011 01:03:24 -0700 Subject: [PATCH 030/123] convert our native pkcs#8 to pkcs#1 for diaspora-public-key xrd field --- include/certfns.php | 4 ++++ mod/xrd.php | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/certfns.php b/include/certfns.php index db0e4645ef..ffdc7f0c34 100644 --- a/include/certfns.php +++ b/include/certfns.php @@ -121,6 +121,10 @@ function rsatopem($key) { return(metopem($m,$e)); } +function pemtorsa($key) { + pemtome($key,$m,$e); + return(metorsa($m,$e)); +} function pemtome($key,&$m,&$e) { require_once('include/salmon.php'); diff --git a/mod/xrd.php b/mod/xrd.php index accc2f68ef..4889639f07 100644 --- a/mod/xrd.php +++ b/mod/xrd.php @@ -1,6 +1,7 @@ $a->get_baseurl(), '$dspr_guid' => $r[0]['guid'], - '$dspr_key' => base64_encode($r[0]['pubkey']) + '$dspr_key' => base64_encode(pemtorsa($r[0]['pubkey'])) )); } else From 149d52da8b49893426a584b14b2a48f81d32113b Mon Sep 17 00:00:00 2001 From: Friendika Date: Sat, 30 Jul 2011 20:48:55 -0700 Subject: [PATCH 031/123] show correct self profile in api --- boot.php | 2 +- include/api.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/boot.php b/boot.php index 5d4f547696..cdde7652c6 100644 --- a/boot.php +++ b/boot.php @@ -1,6 +1,6 @@ Date: Sun, 31 Jul 2011 00:53:46 -0700 Subject: [PATCH 032/123] get attribution correct in api items --- include/api.php | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/include/api.php b/include/api.php index 31bf0624bd..b8dbdd2d32 100644 --- a/include/api.php +++ b/include/api.php @@ -168,9 +168,10 @@ /** * Returns user info array. */ - function api_get_user(&$a, $contact_id=Null){ + function api_get_user(&$a, $contact_id = Null){ $user = null; $extra_query = ""; + if(!is_null($contact_id)){ $user=$contact_id; $extra_query = "AND `contact`.`id` = %d "; @@ -275,6 +276,48 @@ } + function api_item_get_user(&$a, $item) { + if(link_compare($item['url'],$item['author-link'])) + return api_get_user($a,$item['cid']); + $ret = array( + 'uid' => 0, + 'id' => 0, + 'name' => $item['author-name'], + 'screen_name' => '', + 'location' => '', //$uinfo[0]['default-location'], + 'profile_image_url' => $item['author-avatar'], + 'url' => $item['author-link'], + 'contact_url' => 0, + 'protected' => false, # + 'friends_count' => 0, + 'created_at' => '0000-00-00 00:00:00', + 'utc_offset' => 0, #XXX: fix me + 'time_zone' => '', //$uinfo[0]['timezone'], + 'geo_enabled' => false, + 'statuses_count' => 0, + 'lang' => 'en', #XXX: fix me + 'description' => '', + 'followers_count' => 0, + 'favourites_count' => 0, + 'contributors_enabled' => false, + 'follow_request_sent' => false, + 'profile_background_color' => 'cfe8f6', + 'profile_text_color' => '000000', + 'profile_link_color' => 'FF8500', + 'profile_sidebar_fill_color' =>'AD0066', + 'profile_sidebar_border_color' => 'AD0066', + 'profile_background_image_url' => '', + 'profile_background_tile' => false, + 'profile_use_background_image' => false, + 'notifications' => false, + 'verified' => true, #XXX: fix me + 'followers' => '', #XXX: fix me + #'status' => null + ); + + return $ret; + } + /** * apply xmlify() to all values of array $val, recursively */ @@ -503,7 +546,7 @@ $ret = Array(); foreach($r as $item) { - $status_user = (($item['cid']==$user_info['id'])?$user_info: api_get_user($a,$item['cid'])); + $status_user = (($item['cid']==$user_info['id'])?$user_info: api_item_get_user($a,$item)); $status = array( 'created_at'=> api_date($item['created']), 'published' => datetime_convert('UTC','UTC',$item['created'],ATOM_TIME), From 0c9f033505be5dbc2303d8758894e57c616c6534 Mon Sep 17 00:00:00 2001 From: Friendika Date: Sun, 31 Jul 2011 16:35:53 -0700 Subject: [PATCH 033/123] some api enhancements --- boot.php | 28 ++++++- include/api.php | 154 ++++++++++++++++++++++++++++++------- include/conversation.php | 29 +------ include/items.php | 2 - view/api_timeline_atom.tpl | 47 ++--------- 5 files changed, 162 insertions(+), 98 deletions(-) diff --git a/boot.php b/boot.php index cdde7652c6..025f7ef878 100644 --- a/boot.php +++ b/boot.php @@ -1,6 +1,6 @@ contacts,'empty')) + return; + + $r = q("SELECT `id`,`network`,`url`,`thumb` FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 ", + intval($uid) + ); + if(count($r)) { + foreach($r as $rr){ + $url = normalise_link($rr['url']); + $ret[$url] = $rr; + } + } + else + $ret['empty'] = true; + $a->contacts = $ret; + return; +}} diff --git a/include/api.php b/include/api.php index b8dbdd2d32..62dc785064 100644 --- a/include/api.php +++ b/include/api.php @@ -10,7 +10,6 @@ $API = Array(); - function api_date($str){ //Wed May 23 06:01:13 +0000 2007 return datetime_convert('UTC', 'UTC', $str, "D M d h:i:s +0000 Y" ); @@ -111,7 +110,10 @@ if ($info['auth']===true && local_user()===false) { api_login($a); } - + + load_contact_links(local_user()); + + logger('API call for ' . $a->user['username'] . ': ' . $a->query_string); $type="json"; if (strpos($a->query_string, ".xml")>0) $type="xml"; if (strpos($a->query_string, ".json")>0) $type="json"; @@ -157,7 +159,9 @@ $arr['$rss'] = array( 'alternate' => $user_info['url'], 'self' => $a->get_baseurl(). "/". $a->query_string, + 'base' => $a->get_baseurl(), 'updated' => api_date(null), + 'atom_updated' => datetime_convert('UTC','UTC','now',ATOM_TIME), 'language' => $user_info['language'], 'logo' => $a->get_baseurl()."/images/friendika-32.png", ); @@ -277,8 +281,18 @@ } function api_item_get_user(&$a, $item) { - if(link_compare($item['url'],$item['author-link'])) + // The author is our direct contact, in a conversation with us. + if(link_compare($item['url'],$item['author-link'])) { return api_get_user($a,$item['cid']); + } + else { + // The author may be a contact of ours, but is replying to somebody else. + // Figure out if we know him/her. + $normalised = normalise_link((strlen($item['author-link'])) ? $item['author-link'] : $item['url']); + if(($normalised != 'mailbox') && (x($a->contacts[$normalised]))) + return api_get_user($a,$a->contacts[$normalised]['id']); + } + // We don't know this person directly. $ret = array( 'uid' => 0, 'id' => 0, @@ -290,7 +304,7 @@ 'contact_url' => 0, 'protected' => false, # 'friends_count' => 0, - 'created_at' => '0000-00-00 00:00:00', + 'created_at' => '', 'utc_offset' => 0, #XXX: fix me 'time_zone' => '', //$uinfo[0]['timezone'], 'geo_enabled' => false, @@ -332,9 +346,11 @@ */ function api_apply_template($templatename, $type, $data){ + $a = get_app(); + switch($type){ - case "rss": case "atom": + case "rss": case "xml": $data = api_xmlify($data); $tpl = get_markup_template("api_".$templatename."_".$type.".tpl"); @@ -527,7 +543,7 @@ $user_info = api_get_user($a); // get last newtork messages - $sql_extra = " AND `item`.`parent` IN ( SELECT `parent` FROM `item` WHERE `id` = `parent` ) "; +// $sql_extra = " AND `item`.`parent` IN ( SELECT `parent` FROM `item` WHERE `id` = `parent` ) "; $r = q("SELECT `item`.*, `item`.`id` AS `item_id`, `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, @@ -539,10 +555,108 @@ AND `contact`.`id` = `item`.`contact-id` AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 $sql_extra - ORDER BY `item`.`created` DESC LIMIT %d ,%d ", + ORDER BY `item`.`received` DESC LIMIT %d ,%d ", intval($user_info['uid']), 0,20 ); + + $ret = api_format_items($r,$user_info); + + + $data = array('$statuses' => $ret); + switch($type){ + case "atom": + case "rss": + $data = api_rss_extra($a, $data, $user_info); + } + + return api_apply_template("timeline", $type, $data); + } + api_register_func('api/statuses/home_timeline','api_statuses_home_timeline', true); + api_register_func('api/statuses/friends_timeline','api_statuses_home_timeline', true); + + + + function api_statuses_user_timeline(&$a, $type){ + if (local_user()===false) return false; + + $user_info = api_get_user($a); + // get last newtork messages +// $sql_extra = " AND `item`.`parent` IN ( SELECT `parent` FROM `item` WHERE `id` = `parent` ) "; + + $r = q("SELECT `item`.*, `item`.`id` AS `item_id`, + `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, + `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, + `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid` + FROM `item`, `contact` + WHERE `item`.`uid` = %d + AND `item`.`visible` = 1 AND `item`.`deleted` = 0 + AND `item`.`wall` = 1 + AND `contact`.`id` = `item`.`contact-id` + AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 + $sql_extra + ORDER BY `item`.`received` DESC LIMIT %d ,%d ", + intval($user_info['uid']), + 0,20 + ); + + $ret = api_format_items($r,$user_info); + + + $data = array('$statuses' => $ret); + switch($type){ + case "atom": + case "rss": + $data = api_rss_extra($a, $data, $user_info); + } + + return api_apply_template("timeline", $type, $data); + } + + api_register_func('api/statuses/user_timeline','api_statuses_user_timeline', true); + + + function api_favorites(&$a, $type){ + if (local_user()===false) return false; + + $user_info = api_get_user($a); + // get last newtork messages +// $sql_extra = " AND `item`.`parent` IN ( SELECT `parent` FROM `item` WHERE `id` = `parent` ) "; + + $r = q("SELECT `item`.*, `item`.`id` AS `item_id`, + `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, + `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, + `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid` + FROM `item`, `contact` + WHERE `item`.`uid` = %d + AND `item`.`visible` = 1 AND `item`.`deleted` = 0 + AND `item`.`starred` = 1 + AND `contact`.`id` = `item`.`contact-id` + AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 + $sql_extra + ORDER BY `item`.`received` DESC LIMIT %d ,%d ", + intval($user_info['uid']), + 0,20 + ); + + $ret = api_format_items($r,$user_info); + + + $data = array('$statuses' => $ret); + switch($type){ + case "atom": + case "rss": + $data = api_rss_extra($a, $data, $user_info); + } + + return api_apply_template("timeline", $type, $data); + } + + api_register_func('api/favorites','api_favorites', true); + + + function api_format_items($r,$user_info) { + $a = get_app(); $ret = Array(); foreach($r as $item) { @@ -551,7 +665,7 @@ 'created_at'=> api_date($item['created']), 'published' => datetime_convert('UTC','UTC',$item['created'],ATOM_TIME), 'updated' => datetime_convert('UTC','UTC',$item['edited'],ATOM_TIME), - 'id' => $item['id'], + 'id' => $item['uri'], 'text' => strip_tags(bbcode($item['body'])), 'html' => bbcode($item['body']), 'source' => (($item['app']) ? $item['app'] : 'web'), @@ -568,28 +682,16 @@ 'annotations' => '', 'entities' => '', 'user' => $status_user , - 'objecttype' => $item['object-type'], - 'verb' => $item['verb'], - 'self' => $a->get_baseurl()."/api/statuses/show/".$ite['id'].".".$type, - 'edit' => $a->get_baseurl()."/api/statuses/show/".$ite['id'].".".$type, + 'objecttype' => (($item['object-type']) ? $item['object-type'] : ACTIVITY_OBJ_NOTE), + 'verb' => (($item['verb']) ? $item['verb'] : ACTIVITY_POST), + 'self' => $a->get_baseurl()."/api/statuses/show/".$item['id'].".".$type, + 'edit' => $a->get_baseurl()."/api/statuses/show/".$item['id'].".".$type, ); $ret[]=$status; }; - - $data = array('$statuses' => $ret); - switch($type){ - case "atom": - case "rss": - $data = api_rss_extra($a, $data, $user_info); - } - - return api_apply_template("timeline", $type, $data); + return $ret; } - api_register_func('api/statuses/home_timeline','api_statuses_home_timeline', true); - api_register_func('api/statuses/friends_timeline','api_statuses_home_timeline', true); - api_register_func('api/statuses/user_timeline','api_statuses_home_timeline', true); - # TODO: user_timeline should be profile view - + function api_account_rate_limit_status(&$a,$type) { diff --git a/include/conversation.php b/include/conversation.php index 50032cd367..6b1f649250 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -444,7 +444,7 @@ function conversation(&$a, $items, $mode, $update) { $profile_link = ''; $normalised = normalise_link((strlen($item['author-link'])) ? $item['author-link'] : $item['url']); - if(($normalised != 'mailbox') && (x($a->contacts[$normalised]))) + if(($normalised != 'mailbox') && (x($a->contacts,$normalised))) $profile_avatar = $a->contacts[$normalised]['thumb']; else $profile_avatar = (((strlen($item['author-avatar'])) && $diff_author) ? $item['author-avatar'] : $thumb); @@ -533,33 +533,6 @@ function conversation(&$a, $items, $mode, $update) { return $o; } - -if(! function_exists('load_contact_links')) { -function load_contact_links($uid) { - - $a = get_app(); - - $ret = array(); - - if(! $uid || x($a->contacts,'empty')) - return; - - $r = q("SELECT `id`,`network`,`url`,`thumb` FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 ", - intval($uid) - ); - if(count($r)) { - foreach($r as $rr){ - $url = normalise_link($rr['url']); - $ret[$url] = $rr; - } - } - else - $ret['empty'] = true; - $a->contacts = $ret; - return; -}} - - function best_link_url($item,&$sparkle) { $a = get_app(); diff --git a/include/items.php b/include/items.php index 014d758721..6d69c6cc9e 100644 --- a/include/items.php +++ b/include/items.php @@ -6,7 +6,6 @@ require_once('include/salmon.php'); function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) { - // default permissions - anonymous user if(! strlen($owner_nick)) @@ -485,7 +484,6 @@ function get_atom_elements($feed,$item) { if((x($res,'verb')) && ($res['verb'] === 'http://ostatus.org/schema/1.0/unfollow')) $res['verb'] = ACTIVITY_UNFOLLOW; - $cats = $item->get_categories(); if($cats) { $tag_arr = array(); diff --git a/view/api_timeline_atom.tpl b/view/api_timeline_atom.tpl index bd674f3302..dcabba465a 100644 --- a/view/api_timeline_atom.tpl +++ b/view/api_timeline_atom.tpl @@ -1,10 +1,10 @@ StatusNet - tag:friendika:PublicTimeline - Network on Friendika - Your network updates on Friendika + $rss.self + Friendika + Friendika API feed $rss.logo - $rss.updated + $rss.atom_updated @@ -61,20 +61,9 @@ $status.published $status.updated - - + @@ -82,10 +71,8 @@ $status.user.url $status.user.name - - - + $status.user.screen_name $status.user.name @@ -95,29 +82,7 @@ $status.user.url true - - - - http://activitystrea.ms/schema/1.0/person - $status.user.contact_url - $status.user.name - - - - - - - $status.user.screen_name - $status.user.name - - - homepage - $status.user.url - true - - - From b0a9ec0a73183e48158a4e42db49943c56db9098 Mon Sep 17 00:00:00 2001 From: Friendika Date: Sun, 31 Jul 2011 17:52:36 -0700 Subject: [PATCH 034/123] better handling of api comments/replies --- include/api.php | 10 +++++++++- mod/item.php | 25 ++++++++++++++++++++----- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/include/api.php b/include/api.php index 62dc785064..0d446d1a77 100644 --- a/include/api.php +++ b/include/api.php @@ -401,8 +401,16 @@ // convert $_POST array items to the form we use for web posts. + // logger('api_post: ' . print_r($_POST,true)); + $_POST['body'] = urldecode(requestdata('status')); - $_POST['parent'] = requestdata('in_reply_to_status_id'); + + $parent = requestdata('in_reply_to_status_id'); + if(ctype_digit($parent)) + $_POST['parent'] = $parent; + else + $_POST['parent_uri'] = $parent; + if(requestdata('lat') && requestdata('long')) $_POST['coord'] = sprintf("%s %s",requestdata('lat'),requestdata('long')); $_POST['profile_uid'] = local_user(); diff --git a/mod/item.php b/mod/item.php index f6f665a189..c4b4351c18 100644 --- a/mod/item.php +++ b/mod/item.php @@ -34,21 +34,34 @@ function item_post(&$a) { call_hooks('post_local_start', $_POST); $parent = ((x($_POST,'parent')) ? intval($_POST['parent']) : 0); + $parent_uri = ((x($_POST,'parent_uri')) ? trim($_POST['parent_uri']) : ''); $parent_item = null; $parent_contact = null; + $r = false; - if($parent) { - $r = q("SELECT * FROM `item` WHERE `id` = %d LIMIT 1", - intval($parent) - ); - if(! count($r)) { + if($parent || $parent_uri) { + if($parent) { + $r = q("SELECT * FROM `item` WHERE `id` = %d LIMIT 1", + intval($parent) + ); + } + elseif($parent_uri && local_user()) { + // This is coming from an API source, we are logged in + $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + dbesc($parent_uri), + intval(local_user()) + ); + } + + if(($r === false) || (! count($r))) { notice( t('Unable to locate original post.') . EOL); if(x($_POST,'return')) goaway($a->get_baseurl() . "/" . $_POST['return'] ); killme(); } $parent_item = $r[0]; + $parent = $r[0]['id']; if($parent_item['contact-id'] && $uid) { $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1", intval($parent_item['contact-id']), @@ -59,6 +72,8 @@ function item_post(&$a) { } } + if($parent) logger('mod_post: parent=' . $parent); + $profile_uid = ((x($_POST,'profile_uid')) ? intval($_POST['profile_uid']) : 0); $post_id = ((x($_POST['post_id'])) ? intval($_POST['post_id']) : 0); $app = ((x($_POST['source'])) ? strip_tags($_POST['source']) : ''); From 996425206f86967a50923cf61e18776749a49e52 Mon Sep 17 00:00:00 2001 From: Friendika Date: Sun, 31 Jul 2011 20:01:00 -0700 Subject: [PATCH 035/123] api profiles --- include/api.php | 59 ++++++++++++++++++++++++++++++++++++++++--------- mod/item.php | 29 ++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 13 deletions(-) diff --git a/include/api.php b/include/api.php index 0d446d1a77..45ca6f1a82 100644 --- a/include/api.php +++ b/include/api.php @@ -147,7 +147,26 @@ //echo "
"; var_dump($r); die();
 			}
 		}
-		return false;
+		$r = 'not implemented';
+		switch($type){
+			case "xml":
+				header ("Content-Type: text/xml");
+				return ''."\n".$r;
+				break;
+			case "json": 
+				header ("Content-Type: application/json");  
+			    return json_encode(array('error' => 'not implemented'));
+				break;
+			case "rss":
+				header ("Content-Type: application/rss+xml");
+				return ''."\n".$r;
+				break;
+			case "atom":
+				header ("Content-Type: application/atom+xml");
+				return ''."\n".$r;
+				break;
+				
+		}
 	}
 
 	/**
@@ -222,15 +241,25 @@
 			return False;
 		}
 		
-		// count public wall messages
-		$r = q("SELECT COUNT(`id`) as `count` FROM `item`
-				WHERE  `uid` = %d
-				AND `type`='wall' 
-				AND `allow_cid`='' AND `allow_gid`='' AND `deny_cid`='' AND `deny_gid`=''",
-				intval($uinfo[0]['uid'])
-		);
-		$countitms = $r[0]['count'];
-		
+		if($uinfo[0]['self']) {
+			// count public wall messages
+			$r = q("SELECT COUNT(`id`) as `count` FROM `item`
+					WHERE  `uid` = %d
+					AND `type`='wall' 
+					AND `allow_cid`='' AND `allow_gid`='' AND `deny_cid`='' AND `deny_gid`=''",
+					intval($uinfo[0]['uid'])
+			);
+			$countitms = $r[0]['count'];
+		}
+		else {
+			$r = q("SELECT COUNT(`id`) as `count` FROM `item`
+					WHERE  `contact-id` = %d
+					AND `allow_cid`='' AND `allow_gid`='' AND `deny_cid`='' AND `deny_gid`=''",
+					intval($uinfo[0]['id'])
+			);
+			$countitms = $r[0]['count'];
+		}
+
 		// count friends
 		$r = q("SELECT COUNT(`id`) as `count` FROM `contact`
 				WHERE  `uid` = %d
@@ -238,7 +267,10 @@
 				intval($uinfo[0]['uid'])
 		);
 		$countfriends = $r[0]['count'];
-				
+
+		if(! $uinfo[0]['self']) {
+			$countfriends = 0;
+		}
 
 		$ret = Array(
 			'uid' => $uinfo[0]['uid'],
@@ -664,6 +696,11 @@
 
 	
 	function api_format_items($r,$user_info) {
+
+		//logger('api_format_items: ' . print_r($r,true));
+
+		//logger('api_format_items: ' . print_r($user_info,true));
+
 		$a = get_app();
 		$ret = Array();
 
diff --git a/mod/item.php b/mod/item.php
index c4b4351c18..4b9f3d091f 100644
--- a/mod/item.php
+++ b/mod/item.php
@@ -33,11 +33,18 @@ function item_post(&$a) {
 
 	call_hooks('post_local_start', $_POST);
 
+
+	/**
+	 * Is this a reply to something?
+	 */
+
 	$parent = ((x($_POST,'parent')) ? intval($_POST['parent']) : 0);
 	$parent_uri = ((x($_POST,'parent_uri')) ? trim($_POST['parent_uri']) : '');
 
 	$parent_item = null;
 	$parent_contact = null;
+	$thr_parent = '';
+	$parid = 0;
 	$r = false;
 
 	if($parent || $parent_uri) {
@@ -47,11 +54,20 @@ function item_post(&$a) {
 			);
 		}
 		elseif($parent_uri && local_user()) {
+			$_POST['type'] = 'net-comment';
 			// This is coming from an API source, we are logged in
+			// This query will find the immediate parent
 			$r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
 				dbesc($parent_uri),
 				intval(local_user())
 			);
+			// now find the real parent of the conversation
+			if(count($r)) {
+				$parid = $r[0]['parent'];
+				$r = q("SELECT * FROM `item` WHERE `id` = `parent` AND `parent` = %d LIMIT 1",
+					intval($parid)
+				);
+			}
 		}
 
 		if(($r === false) || (! count($r))) {
@@ -62,6 +78,11 @@ function item_post(&$a) {
 		}
 		$parent_item = $r[0];
 		$parent = $r[0]['id'];
+
+		// multi-level threading - preserve the info but re-parent to our single level threading
+		if(($parid) && ($parid != $parent))
+			$thr_parent = $parent_uri;
+
 		if($parent_item['contact-id'] && $uid) {
 			$r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
 				intval($parent_item['contact-id']),
@@ -195,6 +216,8 @@ function item_post(&$a) {
 			$contact_record = $r[0];
 	}
 
+
+
 	$post_type = notags(trim($_POST['type']));
 
 	if($post_type === 'net-comment') {
@@ -458,6 +481,7 @@ function item_post(&$a) {
 	$datarray['private']       = $private;
 	$datarray['pubmail']       = $pubmail_enable;
 	$datarray['attach']        = $attachments;
+	$datarray['thr-parent']    = $thr_parent;
 
 	/**
 	 * These fields are for the convenience of plugins...
@@ -495,9 +519,9 @@ function item_post(&$a) {
 
 
 	$r = q("INSERT INTO `item` (`uid`,`type`,`wall`,`gravity`,`contact-id`,`owner-name`,`owner-link`,`owner-avatar`, 
-		`author-name`, `author-link`, `author-avatar`, `created`, `edited`, `received`, `changed`, `uri`, `title`, `body`, `app`, `location`, `coord`, 
+		`author-name`, `author-link`, `author-avatar`, `created`, `edited`, `received`, `changed`, `uri`, `thr-parent`, `title`, `body`, `app`, `location`, `coord`, 
 		`tag`, `inform`, `verb`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid`, `private`, `pubmail`, `attach` )
-		VALUES( %d, '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s' )",
+		VALUES( %d, '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s' )",
 		intval($datarray['uid']),
 		dbesc($datarray['type']),
 		intval($datarray['wall']),
@@ -514,6 +538,7 @@ function item_post(&$a) {
 		dbesc($datarray['received']),
 		dbesc($datarray['changed']),
 		dbesc($datarray['uri']),
+		dbesc($datarray['thr-parent']),
 		dbesc($datarray['title']),
 		dbesc($datarray['body']),
 		dbesc($datarray['app']),

From 823d7ba42ac619010fa786936ad625461e27e56d Mon Sep 17 00:00:00 2001
From: Friendika 
Date: Sun, 31 Jul 2011 22:22:34 -0700
Subject: [PATCH 036/123] api/friends/ids, api/followers/ids

---
 include/api.php | 64 +++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 60 insertions(+), 4 deletions(-)

diff --git a/include/api.php b/include/api.php
index 45ca6f1a82..7b3dd866ce 100644
--- a/include/api.php
+++ b/include/api.php
@@ -209,7 +209,7 @@
 			$extra_query = "AND `contact`.`nick` = '%s' ";
 		}
 		
-		if (is_null($user)){
+		if (is_null($user) && $a->argc > 3){
 			list($user, $null) = explode(".",$a->argv[3]);
 			if(is_numeric($user)){
 				$user = intval($user);
@@ -262,14 +262,26 @@
 
 		// count friends
 		$r = q("SELECT COUNT(`id`) as `count` FROM `contact`
-				WHERE  `uid` = %d
+				WHERE  `uid` = %d AND `rel` IN ( %d, %d )
 				AND `self`=0 AND `blocked`=0", 
-				intval($uinfo[0]['uid'])
+				intval($uinfo[0]['uid']),
+				intval(REL_FAN),
+				intval(REL_BUD)
 		);
 		$countfriends = $r[0]['count'];
 
+		$r = q("SELECT COUNT(`id`) as `count` FROM `contact`
+				WHERE  `uid` = %d AND `rel` IN ( %d, %d )
+				AND `self`=0 AND `blocked`=0", 
+				intval($uinfo[0]['uid']),
+				intval(REL_VIP),
+				intval(REL_BUD)
+		);
+		$countfollowers = $r[0]['count'];
+
 		if(! $uinfo[0]['self']) {
 			$countfriends = 0;
+			$countfollowers = 0;
 		}
 
 		$ret = Array(
@@ -290,7 +302,7 @@
 			'statuses_count' => $countitms, #XXX: fix me 
 			'lang' => 'en', #XXX: fix me
 			'description' => '',
-			'followers_count' => $countfriends, #XXX: fix me
+			'followers_count' => $countfollowers, #XXX: fix me
 			'favourites_count' => 0,
 			'contributors_enabled' => false,
 			'follow_request_sent' => false,
@@ -795,3 +807,47 @@
 		}
 	}
 	api_register_func('api/statusnet/version','api_statusnet_version',false);
+
+
+	function api_ff_ids(&$a,$type,$qtype) {
+		if(! local_user())
+			return false;
+
+		if($qtype == 'friends')
+			$sql_extra = sprintf(" AND ( `rel` = %d OR `rel` = %d ) ", intval(REL_FAN), intval(REL_BUD));
+		if($qtype == 'followers')
+			$sql_extra = sprintf(" AND ( `rel` = %d OR `rel` = %d ) ", intval(REL_VIP), intval(REL_BUD));
+ 
+
+		$r = q("SELECT id FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 AND `pending` = 0 $sql_extra",
+			intval(local_user())
+		);
+
+		if(is_array($r)) {
+			if($type === 'xml') {
+				header("Content-type: application/xml");
+				echo '' . "\r\n" . '' . "\r\n";
+				foreach($r as $rr)
+					echo '' . $rr['id'] . '' . "\r\n";
+				echo '' . "\r\n";
+				killme();
+			}
+			elseif($type === 'json') {
+				$ret = array();
+				header("Content-type: application/json");
+				foreach($r as $rr) $ret[] = $rr['id'];
+				echo json_encode($ret);
+				killme();
+			}
+		}
+	}
+
+	function api_friends_ids(&$a,$type) {
+		api_ff_ids($a,$type,'friends');
+	}
+	function api_followers_ids(&$a,$type) {
+		api_ff_ids($a,$type,'followers');
+	}
+	api_register_func('api/friends/ids','api_friends_ids',true);
+	api_register_func('api/followers/ids','api_followers_ids',true);
+

From 326a873082cd9f31f70bfb703ad2c3e7fde19bb8 Mon Sep 17 00:00:00 2001
From: Friendika 
Date: Mon, 1 Aug 2011 05:13:59 -0700
Subject: [PATCH 037/123] merge diaspora/friendika vcard formats - not yet
 complete

---
 boot.php               | 40 +++++++++++++++++++++++++++++++++++++++-
 include/api.php        |  5 +++--
 view/profile_vcard.tpl |  2 ++
 3 files changed, 44 insertions(+), 3 deletions(-)

diff --git a/boot.php b/boot.php
index 025f7ef878..4d4b385122 100644
--- a/boot.php
+++ b/boot.php
@@ -2396,6 +2396,8 @@ function profile_load(&$a, $nickname, $profile = 0) {
 if(! function_exists('profile_sidebar')) {
 function profile_sidebar($profile) {
 
+	$a = get_app();
+
 	$o = '';
 	$location = '';
 	$address = false;
@@ -2454,6 +2456,41 @@ function profile_sidebar($profile) {
 		$location = $gender = $marital = $homepage = '';
 	}
 
+	$podloc = $a->get_baseurl();
+	$searchable = (($profile['publish'] && $profile['net-publish']) ? 'true' : 'false' );
+	$nickname = $profile['nick'];
+	$dphoto = $profile['photo'];
+
+	$diaspora_vcard = <<< EOT
+
+
+
+
Nickname
+
+$nickname +
+
+
+
URL
+
+$podloc +
+
+
+
Photo
+
+ +
+
+
+
Searchable
+
+$searchable +
+
+
+EOT; + $tpl = get_markup_template('profile_vcard.tpl'); $o .= replace_macros($tpl, array( @@ -2466,7 +2503,8 @@ function profile_sidebar($profile) { '$gender' => $gender, '$pubkey' => $pubkey, '$marital' => $marital, - '$homepage' => $homepage + '$homepage' => $homepage, + '$diaspora' => $diaspora_vcard )); diff --git a/include/api.php b/include/api.php index 7b3dd866ce..c77c55cef4 100644 --- a/include/api.php +++ b/include/api.php @@ -114,6 +114,7 @@ load_contact_links(local_user()); logger('API call for ' . $a->user['username'] . ': ' . $a->query_string); + logger('API parameters: ' . print_r($_REQUEST,true)); $type="json"; if (strpos($a->query_string, ".xml")>0) $type="xml"; if (strpos($a->query_string, ".json")>0) $type="json"; @@ -220,7 +221,7 @@ } } - if ($user==='') { + if (! $user) { if (local_user()===false) { api_login($a); return False; } else { @@ -230,7 +231,7 @@ } - + logger('api_user: ' . $extra_query . ' ' , $user); // user info $uinfo = q("SELECT *, `contact`.`id` as `cid` FROM `contact` WHERE 1 diff --git a/view/profile_vcard.tpl b/view/profile_vcard.tpl index 3cd96eabff..86655951de 100644 --- a/view/profile_vcard.tpl +++ b/view/profile_vcard.tpl @@ -24,3 +24,5 @@ $marital $homepage +$diaspora + From e76990ae7bffdcc5f1c54146f54fc11729c537b7 Mon Sep 17 00:00:00 2001 From: Friendika Date: Mon, 1 Aug 2011 16:15:37 -0700 Subject: [PATCH 038/123] bug #111 - don't parse an @ address as a feed --- boot.php | 8 +++++--- include/Scrape.php | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/boot.php b/boot.php index 4d4b385122..113f5d72c1 100644 --- a/boot.php +++ b/boot.php @@ -1,6 +1,6 @@ Date: Mon, 1 Aug 2011 16:51:01 -0700 Subject: [PATCH 039/123] breaking up boot file (part of zot refactor) --- boot.php | 1966 +------------------------------------------ include/config.php | 205 +++++ include/network.php | 669 +++++++++++++++ include/plugin.php | 199 +++++ include/salmon.php | 31 - include/text.php | 933 ++++++++++++++++++++ 6 files changed, 2013 insertions(+), 1990 deletions(-) create mode 100644 include/config.php create mode 100644 include/network.php create mode 100644 include/plugin.php create mode 100644 include/text.php diff --git a/boot.php b/boot.php index 113f5d72c1..35cb152392 100644 --- a/boot.php +++ b/boot.php @@ -1,5 +1,12 @@ replace) -// returns substituted string. -// WARNING: this is pretty basic, and doesn't properly handle search strings that are substrings of each other. -// For instance if 'test' => "foo" and 'testing' => "bar", testing could become either bar or fooing, -// depending on the order in which they were declared in the array. - -require_once("include/template_processor.php"); - -if(! function_exists('replace_macros')) { -function replace_macros($s,$r) { - global $t; - - return $t->replace($s,$r); - -}} - - -// curl wrapper. If binary flag is true, return binary -// results. - -if(! function_exists('fetch_url')) { -function fetch_url($url,$binary = false, &$redirects = 0) { - - $a = get_app(); - - $ch = curl_init($url); - if(($redirects > 8) || (! $ch)) - return false; - - curl_setopt($ch, CURLOPT_HEADER, true); - curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); - curl_setopt($ch, CURLOPT_USERAGENT, "Friendika"); - - $curl_time = intval(get_config('system','curl_timeout')); - curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60)); - - // by default we will allow self-signed certs - // but you can override this - - $check_cert = get_config('system','verifyssl'); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false)); - - $prx = get_config('system','proxy'); - if(strlen($prx)) { - curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); - curl_setopt($ch, CURLOPT_PROXY, $prx); - $prxusr = get_config('system','proxyuser'); - if(strlen($prxusr)) - curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr); - } - if($binary) - curl_setopt($ch, CURLOPT_BINARYTRANSFER,1); - - $a->set_curl_code(0); - - // don't let curl abort the entire application - // if it throws any errors. - - $s = @curl_exec($ch); - - $base = $s; - $curl_info = curl_getinfo($ch); - $http_code = $curl_info['http_code']; - - $header = ''; - - // Pull out multiple headers, e.g. proxy and continuation headers - // allow for HTTP/2.x without fixing code - - while(preg_match('/^HTTP\/[1-2].+? [1-5][0-9][0-9]/',$base)) { - $chunk = substr($base,0,strpos($base,"\r\n\r\n")+4); - $header .= $chunk; - $base = substr($base,strlen($chunk)); - } - - if($http_code == 301 || $http_code == 302 || $http_code == 303 || $http_code == 307) { - $matches = array(); - preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches); - $url = trim(array_pop($matches)); - $url_parsed = @parse_url($url); - if (isset($url_parsed)) { - $redirects++; - return fetch_url($url,$binary,$redirects); - } - } - - $a->set_curl_code($http_code); - - $body = substr($s,strlen($header)); - - $a->set_curl_headers($header); - - curl_close($ch); - return($body); -}} - -// post request to $url. $params is an array of post variables. - -if(! function_exists('post_url')) { -function post_url($url,$params, $headers = null, &$redirects = 0) { - $a = get_app(); - $ch = curl_init($url); - if(($redirects > 8) || (! $ch)) - return false; - - curl_setopt($ch, CURLOPT_HEADER, true); - curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); - curl_setopt($ch, CURLOPT_POST,1); - curl_setopt($ch, CURLOPT_POSTFIELDS,$params); - curl_setopt($ch, CURLOPT_USERAGENT, "Friendika"); - - $curl_time = intval(get_config('system','curl_timeout')); - curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60)); - - if(defined('LIGHTTPD')) { - if(!is_array($headers)) { - $headers = array('Expect:'); - } else { - if(!in_array('Expect:', $headers)) { - array_push($headers, 'Expect:'); - } - } - } - if($headers) - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - - $check_cert = get_config('system','verifyssl'); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false)); - $prx = get_config('system','proxy'); - if(strlen($prx)) { - curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); - curl_setopt($ch, CURLOPT_PROXY, $prx); - $prxusr = get_config('system','proxyuser'); - if(strlen($prxusr)) - curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr); - } - - $a->set_curl_code(0); - - // don't let curl abort the entire application - // if it throws any errors. - - $s = @curl_exec($ch); - - $base = $s; - $curl_info = curl_getinfo($ch); - $http_code = $curl_info['http_code']; - - $header = ''; - - // Pull out multiple headers, e.g. proxy and continuation headers - // allow for HTTP/2.x without fixing code - - while(preg_match('/^HTTP\/[1-2].+? [1-5][0-9][0-9]/',$base)) { - $chunk = substr($base,0,strpos($base,"\r\n\r\n")+4); - $header .= $chunk; - $base = substr($base,strlen($chunk)); - } - - if($http_code == 301 || $http_code == 302 || $http_code == 303) { - $matches = array(); - preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches); - $url = trim(array_pop($matches)); - $url_parsed = @parse_url($url); - if (isset($url_parsed)) { - $redirects++; - return post_url($url,$binary,$headers,$redirects); - } - } - $a->set_curl_code($http_code); - $body = substr($s,strlen($header)); - - $a->set_curl_headers($header); - - curl_close($ch); - return($body); -}} - -// random hash, 64 chars - -if(! function_exists('random_string')) { -function random_string() { - return(hash('sha256',uniqid(rand(),true))); -}} - -/** - * This is our primary input filter. - * - * The high bit hack only involved some old IE browser, forget which (IE5/Mac?) - * that had an XSS attack vector due to stripping the high-bit on an 8-bit character - * after cleansing, and angle chars with the high bit set could get through as markup. - * - * This is now disabled because it was interfering with some legitimate unicode sequences - * and hopefully there aren't a lot of those browsers left. - * - * Use this on any text input where angle chars are not valid or permitted - * They will be replaced with safer brackets. This may be filtered further - * if these are not allowed either. - * - */ - -if(! function_exists('notags')) { -function notags($string) { - - return(str_replace(array("<",">"), array('[',']'), $string)); - -// High-bit filter no longer used -// return(str_replace(array("<",">","\xBA","\xBC","\xBE"), array('[',']','','',''), $string)); -}} - -// use this on "body" or "content" input where angle chars shouldn't be removed, -// and allow them to be safely displayed. - -if(! function_exists('escape_tags')) { -function escape_tags($string) { - - return(htmlspecialchars($string)); -}} // wrapper for adding a login box. If $register == true provide a registration // link. This will most always depend on the value of $a->config['register_policy']. @@ -931,77 +634,6 @@ function login($register = false) { return $o; }} -// generate a string that's random, but usually pronounceable. -// used to generate initial passwords - -if(! function_exists('autoname')) { -function autoname($len) { - - $vowels = array('a','a','ai','au','e','e','e','ee','ea','i','ie','o','ou','u'); - if(mt_rand(0,5) == 4) - $vowels[] = 'y'; - - $cons = array( - 'b','bl','br', - 'c','ch','cl','cr', - 'd','dr', - 'f','fl','fr', - 'g','gh','gl','gr', - 'h', - 'j', - 'k','kh','kl','kr', - 'l', - 'm', - 'n', - 'p','ph','pl','pr', - 'qu', - 'r','rh', - 's','sc','sh','sm','sp','st', - 't','th','tr', - 'v', - 'w','wh', - 'x', - 'z','zh' - ); - - $midcons = array('ck','ct','gn','ld','lf','lm','lt','mb','mm', 'mn','mp', - 'nd','ng','nk','nt','rn','rp','rt'); - - $noend = array('bl', 'br', 'cl','cr','dr','fl','fr','gl','gr', - 'kh', 'kl','kr','mn','pl','pr','rh','tr','qu','wh'); - - $start = mt_rand(0,2); - if($start == 0) - $table = $vowels; - else - $table = $cons; - - $word = ''; - - for ($x = 0; $x < $len; $x ++) { - $r = mt_rand(0,count($table) - 1); - $word .= $table[$r]; - - if($table == $vowels) - $table = array_merge($cons,$midcons); - else - $table = $vowels; - - } - - $word = substr($word,0,$len); - - foreach($noend as $noe) { - if((strlen($word) > 2) && (substr($word,-2) == $noe)) { - $word = substr($word,0,-1); - break; - } - } - if(substr($word,-1) == 'q') - $word = substr($word,0,-1); - return $word; -}} - // Used to end the current process, after saving session state. if(! function_exists('killme')) { @@ -1018,23 +650,6 @@ function goaway($s) { killme(); }} -// Generic XML return -// Outputs a basic dfrn XML status structure to STDOUT, with a variable -// of $st and an optional text of $message and terminates the current process. - -if(! function_exists('xml_status')) { -function xml_status($st, $message = '') { - - $xml_message = ((strlen($message)) ? "\t" . xmlify($message) . "\r\n" : ''); - - if($st) - logger('xml_status returning non_zero: ' . $st . " message=" . $message); - - header( "Content-type: text/xml" ); - echo ''."\r\n"; - echo "\r\n\t$st\r\n$xml_message\r\n"; - killme(); -}} // Returns the uid of locally logged in user or false. @@ -1080,1233 +695,6 @@ function get_max_import_size() { }} -// escape text ($str) for XML transport -// returns escaped text. - -if(! function_exists('xmlify')) { -function xmlify($str) { - $buffer = ''; - - for($x = 0; $x < strlen($str); $x ++) { - $char = $str[$x]; - - switch( $char ) { - - case "\r" : - break; - case "&" : - $buffer .= '&'; - break; - case "'" : - $buffer .= '''; - break; - case "\"" : - $buffer .= '"'; - break; - case '<' : - $buffer .= '<'; - break; - case '>' : - $buffer .= '>'; - break; - case "\n" : - $buffer .= "\n"; - break; - default : - $buffer .= $char; - break; - } - } - $buffer = trim($buffer); - return($buffer); -}} - -// undo an xmlify -// pass xml escaped text ($s), returns unescaped text - -if(! function_exists('unxmlify')) { -function unxmlify($s) { - $ret = str_replace('&','&', $s); - $ret = str_replace(array('<','>','"','''),array('<','>','"',"'"),$ret); - return $ret; -}} - -// convenience wrapper, reverse the operation "bin2hex" - -if(! function_exists('hex2bin')) { -function hex2bin($s) { - if(! ctype_xdigit($s)) { - logger('hex2bin: illegal input: ' . print_r(debug_backtrace(), true)); - return($s); - } - - return(pack("H*",$s)); -}} - -// Automatic pagination. -// To use, get the count of total items. -// Then call $a->set_pager_total($number_items); -// Optionally call $a->set_pager_itemspage($n) to the number of items to display on each page -// Then call paginate($a) after the end of the display loop to insert the pager block on the page -// (assuming there are enough items to paginate). -// When using with SQL, the setting LIMIT %d, %d => $a->pager['start'],$a->pager['itemspage'] -// will limit the results to the correct items for the current page. -// The actual page handling is then accomplished at the application layer. - -if(! function_exists('paginate')) { -function paginate(&$a) { - $o = ''; - $stripped = preg_replace('/(&page=[0-9]*)/','',$a->query_string); - $stripped = str_replace('q=','',$stripped); - $stripped = trim($stripped,'/'); - $pagenum = $a->pager['page']; - $url = $a->get_baseurl() . '/' . $stripped; - - - if($a->pager['total'] > $a->pager['itemspage']) { - $o .= '
'; - if($a->pager['page'] != 1) - $o .= ''."pager['page'] - 1).'">' . t('prev') . ' '; - - $o .= "" . t('first') . " "; - - $numpages = $a->pager['total'] / $a->pager['itemspage']; - - $numstart = 1; - $numstop = $numpages; - - if($numpages > 14) { - $numstart = (($pagenum > 7) ? ($pagenum - 7) : 1); - $numstop = (($pagenum > ($numpages - 7)) ? $numpages : ($numstart + 14)); - } - - for($i = $numstart; $i <= $numstop; $i++){ - if($i == $a->pager['page']) - $o .= ''.(($i < 10) ? ' '.$i : $i); - else - $o .= "".(($i < 10) ? ' '.$i : $i).""; - $o .= ' '; - } - - if(($a->pager['total'] % $a->pager['itemspage']) != 0) { - if($i == $a->pager['page']) - $o .= ''.(($i < 10) ? ' '.$i : $i); - else - $o .= "".(($i < 10) ? ' '.$i : $i).""; - $o .= ' '; - } - - $lastpage = (($numpages > intval($numpages)) ? intval($numpages)+1 : $numpages); - $o .= "" . t('last') . " "; - - if(($a->pager['total'] - ($a->pager['itemspage'] * $a->pager['page'])) > 0) - $o .= ''."pager['page'] + 1).'">' . t('next') . ''; - $o .= '
'."\r\n"; - } - return $o; -}} - -// Turn user/group ACLs stored as angle bracketed text into arrays - -if(! function_exists('expand_acl')) { -function expand_acl($s) { - // turn string array of angle-bracketed elements into numeric array - // e.g. "<1><2><3>" => array(1,2,3); - $ret = array(); - - if(strlen($s)) { - $t = str_replace('<','',$s); - $a = explode('>',$t); - foreach($a as $aa) { - if(intval($aa)) - $ret[] = intval($aa); - } - } - return $ret; -}} - -// Used to wrap ACL elements in angle brackets for storage - -if(! function_exists('sanitise_acl')) { -function sanitise_acl(&$item) { - if(intval($item)) - $item = '<' . intval(notags(trim($item))) . '>'; - else - unset($item); -}} - -// retrieve a "family" of config variables from database to cached storage - -if(! function_exists('load_config')) { -function load_config($family) { - global $a; - $r = q("SELECT * FROM `config` WHERE `cat` = '%s'", - dbesc($family) - ); - if(count($r)) { - foreach($r as $rr) { - $k = $rr['k']; - if ($rr['cat'] === 'config') { - $a->config[$k] = $rr['v']; - } else { - $a->config[$family][$k] = $rr['v']; - } - } - } -}} - -// get a particular config variable given the family name -// and key. Returns false if not set. -// $instore is only used by the set_config function -// to determine if the key already exists in the DB -// If a key is found in the DB but doesn't exist in -// local config cache, pull it into the cache so we don't have -// to hit the DB again for this item. - -if(! function_exists('get_config')) { -function get_config($family, $key, $instore = false) { - - global $a; - - if(! $instore) { - if(isset($a->config[$family][$key])) { - if($a->config[$family][$key] === '!!') { - return false; - } - return $a->config[$family][$key]; - } - } - $ret = q("SELECT `v` FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1", - dbesc($family), - dbesc($key) - ); - if(count($ret)) { - // manage array value - $val = (preg_match("|^a:[0-9]+:{.*}$|", $ret[0]['v'])?unserialize( $ret[0]['v']):$ret[0]['v']); - $a->config[$family][$key] = $val; - return $val; - } - else { - $a->config[$family][$key] = '!!'; - } - return false; -}} - -// Store a config value ($value) in the category ($family) -// under the key ($key) -// Return the value, or false if the database update failed - -if(! function_exists('set_config')) { -function set_config($family,$key,$value) { - global $a; - - // manage array value - $dbvalue = (is_array($value)?serialize($value):$value); - - if(get_config($family,$key,true) === false) { - $a->config[$family][$key] = $value; - $ret = q("INSERT INTO `config` ( `cat`, `k`, `v` ) VALUES ( '%s', '%s', '%s' ) ", - dbesc($family), - dbesc($key), - dbesc($dbvalue) - ); - if($ret) - return $value; - return $ret; - } - - $ret = q("UPDATE `config` SET `v` = '%s' WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1", - dbesc($dbvalue), - dbesc($family), - dbesc($key) - ); - - $a->config[$family][$key] = $value; - - if($ret) - return $value; - return $ret; -}} - - -if(! function_exists('load_pconfig')) { -function load_pconfig($uid,$family) { - global $a; - $r = q("SELECT * FROM `pconfig` WHERE `cat` = '%s' AND `uid` = %d", - dbesc($family), - intval($uid) - ); - if(count($r)) { - foreach($r as $rr) { - $k = $rr['k']; - $a->config[$uid][$family][$k] = $rr['v']; - } - } -}} - - - -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; - - if(get_pconfig($uid,$family,$key,true) === false) { - $a->config[$uid][$family][$key] = $value; - $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", - dbesc($value), - intval($uid), - dbesc($family), - dbesc($key) - ); - - $a->config[$uid][$family][$key] = $value; - - 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($family), - dbesc($key) - ); - return $ret; -}} - - -// convert an XML document to a normalised, case-corrected array -// used by webfinger - -if(! function_exists('convert_xml_element_to_array')) { -function convert_xml_element_to_array($xml_element, &$recursion_depth=0) { - - // If we're getting too deep, bail out - if ($recursion_depth > 512) { - return(null); - } - - if (!is_string($xml_element) && - !is_array($xml_element) && - (get_class($xml_element) == 'SimpleXMLElement')) { - $xml_element_copy = $xml_element; - $xml_element = get_object_vars($xml_element); - } - - if (is_array($xml_element)) { - $result_array = array(); - if (count($xml_element) <= 0) { - return (trim(strval($xml_element_copy))); - } - - foreach($xml_element as $key=>$value) { - - $recursion_depth++; - $result_array[strtolower($key)] = - convert_xml_element_to_array($value, $recursion_depth); - $recursion_depth--; - } - if ($recursion_depth == 0) { - $temp_array = $result_array; - $result_array = array( - strtolower($xml_element_copy->getName()) => $temp_array, - ); - } - - return ($result_array); - - } else { - return (trim(strval($xml_element))); - } -}} - -// Given an email style address, perform webfinger lookup and -// return the resulting DFRN profile URL, or if no DFRN profile URL -// is located, returns an OStatus subscription template (prefixed -// with the string 'stat:' to identify it as on OStatus template). -// If this isn't an email style address just return $s. -// Return an empty string if email-style addresses but webfinger fails, -// or if the resultant personal XRD doesn't contain a supported -// subscription/friend-request attribute. - -if(! function_exists('webfinger_dfrn')) { -function webfinger_dfrn($s) { - if(! strstr($s,'@')) { - return $s; - } - $links = webfinger($s); - logger('webfinger_dfrn: ' . $s . ':' . print_r($links,true), LOGGER_DATA); - if(count($links)) { - foreach($links as $link) - if($link['@attributes']['rel'] === NAMESPACE_DFRN) - return $link['@attributes']['href']; - foreach($links as $link) - if($link['@attributes']['rel'] === NAMESPACE_OSTATUSSUB) - return 'stat:' . $link['@attributes']['template']; - } - return ''; -}} - -// Given an email style address, perform webfinger lookup and -// return the array of link attributes from the personal XRD file. -// On error/failure return an empty array. - - -if(! function_exists('webfinger')) { -function webfinger($s) { - $host = ''; - if(strstr($s,'@')) { - $host = substr($s,strpos($s,'@') + 1); - } - if(strlen($host)) { - $tpl = fetch_lrdd_template($host); - logger('webfinger: lrdd template: ' . $tpl); - if(strlen($tpl)) { - $pxrd = str_replace('{uri}', urlencode('acct:' . $s), $tpl); - logger('webfinger: pxrd: ' . $pxrd); - $links = fetch_xrd_links($pxrd); - if(! count($links)) { - // try with double slashes - $pxrd = str_replace('{uri}', urlencode('acct://' . $s), $tpl); - logger('webfinger: pxrd: ' . $pxrd); - $links = fetch_xrd_links($pxrd); - } - return $links; - } - } - return array(); -}} - -if(! function_exists('lrdd')) { -function lrdd($uri) { - - $a = get_app(); - - // default priority is host priority, host-meta first - - $priority = 'host'; - - // All we have is an email address. Resource-priority is irrelevant - // because our URI isn't directly resolvable. - - if(strstr($uri,'@')) { - return(webfinger($uri)); - } - - // get the host meta file - - $host = @parse_url($uri); - - if($host) { - $url = ((x($host,'scheme')) ? $host['scheme'] : 'http') . '://'; - $url .= $host['host'] . '/.well-known/host-meta' ; - } - else - return array(); - - logger('lrdd: constructed url: ' . $url); - - $xml = fetch_url($url); - $headers = $a->get_curl_headers(); - - if (! $xml) - return array(); - - logger('lrdd: host_meta: ' . $xml, LOGGER_DATA); - - $h = parse_xml_string($xml); - if(! $h) - return array(); - - $arr = convert_xml_element_to_array($h); - - if(isset($arr['xrd']['property'])) { - $property = $arr['crd']['property']; - if(! isset($property[0])) - $properties = array($property); - else - $properties = $property; - foreach($properties as $prop) - if((string) $prop['@attributes'] === 'http://lrdd.net/priority/resource') - $priority = 'resource'; - } - - // save the links in case we need them - - $links = array(); - - if(isset($arr['xrd']['link'])) { - $link = $arr['xrd']['link']; - if(! isset($link[0])) - $links = array($link); - else - $links = $link; - } - - // do we have a template or href? - - if(count($links)) { - foreach($links as $link) { - if($link['@attributes']['rel'] && attribute_contains($link['@attributes']['rel'],'lrdd')) { - if(x($link['@attributes'],'template')) - $tpl = $link['@attributes']['template']; - elseif(x($link['@attributes'],'href')) - $href = $link['@attributes']['href']; - } - } - } - - if((! isset($tpl)) || (! strpos($tpl,'{uri}'))) - $tpl = ''; - - if($priority === 'host') { - if(strlen($tpl)) - $pxrd = str_replace('{uri}', urlencode($uri), $tpl); - elseif(isset($href)) - $pxrd = $href; - if(isset($pxrd)) { - logger('lrdd: (host priority) pxrd: ' . $pxrd); - $links = fetch_xrd_links($pxrd); - return $links; - } - - $lines = explode("\n",$headers); - if(count($lines)) { - foreach($lines as $line) { - if((stristr($line,'link:')) && preg_match('/<([^>].*)>.*rel\=[\'\"]lrdd[\'\"]/',$line,$matches)) { - return(fetch_xrd_links($matches[1])); - break; - } - } - } - } - - - // priority 'resource' - - - $html = fetch_url($uri); - $headers = $a->get_curl_headers(); - logger('lrdd: headers=' . $headers, LOGGER_DEBUG); - - // don't try and parse raw xml as html - if(! strstr($html,'getElementsByTagName('link'); - foreach($items as $item) { - $x = $item->getAttribute('rel'); - if($x == "lrdd") { - $pagelink = $item->getAttribute('href'); - break; - } - } - } - } - - if(isset($pagelink)) - return(fetch_xrd_links($pagelink)); - - // next look in HTTP headers - - $lines = explode("\n",$headers); - if(count($lines)) { - foreach($lines as $line) { - // TODO alter the following regex to support multiple relations (space separated) - if((stristr($line,'link:')) && preg_match('/<([^>].*)>.*rel\=[\'\"]lrdd[\'\"]/',$line,$matches)) { - $pagelink = $matches[1]; - break; - } - // don't try and run feeds through the html5 parser - if(stristr($line,'content-type:') && ((stristr($line,'application/atom+xml')) || (stristr($line,'application/rss+xml')))) - return array(); - if(stristr($html,' 'alias' , 'href' => $alias); - } - } - - logger('fetch_xrd_links: ' . print_r($links,true), LOGGER_DATA); - - return $links; - -}} - -// Convert an ACL array to a storable string - -if(! function_exists('perms2str')) { -function perms2str($p) { - $ret = ''; - $tmp = $p; - if(is_array($tmp)) { - array_walk($tmp,'sanitise_acl'); - $ret = implode('',$tmp); - } - return $ret; -}} - -// generate a guaranteed unique (for this domain) item ID for ATOM -// safe from birthday paradox - -if(! function_exists('item_new_uri')) { -function item_new_uri($hostname,$uid) { - - do { - $dups = false; - $hash = random_string(); - - $uri = "urn:X-dfrn:" . $hostname . ':' . $uid . ':' . $hash; - - $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' LIMIT 1", - dbesc($uri)); - if(count($r)) - $dups = true; - } while($dups == true); - return $uri; -}} - -// Generate a guaranteed unique photo ID. -// safe from birthday paradox - -if(! function_exists('photo_new_resource')) { -function photo_new_resource() { - - do { - $found = false; - $resource = hash('md5',uniqid(mt_rand(),true)); - $r = q("SELECT `id` FROM `photo` WHERE `resource-id` = '%s' LIMIT 1", - dbesc($resource) - ); - if(count($r)) - $found = true; - } while($found == true); - return $resource; -}} - - -// Take a URL from the wild, prepend http:// if necessary -// and check DNS to see if it's real -// return true if it's OK, false if something is wrong with it - -if(! function_exists('validate_url')) { -function validate_url(&$url) { - if(substr($url,0,4) != 'http') - $url = 'http://' . $url; - $h = @parse_url($url); - - if(($h) && (dns_get_record($h['host'], DNS_A + DNS_CNAME + DNS_PTR))) { - return true; - } - return false; -}} - -// checks that email is an actual resolvable internet address - -if(! function_exists('validate_email')) { -function validate_email($addr) { - - if(! strpos($addr,'@')) - return false; - $h = substr($addr,strpos($addr,'@') + 1); - - if(($h) && (dns_get_record($h, DNS_A + DNS_CNAME + DNS_PTR + DNS_MX))) { - return true; - } - return false; -}} - -// Check $url against our list of allowed sites, -// wildcards allowed. If allowed_sites is unset return true; -// If url is allowed, return true. -// otherwise, return false - -if(! function_exists('allowed_url')) { -function allowed_url($url) { - - $h = @parse_url($url); - - if(! $h) { - return false; - } - - $str_allowed = get_config('system','allowed_sites'); - if(! $str_allowed) - return true; - - $found = false; - - $host = strtolower($h['host']); - - // always allow our own site - - if($host == strtolower($_SERVER['SERVER_NAME'])) - return true; - - $fnmatch = function_exists('fnmatch'); - $allowed = explode(',',$str_allowed); - - if(count($allowed)) { - foreach($allowed as $a) { - $pat = strtolower(trim($a)); - if(($fnmatch && fnmatch($pat,$host)) || ($pat == $host)) { - $found = true; - break; - } - } - } - return $found; -}} - -// check if email address is allowed to register here. -// Compare against our list (wildcards allowed). -// Returns false if not allowed, true if allowed or if -// allowed list is not configured. - -if(! function_exists('allowed_email')) { -function allowed_email($email) { - - - $domain = strtolower(substr($email,strpos($email,'@') + 1)); - if(! $domain) - return false; - - $str_allowed = get_config('system','allowed_email'); - if(! $str_allowed) - return true; - - $found = false; - - $fnmatch = function_exists('fnmatch'); - $allowed = explode(',',$str_allowed); - - if(count($allowed)) { - foreach($allowed as $a) { - $pat = strtolower(trim($a)); - if(($fnmatch && fnmatch($pat,$domain)) || ($pat == $domain)) { - $found = true; - break; - } - } - } - return $found; -}} - - - -// wrapper to load a view template, checking for alternate -// languages before falling back to the default - -// obsolete, deprecated. - -if(! function_exists('load_view_file')) { -function load_view_file($s) { - global $lang, $a; - if(! isset($lang)) - $lang = 'en'; - $b = basename($s); - $d = dirname($s); - if(file_exists("$d/$lang/$b")) - return file_get_contents("$d/$lang/$b"); - - $theme = current_theme(); - - if(file_exists("$d/theme/$theme/$b")) - return file_get_contents("$d/theme/$theme/$b"); - - return file_get_contents($s); -}} - -if(! function_exists('get_intltext_template')) { -function get_intltext_template($s) { - global $lang; - - if(! isset($lang)) - $lang = 'en'; - - if(file_exists("view/$lang/$s")) - return file_get_contents("view/$lang/$s"); - elseif(file_exists("view/en/$s")) - return file_get_contents("view/en/$s"); - else - return file_get_contents("view/$s"); -}} - -if(! function_exists('get_markup_template')) { -function get_markup_template($s) { - - $theme = current_theme(); - - if(file_exists("view/theme/$theme/$s")) - return file_get_contents("view/theme/$theme/$s"); - else - return file_get_contents("view/$s"); - -}} - - - - - -// for html,xml parsing - let's say you've got -// an attribute foobar="class1 class2 class3" -// and you want to find out if it contains 'class3'. -// you can't use a normal sub string search because you -// might match 'notclass3' and a regex to do the job is -// possible but a bit complicated. -// pass the attribute string as $attr and the attribute you -// are looking for as $s - returns true if found, otherwise false - -if(! function_exists('attribute_contains')) { -function attribute_contains($attr,$s) { - $a = explode(' ', $attr); - if(count($a) && in_array($s,$a)) - return true; - return false; -}} - -if(! function_exists('logger')) { -function logger($msg,$level = 0) { - $debugging = get_config('system','debugging'); - $loglevel = intval(get_config('system','loglevel')); - $logfile = get_config('system','logfile'); - - if((! $debugging) || (! $logfile) || ($level > $loglevel)) - return; - - @file_put_contents($logfile, datetime_convert() . ':' . session_id() . ' ' . $msg . "\n", FILE_APPEND); - return; -}} - - -if(! function_exists('activity_match')) { -function activity_match($haystack,$needle) { - if(($haystack === $needle) || ((basename($needle) === $haystack) && strstr($needle,NAMESPACE_ACTIVITY_SCHEMA))) - return true; - return false; -}} - - -// Pull out all #hashtags and @person tags from $s; -// We also get @person@domain.com - which would make -// the regex quite complicated as tags can also -// end a sentence. So we'll run through our results -// and strip the period from any tags which end with one. -// Returns array of tags found, or empty array. - - -if(! function_exists('get_tags')) { -function get_tags($s) { - $ret = array(); - - // ignore anything in a code block - - $s = preg_replace('/\[code\](.*?)\[\/code\]/sm','',$s); - - // Match full names against @tags including the space between first and last - // We will look these up afterward to see if they are full names or not recognisable. - - if(preg_match_all('/(@[^ \x0D\x0A,:?]+ [^ \x0D\x0A,:?]+)([ \x0D\x0A,:?]|$)/',$s,$match)) { - foreach($match[1] as $mtch) { - if(strstr($mtch,"]")) { - // we might be inside a bbcode color tag - leave it alone - continue; - } - if(substr($mtch,-1,1) === '.') - $ret[] = substr($mtch,0,-1); - else - $ret[] = $mtch; - } - } - - // Otherwise pull out single word tags. These can be @nickname, @first_last - // and #hash tags. - - if(preg_match_all('/([@#][^ \x0D\x0A,:?]+)([ \x0D\x0A,:?]|$)/',$s,$match)) { - foreach($match[1] as $mtch) { - if(strstr($mtch,"]")) { - // we might be inside a bbcode color tag - leave it alone - continue; - } - // ignore strictly numeric tags like #1 - if((strpos($mtch,'#') === 0) && ctype_digit(substr($mtch,1))) - continue; - if(substr($mtch,-1,1) === '.') - $ret[] = substr($mtch,0,-1); - else - $ret[] = $mtch; - } - } - return $ret; -}} - - -// quick and dirty quoted_printable encoding - -if(! function_exists('qp')) { -function qp($s) { -return str_replace ("%","=",rawurlencode($s)); -}} - - - -if(! function_exists('get_mentions')) { -function get_mentions($item) { - $o = ''; - if(! strlen($item['tag'])) - return $o; - - $arr = explode(',',$item['tag']); - foreach($arr as $x) { - $matches = null; - if(preg_match('/@\[url=([^\]]*)\]/',$x,$matches)) { - $o .= "\t\t" . '' . "\r\n"; - $o .= "\t\t" . '' . "\r\n"; - } - } - return $o; -}} - -if(! function_exists('contact_block')) { -function contact_block() { - $o = ''; - $a = get_app(); - - $shown = get_pconfig($a->profile['uid'],'system','display_friend_count'); - if(! $shown) - $shown = 24; - - if((! is_array($a->profile)) || ($a->profile['hide-friends'])) - return $o; - $r = q("SELECT COUNT(*) AS `total` FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 and `pending` = 0", - intval($a->profile['uid']) - ); - if(count($r)) { - $total = intval($r[0]['total']); - } - if(! $total) { - $o .= '

' . t('No contacts') . '

'; - return $o; - } - $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 and `pending` = 0 ORDER BY RAND() LIMIT %d", - intval($a->profile['uid']), - intval($shown) - ); - if(count($r)) { - $o .= '

' . sprintf( tt('%d Contact','%d Contacts', $total),$total) . '

'; - foreach($r as $rr) { - $o .= micropro($rr,true,'mpfriend'); - } - $o .= '
'; - $o .= ''; - - } - - $arr = array('contacts' => $r, 'output' => $o); - - call_hooks('contact_block_end', $arr); - return $o; - -}} - -if(! function_exists('micropro')) { -function micropro($contact, $redirect = false, $class = '', $textmode = false) { - - if($class) - $class = ' ' . $class; - - $url = $contact['url']; - $sparkle = ''; - - if($redirect) { - $a = get_app(); - $redirect_url = $a->get_baseurl() . '/redir/' . $contact['id']; - if(local_user() && ($contact['uid'] == local_user()) && ($contact['network'] === 'dfrn')) { - $url = $redirect_url; - $sparkle = ' sparkle'; - } - } - $click = ((x($contact,'click')) ? ' onclick="' . $contact['click'] . '" ' : ''); - if($click) - $url = ''; - if($textmode) { - return '' . "\r\n"; - } - else { - return '
' . $contact['name'] 
-			. '
' . "\r\n"; - } -}} - - - -if(! function_exists('search')) { -function search($s,$id='search-box',$url='/search') { - $a = get_app(); - $o = '
'; - $o .= '
'; - $o .= ''; - $o .= ''; - $o .= '
'; - return $o; -}} - -if(! function_exists('valid_email')) { -function valid_email($x){ - if(preg_match('/^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+$/',$x)) - return true; - return false; -}} - - -if(! function_exists('gravatar_img')) { -function gravatar_img($email) { - $size = 175; - $opt = 'identicon'; // psuedo-random geometric pattern if not found - $rating = 'pg'; - $hash = md5(trim(strtolower($email))); - - $url = 'http://www.gravatar.com/avatar/' . $hash . '.jpg' - . '?s=' . $size . '&d=' . $opt . '&r=' . $rating; - - logger('gravatar: ' . $email . ' ' . $url); - return $url; -}} - -if(! function_exists('aes_decrypt')) { -function aes_decrypt($val,$ky) -{ - $key="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; - for($a=0;$a=0 and ord(substr($dec, strlen($dec)-1,1))<=16)? chr(ord( substr($dec,strlen($dec)-1,1))):null)); -}} - - -if(! function_exists('aes_encrypt')) { -function aes_encrypt($val,$ky) -{ - $key="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; - for($a=0;$a$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(); - - return str_replace( - array( '<3', '</3', '<\\3', ':-)', ':)', ';-)', ':-(', ':(', ':-P', ':P', ':-"', ':-x', ':-X', ':-D', '8-|', '8-O'), - array( - '<3', - '</3', - '<\\3', - ':-)', - ':)', - ';-)', - ':-(', - ':(', - ':-P', - ':P', - ':-\', - ':-x', - ':-X', - ':-D', - '8-|', - '8-O' - ), $s); -}} - /** * @@ -2518,81 +906,6 @@ EOT; }} -if(! function_exists('register_hook')) { -function register_hook($hook,$file,$function) { - - $r = q("SELECT * FROM `hook` WHERE `hook` = '%s' AND `file` = '%s' AND `function` = '%s' LIMIT 1", - dbesc($hook), - dbesc($file), - dbesc($function) - ); - if(count($r)) - return true; - - $r = q("INSERT INTO `hook` (`hook`, `file`, `function`) VALUES ( '%s', '%s', '%s' ) ", - dbesc($hook), - dbesc($file), - dbesc($function) - ); - return $r; -}} - -if(! function_exists('unregister_hook')) { -function unregister_hook($hook,$file,$function) { - - $r = q("DELETE FROM `hook` WHERE `hook` = '%s' AND `file` = '%s' AND `function` = '%s' LIMIT 1", - dbesc($hook), - dbesc($file), - dbesc($function) - ); - return $r; -}} - - -if(! function_exists('load_hooks')) { -function load_hooks() { - $a = get_app(); - $a->hooks = array(); - $r = q("SELECT * FROM `hook` WHERE 1"); - if(count($r)) { - foreach($r as $rr) { - $a->hooks[] = array($rr['hook'], $rr['file'], $rr['function']); - } - } -}} - - -if(! function_exists('call_hooks')) { -function call_hooks($name, &$data = null) { - $a = get_app(); - - if(count($a->hooks)) { - foreach($a->hooks as $hook) { - if($hook[HOOK_HOOK] === $name) { - @include_once($hook[HOOK_FILE]); - if(function_exists($hook[HOOK_FUNCTION])) { - $func = $hook[HOOK_FUNCTION]; - $func($a,$data); - } - } - } - } -}} - - -if(! function_exists('day_translate')) { -function day_translate($s) { - $ret = str_replace(array('Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'), - array( t('Monday'), t('Tuesday'), t('Wednesday'), t('Thursday'), t('Friday'), t('Saturday'), t('Sunday')), - $s); - - $ret = str_replace(array('January','February','March','April','May','June','July','August','September','October','November','December'), - array( t('January'), t('February'), t('March'), t('April'), t('May'), t('June'), t('July'), t('August'), t('September'), t('October'), t('November'), t('December')), - $ret); - - return $ret; -}} - if(! function_exists('get_birthdays')) { function get_birthdays() { @@ -2643,79 +956,6 @@ function get_birthdays() { }} -if(! function_exists('normalise_link')) { -function normalise_link($url) { - $ret = str_replace(array('https:','//www.'), array('http:','//'), $url); - return(rtrim($ret,'/')); -}} - -/** - * - * Compare two URLs to see if they are the same, but ignore - * slight but hopefully insignificant differences such as if one - * is https and the other isn't, or if one is www.something and - * the other isn't - and also ignore case differences. - * - * Return true if the URLs match, otherwise false. - * - */ - -if(! function_exists('link_compare')) { -function link_compare($a,$b) { - if(strcasecmp(normalise_link($a),normalise_link($b)) === 0) - return true; - return false; -}} - - -if(! function_exists('prepare_body')) { -function prepare_body($item,$attach = false) { - - $s = prepare_text($item['body']); - if(! $attach) - return $s; - - $arr = explode(',',$item['attach']); - if(count($arr)) { - $s .= '
'; - foreach($arr as $r) { - $matches = false; - $icon = ''; - $cnt = preg_match('|\[attach\]href=\"(.*?)\" size=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches); - if($cnt) { - $icontype = strtolower(substr($matches[3],0,strpos($matches[3],'/'))); - switch($icontype) { - case 'video': - case 'audio': - case 'image': - case 'text': - $icon = '
'; - break; - default: - $icon = '
'; - break; - } - $title = ((strlen(trim($matches[4]))) ? escape_tags(trim($matches[4])) : escape_tags($matches[1])); - $title .= ' ' . $matches[2] . ' ' . t('bytes'); - - $s .= '' . $icon . ''; - } - } - $s .= '
'; - } - return $s; -}} - -if(! function_exists('prepare_text')) { -function prepare_text($text) { - - require_once('include/bbcode.php'); - - $s = smilies(bbcode($text)); - - return $s; -}} - /** * * Wrap calls to proc_close(proc_open()) and call hook @@ -2826,106 +1066,6 @@ function feed_birthday($uid,$tz) { return $birthday; }} -/** - * return atom link elements for all of our hubs - */ - -if(! function_exists('feed_hublinks')) { -function feed_hublinks() { - - $hub = get_config('system','huburl'); - - $hubxml = ''; - if(strlen($hub)) { - $hubs = explode(',', $hub); - if(count($hubs)) { - foreach($hubs as $h) { - $h = trim($h); - if(! strlen($h)) - continue; - $hubxml .= '' . "\n" ; - } - } - } - return $hubxml; -}} - -/* return atom link elements for salmon endpoints */ - -if(! function_exists('feed_salmonlinks')) { -function feed_salmonlinks($nick) { - - $a = get_app(); - - $salmon = '' . "\n" ; - - // old style links that status.net still needed as of 12/2010 - - $salmon .= ' ' . "\n" ; - $salmon .= ' ' . "\n" ; - return $salmon; -}} - -if(! function_exists('get_plink')) { -function get_plink($item) { - $a = get_app(); - $plink = (((x($item,'plink')) && (! $item['private'])) ? '' : ''); - return $plink; -}} - -if(! function_exists('unamp')) { -function unamp($s) { - return str_replace('&', '&', $s); -}} - - - - -if(! function_exists('lang_selector')) { -function lang_selector() { - global $lang; - $o = '
'; - $o .= ''; - return $o; -}} - - -if(! function_exists('parse_xml_string')) { -function parse_xml_string($s,$strict = true) { - if($strict) { - if(! strstr($s,'code." at ".$err->line.":".$err->column." : ".$err->message, LOGGER_DATA); - libxml_clear_errors(); - } - return $x; -}} - if(! function_exists('is_site_admin')) { function is_site_admin() { $a = get_app(); @@ -2934,98 +1074,6 @@ function is_site_admin() { return false; }} -/* - * parse plugin comment in search of plugin infos. - * like - * - * * Name: Plugin - * * Description: A plugin which plugs in - * * Version: 1.2.3 - * * Author: John - * * Author: Jane - * * - */ - -if (! function_exists('get_plugin_info')){ -function get_plugin_info($plugin){ - if (!is_file("addon/$plugin/$plugin.php")) return false; - - $f = file_get_contents("addon/$plugin/$plugin.php"); - $r = preg_match("|/\*.*\*/|msU", $f, $m); - - $info=Array( - 'name' => $plugin, - 'description' => "", - 'author' => array(), - 'version' => "" - ); - - if ($r){ - $ll = explode("\n", $m[0]); - foreach( $ll as $l ) { - $l = trim($l,"\t\n\r */"); - if ($l!=""){ - list($k,$v) = array_map("trim", explode(":",$l,2)); - $k= strtolower($k); - if ($k=="author"){ - $r=preg_match("|([^<]+)<([^>]+)>|", $v, $m); - if ($r) { - $info['author'][] = array('name'=>$m[1], 'link'=>$m[2]); - } else { - $info['author'][] = array('name'=>$v); - } - } else { - if (array_key_exists($k,$info)){ - $info[$k]=$v; - } - } - - } - } - - } - return $info; -}} - -if(! function_exists('return_bytes')) { -function return_bytes ($size_str) { - switch (substr ($size_str, -1)) - { - case 'M': case 'm': return (int)$size_str * 1048576; - case 'K': case 'k': return (int)$size_str * 1024; - case 'G': case 'g': return (int)$size_str * 1073741824; - default: return $size_str; - } -}} - -function generate_guid() { - $found = true; - do { - $guid = substr(random_string(),0,16); - $x = q("SELECT `uid` FROM `user` WHERE `guid` = '%s' LIMIT 1", - dbesc($guid) - ); - if(! count($x)) - $found = false; - } while ($found == true ); - return $guid; -} - - -function pkcs5_pad ($text, $blocksize) -{ - $pad = $blocksize - (strlen($text) % $blocksize); - return $text . str_repeat(chr($pad), $pad); -} - -function pkcs5_unpad($text) -{ - $pad = ord($text{strlen($text)-1}); - if ($pad > strlen($text)) return false; - if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false; - return substr($text, 0, -1 * $pad); -} - if(! function_exists('load_contact_links')) { function load_contact_links($uid) { diff --git a/include/config.php b/include/config.php new file mode 100644 index 0000000000..fe675bc33f --- /dev/null +++ b/include/config.php @@ -0,0 +1,205 @@ +config[$k] = $rr['v']; + } else { + $a->config[$family][$k] = $rr['v']; + } + } + } +}} + +// get a particular config variable given the family name +// and key. Returns false if not set. +// $instore is only used by the set_config function +// to determine if the key already exists in the DB +// If a key is found in the DB but doesn't exist in +// local config cache, pull it into the cache so we don't have +// to hit the DB again for this item. + +if(! function_exists('get_config')) { +function get_config($family, $key, $instore = false) { + + global $a; + + if(! $instore) { + if(isset($a->config[$family][$key])) { + if($a->config[$family][$key] === '!!') { + return false; + } + return $a->config[$family][$key]; + } + } + $ret = q("SELECT `v` FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1", + dbesc($family), + dbesc($key) + ); + if(count($ret)) { + // manage array value + $val = (preg_match("|^a:[0-9]+:{.*}$|", $ret[0]['v'])?unserialize( $ret[0]['v']):$ret[0]['v']); + $a->config[$family][$key] = $val; + return $val; + } + else { + $a->config[$family][$key] = '!!'; + } + return false; +}} + +// Store a config value ($value) in the category ($family) +// under the key ($key) +// Return the value, or false if the database update failed + +if(! function_exists('set_config')) { +function set_config($family,$key,$value) { + global $a; + + // manage array value + $dbvalue = (is_array($value)?serialize($value):$value); + + if(get_config($family,$key,true) === false) { + $a->config[$family][$key] = $value; + $ret = q("INSERT INTO `config` ( `cat`, `k`, `v` ) VALUES ( '%s', '%s', '%s' ) ", + dbesc($family), + dbesc($key), + dbesc($dbvalue) + ); + if($ret) + return $value; + return $ret; + } + + $ret = q("UPDATE `config` SET `v` = '%s' WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1", + dbesc($dbvalue), + dbesc($family), + dbesc($key) + ); + + $a->config[$family][$key] = $value; + + if($ret) + return $value; + return $ret; +}} + + +if(! function_exists('load_pconfig')) { +function load_pconfig($uid,$family) { + global $a; + $r = q("SELECT * FROM `pconfig` WHERE `cat` = '%s' AND `uid` = %d", + dbesc($family), + intval($uid) + ); + if(count($r)) { + foreach($r as $rr) { + $k = $rr['k']; + $a->config[$uid][$family][$k] = $rr['v']; + } + } +}} + + + +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; + + if(get_pconfig($uid,$family,$key,true) === false) { + $a->config[$uid][$family][$key] = $value; + $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", + dbesc($value), + intval($uid), + dbesc($family), + dbesc($key) + ); + + $a->config[$uid][$family][$key] = $value; + + 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($family), + dbesc($key) + ); + return $ret; +}} diff --git a/include/network.php b/include/network.php new file mode 100644 index 0000000000..48e830e84a --- /dev/null +++ b/include/network.php @@ -0,0 +1,669 @@ + 8) || (! $ch)) + return false; + + curl_setopt($ch, CURLOPT_HEADER, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); + curl_setopt($ch, CURLOPT_USERAGENT, "Friendika"); + + $curl_time = intval(get_config('system','curl_timeout')); + curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60)); + + // by default we will allow self-signed certs + // but you can override this + + $check_cert = get_config('system','verifyssl'); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false)); + + $prx = get_config('system','proxy'); + if(strlen($prx)) { + curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); + curl_setopt($ch, CURLOPT_PROXY, $prx); + $prxusr = get_config('system','proxyuser'); + if(strlen($prxusr)) + curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr); + } + if($binary) + curl_setopt($ch, CURLOPT_BINARYTRANSFER,1); + + $a->set_curl_code(0); + + // don't let curl abort the entire application + // if it throws any errors. + + $s = @curl_exec($ch); + + $base = $s; + $curl_info = curl_getinfo($ch); + $http_code = $curl_info['http_code']; + + $header = ''; + + // Pull out multiple headers, e.g. proxy and continuation headers + // allow for HTTP/2.x without fixing code + + while(preg_match('/^HTTP\/[1-2].+? [1-5][0-9][0-9]/',$base)) { + $chunk = substr($base,0,strpos($base,"\r\n\r\n")+4); + $header .= $chunk; + $base = substr($base,strlen($chunk)); + } + + if($http_code == 301 || $http_code == 302 || $http_code == 303 || $http_code == 307) { + $matches = array(); + preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches); + $url = trim(array_pop($matches)); + $url_parsed = @parse_url($url); + if (isset($url_parsed)) { + $redirects++; + return fetch_url($url,$binary,$redirects); + } + } + + $a->set_curl_code($http_code); + + $body = substr($s,strlen($header)); + + $a->set_curl_headers($header); + + curl_close($ch); + return($body); +}} + +// post request to $url. $params is an array of post variables. + +if(! function_exists('post_url')) { +function post_url($url,$params, $headers = null, &$redirects = 0) { + $a = get_app(); + $ch = curl_init($url); + if(($redirects > 8) || (! $ch)) + return false; + + curl_setopt($ch, CURLOPT_HEADER, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); + curl_setopt($ch, CURLOPT_POST,1); + curl_setopt($ch, CURLOPT_POSTFIELDS,$params); + curl_setopt($ch, CURLOPT_USERAGENT, "Friendika"); + + $curl_time = intval(get_config('system','curl_timeout')); + curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60)); + + if(defined('LIGHTTPD')) { + if(!is_array($headers)) { + $headers = array('Expect:'); + } else { + if(!in_array('Expect:', $headers)) { + array_push($headers, 'Expect:'); + } + } + } + if($headers) + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + + $check_cert = get_config('system','verifyssl'); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false)); + $prx = get_config('system','proxy'); + if(strlen($prx)) { + curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); + curl_setopt($ch, CURLOPT_PROXY, $prx); + $prxusr = get_config('system','proxyuser'); + if(strlen($prxusr)) + curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr); + } + + $a->set_curl_code(0); + + // don't let curl abort the entire application + // if it throws any errors. + + $s = @curl_exec($ch); + + $base = $s; + $curl_info = curl_getinfo($ch); + $http_code = $curl_info['http_code']; + + $header = ''; + + // Pull out multiple headers, e.g. proxy and continuation headers + // allow for HTTP/2.x without fixing code + + while(preg_match('/^HTTP\/[1-2].+? [1-5][0-9][0-9]/',$base)) { + $chunk = substr($base,0,strpos($base,"\r\n\r\n")+4); + $header .= $chunk; + $base = substr($base,strlen($chunk)); + } + + if($http_code == 301 || $http_code == 302 || $http_code == 303) { + $matches = array(); + preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches); + $url = trim(array_pop($matches)); + $url_parsed = @parse_url($url); + if (isset($url_parsed)) { + $redirects++; + return post_url($url,$binary,$headers,$redirects); + } + } + $a->set_curl_code($http_code); + $body = substr($s,strlen($header)); + + $a->set_curl_headers($header); + + curl_close($ch); + return($body); +}} + +// Generic XML return +// Outputs a basic dfrn XML status structure to STDOUT, with a variable +// of $st and an optional text of $message and terminates the current process. + +if(! function_exists('xml_status')) { +function xml_status($st, $message = '') { + + $xml_message = ((strlen($message)) ? "\t" . xmlify($message) . "\r\n" : ''); + + if($st) + logger('xml_status returning non_zero: ' . $st . " message=" . $message); + + header( "Content-type: text/xml" ); + echo ''."\r\n"; + echo "\r\n\t$st\r\n$xml_message\r\n"; + killme(); +}} + + + +// convert an XML document to a normalised, case-corrected array +// used by webfinger + +if(! function_exists('convert_xml_element_to_array')) { +function convert_xml_element_to_array($xml_element, &$recursion_depth=0) { + + // If we're getting too deep, bail out + if ($recursion_depth > 512) { + return(null); + } + + if (!is_string($xml_element) && + !is_array($xml_element) && + (get_class($xml_element) == 'SimpleXMLElement')) { + $xml_element_copy = $xml_element; + $xml_element = get_object_vars($xml_element); + } + + if (is_array($xml_element)) { + $result_array = array(); + if (count($xml_element) <= 0) { + return (trim(strval($xml_element_copy))); + } + + foreach($xml_element as $key=>$value) { + + $recursion_depth++; + $result_array[strtolower($key)] = + convert_xml_element_to_array($value, $recursion_depth); + $recursion_depth--; + } + if ($recursion_depth == 0) { + $temp_array = $result_array; + $result_array = array( + strtolower($xml_element_copy->getName()) => $temp_array, + ); + } + + return ($result_array); + + } else { + return (trim(strval($xml_element))); + } +}} + +// Given an email style address, perform webfinger lookup and +// return the resulting DFRN profile URL, or if no DFRN profile URL +// is located, returns an OStatus subscription template (prefixed +// with the string 'stat:' to identify it as on OStatus template). +// If this isn't an email style address just return $s. +// Return an empty string if email-style addresses but webfinger fails, +// or if the resultant personal XRD doesn't contain a supported +// subscription/friend-request attribute. + +if(! function_exists('webfinger_dfrn')) { +function webfinger_dfrn($s) { + if(! strstr($s,'@')) { + return $s; + } + $links = webfinger($s); + logger('webfinger_dfrn: ' . $s . ':' . print_r($links,true), LOGGER_DATA); + if(count($links)) { + foreach($links as $link) + if($link['@attributes']['rel'] === NAMESPACE_DFRN) + return $link['@attributes']['href']; + foreach($links as $link) + if($link['@attributes']['rel'] === NAMESPACE_OSTATUSSUB) + return 'stat:' . $link['@attributes']['template']; + } + return ''; +}} + +// Given an email style address, perform webfinger lookup and +// return the array of link attributes from the personal XRD file. +// On error/failure return an empty array. + + +if(! function_exists('webfinger')) { +function webfinger($s) { + $host = ''; + if(strstr($s,'@')) { + $host = substr($s,strpos($s,'@') + 1); + } + if(strlen($host)) { + $tpl = fetch_lrdd_template($host); + logger('webfinger: lrdd template: ' . $tpl); + if(strlen($tpl)) { + $pxrd = str_replace('{uri}', urlencode('acct:' . $s), $tpl); + logger('webfinger: pxrd: ' . $pxrd); + $links = fetch_xrd_links($pxrd); + if(! count($links)) { + // try with double slashes + $pxrd = str_replace('{uri}', urlencode('acct://' . $s), $tpl); + logger('webfinger: pxrd: ' . $pxrd); + $links = fetch_xrd_links($pxrd); + } + return $links; + } + } + return array(); +}} + +if(! function_exists('lrdd')) { +function lrdd($uri) { + + $a = get_app(); + + // default priority is host priority, host-meta first + + $priority = 'host'; + + // All we have is an email address. Resource-priority is irrelevant + // because our URI isn't directly resolvable. + + if(strstr($uri,'@')) { + return(webfinger($uri)); + } + + // get the host meta file + + $host = @parse_url($uri); + + if($host) { + $url = ((x($host,'scheme')) ? $host['scheme'] : 'http') . '://'; + $url .= $host['host'] . '/.well-known/host-meta' ; + } + else + return array(); + + logger('lrdd: constructed url: ' . $url); + + $xml = fetch_url($url); + $headers = $a->get_curl_headers(); + + if (! $xml) + return array(); + + logger('lrdd: host_meta: ' . $xml, LOGGER_DATA); + + $h = parse_xml_string($xml); + if(! $h) + return array(); + + $arr = convert_xml_element_to_array($h); + + if(isset($arr['xrd']['property'])) { + $property = $arr['crd']['property']; + if(! isset($property[0])) + $properties = array($property); + else + $properties = $property; + foreach($properties as $prop) + if((string) $prop['@attributes'] === 'http://lrdd.net/priority/resource') + $priority = 'resource'; + } + + // save the links in case we need them + + $links = array(); + + if(isset($arr['xrd']['link'])) { + $link = $arr['xrd']['link']; + if(! isset($link[0])) + $links = array($link); + else + $links = $link; + } + + // do we have a template or href? + + if(count($links)) { + foreach($links as $link) { + if($link['@attributes']['rel'] && attribute_contains($link['@attributes']['rel'],'lrdd')) { + if(x($link['@attributes'],'template')) + $tpl = $link['@attributes']['template']; + elseif(x($link['@attributes'],'href')) + $href = $link['@attributes']['href']; + } + } + } + + if((! isset($tpl)) || (! strpos($tpl,'{uri}'))) + $tpl = ''; + + if($priority === 'host') { + if(strlen($tpl)) + $pxrd = str_replace('{uri}', urlencode($uri), $tpl); + elseif(isset($href)) + $pxrd = $href; + if(isset($pxrd)) { + logger('lrdd: (host priority) pxrd: ' . $pxrd); + $links = fetch_xrd_links($pxrd); + return $links; + } + + $lines = explode("\n",$headers); + if(count($lines)) { + foreach($lines as $line) { + if((stristr($line,'link:')) && preg_match('/<([^>].*)>.*rel\=[\'\"]lrdd[\'\"]/',$line,$matches)) { + return(fetch_xrd_links($matches[1])); + break; + } + } + } + } + + + // priority 'resource' + + + $html = fetch_url($uri); + $headers = $a->get_curl_headers(); + logger('lrdd: headers=' . $headers, LOGGER_DEBUG); + + // don't try and parse raw xml as html + if(! strstr($html,'getElementsByTagName('link'); + foreach($items as $item) { + $x = $item->getAttribute('rel'); + if($x == "lrdd") { + $pagelink = $item->getAttribute('href'); + break; + } + } + } + } + + if(isset($pagelink)) + return(fetch_xrd_links($pagelink)); + + // next look in HTTP headers + + $lines = explode("\n",$headers); + if(count($lines)) { + foreach($lines as $line) { + // TODO alter the following regex to support multiple relations (space separated) + if((stristr($line,'link:')) && preg_match('/<([^>].*)>.*rel\=[\'\"]lrdd[\'\"]/',$line,$matches)) { + $pagelink = $matches[1]; + break; + } + // don't try and run feeds through the html5 parser + if(stristr($line,'content-type:') && ((stristr($line,'application/atom+xml')) || (stristr($line,'application/rss+xml')))) + return array(); + if(stristr($html,' 'alias' , 'href' => $alias); + } + } + + logger('fetch_xrd_links: ' . print_r($links,true), LOGGER_DATA); + + return $links; + +}} + + +// Take a URL from the wild, prepend http:// if necessary +// and check DNS to see if it's real +// return true if it's OK, false if something is wrong with it + +if(! function_exists('validate_url')) { +function validate_url(&$url) { + if(substr($url,0,4) != 'http') + $url = 'http://' . $url; + $h = @parse_url($url); + + if(($h) && (dns_get_record($h['host'], DNS_A + DNS_CNAME + DNS_PTR))) { + return true; + } + return false; +}} + +// checks that email is an actual resolvable internet address + +if(! function_exists('validate_email')) { +function validate_email($addr) { + + if(! strpos($addr,'@')) + return false; + $h = substr($addr,strpos($addr,'@') + 1); + + if(($h) && (dns_get_record($h, DNS_A + DNS_CNAME + DNS_PTR + DNS_MX))) { + return true; + } + return false; +}} + +// Check $url against our list of allowed sites, +// wildcards allowed. If allowed_sites is unset return true; +// If url is allowed, return true. +// otherwise, return false + +if(! function_exists('allowed_url')) { +function allowed_url($url) { + + $h = @parse_url($url); + + if(! $h) { + return false; + } + + $str_allowed = get_config('system','allowed_sites'); + if(! $str_allowed) + return true; + + $found = false; + + $host = strtolower($h['host']); + + // always allow our own site + + if($host == strtolower($_SERVER['SERVER_NAME'])) + return true; + + $fnmatch = function_exists('fnmatch'); + $allowed = explode(',',$str_allowed); + + if(count($allowed)) { + foreach($allowed as $a) { + $pat = strtolower(trim($a)); + if(($fnmatch && fnmatch($pat,$host)) || ($pat == $host)) { + $found = true; + break; + } + } + } + return $found; +}} + +// check if email address is allowed to register here. +// Compare against our list (wildcards allowed). +// Returns false if not allowed, true if allowed or if +// allowed list is not configured. + +if(! function_exists('allowed_email')) { +function allowed_email($email) { + + + $domain = strtolower(substr($email,strpos($email,'@') + 1)); + if(! $domain) + return false; + + $str_allowed = get_config('system','allowed_email'); + if(! $str_allowed) + return true; + + $found = false; + + $fnmatch = function_exists('fnmatch'); + $allowed = explode(',',$str_allowed); + + if(count($allowed)) { + foreach($allowed as $a) { + $pat = strtolower(trim($a)); + if(($fnmatch && fnmatch($pat,$domain)) || ($pat == $domain)) { + $found = true; + break; + } + } + } + return $found; +}} + + +if(! function_exists('gravatar_img')) { +function gravatar_img($email) { + $size = 175; + $opt = 'identicon'; // psuedo-random geometric pattern if not found + $rating = 'pg'; + $hash = md5(trim(strtolower($email))); + + $url = 'http://www.gravatar.com/avatar/' . $hash . '.jpg' + . '?s=' . $size . '&d=' . $opt . '&r=' . $rating; + + logger('gravatar: ' . $email . ' ' . $url); + return $url; +}} + + +if(! function_exists('parse_xml_string')) { +function parse_xml_string($s,$strict = true) { + if($strict) { + if(! strstr($s,'code." at ".$err->line.":".$err->column." : ".$err->message, LOGGER_DATA); + libxml_clear_errors(); + } + return $x; +}} diff --git a/include/plugin.php b/include/plugin.php new file mode 100644 index 0000000000..9f2832981a --- /dev/null +++ b/include/plugin.php @@ -0,0 +1,199 @@ +hooks = array(); + $r = q("SELECT * FROM `hook` WHERE 1"); + if(count($r)) { + foreach($r as $rr) { + $a->hooks[] = array($rr['hook'], $rr['file'], $rr['function']); + } + } +}} + + +if(! function_exists('call_hooks')) { +function call_hooks($name, &$data = null) { + $a = get_app(); + + if(count($a->hooks)) { + foreach($a->hooks as $hook) { + if($hook[HOOK_HOOK] === $name) { + @include_once($hook[HOOK_FILE]); + if(function_exists($hook[HOOK_FUNCTION])) { + $func = $hook[HOOK_FUNCTION]; + $func($a,$data); + } + } + } + } +}} + + +/* + * parse plugin comment in search of plugin infos. + * like + * + * * Name: Plugin + * * Description: A plugin which plugs in + * * Version: 1.2.3 + * * Author: John + * * Author: Jane + * * + */ + +if (! function_exists('get_plugin_info')){ +function get_plugin_info($plugin){ + if (!is_file("addon/$plugin/$plugin.php")) return false; + + $f = file_get_contents("addon/$plugin/$plugin.php"); + $r = preg_match("|/\*.*\*/|msU", $f, $m); + + $info=Array( + 'name' => $plugin, + 'description' => "", + 'author' => array(), + 'version' => "" + ); + + if ($r){ + $ll = explode("\n", $m[0]); + foreach( $ll as $l ) { + $l = trim($l,"\t\n\r */"); + if ($l!=""){ + list($k,$v) = array_map("trim", explode(":",$l,2)); + $k= strtolower($k); + if ($k=="author"){ + $r=preg_match("|([^<]+)<([^>]+)>|", $v, $m); + if ($r) { + $info['author'][] = array('name'=>$m[1], 'link'=>$m[2]); + } else { + $info['author'][] = array('name'=>$v); + } + } else { + if (array_key_exists($k,$info)){ + $info[$k]=$v; + } + } + + } + } + + } + return $info; +}} + diff --git a/include/salmon.php b/include/salmon.php index 473432f259..4994655df8 100644 --- a/include/salmon.php +++ b/include/salmon.php @@ -18,37 +18,6 @@ function salmon_key($pubkey) { } -function base64url_encode($s, $strip_padding = false) { - - $s = strtr(base64_encode($s),'+/','-_'); - - if($strip_padding) - $s = str_replace('=','',$s); - - return $s; -} - -function base64url_decode($s) { - -/* - * // Placeholder for new rev of salmon which strips base64 padding. - * // PHP base64_decode handles the un-padded input without requiring this step - * // Uncomment if you find you need it. - * - * $l = strlen($s); - * if(! strpos($s,'=')) { - * $m = $l % 4; - * if($m == 2) - * $s .= '=='; - * if($m == 3) - * $s .= '='; - * } - * - */ - - return base64_decode(strtr($s,'-_','+/')); -} - function get_salmon_key($uri,$keyhash) { $ret = array(); diff --git a/include/text.php b/include/text.php new file mode 100644 index 0000000000..d65c778722 --- /dev/null +++ b/include/text.php @@ -0,0 +1,933 @@ + replace) +// returns substituted string. +// WARNING: this is pretty basic, and doesn't properly handle search strings that are substrings of each other. +// For instance if 'test' => "foo" and 'testing' => "bar", testing could become either bar or fooing, +// depending on the order in which they were declared in the array. + +require_once("include/template_processor.php"); + +if(! function_exists('replace_macros')) { +function replace_macros($s,$r) { + global $t; + + return $t->replace($s,$r); + +}} + + +// random hash, 64 chars + +if(! function_exists('random_string')) { +function random_string() { + return(hash('sha256',uniqid(rand(),true))); +}} + +/** + * This is our primary input filter. + * + * The high bit hack only involved some old IE browser, forget which (IE5/Mac?) + * that had an XSS attack vector due to stripping the high-bit on an 8-bit character + * after cleansing, and angle chars with the high bit set could get through as markup. + * + * This is now disabled because it was interfering with some legitimate unicode sequences + * and hopefully there aren't a lot of those browsers left. + * + * Use this on any text input where angle chars are not valid or permitted + * They will be replaced with safer brackets. This may be filtered further + * if these are not allowed either. + * + */ + +if(! function_exists('notags')) { +function notags($string) { + + return(str_replace(array("<",">"), array('[',']'), $string)); + +// High-bit filter no longer used +// return(str_replace(array("<",">","\xBA","\xBC","\xBE"), array('[',']','','',''), $string)); +}} + +// use this on "body" or "content" input where angle chars shouldn't be removed, +// and allow them to be safely displayed. + +if(! function_exists('escape_tags')) { +function escape_tags($string) { + + return(htmlspecialchars($string)); +}} + + +// generate a string that's random, but usually pronounceable. +// used to generate initial passwords + +if(! function_exists('autoname')) { +function autoname($len) { + + $vowels = array('a','a','ai','au','e','e','e','ee','ea','i','ie','o','ou','u'); + if(mt_rand(0,5) == 4) + $vowels[] = 'y'; + + $cons = array( + 'b','bl','br', + 'c','ch','cl','cr', + 'd','dr', + 'f','fl','fr', + 'g','gh','gl','gr', + 'h', + 'j', + 'k','kh','kl','kr', + 'l', + 'm', + 'n', + 'p','ph','pl','pr', + 'qu', + 'r','rh', + 's','sc','sh','sm','sp','st', + 't','th','tr', + 'v', + 'w','wh', + 'x', + 'z','zh' + ); + + $midcons = array('ck','ct','gn','ld','lf','lm','lt','mb','mm', 'mn','mp', + 'nd','ng','nk','nt','rn','rp','rt'); + + $noend = array('bl', 'br', 'cl','cr','dr','fl','fr','gl','gr', + 'kh', 'kl','kr','mn','pl','pr','rh','tr','qu','wh'); + + $start = mt_rand(0,2); + if($start == 0) + $table = $vowels; + else + $table = $cons; + + $word = ''; + + for ($x = 0; $x < $len; $x ++) { + $r = mt_rand(0,count($table) - 1); + $word .= $table[$r]; + + if($table == $vowels) + $table = array_merge($cons,$midcons); + else + $table = $vowels; + + } + + $word = substr($word,0,$len); + + foreach($noend as $noe) { + if((strlen($word) > 2) && (substr($word,-2) == $noe)) { + $word = substr($word,0,-1); + break; + } + } + if(substr($word,-1) == 'q') + $word = substr($word,0,-1); + return $word; +}} + + +// escape text ($str) for XML transport +// returns escaped text. + +if(! function_exists('xmlify')) { +function xmlify($str) { + $buffer = ''; + + for($x = 0; $x < strlen($str); $x ++) { + $char = $str[$x]; + + switch( $char ) { + + case "\r" : + break; + case "&" : + $buffer .= '&'; + break; + case "'" : + $buffer .= '''; + break; + case "\"" : + $buffer .= '"'; + break; + case '<' : + $buffer .= '<'; + break; + case '>' : + $buffer .= '>'; + break; + case "\n" : + $buffer .= "\n"; + break; + default : + $buffer .= $char; + break; + } + } + $buffer = trim($buffer); + return($buffer); +}} + +// undo an xmlify +// pass xml escaped text ($s), returns unescaped text + +if(! function_exists('unxmlify')) { +function unxmlify($s) { + $ret = str_replace('&','&', $s); + $ret = str_replace(array('<','>','"','''),array('<','>','"',"'"),$ret); + return $ret; +}} + +// convenience wrapper, reverse the operation "bin2hex" + +if(! function_exists('hex2bin')) { +function hex2bin($s) { + if(! ctype_xdigit($s)) { + logger('hex2bin: illegal input: ' . print_r(debug_backtrace(), true)); + return($s); + } + + return(pack("H*",$s)); +}} + +// Automatic pagination. +// To use, get the count of total items. +// Then call $a->set_pager_total($number_items); +// Optionally call $a->set_pager_itemspage($n) to the number of items to display on each page +// Then call paginate($a) after the end of the display loop to insert the pager block on the page +// (assuming there are enough items to paginate). +// When using with SQL, the setting LIMIT %d, %d => $a->pager['start'],$a->pager['itemspage'] +// will limit the results to the correct items for the current page. +// The actual page handling is then accomplished at the application layer. + +if(! function_exists('paginate')) { +function paginate(&$a) { + $o = ''; + $stripped = preg_replace('/(&page=[0-9]*)/','',$a->query_string); + $stripped = str_replace('q=','',$stripped); + $stripped = trim($stripped,'/'); + $pagenum = $a->pager['page']; + $url = $a->get_baseurl() . '/' . $stripped; + + + if($a->pager['total'] > $a->pager['itemspage']) { + $o .= '
'; + if($a->pager['page'] != 1) + $o .= ''."pager['page'] - 1).'">' . t('prev') . ' '; + + $o .= "" . t('first') . " "; + + $numpages = $a->pager['total'] / $a->pager['itemspage']; + + $numstart = 1; + $numstop = $numpages; + + if($numpages > 14) { + $numstart = (($pagenum > 7) ? ($pagenum - 7) : 1); + $numstop = (($pagenum > ($numpages - 7)) ? $numpages : ($numstart + 14)); + } + + for($i = $numstart; $i <= $numstop; $i++){ + if($i == $a->pager['page']) + $o .= ''.(($i < 10) ? ' '.$i : $i); + else + $o .= "".(($i < 10) ? ' '.$i : $i).""; + $o .= ' '; + } + + if(($a->pager['total'] % $a->pager['itemspage']) != 0) { + if($i == $a->pager['page']) + $o .= ''.(($i < 10) ? ' '.$i : $i); + else + $o .= "".(($i < 10) ? ' '.$i : $i).""; + $o .= ' '; + } + + $lastpage = (($numpages > intval($numpages)) ? intval($numpages)+1 : $numpages); + $o .= "" . t('last') . " "; + + if(($a->pager['total'] - ($a->pager['itemspage'] * $a->pager['page'])) > 0) + $o .= ''."pager['page'] + 1).'">' . t('next') . ''; + $o .= '
'."\r\n"; + } + return $o; +}} + +// Turn user/group ACLs stored as angle bracketed text into arrays + +if(! function_exists('expand_acl')) { +function expand_acl($s) { + // turn string array of angle-bracketed elements into numeric array + // e.g. "<1><2><3>" => array(1,2,3); + $ret = array(); + + if(strlen($s)) { + $t = str_replace('<','',$s); + $a = explode('>',$t); + foreach($a as $aa) { + if(intval($aa)) + $ret[] = intval($aa); + } + } + return $ret; +}} + +// Used to wrap ACL elements in angle brackets for storage + +if(! function_exists('sanitise_acl')) { +function sanitise_acl(&$item) { + if(intval($item)) + $item = '<' . intval(notags(trim($item))) . '>'; + else + unset($item); +}} + + +// Convert an ACL array to a storable string + +if(! function_exists('perms2str')) { +function perms2str($p) { + $ret = ''; + $tmp = $p; + if(is_array($tmp)) { + array_walk($tmp,'sanitise_acl'); + $ret = implode('',$tmp); + } + return $ret; +}} + +// generate a guaranteed unique (for this domain) item ID for ATOM +// safe from birthday paradox + +if(! function_exists('item_new_uri')) { +function item_new_uri($hostname,$uid) { + + do { + $dups = false; + $hash = random_string(); + + $uri = "urn:X-dfrn:" . $hostname . ':' . $uid . ':' . $hash; + + $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' LIMIT 1", + dbesc($uri)); + if(count($r)) + $dups = true; + } while($dups == true); + return $uri; +}} + +// Generate a guaranteed unique photo ID. +// safe from birthday paradox + +if(! function_exists('photo_new_resource')) { +function photo_new_resource() { + + do { + $found = false; + $resource = hash('md5',uniqid(mt_rand(),true)); + $r = q("SELECT `id` FROM `photo` WHERE `resource-id` = '%s' LIMIT 1", + dbesc($resource) + ); + if(count($r)) + $found = true; + } while($found == true); + return $resource; +}} + + +// wrapper to load a view template, checking for alternate +// languages before falling back to the default + +// obsolete, deprecated. + +if(! function_exists('load_view_file')) { +function load_view_file($s) { + global $lang, $a; + if(! isset($lang)) + $lang = 'en'; + $b = basename($s); + $d = dirname($s); + if(file_exists("$d/$lang/$b")) + return file_get_contents("$d/$lang/$b"); + + $theme = current_theme(); + + if(file_exists("$d/theme/$theme/$b")) + return file_get_contents("$d/theme/$theme/$b"); + + return file_get_contents($s); +}} + +if(! function_exists('get_intltext_template')) { +function get_intltext_template($s) { + global $lang; + + if(! isset($lang)) + $lang = 'en'; + + if(file_exists("view/$lang/$s")) + return file_get_contents("view/$lang/$s"); + elseif(file_exists("view/en/$s")) + return file_get_contents("view/en/$s"); + else + return file_get_contents("view/$s"); +}} + +if(! function_exists('get_markup_template')) { +function get_markup_template($s) { + + $theme = current_theme(); + + if(file_exists("view/theme/$theme/$s")) + return file_get_contents("view/theme/$theme/$s"); + else + return file_get_contents("view/$s"); + +}} + + + + + +// for html,xml parsing - let's say you've got +// an attribute foobar="class1 class2 class3" +// and you want to find out if it contains 'class3'. +// you can't use a normal sub string search because you +// might match 'notclass3' and a regex to do the job is +// possible but a bit complicated. +// pass the attribute string as $attr and the attribute you +// are looking for as $s - returns true if found, otherwise false + +if(! function_exists('attribute_contains')) { +function attribute_contains($attr,$s) { + $a = explode(' ', $attr); + if(count($a) && in_array($s,$a)) + return true; + return false; +}} + +if(! function_exists('logger')) { +function logger($msg,$level = 0) { + $debugging = get_config('system','debugging'); + $loglevel = intval(get_config('system','loglevel')); + $logfile = get_config('system','logfile'); + + if((! $debugging) || (! $logfile) || ($level > $loglevel)) + return; + + @file_put_contents($logfile, datetime_convert() . ':' . session_id() . ' ' . $msg . "\n", FILE_APPEND); + return; +}} + + +if(! function_exists('activity_match')) { +function activity_match($haystack,$needle) { + if(($haystack === $needle) || ((basename($needle) === $haystack) && strstr($needle,NAMESPACE_ACTIVITY_SCHEMA))) + return true; + return false; +}} + + +// Pull out all #hashtags and @person tags from $s; +// We also get @person@domain.com - which would make +// the regex quite complicated as tags can also +// end a sentence. So we'll run through our results +// and strip the period from any tags which end with one. +// Returns array of tags found, or empty array. + + +if(! function_exists('get_tags')) { +function get_tags($s) { + $ret = array(); + + // ignore anything in a code block + + $s = preg_replace('/\[code\](.*?)\[\/code\]/sm','',$s); + + // Match full names against @tags including the space between first and last + // We will look these up afterward to see if they are full names or not recognisable. + + if(preg_match_all('/(@[^ \x0D\x0A,:?]+ [^ \x0D\x0A,:?]+)([ \x0D\x0A,:?]|$)/',$s,$match)) { + foreach($match[1] as $mtch) { + if(strstr($mtch,"]")) { + // we might be inside a bbcode color tag - leave it alone + continue; + } + if(substr($mtch,-1,1) === '.') + $ret[] = substr($mtch,0,-1); + else + $ret[] = $mtch; + } + } + + // Otherwise pull out single word tags. These can be @nickname, @first_last + // and #hash tags. + + if(preg_match_all('/([@#][^ \x0D\x0A,:?]+)([ \x0D\x0A,:?]|$)/',$s,$match)) { + foreach($match[1] as $mtch) { + if(strstr($mtch,"]")) { + // we might be inside a bbcode color tag - leave it alone + continue; + } + // ignore strictly numeric tags like #1 + if((strpos($mtch,'#') === 0) && ctype_digit(substr($mtch,1))) + continue; + if(substr($mtch,-1,1) === '.') + $ret[] = substr($mtch,0,-1); + else + $ret[] = $mtch; + } + } + return $ret; +}} + + +// quick and dirty quoted_printable encoding + +if(! function_exists('qp')) { +function qp($s) { +return str_replace ("%","=",rawurlencode($s)); +}} + + + +if(! function_exists('get_mentions')) { +function get_mentions($item) { + $o = ''; + if(! strlen($item['tag'])) + return $o; + + $arr = explode(',',$item['tag']); + foreach($arr as $x) { + $matches = null; + if(preg_match('/@\[url=([^\]]*)\]/',$x,$matches)) { + $o .= "\t\t" . '' . "\r\n"; + $o .= "\t\t" . '' . "\r\n"; + } + } + return $o; +}} + +if(! function_exists('contact_block')) { +function contact_block() { + $o = ''; + $a = get_app(); + + $shown = get_pconfig($a->profile['uid'],'system','display_friend_count'); + if(! $shown) + $shown = 24; + + if((! is_array($a->profile)) || ($a->profile['hide-friends'])) + return $o; + $r = q("SELECT COUNT(*) AS `total` FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 and `pending` = 0", + intval($a->profile['uid']) + ); + if(count($r)) { + $total = intval($r[0]['total']); + } + if(! $total) { + $o .= '

' . t('No contacts') . '

'; + return $o; + } + $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 and `pending` = 0 ORDER BY RAND() LIMIT %d", + intval($a->profile['uid']), + intval($shown) + ); + if(count($r)) { + $o .= '

' . sprintf( tt('%d Contact','%d Contacts', $total),$total) . '

'; + foreach($r as $rr) { + $o .= micropro($rr,true,'mpfriend'); + } + $o .= '
'; + $o .= ''; + + } + + $arr = array('contacts' => $r, 'output' => $o); + + call_hooks('contact_block_end', $arr); + return $o; + +}} + +if(! function_exists('micropro')) { +function micropro($contact, $redirect = false, $class = '', $textmode = false) { + + if($class) + $class = ' ' . $class; + + $url = $contact['url']; + $sparkle = ''; + + if($redirect) { + $a = get_app(); + $redirect_url = $a->get_baseurl() . '/redir/' . $contact['id']; + if(local_user() && ($contact['uid'] == local_user()) && ($contact['network'] === 'dfrn')) { + $url = $redirect_url; + $sparkle = ' sparkle'; + } + } + $click = ((x($contact,'click')) ? ' onclick="' . $contact['click'] . '" ' : ''); + if($click) + $url = ''; + if($textmode) { + return '' . "\r\n"; + } + else { + return '
' . $contact['name'] 
+			. '
' . "\r\n"; + } +}} + + + +if(! function_exists('search')) { +function search($s,$id='search-box',$url='/search') { + $a = get_app(); + $o = '
'; + $o .= '
'; + $o .= ''; + $o .= ''; + $o .= '
'; + return $o; +}} + +if(! function_exists('valid_email')) { +function valid_email($x){ + if(preg_match('/^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+$/',$x)) + return true; + return false; +}} + + +if(! function_exists('aes_decrypt')) { +function aes_decrypt($val,$ky) +{ + $key="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + for($a=0;$a=0 and ord(substr($dec, strlen($dec)-1,1))<=16)? chr(ord( substr($dec,strlen($dec)-1,1))):null)); +}} + + +if(! function_exists('aes_encrypt')) { +function aes_encrypt($val,$ky) +{ + $key="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + for($a=0;$a$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(); + + return str_replace( + array( '<3', '</3', '<\\3', ':-)', ':)', ';-)', ':-(', ':(', ':-P', ':P', ':-"', ':-x', ':-X', ':-D', '8-|', '8-O'), + array( + '<3', + '</3', + '<\\3', + ':-)', + ':)', + ';-)', + ':-(', + ':(', + ':-P', + ':P', + ':-\', + ':-x', + ':-X', + ':-D', + '8-|', + '8-O' + ), $s); +}} + + + +if(! function_exists('day_translate')) { +function day_translate($s) { + $ret = str_replace(array('Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'), + array( t('Monday'), t('Tuesday'), t('Wednesday'), t('Thursday'), t('Friday'), t('Saturday'), t('Sunday')), + $s); + + $ret = str_replace(array('January','February','March','April','May','June','July','August','September','October','November','December'), + array( t('January'), t('February'), t('March'), t('April'), t('May'), t('June'), t('July'), t('August'), t('September'), t('October'), t('November'), t('December')), + $ret); + + return $ret; +}} + + +if(! function_exists('normalise_link')) { +function normalise_link($url) { + $ret = str_replace(array('https:','//www.'), array('http:','//'), $url); + return(rtrim($ret,'/')); +}} + +/** + * + * Compare two URLs to see if they are the same, but ignore + * slight but hopefully insignificant differences such as if one + * is https and the other isn't, or if one is www.something and + * the other isn't - and also ignore case differences. + * + * Return true if the URLs match, otherwise false. + * + */ + +if(! function_exists('link_compare')) { +function link_compare($a,$b) { + if(strcasecmp(normalise_link($a),normalise_link($b)) === 0) + return true; + return false; +}} + + +if(! function_exists('prepare_body')) { +function prepare_body($item,$attach = false) { + + $s = prepare_text($item['body']); + if(! $attach) + return $s; + + $arr = explode(',',$item['attach']); + if(count($arr)) { + $s .= '
'; + foreach($arr as $r) { + $matches = false; + $icon = ''; + $cnt = preg_match('|\[attach\]href=\"(.*?)\" size=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches); + if($cnt) { + $icontype = strtolower(substr($matches[3],0,strpos($matches[3],'/'))); + switch($icontype) { + case 'video': + case 'audio': + case 'image': + case 'text': + $icon = '
'; + break; + default: + $icon = '
'; + break; + } + $title = ((strlen(trim($matches[4]))) ? escape_tags(trim($matches[4])) : escape_tags($matches[1])); + $title .= ' ' . $matches[2] . ' ' . t('bytes'); + + $s .= '' . $icon . ''; + } + } + $s .= '
'; + } + return $s; +}} + +if(! function_exists('prepare_text')) { +function prepare_text($text) { + + require_once('include/bbcode.php'); + + $s = smilies(bbcode($text)); + + return $s; +}} + + +/** + * return atom link elements for all of our hubs + */ + +if(! function_exists('feed_hublinks')) { +function feed_hublinks() { + + $hub = get_config('system','huburl'); + + $hubxml = ''; + if(strlen($hub)) { + $hubs = explode(',', $hub); + if(count($hubs)) { + foreach($hubs as $h) { + $h = trim($h); + if(! strlen($h)) + continue; + $hubxml .= '' . "\n" ; + } + } + } + return $hubxml; +}} + +/* return atom link elements for salmon endpoints */ + +if(! function_exists('feed_salmonlinks')) { +function feed_salmonlinks($nick) { + + $a = get_app(); + + $salmon = '' . "\n" ; + + // old style links that status.net still needed as of 12/2010 + + $salmon .= ' ' . "\n" ; + $salmon .= ' ' . "\n" ; + return $salmon; +}} + +if(! function_exists('get_plink')) { +function get_plink($item) { + $a = get_app(); + $plink = (((x($item,'plink')) && (! $item['private'])) ? '' : ''); + return $plink; +}} + +if(! function_exists('unamp')) { +function unamp($s) { + return str_replace('&', '&', $s); +}} + + + + +if(! function_exists('lang_selector')) { +function lang_selector() { + global $lang; + $o = '
'; + $o .= ''; + return $o; +}} + + +if(! function_exists('return_bytes')) { +function return_bytes ($size_str) { + switch (substr ($size_str, -1)) + { + case 'M': case 'm': return (int)$size_str * 1048576; + case 'K': case 'k': return (int)$size_str * 1024; + case 'G': case 'g': return (int)$size_str * 1073741824; + default: return $size_str; + } +}} + +function generate_guid() { + $found = true; + do { + $guid = substr(random_string(),0,16); + $x = q("SELECT `uid` FROM `user` WHERE `guid` = '%s' LIMIT 1", + dbesc($guid) + ); + if(! count($x)) + $found = false; + } while ($found == true ); + return $guid; +} + + +function pkcs5_pad ($text, $blocksize) +{ + $pad = $blocksize - (strlen($text) % $blocksize); + return $text . str_repeat(chr($pad), $pad); +} + +function pkcs5_unpad($text) +{ + $pad = ord($text{strlen($text)-1}); + if ($pad > strlen($text)) return false; + if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false; + return substr($text, 0, -1 * $pad); +} + + +function base64url_encode($s, $strip_padding = false) { + + $s = strtr(base64_encode($s),'+/','-_'); + + if($strip_padding) + $s = str_replace('=','',$s); + + return $s; +} + +function base64url_decode($s) { + +/* + * // Placeholder for new rev of salmon which strips base64 padding. + * // PHP base64_decode handles the un-padded input without requiring this step + * // Uncomment if you find you need it. + * + * $l = strlen($s); + * if(! strpos($s,'=')) { + * $m = $l % 4; + * if($m == 2) + * $s .= '=='; + * if($m == 3) + * $s .= '='; + * } + * + */ + + return base64_decode(strtr($s,'-_','+/')); +} From 2637831d9056862f7c3db718702116ef4652629a Mon Sep 17 00:00:00 2001 From: Friendika Date: Mon, 1 Aug 2011 21:02:25 -0700 Subject: [PATCH 040/123] some more zot changes migrating back to f9a mainline --- addon/facebook/facebook.php | 2 +- boot.php | 32 ++++++++++++++++++++++++++++++-- include/auth.php | 12 ++++++------ include/hostxrd.php | 5 ++--- mod/dfrn_confirm.php | 2 +- mod/dfrn_poll.php | 8 ++++---- mod/dfrn_request.php | 6 +++--- mod/friendika.php | 4 ++-- mod/lostpass.php | 8 ++++---- mod/notifications.php | 4 ++-- mod/openid.php | 10 +++++----- mod/redir.php | 4 ++-- mod/register.php | 6 +++--- mod/removeme.php | 2 +- view/xrd_host.tpl | 2 +- 15 files changed, 67 insertions(+), 40 deletions(-) diff --git a/addon/facebook/facebook.php b/addon/facebook/facebook.php index 73518afb2d..10db7652a9 100644 --- a/addon/facebook/facebook.php +++ b/addon/facebook/facebook.php @@ -564,7 +564,7 @@ function facebook_post_hook(&$a,&$b) { $msg = preg_replace("/\[img\](.*?)\[\/img\]/is", t('Image: ') . '$1', $msg); - if((strpos($link,$a->get_baseurl()) !== false) && (! $image)) + if((strpos($link,z_root()) !== false) && (! $image)) $image = $a->get_baseurl() . '/images/friendika-64.jpg'; $msg = trim(strip_tags(bbcode($msg))); diff --git a/boot.php b/boot.php index 35cb152392..fd5de0e94b 100644 --- a/boot.php +++ b/boot.php @@ -325,7 +325,7 @@ class App { if($this->cmd === '.well-known/host-meta') { require_once('include/hostxrd.php'); - hostxrd($this->get_baseurl()); + hostxrd(); // NOTREACHED } @@ -402,7 +402,7 @@ class App { $this->page['title'] = $this->config['sitename']; $tpl = file_get_contents('view/head.tpl'); $this->page['htmlhead'] = replace_macros($tpl,array( - '$baseurl' => $this->get_baseurl(), + '$baseurl' => $this->get_baseurl(), // FIXME for z_path!!!! '$generator' => 'Friendika' . ' ' . FRIENDIKA_VERSION, '$delitem' => t('Delete this item?'), '$comment' => t('Comment') @@ -475,6 +475,34 @@ function system_unavailable() { }} + +function clean_urls() { + global $a; +// if($a->config['system']['clean_urls']) + return true; +// return false; +} + +function z_path() { + global $a; + $base = $a->get_baseurl(); + if(! clean_urls()) + $base .= '/?q='; + return $base; +} + +function z_root() { + global $a; + return $a->get_baseurl(); +} + +function absurl($path) { + if(strpos($path,'/') === 0) + return z_path() . $path; + return $path; +} + + // Primarily involved with database upgrade, but also sets the // base url for use in cmdline programs which don't have // $_SERVER variables, and synchronising the state of installed plugins. diff --git a/include/auth.php b/include/auth.php index d1eb9d131c..768af626fb 100644 --- a/include/auth.php +++ b/include/auth.php @@ -25,7 +25,7 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p nuke_session(); info( t('Logged out.') . EOL); - goaway($a->get_baseurl()); + goaway(z_root()); } if(x($_SESSION,'visitor_id') && (! x($_SESSION,'uid'))) { @@ -45,7 +45,7 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p // extra paranoia - if the IP changed, log them out if($check && ($_SESSION['addr'] != $_SERVER['REMOTE_ADDR'])) { nuke_session(); - goaway($a->get_baseurl()); + goaway(z_root()); } $r = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1", @@ -54,7 +54,7 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p if(! count($r)) { nuke_session(); - goaway($a->get_baseurl()); + goaway(z_root()); } // initialise user environment @@ -118,7 +118,7 @@ else { if(($noid) || (strpos($temp_string,'@')) || (! validate_url($temp_string))) { $a = get_app(); notice( t('Login failed.') . EOL); - goaway($a->get_baseurl()); + goaway(z_root()); // NOTREACHED } @@ -143,7 +143,7 @@ else { if($a->config['register_policy'] == REGISTER_CLOSED) { $a = get_app(); notice( t('Login failed.') . EOL); - goaway($a->get_baseurl()); + goaway(z_root()); // NOTREACHED } // new account @@ -196,7 +196,7 @@ else { if((! $record) || (! count($record))) { logger('authenticate: failed login attempt: ' . trim($_POST['openid_url'])); notice( t('Login failed.') . EOL ); - goaway($a->get_baseurl()); + goaway(z_root()); } $_SESSION['uid'] = $record['uid']; diff --git a/include/hostxrd.php b/include/hostxrd.php index 7040f927d6..18c3e4b1ed 100644 --- a/include/hostxrd.php +++ b/include/hostxrd.php @@ -1,11 +1,10 @@ get_baseurl()); + goaway(z_root()); // NOTREACHED } diff --git a/mod/dfrn_poll.php b/mod/dfrn_poll.php index 76803ef1b4..09f198b814 100644 --- a/mod/dfrn_poll.php +++ b/mod/dfrn_poll.php @@ -61,7 +61,7 @@ function dfrn_poll_init(&$a) { $my_id = '0:' . $dfrn_id; break; default: - goaway($a->get_baseurl()); + goaway(z_root()); break; // NOTREACHED } @@ -100,7 +100,7 @@ function dfrn_poll_init(&$a) { $profile = $r[0]['nickname']; goaway((strlen($destination_url)) ? $destination_url : $a->get_baseurl() . '/profile/' . $profile); } - goaway($a->get_baseurl()); + goaway(z_root()); } @@ -297,7 +297,7 @@ function dfrn_poll_post(&$a) { $my_id = '0:' . $dfrn_id; break; default: - goaway($a->get_baseurl()); + goaway(z_root()); break; // NOTREACHED } @@ -420,7 +420,7 @@ function dfrn_poll_content(&$a) { $my_id = '0:' . $dfrn_id; break; default: - goaway($a->get_baseurl()); + goaway(z_root()); break; // NOTREACHED } diff --git a/mod/dfrn_request.php b/mod/dfrn_request.php index c9811fa60c..6b9558b86f 100644 --- a/mod/dfrn_request.php +++ b/mod/dfrn_request.php @@ -48,7 +48,7 @@ function dfrn_request_post(&$a) { if($_POST['cancel']) { - goaway($a->get_baseurl()); + goaway(z_root()); } @@ -190,7 +190,7 @@ function dfrn_request_post(&$a) { // invalid/bogus request notice( t('Unrecoverable protocol error.') . EOL ); - goaway($a->get_baseurl()); + goaway(z_root()); return; // NOTREACHED } @@ -602,7 +602,7 @@ function dfrn_request_content(&$a) { $myaddr = $a->get_baseurl() . '/profile/' . $a->user['nickname']; } else { - $myaddr = $a->user['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3 ); + $myaddr = $a->user['nickname'] . '@' . substr(z_root(), strpos(z_root(),'://') + 3 ); } } elseif(x($_GET,'addr')) { diff --git a/mod/friendika.php b/mod/friendika.php index 753a9f478f..d0e709c753 100644 --- a/mod/friendika.php +++ b/mod/friendika.php @@ -16,7 +16,7 @@ function friendika_init(&$a) { $data = Array( 'version' => FRIENDIKA_VERSION, - 'url' => $a->get_baseurl(), + 'url' => z_root(), 'plugins' => $a->plugins, 'register_policy' => $register_policy[$a->config['register_policy']], 'admin' => $admin, @@ -40,7 +40,7 @@ function friendika_content(&$a) { $o .= '

'; $o .= t('This is Friendika version') . ' ' . FRIENDIKA_VERSION . ' '; - $o .= t('running at web location') . ' ' . $a->get_baseurl() . '

'; + $o .= t('running at web location') . ' ' . z_root() . '

'; $o .= t('Shared content within the Friendika network is provided under the Creative Commons Attribution 3.0 license') . '

'; diff --git a/mod/lostpass.php b/mod/lostpass.php index 3453a0db43..b71398fa4b 100644 --- a/mod/lostpass.php +++ b/mod/lostpass.php @@ -5,7 +5,7 @@ function lostpass_post(&$a) { $email = notags(trim($_POST['login-name'])); if(! $email) - goaway($a->get_baseurl()); + goaway(z_root()); $r = q("SELECT * FROM `user` WHERE ( `email` = '%s' OR `nickname` = '%s' ) AND `verified` = 1 AND `blocked` = 0 LIMIT 1", dbesc($email), @@ -14,7 +14,7 @@ function lostpass_post(&$a) { if(! count($r)) { notice( t('No valid account found.') . EOL); - goaway($a->get_baseurl()); + goaway(z_root()); } $uid = $r[0]['uid']; @@ -46,7 +46,7 @@ function lostpass_post(&$a) { . 'Content-transfer-encoding: 8bit' ); - goaway($a->get_baseurl()); + goaway(z_root()); } @@ -62,7 +62,7 @@ function lostpass_content(&$a) { ); if(! count($r)) { notice( t("Request could not be verified. \x28You may have previously submitted it.\x29 Password reset failed.") . EOL); - goaway($a->get_baseurl()); + goaway(z_root()); return; } $uid = $r[0]['uid']; diff --git a/mod/notifications.php b/mod/notifications.php index c6f073058d..244563adc4 100644 --- a/mod/notifications.php +++ b/mod/notifications.php @@ -3,7 +3,7 @@ function notifications_post(&$a) { if(! local_user()) { - goaway($a->get_baseurl()); + goaway(z_root()); } $request_id = (($a->argc > 1) ? $a->argv[1] : 0); @@ -60,7 +60,7 @@ function notifications_content(&$a) { if(! local_user()) { notice( t('Permission denied.') . EOL); - goaway($a->get_baseurl()); + return; } $o = ''; diff --git a/mod/openid.php b/mod/openid.php index 537d84ce38..6fbd013b8d 100644 --- a/mod/openid.php +++ b/mod/openid.php @@ -8,7 +8,7 @@ function openid_content(&$a) { $noid = get_config('system','no_openid'); if($noid) - goaway($a->get_baseurl()); + goaway(z_root()); if((x($_GET,'openid_mode')) && (x($_SESSION,'openid'))) { $openid = new LightOpenID; @@ -49,7 +49,7 @@ function openid_content(&$a) { if($a->config['register_policy'] != REGISTER_CLOSED) goaway($a->get_baseurl() . '/register' . $args); else - goaway($a->get_baseurl()); + goaway(z_root()); // NOTREACHED } @@ -60,7 +60,7 @@ function openid_content(&$a) { ); if(! count($r)) { notice( t('Login failed.') . EOL ); - goaway($a->get_baseurl()); + goaway(z_root()); } unset($_SESSION['openid']); @@ -116,10 +116,10 @@ function openid_content(&$a) { if(($a->module !== 'home') && isset($_SESSION['return_url'])) goaway($a->get_baseurl() . '/' . $_SESSION['return_url']); else - goaway($a->get_baseurl()); + goaway(z_root()); } } notice( t('Login failed.') . EOL); - goaway($a->get_baseurl()); + goaway(z_root()); // NOTREACHED } diff --git a/mod/redir.php b/mod/redir.php index 8d0c8f4b3a..eb9702b041 100644 --- a/mod/redir.php +++ b/mod/redir.php @@ -3,7 +3,7 @@ function redir_init(&$a) { if((! local_user()) || (! ($a->argc == 2)) || (! intval($a->argv[1]))) - goaway($a->get_baseurl()); + goaway(z_root()); $cid = $a->argv[1]; $url = ((x($_GET,'url')) ? $_GET['url'] : ''); @@ -13,7 +13,7 @@ function redir_init(&$a) { ); if((! count($r)) || ($r[0]['network'] !== 'dfrn')) - goaway($a->get_baseurl()); + goaway(z_root()); $dfrn_id = $orig_id = (($r[0]['issued-id']) ? $r[0]['issued-id'] : $r[0]['dfrn-id']); diff --git a/mod/register.php b/mod/register.php index 0906395d2d..a8d81f1f6b 100644 --- a/mod/register.php +++ b/mod/register.php @@ -373,7 +373,7 @@ function register_post(&$a) { if($res) { info( t('Registration successful. Please check your email for further instructions.') . EOL ) ; - goaway($a->get_baseurl()); + goaway(z_root()); } else { notice( t('Failed to send email message. Here is the message that failed.') . $email_tpl . EOL ); @@ -382,7 +382,7 @@ function register_post(&$a) { elseif($a->config['register_policy'] == REGISTER_APPROVE) { if(! strlen($a->config['admin_email'])) { notice( t('Your registration can not be processed.') . EOL); - goaway($a->get_baseurl()); + goaway(z_root()); } $hash = random_string(); @@ -428,7 +428,7 @@ function register_post(&$a) { if($res) { info( t('Your registration is pending approval by the site owner.') . EOL ) ; - goaway($a->get_baseurl()); + goaway(z_root()); } } diff --git a/mod/removeme.php b/mod/removeme.php index 6a56963b6c..c181ee6ea1 100644 --- a/mod/removeme.php +++ b/mod/removeme.php @@ -29,7 +29,7 @@ function removeme_post(&$a) { function removeme_content(&$a) { if(! local_user()) - goaway($a->get_baseurl()); + goaway(z_root()); $hash = random_string(); diff --git a/view/xrd_host.tpl b/view/xrd_host.tpl index c6184e306c..f1d9707d57 100644 --- a/view/xrd_host.tpl +++ b/view/xrd_host.tpl @@ -2,7 +2,7 @@ - $domain + $zroot From b7741276162318b7c592cfce03c2bb7bd86d23a9 Mon Sep 17 00:00:00 2001 From: Friendika Date: Tue, 2 Aug 2011 17:04:03 -0700 Subject: [PATCH 041/123] bug #114, warn about linked Facebook item privacy --- addon/facebook/facebook.php | 23 ++++++++++++++++------- boot.php | 2 +- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/addon/facebook/facebook.php b/addon/facebook/facebook.php index 10db7652a9..c3261af09d 100644 --- a/addon/facebook/facebook.php +++ b/addon/facebook/facebook.php @@ -111,13 +111,13 @@ function facebook_init(&$a) { $token = substr($token,0,strpos($token,'&')); set_pconfig($uid,'facebook','access_token',$token); set_pconfig($uid,'facebook','post','1'); + set_pconfig($uid,'facebook','no_linking',1); fb_get_self($uid); fb_get_friends($uid); fb_consume_all($uid); } - // todo: is this a browser session or a server session? where do we go? } } @@ -258,6 +258,8 @@ function fb_get_friends($uid) { } } +// This is the POST method to the facebook settings page +// Content is posted to Facebook in the function facebook_post_hook() function facebook_post(&$a) { @@ -298,6 +300,8 @@ function facebook_post(&$a) { return; } +// Facebook settings form + function facebook_content(&$a) { if(! local_user()) { @@ -347,14 +351,18 @@ function facebook_content(&$a) { $o .= '

'; $post_by_default = get_pconfig(local_user(),'facebook','post_by_default'); $checked = (($post_by_default) ? ' checked="checked" ' : ''); - $o .= '' . ' ' . t('Post to Facebook by default') . '
'; + $o .= '' . ' ' . t('Post to Facebook by default') . EOL; $no_linking = get_pconfig(local_user(),'facebook','no_linking'); $checked = (($no_linking) ? '' : ' checked="checked" '); - $o .= '' . ' ' . t('Link all your Facebook friends and conversations') . '
'; - - + $o .= '' . ' ' . t('Link all your Facebook friends and conversations') . EOL ; + $hidden = (($a->user['hidewall'] || get_config('system','block_public')) ? true : false); + if(! $hidden) { + $o .= EOL; + $o .= t('Warning: Your Facebook privacy settings can not be imported.') . EOL; + $o .= t('Linked Facebook items may be publicly visible, depending on your privacy settings for this website/account.') . EOL; + } $o .= '
'; } @@ -746,6 +754,8 @@ function fb_consume_all($uid) { $access_token = get_pconfig($uid,'facebook','access_token'); if(! $access_token) return; + + $s = fetch_url('https://graph.facebook.com/me/feed?access_token=' . $access_token); if($s) { $j = json_decode($s); @@ -772,13 +782,12 @@ function fb_consume_stream($uid,$j,$wall = false) { intval($uid) ); - $user = q("SELECT `nickname` FROM `user` WHERE `uid` = %d LIMIT 1", + $user = q("SELECT `nickname`, `blockwall` FROM `user` WHERE `uid` = %d LIMIT 1", intval($uid) ); if(count($user)) $my_local_url = $a->get_baseurl() . '/profile/' . $user[0]['nickname']; - $self_id = get_pconfig($uid,'facebook','self_id'); if(! count($j->data) || (! strlen($self_id))) return; diff --git a/boot.php b/boot.php index fd5de0e94b..5f8402ecdb 100644 --- a/boot.php +++ b/boot.php @@ -7,7 +7,7 @@ require_once('include/text.php'); require_once("include/pgettext.php"); -define ( 'FRIENDIKA_VERSION', '2.2.1059' ); +define ( 'FRIENDIKA_VERSION', '2.2.1060' ); define ( 'DFRN_PROTOCOL_VERSION', '2.21' ); define ( 'DB_UPDATE_VERSION', 1076 ); From 1aaded10b405b3ba00fdd2e5cf809bdab01bf132 Mon Sep 17 00:00:00 2001 From: Friendika Date: Tue, 2 Aug 2011 20:02:07 -0700 Subject: [PATCH 042/123] api compatibility fixes --- include/api.php | 5 +++-- view/api_timeline_atom.tpl | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/api.php b/include/api.php index c77c55cef4..082da55b3d 100644 --- a/include/api.php +++ b/include/api.php @@ -723,7 +723,8 @@ 'created_at'=> api_date($item['created']), 'published' => datetime_convert('UTC','UTC',$item['created'],ATOM_TIME), 'updated' => datetime_convert('UTC','UTC',$item['edited'],ATOM_TIME), - 'id' => $item['uri'], + 'id' => $item['id'], + 'message_id' => $item['uri'], 'text' => strip_tags(bbcode($item['body'])), 'html' => bbcode($item['body']), 'source' => (($item['app']) ? $item['app'] : 'web'), @@ -773,7 +774,7 @@ $email = $a->config['admin_email']; $closed = (($a->config['register_policy'] == REGISTER_CLOSED) ? 'true' : 'false'); $private = (($a->config['system']['block_public']) ? 'true' : 'false'); - $textlimit = (($a->config['max_import_size']) ? $a->config['max_import_size'] : '200000'); + $textlimit = (string) (($a->config['max_import_size']) ? $a->config['max_import_size'] : 200000); $ssl = (($a->config['system']['have_ssl']) ? 'true' : 'false'); $sslserver = (($ssl === 'true') ? str_replace('http:','https:',$a->get_baseurl()) : ''); diff --git a/view/api_timeline_atom.tpl b/view/api_timeline_atom.tpl index dcabba465a..aef1f9310a 100644 --- a/view/api_timeline_atom.tpl +++ b/view/api_timeline_atom.tpl @@ -53,7 +53,7 @@ {{ for $statuses as $status }} $status.objecttype - $status.id + $status.message_id $status.text $status.html From 6bf800c6c8ea90d86cadb58b862ecddcf40e7b2a Mon Sep 17 00:00:00 2001 From: Friendika Date: Tue, 2 Aug 2011 20:08:40 -0700 Subject: [PATCH 043/123] Provide a way to lie about textlimit as far as API is concerned. --- include/api.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/api.php b/include/api.php index 082da55b3d..8644c839cf 100644 --- a/include/api.php +++ b/include/api.php @@ -775,6 +775,8 @@ $closed = (($a->config['register_policy'] == REGISTER_CLOSED) ? 'true' : 'false'); $private = (($a->config['system']['block_public']) ? 'true' : 'false'); $textlimit = (string) (($a->config['max_import_size']) ? $a->config['max_import_size'] : 200000); + if($a->config['api_import_size']) + $texlimit = string($a->config['api_import_size']); $ssl = (($a->config['system']['have_ssl']) ? 'true' : 'false'); $sslserver = (($ssl === 'true') ? str_replace('http:','https:',$a->get_baseurl()) : ''); From e22e823e93756fbfbb175f4f9f182aee64c1f5b1 Mon Sep 17 00:00:00 2001 From: Friendika Date: Tue, 2 Aug 2011 22:39:35 -0700 Subject: [PATCH 044/123] allow group selection from contact edit page --- include/group.php | 28 +++++++++++++++++++++++-- include/main.js | 8 +++++++ mod/contactgroup.php | 50 ++++++++++++++++++++++++++++++++++++++++++++ mod/contacts.php | 21 ++++++++++++------- 4 files changed, 97 insertions(+), 10 deletions(-) create mode 100644 mod/contactgroup.php diff --git a/include/group.php b/include/group.php index e16c900d90..8ee7face6d 100644 --- a/include/group.php +++ b/include/group.php @@ -136,7 +136,7 @@ function group_public_members($gid) { -function group_side($every="contacts",$each="group",$edit = false, $group_id = 0) { +function group_side($every="contacts",$each="group",$edit = false, $group_id = 0, $cid = 0) { $o = ''; @@ -160,10 +160,19 @@ EOT; $r = q("SELECT * FROM `group` WHERE `deleted` = 0 AND `uid` = %d ORDER BY `name` ASC", intval($_SESSION['uid']) ); + if($cid) { + $member_of = groups_containing(local_user(),$cid); + } + if(count($r)) { foreach($r as $rr) { $selected = (($group_id == $rr['id']) ? ' class="group-selected" ' : ''); - $o .= ' \r\n"; + $o .= ' \r\n"; } } $o .= " \r\n
"; @@ -204,3 +213,18 @@ function member_of($c) { } +function groups_containing($uid,$c) { + + $r = q("SELECT `gid` FROM `group_member` WHERE `uid` = %d AND `group_member`.`contact-id` = %d ", + intval($uid), + intval($c) + ); + + $ret = array(); + if(count($r)) { + foreach($r as $rr) + $ret[] = $rr['gid']; + } + + return $ret; +} \ No newline at end of file diff --git a/include/main.js b/include/main.js index d17d923e42..e5c78a0654 100644 --- a/include/main.js +++ b/include/main.js @@ -341,6 +341,14 @@ }); } + function contactgroupChangeMember(gid,cid) { + $('body').css('cursor', 'wait'); + $.get('contactgroup/' + gid + '/' + cid, function(data) { + $('body').css('cursor', 'auto'); + }); + } + + function checkboxhighlight(box) { if($(box).is(':checked')) { $(box).addClass('checkeditem'); diff --git a/mod/contactgroup.php b/mod/contactgroup.php new file mode 100644 index 0000000000..bf81afe079 --- /dev/null +++ b/mod/contactgroup.php @@ -0,0 +1,50 @@ +argc > 2) && intval($a->argv[1]) && intval($a->argv[2])) { + $r = q("SELECT `id` FROM `contact` WHERE `id` = %d AND `uid` = %d and `self` = 0 and `blocked` = 0 AND `pending` = 0 LIMIT 1", + intval($a->argv[2]), + intval(local_user()) + ); + if(count($r)) + $change = intval($a->argv[2]); + } + + if(($a->argc > 1) && (intval($a->argv[1]))) { + + $r = q("SELECT * FROM `group` WHERE `id` = %d AND `uid` = %d AND `deleted` = 0 LIMIT 1", + intval($a->argv[1]), + intval(local_user()) + ); + if(! count($r)) { + killme(); + } + + $group = $r[0]; + $members = group_get_members($group['id']); + $preselected = array(); + if(count($members)) { + foreach($members as $member) + $preselected[] = $member['id']; + } + + if($change) { + if(in_array($change,$preselected)) { + group_rmv_member(local_user(),$group['name'],$change); + } + else { + group_add_member(local_user(),$group['name'],$change); + } + } + } + + killme(); +} \ No newline at end of file diff --git a/mod/contacts.php b/mod/contacts.php index 2fc01a0d8b..4f5a49acd1 100644 --- a/mod/contacts.php +++ b/mod/contacts.php @@ -6,10 +6,22 @@ function contacts_init(&$a) { if(! local_user()) return; + $contact_id = 0; + if(($a->argc == 2) && intval($a->argv[1])) { + $contact_id = intval($a->argv[1]); + $r = q("SELECT * FROM `contact` WHERE `uid` = %d and `id` = %d LIMIT 1", + intval(local_user()), + intval($contact_id) + ); + if(! count($r)) { + $contact_id = 0; + } + } + require_once('include/group.php'); if(! x($a->page,'aside')) $a->page['aside'] = ''; - $a->page['aside'] .= group_side(); + $a->page['aside'] .= group_side('contacts','group',false,0,$contact_id); $inv = ''; @@ -264,13 +276,6 @@ function contacts_content(&$a) { } $grps = ''; - $member_of = member_of($r[0]['id']); - if(is_array($member_of) && count($member_of)) { - $grps = t('Member of: ') . EOL . ''; - } $insecure = '

' . t('Privacy Unavailable') . ' ' . t('Private communications are not available for this contact.') . '

'; From d0b7723bbcd4e9a4fd5c97370bd1c738fdcdab28 Mon Sep 17 00:00:00 2001 From: Friendika Date: Wed, 3 Aug 2011 16:29:25 -0700 Subject: [PATCH 045/123] re-parent api post item with parent_id and no parent_uri --- boot.php | 2 +- mod/item.php | 27 +++++++++++++++++++-------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/boot.php b/boot.php index 5f8402ecdb..b306b14029 100644 --- a/boot.php +++ b/boot.php @@ -7,7 +7,7 @@ require_once('include/text.php'); require_once("include/pgettext.php"); -define ( 'FRIENDIKA_VERSION', '2.2.1060' ); +define ( 'FRIENDIKA_VERSION', '2.2.1061' ); define ( 'DFRN_PROTOCOL_VERSION', '2.21' ); define ( 'DB_UPDATE_VERSION', 1076 ); diff --git a/mod/item.php b/mod/item.php index 4b9f3d091f..b71c35041c 100644 --- a/mod/item.php +++ b/mod/item.php @@ -6,6 +6,8 @@ * text stuff. This function handles status, wall-to-wall status, * local comments, and remote coments - that are posted on this site * (as opposed to being delivered in a feed). + * Also processed here are posts and comments coming through the + * statusnet/twitter API. * All of these become an "item" which is our basic unit of * information. * Posts that originate externally or do not fall into the above @@ -33,6 +35,7 @@ function item_post(&$a) { call_hooks('post_local_start', $_POST); + $api_source = ((x($_POST,'api_source')) ? true : false); /** * Is this a reply to something? @@ -48,22 +51,26 @@ function item_post(&$a) { $r = false; if($parent || $parent_uri) { + + if(! x($_POST,'type')) + $_POST['type'] = 'net-comment'; + if($parent) { $r = q("SELECT * FROM `item` WHERE `id` = %d LIMIT 1", intval($parent) ); } elseif($parent_uri && local_user()) { - $_POST['type'] = 'net-comment'; - // This is coming from an API source, we are logged in - // This query will find the immediate parent + // This is coming from an API source, and we are logged in $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($parent_uri), intval(local_user()) ); - // now find the real parent of the conversation - if(count($r)) { - $parid = $r[0]['parent']; + } + // if this isn't the real parent of the conversation, find it + if($r !== false && count($r)) { + $parid = $r[0]['parent']; + if($r[0]['id'] != $r[0]['parent']) { $r = q("SELECT * FROM `item` WHERE `id` = `parent` AND `parent` = %d LIMIT 1", intval($parid) ); @@ -771,12 +778,16 @@ function item_post(&$a) { } logger('post_complete'); + + // figure out how to return, depending on from whence we came + + if($api_source) + return; + if((x($_POST,'return')) && strlen($_POST['return'])) { logger('return: ' . $_POST['return']); goaway($a->get_baseurl() . "/" . $_POST['return'] ); } - if($_POST['api_source']) - return; $json = array('success' => 1); if(x($_POST,'jsreload') && strlen($_POST['jsreload'])) $json['reload'] = $a->get_baseurl() . '/' . $_POST['jsreload']; From ad1e827169d9f57c02746b51c5268bdbe2cd8ac9 Mon Sep 17 00:00:00 2001 From: Friendika Date: Wed, 3 Aug 2011 19:18:58 -0700 Subject: [PATCH 046/123] several fixes for attachments --- include/attach.php | 11 ++++++----- include/items.php | 6 +++--- include/text.php | 2 +- library/simplepie/simplepie.inc | 7 ++++++- mod/item.php | 6 +++++- mod/wall_attach.php | 6 +++--- 6 files changed, 24 insertions(+), 14 deletions(-) diff --git a/include/attach.php b/include/attach.php index ca53081d93..4001d2af13 100644 --- a/include/attach.php +++ b/include/attach.php @@ -1,7 +1,7 @@ 'application/vnd.oasis.opendocument.spreadsheet', ); - if(strpos($filename,'.') !== false) { - $ext = strtolower(array_pop(explode('.',$filename))); + $dot = strpos($filename,'.'); + if($dot !== false) { + $ext = strtolower(substr($filename,$dot+1)); if (array_key_exists($ext, $mime_types)) { return $mime_types[$ext]; } @@ -76,5 +77,5 @@ function mime_content_type($filename) { else { return 'application/octet-stream'; } -}} +} diff --git a/include/items.php b/include/items.php index 6d69c6cc9e..8c6134f94f 100644 --- a/include/items.php +++ b/include/items.php @@ -518,7 +518,7 @@ function get_atom_elements($feed,$item) { if(! $type) $type = 'application/octet-stream'; - $att_arr[] = '[attach]href="' . $link . '" size="' . $len . '" type="' . $type . '" title="' . $title . '"[/attach]'; + $att_arr[] = '[attach]href="' . $link . '" length="' . $len . '" type="' . $type . '" title="' . $title . '"[/attach]'; } $res['attach'] = implode(',', $att_arr); } @@ -1725,11 +1725,11 @@ function item_getfeedattach($item) { if(count($arr)) { foreach($arr as $r) { $matches = false; - $cnt = preg_match('|\[attach\]href=\"(.*?)\" size=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches); + $cnt = preg_match('|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches); if($cnt) { $ret .= 'sanitize($link['attribs']['']['href'], SIMPLEPIE_CONSTRUCT_IRI, $this->get_base($link)); if (isset($link['attribs']['']['type'])) @@ -5527,9 +5528,13 @@ class SimplePie_Item { $length = ceil($link['attribs']['']['length']); } + if (isset($link['attribs']['']['title'])) + { + $title = $this->sanitize($link['attribs']['']['title'], SIMPLEPIE_CONSTRUCT_TEXT); + } // Since we don't have group or content for these, we'll just pass the '*_parent' variables directly to the constructor - $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title_parent, $width); + $this->data['enclosures'][] = new $this->feed->enclosure_class($url, $type, $length, $this->feed->javascript, $bitrate, $captions_parent, $categories_parent, $channels, $copyrights_parent, $credits_parent, $description_parent, $duration_parent, $expression, $framerate, $hashes_parent, $height, $keywords_parent, $lang, $medium, $player_parent, $ratings_parent, $restrictions_parent, $samplingrate, $thumbnails_parent, $title, $width); } } diff --git a/mod/item.php b/mod/item.php index b71c35041c..fd5d405ff6 100644 --- a/mod/item.php +++ b/mod/item.php @@ -304,6 +304,10 @@ function item_post(&$a) { if(count($r)) { $r = q("UPDATE `attach` SET `allow_cid` = '%s', `allow_gid` = '%s', `deny_cid` = '%s', `deny_gid` = '%s' WHERE `uid` = %d AND `id` = %d LIMIT 1", + dbesc($str_contact_allow), + dbesc($str_group_allow), + dbesc($str_contact_deny), + dbesc($str_group_deny), intval($profile_uid), intval($attach) ); @@ -436,7 +440,7 @@ function item_post(&$a) { if(count($r)) { if(strlen($attachments)) $attachments .= ','; - $attachments .= '[attach]href="' . $a->get_baseurl() . '/attach/' . $r[0]['id'] . '" size="' . $r[0]['filesize'] . '" type="' . $r[0]['filetype'] . '" title="' . (($r[0]['filename']) ? $r[0]['filename'] : ' ') . '"[/attach]'; + $attachments .= '[attach]href="' . $a->get_baseurl() . '/attach/' . $r[0]['id'] . '" length="' . $r[0]['filesize'] . '" type="' . $r[0]['filetype'] . '" title="' . (($r[0]['filename']) ? $r[0]['filename'] : '') . '"[/attach]'; } $body = str_replace($match[1],'',$body); } diff --git a/mod/wall_attach.php b/mod/wall_attach.php index f18fd10b74..ef6554df39 100644 --- a/mod/wall_attach.php +++ b/mod/wall_attach.php @@ -60,11 +60,11 @@ function wall_attach_post(&$a) { } $filedata = @file_get_contents($src); - - $mimetype = mime_content_type($src); + $mimetype = z_mime_content_type($filename); + if((! strlen($mimetype)) || ($mimetype === 'application/octet-stream') && function_exists('mime_content_type')) + $mimetype = mime_content_type($filename); $hash = random_string(); $created = datetime_convert(); - $r = q("INSERT INTO `attach` ( `uid`, `hash`, `filename`, `filetype`, `filesize`, `data`, `created`, `edited`, `allow_cid`, `allow_gid`,`deny_cid`, `deny_gid` ) VALUES ( %d, '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", intval($page_owner_uid), From d45a66e700a62626007b05ef1b78dff8f0931210 Mon Sep 17 00:00:00 2001 From: Friendika Date: Wed, 3 Aug 2011 21:05:39 -0700 Subject: [PATCH 047/123] allow custom avatar sizes - needed for Diaspora hcard/vcard --- boot.php | 18 ++++++++++++++++-- include/items.php | 5 +++++ include/text.php | 6 ++++++ mod/photo.php | 17 ++++++++++++++++- 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/boot.php b/boot.php index b306b14029..9ff17b4666 100644 --- a/boot.php +++ b/boot.php @@ -877,7 +877,9 @@ function profile_sidebar($profile) { $podloc = $a->get_baseurl(); $searchable = (($profile['publish'] && $profile['net-publish']) ? 'true' : 'false' ); $nickname = $profile['nick']; - $dphoto = $profile['photo']; + $photo300 = $a->get_baseurl() . '/photo/custom/300/' . $profile['uid'] . '.jpg'; + $photo100 = $a->get_baseurl() . '/photo/custom/100/' . $profile['uid'] . '.jpg'; + $photo50 = $a->get_baseurl() . '/photo/custom/50/' . $profile['uid'] . '.jpg'; $diaspora_vcard = <<< EOT @@ -897,7 +899,19 @@ function profile_sidebar($profile) {
Photo
- + +
+
+
+
Photo
+
+ +
+
+
+
Photo
+
+
diff --git a/include/items.php b/include/items.php index 8c6134f94f..047dd4442b 100644 --- a/include/items.php +++ b/include/items.php @@ -995,6 +995,11 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $secure_fee require_once('library/simplepie/simplepie.inc'); + if(! strlen($xml)) { + logger('consume_feed: empty input'); + return; + } + $feed = new SimplePie(); $feed->set_raw_data($xml); if($datedir) diff --git a/include/text.php b/include/text.php index a568b7a948..d53a2ceb9a 100644 --- a/include/text.php +++ b/include/text.php @@ -732,6 +732,9 @@ function link_compare($a,$b) { return false; }} +// Given an item array, convert the body element from bbcode to html and add smilie icons. +// If attach is true, also add icons for item attachments + if(! function_exists('prepare_body')) { function prepare_body($item,$attach = false) { @@ -771,6 +774,9 @@ function prepare_body($item,$attach = false) { return $s; }} + +// Given a text string, convert from bbcode to html and add smilie icons. + if(! function_exists('prepare_text')) { function prepare_text($text) { diff --git a/mod/photo.php b/mod/photo.php index 9809aa418d..3994620f89 100644 --- a/mod/photo.php +++ b/mod/photo.php @@ -5,6 +5,11 @@ require_once('include/security.php'); function photo_init(&$a) { switch($a->argc) { + case 4: + $person = $a->argv[3]; + $customres = intval($a->argv[2]); + $type = $a->argv[1]; + break; case 3: $person = $a->argv[2]; $type = $a->argv[1]; @@ -29,6 +34,7 @@ function photo_init(&$a) { switch($type) { case 'profile': + case 'custom': $resolution = 4; break; case 'micro': @@ -113,8 +119,17 @@ function photo_init(&$a) { // NOTREACHED } + if(intval($customres) && $customres > 0 && $customres < 500) { + require_once('include/Photo.php'); + $ph = new Photo($data); + if($ph->is_valid()) { + $ph->scaleImageSquare($customres); + $data = $ph->imageString(); + } + } + header("Content-type: image/jpeg"); echo $data; killme(); // NOTREACHED -} \ No newline at end of file +} From 639204c2ec29ccd74fbc2cb1eb597cca86e84f27 Mon Sep 17 00:00:00 2001 From: Friendika Date: Wed, 3 Aug 2011 22:05:20 -0700 Subject: [PATCH 048/123] diaspora structures --- boot.php | 2 +- database.sql | 6 ++++++ update.php | 11 +++++++++-- view/diaspora_comment.tpl | 11 +++++++++++ view/diaspora_comment_relay.tpl | 12 ++++++++++++ view/diaspora_like.tpl | 12 ++++++++++++ view/diaspora_like_relay.tpl | 13 +++++++++++++ view/diaspora_post.tpl | 11 +++++++++++ view/diaspora_retract.tpl | 9 +++++++++ view/diaspora_share.tpl | 8 ++++++++ 10 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 view/diaspora_comment.tpl create mode 100644 view/diaspora_comment_relay.tpl create mode 100644 view/diaspora_like.tpl create mode 100644 view/diaspora_like_relay.tpl create mode 100644 view/diaspora_post.tpl create mode 100644 view/diaspora_retract.tpl create mode 100644 view/diaspora_share.tpl diff --git a/boot.php b/boot.php index 9ff17b4666..3d1d884e30 100644 --- a/boot.php +++ b/boot.php @@ -9,7 +9,7 @@ require_once("include/pgettext.php"); define ( 'FRIENDIKA_VERSION', '2.2.1061' ); define ( 'DFRN_PROTOCOL_VERSION', '2.21' ); -define ( 'DB_UPDATE_VERSION', 1076 ); +define ( 'DB_UPDATE_VERSION', 1077 ); define ( 'EOL', "
\r\n" ); define ( 'ATOM_TIME', 'Y-m-d\TH:i:s\Z' ); diff --git a/database.sql b/database.sql index 50697845a3..9c43df3db3 100644 --- a/database.sql +++ b/database.sql @@ -573,3 +573,9 @@ CREATE TABLE IF NOT EXISTS `attach` ( `deny_gid` MEDIUMTEXT NOT NULL ) ENGINE = MYISAM DEFAULT CHARSET=utf8; +CREATE TABLE IF NOT EXISTS `guid` ( +`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY , +`guid` CHAR( 16 ) NOT NULL , +INDEX ( `guid` ) +) ENGINE = MYISAM DEFAULT CHARSET=utf8; + diff --git a/update.php b/update.php index 9c55f15cf5..4ef362b197 100644 --- a/update.php +++ b/update.php @@ -1,6 +1,6 @@ + + + $guid + $parent_guid + $authorsig + $body + $handle + + + \ No newline at end of file diff --git a/view/diaspora_comment_relay.tpl b/view/diaspora_comment_relay.tpl new file mode 100644 index 0000000000..e82de1171e --- /dev/null +++ b/view/diaspora_comment_relay.tpl @@ -0,0 +1,12 @@ + + + + $guid + $parent_guid + $parentsig + $authorsig + $body + $handle + + + \ No newline at end of file diff --git a/view/diaspora_like.tpl b/view/diaspora_like.tpl new file mode 100644 index 0000000000..76356eb298 --- /dev/null +++ b/view/diaspora_like.tpl @@ -0,0 +1,12 @@ + + + + $type + $guid + $parent_guid + $authorsig + $positive + $handle + + + diff --git a/view/diaspora_like_relay.tpl b/view/diaspora_like_relay.tpl new file mode 100644 index 0000000000..6feba96ad3 --- /dev/null +++ b/view/diaspora_like_relay.tpl @@ -0,0 +1,13 @@ + + + + $guid + $type + $parent_guid + $parentsig + $authrosig + $positive + $handle + + + \ No newline at end of file diff --git a/view/diaspora_post.tpl b/view/diaspora_post.tpl new file mode 100644 index 0000000000..1ba3ebb1fc --- /dev/null +++ b/view/diaspora_post.tpl @@ -0,0 +1,11 @@ + + + + $body + $guid + $handle + $public + $created + + + \ No newline at end of file diff --git a/view/diaspora_retract.tpl b/view/diaspora_retract.tpl new file mode 100644 index 0000000000..6d5b6e22be --- /dev/null +++ b/view/diaspora_retract.tpl @@ -0,0 +1,9 @@ + + + + $guid + $type + $handle + + + \ No newline at end of file diff --git a/view/diaspora_share.tpl b/view/diaspora_share.tpl new file mode 100644 index 0000000000..c16341b1e1 --- /dev/null +++ b/view/diaspora_share.tpl @@ -0,0 +1,8 @@ + + + + $sender + $recipient + + + \ No newline at end of file From f0af4de4a795ed3914f0e8e8ae3417a315d3dcd3 Mon Sep 17 00:00:00 2001 From: Friendika Date: Thu, 4 Aug 2011 19:19:51 -0700 Subject: [PATCH 049/123] wrap text from imported link in [quote] --- boot.php | 14 +++++++++++++- mod/parse_url.php | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/boot.php b/boot.php index 3d1d884e30..35f295d068 100644 --- a/boot.php +++ b/boot.php @@ -7,7 +7,7 @@ require_once('include/text.php'); require_once("include/pgettext.php"); -define ( 'FRIENDIKA_VERSION', '2.2.1061' ); +define ( 'FRIENDIKA_VERSION', '2.2.1062' ); define ( 'DFRN_PROTOCOL_VERSION', '2.21' ); define ( 'DB_UPDATE_VERSION', 1077 ); @@ -607,6 +607,18 @@ function check_config(&$a) { }} +function get_guid() { + $exists = true; + do { + $s = random_string(16); + $r = q("select id from guid where guid = '%s' limit 1", dbesc($s)); + if(! results($r)) + $exists = false; + } while($exists); + q("insert into guid ( guid ) values ( '%s' ) ", dbesc($s)); + return $s; +} + // wrapper for adding a login box. If $register == true provide a registration // link. This will most always depend on the value of $a->config['register_policy']. diff --git a/mod/parse_url.php b/mod/parse_url.php index 46c6b46e97..9bb0bc4640 100644 --- a/mod/parse_url.php +++ b/mod/parse_url.php @@ -116,7 +116,7 @@ function parse_url_content(&$a) { } if(strlen($text)) { - $text = '

' . $text; + $text = '

' . $text . '

'; } echo sprintf($template,$url,($title) ? $title : $url,$text); From 12d5482fc1703b66024ec432994c87ee9c1ea432 Mon Sep 17 00:00:00 2001 From: Friendika Date: Thu, 4 Aug 2011 19:47:45 -0700 Subject: [PATCH 050/123] some fields in API timeline JSON must be int --- include/api.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/api.php b/include/api.php index 8644c839cf..9382f82c48 100644 --- a/include/api.php +++ b/include/api.php @@ -286,8 +286,8 @@ } $ret = Array( - 'uid' => $uinfo[0]['uid'], - 'id' => $uinfo[0]['cid'], + 'uid' => intval($uinfo[0]['uid']), + 'id' => intval($uinfo[0]['cid']), 'name' => $uinfo[0]['name'], 'screen_name' => $uinfo[0]['nick'], 'location' => '', //$uinfo[0]['default-location'], @@ -295,15 +295,15 @@ 'url' => $uinfo[0]['url'], 'contact_url' => $a->get_baseurl()."/contacts/".$uinfo[0]['cid'], 'protected' => false, # - 'friends_count' => $countfriends, + 'friends_count' => intval($countfriends), 'created_at' => api_date($uinfo[0]['name-date']), 'utc_offset' => 0, #XXX: fix me 'time_zone' => '', //$uinfo[0]['timezone'], 'geo_enabled' => false, - 'statuses_count' => $countitms, #XXX: fix me + 'statuses_count' => intval($countitms), #XXX: fix me 'lang' => 'en', #XXX: fix me 'description' => '', - 'followers_count' => $countfollowers, #XXX: fix me + 'followers_count' => intval($countfollowers), #XXX: fix me 'favourites_count' => 0, 'contributors_enabled' => false, 'follow_request_sent' => false, @@ -723,16 +723,16 @@ 'created_at'=> api_date($item['created']), 'published' => datetime_convert('UTC','UTC',$item['created'],ATOM_TIME), 'updated' => datetime_convert('UTC','UTC',$item['edited'],ATOM_TIME), - 'id' => $item['id'], + 'id' => intval($item['id']), 'message_id' => $item['uri'], 'text' => strip_tags(bbcode($item['body'])), 'html' => bbcode($item['body']), 'source' => (($item['app']) ? $item['app'] : 'web'), 'url' => ($item['plink']!=''?$item['plink']:$item['author-link']), 'truncated' => False, - 'in_reply_to_status_id' => ($item['parent']!=$item['id']?$item['parent']:''), + 'in_reply_to_status_id' => ($item['parent']!=$item['id']? intval($item['parent']):''), 'in_reply_to_user_id' => '', - 'favorited' => false, + 'favorited' => $item['starred'] ? true : false, 'in_reply_to_screen_name' => '', 'geo' => '', 'coordinates' => $item['coord'], From 2abcf76ec17a9a7754c399cdde9a4449308a4b02 Mon Sep 17 00:00:00 2001 From: Friendika Date: Thu, 4 Aug 2011 22:37:45 -0700 Subject: [PATCH 051/123] salmon protocol changes magicsig draft-experimental, fixes to hostxrd --- boot.php | 7 +++---- include/salmon.php | 37 +++++++++++++++++++++++++++++++----- {include => mod}/hostxrd.php | 2 +- mod/salmon.php | 19 ++++++++++++------ mod/xrd.php | 2 +- view/magicsig.tpl | 2 +- 6 files changed, 51 insertions(+), 18 deletions(-) rename {include => mod}/hostxrd.php (89%) diff --git a/boot.php b/boot.php index 35f295d068..e1a1c5a503 100644 --- a/boot.php +++ b/boot.php @@ -320,13 +320,12 @@ class App { /** * Special handling for the webfinger/lrdd host XRD file - * Just spit out the contents and exit. */ if($this->cmd === '.well-known/host-meta') { - require_once('include/hostxrd.php'); - hostxrd(); - // NOTREACHED + $this->argc = 1; + $this->argv = array('hostxrd'); + $this->module = 'hostxrd'; } /** diff --git a/include/salmon.php b/include/salmon.php index 4994655df8..f1cef0a497 100644 --- a/include/salmon.php +++ b/include/salmon.php @@ -110,7 +110,7 @@ EOT; $data_type = 'application/atom+xml'; $encoding = 'base64url'; $algorithm = 'RSA-SHA256'; - $keyhash = base64url_encode(hash('sha256',salmon_key($owner['spubkey']))); + $keyhash = base64url_encode(hash('sha256',salmon_key($owner['spubkey'])),true); // Setup RSA stuff to PKCS#1 sign the data @@ -127,11 +127,14 @@ EOT; $precomputed = '.YXBwbGljYXRpb24vYXRvbSt4bWw=.YmFzZTY0dXJs.UlNBLVNIQTI1Ng=='; - $signature = base64url_encode($rsa->sign($data . $precomputed)); + $signature = base64url_encode($rsa->sign(str_replace('=','',$data . $precomputed),true)); - $signature2 = base64url_encode($rsa->sign($data)); + $signature2 = base64url_encode($rsa->sign($data . $precomputed)); + + $signature3 = base64url_encode($rsa->sign($data)); $salmon_tpl = get_markup_template('magicsig.tpl'); + $salmon = replace_macros($salmon_tpl,array( '$data' => $data, '$encoding' => $encoding, @@ -153,11 +156,11 @@ EOT; if($return_code > 299) { - logger('slapper: compliant salmon failed. Falling back to status.net hack'); + logger('slapper: compliant salmon failed. Falling back to status.net hack2'); // Entirely likely that their salmon implementation is // non-compliant. Let's try once more, this time only signing - // the data, without the precomputed blob + // the data, without stripping '=' chars $salmon = replace_macros($salmon_tpl,array( '$data' => $data, @@ -174,6 +177,30 @@ EOT; )); $return_code = $a->get_curl_code(); + + if($return_code > 299) { + + logger('slapper: compliant salmon failed. Falling back to status.net hack3'); + + // Entirely likely that their salmon implementation is + // non-compliant. Let's try once more, this time only signing + // the data, without the precomputed blob + + $salmon = replace_macros($salmon_tpl,array( + '$data' => $data, + '$encoding' => $encoding, + '$algorithm' => $algorithm, + '$keyhash' => $keyhash, + '$signature' => $signature3 + )); + + // slap them + post_url($url,$salmon, array( + 'Content-type: application/magic-envelope+xml', + 'Content-length: ' . strlen($salmon) + )); + $return_code = $a->get_curl_code(); + } } logger('slapper returned ' . $return_code); if(! $return_code) diff --git a/include/hostxrd.php b/mod/hostxrd.php similarity index 89% rename from include/hostxrd.php rename to mod/hostxrd.php index 18c3e4b1ed..c7861d26d0 100644 --- a/include/hostxrd.php +++ b/mod/hostxrd.php @@ -1,6 +1,6 @@ encoding; $alg = $base->alg; - // If we're talking to status.net or one of their ilk, they aren't following the magic envelope spec - // and only signed the data element. We'll be nice and let them validate anyway. + // Salmon magic signatures have evolved and there is no way of knowing ahead of time which + // flavour we have. We'll try and verify it regardless. $stnet_signed_data = $data; + $signed_data = $data . '.' . base64url_encode($type) . '.' . base64url_encode($encoding) . '.' . base64url_encode($alg); + $compliant_format = str_replace('=','',$signed_data); + + // decode the data $data = base64url_decode($data); @@ -150,13 +154,16 @@ function salmon_post(&$a) { $rsa->exponent = new Math_BigInteger($e, 256); // We should have everything we need now. Let's see if it verifies. - // If it fails with the proper data format, try again using just the data - // (e.g. status.net) - $verify = $rsa->verify($signed_data,$signature); + $verify = $rsa->verify($compliant_format,$signature); if(! $verify) { - logger('mod-salmon: message did not verify using protocol. Trying statusnet hack.'); + logger('mod-salmon: message did not verify using protocol. Trying padding hack.'); + $verify = $rsa->verify($signed_data,$signature); + } + + if(! $verify) { + logger('mod-salmon: message did not verify using padding. Trying old statusnet hack.'); $verify = $rsa->verify($stnet_signed_data,$signature); } diff --git a/mod/xrd.php b/mod/xrd.php index 4889639f07..c96c18f3ce 100644 --- a/mod/xrd.php +++ b/mod/xrd.php @@ -23,7 +23,7 @@ function xrd_content(&$a) { if(! count($r)) killme(); - $salmon_key = salmon_key($r[0]['spubkey']); + $salmon_key = str_replace('=','',salmon_key($r[0]['spubkey'])); header('Access-Control-Allow-Origin: *'); header("Content-type: text/xml"); diff --git a/view/magicsig.tpl b/view/magicsig.tpl index 622e7c5a27..75f9bc475b 100644 --- a/view/magicsig.tpl +++ b/view/magicsig.tpl @@ -5,5 +5,5 @@ $data $encoding $algorithm -$signature +$signature From b8592dba8ac86358d3608bcac416965dbf12c21b Mon Sep 17 00:00:00 2001 From: Friendika Date: Fri, 5 Aug 2011 01:02:12 -0700 Subject: [PATCH 052/123] integer values don't show up in xml api results --- include/api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/api.php b/include/api.php index 9382f82c48..ffdcdfdd66 100644 --- a/include/api.php +++ b/include/api.php @@ -383,7 +383,7 @@ function api_xmlify($val){ if (is_bool($val)) return $val?"true":"false"; if (is_array($val)) return array_map('api_xmlify', $val); - return xmlify($val); + return xmlify((string) $val); } /** From 6eec04b09ca428cd2d125857612f7229e226c606 Mon Sep 17 00:00:00 2001 From: Friendika Date: Fri, 5 Aug 2011 01:34:32 -0700 Subject: [PATCH 053/123] updates to Diaspora decrypt/verify from recent testing --- mod/receive.php | 62 +++++++++++-------------------------------------- 1 file changed, 14 insertions(+), 48 deletions(-) diff --git a/mod/receive.php b/mod/receive.php index 86d612dc99..188f55f21e 100644 --- a/mod/receive.php +++ b/mod/receive.php @@ -117,7 +117,6 @@ function receive_post(&$a) { $idom = parse_xml_string($decrypted,false); - print_r($idom); $inner_iv = base64_decode($idom->iv); $inner_aes_key = base64_decode($idom->aes_key); @@ -149,17 +148,14 @@ function receive_post(&$a) { logger('signature: ' . bin2hex($signature)); -// openssl_public_encrypt('test',$rrr,$rpubkey); -// logger('rrr: ' . $rrr); - -// $pubdecsig = ''; -// openssl_public_decrypt($signature,$pubdecsig,$rpubkey); -// logger('decsig: ' . bin2hex($pubdecsig)); - // unpack the data // strip whitespace so our data element will return to one big base64 blob $data = str_replace(array(" ","\t","\r","\n"),array("","","",""),$base->data); + // Add back the 60 char linefeeds + $lines = str_split($data,60); + $data = implode("\n",$lines); + // stash away some other stuff for later @@ -168,7 +164,7 @@ function receive_post(&$a) { $encoding = $base->encoding; $alg = $base->alg; - $signed_data = $data . "\n" . '.' . base64url_encode($type) . "\n" . '.' . base64url_encode($encoding) . "\n" . '.' . base64url_encode($alg) . "\n"; + $signed_data = $data . (($data[-1] != "\n") ? "\n" : '') . '.' . base64url_encode($type) . "\n" . '.' . base64url_encode($encoding) . "\n" . '.' . base64url_encode($alg) . "\n"; logger('signed data: ' . $signed_data); @@ -177,9 +173,6 @@ function receive_post(&$a) { // Now pull out the inner encrypted blob - - - $inner_encrypted = base64_decode($data); $inner_decrypted = @@ -209,52 +202,25 @@ function receive_post(&$a) { receive_return(400); } -// FIXME -// Use non salmon compliant signature -/* + if (version_compare(PHP_VERSION, '5.3.0', '>=')) { + $verify = openssl_verify($signed_data,$signature,$key,'sha256'); + } + else { + // FIXME + // fallback sha256 verify for PHP < 5.3 - // Setup RSA stuff to verify the signature - - set_include_path(get_include_path() . PATH_SEPARATOR . 'library' . PATH_SEPARATOR . 'phpsec'); - - require_once('library/phpsec/Crypt/RSA.php'); - - $key_info = explode('.',$key); - - $m = base64url_decode($key_info[1]); - $e = base64url_decode($key_info[2]); - - logger('mod-salmon: key details: ' . print_r($key_info,true)); - - $rsa = new CRYPT_RSA(); - $rsa->signatureMode = CRYPT_RSA_SIGNATURE_PKCS1; - $rsa->setHash('sha256'); - - $rsa->modulus = new Math_BigInteger($m, 256); - $rsa->k = strlen($rsa->modulus->toBytes()); - $rsa->exponent = new Math_BigInteger($e, 256); - - $verify = $rsa->verify($signed_data,$signature); + } if(! $verify) { logger('mod-diaspora: Message did not verify. Discarding.'); receive_return(400); } -*/ logger('mod-diaspora: Message verified.'); - /* decrypt the sucker */ - /* - // TODO - */ - - /* - * - * If we reached this point, the message is good. Now let's figure out if the author is allowed to send us stuff. - * - */ + // If we reached this point, the message is good. + // Now let's figure out if the author is allowed to send us stuff. $r = q("SELECT * FROM `contact` WHERE `network` = 'dspr' AND ( `url` = '%s' OR `alias` = '%s') AND `uid` = %d LIMIT 1", From 112fc59e3190d29d95139fda3e861818f828b0d0 Mon Sep 17 00:00:00 2001 From: Friendika Date: Fri, 5 Aug 2011 05:17:18 -0700 Subject: [PATCH 054/123] more api tweaks --- include/api.php | 30 ++++++++++++++++++++++-------- view/api_user_xml.tpl | 6 +++--- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/include/api.php b/include/api.php index ffdcdfdd66..6cd3318ef2 100644 --- a/include/api.php +++ b/include/api.php @@ -243,6 +243,13 @@ } if($uinfo[0]['self']) { + $usr = q("select * from user where uid = %d limit 1", + intval(local_user()) + ); + $profile = q("select * from profile where uid = %d and `is-default` = 1 limit 1", + intval(local_user()) + ); + // count public wall messages $r = q("SELECT COUNT(`id`) as `count` FROM `item` WHERE `uid` = %d @@ -280,9 +287,16 @@ ); $countfollowers = $r[0]['count']; + $r = q("SELECT count(`id`) as `count` FROM item where starred = 1 and uid = %d and deleted = 0", + intval($uinfo[0]['uid']) + ); + $starred = $r[0]['count']; + + if(! $uinfo[0]['self']) { $countfriends = 0; $countfollowers = 0; + $starred = 0; } $ret = Array( @@ -290,21 +304,21 @@ 'id' => intval($uinfo[0]['cid']), 'name' => $uinfo[0]['name'], 'screen_name' => $uinfo[0]['nick'], - 'location' => '', //$uinfo[0]['default-location'], + 'location' => ($usr) ? $usr[0]['default-location'] : '', 'profile_image_url' => $uinfo[0]['micro'], 'url' => $uinfo[0]['url'], 'contact_url' => $a->get_baseurl()."/contacts/".$uinfo[0]['cid'], - 'protected' => false, # + 'protected' => false, 'friends_count' => intval($countfriends), 'created_at' => api_date($uinfo[0]['name-date']), - 'utc_offset' => 0, #XXX: fix me - 'time_zone' => '', //$uinfo[0]['timezone'], + 'utc_offset' => "+00:00", + 'time_zone' => 'UTC', //$uinfo[0]['timezone'], 'geo_enabled' => false, 'statuses_count' => intval($countitms), #XXX: fix me 'lang' => 'en', #XXX: fix me - 'description' => '', - 'followers_count' => intval($countfollowers), #XXX: fix me - 'favourites_count' => 0, + 'description' => (($profile) ? $profile[0]['pdesc'] : ''), + 'followers_count' => intval($countfollowers), + 'favourites_count' => intval($starred), 'contributors_enabled' => false, 'follow_request_sent' => false, 'profile_background_color' => 'cfe8f6', @@ -316,8 +330,8 @@ 'profile_background_tile' => false, 'profile_use_background_image' => false, 'notifications' => false, + 'following' => '', #XXX: fix me 'verified' => true, #XXX: fix me - 'followers' => '', #XXX: fix me #'status' => null ); diff --git a/view/api_user_xml.tpl b/view/api_user_xml.tpl index 15517fc42e..78cc1f5306 100644 --- a/view/api_user_xml.tpl +++ b/view/api_user_xml.tpl @@ -7,7 +7,7 @@ $user.profile_image_url $user.url $user.protected - $user.followers + $user.followers_count $user.profile_background_color $user.profile_text_color $user.profile_link_color @@ -21,10 +21,10 @@ $user.profile_background_image_url $user.profile_background_tile $user.profile_use_background_image - + $user.notifications $user.geo_enabled $user.verified - + $user.following $user.statuses_count $user.lang $user.contributors_enabled From 5a5a7bfc4cf551f6353358b961399efcaa8269b3 Mon Sep 17 00:00:00 2001 From: Friendika Date: Fri, 5 Aug 2011 05:37:42 -0700 Subject: [PATCH 055/123] fallback sha256 openssl_verify code for php releases prior to 5.3 --- mod/receive.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mod/receive.php b/mod/receive.php index 188f55f21e..f5a2eb7b6e 100644 --- a/mod/receive.php +++ b/mod/receive.php @@ -194,7 +194,7 @@ function receive_post(&$a) { logger('mod-diaspora: Fetching key for ' . $author_link ); -// Get diaspora public key (pkcs#1) and convert to pkcs#8 + // Get diaspora public key (pkcs#1) and convert to pkcs#8 $key = get_diaspora_key($author_link); if(! $key) { @@ -202,14 +202,17 @@ function receive_post(&$a) { receive_return(400); } + $verify = false; if (version_compare(PHP_VERSION, '5.3.0', '>=')) { $verify = openssl_verify($signed_data,$signature,$key,'sha256'); } else { - // FIXME // fallback sha256 verify for PHP < 5.3 - + $rawsig = ''; + $hash = hash('sha256',$signed_data,true); + openssl_public_decrypt($signature,$rawsig,$key); + $verify = (($rawsig && substr($rawsig,-32) === $hash) ? true : false); } if(! $verify) { From 1d659bd8543c630ff1eda245ab7e9afeec6eebe1 Mon Sep 17 00:00:00 2001 From: Friendika Date: Fri, 5 Aug 2011 06:42:27 -0700 Subject: [PATCH 056/123] move public profile warning to top of page --- mod/profiles.php | 2 +- view/profile_edit.tpl | 3 ++- view/theme/duepuntozero/style.css | 5 ++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mod/profiles.php b/mod/profiles.php index 64fab797f6..b269fa30c7 100644 --- a/mod/profiles.php +++ b/mod/profiles.php @@ -469,7 +469,7 @@ function profiles_content(&$a) { '$id' => $rr['id'], '$alt' => t('Profile Image'), '$profile_name' => $rr['profile-name'], - '$visible' => (($rr['is-default']) ? '' . t('Visible to everybody') . '' + '$visible' => (($rr['is-default']) ? '' . t('visible to everybody') . '' : '' . t('Edit visibility') . '') )); } diff --git a/view/profile_edit.tpl b/view/profile_edit.tpl index 053e19594b..8dab726492 100644 --- a/view/profile_edit.tpl +++ b/view/profile_edit.tpl @@ -1,3 +1,5 @@ +$default +

$banner