Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1598 lines
44 KiB

  1. #include "msrating.h"
  2. #include <npassert.h>
  3. #include "array.h"
  4. #include "msluglob.h"
  5. #include "parselbl.h"
  6. #include "debug.h"
  7. #include <convtime.h>
  8. #include <wininet.h>
  9. extern BOOL LoadWinINet();
  10. COptionsBase::COptionsBase()
  11. {
  12. m_cRef = 1;
  13. m_timeUntil = 0xffffffff; /* as far in the future as possible */
  14. m_fdwFlags = 0;
  15. m_pszInvalidString = NULL;
  16. m_pszURL = NULL;
  17. }
  18. void COptionsBase::AddRef()
  19. {
  20. m_cRef++;
  21. }
  22. void COptionsBase::Release()
  23. {
  24. if (!--m_cRef)
  25. Delete();
  26. }
  27. void COptionsBase::Delete()
  28. {
  29. /* default does nothing when deleting reference */
  30. }
  31. BOOL COptionsBase::CheckUntil(DWORD timeUntil)
  32. {
  33. if (m_timeUntil <= timeUntil)
  34. {
  35. m_fdwFlags |= LBLOPT_EXPIRED;
  36. return FALSE;
  37. }
  38. return TRUE;
  39. }
  40. /* AppendSlash forces pszString to end in a single slash if it doesn't
  41. * already. This may produce a technically invalid URL (for example,
  42. * "http://gregj/default.htm/", but we're only using the result for
  43. * comparisons against other paths similarly mangled.
  44. */
  45. void AppendSlash(LPSTR pszString)
  46. {
  47. LPSTR pszSlash = ::strrchrf(pszString, '/');
  48. if (pszSlash == NULL || *(pszSlash + 1) != '\0')
  49. ::strcatf(pszString, "/");
  50. }
  51. extern BOOL (WINAPI *pfnInternetCrackUrl)(
  52. IN LPCTSTR lpszUrl,
  53. IN DWORD dwUrlLength,
  54. IN DWORD dwFlags,
  55. IN OUT LPURL_COMPONENTS lpUrlComponents
  56. );
  57. extern BOOL (WINAPI *pfnInternetCanonicalizeUrl)(
  58. IN LPCSTR lpszUrl,
  59. OUT LPSTR lpszBuffer,
  60. IN OUT LPDWORD lpdwBufferLength,
  61. IN DWORD dwFlags
  62. );
  63. BOOL DoURLsMatch(LPCSTR pszBaseURL, LPCSTR pszCheckURL, BOOL fGeneric)
  64. {
  65. /* Buffers to canonicalize URLs into */
  66. LPSTR pszBaseCanon = new char[INTERNET_MAX_URL_LENGTH + 1];
  67. LPSTR pszCheckCanon = new char[INTERNET_MAX_URL_LENGTH + 1];
  68. if (pszBaseCanon != NULL && pszCheckCanon != NULL)
  69. {
  70. BOOL fCanonOK = FALSE;
  71. DWORD cbBuffer = INTERNET_MAX_URL_LENGTH + 1;
  72. if (pfnInternetCanonicalizeUrl(pszBaseURL, pszBaseCanon, &cbBuffer, ICU_ENCODE_SPACES_ONLY))
  73. {
  74. cbBuffer = INTERNET_MAX_URL_LENGTH + 1;
  75. if (pfnInternetCanonicalizeUrl(pszCheckURL, pszCheckCanon, &cbBuffer, ICU_ENCODE_SPACES_ONLY))
  76. {
  77. fCanonOK = TRUE;
  78. }
  79. }
  80. if (!fCanonOK)
  81. {
  82. delete pszBaseCanon;
  83. pszBaseCanon = NULL;
  84. delete pszCheckCanon;
  85. pszCheckCanon = NULL;
  86. return FALSE;
  87. }
  88. }
  89. UINT cbBaseURL = strlenf(pszBaseCanon) + 1;
  90. LPSTR pszBaseUrlPath = new char[cbBaseURL];
  91. LPSTR pszBaseExtra = new char[cbBaseURL];
  92. CHAR szBaseHostName[INTERNET_MAX_HOST_NAME_LENGTH];
  93. CHAR szBaseUrlScheme[20]; // reasonable limit
  94. UINT cbCheckURL = strlenf(pszCheckCanon) + 1;
  95. LPSTR pszCheckUrlPath = new char[cbCheckURL];
  96. LPSTR pszCheckExtra = new char[cbCheckURL];
  97. CHAR szCheckHostName[INTERNET_MAX_HOST_NAME_LENGTH];
  98. CHAR szCheckUrlScheme[20]; // reasonable limit
  99. BOOL fOK = FALSE;
  100. if (pszBaseUrlPath != NULL &&
  101. pszBaseExtra != NULL &&
  102. pszCheckUrlPath != NULL &&
  103. pszCheckExtra != NULL)
  104. {
  105. URL_COMPONENTS ucBase, ucCheck;
  106. memset(&ucBase, 0, sizeof(ucBase));
  107. ucBase.dwStructSize = sizeof(ucBase);
  108. ucBase.lpszScheme = szBaseUrlScheme;
  109. ucBase.dwSchemeLength = sizeof(szBaseUrlScheme);
  110. ucBase.lpszHostName = szBaseHostName;
  111. ucBase.dwHostNameLength = sizeof(szBaseHostName);
  112. ucBase.lpszUrlPath = pszBaseUrlPath;
  113. ucBase.dwUrlPathLength = cbBaseURL;
  114. ucBase.lpszExtraInfo = pszBaseExtra;
  115. ucBase.dwExtraInfoLength = cbBaseURL;
  116. memset(&ucCheck, 0, sizeof(ucCheck));
  117. ucCheck.dwStructSize = sizeof(ucCheck);
  118. ucCheck.lpszScheme = szCheckUrlScheme;
  119. ucCheck.dwSchemeLength = sizeof(szCheckUrlScheme);
  120. ucCheck.lpszHostName = szCheckHostName;
  121. ucCheck.dwHostNameLength = sizeof(szCheckHostName);
  122. ucCheck.lpszUrlPath = pszCheckUrlPath;
  123. ucCheck.dwUrlPathLength = cbCheckURL;
  124. ucCheck.lpszExtraInfo = pszCheckExtra;
  125. ucCheck.dwExtraInfoLength = cbCheckURL;
  126. if (pfnInternetCrackUrl(pszBaseCanon, 0, 0, &ucBase) &&
  127. pfnInternetCrackUrl(pszCheckCanon, 0, 0, &ucCheck))
  128. {
  129. /* Scheme and host name must always match */
  130. if (!stricmpf(ucBase.lpszScheme, ucCheck.lpszScheme) &&
  131. !stricmpf(ucBase.lpszHostName, ucCheck.lpszHostName))
  132. {
  133. /* For extra info, just has to match exactly, even for a generic URL. */
  134. if (!*ucBase.lpszExtraInfo ||
  135. !stricmpf(ucBase.lpszExtraInfo, ucCheck.lpszExtraInfo))
  136. {
  137. AppendSlash(ucBase.lpszUrlPath);
  138. AppendSlash(ucCheck.lpszUrlPath);
  139. /* If not a generic label, path must match exactly too */
  140. if (!fGeneric)
  141. {
  142. if (!stricmpf(ucBase.lpszUrlPath, ucCheck.lpszUrlPath))
  143. {
  144. fOK = TRUE;
  145. }
  146. }
  147. else
  148. {
  149. UINT cbBasePath = strlenf(ucBase.lpszUrlPath);
  150. if (!strnicmpf(ucBase.lpszUrlPath, ucCheck.lpszUrlPath, cbBasePath))
  151. {
  152. fOK = TRUE;
  153. }
  154. }
  155. }
  156. }
  157. }
  158. }
  159. delete pszBaseUrlPath;
  160. pszBaseUrlPath = NULL;
  161. delete pszBaseExtra;
  162. pszBaseExtra = NULL;
  163. delete pszCheckUrlPath;
  164. pszCheckUrlPath = NULL;
  165. delete pszCheckExtra;
  166. pszCheckExtra = NULL;
  167. delete pszBaseCanon;
  168. pszBaseCanon = NULL;
  169. delete pszCheckCanon;
  170. pszCheckCanon = NULL;
  171. return fOK;
  172. }
  173. BOOL COptionsBase::CheckURL(LPCSTR pszURL)
  174. {
  175. if (!(m_fdwFlags & LBLOPT_URLCHECKED))
  176. {
  177. m_fdwFlags |= LBLOPT_URLCHECKED;
  178. BOOL fInvalid = FALSE;
  179. if (pszURL != NULL && m_pszURL != NULL)
  180. {
  181. if (LoadWinINet())
  182. {
  183. fInvalid = !DoURLsMatch(m_pszURL, pszURL, m_fdwFlags & LBLOPT_GENERIC);
  184. }
  185. }
  186. if (fInvalid)
  187. {
  188. m_fdwFlags |= LBLOPT_WRONGURL;
  189. }
  190. }
  191. return !(m_fdwFlags & LBLOPT_WRONGURL);
  192. }
  193. void CDynamicOptions::Delete()
  194. {
  195. delete this;
  196. }
  197. CParsedServiceInfo::CParsedServiceInfo()
  198. {
  199. m_pNext = NULL;
  200. m_poptCurrent = &m_opt;
  201. m_poptList = NULL;
  202. m_pszServiceName = NULL;
  203. m_pszErrorString = NULL;
  204. m_fInstalled = TRUE; /* assume the best */
  205. m_pszInvalidString = NULL;
  206. m_pszCurrent = NULL;
  207. }
  208. void FreeOptionsList(CDynamicOptions *pList)
  209. {
  210. while (pList != NULL)
  211. {
  212. CDynamicOptions *pNext = pList->m_pNext;
  213. delete pList;
  214. pList = pNext;
  215. }
  216. }
  217. CParsedServiceInfo::~CParsedServiceInfo()
  218. {
  219. FreeOptionsList(m_poptList);
  220. }
  221. void CParsedServiceInfo::Append(CParsedServiceInfo *pNew)
  222. {
  223. CParsedServiceInfo **ppNext = &m_pNext;
  224. while (*ppNext != NULL)
  225. {
  226. ppNext = &((*ppNext)->m_pNext);
  227. }
  228. *ppNext = pNew;
  229. pNew->m_pNext = NULL;
  230. }
  231. CParsedLabelList::CParsedLabelList()
  232. {
  233. m_pszList = NULL;
  234. m_fRated = FALSE;
  235. m_pszInvalidString = NULL;
  236. m_pszURL = NULL;
  237. m_pszOriginalLabel = NULL;
  238. m_fDenied = FALSE;
  239. m_fIsHelper = FALSE;
  240. m_fNoRating = FALSE;
  241. m_fIsCustomHelper = FALSE;
  242. m_pszRatingName = NULL;
  243. m_pszRatingReason = NULL;
  244. }
  245. CParsedLabelList::~CParsedLabelList()
  246. {
  247. delete m_pszList;
  248. m_pszList = NULL;
  249. CParsedServiceInfo *pInfo = m_ServiceInfo.Next();
  250. while (pInfo != NULL)
  251. {
  252. CParsedServiceInfo *pNext = pInfo->Next();
  253. delete pInfo;
  254. pInfo = pNext;
  255. }
  256. delete m_pszURL;
  257. m_pszURL = NULL;
  258. delete m_pszOriginalLabel;
  259. m_pszOriginalLabel = NULL;
  260. delete [] m_pszRatingName;
  261. m_pszRatingName = NULL;
  262. delete [] m_pszRatingReason;
  263. m_pszRatingReason = NULL;
  264. }
  265. /* SkipWhitespace(&pszString)
  266. *
  267. * advances pszString past whitespace characters
  268. */
  269. void SkipWhitespace(LPSTR *ppsz)
  270. {
  271. UINT cchWhitespace = ::strspnf(*ppsz, szWhitespace);
  272. *ppsz += cchWhitespace;
  273. }
  274. /* FindTokenEnd(pszStart)
  275. *
  276. * Returns a pointer to the end of a contiguous range of similarly-typed
  277. * characters (whitespace, quote mark, punctuation, or alphanumerics).
  278. */
  279. LPSTR FindTokenEnd(LPSTR pszStart)
  280. {
  281. LPSTR pszEnd = pszStart;
  282. if (*pszEnd == '\0')
  283. {
  284. return pszEnd;
  285. }
  286. else if (strchrf(szSingleCharTokens, *pszEnd))
  287. {
  288. return ++pszEnd;
  289. }
  290. UINT cch;
  291. cch = ::strspnf(pszEnd, szWhitespace);
  292. if (cch > 0)
  293. {
  294. return pszEnd + cch;
  295. }
  296. cch = ::strspnf(pszEnd, szExtendedAlphaNum);
  297. if (cch > 0)
  298. {
  299. return pszEnd + cch;
  300. }
  301. return pszEnd; /* unrecognized characters */
  302. }
  303. /* GetBool(LPSTR *ppszToken, BOOL *pfOut, PICSRulesBooleanSwitch PRBoolSwitch)
  304. *
  305. * t-markh 8/98 (
  306. * added default parameter PRBoolSwitch=PR_BOOLEAN_TRUEFALSE
  307. * this allows for no modification of existing code, and extension
  308. * of the GetBool function from true/false to include pass/fail and
  309. * yes/no. The enumerated type PICSRulesBooleanSwitch is defined
  310. * in picsrule.h)
  311. *
  312. * Parses a boolean value at the given token and returns its value in *pfOut.
  313. * Legal values are 't', 'f', 'true', and 'false'. If success, *ppszToken
  314. * is advanced past the boolean token and any following whitespace. If failure,
  315. * *ppszToken is not modified.
  316. *
  317. * pfOut may be NULL if the caller just wants to eat the token and doesn't
  318. * care about its value.
  319. */
  320. HRESULT GetBool(LPSTR *ppszToken, BOOL *pfOut, PICSRulesBooleanSwitch PRBoolSwitch)
  321. {
  322. BOOL bValue;
  323. LPSTR pszTokenEnd = FindTokenEnd(*ppszToken);
  324. switch(PRBoolSwitch)
  325. {
  326. case PR_BOOLEAN_TRUEFALSE:
  327. {
  328. if (IsEqualToken(*ppszToken, pszTokenEnd, szShortTrue) ||
  329. IsEqualToken(*ppszToken, pszTokenEnd, szTrue))
  330. {
  331. bValue = TRUE;
  332. }
  333. else if (IsEqualToken(*ppszToken, pszTokenEnd, szShortFalse) ||
  334. IsEqualToken(*ppszToken, pszTokenEnd, szFalse))
  335. {
  336. bValue = FALSE;
  337. }
  338. else
  339. {
  340. TraceMsg( TF_WARNING, "GetBool() - Failed True/False Token Parse at '%s'!", *ppszToken );
  341. return ResultFromScode(MK_E_SYNTAX);
  342. }
  343. break;
  344. }
  345. case PR_BOOLEAN_PASSFAIL:
  346. {
  347. //szPRShortPass and szPRShortfail are not supported in the
  348. //official PICSRules spec, but we'll catch them anyway
  349. if (IsEqualToken(*ppszToken, pszTokenEnd, szPRShortPass) ||
  350. IsEqualToken(*ppszToken, pszTokenEnd, szPRPass))
  351. {
  352. bValue = PR_PASSFAIL_PASS;
  353. }
  354. else if (IsEqualToken(*ppszToken, pszTokenEnd, szPRShortFail) ||
  355. IsEqualToken(*ppszToken, pszTokenEnd, szPRFail))
  356. {
  357. bValue = PR_PASSFAIL_FAIL;
  358. }
  359. else
  360. {
  361. TraceMsg( TF_WARNING, "GetBool() - Failed Pass/Fail Token Parse at '%s'!", *ppszToken );
  362. return ResultFromScode(MK_E_SYNTAX);
  363. }
  364. break;
  365. }
  366. case PR_BOOLEAN_YESNO:
  367. {
  368. if (IsEqualToken(*ppszToken, pszTokenEnd, szPRShortYes) ||
  369. IsEqualToken(*ppszToken, pszTokenEnd, szPRYes))
  370. {
  371. bValue = PR_YESNO_YES;
  372. }
  373. else if (IsEqualToken(*ppszToken, pszTokenEnd, szPRShortNo) ||
  374. IsEqualToken(*ppszToken, pszTokenEnd, szPRNo))
  375. {
  376. bValue = PR_YESNO_NO;
  377. }
  378. else
  379. {
  380. TraceMsg( TF_WARNING, "GetBool() - Failed Yes/No Token Parse at '%s'!", *ppszToken );
  381. return ResultFromScode(MK_E_SYNTAX);
  382. }
  383. break;
  384. }
  385. default:
  386. {
  387. return(MK_E_UNAVAILABLE);
  388. }
  389. }
  390. if (pfOut != NULL)
  391. {
  392. *pfOut = bValue;
  393. }
  394. *ppszToken = pszTokenEnd;
  395. SkipWhitespace(ppszToken);
  396. return NOERROR;
  397. }
  398. /* GetQuotedToken(&pszThisToken, &pszQuotedToken)
  399. *
  400. * Sets pszQuotedToken to point to the contents of the doublequotes.
  401. * pszQuotedToken may be NULL if the caller just wants to eat the token.
  402. * Sets pszThisToken to point to the first character after the closing
  403. * doublequote.
  404. * Fails if pszThisToken doesn't start with a doublequote or doesn't
  405. * contain a closing doublequote.
  406. * The closing doublequote is replaced with a null terminator, iff the
  407. * function does not fail.
  408. */
  409. HRESULT GetQuotedToken(LPSTR *ppszThisToken, LPSTR *ppszQuotedToken)
  410. {
  411. HRESULT hres = ResultFromScode(MK_E_SYNTAX);
  412. LPSTR pszStart = *ppszThisToken;
  413. if (*pszStart != '\"')
  414. {
  415. TraceMsg( TF_WARNING, "GetQuotedToken() - Failed to Find Start Quote at '%s'!", pszStart );
  416. return hres;
  417. }
  418. pszStart++;
  419. LPSTR pszEndQuote = strchrf(pszStart, '\"');
  420. if (pszEndQuote == NULL)
  421. {
  422. TraceMsg( TF_WARNING, "GetQuotedToken() - Failed to Find End Quote at '%s'!", pszStart );
  423. return hres;
  424. }
  425. *pszEndQuote = '\0';
  426. if (ppszQuotedToken != NULL)
  427. {
  428. *ppszQuotedToken = pszStart;
  429. }
  430. *ppszThisToken = pszEndQuote+1;
  431. return NOERROR;
  432. }
  433. BOOL IsEqualToken(LPCSTR pszTokenStart, LPCSTR pszTokenEnd, LPCSTR pszTokenToMatch)
  434. {
  435. UINT cbToken = strlenf(pszTokenToMatch);
  436. if (cbToken != (UINT)(pszTokenEnd - pszTokenStart) || strnicmpf(pszTokenStart, pszTokenToMatch, cbToken))
  437. {
  438. return FALSE;
  439. }
  440. return TRUE;
  441. }
  442. /* ParseLiteralToken(ppsz, pszToken) tries to match *ppsz against pszToken.
  443. * If they don't match, an error is returned. If they do match, then *ppsz
  444. * is advanced past the token and any following whitespace.
  445. *
  446. * If ppszInvalid is NULL, then the function is non-destructive in the error
  447. * path, so it's OK to call ParseLiteralToken just to see if a possible literal
  448. * token is what's next; if the token isn't found, whatever was there didn't
  449. * get eaten or anything.
  450. *
  451. * If ppszInvalid is not NULL, then if the token doesn't match, *ppszInvalid
  452. * will be set to *ppsz.
  453. */
  454. HRESULT ParseLiteralToken(LPSTR *ppsz, LPCSTR pszToken, LPCSTR *ppszInvalid)
  455. {
  456. LPSTR pszTokenEnd = FindTokenEnd(*ppsz);
  457. if (!IsEqualToken(*ppsz, pszTokenEnd, pszToken))
  458. {
  459. if (ppszInvalid != NULL)
  460. {
  461. *ppszInvalid = *ppsz;
  462. }
  463. // TraceMsg( TF_WARNING, "ParseLiteralToken() - Token '%s' Not Found at '%s'!", pszToken, *ppsz );
  464. return ResultFromScode(MK_E_SYNTAX);
  465. }
  466. *ppsz = pszTokenEnd;
  467. SkipWhitespace(ppsz);
  468. return NOERROR;
  469. }
  470. /* ParseServiceError parses a service-error construct, once it's been
  471. * determined that such is the case. m_pszCurrent has been advanced past
  472. * the 'error' keyword that indicates a service-error.
  473. *
  474. * We're pretty flexible about the contents of this stuff. We basically
  475. * accept anything of the form:
  476. *
  477. * 'error' '(' <error string> [quoted explanations] ')' - or -
  478. * 'error' <error string>
  479. *
  480. * without caring too much about what the error string actually is.
  481. *
  482. * A format with quoted explanations but without the parens would not be
  483. * legal, we wouldn't be able to distinguish the explanations from the
  484. * serviceID of the next service-info.
  485. */
  486. HRESULT CParsedServiceInfo::ParseServiceError()
  487. {
  488. BOOL fParen = FALSE;
  489. HRESULT hres = NOERROR;
  490. if (SUCCEEDED(ParseLiteralToken(&m_pszCurrent, szLeftParen, NULL)))
  491. {
  492. fParen = TRUE;
  493. }
  494. LPSTR pszErrorEnd = FindTokenEnd(m_pszCurrent); /* find end of error string */
  495. m_pszErrorString = m_pszCurrent; /* remember start of error string */
  496. if (fParen)
  497. { /* need to eat explanations */
  498. m_pszCurrent = pszErrorEnd; /* skip error string to get to explanations */
  499. SkipWhitespace();
  500. while (SUCCEEDED(hres))
  501. {
  502. hres = GetQuotedToken(&m_pszCurrent, NULL);
  503. SkipWhitespace();
  504. }
  505. }
  506. if (fParen)
  507. {
  508. hres = ParseLiteralToken(&m_pszCurrent, szRightParen, &m_pszInvalidString);
  509. }
  510. else
  511. {
  512. hres = NOERROR;
  513. }
  514. if (SUCCEEDED(hres))
  515. {
  516. *pszErrorEnd = '\0'; /* null-terminate the error string */
  517. }
  518. return hres;
  519. }
  520. /* ParseNumber parses a numeric token at the specified position. If the
  521. * number makes sense, the pointer is advanced to the end of the number
  522. * and past any following whitespace, and the numeric value is returned
  523. * in *pnOut. Any non-numeric characters are considered to terminate the
  524. * number without error; it is assumed that higher-level parsing code
  525. * will eventually reject such characters if they're not supposed to be
  526. * there.
  527. *
  528. * pnOut may be NULL if the caller doesn't care about the number being
  529. * returned and just wants to eat it.
  530. *
  531. * Floating point numbers of the form nnn.nnn are rounded to the next
  532. * higher integer and returned as such.
  533. */
  534. //t-markh 8/98 - added fPICSRules for line counting support in PICSRules
  535. HRESULT ParseNumber(LPSTR *ppszNumber, INT *pnOut,BOOL fPICSRules)
  536. {
  537. HRESULT hres = ResultFromScode(MK_E_SYNTAX);
  538. BOOL fNegative = FALSE;
  539. INT nAccum = 0;
  540. BOOL fNonZeroDecimal = FALSE;
  541. BOOL fInDecimal = FALSE;
  542. BOOL fFoundDigits = FALSE;
  543. LPSTR pszCurrent = *ppszNumber;
  544. /* Handle one sign character. */
  545. if (*pszCurrent == '+')
  546. {
  547. pszCurrent++;
  548. }
  549. else if (*pszCurrent == '-')
  550. {
  551. pszCurrent++;
  552. fNegative = TRUE;
  553. }
  554. for (;;)
  555. {
  556. if (*pszCurrent == '.')
  557. {
  558. fInDecimal = TRUE;
  559. }
  560. else if (*pszCurrent >= '0' && *pszCurrent <= '9')
  561. {
  562. fFoundDigits = TRUE;
  563. if (fInDecimal)
  564. {
  565. if (*pszCurrent > '0')
  566. {
  567. fNonZeroDecimal = TRUE;
  568. }
  569. }
  570. else
  571. {
  572. nAccum = nAccum * 10 + (*pszCurrent - '0');
  573. }
  574. }
  575. else
  576. {
  577. break;
  578. }
  579. pszCurrent++;
  580. }
  581. if (fFoundDigits)
  582. {
  583. hres = NOERROR;
  584. if (fNonZeroDecimal)
  585. {
  586. nAccum++; /* round away from zero if decimal present */
  587. }
  588. if (fNegative)
  589. {
  590. nAccum = -nAccum;
  591. }
  592. }
  593. if (SUCCEEDED(hres))
  594. {
  595. if (pnOut != NULL)
  596. {
  597. *pnOut = nAccum;
  598. }
  599. *ppszNumber = pszCurrent;
  600. if ( fPICSRules == FALSE )
  601. {
  602. SkipWhitespace(ppszNumber);
  603. }
  604. }
  605. else
  606. {
  607. TraceMsg( TF_WARNING, "ParseNumber() - Failed with hres=0x%x at '%s'!", hres, pszCurrent );
  608. }
  609. return hres;
  610. }
  611. /* ParseExtensionData just needs to get past whatever data was supplied
  612. * for an extension. The PICS spec implies that it can be recursive, which
  613. * complicates matters a bit:
  614. *
  615. * data :: quoted-ISO-date | quotedURL | number | quotedname | '(' data* ')'
  616. *
  617. * Use of recursion here is probably OK, we don't really expect complicated
  618. * nested extensions all that often, and this function doesn't use a lot of
  619. * stack or other resources...
  620. */
  621. HRESULT CParsedServiceInfo::ParseExtensionData(COptionsBase *pOpt)
  622. {
  623. HRESULT hres;
  624. if (SUCCEEDED(ParseLiteralToken(&m_pszCurrent, szLeftParen, NULL)))
  625. {
  626. hres = ParseExtensionData(pOpt);
  627. if (FAILED(hres))
  628. {
  629. return hres;
  630. }
  631. return ParseLiteralToken(&m_pszCurrent, szRightParen, &m_pszInvalidString);
  632. }
  633. if (SUCCEEDED(GetQuotedToken(&m_pszCurrent, NULL)))
  634. {
  635. SkipWhitespace();
  636. return NOERROR;
  637. }
  638. hres = ParseNumber(&m_pszCurrent, NULL);
  639. if (FAILED(hres))
  640. {
  641. m_pszInvalidString = m_pszCurrent;
  642. }
  643. return hres;
  644. }
  645. /* ParseExtension parses an extension option. Syntax is:
  646. *
  647. * extension ( mandatory|optional "identifyingURL" data )
  648. *
  649. * Currently all extensions are parsed but ignored, although a mandatory
  650. * extension causes the entire options structure and anything dependent
  651. * on it to be invalidated.
  652. */
  653. HRESULT CParsedServiceInfo::ParseExtension(COptionsBase *pOpt)
  654. {
  655. HRESULT hres;
  656. hres = ParseLiteralToken(&m_pszCurrent, szLeftParen, &m_pszInvalidString);
  657. if (FAILED(hres))
  658. {
  659. TraceMsg( TF_WARNING, "CParsedServiceInfo::ParseExtension() - Missing '(' at '%s'!", m_pszInvalidString );
  660. return hres;
  661. }
  662. hres = ParseLiteralToken(&m_pszCurrent, szOptional, &m_pszInvalidString);
  663. if (FAILED(hres))
  664. {
  665. hres = ParseLiteralToken(&m_pszCurrent, szMandatory, &m_pszInvalidString);
  666. if (SUCCEEDED(hres))
  667. {
  668. pOpt->m_fdwFlags |= LBLOPT_INVALID;
  669. }
  670. }
  671. if (FAILED(hres))
  672. {
  673. TraceMsg( TF_WARNING, "CParsedServiceInfo::ParseExtension() - Failed ParseLiteralToken() with hres=0x%x at '%s'!", hres, m_pszInvalidString );
  674. return hres; /* this causes us to lose our place -- OK? */
  675. }
  676. hres = GetQuotedToken(&m_pszCurrent, NULL);
  677. if (FAILED(hres))
  678. {
  679. m_pszInvalidString = m_pszCurrent;
  680. TraceMsg( TF_WARNING, "CParsedServiceInfo::ParseExtension() - Missing Quote at '%s'!", m_pszInvalidString );
  681. return hres;
  682. }
  683. SkipWhitespace();
  684. while (*m_pszCurrent != ')' && *m_pszCurrent != '\0')
  685. {
  686. hres = ParseExtensionData(pOpt);
  687. if (FAILED(hres))
  688. {
  689. TraceMsg( TF_WARNING, "CParsedServiceInfo::ParseExtension() - Failed ParseExtensionData() with hres=0x%x!", hres );
  690. return hres;
  691. }
  692. }
  693. if (*m_pszCurrent != ')')
  694. {
  695. m_pszInvalidString = m_pszCurrent;
  696. TraceMsg( TF_WARNING, "CParsedServiceInfo::ParseExtension() - Missing ')' at '%s'!", m_pszInvalidString );
  697. return ResultFromScode(MK_E_SYNTAX);
  698. }
  699. m_pszCurrent++;
  700. SkipWhitespace();
  701. return NOERROR;
  702. }
  703. /* ParseTime parses a "quoted-ISO-date" as found in a label. This is required
  704. * to have the following form, as quoted from the PICS spec:
  705. *
  706. * quoted-ISO-date :: YYYY'.'MM'.'DD'T'hh':'mmStz
  707. * YYYY :: four-digit year
  708. * MM :: two-digit month (01=January, etc.)
  709. * DD :: two-digit day of month (01-31)
  710. * hh :: two digits of hour (00-23)
  711. * mm :: two digits of minute (00-59)
  712. * S :: sign of time zone offset from UTC (+ or -)
  713. * tz :: four digit amount of offset from UTC (e.g., 1512 means 15 hours 12 minutes)
  714. *
  715. * Example: "1994.11.05T08:15-0500" means Nov. 5, 1994, 8:15am, US EST.
  716. *
  717. * Time is parsed into NET format -- seconds since 1970 (easiest to adjust for
  718. * time zones, and compare with). Returns an error if string is invalid.
  719. */
  720. /* Template describing the string format. 'n' means a digit, '+' means a
  721. * plus or minus sign, any other character must match that literal character.
  722. */
  723. const char szTimeTemplate[] = "nnnn.nn.nnTnn:nn+nnnn";
  724. const char szPICSRulesTimeTemplate[] = "nnnn-nn-nnTnn:nn+nnnn";
  725. HRESULT ParseTime(LPSTR pszTime, DWORD *pOut, BOOL fPICSRules)
  726. {
  727. /* Copy the time string into a temporary buffer, since we're going to
  728. * stomp on some separators. We preserve the original in case it turns
  729. * out to be invalid and we have to show it to the user later.
  730. */
  731. LPCSTR pszCurrTemplate;
  732. char szTemp[sizeof(szTimeTemplate)];
  733. if (::strlenf(pszTime) >= sizeof(szTemp))
  734. {
  735. TraceMsg( TF_WARNING, "ParseTime() - Time String Too Long (pszTime='%s', %d chars expected)!", pszTime, sizeof(szTemp) );
  736. return ResultFromScode(MK_E_SYNTAX);
  737. }
  738. strcpyf(szTemp, pszTime);
  739. LPSTR pszCurrent = szTemp;
  740. if(fPICSRules)
  741. {
  742. pszCurrTemplate = szPICSRulesTimeTemplate;
  743. }
  744. else
  745. {
  746. pszCurrTemplate = szTimeTemplate;
  747. }
  748. /* First validate the format against the template. If that succeeds, then
  749. * we get to make all sorts of assumptions later.
  750. *
  751. * We stomp all separators except the +/- for the timezone with spaces
  752. * so that ParseNumber will (a) skip them for us, and (b) not interpret
  753. * the '.' separators as decimal points.
  754. */
  755. BOOL fOK = TRUE;
  756. while (*pszCurrent && *pszCurrTemplate && fOK)
  757. {
  758. char chCurrent = *pszCurrent;
  759. switch (*pszCurrTemplate)
  760. {
  761. case 'n':
  762. if (chCurrent < '0' || chCurrent > '9')
  763. {
  764. fOK = FALSE;
  765. }
  766. break;
  767. case '+':
  768. if (chCurrent != '+' && chCurrent != '-')
  769. {
  770. fOK = FALSE;
  771. }
  772. break;
  773. default:
  774. if (chCurrent != *pszCurrTemplate)
  775. {
  776. fOK = FALSE;
  777. }
  778. else
  779. {
  780. *pszCurrent = ' ';
  781. }
  782. break;
  783. }
  784. pszCurrent++;
  785. pszCurrTemplate++;
  786. }
  787. /* If invalid character, or didn't reach the ends of both strings
  788. * simultaneously, fail.
  789. */
  790. if (!fOK || *pszCurrent || *pszCurrTemplate)
  791. {
  792. TraceMsg( TF_WARNING, "ParseTime() - Invalid Character or Strings Mismatch (fOK=%d, pszCurrent='%s', pszCurrTemplate='%s')!", fOK, pszCurrent, pszCurrTemplate );
  793. return ResultFromScode(MK_E_SYNTAX);
  794. }
  795. HRESULT hres;
  796. int n;
  797. SYSTEMTIME st;
  798. /* We parse into SYSTEMTIME structure because it has separate fields for
  799. * the different components. We then convert to net time (seconds since
  800. * Jan 1 1970) to easily add the timezone bias and compare with other
  801. * times.
  802. *
  803. * The sense of the bias sign is inverted because it indicates the direction
  804. * of the bias FROM UTC. We want to use it to convert the specified time
  805. * back TO UTC.
  806. */
  807. int nBiasSign = -1;
  808. int nBiasNumber;
  809. pszCurrent = szTemp;
  810. hres = ParseNumber(&pszCurrent, &n);
  811. if (SUCCEEDED(hres) && n >= 1980)
  812. {
  813. st.wYear = (WORD)n;
  814. hres = ParseNumber(&pszCurrent, &n);
  815. if (SUCCEEDED(hres) && n <= 12)
  816. {
  817. st.wMonth = (WORD)n;
  818. hres = ParseNumber(&pszCurrent, &n);
  819. if (SUCCEEDED(hres) && n < 32)
  820. {
  821. st.wDay = (WORD)n;
  822. hres = ParseNumber(&pszCurrent, &n);
  823. if (SUCCEEDED(hres) && n <= 23)
  824. {
  825. st.wHour = (WORD)n;
  826. hres = ParseNumber(&pszCurrent, &n);
  827. if (SUCCEEDED(hres) && n <= 59)
  828. {
  829. st.wMinute = (WORD)n;
  830. if (*(pszCurrent++) == '-')
  831. {
  832. nBiasSign = 1;
  833. }
  834. hres = ParseNumber(&pszCurrent, &nBiasNumber);
  835. }
  836. }
  837. }
  838. }
  839. }
  840. /* Seconds are used by the time converter, but are not specified in
  841. * the label.
  842. */
  843. st.wSecond = 0;
  844. /* Other fields (wDayOfWeek, wMilliseconds) are ignored when converting
  845. * to net time.
  846. */
  847. if (FAILED(hres))
  848. {
  849. TraceMsg( TF_WARNING, "ParseTime() - Failed to Parse Time where hres=0x%x!", hres );
  850. return hres;
  851. }
  852. DWORD dwTime = SystemToNetDate(&st);
  853. /* The bias number is 4 digits, but hours and minutes. Convert to
  854. * a number of seconds.
  855. */
  856. nBiasNumber = (((nBiasNumber / 100) * 60) + (nBiasNumber % 100)) * 60;
  857. /* Adjust the time by the timezone bias, and return to the caller. */
  858. *pOut = dwTime + (nBiasNumber * nBiasSign);
  859. return hres;
  860. }
  861. /* ParseOptions parses through any label options that may be present at
  862. * m_pszCurrent. pszTokenEnd initially points to the end of the token at
  863. * m_pszCurrent, a small perf win since the caller has already calculated
  864. * it. If ParseOptions is filling in the static options structure embedded
  865. * in the serviceinfo, pOpt points to it and ppOptOut will be NULL. If pOpt
  866. * is NULL, then ParseOptions will construct a new CDynamicOptions object
  867. * and return it in *ppOptOut, iff any new options are found at the current
  868. * token. pszOptionEndToken indicates the token which ends the list of
  869. * options -- either "labels" or "ratings". A token consisting of just the
  870. * first character of pszOptionEndToken will also terminate the list.
  871. *
  872. * ParseOptions fails iff it finds an option it doesn't recognize, or a
  873. * syntax error in an option it does recognize. It succeeds if all options
  874. * are syntactically correct or if there are no options to parse.
  875. *
  876. * The token which terminates the list of options is also consumed.
  877. *
  878. * FEATURE - how should we flag mandatory extensions, 'until' options that
  879. * give an expired date, etc.? set a flag in the CParsedServiceInfo and
  880. * keep parsing?
  881. */
  882. enum OptionID {
  883. OID_AT,
  884. OID_BY,
  885. OID_COMMENT,
  886. OID_FULL,
  887. OID_EXTENSION,
  888. OID_GENERIC,
  889. OID_FOR,
  890. OID_MIC,
  891. OID_ON,
  892. OID_SIG,
  893. OID_UNTIL
  894. };
  895. enum OptionContents {
  896. OC_QUOTED,
  897. OC_BOOL,
  898. OC_SPECIAL
  899. };
  900. const struct {
  901. LPCSTR pszToken;
  902. OptionID oid;
  903. OptionContents oc;
  904. } aKnownOptions[] = {
  905. { szAtOption, OID_AT, OC_QUOTED },
  906. { szByOption, OID_BY, OC_QUOTED },
  907. { szCommentOption, OID_COMMENT, OC_QUOTED },
  908. { szCompleteLabelOption, OID_FULL, OC_QUOTED },
  909. { szFullOption, OID_FULL, OC_QUOTED },
  910. { szExtensionOption, OID_EXTENSION, OC_SPECIAL },
  911. { szGenericOption, OID_GENERIC, OC_BOOL },
  912. { szShortGenericOption, OID_GENERIC, OC_BOOL },
  913. { szForOption, OID_FOR, OC_QUOTED },
  914. { szMICOption, OID_MIC, OC_QUOTED },
  915. { szMD5Option, OID_MIC, OC_QUOTED },
  916. { szOnOption, OID_ON, OC_QUOTED },
  917. { szSigOption, OID_SIG, OC_QUOTED },
  918. { szUntilOption, OID_UNTIL, OC_QUOTED },
  919. { szExpOption, OID_UNTIL, OC_QUOTED }
  920. };
  921. const UINT cKnownOptions = sizeof(aKnownOptions) / sizeof(aKnownOptions[0]);
  922. HRESULT CParsedServiceInfo::ParseOptions(LPSTR pszTokenEnd, COptionsBase *pOpt,
  923. CDynamicOptions **ppOptOut, LPCSTR pszOptionEndToken)
  924. {
  925. HRESULT hres = NOERROR;
  926. char szShortOptionEndToken[2];
  927. szShortOptionEndToken[0] = *pszOptionEndToken;
  928. szShortOptionEndToken[1] = '\0';
  929. if (pszTokenEnd == NULL)
  930. {
  931. pszTokenEnd = FindTokenEnd(m_pszCurrent);
  932. }
  933. do
  934. {
  935. /* Have we hit the token that signals the end of the options? */
  936. if (IsEqualToken(m_pszCurrent, pszTokenEnd, pszOptionEndToken) ||
  937. IsEqualToken(m_pszCurrent, pszTokenEnd, szShortOptionEndToken))
  938. {
  939. m_pszCurrent = pszTokenEnd;
  940. SkipWhitespace();
  941. return NOERROR;
  942. }
  943. for (UINT i=0; i<cKnownOptions; i++)
  944. {
  945. if (IsEqualToken(m_pszCurrent, pszTokenEnd, aKnownOptions[i].pszToken))
  946. {
  947. break;
  948. }
  949. }
  950. if (i == cKnownOptions)
  951. {
  952. m_pszInvalidString = m_pszCurrent;
  953. TraceMsg( TF_WARNING, "CParsedServiceInfo::ParseOptions() - Unknown Token Encountered at '%s'!", m_pszInvalidString );
  954. return ResultFromScode(MK_E_SYNTAX); /* unrecognized option */
  955. }
  956. m_pszCurrent = pszTokenEnd;
  957. SkipWhitespace();
  958. /* Now parse the stuff that comes after the option token. */
  959. LPSTR pszQuotedString = NULL;
  960. BOOL fBoolOpt = FALSE;
  961. switch (aKnownOptions[i].oc)
  962. {
  963. case OC_QUOTED:
  964. hres = GetQuotedToken(&m_pszCurrent, &pszQuotedString);
  965. break;
  966. case OC_BOOL:
  967. hres = GetBool(&m_pszCurrent, &fBoolOpt);
  968. break;
  969. case OC_SPECIAL:
  970. break; /* we'll handle this specially */
  971. }
  972. if (FAILED(hres))
  973. { /* incorrect stuff after the option token */
  974. m_pszInvalidString = m_pszCurrent;
  975. TraceMsg( TF_WARNING, "CParsedServiceInfo::ParseOptions() - Failed Option Contents Parse at '%s'!", m_pszInvalidString );
  976. return hres;
  977. }
  978. if (pOpt == NULL)
  979. { /* need to allocate a new options structure */
  980. CDynamicOptions *pNew = new CDynamicOptions;
  981. if (pNew == NULL)
  982. {
  983. TraceMsg( TF_WARNING, "CParsedServiceInfo::ParseOptions() - Failed to Create CDynamicOptions Object!" );
  984. return ResultFromScode(E_OUTOFMEMORY);
  985. }
  986. pOpt = pNew;
  987. *ppOptOut = pNew; /* return new structure to caller */
  988. }
  989. /* Now actually do useful stuff based on which option it is. */
  990. switch (aKnownOptions[i].oid)
  991. {
  992. case OID_UNTIL:
  993. hres = ParseTime(pszQuotedString, &pOpt->m_timeUntil);
  994. if (FAILED(hres))
  995. {
  996. m_pszInvalidString = pszQuotedString;
  997. }
  998. break;
  999. case OID_FOR:
  1000. pOpt->m_pszURL = pszQuotedString;
  1001. break;
  1002. case OID_GENERIC:
  1003. if (fBoolOpt)
  1004. {
  1005. pOpt->m_fdwFlags |= LBLOPT_GENERIC;
  1006. }
  1007. else
  1008. {
  1009. pOpt->m_fdwFlags &= ~LBLOPT_GENERIC;
  1010. }
  1011. break;
  1012. case OID_EXTENSION:
  1013. hres = ParseExtension(pOpt);
  1014. break;
  1015. }
  1016. if ( FAILED(hres) )
  1017. {
  1018. TraceMsg( TF_WARNING, "CParsedServiceInfo::ParseOptions() - Failed Option ID Parse at '%s'!", m_pszCurrent );
  1019. }
  1020. SkipWhitespace();
  1021. pszTokenEnd = FindTokenEnd(m_pszCurrent);
  1022. } while (SUCCEEDED(hres));
  1023. return hres;
  1024. }
  1025. /* CParsedServiceInfo::ParseRating parses a single rating -- a transmit-name
  1026. * followed by either a number or a parenthesized list of multi-values. The
  1027. * corresponding rating is stored in the current list of ratings.
  1028. */
  1029. HRESULT CParsedServiceInfo::ParseRating()
  1030. {
  1031. LPSTR pszTokenEnd = FindTokenEnd(m_pszCurrent);
  1032. if (*m_pszCurrent == '\0')
  1033. {
  1034. TraceMsg( TF_WARNING, "CParsedServiceInfo::ParseRating() - Empty String after FindTokenEnd()!" );
  1035. return ResultFromScode(MK_E_SYNTAX);
  1036. }
  1037. *(pszTokenEnd++) = '\0';
  1038. CParsedRating r;
  1039. r.pszTransmitName = m_pszCurrent;
  1040. m_pszCurrent = pszTokenEnd;
  1041. SkipWhitespace();
  1042. HRESULT hres = ParseNumber(&m_pszCurrent, &r.nValue);
  1043. if (FAILED(hres))
  1044. {
  1045. m_pszInvalidString = m_pszCurrent;
  1046. return hres;
  1047. }
  1048. r.pOptions = m_poptCurrent;
  1049. r.fFound = FALSE;
  1050. r.fFailed = FALSE;
  1051. return (aRatings.Append(r) ? NOERROR : ResultFromScode(E_OUTOFMEMORY));
  1052. }
  1053. /* CParsedServiceInfo::ParseSingleLabel starts parsing where a single-label
  1054. * should occur. A single-label may contain options (in which case a new
  1055. * options structure will be allocated), following by the keyword 'ratings'
  1056. * (or 'r') and a parenthesized list of ratings.
  1057. */
  1058. HRESULT CParsedServiceInfo::ParseSingleLabel()
  1059. {
  1060. HRESULT hres;
  1061. CDynamicOptions *pOpt = NULL;
  1062. hres = ParseOptions(NULL, NULL, &pOpt, szRatings);
  1063. if (FAILED(hres))
  1064. {
  1065. if (pOpt != NULL)
  1066. {
  1067. pOpt->Release();
  1068. }
  1069. return hres;
  1070. }
  1071. if (pOpt != NULL)
  1072. {
  1073. pOpt->m_pNext = m_poptList;
  1074. m_poptList = pOpt;
  1075. m_poptCurrent = pOpt;
  1076. }
  1077. hres = ParseLiteralToken(&m_pszCurrent, szLeftParen, &m_pszInvalidString);
  1078. if (FAILED(hres))
  1079. {
  1080. TraceMsg( TF_WARNING, "CParsedServiceInfo::ParseSingleLabel() - ParseLiteralToken() Failed with hres=0x%x!", hres );
  1081. return hres;
  1082. }
  1083. do
  1084. {
  1085. hres = ParseRating();
  1086. } while (SUCCEEDED(hres) && *m_pszCurrent != ')' && *m_pszCurrent != '\0');
  1087. if (FAILED(hres))
  1088. {
  1089. TraceMsg( TF_WARNING, "CParsedServiceInfo::ParseSingleLabel() - ParseRating() Failed with hres=0x%x!", hres );
  1090. return hres;
  1091. }
  1092. return ParseLiteralToken(&m_pszCurrent, szRightParen, &m_pszInvalidString);
  1093. }
  1094. /* CParsedServiceInfo::ParseLabels starts parsing just past the keyword
  1095. * 'labels' (or 'l'). It needs to handle a label-error, a single-label,
  1096. * or a parenthesized list of single-labels.
  1097. */
  1098. HRESULT CParsedServiceInfo::ParseLabels()
  1099. {
  1100. HRESULT hres;
  1101. /* First deal with a label-error. It begins with the keyword 'error'. */
  1102. if (SUCCEEDED(ParseLiteralToken(&m_pszCurrent, szError, NULL)))
  1103. {
  1104. hres = ParseLiteralToken(&m_pszCurrent, szLeftParen, &m_pszInvalidString);
  1105. if (FAILED(hres))
  1106. {
  1107. TraceMsg( TF_WARNING, "CParsedServiceInfo::ParseLabels() - ParseLiteralToken() Failed with hres=0x%x!", hres );
  1108. return hres;
  1109. }
  1110. LPSTR pszTokenEnd = FindTokenEnd(m_pszCurrent);
  1111. m_pszErrorString = m_pszCurrent;
  1112. m_pszCurrent = pszTokenEnd;
  1113. SkipWhitespace();
  1114. while (*m_pszCurrent != ')')
  1115. {
  1116. hres = GetQuotedToken(&m_pszCurrent, NULL);
  1117. if (FAILED(hres))
  1118. {
  1119. m_pszInvalidString = m_pszCurrent;
  1120. TraceMsg( TF_WARNING, "CParsedServiceInfo::ParseLabels() - GetQuotedToken() Failed with hres=0x%x!", hres );
  1121. return hres;
  1122. }
  1123. }
  1124. return NOERROR;
  1125. }
  1126. BOOL fParenthesized = FALSE;
  1127. /* If we see a left paren, it's a parenthesized list of single-labels,
  1128. * which basically means we'll have to eat an extra parenthesis later.
  1129. */
  1130. if (SUCCEEDED(ParseLiteralToken(&m_pszCurrent, szLeftParen, NULL)))
  1131. {
  1132. fParenthesized = TRUE;
  1133. }
  1134. for (;;)
  1135. {
  1136. /* Things which signify the end of the label list:
  1137. * - the close parenthesis checked for above
  1138. * - a quoted string, indicating the next service-info
  1139. * - the end of the string
  1140. * - a service-info saying "error (no-ratings <explanation>)"
  1141. *
  1142. * Check the easy ones first.
  1143. */
  1144. if (*m_pszCurrent == ')' || *m_pszCurrent == '\"' || *m_pszCurrent == '\0')
  1145. {
  1146. break;
  1147. }
  1148. /* Now look for that tricky error-state service-info. */
  1149. LPSTR pszTemp = m_pszCurrent;
  1150. if (SUCCEEDED(ParseLiteralToken(&pszTemp, szError, NULL)) &&
  1151. SUCCEEDED(ParseLiteralToken(&pszTemp, szLeftParen, NULL)) &&
  1152. SUCCEEDED(ParseLiteralToken(&pszTemp, szNoRatings, NULL)))
  1153. {
  1154. break;
  1155. }
  1156. hres = ParseSingleLabel();
  1157. if (FAILED(hres))
  1158. {
  1159. TraceMsg( TF_WARNING, "CParsedServiceInfo::ParseLabels() - ParseSingleLabel() Failed with hres=0x%x!", hres );
  1160. return hres;
  1161. }
  1162. }
  1163. if (fParenthesized)
  1164. {
  1165. return ParseLiteralToken(&m_pszCurrent, szRightParen, &m_pszInvalidString);
  1166. }
  1167. return NOERROR;
  1168. }
  1169. /* Parse is passed a pointer to a pointer to something which should
  1170. * be a service-info string (i.e., not the close paren for the labellist, and
  1171. * not the end of the string). The caller's string pointer is advanced to the
  1172. * end of the service-info string.
  1173. */
  1174. HRESULT CParsedServiceInfo::Parse(LPSTR *ppszServiceInfo)
  1175. {
  1176. /* NOTE: Do not return out of this function without copying m_pszCurrent
  1177. * back into *ppszServiceInfo! Always store your return code in hres and
  1178. * exit out the bottom of the function.
  1179. */
  1180. HRESULT hres;
  1181. m_pszCurrent = *ppszServiceInfo;
  1182. hres = ParseLiteralToken(&m_pszCurrent, szError, NULL);
  1183. if (SUCCEEDED(hres))
  1184. {
  1185. /* Keyword is 'error'. Better be followed by '(', 'no-ratings',
  1186. * explanations, and a close-paren.
  1187. */
  1188. hres = ParseLiteralToken(&m_pszCurrent, szLeftParen, &m_pszInvalidString);
  1189. if (SUCCEEDED(hres))
  1190. {
  1191. hres = ParseLiteralToken(&m_pszCurrent, szNoRatings, &m_pszInvalidString);
  1192. }
  1193. if (SUCCEEDED(hres))
  1194. {
  1195. m_pszErrorString = szNoRatings;
  1196. while (*m_pszCurrent != ')' && *m_pszCurrent != '\0')
  1197. {
  1198. hres = GetQuotedToken(&m_pszCurrent, NULL);
  1199. if (FAILED(hres))
  1200. {
  1201. m_pszInvalidString = m_pszCurrent;
  1202. break;
  1203. }
  1204. SkipWhitespace();
  1205. }
  1206. if (*m_pszCurrent == ')')
  1207. {
  1208. m_pszCurrent++;
  1209. SkipWhitespace();
  1210. }
  1211. }
  1212. }
  1213. else
  1214. {
  1215. /* Keyword is not 'error'. Better start with a serviceID --
  1216. * a quoted URL.
  1217. */
  1218. LPSTR pszServiceID;
  1219. hres = GetQuotedToken(&m_pszCurrent, &pszServiceID);
  1220. if (SUCCEEDED(hres))
  1221. {
  1222. m_pszServiceName = pszServiceID;
  1223. SkipWhitespace();
  1224. /* Past the serviceID. Next either 'error' indicating a service-error,
  1225. * or we start options and then a labelword.
  1226. */
  1227. LPSTR pszTokenEnd = FindTokenEnd(m_pszCurrent);
  1228. if (IsEqualToken(m_pszCurrent, pszTokenEnd, szError))
  1229. {
  1230. m_pszCurrent = pszTokenEnd;
  1231. SkipWhitespace();
  1232. hres = ParseServiceError();
  1233. }
  1234. else
  1235. {
  1236. hres = ParseOptions(pszTokenEnd, &m_opt, NULL, ::szLabelWord);
  1237. if (SUCCEEDED(hres))
  1238. {
  1239. hres = ParseLabels();
  1240. }
  1241. }
  1242. }
  1243. else
  1244. {
  1245. m_pszInvalidString = m_pszCurrent;
  1246. }
  1247. }
  1248. *ppszServiceInfo = m_pszCurrent;
  1249. return hres;
  1250. }
  1251. const char szPicsVersionLabel[] = "PICS-";
  1252. const UINT cchLabel = (sizeof(szPicsVersionLabel)-1) / sizeof(szPicsVersionLabel[0]);
  1253. HRESULT CParsedLabelList::Parse(LPSTR pszCopy)
  1254. {
  1255. m_pszList = pszCopy; /* we own the label list string now */
  1256. /* Make another copy, which we won't carve up during parsing, so that the
  1257. * access-denied dialog can compare literal labels.
  1258. */
  1259. m_pszOriginalLabel = new char[::strlenf(pszCopy)+1];
  1260. if (m_pszOriginalLabel != NULL)
  1261. {
  1262. ::strcpyf(m_pszOriginalLabel, pszCopy);
  1263. }
  1264. m_pszCurrent = m_pszList;
  1265. SkipWhitespace();
  1266. HRESULT hres;
  1267. hres = ParseLiteralToken(&m_pszCurrent, szLeftParen, &m_pszInvalidString);
  1268. if (FAILED(hres))
  1269. {
  1270. TraceMsg( TF_WARNING, "CParsedLabelList::Parse() - ParseLiteralToken() Failed with hres=0x%x!", hres );
  1271. return hres;
  1272. }
  1273. if (strnicmpf(m_pszCurrent, szPicsVersionLabel, cchLabel))
  1274. {
  1275. TraceMsg( TF_WARNING, "CParsedLabelList::Parse() - Pics Version Label Comparison Failed at '%s'!", m_pszCurrent );
  1276. return ResultFromScode(MK_E_SYNTAX);
  1277. }
  1278. m_pszCurrent += cchLabel;
  1279. INT nVersion;
  1280. hres = ParseNumber(&m_pszCurrent, &nVersion);
  1281. if (FAILED(hres))
  1282. {
  1283. TraceMsg( TF_WARNING, "CParsedLabelList::Parse() - ParseNumber() Failed with hres=0x%x!", hres );
  1284. return hres;
  1285. }
  1286. CParsedServiceInfo *psi = &m_ServiceInfo;
  1287. do
  1288. {
  1289. hres = psi->Parse(&m_pszCurrent);
  1290. if (FAILED(hres))
  1291. {
  1292. TraceMsg( TF_WARNING, "CParsedLabelList::Parse() - psi->Parse() Failed with hres=0x%x!", hres );
  1293. return hres;
  1294. }
  1295. if (*m_pszCurrent != ')' && *m_pszCurrent != '\0')
  1296. {
  1297. CParsedServiceInfo *pNew = new CParsedServiceInfo;
  1298. if (pNew == NULL)
  1299. {
  1300. TraceMsg( TF_WARNING, "CParsedLabelList::Parse() - Failed to Create CParsedServiceInfo!" );
  1301. return ResultFromScode(E_OUTOFMEMORY);
  1302. }
  1303. psi->Append(pNew);
  1304. psi = pNew;
  1305. }
  1306. } while (*m_pszCurrent != ')' && *m_pszCurrent != '\0');
  1307. return NOERROR;
  1308. }
  1309. HRESULT ParseLabelList(LPCSTR pszList, CParsedLabelList **ppParsed)
  1310. {
  1311. LPSTR pszCopy = new char[strlenf(pszList)+1];
  1312. if (pszCopy == NULL)
  1313. {
  1314. TraceMsg( TF_WARNING, "ParseLabelList() - Failed to Create pszCopy!" );
  1315. return ResultFromScode(E_OUTOFMEMORY);
  1316. }
  1317. ::strcpyf(pszCopy, pszList);
  1318. *ppParsed = new CParsedLabelList;
  1319. if (*ppParsed == NULL)
  1320. {
  1321. TraceMsg( TF_WARNING, "ParseLabelList() - Failed to Create CParsedLabelList!" );
  1322. delete pszCopy;
  1323. pszCopy = NULL;
  1324. return ResultFromScode(E_OUTOFMEMORY);
  1325. }
  1326. return (*ppParsed)->Parse(pszCopy);
  1327. }
  1328. void FreeParsedLabelList(CParsedLabelList *pList)
  1329. {
  1330. delete pList;
  1331. pList = NULL;
  1332. }