From 19cd337b3c5a72fc381a911f65ef6ce0b3b2bf92 Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Mon, 22 Dec 2014 23:12:23 +0100 Subject: [PATCH 1/7] The hostname of the statistics page has changed. --- statistics_json/statistics_json.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/statistics_json/statistics_json.php b/statistics_json/statistics_json.php index 8ce6a35f..c30d00fc 100644 --- a/statistics_json/statistics_json.php +++ b/statistics_json/statistics_json.php @@ -137,7 +137,7 @@ function statistics_json_cron($a,$b) { logger('statistics_json_cron: local_posts: '.$local_posts, LOGGER_DEBUG); // Now trying to register - $url = "http://pods.jasonrobinson.me/register/".$a->get_hostname(); + $url = "http://the-federation.info/register/".$a->get_hostname(); logger('statistics_json_cron: registering url: '.$url, LOGGER_DEBUG); $ret = fetch_url($url); logger('statistics_json_cron: registering answer: '.$ret, LOGGER_DEBUG); -- 2.43.4 From bc8345d596bb03402c1c6ddbc2f49f96ee690ea4 Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Mon, 22 Dec 2014 23:22:33 +0100 Subject: [PATCH 2/7] Facebook: Better check for empty posts, performance improvement when syncing. --- fbpost/fbpost.php | 9 +++++++-- fbsync/fbsync.php | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/fbpost/fbpost.php b/fbpost/fbpost.php index f0b763cd..7c10d7c3 100644 --- a/fbpost/fbpost.php +++ b/fbpost/fbpost.php @@ -1095,7 +1095,7 @@ function fbpost_fetchwall($a, $uid) { } if(trim($_REQUEST["body"].$content.$pagedata["text"]) == '') { - logger('facebook: empty body 2 '.$item->id.' '.print_r($item, true)); + logger('facebook: empty body 1 '.$item->id.' '.print_r($item, true)); continue; } @@ -1115,7 +1115,7 @@ function fbpost_fetchwall($a, $uid) { } if(trim($_REQUEST["body"].$content.$pagedata["text"]) == '') { - logger('facebook: empty body '.$item->id.' '.print_r($item, true)); + logger('facebook: empty body 2 '.$item->id.' '.print_r($item, true)); continue; } @@ -1151,6 +1151,11 @@ function fbpost_fetchwall($a, $uid) { .' '.substr($item->place->location->longitude, 0, 8); } + if(trim($_REQUEST["body"]) == '') { + logger('facebook: empty body 3 '.$item->id.' '.print_r($item, true)); + continue; + } + //print_r($_REQUEST); logger('facebook: posting for user '.$uid); item_post($a); diff --git a/fbsync/fbsync.php b/fbsync/fbsync.php index 40a7ef46..39931d5b 100644 --- a/fbsync/fbsync.php +++ b/fbsync/fbsync.php @@ -1007,7 +1007,7 @@ function fbsync_fetchfeed($a, $uid) { require_once('include/items.php'); - //if ($last_updated == "") + if ($last_updated == "") $last_updated = 0; logger("fbsync_fetchfeed: fetching content for user ".$self_id); -- 2.43.4 From 909674458d490c8d159aebc0504b7dea7b883693 Mon Sep 17 00:00:00 2001 From: tobiasd Date: Wed, 24 Dec 2014 12:24:27 +0100 Subject: [PATCH 3/7] use new URL in description as well --- statistics_json/statistics_json.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/statistics_json/statistics_json.php b/statistics_json/statistics_json.php index c30d00fc..fd1e5526 100644 --- a/statistics_json/statistics_json.php +++ b/statistics_json/statistics_json.php @@ -2,7 +2,7 @@ /** * Name: Statistics - * Description: Generates some statistics for http://pods.jasonrobinson.me/ + * Description: Generates some statistics for http://http://the-federation.info/ * Version: 0.1 * Author: Michael Vogel */ -- 2.43.4 From 9aefbf3def9a48e6c290f57633222b8ecf5ce504 Mon Sep 17 00:00:00 2001 From: tobiasd Date: Wed, 24 Dec 2014 12:25:03 +0100 Subject: [PATCH 4/7] copy and paste mishap m) --- statistics_json/statistics_json.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/statistics_json/statistics_json.php b/statistics_json/statistics_json.php index fd1e5526..8172c070 100644 --- a/statistics_json/statistics_json.php +++ b/statistics_json/statistics_json.php @@ -2,7 +2,7 @@ /** * Name: Statistics - * Description: Generates some statistics for http://http://the-federation.info/ + * Description: Generates some statistics for http://the-federation.info/ * Version: 0.1 * Author: Michael Vogel */ -- 2.43.4 From e17cb70379ee3eb8cae483cc8fd4909bd36a304e Mon Sep 17 00:00:00 2001 From: fabrixxm Date: Mon, 29 Dec 2014 18:10:32 +0100 Subject: [PATCH 5/7] New experimental plugin 'secure mail' --- securemail/README.md | 11 + securemail/php-gpg/.gitignore | 3 + securemail/php-gpg/README.md | 39 + securemail/php-gpg/libs/GPG.php | 186 +++ securemail/php-gpg/libs/GPG/AES.php | 63 + securemail/php-gpg/libs/GPG/Cipher.php | 335 ++++ securemail/php-gpg/libs/GPG/Expanded_Key.php | 105 ++ securemail/php-gpg/libs/GPG/Public_Key.php | 217 +++ securemail/php-gpg/libs/GPG/Utility.php | 131 ++ securemail/php-gpg/libs/GPG/globals.php | 403 +++++ securemail/php-gpg/tests/gpg/EncryptTest.php | 135 ++ securemail/php-gpg/tests/gpg/KeyTest.php | 1474 ++++++++++++++++++ securemail/php-gpg/tests/runtests.sh | 4 + securemail/securemail.php | 93 ++ 14 files changed, 3199 insertions(+) create mode 100644 securemail/README.md create mode 100644 securemail/php-gpg/.gitignore create mode 100644 securemail/php-gpg/README.md create mode 100644 securemail/php-gpg/libs/GPG.php create mode 100644 securemail/php-gpg/libs/GPG/AES.php create mode 100644 securemail/php-gpg/libs/GPG/Cipher.php create mode 100644 securemail/php-gpg/libs/GPG/Expanded_Key.php create mode 100644 securemail/php-gpg/libs/GPG/Public_Key.php create mode 100644 securemail/php-gpg/libs/GPG/Utility.php create mode 100644 securemail/php-gpg/libs/GPG/globals.php create mode 100644 securemail/php-gpg/tests/gpg/EncryptTest.php create mode 100644 securemail/php-gpg/tests/gpg/KeyTest.php create mode 100644 securemail/php-gpg/tests/runtests.sh create mode 100644 securemail/securemail.php diff --git a/securemail/README.md b/securemail/README.md new file mode 100644 index 00000000..2f24c53c --- /dev/null +++ b/securemail/README.md @@ -0,0 +1,11 @@ +Secure Mail +----------- + +Send notification mails to user encrypted with GPG. +Each user can enable it and submit his public key under Settings-> Addon +-> "Secure Mail" Settings. + +Use 'php-gpg' library, a pure PHP implementation of GPG/PGP, released +under GPL. See [project repo](https://github.com/jasonhinkle/php-gpg). + +This plugin need Friendica version > 3.3.2 to work. diff --git a/securemail/php-gpg/.gitignore b/securemail/php-gpg/.gitignore new file mode 100644 index 00000000..0bd34b35 --- /dev/null +++ b/securemail/php-gpg/.gitignore @@ -0,0 +1,3 @@ +/.buildpath +/.settings +.DS_Store \ No newline at end of file diff --git a/securemail/php-gpg/README.md b/securemail/php-gpg/README.md new file mode 100644 index 00000000..49291151 --- /dev/null +++ b/securemail/php-gpg/README.md @@ -0,0 +1,39 @@ +php-gpg +======= + +php-gpg is a pure PHP implementation of GPG/PGP (currently supports encryption only). The library does not require PGP/GPG binaries and should run on any platform that supports PHP. + +This library is useful for encrypting data before it is sent over an insecure protocol (for example email). Messages encrypted with this library are compatible and can be decrypted by standard GPG/PGP clients. + +Features/Limitations +-------------------- + + * Supports RSA, DSA public key length of 2,4,8,16,512,1024,2048 or 4096 + * Currently supports only encrypt + +Hey You! If you have a good understanding of public key encryption and want to implement signing or decryption your pull request would be welcome. + +Usage +----- + +```php +require_once 'libs/GPG.php'; + +$gpg = new GPG(); + +// create an instance of a GPG public key object based on ASCII key +$pub_key = new GPG_Public_Key($public_key_ascii); + +// using the key, encrypt your plain text using the public key +$encrypted = $gpg->encrypt($pub_key,$plain_text_string); + +echo $encrypted; + +``` + +License +------- + +GPL http://www.gnu.org/copyleft/gpl.html + +I'd like to release this under a more permissive license, but since PGP & GPG itself are GPL, I think this library is likely bound to the terms of GPL as well. \ No newline at end of file diff --git a/securemail/php-gpg/libs/GPG.php b/securemail/php-gpg/libs/GPG.php new file mode 100644 index 00000000..053a54be --- /dev/null +++ b/securemail/php-gpg/libs/GPG.php @@ -0,0 +1,186 @@ +encrypt($pub_key,$plain_text_string); + */ +class GPG +{ + + private $width = 16; + private $el = array(3, 5, 9, 17, 513, 1025, 2049, 4097); + private $version = "1.4.7"; + + private function gpg_encrypt($key, $text) { + + $i = 0; + $i = 0; + $len = strlen($text); + $len = strlen($text); + $iblock = array_fill(0, $this->width, 0); + $rblock = array_fill(0, $this->width, 0); + $ct = array_fill(0, $this->width + 2, 0); + + $cipher = ""; + + if($len % $this->width) { + for($i = ($len % $this->width); $i < $this->width; $i++) $text .= "\0"; + } + + $ekey = new Expanded_Key($key); + + for($i = 0; $i < $this->width; $i++) { + $iblock[$i] = 0; + $rblock[$i] = GPG_Utility::c_random(); + } + + + $iblock = GPG_AES::encrypt($iblock, $ekey); + for($i = 0; $i < $this->width; $i++) { + $ct[$i] = ($iblock[$i] ^= $rblock[$i]); + } + + $iblock = GPG_AES::encrypt($iblock, $ekey); + $ct[$this->width] = ($iblock[0] ^ $rblock[$this->width - 2]); + $ct[$this->width + 1] = ($iblock[1] ^ $rblock[$this->width - 1]); + + for($i = 0; $i < $this->width + 2; $i++) $cipher .= chr($ct[$i]); + + $iblock = array_slice($ct, 2, $this->width + 2); + + for($n = 0; $n < strlen($text); $n += $this->width) { + $iblock = GPG_AES::encrypt($iblock, $ekey); + for($i = 0; $i < $this->width; $i++) { + $iblock[$i] ^= ord($text[$n + $i]); + $cipher .= chr($iblock[$i]); + } + } + + return substr($cipher, 0, $len + $this->width + 2); + } + + private function gpg_header($tag, $len) + { + if ($len > 0xff) $tag += 1; + $h = chr($tag); + if ($len > 0xff) $h .= chr($len / 0x100); + $h .= chr($len % 0x100); + + return $h; + } + + private function gpg_session($key_id, $key_type, $session_key, $public_key) + { + + $mod = array(); + $exp = array(); + $enc = ""; + + $s = base64_decode($public_key); + $l = floor((ord($s[0]) * 256 + ord($s[1]) + 7) / 8); + $mod = mpi2b(substr($s, 0, $l + 2)); + if($key_type) { + $grp = array(); + $y = array(); + $B = array(); + $C = array(); + + $l2 = floor((ord($s[$l + 2]) * 256 + ord($s[$l + 3]) + 7) / 8) + 2; + $grp = mpi2b(substr($s, $l + 2, $l2)); + $y = mpi2b(substr($s, $l + 2 + $l2)); + $exp[0] = $this->el[GPG_Utility::c_random() & 7]; + $B = bmodexp($grp, $exp, $mod); + $C = bmodexp($y, $exp, $mod); + } else { + $exp = mpi2b(substr($s, $l + 2)); + } + + $c = 0; + $lsk = strlen($session_key); + for($i = 0; $i < $lsk; $i++) $c += ord($session_key[$i]); + $c &= 0xffff; + + $lm = ($l - 2) * 8 + 2; + $m = chr($lm / 256) . chr($lm % 256) . + chr(2) . GPG_Utility::s_random($l - $lsk - 6, 1) . "\0" . + chr(7) . $session_key . + chr($c / 256) . chr($c & 0xff); + + if($key_type) { + $enc = b2mpi($B) . b2mpi(bmod(bmul(mpi2b($m), $C), $mod)); + return $this->gpg_header(0x84,strlen($enc) + 10) . + chr(3) . $key_id . chr(16) . $enc; + } else { + $enc = b2mpi(bmodexp(mpi2b($m), $exp, $mod)); + return $this->gpg_header(0x84, strlen($enc) + 10) . + chr(3) . $key_id . chr(1) . $enc; + } + } + + private function gpg_literal($text) + { + if (strpos($text, "\r\n") === false) + $text = str_replace("\n", "\r\n", $text); + + return + $this->gpg_header(0xac, strlen($text) + 10) . "t" . + chr(4) . "file\0\0\0\0" . $text; + } + + private function gpg_data($key, $text) + { + $enc = $this->gpg_encrypt($key, $this->gpg_literal($text)); + return $this->gpg_header(0xa4, strlen($enc)) . $enc; + } + + /** + * GPG Encypts a message to the provided public key + * + * @param GPG_Public_Key $pk + * @param string $plaintext + * @return string encrypted text + */ + function encrypt($pk, $plaintext) + { + // normalize the public key + $key_id = $pk->GetKeyId(); + $key_type = $pk->GetKeyType(); + $public_key = $pk->GetPublicKey(); + + $session_key = GPG_Utility::s_random($this->width, 0); + $key_id = GPG_Utility::hex2bin($key_id); + $cp = $this->gpg_session($key_id, $key_type, $session_key, $public_key) . + $this->gpg_data($session_key, $plaintext); + + $code = base64_encode($cp); + $code = wordwrap($code, 60, "\n", 1); + + return + "-----BEGIN PGP MESSAGE-----\nVersion: VerySimple PHP-GPG v".$this->version."\n\n" . + $code . "\n=" . base64_encode(GPG_Utility::crc24($cp)) . + "\n-----END PGP MESSAGE-----\n"; + } +} + +?> \ No newline at end of file diff --git a/securemail/php-gpg/libs/GPG/AES.php b/securemail/php-gpg/libs/GPG/AES.php new file mode 100644 index 00000000..25e4e4a3 --- /dev/null +++ b/securemail/php-gpg/libs/GPG/AES.php @@ -0,0 +1,63 @@ +rounds; + $b0 = $b[0]; + $b1 = $b[1]; + $b2 = $b[2]; + $b3 = $b[3]; + + for($r = 0; $r < $rounds - 1; $r++) { + $t0 = $b0 ^ $ctx->rk[$r][0]; + $t1 = $b1 ^ $ctx->rk[$r][1]; + $t2 = $b2 ^ $ctx->rk[$r][2]; + $t3 = $b3 ^ $ctx->rk[$r][3]; + + $b0 = $T1[$t0 & 255] ^ $T2[($t1 >> 8) & 255] ^ $T3[($t2 >> 16) & 255] ^ $T4[GPG_Utility::zshift($t3, 24)]; + $b1 = $T1[$t1 & 255] ^ $T2[($t2 >> 8) & 255] ^ $T3[($t3 >> 16) & 255] ^ $T4[GPG_Utility::zshift($t0, 24)]; + $b2 = $T1[$t2 & 255] ^ $T2[($t3 >> 8) & 255] ^ $T3[($t0 >> 16) & 255] ^ $T4[GPG_Utility::zshift($t1, 24)]; + $b3 = $T1[$t3 & 255] ^ $T2[($t0 >> 8) & 255] ^ $T3[($t1 >> 16) & 255] ^ $T4[GPG_Utility::zshift($t2, 24)]; + } + + $r = $rounds - 1; + + $t0 = $b0 ^ $ctx->rk[$r][0]; + $t1 = $b1 ^ $ctx->rk[$r][1]; + $t2 = $b2 ^ $ctx->rk[$r][2]; + $t3 = $b3 ^ $ctx->rk[$r][3]; + + $b[0] = GPG_Cipher::F1($t0, $t1, $t2, $t3) ^ $ctx->rk[$rounds][0]; + $b[1] = GPG_Cipher::F1($t1, $t2, $t3, $t0) ^ $ctx->rk[$rounds][1]; + $b[2] = GPG_Cipher::F1($t2, $t3, $t0, $t1) ^ $ctx->rk[$rounds][2]; + $b[3] = GPG_Cipher::F1($t3, $t0, $t1, $t2) ^ $ctx->rk[$rounds][3]; + + return GPG_Utility::unpack_octets($b); + } +} + +?> \ No newline at end of file diff --git a/securemail/php-gpg/libs/GPG/Cipher.php b/securemail/php-gpg/libs/GPG/Cipher.php new file mode 100644 index 00000000..5940e14b --- /dev/null +++ b/securemail/php-gpg/libs/GPG/Cipher.php @@ -0,0 +1,335 @@ +> 0x8) & 0xff]) << 0x8) | + (GPG_Utility::B1($T1[($x2 >> 0x10) & 0xff]) << 0x10) | (GPG_Utility::B1($T1[GPG_Utility::zshift($x3, 0x18)]) << 0x18); + } + + +} +?> diff --git a/securemail/php-gpg/libs/GPG/Expanded_Key.php b/securemail/php-gpg/libs/GPG/Expanded_Key.php new file mode 100644 index 00000000..fdc69b27 --- /dev/null +++ b/securemail/php-gpg/libs/GPG/Expanded_Key.php @@ -0,0 +1,105 @@ += 0; $j--) $tk[$j] = $k[$j]; + + $r = 0; + $t = 0; + for($j = 0; ($j < $kc) && ($r < $rounds + 1); ) { + for(; ($j < $kc) && ($t < 4); $j++, $t++) { + $keySched[$r][$t] = $tk[$j]; + } + if($t == 4) { + $r++; + $t = 0; + } + } + + while($r < $rounds + 1) { + $temp = $tk[$kc - 1]; + + $tk[0] ^= $S[GPG_Utility::B1($temp)] | ($S[GPG_Utility::B2($temp)] << 0x8) | + ($S[GPG_Utility::B3($temp)] << 0x10) | ($S[GPG_Utility::B0($temp)] << 0x18); + $tk[0] ^= $RCON[$rconpointer++]; + + if ($kc != 8) { + for($j = 1; $j < $kc; $j++) $tk[$j] ^= $tk[$j - 1]; + } else { + for($j = 1; $j < $kc / 2; $j++) $tk[$j] ^= $tk[$j - 1]; + + $temp = $tk[$kc / 2 - 1]; + $tk[$kc / 2] ^= $S[GPG_Utility::B0($temp)] | ($S[GPG_Utility::B1($temp)] << 0x8) | + ($S[GPG_Utility::B2($temp)] << 0x10) | ($S[GPG_Utility::B3($temp)] << 0x18); + + for($j = $kc / 2 + 1; $j < $kc; $j++) $tk[$j] ^= $tk[$j - 1]; + } + + for($j = 0; ($j < $kc) && ($r < $rounds + 1); ) { + for(; ($j < $kc) && ($t < 4); $j++, $t++) { + $keySched[$r][$t] = $tk[$j]; + } + if($t == 4) { + $r++; + $t = 0; + } + } + } + + $this->rounds = $rounds; + $this->rk = $keySched; + return $this; + } +} + +?> \ No newline at end of file diff --git a/securemail/php-gpg/libs/GPG/Public_Key.php b/securemail/php-gpg/libs/GPG/Public_Key.php new file mode 100644 index 00000000..7b813079 --- /dev/null +++ b/securemail/php-gpg/libs/GPG/Public_Key.php @@ -0,0 +1,217 @@ +version != -1 && $this->GetKeyType() != PK_TYPE_UNKNOWN; + } + + function GetKeyType() + { + if (!strcmp($this->type, "ELGAMAL")) return PK_TYPE_ELGAMAL; + if (!strcmp($this->type, "RSA")) return PK_TYPE_RSA; + return PK_TYPE_UNKNOWN; + } + + function GetFingerprint() + { + return strtoupper( trim(chunk_split($this->fp, 4, ' ')) ); + } + + function GetKeyId() + { + return (strlen($this->key_id) == 16) ? strtoupper($this->key_id) : '0000000000000000'; + } + + function GetPublicKey() + { + return str_replace("\n", "", $this->public_key); + } + + function GPG_Public_Key($asc) { + $found = 0; + + // normalize line breaks + $asc = str_replace("\r\n", "\n", $asc); + + if (strpos($asc, "-----BEGIN PGP PUBLIC KEY BLOCK-----\n") === false) + throw new Exception("Missing header block in Public Key"); + + if (strpos($asc, "\n\n") === false) + throw new Exception("Missing body delimiter in Public Key"); + + if (strpos($asc, "\n-----END PGP PUBLIC KEY BLOCK-----") === false) + throw new Exception("Missing footer block in Public Key"); + + // get rid of everything except the base64 encoded key + $headerbody = explode("\n\n", str_replace("\n-----END PGP PUBLIC KEY BLOCK-----", "", $asc), 2); + $asc = trim($headerbody[1]); + + + $len = 0; + $s = base64_decode($asc); + $sa = str_split($s); + + for($i = 0; $i < strlen($s);) { + $tag = ord($sa[$i++]); + + // echo 'TAG=' . $tag . '/'; + + if(($tag & 128) == 0) break; + + if($tag & 64) { + $tag &= 63; + $len = ord($sa[$i++]); + if ($len > 191 && $len < 224) $len = (($len - 192) << 8) + ord($sa[$i++]); + else if ($len == 255) $len = (ord($sa[$i++]) << 24) + (ord($sa[$i++]) << 16) + (ord($sa[$i++]) << 8) + ord($sa[$i++]); + else if ($len > 223 && len < 255) $len = (1 << ($len & 0x1f)); + } else { + $len = $tag & 3; + $tag = ($tag >> 2) & 15; + if ($len == 0) $len = ord($sa[$i++]); + else if($len == 1) $len = (ord($sa[$i++]) << 8) + ord($sa[$i++]); + else if($len == 2) $len = (ord($sa[$i++]) << 24) + (ord($sa[$i++]) << 16) + (ord($sa[$i++]) << 8) + ord($sa[$i++]); + else $len = strlen($s) - 1; + } + + // echo $tag . ' '; + + if ($tag == 6 || $tag == 14) { + $k = $i; + $version = ord($sa[$i++]); + $found = 1; + $this->version = $version; + + $time = (ord($sa[$i++]) << 24) + (ord($sa[$i++]) << 16) + (ord($sa[$i++]) << 8) + ord($sa[$i++]); + + if($version == 2 || $version == 3) $valid = ord($sa[$i++]) << 8 + ord($sa[$i++]); + + $algo = ord($sa[$i++]); + + if($algo == 1 || $algo == 2) { + $m = $i; + $lm = floor((ord($sa[$i]) * 256 + ord($sa[$i + 1]) + 7) / 8); + $i += $lm + 2; + + $mod = substr($s, $m, $lm + 2); + $le = floor((ord($sa[$i]) * 256 + ord($sa[$i+1]) + 7) / 8); + $i += $le + 2; + + $this->public_key = base64_encode(substr($s, $m, $lm + $le + 4)); + $this->type = "RSA"; + + if ($version == 3) { + $this->fp = ''; + $this->key_id = bin2hex(substr($mod, strlen($mod) - 8, 8)); + } else if($version == 4) { + + // https://tools.ietf.org/html/rfc4880#section-12 + $headerPos = strpos($s, chr(0x04)); // TODO: is this always the correct starting point for the pulic key packet 'version' field? + $delim = chr(0x01) . chr(0x00); // TODO: is this the correct delimiter for the end of the public key packet? + $delimPos = strpos($s, $delim) + (3-$headerPos); + + // echo "POSITION: $delimPos\n"; + + $pkt = chr(0x99) . chr($delimPos >> 8) . chr($delimPos & 255) . substr($s, $headerPos, $delimPos); + + // this is the original signing string which seems to have only worked for key lengths of 1024 or less + //$pkt = chr(0x99) . chr($len >> 8) . chr($len & 255) . substr($s, $k, $len); + + $fp = sha1($pkt); + $this->fp = $fp; + $this->key_id = substr($fp, strlen($fp) - 16, 16); + + // uncomment to debug the start point for the signing string +// for ($ii = 5; $ii > -1; $ii--) { +// $pkt = chr(0x99) . chr($ii >> 8) . chr($ii & 255) . substr($s, $headerPos, $ii); +// $fp = sha1($pkt); +// echo "LENGTH=" . $headerPos . '->' . $ii . " CHR(" . ord(substr($s,$ii, 1)) . ") = " . substr($fp, strlen($fp) - 16, 16) . "\n"; +// } +// echo "\n"; + + // uncomment to debug the end point for the signing string +// for ($ii = strlen($s); $ii > 1; $ii--) { +// $pkt = chr(0x99) . chr($ii >> 8) . chr($ii & 255) . substr($s, $headerPos, $ii); +// $fp = sha1($pkt); +// echo "LENGTH=" . $headerPos . '->' . $ii . " CHR(" . ord(substr($s,$ii, 1)) . ") = " . substr($fp, strlen($fp) - 16, 16) . "\n"; +// } + } else { + throw new Exception('GPG Key Version ' . $version . ' is not supported'); + } + $found = 2; + } else if(($algo == 16 || $algo == 20) && $version == 4) { + $m = $i; + + $lp = floor((ord($sa[$i]) * 256 + ord($sa[$i +1]) + 7) / 8); + $i += $lp + 2; + + $lg = floor((ord($sa[$i]) * 256 + ord($sa[$i + 1]) + 7) / 8); + $i += $lg + 2; + + $ly = floor((ord($sa[$i]) * 256 + ord($sa[$i + 1]) + 7)/8); + $i += $ly + 2; + + $this->public_key = base64_encode(substr($s, $m, $lp + $lg + $ly + 6)); + + // TODO: should this be adjusted as it was for RSA (above)..? + + $pkt = chr(0x99) . chr($len >> 8) . chr($len & 255) . substr($s, $k, $len); + $fp = sha1($pkt); + $this->fp = $fp; + $this->key_id = substr($fp, strlen($fp) - 16, 16); + $this->type = "ELGAMAL"; + $found = 3; + } else { + $i = $k + $len; + } + } else if ($tag == 13) { + $this->user = substr($s, $i, $len); + $i += $len; + } else { + $i += $len; + } + } + + if($found < 2) { + + throw new Exception("Unable to parse Public Key"); +// $this->version = ""; +// $this->fp = ""; +// $this->key_id = ""; +// $this->user = ""; +// $this->public_key = ""; + } + } + + function GetExpandedKey() + { + $ek = new Expanded_Key($this->public_key); + } +} + +?> \ No newline at end of file diff --git a/securemail/php-gpg/libs/GPG/Utility.php b/securemail/php-gpg/libs/GPG/Utility.php new file mode 100644 index 00000000..ac3ac6b2 --- /dev/null +++ b/securemail/php-gpg/libs/GPG/Utility.php @@ -0,0 +1,131 @@ +> 0x8) & 0xff); + } + + static function B2($x) { + return (($x >> 0x10) & 0xff); + } + + static function B3($x) { + return (($x >> 0x18) & 0xff); + } + + static function zshift($x, $s) { + $res = $x >> $s; + + $pad = 0; + for ($i = 0; $i < 32 - $s; $i++) $pad += (1 << $i); + + return $res & $pad; + } + + static function pack_octets($octets) + { + $i = 0; + $j = 0; + $len = count($octets); + $b = array_fill(0, $len / 4, 0); + + if (!$octets || $len % 4) return; + + for ($i = 0, $j = 0; $j < $len; $j += 4) { + $b[$i++] = $octets[$j] | ($octets[$j + 1] << 0x8) | ($octets[$j + 2] << 0x10) | ($octets[$j + 3] << 0x18); + + } + + return $b; + } + + static function unpack_octets($packed) + { + $j = 0; + $i = 0; + $l = count($packed); + $r = array_fill(0, $l * 4, 0); + + for ($j = 0; $j < $l; $j++) { + $r[$i++] = GPG_Utility::B0($packed[$j]); + $r[$i++] = GPG_Utility::B1($packed[$j]); + $r[$i++] = GPG_Utility::B2($packed[$j]); + $r[$i++] = GPG_Utility::B3($packed[$j]); + } + + return $r; + } + + + + + static function hex2bin($h) + { + if(strlen($h) % 2) $h += "0"; + + $r = ""; + for($i = 0; $i < strlen($h); $i += 2) { + $r .= chr(intval($h[$i], 16) * 16 + intval($h[$i + 1], 16)); + } + + return $r; + } + + static function crc24($data) + { + $crc = 0xb704ce; + + for($n = 0; $n < strlen($data); $n++) { + $crc ^= (ord($data[$n]) & 0xff) << 0x10; + for($i = 0; $i < 8; $i++) { + $crc <<= 1; + if($crc & 0x1000000) $crc ^= 0x1864cfb; + } + } + + return + chr(($crc >> 0x10) & 0xff) . + chr(($crc >> 0x8) & 0xff) . + chr($crc & 0xff); + } + + static function s_random($len, $textmode) + { + $r = ""; + for($i = 0; $i < $len;) + { + $t = rand(0, 0xff); + if($t == 0 && $textmode) continue; + $i++; + + $r .= chr($t); + } + + return $r; + } + + static function c_random() { + return round(rand(0, 0xff)); + } + +} +?> \ No newline at end of file diff --git a/securemail/php-gpg/libs/GPG/globals.php b/securemail/php-gpg/libs/GPG/globals.php new file mode 100644 index 00000000..5b2dbce1 --- /dev/null +++ b/securemail/php-gpg/libs/GPG/globals.php @@ -0,0 +1,403 @@ +> 1; +$bd = $bs >> 1; +$bdm = (1 << $bd) - 1; + +/** + */ +function mpi2b($s) +{ + global $bs; + global $bx2; + global $bm; + global $bx; + global $bd; + global $bdm; + + $bn = 1; + $r = array(0); + $rn = 0; + $sb = 256; + $c = 0; + $sn = strlen($s); + if($sn < 2) { + echo("string too short, not a MPI"); + return 0; + } + + $len = ($sn - 2) * 8; + $bits = ord($s[0]) * 256 + ord($s[1]); + if ($bits > $len || $bits < $len - 8) { + echo("not a MPI, bits = $bits, len = $len"); + return 0; + } + + for ($n = 0; $n < $len; $n++) { + if (($sb <<= 1) > 255) { + $sb = 1; $c = ord($s[--$sn]); + } + if ($bn > $bm) { + $bn = 1; + $r[++$rn]=0; + } + if ($c & $sb) $r[$rn] |= $bn; + $bn <<= 1; + } + + return $r; +} + +/** + */ +function b2mpi($b) +{ + global $bs; + global $bx2; + global $bm; + global $bx; + global $bd; + global $bdm; + + $bn = 1; + $bc = 0; + $r = array(0); + $rb = 1; + $rn = 0; + $bits = count($b) * $bs; + $n = 0; + $rr = ""; + + for ($n = 0; $n < $bits; $n++) { + if ($b[$bc] & $bn) $r[$rn] |= $rb; + if(($rb <<= 1) > 255) { + $rb = 1; $r[++$rn]=0; + } + if (($bn <<= 1) > $bm) { + $bn=1; $bc++; + } + } + + while ($rn && $r[$rn]==0) $rn--; + + $bn=256; + for($bits = 8; $bits > 0; $bits--) if ($r[$rn] & ($bn >>= 1)) break; + $bits += $rn * 8; + + $rr .= chr($bits / 256 ) . chr($bits % 256); + if ($bits) for($n = $rn; $n >= 0; $n--) $rr .= chr($r[$n]); + + return $rr; +} + +/** + */ +function bmodexp($xx, $y, $m) { + global $bs; + global $bx2; + global $bm; + global $bx; + global $bd; + global $bdm; + + $r = array(1); + $an = 0; + $a = 0; + $x = array_merge((array)$xx); + $n = count($m) * 2; + $mu = array_fill(0, $n + 1, 0); + + $mu[$n--] = 1; + for(; $n >= 0; $n--) $mu[$n] = 0; + $dd = new bdiv($mu, $m); + $mu = $dd->q; + + for($n = 0; $n < count($y); $n++) { + for ($a = 1, $an = 0; $an < $bs; $an++, $a <<= 1) { + if ($y[$n] & $a) $r = bmod2(bmul($r, $x), $m, $mu); + $x = bmod2(bmul($x, $x), $m, $mu); + } + } + + return $r; +} + +/** + */ +function simplemod($i, $m) // returns the mod where m < 2^bd +{ + $c = 0; + $v = 0; + for ($n = count($i) - 1; $n >= 0; $n--) + { + $v = $i[$n]; + $c = (($v >> $bd) + ($c << $bd)) % $m; + $c = (($v & $bdm) + ($c << $bd)) % $m; + } + + return $c; +} + +/** + */ +function bmod($p, $m) // binary modulo +{ + global $bdm; + + if (count($m) == 1) { + if(count($p) == 1) return array($p[0] % $m[0]); + if($m[0] < $bdm) return array(simplemod($p, $m[0])); + } + + $r = new bdiv($p, $m); + return $r->mod; +} + +/** + */ +function bmod2($x, $m, $mu) { + $xl = count($x) - (count($m) << 1); + if ($xl > 0) return bmod2(array_concat(array_slice($x, 0, $xl), bmod2(array_slice($x, $xl), $m, $mu)), $m, $mu); + + $ml1 = count($m) + 1; + $ml2 = count($m) - 1; + $rr = 0; + + $q3 = array_slice(bmul(array_slice($x, $ml2), $mu), $ml1); + $r1 = array_slice($x, 0, $ml1); + $r2 = array_slice(bmul($q3, $m), 0, $ml1); + + $r = bsub($r1, $r2); + if (count($r) == 0) { + $r1[$ml1] = 1; + $r = bsub($r1, $r2); + } + for ($n = 0;; $n++) { + $rr = bsub($r, $m); + if(count($rr) == 0) break; + $r = $rr; + if($n >= 3) return bmod2($r, $m, $mu); + } + + return $r; +} + +/** + */ +function toppart($x, $start, $len) { + global $bx2; + + $n = 0; + while ($start >= 0 && $len-- > 0) $n = $n * $bx2 + $x[$start--]; + + return $n; +} + +/** + */ +function zeros($n) { + $r = array_fill(0, $n, 0); + while ($n-- > 0) $r[$n] = 0; + return $r; +} + +/** + * @package verysimple::Encryption + */ +class bdiv { + var $q; + var $mod; + function bdiv($x, $y) + { + global $bs; + global $bx2; + global $bm; + global $bx; + global $bd; + global $bdm; + + $n = count($x) - 1; + $t = count($y) - 1; + $nmt = $n - $t; + + if ($n < $t || $n == $t && ($x[$n] < $y[$n] || $n > 0 && $x[$n] == $y[$n] && $x[$n - 1] < $y[$n - 1])) { + $this->q = array(0); + $this->mod = array($x); + return; + } + + if ($n == $t && toppart($x, $t, 2) / toppart($y, $t, 2) < 4) { + $qq = 0; + $xx = 0; + for(;;) { + $xx = bsub($x, $y); + if(count($xx) == 0) break; + $x = $xx; $qq++; + } + $this->q = array($qq); + $this->mod = $x; + return; + } + + $shift2 = floor(log($y[$t]) / M_LN2) + 1; + $shift = $bs - $shift2; + if ($shift) { + $x = array_merge((array)$x); $y = array_merge((array)$y); + for($i = $t; $i > 0; $i--) $y[$i] = (($y[$i] << $shift) & $bm) | ($y[$i - 1] >> $shift2); + $y[0] = ($y[0] << $shift) & $bm; + if($x[$n] & (($bm << $shift2) & $bm)) { + $x[++$n] = 0; $nmt++; + } + for($i = $n; $i > 0; $i--) $x[$i] = (($x[$i] << $shift) & $bm) | ($x[$i - 1] >> $shift2); + $x[0] = ($x[0] << $shift) & $bm; + } + + $i = 0; + $j = 0; + $x2 = 0; + $q = zeros($nmt + 1); + $y2 = array_merge(zeros($nmt), (array)$y); + for (;;) { + $x2 = bsub($x, $y2); + if(count($x2) == 0) break; + $q[$nmt]++; + $x = $x2; + } + + $yt = $y[$t]; + $top =toppart($y, $t, 2); + for ($i = $n; $i > $t; $i--) { + $m = $i - $t - 1; + if ($i >= count($x)) $q[$m] = 1; + else if($x[$i] == $yt) $q[$m] = $bm; + else $q[$m] = floor(toppart($x, $i, 2) / $yt); + + $topx = toppart($x, $i, 3); + while ($q[$m] * $top > $topx) $q[$m]--; + + $y2 = array_slice($y2, 1); + $x2 = bsub($x, bmul(array($q[$m]), $y2)); + if (count($x2) == 0) { + $q[$m]--; + $x2 =bsub($x, bmul(array($q[m]), $y2)); + } + $x = $x2; + } + + if ($shift) { + for($i = 0; $i < count($x) - 1; $i++) $x[$i] = ($x[$i] >> $shift) | (($x[$i + 1] << $shift2) & $bm); + $x[count($x) - 1] >>= $shift; + } + $n = count($q); + while ($n > 1 && $q[$n - 1] == 0) $n--; + $this->q = array_slice($q, 0, $n); + $n = count($x); + while ($n > 1 && $x[$n - 1] == 0) $n--; + $this->mod = array_slice($x, 0, $n); + } +} + +/** + */ +function bsub($a, $b) { + global $bs; + global $bx2; + global $bm; + global $bx; + global $bd; + global $bdm; + + $al = count($a); + $bl = count($b); + + if ($bl > $al) return array(); + if ($bl == $al) { + if($b[$bl - 1] > $a[$bl - 1]) return array(); + if($bl == 1) return array($a[0] - $b[0]); + } + + $r = array_fill(0, $al, 0); + $c = 0; + + for ($n = 0; $n < $bl; $n++) { + $c += $a[$n] - $b[$n]; + $r[$n] = $c & $bm; + $c >>= $bs; + } + for (; $n < $al; $n++) { + $c += $a[$n]; + $r[$n] = $c & $bm; + $c >>= $bs; + } + if ($c) return array(); + + if ($r[$n - 1]) return $r; + while ($n > 1 && $r[$n - 1] == 0) $n--; + + return array_slice($r, 0, $n); +} + +/** + */ +function bmul($a, $b) { + global $bs; + global $bx2; + global $bm; + global $bx; + global $bd; + global $bdm; + + $b = array_merge((array)$b, array(0)); + $al = count($a); + $bl = count($b); + $n = 0; + $nn = 0; + $aa = 0; + $c = 0; + $m = 0; + $g = 0; + $gg = 0; + $h = 0; + $hh = 0; + $ghh = 0; + $ghhb = 0; + + $r = zeros($al + $bl + 1); + + for ($n = 0; $n < $al; $n++) { + $aa = $a[$n]; + if ($aa) { + $c = 0; + $hh = $aa >> $bd; $h = $aa & $bdm; + $m = $n; + for ($nn = 0; $nn < $bl; $nn++, $m++) { + $g = $b[$nn]; $gg = $g >> $bd; $g = $g & $bdm; + $ghh = $g * $hh + $h * $gg; + $ghhb = $ghh >> $bd; $ghh &= $bdm; + $c += $r[$m] + $h * $g + ($ghh << $bd); + $r[$m] = $c & $bm; + $c = ($c >> $bs) + $gg * $hh + $ghhb; + } + } + } + $n = count($r); + + if ($r[$n - 1]) return $r; + while ($n > 1 && $r[$n - 1] == 0) $n--; + + return array_slice($r, 0, $n); +} + +?> diff --git a/securemail/php-gpg/tests/gpg/EncryptTest.php b/securemail/php-gpg/tests/gpg/EncryptTest.php new file mode 100644 index 00000000..fa08c3fc --- /dev/null +++ b/securemail/php-gpg/tests/gpg/EncryptTest.php @@ -0,0 +1,135 @@ +getTestKey(); + + // plain text message + $plain_text_string = "Whatever 90's tote bag, meggings put a bird on it cray bicycle rights vinyl semiotics Wes Anderson. Selvage Austin umami, letterpress Tumblr deep v kitsch polaroid. Trust fund messenger bag sartorial gluten-free, cred cray church-key pop-up Intelligentsia. Food truck Tumblr paleo mixtape XOXO banjo PBR&B Pinterest tofu banh mi. Portland messenger bag cornhole PBR Tonx High Life, DIY pork belly bespoke hoodie Terry Richardson dreamcatcher ethical forage. Put a bird on it slow-carb mixtape cardigan craft beer messenger bag. Aesthetic twee art party, Odd Future trust fund banjo ugh small batch semiotics. + +Whatever asymmetrical keffiyeh literally narwhal. Keytar Odd Future blog, wayfarers literally gluten-free beard. Authentic Cosby sweater sustainable hashtag, VHS food truck kogi seitan put a bird on it YOLO. Selvage tousled mustache, flannel craft beer try-hard McSweeney's literally four loko YOLO keytar beard synth forage. Salvia Schlitz narwhal Terry Richardson typewriter, Wes Anderson butcher wolf. Slow-carb whatever bitters, letterpress trust fund pug before they sold out food truck artisan tousled. Church-key Vice craft beer Wes Anderson artisan flexitarian, kogi YOLO hella Tonx chia Neutra. + +Farm-to-table actually Portland, artisan shabby chic vinyl organic seitan roof party distillery. Street art PBR&B banh mi, Tonx authentic you probably haven't heard of them fixie whatever tofu gluten-free. Gentrify locavore lo-fi umami, Thundercats salvia wolf four loko. Mixtape messenger bag gluten-free, squid American Apparel hella Shoreditch whatever selfies sriracha before they sold out. Pickled farm-to-table Intelligentsia occupy. Tumblr Etsy farm-to-table, mlkshk hella shabby chic meh jean shorts dreamcatcher fashion axe trust fund lomo Neutra. Freegan vegan narwhal tousled hoodie wolf flexitarian. + +Flannel sriracha XOXO, slow-carb Godard ennui tousled American Apparel street art drinking vinegar lo-fi blog. Whatever Intelligentsia cardigan, Pinterest PBR&B pop-up semiotics. Jean shorts chillwave semiotics biodiesel. McSweeney's fap cardigan messenger bag fanny pack Cosby sweater Odd Future, Pitchfork four loko Marfa keytar mlkshk. 3 wolf moon McSweeney's gluten-free, umami freegan biodiesel fingerstache aesthetic sriracha swag Echo Park. Shabby chic selfies fixie, art party XOXO four loko chambray post-ironic letterpress messenger bag. Mustache beard lo-fi, flexitarian artisan tofu freegan occupy kale chips Carles twee chia bespoke."; + + $gpg = new GPG(); + $pub_key = new GPG_Public_Key($public_key_ascii); + $encrypted = $gpg->encrypt($pub_key,$plain_text_string); + + $this->assertContains('-----BEGIN PGP MESSAGE-----', $encrypted, 'PGP Header Expected'); + + $this->assertContains('-----END PGP MESSAGE-----', $encrypted, 'PGP Footer Expected'); + + } + +} + +?> \ No newline at end of file diff --git a/securemail/php-gpg/tests/gpg/KeyTest.php b/securemail/php-gpg/tests/gpg/KeyTest.php new file mode 100644 index 00000000..79395021 --- /dev/null +++ b/securemail/php-gpg/tests/gpg/KeyTest.php @@ -0,0 +1,1474 @@ +getGnuPGTestKey(); + + $gpg = new GPG(); + $pub_key = new GPG_Public_Key($public_key_ascii); + + $this->assertEquals(PK_TYPE_RSA,$pub_key->GetKeyType(),'OpenPGP Incorrect Key Type'); + $this->assertEquals('47009B66424E9476',$pub_key->GetKeyId(),'OpenPGP Incorrect Key ID'); + $this->assertEquals('ED4F E89E 38A3 7833 3CD4 D6FA 4700 9B66 424E 9476',$pub_key->GetFingerprint(),'OpenPGP Incorrect Fingerprint'); + + } + + /** + * Test key ID + */ + function test_VerifyOpenPGPKey1() + { + // OpenPGP Test Key + $public_key_ascii = $this->getOpenPGPTestKey1(); + + $gpg = new GPG(); + $pub_key = new GPG_Public_Key($public_key_ascii); + + $this->assertEquals(PK_TYPE_RSA,$pub_key->GetKeyType(),'OpenPGP Incorrect Key Type'); + $this->assertEquals('8DCE498F6091DFD6',$pub_key->GetKeyId(),'OpenPGP Incorrect Key ID'); + $this->assertEquals('C893 35AC EDF1 6046 7534 B25E 8DCE 498F 6091 DFD6',$pub_key->GetFingerprint(),'OpenPGP Incorrect Fingerprint'); + + } + + /** + * Test key ID + */ + function test_VerifyOpenPGPKey2() + { + // OpenPGP Test Key + $public_key_ascii = $this->getOpenPGPTestKey2(); + + $gpg = new GPG(); + $pub_key = new GPG_Public_Key($public_key_ascii); + + $this->assertEquals(PK_TYPE_RSA,$pub_key->GetKeyType(),'OpenPGP Incorrect Key Type'); + $this->assertEquals('C87538697986219A',$pub_key->GetKeyId(),'OpenPGP Incorrect Key ID'); + $this->assertEquals('3C05 9D07 C624 84A4 EF2D 3651 C875 3869 7986 219A',$pub_key->GetFingerprint(),'OpenPGP Incorrect Fingerprint'); + + } + + function test_VerifyGnuPGDSAKey() + { + // OpenPGP Test Key + $public_key_ascii = $this->getGnuPGDSAKey(); + + $gpg = new GPG(); + $pub_key = new GPG_Public_Key($public_key_ascii); + + $this->assertEquals(PK_TYPE_ELGAMAL,$pub_key->GetKeyType(),'OpenPGP Incorrect Key Type'); + $this->assertEquals('76D78F0500D026C4',$pub_key->GetKeyId(),'OpenPGP Incorrect Key ID'); + $this->assertEquals('85E3 8F69 046B 44C1 EC9F B07B 76D7 8F05 00D0 26C4',$pub_key->GetFingerprint(),'OpenPGP Incorrect Fingerprint'); + + + } + +} + +?> \ No newline at end of file diff --git a/securemail/php-gpg/tests/runtests.sh b/securemail/php-gpg/tests/runtests.sh new file mode 100644 index 00000000..32a1f70b --- /dev/null +++ b/securemail/php-gpg/tests/runtests.sh @@ -0,0 +1,4 @@ +## +# Test runner for Phreeze +## +phpunit gpg/ \ No newline at end of file diff --git a/securemail/securemail.php b/securemail/securemail.php new file mode 100644 index 00000000..306d12b4 --- /dev/null +++ b/securemail/securemail.php @@ -0,0 +1,93 @@ + + */ + require_once 'php-gpg/libs/GPG.php'; + + function securemail_install() { + register_hook('plugin_settings', 'addon/securemail/securemail.php', 'securemail_settings'); + register_hook('plugin_settings_post', 'addon/securemail/securemail.php', 'securemail_settings_post'); + + register_hook('emailer_send_prepare', 'addon/securemail/securemail.php', 'securemail_emailer_send_prepare'); + + logger("installed securemail"); +} + +function securemail_uninstall() { + unregister_hook('plugin_settings', 'addon/securemail/securemail.php', 'securemail_settings'); + unregister_hook('plugin_settings_post', 'addon/securemail/securemail.php', 'securemail_settings_post'); + + unregister_hook('emailer_send_prepare', 'addon/securemail/securemail.php', 'securemail_emailer_send_prepare'); + + logger("removed securemail"); +} + + +function securemail_settings(&$a,&$s){ + if(! local_user()) + return; + + $enable_checked = (intval(get_pconfig(local_user(),'securemail','enable')) ? ' checked="checked"' : ''); + $publickey = get_pconfig(local_user(),'securemail','pkey'); + + # all of this should be in a template... + $s .= ''; + $s .= '

' . t('"Secure Mail" Settings').'

'; + $s .= '
'; + $s .= ''; + + return; +} +function securemail_settings_post(&$a, &$b){ + + if(! local_user()) + return; + + if($_POST['securemail-submit']) { + set_pconfig(local_user(),'securemail','pkey',trim($_POST['securemail-pkey'])); + $enable = ((x($_POST,'securemail-enable')) ? 1 : 0); + set_pconfig(local_user(),'securemail','enable', $enable); + info( t('Secure Mail Settings saved.') . EOL); + } +} + +function securemail_emailer_send_prepare(&$a, &$b) { + if (!x($b,'uid')) return; + $uid = $b['uid']; + + $enable_checked = get_pconfig($uid,'securemail','enable'); + if (!$enable_checked) return; + + $public_key_ascii = get_pconfig($uid,'securemail','pkey'); + + $gpg = new GPG(); + + # create an instance of a GPG public key object based on ASCII key + $pub_key = new GPG_Public_Key($public_key_ascii); + + # using the key, encrypt your plain text using the public key + $txt_encrypted = $gpg->encrypt($pub_key,$b['textVersion']); + $html_encrypted = $gpg->encrypt($pub_key,$b['htmlVersion']); + + $b['textVersion'] = $txt_encrypted; + $b['htmlVersion'] = $html_encrypted; + #var_dump($b); +} -- 2.43.4 From a8a37ee6fc9e43ec6671ae9bac497165abc126fa Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Fri, 9 Jan 2015 07:07:07 +0100 Subject: [PATCH 6/7] app.net, acebook, pumpio, statusnet, twitter: Support for the new database fields in contact and unique_contacts. --- appnet/appnet.php | 54 +++++++++++++++++++++++++++++++++++++++++ diaspora/diasphp.php | 7 ++++++ fbsync/fbsync.php | 15 ++++++++++++ pumpio/pumpio.php | 42 ++++++++++++++++++++++++++++++-- statusnet/statusnet.php | 38 +++++++++++++++++++++++++++++ twitter/twitter.php | 39 ++++++++++++++++++++++++++++- 6 files changed, 192 insertions(+), 3 deletions(-) diff --git a/appnet/appnet.php b/appnet/appnet.php index 7e80c6ab..c327bd7d 100644 --- a/appnet/appnet.php +++ b/appnet/appnet.php @@ -1082,6 +1082,30 @@ function appnet_expand_annotations($a, $annotations) { } function appnet_fetchcontact($a, $uid, $contact, $me, $create_user) { + + $r = q("SELECT id FROM unique_contacts WHERE url='%s' LIMIT 1", + dbesc(normalise_link($contact["canonical_url"]))); + + if (count($r) == 0) + q("INSERT INTO unique_contacts (url, name, nick, avatar) VALUES ('%s', '%s', '%s', '%s')", + dbesc(normalise_link($contact["canonical_url"])), + dbesc($contact["name"]), + dbesc($contact["username"]), + dbesc($contact["avatar_image"]["url"])); + else + q("UPDATE unique_contacts SET name = '%s', nick = '%s', avatar = '%s' WHERE url = '%s'", + dbesc($contact["name"]), + dbesc($contact["username"]), + dbesc($contact["avatar_image"]["url"]), + dbesc(normalise_link($contact["canonical_url"]))); + + if (DB_UPDATE_VERSION >= "1177") + q("UPDATE `unique_contacts` SET `location` = '%s', `about` = '%s' WHERE url = '%s'", + dbesc(""), + dbesc($contact["description"]["text"]), + dbesc(normalise_link($contact["canonical_url"]))); + + $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1", intval($uid), dbesc("adn::".$contact["id"])); @@ -1165,6 +1189,14 @@ function appnet_fetchcontact($a, $uid, $contact, $me, $create_user) { intval($contact_id) ); + if (DB_UPDATE_VERSION >= "1177") + q("UPDATE `contact` SET `location` = '%s', + `about` = '%s' + WHERE `id` = %d", + dbesc(""), + dbesc($contact["description"]["text"]), + intval($contact_id) + ); } else { // update profile photos once every two weeks as we have no notification of when they change. @@ -1206,6 +1238,14 @@ function appnet_fetchcontact($a, $uid, $contact, $me, $create_user) { dbesc($contact["username"]), intval($r[0]['id']) ); + if (DB_UPDATE_VERSION >= "1177") + q("UPDATE `contact` SET `location` = '%s', + `about` = '%s' + WHERE `id` = %d", + dbesc(""), + dbesc($contact["description"]["text"]), + intval($r[0]['id']) + ); } } @@ -1265,9 +1305,23 @@ function appnet_cron($a,$b) { } logger('appnet_cron: cron_start'); + $abandon_days = intval(get_config('system','account_abandon_days')); + if ($abandon_days < 1) + $abandon_days = 0; + + $abandon_limit = date("Y-m-d H:i:s", time() - $abandon_days * 86400); + $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'appnet' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()"); if(count($r)) { foreach($r as $rr) { + if ($abandon_days != 0) { + $user = q("SELECT `login_date` FROM `user` WHERE uid=%d AND `login_date` >= '%s'", $rr['uid'], $abandon_limit); + if (!count($user)) { + logger('abandoned account: timeline from user '.$rr['uid'].' will not be imported'); + continue; + } + } + logger('appnet_cron: importing timeline from user '.$rr['uid']); appnet_fetchstream($a, $rr["uid"]); } diff --git a/diaspora/diasphp.php b/diaspora/diasphp.php index 589a5810..6ed0c8ce 100644 --- a/diaspora/diasphp.php +++ b/diaspora/diasphp.php @@ -5,6 +5,8 @@ */ class Diasphp { + private $cookiejar; + function __construct($pod) { $this->token_regex = '/content="(.*?)" name="csrf-token/'; @@ -12,6 +14,11 @@ class Diasphp { $this->cookiejar = tempnam(sys_get_temp_dir(), 'cookies'); } + function __destruct() { + if (file_exists($this->cookiejar)) + unlink($this->cookiejar); + } + function _fetch_token() { $ch = curl_init(); diff --git a/fbsync/fbsync.php b/fbsync/fbsync.php index 39931d5b..20762e9b 100644 --- a/fbsync/fbsync.php +++ b/fbsync/fbsync.php @@ -172,9 +172,24 @@ function fbsync_cron($a,$b) { } logger('fbsync_cron: cron_start'); + $abandon_days = intval(get_config('system','account_abandon_days')); + if ($abandon_days < 1) + $abandon_days = 0; + + $abandon_limit = date("Y-m-d H:i:s", time() - $abandon_days * 86400); + $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'fbsync' AND `k` = 'sync' AND `v` = '1' ORDER BY RAND()"); if(count($r)) { foreach($r as $rr) { + + if ($abandon_days != 0) { + $user = q("SELECT `login_date` FROM `user` WHERE uid=%d AND `login_date` >= '%s'", $rr['uid'], $abandon_limit); + if (!count($user)) { + logger('abandoned account: timeline from user '.$rr['uid'].' will not be imported'); + continue; + } + } + fbsync_get_self($rr['uid']); logger('fbsync_cron: importing timeline from user '.$rr['uid']); diff --git a/pumpio/pumpio.php b/pumpio/pumpio.php index 48dbab74..cb2ee000 100644 --- a/pumpio/pumpio.php +++ b/pumpio/pumpio.php @@ -624,9 +624,23 @@ function pumpio_cron(&$a,$b) { } } + $abandon_days = intval(get_config('system','account_abandon_days')); + if ($abandon_days < 1) + $abandon_days = 0; + + $abandon_limit = date("Y-m-d H:i:s", time() - $abandon_days * 86400); + $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'pumpio' AND `k` = 'import' AND `v` = '1' ORDER BY RAND() "); if(count($r)) { foreach($r as $rr) { + if ($abandon_days != 0) { + $user = q("SELECT `login_date` FROM `user` WHERE uid=%d AND `login_date` >= '%s'", $rr['uid'], $abandon_limit); + if (!count($user)) { + logger('abandoned account: timeline from user '.$rr['uid'].' will not be imported'); + continue; + } + } + logger('pumpio: importing timeline from user '.$rr['uid']); pumpio_fetchinbox($a, $rr['uid']); @@ -919,6 +933,12 @@ function pumpio_get_contact($uid, $contact) { dbesc($contact->image->url), dbesc(normalise_link($contact->url))); + if (DB_UPDATE_VERSION >= "1177") + q("UPDATE `unique_contacts` SET `location` = '%s', `about` = '%s' WHERE url = '%s'", + dbesc($contact->location->displayName), + dbesc($contact->summary), + dbesc(normalise_link($contact->url))); + $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `url` = '%s' LIMIT 1", intval($uid), dbesc($contact->url)); @@ -984,10 +1004,19 @@ function pumpio_get_contact($uid, $contact) { dbesc(datetime_convert()), intval($contact_id) ); + + if (DB_UPDATE_VERSION >= "1177") + q("UPDATE `contact` SET `location` = '%s', + `about` = '%s' + WHERE `id` = %d", + dbesc($contact->location->displayName), + dbesc($contact->summary), + intval($contact_id) + ); } else { // update profile photos once every two weeks as we have no notification of when they change. - - $update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -14 days')) ? true : false); + //$update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -14 days')) ? true : false); + $update_photo = ($r[0]['avatar-date'] < datetime_convert('','','now -12 hours')); // check that we have all the photos, this has been known to fail on occasion @@ -1016,6 +1045,15 @@ function pumpio_get_contact($uid, $contact) { dbesc($contact->preferredUsername), intval($r[0]['id']) ); + + if (DB_UPDATE_VERSION >= "1177") + q("UPDATE `contact` SET `location` = '%s', + `about` = '%s' + WHERE `id` = %d", + dbesc($contact->location->displayName), + dbesc($contact->summary), + intval($r[0]['id']) + ); } } diff --git a/statusnet/statusnet.php b/statusnet/statusnet.php index 7d0f80c3..f4dc7553 100644 --- a/statusnet/statusnet.php +++ b/statusnet/statusnet.php @@ -758,9 +758,23 @@ function statusnet_cron($a,$b) { } } + $abandon_days = intval(get_config('system','account_abandon_days')); + if ($abandon_days < 1) + $abandon_days = 0; + + $abandon_limit = date("Y-m-d H:i:s", time() - $abandon_days * 86400); + $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'statusnet' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()"); if(count($r)) { foreach($r as $rr) { + if ($abandon_days != 0) { + $user = q("SELECT `login_date` FROM `user` WHERE uid=%d AND `login_date` >= '%s'", $rr['uid'], $abandon_limit); + if (!count($user)) { + logger('abandoned account: timeline from user '.$rr['uid'].' will not be imported'); + continue; + } + } + logger('statusnet: importing timeline from user '.$rr['uid']); statusnet_fetchhometimeline($a, $rr["uid"]); } @@ -898,6 +912,12 @@ function statusnet_fetch_contact($uid, $contact, $create_user) { dbesc($contact->profile_image_url), dbesc(normalise_link($contact->statusnet_profile_url))); + if (DB_UPDATE_VERSION >= "1177") + q("UPDATE `unique_contacts` SET `location` = '%s', `about` = '%s' WHERE url = '%s'", + dbesc($contact->location), + dbesc($contact->description), + dbesc(normalise_link($contact->statusnet_profile_url))); + $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1", intval($uid), dbesc(normalise_link($contact->statusnet_profile_url))); @@ -967,6 +987,15 @@ function statusnet_fetch_contact($uid, $contact, $create_user) { intval($contact_id) ); + if (DB_UPDATE_VERSION >= "1177") + q("UPDATE `contact` SET `location` = '%s', + `about` = '%s' + WHERE `id` = %d", + dbesc($contact->location), + dbesc($contact->description), + intval($contact_id) + ); + } else { // update profile photos once every two weeks as we have no notification of when they change. @@ -1008,6 +1037,15 @@ function statusnet_fetch_contact($uid, $contact, $create_user) { dbesc($contact->screen_name), intval($r[0]['id']) ); + + if (DB_UPDATE_VERSION >= "1177") + q("UPDATE `contact` SET `location` = '%s', + `about` = '%s' + WHERE `id` = %d", + dbesc($contact->location), + dbesc($contact->description), + intval($r[0]['id']) + ); } } diff --git a/twitter/twitter.php b/twitter/twitter.php index 380121c2..2482a30c 100644 --- a/twitter/twitter.php +++ b/twitter/twitter.php @@ -603,10 +603,23 @@ function twitter_cron($a,$b) { } } + $abandon_days = intval(get_config('system','account_abandon_days')); + if ($abandon_days < 1) + $abandon_days = 0; + + $abandon_limit = date("Y-m-d H:i:s", time() - $abandon_days * 86400); $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()"); if(count($r)) { foreach($r as $rr) { + if ($abandon_days != 0) { + $user = q("SELECT `login_date` FROM `user` WHERE uid=%d AND `login_date` >= '%s'", $rr['uid'], $abandon_limit); + if (!count($user)) { + logger('abandoned account: timeline from user '.$rr['uid'].' will not be imported'); + continue; + } + } + logger('twitter: importing timeline from user '.$rr['uid']); twitter_fetchhometimeline($a, $rr["uid"]); @@ -924,6 +937,12 @@ function twitter_fetch_contact($uid, $contact, $create_user) { dbesc($avatar), dbesc(normalise_link("https://twitter.com/".$contact->screen_name))); + if (DB_UPDATE_VERSION >= "1177") + q("UPDATE `unique_contacts` SET `location` = '%s', `about` = '%s' WHERE url = '%s'", + dbesc($contact->location), + dbesc($contact->description), + dbesc(normalise_link("https://twitter.com/".$contact->screen_name))); + $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1", intval($uid), dbesc("twitter::".$contact->id_str)); @@ -940,7 +959,7 @@ function twitter_fetch_contact($uid, $contact, $create_user) { q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`, `name`, `nick`, `photo`, `network`, `rel`, `priority`, `writable`, `blocked`, `readonly`, `pending` ) - VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ", + VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0) ", intval($uid), dbesc(datetime_convert()), dbesc("https://twitter.com/".$contact->screen_name), @@ -997,6 +1016,15 @@ function twitter_fetch_contact($uid, $contact, $create_user) { intval($contact_id) ); + if (DB_UPDATE_VERSION >= "1177") + q("UPDATE `contact` SET `location` = '%s', + `about` = '%s' + WHERE `id` = %d", + dbesc($contact->location), + dbesc($contact->description), + intval($contact_id) + ); + } else { // update profile photos once every two weeks as we have no notification of when they change. @@ -1038,6 +1066,15 @@ function twitter_fetch_contact($uid, $contact, $create_user) { dbesc($contact->screen_name), intval($r[0]['id']) ); + + if (DB_UPDATE_VERSION >= "1177") + q("UPDATE `contact` SET `location` = '%s', + `about` = '%s' + WHERE `id` = %d", + dbesc($contact->location), + dbesc($contact->description), + intval($r[0]['id']) + ); } } -- 2.43.4 From 7ba157a68d195a75fee5363112cff1eb13108816 Mon Sep 17 00:00:00 2001 From: gerhard6380 Date: Sat, 17 Jan 2015 21:58:18 +0100 Subject: [PATCH 7/7] windowsphonepush Version 2.0 add functionality to highlight new entries in Windows Phone app, bugfix on sql for counter (likes/dislikes now excluded) --- windowsphonepush/windowsphonepush.php | 50 ++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/windowsphonepush/windowsphonepush.php b/windowsphonepush/windowsphonepush.php index 9938edc7..21f986e0 100644 --- a/windowsphonepush/windowsphonepush.php +++ b/windowsphonepush/windowsphonepush.php @@ -2,7 +2,7 @@ /** * Name: WindowsPhonePush * Description: Enable push notification to send information to Friendica Mobile app on Windows phone (count of unread timeline entries, text of last posting - if wished by user) - * Version: 1.1 + * Version: 2.0 * Author: Gerhard Seeber * * @@ -18,6 +18,11 @@ * Version history: * 1.1 : addon crashed on php versions >= 5.4 as of removed deprecated call-time * pass-by-reference used in function calls within function windowsphonepush_content + * 2.0 : adaption for supporting emphasizing new entries in app (count on tile cannot be read out, + * so we need to retrieve counter through show_settings secondly). Provide new function for + * calling from app to set the counter back after start (if user starts again before cronjob + * sets the counter back + * count only unseen elements which are not type=activity (likes and dislikes not seen as new elements) */ @@ -81,8 +86,13 @@ function windowsphonepush_module() {} function windowsphonepush_settings_post($a,$post) { if(! local_user() || (! x($_POST,'windowsphonepush-submit'))) return; + $enable = intval($_POST['windowsphonepush']); + set_pconfig(local_user(),'windowsphonepush','enable',$enable); + + if($enable) { + set_pconfig(local_user(),'windowsphonepush','counterunseen', 0); + } - set_pconfig(local_user(),'windowsphonepush','enable',intval($_POST['windowsphonepush'])); set_pconfig(local_user(),'windowsphonepush','senditemtext',intval($_POST['windowsphonepush-senditemtext'])); info( t('WindowsPhonePush settings updated.') . EOL); @@ -164,7 +174,7 @@ function windowsphonepush_cron() { } else { // retrieve the number of unseen items and the id of the latest one (if there are more than // one new entries since last poller run, only the latest one will be pushed) - $count = q("SELECT count(`id`) as count, max(`id`) as max FROM `item` WHERE `unseen` = 1 AND `uid` = %d", + $count = q("SELECT count(`id`) as count, max(`id`) as max FROM `item` WHERE `unseen` = 1 AND `type` <> 'activity' AND `uid` = %d", intval($rr['uid']) ); @@ -174,7 +184,8 @@ function windowsphonepush_cron() { $res_tile = send_tile_update($device_url, "", $count[0]['count'], ""); switch (trim($res_tile)) { case "Received": - // ok, count has been pushed + // ok, count has been pushed, let's save it in personal settings + set_pconfig($rr['uid'], 'windowsphonepush', 'counterunseen', $count[0]['count']); break; case "QueueFull": // maximum of 30 messages reached, server rejects any further push notification until device reconnects @@ -342,6 +353,7 @@ function get_header_value($content, $header) { * reading information from url and deciding which function to start * show_settings = delivering settings to check * update_settings = set the device_url + * update_counterunseen = set counter for unseen elements to zero * */ function windowsphonepush_content(&$a) { @@ -362,6 +374,12 @@ function windowsphonepush_content(&$a) { echo json_encode(array('status' => $ret)); killme(); break; + case "update_counterunseen": + $ret = windowsphonepush_updatecounterunseen(); + header("Content-Type: application/json; charset=utf-8"); + echo json_encode(array('status' => $ret)); + killme(); + break; default: echo "Fehler"; } @@ -379,6 +397,8 @@ function windowsphonepush_showsettings(&$a) { $device_url = get_pconfig(local_user(), 'windowsphonepush', 'device_url'); $senditemtext = get_pconfig(local_user(), 'windowsphonepush', 'senditemtext'); $lastpushid = get_pconfig(local_user(), 'windowsphonepush', 'lastpushid'); + $counterunseen = get_pconfig(local_user(), 'windowsphonepush', 'counterunseen'); + $addonversion = "2.0"; if (!$device_url) $device_url = ""; @@ -391,7 +411,9 @@ function windowsphonepush_showsettings(&$a) { 'enable' => $enable, 'device_url' => $device_url, 'senditemtext' => $senditemtext, - 'lastpushid' => $lastpushid)); + 'lastpushid' => $lastpushid, + 'counterunseen' => $counterunseen, + 'addonversion' => $addonversion)); } /* @@ -437,6 +459,24 @@ function windowsphonepush_updatesettings(&$a) { return "Device-URL updated successfully!"; } +/* + * update_counterunseen is used to reset the counter to zero from Windows Phone app + */ +function windowsphonepush_updatecounterunseen() { + if(! local_user()) { + return "Not Authenticated"; + } + + // no updating if user hasn't enabled the plugin + $enable = get_pconfig(local_user(), 'windowsphonepush', 'enable'); + if(! $enable) { + return "Plug-in not enabled"; + } + + set_pconfig(local_user(),'windowsphonepush','counterunseen', 0); + return "Counter set to zero"; +} + /* * helper function to login to the server with the specified Network credentials * (mainly copied from api.php) -- 2.43.4