You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Engine.php 69KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * Crypt_GPG is a package to use GPG from PHP
  5. *
  6. * This file contains an engine that handles GPG subprocess control and I/O.
  7. * PHP's process manipulation functions are used to handle the GPG subprocess.
  8. *
  9. * PHP version 5
  10. *
  11. * LICENSE:
  12. *
  13. * This library is free software; you can redistribute it and/or modify
  14. * it under the terms of the GNU Lesser General Public License as
  15. * published by the Free Software Foundation; either version 2.1 of the
  16. * License, or (at your option) any later version.
  17. *
  18. * This library is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  21. * Lesser General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU Lesser General Public
  24. * License along with this library; if not, see
  25. * <http://www.gnu.org/licenses/>
  26. *
  27. * @category Encryption
  28. * @package Crypt_GPG
  29. * @author Nathan Fredrickson <nathan@silverorange.com>
  30. * @author Michael Gauthier <mike@silverorange.com>
  31. * @copyright 2005-2013 silverorange
  32. * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
  33. * @link http://pear.php.net/package/Crypt_GPG
  34. * @link http://www.gnupg.org/
  35. */
  36. /**
  37. * Crypt_GPG base class.
  38. */
  39. require_once 'Crypt/GPG.php';
  40. /**
  41. * GPG exception classes.
  42. */
  43. require_once 'Crypt/GPG/Exceptions.php';
  44. /**
  45. * Status/Error handler class.
  46. */
  47. require_once 'Crypt/GPG/ProcessHandler.php';
  48. /**
  49. * Process control methods.
  50. */
  51. require_once 'Crypt/GPG/ProcessControl.php';
  52. /**
  53. * Information about a created signature
  54. */
  55. require_once 'Crypt/GPG/SignatureCreationInfo.php';
  56. /**
  57. * Standard PEAR exception is used if GPG binary is not found.
  58. */
  59. require_once 'PEAR/Exception.php';
  60. // {{{ class Crypt_GPG_Engine
  61. /**
  62. * Native PHP Crypt_GPG I/O engine
  63. *
  64. * This class is used internally by Crypt_GPG and does not need be used
  65. * directly. See the {@link Crypt_GPG} class for end-user API.
  66. *
  67. * This engine uses PHP's native process control functions to directly control
  68. * the GPG process. The GPG executable is required to be on the system.
  69. *
  70. * All data is passed to the GPG subprocess using file descriptors. This is the
  71. * most secure method of passing data to the GPG subprocess.
  72. *
  73. * @category Encryption
  74. * @package Crypt_GPG
  75. * @author Nathan Fredrickson <nathan@silverorange.com>
  76. * @author Michael Gauthier <mike@silverorange.com>
  77. * @copyright 2005-2013 silverorange
  78. * @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
  79. * @link http://pear.php.net/package/Crypt_GPG
  80. * @link http://www.gnupg.org/
  81. */
  82. class Crypt_GPG_Engine
  83. {
  84. // {{{ constants
  85. /**
  86. * Size of data chunks that are sent to and retrieved from the IPC pipes.
  87. *
  88. * The value of 65536 has been chosen empirically
  89. * as the one with best performance.
  90. *
  91. * @see https://pear.php.net/bugs/bug.php?id=21077
  92. */
  93. const CHUNK_SIZE = 65536;
  94. /**
  95. * Standard input file descriptor. This is used to pass data to the GPG
  96. * process.
  97. */
  98. const FD_INPUT = 0;
  99. /**
  100. * Standard output file descriptor. This is used to receive normal output
  101. * from the GPG process.
  102. */
  103. const FD_OUTPUT = 1;
  104. /**
  105. * Standard output file descriptor. This is used to receive error output
  106. * from the GPG process.
  107. */
  108. const FD_ERROR = 2;
  109. /**
  110. * GPG status output file descriptor. The status file descriptor outputs
  111. * detailed information for many GPG commands. See the second section of
  112. * the file <b>doc/DETAILS</b> in the
  113. * {@link http://www.gnupg.org/download/ GPG package} for a detailed
  114. * description of GPG's status output.
  115. */
  116. const FD_STATUS = 3;
  117. /**
  118. * Command input file descriptor. This is used for methods requiring
  119. * passphrases.
  120. */
  121. const FD_COMMAND = 4;
  122. /**
  123. * Extra message input file descriptor. This is used for passing signed
  124. * data when verifying a detached signature.
  125. */
  126. const FD_MESSAGE = 5;
  127. /**
  128. * Minimum version of GnuPG that is supported.
  129. */
  130. const MIN_VERSION = '1.0.2';
  131. // }}}
  132. // {{{ private class properties
  133. /**
  134. * Whether or not to use strict mode
  135. *
  136. * When set to true, any clock problems (e.g. keys generate in future)
  137. * are errors, otherwise they are just warnings.
  138. *
  139. * Strict mode is disabled by default.
  140. *
  141. * @var boolean
  142. * @see Crypt_GPG_Engine::__construct()
  143. */
  144. private $_strict = false;
  145. /**
  146. * Whether or not to use debugging mode
  147. *
  148. * When set to true, every GPG command is echoed before it is run. Sensitive
  149. * data is always handled using pipes and is not specified as part of the
  150. * command. As a result, sensitive data is never displayed when debug is
  151. * enabled. Sensitive data includes private key data and passphrases.
  152. *
  153. * This can be set to a callable function where first argument is the
  154. * debug line to process.
  155. *
  156. * Debugging is off by default.
  157. *
  158. * @var mixed
  159. * @see Crypt_GPG_Engine::__construct()
  160. */
  161. private $_debug = false;
  162. /**
  163. * Location of GPG binary
  164. *
  165. * @var string
  166. * @see Crypt_GPG_Engine::__construct()
  167. * @see Crypt_GPG_Engine::_getBinary()
  168. */
  169. private $_binary = '';
  170. /**
  171. * Location of GnuPG agent binary
  172. *
  173. * Only used for GnuPG 2.x
  174. *
  175. * @var string
  176. * @see Crypt_GPG_Engine::__construct()
  177. * @see Crypt_GPG_Engine::_getAgent()
  178. */
  179. private $_agent = '';
  180. /**
  181. * Location of GnuPG conf binary
  182. *
  183. * Only used for GnuPG 2.1.x
  184. *
  185. * @var string
  186. * @see Crypt_GPG_Engine::__construct()
  187. * @see Crypt_GPG_Engine::_getGPGConf()
  188. */
  189. private $_gpgconf = null;
  190. /**
  191. * Directory containing the GPG key files
  192. *
  193. * This property only contains the path when the <i>homedir</i> option
  194. * is specified in the constructor.
  195. *
  196. * @var string
  197. * @see Crypt_GPG_Engine::__construct()
  198. */
  199. private $_homedir = '';
  200. /**
  201. * File path of the public keyring
  202. *
  203. * This property only contains the file path when the <i>public_keyring</i>
  204. * option is specified in the constructor.
  205. *
  206. * If the specified file path starts with <kbd>~/</kbd>, the path is
  207. * relative to the <i>homedir</i> if specified, otherwise to
  208. * <kbd>~/.gnupg</kbd>.
  209. *
  210. * @var string
  211. * @see Crypt_GPG_Engine::__construct()
  212. */
  213. private $_publicKeyring = '';
  214. /**
  215. * File path of the private (secret) keyring
  216. *
  217. * This property only contains the file path when the <i>private_keyring</i>
  218. * option is specified in the constructor.
  219. *
  220. * If the specified file path starts with <kbd>~/</kbd>, the path is
  221. * relative to the <i>homedir</i> if specified, otherwise to
  222. * <kbd>~/.gnupg</kbd>.
  223. *
  224. * @var string
  225. * @see Crypt_GPG_Engine::__construct()
  226. */
  227. private $_privateKeyring = '';
  228. /**
  229. * File path of the trust database
  230. *
  231. * This property only contains the file path when the <i>trust_db</i>
  232. * option is specified in the constructor.
  233. *
  234. * If the specified file path starts with <kbd>~/</kbd>, the path is
  235. * relative to the <i>homedir</i> if specified, otherwise to
  236. * <kbd>~/.gnupg</kbd>.
  237. *
  238. * @var string
  239. * @see Crypt_GPG_Engine::__construct()
  240. */
  241. private $_trustDb = '';
  242. /**
  243. * Array of pipes used for communication with the GPG binary
  244. *
  245. * This is an array of file descriptor resources.
  246. *
  247. * @var array
  248. */
  249. private $_pipes = array();
  250. /**
  251. * Array of pipes used for communication with the gpg-agent binary
  252. *
  253. * This is an array of file descriptor resources.
  254. *
  255. * @var array
  256. */
  257. private $_agentPipes = array();
  258. /**
  259. * Array of currently opened pipes
  260. *
  261. * This array is used to keep track of remaining opened pipes so they can
  262. * be closed when the GPG subprocess is finished. This array is a subset of
  263. * the {@link Crypt_GPG_Engine::$_pipes} array and contains opened file
  264. * descriptor resources.
  265. *
  266. * @var array
  267. * @see Crypt_GPG_Engine::_closePipe()
  268. */
  269. private $_openPipes = array();
  270. /**
  271. * A handle for the GPG process
  272. *
  273. * @var resource
  274. */
  275. private $_process = null;
  276. /**
  277. * A handle for the gpg-agent process
  278. *
  279. * @var resource
  280. */
  281. private $_agentProcess = null;
  282. /**
  283. * GPG agent daemon socket and PID for running gpg-agent
  284. *
  285. * @var string
  286. */
  287. private $_agentInfo = null;
  288. /**
  289. * Whether or not the operating system is Darwin (OS X)
  290. *
  291. * @var boolean
  292. */
  293. private $_isDarwin = false;
  294. /**
  295. * Message digest algorithm.
  296. *
  297. * @var string
  298. */
  299. private $_digest_algo = null;
  300. /**
  301. * Symmetric cipher algorithm.
  302. *
  303. * @var string
  304. */
  305. private $_cipher_algo = null;
  306. /**
  307. * Commands to be sent to GPG's command input stream
  308. *
  309. * @var string
  310. * @see Crypt_GPG_Engine::sendCommand()
  311. */
  312. private $_commandBuffer = '';
  313. /**
  314. * A status/error handler
  315. *
  316. * @var Crypt_GPG_ProcessHanler
  317. */
  318. private $_processHandler = null;
  319. /**
  320. * Array of status line handlers
  321. *
  322. * @var array
  323. * @see Crypt_GPG_Engine::addStatusHandler()
  324. */
  325. private $_statusHandlers = array();
  326. /**
  327. * Array of error line handlers
  328. *
  329. * @var array
  330. * @see Crypt_GPG_Engine::addErrorHandler()
  331. */
  332. private $_errorHandlers = array();
  333. /**
  334. * The input source
  335. *
  336. * This is data to send to GPG. Either a string or a stream resource.
  337. *
  338. * @var string|resource
  339. * @see Crypt_GPG_Engine::setInput()
  340. */
  341. private $_input = null;
  342. /**
  343. * The extra message input source
  344. *
  345. * Either a string or a stream resource.
  346. *
  347. * @var string|resource
  348. * @see Crypt_GPG_Engine::setMessage()
  349. */
  350. private $_message = null;
  351. /**
  352. * The output location
  353. *
  354. * This is where the output from GPG is sent. Either a string or a stream
  355. * resource.
  356. *
  357. * @var string|resource
  358. * @see Crypt_GPG_Engine::setOutput()
  359. */
  360. private $_output = '';
  361. /**
  362. * The GPG operation to execute
  363. *
  364. * @var string
  365. * @see Crypt_GPG_Engine::setOperation()
  366. */
  367. private $_operation;
  368. /**
  369. * Arguments for the current operation
  370. *
  371. * @var array
  372. * @see Crypt_GPG_Engine::setOperation()
  373. */
  374. private $_arguments = array();
  375. /**
  376. * The version number of the GPG binary
  377. *
  378. * @var string
  379. * @see Crypt_GPG_Engine::getVersion()
  380. */
  381. private $_version = '';
  382. // }}}
  383. // {{{ __construct()
  384. /**
  385. * Creates a new GPG engine
  386. *
  387. * Available options are:
  388. *
  389. * - <kbd>string homedir</kbd> - the directory where the GPG
  390. * keyring files are stored. If not
  391. * specified, Crypt_GPG uses the
  392. * default of <kbd>~/.gnupg</kbd>.
  393. * - <kbd>string publicKeyring</kbd> - the file path of the public
  394. * keyring. Use this if the public
  395. * keyring is not in the homedir, or
  396. * if the keyring is in a directory
  397. * not writable by the process
  398. * invoking GPG (like Apache). Then
  399. * you can specify the path to the
  400. * keyring with this option
  401. * (/foo/bar/pubring.gpg), and specify
  402. * a writable directory (like /tmp)
  403. * using the <i>homedir</i> option.
  404. * - <kbd>string privateKeyring</kbd> - the file path of the private
  405. * keyring. Use this if the private
  406. * keyring is not in the homedir, or
  407. * if the keyring is in a directory
  408. * not writable by the process
  409. * invoking GPG (like Apache). Then
  410. * you can specify the path to the
  411. * keyring with this option
  412. * (/foo/bar/secring.gpg), and specify
  413. * a writable directory (like /tmp)
  414. * using the <i>homedir</i> option.
  415. * - <kbd>string trustDb</kbd> - the file path of the web-of-trust
  416. * database. Use this if the trust
  417. * database is not in the homedir, or
  418. * if the database is in a directory
  419. * not writable by the process
  420. * invoking GPG (like Apache). Then
  421. * you can specify the path to the
  422. * trust database with this option
  423. * (/foo/bar/trustdb.gpg), and specify
  424. * a writable directory (like /tmp)
  425. * using the <i>homedir</i> option.
  426. * - <kbd>string binary</kbd> - the location of the GPG binary. If
  427. * not specified, the driver attempts
  428. * to auto-detect the GPG binary
  429. * location using a list of known
  430. * default locations for the current
  431. * operating system. The option
  432. * <kbd>gpgBinary</kbd> is a
  433. * deprecated alias for this option.
  434. * - <kbd>string agent</kbd> - the location of the GnuPG agent
  435. * binary. The gpg-agent is only
  436. * used for GnuPG 2.x. If not
  437. * specified, the engine attempts
  438. * to auto-detect the gpg-agent
  439. * binary location using a list of
  440. * know default locations for the
  441. * current operating system.
  442. * - <kbd>string|false gpgconf</kbd> - the location of the GnuPG conf
  443. * binary. The gpgconf is only
  444. * used for GnuPG >= 2.1. If not
  445. * specified, the engine attempts
  446. * to auto-detect the location using
  447. * a list of know default locations.
  448. * When set to FALSE `gpgconf --kill`
  449. * will not be executed via destructor.
  450. * - <kbd>string digest-algo</kbd> - Sets the message digest algorithm.
  451. * - <kbd>string cipher-algo</kbd> - Sets the symmetric cipher.
  452. * - <kbd>boolean strict</kbd> - In strict mode clock problems on
  453. * subkeys and signatures are not ignored
  454. * (--ignore-time-conflict
  455. * and --ignore-valid-from options)
  456. * - <kbd>mixed debug</kbd> - whether or not to use debug mode.
  457. * When debug mode is on, all
  458. * communication to and from the GPG
  459. * subprocess is logged. This can be
  460. * useful to diagnose errors when
  461. * using Crypt_GPG.
  462. *
  463. * @param array $options optional. An array of options used to create the
  464. * GPG object. All options are optional and are
  465. * represented as key-value pairs.
  466. *
  467. * @throws Crypt_GPG_FileException if the <kbd>homedir</kbd> does not exist
  468. * and cannot be created. This can happen if <kbd>homedir</kbd> is
  469. * not specified, Crypt_GPG is run as the web user, and the web
  470. * user has no home directory. This exception is also thrown if any
  471. * of the options <kbd>publicKeyring</kbd>,
  472. * <kbd>privateKeyring</kbd> or <kbd>trustDb</kbd> options are
  473. * specified but the files do not exist or are are not readable.
  474. * This can happen if the user running the Crypt_GPG process (for
  475. * example, the Apache user) does not have permission to read the
  476. * files.
  477. *
  478. * @throws PEAR_Exception if the provided <kbd>binary</kbd> is invalid, or
  479. * if no <kbd>binary</kbd> is provided and no suitable binary could
  480. * be found.
  481. *
  482. * @throws PEAR_Exception if the provided <kbd>agent</kbd> is invalid, or
  483. * if no <kbd>agent</kbd> is provided and no suitable gpg-agent
  484. * cound be found.
  485. */
  486. public function __construct(array $options = array())
  487. {
  488. $this->_isDarwin = (strncmp(strtoupper(PHP_OS), 'DARWIN', 6) === 0);
  489. // get homedir
  490. if (array_key_exists('homedir', $options)) {
  491. $this->_homedir = (string)$options['homedir'];
  492. } else {
  493. if (extension_loaded('posix')) {
  494. // note: this requires the package OS dep exclude 'windows'
  495. $info = posix_getpwuid(posix_getuid());
  496. $this->_homedir = $info['dir'].'/.gnupg';
  497. } else {
  498. if (isset($_SERVER['HOME'])) {
  499. $this->_homedir = $_SERVER['HOME'];
  500. } else {
  501. $this->_homedir = getenv('HOME');
  502. }
  503. }
  504. if ($this->_homedir === false) {
  505. throw new Crypt_GPG_FileException(
  506. 'Could not locate homedir. Please specify the homedir ' .
  507. 'to use with the \'homedir\' option when instantiating ' .
  508. 'the Crypt_GPG object.'
  509. );
  510. }
  511. }
  512. // attempt to create homedir if it does not exist
  513. if (!is_dir($this->_homedir)) {
  514. if (@mkdir($this->_homedir, 0777, true)) {
  515. // Set permissions on homedir. Parent directories are created
  516. // with 0777, homedir is set to 0700.
  517. chmod($this->_homedir, 0700);
  518. } else {
  519. throw new Crypt_GPG_FileException(
  520. 'The \'homedir\' "' . $this->_homedir . '" is not ' .
  521. 'readable or does not exist and cannot be created. This ' .
  522. 'can happen if \'homedir\' is not specified in the ' .
  523. 'Crypt_GPG options, Crypt_GPG is run as the web user, ' .
  524. 'and the web user has no home directory.',
  525. 0,
  526. $this->_homedir
  527. );
  528. }
  529. }
  530. // check homedir permissions (See Bug #19833)
  531. if (!is_executable($this->_homedir)) {
  532. throw new Crypt_GPG_FileException(
  533. 'The \'homedir\' "' . $this->_homedir . '" is not enterable ' .
  534. 'by the current user. Please check the permissions on your ' .
  535. 'homedir and make sure the current user can both enter and ' .
  536. 'write to the directory.',
  537. 0,
  538. $this->_homedir
  539. );
  540. }
  541. if (!is_writeable($this->_homedir)) {
  542. throw new Crypt_GPG_FileException(
  543. 'The \'homedir\' "' . $this->_homedir . '" is not writable ' .
  544. 'by the current user. Please check the permissions on your ' .
  545. 'homedir and make sure the current user can both enter and ' .
  546. 'write to the directory.',
  547. 0,
  548. $this->_homedir
  549. );
  550. }
  551. // get binary
  552. if (array_key_exists('binary', $options)) {
  553. $this->_binary = (string)$options['binary'];
  554. } elseif (array_key_exists('gpgBinary', $options)) {
  555. // deprecated alias
  556. $this->_binary = (string)$options['gpgBinary'];
  557. } else {
  558. $this->_binary = $this->_getBinary();
  559. }
  560. if ($this->_binary == '' || !is_executable($this->_binary)) {
  561. throw new PEAR_Exception(
  562. 'GPG binary not found. If you are sure the GPG binary is ' .
  563. 'installed, please specify the location of the GPG binary ' .
  564. 'using the \'binary\' driver option.'
  565. );
  566. }
  567. // get agent
  568. if (array_key_exists('agent', $options)) {
  569. $this->_agent = (string)$options['agent'];
  570. if ($this->_agent && !is_executable($this->_agent)) {
  571. throw new PEAR_Exception(
  572. 'Specified gpg-agent binary is not executable.'
  573. );
  574. }
  575. } else {
  576. $this->_agent = $this->_getAgent();
  577. }
  578. if (array_key_exists('gpgconf', $options)) {
  579. $this->_gpgconf = $options['gpgconf'];
  580. if ($this->_gpgconf && !is_executable($this->_gpgconf)) {
  581. throw new PEAR_Exception(
  582. 'Specified gpgconf binary is not executable.'
  583. );
  584. }
  585. }
  586. /*
  587. * Note:
  588. *
  589. * Normally, GnuPG expects keyrings to be in the homedir and expects
  590. * to be able to write temporary files in the homedir. Sometimes,
  591. * keyrings are not in the homedir, or location of the keyrings does
  592. * not allow writing temporary files. In this case, the <i>homedir</i>
  593. * option by itself is not enough to specify the keyrings because GnuPG
  594. * can not write required temporary files. Additional options are
  595. * provided so you can specify the location of the keyrings separately
  596. * from the homedir.
  597. */
  598. // get public keyring
  599. if (array_key_exists('publicKeyring', $options)) {
  600. $this->_publicKeyring = (string)$options['publicKeyring'];
  601. if (!is_readable($this->_publicKeyring)) {
  602. throw new Crypt_GPG_FileException(
  603. 'The \'publicKeyring\' "' . $this->_publicKeyring .
  604. '" does not exist or is not readable. Check the location ' .
  605. 'and ensure the file permissions are correct.',
  606. 0, $this->_publicKeyring
  607. );
  608. }
  609. }
  610. // get private keyring
  611. if (array_key_exists('privateKeyring', $options)) {
  612. $this->_privateKeyring = (string)$options['privateKeyring'];
  613. if (!is_readable($this->_privateKeyring)) {
  614. throw new Crypt_GPG_FileException(
  615. 'The \'privateKeyring\' "' . $this->_privateKeyring .
  616. '" does not exist or is not readable. Check the location ' .
  617. 'and ensure the file permissions are correct.',
  618. 0, $this->_privateKeyring
  619. );
  620. }
  621. }
  622. // get trust database
  623. if (array_key_exists('trustDb', $options)) {
  624. $this->_trustDb = (string)$options['trustDb'];
  625. if (!is_readable($this->_trustDb)) {
  626. throw new Crypt_GPG_FileException(
  627. 'The \'trustDb\' "' . $this->_trustDb .
  628. '" does not exist or is not readable. Check the location ' .
  629. 'and ensure the file permissions are correct.',
  630. 0, $this->_trustDb
  631. );
  632. }
  633. }
  634. if (array_key_exists('debug', $options)) {
  635. $this->_debug = $options['debug'];
  636. }
  637. $this->_strict = !empty($options['strict']);
  638. if (!empty($options['digest-algo'])) {
  639. $this->_digest_algo = $options['digest-algo'];
  640. }
  641. if (!empty($options['cipher-algo'])) {
  642. $this->_cipher_algo = $options['cipher-algo'];
  643. }
  644. }
  645. // }}}
  646. // {{{ __destruct()
  647. /**
  648. * Closes open GPG subprocesses when this object is destroyed
  649. *
  650. * Subprocesses should never be left open by this class unless there is
  651. * an unknown error and unexpected script termination occurs.
  652. */
  653. public function __destruct()
  654. {
  655. $this->_closeSubprocess();
  656. $this->_closeIdleAgents();
  657. }
  658. // }}}
  659. // {{{ addErrorHandler()
  660. /**
  661. * Adds an error handler method
  662. *
  663. * The method is run every time a new error line is received from the GPG
  664. * subprocess. The handler method must accept the error line to be handled
  665. * as its first parameter.
  666. *
  667. * @param callback $callback the callback method to use.
  668. * @param array $args optional. Additional arguments to pass as
  669. * parameters to the callback method.
  670. *
  671. * @return void
  672. */
  673. public function addErrorHandler($callback, array $args = array())
  674. {
  675. $this->_errorHandlers[] = array(
  676. 'callback' => $callback,
  677. 'args' => $args
  678. );
  679. }
  680. // }}}
  681. // {{{ addStatusHandler()
  682. /**
  683. * Adds a status handler method
  684. *
  685. * The method is run every time a new status line is received from the
  686. * GPG subprocess. The handler method must accept the status line to be
  687. * handled as its first parameter.
  688. *
  689. * @param callback $callback the callback method to use.
  690. * @param array $args optional. Additional arguments to pass as
  691. * parameters to the callback method.
  692. *
  693. * @return void
  694. */
  695. public function addStatusHandler($callback, array $args = array())
  696. {
  697. $this->_statusHandlers[] = array(
  698. 'callback' => $callback,
  699. 'args' => $args
  700. );
  701. }
  702. // }}}
  703. // {{{ sendCommand()
  704. /**
  705. * Sends a command to the GPG subprocess over the command file-descriptor
  706. * pipe
  707. *
  708. * @param string $command the command to send.
  709. *
  710. * @return void
  711. *
  712. * @sensitive $command
  713. */
  714. public function sendCommand($command)
  715. {
  716. if (array_key_exists(self::FD_COMMAND, $this->_openPipes)) {
  717. $this->_commandBuffer .= $command . PHP_EOL;
  718. }
  719. }
  720. // }}}
  721. // {{{ reset()
  722. /**
  723. * Resets the GPG engine, preparing it for a new operation
  724. *
  725. * @return void
  726. *
  727. * @see Crypt_GPG_Engine::run()
  728. * @see Crypt_GPG_Engine::setOperation()
  729. */
  730. public function reset()
  731. {
  732. $this->_operation = '';
  733. $this->_arguments = array();
  734. $this->_input = null;
  735. $this->_message = null;
  736. $this->_output = '';
  737. $this->_commandBuffer = '';
  738. $this->_statusHandlers = array();
  739. $this->_errorHandlers = array();
  740. if ($this->_debug) {
  741. $this->addStatusHandler(array($this, '_handleDebugStatus'));
  742. $this->addErrorHandler(array($this, '_handleDebugError'));
  743. }
  744. $this->_processHandler = new Crypt_GPG_ProcessHandler($this);
  745. $this->addStatusHandler(array($this->_processHandler, 'handleStatus'));
  746. $this->addErrorHandler(array($this->_processHandler, 'handleError'));
  747. }
  748. // }}}
  749. // {{{ run()
  750. /**
  751. * Runs the current GPG operation.
  752. *
  753. * This creates and manages the GPG subprocess.
  754. * This will close input/output file handles.
  755. *
  756. * The operation must be set with {@link Crypt_GPG_Engine::setOperation()}
  757. * before this method is called.
  758. *
  759. * @return void
  760. *
  761. * @throws Crypt_GPG_InvalidOperationException if no operation is specified.
  762. * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
  763. *
  764. * @see Crypt_GPG_Engine::reset()
  765. * @see Crypt_GPG_Engine::setOperation()
  766. */
  767. public function run()
  768. {
  769. if ($this->_operation === '') {
  770. throw new Crypt_GPG_InvalidOperationException(
  771. 'No GPG operation specified. Use Crypt_GPG_Engine::setOperation() ' .
  772. 'before calling Crypt_GPG_Engine::run().'
  773. );
  774. }
  775. $this->_openSubprocess();
  776. $this->_process();
  777. $this->_closeSubprocess();
  778. }
  779. // }}}
  780. // {{{ setInput()
  781. /**
  782. * Sets the input source for the current GPG operation
  783. *
  784. * @param string|resource &$input either a reference to the string
  785. * containing the input data or an open
  786. * stream resource containing the input
  787. * data.
  788. *
  789. * @return void
  790. */
  791. public function setInput(&$input)
  792. {
  793. $this->_input =& $input;
  794. }
  795. // }}}
  796. // {{{ setMessage()
  797. /**
  798. * Sets the message source for the current GPG operation
  799. *
  800. * Detached signature data should be specified here.
  801. *
  802. * @param string|resource &$message either a reference to the string
  803. * containing the message data or an open
  804. * stream resource containing the message
  805. * data.
  806. *
  807. * @return void
  808. */
  809. public function setMessage(&$message)
  810. {
  811. $this->_message =& $message;
  812. }
  813. // }}}
  814. // {{{ setOutput()
  815. /**
  816. * Sets the output destination for the current GPG operation
  817. *
  818. * @param string|resource &$output either a reference to the string in
  819. * which to store GPG output or an open
  820. * stream resource to which the output data
  821. * should be written.
  822. *
  823. * @return void
  824. */
  825. public function setOutput(&$output)
  826. {
  827. $this->_output =& $output;
  828. }
  829. // }}}
  830. // {{{ setOperation()
  831. /**
  832. * Sets the operation to perform
  833. *
  834. * @param string $operation the operation to perform. This should be one
  835. * of GPG's operations. For example,
  836. * <kbd>--encrypt</kbd>, <kbd>--decrypt</kbd>,
  837. * <kbd>--sign</kbd>, etc.
  838. * @param array $arguments optional. Additional arguments for the GPG
  839. * subprocess. See the GPG manual for specific
  840. * values.
  841. *
  842. * @return void
  843. *
  844. * @see Crypt_GPG_Engine::reset()
  845. * @see Crypt_GPG_Engine::run()
  846. */
  847. public function setOperation($operation, array $arguments = array())
  848. {
  849. $this->_operation = $operation;
  850. $this->_arguments = $arguments;
  851. $this->_processHandler->setOperation($operation);
  852. }
  853. // }}}
  854. // {{{ setPins()
  855. /**
  856. * Sets the PINENTRY_USER_DATA environment variable with the currently
  857. * added keys and passphrases
  858. *
  859. * Keys and passphrases are stored as an indexed array of passphrases
  860. * in JSON encoded to a flat string.
  861. *
  862. * For GnuPG 2.x this is how passphrases are passed. For GnuPG 1.x the
  863. * environment variable is set but not used.
  864. *
  865. * @param array $keys the internal key array to use.
  866. *
  867. * @return void
  868. */
  869. public function setPins(array $keys)
  870. {
  871. $envKeys = array();
  872. foreach ($keys as $keyId => $key) {
  873. $envKeys[$keyId] = is_array($key) ? $key['passphrase'] : $key;
  874. }
  875. $_ENV['PINENTRY_USER_DATA'] = json_encode($envKeys);
  876. }
  877. // }}}
  878. // {{{ getVersion()
  879. /**
  880. * Gets the version of the GnuPG binary
  881. *
  882. * @return string a version number string containing the version of GnuPG
  883. * being used. This value is suitable to use with PHP's
  884. * version_compare() function.
  885. *
  886. * @throws Crypt_GPG_Exception if an unknown or unexpected error occurs.
  887. * Use the <kbd>debug</kbd> option and file a bug report if these
  888. * exceptions occur.
  889. *
  890. * @throws Crypt_GPG_UnsupportedException if the provided binary is not
  891. * GnuPG or if the GnuPG version is less than 1.0.2.
  892. */
  893. public function getVersion()
  894. {
  895. if ($this->_version == '') {
  896. $options = array(
  897. 'homedir' => $this->_homedir,
  898. 'binary' => $this->_binary,
  899. 'debug' => $this->_debug,
  900. 'agent' => $this->_agent,
  901. );
  902. $engine = new self($options);
  903. $info = '';
  904. // Set a garbage version so we do not end up looking up the version
  905. // recursively.
  906. $engine->_version = '1.0.0';
  907. $engine->reset();
  908. $engine->setOutput($info);
  909. $engine->setOperation('--version');
  910. $engine->run();
  911. $matches = array();
  912. $expression = '#gpg \(GnuPG[A-Za-z0-9/]*?\) (\S+)#';
  913. if (preg_match($expression, $info, $matches) === 1) {
  914. $this->_version = $matches[1];
  915. } else {
  916. throw new Crypt_GPG_Exception(
  917. 'No GnuPG version information provided by the binary "' .
  918. $this->_binary . '". Are you sure it is GnuPG?'
  919. );
  920. }
  921. if (version_compare($this->_version, self::MIN_VERSION, 'lt')) {
  922. throw new Crypt_GPG_Exception(
  923. 'The version of GnuPG being used (' . $this->_version .
  924. ') is not supported by Crypt_GPG. The minimum version ' .
  925. 'required by Crypt_GPG is ' . self::MIN_VERSION
  926. );
  927. }
  928. }
  929. return $this->_version;
  930. }
  931. // }}}
  932. // {{{ getProcessData()
  933. /**
  934. * Get data from the last process execution.
  935. *
  936. * @param string $name Data element name (e.g. 'SignatureInfo')
  937. *
  938. * @return mixed
  939. * @see Crypt_GPG_ProcessHandler::getData()
  940. */
  941. public function getProcessData($name)
  942. {
  943. if ($this->_processHandler) {
  944. switch ($name) {
  945. case 'SignatureInfo':
  946. if ($data = $this->_processHandler->getData('SigCreated')) {
  947. return new Crypt_GPG_SignatureCreationInfo($data);
  948. }
  949. break;
  950. case 'Signatures':
  951. return (array) $this->_processHandler->getData('Signatures');
  952. default:
  953. return $this->_processHandler->getData($name);
  954. }
  955. }
  956. }
  957. // }}}
  958. // {{{ setProcessData()
  959. /**
  960. * Set some data for the process execution.
  961. *
  962. * @param string $name Data element name (e.g. 'Handle')
  963. * @param mixed $value Data value
  964. *
  965. * @return void
  966. */
  967. public function setProcessData($name, $value)
  968. {
  969. if ($this->_processHandler) {
  970. $this->_processHandler->setData($name, $value);
  971. }
  972. }
  973. // }}}
  974. // {{{ _handleDebugStatus()
  975. /**
  976. * Displays debug output for status lines
  977. *
  978. * @param string $line the status line to handle.
  979. *
  980. * @return void
  981. */
  982. private function _handleDebugStatus($line)
  983. {
  984. $this->_debug('STATUS: ' . $line);
  985. }
  986. // }}}
  987. // {{{ _handleDebugError()
  988. /**
  989. * Displays debug output for error lines
  990. *
  991. * @param string $line the error line to handle.
  992. *
  993. * @return void
  994. */
  995. private function _handleDebugError($line)
  996. {
  997. $this->_debug('ERROR: ' . $line);
  998. }
  999. // }}}
  1000. // {{{ _process()
  1001. /**
  1002. * Performs internal streaming operations for the subprocess using either
  1003. * strings or streams as input / output points
  1004. *
  1005. * This is the main I/O loop for streaming to and from the GPG subprocess.
  1006. *
  1007. * The implementation of this method is verbose mainly for performance
  1008. * reasons. Adding streams to a lookup array and looping the array inside
  1009. * the main I/O loop would be siginficantly slower for large streams.
  1010. *
  1011. * @return void
  1012. *
  1013. * @throws Crypt_GPG_Exception if there is an error selecting streams for
  1014. * reading or writing. If this occurs, please file a bug report at
  1015. * http://pear.php.net/bugs/report.php?package=Crypt_GPG.
  1016. */
  1017. private function _process()
  1018. {
  1019. $this->_debug('BEGIN PROCESSING');
  1020. $this->_commandBuffer = ''; // buffers input to GPG
  1021. $messageBuffer = ''; // buffers input to GPG
  1022. $inputBuffer = ''; // buffers input to GPG
  1023. $outputBuffer = ''; // buffers output from GPG
  1024. $statusBuffer = ''; // buffers output from GPG
  1025. $errorBuffer = ''; // buffers output from GPG
  1026. $inputComplete = false; // input stream is completely buffered
  1027. $messageComplete = false; // message stream is completely buffered
  1028. if (is_string($this->_input)) {
  1029. $inputBuffer = $this->_input;
  1030. $inputComplete = true;
  1031. }
  1032. if (is_string($this->_message)) {
  1033. $messageBuffer = $this->_message;
  1034. $messageComplete = true;
  1035. }
  1036. if (is_string($this->_output)) {
  1037. $outputBuffer =& $this->_output;
  1038. }
  1039. // convenience variables
  1040. $fdInput = $this->_pipes[self::FD_INPUT];
  1041. $fdOutput = $this->_pipes[self::FD_OUTPUT];
  1042. $fdError = $this->_pipes[self::FD_ERROR];
  1043. $fdStatus = $this->_pipes[self::FD_STATUS];
  1044. $fdCommand = $this->_pipes[self::FD_COMMAND];
  1045. $fdMessage = $this->_pipes[self::FD_MESSAGE];
  1046. // select loop delay in milliseconds
  1047. $delay = 0;
  1048. $inputPosition = 0;
  1049. $eolLength = mb_strlen(PHP_EOL, '8bit');
  1050. while (true) {
  1051. $inputStreams = array();
  1052. $outputStreams = array();
  1053. $exceptionStreams = array();
  1054. // set up input streams
  1055. if (is_resource($this->_input) && !$inputComplete) {
  1056. if (feof($this->_input)) {
  1057. $inputComplete = true;
  1058. } else {
  1059. $inputStreams[] = $this->_input;
  1060. }
  1061. }
  1062. // close GPG input pipe if there is no more data
  1063. if ($inputBuffer == '' && $inputComplete) {
  1064. $this->_debug('=> closing GPG input pipe');
  1065. $this->_closePipe(self::FD_INPUT);
  1066. }
  1067. if (is_resource($this->_message) && !$messageComplete) {
  1068. if (feof($this->_message)) {
  1069. $messageComplete = true;
  1070. } else {
  1071. $inputStreams[] = $this->_message;
  1072. }
  1073. }
  1074. // close GPG message pipe if there is no more data
  1075. if ($messageBuffer == '' && $messageComplete) {
  1076. $this->_debug('=> closing GPG message pipe');
  1077. $this->_closePipe(self::FD_MESSAGE);
  1078. }
  1079. if (!feof($fdOutput)) {
  1080. $inputStreams[] = $fdOutput;
  1081. }
  1082. if (!feof($fdStatus)) {
  1083. $inputStreams[] = $fdStatus;
  1084. }
  1085. if (!feof($fdError)) {
  1086. $inputStreams[] = $fdError;
  1087. }
  1088. // set up output streams
  1089. if ($outputBuffer != '' && is_resource($this->_output)) {
  1090. $outputStreams[] = $this->_output;
  1091. }
  1092. if ($this->_commandBuffer != '' && is_resource($fdCommand)) {
  1093. $outputStreams[] = $fdCommand;
  1094. }
  1095. if ($messageBuffer != '' && is_resource($fdMessage)) {
  1096. $outputStreams[] = $fdMessage;
  1097. }
  1098. if ($inputBuffer != '' && is_resource($fdInput)) {
  1099. $outputStreams[] = $fdInput;
  1100. }
  1101. // no streams left to read or write, we're all done
  1102. if (count($inputStreams) === 0 && count($outputStreams) === 0) {
  1103. break;
  1104. }
  1105. $this->_debug('selecting streams');
  1106. $ready = stream_select(
  1107. $inputStreams,
  1108. $outputStreams,
  1109. $exceptionStreams,
  1110. null
  1111. );
  1112. $this->_debug('=> got ' . $ready);
  1113. if ($ready === false) {
  1114. throw new Crypt_GPG_Exception(
  1115. 'Error selecting stream for communication with GPG ' .
  1116. 'subprocess. Please file a bug report at: ' .
  1117. 'http://pear.php.net/bugs/report.php?package=Crypt_GPG'
  1118. );
  1119. }
  1120. if ($ready === 0) {
  1121. throw new Crypt_GPG_Exception(
  1122. 'stream_select() returned 0. This can not happen! Please ' .
  1123. 'file a bug report at: ' .
  1124. 'http://pear.php.net/bugs/report.php?package=Crypt_GPG'
  1125. );
  1126. }
  1127. // write input (to GPG)
  1128. if (in_array($fdInput, $outputStreams, true)) {
  1129. $this->_debug('GPG is ready for input');
  1130. $chunk = mb_substr($inputBuffer, $inputPosition, self::CHUNK_SIZE, '8bit');
  1131. $length = mb_strlen($chunk, '8bit');
  1132. $this->_debug(
  1133. '=> about to write ' . $length . ' bytes to GPG input'
  1134. );
  1135. $length = fwrite($fdInput, $chunk, $length);
  1136. if ($length === 0) {
  1137. // If we wrote 0 bytes it was either EAGAIN or EPIPE. Since
  1138. // the pipe was seleted for writing, we assume it was EPIPE.
  1139. // There's no way to get the actual error code in PHP. See
  1140. // PHP Bug #39598. https://bugs.php.net/bug.php?id=39598
  1141. $this->_debug('=> broken pipe on GPG input');
  1142. $this->_debug('=> closing pipe GPG input');
  1143. $this->_closePipe(self::FD_INPUT);
  1144. } else {
  1145. $this->_debug('=> wrote ' . $length . ' bytes');
  1146. // Move the position pointer, don't modify $inputBuffer (#21081)
  1147. if (is_string($this->_input)) {
  1148. $inputPosition += $length;
  1149. } else {
  1150. $inputPosition = 0;
  1151. $inputBuffer = mb_substr($inputBuffer, $length, null, '8bit');
  1152. }
  1153. }
  1154. }
  1155. // read input (from PHP stream)
  1156. // If the buffer is too big wait until it's smaller, we don't want
  1157. // to use too much memory
  1158. if (in_array($this->_input, $inputStreams, true)
  1159. && mb_strlen($inputBuffer, '8bit') < self::CHUNK_SIZE
  1160. ) {
  1161. $this->_debug('input stream is ready for reading');
  1162. $this->_debug(
  1163. '=> about to read ' . self::CHUNK_SIZE .
  1164. ' bytes from input stream'
  1165. );
  1166. $chunk = fread($this->_input, self::CHUNK_SIZE);
  1167. $length = mb_strlen($chunk, '8bit');
  1168. $inputBuffer .= $chunk;
  1169. $this->_debug('=> read ' . $length . ' bytes');
  1170. }
  1171. // write message (to GPG)
  1172. if (in_array($fdMessage, $outputStreams, true)) {
  1173. $this->_debug('GPG is ready for message data');
  1174. $chunk = mb_substr($messageBuffer, 0, self::CHUNK_SIZE, '8bit');
  1175. $length = mb_strlen($chunk, '8bit');
  1176. $this->_debug(
  1177. '=> about to write ' . $length . ' bytes to GPG message'
  1178. );
  1179. $length = fwrite($fdMessage, $chunk, $length);
  1180. if ($length === 0) {
  1181. // If we wrote 0 bytes it was either EAGAIN or EPIPE. Since
  1182. // the pipe was seleted for writing, we assume it was EPIPE.
  1183. // There's no way to get the actual error code in PHP. See
  1184. // PHP Bug #39598. https://bugs.php.net/bug.php?id=39598
  1185. $this->_debug('=> broken pipe on GPG message');
  1186. $this->_debug('=> closing pipe GPG message');
  1187. $this->_closePipe(self::FD_MESSAGE);
  1188. } else {
  1189. $this->_debug('=> wrote ' . $length . ' bytes');
  1190. $messageBuffer = mb_substr($messageBuffer, $length, null, '8bit');
  1191. }
  1192. }
  1193. // read message (from PHP stream)
  1194. if (in_array($this->_message, $inputStreams, true)) {
  1195. $this->_debug('message stream is ready for reading');
  1196. $this->_debug(
  1197. '=> about to read ' . self::CHUNK_SIZE .
  1198. ' bytes from message stream'
  1199. );
  1200. $chunk = fread($this->_message, self::CHUNK_SIZE);
  1201. $length = mb_strlen($chunk, '8bit');
  1202. $messageBuffer .= $chunk;
  1203. $this->_debug('=> read ' . $length . ' bytes');
  1204. }
  1205. // read output (from GPG)
  1206. if (in_array($fdOutput, $inputStreams, true)) {
  1207. $this->_debug('GPG output stream ready for reading');
  1208. $this->_debug(
  1209. '=> about to read ' . self::CHUNK_SIZE .
  1210. ' bytes from GPG output'
  1211. );
  1212. $chunk = fread($fdOutput, self::CHUNK_SIZE);
  1213. $length = mb_strlen($chunk, '8bit');
  1214. $outputBuffer .= $chunk;
  1215. $this->_debug('=> read ' . $length . ' bytes');
  1216. }
  1217. // write output (to PHP stream)
  1218. if (in_array($this->_output, $outputStreams, true)) {
  1219. $this->_debug('output stream is ready for data');
  1220. $chunk = mb_substr($outputBuffer, 0, self::CHUNK_SIZE, '8bit');
  1221. $length = mb_strlen($chunk, '8bit');
  1222. $this->_debug(
  1223. '=> about to write ' . $length . ' bytes to output stream'
  1224. );
  1225. $length = fwrite($this->_output, $chunk, $length);
  1226. $outputBuffer = mb_substr($outputBuffer, $length, null, '8bit');
  1227. $this->_debug('=> wrote ' . $length . ' bytes');
  1228. }
  1229. // read error (from GPG)
  1230. if (in_array($fdError, $inputStreams, true)) {
  1231. $this->_debug('GPG error stream ready for reading');
  1232. $this->_debug(
  1233. '=> about to read ' . self::CHUNK_SIZE .
  1234. ' bytes from GPG error'
  1235. );
  1236. $chunk = fread($fdError, self::CHUNK_SIZE);
  1237. $length = mb_strlen($chunk, '8bit');
  1238. $errorBuffer .= $chunk;
  1239. $this->_debug('=> read ' . $length . ' bytes');
  1240. // pass lines to error handlers
  1241. while (($pos = strpos($errorBuffer, PHP_EOL)) !== false) {
  1242. $line = mb_substr($errorBuffer, 0, $pos, '8bit');
  1243. foreach ($this->_errorHandlers as $handler) {
  1244. array_unshift($handler['args'], $line);
  1245. call_user_func_array(
  1246. $handler['callback'],
  1247. $handler['args']
  1248. );
  1249. array_shift($handler['args']);
  1250. }
  1251. $errorBuffer = mb_substr($errorBuffer, $pos + $eolLength, null, '8bit');
  1252. }
  1253. }
  1254. // read status (from GPG)
  1255. if (in_array($fdStatus, $inputStreams, true)) {
  1256. $this->_debug('GPG status stream ready for reading');
  1257. $this->_debug(
  1258. '=> about to read ' . self::CHUNK_SIZE .
  1259. ' bytes from GPG status'
  1260. );
  1261. $chunk = fread($fdStatus, self::CHUNK_SIZE);
  1262. $length = mb_strlen($chunk, '8bit');
  1263. $statusBuffer .= $chunk;
  1264. $this->_debug('=> read ' . $length . ' bytes');
  1265. // pass lines to status handlers
  1266. while (($pos = strpos($statusBuffer, PHP_EOL)) !== false) {
  1267. $line = mb_substr($statusBuffer, 0, $pos, '8bit');
  1268. // only pass lines beginning with magic prefix
  1269. if (mb_substr($line, 0, 9, '8bit') == '[GNUPG:] ') {
  1270. $line = mb_substr($line, 9, null, '8bit');
  1271. foreach ($this->_statusHandlers as $handler) {
  1272. array_unshift($handler['args'], $line);
  1273. call_user_func_array(
  1274. $handler['callback'],
  1275. $handler['args']
  1276. );
  1277. array_shift($handler['args']);
  1278. }
  1279. }
  1280. $statusBuffer = mb_substr($statusBuffer, $pos + $eolLength, null, '8bit');
  1281. }
  1282. }
  1283. // write command (to GPG)
  1284. if (in_array($fdCommand, $outputStreams, true)) {
  1285. $this->_debug('GPG is ready for command data');
  1286. // send commands
  1287. $chunk = mb_substr($this->_commandBuffer, 0, self::CHUNK_SIZE, '8bit');
  1288. $length = mb_strlen($chunk, '8bit');
  1289. $this->_debug(
  1290. '=> about to write ' . $length . ' bytes to GPG command'
  1291. );
  1292. $length = fwrite($fdCommand, $chunk, $length);
  1293. if ($length === 0) {
  1294. // If we wrote 0 bytes it was either EAGAIN or EPIPE. Since
  1295. // the pipe was seleted for writing, we assume it was EPIPE.
  1296. // There's no way to get the actual error code in PHP. See
  1297. // PHP Bug #39598. https://bugs.php.net/bug.php?id=39598
  1298. $this->_debug('=> broken pipe on GPG command');
  1299. $this->_debug('=> closing pipe GPG command');
  1300. $this->_closePipe(self::FD_COMMAND);
  1301. } else {
  1302. $this->_debug('=> wrote ' . $length);
  1303. $this->_commandBuffer = mb_substr($this->_commandBuffer, $length, null, '8bit');
  1304. }
  1305. }
  1306. if (count($outputStreams) === 0 || count($inputStreams) === 0) {
  1307. // we have an I/O imbalance, increase the select loop delay
  1308. // to smooth things out
  1309. $delay += 10;
  1310. } else {
  1311. // things are running smoothly, decrease the delay
  1312. $delay -= 8;
  1313. $delay = max(0, $delay);
  1314. }
  1315. if ($delay > 0) {
  1316. usleep($delay);
  1317. }
  1318. } // end loop while streams are open
  1319. $this->_debug('END PROCESSING');
  1320. }
  1321. // }}}
  1322. // {{{ _openSubprocess()
  1323. /**
  1324. * Opens an internal GPG subprocess for the current operation
  1325. *
  1326. * Opens a GPG subprocess, then connects the subprocess to some pipes. Sets
  1327. * the private class property {@link Crypt_GPG_Engine::$_process} to
  1328. * the new subprocess.
  1329. *
  1330. * @return void
  1331. *
  1332. * @throws Crypt_GPG_OpenSubprocessException if the subprocess could not be
  1333. * opened.
  1334. *
  1335. * @see Crypt_GPG_Engine::setOperation()
  1336. * @see Crypt_GPG_Engine::_closeSubprocess()
  1337. * @see Crypt_GPG_Engine::$_process
  1338. */
  1339. private function _openSubprocess()
  1340. {
  1341. $version = $this->getVersion();
  1342. // log versions, but not when looking for the version number
  1343. if ($version !== '1.0.0') {
  1344. $this->_debug('USING GPG ' . $version . ' with PHP ' . PHP_VERSION);
  1345. }
  1346. // Binary operations will not work on Windows with PHP < 5.2.6. This is
  1347. // in case stream_select() ever works on Windows.
  1348. $rb = (version_compare(PHP_VERSION, '5.2.6') < 0) ? 'r' : 'rb';
  1349. $wb = (version_compare(PHP_VERSION, '5.2.6') < 0) ? 'w' : 'wb';
  1350. $env = $_ENV;
  1351. // Newer versions of GnuPG return localized results. Crypt_GPG only
  1352. // works with English, so set the locale to 'C' for the subprocess.
  1353. $env['LC_ALL'] = 'C';
  1354. // If using GnuPG 2.x < 2.1.13 start the gpg-agent
  1355. if (version_compare($version, '2.0.0', 'ge')
  1356. && version_compare($version, '2.1.13', 'lt')
  1357. ) {
  1358. if (!$this->_agent) {
  1359. throw new Crypt_GPG_OpenSubprocessException(
  1360. 'Unable to open gpg-agent subprocess (gpg-agent not found). ' .
  1361. 'Please specify location of the gpg-agent binary ' .
  1362. 'using the \'agent\' driver option.'
  1363. );
  1364. }
  1365. $agentArguments = array(
  1366. '--daemon',
  1367. '--options /dev/null', // ignore any saved options
  1368. '--csh', // output is easier to parse
  1369. '--keep-display', // prevent passing --display to pinentry
  1370. '--no-grab',
  1371. '--ignore-cache-for-signing',
  1372. '--pinentry-touch-file /dev/null',
  1373. '--disable-scdaemon',
  1374. '--no-use-standard-socket',
  1375. '--pinentry-program ' . escapeshellarg($this->_getPinEntry())
  1376. );
  1377. if ($this->_homedir) {
  1378. $agentArguments[] = '--homedir ' .
  1379. escapeshellarg($this->_homedir);
  1380. }
  1381. if ($version21 = version_compare($version, '2.1.0', 'ge')) {
  1382. // This is needed to get socket file location in stderr output
  1383. // Note: This does not help when the agent already is running
  1384. $agentArguments[] = '--verbose';
  1385. }
  1386. $agentCommandLine = $this->_agent . ' ' . implode(' ', $agentArguments);
  1387. $agentDescriptorSpec = array(
  1388. self::FD_INPUT => array('pipe', $rb), // stdin
  1389. self::FD_OUTPUT => array('pipe', $wb), // stdout
  1390. self::FD_ERROR => array('pipe', $wb) // stderr
  1391. );
  1392. $this->_debug('OPENING GPG-AGENT SUBPROCESS WITH THE FOLLOWING COMMAND:');
  1393. $this->_debug($agentCommandLine);
  1394. $this->_agentProcess = proc_open(
  1395. $agentCommandLine,
  1396. $agentDescriptorSpec,
  1397. $this->_agentPipes,
  1398. null,
  1399. $env,
  1400. array('binary_pipes' => true)
  1401. );
  1402. if (!is_resource($this->_agentProcess)) {
  1403. throw new Crypt_GPG_OpenSubprocessException(
  1404. 'Unable to open gpg-agent subprocess.',
  1405. 0,
  1406. $agentCommandLine
  1407. );
  1408. }
  1409. // Get GPG_AGENT_INFO and set environment variable for gpg process.
  1410. // This is a blocking read, but is only 1 line.
  1411. $agentInfo = fread($this->_agentPipes[self::FD_OUTPUT], self::CHUNK_SIZE);
  1412. // For GnuPG 2.1 we need to read both stderr and stdout
  1413. if ($version21) {
  1414. $agentInfo .= "\n" . fread($this->_agentPipes[self::FD_ERROR], self::CHUNK_SIZE);
  1415. }
  1416. if ($agentInfo) {
  1417. foreach (explode("\n", $agentInfo) as $line) {
  1418. if ($version21) {
  1419. if (preg_match('/listening on socket \'([^\']+)/', $line, $m)) {
  1420. $this->_agentInfo = $m[1];
  1421. } else if (preg_match('/gpg-agent\[([0-9]+)\].* started/', $line, $m)) {
  1422. $this->_agentInfo .= ':' . $m[1] . ':1';
  1423. }
  1424. } else if (preg_match('/GPG_AGENT_INFO[=\s]([^;]+)/', $line, $m)) {
  1425. $this->_agentInfo = $m[1];
  1426. break;
  1427. }
  1428. }
  1429. }
  1430. $this->_debug('GPG-AGENT-INFO: ' . $this->_agentInfo);
  1431. $env['GPG_AGENT_INFO'] = $this->_agentInfo;
  1432. // gpg-agent daemon is started, we can close the launching process
  1433. $this->_closeAgentLaunchProcess();
  1434. // Terminate processes if something went wrong
  1435. register_shutdown_function(array($this, '__destruct'));
  1436. }
  1437. // "Register" GPGConf existence for _closeIdleAgents()
  1438. if (version_compare($version, '2.1.0', 'ge')) {
  1439. if ($this->_gpgconf === null) {
  1440. $this->_gpgconf = $this->_getGPGConf();
  1441. }
  1442. } else {
  1443. $this->_gpgconf = false;
  1444. }
  1445. $commandLine = $this->_binary;
  1446. $defaultArguments = array(
  1447. '--status-fd ' . escapeshellarg(self::FD_STATUS),
  1448. '--command-fd ' . escapeshellarg(self::FD_COMMAND),
  1449. '--no-secmem-warning',
  1450. '--no-tty',
  1451. '--no-default-keyring', // ignored if keying files are not specified
  1452. '--no-options' // prevent creation of ~/.gnupg directory
  1453. );
  1454. if (version_compare($version, '1.0.7', 'ge')) {
  1455. if (version_compare($version, '2.0.0', 'lt')) {
  1456. $defaultArguments[] = '--no-use-agent';
  1457. }
  1458. $defaultArguments[] = '--no-permission-warning';
  1459. }
  1460. if (version_compare($version, '1.4.2', 'ge')) {
  1461. $defaultArguments[] = '--exit-on-status-write-error';
  1462. }
  1463. if (version_compare($version, '1.3.2', 'ge')) {
  1464. $defaultArguments[] = '--trust-model always';
  1465. } else {
  1466. $defaultArguments[] = '--always-trust';
  1467. }
  1468. // Since 2.1.13 we can use "loopback mode" instead of gpg-agent
  1469. if (version_compare($version, '2.1.13', 'ge')) {
  1470. $defaultArguments[] = '--pinentry-mode loopback';
  1471. }
  1472. if (!$this->_strict) {
  1473. $defaultArguments[] = '--ignore-time-conflict';
  1474. $defaultArguments[] = '--ignore-valid-from';
  1475. }
  1476. if (!empty($this->_digest_algo)) {
  1477. $defaultArguments[] = '--digest-algo ' . escapeshellarg($this->_digest_algo);
  1478. $defaultArguments[] = '--s2k-digest-algo ' . escapeshellarg($this->_digest_algo);
  1479. }
  1480. if (!empty($this->_cipher_algo)) {
  1481. $defaultArguments[] = '--cipher-algo ' . escapeshellarg($this->_cipher_algo);
  1482. $defaultArguments[] = '--s2k-cipher-algo ' . escapeshellarg($this->_cipher_algo);
  1483. }
  1484. $arguments = array_merge($defaultArguments, $this->_arguments);
  1485. if ($this->_homedir) {
  1486. $arguments[] = '--homedir ' . escapeshellarg($this->_homedir);
  1487. // the random seed file makes subsequent actions faster so only
  1488. // disable it if we have to.
  1489. if (!is_writeable($this->_homedir)) {
  1490. $arguments[] = '--no-random-seed-file';
  1491. }
  1492. }
  1493. if ($this->_publicKeyring) {
  1494. $arguments[] = '--keyring ' . escapeshellarg($this->_publicKeyring);
  1495. }
  1496. if ($this->_privateKeyring) {
  1497. $arguments[] = '--secret-keyring ' .
  1498. escapeshellarg($this->_privateKeyring);
  1499. }
  1500. if ($this->_trustDb) {
  1501. $arguments[] = '--trustdb-name ' . escapeshellarg($this->_trustDb);
  1502. }
  1503. $commandLine .= ' ' . implode(' ', $arguments) . ' ' .
  1504. $this->_operation;
  1505. $descriptorSpec = array(
  1506. self::FD_INPUT => array('pipe', $rb), // stdin
  1507. self::FD_OUTPUT => array('pipe', $wb), // stdout
  1508. self::FD_ERROR => array('pipe', $wb), // stderr
  1509. self::FD_STATUS => array('pipe', $wb), // status
  1510. self::FD_COMMAND => array('pipe', $rb), // command
  1511. self::FD_MESSAGE => array('pipe', $rb) // message
  1512. );
  1513. $this->_debug('OPENING GPG SUBPROCESS WITH THE FOLLOWING COMMAND:');
  1514. $this->_debug($commandLine);
  1515. $this->_process = proc_open(
  1516. $commandLine,
  1517. $descriptorSpec,
  1518. $this->_pipes,
  1519. null,
  1520. $env,
  1521. array('binary_pipes' => true)
  1522. );
  1523. if (!is_resource($this->_process)) {
  1524. throw new Crypt_GPG_OpenSubprocessException(
  1525. 'Unable to open GPG subprocess.', 0, $commandLine
  1526. );
  1527. }
  1528. // Set streams as non-blocking. See Bug #18618.
  1529. foreach ($this->_pipes as $pipe) {
  1530. stream_set_blocking($pipe, 0);
  1531. stream_set_write_buffer($pipe, self::CHUNK_SIZE);
  1532. stream_set_chunk_size($pipe, self::CHUNK_SIZE);
  1533. stream_set_read_buffer($pipe, self::CHUNK_SIZE);
  1534. }
  1535. $this->_openPipes = $this->_pipes;
  1536. }
  1537. // }}}
  1538. // {{{ _closeSubprocess()
  1539. /**
  1540. * Closes the internal GPG subprocess
  1541. *
  1542. * Closes the internal GPG subprocess. Sets the private class property
  1543. * {@link Crypt_GPG_Engine::$_process} to null.
  1544. *
  1545. * @return void
  1546. *
  1547. * @see Crypt_GPG_Engine::_openSubprocess()
  1548. * @see Crypt_GPG_Engine::$_process
  1549. */
  1550. private function _closeSubprocess()
  1551. {
  1552. // clear PINs from environment if they were set
  1553. $_ENV['PINENTRY_USER_DATA'] = null;
  1554. if (is_resource($this->_process)) {
  1555. $this->_debug('CLOSING GPG SUBPROCESS');
  1556. // close remaining open pipes
  1557. foreach (array_keys($this->_openPipes) as $pipeNumber) {
  1558. $this->_closePipe($pipeNumber);
  1559. }
  1560. $status = proc_get_status($this->_process);
  1561. $exitCode = proc_close($this->_process);
  1562. // proc_close() can return -1 in some cases,
  1563. // get the real exit code from the process status
  1564. if ($exitCode < 0 && $status && !$status['running']) {
  1565. $exitCode = $status['exitcode'];
  1566. }
  1567. if ($exitCode > 0) {
  1568. $this->_debug(
  1569. '=> subprocess returned an unexpected exit code: ' .
  1570. $exitCode
  1571. );
  1572. }
  1573. $this->_process = null;
  1574. $this->_pipes = array();
  1575. // close file handles before throwing an exception
  1576. if (is_resource($this->_input)) {
  1577. fclose($this->_input);
  1578. }
  1579. if (is_resource($this->_output)) {
  1580. fclose($this->_output);
  1581. }
  1582. $this->_processHandler->throwException($exitCode);
  1583. }
  1584. $this->_closeAgentLaunchProcess();
  1585. if ($this->_agentInfo !== null) {
  1586. $parts = explode(':', $this->_agentInfo, 3);
  1587. if (!empty($parts[1])) {
  1588. $this->_debug('STOPPING GPG-AGENT DAEMON');
  1589. $process = new Crypt_GPG_ProcessControl($parts[1]);
  1590. // terminate agent daemon
  1591. $process->terminate();
  1592. while ($process->isRunning()) {
  1593. usleep(10000); // 10 ms
  1594. $process->terminate();
  1595. }
  1596. $this->_debug('GPG-AGENT DAEMON STOPPED');
  1597. }
  1598. $this->_agentInfo = null;
  1599. }
  1600. }
  1601. // }}}
  1602. // {{{ _closeAgentLaunchProcess()
  1603. /**
  1604. * Closes a the internal GPG-AGENT subprocess
  1605. *
  1606. * Closes the internal GPG-AGENT subprocess. Sets the private class property
  1607. * {@link Crypt_GPG_Engine::$_agentProcess} to null.
  1608. *
  1609. * @return void
  1610. *
  1611. * @see Crypt_GPG_Engine::_openSubprocess()
  1612. * @see Crypt_GPG_Engine::$_agentProcess
  1613. */
  1614. private function _closeAgentLaunchProcess()
  1615. {
  1616. if (is_resource($this->_agentProcess)) {
  1617. $this->_debug('CLOSING GPG-AGENT LAUNCH PROCESS');
  1618. // close agent pipes
  1619. foreach ($this->_agentPipes as $pipe) {
  1620. fflush($pipe);
  1621. fclose($pipe);
  1622. }
  1623. // close agent launching process
  1624. proc_close($this->_agentProcess);
  1625. $this->_agentProcess = null;
  1626. $this->_agentPipes = array();
  1627. $this->_debug('GPG-AGENT LAUNCH PROCESS CLOSED');
  1628. }
  1629. }
  1630. // }}}
  1631. // {{{ _closePipe()
  1632. /**
  1633. * Closes an opened pipe used to communicate with the GPG subprocess
  1634. *
  1635. * If the pipe is already closed, it is ignored. If the pipe is open, it
  1636. * is flushed and then closed.
  1637. *
  1638. * @param integer $pipeNumber the file descriptor number of the pipe to
  1639. * close.
  1640. *
  1641. * @return void
  1642. */
  1643. private function _closePipe($pipeNumber)
  1644. {
  1645. $pipeNumber = intval($pipeNumber);
  1646. if (array_key_exists($pipeNumber, $this->_openPipes)) {
  1647. fflush($this->_openPipes[$pipeNumber]);
  1648. fclose($this->_openPipes[$pipeNumber]);
  1649. unset($this->_openPipes[$pipeNumber]);
  1650. }
  1651. }
  1652. // }}}
  1653. // {{{ _closeIdleAgents()
  1654. /**
  1655. * Forces automatically started gpg-agent process to cleanup and exit
  1656. * within a minute.
  1657. *
  1658. * This is needed in GnuPG 2.1 where agents are started
  1659. * automatically by gpg process, not our code.
  1660. *
  1661. * @return void
  1662. */
  1663. private function _closeIdleAgents()
  1664. {
  1665. if ($this->_gpgconf) {
  1666. // before 2.1.13 --homedir wasn't supported, use env variable
  1667. $env = array('GNUPGHOME' => $this->_homedir);
  1668. $cmd = $this->_gpgconf . ' --kill gpg-agent';
  1669. if ($process = proc_open($cmd, array(), $pipes, null, $env)) {
  1670. proc_close($process);
  1671. }
  1672. }
  1673. }
  1674. // }}}
  1675. // {{{ _getBinary()
  1676. /**
  1677. * Gets the name of the GPG binary for the current operating system
  1678. *
  1679. * This method is called if the '<kbd>binary</kbd>' option is <i>not</i>
  1680. * specified when creating this driver.
  1681. *
  1682. * @return string the name of the GPG binary for the current operating
  1683. * system. If no suitable binary could be found, an empty
  1684. * string is returned.
  1685. */
  1686. private function _getBinary()
  1687. {
  1688. if ($binary = $this->_findBinary('gpg')) {
  1689. return $binary;
  1690. }
  1691. return $this->_findBinary('gpg2');
  1692. }
  1693. // }}}
  1694. // {{{ _getAgent()
  1695. /**
  1696. * Gets the name of the GPG-AGENT binary for the current operating system
  1697. *
  1698. * @return string the name of the GPG-AGENT binary for the current operating
  1699. * system. If no suitable binary could be found, an empty
  1700. * string is returned.
  1701. */
  1702. private function _getAgent()
  1703. {
  1704. return $this->_findBinary('gpg-agent');
  1705. }
  1706. // }}}
  1707. // {{{ _getGPGConf()
  1708. /**
  1709. * Gets the name of the GPGCONF binary for the current operating system
  1710. *
  1711. * @return string the name of the GPGCONF binary for the current operating
  1712. * system. If no suitable binary could be found, an empty
  1713. * string is returned.
  1714. */
  1715. private function _getGPGConf()
  1716. {
  1717. return $this->_findBinary('gpgconf');
  1718. }
  1719. // }}}
  1720. // {{{ _findBinary()
  1721. /**
  1722. * Gets the location of a binary for the current operating system
  1723. *
  1724. * @param string $name Name of a binary program
  1725. *
  1726. * @return string The location of the binary for the current operating
  1727. * system. If no suitable binary could be found, an empty
  1728. * string is returned.
  1729. */
  1730. private function _findBinary($name)
  1731. {
  1732. $binary = '';
  1733. if ($this->_isDarwin) {
  1734. $locations = array(
  1735. '/opt/local/bin/', // MacPorts
  1736. '/usr/local/bin/', // Mac GPG
  1737. '/sw/bin/', // Fink
  1738. '/usr/bin/'
  1739. );
  1740. } else {
  1741. $locations = array(
  1742. '/usr/bin/',
  1743. '/usr/local/bin/'
  1744. );
  1745. }
  1746. foreach ($locations as $location) {
  1747. if (is_executable($location . $name)) {
  1748. $binary = $location . $name;
  1749. break;
  1750. }
  1751. }
  1752. return $binary;
  1753. }
  1754. // }}}
  1755. // {{{ _getPinEntry()
  1756. /**
  1757. * Gets the location of the PinEntry script
  1758. *
  1759. * @return string the location of the PinEntry script.
  1760. */
  1761. private function _getPinEntry()
  1762. {
  1763. // Find PinEntry program depending on the way how the package is installed
  1764. $ds = DIRECTORY_SEPARATOR;
  1765. $root = __DIR__ . $ds . '..' . $ds . '..' . $ds;
  1766. $paths = array(
  1767. '@bin-dir@', // PEAR
  1768. $root . 'scripts', // Git
  1769. $root . 'bin', // Composer
  1770. );
  1771. foreach ($paths as $path) {
  1772. if (file_exists($path . $ds . 'crypt-gpg-pinentry')) {
  1773. return $path . $ds . 'crypt-gpg-pinentry';
  1774. }
  1775. }
  1776. }
  1777. // }}}
  1778. // {{{ _debug()
  1779. /**
  1780. * Displays debug text if debugging is turned on
  1781. *
  1782. * Debugging text is prepended with a debug identifier and echoed to stdout.
  1783. *
  1784. * @param string $text the debugging text to display.
  1785. *
  1786. * @return void
  1787. */
  1788. private function _debug($text)
  1789. {
  1790. if ($this->_debug) {
  1791. if (php_sapi_name() === 'cli') {
  1792. foreach (explode(PHP_EOL, $text) as $line) {
  1793. echo "Crypt_GPG DEBUG: ", $line, PHP_EOL;
  1794. }
  1795. } else if (is_callable($this->_debug)) {
  1796. call_user_func($this->_debug, $text);
  1797. } else {
  1798. // running on a web server, format debug output nicely
  1799. foreach (explode(PHP_EOL, $text) as $line) {
  1800. echo "Crypt_GPG DEBUG: <strong>", htmlspecialchars($line),
  1801. '</strong><br />', PHP_EOL;
  1802. }
  1803. }
  1804. }
  1805. }
  1806. // }}}
  1807. }
  1808. // }}}
  1809. ?>