提交 acf59575 编写于 作者: N Nick Hengeveld 提交者: Junio C Hamano

Improve XML parsing in http-push

Improved XML parsing - replace specialized doc parser callbacks with generic
functions that track the parser context and use document-specific callbacks
to process that data.
Signed-off-by: NNick Hengeveld <nickh@reactrix.com>
Signed-off-by: NJunio C Hamano <junkio@cox.net>
上级 5e3a7691
...@@ -24,13 +24,28 @@ enum XML_Status { ...@@ -24,13 +24,28 @@ enum XML_Status {
#define RANGE_HEADER_SIZE 30 #define RANGE_HEADER_SIZE 30
/* DAV method names and request body templates */ /* DAV methods */
#define DAV_LOCK "LOCK" #define DAV_LOCK "LOCK"
#define DAV_MKCOL "MKCOL" #define DAV_MKCOL "MKCOL"
#define DAV_MOVE "MOVE" #define DAV_MOVE "MOVE"
#define DAV_PROPFIND "PROPFIND" #define DAV_PROPFIND "PROPFIND"
#define DAV_PUT "PUT" #define DAV_PUT "PUT"
#define DAV_UNLOCK "UNLOCK" #define DAV_UNLOCK "UNLOCK"
/* DAV lock flags */
#define DAV_PROP_LOCKWR (1u << 0)
#define DAV_PROP_LOCKEX (1u << 1)
#define DAV_LOCK_OK (1u << 2)
/* DAV XML properties */
#define DAV_CTX_LOCKENTRY ".multistatus.response.propstat.prop.supportedlock.lockentry"
#define DAV_CTX_LOCKTYPE_WRITE ".multistatus.response.propstat.prop.supportedlock.lockentry.locktype.write"
#define DAV_CTX_LOCKTYPE_EXCLUSIVE ".multistatus.response.propstat.prop.supportedlock.lockentry.lockscope.exclusive"
#define DAV_ACTIVELOCK_OWNER ".prop.lockdiscovery.activelock.owner.href"
#define DAV_ACTIVELOCK_TIMEOUT ".prop.lockdiscovery.activelock.timeout"
#define DAV_ACTIVELOCK_TOKEN ".prop.lockdiscovery.activelock.locktoken.href"
/* DAV request body templates */
#define PROPFIND_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:prop xmlns:R=\"%s\">\n<D:supportedlock/>\n</D:prop>\n</D:propfind>" #define PROPFIND_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:prop xmlns:R=\"%s\">\n<D:supportedlock/>\n</D:prop>\n</D:propfind>"
#define LOCK_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:lockinfo xmlns:D=\"DAV:\">\n<D:lockscope><D:exclusive/></D:lockscope>\n<D:locktype><D:write/></D:locktype>\n<D:owner>\n<D:href>mailto:%s</D:href>\n</D:owner>\n</D:lockinfo>" #define LOCK_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:lockinfo xmlns:D=\"DAV:\">\n<D:lockscope><D:exclusive/></D:lockscope>\n<D:locktype><D:write/></D:locktype>\n<D:owner>\n<D:href>mailto:%s</D:href>\n</D:owner>\n</D:lockinfo>"
...@@ -92,14 +107,17 @@ struct transfer_request ...@@ -92,14 +107,17 @@ struct transfer_request
static struct transfer_request *request_queue_head = NULL; static struct transfer_request *request_queue_head = NULL;
struct xml_ctx
{
char *name;
int len;
char *cdata;
void (*userFunc)(struct xml_ctx *ctx, int tag_closed);
void *userData;
};
struct active_lock struct active_lock
{ {
int ctx_activelock;
int ctx_owner;
int ctx_owner_href;
int ctx_timeout;
int ctx_locktoken;
int ctx_locktoken_href;
char *url; char *url;
char *owner; char *owner;
char *token; char *token;
...@@ -108,16 +126,6 @@ struct active_lock ...@@ -108,16 +126,6 @@ struct active_lock
int refreshing; int refreshing;
}; };
struct lockprop
{
int supported_lock;
int lock_entry;
int lock_scope;
int lock_type;
int lock_exclusive;
int lock_exclusive_write;
};
static void finish_request(struct transfer_request *request); static void finish_request(struct transfer_request *request);
static void process_response(void *callback_data) static void process_response(void *callback_data)
...@@ -734,107 +742,101 @@ int fetch_ref(char *ref, unsigned char *sha1) ...@@ -734,107 +742,101 @@ int fetch_ref(char *ref, unsigned char *sha1)
return 0; return 0;
} }
static void static void handle_lockprop_ctx(struct xml_ctx *ctx, int tag_closed)
start_activelock_element(void *userData, const char *name, const char **atts)
{ {
struct active_lock *lock = (struct active_lock *)userData; int *lock_flags = (int *)ctx->userData;
if (lock->ctx_activelock && !strcmp(name, "D:timeout")) if (tag_closed) {
lock->ctx_timeout = 1; if (!strcmp(ctx->name, DAV_CTX_LOCKENTRY)) {
else if (lock->ctx_owner && strstr(name, "href")) if ((*lock_flags & DAV_PROP_LOCKEX) &&
lock->ctx_owner_href = 1; (*lock_flags & DAV_PROP_LOCKWR)) {
else if (lock->ctx_activelock && strstr(name, "owner")) *lock_flags |= DAV_LOCK_OK;
lock->ctx_owner = 1; }
else if (lock->ctx_locktoken && !strcmp(name, "D:href")) *lock_flags &= DAV_LOCK_OK;
lock->ctx_locktoken_href = 1; } else if (!strcmp(ctx->name, DAV_CTX_LOCKTYPE_WRITE)) {
else if (lock->ctx_activelock && !strcmp(name, "D:locktoken")) *lock_flags |= DAV_PROP_LOCKWR;
lock->ctx_locktoken = 1; } else if (!strcmp(ctx->name, DAV_CTX_LOCKTYPE_EXCLUSIVE)) {
else if (!strcmp(name, "D:activelock")) *lock_flags |= DAV_PROP_LOCKEX;
lock->ctx_activelock = 1; }
}
} }
static void static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed)
end_activelock_element(void *userData, const char *name)
{ {
struct active_lock *lock = (struct active_lock *)userData; struct active_lock *lock = (struct active_lock *)ctx->userData;
if (lock->ctx_timeout && !strcmp(name, "D:timeout")) { if (tag_closed && ctx->cdata) {
lock->ctx_timeout = 0; if (!strcmp(ctx->name, DAV_ACTIVELOCK_OWNER)) {
} else if (lock->ctx_owner_href && strstr(name, "href")) { lock->owner = xmalloc(strlen(ctx->cdata) + 1);
lock->ctx_owner_href = 0; strcpy(lock->owner, ctx->cdata);
} else if (lock->ctx_owner && strstr(name, "owner")) { } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TIMEOUT)) {
lock->ctx_owner = 0; if (!strncmp(ctx->cdata, "Second-", 7))
} else if (lock->ctx_locktoken_href && !strcmp(name, "D:href")) { lock->timeout =
lock->ctx_locktoken_href = 0; strtol(ctx->cdata + 7, NULL, 10);
} else if (lock->ctx_locktoken && !strcmp(name, "D:locktoken")) { } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) {
lock->ctx_locktoken = 0; if (!strncmp(ctx->cdata, "opaquelocktoken:", 16)) {
} else if (lock->ctx_activelock && !strcmp(name, "D:activelock")) { lock->token = xmalloc(strlen(ctx->cdata - 15));
lock->ctx_activelock = 0; strcpy(lock->token, ctx->cdata + 16);
}
}
} }
} }
static void static void
activelock_cdata(void *userData, const XML_Char *s, int len) xml_start_tag(void *userData, const char *name, const char **atts)
{ {
struct active_lock *lock = (struct active_lock *)userData; struct xml_ctx *ctx = (struct xml_ctx *)userData;
char *this = malloc(len+1); const char *c = index(name, ':');
strncpy(this, s, len); int new_len;
if (lock->ctx_owner_href) { if (c == NULL)
lock->owner = malloc(len+1); c = name;
strcpy(lock->owner, this); else
} else if (lock->ctx_locktoken_href) { c++;
if (!strncmp(this, "opaquelocktoken:", 16)) {
lock->token = malloc(len-15); new_len = strlen(ctx->name) + strlen(c) + 2;
strcpy(lock->token, this+16);
} if (new_len > ctx->len) {
} else if (lock->ctx_timeout) { ctx->name = xrealloc(ctx->name, new_len);
if (!strncmp(this, "Second-", 7)) ctx->len = new_len;
lock->timeout = strtol(this+7, NULL, 10);
} }
strcat(ctx->name, ".");
strcat(ctx->name, c);
free(this); if (ctx->cdata) {
free(ctx->cdata);
ctx->cdata = NULL;
}
ctx->userFunc(ctx, 0);
} }
static void static void
start_lockprop_element(void *userData, const char *name, const char **atts) xml_end_tag(void *userData, const char *name)
{ {
struct lockprop *prop = (struct lockprop *)userData; struct xml_ctx *ctx = (struct xml_ctx *)userData;
const char *c = index(name, ':');
char *ep;
if (prop->lock_type && !strcmp(name, "D:write")) { ctx->userFunc(ctx, 1);
if (prop->lock_exclusive) {
prop->lock_exclusive_write = 1; if (c == NULL)
} c = name;
} else if (prop->lock_scope && !strcmp(name, "D:exclusive")) { else
prop->lock_exclusive = 1; c++;
} else if (prop->lock_entry) {
if (!strcmp(name, "D:lockscope")) { ep = ctx->name + strlen(ctx->name) - strlen(c) - 1;
prop->lock_scope = 1; *ep = 0;
} else if (!strcmp(name, "D:locktype")) {
prop->lock_type = 1;
}
} else if (prop->supported_lock) {
if (!strcmp(name, "D:lockentry")) {
prop->lock_entry = 1;
}
} else if (!strcmp(name, "D:supportedlock")) {
prop->supported_lock = 1;
}
} }
static void static void
end_lockprop_element(void *userData, const char *name) xml_cdata(void *userData, const XML_Char *s, int len)
{ {
struct lockprop *prop = (struct lockprop *)userData; struct xml_ctx *ctx = (struct xml_ctx *)userData;
if (ctx->cdata)
if (!strcmp(name, "D:lockentry")) { free(ctx->cdata);
prop->lock_entry = 0; ctx->cdata = xcalloc(len+1, 1);
prop->lock_scope = 0; strncpy(ctx->cdata, s, len);
prop->lock_type = 0;
prop->lock_exclusive = 0;
} else if (!strcmp(name, "D:supportedlock")) {
prop->supported_lock = 0;
}
} }
static struct active_lock *lock_remote(char *file, long timeout) static struct active_lock *lock_remote(char *file, long timeout)
...@@ -847,10 +849,11 @@ static struct active_lock *lock_remote(char *file, long timeout) ...@@ -847,10 +849,11 @@ static struct active_lock *lock_remote(char *file, long timeout)
char *url; char *url;
char *ep; char *ep;
char timeout_header[25]; char timeout_header[25];
struct active_lock *new_lock; struct active_lock *new_lock = NULL;
XML_Parser parser = XML_ParserCreate(NULL); XML_Parser parser = XML_ParserCreate(NULL);
enum XML_Status result; enum XML_Status result;
struct curl_slist *dav_headers = NULL; struct curl_slist *dav_headers = NULL;
struct xml_ctx ctx;
url = xmalloc(strlen(remote->url) + strlen(file) + 1); url = xmalloc(strlen(remote->url) + strlen(file) + 1);
sprintf(url, "%s%s", remote->url, file); sprintf(url, "%s%s", remote->url, file);
...@@ -894,12 +897,6 @@ static struct active_lock *lock_remote(char *file, long timeout) ...@@ -894,12 +897,6 @@ static struct active_lock *lock_remote(char *file, long timeout)
in_buffer.posn = 0; in_buffer.posn = 0;
in_buffer.buffer = in_data; in_buffer.buffer = in_data;
new_lock = xcalloc(1, sizeof(*new_lock));
new_lock->owner = NULL;
new_lock->token = NULL;
new_lock->timeout = -1;
new_lock->refreshing = 0;
sprintf(timeout_header, "Timeout: Second-%ld", timeout); sprintf(timeout_header, "Timeout: Second-%ld", timeout);
dav_headers = curl_slist_append(dav_headers, timeout_header); dav_headers = curl_slist_append(dav_headers, timeout_header);
dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml"); dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
...@@ -915,40 +912,41 @@ static struct active_lock *lock_remote(char *file, long timeout) ...@@ -915,40 +912,41 @@ static struct active_lock *lock_remote(char *file, long timeout)
curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK); curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK);
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
new_lock = xcalloc(1, sizeof(*new_lock));
new_lock->owner = NULL;
new_lock->token = NULL;
new_lock->timeout = -1;
new_lock->refreshing = 0;
if (start_active_slot(slot)) { if (start_active_slot(slot)) {
run_active_slot(slot); run_active_slot(slot);
if (slot->curl_result != CURLE_OK) { if (slot->curl_result == CURLE_OK) {
fprintf(stderr, "Got HTTP error %ld\n", slot->http_code); ctx.name = xcalloc(10, 1);
free(new_lock); ctx.len = 0;
free(url); ctx.cdata = NULL;
free(out_data); ctx.userFunc = handle_new_lock_ctx;
free(in_data); ctx.userData = new_lock;
return NULL; XML_SetUserData(parser, &ctx);
XML_SetElementHandler(parser, xml_start_tag,
xml_end_tag);
XML_SetCharacterDataHandler(parser, xml_cdata);
result = XML_Parse(parser, in_buffer.buffer,
in_buffer.posn, 1);
free(ctx.name);
if (result != XML_STATUS_OK) {
fprintf(stderr, "XML error: %s\n",
XML_ErrorString(
XML_GetErrorCode(parser)));
new_lock->timeout = -1;
}
} }
} else { } else {
free(new_lock);
free(url);
free(out_data);
free(in_data);
fprintf(stderr, "Unable to start request\n"); fprintf(stderr, "Unable to start request\n");
return NULL;
} }
curl_slist_free_all(dav_headers);
free(out_data); free(out_data);
XML_SetUserData(parser, new_lock);
XML_SetElementHandler(parser, start_activelock_element,
end_activelock_element);
XML_SetCharacterDataHandler(parser, activelock_cdata);
result = XML_Parse(parser, in_buffer.buffer, in_buffer.posn, 1);
free(in_data); free(in_data);
if (result != XML_STATUS_OK) {
fprintf(stderr, "%s", XML_ErrorString(
XML_GetErrorCode(parser)));
free(url);
free(new_lock);
return NULL;
}
if (new_lock->token == NULL || new_lock->timeout <= 0) { if (new_lock->token == NULL || new_lock->timeout <= 0) {
if (new_lock->token != NULL) if (new_lock->token != NULL)
...@@ -957,11 +955,12 @@ static struct active_lock *lock_remote(char *file, long timeout) ...@@ -957,11 +955,12 @@ static struct active_lock *lock_remote(char *file, long timeout)
free(new_lock->owner); free(new_lock->owner);
free(url); free(url);
free(new_lock); free(new_lock);
return NULL; new_lock = NULL;
} else {
new_lock->url = url;
new_lock->start_time = time(NULL);
} }
new_lock->url = url;
new_lock->start_time = time(NULL);
return new_lock; return new_lock;
} }
...@@ -1000,13 +999,15 @@ static int unlock_remote(struct active_lock *lock) ...@@ -1000,13 +999,15 @@ static int unlock_remote(struct active_lock *lock)
if (lock->owner != NULL) if (lock->owner != NULL)
free(lock->owner); free(lock->owner);
free(lock->url); free(lock->url);
/* Freeing the token causes a segfault...
free(lock->token); free(lock->token);
*/
free(lock); free(lock);
return rc; return rc;
} }
static int check_locking(void) static int locking_available(void)
{ {
struct active_request_slot *slot; struct active_request_slot *slot;
struct buffer in_buffer; struct buffer in_buffer;
...@@ -1015,8 +1016,9 @@ static int check_locking(void) ...@@ -1015,8 +1016,9 @@ static int check_locking(void)
char *out_data; char *out_data;
XML_Parser parser = XML_ParserCreate(NULL); XML_Parser parser = XML_ParserCreate(NULL);
enum XML_Status result; enum XML_Status result;
struct lockprop supported_lock;
struct curl_slist *dav_headers = NULL; struct curl_slist *dav_headers = NULL;
struct xml_ctx ctx;
int lock_flags = 0;
out_buffer.size = strlen(PROPFIND_REQUEST) + strlen(remote->url) - 2; out_buffer.size = strlen(PROPFIND_REQUEST) + strlen(remote->url) - 2;
out_data = xmalloc(out_buffer.size + 1); out_data = xmalloc(out_buffer.size + 1);
...@@ -1045,30 +1047,35 @@ static int check_locking(void) ...@@ -1045,30 +1047,35 @@ static int check_locking(void)
if (start_active_slot(slot)) { if (start_active_slot(slot)) {
run_active_slot(slot); run_active_slot(slot);
free(out_data); if (slot->curl_result == CURLE_OK) {
if (slot->curl_result != CURLE_OK) { ctx.name = xcalloc(10, 1);
free(in_buffer.buffer); ctx.len = 0;
return -1; ctx.cdata = NULL;
ctx.userFunc = handle_lockprop_ctx;
ctx.userData = &lock_flags;
XML_SetUserData(parser, &ctx);
XML_SetElementHandler(parser, xml_start_tag,
xml_end_tag);
result = XML_Parse(parser, in_buffer.buffer,
in_buffer.posn, 1);
free(ctx.name);
if (result != XML_STATUS_OK) {
fprintf(stderr, "XML error: %s\n",
XML_ErrorString(
XML_GetErrorCode(parser)));
lock_flags = 0;
}
} }
XML_SetUserData(parser, &supported_lock);
XML_SetElementHandler(parser, start_lockprop_element,
end_lockprop_element);
result = XML_Parse(parser, in_buffer.buffer, in_buffer.posn, 1);
free(in_buffer.buffer);
if (result != XML_STATUS_OK)
return error("%s", XML_ErrorString(
XML_GetErrorCode(parser)));
} else { } else {
free(out_data); fprintf(stderr, "Unable to start request\n");
free(in_buffer.buffer);
return error("Unable to start request");
} }
if (supported_lock.lock_exclusive_write) free(out_data);
return 0; free(in_buffer.buffer);
else curl_slist_free_all(dav_headers);
return 1;
return lock_flags;
} }
static int is_ancestor(unsigned char *sha1, struct commit *commit) static int is_ancestor(unsigned char *sha1, struct commit *commit)
...@@ -1269,7 +1276,7 @@ int main(int argc, char **argv) ...@@ -1269,7 +1276,7 @@ int main(int argc, char **argv)
"Pragma: no-cache"); "Pragma: no-cache");
/* Verify DAV compliance/lock support */ /* Verify DAV compliance/lock support */
if (check_locking() != 0) { if (!locking_available()) {
fprintf(stderr, "Error: no DAV locking support on remote repo %s\n", remote->url); fprintf(stderr, "Error: no DAV locking support on remote repo %s\n", remote->url);
rc = 1; rc = 1;
goto cleanup; goto cleanup;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册