Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34109760
rungpg.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
42 KB
Subscribers
None
rungpg.c
View Options
/* rungpg.c
* Copyright (C) 2000 Werner Koch (dd9jn)
* Copyright (C) 2001, 2002 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 General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU 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
*/
#include
<config.h>
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
#include
<assert.h>
#include
<errno.h>
#include
<time.h>
#include
<sys/time.h>
#include
<sys/types.h>
#include
<signal.h>
#include
<fcntl.h>
#include
"unistd.h"
#include
"gpgme.h"
#include
"util.h"
#include
"ops.h"
#include
"wait.h"
#include
"rungpg.h"
#include
"context.h"
/*temp hack until we have GpmeData methods to do I/O */
#include
"io.h"
#include
"sema.h"
#include
"status-table.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
;
GpgmeData
data
;
/* If this is not NULL .. */
int
dup_to
;
int
print_fd
;
/* print the fd number and not the special form of it */
char
arg
[
1
];
/* .. this is used */
};
struct
fd_data_map_s
{
GpgmeData
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
;
};
struct
gpg_object_s
{
struct
arg_and_data_s
*
arglist
;
struct
arg_and_data_s
**
argtail
;
int
arg_error
;
struct
{
int
fd
[
2
];
size_t
bufsize
;
char
*
buffer
;
size_t
readpos
;
int
eof
;
GpgStatusHandler
fnc
;
void
*
fnc_value
;
void
*
tag
;
}
status
;
/* This is a kludge - see the comment at gpg_colon_line_handler */
struct
{
int
fd
[
2
];
size_t
bufsize
;
char
*
buffer
;
size_t
readpos
;
int
eof
;
GpgColonLineHandler
fnc
;
/* this indicate use of this structrue */
void
*
fnc_value
;
void
*
tag
;
int
simple
;
}
colon
;
char
**
argv
;
struct
fd_data_map_s
*
fd_data_map
;
int
pid
;
/* we can't use pid_t because we don't use it in Windoze */
/* stuff needed for pipemode */
struct
{
int
used
;
int
active
;
GpgmeData
sig
;
GpgmeData
text
;
int
stream_started
;
}
pm
;
/* stuff needed for interactive (command) mode */
struct
{
int
used
;
int
fd
;
int
idx
;
/* Index in fd_data_map */
GpgmeData
cb_data
;
/* hack to get init the above idx later */
GpgStatusCode
code
;
/* last code */
char
*
keyword
;
/* what has been requested (malloced) */
GpgCommandHandler
fnc
;
void
*
fnc_value
;
}
cmd
;
struct
GpgmeIOCbs
io_cbs
;
};
static
void
free_argv
(
char
**
argv
);
static
void
free_fd_data_map
(
struct
fd_data_map_s
*
fd_data_map
);
static
void
gpg_status_handler
(
void
*
opaque
,
int
fd
);
static
GpgmeError
read_status
(
GpgObject
gpg
);
static
void
gpg_colon_line_handler
(
void
*
opaque
,
int
fd
);
static
GpgmeError
read_colon_line
(
GpgObject
gpg
);
static
int
pipemode_cb
(
void
*
opaque
,
char
*
buffer
,
size_t
length
,
size_t
*
nread
);
static
int
command_cb
(
void
*
opaque
,
char
*
buffer
,
size_t
length
,
size_t
*
nread
);
static
void
close_notify_handler
(
int
fd
,
void
*
opaque
)
{
GpgObject
gpg
=
opaque
;
int
possibly_done
=
0
;
int
not_done
=
0
;
assert
(
fd
!=
-1
);
if
(
gpg
->
status
.
fd
[
0
]
==
fd
)
{
if
(
gpg
->
status
.
tag
)
{
(
*
gpg
->
io_cbs
.
remove
)
(
gpg
->
status
.
tag
);
possibly_done
=
1
;
}
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
);
possibly_done
=
1
;
}
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
);
possibly_done
=
1
;
}
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
;
}
}
}
if
(
!
possibly_done
)
not_done
=
1
;
else
if
(
gpg
->
status
.
fd
[
0
]
!=
-1
)
not_done
=
1
;
else
if
(
gpg
->
colon
.
fd
[
0
]
!=
-1
)
not_done
=
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
!=
-1
)
{
not_done
=
1
;
break
;
}
}
if
(
!
not_done
&&
gpg
->
io_cbs
.
event
)
(
*
gpg
->
io_cbs
.
event
)
(
gpg
->
io_cbs
.
event_priv
,
GPGME_EVENT_DONE
,
NULL
);
}
const
char
*
_gpgme_gpg_get_version
(
void
)
{
static
const
char
*
gpg_version
;
DEFINE_STATIC_LOCK
(
gpg_version_lock
);
LOCK
(
gpg_version_lock
);
if
(
!
gpg_version
)
gpg_version
=
_gpgme_get_program_version
(
_gpgme_get_gpg_path
());
UNLOCK
(
gpg_version_lock
);
return
gpg_version
;
}
GpgmeError
_gpgme_gpg_check_version
(
void
)
{
return
_gpgme_compare_versions
(
_gpgme_gpg_get_version
(),
NEED_GPG_VERSION
)
?
0
:
mk_error
(
Invalid_Engine
);
}
GpgmeError
_gpgme_gpg_new
(
GpgObject
*
r_gpg
)
{
GpgObject
gpg
;
int
rc
=
0
;
gpg
=
xtrycalloc
(
1
,
sizeof
*
gpg
);
if
(
!
gpg
)
{
rc
=
mk_error
(
Out_Of_Core
);
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
->
pid
=
-1
;
/* Allocate the read buffer for the status pipe. */
gpg
->
status
.
bufsize
=
1024
;
gpg
->
status
.
readpos
=
0
;
gpg
->
status
.
buffer
=
xtrymalloc
(
gpg
->
status
.
bufsize
);
if
(
!
gpg
->
status
.
buffer
)
{
rc
=
mk_error
(
Out_Of_Core
);
goto
leave
;
}
/* In any case we need a status pipe - create it right here and
don't handle it with our generic GpgmeData mechanism. */
if
(
_gpgme_io_pipe
(
gpg
->
status
.
fd
,
1
)
==
-1
)
{
rc
=
mk_error
(
Pipe_Error
);
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
=
mk_error
(
General_Error
);
goto
leave
;
}
gpg
->
status
.
eof
=
0
;
_gpgme_gpg_add_arg
(
gpg
,
"--status-fd"
);
{
char
buf
[
25
];
sprintf
(
buf
,
"%d"
,
gpg
->
status
.
fd
[
1
]);
_gpgme_gpg_add_arg
(
gpg
,
buf
);
}
_gpgme_gpg_add_arg
(
gpg
,
"--no-tty"
);
leave
:
if
(
rc
)
{
_gpgme_gpg_release
(
gpg
);
*
r_gpg
=
NULL
;
}
else
*
r_gpg
=
gpg
;
return
rc
;
}
void
_gpgme_gpg_release
(
GpgObject
gpg
)
{
if
(
!
gpg
)
return
;
while
(
gpg
->
arglist
)
{
struct
arg_and_data_s
*
next
=
gpg
->
arglist
->
next
;
xfree
(
gpg
->
arglist
);
gpg
->
arglist
=
next
;
}
xfree
(
gpg
->
status
.
buffer
);
xfree
(
gpg
->
colon
.
buffer
);
if
(
gpg
->
argv
)
free_argv
(
gpg
->
argv
);
gpgme_data_release
(
gpg
->
cmd
.
cb_data
);
xfree
(
gpg
->
cmd
.
keyword
);
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
]);
free_fd_data_map
(
gpg
->
fd_data_map
);
if
(
gpg
->
cmd
.
fd
!=
-1
)
_gpgme_io_close
(
gpg
->
cmd
.
fd
);
if
(
gpg
->
pid
!=
-1
)
_gpgme_engine_add_child_to_reap_list
(
gpg
,
sizeof
*
gpg
,
gpg
->
pid
);
else
xfree
(
gpg
);
}
void
_gpgme_gpg_enable_pipemode
(
GpgObject
gpg
)
{
gpg
->
pm
.
used
=
1
;
assert
(
!
gpg
->
pm
.
sig
);
assert
(
!
gpg
->
pm
.
text
);
}
GpgmeError
_gpgme_gpg_add_arg
(
GpgObject
gpg
,
const
char
*
arg
)
{
struct
arg_and_data_s
*
a
;
assert
(
gpg
);
assert
(
arg
);
if
(
gpg
->
pm
.
active
)
return
0
;
a
=
xtrymalloc
(
sizeof
*
a
+
strlen
(
arg
)
);
if
(
!
a
)
{
gpg
->
arg_error
=
1
;
return
mk_error
(
Out_Of_Core
);
}
a
->
next
=
NULL
;
a
->
data
=
NULL
;
a
->
dup_to
=
-1
;
strcpy
(
a
->
arg
,
arg
);
*
gpg
->
argtail
=
a
;
gpg
->
argtail
=
&
a
->
next
;
return
0
;
}
GpgmeError
_gpgme_gpg_add_data
(
GpgObject
gpg
,
GpgmeData
data
,
int
dup_to
)
{
struct
arg_and_data_s
*
a
;
assert
(
gpg
);
assert
(
data
);
if
(
gpg
->
pm
.
active
)
return
0
;
a
=
xtrymalloc
(
sizeof
*
a
-
1
);
if
(
!
a
)
{
gpg
->
arg_error
=
1
;
return
mk_error
(
Out_Of_Core
);
}
a
->
next
=
NULL
;
a
->
data
=
data
;
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
;
}
GpgmeError
_gpgme_gpg_add_pm_data
(
GpgObject
gpg
,
GpgmeData
data
,
int
what
)
{
GpgmeError
rc
=
0
;
assert
(
gpg
->
pm
.
used
);
if
(
!
what
)
{
/* the signature */
assert
(
!
gpg
->
pm
.
sig
);
gpg
->
pm
.
sig
=
data
;
}
else
if
(
what
==
1
)
{
/* the signed data */
assert
(
!
gpg
->
pm
.
text
);
gpg
->
pm
.
text
=
data
;
}
else
{
assert
(
0
);
}
if
(
gpg
->
pm
.
sig
&&
gpg
->
pm
.
text
)
{
if
(
!
gpg
->
pm
.
active
)
{
/* create the callback handler and connect it to stdin */
GpgmeData
tmp
;
rc
=
gpgme_data_new_with_read_cb
(
&
tmp
,
pipemode_cb
,
gpg
);
if
(
!
rc
)
rc
=
_gpgme_gpg_add_data
(
gpg
,
tmp
,
0
);
}
if
(
!
rc
)
{
/* here we can reset the handler stuff */
gpg
->
pm
.
stream_started
=
0
;
}
}
return
rc
;
}
/*
* Note, that the status_handler is allowed to modifiy the args value
*/
void
_gpgme_gpg_set_status_handler
(
GpgObject
gpg
,
GpgStatusHandler
fnc
,
void
*
fnc_value
)
{
assert
(
gpg
);
if
(
gpg
->
pm
.
active
)
return
;
gpg
->
status
.
fnc
=
fnc
;
gpg
->
status
.
fnc_value
=
fnc_value
;
}
/* Kludge to process --with-colon output */
GpgmeError
_gpgme_gpg_set_colon_line_handler
(
GpgObject
gpg
,
GpgColonLineHandler
fnc
,
void
*
fnc_value
)
{
assert
(
gpg
);
if
(
gpg
->
pm
.
active
)
return
0
;
gpg
->
colon
.
bufsize
=
1024
;
gpg
->
colon
.
readpos
=
0
;
gpg
->
colon
.
buffer
=
xtrymalloc
(
gpg
->
colon
.
bufsize
);
if
(
!
gpg
->
colon
.
buffer
)
{
return
mk_error
(
Out_Of_Core
);
}
if
(
_gpgme_io_pipe
(
gpg
->
colon
.
fd
,
1
)
==
-1
)
{
xfree
(
gpg
->
colon
.
buffer
);
gpg
->
colon
.
buffer
=
NULL
;
return
mk_error
(
Pipe_Error
);
}
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
mk_error
(
General_Error
);
}
gpg
->
colon
.
eof
=
0
;
gpg
->
colon
.
fnc
=
fnc
;
gpg
->
colon
.
fnc_value
=
fnc_value
;
gpg
->
colon
.
simple
=
0
;
return
0
;
}
GpgmeError
_gpgme_gpg_set_simple_line_handler
(
GpgObject
gpg
,
GpgColonLineHandler
fnc
,
void
*
fnc_value
)
{
GpgmeError
err
;
err
=
_gpgme_gpg_set_colon_line_handler
(
gpg
,
fnc
,
fnc_value
);
if
(
!
err
)
gpg
->
colon
.
simple
=
1
;
return
err
;
}
/*
* 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.
*/
GpgmeError
_gpgme_gpg_set_command_handler
(
GpgObject
gpg
,
GpgCommandHandler
fnc
,
void
*
fnc_value
)
{
GpgmeData
tmp
;
GpgmeError
err
;
assert
(
gpg
);
if
(
gpg
->
pm
.
active
)
return
0
;
err
=
gpgme_data_new_with_read_cb
(
&
tmp
,
command_cb
,
gpg
);
if
(
err
)
return
err
;
_gpgme_gpg_add_arg
(
gpg
,
"--command-fd"
);
_gpgme_gpg_add_data
(
gpg
,
tmp
,
-2
);
gpg
->
cmd
.
cb_data
=
tmp
;
gpg
->
cmd
.
fnc
=
fnc
;
gpg
->
cmd
.
fnc_value
=
fnc_value
;
gpg
->
cmd
.
used
=
1
;
return
0
;
}
static
void
free_argv
(
char
**
argv
)
{
int
i
;
for
(
i
=
0
;
argv
[
i
];
i
++
)
xfree
(
argv
[
i
]);
xfree
(
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 */
}
xfree
(
fd_data_map
);
}
static
GpgmeError
build_argv
(
GpgObject
gpg
)
{
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. */
p
=
getenv
(
"GPG_AGENT_INFO"
);
use_agent
=
(
p
&&
strchr
(
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
++
;
argc
+=
2
;
/* --comment */
argv
=
xtrycalloc
(
argc
+
1
,
sizeof
*
argv
);
if
(
!
argv
)
return
mk_error
(
Out_Of_Core
);
fd_data_map
=
xtrycalloc
(
datac
+
1
,
sizeof
*
fd_data_map
);
if
(
!
fd_data_map
)
{
free_argv
(
argv
);
return
mk_error
(
Out_Of_Core
);
}
argc
=
datac
=
0
;
argv
[
argc
]
=
xtrystrdup
(
"gpg"
);
/* argv[0] */
if
(
!
argv
[
argc
])
{
xfree
(
fd_data_map
);
free_argv
(
argv
);
return
mk_error
(
Out_Of_Core
);
}
argc
++
;
if
(
need_special
)
{
argv
[
argc
]
=
xtrystrdup
(
"--enable-special-filenames"
);
if
(
!
argv
[
argc
])
{
xfree
(
fd_data_map
);
free_argv
(
argv
);
return
mk_error
(
Out_Of_Core
);
}
argc
++
;
}
if
(
use_agent
)
{
argv
[
argc
]
=
xtrystrdup
(
"--use-agent"
);
if
(
!
argv
[
argc
])
{
xfree
(
fd_data_map
);
free_argv
(
argv
);
return
mk_error
(
Out_Of_Core
);
}
argc
++
;
}
if
(
!
gpg
->
cmd
.
used
)
{
argv
[
argc
]
=
xtrystrdup
(
"--batch"
);
if
(
!
argv
[
argc
])
{
xfree
(
fd_data_map
);
free_argv
(
argv
);
return
mk_error
(
Out_Of_Core
);
}
argc
++
;
}
argv
[
argc
]
=
xtrystrdup
(
"--comment"
);
if
(
!
argv
[
argc
])
{
xfree
(
fd_data_map
);
free_argv
(
argv
);
return
mk_error
(
Out_Of_Core
);
}
argc
++
;
argv
[
argc
]
=
xtrystrdup
(
""
);
if
(
!
argv
[
argc
])
{
xfree
(
fd_data_map
);
free_argv
(
argv
);
return
mk_error
(
Out_Of_Core
);
}
argc
++
;
for
(
a
=
gpg
->
arglist
;
a
;
a
=
a
->
next
)
{
if
(
a
->
data
)
{
switch
(
_gpgme_data_get_mode
(
a
->
data
))
{
case
GPGME_DATA_MODE_NONE
:
case
GPGME_DATA_MODE_INOUT
:
xfree
(
fd_data_map
);
free_argv
(
argv
);
return
mk_error
(
Invalid_Mode
);
case
GPGME_DATA_MODE_IN
:
/* Create a pipe to read from gpg. */
fd_data_map
[
datac
].
inbound
=
1
;
break
;
case
GPGME_DATA_MODE_OUT
:
/* Create a pipe to pass it down to gpg. */
fd_data_map
[
datac
].
inbound
=
0
;
break
;
}
switch
(
gpgme_data_get_type
(
a
->
data
))
{
case
GPGME_DATA_TYPE_NONE
:
if
(
fd_data_map
[
datac
].
inbound
)
break
;
/* Allowed. */
xfree
(
fd_data_map
);
free_argv
(
argv
);
return
mk_error
(
Invalid_Type
);
case
GPGME_DATA_TYPE_MEM
:
case
GPGME_DATA_TYPE_CB
:
break
;
case
GPGME_DATA_TYPE_FD
:
case
GPGME_DATA_TYPE_FILE
:
xfree
(
fd_data_map
);
free_argv
(
argv
);
return
mk_error
(
Not_Implemented
);
}
/* Create a pipe. */
{
int
fds
[
2
];
if
(
_gpgme_io_pipe
(
fds
,
fd_data_map
[
datac
].
inbound
?
1
:
0
)
==
-1
)
{
xfree
(
fd_data_map
);
free_argv
(
argv
);
return
mk_error
(
Pipe_Error
);
}
if
(
_gpgme_io_set_close_notify
(
fds
[
0
],
close_notify_handler
,
gpg
)
||
_gpgme_io_set_close_notify
(
fds
[
1
],
close_notify_handler
,
gpg
))
{
return
mk_error
(
General_Error
);
}
/* 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
&&
gpg
->
cmd
.
cb_data
==
a
->
data
)
{
assert
(
gpg
->
cmd
.
idx
==
-1
);
gpg
->
cmd
.
idx
=
datac
;
}
fd_data_map
[
datac
].
data
=
a
->
data
;
fd_data_map
[
datac
].
dup_to
=
a
->
dup_to
;
if
(
a
->
dup_to
==
-1
)
{
argv
[
argc
]
=
xtrymalloc
(
25
);
if
(
!
argv
[
argc
])
{
xfree
(
fd_data_map
);
free_argv
(
argv
);
return
mk_error
(
Out_Of_Core
);
}
sprintf
(
argv
[
argc
],
a
->
print_fd
?
"%d"
:
"-&%d"
,
fd_data_map
[
datac
].
peer_fd
);
argc
++
;
}
datac
++
;
}
else
{
argv
[
argc
]
=
xtrystrdup
(
a
->
arg
);
if
(
!
argv
[
argc
])
{
xfree
(
fd_data_map
);
free_argv
(
argv
);
return
mk_error
(
Out_Of_Core
);
}
argc
++
;
}
}
gpg
->
argv
=
argv
;
gpg
->
fd_data_map
=
fd_data_map
;
return
0
;
}
static
GpgmeError
_gpgme_gpg_add_io_cb
(
GpgObject
gpg
,
int
fd
,
int
dir
,
GpgmeIOCb
handler
,
void
*
data
,
void
**
tag
)
{
GpgmeError
err
=
0
;
*
tag
=
(
*
gpg
->
io_cbs
.
add
)
(
gpg
->
io_cbs
.
add_priv
,
fd
,
dir
,
handler
,
data
);
if
(
!
tag
)
err
=
mk_error
(
General_Error
);
if
(
!
err
&&
!
dir
)
/* FIXME Kludge around poll() problem. */
err
=
_gpgme_io_set_nonblocking
(
fd
);
return
err
;
}
GpgmeError
_gpgme_gpg_spawn
(
GpgObject
gpg
,
void
*
opaque
)
{
GpgmeError
rc
;
int
i
,
n
;
int
pid
;
struct
spawn_fd_item_s
*
fd_child_list
,
*
fd_parent_list
;
if
(
!
gpg
)
return
mk_error
(
Invalid_Value
);
if
(
!
_gpgme_get_gpg_path
())
return
mk_error
(
Invalid_Engine
);
/* Kludge, so that we don't need to check the return code of all the
gpgme_gpg_add_arg(). we bail out here instead */
if
(
gpg
->
arg_error
)
return
mk_error
(
Out_Of_Core
);
if
(
gpg
->
pm
.
active
)
return
0
;
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
=
xtrycalloc
(
n
+
n
,
sizeof
*
fd_child_list
);
if
(
!
fd_child_list
)
return
mk_error
(
Out_Of_Core
);
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
++
;
gpg
->
status
.
fd
[
1
]
=
-1
;
}
if
(
gpg
->
colon
.
fd
[
1
]
!=
-1
)
{
fd_parent_list
[
n
].
fd
=
gpg
->
colon
.
fd
[
1
];
fd_parent_list
[
n
].
dup_to
=
-1
;
n
++
;
gpg
->
colon
.
fd
[
1
]
=
-1
;
}
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
++
;
gpg
->
fd_data_map
[
i
].
peer_fd
=
-1
;
}
fd_parent_list
[
n
].
fd
=
-1
;
fd_parent_list
[
n
].
dup_to
=
-1
;
pid
=
_gpgme_io_spawn
(
_gpgme_get_gpg_path
(),
gpg
->
argv
,
fd_child_list
,
fd_parent_list
);
xfree
(
fd_child_list
);
if
(
pid
==
-1
)
return
mk_error
(
Exec_Error
);
gpg
->
pid
=
pid
;
if
(
gpg
->
pm
.
used
)
gpg
->
pm
.
active
=
1
;
/*_gpgme_register_term_handler ( closure, closure_value, pid );*/
rc
=
_gpgme_gpg_add_io_cb
(
gpg
,
gpg
->
status
.
fd
[
0
],
1
,
gpg_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
=
_gpgme_gpg_add_io_cb
(
gpg
,
gpg
->
colon
.
fd
[
0
],
1
,
gpg_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
=
_gpgme_gpg_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
;
}
}
/* fixme: check what data we can release here */
return
0
;
}
static
void
gpg_status_handler
(
void
*
opaque
,
int
fd
)
{
GpgObject
gpg
=
opaque
;
int
err
;
assert
(
fd
==
gpg
->
status
.
fd
[
0
]);
err
=
read_status
(
gpg
);
if
(
err
)
{
/* XXX Horrible kludge. We really must not make use of
fnc_value. */
GpgmeCtx
ctx
=
(
GpgmeCtx
)
gpg
->
status
.
fnc_value
;
ctx
->
error
=
err
;
DEBUG1
(
"gpg_handler: read_status problem %d
\n
- stop"
,
err
);
_gpgme_io_close
(
fd
);
return
;
}
if
(
gpg
->
status
.
eof
)
_gpgme_io_close
(
fd
);
}
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
GpgmeError
read_status
(
GpgObject
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
=
xtryrealloc
(
buffer
,
bufsize
);
if
(
!
buffer
)
return
mk_error
(
Out_Of_Core
);
}
nread
=
_gpgme_io_read
(
gpg
->
status
.
fd
[
0
],
buffer
+
readpos
,
bufsize
-
readpos
);
if
(
nread
==
-1
)
return
mk_error
(
Read_Error
);
if
(
!
nread
)
{
gpg
->
status
.
eof
=
1
;
if
(
gpg
->
status
.
fnc
)
gpg
->
status
.
fnc
(
gpg
->
status
.
fnc_value
,
STATUS_EOF
,
""
);
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 as one extra element) */
r
=
bsearch
(
&
t
,
status_table
,
DIM
(
status_table
)
-1
,
sizeof
t
,
status_cmp
);
if
(
r
)
{
if
(
gpg
->
cmd
.
used
&&
(
r
->
code
==
STATUS_GET_BOOL
||
r
->
code
==
STATUS_GET_LINE
||
r
->
code
==
STATUS_GET_HIDDEN
))
{
gpg
->
cmd
.
code
=
r
->
code
;
xfree
(
gpg
->
cmd
.
keyword
);
gpg
->
cmd
.
keyword
=
xtrystrdup
(
rest
);
if
(
!
gpg
->
cmd
.
keyword
)
return
mk_error
(
Out_Of_Core
);
/* 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"
);
_gpgme_gpg_add_io_cb
(
gpg
,
gpg
->
cmd
.
fd
,
0
,
_gpgme_data_outbound_handler
,
gpg
->
fd_data_map
[
gpg
->
cmd
.
idx
].
data
,
&
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
)
{
gpg
->
status
.
fnc
(
gpg
->
status
.
fnc_value
,
r
->
code
,
rest
);
}
if
(
r
->
code
==
STATUS_END_STREAM
)
{
if
(
gpg
->
cmd
.
used
)
{
/* 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
;
}
/*
* This colonline handler thing is not the clean way to do it.
* It might be better to enhance the GpgmeData object to act as
* a wrapper for a callback. Same goes for the status thing.
* For now we use this thing here becuase it is easier to implement.
*/
static
void
gpg_colon_line_handler
(
void
*
opaque
,
int
fd
)
{
GpgObject
gpg
=
opaque
;
GpgmeError
rc
=
0
;
assert
(
fd
==
gpg
->
colon
.
fd
[
0
]);
rc
=
read_colon_line
(
gpg
);
if
(
rc
)
{
DEBUG1
(
"gpg_colon_line_handler: "
"read problem %d
\n
- stop"
,
rc
);
_gpgme_io_close
(
fd
);
return
;
}
if
(
gpg
->
colon
.
eof
)
_gpgme_io_close
(
fd
);
}
static
GpgmeError
read_colon_line
(
GpgObject
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
=
xtryrealloc
(
buffer
,
bufsize
);
if
(
!
buffer
)
return
mk_error
(
Out_Of_Core
);
}
nread
=
_gpgme_io_read
(
gpg
->
colon
.
fd
[
0
],
buffer
+
readpos
,
bufsize
-
readpos
);
if
(
nread
==
-1
)
return
mk_error
(
Read_Error
);
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
(
gpg
->
colon
.
simple
||
(
*
buffer
&&
strchr
(
buffer
,
':'
))
)
{
assert
(
gpg
->
colon
.
fnc
);
gpg
->
colon
.
fnc
(
gpg
->
colon
.
fnc_value
,
buffer
);
}
/* 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
;
}
static
GpgmeError
pipemode_copy
(
char
*
buffer
,
size_t
length
,
size_t
*
nread
,
GpgmeData
data
)
{
GpgmeError
err
;
size_t
nbytes
;
char
tmp
[
1000
],
*
s
,
*
d
;
/* we can optimize this whole thing but for now we just
* return after each escape character */
if
(
length
>
990
)
length
=
990
;
err
=
gpgme_data_read
(
data
,
tmp
,
length
,
&
nbytes
);
if
(
err
)
return
err
;
for
(
s
=
tmp
,
d
=
buffer
;
nbytes
;
s
++
,
nbytes
--
)
{
*
d
++
=
*
s
;
if
(
*
s
==
'@'
)
{
*
d
++
=
'@'
;
break
;
}
}
*
nread
=
d
-
buffer
;
return
0
;
}
static
int
pipemode_cb
(
void
*
opaque
,
char
*
buffer
,
size_t
length
,
size_t
*
nread
)
{
GpgObject
gpg
=
opaque
;
GpgmeError
err
;
if
(
!
buffer
||
!
length
||
!
nread
)
return
0
;
/* those values are reserved for extensions */
*
nread
=
0
;
if
(
!
gpg
->
pm
.
stream_started
)
{
assert
(
length
>
4
);
strcpy
(
buffer
,
"@<@B"
);
*
nread
=
4
;
gpg
->
pm
.
stream_started
=
1
;
}
else
if
(
gpg
->
pm
.
sig
)
{
err
=
pipemode_copy
(
buffer
,
length
,
nread
,
gpg
->
pm
.
sig
);
if
(
err
==
GPGME_EOF
)
{
gpg
->
pm
.
sig
=
NULL
;
assert
(
length
>
4
);
strcpy
(
buffer
,
"@t"
);
*
nread
=
2
;
}
else
if
(
err
)
{
DEBUG1
(
"pipemode_cb: copy sig failed: %s
\n
"
,
gpgme_strerror
(
err
)
);
return
-1
;
}
}
else
if
(
gpg
->
pm
.
text
)
{
err
=
pipemode_copy
(
buffer
,
length
,
nread
,
gpg
->
pm
.
text
);
if
(
err
==
GPGME_EOF
)
{
gpg
->
pm
.
text
=
NULL
;
assert
(
length
>
4
);
strcpy
(
buffer
,
"@.@>"
);
*
nread
=
4
;
}
else
if
(
err
)
{
DEBUG1
(
"pipemode_cb: copy data failed: %s
\n
"
,
gpgme_strerror
(
err
)
);
return
-1
;
}
}
else
{
return
0
;
/* eof */
}
return
0
;
}
/*
* Here we handle --command-fd. This works closely together with
* the status handler.
*/
static
int
command_cb
(
void
*
opaque
,
char
*
buffer
,
size_t
length
,
size_t
*
nread
)
{
GpgObject
gpg
=
opaque
;
const
char
*
value
;
int
value_len
;
DEBUG0
(
"command_cb: enter
\n
"
);
assert
(
gpg
->
cmd
.
used
);
if
(
!
buffer
||
!
length
||
!
nread
)
return
0
;
/* These values are reserved for extensions. */
*
nread
=
0
;
if
(
!
gpg
->
cmd
.
code
)
{
DEBUG0
(
"command_cb: no code
\n
"
);
return
-1
;
}
if
(
!
gpg
->
cmd
.
fnc
)
{
DEBUG0
(
"command_cb: no user cb
\n
"
);
return
-1
;
}
value
=
gpg
->
cmd
.
fnc
(
gpg
->
cmd
.
fnc_value
,
gpg
->
cmd
.
code
,
gpg
->
cmd
.
keyword
);
if
(
!
value
)
{
DEBUG0
(
"command_cb: no data from user cb
\n
"
);
gpg
->
cmd
.
fnc
(
gpg
->
cmd
.
fnc_value
,
0
,
value
);
return
-1
;
}
value_len
=
strlen
(
value
);
if
(
value_len
+
1
>
length
)
{
DEBUG0
(
"command_cb: too much data from user cb
\n
"
);
gpg
->
cmd
.
fnc
(
gpg
->
cmd
.
fnc_value
,
0
,
value
);
return
-1
;
}
memcpy
(
buffer
,
value
,
value_len
);
if
(
!
value_len
||
(
value_len
&&
value
[
value_len
-1
]
!=
'\n'
))
buffer
[
value_len
++
]
=
'\n'
;
*
nread
=
value_len
;
gpg
->
cmd
.
fnc
(
gpg
->
cmd
.
fnc_value
,
0
,
value
);
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
;
}
GpgmeError
_gpgme_gpg_op_decrypt
(
GpgObject
gpg
,
GpgmeData
ciph
,
GpgmeData
plain
)
{
GpgmeError
err
;
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--decrypt"
);
/* Tell the gpg object about the data. */
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--output"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"-"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_data
(
gpg
,
plain
,
1
);
if
(
!
err
)
err
=
_gpgme_gpg_add_data
(
gpg
,
ciph
,
0
);
return
err
;
}
GpgmeError
_gpgme_gpg_op_delete
(
GpgObject
gpg
,
GpgmeKey
key
,
int
allow_secret
)
{
GpgmeError
err
;
err
=
_gpgme_gpg_add_arg
(
gpg
,
allow_secret
?
"--delete-secret-and-public-key"
:
"--delete-key"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--"
);
if
(
!
err
)
{
const
char
*
s
=
gpgme_key_get_string_attr
(
key
,
GPGME_ATTR_FPR
,
NULL
,
0
);
if
(
!
s
)
err
=
mk_error
(
Invalid_Key
);
else
err
=
_gpgme_gpg_add_arg
(
gpg
,
s
);
}
return
err
;
}
static
GpgmeError
_gpgme_append_gpg_args_from_recipients
(
GpgObject
gpg
,
const
GpgmeRecipients
rset
)
{
GpgmeError
err
=
0
;
struct
user_id_s
*
r
;
assert
(
rset
);
for
(
r
=
rset
->
list
;
r
;
r
=
r
->
next
)
{
err
=
_gpgme_gpg_add_arg
(
gpg
,
"-r"
);
if
(
!
err
)
_gpgme_gpg_add_arg
(
gpg
,
r
->
name
);
if
(
err
)
break
;
}
return
err
;
}
static
GpgmeError
_gpgme_append_gpg_args_from_signers
(
GpgObject
gpg
,
GpgmeCtx
ctx
/* FIXME */
)
{
GpgmeError
err
=
0
;
int
i
;
GpgmeKey
key
;
for
(
i
=
0
;
(
key
=
gpgme_signers_enum
(
ctx
,
i
));
i
++
)
{
const
char
*
s
=
gpgme_key_get_string_attr
(
key
,
GPGME_ATTR_KEYID
,
NULL
,
0
);
if
(
s
)
{
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"-u"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
s
);
}
gpgme_key_unref
(
key
);
if
(
err
)
break
;
}
return
err
;
}
GpgmeError
_gpgme_gpg_op_encrypt
(
GpgObject
gpg
,
GpgmeRecipients
recp
,
GpgmeData
plain
,
GpgmeData
ciph
,
int
use_armor
)
{
GpgmeError
err
;
int
symmetric
=
!
recp
;
err
=
_gpgme_gpg_add_arg
(
gpg
,
symmetric
?
"--symmetric"
:
"--encrypt"
);
if
(
!
err
&&
use_armor
)
err
=
_gpgme_gpg_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
&&
_gpgme_recipients_all_valid
(
recp
))
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--always-trust"
);
if
(
!
err
)
err
=
_gpgme_append_gpg_args_from_recipients
(
gpg
,
recp
);
}
/* Tell the gpg object about the data. */
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--output"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"-"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_data
(
gpg
,
ciph
,
1
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_data
(
gpg
,
plain
,
0
);
return
err
;
}
GpgmeError
_gpgme_gpg_op_encrypt_sign
(
GpgObject
gpg
,
GpgmeRecipients
recp
,
GpgmeData
plain
,
GpgmeData
ciph
,
int
use_armor
,
GpgmeCtx
ctx
/* FIXME */
)
{
GpgmeError
err
;
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--encrypt"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--sign"
);
if
(
!
err
&&
use_armor
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--armor"
);
/* If we know that all recipients are valid (full or ultimate trust)
* we can suppress further checks */
if
(
!
err
&&
_gpgme_recipients_all_valid
(
recp
))
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--always-trust"
);
if
(
!
err
)
err
=
_gpgme_append_gpg_args_from_recipients
(
gpg
,
recp
);
if
(
!
err
)
err
=
_gpgme_append_gpg_args_from_signers
(
gpg
,
ctx
);
/* Tell the gpg object about the data. */
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--output"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"-"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_data
(
gpg
,
ciph
,
1
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_data
(
gpg
,
plain
,
0
);
return
err
;
}
GpgmeError
_gpgme_gpg_op_export
(
GpgObject
gpg
,
GpgmeRecipients
recp
,
GpgmeData
keydata
,
int
use_armor
)
{
GpgmeError
err
;
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--export"
);
if
(
!
err
&&
use_armor
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--armor"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_data
(
gpg
,
keydata
,
1
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--"
);
if
(
!
err
)
{
void
*
ec
;
const
char
*
s
;
err
=
gpgme_recipients_enum_open
(
recp
,
&
ec
);
while
(
!
err
&&
(
s
=
gpgme_recipients_enum_read
(
recp
,
&
ec
)))
err
=
_gpgme_gpg_add_arg
(
gpg
,
s
);
if
(
!
err
)
err
=
gpgme_recipients_enum_close
(
recp
,
&
ec
);
}
return
err
;
}
GpgmeError
_gpgme_gpg_op_genkey
(
GpgObject
gpg
,
GpgmeData
help_data
,
int
use_armor
,
GpgmeData
pubkey
,
GpgmeData
seckey
)
{
GpgmeError
err
;
if
(
!
gpg
)
return
mk_error
(
Invalid_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
err
=
mk_error
(
Not_Implemented
);
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--gen-key"
);
if
(
!
err
&&
use_armor
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--armor"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_data
(
gpg
,
help_data
,
0
);
return
err
;
}
GpgmeError
_gpgme_gpg_op_import
(
GpgObject
gpg
,
GpgmeData
keydata
)
{
GpgmeError
err
;
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--import"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_data
(
gpg
,
keydata
,
0
);
return
err
;
}
GpgmeError
_gpgme_gpg_op_keylist
(
GpgObject
gpg
,
const
char
*
pattern
,
int
secret_only
,
int
keylist_mode
)
{
GpgmeError
err
;
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--with-colons"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--fixed-list-mode"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--with-fingerprint"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
secret_only
?
"--list-secret-keys"
:
"--list-keys"
);
/* Tell the gpg object about the data */
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--"
);
if
(
!
err
&&
pattern
&&
*
pattern
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
pattern
);
return
err
;
}
GpgmeError
_gpgme_gpg_op_keylist_ext
(
GpgObject
gpg
,
const
char
*
pattern
[],
int
secret_only
,
int
reserved
,
int
keylist_mode
)
{
GpgmeError
err
;
if
(
reserved
)
return
mk_error
(
Invalid_Value
);
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--with-colons"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--fixed-list-mode"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--with-fingerprint"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
secret_only
?
"--list-secret-keys"
:
"--list-keys"
);
/* Tell the gpg object about the data */
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--"
);
if
(
!
err
&&
pattern
&&
*
pattern
)
{
while
(
*
pattern
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
*
(
pattern
++
));
}
return
err
;
}
GpgmeError
_gpgme_gpg_op_sign
(
GpgObject
gpg
,
GpgmeData
in
,
GpgmeData
out
,
GpgmeSigMode
mode
,
int
use_armor
,
int
use_textmode
,
GpgmeCtx
ctx
/* FIXME */
)
{
GpgmeError
err
;
if
(
mode
==
GPGME_SIG_MODE_CLEAR
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--clearsign"
);
else
{
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--sign"
);
if
(
!
err
&&
mode
==
GPGME_SIG_MODE_DETACH
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--detach"
);
if
(
!
err
&&
use_armor
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--armor"
);
if
(
!
err
&&
use_textmode
)
_gpgme_gpg_add_arg
(
gpg
,
"--textmode"
);
}
if
(
!
err
)
err
=
_gpgme_append_gpg_args_from_signers
(
gpg
,
ctx
);
/* Tell the gpg object about the data. */
if
(
!
err
)
err
=
_gpgme_gpg_add_data
(
gpg
,
in
,
0
);
if
(
!
err
)
err
=
_gpgme_gpg_add_data
(
gpg
,
out
,
1
);
return
err
;
}
GpgmeError
_gpgme_gpg_op_trustlist
(
GpgObject
gpg
,
const
char
*
pattern
)
{
GpgmeError
err
;
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--with-colons"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--list-trust-path"
);
/* Tell the gpg object about the data */
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
pattern
);
return
err
;
}
GpgmeError
_gpgme_gpg_op_verify
(
GpgObject
gpg
,
GpgmeData
sig
,
GpgmeData
text
)
{
GpgmeError
err
=
0
;
if
(
_gpgme_data_get_mode
(
text
)
==
GPGME_DATA_MODE_IN
)
{
/* Normal or cleartext signature. */
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--output"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"-"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_data
(
gpg
,
sig
,
0
);
if
(
!
err
)
err
=
_gpgme_gpg_add_data
(
gpg
,
text
,
1
);
}
else
{
if
(
gpg
->
pm
.
used
)
{
err
=
_gpgme_gpg_add_arg
(
gpg
,
gpg
->
pm
.
used
?
"--pipemode"
:
"--verify"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_pm_data
(
gpg
,
sig
,
0
);
if
(
!
err
)
err
=
_gpgme_gpg_add_pm_data
(
gpg
,
text
,
1
);
}
else
{
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--verify"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"--"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_data
(
gpg
,
sig
,
-1
);
if
(
text
)
{
if
(
!
err
)
err
=
_gpgme_gpg_add_arg
(
gpg
,
"-"
);
if
(
!
err
)
err
=
_gpgme_gpg_add_data
(
gpg
,
text
,
0
);
}
}
}
return
err
;
}
void
_gpgme_gpg_set_io_cbs
(
GpgObject
gpg
,
struct
GpgmeIOCbs
*
io_cbs
)
{
gpg
->
io_cbs
=
*
io_cbs
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Fri, Dec 5, 4:53 AM (1 d, 1 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
47/b2/b3790cc2cc9019888b62a32188f7
Attached To
rM GPGME
Event Timeline
Log In to Comment