#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <unistd.h>
#include<apt-pkg/configuration.h>
#include<apt-pkg/error.h>
#include <apti18n.h>
/*}}}*/
-char * GenerateTemporaryFileTemplate(const char *basename) /*{{{*/
+static char * GenerateTemporaryFileTemplate(const char *basename) /*{{{*/
{
- const char *tmpdir = getenv("TMPDIR");
-#ifdef P_tmpdir
- if (!tmpdir)
- tmpdir = P_tmpdir;
-#endif
- if (!tmpdir)
- tmpdir = "/tmp";
-
std::string out;
- strprintf(out, "%s/%s.XXXXXX", tmpdir, basename);
+ std::string tmpdir = GetTempDir();
+ strprintf(out, "%s/%s.XXXXXX", tmpdir.c_str(), basename);
return strdup(out.c_str());
}
/*}}}*/
Also, as gpgv has no options to enforce a certain reduced style of
clear-signed files (=the complete content of the file is signed and
the content isn't encoded) we do a divide and conquer approach here
+ and split up the clear-signed file in message and signature for gpgv
*/
void ExecGPGV(std::string const &File, std::string const &FileGPG,
int const &statusfd, int fd[2])
}
}
- int sigFd = -1;
- int dataFd = -1;
std::vector<std::string> dataHeader;
char * sig = NULL;
char * data = NULL;
data = GenerateTemporaryFileTemplate("apt.data");
if (sig == NULL || data == NULL)
{
+ ioprintf(std::cerr, "Couldn't create tempfile names for splitting up %s", File.c_str());
+ exit(EINTERNAL);
+ }
+
+ int const sigFd = mkstemp(sig);
+ int const dataFd = mkstemp(data);
+ if (sigFd == -1 || dataFd == -1)
+ {
+ if (dataFd != -1)
+ unlink(sig);
+ if (sigFd != -1)
+ unlink(data);
ioprintf(std::cerr, "Couldn't create tempfiles for splitting up %s", File.c_str());
exit(EINTERNAL);
}
- sigFd = mkstemp(sig);
- dataFd = mkstemp(data);
- int const duppedSigFd = dup(sigFd);
- int const duppedDataFd = dup(dataFd);
+ FileFd signature;
+ signature.OpenDescriptor(sigFd, FileFd::WriteOnly, true);
+ FileFd message;
+ message.OpenDescriptor(dataFd, FileFd::WriteOnly, true);
- if (dataFd == -1 || sigFd == -1 || duppedDataFd == -1 || duppedSigFd == -1 ||
- SplitClearSignedFile(File, duppedDataFd, &dataHeader, duppedSigFd) == false)
+ if (signature.Failed() == true || message.Failed() == true ||
+ SplitClearSignedFile(File, &message, &dataHeader, &signature) == false)
{
if (dataFd != -1)
unlink(sig);
if (sigFd != -1)
unlink(data);
ioprintf(std::cerr, "Splitting up %s into data and signature failed", File.c_str());
- exit(EINTERNAL);
+ exit(112);
}
- lseek(dataFd, 0, SEEK_SET);
- lseek(sigFd, 0, SEEK_SET);
Args.push_back(sig);
Args.push_back(data);
}
ioprintf(std::cerr, _("Waited for %s but it wasn't there"), "gpgv");
UNLINK_EXIT(EINTERNAL);
}
+#undef UNLINK_EXIT
+ // we don't need the files any longer
+ unlink(sig);
+ unlink(data);
+ free(sig);
+ free(data);
// check if it exit'ed normally …
if (WIFEXITED(Status) == false)
{
ioprintf(std::cerr, _("Sub-process %s exited unexpectedly"), "gpgv");
- UNLINK_EXIT(EINTERNAL);
+ exit(EINTERNAL);
}
// … and with a good exit code
if (WEXITSTATUS(Status) != 0)
{
ioprintf(std::cerr, _("Sub-process %s returned an error code (%u)"), "gpgv", WEXITSTATUS(Status));
- UNLINK_EXIT(WEXITSTATUS(Status));
+ exit(WEXITSTATUS(Status));
}
- /* looks like its fine. Our caller will check the status fd,
- but we construct a good-known clear-signed file without garbage
- and other non-sense. In a perfect world, we get the same file,
- but empty lines, trailing whitespaces and stuff makes it inperfect … */
- if (RecombineToClearSignedFile(File, dataFd, dataHeader, sigFd) == false)
- {
- _error->DumpErrors(std::cerr);
- UNLINK_EXIT(EINTERNAL);
- }
-
- // everything fine, we have a clean file now!
- UNLINK_EXIT(0);
-#undef UNLINK_EXIT
+ // everything fine
+ exit(0);
}
exit(EINTERNAL); // unreachable safe-guard
}
/*}}}*/
-// RecombineToClearSignedFile - combine data/signature to message /*{{{*/
-bool RecombineToClearSignedFile(std::string const &OutFile, int const ContentFile,
- std::vector<std::string> const &ContentHeader, int const SignatureFile)
-{
- FILE *clean_file = fopen(OutFile.c_str(), "w");
- fputs("-----BEGIN PGP SIGNED MESSAGE-----\n", clean_file);
- for (std::vector<std::string>::const_iterator h = ContentHeader.begin(); h != ContentHeader.end(); ++h)
- fprintf(clean_file, "%s\n", h->c_str());
- fputs("\n", clean_file);
-
- FILE *data_file = fdopen(ContentFile, "r");
- FILE *sig_file = fdopen(SignatureFile, "r");
- if (data_file == NULL || sig_file == NULL)
- return _error->Error("Couldn't open splitfiles to recombine them into %s", OutFile.c_str());
- char *buf = NULL;
- size_t buf_size = 0;
- while (getline(&buf, &buf_size, data_file) != -1)
- fputs(buf, clean_file);
- fclose(data_file);
- fputs("\n", clean_file);
- while (getline(&buf, &buf_size, sig_file) != -1)
- fputs(buf, clean_file);
- fclose(sig_file);
- fclose(clean_file);
- return true;
-}
- /*}}}*/
// SplitClearSignedFile - split message into data/signature /*{{{*/
-bool SplitClearSignedFile(std::string const &InFile, int const ContentFile,
- std::vector<std::string> * const ContentHeader, int const SignatureFile)
+bool SplitClearSignedFile(std::string const &InFile, FileFd * const ContentFile,
+ std::vector<std::string> * const ContentHeader, FileFd * const SignatureFile)
{
FILE *in = fopen(InFile.c_str(), "r");
if (in == NULL)
return _error->Errno("fopen", "can not open %s", InFile.c_str());
- FILE *out_content = NULL;
- FILE *out_signature = NULL;
- if (ContentFile != -1)
- {
- out_content = fdopen(ContentFile, "w");
- if (out_content == NULL)
- return _error->Errno("fdopen", "Failed to open file to write content to from %s", InFile.c_str());
- }
- if (SignatureFile != -1)
- {
- out_signature = fdopen(SignatureFile, "w");
- if (out_signature == NULL)
- return _error->Errno("fdopen", "Failed to open file to write signature to from %s", InFile.c_str());
- }
-
bool found_message_start = false;
bool found_message_end = false;
bool skip_until_empty_line = false;
char *buf = NULL;
size_t buf_size = 0;
- while (getline(&buf, &buf_size, in) != -1)
+ ssize_t line_len = 0;
+ while ((line_len = getline(&buf, &buf_size, in)) != -1)
{
_strrstrip(buf);
if (found_message_start == false)
{
found_signature = true;
found_message_end = true;
- if (out_signature != NULL)
- fprintf(out_signature, "%s\n", buf);
+ if (SignatureFile != NULL)
+ {
+ SignatureFile->Write(buf, strlen(buf));
+ SignatureFile->Write("\n", 1);
+ }
}
- else if (found_message_end == false)
+ else if (found_message_end == false) // we are in the message block
{
- // we are in the message block
+ // we don't have any fields which need dash-escaped,
+ // but implementations are free to encode all lines …
+ char const * dashfree = buf;
+ if (strncmp(dashfree, "- ", 2) == 0)
+ dashfree += 2;
if(first_line == true) // first line does not need a newline
- {
- if (out_content != NULL)
- fprintf(out_content, "%s", buf);
first_line = false;
- }
- else if (out_content != NULL)
- fprintf(out_content, "\n%s", buf);
+ else if (ContentFile != NULL)
+ ContentFile->Write("\n", 1);
+ else
+ continue;
+ if (ContentFile != NULL)
+ ContentFile->Write(dashfree, strlen(dashfree));
}
}
else if (found_signature == true)
{
- if (out_signature != NULL)
- fprintf(out_signature, "%s\n", buf);
+ if (SignatureFile != NULL)
+ {
+ SignatureFile->Write(buf, strlen(buf));
+ SignatureFile->Write("\n", 1);
+ }
if (strcmp(buf, "-----END PGP SIGNATURE-----") == 0)
found_signature = false; // look for other signatures
}
// all the rest is whitespace, unsigned garbage or additional message blocks we ignore
}
- if (out_content != NULL)
- fclose(out_content);
- if (out_signature != NULL)
- fclose(out_signature);
+ fclose(in);
+
+ if (found_signature == true)
+ return _error->Error("Signature in file %s wasn't closed", InFile.c_str());
+
+ // if we haven't found any of them, this an unsigned file,
+ // so don't generate an error, but splitting was unsuccessful none-the-less
+ if (first_line == true && found_message_start == false && found_message_end == false)
+ return false;
+ // otherwise one missing indicates a syntax error
+ else if (first_line == true || found_message_start == false || found_message_end == false)
+ return _error->Error("Splitting of file %s failed as it doesn't contain all expected parts %i %i %i", InFile.c_str(), first_line, found_message_start, found_message_end);
return true;
}
+ /*}}}*/
+bool OpenMaybeClearSignedFile(std::string const &ClearSignedFileName, FileFd &MessageFile) /*{{{*/
+{
+ char * const message = GenerateTemporaryFileTemplate("fileutl.message");
+ int const messageFd = mkstemp(message);
+ if (messageFd == -1)
+ {
+ free(message);
+ return _error->Errno("mkstemp", "Couldn't create temporary file to work with %s", ClearSignedFileName.c_str());
+ }
+ // we have the fd, thats enough for us
+ unlink(message);
+ free(message);
+
+ MessageFile.OpenDescriptor(messageFd, FileFd::ReadWrite, true);
+ if (MessageFile.Failed() == true)
+ return _error->Error("Couldn't open temporary file to work with %s", ClearSignedFileName.c_str());
+
+ _error->PushToStack();
+ bool const splitDone = SplitClearSignedFile(ClearSignedFileName.c_str(), &MessageFile, NULL, NULL);
+ bool const errorDone = _error->PendingError();
+ _error->MergeWithStack();
+ if (splitDone == false)
+ {
+ MessageFile.Close();
+
+ if (errorDone == true)
+ return false;
+
+ // we deal with an unsigned file
+ MessageFile.Open(ClearSignedFileName, FileFd::ReadOnly);
+ }
+ else // clear-signed
+ {
+ if (MessageFile.Seek(0) == false)
+ return _error->Errno("lseek", "Unable to seek back in message for file %s", ClearSignedFileName.c_str());
+ }
+
+ return MessageFile.Failed() == false;
+}
+ /*}}}*/