diff options
-rwxr-xr-x | boot.php | 3 | ||||
-rw-r--r-- | include/ItemObject.php | 8 | ||||
-rw-r--r-- | include/conversation.php | 7 | ||||
-rwxr-xr-x | include/items.php | 137 | ||||
-rw-r--r-- | include/zot.php | 8 | ||||
-rw-r--r-- | install/database.sql | 2 | ||||
-rw-r--r-- | install/update.php | 9 | ||||
-rw-r--r-- | mod/item.php | 15 | ||||
-rw-r--r-- | mod/photos.php | 18 | ||||
-rwxr-xr-x | view/tpl/conv_item.tpl | 2 |
10 files changed, 146 insertions, 63 deletions
@@ -45,7 +45,7 @@ define ( 'RED_PLATFORM', 'Red Matrix' ); define ( 'RED_VERSION', trim(file_get_contents('version.inc')) . 'R'); define ( 'ZOT_REVISION', 1 ); -define ( 'DB_UPDATE_VERSION', 1076 ); +define ( 'DB_UPDATE_VERSION', 1077 ); define ( 'EOL', '<br />' . "\r\n" ); define ( 'ATOM_TIME', 'Y-m-d\TH:i:s\Z' ); @@ -497,6 +497,7 @@ define ( 'ITEM_RELAY', 0x0200); // used only in the communication lay define ( 'ITEM_MENTIONSME', 0x0400); define ( 'ITEM_NOCOMMENT', 0x0800); // commenting/followups are disabled define ( 'ITEM_OBSCURED', 0x1000); // bit-mangled to protect from casual browsing by site admin +define ( 'ITEM_VERIFIED', 0x2000); // Signature verification was successful /** * diff --git a/include/ItemObject.php b/include/ItemObject.php index 6c43d4e9e..bf55b484a 100644 --- a/include/ItemObject.php +++ b/include/ItemObject.php @@ -156,6 +156,12 @@ class Item extends BaseObject { $indent = 'comment'; } + + $verified = (($item['item_flags'] & ITEM_VERIFIED) ? t('Message is verified') : ''); + $unverified = '' ; // (($this->is_wall_to_wall() && (! ($item['item_flags'] & ITEM_VERIFIED))) ? t('Message cannot be verified') : ''); + + + // FIXME - check this permission if($conv->get_profile_owner() == local_user()) { $tagger = array( @@ -210,6 +216,8 @@ class Item extends BaseObject { 'isotime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'c'), 'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'), 'lock' => $lock, + 'verified' => $verified, + 'unverified' => $unverified, 'location' => $location, 'indent' => $indent, 'owner_url' => $this->get_owner_url(), diff --git a/include/conversation.php b/include/conversation.php index 254453ad1..bcd9ae51a 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -638,6 +638,11 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional') { $likebuttons = false; $shareable = false; + $verified = (($item['item_flags'] & ITEM_VERIFIED) ? t('Message is verified') : ''); + $unverified = ''; + + + $tags=array(); $terms = get_terms_oftype($item['term'],array(TERM_HASHTAG,TERM_MENTION,TERM_UNKNOWN)); if(count($terms)) @@ -665,6 +670,8 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional') { 'tags' => $tags, 'hashtags' => $hashtags, 'mentions' => $mentions, + 'verified' => $verified, + 'unverified' => $unverified, 'txt_cats' => t('Categories:'), 'txt_folders' => t('Filed under:'), 'has_cats' => ((count($categories)) ? 'true' : ''), diff --git a/include/items.php b/include/items.php index 18736f974..7cc08197e 100755 --- a/include/items.php +++ b/include/items.php @@ -546,6 +546,7 @@ function title_is_body($title, $body) { function get_item_elements($x) { +// logger('get_item_elements'); $arr = array(); $arr['body'] = (($x['body']) ? htmlentities($x['body'],ENT_COMPAT,'UTF-8',false) : ''); @@ -579,6 +580,9 @@ function get_item_elements($x) { $arr['obj_type'] = (($x['object_type']) ? htmlentities($x['object_type'], ENT_COMPAT,'UTF-8',false) : ''); $arr['tgt_type'] = (($x['target_type']) ? htmlentities($x['target_type'], ENT_COMPAT,'UTF-8',false) : ''); $arr['comment_policy'] = (($x['comment_scope']) ? htmlentities($x['comment_scope'], ENT_COMPAT,'UTF-8',false) : 'contacts'); + + $arr['sig'] = (($x['signature']) ? htmlentities($x['signature'], ENT_COMPAT,'UTF-8',false) : ''); + $arr['object'] = activity_sanitise($x['object']); $arr['target'] = activity_sanitise($x['target']); @@ -590,18 +594,6 @@ function get_item_elements($x) { $arr['item_flags'] = 0; - // if it's a private post, encrypt it in the DB. - // We have to do that here because we need to cleanse the input and prevent bad stuff from getting in, - // and we need plaintext to do that. - - if(intval($arr['item_private'])) { - $arr['item_flags'] = $arr['item_flags'] | ITEM_OBSCURED; - $key = get_config('system','pubkey'); - if($arr['title']) - $arr['title'] = json_encode(aes_encapsulate($arr['title'],$key)); - if($arr['body']) - $arr['body'] = json_encode(aes_encapsulate($arr['body'],$key)); - } if(array_key_exists('flags',$x) && in_array('deleted',$x['flags'])) $arr['item_restrict'] = ITEM_DELETED; @@ -628,6 +620,31 @@ function get_item_elements($x) { } + if($arr['sig']) { + $r = q("select xchan_pubkey from xchan where xchan_hash = '%s' limit 1", + dbesc($arr['author_xchan']) + ); + if($r && rsa_verify($x['body'],base64url_decode($arr['sig']),$r[0]['xchan_pubkey'])) + $arr['item_flags'] |= ITEM_VERIFIED; + else + logger('get_item_elements: message verification failed.'); + } + + + // if it's a private post, encrypt it in the DB. + // We have to do that here because we need to cleanse the input and prevent bad stuff from getting in, + // and we need plaintext to do that. + + if(intval($arr['item_private'])) { + $arr['item_flags'] = $arr['item_flags'] | ITEM_OBSCURED; + $key = get_config('system','pubkey'); + if($arr['title']) + $arr['title'] = json_encode(aes_encapsulate($arr['title'],$key)); + if($arr['body']) + $arr['body'] = json_encode(aes_encapsulate($arr['body'],$key)); + } + + return $arr; } @@ -656,7 +673,7 @@ function encode_item($item) { $x = array(); $x['type'] = 'activity'; - logger('encode_item: ' . print_r($item,true)); +// logger('encode_item: ' . print_r($item,true)); $r = q("select channel_r_stream, channel_w_comment from channel where channel_id = %d limit 1", intval($item['uid']) @@ -707,6 +724,7 @@ function encode_item($item) { $x['permalink'] = $item['plink']; $x['location'] = $item['location']; $x['longlat'] = $item['coord']; + $x['signature'] = $item['sig']; $x['owner'] = encode_item_xchan($item['owner']); $x['author'] = encode_item_xchan($item['author']); @@ -730,6 +748,8 @@ function encode_item($item) { if($item['term']) $x['tags'] = encode_item_terms($item['term']); + logger('encode_item: ' . print_r($x,true)); + return $x; } @@ -1445,18 +1465,28 @@ function item_store($arr,$allow_exec = false) { $arr['deny_gid'] = ((x($arr,'deny_gid')) ? trim($arr['deny_gid']) : ''); $arr['item_private'] = ((x($arr,'item_private')) ? intval($arr['item_private']) : 0 ); $arr['item_flags'] = ((x($arr,'item_flags')) ? intval($arr['item_flags']) : 0 ); - $arr['title'] = escape_tags($arr['title']); + // only detect language if we have text content, and if the post is private but not yet // obscured, make it so. if(! ($arr['item_flags'] & ITEM_OBSCURED)) { + $arr['lang'] = detect_language($arr['body']); // apply the input filter here - if it is obscured it has been filtered already $arr['body'] = z_input_filter($arr['uid'],$arr['body'],$arr['mimetype']); + + if(local_user() && (! $arr['sig'])) { + $channel = get_app()->get_channel(); + if($channel['channel_hash'] === $arr['author_xchan']) { + $arr['sig'] = base64url_encode(rsa_sign($arr['body'],$channel['channel_prvkey'])); + $arr['item_flags'] |= ITEM_VERIFIED; + } + } + $allowed_languages = get_pconfig($arr['uid'],'system','allowed_languages'); if((is_array($allowed_languages)) && ($arr['lang']) && (! array_key_exists($arr['lang'],$allowed_languages))) { @@ -1767,22 +1797,6 @@ function item_store_update($arr,$allow_exec = false) { $uid = $arr['uid']; unset($arr['uid']); - - $arr['lang'] = detect_language($arr['body']); - - $allowed_languages = get_pconfig($arr['uid'],'system','allowed_languages'); - - if((is_array($allowed_languages)) && ($arr['lang']) && (! array_key_exists($arr['lang'],$allowed_languages))) { - $translate = array('item' => $arr, 'from' => $arr['lang'], 'to' => $allowed_languages, 'translated' => false); - call_hooks('item_translate', $translate); - if((! $translate['translated']) && (intval(get_pconfig($arr['uid'],'system','reject_disallowed_languages')))) { - logger('item_store: language ' . $arr['lang'] . ' not accepted for uid ' . $arr['uid']); - $ret['message'] = 'language not accepted'; - return $ret; - } - $arr = $translate['item']; - } - $arr['mimetype'] = ((x($arr,'mimetype')) ? notags(trim($arr['mimetype'])) : 'text/bbcode'); if(($arr['mimetype'] == 'application/x-php') && (! $allow_exec)) { @@ -1791,30 +1805,60 @@ function item_store_update($arr,$allow_exec = false) { return $ret; } + if(! ($arr['item_flags'] & ITEM_OBSCURED)) { + + $arr['lang'] = detect_language($arr['body']); + // apply the input filter here - if it is obscured it has been filtered already + $arr['body'] = z_input_filter($arr['uid'],$arr['body'],$arr['mimetype']); + + if(local_user() && (! $arr['sig'])) { + $channel = get_app()->get_channel(); + if($channel['channel_hash'] === $arr['author_xchan']) { + $arr['sig'] = base64url_encode(rsa_sign($arr['body'],$channel['channel_prvkey'])); + $arr['item_flags'] |= ITEM_VERIFIED; + } + } - // Shouldn't happen but we want to make absolutely sure it doesn't leak from a plugin. + $allowed_languages = get_pconfig($arr['uid'],'system','allowed_languages'); + + if((is_array($allowed_languages)) && ($arr['lang']) && (! array_key_exists($arr['lang'],$allowed_languages))) { + $translate = array('item' => $arr, 'from' => $arr['lang'], 'to' => $allowed_languages, 'translated' => false); + call_hooks('item_translate', $translate); + if((! $translate['translated']) && (intval(get_pconfig($arr['uid'],'system','reject_disallowed_languages')))) { + logger('item_store: language ' . $arr['lang'] . ' not accepted for uid ' . $arr['uid']); + $ret['message'] = 'language not accepted'; + return $ret; + } + $arr = $translate['item']; + } + if($arr['item_private']) { + $key = get_config('system','pubkey'); + $arr['item_flags'] = $arr['item_flags'] | ITEM_OBSCURED; + if($arr['title']) + $arr['title'] = json_encode(aes_encapsulate($arr['title'],$key)); + if($arr['body']) + $arr['body'] = json_encode(aes_encapsulate($arr['body'],$key)); + } - if($arr['mimetype'] != 'text/html' && $arr['mimetype'] != 'application/x-php') { + } - if((strpos($arr['body'],'<') !== false) || (strpos($arr['body'],'>') !== false)) - $arr['body'] = escape_tags($arr['body']); - if((x($arr,'object')) && is_array($arr['object'])) { - activity_sanitise($arr['object']); - $arr['object'] = json_encode($arr['object']); - } + if((x($arr,'object')) && is_array($arr['object'])) { + activity_sanitise($arr['object']); + $arr['object'] = json_encode($arr['object']); + } - if((x($arr,'target')) && is_array($arr['target'])) { - activity_sanitise($arr['target']); - $arr['target'] = json_encode($arr['target']); - } + if((x($arr,'target')) && is_array($arr['target'])) { + activity_sanitise($arr['target']); + $arr['target'] = json_encode($arr['target']); + } - if((x($arr,'attach')) && is_array($arr['attach'])) { - activity_sanitise($arr['attach']); - $arr['attach'] = json_encode($arr['attach']); - } + if((x($arr,'attach')) && is_array($arr['attach'])) { + activity_sanitise($arr['attach']); + $arr['attach'] = json_encode($arr['attach']); } + $orig = q("select * from item where id = %d and uid = %d limit 1", intval($orig_post_id), intval($uid) @@ -1860,6 +1904,7 @@ function item_store_update($arr,$allow_exec = false) { $arr['item_restrict'] = ((x($arr,'item_restrict')) ? intval($arr['item_restrict']) : $orig[0]['item_restrict'] ); $arr['item_flags'] = ((x($arr,'item_flags')) ? intval($arr['item_flags']) : $orig[0]['item_flags'] ); + $arr['sig'] = ((x($arr,'sig')) ? $arr['sig'] : ''); call_hooks('post_remote_update',$arr); diff --git a/include/zot.php b/include/zot.php index 945ea0cf9..8df57bb51 100644 --- a/include/zot.php +++ b/include/zot.php @@ -1327,6 +1327,14 @@ function process_mail_delivery($sender,$arr,$deliveries) { $result = array(); + + + if($sender['hash'] != $arr['from_xchan']) { + logger('process_mail_delivery: sender is not mail author'); + return; + } + + foreach($deliveries as $d) { $r = q("select * from channel where channel_hash = '%s' limit 1", diff --git a/install/database.sql b/install/database.sql index eb573fb4d..6be3a31aa 100644 --- a/install/database.sql +++ b/install/database.sql @@ -441,7 +441,7 @@ CREATE TABLE IF NOT EXISTS `item` ( `resource_id` char(255) NOT NULL DEFAULT '', `resource_type` char(16) NOT NULL DEFAULT '', `attach` mediumtext NOT NULL, - `inform` mediumtext NOT NULL, + `sig` text NOT NULL DEFAULT '', `location` char(255) NOT NULL DEFAULT '', `coord` char(255) NOT NULL DEFAULT '', `comment_policy` char(255) NOT NULL DEFAULT '', diff --git a/install/update.php b/install/update.php index 8a583a947..20af0de45 100644 --- a/install/update.php +++ b/install/update.php @@ -1,6 +1,6 @@ <?php -define( 'UPDATE_VERSION' , 1076 ); +define( 'UPDATE_VERSION' , 1077 ); /** * @@ -859,3 +859,10 @@ ADD INDEX ( `channel_a_republish` )"); return UPDATE_SUCCESS; return UPDATE_FAILED; } + +function update_r1076() { + $r = q("ALTER TABLE `item` CHANGE `inform` `sig` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' "); + if($r) + return UPDATE_SUCCESS; + return UPDATE_FAILED; +} diff --git a/mod/item.php b/mod/item.php index f1fbd53b3..d6a7789dd 100644 --- a/mod/item.php +++ b/mod/item.php @@ -588,7 +588,6 @@ function item_post(&$a) { $datarray['app'] = $app; $datarray['location'] = $location; $datarray['coord'] = $coord; - $datarray['inform'] = $inform; $datarray['verb'] = $verb; $datarray['allow_cid'] = $str_contact_allow; $datarray['allow_gid'] = $str_group_allow; @@ -642,6 +641,13 @@ function item_post(&$a) { $datarray['body'] = z_input_filter($datarray['uid'],$datarray['body'],$datarray['mimetype']); + if(local_user()) { + if($channel['channel_hash'] === $datarray['author_xchan']) { + $datarray['sig'] = base64url_encode(rsa_sign($datarray['body'],$channel['channel_prvkey'])); + $datarray['item_flags'] = $datarray['item_flags'] | ITEM_VERIFIED; + } + } + logger('Encrypting local storage'); $key = get_config('system','pubkey'); $datarray['item_flags'] = $datarray['item_flags'] | ITEM_OBSCURED; @@ -651,17 +657,16 @@ function item_post(&$a) { $datarray['body'] = json_encode(aes_encapsulate($datarray['body'],$key)); } - - - if($orig_post) { - $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `mimetype` = '%s', `attach` = '%s', `edited` = '%s', layout_mid = '%s' WHERE `id` = %d AND `uid` = %d LIMIT 1", + $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `mimetype` = '%s', `attach` = '%s', `edited` = '%s', layout_mid = '%s', sig = '%s', item_flags = %d WHERE `id` = %d AND `uid` = %d LIMIT 1", dbesc($datarray['title']), dbesc($datarray['body']), dbesc($datarray['mimetype']), dbesc($datarray['attach']), dbesc(datetime_convert()), dbesc($layout_mid), + dbesc($datarray['sig']), + intval($item_flags), intval($post_id), intval($profile_uid) ); diff --git a/mod/photos.php b/mod/photos.php index 8e63a42ea..45c8e67c6 100644 --- a/mod/photos.php +++ b/mod/photos.php @@ -454,15 +454,15 @@ function photos_post(&$a) { if(strlen($newinform) && strlen($inform)) $newinform .= ','; $newinform .= $inform; - - $r = q("UPDATE `item` SET `tag` = '%s', `inform` = '%s', `edited` = '%s', `changed` = '%s' WHERE `id` = %d AND `uid` = %d LIMIT 1", - dbesc($newtag), - dbesc($newinform), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - intval($item_id), - intval($page_owner_uid) - ); +//FIXME - inform is gone +// $r = q("UPDATE `item` SET `tag` = '%s', `inform` = '%s', `edited` = '%s', `changed` = '%s' WHERE `id` = %d AND `uid` = %d LIMIT 1", +// dbesc($newtag), +// dbesc($newinform), +// dbesc(datetime_convert()), +// dbesc(datetime_convert()), +// intval($item_id), +// intval($page_owner_uid) +// ); $best = 0; foreach($p as $scales) { diff --git a/view/tpl/conv_item.tpl b/view/tpl/conv_item.tpl index 806e2ba02..f327b62a9 100755 --- a/view/tpl/conv_item.tpl +++ b/view/tpl/conv_item.tpl @@ -34,6 +34,8 @@ {{if $item.lock}}<div class="wall-item-lock"><img src="images/lock_icon.gif" class="lockview" alt="{{$item.lock}}" onclick="lockview(event,{{$item.id}});" /></div> {{else}}<div class="wall-item-lock"></div>{{/if}} <div class="wall-item-location" id="wall-item-location-{{$item.id}}">{{$item.location}}</div> + {{if $item.verified}}<img src="images/lock_icon.gif" alt="{{$item.verified}}" title="{{$item.verified}}" height="10" width="10" />{{/if}} + {{if $item.unverified}}<img src="images/unlock_icon.gif" alt="{{$item.unverified}}" title="{{$item.unverified}}" height="10" width="10" />{{/if}} </div> </div> <div class="wall-item-author"> |