diff options
Diffstat (limited to 'include/zot.php')
-rw-r--r-- | include/zot.php | 818 |
1 files changed, 693 insertions, 125 deletions
diff --git a/include/zot.php b/include/zot.php index 524f958ad..bddbc9bee 100644 --- a/include/zot.php +++ b/include/zot.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ require_once('include/crypto.php'); require_once('include/items.php'); @@ -47,34 +47,6 @@ function zot_get_hubloc($arr,$primary = false) { } -function zot_notify($channel,$url,$type = 'notify',$recipients = null, $remote_key = null) { - - $params = array( - 'type' => $type, - 'sender' => json_encode(array( - 'guid' => $channel['channel_guid'], - 'guid_sig' => base64url_encode(rsa_sign($channel['channel_guid'],$channel['channel_prvkey'])), - 'url' => z_root(), - 'url_sig' => base64url_encode(rsa_sign(z_root(),$channel['channel_prvkey'])) - )), - 'callback' => '/post', - 'version' => ZOT_REVISION - ); - - - if($recipients) - $params['recipients'] = json_encode($recipients); - - // Hush-hush ultra top-secret mode - - if($remote_key) { - $params = aes_encapsulate($params,$remote_key); - } - - $x = z_post_url($url,$params); - return($x); -} - /* * * zot_build_packet builds a notification packet that you can either @@ -117,10 +89,33 @@ function zot_build_packet($channel,$type = 'notify',$recipients = null, $remote_ } +/** + * @function: zot_zot + * @param: string $url + * @param: array $data + * + * @returns: array => see z_post_url for returned data format + */ + + + function zot_zot($url,$data) { return z_post_url($url,array('data' => $data)); } +/** + * @function: zot_finger + * + * Look up information about channel + * @param: string $webbie + * does not have to be host qualified e.g. 'foo' is treated as 'foo@thishub' + * @param: array $channel + * (optional), if supplied permissions will be enumerated specifically for $channel + * + * @returns: array => see z_post_url and mod/zfinger.php + */ + + function zot_finger($webbie,$channel) { @@ -135,6 +130,11 @@ function zot_finger($webbie,$channel) { $xchan_addr = $address . '@' . $host; + if((! $address) || (! $xchan_addr)) { + logger('zot_finger: no address :' . $webbie); + return array('success' => false); + } + $r = q("select xchan.*, hubloc.* from xchan left join hubloc on xchan_hash = hubloc_hash where xchan_addr = '%s' and (hubloc_flags & %d) limit 1", @@ -152,7 +152,7 @@ function zot_finger($webbie,$channel) { $rhs = '/.well-known/zot-info'; $https = ((strpos($url,'https://') === 0) ? true : false); - logger('zot_finger: ' . $url, LOGGER_DEBUG); + logger('zot_finger: ' . $address . ' at ' . $url, LOGGER_DEBUG); if($channel) { $postvars = array( @@ -191,6 +191,15 @@ function zot_finger($webbie,$channel) { } +/** + * @function: zot_refresh + * + * zot_refresh is typically invoked when somebody has changed permissions of a channel and they are notified + * to fetch new permissions via a finger operation. This may result in a new connection (abook entry) being added to a local channel + * and it may result in auto-permissions being granted. + * + */ + function zot_refresh($them,$channel = null) { logger('zot_refresh: them: ' . print_r($them,true), LOGGER_DATA); @@ -329,37 +338,25 @@ function zot_refresh($them,$channel = null) { intval(ABOOK_FLAG_SELF) ); if($z) - proc_run('php','include/notifier.php','permissions_update',$z[0]['abook_id']); + proc_run('php','include/notifier.php','permission_update',$z[0]['abook_id']); } } } } - else { - - logger('zot_refresh: importing profile if available'); - - // Are we a directory server of some kind? - $dirmode = intval(get_config('system','directory_mode')); - if($dirmode != DIRECTORY_MODE_NORMAL) { - if(array_key_exists('profile',$x) && is_array($x['profile'])) { - import_directory_profile($x['hash'],$x['profile']); - } - else { - // they may have made it private - $r = q("delete from xprof where xprof_hash = '%s' limit 1", - dbesc($x['hash']) - ); - $r = q("delete from xtag where xtag_hash = '%s' limit 1", - dbesc($x['hash']) - ); - } - } - } return true; } return false; } +/** + * @function: zot_gethub + * + * A guid and a url, both signed by the sender, distinguish a known sender at a known location + * This function looks these up to see if the channel is known. If not, we will need to verify it. + * @returns: array => hubloc record + */ + + function zot_gethub($arr) { @@ -378,7 +375,7 @@ function zot_gethub($arr) { return $r[0]; } } - logger('zot_gethub: not found', LOGGER_DEBUG); + logger('zot_gethub: not found: ' . print_r($arr,true), LOGGER_DEBUG); return null; } @@ -417,6 +414,16 @@ function zot_register_hub($arr) { function import_xchan($arr) { $ret = array('success' => false); + $dirmode = intval(get_config('system','directory_mode')); + + $changed = false; + + if(! (is_array($arr) && array_key_exists('success',$arr) && $arr['success'])) { + logger('import_xchan: invalid data packet: ' . print_r($arr,true)); + $ret['message'] = t('Invalid data packet'); + return $ret; + } + $xchan_hash = base64url_encode(hash('whirlpool',$arr['guid'] . $arr['guid_sig'], true)); $import_photos = false; @@ -427,6 +434,9 @@ function import_xchan($arr) { return $ret; } + + logger('import_xchan: ' . $xchan_hash, LOGGER_DEBUG); + $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($xchan_hash) ); @@ -454,21 +464,47 @@ function import_xchan($arr) { $new_flags = $r[0]['xchan_flags']; - if(($r[0]['xchan_name_date'] != $arr['name_updated']) || ($r[0]['xchan_connurl'] != $arr['connections_url']) || ($r[0]['xchan_flags'] != $new_flags)) { - $r = q("update xchan set xchan_name = '%s', xchan_name_date = '%s', xchan_connurl = '%s', xchan_flags = %d where xchan_hash = '%s' limit 1", + if(($r[0]['xchan_name_date'] != $arr['name_updated']) + || ($r[0]['xchan_connurl'] != $arr['connections_url']) + || ($r[0]['xchan_flags'] != $new_flags) + || ($r[0]['xchan_addr'] != $arr['address']) + || ($r[0]['xchan_url'] != $arr['url'])) { + $r = q("update xchan set xchan_name = '%s', xchan_name_date = '%s', xchan_connurl = '%s', xchan_flags = %d, + xchan_addr = '%s', xchan_url = '%s' where xchan_hash = '%s' limit 1", dbesc($arr['name']), dbesc($arr['name_updated']), dbesc($arr['connections_url']), intval($new_flags), + dbesc($arr['address']), + dbesc($arr['url']), dbesc($xchan_hash) ); + + logger('import_xchan: existing: ' . print_r($r[0],true), LOGGER_DATA); + logger('import_xchan: new: ' . print_r($arr,true), LOGGER_DATA); + + update_modtime($xchan_hash); + $changed = true; } } else { $import_photos = true; + + + if((($arr['site']['directory_mode'] === 'standalone') || ($dirmode & DIRECTORY_MODE_STANDALONE)) +&& ($arr['site']['url'] != z_root())) + $arr['searchable'] = false; + + $hidden = (1 - intval($arr['searchable'])); + + if($hidden) + $new_flags = XCHAN_FLAGS_HIDDEN; + else + $new_flags = 0; + $x = q("insert into xchan ( xchan_hash, xchan_guid, xchan_guid_sig, xchan_pubkey, xchan_photo_mimetype, - xchan_photo_l, xchan_addr, xchan_url, xchan_connurl, xchan_name, xchan_network, xchan_photo_date, xchan_name_date) - values ( '%s', '%s', '%s', '%s' , '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", + xchan_photo_l, xchan_addr, xchan_url, xchan_connurl, xchan_name, xchan_network, xchan_photo_date, xchan_name_date, xchan_flags) + values ( '%s', '%s', '%s', '%s' , '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d) ", dbesc($xchan_hash), dbesc($arr['guid']), dbesc($arr['guid_sig']), @@ -481,15 +517,18 @@ function import_xchan($arr) { dbesc($arr['name']), dbesc('zot'), dbesc($arr['photo_updated']), - dbesc($arr['name_updated']) + dbesc($arr['name_updated']), + intval($new_flags) ); + update_modtime($xchan_hash); + $changed = true; } if($import_photos) { - require_once("Photo.php"); + require_once('include/photo/photo_driver.php'); $photos = import_profile_photo($arr['photo'],$xchan_hash); $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' @@ -501,9 +540,20 @@ function import_xchan($arr) { dbesc($photos[3]), dbesc($xchan_hash) ); + + update_modtime($xchan_hash); + $changed = true; } + // what we are missing for true hub independence is for any changes in the primary hub to + // get reflected not only in the hublocs, but also to update the URLs and addr in the appropriate xchan + if($arr['locations']) { + + $xisting = q("select hubloc_id, hubloc_url from hubloc where hubloc_hash = '%s'", + dbesc($xchan_hash) + ); + foreach($arr['locations'] as $location) { if(! rsa_verify($location['url'],base64url_decode($location['url_sig']),$arr['key'])) { logger('import_xchan: Unable to verify site signature for ' . $location['url']); @@ -511,20 +561,49 @@ function import_xchan($arr) { continue; } - $r = q("select * from hubloc where hubloc_hash = '%s' and hubloc_url = '%s' limit 1", + for($x = 0; $x < count($xisting); $x ++) { + if($xisting[$x]['hubloc_url'] == $location['url']) { + $xisting[$x]['updated'] = true; + } + } + + $r = q("select * from hubloc where hubloc_hash = '%s' and hubloc_guid = '%s' and hubloc_guid_sig = '%s' + and hubloc_url = '%s' and hubloc_url_sig = '%s' limit 1", dbesc($xchan_hash), - dbesc($location['url']) + dbesc($arr['guid']), + dbesc($arr['guid_sig']), + dbesc($location['url']), + dbesc($location['url_sig']) ); if($r) { - if(($r[0]['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY) && (! $location['primary'])) { + logger('import_xchan: hub exists: ' . $location['url']); + if((($r[0]['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY) && (! $location['primary'])) + || ((! ($r[0]['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY)) && ($location['primary']))) { $r = q("update hubloc set hubloc_flags = (hubloc_flags ^ %d) where hubloc_id = %d limit 1", intval(HUBLOC_FLAGS_PRIMARY), intval($r[0]['hubloc_id']) ); + update_modtime($xchan_hash); + $changed = true; } continue; } + if(! $location['sitekey']) { + logger('import_xchan: empty hubloc sitekey. ' . print_r($location,true)); + continue; + } + + // new hub claiming to be primary. Make it so. + + if(intval($location['primary'])) { + $r = q("update hubloc set hubloc_flags = (hubloc_flags ^ %d) where hubloc_hash = '%s' and (hubloc_flags & %d )", + intval(HUBLOC_FLAGS_PRIMARY), + dbesc($xchan_hash), + intval(HUBLOC_FLAGS_PRIMARY) + ); + } + logger('import_xchan: new hub: ' . $location['url']); $r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_flags, hubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey) values ( '%s','%s','%s','%s', %d ,'%s','%s','%s','%s','%s')", dbesc($arr['guid']), @@ -538,8 +617,61 @@ function import_xchan($arr) { dbesc($location['callback']), dbesc($location['sitekey']) ); + update_modtime($xchan_hash); + $changed = true; + } + + // get rid of any hubs we have for this channel which weren't reported. + if($xisting) { + foreach($xisting as $x) { + if(! array_key_exists('updated',$x)) { + logger('import_xchan: removing unreferenced hub location ' . $x['hubloc_url']); + $r = q("delete from hubloc where hubloc_id = %d limit 1", + intval($x['hubloc_id']) + ); + update_modtime($xchan_hash); + $changed = true; + } + } + } + + } + + // Are we a directory server of some kind? + + if($dirmode != DIRECTORY_MODE_NORMAL) { + if(array_key_exists('profile',$arr) && is_array($arr['profile'])) { + $profile_changed = import_directory_profile($xchan_hash,$arr['profile']); + if($profile_changed) { + update_modtime($xchan_hash); + $changed = true; + } + } + else { + logger('import_xchan: profile not available - hiding'); + // they may have made it private + $r = q("delete from xprof where xprof_hash = '%s' limit 1", + dbesc($xchan_hash) + ); + $r = q("delete from xtag where xtag_hash = '%s' limit 1", + dbesc($xchan_hash) + ); + } + } + if(array_key_exists('site',$arr) && is_array($arr['site'])) { + $profile_changed = import_site($arr['site'],$arr['key']); + if($profile_changed) { + update_modtime($xchan_hash); + $changed = true; } + } + + + + if($changed) { + // send out a directory mirror update packet if we're a directory server or some kind + } @@ -586,6 +718,18 @@ function zot_process_response($hub,$arr,$outq) { logger('zot_process_response: ' . print_r($x,true), LOGGER_DATA); } +/** + * @function: zot_fetch + * + * We received a notification packet (in mod/post.php) that a message is waiting for us, and we've verified the sender. + * Now send back a pickup message, using our message tracking ID ($arr['secret']), which we will sign. + * The entire pickup message is encrypted with the remote site's public key. + * If everything checks out on the remote end, we will receive back a packet containing one or more messages, + * which will be processed before returning. + * + */ + + function zot_fetch($arr) { logger('zot_fetch: ' . print_r($arr,true), LOGGER_DATA); @@ -594,7 +738,7 @@ function zot_fetch($arr) { $ret_hub = zot_gethub($arr['sender']); if(! $ret_hub) { - logger('zot_fetch: not ret_hub'); + logger('zot_fetch: no hub: ' . print_r($arr['sender'],true)); return; } @@ -620,11 +764,17 @@ function zot_fetch($arr) { return $result; } +/** + * @function zot_import + * + * Process an incoming array of messages which were obtained via pickup, and + * import, update, delete as directed. + * + * The message types handled here are 'activity' (e.g. posts), 'mail' and 'profile' + */ function zot_import($arr) { -// logger('zot_import: ' . print_r($arr,true), LOGGER_DATA); - $data = json_decode($arr['body'],true); if(! $data) { @@ -632,14 +782,10 @@ function zot_import($arr) { return array(); } -// logger('zot_import: data1: ' . print_r($data,true)); - if(array_key_exists('iv',$data)) { $data = json_decode(aes_unencapsulate($data,get_config('system','prvkey')),true); } - logger('zot_import: data' . print_r($data,true), LOGGER_DATA); - $incoming = $data['pickup']; $return = array(); @@ -692,10 +838,12 @@ function zot_import($arr) { if($i['message']) { if($i['message']['type'] === 'activity') { $arr = get_item_elements($i['message']); + if(! array_key_exists('created',$arr)) { logger('Activity rejected: probable failure to lookup author/owner. ' . print_r($i['message'],true)); continue; } + logger('Activity received: ' . print_r($arr,true), LOGGER_DATA); logger('Activity recipients: ' . print_r($deliveries,true), LOGGER_DATA); @@ -722,6 +870,16 @@ function zot_import($arr) { $result = process_profile_delivery($i['notify']['sender'],$arr,$deliveries); } + elseif($i['message']['type'] === 'channel_sync') { +// $arr = get_channelsync_elements($i['message']); + + $arr = $i['message']; + + logger('Channel sync received: ' . print_r($arr,true), LOGGER_DATA); + logger('Channel sync recipients: ' . print_r($deliveries,true), LOGGER_DATA); + + $result = process_channel_sync_delivery($i['notify']['sender'],$arr,$deliveries); + } } if($result) $return = array_merge($return,$result); @@ -780,7 +938,7 @@ function public_recips($msg) { $x = array(); $r = array_merge($r,$x); - + logger('public_recips: ' . print_r($r,true), LOGGER_DATA); return $r; } @@ -832,7 +990,7 @@ function allowed_public_recips($msg) { $condensed_recips[] = $rr['hash']; $results = array(); - $r = q("select channel_hash as hash from channel left join abook on abook_channel = channel_id where abook_hash = '%s' ", + $r = q("select channel_hash as hash from channel left join abook on abook_channel = channel_id where abook_xchan = '%s' ", dbesc($hash) ); if($r) { @@ -857,34 +1015,52 @@ function process_delivery($sender,$arr,$deliveries,$relay) { ); if(! $r) { - $result[] = array($d['hash'],'not found'); + $result[] = array($d['hash'],'recipients not found'); continue; } $channel = $r[0]; - $perm = (($arr['uri'] == $arr['parent_uri']) ? 'send_stream' : 'post_comments'); + $tag_delivery = tgroup_check($channel['channel_id'],$arr); + + $perm = (($arr['mid'] == $arr['parent_mid']) ? 'send_stream' : 'post_comments'); - if(! perm_is_allowed($channel['channel_id'],$sender['hash'],$perm)) { + // This is our own post, possibly coming from a channel clone + + if($arr['owner_xchan'] == $d['hash']) { + $arr['item_flags'] = $arr['item_flags'] | ITEM_WALL; + } + else { + // clear the wall flag if it is set + if($arr['item_flags'] & ITEM_WALL) { + $arr['item_flags'] = ($arr['item_flags'] ^ ITEM_WALL); + } + } + + if((! perm_is_allowed($channel['channel_id'],$sender['hash'],$perm)) && (! $tag_delivery)) { logger("permission denied for delivery {$channel['channel_id']}"); - $result[] = array($d['hash'],'permission denied'); + $result[] = array($d['hash'],'permission denied',$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>'); continue; } if($arr['item_restrict'] & ITEM_DELETED) { + + // remove_community_tag is a no-op if this isn't a community tag activity + remove_community_tag($sender,$arr,$channel['channel_id']); + $item_id = delete_imported_item($sender,$arr,$channel['channel_id']); - $result[] = array($d['hash'],'deleted'); + $result[] = array($d['hash'],'deleted',$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>'); if($relay && $item_id) { logger('process_delivery: invoking relay'); proc_run('php','include/notifier.php','relay',intval($item_id)); - $result[] = array($d['hash'],'relayed'); + $result[] = array($d['hash'],'relayed',$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>'); } continue; } - // for events, extract the event info and create and event linked to an item + // for events, extract the event info and create an event linked to an item if((x($arr,'obj_type')) && (activity_match($arr['obj_type'],ACTIVITY_OBJ_EVENT))) { require_once('include/event.php'); @@ -894,13 +1070,13 @@ function process_delivery($sender,$arr,$deliveries,$relay) { $ev['uid'] = $channel['channel_id']; $ev['account'] = $channel['channel_account_id']; $ev['edited'] = $arr['edited']; - $ev['uri'] = $arr['uri']; + $ev['mid'] = $arr['mid']; $ev['private'] = $arr['item_private']; // is this an edit? - $r = q("SELECT resource_id FROM item where uri = '%s' and uid = %d and resource_type = 'event' limit 1", - dbesc($arr['uri']), + $r = q("SELECT resource_id FROM item where mid = '%s' and uid = %d and resource_type = 'event' limit 1", + dbesc($arr['mid']), intval($channel['channel_id']) ); if($r) { @@ -909,34 +1085,37 @@ function process_delivery($sender,$arr,$deliveries,$relay) { $xyz = event_store($ev); - $result = array($d['hash'],'event processed'); + $result = array($d['hash'],'event processed',$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>'); continue; } } - $r = q("select id, edited from item where uri = '%s' and uid = %d limit 1", - dbesc($arr['uri']), + $r = q("select id, edited from item where mid = '%s' and uid = %d limit 1", + dbesc($arr['mid']), intval($channel['channel_id']) ); if($r) { - if($arr['edited'] > $r[0]['edited']) + if($arr['edited'] > $r[0]['edited']) { + $arr['id'] = $r[0]['id']; + $arr['uid'] = $channel['channel_id']; update_imported_item($sender,$arr,$channel['channel_id']); - $result[] = array($d['hash'],'updated'); + } + $result[] = array($d['hash'],'updated',$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>'); $item_id = $r[0]['id']; } else { $arr['aid'] = $channel['channel_account_id']; $arr['uid'] = $channel['channel_id']; $item_id = item_store($arr); - $result[] = array($d['hash'],'posted'); + $result[] = array($d['hash'],(($item_id) ? 'posted' : 'storage failed'),$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>'); } if($relay && $item_id) { logger('process_delivery: invoking relay'); proc_run('php','include/notifier.php','relay',intval($item_id)); - $result[] = array($d['hash'],'relayed'); + $result[] = array($d['hash'],'relayed',$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>'); } } @@ -949,8 +1128,71 @@ function process_delivery($sender,$arr,$deliveries,$relay) { } +function remove_community_tag($sender,$arr,$uid) { + + if(! (activity_match($arr['verb'],ACTIVITY_TAG) && ($arr['obj_type'] == ACTIVITY_OBJ_TAGTERM))) + return; + + logger('remove_community_tag: invoked'); + + + if(! get_pconfig($uid,'system','blocktags')) { + logger('remove_community tag: permission denied.'); + return; + } + + $r = q("select * from item where mid = '%s' and uid = %d limit 1", + dbesc($arr['mid']), + intval($uid) + ); + if(! $r) { + logger('remove_community_tag: no item'); + return; + } + + if(($sender['hash'] != $r[0]['owner_xchan']) && ($sender['hash'] != $r[0]['author_xchan'])) { + logger('remove_community_tag: sender not authorised.'); + return; + } + + $i = $r[0]; + + if($i['target']) + $i['target'] = json_decode_plus($i['target']); + if($i['object']) + $i['object'] = json_decode_plus($i['object']); + + if(! ($i['target'] && $i['object'])) { + logger('remove_community_tag: no target/object'); + return; + } + + $message_id = $i['target']['id']; + + $r = q("select id from item where mid = '%s' and uid = %d limit 1", + dbesc($message_id), + intval($uid) + ); + if(! $r) { + logger('remove_community_tag: no parent message'); + return; + } + + $x = q("delete from term where uid = %d and oid = %d and otype = %d and type = %d and term = '%s' and url = '%s' limit 1", + intval($uid), + intval($r[0]['id']), + intval(TERM_OBJ_POST), + intval(TERM_HASHTAG), + dbesc($i['object']['title']), + dbesc(get_rel_link($i['object']['link'],'alternate')) + ); + + return; +} + function update_imported_item($sender,$item,$uid) { -// FIXME + + item_store_update($item); logger('update_imported_item'); } @@ -960,10 +1202,10 @@ function delete_imported_item($sender,$item,$uid) { logger('delete_imported_item invoked',LOGGER_DEBUG); $r = q("select id from item where ( author_xchan = '%s' or owner_xchan = '%s' ) - and uri = '%s' and uid = %d limit 1", + and mid = '%s' and uid = %d limit 1", dbesc($sender['hash']), dbesc($sender['hash']), - dbesc($item['uri']), + dbesc($item['mid']), intval($uid) ); @@ -978,37 +1220,56 @@ function delete_imported_item($sender,$item,$uid) { } function process_mail_delivery($sender,$arr,$deliveries) { + + + $result = array(); foreach($deliveries as $d) { $r = q("select * from channel where channel_hash = '%s' limit 1", dbesc($d['hash']) ); - if(! $r) + if(! $r) { + $result[] = array($d['hash'],'not found'); continue; + } $channel = $r[0]; if(! perm_is_allowed($channel['channel_id'],$sender['hash'],'post_mail')) { logger("permission denied for mail delivery {$channel['channel_id']}"); + $result[] = array($d['hash'],'permission denied',$channel['channel_name']); continue; } - $r = q("select id from mail where uri = '%s' and channel_id = %d limit 1", - dbesc($arr['uri']), + $r = q("select id from mail where mid = '%s' and channel_id = %d limit 1", + dbesc($arr['mid']), intval($channel['channel_id']) ); if($r) { - logger('duplicate mail received'); + if($arr['mail_flags'] & MAIL_RECALLED) { + $x = q("delete from mail where id = %d and channel_id = %d limit 1", + intval($r[0]['id']), + intval($channel['channel_id']) + ); + $result[] = array($d['hash'],'mail recalled',$channel['channel_name']); + logger('mail_recalled'); + } + else { + $result[] = array($d['hash'],'duplicate mail received',$channel['channel_name']); + logger('duplicate mail received'); + } continue; } else { $arr['account_id'] = $channel['channel_account_id']; $arr['channel_id'] = $channel['channel_id']; $item_id = mail_store($arr); + $result[] = array($d['hash'],'mail delivered',$channel['channel_name']); } } + return $result; } function process_profile_delivery($sender,$arr,$deliveries) { @@ -1019,17 +1280,26 @@ function process_profile_delivery($sender,$arr,$deliveries) { import_directory_profile($sender['hash'],$arr); } + +/* + * @function import_directory_profile + * + * @returns boolean $updated if something changed + * + */ + function import_directory_profile($hash,$profile) { logger('import_directory_profile', LOGGER_DEBUG); if(! $hash) - return; + return false; $arr = array(); $arr['xprof_hash'] = $hash; $arr['xprof_desc'] = (($profile['description']) ? htmlentities($profile['description'], ENT_COMPAT,'UTF-8',false) : ''); $arr['xprof_dob'] = datetime_convert('','',$profile['birthday'],'Y-m-d'); // !!!! check this for 0000 year + $arr['xprof_age'] = (($profile['age']) ? intval($profile['age']) : 0); $arr['xprof_gender'] = (($profile['gender']) ? htmlentities($profile['gender'], ENT_COMPAT,'UTF-8',false) : ''); $arr['xprof_marital'] = (($profile['marital']) ? htmlentities($profile['marital'], ENT_COMPAT,'UTF-8',false) : ''); $arr['xprof_sexual'] = (($profile['sexual']) ? htmlentities($profile['sexual'], ENT_COMPAT,'UTF-8',false) : ''); @@ -1043,47 +1313,60 @@ function import_directory_profile($hash,$profile) { import_directory_keywords($hash,$profile['keywords']); foreach($profile['keywords'] as $kw) { $kw = trim(htmlentities($kw,ENT_COMPAT,'UTF-8',false)); + $kw = trim($kw,','); } $clean[] = $kw; } $arr['xprof_keywords'] = implode(' ',$clean); - $r = q("select * from xprof where xprof_hash = '%s' limit 1", dbesc($hash) ); if($r) { - $x = q("update xprof set - xprof_desc = '%s', - xprof_dob = '%s', - xprof_gender = '%s', - xprof_marital = '%s', - xprof_sexual = '%s', - xprof_locale = '%s', - xprof_region = '%s', - xprof_postcode = '%s', - xprof_country = '%s', - xprof_keywords = '%s' - where xprof_hash = '%s' limit 1", - dbesc($arr['xprof_desc']), - dbesc($arr['xprof_dob']), - dbesc($arr['xprof_gender']), - dbesc($arr['xprof_marital']), - dbesc($arr['xprof_sexual']), - dbesc($arr['xprof_locale']), - dbesc($arr['xprof_region']), - dbesc($arr['xprof_postcode']), - dbesc($arr['xprof_country']), - dbesc($arr['xprof_keywords']), - dbesc($arr['xprof_hash']) - ); + $update = false; + foreach($r[0] as $k => $v) { + if((array_key_exists($k,$arr)) && ($arr[$k] != $v)) { + $update = true; + break; + } + } + if($update) { + $x = q("update xprof set + xprof_desc = '%s', + xprof_dob = '%s', + xprof_age = %d, + xprof_gender = '%s', + xprof_marital = '%s', + xprof_sexual = '%s', + xprof_locale = '%s', + xprof_region = '%s', + xprof_postcode = '%s', + xprof_country = '%s', + xprof_keywords = '%s' + where xprof_hash = '%s' limit 1", + dbesc($arr['xprof_desc']), + dbesc($arr['xprof_dob']), + intval($arr['xprof_age']), + dbesc($arr['xprof_gender']), + dbesc($arr['xprof_marital']), + dbesc($arr['xprof_sexual']), + dbesc($arr['xprof_locale']), + dbesc($arr['xprof_region']), + dbesc($arr['xprof_postcode']), + dbesc($arr['xprof_country']), + dbesc($arr['xprof_keywords']), + dbesc($arr['xprof_hash']) + ); + } } else { - $x = q("insert into xprof (xprof_hash, xprof_desc, xprof_dob, xprof_gender, xprof_marital, xprof_sexual, xprof_locale, xprof_region, xprof_postcode, xprof_country, xprof_keywords) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", + $update = true; + $x = q("insert into xprof (xprof_hash, xprof_desc, xprof_dob, xprof_age, xprof_gender, xprof_marital, xprof_sexual, xprof_locale, xprof_region, xprof_postcode, xprof_country, xprof_keywords) values ('%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", dbesc($arr['xprof_hash']), dbesc($arr['xprof_desc']), dbesc($arr['xprof_dob']), + intval($arr['xprof_age']), dbesc($arr['xprof_gender']), dbesc($arr['xprof_marital']), dbesc($arr['xprof_sexual']), @@ -1095,7 +1378,12 @@ function import_directory_profile($hash,$profile) { ); } - return; + $d = array('xprof' => $arr, 'profile' => $profile, 'update' => $update); + call_hooks('import_directory_profile', $d); + + if($d['update']) + update_modtime($arr['xprof_hash']); + return $d['update']; } function import_directory_keywords($hash,$keywords) { @@ -1125,9 +1413,289 @@ function import_directory_keywords($hash,$keywords) { } foreach($clean as $x) { if(! in_array($x,$existing)) - $r = q("insert int xtag ( xtag_hash, xtag_term) values ( '%s' ,'%s' )", + $r = q("insert into xtag ( xtag_hash, xtag_term) values ( '%s' ,'%s' )", dbesc($hash), dbesc($x) ); } -}
\ No newline at end of file +} + + +function update_modtime($hash) { + $r = q("select * from updates where ud_hash = '%s' limit 1", + dbesc($hash) + ); + if($r) + q("update updates set ud_date = '%s' where ud_hash = '%s' limit 1", + dbesc(datetime_convert()), + dbesc($hash) + ); + else + q("insert into updates (ud_hash, ud_date) values ( '%s', '%s' )", + dbesc($hash), + dbesc(datetime_convert()) + ); +} + + +function import_site($arr,$pubkey) { + if( (! is_array($arr)) || (! $arr['url']) || (! $arr['url_sig'])) + return false; + + if(! rsa_verify($arr['url'],base64url_decode($arr['url_sig']),$pubkey)) { + logger('import_site: bad url_sig'); + return false; + } + + $update = false; + + $r = q("select * from site where site_url = '%s' limit 1", + dbesc($arr['url']) + ); + if($r) + $update = true; + + $site_directory = 0; + if($arr['directory_mode'] == 'normal') + $site_directory = DIRECTORY_MODE_NORMAL; + + if($arr['directory_mode'] == 'primary') + $site_directory = DIRECTORY_MODE_PRIMARY; + if($arr['directory_mode'] == 'secondary') + $site_directory = DIRECTORY_MODE_SECONDARY; + if($arr['directory_mode'] == 'standalone') + $site_directory = DIRECTORY_MODE_STANDALONE; + + $register_policy = 0; + if($arr['register_policy'] == 'closed') + $register_policy = REGISTER_CLOSED; + if($arr['register_policy'] == 'open') + $register_policy = REGISTER_OPEN; + if($arr['register_policy'] == 'approve') + $register_policy = REGISTER_APPROVE; + + if($update) { + $r = q("update site set site_flags = %d, site_directory = '%s', site_register = %d, site_update = '%s' + where site_url = '%s' limit 1", + intval($site_directory), + dbesc(htmlentities($arr['directory_url'],ENT_COMPAT,'UTF-8',false)), + intval($register_policy), + dbesc(datetime_convert()), + dbesc(htmlentities($arr['url'],ENT_COMPAT,'UTF-8',false)) + ); + if(! $r) { + logger('import_site: update failed. ' . print_r($arr,true)); + } + } + else { + $r = q("insert into site ( site_url, site_flags, site_update, site_directory, site_register ) + values ( '%s', %d, '%s', '%s', %d )", + dbesc(htmlentities($arr['url'],ENT_COMPAT,'UTF-8',false)), + intval($site_directory), + dbesc(datetime_convert()), + dbesc(htmlentities($arr['directory_url'],ENT_COMPAT,'UTF-8',false)), + intval($register_policy) + ); + if(! $r) { + logger('import_site: record create failed. ' . print_r($arr,true)); + } + } + + return $r; + +} + + + +/** + * Send a zot packet to all hubs where this channel is duplicated, refreshing + * such things as personal settings, channel permissions, address book updates, etc. + */ + +function build_sync_packet($uid = 0, $packet = null) { + + $a = get_app(); + + logger('build_sync_packet'); + + if(! $uid) + $uid = local_user(); + + if(! $uid) + return; + + $r = q("select * from channel where channel_id = %d limit 1", + intval($uid) + ); + if(! $r) + return; + + $channel = $r[0]; + + $h = q("select * from hubloc where hubloc_hash = '%s'", + dbesc($channel['channel_hash']) + ); + + if(! $h) + return; + + $synchubs = array(); + + foreach($h as $x) { + if($x['hubloc_host'] == $a->get_hostname()) + continue; + $synchubs[] = $x; + } + + if(! $synchubs) + return; + + $r = q("select xchan_guid, xchan_guid_sig from xchan where xchan_hash = '%s' limit 1", + dbesc($channel['channel_hash']) + ); + if(! $r) + return; + + $env_recips = array(); + $env_recips[] = array('guid' => $r[0]['xchan_guid'],'guid_sig' => $r[0]['xchan_guid_sig']); + + $info = (($packet) ? $packet : array()); + $info['type'] = 'channel_sync'; + + if(array_key_exists($uid,$a->config) && array_key_exists('transient',$a->config[$uid])) { + $settings = $a->config[$uid]['transient']; + if($settings) { + $info['config'] = $settings; + } + } + + if($channel) { + $info['channel'] = array(); + foreach($channel as $k => $v) { + + // filter out any joined tables like xchan + + if(strpos($k,'channel_') !== 0) + continue; + + // don't pass these elements, they should not be synchronised + + $disallowed = array('channel_id','channel_account_id','channel_primary','channel_prvkey','channel_address'); + + if(in_array($k,$disallowed)) + continue; + + $info['channel'][$k] = $v; + } + } + + $interval = ((get_config('system','delivery_interval') !== false) + ? intval(get_config('system','delivery_interval')) : 2 ); + + + logger('build_sync_packet: packet: ' . print_r($info,true), LOGGER_DATA); + + foreach($synchubs as $hub) { + $hash = random_string(); + $n = zot_build_packet($channel,'notify',$env_recips,$hub['hubloc_sitekey'],$hash); + q("insert into outq ( outq_hash, outq_account, outq_channel, outq_posturl, outq_async, outq_created, outq_updated, outq_notify, outq_msg ) values ( '%s', %d, %d, '%s', %d, '%s', '%s', '%s', '%s' )", + dbesc($hash), + intval($channel['channel_account']), + intval($channel['channel_id']), + dbesc($hub['hubloc_callback']), + intval(1), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc($n), + dbesc(json_encode($info)) + ); + + proc_run('php','include/deliver.php',$hash); + if($interval) + @time_sleep_until(microtime(true) + (float) $interval); + } + + +} + +function process_channel_sync_delivery($sender,$arr,$deliveries) { + +// FIXME - this will sync red structures (channel, pconfig and abook). Eventually we need to make this application agnostic. +// TODO: missing group membership changes + + $result = array(); + + foreach($deliveries as $d) { + $r = q("select * from channel where channel_hash = '%s' limit 1", + dbesc($d['hash']) + ); + + if(! $r) { + $result[] = array($d['hash'],'not found'); + continue; + } + + $channel = $r[0]; + + if($channel['channel_hash'] != $sender['hash']) { + logger('process_channel_sync_delivery: possible forgery. Sender ' . $sender['hash'] . ' is not ' . $channel['channel_hash']); + $result[] = array($d['hash'],'channel mismatch',$channel['channel_name']); + continue; + } + + if(array_key_exists('config',$arr) && is_array($arr['config']) && count($arr['config'])) { + foreach($arr['config'] as $cat => $k) { + foreach($arr['config'][$cat] as $k => $v) + set_pconfig($channel['channel_id'],$cat,$k,$v); + } + } + + if(array_key_exists('channel',$arr) && is_array($arr['channel']) && count($arr['channel'])) { + $disallowed = array('channel_id','channel_account_id','channel_primary','channel_prvkey', 'channel_address'); + + $clean = array(); + foreach($arr['channel'] as $k => $v) { + if(in_array($k,$disallowed)) + continue; + $clean[$k] = $v; + } + if(count($clean)) { + foreach($clean as $k => $v) { + $r = dbq("UPDATE channel set " . dbesc($k) . " = '" . dbesc($v) + . "' where channel_id = " . intval($channel['channel_id']) . " limit 1"); + } + } + } + + + if(array_key_exists('abook',$arr) && is_array($arr['abook']) && count($arr['abook'])) { + + $disallowed = array('abook_id','abook_account','abook_channel'); + + $clean = array(); + foreach($arr['abook'] as $abook) { + foreach($abook as $k => $v) { + if(in_array($k,$disallowed)) + continue; + $clean[$k] = $v; + } + + if(! array_key_exists('abook_xchan',$clean)) + continue; + + if(count($clean)) { + foreach($clean as $k => $v) { + $r = dbq("UPDATE abook set " . dbesc($k) . " = '" . dbesc($v) + . "' where abook_xchan = '" . dbesc($clean['abook_xchan']) . "' and abook_channel = " . intval($channel['channel_id']) + . " limit 1"); + } + } + } + } + + $result[] = array($d['hash'],'channel sync updated',$channel['channel_name']); + + + } + return $result; +} |