1
0
mirror of https://github.com/OlafvdSpek/ctemplate.git synced 2025-09-28 19:05:49 +08:00

* Avoid "unused var" warning in opt mode (mec)

* Use ascii_is*() to avoid langtype issues (csilvers)
	* Fix 'class not properly dll-exported' warnings (csilvers)
	* mv README.windows to windows-friendly README-windows.txt (csilvers)
	* Update README.windows to emphasize $IncludeDir a bit more (csilvers)
	* Add :I=html, :I=css image URL modifiers (jshneier)
	* Document the new image URL modifier (dougy)
	* Update NEWS file to be non-empty (csilvers)
This commit is contained in:
csilvers 2010-06-18 23:14:04 +00:00
parent b3b532aa19
commit 8c338f322e
24 changed files with 648 additions and 185 deletions

35
INSTALL
View File

@ -1,3 +1,38 @@
Ctemplate-Specific Install Notes
================================
This code should work on any modern C++ system. It has been tested on
the following systems:
FreeBSD 6.0
Linux Fedora Core 3
Linux Fedora Core 4
Linux Fedora Core 5
Linux Fedora Core 6
Linux RedHat 9
Linux Ubuntu 6.06.1
Mac OS X 10.3.9 (Panther)
Mac OS X 10.4.8 (Tiger)
Solaris 10 (x86)
Windows XP, Visual Studio 2003 (VC++7.1)
Windows XP, Visual Studio 2005 (VC++8)
The only known problem is with Mac OS X 10.3.9 (Panther), which may
have problems with the export-symbols optimization this library does
to limit its exposed API. If you have problems compiling that seem to
involve nmedit, the solution is to edit the Makefile and remove all
instances of
-export-symbols-regex $(CTEMPLATE_TESTING_SYMBOLS)
Just remove that string exactly. After that, code should build.
For Solaris 10 6/06, we have tested only with gcc (not the Sun
compiler). To get this to work, and to work around a bug in Solaris,
install as follows:
% PATH=${PATH}:/usr/sfw/bin/:/usr/ccs/bin ./configure LDFLAGS="-Lsrc/solaris -lrt"
% PATH=${PATH}:/usr/sfw/bin/:/usr/ccs/bin make
See README.windows for installation instructions for Windows.
Installation Instructions
*************************

View File

@ -48,7 +48,7 @@ docdir = $(prefix)/share/doc/$(PACKAGE)-$(VERSION)
## This is for HTML and other documentation you want to install.
## Add your documentation files (in doc/) in addition to these
## top-level boilerplate files. Also add a TODO file if you have one.
dist_doc_DATA = AUTHORS COPYING ChangeLog INSTALL NEWS README README.windows \
dist_doc_DATA = AUTHORS COPYING ChangeLog INSTALL NEWS README README_windows.txt \
doc/designstyle.css doc/index.html \
doc/howto.html doc/tips.html doc/example.html doc/auto_escape.html \
doc/xss_resources.html

View File

@ -673,7 +673,7 @@ noinst_HEADERS = \
src/ctemplate/template_namelist.h.in \
src/ctemplate/per_expand_data.h.in
dist_doc_DATA = AUTHORS COPYING ChangeLog INSTALL NEWS README README.windows \
dist_doc_DATA = AUTHORS COPYING ChangeLog INSTALL NEWS README README_windows.txt \
doc/designstyle.css doc/index.html \
doc/howto.html doc/tips.html doc/example.html doc/auto_escape.html \
doc/xss_resources.html

146
NEWS
View File

@ -0,0 +1,146 @@
=== 20 April 2010 ==
I've just released ctemplate 0.97. This change consists primarily of
a significant change to the API: the addition of the `TemplateCache`
class, combined with deprecation of the `Template` class.
`TemplateCache` is a class that holds a collection of templates; this
concept always existed in ctemplate, but was not previously exposed.
Many static methods of the `Template` class, such as
`ReloadAllIfChanged()`, have become methods on `TemplateCache` instead
(the `Template` methods have been retained for backwards
compatibility.) Other methods, such as `Expand()`, have become free
functions. In fact, the entire `Template` class has been deprecated.
The deprecation of `Template` calls for changes in all clients of the
template code -- you can see in the example at the top of this page
how the code has changed from `Template* tpl =
ctemplate::Template::GetTemplate("example.tpl",
ctemplate::DO_NOT_STRIP); tpl->Expand(&output, &dict);` to
`ctemplate::ExpandTemplate("example.tpl", ctemplate::DO_NOT_STRIP,
&dict, &output);`. These changes will make the code simpler and more
thread-safe.
Old code should continue to work -- the `Template` class remains --
but new code should use the new API, and old code should transition as
convenient. One old API method is intrinsically thread-unsafe, and
should be prioritized to change: `tpl->ReloadIfChanged` should change
to `ctemplate::Template::ReloadAllIfChanged()`. Note this is a
semantic change: all templates are now reloaded, rather than just one.
However, since templates are reloaded lazily, and only if they've
changed on disk, I'm hopeful it will always be a reasonable change to
make.
To go along with these changes, the documentation has been almost
entirely revamped and made more accessible. Obscure ctemplate
features have been excised from the user's guide and moved into a
separate reference document. The new API is fully documented,
including new flexibility around reloading templates, made available
by the introduction of `TemplateCache`.
There are some more minor changes as well, such as the addition of
#include guards in the auto-generated .tpl.h files, to make it safe to
multiply-include them. I've also been continuing the portability
work: ctemplate should now work under Cygwin and MinGW. A full list
of changes is available in the
[http://google-ctemplate.googlecode.com/svn/tags/ctemplate-0.97/ChangeLog
ChangeLog].
I know I've said this before, but I don't expect major API changes
before the 1.0 release. The most significant changes I expect to see
are the potential removal of some of the 'forwarding' methods in the
(deprecated) `Template` class.
=== 12 June 2009 ==
I've just released ctemplate 0.95. This is entirely an API cleanup
release. Actually, relatively little of the API proper has changed:
`StringToTemplate` no longer takes an autoescape-context arg (instead
you specify this as part of the template-string, using the
`AUTOESCAPE` pragma). A few obsolete constructs have gone away, such
as the `TemplateFromString` class and
`TemplateDictionary::html_escape` and friends (just use the top-level
`html_escape`). See the
[http://google-ctemplate.googlecode.com/svn/tags/ctemplate-0.95/ChangeLog
ChangeLog] for a full list of these changes.
The biggest change is a renaming: the default namespace is now
`ctemplate` rather than `google`, and the include directory is
`ctemplate` rather than `google`. Other namespaces, such as
`template_modifiers`, have gone away.
All these changes will require you to modify your old code to get it
working with ctemplate 0.95. I've written a
[http://google-ctemplate.googlecode.com/svn/trunk/contrib/convert_to_95.pl
script] to help you do that. Please open an
[http://code.google.com/p/google-ctemplate/issues/list issue] if you
see a problem with the script. I've tested it, but not as widely as
I'd like. Also note the script will not be perfect for more complex
constructs, which you will have to clean up by hand.
I hope (expect) the API is now stable, and we won't see any more such
changes before ctemplate 1.0. I tried to isolate them all in this
release; except for the API changes, this release should behave
identically to ctemplate 0.94.
=== 7 May 2009 ===
I've just released ctemplate 0.94. A few new features have been
added, such as the ability to expand a template into your own custom
`ExpandEmitter` instance, and the ability to hook the annotation
system (typically used for debugging). You can now remove strings
from the template cache in addition to adding them. Also, there
continues to be a trickle of new modifiers, in this case a modifier
for URL's in a CSS context.
However, the most invasive changes were made for speed reasons. The
biggest is that (almost) all `TemplateDictionary` allocations are now
done on the arena -- this includes allocations by the STL classes
inside the dictionary. This allows us to free all the memory at once,
rather than item by item, and has yielded a 3-4% speed improvement in
our tests. I've combined this with a `small_map` class that stores
items in a vector instead of a hash-map until we get to 3 or 4 items;
this gives another speed increase in the (common) case a template has
only a few sections or includes.
I also changed the hashing code to use
[http://murmurhash.googlepages.com/ MurmurHash] everywhere, rather
than the string hash function built into the STL library. This should
be faster.
All these changes should not be outwardly visible, but they do use
more advanced features of C++ than ctemplate has to date. This may
result in some problems compiling, or conceivably when running. If
you see any, please file an
[http://code.google.com/p/google-ctemplate/issues/list issue report].
You can see a full list of changes on the
[http://google-ctemplate.googlecode.com/svn/tags/ctemplate-0.94/ChangeLog
ChangeLog].
=== 20 August 2008 ===
ctemplate 0.91 introduces the beginning of some API changes, as I look
to clean up the API in preparation for ctemplate 1.0. After 1.0, the
API will remain backwards compatible, but until that time, the API may
change. Please take a look at the
[http://google-ctemplate.googlecode.com/svn/trunk/ChangeLog ChangeLog]
to see if any of these changes affect you.
One change is the introduction of a new `PerExpandData` class, which
holds some state that was formerly in the `TemplateDictionary` class.
I'm still not sure if this class is a good idea, if it should be
separate from `TemplateDictionary` or a member, or what functionality
should move there (for instance, should `SetTemplateGlobal` move
there, since template-global variables are really, in some sense,
per-expand variables?) If you have any feedback, ideally based on
your own experience using the current API, feel free to post it at
`google-ctemplate@googlegroups.com`.
ctemplate also has several new features, including the addition of
"separator" sections, and the ability to change the markup character
(from `{{`). See the
[http://google-ctemplate.googlecode.com/svn/trunk/ChangeLog ChangeLog]
for a complete list, and the
[http://google-ctemplate.googlecode.com/svn/trunk/doc/howto.html howto
documentation] for more details on these new features.

View File

@ -1,38 +1,43 @@
This project has been ported to Windows. A working solution file
exists in this directory:
google-ctemplate.sln
You can load this solution file into either VC++ 7.1 (Visual Studio
2003) or VC++ 8.0 (Visual Studio 2005) -- in the latter case, it will
automatically convert the files to the latest format for you.
When you build the solution, it will create libctemplate.dll, the main
library for this project, plus a number of unittests, which you can
run by hand (or, more easily, under the Visual Studio debugger) to
make sure everything is working properly on your system. The binaries
will end up in a directory called "debug" or "release" in the
top-level directory (next to the .sln file).
If you wish to link to ctemplate statically instead of using the .dll,
you can; see the example project template_unittest_static. For this
to work, you'll need to add "/D CTEMPLATE_DLL_DECL=" to the compile
line of every ctemplate .cc file.
Note that these systems are set to build in Debug mode by default.
You may want to change them to Release mode.
Currently, Template::StringToTemplate returns a Template object that
you, the caller, must free. We've heard reports that Windows can have
trouble allocating memory in a .dll that is meant to be freed in the
application. Thus, we suggest you not use StringToTemplate from
Windows. Instead, you can use Template::StringToTemplateCache()
followed by Template::GetTemplate().
I have little experience with Windows programming, so there may be
better ways to set this up than I've done! If you run across any
problems, please post to the google-ctemplate Google Group, or report
them on the google-ctemplate Google Code site:
http://groups.google.com/group/google-ctemplate
http://code.google.com/p/google-ctemplate/issues/list
-- craig
This project has been ported to Windows. A working solution file
exists in this directory:
google-ctemplate.sln
You can load this solution file into either VC++ 7.1 (Visual Studio
2003) or VC++ 8.0 (Visual Studio 2005) -- in the latter case, it will
automatically convert the files to the latest format for you.
When you build the solution, it will create libctemplate.dll, the main
library for this project, plus a number of unittests, which you can
run by hand (or, more easily, under the Visual Studio debugger) to
make sure everything is working properly on your system. The binaries
will end up in a directory called "debug" or "release" in the
top-level directory (next to the .sln file).
I don't know very much about how to install DLLs on Windows, so you'll
have to figure out that part for yourself. If you choose to just
re-use the existing .sln, make sure you set the IncludeDir's
appropriately! Look at the properties for libctemplate.dll.
If you wish to link to ctemplate statically instead of using the .dll,
you can; see the example project template_unittest_static. For this
to work, you'll need to add "/D CTEMPLATE_DLL_DECL=" to the compile
line of every ctemplate .cc file.
Note that these systems are set to build in Debug mode by default.
You may want to change them to Release mode.
Currently, Template::StringToTemplate returns a Template object that
you, the caller, must free. We've heard reports that Windows can have
trouble allocating memory in a .dll that is meant to be freed in the
application. Thus, we suggest you not use StringToTemplate from
Windows. Instead, you can use Template::StringToTemplateCache()
followed by Template::GetTemplate().
I have little experience with Windows programming, so there may be
better ways to set this up than I've done! If you run across any
problems, please post to the google-ctemplate Google Group, or report
them on the google-ctemplate Google Code site:
http://groups.google.com/group/google-ctemplate
http://code.google.com/p/google-ctemplate/issues/list
-- craig

View File

@ -94,12 +94,12 @@ the appropriate escaping modifiers after your own.</p>
that use but allows you to indicate a different choice as shown
below:
<p><table border=1 cellpadding=3>
<table border="1" cellpadding="3" summary="Alternatives to modifiers">
<tr><th>TemplateContext</th><th>Primary Modifier</th>
<th>Accepted alternatives</th></tr>
<tr>
<td><code>TC_HTML</code></code></td>
<td><code>TC_HTML</code></td>
<td><code>:html_escape</code></td>
<td><ul>
<li><code>:pre_escape</code></li>
@ -108,6 +108,7 @@ the appropriate escaping modifiers after your own.</p>
<li><code>:html_escape_with_arg=url</code></li>
<li><code>:url_query_escape</code></li>
<li><code>:url_escape_with_arg=html</code></li>
<li><code>:img_src_url_escape_with_arg=html</code></li>
</ul></td>
</tr>
@ -116,6 +117,7 @@ the appropriate escaping modifiers after your own.</p>
<td><code>:cleanse_css</code></td>
<td><ul>
<li><code>:url_escape_with_arg=css</code></li>
<li><code>:img_src_url_escape_with_arg=css</code></li>
</ul></td>
</tr>
@ -125,7 +127,7 @@ the appropriate escaping modifiers after your own.</p>
<td><ul>
<li><code>:html_escape</code></li>
<li><code>:html_escape_with_arg=attribute</code></li>
</td></ul>
</ul></td>
</tr>
<tr>
@ -133,10 +135,10 @@ the appropriate escaping modifiers after your own.</p>
<td><code>:javascript_escape</code></td>
<td><ul>
<li><code>:json_escape</code></li>
</td></ul>
</ul></td>
</tr>
</table></p></li>
</table></li>
<li> To add additional escaping modifiers. The Template System will
never remove escaping directives you explicitly specify. </li>
@ -159,13 +161,13 @@ the semantics of the variable therefore we fail the template
initialization. An error is logged indicating the cause of
the failure.</p>
<table border=1 cellpadding=3>
<tr bgcolor="RoyalBlue">
<table border="1" cellpadding="3" summary="Contexts for using modifiers">
<tr bgcolor="#4169E1">
<th>Context</th><th>HTML Quoted?</th>
<th>Examples</th><th>Action Performed</th>
</tr>
<tr bgcolor="AliceBlue">
<tr bgcolor="#F0F8FF">
<td>Regular HTML Body and HTML comments</td><td>Any</td>
<td><pre>&lt;p&gt;Hello {{USER}}&lt;/p&gt;
</pre></td>
@ -173,95 +175,95 @@ the failure.</p>
<code>:html_escape</code></td>
</tr>
<tr bgcolor="AntiqueWhite">
<tr bgcolor="#FAEBD7">
<td>In URL attribute: Starts at pos 0</td><td>Yes</td>
<td><pre>&lt;img src="{{URL}}"&gt;;</pre></td>
<td><pre>&lt;a href="{{URL}}"&gt;;</pre></td>
<td>Escape <code>URL</code> using
<code>:url_escape_with_arg=html</code></td>
</tr>
<tr bgcolor="AntiqueWhite">
<tr bgcolor="#FAEBD7">
<td>In URL attribute: Other</td><td>Yes</td>
<td><pre>&lt;a href="/foo?q={{QUERY}}"&gt;</pre></td>
<td>Escape <code>QUERY</code> using
<code>:html_escape</code></td>
</tr>
<tr bgcolor="AntiqueWhite">
<tr bgcolor="#FAEBD7">
<td>In URL attribute: Starting at pos 0</td><td>No</td>
<td><pre>&lt;form action={{URL}}&gt;</pre></td>
<td><em>Fail template initialization</em></td>
</tr>
<tr bgcolor="AntiqueWhite">
<tr bgcolor="#FAEBD7">
<td>In URL attribute: Other</td><td>No</td>
<td><pre>&lt;a href=/foo?q={{QUERY}}&gt;My Link&lt;/a&gt;</pre></td>
<td>Escape <code>QUERY</code> using
<code>:url_query_escape</code></td>
</tr>
<tr bgcolor="AliceBlue">
<tr bgcolor="#F0F8FF">
<td>In STYLE attribute</td><td>Yes</td>
<td><pre>&lt;div style="color:{{COLOR}};"&gt;</pre></td>
<td>Escape <code>COLOR</code> using
<code>:cleanse_css</code></td>
</tr>
<tr bgcolor="AliceBlue">
<tr bgcolor="#F0F8FF">
<td>In STYLE attribute</td><td>No</td>
<td><pre>&lt;div style=color:{{COLOR}};&gt;</pre></td>
<td><em>Fail template initialization</em></td>
</tr>
<tr bgcolor="AntiqueWhite">
<tr bgcolor="#FAEBD7">
<td>In Javascript attribute: String literal</td><td>Yes</td>
<td><pre>&lt;a href="url" onclick="doFoo('{{ARG}}');"&gt;</pre></td>
<td>Escape <code>ARG</code> using
<code>:javascript_escape</code></td>
</tr>
<tr bgcolor="AntiqueWhite">
<tr bgcolor="#FAEBD7">
<td>In Javascript attribute: Non-string literal</td><td>Yes</td>
<td><pre>&lt;a href="url" onclick="doFoo({{ARG}});"&gt;</pre></td>
<td>Escape <code>ARG</code> using
<code>:javascript_escape_with_arg=number</code></td>
</tr>
<tr bgcolor="AntiqueWhite">
<tr bgcolor="#FAEBD7">
<td>In Javascript attribute: Any</td><td>No</td>
<td><pre>&lt;a href="url" onclick=doFoo('{{ARG}}');&gt;</pre></td>
<td><em>Fail template initialization.</em></td>
</tr>
<tr bgcolor="AliceBlue">
<tr bgcolor="#F0F8FF">
<td>In all other attributes</td><td>Yes</td>
<td><pre>&lt;b class="{{CLASS}}"&gt;</pre></td>
<td>Escape <code>CLASS</code> using
<code>:html_escape</code></td>
</tr>
<tr bgcolor="AliceBlue">
<tr bgcolor="#F0F8FF">
<td>In all other attributes</td><td>No</td>
<td><pre>&lt;table border={{BORDER}}&gt;</pre></td>
<td>Escape <code>BORDER</code> using
<code>:html_escape_with_arg=attribute</code></td>
</tr>
<tr bgcolor="AntiqueWhite">
<tr bgcolor="#FAEBD7">
<td>In Javascript code: In a string literal</td><td>Any</td>
<td><pre>&lt;script&gt;var a = '{{VALUE}}';&lt;/script&gt;</pre></td>
<td>Escape <code>VALUE</code> using
<code>:javascript_escape</code></td>
</tr>
<tr bgcolor="AntiqueWhite">
<tr bgcolor="#FAEBD7">
<td>In Javascript code: Non-string literal</td><td>Any</td>
<td><pre>&lt;script&gt;var a = {{VALUE}};&lt;/script&gt;</pre></td>
<td>Escape <code>VALUE</code> using
<code>:javascript_escape_with_arg=number</code></td>
</tr>
<tr bgcolor="AliceBlue">
<tr bgcolor="#F0F8FF">
<td>In a &lt;style&gt; tag</td><td>Any</td>
<td><pre>&lt;style&gt;font-size={{FONTSIZE}};&lt;/style&gt;</pre></td>
<td>Escape <code>FONTSIZE</code> using
@ -270,7 +272,7 @@ the failure.</p>
</table>
<p><h4>Comments:</h4>
<h4>Comments:</h4>
<ul>
<li>For values of URL-accepting attributes, we apply a different
modifier if the variable is at the beginning of the attribute
@ -357,7 +359,7 @@ restrictions governing the use of this template system.</p>
Javascript (<code>TC_JS</code>) templates are well supported.
Other template contexts have only basic support.
For these contexts, variables are escaped as follows:</p>
<table border=1 cellpadding=3>
<table border="1" cellpadding="3" summary="HTML contexts for modifiers">
<tr><th>TemplateContext</th><th>Escaping Applied</th></tr>
<tr><td>TC_JSON</td>
<td><code>:javascript_escape</code></td></tr>
@ -406,7 +408,6 @@ it does not match the template type given, we log a warning.</p>
<p>
<hr>
<address>
Jad Boutros<br>

View File

@ -99,7 +99,7 @@ call "expanding"), here's the output we would get:</p>
It's boring&lt;/body&gt;&lt;/html&gt;
</pre>
<p><code>{{TITLE}}</code> and <code>{{{BODY}}</code> are <b>template
<p><code>{{TITLE}}</code> and <code>{{BODY}}</code> are <b>template
elements</b>, also called <b>markers</b>. In the dictionary,
<code>TITLE</code>, <code>BODY</code>, and <code>DATE</code> are
<b>dictionary names</b>, and the values associated with each one, such
@ -728,7 +728,7 @@ as if it were a template file, and inserts it into the global cache
with the key and strip-mode that you provide. You can then use this
key and strip-mode as the first two arguments to
<code>ExpandTemplate</code>. You can also use the key as the argument
to <code>ctemplate::TemplateDictionary::SetFilename()<code>.</p>
to <code>ctemplate::TemplateDictionary::SetFilename()</code>.</p>
<p>Prefer file-based to string-based templates where possible.
Updating a file-based template requires merely a data push, rather

View File

@ -68,8 +68,7 @@ templates.</p>
option is set, which puts template code in namespace
<code>ctemplate</code>.)</p>
<h2> <A NAME="template">Expanding Templates </h2>
<h2> <A NAME="template">Expanding Templates</A> </h2>
<h3> The Template Language </h3>
@ -526,8 +525,8 @@ code:
ctemplate::TemplateDictionary::SetGlobalValue("NAME", "John Doe");
ctemplate::TemplateDictionary dict_c("set_value_demo, part 2");
ctemplate::ExpandTemplate("A.tpl", ..., &dict);
ctemplate::ExpandTemplate("C.tpl", ..., &dict_c);
ctemplate::ExpandTemplate("A.tpl", ..., &amp;dict);
ctemplate::ExpandTemplate("C.tpl", ..., &amp;dict_c);
</pre>
<p>The first expand yields this:</p>
@ -996,19 +995,69 @@ sign:</p>
<p>Here are the modifiers that are built in to the template system.
They are all defined in template_modifiers.cc:</p>
<table border=1 cellpadding=3>
<table border="1" cellpadding="3" summary="List of built-in modifiers">
<tr><th>long name</th><th>short name</th><th>description</th></tr>
<tr><td><code>:cleanse_css</code></td><td><code>:c</code></td>
<td>Removes characters not safe for a CSS value. Safe characters
are alphanumeric, space, underscore, period, coma, exclamation
mark, pound, percent, and dash.</td>
</tr>
<tr><td><code>:html_escape</code></td><td><code>:h</code></td>
<td>html-escapes the variable before output
(eg <code>&amp;</code> -> <code>&amp;amp;</code>)</td>
</tr>
<tr><td><code>:html_escape_with_arg</code></td><td><code>:H</code></td>
<td>special purpose html escaping. See
<a href="#html_escape_args">:H Arguments</a>
below for details.</td>
</tr>
<tr><td><code>:img_src_url_escape_with_arg</code></td><td><code>:I</code></td>
<td>special purpose image url escaping. See
<a href="#url_escape_args">:I and :U Arguments</a>
below for details.</td>
</tr>
<tr><td><code>:javascript_escape</code></td><td><code>:j</code></td>
<td>javascript-escapes the variable before output (eg
<code>&quot;</code> -> <code>\x27</code> and
<code>&amp;</code> -> <code>\x26</code>)</td>
</tr>
<tr><td><code>:javascript_escape_with_arg</code></td><td><code>:J</code>
</td>
<td>special purpose javascript escaping. See
<a href="#javascript_escape_args">:J Arguments</a>
below for details.</td>
</tr>
<tr><td><code>:json_escape</code></td><td><code>:o</code></td>
<td>json-escapes a variable before output as a string in json;
HTML characters are escaped using Unicode escape sequences
(e.g <code>&amp;</code> -> <code>\u0026</code>) to comply with
<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>.
</td>
</tr>
<tr><td><code>:none</code></td><td></td>
<td>leaves the variable as is (used to disable <A
HREF="#auto_escape">auto-escaping</A>)</td>
</tr>
<tr><td><code>:pre_escape</code></td><td><code>:p</code></td>
<td>pre-escapes the variable before output (same as html_escape but
whitespace is preserved; useful for &lt;pre&gt;...&lt;/pre&gt;)</td>
</tr>
<tr><td><code>:url_escape_with_arg</code></td><td><code>:U</code></td>
<td>special purpose url escaping. See
<a href="#url_escape_args">:I and :U Arguments</a>
below for details.</td>
</tr>
<tr><td><code>:url_query_escape</code></td><td><code>:u</code></td>
<td>performs URL escaping on the variable before output.
space is turned into +, and everything other than [0-9a-zA-Z.,_:*/~!()-], is
@ -1018,39 +1067,6 @@ They are all defined in template_modifiers.cc:</p>
</td>
</tr>
<tr><td><code>:javascript_escape</code></td><td><code>:j</code></td>
<td>javascript-escapes the variable before output (eg
<code>&quot;</code> -> <code>\x27</code> and
<code>&amp;</code> -> <code>\x26</code>)</td>
</tr>
<tr><td><code>:javascript_escape_with_arg</code></td><td><code>:J</code>
</td>
<td>special purpose javascript escaping. See below for details.</td>
</tr>
<tr><td><code>:cleanse_css</code></td><td><code>:c</code></td>
<td>Removes characters not safe for a CSS value. Safe characters
are alphanumeric, space, underscore, period, coma, exclamation
mark, pound, percent, and dash.</td>
</tr>
<tr><td><code>:json_escape</code></td><td><code>:o</code></td>
<td>json-escapes a variable before output as a string in json;
HTML characters are escaped using Unicode escape sequences
(e.g <code>&amp;</code> -> <code>\u0026</code>) to comply with
<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>.
</td>
</tr>
<tr><td><code>:html_escape_with_arg</code></td><td><code>:H</code></td>
<td>special purpose html escaping. See below for details.</td>
</tr>
<tr><td><code>:url_escape_with_arg</code></td><td><code>:U</code></td>
<td>special purpose url escaping. See below for details.</td>
</tr>
<tr><td><code>:xml_escape</code></td><td></td>
<td>xml-escapes the variable before output
(the five characters <code>&lt;&gt;&amp;&quot;&#39;</code> become
@ -1059,19 +1075,16 @@ They are all defined in template_modifiers.cc:</p>
for escaping content within CDATA blocks.</td>
</tr>
<tr><td><code>:none</code></td><td></td>
<td>leaves the variable as is (used to disable <A
HREF="#auto_escape">auto-escaping</A>)</td>
</tr>
</table>
<p>The <code>*_with_arg</code> modifiers require an argument to
specify the type of escaping to use.
specify the type of escaping to use. The following sections list
the supported arguments for each of these modifiers.
<h4 id="html_escape_args">:H Arguments</h4>
<p>Here are the values that are supported by
the <code>html_escape_with_arg</code> modifier:</p>
<table border=1 cellpadding=3>
<table border="1" cellpadding="3" summary="Arguments for :H modifier">
<tr><th>value</th><th>description</th></tr>
<tr><td><code>=snippet</code></td>
@ -1097,16 +1110,33 @@ the <code>html_escape_with_arg</code> modifier:</p>
</table>
<h4 id="url_escape_args">:I and :U Arguments</h4>
<p>Here are the values that are supported by
the <code>url_escape_with_arg</code> modifier:</p>
<table border=1 cellpadding=3>
the <code>img_src_url_escape_with_arg</code> and
<code>url_escape_with_arg</code> modifiers:</p>
<table border="1" cellpadding="3" summary="Arguments for :I and :U modifiers">
<tr><th>value</th><th>description</th></tr>
<tr><td><code>=html</code></td>
<td>Ensures that a variable contains a safe URL. Safe means that
it is either a http or https URL, or else it has no protocol
specified. If the URL is safe it is html-escaped, otherwise
it is replaced with <code>#</code>.</td>
specified.
<ul>
<li>If the URL is safe, the modifier HTML-escapes the URL.
<li>Otherwise, the modifier replaces the unsafe URL with one of the
following values:
<ul>
<li><code>/images/cleardot.gif</code> (the <code>:I</code>
modifier)
<li><code>#</code> (the <code>:U</code> modifier)
</ul>
</li>
</ul>
<b>Do not use <code>:U</code> for image URLs.</b> Use <code>:I</code>
instead. <code>#</code> is not a safe replacement for an image URL.
<code>&lt;img src=#&gt;</code> can cause each browser to request the
entire page again.
</td>
</tr>
<tr><td><code>=javascript</code></td>
@ -1123,14 +1153,16 @@ the <code>url_escape_with_arg</code> modifier:</p>
</tr>
<tr><td><code>=query</code></td>
<td>Same as <code>url_query_escape</code>.</td>
<td>(Supported for <code>:U</code> only) Same as
<code>url_query_escape</code>.</td>
</tr>
</table>
<h4 id="javascript_escape_args">:J Arguments</h4>
<p>Here are the values that are supported by
the <code>javascript_escape_with_arg</code> modifier:</p>
<table border=1 cellpadding=3>
<table border="1" cellpadding="3" summary="Arguments for :J modifier">
<tr><th>value</th><th>description</th></tr>
<tr><td><code>=number</code></td>
@ -1149,12 +1181,6 @@ the <code>javascript_escape_with_arg</code> modifier:</p>
</tr>
</table>
<p><strong>NOTE:</strong> At the moment, there are no filters for
handling XML attributes and text nodes. For HTML snippets, use the
html filter; in other situations, it may be appropriate to use CDATA
blocks.</p>
<h3> Custom Modifiers </h3>
<p>In addition to the built-in modifiers, you can write your own
@ -1480,7 +1506,7 @@ workflow: the common template use-case would be:</p>
<pre>
Template* tpl = Template::GetTemplate(filename, strip_mode);
TemplateDictionary dict(name);
tpl->Expand(&dict, &outstring);
tpl->Expand(&amp;dict, &amp;outstring);
</pre>
<p>In current use, this model is deprecated in favor of the single

View File

@ -1,7 +1,7 @@
Source: ctemplate
Section: libdevel
Priority: optional
Maintainer: Google Inc. <opensource@google.com>
Maintainer: Google Inc. <google-ctemplate@googlegroups.com>
Build-Depends: debhelper (>= 4.0.0)
Standards-Version: 3.6.1

View File

@ -10,7 +10,7 @@ Group: Development/Libraries
URL: http://code.google.com/p/google-ctemplate
License: BSD
Vendor: Google
Packager: Google Inc. <opensource@google.com>
Packager: Google Inc. <google-ctemplate@googlegroups.com>
Source: http://%{NAME}.googlecode.com/files/%{NAME}-%{VERSION}.tar.gz
Distribution: Redhat 7 and above.
Buildroot: %{_tmppath}/%{name}-root

View File

@ -60,7 +60,10 @@ class @ac_windows_dllexport@ PerExpandData {
PerExpandData()
: annotate_path_(NULL),
annotator_(NULL),
expand_modifier_(NULL) { }
expand_modifier_(NULL),
map_(NULL) { }
~PerExpandData();
// Indicate that annotations should be inserted during template expansion.
// template_path_start - the start of a template path. When
@ -110,16 +113,11 @@ class @ac_windows_dllexport@ PerExpandData {
// (see template_modifiers.h). Call with value set to NULL to clear
// any value previously set. Caller is responsible for ensuring key
// and value point to valid data for the lifetime of this object.
void InsertForModifiers(const char* key, const void* value) {
map_[key] = value;
}
void InsertForModifiers(const char* key, const void* value);
// Retrieve data specific to this Expand call. Returns NULL if key
// is not found. This should only be used by template modifiers.
const void* LookupForModifiers(const char* key) const {
const DataMap::const_iterator it = map_.find(key);
return it == map_.end() ? NULL : it->second;
}
const void* LookupForModifiers(const char* key) const;
// Same as Lookup, but casts the result to a c string.
const char* LookupForModifiersAsString(const char* key) const {
@ -140,7 +138,7 @@ class @ac_windows_dllexport@ PerExpandData {
const char* annotate_path_;
TemplateAnnotator* annotator_;
const TemplateModifier* expand_modifier_;
DataMap map_;
DataMap* map_;
PerExpandData(const PerExpandData&); // disallow evil copy constructor
void operator=(const PerExpandData&); // disallow evil operator=

View File

@ -400,7 +400,7 @@ class @ac_windows_dllexport@ Template {
// Template markers have the form {{VARIABLE}}, etc. These constants
// define the {{ and }} that delimit template markers.
struct MarkerDelimiters {
struct @ac_windows_dllexport@ MarkerDelimiters {
const char* start_marker;
size_t start_marker_len;
const char* end_marker;
@ -415,7 +415,7 @@ class @ac_windows_dllexport@ Template {
};
// The current parsing state. Used in BuildTree() and subroutines
struct ParseState {
struct @ac_windows_dllexport@ ParseState {
const char* bufstart;
const char* bufend;
enum { PS_UNUSED, GETTING_TEXT, GETTING_NAME } phase;

View File

@ -182,18 +182,31 @@ extern @ac_windows_dllexport@ CleanseCss cleanse_css;
// url that doesn't have a protocol hidden in it (ie [foo.html] is
// fine, but not [javascript:foo]) and then performs another type of
// escaping. Returns the url escaped with the specified modifier if
// good, otherwise returns "#".
// good, otherwise returns a safe replacement URL.
// This is normally "#", but for <img> tags, it is not safe to set
// the src attribute to "#". This is because this causes some browsers
// to reload the page, which can cause a DoS.
class @ac_windows_dllexport@ ValidateUrl : public TemplateModifier {
public:
explicit ValidateUrl(const TemplateModifier& chained_modifier)
: chained_modifier_(chained_modifier) { }
explicit ValidateUrl(const TemplateModifier& chained_modifier,
const char* unsafe_url_replacement)
: chained_modifier_(chained_modifier),
unsafe_url_replacement_(unsafe_url_replacement),
unsafe_url_replacement_length_(strlen(unsafe_url_replacement)) { }
MODIFY_SIGNATURE_;
static const char* const kUnsafeUrlReplacement;
static const char* const kUnsafeImgSrcUrlReplacement;
private:
const TemplateModifier& chained_modifier_;
const char* unsafe_url_replacement_;
int unsafe_url_replacement_length_;
};
extern @ac_windows_dllexport@ ValidateUrl validate_url_and_html_escape;
extern @ac_windows_dllexport@ ValidateUrl validate_url_and_javascript_escape;
extern @ac_windows_dllexport@ ValidateUrl validate_url_and_css_escape;
extern @ac_windows_dllexport@ ValidateUrl validate_img_src_url_and_html_escape;
extern @ac_windows_dllexport@ ValidateUrl validate_img_src_url_and_javascript_escape;
extern @ac_windows_dllexport@ ValidateUrl validate_img_src_url_and_css_escape;
// Escapes < > & " ' to &lt; &gt; &amp; &quot; &#39; (same as in HtmlEscape).
// If you use it within a CDATA section, you may be escaping more characters

View File

@ -124,12 +124,12 @@ static void Version(FILE* outfile) {
// Removes all non alphanumeric characters from a string to form a
// valid C identifier to use as a double-inclusion guard.
static void ConvertToIdentifier(string& s) {
for (string::size_type i = 0; i < s.size(); i++) {
if (!isalnum(s[i]))
s[i] = '_';
static void ConvertToIdentifier(string* s) {
for (string::size_type i = 0; i < s->size(); i++) {
if (!isalnum((*s)[i]))
(*s)[i] = '_';
else
s[i] = toupper(s[i]);
(*s)[i] = toupper((*s)[i]);
}
}
@ -256,7 +256,7 @@ int main(int argc, char **argv) {
"//\n");
string guard(string("TPL_") + header_file);
ConvertToIdentifier(guard);
ConvertToIdentifier(&guard);
guard.append("_H_");
contents.append(string("#ifndef ") + guard + "\n");

View File

@ -49,6 +49,10 @@ bool PerExpandData::DataEq::operator()(const char* s1, const char* s2) const {
}
#endif
PerExpandData::~PerExpandData() {
delete map_;
}
TemplateAnnotator* PerExpandData::annotator() const {
if (annotator_ != NULL) {
return annotator_;
@ -59,4 +63,19 @@ TemplateAnnotator* PerExpandData::annotator() const {
return &g_default_annotator;
}
void PerExpandData::InsertForModifiers(const char* key, const void* value) {
if (!map_)
map_ = new DataMap;
(*map_)[key] = value;
}
// Retrieve data specific to this Expand call. Returns NULL if key
// is not found. This should only be used by template modifiers.
const void* PerExpandData::LookupForModifiers(const char* key) const {
if (!map_)
return NULL;
const DataMap::const_iterator it = map_->find(key);
return it == map_->end() ? NULL : it->second;
}
} // namespace ctemplate

View File

@ -35,8 +35,8 @@
#include "base/mutex.h" // This must go first so we get _XOPEN_SOURCE
#include <assert.h>
#include <errno.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h> // for fwrite, fflush
#include <stdlib.h>
#include <string.h>
@ -97,6 +97,15 @@ using HTMLPARSER_NAMESPACE::HtmlParser;
#define arraysize(x) ( sizeof(x) / sizeof(*(x)) )
// TODO(csilvers): use our own tables for these?
static bool ascii_isalnum(char c) {
return ((c & 0x80) == 0) && isalnum(c); // 7-bit ascii, and an alnum
}
static bool ascii_isspace(char c) {
return ((c & 0x80) == 0) && isspace(c); // 7-bit ascii, and a space
}
TemplateId GlobalIdForSTS_INIT(const TemplateString& s) {
return s.GetGlobalId(); // normally this method is private
}
@ -1779,7 +1788,7 @@ bool SectionTemplateNode::AddSubnode(Template *my_template) {
// nothing else.
static bool IsValidName(const char* name, int namelen) {
for (const char *cur_char = name; cur_char - name < namelen; ++cur_char) {
if (!isalnum(*cur_char) && *cur_char != '_')
if (!ascii_isalnum(*cur_char) && *cur_char != '_')
return false;
}
return true;
@ -2386,12 +2395,12 @@ bool Template::ParseDelimiters(const char* text, size_t textlen,
// so we can take a size_t instead of an int. The code is simple enough.
static void StripTemplateWhiteSpace(const char** str, size_t* len) {
// Strip off trailing whitespace.
while ((*len) > 0 && isspace((*str)[(*len)-1])) {
while ((*len) > 0 && ascii_isspace((*str)[(*len)-1])) {
(*len)--;
}
// Strip off leading whitespace.
while ((*len) > 0 && isspace((*str)[0])) {
while ((*len) > 0 && ascii_isspace((*str)[0])) {
(*len)--;
(*str)++;
}

View File

@ -339,6 +339,11 @@ void CssUrlEscape::Modify(const char* in, size_t inlen,
}
CssUrlEscape css_url_escape;
// These URLs replace unsafe URLs for :U and :I url-escaping modes.
const char* const ValidateUrl::kUnsafeUrlReplacement = "#";
const char* const ValidateUrl::kUnsafeImgSrcUrlReplacement =
"/images/cleardot.gif";
void ValidateUrl::Modify(const char* in, size_t inlen,
const PerExpandData* per_expand_data,
ExpandEmitter* out, const string& arg) const {
@ -359,16 +364,35 @@ void ValidateUrl::Modify(const char* in, size_t inlen,
// and ftp
} else {
// It's a bad protocol, so return something safe
chained_modifier_.Modify("#", 1, per_expand_data, out, "");
chained_modifier_.Modify(unsafe_url_replacement_,
unsafe_url_replacement_length_,
per_expand_data,
out,
"");
return;
}
}
// If we get here, it's a valid url, so just escape it
chained_modifier_.Modify(in, inlen, per_expand_data, out, "");
}
ValidateUrl validate_url_and_html_escape(html_escape);
ValidateUrl validate_url_and_javascript_escape(javascript_escape);
ValidateUrl validate_url_and_css_escape(css_url_escape);
ValidateUrl validate_url_and_html_escape(
html_escape,
ValidateUrl::kUnsafeUrlReplacement);
ValidateUrl validate_url_and_javascript_escape(
javascript_escape,
ValidateUrl::kUnsafeUrlReplacement);
ValidateUrl validate_url_and_css_escape(
css_url_escape,
ValidateUrl::kUnsafeUrlReplacement);
ValidateUrl validate_img_src_url_and_html_escape(
html_escape,
ValidateUrl::kUnsafeImgSrcUrlReplacement);
ValidateUrl validate_img_src_url_and_javascript_escape(
javascript_escape,
ValidateUrl::kUnsafeImgSrcUrlReplacement);
ValidateUrl validate_img_src_url_and_css_escape(
css_url_escape,
ValidateUrl::kUnsafeImgSrcUrlReplacement);
void XmlEscape::Modify(const char* in, size_t inlen,
const PerExpandData*,
@ -675,7 +699,9 @@ static struct ModifierWithAlternatives {
} g_modifiers[] = {
/* 0 */ { ModifierInfo("cleanse_css", 'c',
XSS_WEB_STANDARD, &cleanse_css),
{&g_modifiers[16].modifier_info} }, // url_escape_with_arg=css
{&g_modifiers[16].modifier_info, // url_escape_with_arg=css
// img_src_url_escape_with_arg=css
&g_modifiers[19].modifier_info} },
/* 1 */ { ModifierInfo("html_escape", 'h',
XSS_WEB_STANDARD, &html_escape),
{&g_modifiers[2].modifier_info, // html_escape_with_arg=snippet
@ -685,7 +711,9 @@ static struct ModifierWithAlternatives {
&g_modifiers[8].modifier_info, // pre_escape
&g_modifiers[9].modifier_info, // url_query_escape
&g_modifiers[11].modifier_info, // url_escape_with_arg=html
&g_modifiers[12].modifier_info} }, // url_escape_with_arg=query
&g_modifiers[12].modifier_info, // url_escape_with_arg=query
// img_src_url_escape_with_arg=html
&g_modifiers[18].modifier_info} },
/* 2 */ { ModifierInfo("html_escape_with_arg=snippet", 'H',
XSS_WEB_STANDARD, &snippet_escape),
@ -736,6 +764,15 @@ static struct ModifierWithAlternatives {
XSS_WEB_STANDARD, &javascript_number), {} },
/* 16 */ { ModifierInfo("url_escape_with_arg=css", 'U',
XSS_WEB_STANDARD, &validate_url_and_css_escape), {} },
/* 17 */ { ModifierInfo("img_src_url_escape_with_arg=javascript", 'I',
XSS_WEB_STANDARD,
&validate_img_src_url_and_javascript_escape), {} },
/* 18 */ { ModifierInfo("img_src_url_escape_with_arg=html", 'I',
XSS_WEB_STANDARD,
&validate_img_src_url_and_html_escape), {} },
/* 19 */ { ModifierInfo("img_src_url_escape_with_arg=css", 'I',
XSS_WEB_STANDARD,
&validate_img_src_url_and_css_escape), {} },
};
static vector<const ModifierInfo*> g_extension_modifiers;

View File

@ -876,6 +876,7 @@ class TemplateCacheUnittest {
AssertExpandIs(cache_tpl1, &dict, "{valid template}", true);
const Template* cache_tpl2 = cache.GetTemplate(filename2, DO_NOT_STRIP);
assert(cache_tpl2);
static_cast<void>(cache_tpl2); // avoid unused var warning in opt mode
AssertExpandWithCacheIs(&cache, filename2, DO_NOT_STRIP, &dict, NULL,
"hi bar\n", true);
@ -892,6 +893,7 @@ class TemplateCacheUnittest {
string filename3 = StringToTemplateFile("{yet another valid template}");
const Template* cache_tpl3 = cache.GetTemplate(filename3, STRIP_WHITESPACE);
assert(!cache_tpl3);
static_cast<void>(cache_tpl3); // avoid unused var warning in opt mode
// 2. Reloading existing templates fails.
StringToFile("{file1 contents changed}", filename1);

View File

@ -230,9 +230,49 @@ class TemplateModifiersUnittest {
ASSERT_STREQ(peer.GetSectionValue("harder https URL"),
"https://www.google.com/search?q=f&amp;hl=en");
ASSERT_STREQ(peer.GetSectionValue("easy javascript URL"),
"#");
ctemplate::ValidateUrl::kUnsafeUrlReplacement);
ASSERT_STREQ(peer.GetSectionValue("harder javascript URL"),
"#");
ctemplate::ValidateUrl::kUnsafeUrlReplacement);
ASSERT_STREQ(peer.GetSectionValue("easy relative URL"),
"foobar.html");
ASSERT_STREQ(peer.GetSectionValue("harder relative URL"),
"/search?q=green flowers&amp;hl=en");
ASSERT_STREQ(peer.GetSectionValue("ftp URL"),
"ftp://ftp.example.org/pub/file.txt");
}
static void TestValidateImgSrcUrlHtmlEscape() {
TemplateDictionary dict("TestValidateImgSrcUrlHtmlEscape", NULL);
dict.SetEscapedValue("easy http URL", "http://www.google.com",
ctemplate::validate_img_src_url_and_html_escape);
dict.SetEscapedValue("harder https URL",
"https://www.google.com/search?q=f&hl=en",
ctemplate::validate_img_src_url_and_html_escape);
dict.SetEscapedValue("easy javascript URL",
"javascript:alert(document.cookie)",
ctemplate::validate_img_src_url_and_html_escape);
dict.SetEscapedValue("harder javascript URL",
"javascript:alert(10/5)",
ctemplate::validate_img_src_url_and_html_escape);
dict.SetEscapedValue("easy relative URL",
"foobar.html",
ctemplate::validate_img_src_url_and_html_escape);
dict.SetEscapedValue("harder relative URL",
"/search?q=green flowers&hl=en",
ctemplate::validate_img_src_url_and_html_escape);
dict.SetEscapedValue("ftp URL",
"ftp://ftp.example.org/pub/file.txt",
ctemplate::validate_img_src_url_and_html_escape);
TemplateDictionaryPeer peer(&dict); // peer can look inside the dict
ASSERT_STREQ(peer.GetSectionValue("easy http URL"),
"http://www.google.com");
ASSERT_STREQ(peer.GetSectionValue("harder https URL"),
"https://www.google.com/search?q=f&amp;hl=en");
ASSERT_STREQ(peer.GetSectionValue("easy javascript URL"),
ctemplate::ValidateUrl::kUnsafeImgSrcUrlReplacement);
ASSERT_STREQ(peer.GetSectionValue("harder javascript URL"),
ctemplate::ValidateUrl::kUnsafeImgSrcUrlReplacement);
ASSERT_STREQ(peer.GetSectionValue("easy relative URL"),
"foobar.html");
ASSERT_STREQ(peer.GetSectionValue("harder relative URL"),
@ -290,20 +330,84 @@ class TemplateModifiersUnittest {
"https://www.google.com/search?q\\x3df\\x26hl\\x3den");
ASSERT_STREQ(peer.GetSectionValue("mangled http URL"),
"HTTP://www.google.com");
ASSERT_STREQ(peer.GetSectionValue("easy javascript URL"),
"#");
ASSERT_STREQ(peer.GetSectionValue("easy javascript URL"), "#");
ASSERT_STREQ(peer.GetSectionValue("harder javascript URL"),
"#");
ctemplate::ValidateUrl::kUnsafeUrlReplacement);
ASSERT_STREQ(peer.GetSectionValue("easy relative URL"),
"foobar.html");
ASSERT_STREQ(peer.GetSectionValue("harder relative URL"),
"/search?q\\x3dgreen flowers\\x26hl\\x3den");
ASSERT_STREQ(peer.GetSectionValue("data URL"),
"#");
ctemplate::ValidateUrl::kUnsafeUrlReplacement);
ASSERT_STREQ(peer.GetSectionValue("mangled javascript URL"),
"#");
ctemplate::ValidateUrl::kUnsafeUrlReplacement);
ASSERT_STREQ(peer.GetSectionValue("harder mangled javascript URL"),
"#");
ctemplate::ValidateUrl::kUnsafeUrlReplacement);
}
static void TestValidateImgSrcUrlJavascriptEscape() {
TemplateDictionary dict("TestValidateImgSrcUrlJavascriptEscape", NULL);
dict.SetEscapedValue(
"easy http URL", "http://www.google.com",
ctemplate::validate_img_src_url_and_javascript_escape);
dict.SetEscapedValue(
"harder https URL",
"https://www.google.com/search?q=f&hl=en",
ctemplate::validate_img_src_url_and_javascript_escape);
dict.SetEscapedValue(
"mangled http URL", "HTTP://www.google.com",
ctemplate::validate_img_src_url_and_javascript_escape);
dict.SetEscapedValue(
"easy javascript URL",
"javascript:alert(document.cookie)",
ctemplate::validate_img_src_url_and_javascript_escape);
dict.SetEscapedValue(
"harder javascript URL",
"javascript:alert(10/5)",
ctemplate::validate_img_src_url_and_javascript_escape);
dict.SetEscapedValue(
"easy relative URL",
"foobar.html",
ctemplate::validate_img_src_url_and_javascript_escape);
dict.SetEscapedValue(
"harder relative URL",
"/search?q=green flowers&hl=en",
ctemplate::validate_img_src_url_and_javascript_escape);
dict.SetEscapedValue(
"data URL",
"data: text/html",
ctemplate::validate_img_src_url_and_javascript_escape);
dict.SetEscapedValue(
"mangled javascript URL",
"javaSCRIPT:alert(5)",
ctemplate::validate_img_src_url_and_javascript_escape);
dict.SetEscapedValue(
"harder mangled javascript URL",
"java\nSCRIPT:alert(5)",
ctemplate::validate_img_src_url_and_javascript_escape);
TemplateDictionaryPeer peer(&dict); // peer can look inside the dict
ASSERT_STREQ(peer.GetSectionValue("easy http URL"),
"http://www.google.com");
ASSERT_STREQ(peer.GetSectionValue("harder https URL"),
"https://www.google.com/search?q\\x3df\\x26hl\\x3den");
ASSERT_STREQ(peer.GetSectionValue("mangled http URL"),
"HTTP://www.google.com");
ASSERT_STREQ(peer.GetSectionValue("easy javascript URL"),
ctemplate::ValidateUrl::kUnsafeImgSrcUrlReplacement);
ASSERT_STREQ(peer.GetSectionValue("harder javascript URL"),
ctemplate::ValidateUrl::kUnsafeImgSrcUrlReplacement);
ASSERT_STREQ(peer.GetSectionValue("easy relative URL"),
"foobar.html");
ASSERT_STREQ(peer.GetSectionValue("harder relative URL"),
"/search?q\\x3dgreen flowers\\x26hl\\x3den");
ASSERT_STREQ(peer.GetSectionValue("data URL"),
"/images/cleardot.gif");
ASSERT_STREQ(peer.GetSectionValue("mangled javascript URL"),
ctemplate::ValidateUrl::kUnsafeImgSrcUrlReplacement);
ASSERT_STREQ(peer.GetSectionValue("harder mangled javascript URL"),
ctemplate::ValidateUrl::kUnsafeImgSrcUrlReplacement);
}
static void TestValidateUrlCssEscape() {
@ -327,7 +431,38 @@ class TemplateModifiersUnittest {
"http://www.google.com");
ASSERT_STREQ(peer.GetSectionValue("harder https URL"),
"https://www.google.com/search?q=f&hl=en");
ASSERT_STREQ(peer.GetSectionValue("javascript URL"), "#");
ASSERT_STREQ(peer.GetSectionValue("javascript URL"),
ctemplate::ValidateUrl::kUnsafeUrlReplacement);
ASSERT_STREQ(peer.GetSectionValue("relative URL"),
"/search?q=green flowers&hl=en");
ASSERT_STREQ(peer.GetSectionValue("hardest URL"),
"http://www.google.com/s?q=%27bla%27"
"&a=%22%22&b=%28%3Ctag%3E%29&c=%2A%0D%0A%5C%5Cbla");
}
static void TestValidateImgSrcUrlCssEscape() {
TemplateDictionary dict("TestValidateImgSrcUrlCssEscape", NULL);
dict.SetEscapedValue("easy http URL", "http://www.google.com",
ctemplate::validate_img_src_url_and_css_escape);
dict.SetEscapedValue("harder https URL",
"https://www.google.com/search?q=f&hl=en",
ctemplate::validate_img_src_url_and_css_escape);
dict.SetEscapedValue("javascript URL",
"javascript:alert(document.cookie)",
ctemplate::validate_img_src_url_and_css_escape);
dict.SetEscapedValue("relative URL", "/search?q=green flowers&hl=en",
ctemplate::validate_img_src_url_and_css_escape);
dict.SetEscapedValue("hardest URL", "http://www.google.com/s?q='bla'"
"&a=\"\"&b=(<tag>)&c=*\r\n\\\\bla",
ctemplate::validate_img_src_url_and_css_escape);
TemplateDictionaryPeer peer(&dict); // peer can look inside the dict
ASSERT_STREQ(peer.GetSectionValue("easy http URL"),
"http://www.google.com");
ASSERT_STREQ(peer.GetSectionValue("harder https URL"),
"https://www.google.com/search?q=f&hl=en");
ASSERT_STREQ(peer.GetSectionValue("javascript URL"),
ctemplate::ValidateUrl::kUnsafeImgSrcUrlReplacement);
ASSERT_STREQ(peer.GetSectionValue("relative URL"),
"/search?q=green flowers&hl=en");
ASSERT_STREQ(peer.GetSectionValue("hardest URL"),
@ -900,8 +1035,11 @@ int main(int argc, char** argv) {
TemplateModifiersUnittest::TestPreEscape();
TemplateModifiersUnittest::TestXmlEscape();
TemplateModifiersUnittest::TestValidateUrlHtmlEscape();
TemplateModifiersUnittest::TestValidateImgSrcUrlHtmlEscape();
TemplateModifiersUnittest::TestValidateUrlJavascriptEscape();
TemplateModifiersUnittest::TestValidateImgSrcUrlJavascriptEscape();
TemplateModifiersUnittest::TestValidateUrlCssEscape();
TemplateModifiersUnittest::TestValidateImgSrcUrlCssEscape();
TemplateModifiersUnittest::TestCleanseAttribute();
TemplateModifiersUnittest::TestCleanseCss();
TemplateModifiersUnittest::TestJavascriptEscape();

View File

@ -65,7 +65,10 @@ class CTEMPLATE_DLL_DECL PerExpandData {
PerExpandData()
: annotate_path_(NULL),
annotator_(NULL),
expand_modifier_(NULL) { }
expand_modifier_(NULL),
map_(NULL) { }
~PerExpandData();
// Indicate that annotations should be inserted during template expansion.
// template_path_start - the start of a template path. When
@ -115,16 +118,11 @@ class CTEMPLATE_DLL_DECL PerExpandData {
// (see template_modifiers.h). Call with value set to NULL to clear
// any value previously set. Caller is responsible for ensuring key
// and value point to valid data for the lifetime of this object.
void InsertForModifiers(const char* key, const void* value) {
map_[key] = value;
}
void InsertForModifiers(const char* key, const void* value);
// Retrieve data specific to this Expand call. Returns NULL if key
// is not found. This should only be used by template modifiers.
const void* LookupForModifiers(const char* key) const {
const DataMap::const_iterator it = map_.find(key);
return it == map_.end() ? NULL : it->second;
}
const void* LookupForModifiers(const char* key) const;
// Same as Lookup, but casts the result to a c string.
const char* LookupForModifiersAsString(const char* key) const {
@ -145,7 +143,7 @@ class CTEMPLATE_DLL_DECL PerExpandData {
const char* annotate_path_;
TemplateAnnotator* annotator_;
const TemplateModifier* expand_modifier_;
DataMap map_;
DataMap* map_;
PerExpandData(const PerExpandData&); // disallow evil copy constructor
void operator=(const PerExpandData&); // disallow evil operator=

View File

@ -405,7 +405,7 @@ class CTEMPLATE_DLL_DECL Template {
// Template markers have the form {{VARIABLE}}, etc. These constants
// define the {{ and }} that delimit template markers.
struct MarkerDelimiters {
struct CTEMPLATE_DLL_DECL MarkerDelimiters {
const char* start_marker;
size_t start_marker_len;
const char* end_marker;
@ -420,7 +420,7 @@ class CTEMPLATE_DLL_DECL Template {
};
// The current parsing state. Used in BuildTree() and subroutines
struct ParseState {
struct CTEMPLATE_DLL_DECL ParseState {
const char* bufstart;
const char* bufend;
enum { PS_UNUSED, GETTING_TEXT, GETTING_NAME } phase;

View File

@ -53,6 +53,11 @@ struct stat;
// as a compiler flag in your project file to turn off the dllimports.
#ifndef CTEMPLATE_DLL_DECL
# define CTEMPLATE_DLL_DECL __declspec(dllimport)
extern template class __declspec(dllimport) std::allocator<std::string>;
extern template class __declspec(dllimport) std::vector<std::string>;
#else
template class __declspec(dllexport) std::allocator<std::string>;
template class __declspec(dllexport) std::vector<std::string>;
#endif
namespace ctemplate {

View File

@ -187,18 +187,31 @@ extern CTEMPLATE_DLL_DECL CleanseCss cleanse_css;
// url that doesn't have a protocol hidden in it (ie [foo.html] is
// fine, but not [javascript:foo]) and then performs another type of
// escaping. Returns the url escaped with the specified modifier if
// good, otherwise returns "#".
// good, otherwise returns a safe replacement URL.
// This is normally "#", but for <img> tags, it is not safe to set
// the src attribute to "#". This is because this causes some browsers
// to reload the page, which can cause a DoS.
class CTEMPLATE_DLL_DECL ValidateUrl : public TemplateModifier {
public:
explicit ValidateUrl(const TemplateModifier& chained_modifier)
: chained_modifier_(chained_modifier) { }
explicit ValidateUrl(const TemplateModifier& chained_modifier,
const char* unsafe_url_replacement)
: chained_modifier_(chained_modifier),
unsafe_url_replacement_(unsafe_url_replacement),
unsafe_url_replacement_length_(strlen(unsafe_url_replacement)) { }
MODIFY_SIGNATURE_;
static const char* const kUnsafeUrlReplacement;
static const char* const kUnsafeImgSrcUrlReplacement;
private:
const TemplateModifier& chained_modifier_;
const char* unsafe_url_replacement_;
int unsafe_url_replacement_length_;
};
extern CTEMPLATE_DLL_DECL ValidateUrl validate_url_and_html_escape;
extern CTEMPLATE_DLL_DECL ValidateUrl validate_url_and_javascript_escape;
extern CTEMPLATE_DLL_DECL ValidateUrl validate_url_and_css_escape;
extern CTEMPLATE_DLL_DECL ValidateUrl validate_img_src_url_and_html_escape;
extern CTEMPLATE_DLL_DECL ValidateUrl validate_img_src_url_and_javascript_escape;
extern CTEMPLATE_DLL_DECL ValidateUrl validate_img_src_url_and_css_escape;
// Escapes < > & " ' to &lt; &gt; &amp; &quot; &#39; (same as in HtmlEscape).
// If you use it within a CDATA section, you may be escaping more characters

View File

@ -58,6 +58,21 @@ DLLDEF_DEFINES="\
# define $DLLDEF_MACRO_NAME __declspec(dllimport)\n\
#endif"
# template_cache.h gets a special DEFINE to work around the
# difficulties in dll-exporting stl containers. Ugh.
TEMPLATE_CACHE_DLLDEF_DEFINES="\
// NOTE: if you are statically linking the template library into your binary\n\
// (rather than using the template .dll), set '/D $DLLDEF_MACRO_NAME='\n\
// as a compiler flag in your project file to turn off the dllimports.\n\
#ifndef $DLLDEF_MACRO_NAME\n\
# define $DLLDEF_MACRO_NAME __declspec(dllimport)\n\
extern template class __declspec(dllimport) std::allocator<std::string>;\n\
extern template class __declspec(dllimport) std::vector<std::string>;\n\
#else\n\
template class __declspec(dllexport) std::allocator<std::string>;\n\
template class __declspec(dllexport) std::vector<std::string>;\n\
#endif"
# Read all the windows config info into variables
# In order for the 'set' to take, this requires putting all in a subshell.
(
@ -72,13 +87,16 @@ DLLDEF_DEFINES="\
echo "Processing $file"
outfile="$1/windows/ctemplate/`basename $file .in`"
if test "`basename $file`" = template_cache.h.in; then
MY_DLLDEF_DEFINES=$TEMPLATE_CACHE_DLLDEF_DEFINES
else
MY_DLLDEF_DEFINES=$DLLDEF_DEFINES
fi
# Besides replacing @...@, we also need to turn on dllimport
# We also need to replace hash by hash_compare (annoying we hard-code :-( )
# Finally, we get rid of the header files that try to find @ac_cv_unit64@
# We tell them by their comment: "a place @ac_cv_uint64@ might live".
# Except it has a typo and says "unit64", so I check for both.
sed -e "s!@ac_windows_dllexport@!$DLLDEF_MACRO_NAME!g" \
-e "s!@ac_windows_dllexport_defines@!$DLLDEF_DEFINES!g" \
-e "s!@ac_windows_dllexport_defines@!$MY_DLLDEF_DEFINES!g" \
-e "s!@ac_cv_cxx_hash_map@!$HASH_MAP_H!g" \
-e "s!@ac_cv_cxx_hash_set@!$HASH_SET_H!g" \
-e "s!@ac_cv_cxx_hash_map_class@!$HASH_NAMESPACE::hash_map!g" \