Fix remove_dots()

Rewrite to handle all normal and abnormal examples mentioned in RFC 3986
section 5.4.
The previous implementation failed the following test cases:

'http://www.libupnp.org/path1/path1' | '#frag1' -> 'http://www.libupnp.org/path1/#frag1' != 'http://www.libupnp.org/path1/path1#frag1' (0)
'http://127.0.0.1:6544/getDeviceDesc' | 'CDS_Event' -> 'http://127.0.0.1:6544/CDS_EventDesc' != 'http://127.0.0.1:6544/CDS_Event' (0)
'http://localhost/b/c/d;p?q' | 'g' -> 'http://localhost/b/c/g;p' != 'http://localhost/b/c/g' (0)
'http://localhost/b/c/d;p?q' | 'g/' -> 'http://localhost/b/c/g/p' != 'http://localhost/b/c/g/' (0)
'http://localhost/b/c/d;p?q' | '?y' -> 'http://localhost/b/c/?yp' != 'http://localhost/b/c/d;p?y' (0)
'http://localhost/b/c/d;p?q' | '#s' -> 'http://localhost/b/c/#sp' != 'http://localhost/b/c/d;p?q#s' (0)
'http://localhost/b/c/d;p?q' | ';x' -> 'http://localhost/b/c/;xp' != 'http://localhost/b/c/;x' (0)
'http://localhost/b/c/d;p?q' | '.' -> 'http://localhost/b/c/.;p' != 'http://localhost/b/c/' (0)
'http://localhost/b/c/d;p?q' | './' -> 'http://localhost/b/c/p' != 'http://localhost/b/c/' (0)
'http://localhost/b/c/d;p?q' | '..' -> 'http://localhost/b/c/..p' != 'http://localhost/b/' (0)
'http://localhost/b/c/d;p?q' | '/./g' -> 'http://localhost/./g' != 'http://localhost/g' (0)
'http://localhost/b/c/d;p?q' | '/../g' -> 'http://localhost/../g' != 'http://localhost/g' (0)
'http://localhost/b/c/d;p?q' | 'g.' -> 'http://localhost/b/c/g.p' != 'http://localhost/b/c/g.' (0)
'http://localhost/b/c/d;p?q' | '.g' -> 'http://localhost/b/c/.gp' != 'http://localhost/b/c/.g' (0)

Signed-off-by: Marcelo Roberto Jimenez <mroberto@users.sourceforge.net>
This commit is contained in:
Philipp Matthias Hahn 2014-05-01 10:41:20 +02:00 committed by Marcelo Roberto Jimenez
parent 0508fb0d6e
commit fbbb24f406

View File

@ -510,68 +510,81 @@ int remove_escaped_chars(INOUT char *in, INOUT size_t *size)
} }
int remove_dots(char *in, size_t size) static UPNP_INLINE int is_end_path(char c) {
switch (c) {
case '?':
case '#':
case '\0':
return 1;
}
return 0;
}
/* This function directly implements the "Remove Dot Segments"
* algorithm described in RFC 3986 section 5.2.4. */
int remove_dots(char *buf, size_t size)
{ {
char *copyTo = in; char *in = buf;
char *copyFrom = in; char *out = buf;
char *max = in + size; char *max = buf + size;
char **Segments = NULL;
int lastSegment = -1;
Segments = malloc( sizeof( char * ) * size ); while (!is_end_path(in[0])) {
assert (buf <= out);
assert (out <= in);
assert (in < max);
if( Segments == NULL ) /* case 2.A: */
return UPNP_E_OUTOF_MEMORY; if (strncmp(in, "./", 2) == 0) {
in += 2;
Segments[0] = NULL; } else if (strncmp(in, "../", 3) == 0) {
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, in += 3;
"REMOVE_DOTS: before: %s\n", in ); /* case 2.B: */
while( ( copyFrom < max ) && ( *copyFrom != '?' ) } else if (strncmp(in, "/./", 3) == 0) {
&& ( *copyFrom != '#' ) ) { in += 2;
} else if (strncmp(in, "/.", 2) == 0 && is_end_path(in[2])) {
if( ( ( *copyFrom ) == '.' ) in += 1;
&& ( ( copyFrom == in ) || ( *( copyFrom - 1 ) == '/' ) ) ) { in[0] = '/';
if( ( copyFrom + 1 == max ) /* case 2.C: */
|| ( *( copyFrom + 1 ) == '/' ) ) { } else if (strncmp(in, "/../", 4) == 0 || (strncmp(in, "/..", 3) == 0 && is_end_path(in[3]))) {
/* Make the next character in the input buffer a '/': */
copyFrom += 2; if (is_end_path(in[3])) { /* terminating "/.." case */
continue; in += 2;
} else if( ( *( copyFrom + 1 ) == '.' ) in[0] = '/';
&& ( ( copyFrom + 2 == max ) } else { /* "/../" prefix case */
|| ( *( copyFrom + 2 ) == '/' ) ) ) { in += 3;
copyFrom += 3;
if( lastSegment > 0 ) {
copyTo = Segments[--lastSegment];
} else {
free( Segments );
/*TRACE("ERROR RESOLVING URL, ../ at ROOT"); */
return UPNP_E_INVALID_URL;
}
continue;
} }
} /* Trim the last component from the output buffer, or empty it. */
while (buf < out)
if( ( *copyFrom ) == '/' ) { if (*--out == '/')
break;
lastSegment++; #ifdef DEBUG
Segments[lastSegment] = copyTo + 1; if (out < in)
} out[0] = '\0';
( *copyTo ) = ( *copyFrom ); #endif
copyTo++; /* case 2.D: */
copyFrom++; } else if (strncmp(in, ".", 1) == 0 && is_end_path(in[1])) {
} in += 1;
if( copyFrom < max ) { } else if (strncmp(in, "..", 2) == 0 && is_end_path(in[2])) {
while( copyFrom < max ) { in += 2;
( *copyTo ) = ( *copyFrom ); /* case 2.E */
copyTo++; } else {
copyFrom++; /* move initial '/' character (if any) */
if (in[0] == '/')
*out++ = *in++;
/* move first segment up to, but not including, the next '/' character */
while (in < max && in[0] != '/' && !is_end_path(in[0]))
*out++ = *in++;
#ifdef DEBUG
if (out < in)
out[0] = '\0';
#endif
} }
} }
( *copyTo ) = 0; while (in < max)
free( Segments ); *out++ = *in++;
UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, if (out < max)
"REMOVE_DOTS: after: %s\n", in ); out[0] = '\0';
return UPNP_E_SUCCESS; return UPNP_E_SUCCESS;
} }