1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
3 Copyright (C) 2006-2007 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include <sys/types.h>
29 #include <glib/gprintf.h>
46 #include "pwmd_error.h"
50 static void *z_alloc(void *data
, unsigned items
, unsigned size
)
53 return gcry_calloc(items
, size
);
55 return calloc(items
, size
);
59 static void z_free(void *data
, void *p
)
68 static gpg_error_t
file_modified(struct client_s
*client
)
72 if (client
->state
!= STATE_OPEN
)
75 if (stat(client
->filename
, &st
) == 0 && client
->mtime
) {
76 if (client
->mtime
!= st
.st_mtime
)
77 return EPWMD_FILE_MODIFIED
;
83 static gboolean
encrypt_xml(gcry_cipher_hd_t gh
, void *outbuf
, gsize outsize
,
84 void *inbuf
, gsize insize
)
88 if ((rc
= gcry_cipher_encrypt(gh
, outbuf
, outsize
, inbuf
, insize
))) {
89 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
96 gpg_error_t
decrypt_xml(gcry_cipher_hd_t gh
, void *outbuf
, gsize outsize
,
97 void *inbuf
, gsize insize
)
101 if ((rc
= gcry_cipher_decrypt(gh
, outbuf
, outsize
, inbuf
, insize
)))
102 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
107 static gpg_error_t
parse_xml(assuan_context_t ctx
)
109 struct client_s
*client
= assuan_get_pointer(ctx
);
111 client
->doc
= xmlReadMemory(client
->xml
, client
->len
, NULL
, "UTF-8", XML_PARSE_NOBLANKS
);
114 return EPWMD_LIBXML_ERROR
;
119 gboolean
valid_filename(const gchar
*filename
)
123 if (!filename
|| !*filename
)
126 for (p
= filename
; *p
; p
++) {
127 if (g_ascii_isalnum(*p
) == FALSE
&& *p
!= '-' && *p
!= '_' && *p
!= '.')
134 gint
open_file(const gchar
*filename
, struct stat
*st
)
138 if ((fd
= open(filename
, O_RDONLY
)) == -1)
141 if (stat(filename
, st
) == -1) {
149 void unlock_file_mutex(struct client_s
*client
)
153 if (client
->has_lock
== FALSE
)
156 CACHE_LOCK(client
->ctx
);
158 if (cache_get_mutex(client
->md5file
, &m
) == FALSE
) {
164 pth_mutex_release(m
);
165 client
->has_lock
= FALSE
;
168 gpg_error_t
lock_file_mutex(struct client_s
*client
)
172 if (client
->has_lock
== TRUE
)
175 CACHE_LOCK(client
->ctx
);
177 if (cache_get_mutex(client
->md5file
, &m
) == FALSE
) {
184 if (pth_mutex_acquire(m
, TRUE
, NULL
) == FALSE
) {
185 if (errno
== EBUSY
) {
187 assuan_write_status(client
->ctx
, "LOCKED", N_("Waiting for lock"));
189 pth_mutex_acquire(m
, FALSE
, NULL
);
193 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, strerror(errno
));
194 return gpg_error_from_errno(e
);
198 client
->has_lock
= TRUE
;
202 static void cleanup_client(struct client_s
*client
)
204 assuan_context_t ctx
= client
->ctx
;
206 unlock_file_mutex(client
);
207 CACHE_LOCK(client
->ctx
);
208 cache_decr_refcount(client
->md5file
);
211 * This may be a new file so don't use a cache slot. save_command() will
212 * set this to FALSE on success.
214 if (client
->new == TRUE
)
215 cache_clear(client
->md5file
, 1);
218 xmlFreeDoc(client
->doc
);
221 gcry_free(client
->xml
);
223 if (client
->filename
)
224 g_free(client
->filename
);
227 gcry_cipher_close(client
->gh
);
229 memset(client
, 0, sizeof(struct client_s
));
230 client
->state
= STATE_CONNECTED
;
232 client
->freed
= TRUE
;
236 static gchar
*print_fmt(const char *fmt
, ...)
239 static gchar buf
[ASSUAN_LINELENGTH
] = {0};
242 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
247 gboolean
do_decompress(assuan_context_t ctx
, gpointer in
, gint insize
,
248 gpointer
*out
, glong
*outsize
, gint
*error
)
261 z
.avail_out
= zlib_bufsize
;
262 z
.next_out
= pout
= g_malloc(zlib_bufsize
);
265 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, strerror(ENOMEM
));
266 *error
= Z_MEM_ERROR
;
270 ret
= inflateInit2(&z
, 47);
273 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
278 memset(&h
, 0, sizeof(gz_header
));
279 h
.comment
= (guchar
*)buf
;
280 h
.comm_max
= sizeof(buf
);
281 ret
= inflateGetHeader(&z
, &h
);
284 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
290 ret
= inflate(&z
, Z_BLOCK
);
293 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
300 insize
= atoi((gchar
*)h
.comment
);
305 ret
= inflate(&z
, Z_FINISH
);
312 p
= g_realloc(pout
, z
.total_out
+ zlib_bufsize
);
320 z
.next_out
= pout
+ z
.total_out
;
321 z
.avail_out
= zlib_bufsize
;
324 rc
= assuan_write_status(ctx
, "DECOMPRESS",
325 print_fmt("%i %i", z
.total_out
, insize
));
342 } while (ret
!= Z_STREAM_END
);
345 rc
= assuan_write_status(ctx
, "DECOMPRESS",
346 print_fmt("%i %i", z
.total_out
, insize
));
355 *outsize
= z
.total_out
;
360 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
367 static int open_command(assuan_context_t ctx
, char *line
)
371 guchar shakey
[gcrykeysize
];
375 struct client_s
*client
= assuan_get_pointer(ctx
);
377 gchar
*filename
= NULL
;
379 if ((req
= split_input_line(line
, " ", 2)) != NULL
)
382 if (!filename
|| !*filename
) {
384 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
387 if (valid_filename(filename
) == FALSE
) {
389 return send_error(ctx
, EPWMD_INVALID_FILENAME
);
392 if (client
->state
== STATE_OPEN
)
393 cleanup_client(client
);
395 gcry_md_hash_buffer(GCRY_MD_MD5
, client
->md5file
, filename
, strlen(filename
));
396 CACHE_LOCK(client
->ctx
);
398 if (cache_has_file(client
->md5file
) == FALSE
) {
399 if (cache_add_file(client
->md5file
, NULL
) == FALSE
) {
402 return send_error(ctx
, EPWMD_MAX_SLOTS
);
406 cache_incr_refcount(client
->md5file
);
408 error
= lock_file_mutex(client
);
412 return send_error(ctx
, error
);
415 client
->freed
= FALSE
;
417 if ((error
= gcry_cipher_open(&client
->gh
, GCRY_CIPHER_AES256
, GCRY_CIPHER_MODE_CBC
, 0))) {
419 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
420 cleanup_client(client
);
421 return send_error(ctx
, error
);
424 if (stat(filename
, &st
) == 0) {
425 if (!S_ISREG(st
.st_mode
) && !S_ISLNK(st
.st_mode
)) {
426 log_write("%s: %s", filename
, pwmd_strerror(EPWMD_INVALID_FILENAME
));
428 cleanup_client(client
);
429 return send_error(ctx
, EPWMD_INVALID_FILENAME
);
432 client
->mtime
= st
.st_mtime
;
436 * New files don't need a key.
438 if (access(filename
, R_OK
|W_OK
) != 0) {
439 if (errno
!= ENOENT
) {
441 log_write("%s: %s", filename
, strerror(errno
));
443 cleanup_client(client
);
444 return send_syserror(ctx
, error
);
447 if ((client
->xml
= new_document()) == NULL
) {
448 log_write("%s", strerror(ENOMEM
));
450 cleanup_client(client
);
451 return send_syserror(ctx
, ENOMEM
);
454 client
->len
= xmlStrlen(client
->xml
);
456 client
->filename
= g_strdup(filename
);
458 if (!client
->filename
) {
460 cleanup_client(client
);
461 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
462 return send_syserror(ctx
, ENOMEM
);
465 if (req
[1] && *req
[1]) {
466 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, req
[1], strlen(req
[1]));
467 memset(req
[1], 0, strlen(req
[1]));
474 if ((fd
= open_file(filename
, &st
)) == -1) {
476 log_write("%s: %s", filename
, strerror(errno
));
478 cleanup_client(client
);
479 return send_syserror(ctx
, error
);
485 CACHE_LOCK(client
->ctx
);
487 if (cache_get_key(client
->md5file
, shakey
) == TRUE
)
491 * No key specified and no matching filename found in the cache.
493 if (!req
[1] || !*req
[1]) {
496 cleanup_client(client
);
498 return send_error(ctx
, EPWMD_KEY
);
505 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, req
[1], strlen(req
[1]));
506 memset(req
[1], 0, strlen(req
[1]));
509 error
= try_xml_decrypt(ctx
, fd
, st
, shakey
);
513 memset(shakey
, 0, sizeof(shakey
));
515 cleanup_client(client
);
516 return send_error(ctx
, error
);
520 client
->filename
= g_strdup(filename
);
522 if (!client
->filename
) {
523 memset(shakey
, 0, sizeof(shakey
));
524 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
526 cleanup_client(client
);
527 return send_syserror(ctx
, ENOMEM
);
531 CACHE_LOCK(client
->ctx
);
534 if (cache_update_key(client
->md5file
, shakey
) == FALSE
) {
535 memset(shakey
, 0, sizeof(shakey
));
536 log_write("%s(%i): %s", __FILE__
, __LINE__
, pwmd_strerror(EPWMD_MAX_SLOTS
));
538 cleanup_client(client
);
540 return send_error(ctx
, EPWMD_MAX_SLOTS
);
543 timeout
= get_key_file_integer(client
->filename
, "cache_timeout");
544 cache_reset_timeout(client
->md5file
, timeout
);
547 cache_set_timeout(client
->md5file
, -2);
550 memset(shakey
, 0, sizeof(shakey
));
554 error
= parse_xml(ctx
);
557 gcry_free(client
->xml
);
562 if (client
->new == FALSE
)
563 send_cache_status_all();
565 client
->state
= STATE_OPEN
;
568 return send_error(ctx
, error
);
571 gboolean
do_compress(assuan_context_t ctx
, gint level
, gpointer data
,
572 gint size
, gpointer
*out
, glong
*outsize
, gint
*error
)
579 gint cmd
= Z_NO_FLUSH
;
584 z
.next_in
= pin
= data
;
585 z
.avail_in
= size
< zlib_bufsize
? size
: zlib_bufsize
;
586 z
.avail_out
= zlib_bufsize
;
587 z
.next_out
= pout
= g_malloc(zlib_bufsize
);
590 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, strerror(ENOMEM
));
591 *error
= Z_MEM_ERROR
;
595 ret
= deflateInit2(&z
, level
, Z_DEFLATED
, 31, 8, Z_DEFAULT_STRATEGY
);
598 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
604 memset(&h
, 0, sizeof(gz_header
));
605 snprintf(buf
, sizeof(buf
), "%i", size
);
606 h
.comment
= (guchar
*)buf
;
607 ret
= deflateSetHeader(&z
, &h
);
610 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
620 ret
= deflate(&z
, cmd
);
627 p
= g_realloc(pout
, z
.total_out
+ zlib_bufsize
);
635 z
.next_out
= pout
+ z
.total_out
;
636 z
.avail_out
= zlib_bufsize
;
639 if (!z
.avail_in
&& z
.total_in
< size
) {
640 if (z
.total_in
+ zlib_bufsize
> size
)
641 z
.avail_in
= size
- z
.total_in
;
643 z
.avail_in
= zlib_bufsize
;
646 rc
= assuan_write_status(ctx
, "COMPRESS",
647 print_fmt("%i %i", z
.total_in
, size
));
656 if (z
.total_in
>= size
)
667 } while (ret
!= Z_STREAM_END
);
670 rc
= assuan_write_status(ctx
, "COMPRESS",
671 print_fmt("%i %i", z
.total_in
, size
));
680 *outsize
= z
.total_out
;
685 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
692 gpg_error_t
do_xml_encrypt(struct client_s
*client
, gcry_cipher_hd_t gh
,
693 const gchar
*filename
, gpointer data
, size_t insize
,
694 const guchar
*shakey
, guint iter
)
699 guchar tkey
[gcrykeysize
];
703 guint iter_progress
= 0, n_iter
= 0, xiter
= 0;
704 gchar tmp
[FILENAME_MAX
];
705 struct file_header_s
{
707 guchar iv
[gcryblocksize
];
710 if (insize
/ gcryblocksize
) {
711 len
= (insize
/ gcryblocksize
) * gcryblocksize
;
713 if (insize
% gcryblocksize
)
714 len
+= gcryblocksize
;
718 * Resize the existing xml buffer to the block size required by gcrypt
719 * rather than duplicating it and wasting memory.
721 inbuf
= gcry_realloc(data
, len
);
724 return gpg_error_from_errno(ENOMEM
);
727 gcry_create_nonce(file_header
.iv
, sizeof(file_header
.iv
));
728 memcpy(tkey
, shakey
, sizeof(tkey
));
731 if ((rc
= gcry_cipher_setkey(gh
, tkey
, gcrykeysize
))) {
733 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
737 file_header
.iter
= iter
;
740 iter_progress
= get_key_file_integer("default", "iteration_progress");
742 if (client
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
743 error
= assuan_write_status(client
->ctx
, "ENCRYPT", "0");
751 while (xiter
< file_header
.iter
) {
752 if (client
&& iter_progress
> 0 && xiter
>= iter_progress
) {
753 if (!(xiter
% iter_progress
)) {
754 error
= assuan_write_status(client
->ctx
, "ENCRYPT", print_fmt("%i",
755 ++n_iter
* iter_progress
));
764 if ((rc
= gcry_cipher_setiv(gh
, file_header
.iv
,
765 sizeof(file_header
.iv
)))) {
767 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
771 if (encrypt_xml(gh
, inbuf
, insize
, NULL
, 0)
774 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
782 if ((rc
= gcry_cipher_setiv(gh
, file_header
.iv
,
783 sizeof(file_header
.iv
)))) {
785 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
789 if ((rc
= gcry_cipher_setkey(gh
, shakey
, gcrykeysize
))) {
791 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
795 if (encrypt_xml(gh
, inbuf
, insize
, NULL
, 0) == FALSE
) {
797 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
801 if (client
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
802 error
= assuan_write_status(client
->ctx
, "ENCRYPT",
803 print_fmt("%i", file_header
.iter
));
812 g_snprintf(tmp
, sizeof(tmp
), ".%s.tmp", filename
);
814 if ((fd
= open(tmp
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600)) == -1) {
817 p
= strrchr(tmp
, '/');
819 log_write("%s: %s", p
, strerror(errno
));
820 return gpg_error_from_errno(error
);
825 * xml_import() from command line.
829 len
= pth_write(fd
, &file_header
, sizeof(struct file_header_s
));
831 if (len
!= sizeof(file_header
)) {
838 return gpg_error_from_errno(len
);
841 len
= pth_write(fd
, inbuf
, insize
);
850 return gpg_error_from_errno(len
);
853 if (fsync(fd
) == -1) {
857 return gpg_error_from_errno(len
);
866 if (stat(filename
, &st
) == 0)
867 mode
= st
.st_mode
& (S_IRWXU
|S_IRWXG
|S_IRWXO
);
869 if (rename(tmp
, filename
) == -1) {
872 return gpg_error_from_errno(len
);
876 chmod(filename
, mode
);
883 static int save_command(assuan_context_t ctx
, char *line
)
889 guchar shakey
[gcrykeysize
];
892 struct client_s
*client
= assuan_get_pointer(ctx
);
899 error
= file_modified(client
);
902 log_write("%s: %s", client
->filename
? client
->filename
: "",
903 pwmd_strerror(error
));
904 return send_error(ctx
, error
);
907 error
= lock_file_mutex(client
);
910 return send_error(ctx
, error
);
912 if (stat(client
->filename
, &st
) == -1 && errno
!= ENOENT
)
913 return send_syserror(ctx
, errno
);
915 if (errno
!= ENOENT
&& !S_ISREG(st
.st_mode
) && !S_ISLNK(st
.st_mode
)) {
916 log_write("%s: %s", client
->filename
, pwmd_strerror(EPWMD_INVALID_FILENAME
));
917 return send_error(ctx
, EPWMD_INVALID_FILENAME
);
920 if (!line
|| !*line
) {
923 if (cache_get_key(client
->md5file
, shakey
) == FALSE
) {
925 return send_error(ctx
, EPWMD_KEY
);
932 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, line
, strlen(line
));
933 memset(line
, 0, strlen(line
));
936 xmlDocDumpFormatMemory(client
->doc
, &p
, &len
, 0);
939 iter
= get_key_file_integer(client
->filename
, "compression_level");
944 if (do_compress(ctx
, iter
, xmlbuf
, len
, &outbuf
, &outsize
, &zerror
) == FALSE
) {
945 memset(shakey
, 0, sizeof(shakey
));
948 if (zerror
== Z_MEM_ERROR
) {
949 return send_syserror(ctx
, ENOMEM
);
952 return send_error(ctx
, GPG_ERR_COMPR_ALGO
);
960 if ((iter
= get_key_file_integer(client
->filename
, "iterations")) == -1)
963 error
= do_xml_encrypt(client
, client
->gh
, client
->filename
, xmlbuf
, len
, shakey
, iter
);
966 memset(shakey
, 0, sizeof(shakey
));
967 return send_error(ctx
, error
);
970 stat(client
->filename
, &st
);
971 client
->mtime
= st
.st_mtime
;
972 timeout
= get_key_file_integer(client
->filename
, "cache_timeout");
973 CACHE_LOCK(client
->ctx
);
976 memset(shakey
, 0, sizeof(shakey
));
977 cache_reset_timeout(client
->md5file
, timeout
);
980 if (client
->new == TRUE
)
981 send_cache_status_all();
984 return send_error(ctx
, 0);
987 if (cache_update_key(client
->md5file
, shakey
) == FALSE
) {
988 memset(shakey
, 0, sizeof(shakey
));
989 log_write("%s(%i): %s", __FILE__
, __LINE__
, pwmd_strerror(EPWMD_MAX_SLOTS
));
991 return send_error(ctx
, EPWMD_MAX_SLOTS
);
995 memset(shakey
, 0, sizeof(shakey
));
996 cache_reset_timeout(client
->md5file
, timeout
);
998 send_cache_status_all();
999 return send_error(ctx
, 0);
1002 static gboolean
contains_whitespace(const gchar
*str
)
1004 const gchar
*p
= str
;
1008 len
= g_utf8_strlen(p
++, -1) -1;
1011 c
= g_utf8_get_char(p
++);
1013 if (g_unichar_isspace(c
))
1020 static int delete_command(assuan_context_t ctx
, char *line
)
1022 struct client_s
*client
= assuan_get_pointer(ctx
);
1027 error
= file_modified(client
);
1030 log_write("%s: %s", client
->filename
? client
->filename
: "",
1031 pwmd_strerror(error
));
1032 return send_error(ctx
, error
);
1035 if (strchr(line
, '\t'))
1036 req
= split_input_line(line
, "\t", -1);
1038 req
= split_input_line(line
, " ", -1);
1041 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1043 n
= find_account(client
->doc
, &req
, &error
, NULL
, 0);
1047 return send_error(ctx
, error
);
1051 * No sub-node defined. Remove the entire node (account).
1060 return send_error(ctx
, 0);
1063 n
= find_elements(client
->doc
, n
->children
, req
+1, &error
, NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1067 return send_error(ctx
, error
);
1074 return send_error(ctx
, 0);
1078 * Don't return with assuan_process_done() here. This has been called from
1079 * assuan_process_next() and the command should be finished in
1082 static int store_command_finalize(gpointer data
, gint rc
, guchar
*line
,
1085 assuan_context_t ctx
= data
;
1086 struct client_s
*client
= assuan_get_pointer(ctx
);
1088 guchar
*result
= line
;
1090 gpg_error_t error
= file_modified(client
);
1102 req
= split_input_line((gchar
*)result
, "\t", 0);
1117 return EPWMD_COMMAND_SYNTAX
;
1119 if (valid_xml_element((xmlChar
*)*req
) == FALSE
) {
1121 return EPWMD_INVALID_ELEMENT
;
1124 if (valid_element_path(req
+1, TRUE
) == FALSE
) {
1126 return EPWMD_INVALID_ELEMENT
;
1130 n
= find_account(client
->doc
, &req
, &error
, NULL
, 0);
1132 if (error
&& error
== EPWMD_ELEMENT_NOT_FOUND
) {
1133 error
= new_account(client
->doc
, *req
);
1150 create_elements_cb(n
, req
+1, &error
, NULL
);
1152 find_elements(client
->doc
, n
->children
, req
+1, &error
,
1153 NULL
, NULL
, create_elements_cb
, FALSE
, 0, NULL
);
1157 client
->inquire_status
= INQUIRE_DONE
;
1161 static int store_command(assuan_context_t ctx
, char *line
)
1163 struct client_s
*client
= assuan_get_pointer(ctx
);
1164 gpg_error_t error
= file_modified(client
);
1167 log_write("%s: %s", client
->filename
? client
->filename
: "",
1168 pwmd_strerror(error
));
1169 return send_error(ctx
, error
);
1172 error
= assuan_inquire_ext(ctx
, "STORE", 0, store_command_finalize
, ctx
);
1175 return send_error(ctx
, error
);
1177 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1178 client
->inquire_status
= INQUIRE_BUSY
;
1182 static int get_command(assuan_context_t ctx
, char *line
)
1184 struct client_s
*client
= assuan_get_pointer(ctx
);
1189 error
= file_modified(client
);
1192 log_write("%s: %s", client
->filename
? client
->filename
: "",
1193 pwmd_strerror(error
));
1194 return send_error(ctx
, error
);
1197 req
= split_input_line(line
, "\t", -1);
1199 if (!req
|| !*req
) {
1201 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1204 n
= find_account(client
->doc
, &req
, &error
, NULL
, 0);
1208 return send_error(ctx
, error
);
1212 n
= find_elements(client
->doc
, n
->children
, req
+1, &error
, NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1217 return send_error(ctx
, error
);
1219 if (!n
|| !n
->children
)
1220 return send_error(ctx
, EPWMD_EMPTY_ELEMENT
);
1224 if (!n
|| !n
->content
|| !*n
->content
)
1225 return send_error(ctx
, EPWMD_EMPTY_ELEMENT
);
1227 error
= assuan_send_data(ctx
, n
->content
, xmlStrlen(n
->content
));
1228 return send_error(ctx
, error
);
1231 static gchar
*element_path_to_req(const gchar
*account
, xmlChar
*path
)
1240 for (n
= 0; *p
&& n
< 3; p
++) {
1245 if (strstr((gchar
*)p
, "text()") != NULL
)
1246 p
[xmlStrlen(p
) - 7] = 0;
1248 for (n
= 0; p
[n
]; n
++) {
1253 buf
= g_strdup_printf("%s\t%s", account
, p
);
1257 gboolean
strv_printf(gchar
***array
, const gchar
*fmt
, ...)
1262 gint len
= *array
? g_strv_length(*array
) : 0;
1268 if ((a
= g_realloc(*array
, (len
+ 2) * sizeof(gchar
*))) == NULL
)
1272 ret
= g_vasprintf(&buf
, fmt
, ap
);
1288 static xmlNodePtr
realpath_elements_cb(xmlNodePtr node
, gchar
**req
,
1289 gpg_error_t
*error
, void *data
)
1291 struct realpath_s
*rp
= data
;
1294 g_free(rp
->account
);
1296 rp
->account
= g_strdup(req
[0]);
1299 *error
= gpg_error_from_errno(ENOMEM
);
1306 static int realpath_command(assuan_context_t ctx
, char *line
)
1309 struct client_s
*client
= assuan_get_pointer(ctx
);
1315 struct realpath_s
*rp
;
1318 error
= file_modified(client
);
1321 log_write("%s: %s", client
->filename
? client
->filename
: "",
1322 pwmd_strerror(error
));
1323 return send_error(ctx
, error
);
1326 if (strchr(line
, '\t') != NULL
) {
1327 if ((req
= split_input_line(line
, "\t", 0)) == NULL
)
1328 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1331 if ((req
= split_input_line(line
, " ", 0)) == NULL
)
1332 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1335 n
= find_account(client
->doc
, &req
, &error
, NULL
, 0);
1339 return send_error(ctx
, error
);
1342 rp
= g_malloc(sizeof(struct realpath_s
));
1346 return send_syserror(ctx
, ENOMEM
);
1349 rp
->account
= g_strdup(req
[0]);
1353 return send_syserror(ctx
, ENOMEM
);
1357 n
= find_elements(client
->doc
, n
->children
, req
+1, &error
,
1358 NULL
, realpath_elements_cb
, NULL
, FALSE
, 0, rp
);
1361 g_free(rp
->account
);
1364 return send_error(ctx
, error
);
1368 p
= xmlGetNodePath(n
);
1369 result
= element_path_to_req(rp
->account
, p
);
1373 g_free(rp
->account
);
1377 return send_syserror(ctx
, ENOMEM
);
1380 string
= g_string_new(result
);
1382 g_free(rp
->account
);
1389 for (t
= string
->str
+ i
; *t
; t
++, i
++) {
1390 if (!i
|| *t
== '\t') {
1391 string
= g_string_insert_c(string
, !i
? i
++ : ++i
, '!');
1396 error
= assuan_send_data(ctx
, string
->str
, string
->len
);
1397 g_string_free(string
, TRUE
);
1398 return send_error(ctx
, error
);
1401 struct list_element_s
{
1406 static gboolean
append_to_element_list(struct list_element_s
*elements
)
1413 if (!elements
|| !elements
->elements
)
1416 tmp
= g_strjoinv("\t", elements
->elements
);
1421 g_strfreev(elements
->elements
);
1422 elements
->elements
= NULL
;
1423 total
= g_slist_length(elements
->list
);
1424 a
= g_utf8_collate_key(tmp
, -1);
1432 * Removes duplicate element paths from the list. This is needed when
1433 * appending an element tree from list_command(). The glib docs recommend
1434 * using g_utf8_collate_key() for a large number of strings.
1436 for (i
= 0; i
< total
; i
++) {
1437 gchar
*p
= g_slist_nth_data(elements
->list
, i
);
1438 gchar
*b
= g_utf8_collate_key(p
, -1);
1446 if (strcmp(a
, b
) == 0) {
1457 list
= g_slist_append(elements
->list
, tmp
);
1462 elements
->list
= list
;
1466 static gpg_error_t
do_list_recurse(xmlDocPtr doc
, xmlNodePtr node
,
1467 struct list_element_s
*elements
, gchar
*prefix
)
1472 if (append_to_element_list(elements
) == FALSE
)
1473 return gpg_error_from_errno(ENOMEM
);
1475 for (n
= node
; n
; n
= n
->next
) {
1476 if (n
->type
== XML_ELEMENT_NODE
) {
1477 xmlChar
*content
= node_has_attribute(n
, (xmlChar
*)"target");
1481 if (strv_printf(&elements
->elements
, "%s\t%s", prefix
, n
->name
) == FALSE
)
1482 return gpg_error_from_errno(ENOMEM
);
1484 if (append_to_element_list(elements
) == FALSE
)
1485 return gpg_error_from_errno(ENOMEM
);
1488 tmp
= g_strdup_printf("%s\t!%s", prefix
, n
->name
);
1491 return gpg_error_from_errno(ENOMEM
);
1493 if (strv_printf(&elements
->elements
, "%s", tmp
) == FALSE
) {
1495 return gpg_error_from_errno(ENOMEM
);
1499 error
= do_list_recurse(doc
, n
->children
, elements
, tmp
);
1502 if (error
&& error
!= EPWMD_ELEMENT_NOT_FOUND
)
1508 if (append_to_element_list(elements
) == FALSE
)
1509 return gpg_error_from_errno(ENOMEM
);
1516 static gpg_error_t
do_list_command(assuan_context_t ctx
, xmlDocPtr doc
,
1517 struct list_element_s
*elements
, char *line
)
1519 gchar
*prefix
= NULL
, *account
;
1520 gchar
**req
= NULL
, **oreq
= NULL
, *tmp
;
1522 gboolean account_is_literal
, account_has_target
= FALSE
;
1527 if ((req
= split_input_line(line
, "\t", 0)) == NULL
) {
1528 if ((req
= split_input_line(line
, " ", 0)) == NULL
)
1529 return EPWMD_COMMAND_SYNTAX
;
1532 prefix
= g_strdup(*req
);
1536 return gpg_error_from_errno(ENOMEM
);
1539 account
= g_strdup(*req
);
1544 return gpg_error_from_errno(ENOMEM
);
1547 oreq
= g_strdupv(req
);
1553 return gpg_error_from_errno(ENOMEM
);
1558 account_has_target
= FALSE
;
1559 account_is_literal
= is_literal_element_str(prefix
);
1560 n
= find_account(doc
, &p
, &error
, &account_has_target
, 0);
1570 if (!which
&& account_is_literal
== FALSE
&& account_has_target
== FALSE
) {
1571 tmp
= g_strdup_printf("!%s", prefix
);
1574 error
= gpg_error_from_errno(ENOMEM
);
1585 n
= find_elements(doc
, n
->children
, p
+1, &error
, NULL
, NULL
, NULL
,
1591 tmp
= g_strjoinv("\t", p
+1);
1593 error
= gpg_error_from_errno(ENOMEM
);
1597 t
= g_strdup_printf("%s\t%s", prefix
, tmp
);
1599 error
= gpg_error_from_errno(ENOMEM
);
1608 if (strv_printf(&elements
->elements
, "%s", prefix
) == FALSE
) {
1609 error
= gpg_error_from_errno(ENOMEM
);
1613 if (node_has_child_element(n
->children
) == FALSE
) {
1614 if (append_to_element_list(elements
) == FALSE
) {
1615 error
= gpg_error_from_errno(ENOMEM
);
1620 error
= do_list_recurse(doc
, n
->children
, elements
, prefix
);
1625 if (!which
++ && !*(p
+1) && account_is_literal
== FALSE
&& account_has_target
== TRUE
) {
1627 *oreq
= g_strdup_printf("!%s", account
);
1630 error
= gpg_error_from_errno(ENOMEM
);
1636 prefix
= g_strdup(*oreq
);
1639 error
= gpg_error_from_errno(ENOMEM
);
1658 * This could be faster especially when finding "target" attributes.
1660 static int list_command(assuan_context_t ctx
, char *line
)
1662 struct client_s
*client
= assuan_get_pointer(ctx
);
1664 struct list_element_s
*elements
= NULL
;
1669 if (disable_list_and_dump
== TRUE
)
1670 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
1672 error
= file_modified(client
);
1675 log_write("%s: %s", client
->filename
, pwmd_strerror(error
));
1676 return send_error(ctx
, error
);
1682 error
= list_accounts(client
->doc
, &str
);
1685 return send_error(ctx
, error
);
1687 error
= assuan_send_data(ctx
, str
->str
, str
->len
);
1688 g_string_free(str
, TRUE
);
1689 return send_error(ctx
, error
);
1692 elements
= g_malloc0(sizeof(struct list_element_s
));
1695 error
= gpg_error_from_errno(ENOMEM
);
1699 error
= do_list_command(ctx
, client
->doc
, elements
, line
);
1705 total
= g_slist_length(elements
->list
);
1708 error
= EPWMD_EMPTY_ELEMENT
;
1713 * Find element paths with a target and append those element trees to
1716 for (i
= 0; i
< total
; i
++) {
1719 tmp
= g_slist_nth_data(elements
->list
, i
);
1720 req
= split_input_line(tmp
, "\t", 0);
1723 if (g_str_has_prefix(tmp
, "!") == TRUE
) {
1731 for (p
= req
; *p
; p
++) {
1732 if (g_str_has_prefix(*p
, "!") == FALSE
)
1743 error
= do_list_command(ctx
, client
->doc
, elements
, tmp
);
1745 if (error
&& error
!= EPWMD_ELEMENT_NOT_FOUND
)
1748 total
= g_slist_length(elements
->list
);
1752 string
= g_string_new(NULL
);
1754 for (i
= 0; i
< total
; i
++) {
1755 tmp
= g_slist_nth_data(elements
->list
, i
);
1756 g_string_append_printf(string
, "%s\n", tmp
);
1760 string
= g_string_truncate(string
, string
->len
- 1);
1761 error
= assuan_send_data(ctx
, string
->str
, string
->len
);
1762 g_string_free(string
, TRUE
);
1767 g_slist_free(elements
->list
);
1769 if (elements
->elements
)
1770 g_strfreev(elements
->elements
);
1775 return send_error(ctx
, error
);
1778 static gpg_error_t
add_attribute(xmlNodePtr node
, const gchar
*name
,
1783 if ((a
= xmlHasProp(node
, (xmlChar
*)name
)) == NULL
) {
1784 a
= xmlNewProp(node
, (xmlChar
*)name
, (xmlChar
*)value
);
1787 return EPWMD_LIBXML_ERROR
;
1790 xmlNodeSetContent(a
->children
, (xmlChar
*)value
);
1796 * req[0] - element path
1798 static int attribute_list(assuan_context_t ctx
, gchar
**req
)
1800 struct client_s
*client
= assuan_get_pointer(ctx
);
1801 gchar
**attrlist
= NULL
;
1803 gchar
**path
= NULL
;
1809 if (!req
|| !req
[0])
1810 return EPWMD_COMMAND_SYNTAX
;
1812 if ((path
= split_input_line(req
[0], "\t", 0)) == NULL
) {
1814 * The first argument may be only an account.
1816 if ((path
= split_input_line(req
[0], " ", 0)) == NULL
)
1817 return EPWMD_COMMAND_SYNTAX
;
1820 n
= find_account(client
->doc
, &path
, &error
, NULL
, 0);
1828 n
= find_elements(client
->doc
, n
->children
, path
+1, &error
,
1829 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1839 for (a
= n
->properties
; a
; a
= a
->next
) {
1842 if ((pa
= g_realloc(attrlist
, (i
+ 2) * sizeof(gchar
*))) == NULL
) {
1844 g_strfreev(attrlist
);
1847 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(errno
));
1848 return gpg_error_from_errno(error
);
1853 attrlist
[i
] = g_strdup_printf("%s\t%s", (gchar
*)a
->name
, (gchar
*)an
->content
);
1856 g_strfreev(attrlist
);
1857 return gpg_error_from_errno(ENOMEM
);
1860 attrlist
[++i
] = NULL
;
1864 return EPWMD_EMPTY_ELEMENT
;
1866 line
= g_strjoinv("\n", attrlist
);
1869 g_strfreev(attrlist
);
1870 return gpg_error_from_errno(ENOMEM
);
1873 error
= assuan_send_data(ctx
, line
, strlen(line
));
1875 g_strfreev(attrlist
);
1880 * req[0] - attribute
1881 * req[1] - element path
1883 static int attribute_delete(struct client_s
*client
, gchar
**req
)
1887 gchar
**path
= NULL
;
1890 if (!req
|| !req
[0] || !req
[1])
1891 return EPWMD_COMMAND_SYNTAX
;
1893 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1895 * The first argument may be only an account.
1897 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
1898 return EPWMD_COMMAND_SYNTAX
;
1902 * Don't remove the "name" attribute for the account element. To remove an
1903 * account use DELETE <account>.
1905 if (!path
[1] && xmlStrEqual((xmlChar
*)req
[0], (xmlChar
*)"name")) {
1906 error
= EPWMD_ATTR_SYNTAX
;
1910 n
= find_account(client
->doc
, &path
, &error
, NULL
, 0);
1916 n
= find_elements(client
->doc
, n
->children
, path
+1, &error
,
1917 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1925 if ((a
= xmlHasProp(n
, (xmlChar
*)req
[0])) == NULL
)
1926 return EPWMD_ATTR_NOT_FOUND
;
1928 if (xmlRemoveProp(a
) == -1)
1929 return EPWMD_LIBXML_ERROR
;
1939 * Creates a "target" attribute. When other commands encounter an element with
1940 * this attribute, the element path is modified to the target value. If the
1941 * source element path doesn't exist when using 'ATTR SET target', it is
1942 * created, but the destination element path must exist.
1944 * req[0] - source element path
1945 * req[1] - destination element path
1947 static gpg_error_t
target_attribute(struct client_s
*client
, gchar
**req
)
1949 gchar
**src
, **dst
, *line
;
1953 if (!req
|| !req
[0] || !req
[1])
1954 return EPWMD_COMMAND_SYNTAX
;
1956 if ((src
= split_input_line(req
[0], "\t", 0)) == NULL
) {
1958 * The first argument may be only an account.
1960 if ((src
= split_input_line(req
[0], " ", 0)) == NULL
)
1961 return EPWMD_COMMAND_SYNTAX
;
1964 if (valid_element_path(src
, FALSE
) == FALSE
) {
1966 return EPWMD_INVALID_ELEMENT
;
1969 if ((dst
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1971 * The first argument may be only an account.
1973 if ((dst
= split_input_line(req
[1], " ", 0)) == NULL
) {
1974 error
= EPWMD_COMMAND_SYNTAX
;
1979 n
= find_account(client
->doc
, &dst
, &error
, NULL
, 0);
1982 * Make sure the destination element path exists.
1988 n
= find_elements(client
->doc
, n
->children
, dst
+1, &error
,
1989 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1996 n
= find_account(client
->doc
, &src
, &error
, NULL
, 0);
1999 if (error
== EPWMD_ELEMENT_NOT_FOUND
) {
2000 error
= new_account(client
->doc
, src
[0]);
2013 n
= create_target_elements_cb(n
, src
+1, &error
, NULL
);
2015 n
= find_elements(client
->doc
, n
->children
, src
+1, &error
,
2016 NULL
, NULL
, create_target_elements_cb
, FALSE
, 0, NULL
);
2022 * Reset the position of the element tree now that the elements
2023 * have been created.
2025 n
= find_account(client
->doc
, &src
, &error
, NULL
, 0);
2030 n
= find_elements(client
->doc
, n
->children
, src
+1, &error
,
2031 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
2037 line
= g_strjoinv("\t", dst
);
2038 error
= add_attribute(n
, "target", line
);
2057 * req[0] - account name
2060 static gpg_error_t
name_attribute(struct client_s
*client
, gchar
**req
)
2066 tmp
= g_strdupv(req
);
2069 return gpg_error_from_errno(ENOMEM
);
2071 n
= find_account(client
->doc
, &tmp
, &error
, NULL
, 0);
2077 if (g_utf8_collate(req
[0], req
[1]) == 0)
2081 * Will not overwrite an existing account.
2083 tmp
= g_strdupv(req
+1);
2086 return gpg_error_from_errno(ENOMEM
);
2088 n
= find_account(client
->doc
, &tmp
, &error
, NULL
, 0);
2091 if (error
&& error
!= EPWMD_ELEMENT_NOT_FOUND
)
2095 return EPWMD_ACCOUNT_EXISTS
;
2098 * Whitespace not allowed in account names.
2100 if (contains_whitespace(req
[1]) == TRUE
)
2101 return EPWMD_ATTR_SYNTAX
;
2103 tmp
= g_strdupv(req
);
2106 return gpg_error_from_errno(ENOMEM
);
2108 n
= find_account(client
->doc
, &tmp
, &error
, NULL
, 0);
2112 return EPWMD_ELEMENT_NOT_FOUND
;
2114 return add_attribute(n
, "name", req
[1]);
2118 * req[0] - attribute
2119 * req[1] - element path
2121 static int attribute_get(assuan_context_t ctx
, gchar
**req
)
2123 struct client_s
*client
= assuan_get_pointer(ctx
);
2129 if (!req
|| !req
[0] || !req
[1])
2130 return EPWMD_COMMAND_SYNTAX
;
2132 if (strchr(req
[1], '\t')) {
2133 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
)
2134 return EPWMD_COMMAND_SYNTAX
;
2137 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
2138 return EPWMD_COMMAND_SYNTAX
;
2141 n
= find_account(client
->doc
, &path
, &error
, NULL
, 0);
2147 n
= find_elements(client
->doc
, n
->children
, path
+1, &error
,
2148 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
2156 if ((a
= xmlGetProp(n
, (xmlChar
*)req
[0])) == NULL
)
2157 return EPWMD_ATTR_NOT_FOUND
;
2159 error
= assuan_send_data(ctx
, a
, xmlStrlen(a
));
2169 * req[0] - attribute
2170 * req[1] - element path
2173 static int attribute_set(struct client_s
*client
, gchar
**req
)
2175 gchar
**path
= NULL
;
2179 if (!req
|| !req
[0] || !req
[1] || !req
[2])
2180 return EPWMD_COMMAND_SYNTAX
;
2183 * Reserved attribute names.
2185 if (g_utf8_collate(req
[0], "name") == 0) {
2187 * Only reserved for the account element. Not the rest of the
2190 if (strchr(req
[1], '\t') == NULL
)
2191 return name_attribute(client
, req
+ 1);
2193 else if (g_utf8_collate(req
[0], "target") == 0)
2194 return target_attribute(client
, req
+ 1);
2196 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
) {
2198 * The first argument may be only an account.
2200 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
2201 return EPWMD_COMMAND_SYNTAX
;
2204 n
= find_account(client
->doc
, &path
, &error
, NULL
, 0);
2210 n
= find_elements(client
->doc
, n
->children
, path
+1, &error
,
2211 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
2218 return add_attribute(n
, req
[0], req
[2]);
2227 * req[1] - attribute name or element path if command is LIST
2228 * req[2] - element path
2229 * req[2] - element path or value
2231 static int attr_command(assuan_context_t ctx
, char *line
)
2233 struct client_s
*client
= assuan_get_pointer(ctx
);
2234 gchar
**req
= split_input_line(line
, " ", 4);
2235 gpg_error_t error
= 0;
2237 error
= file_modified(client
);
2240 log_write("%s: %s", client
->filename
? client
->filename
: "",
2241 pwmd_strerror(error
));
2243 return send_error(ctx
, error
);
2246 if (!req
|| !req
[0] || !req
[1]) {
2248 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2251 if (g_ascii_strcasecmp(req
[0], "SET") == 0)
2252 error
= attribute_set(client
, req
+1);
2253 else if (g_ascii_strcasecmp(req
[0], "GET") == 0)
2254 error
= attribute_get(ctx
, req
+1);
2255 else if (g_ascii_strcasecmp(req
[0], "DELETE") == 0)
2256 error
= attribute_delete(client
, req
+1);
2257 else if (g_ascii_strcasecmp(req
[0], "LIST") == 0)
2258 error
= attribute_list(ctx
, req
+1);
2260 error
= EPWMD_COMMAND_SYNTAX
;
2263 return send_error(ctx
, error
);
2266 static int iscached_command(assuan_context_t ctx
, char *line
)
2268 gchar
**req
= split_input_line(line
, " ", 0);
2271 if (!req
|| !*req
) {
2273 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2276 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[0], strlen(req
[0]));
2280 if (cache_iscached(md5file
) == FALSE
) {
2282 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2286 return send_error(ctx
, 0);
2289 gpg_error_t
send_cache_status(assuan_context_t ctx
)
2292 struct client_s
*client
= assuan_get_pointer(ctx
);
2294 CACHE_LOCK(client
->ctx
);
2295 tmp
= print_fmt("%i %i",
2297 (cache_size
/ sizeof(file_cache_t
)) - cache_file_count());
2300 return assuan_write_status(ctx
, "CACHE", tmp
);
2303 static int clearcache_command(assuan_context_t ctx
, char *line
)
2305 struct client_s
*client
= assuan_get_pointer(ctx
);
2306 gchar
**req
= split_input_line(line
, " ", 0);
2311 if (!req
|| !*req
) {
2313 cache_clear(client
->md5file
, 2);
2315 return send_error(ctx
, 0);
2318 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[0], strlen(req
[0]));
2321 if (cache_clear(md5file
, 1) == FALSE
) {
2323 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2327 return send_error(ctx
, 0);
2330 static int cachetimeout_command(assuan_context_t ctx
, char *line
)
2334 gchar
**req
= split_input_line(line
, " ", 0);
2336 struct client_s
*client
= assuan_get_pointer(ctx
);
2338 if (!req
|| !*req
|| !req
[1]) {
2340 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2344 timeout
= strtol(req
[0], &p
, 10);
2346 if (errno
!= 0 || *p
!= 0) {
2348 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2351 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[1], strlen(req
[1]));
2353 CACHE_LOCK(client
->ctx
);
2355 if (cache_set_timeout(md5file
, timeout
) == FALSE
) {
2357 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2361 send_cache_status_all();
2362 return send_error(ctx
, 0);
2365 static int dump_command(assuan_context_t ctx
, char *line
)
2369 struct client_s
*client
= assuan_get_pointer(ctx
);
2372 if (disable_list_and_dump
== TRUE
)
2373 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
2375 error
= file_modified(client
);
2378 log_write("%s: %s", client
->filename
? client
->filename
: "",
2379 pwmd_strerror(error
));
2380 return send_error(ctx
, error
);
2383 xmlDocDumpFormatMemory(client
->doc
, &xml
, &len
, 1);
2384 error
= assuan_send_data(ctx
, xml
, len
);
2386 return send_error(ctx
, error
);
2389 static int getconfig_command(assuan_context_t ctx
, gchar
*line
)
2391 struct client_s
*client
= assuan_get_pointer(ctx
);
2392 gpg_error_t error
= 0;
2395 if (strcmp(line
, "key") == 0 || strcmp(line
, "key_file") == 0)
2396 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
2398 p
= get_key_file_string(client
->filename
? client
->filename
: "default", line
);
2401 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2403 tmp
= expand_homedir(p
);
2406 error
= assuan_send_data(ctx
, p
, strlen(p
));
2408 return send_error(ctx
, error
);
2411 void cleanup_assuan(assuan_context_t ctx
)
2413 struct client_s
*cl
= assuan_get_pointer(ctx
);
2418 static void reset_notify(assuan_context_t ctx
)
2420 struct client_s
*cl
= assuan_get_pointer(ctx
);
2425 gpg_error_t
register_commands(assuan_context_t ctx
)
2429 int (*handler
)(assuan_context_t
, char *line
);
2431 { "OPEN", open_command
},
2432 { "SAVE", save_command
},
2433 { "LIST", list_command
},
2434 { "REALPATH", realpath_command
},
2435 { "STORE", store_command
},
2436 { "DELETE", delete_command
},
2437 { "GET", get_command
},
2438 { "ATTR", attr_command
},
2439 { "ISCACHED", iscached_command
},
2440 { "CLEARCACHE", clearcache_command
},
2441 { "CACHETIMEOUT", cachetimeout_command
},
2442 { "GETCONFIG", getconfig_command
},
2443 { "DUMP", dump_command
},
2450 for (i
=0; table
[i
].name
; i
++) {
2451 rc
= assuan_register_command (ctx
, table
[i
].name
, table
[i
].handler
);
2457 rc
= assuan_register_bye_notify(ctx
, cleanup_assuan
);
2461 return assuan_register_reset_notify(ctx
, reset_notify
);
2464 gpg_error_t
try_xml_decrypt(assuan_context_t ctx
, gint fd
, struct stat st
,
2470 guchar tkey
[gcrykeysize
];
2471 struct file_header_s
{
2473 guchar iv
[gcryblocksize
];
2475 struct client_s
*client
= ctx
? assuan_get_pointer(ctx
) : NULL
;
2476 gcry_cipher_hd_t gh
;
2477 guint iter
= 0, n_iter
= 0;
2479 void *outbuf
= NULL
;
2485 error
= gcry_cipher_open(&gh
, GCRY_CIPHER_AES256
, GCRY_CIPHER_MODE_CBC
, 0);
2493 lseek(fd
, 0, SEEK_SET
);
2494 insize
= st
.st_size
- sizeof(struct file_header_s
);
2495 iv
= gcry_malloc(gcryblocksize
);
2499 gcry_cipher_close(gh
);
2501 return gpg_error_from_errno(ENOMEM
);
2504 len
= pth_read(fd
, &file_header
, sizeof(struct file_header_s
));
2506 if (len
!= sizeof(file_header
)) {
2510 gcry_cipher_close(gh
);
2514 return gpg_error_from_errno(errno
);
2517 memcpy(iv
, &file_header
.iv
, sizeof(file_header
.iv
));
2518 inbuf
= gcry_malloc(insize
);
2522 gcry_cipher_close(gh
);
2525 return gpg_error_from_errno(ENOMEM
);
2528 len
= pth_read(fd
, inbuf
, insize
);
2530 if (len
!= insize
) {
2534 gcry_cipher_close(gh
);
2538 return gpg_error_from_errno(errno
);
2541 memcpy(tkey
, key
, sizeof(tkey
));
2544 if ((error
= gcry_cipher_setiv(gh
, iv
, gcryblocksize
))) {
2546 gcry_cipher_close(gh
);
2547 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2550 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2557 if ((error
= gcry_cipher_setkey(gh
, key
, gcrykeysize
))) {
2559 gcry_cipher_close(gh
);
2560 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2563 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2569 gcry_cipher_close(gh
);
2574 iter_progress
= get_key_file_integer("default", "iteration_progress");
2576 if (ctx
&& iter_progress
> 0 && file_header
.iter
>= iter_progress
) {
2577 error
= assuan_write_status(client
->ctx
, "DECRYPT", "0");
2586 error
= decrypt_xml(gh
, inbuf
, insize
, NULL
, 0);
2594 if ((error
= gcry_cipher_setkey(gh
, tkey
, gcrykeysize
))) {
2596 gcry_cipher_close(gh
);
2597 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2600 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2607 while (iter
< file_header
.iter
) {
2608 if (ctx
&& iter_progress
> 0 && iter
>= iter_progress
) {
2609 if (!(iter
% iter_progress
)) {
2610 error
= assuan_write_status(ctx
, "DECRYPT", print_fmt("%i",
2611 ++n_iter
* iter_progress
));
2621 if ((error
= gcry_cipher_setiv(gh
, iv
, gcryblocksize
))) {
2623 gcry_cipher_close(gh
);
2624 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2627 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2634 error
= decrypt_xml(gh
, inbuf
, insize
, NULL
, 0);
2638 gcry_cipher_close(gh
);
2639 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2642 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2653 if (ctx
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
2654 error
= assuan_write_status(ctx
, "DECRYPT", print_fmt("%i", file_header
.iter
));
2665 if (do_decompress(ctx
, inbuf
, insize
, &outbuf
, &outsize
, &zerror
) == FALSE
) {
2667 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2669 if (zerror
== Z_MEM_ERROR
) {
2671 return gpg_error_from_errno(ENOMEM
);
2673 else if (zerror
!= Z_DATA_ERROR
) {
2677 gcry_cipher_close(gh
);
2679 return EPWMD_BADKEY
;
2688 if (g_strncasecmp(inbuf
, "<?xml version=\"1.0\"?>", 21) != 0) {
2692 gcry_cipher_close(gh
);
2694 return EPWMD_BADKEY
;
2698 client
->xml
= inbuf
;
2699 client
->len
= insize
;
2702 gcry_cipher_close(gh
);
2710 * This is called after every Assuan command.
2712 void command_finalize(assuan_context_t ctx
, gint error
)
2714 struct client_s
*client
= assuan_get_pointer(ctx
);
2716 unlock_file_mutex(client
);