Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34109876
rungpg.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
43 KB
Subscribers
None
rungpg.c
View Options
/* rungpg.c - Gpg Engine.
Copyright (C) 2000 Werner Koch (dd9jn)
Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 g10 Code GmbH
This file is part of GPGME.
GPGME is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.
GPGME is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
#if HAVE_CONFIG_H
#include
<config.h>
#endif
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
<assert.h>
#include
<errno.h>
#include
"gpgme.h"
#include
"util.h"
#include
"ops.h"
#include
"wait.h"
#include
"context.h"
/*temp hack until we have GpmeData methods to do I/O */
#include
"priv-io.h"
#include
"sema.h"
#include
"debug.h"
#include
"status-table.h"
#include
"engine-backend.h"
/* This type is used to build a list of gpg arguments and data
sources/sinks. */
struct
arg_and_data_s
{
struct
arg_and_data_s
*
next
;
gpgme_data_t
data
;
/* If this is not NULL, use arg below. */
int
inbound
;
/* True if this is used for reading from gpg. */
int
dup_to
;
int
print_fd
;
/* Print the fd number and not the special form of it. */
char
arg
[
1
];
/* Used if data above is not used. */
};
struct
fd_data_map_s
{
gpgme_data_t
data
;
int
inbound
;
/* true if this is used for reading from gpg */
int
dup_to
;
int
fd
;
/* the fd to use */
int
peer_fd
;
/* the outher side of the pipe */
void
*
tag
;
};
typedef
gpgme_error_t
(
*
colon_preprocessor_t
)
(
char
*
line
,
char
**
rline
);
struct
engine_gpg
{
char
*
file_name
;
struct
arg_and_data_s
*
arglist
;
struct
arg_and_data_s
**
argtail
;
struct
{
int
fd
[
2
];
size_t
bufsize
;
char
*
buffer
;
size_t
readpos
;
int
eof
;
engine_status_handler_t
fnc
;
void
*
fnc_value
;
void
*
tag
;
}
status
;
/* This is a kludge - see the comment at colon_line_handler. */
struct
{
int
fd
[
2
];
size_t
bufsize
;
char
*
buffer
;
size_t
readpos
;
int
eof
;
engine_colon_line_handler_t
fnc
;
/* this indicate use of this structrue */
void
*
fnc_value
;
void
*
tag
;
colon_preprocessor_t
preprocess_fnc
;
}
colon
;
char
**
argv
;
struct
fd_data_map_s
*
fd_data_map
;
/* stuff needed for interactive (command) mode */
struct
{
int
used
;
int
fd
;
void
*
cb_data
;
int
idx
;
/* Index in fd_data_map */
gpgme_status_code_t
code
;
/* last code */
char
*
keyword
;
/* what has been requested (malloced) */
engine_command_handler_t
fnc
;
void
*
fnc_value
;
/* The kludges never end. This is used to couple command handlers
with output data in edit key mode. */
gpgme_data_t
linked_data
;
int
linked_idx
;
}
cmd
;
struct
gpgme_io_cbs
io_cbs
;
};
typedef
struct
engine_gpg
*
engine_gpg_t
;
static
void
gpg_io_event
(
void
*
engine
,
gpgme_event_io_t
type
,
void
*
type_data
)
{
engine_gpg_t
gpg
=
engine
;
if
(
gpg
->
io_cbs
.
event
)
(
*
gpg
->
io_cbs
.
event
)
(
gpg
->
io_cbs
.
event_priv
,
type
,
type_data
);
}
static
void
close_notify_handler
(
int
fd
,
void
*
opaque
)
{
engine_gpg_t
gpg
=
opaque
;
assert
(
fd
!=
-1
);
if
(
gpg
->
status
.
fd
[
0
]
==
fd
)
{
if
(
gpg
->
status
.
tag
)
(
*
gpg
->
io_cbs
.
remove
)
(
gpg
->
status
.
tag
);
gpg
->
status
.
fd
[
0
]
=
-1
;
}
else
if
(
gpg
->
status
.
fd
[
1
]
==
fd
)
gpg
->
status
.
fd
[
1
]
=
-1
;
else
if
(
gpg
->
colon
.
fd
[
0
]
==
fd
)
{
if
(
gpg
->
colon
.
tag
)
(
*
gpg
->
io_cbs
.
remove
)
(
gpg
->
colon
.
tag
);
gpg
->
colon
.
fd
[
0
]
=
-1
;
}
else
if
(
gpg
->
colon
.
fd
[
1
]
==
fd
)
gpg
->
colon
.
fd
[
1
]
=
-1
;
else
if
(
gpg
->
fd_data_map
)
{
int
i
;
for
(
i
=
0
;
gpg
->
fd_data_map
[
i
].
data
;
i
++
)
{
if
(
gpg
->
fd_data_map
[
i
].
fd
==
fd
)
{
if
(
gpg
->
fd_data_map
[
i
].
tag
)
(
*
gpg
->
io_cbs
.
remove
)
(
gpg
->
fd_data_map
[
i
].
tag
);
gpg
->
fd_data_map
[
i
].
fd
=
-1
;
break
;
}
if
(
gpg
->
fd_data_map
[
i
].
peer_fd
==
fd
)
{
gpg
->
fd_data_map
[
i
].
peer_fd
=
-1
;
break
;
}
}
}
}
static
gpgme_error_t
add_arg
(
engine_gpg_t
gpg
,
const
char
*
arg
)
{
struct
arg_and_data_s
*
a
;
assert
(
gpg
);
assert
(
arg
);
a
=
malloc
(
sizeof
*
a
+
strlen
(
arg
));
if
(
!
a
)
return
gpg_error_from_errno
(
errno
);
a
->
next
=
NULL
;
a
->
data
=
NULL
;
a
->
dup_to
=
-1
;
strcpy
(
a
->
arg
,
arg
);
*
gpg
->
argtail
=
a
;
gpg
->
argtail
=
&
a
->
next
;
return
0
;
}
static
gpgme_error_t
add_data
(
engine_gpg_t
gpg
,
gpgme_data_t
data
,
int
dup_to
,
int
inbound
)
{
struct
arg_and_data_s
*
a
;
assert
(
gpg
);
assert
(
data
);
a
=
malloc
(
sizeof
*
a
-
1
);
if
(
!
a
)
return
gpg_error_from_errno
(
errno
);
a
->
next
=
NULL
;
a
->
data
=
data
;
a
->
inbound
=
inbound
;
if
(
dup_to
==
-2
)
{
a
->
print_fd
=
1
;
a
->
dup_to
=
-1
;
}
else
{
a
->
print_fd
=
0
;
a
->
dup_to
=
dup_to
;
}
*
gpg
->
argtail
=
a
;
gpg
->
argtail
=
&
a
->
next
;
return
0
;
}
static
char
*
gpg_get_version
(
const
char
*
file_name
)
{
return
_gpgme_get_program_version
(
file_name
?
file_name
:
_gpgme_get_gpg_path
());
}
static
const
char
*
gpg_get_req_version
(
void
)
{
return
NEED_GPG_VERSION
;
}
static
void
free_argv
(
char
**
argv
)
{
int
i
;
for
(
i
=
0
;
argv
[
i
];
i
++
)
free
(
argv
[
i
]);
free
(
argv
);
}
static
void
free_fd_data_map
(
struct
fd_data_map_s
*
fd_data_map
)
{
int
i
;
if
(
!
fd_data_map
)
return
;
for
(
i
=
0
;
fd_data_map
[
i
].
data
;
i
++
)
{
if
(
fd_data_map
[
i
].
fd
!=
-1
)
_gpgme_io_close
(
fd_data_map
[
i
].
fd
);
if
(
fd_data_map
[
i
].
peer_fd
!=
-1
)
_gpgme_io_close
(
fd_data_map
[
i
].
peer_fd
);
/* Don't release data because this is only a reference. */
}
free
(
fd_data_map
);
}
static
gpgme_error_t
gpg_cancel
(
void
*
engine
)
{
engine_gpg_t
gpg
=
engine
;
if
(
!
gpg
)
return
gpg_error
(
GPG_ERR_INV_VALUE
);
if
(
gpg
->
status
.
fd
[
0
]
!=
-1
)
_gpgme_io_close
(
gpg
->
status
.
fd
[
0
]);
if
(
gpg
->
status
.
fd
[
1
]
!=
-1
)
_gpgme_io_close
(
gpg
->
status
.
fd
[
1
]);
if
(
gpg
->
colon
.
fd
[
0
]
!=
-1
)
_gpgme_io_close
(
gpg
->
colon
.
fd
[
0
]);
if
(
gpg
->
colon
.
fd
[
1
]
!=
-1
)
_gpgme_io_close
(
gpg
->
colon
.
fd
[
1
]);
if
(
gpg
->
fd_data_map
)
{
free_fd_data_map
(
gpg
->
fd_data_map
);
gpg
->
fd_data_map
=
NULL
;
}
if
(
gpg
->
cmd
.
fd
!=
-1
)
_gpgme_io_close
(
gpg
->
cmd
.
fd
);
return
0
;
}
static
void
gpg_release
(
void
*
engine
)
{
engine_gpg_t
gpg
=
engine
;
if
(
!
gpg
)
return
;
gpg_cancel
(
engine
);
if
(
gpg
->
file_name
)
free
(
gpg
->
file_name
);
while
(
gpg
->
arglist
)
{
struct
arg_and_data_s
*
next
=
gpg
->
arglist
->
next
;
if
(
gpg
->
arglist
)
free
(
gpg
->
arglist
);
gpg
->
arglist
=
next
;
}
if
(
gpg
->
status
.
buffer
)
free
(
gpg
->
status
.
buffer
);
if
(
gpg
->
colon
.
buffer
)
free
(
gpg
->
colon
.
buffer
);
if
(
gpg
->
argv
)
free_argv
(
gpg
->
argv
);
if
(
gpg
->
cmd
.
keyword
)
free
(
gpg
->
cmd
.
keyword
);
free
(
gpg
);
}
static
gpgme_error_t
gpg_new
(
void
**
engine
,
const
char
*
file_name
,
const
char
*
home_dir
,
const
char
*
lc_ctype
,
const
char
*
lc_messages
)
{
engine_gpg_t
gpg
;
gpgme_error_t
rc
=
0
;
gpg
=
calloc
(
1
,
sizeof
*
gpg
);
if
(
!
gpg
)
return
gpg_error_from_errno
(
errno
);
if
(
file_name
)
{
gpg
->
file_name
=
strdup
(
file_name
);
if
(
!
gpg
->
file_name
)
{
rc
=
gpg_error_from_errno
(
errno
);
goto
leave
;
}
}
gpg
->
argtail
=
&
gpg
->
arglist
;
gpg
->
status
.
fd
[
0
]
=
-1
;
gpg
->
status
.
fd
[
1
]
=
-1
;
gpg
->
colon
.
fd
[
0
]
=
-1
;
gpg
->
colon
.
fd
[
1
]
=
-1
;
gpg
->
cmd
.
fd
=
-1
;
gpg
->
cmd
.
idx
=
-1
;
gpg
->
cmd
.
linked_data
=
NULL
;
gpg
->
cmd
.
linked_idx
=
-1
;
/* Allocate the read buffer for the status pipe. */
gpg
->
status
.
bufsize
=
1024
;
gpg
->
status
.
readpos
=
0
;
gpg
->
status
.
buffer
=
malloc
(
gpg
->
status
.
bufsize
);
if
(
!
gpg
->
status
.
buffer
)
{
rc
=
gpg_error_from_errno
(
errno
);
goto
leave
;
}
/* In any case we need a status pipe - create it right here and
don't handle it with our generic gpgme_data_t mechanism. */
if
(
_gpgme_io_pipe
(
gpg
->
status
.
fd
,
1
)
==
-1
)
{
rc
=
gpg_error_from_errno
(
errno
);
goto
leave
;
}
if
(
_gpgme_io_set_close_notify
(
gpg
->
status
.
fd
[
0
],
close_notify_handler
,
gpg
)
||
_gpgme_io_set_close_notify
(
gpg
->
status
.
fd
[
1
],
close_notify_handler
,
gpg
))
{
rc
=
gpg_error
(
GPG_ERR_GENERAL
);
goto
leave
;
}
gpg
->
status
.
eof
=
0
;
if
(
home_dir
)
{
rc
=
add_arg
(
gpg
,
"--homedir"
);
if
(
!
rc
)
rc
=
add_arg
(
gpg
,
home_dir
);
if
(
rc
)
goto
leave
;
}
rc
=
add_arg
(
gpg
,
"--status-fd"
);
if
(
rc
)
goto
leave
;
{
char
buf
[
25
];
_gpgme_io_fd2str
(
buf
,
sizeof
(
buf
),
gpg
->
status
.
fd
[
1
]);
rc
=
add_arg
(
gpg
,
buf
);
if
(
rc
)
goto
leave
;
}
rc
=
add_arg
(
gpg
,
"--no-tty"
);
if
(
!
rc
)
rc
=
add_arg
(
gpg
,
"--charset"
);
if
(
!
rc
)
rc
=
add_arg
(
gpg
,
"utf8"
);
if
(
!
rc
)
rc
=
add_arg
(
gpg
,
"--enable-progress-filter"
);
leave
:
if
(
rc
)
gpg_release
(
gpg
);
else
*
engine
=
gpg
;
return
rc
;
}
/* Note, that the status_handler is allowed to modifiy the args
value. */
static
void
gpg_set_status_handler
(
void
*
engine
,
engine_status_handler_t
fnc
,
void
*
fnc_value
)
{
engine_gpg_t
gpg
=
engine
;
gpg
->
status
.
fnc
=
fnc
;
gpg
->
status
.
fnc_value
=
fnc_value
;
}
/* Kludge to process --with-colon output. */
static
gpgme_error_t
gpg_set_colon_line_handler
(
void
*
engine
,
engine_colon_line_handler_t
fnc
,
void
*
fnc_value
)
{
engine_gpg_t
gpg
=
engine
;
gpg
->
colon
.
bufsize
=
1024
;
gpg
->
colon
.
readpos
=
0
;
gpg
->
colon
.
buffer
=
malloc
(
gpg
->
colon
.
bufsize
);
if
(
!
gpg
->
colon
.
buffer
)
return
gpg_error_from_errno
(
errno
);
if
(
_gpgme_io_pipe
(
gpg
->
colon
.
fd
,
1
)
==
-1
)
{
int
saved_errno
=
errno
;
free
(
gpg
->
colon
.
buffer
);
gpg
->
colon
.
buffer
=
NULL
;
return
gpg_error_from_errno
(
saved_errno
);
}
if
(
_gpgme_io_set_close_notify
(
gpg
->
colon
.
fd
[
0
],
close_notify_handler
,
gpg
)
||
_gpgme_io_set_close_notify
(
gpg
->
colon
.
fd
[
1
],
close_notify_handler
,
gpg
))
return
gpg_error
(
GPG_ERR_GENERAL
);
gpg
->
colon
.
eof
=
0
;
gpg
->
colon
.
fnc
=
fnc
;
gpg
->
colon
.
fnc_value
=
fnc_value
;
return
0
;
}
static
gpgme_error_t
command_handler
(
void
*
opaque
,
int
fd
)
{
gpgme_error_t
err
;
engine_gpg_t
gpg
=
(
engine_gpg_t
)
opaque
;
int
processed
=
0
;
assert
(
gpg
->
cmd
.
used
);
assert
(
gpg
->
cmd
.
code
);
assert
(
gpg
->
cmd
.
fnc
);
err
=
gpg
->
cmd
.
fnc
(
gpg
->
cmd
.
fnc_value
,
gpg
->
cmd
.
code
,
gpg
->
cmd
.
keyword
,
fd
,
&
processed
);
if
(
err
)
return
err
;
/* We always need to send at least a newline character. */
if
(
!
processed
)
_gpgme_io_write
(
fd
,
"
\n
"
,
1
);
gpg
->
cmd
.
code
=
0
;
/* And sleep again until read_status will wake us up again. */
/* XXX We must check if there are any more fds active after removing
this one. */
(
*
gpg
->
io_cbs
.
remove
)
(
gpg
->
fd_data_map
[
gpg
->
cmd
.
idx
].
tag
);
gpg
->
cmd
.
fd
=
gpg
->
fd_data_map
[
gpg
->
cmd
.
idx
].
fd
;
gpg
->
fd_data_map
[
gpg
->
cmd
.
idx
].
fd
=
-1
;
return
0
;
}
/* The Fnc will be called to get a value for one of the commands with
a key KEY. If the Code pssed to FNC is 0, the function may release
resources associated with the returned value from another call. To
match such a second call to a first call, the returned value from
the first call is passed as keyword. */
static
gpgme_error_t
gpg_set_command_handler
(
void
*
engine
,
engine_command_handler_t
fnc
,
void
*
fnc_value
,
gpgme_data_t
linked_data
)
{
engine_gpg_t
gpg
=
engine
;
gpgme_error_t
rc
;
rc
=
add_arg
(
gpg
,
"--command-fd"
);
if
(
rc
)
return
rc
;
/* This is a hack. We don't have a real data object. The only
thing that matters is that we use something unique, so we use the
address of the cmd structure in the gpg object. */
rc
=
add_data
(
gpg
,
(
void
*
)
&
gpg
->
cmd
,
-2
,
0
);
if
(
rc
)
return
rc
;
gpg
->
cmd
.
fnc
=
fnc
;
gpg
->
cmd
.
cb_data
=
(
void
*
)
&
gpg
->
cmd
;
gpg
->
cmd
.
fnc_value
=
fnc_value
;
gpg
->
cmd
.
linked_data
=
linked_data
;
gpg
->
cmd
.
used
=
1
;
return
0
;
}
static
gpgme_error_t
build_argv
(
engine_gpg_t
gpg
)
{
gpgme_error_t
err
;
struct
arg_and_data_s
*
a
;
struct
fd_data_map_s
*
fd_data_map
;
size_t
datac
=
0
,
argc
=
0
;
char
**
argv
;
int
need_special
=
0
;
int
use_agent
=
0
;
char
*
p
;
/* We don't want to use the agent with a malformed environment
variable. This is only a very basic test but sufficient to make
our life in the regression tests easier. */
err
=
_gpgme_getenv
(
"GPG_AGENT_INFO"
,
&
p
);
if
(
err
)
return
err
;
use_agent
=
(
p
&&
strchr
(
p
,
':'
));
if
(
p
)
free
(
p
);
if
(
gpg
->
argv
)
{
free_argv
(
gpg
->
argv
);
gpg
->
argv
=
NULL
;
}
if
(
gpg
->
fd_data_map
)
{
free_fd_data_map
(
gpg
->
fd_data_map
);
gpg
->
fd_data_map
=
NULL
;
}
argc
++
;
/* For argv[0]. */
for
(
a
=
gpg
->
arglist
;
a
;
a
=
a
->
next
)
{
argc
++
;
if
(
a
->
data
)
{
/*fprintf (stderr, "build_argv: data\n" );*/
datac
++
;
if
(
a
->
dup_to
==
-1
&&
!
a
->
print_fd
)
need_special
=
1
;
}
else
{
/* fprintf (stderr, "build_argv: arg=`%s'\n", a->arg );*/
}
}
if
(
need_special
)
argc
++
;
if
(
use_agent
)
argc
++
;
if
(
!
gpg
->
cmd
.
used
)
argc
++
;
/* --batch */
argc
+=
1
;
/* --no-sk-comment */
argv
=
calloc
(
argc
+
1
,
sizeof
*
argv
);
if
(
!
argv
)
return
gpg_error_from_errno
(
errno
);
fd_data_map
=
calloc
(
datac
+
1
,
sizeof
*
fd_data_map
);
if
(
!
fd_data_map
)
{
int
saved_errno
=
errno
;
free_argv
(
argv
);
return
gpg_error_from_errno
(
saved_errno
);
}
argc
=
datac
=
0
;
argv
[
argc
]
=
strdup
(
"gpg"
);
/* argv[0] */
if
(
!
argv
[
argc
])
{
int
saved_errno
=
errno
;
free
(
fd_data_map
);
free_argv
(
argv
);
return
gpg_error_from_errno
(
saved_errno
);
}
argc
++
;
if
(
need_special
)
{
argv
[
argc
]
=
strdup
(
"--enable-special-filenames"
);
if
(
!
argv
[
argc
])
{
int
saved_errno
=
errno
;
free
(
fd_data_map
);
free_argv
(
argv
);
return
gpg_error_from_errno
(
saved_errno
);
}
argc
++
;
}
if
(
use_agent
)
{
argv
[
argc
]
=
strdup
(
"--use-agent"
);
if
(
!
argv
[
argc
])
{
int
saved_errno
=
errno
;
free
(
fd_data_map
);
free_argv
(
argv
);
return
gpg_error_from_errno
(
saved_errno
);
}
argc
++
;
}
if
(
!
gpg
->
cmd
.
used
)
{
argv
[
argc
]
=
strdup
(
"--batch"
);
if
(
!
argv
[
argc
])
{
int
saved_errno
=
errno
;
free
(
fd_data_map
);
free_argv
(
argv
);
return
gpg_error_from_errno
(
saved_errno
);
}
argc
++
;
}
argv
[
argc
]
=
strdup
(
"--no-sk-comment"
);
if
(
!
argv
[
argc
])
{
int
saved_errno
=
errno
;
free
(
fd_data_map
);
free_argv
(
argv
);
return
gpg_error_from_errno
(
saved_errno
);
}
argc
++
;
for
(
a
=
gpg
->
arglist
;
a
;
a
=
a
->
next
)
{
if
(
a
->
data
)
{
/* Create a pipe to pass it down to gpg. */
fd_data_map
[
datac
].
inbound
=
a
->
inbound
;
/* Create a pipe. */
{
int
fds
[
2
];
if
(
_gpgme_io_pipe
(
fds
,
fd_data_map
[
datac
].
inbound
?
1
:
0
)
==
-1
)
{
int
saved_errno
=
errno
;
free
(
fd_data_map
);
free_argv
(
argv
);
return
gpg_error
(
saved_errno
);
}
if
(
_gpgme_io_set_close_notify
(
fds
[
0
],
close_notify_handler
,
gpg
)
||
_gpgme_io_set_close_notify
(
fds
[
1
],
close_notify_handler
,
gpg
))
{
return
gpg_error
(
GPG_ERR_GENERAL
);
}
/* If the data_type is FD, we have to do a dup2 here. */
if
(
fd_data_map
[
datac
].
inbound
)
{
fd_data_map
[
datac
].
fd
=
fds
[
0
];
fd_data_map
[
datac
].
peer_fd
=
fds
[
1
];
}
else
{
fd_data_map
[
datac
].
fd
=
fds
[
1
];
fd_data_map
[
datac
].
peer_fd
=
fds
[
0
];
}
}
/* Hack to get hands on the fd later. */
if
(
gpg
->
cmd
.
used
)
{
if
(
gpg
->
cmd
.
cb_data
==
a
->
data
)
{
assert
(
gpg
->
cmd
.
idx
==
-1
);
gpg
->
cmd
.
idx
=
datac
;
}
else
if
(
gpg
->
cmd
.
linked_data
==
a
->
data
)
{
assert
(
gpg
->
cmd
.
linked_idx
==
-1
);
gpg
->
cmd
.
linked_idx
=
datac
;
}
}
fd_data_map
[
datac
].
data
=
a
->
data
;
fd_data_map
[
datac
].
dup_to
=
a
->
dup_to
;
if
(
a
->
dup_to
==
-1
)
{
char
*
ptr
;
int
buflen
=
25
;
argv
[
argc
]
=
malloc
(
buflen
);
if
(
!
argv
[
argc
])
{
int
saved_errno
=
errno
;
free
(
fd_data_map
);
free_argv
(
argv
);
return
gpg_error_from_errno
(
saved_errno
);
}
ptr
=
argv
[
argc
];
if
(
!
a
->
print_fd
)
{
*
(
ptr
++
)
=
'-'
;
*
(
ptr
++
)
=
'&'
;
buflen
-=
2
;
}
_gpgme_io_fd2str
(
ptr
,
buflen
,
fd_data_map
[
datac
].
peer_fd
);
argc
++
;
}
datac
++
;
}
else
{
argv
[
argc
]
=
strdup
(
a
->
arg
);
if
(
!
argv
[
argc
])
{
int
saved_errno
=
errno
;
free
(
fd_data_map
);
free_argv
(
argv
);
return
gpg_error_from_errno
(
saved_errno
);
}
argc
++
;
}
}
gpg
->
argv
=
argv
;
gpg
->
fd_data_map
=
fd_data_map
;
return
0
;
}
static
gpgme_error_t
add_io_cb
(
engine_gpg_t
gpg
,
int
fd
,
int
dir
,
gpgme_io_cb_t
handler
,
void
*
data
,
void
**
tag
)
{
gpgme_error_t
err
;
err
=
(
*
gpg
->
io_cbs
.
add
)
(
gpg
->
io_cbs
.
add_priv
,
fd
,
dir
,
handler
,
data
,
tag
);
if
(
err
)
return
err
;
if
(
!
dir
)
/* FIXME Kludge around poll() problem. */
err
=
_gpgme_io_set_nonblocking
(
fd
);
return
err
;
}
static
int
status_cmp
(
const
void
*
ap
,
const
void
*
bp
)
{
const
struct
status_table_s
*
a
=
ap
;
const
struct
status_table_s
*
b
=
bp
;
return
strcmp
(
a
->
name
,
b
->
name
);
}
/* Handle the status output of GnuPG. This function does read entire
lines and passes them as C strings to the callback function (we can
use C Strings because the status output is always UTF-8 encoded).
Of course we have to buffer the lines to cope with long lines
e.g. with a large user ID. Note: We can optimize this to only cope
with status line code we know about and skip all other stuff
without buffering (i.e. without extending the buffer). */
static
gpgme_error_t
read_status
(
engine_gpg_t
gpg
)
{
char
*
p
;
int
nread
;
size_t
bufsize
=
gpg
->
status
.
bufsize
;
char
*
buffer
=
gpg
->
status
.
buffer
;
size_t
readpos
=
gpg
->
status
.
readpos
;
assert
(
buffer
);
if
(
bufsize
-
readpos
<
256
)
{
/* Need more room for the read. */
bufsize
+=
1024
;
buffer
=
realloc
(
buffer
,
bufsize
);
if
(
!
buffer
)
return
gpg_error_from_errno
(
errno
);
}
nread
=
_gpgme_io_read
(
gpg
->
status
.
fd
[
0
],
buffer
+
readpos
,
bufsize
-
readpos
);
if
(
nread
==
-1
)
return
gpg_error_from_errno
(
errno
);
if
(
!
nread
)
{
gpg
->
status
.
eof
=
1
;
if
(
gpg
->
status
.
fnc
)
{
gpgme_error_t
err
;
err
=
gpg
->
status
.
fnc
(
gpg
->
status
.
fnc_value
,
GPGME_STATUS_EOF
,
""
);
if
(
err
)
return
err
;
}
return
0
;
}
while
(
nread
>
0
)
{
for
(
p
=
buffer
+
readpos
;
nread
;
nread
--
,
p
++
)
{
if
(
*
p
==
'\n'
)
{
/* (we require that the last line is terminated by a LF) */
*
p
=
0
;
if
(
!
strncmp
(
buffer
,
"[GNUPG:] "
,
9
)
&&
buffer
[
9
]
>=
'A'
&&
buffer
[
9
]
<=
'Z'
)
{
struct
status_table_s
t
,
*
r
;
char
*
rest
;
rest
=
strchr
(
buffer
+
9
,
' '
);
if
(
!
rest
)
rest
=
p
;
/* Set to an empty string. */
else
*
rest
++
=
0
;
t
.
name
=
buffer
+
9
;
/* (the status table has one extra element) */
r
=
bsearch
(
&
t
,
status_table
,
DIM
(
status_table
)
-
1
,
sizeof
t
,
status_cmp
);
if
(
r
)
{
if
(
gpg
->
cmd
.
used
&&
(
r
->
code
==
GPGME_STATUS_GET_BOOL
||
r
->
code
==
GPGME_STATUS_GET_LINE
||
r
->
code
==
GPGME_STATUS_GET_HIDDEN
))
{
gpg
->
cmd
.
code
=
r
->
code
;
if
(
gpg
->
cmd
.
keyword
)
free
(
gpg
->
cmd
.
keyword
);
gpg
->
cmd
.
keyword
=
strdup
(
rest
);
if
(
!
gpg
->
cmd
.
keyword
)
return
gpg_error_from_errno
(
errno
);
/* This should be the last thing we have
received and the next thing will be that
the command handler does its action. */
if
(
nread
>
1
)
DEBUG0
(
"ERROR, unexpected data in read_status"
);
add_io_cb
(
gpg
,
gpg
->
cmd
.
fd
,
0
,
command_handler
,
gpg
,
&
gpg
->
fd_data_map
[
gpg
->
cmd
.
idx
].
tag
);
gpg
->
fd_data_map
[
gpg
->
cmd
.
idx
].
fd
=
gpg
->
cmd
.
fd
;
gpg
->
cmd
.
fd
=
-1
;
}
else
if
(
gpg
->
status
.
fnc
)
{
gpgme_error_t
err
;
err
=
gpg
->
status
.
fnc
(
gpg
->
status
.
fnc_value
,
r
->
code
,
rest
);
if
(
err
)
return
err
;
}
if
(
r
->
code
==
GPGME_STATUS_END_STREAM
)
{
if
(
gpg
->
cmd
.
used
)
{
/* Before we can actually add the
command fd, we might have to flush
the linked output data pipe. */
if
(
gpg
->
cmd
.
linked_idx
!=
-1
&&
gpg
->
fd_data_map
[
gpg
->
cmd
.
linked_idx
].
fd
!=
-1
)
{
struct
io_select_fd_s
fds
;
fds
.
fd
=
gpg
->
fd_data_map
[
gpg
->
cmd
.
linked_idx
].
fd
;
fds
.
for_read
=
1
;
fds
.
for_write
=
0
;
fds
.
frozen
=
0
;
fds
.
opaque
=
NULL
;
do
{
fds
.
signaled
=
0
;
_gpgme_io_select
(
&
fds
,
1
,
1
);
if
(
fds
.
signaled
)
_gpgme_data_inbound_handler
(
gpg
->
cmd
.
linked_data
,
fds
.
fd
);
}
while
(
fds
.
signaled
);
}
/* XXX We must check if there are any
more fds active after removing this
one. */
(
*
gpg
->
io_cbs
.
remove
)
(
gpg
->
fd_data_map
[
gpg
->
cmd
.
idx
].
tag
);
gpg
->
cmd
.
fd
=
gpg
->
fd_data_map
[
gpg
->
cmd
.
idx
].
fd
;
gpg
->
fd_data_map
[
gpg
->
cmd
.
idx
].
fd
=
-1
;
}
}
}
}
/* To reuse the buffer for the next line we have to
shift the remaining data to the buffer start and
restart the loop Hmmm: We can optimize this function
by looking forward in the buffer to see whether a
second complete line is available and in this case
avoid the memmove for this line. */
nread
--
;
p
++
;
if
(
nread
)
memmove
(
buffer
,
p
,
nread
);
readpos
=
0
;
break
;
/* the for loop */
}
else
readpos
++
;
}
}
/* Update the gpg object. */
gpg
->
status
.
bufsize
=
bufsize
;
gpg
->
status
.
buffer
=
buffer
;
gpg
->
status
.
readpos
=
readpos
;
return
0
;
}
static
gpgme_error_t
status_handler
(
void
*
opaque
,
int
fd
)
{
engine_gpg_t
gpg
=
opaque
;
int
err
;
assert
(
fd
==
gpg
->
status
.
fd
[
0
]);
err
=
read_status
(
gpg
);
if
(
err
)
return
err
;
if
(
gpg
->
status
.
eof
)
_gpgme_io_close
(
fd
);
return
0
;
}
static
gpgme_error_t
read_colon_line
(
engine_gpg_t
gpg
)
{
char
*
p
;
int
nread
;
size_t
bufsize
=
gpg
->
colon
.
bufsize
;
char
*
buffer
=
gpg
->
colon
.
buffer
;
size_t
readpos
=
gpg
->
colon
.
readpos
;
assert
(
buffer
);
if
(
bufsize
-
readpos
<
256
)
{
/* Need more room for the read. */
bufsize
+=
1024
;
buffer
=
realloc
(
buffer
,
bufsize
);
if
(
!
buffer
)
return
gpg_error_from_errno
(
errno
);
}
nread
=
_gpgme_io_read
(
gpg
->
colon
.
fd
[
0
],
buffer
+
readpos
,
bufsize
-
readpos
);
if
(
nread
==
-1
)
return
gpg_error_from_errno
(
errno
);
if
(
!
nread
)
{
gpg
->
colon
.
eof
=
1
;
assert
(
gpg
->
colon
.
fnc
);
gpg
->
colon
.
fnc
(
gpg
->
colon
.
fnc_value
,
NULL
);
return
0
;
}
while
(
nread
>
0
)
{
for
(
p
=
buffer
+
readpos
;
nread
;
nread
--
,
p
++
)
{
if
(
*
p
==
'\n'
)
{
/* (we require that the last line is terminated by a LF)
and we skip empty lines. Note: we use UTF8 encoding
and escaping of special characters. We require at
least one colon to cope with some other printed
information. */
*
p
=
0
;
if
(
*
buffer
&&
strchr
(
buffer
,
':'
))
{
char
*
line
=
NULL
;
if
(
gpg
->
colon
.
preprocess_fnc
)
{
gpgme_error_t
err
;
err
=
gpg
->
colon
.
preprocess_fnc
(
buffer
,
&
line
);
if
(
err
)
return
err
;
}
assert
(
gpg
->
colon
.
fnc
);
gpg
->
colon
.
fnc
(
gpg
->
colon
.
fnc_value
,
line
?
line
:
buffer
);
if
(
line
)
free
(
line
);
}
/* To reuse the buffer for the next line we have to
shift the remaining data to the buffer start and
restart the loop Hmmm: We can optimize this function
by looking forward in the buffer to see whether a
second complete line is available and in this case
avoid the memmove for this line. */
nread
--
;
p
++
;
if
(
nread
)
memmove
(
buffer
,
p
,
nread
);
readpos
=
0
;
break
;
/* The for loop. */
}
else
readpos
++
;
}
}
/* Update the gpg object. */
gpg
->
colon
.
bufsize
=
bufsize
;
gpg
->
colon
.
buffer
=
buffer
;
gpg
->
colon
.
readpos
=
readpos
;
return
0
;
}
/* This colonline handler thing is not the clean way to do it. It
might be better to enhance the gpgme_data_t object to act as a wrapper
for a callback. Same goes for the status thing. For now we use
this thing here because it is easier to implement. */
static
gpgme_error_t
colon_line_handler
(
void
*
opaque
,
int
fd
)
{
engine_gpg_t
gpg
=
opaque
;
gpgme_error_t
rc
=
0
;
assert
(
fd
==
gpg
->
colon
.
fd
[
0
]);
rc
=
read_colon_line
(
gpg
);
if
(
rc
)
return
rc
;
if
(
gpg
->
colon
.
eof
)
_gpgme_io_close
(
fd
);
return
0
;
}
static
gpgme_error_t
start
(
engine_gpg_t
gpg
)
{
gpgme_error_t
rc
;
int
saved_errno
;
int
i
,
n
;
int
status
;
struct
spawn_fd_item_s
*
fd_child_list
,
*
fd_parent_list
;
if
(
!
gpg
)
return
gpg_error
(
GPG_ERR_INV_VALUE
);
if
(
!
gpg
->
file_name
&&
!
_gpgme_get_gpg_path
())
return
gpg_error
(
GPG_ERR_INV_ENGINE
);
rc
=
build_argv
(
gpg
);
if
(
rc
)
return
rc
;
n
=
3
;
/* status_fd, colon_fd and end of list */
for
(
i
=
0
;
gpg
->
fd_data_map
[
i
].
data
;
i
++
)
n
++
;
fd_child_list
=
calloc
(
n
+
n
,
sizeof
*
fd_child_list
);
if
(
!
fd_child_list
)
return
gpg_error_from_errno
(
errno
);
fd_parent_list
=
fd_child_list
+
n
;
/* build the fd list for the child */
n
=
0
;
if
(
gpg
->
colon
.
fnc
)
{
fd_child_list
[
n
].
fd
=
gpg
->
colon
.
fd
[
1
];
fd_child_list
[
n
].
dup_to
=
1
;
/* dup to stdout */
n
++
;
}
for
(
i
=
0
;
gpg
->
fd_data_map
[
i
].
data
;
i
++
)
{
if
(
gpg
->
fd_data_map
[
i
].
dup_to
!=
-1
)
{
fd_child_list
[
n
].
fd
=
gpg
->
fd_data_map
[
i
].
peer_fd
;
fd_child_list
[
n
].
dup_to
=
gpg
->
fd_data_map
[
i
].
dup_to
;
n
++
;
}
}
fd_child_list
[
n
].
fd
=
-1
;
fd_child_list
[
n
].
dup_to
=
-1
;
/* Build the fd list for the parent. */
n
=
0
;
if
(
gpg
->
status
.
fd
[
1
]
!=
-1
)
{
fd_parent_list
[
n
].
fd
=
gpg
->
status
.
fd
[
1
];
fd_parent_list
[
n
].
dup_to
=
-1
;
n
++
;
}
if
(
gpg
->
colon
.
fd
[
1
]
!=
-1
)
{
fd_parent_list
[
n
].
fd
=
gpg
->
colon
.
fd
[
1
];
fd_parent_list
[
n
].
dup_to
=
-1
;
n
++
;
}
for
(
i
=
0
;
gpg
->
fd_data_map
[
i
].
data
;
i
++
)
{
fd_parent_list
[
n
].
fd
=
gpg
->
fd_data_map
[
i
].
peer_fd
;
fd_parent_list
[
n
].
dup_to
=
-1
;
n
++
;
}
fd_parent_list
[
n
].
fd
=
-1
;
fd_parent_list
[
n
].
dup_to
=
-1
;
status
=
_gpgme_io_spawn
(
gpg
->
file_name
?
gpg
->
file_name
:
_gpgme_get_gpg_path
(),
gpg
->
argv
,
fd_child_list
,
fd_parent_list
);
saved_errno
=
errno
;
free
(
fd_child_list
);
if
(
status
==
-1
)
return
gpg_error_from_errno
(
saved_errno
);
/*_gpgme_register_term_handler ( closure, closure_value, pid );*/
rc
=
add_io_cb
(
gpg
,
gpg
->
status
.
fd
[
0
],
1
,
status_handler
,
gpg
,
&
gpg
->
status
.
tag
);
if
(
rc
)
/* FIXME: kill the child */
return
rc
;
if
(
gpg
->
colon
.
fnc
)
{
assert
(
gpg
->
colon
.
fd
[
0
]
!=
-1
);
rc
=
add_io_cb
(
gpg
,
gpg
->
colon
.
fd
[
0
],
1
,
colon_line_handler
,
gpg
,
&
gpg
->
colon
.
tag
);
if
(
rc
)
/* FIXME: kill the child */
return
rc
;
}
for
(
i
=
0
;
gpg
->
fd_data_map
[
i
].
data
;
i
++
)
{
if
(
gpg
->
cmd
.
used
&&
i
==
gpg
->
cmd
.
idx
)
{
/* Park the cmd fd. */
gpg
->
cmd
.
fd
=
gpg
->
fd_data_map
[
i
].
fd
;
gpg
->
fd_data_map
[
i
].
fd
=
-1
;
}
else
{
rc
=
add_io_cb
(
gpg
,
gpg
->
fd_data_map
[
i
].
fd
,
gpg
->
fd_data_map
[
i
].
inbound
,
gpg
->
fd_data_map
[
i
].
inbound
?
_gpgme_data_inbound_handler
:
_gpgme_data_outbound_handler
,
gpg
->
fd_data_map
[
i
].
data
,
&
gpg
->
fd_data_map
[
i
].
tag
);
if
(
rc
)
/* FIXME: kill the child */
return
rc
;
}
}
(
*
gpg
->
io_cbs
.
event
)
(
gpg
->
io_cbs
.
event_priv
,
GPGME_EVENT_START
,
NULL
);
/* fixme: check what data we can release here */
return
0
;
}
static
gpgme_error_t
gpg_decrypt
(
void
*
engine
,
gpgme_data_t
ciph
,
gpgme_data_t
plain
)
{
engine_gpg_t
gpg
=
engine
;
gpgme_error_t
err
;
err
=
add_arg
(
gpg
,
"--decrypt"
);
/* Tell the gpg object about the data. */
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--output"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"-"
);
if
(
!
err
)
err
=
add_data
(
gpg
,
plain
,
1
,
1
);
if
(
!
err
)
err
=
add_data
(
gpg
,
ciph
,
0
,
0
);
if
(
!
err
)
start
(
gpg
);
return
err
;
}
static
gpgme_error_t
gpg_delete
(
void
*
engine
,
gpgme_key_t
key
,
int
allow_secret
)
{
engine_gpg_t
gpg
=
engine
;
gpgme_error_t
err
;
err
=
add_arg
(
gpg
,
allow_secret
?
"--delete-secret-and-public-key"
:
"--delete-key"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--"
);
if
(
!
err
)
{
if
(
!
key
->
subkeys
||
!
key
->
subkeys
->
fpr
)
return
gpg_error
(
GPG_ERR_INV_VALUE
);
else
err
=
add_arg
(
gpg
,
key
->
subkeys
->
fpr
);
}
if
(
!
err
)
start
(
gpg
);
return
err
;
}
static
gpgme_error_t
append_args_from_signers
(
engine_gpg_t
gpg
,
gpgme_ctx_t
ctx
/* FIXME */
)
{
gpgme_error_t
err
=
0
;
int
i
;
gpgme_key_t
key
;
for
(
i
=
0
;
(
key
=
gpgme_signers_enum
(
ctx
,
i
));
i
++
)
{
const
char
*
s
=
key
->
subkeys
?
key
->
subkeys
->
keyid
:
NULL
;
if
(
s
)
{
if
(
!
err
)
err
=
add_arg
(
gpg
,
"-u"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
s
);
}
gpgme_key_unref
(
key
);
if
(
err
)
break
;
}
return
err
;
}
static
gpgme_error_t
append_args_from_sig_notations
(
engine_gpg_t
gpg
,
gpgme_ctx_t
ctx
/* FIXME */
)
{
gpgme_error_t
err
=
0
;
gpgme_sig_notation_t
notation
;
notation
=
gpgme_sig_notation_get
(
ctx
);
while
(
!
err
&&
notation
)
{
if
(
notation
->
name
&&
!
(
notation
->
flags
&
GPGME_SIG_NOTATION_HUMAN_READABLE
))
err
=
gpg_error
(
GPG_ERR_INV_VALUE
);
else
if
(
notation
->
name
)
{
char
*
arg
;
/* Maximum space needed is one byte for the "critical" flag,
the name, one byte for '=', the value, and a terminating
'\0'. */
arg
=
malloc
(
1
+
notation
->
name_len
+
1
+
notation
->
value_len
+
1
);
if
(
!
arg
)
err
=
gpg_error_from_errno
(
errno
);
if
(
!
err
)
{
char
*
argp
=
arg
;
if
(
notation
->
critical
)
*
(
argp
++
)
=
'!'
;
memcpy
(
argp
,
notation
->
name
,
notation
->
name_len
);
argp
+=
notation
->
name_len
;
*
(
argp
++
)
=
'='
;
/* We know that notation->name is '\0' terminated. */
strcpy
(
argp
,
notation
->
value
);
}
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--sig-notation"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
arg
);
if
(
arg
)
free
(
arg
);
}
else
{
/* This is a policy URL. */
char
*
value
;
if
(
notation
->
critical
)
{
value
=
malloc
(
1
+
notation
->
value_len
+
1
);
if
(
!
value
)
err
=
gpg_error_from_errno
(
errno
);
else
{
value
[
0
]
=
'!'
;
/* We know that notation->value is '\0' terminated. */
strcpy
(
&
value
[
1
],
notation
->
value
);
}
}
else
value
=
notation
->
value
;
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--sig-policy-url"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
value
);
if
(
value
!=
notation
->
value
)
free
(
value
);
}
notation
=
notation
->
next
;
}
return
err
;
}
static
gpgme_error_t
gpg_edit
(
void
*
engine
,
int
type
,
gpgme_key_t
key
,
gpgme_data_t
out
,
gpgme_ctx_t
ctx
/* FIXME */
)
{
engine_gpg_t
gpg
=
engine
;
gpgme_error_t
err
;
err
=
add_arg
(
gpg
,
"--with-colons"
);
if
(
!
err
)
err
=
append_args_from_signers
(
gpg
,
ctx
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
type
==
0
?
"--edit-key"
:
"--card-edit"
);
if
(
!
err
)
err
=
add_data
(
gpg
,
out
,
1
,
1
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--"
);
if
(
!
err
&&
type
==
0
)
{
const
char
*
s
=
key
->
subkeys
?
key
->
subkeys
->
fpr
:
NULL
;
if
(
!
s
)
err
=
gpg_error
(
GPG_ERR_INV_VALUE
);
else
err
=
add_arg
(
gpg
,
s
);
}
if
(
!
err
)
err
=
start
(
gpg
);
return
err
;
}
static
gpgme_error_t
append_args_from_recipients
(
engine_gpg_t
gpg
,
gpgme_key_t
recp
[])
{
gpgme_error_t
err
=
0
;
int
i
=
0
;
while
(
recp
[
i
])
{
if
(
!
recp
[
i
]
->
subkeys
||
!
recp
[
i
]
->
subkeys
->
fpr
)
err
=
gpg_error
(
GPG_ERR_INV_VALUE
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"-r"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
recp
[
i
]
->
subkeys
->
fpr
);
if
(
err
)
break
;
i
++
;
}
return
err
;
}
static
gpgme_error_t
gpg_encrypt
(
void
*
engine
,
gpgme_key_t
recp
[],
gpgme_encrypt_flags_t
flags
,
gpgme_data_t
plain
,
gpgme_data_t
ciph
,
int
use_armor
)
{
engine_gpg_t
gpg
=
engine
;
gpgme_error_t
err
;
int
symmetric
=
!
recp
;
err
=
add_arg
(
gpg
,
symmetric
?
"--symmetric"
:
"--encrypt"
);
if
(
!
err
&&
use_armor
)
err
=
add_arg
(
gpg
,
"--armor"
);
if
(
!
symmetric
)
{
/* If we know that all recipients are valid (full or ultimate trust)
we can suppress further checks. */
if
(
!
err
&&
!
symmetric
&&
(
flags
&
GPGME_ENCRYPT_ALWAYS_TRUST
))
err
=
add_arg
(
gpg
,
"--always-trust"
);
if
(
!
err
)
err
=
append_args_from_recipients
(
gpg
,
recp
);
}
/* Tell the gpg object about the data. */
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--output"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"-"
);
if
(
!
err
)
err
=
add_data
(
gpg
,
ciph
,
1
,
1
);
if
(
gpgme_data_get_file_name
(
plain
))
{
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--set-filename"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
gpgme_data_get_file_name
(
plain
));
}
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--"
);
if
(
!
err
)
err
=
add_data
(
gpg
,
plain
,
0
,
0
);
if
(
!
err
)
err
=
start
(
gpg
);
return
err
;
}
static
gpgme_error_t
gpg_encrypt_sign
(
void
*
engine
,
gpgme_key_t
recp
[],
gpgme_encrypt_flags_t
flags
,
gpgme_data_t
plain
,
gpgme_data_t
ciph
,
int
use_armor
,
gpgme_ctx_t
ctx
/* FIXME */
)
{
engine_gpg_t
gpg
=
engine
;
gpgme_error_t
err
;
err
=
add_arg
(
gpg
,
"--encrypt"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--sign"
);
if
(
!
err
&&
use_armor
)
err
=
add_arg
(
gpg
,
"--armor"
);
/* If we know that all recipients are valid (full or ultimate trust)
we can suppress further checks. */
if
(
!
err
&&
(
flags
&
GPGME_ENCRYPT_ALWAYS_TRUST
))
err
=
add_arg
(
gpg
,
"--always-trust"
);
if
(
!
err
)
err
=
append_args_from_recipients
(
gpg
,
recp
);
if
(
!
err
)
err
=
append_args_from_signers
(
gpg
,
ctx
);
if
(
!
err
)
err
=
append_args_from_sig_notations
(
gpg
,
ctx
);
/* Tell the gpg object about the data. */
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--output"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"-"
);
if
(
!
err
)
err
=
add_data
(
gpg
,
ciph
,
1
,
1
);
if
(
gpgme_data_get_file_name
(
plain
))
{
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--set-filename"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
gpgme_data_get_file_name
(
plain
));
}
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--"
);
if
(
!
err
)
err
=
add_data
(
gpg
,
plain
,
0
,
0
);
if
(
!
err
)
err
=
start
(
gpg
);
return
err
;
}
static
gpgme_error_t
gpg_export
(
void
*
engine
,
const
char
*
pattern
,
unsigned
int
reserved
,
gpgme_data_t
keydata
,
int
use_armor
)
{
engine_gpg_t
gpg
=
engine
;
gpgme_error_t
err
;
if
(
reserved
)
return
gpg_error
(
GPG_ERR_INV_VALUE
);
err
=
add_arg
(
gpg
,
"--export"
);
if
(
!
err
&&
use_armor
)
err
=
add_arg
(
gpg
,
"--armor"
);
if
(
!
err
)
err
=
add_data
(
gpg
,
keydata
,
1
,
1
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--"
);
if
(
!
err
&&
pattern
&&
*
pattern
)
err
=
add_arg
(
gpg
,
pattern
);
if
(
!
err
)
err
=
start
(
gpg
);
return
err
;
}
static
gpgme_error_t
gpg_export_ext
(
void
*
engine
,
const
char
*
pattern
[],
unsigned
int
reserved
,
gpgme_data_t
keydata
,
int
use_armor
)
{
engine_gpg_t
gpg
=
engine
;
gpgme_error_t
err
;
if
(
reserved
)
return
gpg_error
(
GPG_ERR_INV_VALUE
);
err
=
add_arg
(
gpg
,
"--export"
);
if
(
!
err
&&
use_armor
)
err
=
add_arg
(
gpg
,
"--armor"
);
if
(
!
err
)
err
=
add_data
(
gpg
,
keydata
,
1
,
1
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--"
);
if
(
pattern
)
{
while
(
!
err
&&
*
pattern
&&
**
pattern
)
err
=
add_arg
(
gpg
,
*
(
pattern
++
));
}
if
(
!
err
)
err
=
start
(
gpg
);
return
err
;
}
static
gpgme_error_t
gpg_genkey
(
void
*
engine
,
gpgme_data_t
help_data
,
int
use_armor
,
gpgme_data_t
pubkey
,
gpgme_data_t
seckey
)
{
engine_gpg_t
gpg
=
engine
;
gpgme_error_t
err
;
if
(
!
gpg
)
return
gpg_error
(
GPG_ERR_INV_VALUE
);
/* We need a special mechanism to get the fd of a pipe here, so that
we can use this for the %pubring and %secring parameters. We
don't have this yet, so we implement only the adding to the
standard keyrings. */
if
(
pubkey
||
seckey
)
return
gpg_error
(
GPG_ERR_NOT_IMPLEMENTED
);
err
=
add_arg
(
gpg
,
"--gen-key"
);
if
(
!
err
&&
use_armor
)
err
=
add_arg
(
gpg
,
"--armor"
);
if
(
!
err
)
err
=
add_data
(
gpg
,
help_data
,
0
,
0
);
if
(
!
err
)
err
=
start
(
gpg
);
return
err
;
}
static
gpgme_error_t
gpg_import
(
void
*
engine
,
gpgme_data_t
keydata
)
{
engine_gpg_t
gpg
=
engine
;
gpgme_error_t
err
;
err
=
add_arg
(
gpg
,
"--import"
);
if
(
!
err
)
err
=
add_data
(
gpg
,
keydata
,
0
,
0
);
if
(
!
err
)
err
=
start
(
gpg
);
return
err
;
}
/* The output for external keylistings in GnuPG is different from all
the other key listings. We catch this here with a special
preprocessor that reformats the colon handler lines. */
static
gpgme_error_t
gpg_keylist_preprocess
(
char
*
line
,
char
**
r_line
)
{
enum
{
RT_NONE
,
RT_INFO
,
RT_PUB
,
RT_UID
}
rectype
=
RT_NONE
;
#define NR_FIELDS 16
char
*
field
[
NR_FIELDS
];
int
fields
=
0
;
*
r_line
=
NULL
;
while
(
line
&&
fields
<
NR_FIELDS
)
{
field
[
fields
++
]
=
line
;
line
=
strchr
(
line
,
':'
);
if
(
line
)
*
(
line
++
)
=
'\0'
;
}
if
(
!
strcmp
(
field
[
0
],
"info"
))
rectype
=
RT_INFO
;
else
if
(
!
strcmp
(
field
[
0
],
"pub"
))
rectype
=
RT_PUB
;
else
if
(
!
strcmp
(
field
[
0
],
"uid"
))
rectype
=
RT_UID
;
else
rectype
=
RT_NONE
;
switch
(
rectype
)
{
case
RT_INFO
:
/* FIXME: Eventually, check the version number at least. */
return
0
;
case
RT_PUB
:
if
(
fields
<
7
)
return
0
;
/* The format is:
pub:<keyid>:<algo>:<keylen>:<creationdate>:<expirationdate>:<flags>
as defined in 5.2. Machine Readable Indexes of the OpenPGP
HTTP Keyserver Protocol (draft).
We want:
pub:o<flags>:<keylen>:<algo>:<keyid>:<creatdate>:<expdate>::::::::
*/
if
(
asprintf
(
r_line
,
"pub:o%s:%s:%s:%s:%s:%s::::::::"
,
field
[
6
],
field
[
3
],
field
[
2
],
field
[
1
],
field
[
4
],
field
[
5
])
<
0
)
return
gpg_error_from_errno
(
errno
);
return
0
;
case
RT_UID
:
/* The format is:
uid:<escaped uid string>:<creationdate>:<expirationdate>:<flags>
as defined in 5.2. Machine Readable Indexes of the OpenPGP
HTTP Keyserver Protocol (draft).
We want:
uid:o<flags>::::<creatdate>:<expdate>:::<uid>:
*/
if
(
asprintf
(
r_line
,
"uid:o%s::::%s:%s:::%s:"
,
field
[
4
],
field
[
2
],
field
[
3
],
field
[
1
])
<
0
)
return
gpg_error_from_errno
(
errno
);
return
0
;
case
RT_NONE
:
/* Unknown record. */
break
;
}
return
0
;
}
static
gpgme_error_t
gpg_keylist
(
void
*
engine
,
const
char
*
pattern
,
int
secret_only
,
gpgme_keylist_mode_t
mode
)
{
engine_gpg_t
gpg
=
engine
;
gpgme_error_t
err
;
if
(
mode
&
GPGME_KEYLIST_MODE_EXTERN
)
{
if
((
mode
&
GPGME_KEYLIST_MODE_LOCAL
)
||
secret_only
)
return
gpg_error
(
GPG_ERR_NOT_SUPPORTED
);
}
err
=
add_arg
(
gpg
,
"--with-colons"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--fixed-list-mode"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--with-fingerprint"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--with-fingerprint"
);
if
(
!
err
&&
(
mode
&
GPGME_KEYLIST_MODE_SIGS
)
&&
(
mode
&
GPGME_KEYLIST_MODE_SIG_NOTATIONS
))
{
err
=
add_arg
(
gpg
,
"--list-options"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"show-sig-subpackets=
\"
20,26
\"
"
);
}
if
(
!
err
)
{
if
(
mode
&
GPGME_KEYLIST_MODE_EXTERN
)
{
err
=
add_arg
(
gpg
,
"--search-keys"
);
gpg
->
colon
.
preprocess_fnc
=
gpg_keylist_preprocess
;
}
else
{
err
=
add_arg
(
gpg
,
secret_only
?
"--list-secret-keys"
:
((
mode
&
GPGME_KEYLIST_MODE_SIGS
)
?
"--check-sigs"
:
"--list-keys"
));
}
}
/* Tell the gpg object about the data. */
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--"
);
if
(
!
err
&&
pattern
&&
*
pattern
)
err
=
add_arg
(
gpg
,
pattern
);
if
(
!
err
)
err
=
start
(
gpg
);
return
err
;
}
static
gpgme_error_t
gpg_keylist_ext
(
void
*
engine
,
const
char
*
pattern
[],
int
secret_only
,
int
reserved
,
gpgme_keylist_mode_t
mode
)
{
engine_gpg_t
gpg
=
engine
;
gpgme_error_t
err
;
if
(
reserved
)
return
gpg_error
(
GPG_ERR_INV_VALUE
);
err
=
add_arg
(
gpg
,
"--with-colons"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--fixed-list-mode"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--with-fingerprint"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--with-fingerprint"
);
if
(
!
err
&&
(
mode
&
GPGME_KEYLIST_MODE_SIGS
)
&&
(
mode
&
GPGME_KEYLIST_MODE_SIG_NOTATIONS
))
{
err
=
add_arg
(
gpg
,
"--list-options"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"show-sig-subpackets=
\"
20,26
\"
"
);
}
if
(
!
err
)
err
=
add_arg
(
gpg
,
secret_only
?
"--list-secret-keys"
:
((
mode
&
GPGME_KEYLIST_MODE_SIGS
)
?
"--check-sigs"
:
"--list-keys"
));
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--"
);
if
(
pattern
)
{
while
(
!
err
&&
*
pattern
&&
**
pattern
)
err
=
add_arg
(
gpg
,
*
(
pattern
++
));
}
if
(
!
err
)
err
=
start
(
gpg
);
return
err
;
}
static
gpgme_error_t
gpg_sign
(
void
*
engine
,
gpgme_data_t
in
,
gpgme_data_t
out
,
gpgme_sig_mode_t
mode
,
int
use_armor
,
int
use_textmode
,
int
include_certs
,
gpgme_ctx_t
ctx
/* FIXME */
)
{
engine_gpg_t
gpg
=
engine
;
gpgme_error_t
err
;
if
(
mode
==
GPGME_SIG_MODE_CLEAR
)
err
=
add_arg
(
gpg
,
"--clearsign"
);
else
{
err
=
add_arg
(
gpg
,
"--sign"
);
if
(
!
err
&&
mode
==
GPGME_SIG_MODE_DETACH
)
err
=
add_arg
(
gpg
,
"--detach"
);
if
(
!
err
&&
use_armor
)
err
=
add_arg
(
gpg
,
"--armor"
);
if
(
!
err
&&
use_textmode
)
err
=
add_arg
(
gpg
,
"--textmode"
);
}
if
(
!
err
)
err
=
append_args_from_signers
(
gpg
,
ctx
);
if
(
!
err
)
err
=
append_args_from_sig_notations
(
gpg
,
ctx
);
if
(
gpgme_data_get_file_name
(
in
))
{
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--set-filename"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
gpgme_data_get_file_name
(
in
));
}
/* Tell the gpg object about the data. */
if
(
!
err
)
err
=
add_data
(
gpg
,
in
,
0
,
0
);
if
(
!
err
)
err
=
add_data
(
gpg
,
out
,
1
,
1
);
if
(
!
err
)
start
(
gpg
);
return
err
;
}
static
gpgme_error_t
gpg_trustlist
(
void
*
engine
,
const
char
*
pattern
)
{
engine_gpg_t
gpg
=
engine
;
gpgme_error_t
err
;
err
=
add_arg
(
gpg
,
"--with-colons"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--list-trust-path"
);
/* Tell the gpg object about the data. */
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
pattern
);
if
(
!
err
)
err
=
start
(
gpg
);
return
err
;
}
static
gpgme_error_t
gpg_verify
(
void
*
engine
,
gpgme_data_t
sig
,
gpgme_data_t
signed_text
,
gpgme_data_t
plaintext
)
{
engine_gpg_t
gpg
=
engine
;
gpgme_error_t
err
=
0
;
if
(
plaintext
)
{
/* Normal or cleartext signature. */
err
=
add_arg
(
gpg
,
"--output"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"-"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--"
);
if
(
!
err
)
err
=
add_data
(
gpg
,
sig
,
0
,
0
);
if
(
!
err
)
err
=
add_data
(
gpg
,
plaintext
,
1
,
1
);
}
else
{
err
=
add_arg
(
gpg
,
"--verify"
);
if
(
!
err
)
err
=
add_arg
(
gpg
,
"--"
);
if
(
!
err
)
err
=
add_data
(
gpg
,
sig
,
-1
,
0
);
if
(
signed_text
)
{
if
(
!
err
)
err
=
add_arg
(
gpg
,
"-"
);
if
(
!
err
)
err
=
add_data
(
gpg
,
signed_text
,
0
,
0
);
}
}
if
(
!
err
)
err
=
start
(
gpg
);
return
err
;
}
static
void
gpg_set_io_cbs
(
void
*
engine
,
gpgme_io_cbs_t
io_cbs
)
{
engine_gpg_t
gpg
=
engine
;
gpg
->
io_cbs
=
*
io_cbs
;
}
struct
engine_ops
_gpgme_engine_ops_gpg
=
{
/* Static functions. */
_gpgme_get_gpg_path
,
gpg_get_version
,
gpg_get_req_version
,
gpg_new
,
/* Member functions. */
gpg_release
,
gpg_set_status_handler
,
gpg_set_command_handler
,
gpg_set_colon_line_handler
,
gpg_decrypt
,
gpg_delete
,
gpg_edit
,
gpg_encrypt
,
gpg_encrypt_sign
,
gpg_export
,
gpg_export_ext
,
gpg_genkey
,
gpg_import
,
gpg_keylist
,
gpg_keylist_ext
,
gpg_sign
,
gpg_trustlist
,
gpg_verify
,
gpg_set_io_cbs
,
gpg_io_event
,
gpg_cancel
};
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Fri, Dec 5, 5:27 AM (1 d, 22 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
38/d4/b7473859c4c7f31c38bc109ec9c7
Attached To
rM GPGME
Event Timeline
Log In to Comment