From 6fc9a5731fbf64bf893836d4ebb0631768a99487 Mon Sep 17 00:00:00 2001 From: Ben Kibbey Date: Wed, 23 Jan 2008 20:08:14 -0500 Subject: [PATCH] Fixed the IMPORT command to overwrite an existing node of the same name as the root node of the imported content. --- COMMANDS | 78 +++++++++++++++++++++++++++++++--------------------------- src/commands.c | 59 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 91 insertions(+), 46 deletions(-) diff --git a/COMMANDS b/COMMANDS index 8589464d..5c193a3a 100644 --- a/COMMANDS +++ b/COMMANDS @@ -1,20 +1,19 @@ -The server uses a protocol provided by libassuan to communicate with a client. -An OK response is returned when a command succeeds or ERR along with an error -code and description, if not. When a command requests data for retrieval -(e.g., GET) the output is prefixed with D then a single SPACE then the actual -data followed by an OK response if successful. Read the libassuan docs for -more info about the protocol. +The server uses a protocol provided by libassuan to communicate with the +client. An OK response is returned when a command succeeds or ERR along with +an error code and description, if not. When a command requests data for +retrieval (e.g., GET) the output is prefixed with D then a single SPACE then +the actual data followed by a response. Read the libassuan docs for more info +about the protocol. PROTOCOL COMMANDS ----------------- - OPEN [] Opens using . If file is not found on the file-system, then a new document will be created. If the file is found, it is looked for in the file cache for an existing key. When found, the existing key will be used for decryption. When not found, pinentry(1) will be used to retrieve - the key (see OPTIONS below). You can also open another file using the + the key (see OPTIONS below). You can also open a different file using the same connection. When using an empty key and you want to avoid the pinentry dialog, set PINENTRY to 0 (see OPTIONS below). @@ -68,7 +67,7 @@ STORE [!]element[[[!]element[...]][content]] created. Otherwise, elements are TAB delimited and the content will be set to the last TAB delimited argument. If no content is specified after the last TAB then the content for the last specified element will be removed - or the content will be empty when creating a new element. + or empty if creating a new element. The only restriction of element names is that they not begin with a punctuation character (the literal '!' character is an exception) or digit @@ -79,9 +78,9 @@ STORE [!]element[[[!]element[...]][content]] PWMD reads the element path from the client via the Assuan INQUIRE protocol response. The STORE command is sent by itself without arguments, then the server responds with INQUIRE. The client then sends the element - path prefixed by a "D " data response. When finished, the client - sends "END" on an empty line. This is needed so an element path and value - can be more than 1000 bytes long, the Assuan protocol line limit. + path prefixed with a "D " data response. When finished, the client sends + "END" on an empty line. This is needed so an element path and value can be + more than 1000 bytes long, the Assuan protocol line limit. DELETE [!]element[[!]element[...]] @@ -89,13 +88,13 @@ DELETE [!]element[[!]element[...]] GET [!]element[[!]element[...]] - Retrieves the content of the specified element path. The data is returned - with a data response. + Retrieves the text content of the specified element path. The data is + returned with a data response. ATTR SET|GET|DELETE|LIST [] [!] [!][arg2] ATTR SET attribute [!]element[[!]element[...]] attribute_value - Stores or updates an attribute value to an element path. + Stores or updates an attribute value of an element path. ATTR DELETE attribute [!]element[[!]element[...]] Removes an attribute from an element path. @@ -122,18 +121,18 @@ XPATH [value] IMPORT [!]element[[!]element[...]] - Like the STORE command (an INQUIRE), but the content argument is real XML + Like the STORE command (an INQUIRE), but the content argument is raw XML data. The content is created as a child of the specified element path. If - an element of the element path doesn't exist, it is created. + an element of the element path does not exist, it is created. Note that the new content must begin with an element and not text. Also - note that the an existing child of the same name will not be overwritten - which will cause the new child to be unreachable to other commands. Use - the DELETE command to remove the original child before using this command. + note that an existing child node of the same element name as the root node + of the imported content will be overwritten. DUMP - Shows the in memory XML document with indenting. + Shows the in memory XML document with indenting. To dump a specific + element tree, use the XPATH command. GETCONFIG @@ -142,7 +141,7 @@ GETCONFIG "key_file" variables are ignored. -OPTION NAME=VALUE +OPTION = Sets an option NAME to VALUE. See OPTIONS below. @@ -158,7 +157,7 @@ error codes. OPTIONS ------- -Commands that require a key that is neither cached or specified will use +Commands that require a key that is neither cached nor specified will use pinentry(1) to retrieve the key. Pinentry options can be set with the OPTION command followed by the option name and value. Below are the available pinentry options: @@ -237,8 +236,10 @@ configuration settings: THE TARGET ATTRIBUTE -------------------- There is a special attribute "target" (case sensitive) that can be set with -ATTR SET. The value of this attribute is an element path somewhere else in the -XML document: +ATTR SET. The value of this attribute is an element path that is located +somewhere else in the XML document and are alot like how XPath treats +entities, but are needed do to how pwmd commands are implemented. The syntax +is as follows: ATTR SET target [!]element[[!]element[..]] [!]element[[!]element[..]] arg1^^^^^^^^^^^^^^^^^^^^^^^^ arg2^^^^^^^^^^^^^^^^^^^^^^^^ @@ -338,18 +339,18 @@ cache entry also contains a mutex for the opened file and a reference count which is adjusted each time a client connects or disconnects. This allows client A to issue commands that aren't related to the file opened by client B. The refcount is needed to prevent the mutex being overwritten when the entry -needs to be reset (CACHETIMEOUT for example) and there are any client +needs to be reset (CACHETIMEOUT for example) and there are any clients connected. The client_thread() uses an event to wait for the client FD to become ready for reading. When ready, it uses the libassuan assuan_process_next() function to prevent blocking. This allows for the keepalive_thread() and any signals send to client_thread() to continue to work and lets the Pth scheduler let -other (client) threads do their work too. +other (client) threads do their work too. It's completely asynchronous. The cleanup_thread() is a loop that sleeps at some interval and is responsible -for freeing the resources of the client after the associated client_thread() -terminates or exits. How it determines if the client exited is done with the +for freeing the resources of a client after client_thread() terminates or +exits. How it determines if the client exited is done with the PTH_UNTIL_TID_DEAD event; it checks a list of recorded connections for that event. @@ -357,10 +358,9 @@ When a key is required for the OPEN or SAVE commands, and OPTION PINENTRY=1, pinentry(1) is used to retrieve the key. This is done by creating a pipe() with the client_thread(), then pth_fork()'s the pinentry process which will write the key or error to the pipe that client_thread() will read from. After -the read, the pinentry should have exited and client_thread() calls the +the read, the pinentry will have exited and client_thread() calls the finalization function for the command that used pinentry. - ------------- --------------- ----------[server_loop]-_-_-_-[accept_thread] | ------------- --------------- @@ -383,11 +383,17 @@ finalization function for the command that used pinentry. | | | ----------- = | | |----[OPEN/SAVE] = | | ----------- = - -------------------- : fork() = - [ command finalize ] ---------- = - -------------------- [pinentry]==== + ------------------ : fork() = + [command finalize] ---------- = + ------------------ [pinentry]==== ---------- +There is also a library libpwmd which makes it easy for applications to use +the server. It supports it's own key retrieval and asynchronous interaction +with pwmd. + +The latest version of both pwmd and libpwmd can be obtained from +http://bjk.sourceforge.net/pwmd/. Feel free to send me any questions, bug +reports or feature requests. -Questions, bugs or feature requests can be sent to -Ben Kibbey . +Ben Kibbey diff --git a/src/commands.c b/src/commands.c index 4501e049..b1802f02 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1709,6 +1709,8 @@ static xmlNodePtr create_element_path(struct client_s *client, gchar ***path, gchar **src_orig = g_strdupv(src); xmlNodePtr n = NULL; + *rc = 0; + if (!src_orig) { *rc = gpg_error_from_errno(ENOMEM); goto fail; @@ -2286,7 +2288,7 @@ static int import_command_finalize(gpointer data, gint assuan_rc, guchar *line, { struct client_s *client = assuan_get_pointer((assuan_context_t)data); gpg_error_t rc = file_modified(client); - gchar **req, **path = NULL, *content; + gchar **req, **path = NULL, **path_orig = NULL, *content; xmlDocPtr doc; xmlNodePtr n, root, copy; @@ -2332,11 +2334,6 @@ static int import_command_finalize(gpointer data, gint assuan_rc, guchar *line, goto fail; } - n = create_element_path(client, &path, &rc); - - if (rc) - goto fail; - doc = xmlReadDoc((xmlChar *)content, NULL, "UTF-8", XML_PARSE_NOBLANKS); if (!doc) { @@ -2345,16 +2342,58 @@ static int import_command_finalize(gpointer data, gint assuan_rc, guchar *line, } root = xmlDocGetRootElement(doc); - copy = xmlCopyNode(root, 1); - n = xmlAddChild(n, copy); + path_orig = g_strdupv(path); - if (!n) { - rc = EPWMD_LIBXML_ERROR; + if (strv_printf(&path, "%s", (gchar *)root->name) == FALSE) { + g_strfreev(path_orig); + xmlFreeDoc(doc); + rc = gpg_error_from_errno(ENOMEM); goto fail; } + n = find_account(client->doc, &path, &rc, NULL, 0); + + if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) { + g_strfreev(path_orig); + xmlFreeDoc(doc); + goto fail; + } + else if (!rc) { + n = find_elements(client->doc, n->children, path+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL); + + if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) { + g_strfreev(path_orig); + xmlFreeDoc(doc); + goto fail; + } + else if (!rc) { + xmlNodePtr parent = n->parent; + + xmlUnlinkNode(n); + xmlFreeNode(n); + n = parent; + } + } + + g_strfreev(path); + path = path_orig; + + if (rc == EPWMD_ELEMENT_NOT_FOUND) { + n = create_element_path(client, &path, &rc); + + if (rc) { + xmlFreeDoc(doc); + goto fail; + } + } + + copy = xmlCopyNode(root, 1); + n = xmlAddChild(n, copy); xmlFreeDoc(doc); + if (!n) + rc = EPWMD_LIBXML_ERROR; + fail: g_strfreev(path); g_strfreev(req); -- 2.11.4.GIT