summaryrefslogtreecommitdiff
path: root/src/main.c
blob: ef7896950605ae99fdd867d9cfdc80f84e5c5108 (plain)
    1 // GPGEdit edits GPG encrypted files
    2 // Copyright (C) 2018  Aaron Ball <nullspoon@oper.io>
    3 //
    4 // This program is free software: you can redistribute it and/or modify
    5 // it under the terms of the GNU General Public License as published by
    6 // the Free Software Foundation, either version 3 of the License, or
    7 // (at your option) any later version.
    8 //
    9 // This program is distributed in the hope that it will be useful,
   10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
   11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12 // GNU General Public License for more details.
   13 //
   14 // You should have received a copy of the GNU General Public License
   15 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
   16 
   17 #include <stdio.h>
   18 #include <stdlib.h>
   19 #include <string.h>
   20 #include <unistd.h>
   21 #include <gpgme.h>
   22 
   23 #include "gpg.h"
   24 #include "gpgedit_config.h"
   25 #include "strll.h"
   26 #include "logger.h"
   27 
   28 void system_edit(char* file) {
   29   char cmd[256]; // Buffer for editor command
   30   char* editor;  // Editor to use when editing the file
   31 
   32   // Read the environment variable for "EDITOR". If null, default to vim.
   33   editor = getenv("EDITOR");
   34   if(editor == NULL)
   35     editor = "vim";
   36 
   37   sprintf(cmd, "%s %s\n", editor, file);
   38   system(cmd);
   39 }
   40 
   41 
   42 void usage() {
   43   const char* helptext = 
   44   "GPGEdit is an editor wrapper for GPG encrypted files. It will decrypt the\n"
   45   "specified text file to a secure location in memory, and open the specified\n"
   46   "editor. Upon closing the editor, the unencrypted copy in memory is\n"
   47   "re-encrypted, written back to the original encrypted location, and the\n"
   48   "decrypted copy is deleted from memory.\n\n"
   49   "NOTE: If power is lost while editing a file, all changes will be lost as\n"
   50   "they are stored exclusively in memory.\n"
   51   "\nUsage:\n\n"
   52   "  gpgedit [-r user@host] <file.gpg>\n"
   53   "\nArguments:\n"
   54   "  -h,--help        Print this help text\n"
   55   "  -q,--quiet       Quiet output (only error and fatal)\n"
   56   "  -r,--recipient   Adds a recipient key to the encrypted output\n"
   57   "  -s,--silent      No output (only if you have good karma)\n"
   58   "  -v,--verbose     Verbose output (debug level)\n";
   59 
   60   printf("%s\n", helptext);
   61 }
   62 
   63 
   64 void parseargs(int argc, char* argv[], struct gpgedit_config* config) {
   65   int i = 1;
   66 
   67   while(i < argc) {
   68     if(strcmp(argv[i], "-r") == 0 || strcmp(argv[i], "--recipient") == 0) {
   69       i++;
   70       logger(LOG_DEBUG, "Adding recipient %s\n", argv[i]);
   71       strll_add(config->recipients, argv[i]);
   72     } else if(strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
   73       config->log_level = LOG_DEBUG;
   74     } else if(strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quiet") == 0) {
   75       config->log_level = LOG_ERROR;
   76     } else if(strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--silent") == 0) {
   77       config->log_level = -1;
   78     } else if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
   79       usage();
   80       exit(0);
   81     } else {
   82       gpgedit_config_file_set(config, argv[i]);
   83     }
   84     i++;
   85   }
   86 }
   87 
   88 
   89 int main(int argc, char* argv[]) {
   90   char  tmpfile[32] = "/tmp/gpgedit-XXXXXX";
   91   gpgme_ctx_t decctx;
   92   gpgme_error_t err;
   93   struct gpgedit_config* config;
   94 
   95   config = gpgedit_config_new();
   96   parseargs(argc, argv, config);
   97   // Initialize the logger
   98   logger_init(config->log_level);
   99 
  100   if(config->file == NULL) {
  101     logger(LOG_INFO, "Please specify a gpg encrypted file to edit\n");
  102     return 1;
  103   }
  104 
  105 
  106   // Check if fd opened. If not, the specified file doesn't exist, so we need
  107   // to create it.
  108   FILE* fd = fopen(config->file, "r");
  109   if(!fd) {
  110     logger(LOG_INFO, "File '%s' does not exist. Creating.\n", config->file);
  111     mkstemp(tmpfile);
  112   } else {
  113     // File exists, close the fd for testing existance
  114     fclose(fd);
  115 
  116     // Initialize decryption context, create tmp file to write decrypted contents
  117     // to, and run decryption operation.
  118     err = init_gpg(&decctx, GPGME_PROTOCOL_OPENPGP);
  119     if(err != GPG_ERR_NO_ERROR) {
  120       logger(LOG_ERROR, "%s\n", gpgme_strerror(err));
  121       return 1;
  122     }
  123 
  124     mkstemp(tmpfile);
  125     gpgme_decrypt_result_t res = gpg_decrypt_file(config->file, tmpfile, decctx);
  126     // Get keyids array from recipients list
  127     gpgedit_config_recip_from_gpgme_recip(config, res->recipients);
  128     // Freedom!
  129     gpgme_release(decctx);
  130   }
  131 
  132   // We can't do anything if the recipient list is empty (eg: new file but no
  133   // recipients specified).
  134   if(config->recipients->str == NULL) {
  135     logger(LOG_INFO, "Recipient list empty. Cannot proceed.\n");
  136   } else {
  137     // Open the system editor
  138     system_edit(tmpfile);
  139 
  140     // Re-encrypt the plaintext tmp file
  141     logger(LOG_DEBUG,
  142            "Re-encrypting file '%s' -> '%s'\n",
  143            tmpfile,
  144            config->file);
  145     gpg_encrypt_file(tmpfile, config->file, config->recipients);
  146   }
  147 
  148   // Clean up tmpfile
  149   logger(LOG_DEBUG, "Releasing config memory\n");
  150   gpgedit_config_release(config);
  151   logger(LOG_DEBUG, "Unlinking temp file '%s'\n", tmpfile);
  152   unlink(tmpfile);
  153   return 0;
  154 }

Generated by cgit