From ac20966446c913f2dbf55d518b2d67a6dc31af43 Mon Sep 17 00:00:00 2001 From: Markus Braun Date: Fri, 30 May 2003 09:29:16 -0400 Subject: [PATCH] releasing gnupg.vim 1.26 --- plugin/gnupg.vim | 511 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 511 insertions(+) create mode 100644 plugin/gnupg.vim diff --git a/plugin/gnupg.vim b/plugin/gnupg.vim new file mode 100644 index 0000000..f1b4ee6 --- /dev/null +++ b/plugin/gnupg.vim @@ -0,0 +1,511 @@ +" Name: gnupg.vim +" Version: $Id: gnupg.vim,v 1.26 2003/05/30 09:29:16 mb Exp $ +" Author: Markus Braun +" Summary: Vim plugin for transparent editing of gpg encrypted files. +" TODO enable signing +" TODO GPGOptions for encrypting, signing, auto fetch .. +" Section: Documentation {{{1 +" Description: +" +" This script implements transparent editing of gpg encrypted files. The +" filename must have a ".gpg" suffix. When opening such a file the content is +" decrypted, when opening a new file the script will ask for the recipients of +" the encrypted file. The file content will be encrypted to all recipients +" before it is written. The script turns off viminfo and swapfile to increase +" security. +" +" Installation: +" +" Copy the gnupg.vim file to the $HOME/.vim/plugin directory. +" Refer to ':help add-plugin', ':help add-global-plugin' and ':help +" runtimepath' for more details about Vim plugins. +" +" Commands: +" +" :GPGEditRecipients +" Opens a scratch buffer to change the list of recipients. Recipients that +" are unknown (not in your public key) are highlighted and have a +" prepended "!". Closing the buffer with :x or :bd makes the changes permanent. +" +" :GPGViewRecipients +" Prints the list of recipients. +" +" Credits: +" Mathieu Clabaut for inspirations through his vimspell.vim script. +" Section: Plugin header {{{1 +if (exists("loaded_gnupg") || &cp || exists("#BufReadPre#*.gpg")) + finish +endi +let loaded_gnupg = 1 + +" dettermine if gnupg can use the gpg-agent +if (exists("$GPG_AGENT_INFO")) + let s:gpgcommand = "gpg --use-agent" +else + let s:gpgcommand = "gpg" +endif + +" Section: Autocmd setup {{{1 +augroup GnuPG + au! + + " First make sure nothing is written to ~/.viminfo while editing + " an encrypted file. + autocmd BufNewFile,BufReadPre,FileReadPre *.gpg set viminfo= + " We don't want a swap file, as it writes unencrypted data to disk + autocmd BufNewFile,BufReadPre,FileReadPre *.gpg set noswapfile + " Force the user to edit the recipient list if he opens a new file + autocmd BufNewFile *.gpg call s:GPGEditRecipients() + " Switch to binary mode to read the encrypted file + autocmd BufReadPre,FileReadPre *.gpg set bin + autocmd BufReadPost,FileReadPost *.gpg call s:GPGDecrypt() + " Switch to normal mode for editing + autocmd BufReadPost,FileReadPost *.gpg set nobin + " Call the autocommand for the file minus .gpg$ + autocmd BufReadPost,FileReadPost *.gpg execute ":doautocmd BufReadPost " . expand("%:r") + autocmd BufReadPost,FileReadPost *.gpg execute ":redraw!" + + " Switch to binary mode before encrypt the file + autocmd BufWritePre,FileWritePre *.gpg set bin + " Convert all text to encrypted text before writing + autocmd BufWritePre,FileWritePre *.gpg 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 silent u + " Switch back to normal mode for editing + autocmd BufWritePost,FileWritePost *.gpg set nobin +augroup END +" Section: Highlight setup {{{1 +highlight default GPGWarning term=reverse ctermfg=Yellow guifg=Yellow +highlight default GPGError term=reverse ctermfg=Red guifg=Red +highlight default GPGHighlightUnknownRecipient term=reverse ctermfg=Red cterm=underline guifg=Red gui=underline +" Section: Functions {{{1 +" Function: s:GPGDecrypt() {{{2 +" +" decrypt the buffer and find all recipients of the encrypted file +" +fun s:GPGDecrypt() + " get the filename of the current buffer + let filename=escape(expand("%:p"), ' *?\"'."'") + + " clear GPGRecipients, GPGUnknownRecipients and GPGOptions + let b:GPGRecipients="" + let b:GPGUnknownRecipients="" + let b:GPGOptions="" + + " find the recipients of the file + let output=system(s:gpgcommand . " --decrypt --dry-run --batch " . filename) + let start=match(output, "ID [[:xdigit:]]\\{8}", 0) + while (start >= 0) + let start=start+3 + let recipient=strpart(output, start, 8) + let name=s:GPGNameToID(recipient) + if (strlen(name) > 0) + let b:GPGRecipients=b:GPGRecipients . name . ":" + else + let b:GPGUnknownRecipients=b:GPGUnknownRecipients . recipient . ":" + echohl GPGWarning + echo "The recipient " . recipient . " is not in your public keyring!" + echohl None + end + let start=match(output, "ID [[:xdigit:]]\\{8}", start) + endw + + "echo "GPGRecipients=\"" . b:GPGRecipients . "\"" + + " Find out if the message is armored + if (stridx(getline(1), "-----BEGIN PGP MESSAGE-----") >= 0) + let b:GPGOptions=b:GPGOptions . "armor:" + endi + + " finally decrypt the buffer content + " since even with the --quiet option passphrase typos will be reported, + " we must redirect stderr (using sh temporarily) + let shsave=&sh + let &sh='sh' + exec "'[,']!" . s:gpgcommand . " --quiet --decrypt 2>/dev/null" + let &sh=shsave + if (v:shell_error) " message could not be decrypted + silent u + echohl GPGError + let asd=input("Message could not be decrypted! (Press ENTER)") + echohl None + bwipeout + return + endi +endf + +" Function: s:GPGEncrypt() {{{2 +" +" encrypts the buffer to all previous recipients +" +fun s:GPGEncrypt() + let options="" + let recipients="" + let field=0 + + " built list of options + if (exists("b:GPGOptions")) + let field=0 + let option=s:GetField(b:GPGOptions, ":", field) + while (strlen(option)) + let options=options . " --" . option . " " + let field=field+1 + let option=s:GetField(b:GPGOptions, ":", field) + endw + endi + + " check if there are unknown recipients and warn + if (exists("b:GPGUnknownRecipients")) + if (strlen(b:GPGUnknownRecipients) > 0) + echohl GPGWarning + echo "There are unknown recipients!!" + echo "Please use GPGEditRecipients to correct!!" + echohl None + endi + endi + + " built list of recipients + if (exists("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 + else + echohl GPGWarning + echo "There are no recipients!!" + echo "Please use GPGEditRecipients to correct!!" + echohl None + endi + + " encrypt the buffer + let shsave=&sh + let &sh='sh' + exec "'[,']!" . s:gpgcommand . " --quiet --no-encrypt-to --encrypt " . options . recipients . " 2>/dev/null" + let &sh=shsave + + redraw! +endf + +" Function: s:GPGViewRecipients() {{{2 +" +" echo the recipients +" +fun s:GPGViewRecipients() + 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 field=field+1 + let name=s:GetField(b:GPGRecipients, ":", field) + endw + + " 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 + + let field=field+1 + let name=s:GetField(b:GPGUnknownRecipients, ":", field) + endw + 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 + +" Function: s:GPGEditRecipients() {{{2 +" +" create a scratch buffer with all recipients to add/remove recipients +" +fun s:GPGEditRecipients() + " only do this if it isn't already a GPGRecipients_* buffer + if (match(bufname("%"), "GPGRecipients_") != 0 && match(bufname("%"), "\.gpg$") >= 0) + + " save buffer name + let buffername=bufname("%") + let editbuffername="GPGRecipients_" . buffername + + " create scratch buffer + exe 'silent! split ' . editbuffername + + " check if this buffer exists + if (bufexists(editbuffername)) + " empty the buffer + silent normal! 1GdG + endi + + " Mark the buffer as a scratch buffer + setlocal buftype=nofile + setlocal noswapfile + setlocal nowrap + setlocal nobuflisted + setlocal nonumber + + " so we know for which other buffer this edit buffer is + let b:corresponding_to=buffername + + " put some comments to the scratch buffer + silent put ='GPG: ----------------------------------------------------------------------' + silent put ='GPG: Please edit the list of recipients, one recipient per line' + silent put ='GPG: Unknown recipients have a prepended \"!\"' + silent put ='GPG: Lines beginning with \"GPG:\" are removed automatically' + silent put ='GPG: Use :x or :bd to close this buffer' + silent put ='GPG: ----------------------------------------------------------------------' + + " put the recipients in the scratch buffer + let recipients=getbufvar(b:corresponding_to, "GPGRecipients") + let field=0 + + let name=s:GetField(recipients, ":", field) + while (strlen(name) > 0) + let name=s:GPGIDToName(name) + silent put =name + + let field=field+1 + let name=s:GetField(recipients, ":", field) + endw + + " 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 name="!" . name + let syntaxPattern=syntaxPattern . "\\|" . name + silent put =name + + let field=field+1 + let name=s:GetField(unknownRecipients, ":", field) + endw + + let syntaxPattern=syntaxPattern . "\\)" + + " define highlight + if (has("syntax") && exists("g:syntax_on")) + exec('syntax match GPGUnknownRecipient "' . syntaxPattern . '"') + highlight clear GPGUnknownRecipient + highlight link GPGUnknownRecipient GPGHighlightUnknownRecipient + + syntax match GPGComment "^GPG:.*$" + highlight clear GPGComment + highlight link GPGComment Comment + endi + + " delete the empty first line + silent normal! 1Gdd + + " jump to the first recipient + silent normal! 6G + + " add a autocommand to regenerate the recipients after a write + augroup GPGEditRecipients + augroup END + execute 'au GPGEditRecipients BufHidden ' . editbuffername . ' call s:GPGFinishRecipientsBuffer()' + + endi + +endf + +" Function: s:GPGFinishRecipientsBuffer() {{{2 +" +" create a new recipient list from RecipientsBuffer +fun s:GPGFinishRecipientsBuffer() + " clear GPGRecipients and GPGUnknownRecipients + let GPGRecipients="" + let GPGUnknownRecipients="" + + " delete the autocommand + exe "au! GPGEditRecipients * " . bufname("%") + + 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 recipient=substitute(recipient, "^[[:space:]!]*\\(.\\{-}\\)[[:space:]]*$", "\\1", "") + " delete comment lines + let recipient=substitute(recipient, "^GPG:.*$", "", "") + + " only do this if the line is not empty + if (strlen(recipient) > 0) + let gpgid=s:GPGNameToID(recipient) + if (strlen(gpgid) > 0) + let GPGRecipients=GPGRecipients . gpgid . ":" + else + let GPGUnknownRecipients=GPGUnknownRecipients . recipient . ":" + echohl GPGWarning + echo "The recipient " . recipient . " is not in your public keyring!" + echohl None + end + endi + + let currentline=currentline+1 + let recipient=getline(currentline) + endw + + " write back the new recipient list to the corresponding buffer and mark it + " as modified + call setbufvar(b:corresponding_to, "GPGRecipients", GPGRecipients) + call setbufvar(b:corresponding_to, "GPGUnknownRecipients", GPGUnknownRecipients) + call setbufvar(b:corresponding_to, "&mod", 1) + "echo "GPGRecipients=\"" . getbufvar(b:corresponding_to, "GPGRecipients") . "\"" + + " check if there is any known recipient + if (strlen(s:GetField(GPGRecipients, ":", 0)) == 0) + echohl GPGError + echo 'There are no known recipients!' + echohl None + endi + + +endf + +" Function: s:GPGNameToID(name) {{{2 +" +" find GPG key ID corresponding to a name +" Returns: ID for the given name +fun s:GPGNameToID(name) + " ask gpg for the id for a name + let output=system(s:gpgcommand . " --quiet --with-colons --fixed-list-mode --list-keys \"" . a:name . "\"") + + " parse the output of gpg + let pub_seen=0 + let uid_seen=0 + let line=0 + let counter=0 + 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)) + " 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 + else + let uid_seen=0 + let pub_seen=0 + endi + endi + + " 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 + + let line=line+1 + let linecontent=s:GetField(output, "\n", line) + endw + + " counter > 1 means we have more than one results + let answer=0 + if (counter > 1) + let choices=choices . "Enter number: " + let answer=input(choices, "0") + while (answer == "") + let answer=input("Enter number: ", "0") + endw + endi + + return s:GetField(gpgids, ":", answer) +endf + +" Function: s:GPGIDToName(identity) {{{2 +" +" find name corresponding to a GPG key ID +" Returns: Name for the given ID +fun s:GPGIDToName(identity) + " TODO is the encryption subkey really unique? + + " ask gpg for the id for a name + let output=system(s:gpgcommand . " --quiet --with-colons --fixed-list-mode --list-keys " . a:identity ) + + " 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 + + let line=line+1 + let linecontent=s:GetField(output, "\n", line) + endw + + 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 +" Section: Command definitions {{{1 +com! GPGViewRecipients call s:GPGViewRecipients() +com! GPGEditRecipients call s:GPGEditRecipients() + +" vim600: set foldmethod=marker: