diff --git a/plugin/gnupg.vim b/plugin/gnupg.vim index be7d9f6..d498594 100644 --- a/plugin/gnupg.vim +++ b/plugin/gnupg.vim @@ -1,5 +1,5 @@ " Name: gnupg.vim -" Version: $Id: gnupg.vim 1933 2008-01-23 09:49:33Z mbr $ +" Version: $Id: gnupg.vim 2249 2008-07-31 11:43:14Z mbr $ " Author: Markus Braun " Summary: Vim plugin for transparent editing of gpg encrypted files. " Licence: This program is free software; you can redistribute it and/or @@ -21,6 +21,19 @@ " Refer to ':help add-plugin', ':help add-global-plugin' and ':help " runtimepath' for more details about Vim plugins. " +" From "man 1 gpg-agent": +" +" ... +" You should always add the following lines to your .bashrc or whatever +" initialization file is used for all shell invocations: +" +" GPG_TTY=‘tty‘ +" export GPG_TTY +" +" It is important that this environment variable always reflects the out‐ +" put of the tty command. For W32 systems this option is not required. +" ... +" " Commands: " " :GPGEditRecipients @@ -42,6 +55,10 @@ " " Variables: " +" g:GPGExecutable +" If set used as gpg executable, otherwise the system chooses what is run +" when "gpg" is called. Defaults to "gpg". +" " g:GPGUseAgent " If set to 0 a possible available gpg-agent won't be used. Defaults to 1. " @@ -52,36 +69,45 @@ " If set to 1 armored data is preferred for new files. Defaults to 0. " " Credits: -" Mathieu Clabaut for inspirations through his vimspell.vim script. -" Richard Bronosky for patch to enable ".pgp" suffix. -" Erik Remmelzwaal for patch to enable windows support and patient beta +" - Mathieu Clabaut for inspirations through his vimspell.vim script. +" - Richard Bronosky for patch to enable ".pgp" suffix. +" - Erik Remmelzwaal for patch to enable windows support and patient beta " testing. -" Lars Becker for patch to make gpg2 working. +" - Lars Becker for patch to make gpg2 working. +" - Thomas Arendsen Hein for patch to convert encoding of gpg output +" - Karl-Heinz Ruskowski for patch to fix unknown recipients and trust model +" and patient beta testing. +" - Giel van Schijndel for patch to get GPG_TTY dynamically. " " Section: Plugin header {{{1 +if v:version < 700 + echohl ErrorMsg | echo 'plugin gnupg.vim requires Vim version >= 7.0' | echohl None + finish +endif + if (exists("g:loaded_gnupg") || &cp || exists("#BufReadPre#*.\(gpg\|asc\|pgp\)")) finish -endi -let g:loaded_gnupg = "$Revision: 1933 $" +endif + +let g:loaded_gnupg = "$Revision: 2249 $" " Section: Autocmd setup {{{1 augroup GnuPG -autocmd! + autocmd! -" initialize the internal variables -autocmd BufNewFile,BufReadPre,FileReadPre *.\(gpg\|asc\|pgp\) call s:GPGInit() -" force the user to edit the recipient list if he opens a new file and public -" keys are preferred -autocmd BufNewFile *.\(gpg\|asc\|pgp\) if (exists("g:GPGPreferSymmetric") && g:GPGPreferSymmetric == 0) | call s:GPGEditRecipients() | endi -" do the decryption -autocmd BufReadPost,FileReadPost *.\(gpg\|asc\|pgp\) call s:GPGDecrypt() - -" convert all text to encrypted text before writing -autocmd BufWritePre,FileWritePre *.\(gpg\|asc\|pgp\) call s:GPGEncrypt() -" undo the encryption so we are back in the normal text, directly -" after the file has been written. -autocmd BufWritePost,FileWritePost *.\(gpg\|asc\|pgp\) call s:GPGEncryptPost() + " initialize the internal variables + autocmd BufNewFile,BufReadPre,FileReadPre *.\(gpg\|asc\|pgp\) call s:GPGInit() + " force the user to edit the recipient list if he opens a new file and public + " keys are preferred + autocmd BufNewFile *.\(gpg\|asc\|pgp\) if (exists("g:GPGPreferSymmetric") && g:GPGPreferSymmetric == 0) | call s:GPGEditRecipients() | endif + " do the decryption + autocmd BufReadPost,FileReadPost *.\(gpg\|asc\|pgp\) call s:GPGDecrypt() + " convert all text to encrypted text before writing + autocmd BufWritePre,FileWritePre *.\(gpg\|asc\|pgp\) call s:GPGEncrypt() + " undo the encryption so we are back in the normal text, directly + " after the file has been written. + autocmd BufWritePost,FileWritePost *.\(gpg\|asc\|pgp\) call s:GPGEncryptPost() augroup END " Section: Highlight setup {{{1 @@ -94,7 +120,7 @@ highlight default link GPGHighlightUnknownRecipient ErrorMsg " " initialize the plugin " -fun s:GPGInit() +function s:GPGInit() " first make sure nothing is written to ~/.viminfo while editing " an encrypted file. set viminfo= @@ -102,6 +128,11 @@ fun s:GPGInit() " we don't want a swap file, as it writes unencrypted data to disk set noswapfile + " check what gpg command to use + if (!exists("g:GPGExecutable")) + let g:GPGExecutable = "gpg --trust-model always" + endif + " check if gpg-agent is allowed if (!exists("g:GPGUseAgent")) let g:GPGUseAgent = 1 @@ -121,21 +152,25 @@ fun s:GPGInit() if (!exists("g:GPGDebugLevel")) let g:GPGDebugLevel = 0 endif - + " print version call s:GPGDebug(1, "gnupg.vim ". g:loaded_gnupg) " determine if gnupg can use the gpg-agent if (exists("$GPG_AGENT_INFO") && g:GPGUseAgent == 1) - if (!exists("$GPG_TTY")) - echohl GPGError - echo "The GPG_TTY is not set!" - echo "gpg-agent might not work." - echohl None + if (!exists("$GPG_TTY") && !has("gui_running")) + let $GPG_TTY = system("tty") + if (v:shell_error) + let $GPG_TTY = "" + echohl GPGError + echom "The GPG_TTY is not set and no TTY could be found using the `tty` command!" + echom "gpg-agent might not work." + echohl None + endif endif - let s:GPGCommand="gpg --use-agent" + let s:GPGCommand=g:GPGExecutable . " --use-agent" else - let s:GPGCommand="gpg --no-use-agent" + let s:GPGCommand=g:GPGExecutable . " --no-use-agent" endif " don't use tty in gvim @@ -148,7 +183,7 @@ fun s:GPGInit() " setup shell environment for unix and windows let s:shellredirsave=&shellredir let s:shellsave=&shell - if (match(&shell,"\\(cmd\\|command\\).exe") >= 0) + if (match(&shell,"\\(cmd\\|command\\).execute") >= 0) " windows specific settings let s:shellredir = '>%s' let s:shell = &shell @@ -159,7 +194,7 @@ fun s:GPGInit() let s:shell = 'sh' let s:stderrredirnull ='2>/dev/null' let s:GPGCommand="LANG=C LC_ALL=C " . s:GPGCommand - endi + endif " find the supported algorithms let &shellredir=s:shellredir @@ -172,24 +207,23 @@ fun s:GPGInit() let s:GPGCipher=substitute(output, ".*Cipher: \\(.\\{-}\\)\n.*", "\\1", "") let s:GPGHash=substitute(output, ".*Hash: \\(.\\{-}\\)\n.*", "\\1", "") let s:GPGCompress=substitute(output, ".*Compress: \\(.\\{-}\\)\n.*", "\\1", "") -endf +endfunction " Function: s:GPGDecrypt() {{{2 " " decrypt the buffer and find all recipients of the encrypted file " -fun s:GPGDecrypt() +function s:GPGDecrypt() " switch to binary mode to read the encrypted file set bin " get the filename of the current buffer let filename=escape(expand("%:p"), '\"') - " clear GPGEncrypted, GPGRecipients, GPGUnknownRecipients and GPGOptions + " clear GPGEncrypted, GPGRecipients and GPGOptions let b:GPGEncrypted=0 - let b:GPGRecipients="" - let b:GPGUnknownRecipients="" - let b:GPGOptions="" + let b:GPGRecipients=[] + let b:GPGOptions=[] " find the recipients of the file let &shellredir=s:shellredir @@ -206,25 +240,27 @@ fun s:GPGDecrypt() let b:GPGEncrypted=1 call s:GPGDebug(1, "this file is symmetric encrypted") - let b:GPGOptions=b:GPGOptions . "symmetric:" + let b:GPGOptions+=["symmetric"] + " find the used cipher algorithm let cipher=substitute(output, ".*gpg: \\([^ ]\\+\\) encrypted data.*", "\\1", "") if (match(s:GPGCipher, "\\<" . cipher . "\\>") >= 0) - let b:GPGOptions=b:GPGOptions . "cipher-algo " . cipher . ":" + let b:GPGOptions+=["cipher-algo " . cipher] call s:GPGDebug(1, "cipher-algo is " . cipher) else echohl GPGWarning - echo "The cipher " . cipher . " is not known by the local gpg command. Using default!" + echom "The cipher " . cipher . " is not known by the local gpg command. Using default!" echo echohl None - endi + endif elseif (match(output, "gpg: public key is [[:xdigit:]]\\{8}") >= 0) " file is asymmetric encrypted let b:GPGEncrypted=1 call s:GPGDebug(1, "this file is asymmetric encrypted") - let b:GPGOptions=b:GPGOptions . "encrypt:" + let b:GPGOptions+=["encrypt"] + " find the used public keys let start=match(output, "gpg: public key is [[:xdigit:]]\\{8}") while (start >= 0) let start=start + strlen("gpg: public key is ") @@ -232,32 +268,32 @@ fun s:GPGDecrypt() call s:GPGDebug(1, "recipient is " . recipient) let name=s:GPGNameToID(recipient) if (strlen(name) > 0) - let b:GPGRecipients=b:GPGRecipients . name . ":" + let b:GPGRecipients+=[name] call s:GPGDebug(1, "name of recipient is " . name) else - let b:GPGUnknownRecipients=b:GPGUnknownRecipients . recipient . ":" - echohl GPGWarning - echo "The recipient " . recipient . " is not in your public keyring!" - echohl None + let b:GPGRecipients+=[recipient] + echohl GPGWarning + echom "The recipient \"" . recipient . "\" is not in your public keyring!" + echohl None end let start=match(output, "gpg: public key is [[:xdigit:]]\\{8}", start) - endw + endwhile else " file is not encrypted let b:GPGEncrypted=0 call s:GPGDebug(1, "this file is not encrypted") echohl GPGWarning - echo "File is not encrypted, all GPG functions disabled!" + echom "File is not encrypted, all GPG functions disabled!" echohl None set nobin return - endi + endif " check if the message is armored if (match(output, "gpg: armor header") >= 0) call s:GPGDebug(1, "this file is armored") - let b:GPGOptions=b:GPGOptions . "armor:" - endi + let b:GPGOptions+=["armor"] + endif " finally decrypt the buffer content " since even with the --quiet option passphrase typos will be reported, @@ -270,12 +306,12 @@ fun s:GPGDecrypt() if (v:shell_error) " message could not be decrypted silent u echohl GPGError - let asd=input("Message could not be decrypted! (Press ENTER)") + let blackhole=input("Message could not be decrypted! (Press ENTER)") echohl None bwipeout set nobin return - endi + endif " turn off binary mode set nobin @@ -286,13 +322,13 @@ fun s:GPGDecrypt() " refresh screen redraw! -endf +endfunction " Function: s:GPGEncrypt() {{{2 " " encrypts the buffer to all previous recipients " -fun s:GPGEncrypt() +function s:GPGEncrypt() " save window view let s:GPGWindowView = winsaveview() call s:GPGDebug(2, "saved window view " . string(s:GPGWindowView)) @@ -305,7 +341,7 @@ fun s:GPGEncrypt() else let s:GPGEncoding = "" call s:GPGDebug(2, "encoding and fileencoding are the same (\"" . &encoding . "\"), not switching") - endi + endif " switch buffer to binary mode set bin @@ -313,92 +349,87 @@ fun s:GPGEncrypt() " guard for unencrypted files if (exists("b:GPGEncrypted") && b:GPGEncrypted == 0) echohl GPGWarning - echo "File is not encrypted, all GPG functions disabled!" + echom "File is not encrypted, all GPG functions disabled!" echohl None return - endi + endif - let options="" - let recipients="" - let field=0 + " initialize GPGOptions if not happened before + if (!exists("b:GPGOptions") || len(b:GPGOptions) == 0) + let b:GPGOptions=[] + if (exists("g:GPGPreferSymmetric") && g:GPGPreferSymmetric == 1) + let b:GPGOptions+=["symmetric"] + else + let b:GPGOptions+=["encrypt"] + endif + if (exists("g:GPGPreferArmor") && g:GPGPreferArmor == 1) + let b:GPGOptions+=["armor"] + endif + call s:GPGDebug(1, "no options set, so using default options: " . string(b:GPGOptions)) + endif " built list of options - if (!exists("b:GPGOptions") || strlen(b:GPGOptions) == 0) - if (exists("g:GPGPreferSymmetric") && g:GPGPreferSymmetric == 1) - let b:GPGOptions="symmetric:" - else - let b:GPGOptions="encrypt:" - endi - if (exists("g:GPGPreferArmor") && g:GPGPreferArmor == 1) - let b:GPGOptions=b:GPGOptions . "armor:" - endi - call s:GPGDebug(1, "no options set, so using default options: " . b:GPGOptions) - endi - let field=0 - let option=s:GetField(b:GPGOptions, ":", field) - while (strlen(option)) + let options="" + for option in b:GPGOptions let options=options . " --" . option . " " - let field=field+1 - let option=s:GetField(b:GPGOptions, ":", field) - endw + endfor + + " check here again if all recipients are available in the keyring + let [ recipients, unknownrecipients ] = s:GPGCheckRecipients(b:GPGRecipients) " check if there are unknown recipients and warn - if (exists("b:GPGUnknownRecipients") && strlen(b:GPGUnknownRecipients) > 0) + if(len(unknownrecipients) > 0) echohl GPGWarning - echo "There are unknown recipients!!" - echo "Please use GPGEditRecipients to correct!!" + echom "Please use GPGEditRecipients to correct!!" echo echohl None - call s:GPGDebug(1, "unknown recipients are: " . b:GPGUnknownRecipients) - endi + + " Let user know whats happend and copy known_recipients back to buffer + let dummy=input("Press ENTER to quit") + endif " built list of recipients - if (exists("b:GPGRecipients") && strlen(b:GPGRecipients) > 0) - call s:GPGDebug(1, "recipients are: " . b:GPGRecipients) - let field=0 - let gpgid=s:GetField(b:GPGRecipients, ":", field) - while (strlen(gpgid)) - let recipients=recipients . " -r " . gpgid - let field=field+1 - let gpgid=s:GetField(b:GPGRecipients, ":", field) - endw + if (len(recipients) > 0) + for gpgid in recipients + let options=options . " -r " . gpgid + endfor else - if (match(b:GPGOptions, "encrypt:") >= 0) + if (match(b:GPGOptions, "encrypt") >= 0) echohl GPGError - echo "There are no recipients!!" - echo "Please use GPGEditRecipients to correct!!" + echom "There are no recipients!!" + echom "Please use GPGEditRecipients to correct!!" echo echohl None - endi - endi + endif + endif " encrypt the buffer let &shellredir=s:shellredir let &shell=s:shell - silent exec "'[,']!" . s:GPGCommand . " --quiet --no-encrypt-to " . options . recipients . " " . s:stderrredirnull + silent exec "'[,']!" . s:GPGCommand . " --quiet --no-encrypt-to " . options . " " . s:stderrredirnull let &shellredir=s:shellredirsave let &shell=s:shellsave - call s:GPGDebug(1, "called gpg command is: " . "'[,']!" . s:GPGCommand . " --quiet --no-encrypt-to " . options . recipients . " " . s:stderrredirnull) + call s:GPGDebug(1, "called gpg command is: " . "'[,']!" . s:GPGCommand . " --quiet --no-encrypt-to " . options . " " . s:stderrredirnull) if (v:shell_error) " message could not be encrypted silent u echohl GPGError - let asd=input("Message could not be encrypted! File might be empty! (Press ENTER)") + let blackhole=input("Message could not be encrypted! File might be empty! (Press ENTER)") echohl None bwipeout return - endi + endif -endf +endfunction " Function: s:GPGEncryptPost() {{{2 " " undo changes don by encrypt, after writing " -fun s:GPGEncryptPost() - +function s:GPGEncryptPost() + " guard for unencrypted files if (exists("b:GPGEncrypted") && b:GPGEncrypted == 0) return - endi + endif " undo encryption of buffer content silent u @@ -410,7 +441,7 @@ fun s:GPGEncryptPost() if s:GPGEncoding != "" let &encoding = s:GPGEncoding call s:GPGDebug(2, "restored encoding \"" . &encoding . "\"") - endi + endif " restore window view call winrestview(s:GPGWindowView) @@ -418,68 +449,58 @@ fun s:GPGEncryptPost() " refresh screen redraw! -endf +endfunction " Function: s:GPGViewRecipients() {{{2 " " echo the recipients " -fun s:GPGViewRecipients() +function s:GPGViewRecipients() " guard for unencrypted files if (exists("b:GPGEncrypted") && b:GPGEncrypted == 0) echohl GPGWarning - echo "File is not encrypted, all GPG functions disabled!" + echom "File is not encrypted, all GPG functions disabled!" echohl None return - endi + endif - if (exists("b:GPGRecipients")) - echo 'This file has following recipients (Unknown recipients have a prepended "!"):' - " echo the recipients - let field=0 - let name=s:GetField(b:GPGRecipients, ":", field) - while (strlen(name) > 0) - let name=s:GPGIDToName(name) - echo name + let [ recipients, unknownrecipients ] = s:GPGCheckRecipients(b:GPGRecipients) - let field=field+1 - let name=s:GetField(b:GPGRecipients, ":", field) - endw + echo 'This file has following recipients (Unknown recipients have a prepended "!"):' + " echo the recipients + for name in recipients + let name=s:GPGIDToName(name) + echo name + endfor - " put the unknown recipients in the scratch buffer - let field=0 - echohl GPGWarning - let name=s:GetField(b:GPGUnknownRecipients, ":", field) - while (strlen(name) > 0) - let name="!" . name - echo name + " echo the unknown recipients + echohl GPGWarning + for name in unknownrecipients + let name="!" . name + echo name + endfor + echohl None - let field=field+1 - let name=s:GetField(b:GPGUnknownRecipients, ":", field) - endw + " check if there is any known recipient + if (len(recipients) == 0) + echohl GPGError + echom 'There are no known recipients!' echohl None - - " check if there is any known recipient - if (strlen(s:GetField(b:GPGRecipients, ":", 0)) == 0) - echohl GPGError - echo 'There are no known recipients!' - echohl None - endi - endi -endf + endif +endfunction " Function: s:GPGEditRecipients() {{{2 " " create a scratch buffer with all recipients to add/remove recipients " -fun s:GPGEditRecipients() +function s:GPGEditRecipients() " guard for unencrypted files if (exists("b:GPGEncrypted") && b:GPGEncrypted == 0) echohl GPGWarning - echo "File is not encrypted, all GPG functions disabled!" + echom "File is not encrypted, all GPG functions disabled!" echohl None return - endi + endif " only do this if it isn't already a GPGRecipients_* buffer if (match(bufname("%"), "^\\(GPGRecipients_\\|GPGOptions_\\)") != 0 && match(bufname("%"), "\.\\(gpg\\|asc\\|pgp\\)$") >= 0) @@ -491,35 +512,36 @@ fun s:GPGEditRecipients() " check if this buffer exists if (!bufexists(editbuffername)) " create scratch buffer - exe 'silent! split ' . escape(editbuffername, ' *?\"'."'") + execute 'silent! split ' . escape(editbuffername, ' *?\"'."'") " add a autocommand to regenerate the recipients after a write - autocmd BufHidden,BufUnload call s:GPGFinishRecipientsBuffer() + autocmd BufHidden,BufUnload,BufWriteCmd call s:GPGFinishRecipientsBuffer() else if (bufwinnr(editbuffername) >= 0) - " switch to scratch buffer window - exe 'silent! ' . bufwinnr(editbuffername) . "wincmd w" + " switch to scratch buffer window + execute 'silent! ' . bufwinnr(editbuffername) . "wincmd w" else - " split scratch buffer window - exe 'silent! sbuffer ' . escape(editbuffername, ' *?\"'."'") + " split scratch buffer window + execute 'silent! sbuffer ' . escape(editbuffername, ' *?\"'."'") - " add a autocommand to regenerate the recipients after a write - autocmd BufHidden,BufUnload call s:GPGFinishRecipientsBuffer() - endi + " add a autocommand to regenerate the recipients after a write + autocmd BufHidden,BufUnload,BufWriteCmd call s:GPGFinishRecipientsBuffer() + endif " empty the buffer silent normal! 1GdG - endi + endif " Mark the buffer as a scratch buffer - setlocal buftype=nofile + setlocal buftype=acwrite + setlocal bufhidden=hide setlocal noswapfile setlocal nowrap setlocal nobuflisted setlocal nonumber " so we know for which other buffer this edit buffer is - let b:corresponding_to=buffername + let b:GPGCorrespondingTo=buffername " put some comments to the scratch buffer silent put ='GPG: ----------------------------------------------------------------------' @@ -529,34 +551,22 @@ fun s:GPGEditRecipients() silent put ='GPG: Closing this buffer commits changes' silent put ='GPG: ----------------------------------------------------------------------' - " put the recipients in the scratch buffer - let recipients=getbufvar(b:corresponding_to, "GPGRecipients") - let field=0 + " get the recipients + let [ recipients, unknownrecipients ] = s:GPGCheckRecipients(getbufvar(b:GPGCorrespondingTo, "GPGRecipients")) - let name=s:GetField(recipients, ":", field) - while (strlen(name) > 0) + " put the recipients in the scratch buffer + for name in recipients let name=s:GPGIDToName(name) silent put =name - - let field=field+1 - let name=s:GetField(recipients, ":", field) - endw + endfor " put the unknown recipients in the scratch buffer - let unknownRecipients=getbufvar(b:corresponding_to, "GPGUnknownRecipients") - let field=0 - let syntaxPattern="\\(nonexistingwordinthisbuffer" - - let name=s:GetField(unknownRecipients, ":", field) - while (strlen(name) > 0) + let syntaxPattern="\\(nonexxistinwordinthisbuffer" + for name in unknownrecipients let name="!" . name let syntaxPattern=syntaxPattern . "\\|" . name silent put =name - - let field=field+1 - let name=s:GetField(unknownRecipients, ":", field) - endw - + endfor let syntaxPattern=syntaxPattern . "\\)" " define highlight @@ -568,7 +578,7 @@ fun s:GPGEditRecipients() syntax match GPGComment "^GPG:.*$" highlight clear GPGComment highlight link GPGComment Comment - endi + endif " delete the empty first line silent normal! 1Gdd @@ -576,41 +586,37 @@ fun s:GPGEditRecipients() " jump to the first recipient silent normal! G - endi -endf + endif +endfunction " Function: s:GPGFinishRecipientsBuffer() {{{2 " " create a new recipient list from RecipientsBuffer -fun s:GPGFinishRecipientsBuffer() +function s:GPGFinishRecipientsBuffer() " guard for unencrypted files if (exists("b:GPGEncrypted") && b:GPGEncrypted == 0) echohl GPGWarning - echo "File is not encrypted, all GPG functions disabled!" + echom "File is not encrypted, all GPG functions disabled!" echohl None return - endi + endif " go to buffer before doing work if (bufnr("%") != expand("")) " switch to scratch buffer window - exe 'silent! ' . bufwinnr(expand("")) . "wincmd w" - endi - - " clear GPGRecipients and GPGUnknownRecipients - let GPGRecipients="" - let GPGUnknownRecipients="" + execute 'silent! ' . bufwinnr(expand("")) . "wincmd w" + endif " delete the autocommand autocmd! * - let currentline=1 - let recipient=getline(currentline) " get the recipients from the scratch buffer - while (currentline <= line("$")) - " delete all spaces at beginning and end of the line - " also delete a '!' at the beginning of the line + let recipients=[] + let lines=getline(1,"$") + for recipient in lines + " delete all spaces at beginning and end of the recipient + " also delete a '!' at the beginning of the recipient let recipient=substitute(recipient, "^[[:space:]!]*\\(.\\{-}\\)[[:space:]]*$", "\\1", "") " delete comment lines let recipient=substitute(recipient, "^GPG:.*$", "", "") @@ -619,73 +625,71 @@ fun s:GPGFinishRecipientsBuffer() if (strlen(recipient) > 0) let gpgid=s:GPGNameToID(recipient) if (strlen(gpgid) > 0) - let GPGRecipients=GPGRecipients . gpgid . ":" + if (match(recipients, gpgid) < 0) + let recipients+=[gpgid] + endif else - let GPGUnknownRecipients=GPGUnknownRecipients . recipient . ":" - echohl GPGWarning - echo "The recipient " . recipient . " is not in your public keyring!" - echohl None + if (match(recipients, recipient) < 0) + let recipients+=[recipient] + echohl GPGWarning + echom "The recipient \"" . recipient . "\" is not in your public keyring!" + echohl None + endif end - endi - - let currentline=currentline+1 - let recipient=getline(currentline) - endw + endif + endfor " write back the new recipient list to the corresponding buffer and mark it " as modified. Buffer is now for sure a encrypted buffer. - call setbufvar(b:corresponding_to, "GPGRecipients", GPGRecipients) - call setbufvar(b:corresponding_to, "GPGUnknownRecipients", GPGUnknownRecipients) - call setbufvar(b:corresponding_to, "&mod", 1) - call setbufvar(b:corresponding_to, "GPGEncrypted", 1) + call setbufvar(b:GPGCorrespondingTo, "GPGRecipients", recipients) + call setbufvar(b:GPGCorrespondingTo, "&mod", 1) + call setbufvar(b:GPGCorrespondingTo, "GPGEncrypted", 1) " check if there is any known recipient - if (strlen(s:GetField(GPGRecipients, ":", 0)) == 0) + if (len(recipients) == 0) echohl GPGError - echo 'There are no known recipients!' + echom 'There are no known recipients!' echohl None - endi -endf + endif + + " reset modified flag + set nomodified +endfunction " Function: s:GPGViewOptions() {{{2 " " echo the recipients " -fun s:GPGViewOptions() +function s:GPGViewOptions() " guard for unencrypted files if (exists("b:GPGEncrypted") && b:GPGEncrypted == 0) echohl GPGWarning - echo "File is not encrypted, all GPG functions disabled!" + echom "File is not encrypted, all GPG functions disabled!" echohl None return - endi + endif if (exists("b:GPGOptions")) echo 'This file has following options:' " echo the options - let field=0 - let option=s:GetField(b:GPGOptions, ":", field) - while (strlen(option) > 0) + for option in b:GPGOptions echo option - - let field=field+1 - let option=s:GetField(b:GPGOptions, ":", field) - endw - endi -endf + endfor + endif +endfunction " Function: s:GPGEditOptions() {{{2 " " create a scratch buffer with all recipients to add/remove recipients " -fun s:GPGEditOptions() +function s:GPGEditOptions() " guard for unencrypted files if (exists("b:GPGEncrypted") && b:GPGEncrypted == 0) echohl GPGWarning - echo "File is not encrypted, all GPG functions disabled!" + echom "File is not encrypted, all GPG functions disabled!" echohl None return - endi + endif " only do this if it isn't already a GPGOptions_* buffer if (match(bufname("%"), "^\\(GPGRecipients_\\|GPGOptions_\\)") != 0 && match(bufname("%"), "\.\\(gpg\\|asc\\|pgp\\)$") >= 0) @@ -697,25 +701,25 @@ fun s:GPGEditOptions() " check if this buffer exists if (!bufexists(editbuffername)) " create scratch buffer - exe 'silent! split ' . escape(editbuffername, ' *?\"'."'") + execute 'silent! split ' . escape(editbuffername, ' *?\"'."'") " add a autocommand to regenerate the options after a write - autocmd BufHidden,BufUnload call s:GPGFinishOptionsBuffer() + autocmd BufHidden,BufUnload,BufWriteCmd call s:GPGFinishOptionsBuffer() else if (bufwinnr(editbuffername) >= 0) - " switch to scratch buffer window - exe 'silent! ' . bufwinnr(editbuffername) . "wincmd w" + " switch to scratch buffer window + execute 'silent! ' . bufwinnr(editbuffername) . "wincmd w" else - " split scratch buffer window - exe 'silent! sbuffer ' . escape(editbuffername, ' *?\"'."'") + " split scratch buffer window + execute 'silent! sbuffer ' . escape(editbuffername, ' *?\"'."'") - " add a autocommand to regenerate the options after a write - autocmd BufHidden,BufUnload call s:GPGFinishOptionsBuffer() - endi + " add a autocommand to regenerate the options after a write + autocmd BufHidden,BufUnload,BufWriteCmd call s:GPGFinishOptionsBuffer() + endif " empty the buffer silent normal! 1GdG - endi + endif " Mark the buffer as a scratch buffer setlocal buftype=nofile @@ -725,7 +729,7 @@ fun s:GPGEditOptions() setlocal nonumber " so we know for which other buffer this edit buffer is - let b:corresponding_to=buffername + let b:GPGCorrespondingTo=buffername " put some comments to the scratch buffer silent put ='GPG: ----------------------------------------------------------------------' @@ -739,16 +743,11 @@ fun s:GPGEditOptions() silent put ='GPG: ----------------------------------------------------------------------' " put the options in the scratch buffer - let options=getbufvar(b:corresponding_to, "GPGOptions") - let field=0 + let options=getbufvar(b:GPGCorrespondingTo, "GPGOptions") - let option=s:GetField(options, ":", field) - while (strlen(option) > 0) + for option in options silent put =option - - let field=field+1 - let option=s:GetField(options, ":", field) - endw + endfor " delete the empty first line silent normal! 1Gdd @@ -761,67 +760,96 @@ fun s:GPGEditOptions() syntax match GPGComment "^GPG:.*$" highlight clear GPGComment highlight link GPGComment Comment - endi - endi -endf + endif + endif +endfunction " Function: s:GPGFinishOptionsBuffer() {{{2 " " create a new option list from OptionsBuffer -fun s:GPGFinishOptionsBuffer() +function s:GPGFinishOptionsBuffer() " guard for unencrypted files if (exists("b:GPGEncrypted") && b:GPGEncrypted == 0) echohl GPGWarning - echo "File is not encrypted, all GPG functions disabled!" + echom "File is not encrypted, all GPG functions disabled!" echohl None return - endi + endif " go to buffer before doing work if (bufnr("%") != expand("")) " switch to scratch buffer window - exe 'silent! ' . bufwinnr(expand("")) . "wincmd w" - endi + execute 'silent! ' . bufwinnr(expand("")) . "wincmd w" + endif - " clear GPGOptions and GPGUnknownOptions - let GPGOptions="" - let GPGUnknownOptions="" + " clear options and unknownOptions + let options=[] + let unknownOptions=[] " delete the autocommand autocmd! * - let currentline=1 - let option=getline(currentline) - " get the options from the scratch buffer - while (currentline <= line("$")) - " delete all spaces at beginning and end of the line - " also delete a '!' at the beginning of the line + let lines=getline(1, "$") + for option in lines + " delete all spaces at beginning and end of the option + " also delete a '!' at the beginning of the option let option=substitute(option, "^[[:space:]!]*\\(.\\{-}\\)[[:space:]]*$", "\\1", "") " delete comment lines let option=substitute(option, "^GPG:.*$", "", "") " only do this if the line is not empty - if (strlen(option) > 0) - let GPGOptions=GPGOptions . option . ":" - endi - - let currentline=currentline+1 - let option=getline(currentline) - endw + if (strlen(option) > 0 && match(options, option) < 0) + let options+=[option] + endif + endfor " write back the new option list to the corresponding buffer and mark it " as modified - call setbufvar(b:corresponding_to, "GPGOptions", GPGOptions) - call setbufvar(b:corresponding_to, "&mod", 1) + call setbufvar(b:GPGCorrespondingTo, "GPGOptions", options) + call setbufvar(b:GPGCorrespondingTo, "&mod", 1) -endf + " reset modified flag + set nomodified +endfunction + +" Function: s:GPGCheckRecipients(tocheck) {{{2 +" +" check if recipients are known +" Returns: two lists recipients and unknownrecipients +function s:GPGCheckRecipients(tocheck) + let recipients=[] + let unknownrecipients=[] + + if (type(a:tocheck) == type([])) + for recipient in a:tocheck + let gpgid=s:GPGNameToID(recipient) + if (strlen(gpgid) > 0) + if (match(recipients, gpgid) < 0) + let recipients+=[gpgid] + endif + else + if (match(unknownrecipients, recipient) < 0) + let unknownrecipients+=[recipient] + echohl GPGWarning + echom "The recipient \"" . recipient . "\" is not in your public keyring!" + echohl None + endif + end + endfor + endif + + call s:GPGDebug(2, "recipients are: " . string(recipients)) + call s:GPGDebug(2, "unknown recipients are: " . string(unknownrecipients)) + + return [ recipients, unknownrecipients ] +endfunction " Function: s:GPGNameToID(name) {{{2 " " find GPG key ID corresponding to a name " Returns: ID for the given name -fun s:GPGNameToID(name) +function s:GPGNameToID(name) " ask gpg for the id for a name let &shellredir=s:shellredir let &shell=s:shell @@ -829,42 +857,46 @@ fun s:GPGNameToID(name) let &shellredir=s:shellredirsave let &shell=s:shellsave + " when called with "--with-colons" gpg encodes its output _ALWAYS_ as UTF-8, + " so convert it, if necessary + if &encoding != "utf-8" + let output=iconv(output, "utf-8", &encoding) + endif + let lines=split(output, "\n") + " parse the output of gpg - let pub_seen=0 - let uid_seen=0 - let line=0 + let pubseen=0 + let uidseen=0 let counter=0 - let gpgids="" + let gpgids=[] let choices="The name \"" . a:name . "\" is ambiguous. Please select the correct key:\n" - let linecontent=s:GetField(output, "\n", line) - while (strlen(linecontent)) + for line in lines + let fields=split(line, ":") " search for the next uid - if (pub_seen == 1) - if (s:GetField(linecontent, ":", 0) == "uid") - if (uid_seen == 0) - let choices=choices . counter . ": " . s:GetField(linecontent, ":", 9) . "\n" - let counter=counter+1 - let uid_seen=1 - else - let choices=choices . " " . s:GetField(linecontent, ":", 9) . "\n" - endi + if (pubseen == 1) + if (fields[0] == "uid") + if (uidseen == 0) + let choices=choices . counter . ": " . fields[9] . "\n" + let counter=counter+1 + let uidseen=1 + else + let choices=choices . " " . fields[9] . "\n" + endif else - let uid_seen=0 - let pub_seen=0 - endi - endi + let uidseen=0 + let pubseen=0 + endif + endif " search for the next pub - if (pub_seen == 0) - if (s:GetField(linecontent, ":", 0) == "pub") - let gpgids=gpgids . s:GetField(linecontent, ":", 4) . ":" - let pub_seen=1 - endi - endi + if (pubseen == 0) + if (fields[0] == "pub") + let gpgids+=[fields[4]] + let pubseen=1 + endif + endif - let line=line+1 - let linecontent=s:GetField(output, "\n", line) - endw + endfor " counter > 1 means we have more than one results let answer=0 @@ -873,17 +905,17 @@ fun s:GPGNameToID(name) let answer=input(choices, "0") while (answer == "") let answer=input("Enter number: ", "0") - endw - endi + endwhile + endif - return s:GetField(gpgids, ":", answer) -endf + return get(gpgids, answer, "") +endfunction " Function: s:GPGIDToName(identity) {{{2 " " find name corresponding to a GPG key ID " Returns: Name for the given ID -fun s:GPGIDToName(identity) +function s:GPGIDToName(identity) " TODO is the encryption subkey really unique? " ask gpg for the id for a name @@ -893,75 +925,53 @@ fun s:GPGIDToName(identity) let &shellredir=s:shellredirsave let &shell=s:shellsave - " parse the output of gpg - let pub_seen=0 - let finish=0 - let line=0 - let linecontent=s:GetField(output, "\n", line) - while (strlen(linecontent) && !finish) - if (pub_seen == 0) " search for the next pub - if (s:GetField(linecontent, ":", 0) == "pub") - let pub_seen=1 - endi - else " search for the next uid - if (s:GetField(linecontent, ":", 0) == "uid") - let pub_seen=0 - let finish=1 - let uid=s:GetField(linecontent, ":", 9) - endi - endi + " when called with "--with-colons" gpg encodes its output _ALWAYS_ as UTF-8, + " so convert it, if necessary + if &encoding != "utf-8" + let output=iconv(output, "utf-8", &encoding) + endif + let lines=split(output, "\n") - let line=line+1 - let linecontent=s:GetField(output, "\n", line) - endw + " parse the output of gpg + let pubseen=0 + let uid="" + for line in lines + let fields=split(line, ":") + if (pubseen == 0) " search for the next pub + if (fields[0] == "pub") + let pubseen=1 + endif + else " search for the next uid + if (fields[0] == "uid") + let pubseen=0 + let uid=fields[9] + break + endif + endif + endfor return uid -endf - -" Function: s:GetField(line, separator, field) {{{2 -" -" find field of 'separator' separated string, counting starts with 0 -" Returns: content of the field, if field doesn't exist it returns an empty -" string -fun s:GetField(line, separator, field) - let counter=a:field - let separatorLength=strlen(a:separator) - let start=0 - let end=match(a:line, a:separator) - if (end < 0) - let end=strlen(a:line) - endi - - " search for requested field - while (start < strlen(a:line) && counter > 0) - let counter=counter-separatorLength - let start=end+separatorLength - let end=match(a:line, a:separator, start) - if (end < 0) - let end=strlen(a:line) - endi - endw - - if (start < strlen(a:line)) - return strpart(a:line, start, end-start) - else - return "" - endi -endf +endfunction " Function: s:GPGDebug(level, text) {{{2 " " output debug message, if this message has high enough importance -fun s:GPGDebug(level, text) +function s:GPGDebug(level, text) if (g:GPGDebugLevel >= a:level) echom a:text - endi -endf + endif +endfunction " Section: Command definitions {{{1 -com! GPGViewRecipients call s:GPGViewRecipients() -com! GPGEditRecipients call s:GPGEditRecipients() -com! GPGViewOptions call s:GPGViewOptions() -com! GPGEditOptions call s:GPGEditOptions() - +command! GPGViewRecipients call s:GPGViewRecipients() +command! GPGEditRecipients call s:GPGEditRecipients() +command! GPGViewOptions call s:GPGViewOptions() +command! GPGEditOptions call s:GPGEditOptions() +" Section: Menu {{{1 +if has("menu") + amenu Plugin.GnuPG.View\ Recipients :GPGViewRecipients + amenu Plugin.GnuPG.Edit\ Recipients :GPGEditRecipients + amenu Plugin.GnuPG.View\ Options :GPGViewOptions + amenu Plugin.GnuPG.Edit\ Options :GPGEditOptions +endif " vim600: foldmethod=marker:foldlevel=0