fileurl.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. /** BEGIN COPYRIGHT BLOCK
  2. * This Program is free software; you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation; version 2 of the License.
  5. *
  6. * This Program is distributed in the hope that it will be useful, but WITHOUT
  7. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  8. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  9. *
  10. * You should have received a copy of the GNU General Public License along with
  11. * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
  12. * Place, Suite 330, Boston, MA 02111-1307 USA.
  13. *
  14. * In addition, as a special exception, Red Hat, Inc. gives You the additional
  15. * right to link the code of this Program with code not covered under the GNU
  16. * General Public License ("Non-GPL Code") and to distribute linked combinations
  17. * including the two, subject to the limitations in this paragraph. Non-GPL Code
  18. * permitted under this exception must only link to the code of this Program
  19. * through those well defined interfaces identified in the file named EXCEPTION
  20. * found in the source code files (the "Approved Interfaces"). The files of
  21. * Non-GPL Code may instantiate templates or use macros or inline functions from
  22. * the Approved Interfaces without causing the resulting work to be covered by
  23. * the GNU General Public License. Only Red Hat, Inc. may make changes or
  24. * additions to the list of Approved Interfaces. You must obey the GNU General
  25. * Public License in all respects for all of the Program code and other code used
  26. * in conjunction with the Program except the Non-GPL Code covered by this
  27. * exception. If you modify this file, you may extend this exception to your
  28. * version of the file, but you are not obligated to do so. If you do not wish to
  29. * provide this exception without modification, you must delete this exception
  30. * statement from your version and license this file solely under the GPL without
  31. * exception.
  32. *
  33. *
  34. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  35. * Copyright (C) 2005 Red Hat, Inc.
  36. * All rights reserved.
  37. * END COPYRIGHT BLOCK **/
  38. /*
  39. * LDIF tools fileurl.c -- functions for handling file URLs.
  40. * Used by ldif_parse_line.
  41. */
  42. #include <stdio.h>
  43. #include <stdlib.h>
  44. #include <string.h>
  45. #include <errno.h>
  46. #include "fileurl.h"
  47. #include <ctype.h> /* for isalpha() */
  48. static int str_starts_with( char *s, char *prefix );
  49. static void hex_unescape( char *s );
  50. static int unhex( char c );
  51. static void strcpy_escaped_and_convert( char *s1, char *s2 );
  52. /*
  53. * Convert a file URL to a local path.
  54. *
  55. * If successful, LDIF_FILEURL_SUCCESS is returned and *localpathp is
  56. * set point to an allocated string. If not, an different LDIF_FILEURL_
  57. * error code is returned.
  58. *
  59. * See RFCs 1738 and 2396 for a specification for file URLs... but
  60. * Netscape Navigator seems to be a bit more lenient in what it will
  61. * accept, especially on Windows).
  62. *
  63. * This function parses file URLs of these three forms:
  64. *
  65. * file:///path
  66. * file:/path
  67. * file://localhost/path
  68. * file://host/path (rejected with a ...NONLOCAL error)
  69. *
  70. * On Windows, we convert leading drive letters of the form C| to C:
  71. * and if a drive letter is present we strip off the slash that precedes
  72. * path. Otherwise, the leading slash is returned.
  73. *
  74. */
  75. int
  76. ldif_fileurl2path( char *fileurl, char **localpathp )
  77. {
  78. char *path;
  79. /*
  80. * Make sure this is a file name or URL we can handle.
  81. */
  82. if ( *fileurl == '/' ||
  83. ( isalpha( fileurl[0] ) && ( fileurl[1] == '|' || fileurl[1] == ':' ) ) ) {
  84. path = fileurl;
  85. goto path_ready;
  86. } else if ( !str_starts_with( fileurl, "file:" )) {
  87. return( LDIF_FILEURL_NOTAFILEURL );
  88. }
  89. path = fileurl + 5; /* skip past "file:" scheme prefix */
  90. if ( *path != '/' ) {
  91. return( LDIF_FILEURL_MISSINGPATH );
  92. }
  93. ++path; /* skip past '/' at end of "file:/" */
  94. if ( *path == '/' ) {
  95. ++path; /* remainder is now host/path or /path */
  96. if ( *path != '/' ) {
  97. /*
  98. * Make sure it is for the local host.
  99. */
  100. if ( str_starts_with( path, "localhost/" )) {
  101. path += 9;
  102. } else {
  103. return( LDIF_FILEURL_NONLOCAL );
  104. }
  105. }
  106. } else { /* URL is of the form file:/path */
  107. --path;
  108. }
  109. /*
  110. * The remainder is now of the form /path. On Windows, skip past the
  111. * leading slash if a drive letter is present.
  112. */
  113. #ifdef _WIN32
  114. if ( isalpha( path[1] ) && ( path[2] == '|' || path[2] == ':' )) {
  115. ++path;
  116. }
  117. #endif /* _WIN32 */
  118. path_ready:
  119. /*
  120. * Duplicate the path so we can safely alter it.
  121. * Unescape any %HH sequences.
  122. */
  123. if (( path = strdup( path )) == NULL ) {
  124. return( LDIF_FILEURL_NOMEMORY );
  125. }
  126. hex_unescape( path );
  127. #ifdef _WIN32
  128. /*
  129. * Convert forward slashes to backslashes for Windows. Also,
  130. * if we see a drive letter / vertical bar combination (e.g., c|)
  131. * at the beginning of the path, replace the '|' with a ':'.
  132. */
  133. {
  134. char *p;
  135. for ( p = path; *p != '\0'; ++p ) {
  136. if ( *p == '/' ) {
  137. *p = '\\';
  138. }
  139. }
  140. }
  141. if ( isalpha( path[0] ) && path[1] == '|' ) {
  142. path[1] = ':';
  143. }
  144. #endif /* _WIN32 */
  145. *localpathp = path;
  146. return( LDIF_FILEURL_SUCCESS );
  147. }
  148. /*
  149. * Convert a local path to a file URL.
  150. *
  151. * If successful, LDIF_FILEURL_SUCCESS is returned and *urlp is
  152. * set point to an allocated string. If not, an different LDIF_FILEURL_
  153. * error code is returned. At present, the only possible error is
  154. * LDIF_FILEURL_NOMEMORY.
  155. *
  156. * This function produces file URLs of the form file:path.
  157. *
  158. * On Windows, we convert leading drive letters to C|.
  159. *
  160. */
  161. int
  162. ldif_path2fileurl( char *path, char **urlp )
  163. {
  164. char *p, *url, *prefix ="file:";
  165. if ( NULL == path ) {
  166. path = "/";
  167. }
  168. /*
  169. * Allocate space for the URL, taking into account that path may
  170. * expand during the hex escaping process.
  171. */
  172. if (( url = malloc( strlen( prefix ) + 3 * strlen( path ) + 1 )) == NULL ) {
  173. return( LDIF_FILEURL_NOMEMORY );
  174. }
  175. strcpy( url, prefix );
  176. p = url + strlen( prefix );
  177. #ifdef _WIN32
  178. /*
  179. * On Windows, convert leading drive letters (e.g., C:) to the correct URL
  180. * syntax (e.g., C|).
  181. */
  182. if ( isalpha( path[0] ) && path[1] == ':' ) {
  183. *p++ = path[0];
  184. *p++ = '|';
  185. path += 2;
  186. *p = '\0';
  187. }
  188. #endif /* _WIN32 */
  189. /*
  190. * Append the path, encoding any URL-special characters using the %HH
  191. * convention.
  192. * On Windows, convert backwards slashes in the path to forward ones.
  193. */
  194. strcpy_escaped_and_convert( p, path );
  195. *urlp = url;
  196. return( LDIF_FILEURL_SUCCESS );
  197. }
  198. /*
  199. * Return a non-zero value if the string s begins with prefix and zero if not.
  200. */
  201. static int
  202. str_starts_with( char *s, char *prefix )
  203. {
  204. size_t prefix_len;
  205. if ( s == NULL || prefix == NULL ) {
  206. return( 0 );
  207. }
  208. prefix_len = strlen( prefix );
  209. if ( strlen( s ) < prefix_len ) {
  210. return( 0 );
  211. }
  212. return( strncmp( s, prefix, prefix_len ) == 0 );
  213. }
  214. /*
  215. * Remove URL hex escapes from s... done in place. The basic concept for
  216. * this routine is borrowed from the WWW library HTUnEscape() routine.
  217. *
  218. */
  219. static void
  220. hex_unescape( char *s )
  221. {
  222. char *p;
  223. for ( p = s; *s != '\0'; ++s ) {
  224. if ( *s == '%' ) {
  225. if ( *++s != '\0' ) {
  226. *p = unhex( *s ) << 4;
  227. }
  228. if ( *++s != '\0' ) {
  229. *p++ += unhex( *s );
  230. }
  231. } else {
  232. *p++ = *s;
  233. }
  234. }
  235. *p = '\0';
  236. }
  237. /*
  238. * Return the integer equivalent of one hex digit (in c).
  239. *
  240. */
  241. static int
  242. unhex( char c )
  243. {
  244. return( c >= '0' && c <= '9' ? c - '0'
  245. : c >= 'A' && c <= 'F' ? c - 'A' + 10
  246. : c - 'a' + 10 );
  247. }
  248. #define HREF_CHAR_ACCEPTABLE( c ) (( c >= '-' && c <= '9' ) || \
  249. ( c >= '@' && c <= 'Z' ) || \
  250. ( c == '_' ) || \
  251. ( c >= 'a' && c <= 'z' ))
  252. /*
  253. * Like strcat(), except if any URL-special characters are found in s2
  254. * they are escaped using the %HH convention and backslash characters are
  255. * converted to forward slashes on Windows.
  256. *
  257. * Maximum space needed in s1 is 3 * strlen( s2 ) + 1.
  258. *
  259. */
  260. static void
  261. strcpy_escaped_and_convert( char *s1, char *s2 )
  262. {
  263. char *p, *q;
  264. char *hexdig = "0123456789ABCDEF";
  265. p = s1 + strlen( s1 );
  266. for ( q = s2; *q != '\0'; ++q ) {
  267. #ifdef _WIN32
  268. if ( *q == '\\' ) {
  269. *p++ = '/';
  270. } else
  271. #endif /* _WIN32 */
  272. if ( HREF_CHAR_ACCEPTABLE( *q )) {
  273. *p++ = *q;
  274. } else {
  275. *p++ = '%';
  276. *p++ = hexdig[ 0x0F & ((*(unsigned char*)q) >> 4) ];
  277. *p++ = hexdig[ 0x0F & *q ];
  278. }
  279. }
  280. *p = '\0';
  281. }