comparison .vim/plugin/gnupg.vim @ 92:fa4f783acc35

New version of gnupg.vim
author Steve Huston <huston@srhuston.net>
date Tue, 30 Jul 2013 17:17:32 -0400
parents 2bea356b1032
children ab30008c9e93
comparison
equal deleted inserted replaced
91:9d5fcbb36232 92:fa4f783acc35
1 " Name: gnupg.vim 1 " Name: gnupg.vim
2 " Version: $Id: gnupg.vim 3026 2010-01-27 08:18:04Z mbr $ 2 " Last Change: 2012 May 31
3 " Author: Markus Braun <markus.braun@krawel.de> 3 " Maintainer: James McCoy <vega.james@gmail.com>
4 " Original Author: Markus Braun <markus.braun@krawel.de>
4 " Summary: Vim plugin for transparent editing of gpg encrypted files. 5 " Summary: Vim plugin for transparent editing of gpg encrypted files.
5 " Licence: This program is free software; you can redistribute it and/or 6 " License: This program is free software; you can redistribute it and/or
6 " modify it under the terms of the GNU General Public License. 7 " modify it under the terms of the GNU General Public License
7 " See http://www.gnu.org/copyleft/gpl.txt 8 " as published by the Free Software Foundation; either version
9 " 2 of the License, or (at your option) any later version.
10 " See http://www.gnu.org/copyleft/gpl-2.0.txt
8 " 11 "
9 " Section: Documentation {{{1 12 " Section: Documentation {{{1
10 " 13 "
11 " Description: {{{2 14 " Description: {{{2
12 " 15 "
13 " This script implements transparent editing of gpg encrypted files. The 16 " This script implements transparent editing of gpg encrypted files. The
14 " filename must have a ".gpg", ".pgp" or ".asc" suffix. When opening such 17 " filename must have a ".gpg", ".pgp" or ".asc" suffix. When opening such
15 " a file the content is decrypted, when opening a new file the script will 18 " a file the content is decrypted, when opening a new file the script will
16 " ask for the recipients of the encrypted file. The file content will be 19 " ask for the recipients of the encrypted file. The file content will be
17 " encrypted to all recipients before it is written. The script turns off 20 " encrypted to all recipients before it is written. The script turns off
18 " viminfo and swapfile to increase security. 21 " viminfo, swapfile, and undofile to increase security.
19 " 22 "
20 " Installation: {{{2 23 " Installation: {{{2
21 " 24 "
22 " Copy the gnupg.vim file to the $HOME/.vim/plugin directory. 25 " Copy the gnupg.vim file to the $HOME/.vim/plugin directory.
23 " Refer to ':help add-plugin', ':help add-global-plugin' and ':help 26 " Refer to ':help add-plugin', ':help add-global-plugin' and ':help
69 " 72 "
70 " g:GPGPreferSymmetric 73 " g:GPGPreferSymmetric
71 " If set to 1 symmetric encryption is preferred for new files. Defaults to 0. 74 " If set to 1 symmetric encryption is preferred for new files. Defaults to 0.
72 " 75 "
73 " g:GPGPreferArmor 76 " g:GPGPreferArmor
74 " If set to 1 armored data is preferred for new files. Defaults to 0. 77 " If set to 1 armored data is preferred for new files. Defaults to 0
78 " unless a "*.asc" file is being edited.
75 " 79 "
76 " g:GPGPreferSign 80 " g:GPGPreferSign
77 " If set to 1 signed data is preferred for new files. Defaults to 0. 81 " If set to 1 signed data is preferred for new files. Defaults to 0.
78 " 82 "
79 " g:GPGDefaultRecipients 83 " g:GPGDefaultRecipients
80 " If set, these recipients are used as defaults when no other recipient is 84 " If set, these recipients are used as defaults when no other recipient is
81 " defined. This variable is a Vim list. Default is unset. 85 " defined. This variable is a Vim list. Default is unset.
82 " 86 "
87 " g:GPGUsePipes
88 " If set to 1, use pipes instead of temporary files when interacting with
89 " gnupg. When set to 1, this can cause terminal-based gpg agents to not
90 " display correctly when prompting for passwords. Defaults to 0.
91 "
92 " g:GPGHomedir
93 " If set, specifies the directory that will be used for GPG's homedir.
94 " This corresponds to gpg's --homedir option. This variable is a Vim
95 " string.
96 "
83 " Known Issues: {{{2 97 " Known Issues: {{{2
84 " 98 "
85 " In some cases gvim can't decryt files 99 " In some cases gvim can't decrypt files
86 100
87 " This is caused by the fact that a running gvim has no TTY and thus gpg is 101 " This is caused by the fact that a running gvim has no TTY and thus gpg is
88 " not able to ask for the passphrase by itself. This is a problem for Windows 102 " not able to ask for the passphrase by itself. This is a problem for Windows
89 " and Linux versions of gvim and could not be solved unless a "terminal 103 " and Linux versions of gvim and could not be solved unless a "terminal
90 " emulation" is implemented for gvim. To circumvent this you have to use any 104 " emulation" is implemented for gvim. To circumvent this you have to use any
107 " - Mathieu Clabaut for inspirations through his vimspell.vim script. 121 " - Mathieu Clabaut for inspirations through his vimspell.vim script.
108 " - Richard Bronosky for patch to enable ".pgp" suffix. 122 " - Richard Bronosky for patch to enable ".pgp" suffix.
109 " - Erik Remmelzwaal for patch to enable windows support and patient beta 123 " - Erik Remmelzwaal for patch to enable windows support and patient beta
110 " testing. 124 " testing.
111 " - Lars Becker for patch to make gpg2 working. 125 " - Lars Becker for patch to make gpg2 working.
112 " - Thomas Arendsen Hein for patch to convert encoding of gpg output 126 " - Thomas Arendsen Hein for patch to convert encoding of gpg output.
113 " - Karl-Heinz Ruskowski for patch to fix unknown recipients and trust model 127 " - Karl-Heinz Ruskowski for patch to fix unknown recipients and trust model
114 " and patient beta testing. 128 " and patient beta testing.
115 " - Giel van Schijndel for patch to get GPG_TTY dynamically. 129 " - Giel van Schijndel for patch to get GPG_TTY dynamically.
116 " - Sebastian Luettich for patch to fix issue with symmetric encryption an set 130 " - Sebastian Luettich for patch to fix issue with symmetric encryption an set
117 " recipients. 131 " recipients.
118 " - Tim Swast for patch to generate signed files 132 " - Tim Swast for patch to generate signed files.
133 " - James Vega for patches for better '*.asc' handling, better filename
134 " escaping and better handling of multiple keyrings.
119 " 135 "
120 " Section: Plugin header {{{1 136 " Section: Plugin header {{{1
121 137
122 " guard against multiple loads {{{2 138 " guard against multiple loads {{{2
123 if (exists("g:loaded_gnupg") || &cp || exists("#BufReadPre#*.\(gpg\|asc\|pgp\)")) 139 if (exists("g:loaded_gnupg") || &cp || exists("#GnuPG"))
124 finish 140 finish
125 endif 141 endif
126 let g:loaded_gnupg = "$Revision: 3026 $" 142 let g:loaded_gnupg = '2.5'
143 let s:GPGInitRun = 0
127 144
128 " check for correct vim version {{{2 145 " check for correct vim version {{{2
129 if (v:version < 700) 146 if (v:version < 702)
130 echohl ErrorMsg | echo 'plugin gnupg.vim requires Vim version >= 7.0' | echohl None 147 echohl ErrorMsg | echo 'plugin gnupg.vim requires Vim version >= 7.2' | echohl None
131 finish 148 finish
132 endif 149 endif
133 150
134 " Section: Autocmd setup {{{1 151 " Section: Autocmd setup {{{1
135 152
136 augroup GnuPG 153 augroup GnuPG
137 autocmd! 154 autocmd!
138 155
139 " initialize the internal variables
140 autocmd BufNewFile,BufReadPre,FileReadPre *.\(gpg\|asc\|pgp\) call s:GPGInit()
141 " force the user to edit the recipient list if he opens a new file and public
142 " keys are preferred
143 autocmd BufNewFile *.\(gpg\|asc\|pgp\) if (exists("g:GPGPreferSymmetric") && g:GPGPreferSymmetric == 0) | call s:GPGEditRecipients() | endif
144 " do the decryption 156 " do the decryption
145 autocmd BufReadPost,FileReadPost *.\(gpg\|asc\|pgp\) call s:GPGDecrypt() 157 autocmd BufReadCmd *.\(gpg\|asc\|pgp\) call s:GPGInit(1)
158 autocmd BufReadCmd *.\(gpg\|asc\|pgp\) call s:GPGDecrypt(1)
159 autocmd BufReadCmd *.\(gpg\|asc\|pgp\) call s:GPGBufReadPost()
160 autocmd FileReadCmd *.\(gpg\|asc\|pgp\) call s:GPGInit(0)
161 autocmd FileReadCmd *.\(gpg\|asc\|pgp\) call s:GPGDecrypt(0)
146 162
147 " convert all text to encrypted text before writing 163 " convert all text to encrypted text before writing
148 autocmd BufWritePre,FileWritePre *.\(gpg\|asc\|pgp\) call s:GPGEncrypt() 164 autocmd BufWriteCmd *.\(gpg\|asc\|pgp\) call s:GPGBufWritePre()
149 " undo the encryption so we are back in the normal text, directly 165 autocmd BufWriteCmd,FileWriteCmd *.\(gpg\|asc\|pgp\) call s:GPGInit(0)
150 " after the file has been written. 166 autocmd BufWriteCmd,FileWriteCmd *.\(gpg\|asc\|pgp\) call s:GPGEncrypt()
151 autocmd BufWritePost,FileWritePost *.\(gpg\|asc\|pgp\) call s:GPGEncryptPost()
152 167
153 " cleanup on leaving vim 168 " cleanup on leaving vim
154 autocmd VimLeave *.\(gpg\|asc\|pgp\) call s:GPGCleanup() 169 autocmd VimLeave *.\(gpg\|asc\|pgp\) call s:GPGCleanup()
155 augroup END 170 augroup END
156 171
157 " Section: Constants {{{1 172 " Section: Constants {{{1
158 173
159 let s:GPGMagicString = "\t \t" 174 let s:GPGMagicString = "\t \t"
175 let s:keyPattern = '\%(0x\)\=[[:xdigit:]]\{8,16}'
160 176
161 " Section: Highlight setup {{{1 177 " Section: Highlight setup {{{1
162 178
163 highlight default link GPGWarning WarningMsg 179 highlight default link GPGWarning WarningMsg
164 highlight default link GPGError ErrorMsg 180 highlight default link GPGError ErrorMsg
165 highlight default link GPGHighlightUnknownRecipient ErrorMsg 181 highlight default link GPGHighlightUnknownRecipient ErrorMsg
166 182
167 " Section: Functions {{{1 183 " Section: Functions {{{1
168 184
169 " Function: s:GPGInit() {{{2 185 " Function: s:GPGInit(bufread) {{{2
170 " 186 "
171 " initialize the plugin 187 " initialize the plugin
172 " 188 " The bufread argument specifies whether this was called due to BufReadCmd
173 function s:GPGInit() 189 "
174 call s:GPGDebug(3, ">>>>>>>> Entering s:GPGInit()") 190 function s:GPGInit(bufread)
175 191 call s:GPGDebug(3, printf(">>>>>>>> Entering s:GPGInit(%d)", a:bufread))
176 " first make sure nothing is written to ~/.viminfo while editing 192
177 " an encrypted file. 193 " For FileReadCmd, we're reading the contents into another buffer. If that
178 set viminfo= 194 " buffer is also destined to be encrypted, then these settings will have
179 195 " already been set, otherwise don't set them since it limits the
180 " we don't want a swap file, as it writes unencrypted data to disk 196 " functionality of the cleartext buffer.
181 set noswapfile 197 if a:bufread
198 " we don't want a swap file, as it writes unencrypted data to disk
199 setl noswapfile
200
201 " if persistent undo is present, disable it for this buffer
202 if exists('+undofile')
203 setl noundofile
204 endif
205
206 " first make sure nothing is written to ~/.viminfo while editing
207 " an encrypted file.
208 set viminfo=
209 endif
210
211 " the rest only has to be run once
212 if s:GPGInitRun
213 return
214 endif
182 215
183 " check what gpg command to use 216 " check what gpg command to use
184 if (!exists("g:GPGExecutable")) 217 if (!exists("g:GPGExecutable"))
185 let g:GPGExecutable = "gpg --trust-model always" 218 let g:GPGExecutable = "gpg --trust-model always"
186 endif 219 endif
195 let g:GPGPreferSymmetric = 0 228 let g:GPGPreferSymmetric = 0
196 endif 229 endif
197 230
198 " check if armored files are preferred 231 " check if armored files are preferred
199 if (!exists("g:GPGPreferArmor")) 232 if (!exists("g:GPGPreferArmor"))
200 let g:GPGPreferArmor = 0 233 " .asc files should be armored as that's what the extension is used for
234 if expand('<afile>') =~ '\.asc$'
235 let g:GPGPreferArmor = 1
236 else
237 let g:GPGPreferArmor = 0
238 endif
201 endif 239 endif
202 240
203 " check if signed files are preferred 241 " check if signed files are preferred
204 if (!exists("g:GPGPreferSign")) 242 if (!exists("g:GPGPreferSign"))
205 let g:GPGPreferSign = 0 243 let g:GPGPreferSign = 0
206 endif 244 endif
207 245
208 " start with empty default recipients if none is defined so far 246 " start with empty default recipients if none is defined so far
209 if (!exists("g:GPGDefaultRecipients")) 247 if (!exists("g:GPGDefaultRecipients"))
210 let g:GPGDefaultRecipients = [] 248 let g:GPGDefaultRecipients = []
249 endif
250
251 " prefer not to use pipes since it can garble gpg agent display
252 if (!exists("g:GPGUsePipes"))
253 let g:GPGUsePipes = 0
254 endif
255
256 " allow alternate gnupg homedir
257 if (!exists('g:GPGHomedir'))
258 let g:GPGHomedir = ''
211 endif 259 endif
212 260
213 " print version 261 " print version
214 call s:GPGDebug(1, "gnupg.vim ". g:loaded_gnupg) 262 call s:GPGDebug(1, "gnupg.vim ". g:loaded_gnupg)
215 263
228 let s:GPGCommand = g:GPGExecutable . " --use-agent" 276 let s:GPGCommand = g:GPGExecutable . " --use-agent"
229 else 277 else
230 let s:GPGCommand = g:GPGExecutable . " --no-use-agent" 278 let s:GPGCommand = g:GPGExecutable . " --no-use-agent"
231 endif 279 endif
232 280
233 " don't use tty in gvim 281 " don't use tty in gvim except for windows: we get their a tty for free.
234 " FIXME find a better way to avoid an error. 282 " FIXME find a better way to avoid an error.
235 " with this solution only --use-agent will work 283 " with this solution only --use-agent will work
236 if (has("gui_running")) 284 if (has("gui_running") && !has("gui_win32"))
237 let s:GPGCommand = s:GPGCommand . " --no-tty" 285 let s:GPGCommand = s:GPGCommand . " --no-tty"
238 endif 286 endif
239 287
240 " setup shell environment for unix and windows 288 " setup shell environment for unix and windows
241 let s:shellredirsave = &shellredir 289 let s:shellredirsave = &shellredir
242 let s:shellsave = &shell 290 let s:shellsave = &shell
291 let s:shelltempsave = &shelltemp
292 " noshelltemp isn't currently supported on Windows, but it doesn't cause any
293 " errors and this future proofs us against requiring changes if Windows
294 " gains noshelltemp functionality
295 let s:shelltemp = !g:GPGUsePipes
243 if (has("unix")) 296 if (has("unix"))
244 " unix specific settings 297 " unix specific settings
245 let s:shellredir = ">%s 2>&1" 298 let s:shellredir = ">%s 2>&1"
246 let s:shell = '/bin/sh' 299 let s:shell = '/bin/sh'
247 let s:stderrredirnull = '2>/dev/null' 300 let s:stderrredirnull = '2>/dev/null'
253 let s:stderrredirnull = '2>nul' 306 let s:stderrredirnull = '2>nul'
254 endif 307 endif
255 308
256 call s:GPGDebug(3, "shellredirsave: " . s:shellredirsave) 309 call s:GPGDebug(3, "shellredirsave: " . s:shellredirsave)
257 call s:GPGDebug(3, "shellsave: " . s:shellsave) 310 call s:GPGDebug(3, "shellsave: " . s:shellsave)
311 call s:GPGDebug(3, "shelltempsave: " . s:shelltempsave)
258 312
259 call s:GPGDebug(3, "shell: " . s:shell) 313 call s:GPGDebug(3, "shell: " . s:shell)
260 call s:GPGDebug(3, "shellcmdflag: " . &shellcmdflag) 314 call s:GPGDebug(3, "shellcmdflag: " . &shellcmdflag)
261 call s:GPGDebug(3, "shellxquote: " . &shellxquote) 315 call s:GPGDebug(3, "shellxquote: " . &shellxquote)
262 call s:GPGDebug(3, "shellredir: " . s:shellredir) 316 call s:GPGDebug(3, "shellredir: " . s:shellredir)
263 call s:GPGDebug(3, "stderrredirnull: " . s:stderrredirnull) 317 call s:GPGDebug(3, "stderrredirnull: " . s:stderrredirnull)
264 318
265 call s:GPGDebug(3, "shell implementation: " . resolve(s:shell)) 319 call s:GPGDebug(3, "shell implementation: " . resolve(s:shell))
266 320
267 " find the supported algorithms 321 " find the supported algorithms
268 let commandline = s:GPGCommand . " --version" 322 let output = s:GPGSystem({ 'level': 2, 'args': '--version' })
269 call s:GPGDebug(2, "command: ". commandline)
270 let &shellredir = s:shellredir
271 let &shell = s:shell
272 let output = system(commandline)
273 let &shellredir = s:shellredirsave
274 let &shell = s:shellsave
275 call s:GPGDebug(2, "output: ". output)
276 323
277 let s:GPGPubkey = substitute(output, ".*Pubkey: \\(.\\{-}\\)\n.*", "\\1", "") 324 let s:GPGPubkey = substitute(output, ".*Pubkey: \\(.\\{-}\\)\n.*", "\\1", "")
278 let s:GPGCipher = substitute(output, ".*Cipher: \\(.\\{-}\\)\n.*", "\\1", "") 325 let s:GPGCipher = substitute(output, ".*Cipher: \\(.\\{-}\\)\n.*", "\\1", "")
279 let s:GPGHash = substitute(output, ".*Hash: \\(.\\{-}\\)\n.*", "\\1", "") 326 let s:GPGHash = substitute(output, ".*Hash: \\(.\\{-}\\)\n.*", "\\1", "")
280 let s:GPGCompress = substitute(output, ".*Compress.\\{-}: \\(.\\{-}\\)\n.*", "\\1", "") 327 let s:GPGCompress = substitute(output, ".*Compress.\\{-}: \\(.\\{-}\\)\n.*", "\\1", "")
282 call s:GPGDebug(2, "public key algorithms: " . s:GPGPubkey) 329 call s:GPGDebug(2, "public key algorithms: " . s:GPGPubkey)
283 call s:GPGDebug(2, "cipher algorithms: " . s:GPGCipher) 330 call s:GPGDebug(2, "cipher algorithms: " . s:GPGCipher)
284 call s:GPGDebug(2, "hashing algorithms: " . s:GPGHash) 331 call s:GPGDebug(2, "hashing algorithms: " . s:GPGHash)
285 call s:GPGDebug(2, "compression algorithms: " . s:GPGCompress) 332 call s:GPGDebug(2, "compression algorithms: " . s:GPGCompress)
286 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGInit()") 333 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGInit()")
334 let s:GPGInitRun = 1
287 endfunction 335 endfunction
288 336
289 " Function: s:GPGCleanup() {{{2 337 " Function: s:GPGCleanup() {{{2
290 " 338 "
291 " cleanup on leaving vim 339 " cleanup on leaving vim
298 redraw! 346 redraw!
299 347
300 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGCleanup()") 348 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGCleanup()")
301 endfunction 349 endfunction
302 350
303 " Function: s:GPGDecrypt() {{{2 351 " Function: s:GPGDecrypt(bufread) {{{2
304 " 352 "
305 " decrypt the buffer and find all recipients of the encrypted file 353 " decrypt the buffer and find all recipients of the encrypted file
306 " 354 " The bufread argument specifies whether this was called due to BufReadCmd
307 function s:GPGDecrypt() 355 "
308 call s:GPGDebug(3, ">>>>>>>> Entering s:GPGDecrypt()") 356 function s:GPGDecrypt(bufread)
309 357 call s:GPGDebug(3, printf(">>>>>>>> Entering s:GPGDecrypt(%d)", a:bufread))
310 " switch to binary mode to read the encrypted file
311 set bin
312 358
313 " get the filename of the current buffer 359 " get the filename of the current buffer
314 let filename = escape(expand("%:p"), '\"') 360 let filename = expand("<afile>:p")
315 361
316 " clear GPGEncrypted, GPGRecipients and GPGOptions 362 " clear GPGRecipients and GPGOptions
363 let b:GPGRecipients = g:GPGDefaultRecipients
364 let b:GPGOptions = []
365
366 " File doesn't exist yet, so nothing to decrypt
367 if empty(glob(filename))
368 return
369 endif
370
371 " Only let this if the file actually exists, otherwise GPG functionality
372 " will be disabled when editing a buffer that doesn't yet have a backing
373 " file
317 let b:GPGEncrypted = 0 374 let b:GPGEncrypted = 0
318 let b:GPGRecipients = []
319 let b:GPGOptions = []
320 375
321 " find the recipients of the file 376 " find the recipients of the file
322 let commandline = s:GPGCommand . " --verbose --decrypt --list-only --dry-run --batch --no-use-agent --logger-fd 1 \"" . filename . "\"" 377 let cmd = { 'level': 3 }
323 call s:GPGDebug(3, "command: " . commandline) 378 let cmd.args = '--verbose --decrypt --list-only --dry-run --batch --no-use-agent --logger-fd 1 ' . shellescape(filename)
324 let &shellredir = s:shellredir 379 let output = s:GPGSystem(cmd)
325 let &shell = s:shell 380
326 let output = system(commandline) 381 " Suppress the "N more lines" message when editing a file, not when reading
327 let &shellredir = s:shellredirsave 382 " the contents of a file into a buffer
328 let &shell = s:shellsave 383 let silent = a:bufread ? 'silent ' : ''
329 call s:GPGDebug(3, "output: ". output) 384
330 385 let asymmPattern = 'gpg: public key is ' . s:keyPattern
331 " check if the file is symmetric/asymmetric encrypted 386 " check if the file is symmetric/asymmetric encrypted
332 if (match(output, "gpg: encrypted with [[:digit:]]\\+ passphrase") >= 0) 387 if (match(output, "gpg: encrypted with [[:digit:]]\\+ passphrase") >= 0)
333 " file is symmetric encrypted 388 " file is symmetric encrypted
334 let b:GPGEncrypted = 1 389 let b:GPGEncrypted = 1
335 call s:GPGDebug(1, "this file is symmetric encrypted") 390 call s:GPGDebug(1, "this file is symmetric encrypted")
345 echohl GPGWarning 400 echohl GPGWarning
346 echom "The cipher " . cipher . " is not known by the local gpg command. Using default!" 401 echom "The cipher " . cipher . " is not known by the local gpg command. Using default!"
347 echo 402 echo
348 echohl None 403 echohl None
349 endif 404 endif
350 elseif (match(output, "gpg: public key is [[:xdigit:]]\\{8}") >= 0) 405 elseif (match(output, asymmPattern) >= 0)
351 " file is asymmetric encrypted 406 " file is asymmetric encrypted
352 let b:GPGEncrypted = 1 407 let b:GPGEncrypted = 1
353 call s:GPGDebug(1, "this file is asymmetric encrypted") 408 call s:GPGDebug(1, "this file is asymmetric encrypted")
354 409
355 let b:GPGOptions += ["encrypt"] 410 let b:GPGOptions += ["encrypt"]
356 411
357 " find the used public keys 412 " find the used public keys
358 let start = match(output, "gpg: public key is [[:xdigit:]]\\{8}") 413 let start = match(output, asymmPattern)
359 while (start >= 0) 414 while (start >= 0)
360 let start = start + strlen("gpg: public key is ") 415 let start = start + strlen("gpg: public key is ")
361 let recipient = strpart(output, start, 8) 416 let recipient = matchstr(output, s:keyPattern, start)
362 call s:GPGDebug(1, "recipient is " . recipient) 417 call s:GPGDebug(1, "recipient is " . recipient)
363 let name = s:GPGNameToID(recipient) 418 let name = s:GPGNameToID(recipient)
364 if (strlen(name) > 0) 419 if (strlen(name) > 0)
365 let b:GPGRecipients += [name] 420 let b:GPGRecipients += [name]
366 call s:GPGDebug(1, "name of recipient is " . name) 421 call s:GPGDebug(1, "name of recipient is " . name)
368 let b:GPGRecipients += [recipient] 423 let b:GPGRecipients += [recipient]
369 echohl GPGWarning 424 echohl GPGWarning
370 echom "The recipient \"" . recipient . "\" is not in your public keyring!" 425 echom "The recipient \"" . recipient . "\" is not in your public keyring!"
371 echohl None 426 echohl None
372 end 427 end
373 let start = match(output, "gpg: public key is [[:xdigit:]]\\{8}", start) 428 let start = match(output, asymmPattern, start)
374 endwhile 429 endwhile
375 else 430 else
376 " file is not encrypted 431 " file is not encrypted
377 let b:GPGEncrypted = 0 432 let b:GPGEncrypted = 0
378 call s:GPGDebug(1, "this file is not encrypted") 433 call s:GPGDebug(1, "this file is not encrypted")
379 echohl GPGWarning 434 echohl GPGWarning
380 echom "File is not encrypted, all GPG functions disabled!" 435 echom "File is not encrypted, all GPG functions disabled!"
381 echohl None 436 echohl None
382 set nobin 437 exe printf('%sr %s', silent, fnameescape(filename))
383 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGDecrypt()") 438 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGDecrypt()")
384 return 439 return
385 endif 440 endif
386 441
387 " check if the message is armored 442 " check if the message is armored
392 447
393 " finally decrypt the buffer content 448 " finally decrypt the buffer content
394 " since even with the --quiet option passphrase typos will be reported, 449 " since even with the --quiet option passphrase typos will be reported,
395 " we must redirect stderr (using shell temporarily) 450 " we must redirect stderr (using shell temporarily)
396 call s:GPGDebug(1, "decrypting file") 451 call s:GPGDebug(1, "decrypting file")
397 let commandline = "'[,']!" . s:GPGCommand . " --quiet --decrypt " . s:stderrredirnull 452 let cmd = { 'level': 1, 'ex': silent . 'r !' }
398 call s:GPGDebug(1, "command: " . commandline) 453 let cmd.args = '--quiet --decrypt ' . shellescape(filename, 1)
399 let &shellredir = s:shellredir 454 call s:GPGExecute(cmd)
400 let &shell = s:shell 455
401 execute commandline
402 let &shellredir = s:shellredirsave
403 let &shell = s:shellsave
404 if (v:shell_error) " message could not be decrypted 456 if (v:shell_error) " message could not be decrypted
405 silent u
406 echohl GPGError 457 echohl GPGError
407 let blackhole = input("Message could not be decrypted! (Press ENTER)") 458 let blackhole = input("Message could not be decrypted! (Press ENTER)")
408 echohl None 459 echohl None
409 bwipeout 460 " Only wipeout the buffer if we were creating one to start with.
410 set nobin 461 " FileReadCmd just reads the content into the existing buffer
462 if a:bufread
463 silent bwipeout!
464 endif
411 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGDecrypt()") 465 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGDecrypt()")
412 return 466 return
413 endif 467 endif
414 468
415 " turn off binary mode
416 set nobin
417
418 " call the autocommand for the file minus .gpg$
419 execute ":doautocmd BufReadPost " . escape(expand("%:r"), ' *?\"'."'")
420 call s:GPGDebug(2, "called autocommand for " . escape(expand("%:r"), ' *?\"'."'"))
421
422 " refresh screen 469 " refresh screen
423 redraw! 470 redraw!
424 471
425 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGDecrypt()") 472 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGDecrypt()")
426 endfunction 473 endfunction
427 474
475 " Function: s:GPGBufReadPost() {{{2
476 "
477 " Handle functionality specific to opening a file for reading rather than
478 " reading the contents of a file into a buffer
479 "
480 function s:GPGBufReadPost()
481 call s:GPGDebug(3, ">>>>>>>> Entering s:GPGBufReadPost()")
482 " In order to make :undo a no-op immediately after the buffer is read,
483 " we need to do this dance with 'undolevels'. Actually discarding the undo
484 " history requires performing a change after setting 'undolevels' to -1 and,
485 " luckily, we have one we need to do (delete the extra line from the :r
486 " command)
487 let levels = &undolevels
488 set undolevels=-1
489 silent 1delete
490 let &undolevels = levels
491 " call the autocommand for the file minus .gpg$
492 silent execute ':doautocmd BufReadPost ' . fnameescape(expand('<afile>:r'))
493 call s:GPGDebug(2, 'called autocommand for ' . fnameescape(expand('<afile>:r')))
494 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGBufReadPost()")
495 endfunction
496
497 " Function: s:GPGBufWritePre() {{{2
498 "
499 " Handle functionality specific to saving an entire buffer to a file rather
500 " than saving a partial buffer
501 "
502 function s:GPGBufWritePre()
503 call s:GPGDebug(3, ">>>>>>>> Entering s:GPGBufWritePre()")
504 " call the autocommand for the file minus .gpg$
505 silent execute ':doautocmd BufWritePre ' . fnameescape(expand('<afile>:r'))
506 call s:GPGDebug(2, 'called autocommand for ' . fnameescape(expand('<afile>:r')))
507 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGBufWritePre()")
508 endfunction
509
428 " Function: s:GPGEncrypt() {{{2 510 " Function: s:GPGEncrypt() {{{2
429 " 511 "
430 " encrypts the buffer to all previous recipients 512 " encrypts the buffer to all previous recipients
431 " 513 "
432 function s:GPGEncrypt() 514 function s:GPGEncrypt()
433 call s:GPGDebug(3, ">>>>>>>> Entering s:GPGEncrypt()") 515 call s:GPGDebug(3, ">>>>>>>> Entering s:GPGEncrypt()")
434
435 " save window view
436 let s:GPGWindowView = winsaveview()
437 call s:GPGDebug(2, "saved window view " . string(s:GPGWindowView))
438 516
439 " store encoding and switch to a safe one 517 " store encoding and switch to a safe one
440 if (&fileencoding != &encoding) 518 if (&fileencoding != &encoding)
441 let s:GPGEncoding = &encoding 519 let s:GPGEncoding = &encoding
442 let &encoding = &fileencoding 520 let &encoding = &fileencoding
444 else 522 else
445 let s:GPGEncoding = "" 523 let s:GPGEncoding = ""
446 call s:GPGDebug(2, "encoding and fileencoding are the same (\"" . &encoding . "\"), not switching") 524 call s:GPGDebug(2, "encoding and fileencoding are the same (\"" . &encoding . "\"), not switching")
447 endif 525 endif
448 526
449 " switch buffer to binary mode
450 set bin
451
452 " guard for unencrypted files 527 " guard for unencrypted files
453 if (!exists("b:GPGEncrypted") || b:GPGEncrypted == 0) 528 if (exists("b:GPGEncrypted") && b:GPGEncrypted == 0)
454 echohl GPGError 529 echohl GPGError
455 let blackhole = input("Message could not be encrypted! File might be empty! (Press ENTER)") 530 let blackhole = input("Message could not be encrypted! (Press ENTER)")
456 echohl None 531 echohl None
457 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEncrypt()") 532 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEncrypt()")
458 return 533 return
459 endif 534 endif
460 535
480 let options = "" 555 let options = ""
481 for option in b:GPGOptions 556 for option in b:GPGOptions
482 let options = options . " --" . option . " " 557 let options = options . " --" . option . " "
483 endfor 558 endfor
484 559
560 if (!exists('b:GPGRecipients'))
561 let b:GPGRecipients = []
562 endif
563
485 " check here again if all recipients are available in the keyring 564 " check here again if all recipients are available in the keyring
486 let [ recipients, unknownrecipients ] = s:GPGCheckRecipients(b:GPGRecipients) 565 let [ recipients, unknownrecipients ] = s:GPGCheckRecipients(b:GPGRecipients)
487 566
488 " check if there are unknown recipients and warn 567 " check if there are unknown recipients and warn
489 if (len(unknownrecipients) > 0) 568 if (len(unknownrecipients) > 0)
499 " built list of recipients 578 " built list of recipients
500 if (len(recipients) > 0) 579 if (len(recipients) > 0)
501 for gpgid in recipients 580 for gpgid in recipients
502 let options = options . " -r " . gpgid 581 let options = options . " -r " . gpgid
503 endfor 582 endfor
504 else
505 if (match(b:GPGOptions, "encrypt") >= 0)
506 echohl GPGError
507 echom "There are no recipients!!"
508 echom "Please use GPGEditRecipients to correct!!"
509 echo
510 echohl None
511 endif
512 endif 583 endif
513 584
514 " encrypt the buffer 585 " encrypt the buffer
515 let commandline = "'[,']!" . s:GPGCommand . " --quiet --no-encrypt-to " . options . " " . s:stderrredirnull 586 let destfile = tempname()
516 call s:GPGDebug(1, "command: " . commandline) 587 let cmd = { 'level': 1, 'ex': "'[,']w !" }
517 let &shellredir = s:shellredir 588 let cmd.args = '--quiet --no-encrypt-to ' . options
518 let &shell = s:shell 589 let cmd.redirect = '>' . shellescape(destfile, 1)
519 silent execute commandline 590 call s:GPGExecute(cmd)
520 let &shellredir = s:shellredirsave
521 let &shell = s:shellsave
522 if (v:shell_error) " message could not be encrypted
523 " delete content of the buffer to be sure no data is written unencrypted
524 " content will be recovered in GPGEncryptPost()
525 silent normal! 1GdG
526
527 echohl GPGError
528 let blackhole = input("Message could not be encrypted! File might be empty! (Press ENTER)")
529 echohl None
530 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEncrypt()")
531 return
532 endif
533
534 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEncrypt()")
535 endfunction
536
537 " Function: s:GPGEncryptPost() {{{2
538 "
539 " undo changes don by encrypt, after writing
540 "
541 function s:GPGEncryptPost()
542 call s:GPGDebug(3, ">>>>>>>> Entering s:GPGEncryptPost()")
543
544 " guard for unencrypted files
545 if (exists("b:GPGEncrypted") && b:GPGEncrypted == 0)
546 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEncryptPost()")
547 return
548 endif
549
550 " undo encryption of buffer content
551 silent u
552
553 " switch back from binary mode
554 set nobin
555 591
556 " restore encoding 592 " restore encoding
557 if (s:GPGEncoding != "") 593 if (s:GPGEncoding != "")
558 let &encoding = s:GPGEncoding 594 let &encoding = s:GPGEncoding
559 call s:GPGDebug(2, "restored encoding \"" . &encoding . "\"") 595 call s:GPGDebug(2, "restored encoding \"" . &encoding . "\"")
560 endif 596 endif
561 597
562 " restore window view 598 if (v:shell_error) " message could not be encrypted
563 call winrestview(s:GPGWindowView) 599 " Command failed, so clean up the tempfile
564 call s:GPGDebug(2, "restored window view" . string(s:GPGWindowView)) 600 call delete(destfile)
565 601 echohl GPGError
566 " refresh screen 602 let blackhole = input("Message could not be encrypted! (Press ENTER)")
567 redraw! 603 echohl None
568 604 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEncrypt()")
569 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEncryptPost()") 605 return
606 endif
607
608 call rename(destfile, resolve(expand('<afile>')))
609 setl nomodified
610 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEncrypt()")
570 endfunction 611 endfunction
571 612
572 " Function: s:GPGViewRecipients() {{{2 613 " Function: s:GPGViewRecipients() {{{2
573 " 614 "
574 " echo the recipients 615 " echo the recipients
636 let editbuffername = "GPGRecipients_" . buffername 677 let editbuffername = "GPGRecipients_" . buffername
637 678
638 " check if this buffer exists 679 " check if this buffer exists
639 if (!bufexists(editbuffername)) 680 if (!bufexists(editbuffername))
640 " create scratch buffer 681 " create scratch buffer
641 execute 'silent! split ' . escape(editbuffername, ' *?\"'."'") 682 execute 'silent! split ' . fnameescape(editbuffername)
642 683
643 " add a autocommand to regenerate the recipients after a write 684 " add a autocommand to regenerate the recipients after a write
644 autocmd BufHidden,BufUnload,BufWriteCmd <buffer> call s:GPGFinishRecipientsBuffer() 685 autocmd BufHidden,BufUnload,BufWriteCmd <buffer> call s:GPGFinishRecipientsBuffer()
645 else 686 else
646 if (bufwinnr(editbuffername) >= 0) 687 if (bufwinnr(editbuffername) >= 0)
647 " switch to scratch buffer window 688 " switch to scratch buffer window
648 execute 'silent! ' . bufwinnr(editbuffername) . "wincmd w" 689 execute 'silent! ' . bufwinnr(editbuffername) . "wincmd w"
649 else 690 else
650 " split scratch buffer window 691 " split scratch buffer window
651 execute 'silent! sbuffer ' . escape(editbuffername, ' *?\"'."'") 692 execute 'silent! sbuffer ' . fnameescape(editbuffername)
652 693
653 " add a autocommand to regenerate the recipients after a write 694 " add a autocommand to regenerate the recipients after a write
654 autocmd BufHidden,BufUnload,BufWriteCmd <buffer> call s:GPGFinishRecipientsBuffer() 695 autocmd BufHidden,BufUnload,BufWriteCmd <buffer> call s:GPGFinishRecipientsBuffer()
655 endif 696 endif
656 697
657 " empty the buffer 698 " empty the buffer
658 silent normal! 1GdG 699 silent %delete
659 endif 700 endif
660 701
661 " Mark the buffer as a scratch buffer 702 " Mark the buffer as a scratch buffer
662 setlocal buftype=acwrite 703 setlocal buftype=acwrite
663 setlocal bufhidden=hide 704 setlocal bufhidden=hide
700 741
701 " put the unknown recipients in the scratch buffer 742 " put the unknown recipients in the scratch buffer
702 let syntaxPattern = "\\(nonexxistinwordinthisbuffer" 743 let syntaxPattern = "\\(nonexxistinwordinthisbuffer"
703 for name in unknownrecipients 744 for name in unknownrecipients
704 let name = "!" . name 745 let name = "!" . name
705 let syntaxPattern = syntaxPattern . "\\|" . name 746 let syntaxPattern = syntaxPattern . "\\|" . fnameescape(name)
706 silent put =name 747 silent put =name
707 endfor 748 endfor
708 let syntaxPattern = syntaxPattern . "\\)" 749 let syntaxPattern = syntaxPattern . "\\)"
709 750
710 " define highlight 751 " define highlight
718 highlight clear GPGComment 759 highlight clear GPGComment
719 highlight link GPGComment Comment 760 highlight link GPGComment Comment
720 endif 761 endif
721 762
722 " delete the empty first line 763 " delete the empty first line
723 silent normal! 1Gdd 764 silent 1delete
724 765
725 " jump to the first recipient 766 " jump to the first recipient
726 silent normal! G 767 silent $
727 768
728 endif 769 endif
729 770
730 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEditRecipients()") 771 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGEditRecipients()")
731 endfunction 772 endfunction
753 endif 794 endif
754 795
755 " delete the autocommand 796 " delete the autocommand
756 autocmd! * <buffer> 797 autocmd! * <buffer>
757 798
758
759 " get the recipients from the scratch buffer 799 " get the recipients from the scratch buffer
760 let recipients = [] 800 let recipients = []
761 let lines = getline(1,"$") 801 let lines = getline(1,"$")
762 for recipient in lines 802 for recipient in lines
763 " delete all text after magic string 803 let matches = matchlist(recipient, '^\(.\{-}\)\%(' . s:GPGMagicString . '(ID:\s\+\(' . s:keyPattern . '\)\s\+.*\)\=$')
764 let recipient = substitute(recipient, s:GPGMagicString . ".*$", "", "") 804
805 let recipient = matches[2] ? matches[2] : matches[1]
765 806
766 " delete all spaces at beginning and end of the recipient 807 " delete all spaces at beginning and end of the recipient
767 " also delete a '!' at the beginning of the recipient 808 " also delete a '!' at the beginning of the recipient
768 let recipient = substitute(recipient, "^[[:space:]!]*\\(.\\{-}\\)[[:space:]]*$", "\\1", "") 809 let recipient = substitute(recipient, "^[[:space:]!]*\\(.\\{-}\\)[[:space:]]*$", "\\1", "")
769 810
800 echom 'There are no known recipients!' 841 echom 'There are no known recipients!'
801 echohl None 842 echohl None
802 endif 843 endif
803 844
804 " reset modified flag 845 " reset modified flag
805 set nomodified 846 setl nomodified
806 847
807 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGFinishRecipientsBuffer()") 848 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGFinishRecipientsBuffer()")
808 endfunction 849 endfunction
809 850
810 " Function: s:GPGViewOptions() {{{2 851 " Function: s:GPGViewOptions() {{{2
858 let editbuffername = "GPGOptions_" . buffername 899 let editbuffername = "GPGOptions_" . buffername
859 900
860 " check if this buffer exists 901 " check if this buffer exists
861 if (!bufexists(editbuffername)) 902 if (!bufexists(editbuffername))
862 " create scratch buffer 903 " create scratch buffer
863 execute 'silent! split ' . escape(editbuffername, ' *?\"'."'") 904 execute 'silent! split ' . fnameescape(editbuffername)
864 905
865 " add a autocommand to regenerate the options after a write 906 " add a autocommand to regenerate the options after a write
866 autocmd BufHidden,BufUnload,BufWriteCmd <buffer> call s:GPGFinishOptionsBuffer() 907 autocmd BufHidden,BufUnload,BufWriteCmd <buffer> call s:GPGFinishOptionsBuffer()
867 else 908 else
868 if (bufwinnr(editbuffername) >= 0) 909 if (bufwinnr(editbuffername) >= 0)
869 " switch to scratch buffer window 910 " switch to scratch buffer window
870 execute 'silent! ' . bufwinnr(editbuffername) . "wincmd w" 911 execute 'silent! ' . bufwinnr(editbuffername) . "wincmd w"
871 else 912 else
872 " split scratch buffer window 913 " split scratch buffer window
873 execute 'silent! sbuffer ' . escape(editbuffername, ' *?\"'."'") 914 execute 'silent! sbuffer ' . fnameescape(editbuffername)
874 915
875 " add a autocommand to regenerate the options after a write 916 " add a autocommand to regenerate the options after a write
876 autocmd BufHidden,BufUnload,BufWriteCmd <buffer> call s:GPGFinishOptionsBuffer() 917 autocmd BufHidden,BufUnload,BufWriteCmd <buffer> call s:GPGFinishOptionsBuffer()
877 endif 918 endif
878 919
879 " empty the buffer 920 " empty the buffer
880 silent normal! 1GdG 921 silent %delete
881 endif 922 endif
882 923
883 " Mark the buffer as a scratch buffer 924 " Mark the buffer as a scratch buffer
884 setlocal buftype=nofile 925 setlocal buftype=nofile
885 setlocal noswapfile 926 setlocal noswapfile
907 for option in options 948 for option in options
908 silent put =option 949 silent put =option
909 endfor 950 endfor
910 951
911 " delete the empty first line 952 " delete the empty first line
912 silent normal! 1Gdd 953 silent 1delete
913 954
914 " jump to the first option 955 " jump to the first option
915 silent normal! G 956 silent $
916 957
917 " define highlight 958 " define highlight
918 if (has("syntax") && exists("g:syntax_on")) 959 if (has("syntax") && exists("g:syntax_on"))
919 syntax match GPGComment "^GPG:.*$" 960 syntax match GPGComment "^GPG:.*$"
920 highlight clear GPGComment 961 highlight clear GPGComment
973 " as modified 1014 " as modified
974 call setbufvar(b:GPGCorrespondingTo, "GPGOptions", options) 1015 call setbufvar(b:GPGCorrespondingTo, "GPGOptions", options)
975 call setbufvar(b:GPGCorrespondingTo, "&mod", 1) 1016 call setbufvar(b:GPGCorrespondingTo, "&mod", 1)
976 1017
977 " reset modified flag 1018 " reset modified flag
978 set nomodified 1019 setl nomodified
979 1020
980 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGFinishOptionsBuffer()") 1021 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGFinishOptionsBuffer()")
981 endfunction 1022 endfunction
982 1023
983 " Function: s:GPGCheckRecipients(tocheck) {{{2 1024 " Function: s:GPGCheckRecipients(tocheck) {{{2
1023 " 1064 "
1024 function s:GPGNameToID(name) 1065 function s:GPGNameToID(name)
1025 call s:GPGDebug(3, ">>>>>>>> Entering s:GPGNameToID()") 1066 call s:GPGDebug(3, ">>>>>>>> Entering s:GPGNameToID()")
1026 1067
1027 " ask gpg for the id for a name 1068 " ask gpg for the id for a name
1028 let commandline = s:GPGCommand . " --quiet --with-colons --fixed-list-mode --list-keys \"" . a:name . "\"" 1069 let cmd = { 'level': 2 }
1029 call s:GPGDebug(2, "command: ". commandline) 1070 let cmd.args = '--quiet --with-colons --fixed-list-mode --list-keys ' . shellescape(a:name)
1030 let &shellredir = s:shellredir 1071 let output = s:GPGSystem(cmd)
1031 let &shell = s:shell
1032 let output = system(commandline)
1033 let &shellredir = s:shellredirsave
1034 let &shell = s:shellsave
1035 call s:GPGDebug(2, "output: ". output)
1036 1072
1037 " when called with "--with-colons" gpg encodes its output _ALWAYS_ as UTF-8, 1073 " when called with "--with-colons" gpg encodes its output _ALWAYS_ as UTF-8,
1038 " so convert it, if necessary 1074 " so convert it, if necessary
1039 if (&encoding != "utf-8") 1075 if (&encoding != "utf-8")
1040 let output = iconv(output, "utf-8", &encoding) 1076 let output = iconv(output, "utf-8", &encoding)
1043 1079
1044 " parse the output of gpg 1080 " parse the output of gpg
1045 let pubseen = 0 1081 let pubseen = 0
1046 let counter = 0 1082 let counter = 0
1047 let gpgids = [] 1083 let gpgids = []
1084 let duplicates = {}
1048 let choices = "The name \"" . a:name . "\" is ambiguous. Please select the correct key:\n" 1085 let choices = "The name \"" . a:name . "\" is ambiguous. Please select the correct key:\n"
1049 for line in lines 1086 for line in lines
1050 let fields = split(line, ":") 1087
1051 " search for the next uid 1088 " check if this line has already been processed
1052 if (pubseen == 1) 1089 if !has_key(duplicates, line)
1053 if (fields[0] == "uid") 1090 let duplicates[line] = 1
1054 let choices = choices . " " . fields[9] . "\n" 1091
1092 let fields = split(line, ":")
1093
1094 " search for the next uid
1095 if pubseen
1096 if (fields[0] == "uid")
1097 let choices = choices . " " . fields[9] . "\n"
1098 else
1099 let pubseen = 0
1100 endif
1101 " search for the next pub
1055 else 1102 else
1056 let pubseen = 0 1103 if (fields[0] == "pub")
1057 endif 1104 " Ignore keys which are not usable for encryption
1058 endif 1105 if fields[11] !~? 'e'
1059 1106 continue
1060 " search for the next pub 1107 endif
1061 if (pubseen == 0) 1108
1062 if (fields[0] == "pub") 1109 let identity = fields[4]
1063 let identity = fields[4] 1110 let gpgids += [identity]
1064 let gpgids += [identity] 1111 if exists("*strftime")
1065 if exists("*strftime") 1112 let choices = choices . counter . ": ID: 0x" . identity . " created at " . strftime("%c", fields[5]) . "\n"
1066 let choices = choices . counter . ": ID: 0x" . identity . " created at " . strftime("%c", fields[5]) . "\n" 1113 else
1067 else 1114 let choices = choices . counter . ": ID: 0x" . identity . "\n"
1068 let choices = choices . counter . ": ID: 0x" . identity . "\n" 1115 endif
1116 let counter = counter+1
1117 let pubseen = 1
1069 endif 1118 endif
1070 let counter = counter+1
1071 let pubseen = 1
1072 endif 1119 endif
1073 endif 1120 endif
1074 1121
1075 endfor 1122 endfor
1076 1123
1082 while (answer == "") 1129 while (answer == "")
1083 let answer = input("Enter number: ", "0") 1130 let answer = input("Enter number: ", "0")
1084 endwhile 1131 endwhile
1085 endif 1132 endif
1086 1133
1087 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGIDToName()") 1134 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGNameToID()")
1088 return get(gpgids, answer, "") 1135 return get(gpgids, answer, "")
1089 endfunction 1136 endfunction
1090 1137
1091 " Function: s:GPGIDToName(identity) {{{2 1138 " Function: s:GPGIDToName(identity) {{{2
1092 " 1139 "
1097 call s:GPGDebug(3, ">>>>>>>> Entering s:GPGIDToName()") 1144 call s:GPGDebug(3, ">>>>>>>> Entering s:GPGIDToName()")
1098 1145
1099 " TODO is the encryption subkey really unique? 1146 " TODO is the encryption subkey really unique?
1100 1147
1101 " ask gpg for the id for a name 1148 " ask gpg for the id for a name
1102 let commandline = s:GPGCommand . " --quiet --with-colons --fixed-list-mode --list-keys " . a:identity 1149 let cmd = { 'level': 2 }
1103 call s:GPGDebug(2, "command: ". commandline) 1150 let cmd.args = '--quiet --with-colons --fixed-list-mode --list-keys ' . a:identity
1104 let &shellredir = s:shellredir 1151 let output = s:GPGSystem(cmd)
1105 let &shell = s:shell
1106 let output = system(commandline)
1107 let &shellredir = s:shellredirsave
1108 let &shell = s:shellsave
1109 call s:GPGDebug(2, "output: ". output)
1110 1152
1111 " when called with "--with-colons" gpg encodes its output _ALWAYS_ as UTF-8, 1153 " when called with "--with-colons" gpg encodes its output _ALWAYS_ as UTF-8,
1112 " so convert it, if necessary 1154 " so convert it, if necessary
1113 if (&encoding != "utf-8") 1155 if (&encoding != "utf-8")
1114 let output = iconv(output, "utf-8", &encoding) 1156 let output = iconv(output, "utf-8", &encoding)
1118 " parse the output of gpg 1160 " parse the output of gpg
1119 let pubseen = 0 1161 let pubseen = 0
1120 let uid = "" 1162 let uid = ""
1121 for line in lines 1163 for line in lines
1122 let fields = split(line, ":") 1164 let fields = split(line, ":")
1123 if (pubseen == 0) " search for the next pub 1165
1166 if !pubseen " search for the next pub
1124 if (fields[0] == "pub") 1167 if (fields[0] == "pub")
1168 " Ignore keys which are not usable for encryption
1169 if fields[11] !~? 'e'
1170 continue
1171 endif
1172
1125 let pubseen = 1 1173 let pubseen = 1
1126 endif 1174 endif
1127 else " search for the next uid 1175 else " search for the next uid
1128 if (fields[0] == "uid") 1176 if (fields[0] == "uid")
1129 let pubseen = 0 1177 let pubseen = 0
1139 1187
1140 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGIDToName()") 1188 call s:GPGDebug(3, "<<<<<<<< Leaving s:GPGIDToName()")
1141 return uid 1189 return uid
1142 endfunction 1190 endfunction
1143 1191
1192 function s:GPGPreCmd()
1193 let &shellredir = s:shellredir
1194 let &shell = s:shell
1195 let &shelltemp = s:shelltemp
1196 endfunction
1197
1198 function s:GPGPostCmd()
1199 let &shellredir = s:shellredirsave
1200 let &shell = s:shellsave
1201 let &shelltemp = s:shelltempsave
1202 endfunction
1203
1204 " Function: s:GPGSystem(dict) {{{2
1205 "
1206 " run g:GPGCommand using system(), logging the commandline and output
1207 " Recognized keys are:
1208 " level - Debug level at which the commandline and output will be logged
1209 " args - Arguments to be given to g:GPGCommand
1210 "
1211 " Returns: command output
1212 "
1213 function s:GPGSystem(dict)
1214 let commandline = printf('%s %s', s:GPGCommand, a:dict.args)
1215 if (!empty(g:GPGHomedir))
1216 let commandline .= ' --homedir ' . shellescape(g:GPGHomedir)
1217 endif
1218 let commandline .= ' ' . s:stderrredirnull
1219 call s:GPGDebug(a:dict.level, "command: ". commandline)
1220
1221 call s:GPGPreCmd()
1222 let output = system(commandline)
1223 call s:GPGPostCmd()
1224
1225 call s:GPGDebug(a:dict.level, "output: ". output)
1226 return output
1227 endfunction
1228
1229 " Function: s:GPGExecute(dict) {{{2
1230 "
1231 " run g:GPGCommand using :execute, logging the commandline
1232 " Recognized keys are:
1233 " level - Debug level at which the commandline will be logged
1234 " args - Arguments to be given to g:GPGCommand
1235 " ex - Ex command which will be :executed
1236 " redirect - Shell redirect to use, if needed
1237 "
1238 function s:GPGExecute(dict)
1239 let commandline = printf('%s%s %s', a:dict.ex, s:GPGCommand, a:dict.args)
1240 if (!empty(g:GPGHomedir))
1241 let commandline .= ' --homedir ' . shellescape(g:GPGHomedir, 1)
1242 endif
1243 if (has_key(a:dict, 'redirect'))
1244 let commandline .= ' ' . a:dict.redirect
1245 endif
1246 let commandline .= ' ' . s:stderrredirnull
1247 call s:GPGDebug(a:dict.level, "command: " . commandline)
1248
1249 call s:GPGPreCmd()
1250 execute commandline
1251 call s:GPGPostCmd()
1252 endfunction
1253
1144 " Function: s:GPGDebug(level, text) {{{2 1254 " Function: s:GPGDebug(level, text) {{{2
1145 " 1255 "
1146 " output debug message, if this message has high enough importance 1256 " output debug message, if this message has high enough importance
1147 " only define function if GPGDebugLevel set at all 1257 " only define function if GPGDebugLevel set at all
1148 " 1258 "
1149 function s:GPGDebug(level, text) 1259 function s:GPGDebug(level, text)
1150 if exists("g:GPGDebugLevel") && g:GPGDebugLevel >= a:level 1260 if exists("g:GPGDebugLevel") && g:GPGDebugLevel >= a:level
1151 if exists("g:GPGDebugLog") 1261 if exists("g:GPGDebugLog")
1152 execute "redir >> " . g:GPGDebugLog 1262 execute "redir >> " . g:GPGDebugLog
1153 echom "GnuPG: " . a:text 1263 silent echom "GnuPG: " . a:text
1154 redir END 1264 redir END
1155 else 1265 else
1156 echom "GnuPG: " . a:text 1266 echom "GnuPG: " . a:text
1157 endif 1267 endif
1158 endif 1268 endif