HTParse.c
上传用户:zlh9724
上传日期:2007-01-04
资源大小:1991k
文件大小:16k
源码类别:

浏览器

开发平台:

Unix_Linux

  1. /*       HTParse.c
  2. ** URI MANAGEMENT
  3. **
  4. ** (c) COPYRIGHT MIT 1995.
  5. ** Please first read the full copyright statement in the file COPYRIGH.
  6. **
  7. ** history:
  8. ** May 12 94 TAB added as legal char in HTCleanTelnetString
  9. **
  10. */
  11. /* Library include files */
  12. #include "tcp.h"
  13. #include "HTUtils.h"
  14. #include "HTParse.h"
  15. #include "HTString.h"
  16. #include "HTTCP.h"
  17. struct struct_parts {
  18. char * access; /* Now known as "scheme" */
  19. char * host;
  20. char * absolute;
  21. char * relative;
  22. /* char * search; no - treated as part of path */
  23. char * anchor;
  24. };
  25. /* Scan a filename for its consituents
  26. ** -----------------------------------
  27. **
  28. ** On entry,
  29. ** name points to a document name which may be incomplete.
  30. ** On exit,
  31. **      absolute or relative may be nonzero (but not both).
  32. ** host, anchor and access may be nonzero if they were specified.
  33. ** Any which are nonzero point to zero terminated strings.
  34. */
  35. PRIVATE void scan (char * name, struct struct_parts * parts)
  36. {
  37.     char * after_access;
  38.     char * p;
  39.     int length = strlen(name);
  40.     
  41.     parts->access = 0;
  42.     parts->host = 0;
  43.     parts->absolute = 0;
  44.     parts->relative = 0;
  45.     parts->anchor = 0;
  46.     
  47.     after_access = name;
  48.     for(p=name; *p; p++) {
  49. if (*p==':') {
  50. *p = 0;
  51. parts->access = after_access; /* Scheme has been specified */
  52. /* The combination of gcc, the "-O" flag and the HP platform is
  53.    unhealthy. The following three lines is a quick & dirty fix, but is
  54.    not recommended. Rather, turn off "-O". */
  55. /* after_access = p;*/
  56. /* while (*after_access == 0)*/
  57. /*     after_access++;*/
  58. after_access = p+1;
  59. if (0==strcasecomp("URL", parts->access)) {
  60.     parts->access = NULL;  /* Ignore IETF's URL: pre-prefix */
  61. } else break;
  62. }
  63. if (*p=='/') break; /* Access has not been specified */
  64. if (*p=='#') break;
  65.     }
  66.     
  67.     for(p=name+length-1; p>=name; p--) {
  68. if (*p =='#') {
  69.     parts->anchor=p+1;
  70.     *p=0; /* terminate the rest */
  71. }
  72.     }
  73.     p = after_access;
  74.     if (*p=='/'){
  75. if (p[1]=='/') {
  76.     parts->host = p+2; /* host has been specified  */
  77.     *p=0; /* Terminate access  */
  78.     p=strchr(parts->host,'/'); /* look for end of host name if any */
  79.     if(p) {
  80.         *p=0; /* Terminate host */
  81.         parts->absolute = p+1; /* Root has been found */
  82.     }
  83. } else {
  84.     parts->absolute = p+1; /* Root found but no host */
  85. }     
  86.     } else {
  87.         parts->relative = (*after_access) ? after_access : 0; /* zero for "" */
  88.     }
  89. }
  90. /* Parse a Name relative to another name
  91. ** -------------------------------------
  92. **
  93. ** This returns those parts of a name which are given (and requested)
  94. ** substituting bits from the related name where necessary.
  95. **
  96. ** On entry,
  97. ** aName A filename given
  98. **      relatedName     A name relative to which aName is to be parsed. Give
  99. **                      it an empty string if aName is absolute.
  100. **      wanted          A mask for the bits which are wanted.
  101. **
  102. ** On exit,
  103. ** returns A pointer to a malloc'd string which MUST BE FREED
  104. */
  105. PUBLIC char * HTParse (CONST char *aName, CONST char *relatedName, int wanted)
  106. {
  107.     char * result = 0;
  108.     char * return_value = 0;
  109.     int len;
  110.     char * name = 0;
  111.     char * rel = 0;
  112.     char * p;
  113.     char * access;
  114.     struct struct_parts given, related;
  115.     
  116.     if (!relatedName)        /* HWL 23/8/94: dont dump due to NULL */
  117.         relatedName = "";
  118.     
  119.     /* Make working copies of input strings to cut up: */
  120.     len = strlen(aName)+strlen(relatedName)+10;
  121.     if ((result=(char *) HT_MALLOC(len)) == NULL) /* Lots of space: more than enough */
  122. HT_OUTOFMEM("parse space");
  123.     StrAllocCopy(name, aName);
  124.     StrAllocCopy(rel, relatedName);
  125.     
  126.     scan(name, &given);
  127.     scan(rel,  &related); 
  128.     result[0]=0; /* Clear string  */
  129.     access = given.access ? given.access : related.access;
  130.     if (wanted & PARSE_ACCESS)
  131.         if (access) {
  132.     strcat(result, access);
  133.     if(wanted & PARSE_PUNCTUATION) strcat(result, ":");
  134. }
  135.     if (given.access && related.access) /* If different, inherit nothing. */
  136.         if (strcmp(given.access, related.access)!=0) {
  137.     related.host=0;
  138.     related.absolute=0;
  139.     related.relative=0;
  140.     related.anchor=0;
  141. }
  142.     if (wanted & PARSE_HOST)
  143.         if(given.host || related.host) {
  144.     if(wanted & PARSE_PUNCTUATION) strcat(result, "//");
  145.     strcat(result, given.host ? given.host : related.host);
  146. }
  147.     if (given.host && related.host)  /* If different hosts, inherit no path. */
  148.         if (strcmp(given.host, related.host)!=0) {
  149.     related.absolute=0;
  150.     related.relative=0;
  151.     related.anchor=0;
  152. }
  153.     if (wanted & PARSE_PATH) {
  154.         if(given.absolute) { /* All is given */
  155.     if(wanted & PARSE_PUNCTUATION) strcat(result, "/");
  156.     strcat(result, given.absolute);
  157. } else if(related.absolute) { /* Adopt path not name */
  158.     strcat(result, "/");
  159.     strcat(result, related.absolute);
  160.     if (given.relative) {
  161. p = strchr(result, '?'); /* Search part? */
  162. if (!p) p=result+strlen(result)-1;
  163. for (; *p!='/'; p--); /* last / */
  164. p[1]=0; /* Remove filename */
  165. strcat(result, given.relative); /* Add given one */
  166. #if 0
  167. result = HTSimplify (&result);
  168. #endif
  169.     }
  170. } else if(given.relative) {
  171.     strcat(result, given.relative); /* what we've got */
  172. } else if(related.relative) {
  173.     strcat(result, related.relative);
  174. } else {  /* No inheritance */
  175.     strcat(result, "/");
  176. }
  177.     }
  178.     if (wanted & PARSE_ANCHOR)
  179. if(given.anchor || related.anchor) {
  180.     if(given.absolute && given.anchor) {   /*Fixes for relURLs...*/
  181. if(wanted & PARSE_PUNCTUATION) strcat(result, "#");
  182. strcat(result, given.anchor); 
  183.     } else if (!(given.absolute) && !(given.anchor)) {
  184. strcat(result, "");
  185.     } else {
  186. if(wanted & PARSE_PUNCTUATION) strcat(result, "#");
  187. strcat(result, given.anchor ? given.anchor : related.anchor); 
  188.     }
  189. }
  190.     HT_FREE(rel);
  191.     HT_FREE(name);
  192.     
  193.     StrAllocCopy(return_value, result);
  194.     HT_FREE(result);
  195.     return return_value; /* exactly the right length */
  196. }
  197. /*        HTCanon
  198. **
  199. ** Canonicalizes the URL in the following manner starting from the host
  200. ** pointer:
  201. **
  202. ** 1) The host name is converted to lowercase
  203. ** 2) Expands the host name of the URL from a local name to a full
  204. **    domain name. A host name is started by `://'.
  205. ** 3) Chop off port if `:80' (http), `:70' (gopher), or `:21' (ftp)
  206. **
  207. ** Return: OK The position of the current path part of the URL
  208. ** which might be the old one or a new one.
  209. */
  210. PRIVATE char *HTCanon  (char ** filename, char * host)
  211. {
  212.     char *newname = NULL;
  213.     char *port;
  214.     char *strptr;
  215.     char *path;
  216.     char *access = host-3;
  217.     while (access>*filename && *(access-1)!='/')       /* Find access method */
  218. access--;
  219.     if ((path = strchr(host, '/')) == NULL) /* Find path */
  220. path = host + strlen(host);
  221.     if ((strptr = strchr(host, '@')) != NULL && strptr<path)    /* UserId */
  222. host = strptr;
  223.     if ((port = strchr(host, ':')) != NULL && port>path)      /* Port number */
  224. port = NULL;
  225.     strptr = host;     /* Convert to lower-case */
  226.     while (strptr<path) {
  227. *strptr = TOLOWER(*strptr);
  228. strptr++;
  229.     }
  230.     
  231.     /* Does the URL contain a full domain name? This also works for a
  232.        numerical host name. The domain name is already made lower-case
  233.        and without a trailing dot. */
  234.     if (((strptr = strchr(host, '.')) == NULL || strptr >= path) &&
  235. strncasecomp(host, "localhost", 9)) {
  236. CONST char *domain = HTGetDomainName();
  237. if (domain && *domain) {
  238.     if ((newname = (char *) HT_CALLOC(1, strlen(*filename) + strlen(domain)+2)) == NULL)
  239. HT_OUTOFMEM("HTCanon");
  240.     if (port)
  241. strncpy(newname, *filename, (int) (port-*filename));
  242.     else
  243. strncpy(newname, *filename, (int) (path-*filename));
  244.     strcat(newname, ".");
  245.     strcat(newname, domain);
  246. }
  247.     } else {   /* Look for a trailing dot */
  248. char *dot = port ? port : path;
  249. if (dot > *filename && *--dot=='.') {
  250.     char *orig=dot, *dest=dot+1;
  251.     while((*orig++ = *dest++));
  252.     if (port) port--;
  253.     path--;
  254. }
  255.     }
  256.     /* Chop off port if `:', `:80' (http), `:70' (gopher), or `:21' (ftp) */
  257.     if (port) {
  258. if (!*(port+1) || *(port+1)=='/') {
  259.     if (!newname) {
  260. char *orig=port, *dest=port+1;
  261. while((*orig++ = *dest++));
  262.     }
  263. } else if ((!strncmp(access, "http", 4) &&
  264.      (*(port+1)=='8'&&*(port+2)=='0'&&(*(port+3)=='/'||!*(port+3)))) ||
  265.     (!strncmp(access, "gopher", 6) &&
  266.      (*(port+1)=='7'&&*(port+2)=='0'&&(*(port+3)=='/'||!*(port+3)))) ||
  267.     (!strncmp(access, "ftp", 3) &&
  268.      (*(port+1)=='2'&&*(port+2)=='1'&&(*(port+3)=='/'||!*(port+3))))) {
  269.     if (!newname) {
  270. char *orig=port, *dest=port+3;
  271. while((*orig++ = *dest++));
  272. path -= 3;           /* Update path position, Henry Minsky */
  273.     }
  274. } else if (newname)
  275.     strncat(newname, port, (int) (path-port));
  276.     }
  277.     if (newname) {
  278. char *newpath = newname+strlen(newname);
  279. strcat(newname, path);
  280. path = newpath;
  281. HT_FREE(*filename);     /* Free old copy */
  282. *filename = newname;
  283.     }
  284.     return path;
  285. }
  286. /*         Simplify a URI
  287. // --------------
  288. // A URI is allowed to contain the seqeunce xxx/../ which may be
  289. // replaced by "" , and the seqeunce "/./" which may be replaced by "/".
  290. // Simplification helps us recognize duplicate URIs. 
  291. //
  292. // Thus,  /etc/junk/../fred  becomes /etc/fred
  293. // /etc/junk/./fred becomes /etc/junk/fred
  294. //
  295. //      but we should NOT change
  296. // http://fred.xxx.edu/../..
  297. //
  298. // or ../../albert.html
  299. //
  300. // In order to avoid empty URLs the following URLs become:
  301. //
  302. // /fred/.. becomes /fred/..
  303. // /fred/././.. becomes /fred/..
  304. // /fred/.././junk/.././ becomes /fred/..
  305. //
  306. // If more than one set of `://' is found (several proxies in cascade) then
  307. // only the part after the last `://' is simplified.
  308. //
  309. // Returns: A string which might be the old one or a new one.
  310. */
  311. PUBLIC char *HTSimplify (char ** url)
  312. {
  313.     char *path;
  314.     char *p;
  315.     if (!url || !*url) {
  316. if (URI_TRACE) TTYPrint(TDEST, "HTSimplify.. Nothing donen");
  317. return *url;
  318.     }
  319.     if (URI_TRACE) TTYPrint(TDEST, "HTSimplify.. `%s' ", *url);
  320.     /* Find any scheme name */
  321.     if ((path = strstr(*url, "://")) != NULL) {    /* Find host name */
  322. char *newptr;
  323. char *access = *url;
  324. while (access<path && (*access=TOLOWER(*access))) access++;
  325. path += 3;
  326. while ((newptr = strstr(path, "://")) != NULL)        /* For proxies */
  327.     path = newptr+3;
  328. path = HTCanon(url, path);              /* We have a host name */
  329.     } else if ((path = strstr(*url, ":/")) != NULL) {
  330. path += 2;
  331.     } else
  332. path = *url;
  333.     if (*path == '/' && *(path+1)=='/') {   /* Some URLs start //<foo> */
  334. path += 1;
  335.     } else if (!strncmp(path, "news:", 5)) {
  336. char *ptr = strchr(path+5, '@');
  337. if (!ptr) ptr = path+5;
  338. while (*ptr) {     /* Make group or host lower case */
  339.     *ptr = TOLOWER(*ptr);
  340.     ptr++;
  341. }
  342. if (URI_TRACE)
  343.     TTYPrint(TDEST, "inton............ `%s'n", *url);
  344. return *url;       /* Doesn't need to do any more */
  345.     }
  346.     if ((p = path)) {
  347. char *end;
  348. if (!((end = strchr(path, ';')) || (end = strchr(path, '?')) ||
  349.       (end = strchr(path, '#'))))
  350.     end = path+strlen(path);
  351. /* Parse string second time to simplify */
  352. p = path;
  353. while(p<end) {
  354.     if (*p=='/') {
  355. if (p>*url && *(p+1)=='.' && (*(p+2)=='/' || !*(p+2))) {
  356.     char *orig = p+1;
  357.     char *dest = (*(p+2)!='/') ? p+2 : p+3;
  358.     while ((*orig++ = *dest++)); /* Remove a slash and a dot */
  359.     end = orig-1;
  360. } else if (*(p+1)=='.' && *(p+2)=='.' && (*(p+3)=='/' || !*(p+3))) {
  361.     char *q = p;
  362.     while (q>path && *--q!='/');        /* prev slash */
  363.     if (strncmp(q, "/../", 4)) {
  364. char *orig = q+1;
  365. char *dest = (*(p+3)!='/') ? p+3 : p+4;
  366. while ((*orig++ = *dest++));    /* Remove /xxx/.. */
  367. end = orig-1;
  368. p = q;       /* Start again with prev slash */
  369.     } else
  370. p++;
  371. } else if (*(p+1)=='/') {
  372.     while (*(p+1)=='/') {
  373. char *orig=p, *dest=p+1;
  374. while ((*orig++ = *dest++));  /* Remove multiple /'s */
  375. end = orig-1;
  376.     }
  377. } else
  378.     p++;
  379.     } else
  380. p++;
  381. }
  382.     }
  383.     if (URI_TRACE)
  384. TTYPrint(TDEST, "inton............ `%s'n", *url);
  385.     return *url;
  386. }
  387. #ifdef OLD_CODE
  388.     char * p = filename;
  389.     char * q;
  390.     
  391.     if (p) {
  392. while (*p && (*p == '/' || *p == '.'))     /* Pass starting / or .'s */
  393.     p++;
  394. while(*p) {
  395.     if (*p=='/') {
  396.     if ((p[1]=='.') && (p[2]=='.') && (p[3]=='/' || !p[3] )) {
  397. for (q=p-1; (q>=filename) && (*q!='/'); q--); /* prev slash */
  398. if (q[0]=='/' && 0!=strncmp(q, "/../", 4)
  399. &&!(q-1>filename && q[-1]=='/')) {
  400.             ari_strcpy(q, p+3); /* Remove  /xxx/.. */
  401.     if (!*filename) strcpy(filename, "/");
  402.     p = q-1; /* Start again with prev slash  */
  403. } else { /*   xxx/.. leave it! */
  404. #ifdef BUG_CODE
  405.     ari_strcpy(filename, p[3] ? p+4 : p+3); /* rm  xxx/../ */
  406.     p = filename; /* Start again */
  407. #endif
  408. }
  409.     } else if ((p[1]=='.') && (p[2]=='/' || !p[2])) {
  410.         ari_strcpy(p, p+2); /* Remove a slash and a dot */
  411.     } else if (p[-1] != ':') {
  412. while (p[1] == '/') {
  413.     ari_strcpy(p, p+1); /* Remove multiple slashes */
  414. }
  415.     }
  416.     }
  417.     p++;
  418. }  /* end while (*p) */
  419.     } /* end if (p) */
  420. }
  421. #endif /* OLD_CODE */
  422. /* Make Relative Name
  423. ** ------------------
  424. **
  425. ** This function creates and returns a string which gives an expression of
  426. ** one address as related to another. Where there is no relation, an absolute
  427. ** address is retured.
  428. **
  429. **  On entry,
  430. ** Both names must be absolute, fully qualified names of nodes
  431. ** (no anchor bits)
  432. **
  433. **  On exit,
  434. ** The return result points to a newly allocated name which, if
  435. ** parsed by HTParse relative to relatedName, will yield aName.
  436. ** The caller is responsible for freeing the resulting name later.
  437. **
  438. */
  439. PUBLIC char * HTRelative (CONST char * aName, CONST char * relatedName)
  440. {
  441.     char * result = 0;
  442.     CONST char *p = aName;
  443.     CONST char *q = relatedName;
  444.     CONST char * after_access = 0;
  445.     CONST char * path = 0;
  446.     CONST char * last_slash = 0;
  447.     int slashes = 0;
  448.     
  449.     for(;*p; p++, q++) { /* Find extent of match */
  450.      if (*p!=*q) break;
  451. if (*p==':') after_access = p+1;
  452. if (*p=='/') {
  453.     last_slash = p;
  454.     slashes++;
  455.     if (slashes==3) path=p;
  456. }
  457.     }
  458.     
  459.     /* q, p point to the first non-matching character or zero */
  460.     
  461.     if (!after_access) { /* Different access */
  462.         StrAllocCopy(result, aName);
  463.     } else if (slashes<3){ /* Different nodes */
  464.      StrAllocCopy(result, after_access);
  465.     } else { /* Some path in common */
  466.         int levels= 0;
  467.         for(; *q && (*q!='#'); q++)  if (*q=='/') levels++;
  468. if ((result = (char  *) HT_MALLOC(3*levels + strlen(last_slash) + 1)) == NULL)
  469.     HT_OUTOFMEM("HTRelative");
  470. result[0]=0;
  471. for(;levels; levels--)strcat(result, "../");
  472. strcat(result, last_slash+1);
  473.     }
  474.     if (URI_TRACE) TTYPrint(TDEST,
  475.       "HTRelative.. `%s' expressed relative to `%s' is `%s'n",
  476.        aName, relatedName, result);
  477.     return result;
  478. }
  479. /* HTCleanTelnetString()
  480.  * Make sure that the given string doesn't contain characters that
  481.  * could cause security holes, such as newlines in ftp, gopher,
  482.  * news or telnet URLs; more specifically: allows everything between
  483.  * ASCII 20-7E, and also A0-FE, inclusive. Also TAB ('t') allowed!
  484.  *
  485.  * On entry,
  486.  * str the string that is *modified* if necessary.  The
  487.  * string will be truncated at the first illegal
  488.  * character that is encountered.
  489.  * On exit,
  490.  * returns YES, if the string was modified.
  491.  * NO, otherwise.
  492.  */
  493. PUBLIC BOOL HTCleanTelnetString (char * str)
  494. {
  495.     char * cur = str;
  496.     if (!str) return NO;
  497.     while (*cur) {
  498. int a = TOASCII((unsigned char) *cur);
  499. if (a != 0x9 && (a < 0x20 || (a > 0x7E && a < 0xA0) ||  a > 0xFE)) {
  500.     if (URI_TRACE)
  501. TTYPrint(TDEST, "Illegal..... character in URL: "%s"n",str);
  502.     *cur = 0;
  503.     if (URI_TRACE)
  504. TTYPrint(TDEST, "Truncated... "%s"n",str);
  505.     return YES;
  506. }
  507. cur++;
  508.     }
  509.     return NO;
  510. }