[Xfce4-commits] [www/forum.xfce.org] 01/79: Intial import of FluxBB 1.4.2.

noreply at xfce.org noreply at xfce.org
Tue May 21 14:47:56 CEST 2019


This is an automated email from the git hooks/post-receive script.

s   k   u   n   n   y   k       p   u   s   h   e   d       a       c   o   m   m   i   t       t   o       b   r   a   n   c   h       o   l   d   f   o   r   u   m   
   in repository www/forum.xfce.org.

commit 0b1b8a83d3315f687b6ee8a4c69f5b4e8a13f984
Author: Nick Schermer <nick at xfce.org>
Date:   Fri Nov 12 18:01:46 2010 +0100

    Intial import of FluxBB 1.4.2.
---
 .gitignore                                        |    6 +
 COPYING                                           |  340 ++++
 admin_bans.php                                    |  541 +++++++
 admin_categories.php                              |  267 ++++
 admin_censoring.php                               |  150 ++
 admin_forums.php                                  |  473 ++++++
 admin_groups.php                                  |  565 +++++++
 admin_index.php                                   |  176 +++
 admin_loader.php                                  |   55 +
 admin_maintenance.php                             |  178 +++
 admin_options.php                                 |  799 ++++++++++
 admin_permissions.php                             |  177 +++
 admin_prune.php                                   |  217 +++
 admin_ranks.php                                   |  186 +++
 admin_reports.php                                 |  176 +++
 admin_users.php                                   |  619 ++++++++
 cache/.htaccess                                   |    4 +
 cache/index.html                                  |    1 +
 db_update.php                                     | 1692 ++++++++++++++++++++
 delete.php                                        |  133 ++
 edit.php                                          |  257 +++
 extern.php                                        |  503 ++++++
 footer.php                                        |  201 +++
 header.php                                        |  244 +++
 help.php                                          |  144 ++
 img/avatars/index.html                            |    1 +
 img/index.html                                    |    1 +
 img/smilies/big_smile.png                         |  Bin 0 -> 544 bytes
 img/smilies/cool.png                              |  Bin 0 -> 560 bytes
 img/smilies/hmm.png                               |  Bin 0 -> 564 bytes
 img/smilies/index.html                            |    1 +
 img/smilies/lol.png                               |  Bin 0 -> 538 bytes
 img/smilies/mad.png                               |  Bin 0 -> 560 bytes
 img/smilies/neutral.png                           |  Bin 0 -> 565 bytes
 img/smilies/roll.png                              |  Bin 0 -> 559 bytes
 img/smilies/sad.png                               |  Bin 0 -> 568 bytes
 img/smilies/smile.png                             |  Bin 0 -> 598 bytes
 img/smilies/tongue.png                            |  Bin 0 -> 563 bytes
 img/smilies/wink.png                              |  Bin 0 -> 642 bytes
 img/smilies/yikes.png                             |  Bin 0 -> 554 bytes
 img/test.png                                      |  Bin 0 -> 6369 bytes
 include/cache.php                                 |  157 ++
 include/common.php                                |  183 +++
 include/common_admin.php                          |  133 ++
 include/dblayer/common_db.php                     |   48 +
 include/dblayer/index.html                        |    1 +
 include/dblayer/mysql.php                         |  362 +++++
 include/dblayer/mysql_innodb.php                  |  376 +++++
 include/dblayer/mysqli.php                        |  369 +++++
 include/dblayer/mysqli_innodb.php                 |  382 +++++
 include/dblayer/pgsql.php                         |  426 +++++
 include/dblayer/sqlite.php                        |  538 +++++++
 include/email.php                                 |  186 +++
 include/functions.php                             | 1746 ++++++++++++++++++++
 include/index.html                                |    1 +
 include/parser.php                                |  932 +++++++++++
 include/search_idx.php                            |  311 ++++
 include/template/admin.tpl                        |   38 +
 include/template/help.tpl                         |   23 +
 include/template/index.html                       |    1 +
 include/template/main.tpl                         |   38 +
 include/template/maintenance.tpl                  |   23 +
 include/template/redirect.tpl                     |   25 +
 include/utf8/index.html                           |    1 +
 include/utf8/mbstring/core.php                    |  144 ++
 include/utf8/mbstring/index.html                  |    1 +
 include/utf8/native/core.php                      |  422 +++++
 include/utf8/native/index.html                    |    1 +
 include/utf8/ord.php                              |   78 +
 include/utf8/str_ireplace.php                     |   72 +
 include/utf8/str_pad.php                          |   59 +
 include/utf8/str_split.php                        |   33 +
 include/utf8/strcasecmp.php                       |   27 +
 include/utf8/strcspn.php                          |   36 +
 include/utf8/stristr.php                          |   34 +
 include/utf8/strrev.php                           |   22 +
 include/utf8/strspn.php                           |   32 +
 include/utf8/substr_replace.php                   |   27 +
 include/utf8/trim.php                             |   74 +
 include/utf8/ucfirst.php                          |   35 +
 include/utf8/ucwords.php                          |   46 +
 include/utf8/utf8.php                             |   72 +
 include/utf8/utils/ascii.php                      |  221 +++
 include/utf8/utils/bad.php                        |  435 +++++
 include/utf8/utils/index.html                     |    1 +
 include/utf8/utils/patterns.php                   |   67 +
 include/utf8/utils/position.php                   |  171 ++
 include/utf8/utils/specials.php                   |  131 ++
 include/utf8/utils/unicode.php                    |  241 +++
 include/utf8/utils/validation.php                 |  186 +++
 index.php                                         |  241 +++
 install.php                                       | 1754 +++++++++++++++++++++
 lang/English/admin_bans.php                       |   66 +
 lang/English/admin_categories.php                 |   29 +
 lang/English/admin_censoring.php                  |   22 +
 lang/English/admin_common.php                     |   44 +
 lang/English/admin_forums.php                     |   52 +
 lang/English/admin_groups.php                     |   82 +
 lang/English/admin_index.php                      |   51 +
 lang/English/admin_maintenance.php                |   23 +
 lang/English/admin_options.php                    |  217 +++
 lang/English/admin_permissions.php                |   36 +
 lang/English/admin_plugin_example.php             |   17 +
 lang/English/admin_prune.php                      |   23 +
 lang/English/admin_ranks.php                      |   23 +
 lang/English/admin_reports.php                    |   20 +
 lang/English/admin_users.php                      |   70 +
 lang/English/common.php                           |  169 ++
 lang/English/delete.php                           |   16 +
 lang/English/forum.php                            |   14 +
 lang/English/help.php                             |   59 +
 lang/English/index.html                           |    1 +
 lang/English/index.php                            |   20 +
 lang/English/login.php                            |   26 +
 lang/English/mail_templates/activate_email.tpl    |   12 +
 lang/English/mail_templates/activate_password.tpl |   14 +
 lang/English/mail_templates/form_email.tpl        |   13 +
 lang/English/mail_templates/index.html            |    1 +
 lang/English/mail_templates/new_reply.tpl         |   11 +
 lang/English/mail_templates/new_reply_full.tpl    |   18 +
 lang/English/mail_templates/rename.tpl            |   12 +
 lang/English/mail_templates/welcome.tpl           |   12 +
 lang/English/misc.php                             |   91 ++
 lang/English/post.php                             |   36 +
 lang/English/prof_reg.php                         |   79 +
 lang/English/profile.php                          |  137 ++
 lang/English/register.php                         |   37 +
 lang/English/search.php                           |   49 +
 lang/English/stopwords.txt                        |  175 ++
 lang/English/topic.php                            |   31 +
 lang/English/userlist.php                         |   13 +
 lang/index.html                                   |    1 +
 login.php                                         |  263 +++
 misc.php                                          |  334 ++++
 moderate.php                                      |  923 +++++++++++
 plugins/AMP_Example.php                           |  132 ++
 plugins/index.html                                |    1 +
 post.php                                          |  617 ++++++++
 profile.php                                       | 1718 ++++++++++++++++++++
 register.php                                      |  411 +++++
 search.php                                        |  750 +++++++++
 style/Air.css                                     | 1606 +++++++++++++++++++
 style/Air/base_admin.css                          |  143 ++
 style/Air/img/asterisk.png                        |  Bin 0 -> 188 bytes
 style/Air/img/bull.png                            |  Bin 0 -> 107 bytes
 style/Air/img/email.png                           |  Bin 0 -> 500 bytes
 style/Air/img/exclaim.png                         |  Bin 0 -> 513 bytes
 style/Air/img/ext.png                             |  Bin 0 -> 136 bytes
 style/Air/img/feed.png                            |  Bin 0 -> 496 bytes
 style/Air/img/help.png                            |  Bin 0 -> 491 bytes
 style/Air/img/index.html                          |    1 +
 style/Air/img/main_end.png                        |  Bin 0 -> 360 bytes
 style/Air/img/main_top.png                        |  Bin 0 -> 361 bytes
 style/Air/index.html                              |    1 +
 style/Cobalt.css                                  | 1086 +++++++++++++
 style/Earth.css                                   | 1606 +++++++++++++++++++
 style/Earth/base_admin.css                        |  143 ++
 style/Earth/img/asterisk.png                      |  Bin 0 -> 188 bytes
 style/Earth/img/bull.png                          |  Bin 0 -> 107 bytes
 style/Earth/img/email.png                         |  Bin 0 -> 410 bytes
 style/Earth/img/exclaim.png                       |  Bin 0 -> 513 bytes
 style/Earth/img/ext.png                           |  Bin 0 -> 131 bytes
 style/Earth/img/feed.png                          |  Bin 0 -> 496 bytes
 style/Earth/img/help.png                          |  Bin 0 -> 441 bytes
 style/Earth/img/index.html                        |    1 +
 style/Earth/img/main_end.png                      |  Bin 0 -> 259 bytes
 style/Earth/img/main_top.png                      |  Bin 0 -> 257 bytes
 style/Earth/index.html                            |    1 +
 style/Fire.css                                    | 1606 +++++++++++++++++++
 style/Fire/base_admin.css                         |  143 ++
 style/Fire/img/asterisk.png                       |  Bin 0 -> 188 bytes
 style/Fire/img/bull.png                           |  Bin 0 -> 107 bytes
 style/Fire/img/email.png                          |  Bin 0 -> 419 bytes
 style/Fire/img/exclaim.png                        |  Bin 0 -> 513 bytes
 style/Fire/img/ext.png                            |  Bin 0 -> 136 bytes
 style/Fire/img/feed.png                           |  Bin 0 -> 496 bytes
 style/Fire/img/help.png                           |  Bin 0 -> 448 bytes
 style/Fire/img/index.html                         |    1 +
 style/Fire/img/main_end.png                       |  Bin 0 -> 247 bytes
 style/Fire/img/main_top.png                       |  Bin 0 -> 241 bytes
 style/Fire/index.html                             |    1 +
 style/Lithium.css                                 | 1085 +++++++++++++
 style/Mercury.css                                 | 1086 +++++++++++++
 style/Oxygen.css                                  | 1086 +++++++++++++
 style/Radium.css                                  | 1086 +++++++++++++
 style/Sulfur.css                                  | 1085 +++++++++++++
 style/Technetium.css                              | 1335 ++++++++++++++++
 style/Technetium/bg.png                           |  Bin 0 -> 2004 bytes
 style/Technetium/dark-shade.png                   |  Bin 0 -> 227 bytes
 style/Technetium/darker-shade.png                 |  Bin 0 -> 238 bytes
 style/Technetium/feed.png                         |  Bin 0 -> 496 bytes
 style/Technetium/icon-closed-sticky.png           |  Bin 0 -> 948 bytes
 style/Technetium/icon-closed.png                  |  Bin 0 -> 469 bytes
 style/Technetium/icon-moved.png                   |  Bin 0 -> 881 bytes
 style/Technetium/icon-new-sticky.png              |  Bin 0 -> 971 bytes
 style/Technetium/icon-new.png                     |  Bin 0 -> 577 bytes
 style/Technetium/icon-nonew-sticky.png            |  Bin 0 -> 889 bytes
 style/Technetium/icon-nonew.png                   |  Bin 0 -> 513 bytes
 style/Technetium/index.html                       |    1 +
 style/Technetium/inv-shade.png                    |  Bin 0 -> 217 bytes
 style/Technetium/light-shade.png                  |  Bin 0 -> 219 bytes
 style/imports/base_admin.css                      |   48 +
 style/imports/index.html                          |    1 +
 style/imports/minmax.js                           |   84 +
 style/index.html                                  |    1 +
 userlist.php                                      |  182 +++
 viewforum.php                                     |  263 +++
 viewtopic.php                                     |  434 +++++
 208 files changed, 40157 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..360e483
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+cache/cache_*.php
+include/user/*
+img/avatars/*.png
+img/avatars/*.jpg
+img/avatars/*.gif
+config.php
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/admin_bans.php b/admin_bans.php
new file mode 100644
index 0000000..644dcff
--- /dev/null
+++ b/admin_bans.php
@@ -0,0 +1,541 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if ($pun_user['g_id'] != PUN_ADMIN && ($pun_user['g_moderator'] != '1' || $pun_user['g_mod_ban_users'] == '0'))
+	message($lang_common['No permission']);
+
+// Load the admin_bans.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_bans.php';
+
+// Add/edit a ban (stage 1)
+if (isset($_REQUEST['add_ban']) || isset($_GET['edit_ban']))
+{
+	if (isset($_GET['add_ban']) || isset($_POST['add_ban']))
+	{
+		// If the ID of the user to ban was provided through GET (a link from profile.php)
+		if (isset($_GET['add_ban']))
+		{
+			$user_id = intval($_GET['add_ban']);
+			if ($user_id < 2)
+				message($lang_common['Bad request']);
+
+			$result = $db->query('SELECT group_id, username, email FROM '.$db->prefix.'users WHERE id='.$user_id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+			if ($db->num_rows($result))
+				list($group_id, $ban_user, $ban_email) = $db->fetch_row($result);
+			else
+				message($lang_admin_bans['No user ID message']);
+		}
+		else // Otherwise the username is in POST
+		{
+			$ban_user = pun_trim($_POST['new_ban_user']);
+
+			if ($ban_user != '')
+			{
+				$result = $db->query('SELECT id, group_id, username, email FROM '.$db->prefix.'users WHERE username=\''.$db->escape($ban_user).'\' AND id>1') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+				if ($db->num_rows($result))
+					list($user_id, $group_id, $ban_user, $ban_email) = $db->fetch_row($result);
+				else
+					message($lang_admin_bans['No user message']);
+			}
+		}
+
+		// Make sure we're not banning an admin
+		if (isset($group_id) && $group_id == PUN_ADMIN)
+			message(sprintf($lang_admin_bans['User is admin message'], pun_htmlspecialchars($ban_user)));
+
+		// If we have a $user_id, we can try to find the last known IP of that user
+		if (isset($user_id))
+		{
+			$result = $db->query('SELECT poster_ip FROM '.$db->prefix.'posts WHERE poster_id='.$user_id.' ORDER BY posted DESC LIMIT 1') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+			$ban_ip = ($db->num_rows($result)) ? $db->result($result) : '';
+
+			if ($ban_ip == '')
+			{
+				$result = $db->query('SELECT registration_ip FROM '.$db->prefix.'users WHERE id='.$user_id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+				$ban_ip = ($db->num_rows($result)) ? $db->result($result) : '';
+			}
+		}
+
+		$mode = 'add';
+	}
+	else // We are editing a ban
+	{
+		$ban_id = intval($_GET['edit_ban']);
+		if ($ban_id < 1)
+			message($lang_common['Bad request']);
+
+		$result = $db->query('SELECT username, ip, email, message, expire FROM '.$db->prefix.'bans WHERE id='.$ban_id) or error('Unable to fetch ban info', __FILE__, __LINE__, $db->error());
+		if ($db->num_rows($result))
+			list($ban_user, $ban_ip, $ban_email, $ban_message, $ban_expire) = $db->fetch_row($result);
+		else
+			message($lang_common['Bad request']);
+
+		$diff = ($pun_user['timezone'] + $pun_user['dst']) * 3600;
+		$ban_expire = ($ban_expire != '') ? gmdate('Y-m-d', $ban_expire + $diff) : '';
+
+		$mode = 'edit';
+	}
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Bans']);
+	$focus_element = array('bans2', 'ban_user');
+	define('PUN_ACTIVE_PAGE', 'admin');
+	require PUN_ROOT.'header.php';
+
+	generate_admin_menu('bans');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_bans['Ban advanced head'] ?></span></h2>
+		<div class="box">
+			<form id="bans2" method="post" action="admin_bans.php">
+				<div class="inform">
+				<input type="hidden" name="mode" value="<?php echo $mode ?>" />
+<?php if ($mode == 'edit'): ?>				<input type="hidden" name="ban_id" value="<?php echo $ban_id ?>" />
+<?php endif; ?>				<fieldset>
+						<legend><?php echo $lang_admin_bans['Ban advanced subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_bans['Username label'] ?></th>
+									<td>
+										<input type="text" name="ban_user" size="25" maxlength="25" value="<?php if (isset($ban_user)) echo pun_htmlspecialchars($ban_user); ?>" tabindex="1" />
+										<span><?php echo $lang_admin_bans['Username help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_bans['IP label'] ?></th>
+									<td>
+										<input type="text" name="ban_ip" size="45" maxlength="255" value="<?php if (isset($ban_ip)) echo $ban_ip; ?>" tabindex="2" />
+										<span><?php echo $lang_admin_bans['IP help'] ?><?php if ($ban_user != '' && isset($user_id)) printf(' '.$lang_admin_bans['IP help link'], '<a href="admin_users.php?ip_stats='.$user_id.'">'.$lang_admin_common['here'].'</a>') ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_bans['E-mail label'] ?></th>
+									<td>
+										<input type="text" name="ban_email" size="40" maxlength="80" value="<?php if (isset($ban_email)) echo $ban_email; ?>" tabindex="3" />
+										<span><?php echo $lang_admin_bans['E-mail help'] ?></span>
+									</td>
+								</tr>
+							</table>
+							<p class="topspace"><strong class="warntext"><?php echo $lang_admin_bans['Ban IP range info'] ?></strong></p>
+						</div>
+					</fieldset>
+				</div>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_bans['Message expiry subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_bans['Ban message label'] ?></th>
+									<td>
+										<input type="text" name="ban_message" size="50" maxlength="255" value="<?php if (isset($ban_message)) echo pun_htmlspecialchars($ban_message); ?>" tabindex="4" />
+										<span><?php echo $lang_admin_bans['Ban message help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_bans['Expire date label'] ?></th>
+									<td>
+										<input type="text" name="ban_expire" size="17" maxlength="10" value="<?php if (isset($ban_expire)) echo $ban_expire; ?>" tabindex="5" />
+										<span><?php echo $lang_admin_bans['Expire date help'] ?></span>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+				<p class="submitend"><input type="submit" name="add_edit_ban" value="<?php echo $lang_admin_common['Save'] ?>" tabindex="6" /></p>
+			</form>
+		</div>
+	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+// Add/edit a ban (stage 2)
+else if (isset($_POST['add_edit_ban']))
+{
+	confirm_referrer('admin_bans.php');
+
+	$ban_user = pun_trim($_POST['ban_user']);
+	$ban_ip = trim($_POST['ban_ip']);
+	$ban_email = strtolower(trim($_POST['ban_email']));
+	$ban_message = pun_trim($_POST['ban_message']);
+	$ban_expire = trim($_POST['ban_expire']);
+
+	if ($ban_user == '' && $ban_ip == '' && $ban_email == '')
+		message($lang_admin_bans['Must enter message']);
+	else if (strtolower($ban_user) == 'guest')
+		message($lang_admin_bans['Cannot ban guest message']);
+
+	// Validate IP/IP range (it's overkill, I know)
+	if ($ban_ip != '')
+	{
+		$ban_ip = preg_replace('/\s{2,}/S', ' ', $ban_ip);
+		$addresses = explode(' ', $ban_ip);
+		$addresses = array_map('pun_trim', $addresses);
+
+		for ($i = 0; $i < count($addresses); ++$i)
+		{
+			if (strpos($addresses[$i], ':') !== false)
+			{
+				$octets = explode(':', $addresses[$i]);
+
+				for ($c = 0; $c < count($octets); ++$c)
+				{
+					$octets[$c] = ltrim($octets[$c], "0");
+
+					if ($c > 7 || (!empty($octets[$c]) && !ctype_xdigit($octets[$c])) || intval($octets[$c], 16) > 65535)
+						message($lang_admin_bans['Invalid IP message']);
+				}
+
+				$cur_address = implode(':', $octets);
+				$addresses[$i] = $cur_address;
+			}
+			else
+			{
+				$octets = explode('.', $addresses[$i]);
+
+				for ($c = 0; $c < count($octets); ++$c)
+				{
+					$octets[$c] = (strlen($octets[$c]) > 1) ? ltrim($octets[$c], "0") : $octets[$c];
+
+					if ($c > 3 || preg_match('/[^0-9]/', $octets[$c]) || intval($octets[$c]) > 255)
+						message($lang_admin_bans['Invalid IP message']);
+				}
+
+				$cur_address = implode('.', $octets);
+				$addresses[$i] = $cur_address;
+			}
+		}
+
+		$ban_ip = implode(' ', $addresses);
+	}
+
+	require PUN_ROOT.'include/email.php';
+	if ($ban_email != '' && !is_valid_email($ban_email))
+	{
+		if (!preg_match('/^[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$/', $ban_email))
+			message($lang_admin_bans['Invalid e-mail message']);
+	}
+
+	if ($ban_expire != '' && $ban_expire != 'Never')
+	{
+		$ban_expire = strtotime($ban_expire.' GMT');
+
+		if ($ban_expire == -1 || !$ban_expire)
+			message($lang_admin_bans['Invalid date message'].' '.$lang_admin_bans['Invalid date reasons']);
+
+		$diff = ($pun_user['timezone'] + $pun_user['dst']) * 3600;
+		$ban_expire -= $diff;
+
+		if ($ban_expire <= time())
+			message($lang_admin_bans['Invalid date message'].' '.$lang_admin_bans['Invalid date reasons']);
+	}
+	else
+		$ban_expire = 'NULL';
+
+	$ban_user = ($ban_user != '') ? '\''.$db->escape($ban_user).'\'' : 'NULL';
+	$ban_ip = ($ban_ip != '') ? '\''.$db->escape($ban_ip).'\'' : 'NULL';
+	$ban_email = ($ban_email != '') ? '\''.$db->escape($ban_email).'\'' : 'NULL';
+	$ban_message = ($ban_message != '') ? '\''.$db->escape($ban_message).'\'' : 'NULL';
+
+	if ($_POST['mode'] == 'add')
+		$db->query('INSERT INTO '.$db->prefix.'bans (username, ip, email, message, expire, ban_creator) VALUES('.$ban_user.', '.$ban_ip.', '.$ban_email.', '.$ban_message.', '.$ban_expire.', '.$pun_user['id'].')') or error('Unable to add ban', __FILE__, __LINE__, $db->error());
+	else
+		$db->query('UPDATE '.$db->prefix.'bans SET username='.$ban_user.', ip='.$ban_ip.', email='.$ban_email.', message='.$ban_message.', expire='.$ban_expire.' WHERE id='.intval($_POST['ban_id'])) or error('Unable to update ban', __FILE__, __LINE__, $db->error());
+
+	// Regenerate the bans cache
+	if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+		require PUN_ROOT.'include/cache.php';
+
+	generate_bans_cache();
+
+	if ($_POST['mode'] == 'edit')
+		redirect('admin_bans.php', $lang_admin_bans['Ban edited redirect']);
+	else
+		redirect('admin_bans.php', $lang_admin_bans['Ban added redirect']);
+}
+
+// Remove a ban
+else if (isset($_GET['del_ban']))
+{
+	confirm_referrer('admin_bans.php');
+
+	$ban_id = intval($_GET['del_ban']);
+	if ($ban_id < 1)
+		message($lang_common['Bad request']);
+
+	$db->query('DELETE FROM '.$db->prefix.'bans WHERE id='.$ban_id) or error('Unable to delete ban', __FILE__, __LINE__, $db->error());
+
+	// Regenerate the bans cache
+	if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+		require PUN_ROOT.'include/cache.php';
+
+	generate_bans_cache();
+
+	redirect('admin_bans.php', $lang_admin_bans['Ban removed redirect']);
+}
+
+// Find bans
+else if (isset($_GET['find_ban']))
+{
+	$form = isset($_GET['form']) ? $_GET['form'] : array();
+
+	// trim() all elements in $form
+	$form = array_map('pun_trim', $form);
+	$conditions = $query_str = array();
+
+	$expire_after = isset($_GET['expire_after']) ? trim($_GET['expire_after']) : '';
+	$expire_before = isset($_GET['expire_before']) ? trim($_GET['expire_before']) : '';
+	$order_by = isset($_GET['order_by']) && in_array($_GET['order_by'], array('username', 'ip', 'email', 'expire')) ? $_GET['order_by'] : 'username';
+	$direction = isset($_GET['direction']) && $_GET['direction'] == 'DESC' ? 'DESC' : 'ASC';
+
+	$query_str[] = 'order_by='.$order_by;
+	$query_str[] = 'direction='.$direction;
+
+	// Try to convert date/time to timestamps
+	if ($expire_after != '')
+	{
+		$query_str[] = 'expire_after='.$expire_after;
+
+		$expire_after = strtotime($expire_after);
+		if ($expire_after === false || $expire_after == -1)
+			message($lang_admin_bans['Invalid date message']);
+
+		$conditions[] = 'expire>'.$expire_after;
+	}
+	if ($expire_before != '')
+	{
+		$query_str[] = 'expire_before='.$expire_before;
+
+		$expire_before = strtotime($expire_before);
+		if ($expire_before === false || $expire_before == -1)
+			message($lang_admin_bans['Invalid date message']);
+
+		$conditions[] = 'expire<'.$expire_before;
+	}
+
+	$like_command = ($db_type == 'pgsql') ? 'ILIKE' : 'LIKE';
+	foreach ($form as $key => $input)
+	{
+		if ($input != '' && in_array($key, array('username', 'ip', 'email', 'message')))
+		{
+			$conditions[] = 'b.'.$db->escape($key).' '.$like_command.' \''.$db->escape(str_replace('*', '%', $input)).'\'';
+			$query_str[] = 'form%5B'.$key.'%5D='.urlencode($input);
+		}
+	}
+
+	// Fetch ban count
+	$result = $db->query('SELECT COUNT(id) FROM '.$db->prefix.'bans as b WHERE b.id>0'.(!empty($conditions) ? ' AND '.implode(' AND ', $conditions) : '')) or error('Unable to fetch ban list', __FILE__, __LINE__, $db->error());
+	$num_bans = $db->result($result);
+
+	// Determine the ban offset (based on $_GET['p'])
+	$num_pages = ceil($num_bans / 50);
+
+	$p = (!isset($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : intval($_GET['p']);
+	$start_from = 50 * ($p - 1);
+
+	// Generate paging links
+	$paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'admin_bans.php?find_ban=&'.implode('&', $query_str));
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Bans'], $lang_admin_bans['Results head']);
+	define('PUN_ACTIVE_PAGE', 'admin');
+	require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+	<div class="inbox crumbsplus">
+		<ul class="crumbs">
+			<li><a href="admin_index.php"><?php echo $lang_admin_common['Admin'].' '.$lang_admin_common['Index'] ?></a></li>
+			<li><span>» </span><a href="admin_bans.php"><?php echo $lang_admin_common['Bans'] ?></a></li>
+			<li><span>» </span><strong><?php echo $lang_admin_bans['Results head'] ?></strong></li>
+		</ul>
+		<div class="pagepost">
+			<p class="pagelink"><?php echo $paging_links ?></p>
+		</div>
+		<div class="clearer"></div>
+	</div>
+</div>
+
+
+<div id="bans1" class="blocktable">
+	<h2><span><?php echo $lang_admin_bans['Results head'] ?></span></h2>
+	<div class="box">
+		<div class="inbox">
+			<table cellspacing="0">
+			<thead>
+				<tr>
+					<th class="tcl" scope="col"><?php echo $lang_admin_bans['Results username head'] ?></th>
+					<th class="tc2" scope="col"><?php echo $lang_admin_bans['Results e-mail head'] ?></th>
+					<th class="tc3" scope="col"><?php echo $lang_admin_bans['Results IP address head'] ?></th>
+					<th class="tc4" scope="col"><?php echo $lang_admin_bans['Results expire head'] ?></th>
+					<th class="tc5" scope="col"><?php echo $lang_admin_bans['Results message head'] ?></th>
+					<th class="tc6" scope="col"><?php echo $lang_admin_bans['Results banned by head'] ?></th>
+					<th class="tcr" scope="col"><?php echo $lang_admin_bans['Results actions head'] ?></th>
+				</tr>
+			</thead>
+			<tbody>
+<?php
+
+	$result = $db->query('SELECT b.id, b.username, b.ip, b.email, b.message, b.expire, b.ban_creator, u.username AS ban_creator_username FROM '.$db->prefix.'bans AS b LEFT JOIN '.$db->prefix.'users AS u ON b.ban_creator=u.id WHERE b.id>0'.(!empty($conditions) ? ' AND '.implode(' AND ', $conditions) : '').' ORDER BY '.$db->escape($order_by).' '.$db->escape($direction).' LIMIT '.$start_from.', 50') or error('Unable to fetch ban list', __FILE__, __LINE__, $db->error());
+	if ($db->num_rows($result))
+	{
+		while ($ban_data = $db->fetch_assoc($result))
+		{
+
+			$actions = '<a href="admin_bans.php?edit_ban='.$ban_data['id'].'">'.$lang_admin_common['Edit'].'</a> | <a href="admin_bans.php?del_ban='.$ban_data['id'].'">'.$lang_admin_common['Remove'].'</a>';
+			$expire = format_time($ban_data['expire'], true);
+
+?>
+				<tr>
+					<td class="tcl"><?php echo ($ban_data['username'] != '') ? pun_htmlspecialchars($ban_data['username']) : ' ' ?></td>
+					<td class="tc2"><?php echo ($ban_data['email'] != '') ? $ban_data['email'] : ' ' ?></td>
+					<td class="tc3"><?php echo ($ban_data['ip'] != '') ? $ban_data['ip'] : ' ' ?></td>
+					<td class="tc4"><?php echo $expire ?></td>
+					<td class="tc5"><?php echo ($ban_data['message'] != '') ? pun_htmlspecialchars($ban_data['message']) : ' ' ?></td>
+					<td class="tc6"><?php echo ($ban_data['ban_creator_username'] != '') ? '<a href="profile.php?id='.$ban_data['ban_creator'].'">'.pun_htmlspecialchars($ban_data['ban_creator_username']).'</a>' : $lang_admin_bans['Unknown'] ?></td>
+					<td class="tcr"><?php echo $actions ?></td>
+				</tr>
+<?php
+
+		}
+	}
+	else
+		echo "\t\t\t\t".'<tr><td class="tcl" colspan="7">'.$lang_admin_bans['No match'].'</td></tr>'."\n";
+
+?>
+			</tbody>
+			</table>
+		</div>
+	</div>
+</div>
+
+<div class="linksb">
+	<div class="inbox crumbsplus">
+		<div class="pagepost">
+			<p class="pagelink"><?php echo $paging_links ?></p>
+		</div>
+		<ul class="crumbs">
+			<li><a href="admin_index.php"><?php echo $lang_admin_common['Admin'].' '.$lang_admin_common['Index'] ?></a></li>
+			<li><span>» </span><a href="admin_bans.php"><?php echo $lang_admin_common['Bans'] ?></a></li>
+			<li><span>» </span><strong><?php echo $lang_admin_bans['Results head'] ?></strong></li>
+		</ul>
+		<div class="clearer"></div>
+	</div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Bans']);
+$focus_element = array('bans', 'new_ban_user');
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('bans');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_bans['New ban head'] ?></span></h2>
+		<div class="box">
+			<form id="bans" method="post" action="admin_bans.php?action=more">
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_bans['Add ban subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_bans['Username label'] ?><div><input type="submit" name="add_ban" value="<?php echo $lang_admin_common['Add'] ?>" tabindex="2" /></div></th>
+									<td>
+										<input type="text" name="new_ban_user" size="25" maxlength="25" tabindex="1" />
+										<span><?php echo $lang_admin_bans['Username advanced help'] ?></span>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+			</form>
+		</div>
+
+		<h2 class="block2"><span><?php echo $lang_admin_bans['Ban search head'] ?></span></h2>
+		<div class="box">
+			<form id="find_band" method="get" action="admin_bans.php">
+				<p class="submittop"><input type="submit" name="find_ban" value="<?php echo $lang_admin_bans['Submit search'] ?>" tabindex="3" /></p>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_bans['Ban search subhead'] ?></legend>
+						<div class="infldset">
+							<p><?php echo $lang_admin_bans['Ban search info'] ?></p>
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_bans['Username label'] ?></th>
+									<td><input type="text" name="form[username]" size="25" maxlength="25" tabindex="4" /></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_bans['IP label'] ?></th>
+									<td><input type="text" name="form[ip]" size="30" maxlength="255" tabindex="5" /></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_bans['E-mail label'] ?></th>
+									<td><input type="text" name="form[email]" size="30" maxlength="80" tabindex="6" /></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_bans['Message label'] ?></th>
+									<td><input type="text" name="form[message]" size="30" maxlength="255" tabindex="7" /></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_bans['Expire after label'] ?></th>
+									<td><input type="text" name="expire_after" size="10" maxlength="10" tabindex="8" />
+									<span><?php echo $lang_admin_bans['Date help'] ?></span></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_bans['Expire before label'] ?></th>
+									<td><input type="text" name="expire_before" size="10" maxlength="10" tabindex="9" />
+									<span><?php echo $lang_admin_bans['Date help'] ?></span></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_bans['Order by label'] ?></th>
+									<td>
+										<select name="order_by" tabindex="10">
+											<option value="username" selected="selected"><?php echo $lang_admin_bans['Order by username'] ?></option>
+											<option value="ip"><?php echo $lang_admin_bans['Order by ip'] ?></option>
+											<option value="email"><?php echo $lang_admin_bans['Order by e-mail'] ?></option>
+											<option value="expire"><?php echo $lang_admin_bans['Order by expire'] ?></option>
+										</select>   <select name="direction" tabindex="11">
+											<option value="ASC" selected="selected"><?php echo $lang_admin_bans['Ascending'] ?></option>
+											<option value="DESC"><?php echo $lang_admin_bans['Descending'] ?></option>
+										</select>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+				<p class="submitend"><input type="submit" name="find_ban" value="<?php echo $lang_admin_bans['Submit search'] ?>" tabindex="12" /></p>
+			</form>
+		</div>
+	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_categories.php b/admin_categories.php
new file mode 100644
index 0000000..7d4129e
--- /dev/null
+++ b/admin_categories.php
@@ -0,0 +1,267 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if ($pun_user['g_id'] != PUN_ADMIN)
+	message($lang_common['No permission']);
+
+// Load the admin_categories.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_categories.php';
+
+// Add a new category
+if (isset($_POST['add_cat']))
+{
+	confirm_referrer('admin_categories.php');
+
+	$new_cat_name = pun_trim($_POST['new_cat_name']);
+	if ($new_cat_name == '')
+		message($lang_admin_categories['Must enter name message']);
+
+	$db->query('INSERT INTO '.$db->prefix.'categories (cat_name) VALUES(\''.$db->escape($new_cat_name).'\')') or error('Unable to create category', __FILE__, __LINE__, $db->error());
+
+	redirect('admin_categories.php', $lang_admin_categories['Category added redirect']);
+}
+
+// Delete a category
+else if (isset($_POST['del_cat']) || isset($_POST['del_cat_comply']))
+{
+	confirm_referrer('admin_categories.php');
+
+	$cat_to_delete = intval($_POST['cat_to_delete']);
+	if ($cat_to_delete < 1)
+		message($lang_common['Bad request']);
+
+	if (isset($_POST['del_cat_comply'])) // Delete a category with all forums and posts
+	{
+		@set_time_limit(0);
+
+		$result = $db->query('SELECT id FROM '.$db->prefix.'forums WHERE cat_id='.$cat_to_delete) or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
+		$num_forums = $db->num_rows($result);
+
+		for ($i = 0; $i < $num_forums; ++$i)
+		{
+			$cur_forum = $db->result($result, $i);
+
+			// Prune all posts and topics
+			prune($cur_forum, 1, -1);
+
+			// Delete the forum
+			$db->query('DELETE FROM '.$db->prefix.'forums WHERE id='.$cur_forum) or error('Unable to delete forum', __FILE__, __LINE__, $db->error());
+		}
+
+		// Locate any "orphaned redirect topics" and delete them
+		$result = $db->query('SELECT t1.id FROM '.$db->prefix.'topics AS t1 LEFT JOIN '.$db->prefix.'topics AS t2 ON t1.moved_to=t2.id WHERE t2.id IS NULL AND t1.moved_to IS NOT NULL') or error('Unable to fetch redirect topics', __FILE__, __LINE__, $db->error());
+		$num_orphans = $db->num_rows($result);
+
+		if ($num_orphans)
+		{
+			for ($i = 0; $i < $num_orphans; ++$i)
+				$orphans[] = $db->result($result, $i);
+
+			$db->query('DELETE FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $orphans).')') or error('Unable to delete redirect topics', __FILE__, __LINE__, $db->error());
+		}
+
+		// Delete the category
+		$db->query('DELETE FROM '.$db->prefix.'categories WHERE id='.$cat_to_delete) or error('Unable to delete category', __FILE__, __LINE__, $db->error());
+
+		// Regenerate the quick jump cache
+		if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+			require PUN_ROOT.'include/cache.php';
+
+		generate_quickjump_cache();
+
+		redirect('admin_categories.php', $lang_admin_categories['Category deleted redirect']);
+	}
+	else // If the user hasn't comfirmed the delete
+	{
+		$result = $db->query('SELECT cat_name FROM '.$db->prefix.'categories WHERE id='.$cat_to_delete) or error('Unable to fetch category info', __FILE__, __LINE__, $db->error());
+		$cat_name = $db->result($result);
+
+		$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Categories']);
+		define('PUN_ACTIVE_PAGE', 'admin');
+		require PUN_ROOT.'header.php';
+
+		generate_admin_menu('categories');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_categories['Delete category head'] ?></span></h2>
+		<div class="box">
+			<form method="post" action="admin_categories.php">
+				<div class="inform">
+				<input type="hidden" name="cat_to_delete" value="<?php echo $cat_to_delete ?>" />
+					<fieldset>
+						<legend><?php echo $lang_admin_categories['Confirm delete subhead'] ?></legend>
+						<div class="infldset">
+							<p><?php printf($lang_admin_categories['Confirm delete info'], pun_htmlspecialchars($cat_name)) ?></p>
+							<p class="warntext"><?php echo $lang_admin_categories['Delete category warn'] ?></p>
+						</div>
+					</fieldset>
+				</div>
+				<p class="buttons"><input type="submit" name="del_cat_comply" value="<?php echo $lang_admin_common['Delete'] ?>" /><a href="javascript:history.go(-1)"><?php echo $lang_admin_common['Go back'] ?></a></p>
+			</form>
+		</div>
+	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+		require PUN_ROOT.'footer.php';
+	}
+}
+
+else if (isset($_POST['update'])) // Change position and name of the categories
+{
+	confirm_referrer('admin_categories.php');
+
+	$cat_order = array_map('trim', $_POST['cat_order']);
+	$cat_name = array_map('pun_trim', $_POST['cat_name']);
+
+	$result = $db->query('SELECT id, disp_position FROM '.$db->prefix.'categories ORDER BY disp_position') or error('Unable to fetch category list', __FILE__, __LINE__, $db->error());
+	$num_cats = $db->num_rows($result);
+
+	for ($i = 0; $i < $num_cats; ++$i)
+	{
+		if ($cat_name[$i] == '')
+			message($lang_admin_categories['Must enter name message']);
+
+		if ($cat_order[$i] == '' || preg_match('/[^0-9]/', $cat_order[$i]))
+			message($lang_admin_categories['Must enter integer message']);
+
+		list($cat_id, $position) = $db->fetch_row($result);
+
+		$db->query('UPDATE '.$db->prefix.'categories SET cat_name=\''.$db->escape($cat_name[$i]).'\', disp_position='.$cat_order[$i].' WHERE id='.$cat_id) or error('Unable to update category', __FILE__, __LINE__, $db->error());
+	}
+
+	// Regenerate the quick jump cache
+	if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+		require PUN_ROOT.'include/cache.php';
+
+	generate_quickjump_cache();
+
+	redirect('admin_categories.php', $lang_admin_categories['Categories updated redirect']);
+}
+
+// Generate an array with all categories
+$result = $db->query('SELECT id, cat_name, disp_position FROM '.$db->prefix.'categories ORDER BY disp_position') or error('Unable to fetch category list', __FILE__, __LINE__, $db->error());
+$num_cats = $db->num_rows($result);
+
+for ($i = 0; $i < $num_cats; ++$i)
+	$cat_list[] = $db->fetch_row($result);
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Categories']);
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('categories');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_categories['Add categories head'] ?></span></h2>
+		<div class="box">
+			<form method="post" action="admin_categories.php?action=foo">
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_categories['Add categories subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_categories['Add category label'] ?><div><input type="submit" name="add_cat" value="<?php echo $lang_admin_categories['Add new submit'] ?>" tabindex="2" /></div></th>
+									<td>
+										<input type="text" name="new_cat_name" size="35" maxlength="80" tabindex="1" />
+										<span><?php printf($lang_admin_categories['Add category help'], '<a href="admin_forums.php">'.$lang_admin_common['Forums'].'</a>') ?></span>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+			</form>
+		</div>
+
+<?php if ($num_cats): ?>		<h2 class="block2"><span><?php echo $lang_admin_categories['Delete categories head'] ?></span></h2>
+		<div class="box">
+			<form method="post" action="admin_categories.php?action=foo">
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_categories['Delete categories subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_categories['Delete category label'] ?><div><input type="submit" name="del_cat" value="<?php echo $lang_admin_common['Delete'] ?>" tabindex="4" /></div></th>
+									<td>
+										<select name="cat_to_delete" tabindex="3">
+<?php
+
+	foreach ($cat_list as $category)
+		echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$category[0].'">'.pun_htmlspecialchars($category[1]).'</option>'."\n";
+
+?>
+										</select>
+										<span><?php echo $lang_admin_categories['Delete category help'] ?></span>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+			</form>
+		</div>
+<?php endif; ?>
+
+<?php if ($num_cats): ?>		<h2 class="block2"><span><?php echo $lang_admin_categories['Edit categories head'] ?></span></h2>
+		<div class="box">
+			<form method="post" action="admin_categories.php?action=foo">
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_categories['Edit categories subhead'] ?></legend>
+						<div class="infldset">
+							<table id="categoryedit" cellspacing="0" >
+							<thead>
+								<tr>
+									<th class="tcl" scope="col"><?php echo $lang_admin_categories['Category name label'] ?></th>
+									<th scope="col"><?php echo $lang_admin_categories['Category position label'] ?></th>
+								</tr>
+							</thead>
+							<tbody>
+<?php
+
+	foreach ($cat_list as $i => $category)
+	{
+
+?>
+								<tr>
+									<td class="tcl"><input type="text" name="cat_name[<?php echo $i ?>]" value="<?php echo pun_htmlspecialchars($category[1]) ?>" size="35" maxlength="80" /></td>
+									<td><input type="text" name="cat_order[<?php echo $i ?>]" value="<?php echo $category[2] ?>" size="3" maxlength="3" /></td>
+								</tr>
+<?php
+
+	}
+
+?>
+							</tbody>
+							</table>
+							<div class="fsetsubmit"><input type="submit" name="update" value="<?php echo $lang_admin_common['Update'] ?>" /></div>
+						</div>
+					</fieldset>
+				</div>
+			</form>
+		</div>
+<?php endif; ?>	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_censoring.php b/admin_censoring.php
new file mode 100644
index 0000000..69282b1
--- /dev/null
+++ b/admin_censoring.php
@@ -0,0 +1,150 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if (!$pun_user['is_admmod'] || ($pun_user['g_moderator'] == '1' && $pun_config['o_censoring'] == '0'))
+	message($lang_common['No permission']);
+
+// Load the admin_censoring.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_censoring.php';
+
+// Add a censor word
+if (isset($_POST['add_word']))
+{
+	confirm_referrer('admin_censoring.php');
+
+	$search_for = pun_trim($_POST['new_search_for']);
+	$replace_with = pun_trim($_POST['new_replace_with']);
+
+	if ($search_for == '' || $replace_with == '')
+		message($lang_admin_censoring['Must enter both message']);
+
+	$db->query('INSERT INTO '.$db->prefix.'censoring (search_for, replace_with) VALUES (\''.$db->escape($search_for).'\', \''.$db->escape($replace_with).'\')') or error('Unable to add censor word', __FILE__, __LINE__, $db->error());
+
+	redirect('admin_censoring.php', $lang_admin_censoring['Word added redirect']);
+}
+
+// Update a censor word
+else if (isset($_POST['update']))
+{
+	confirm_referrer('admin_censoring.php');
+
+	$id = intval(key($_POST['update']));
+
+	$search_for = pun_trim($_POST['search_for'][$id]);
+	$replace_with = pun_trim($_POST['replace_with'][$id]);
+
+	if ($search_for == '' || $replace_with == '')
+		message($lang_admin_censoring['Must search both message']);
+
+	$db->query('UPDATE '.$db->prefix.'censoring SET search_for=\''.$db->escape($search_for).'\', replace_with=\''.$db->escape($replace_with).'\' WHERE id='.$id) or error('Unable to update censor word', __FILE__, __LINE__, $db->error());
+
+	redirect('admin_censoring.php', $lang_admin_censoring['Word updated redirect']);
+}
+
+// Remove a censor word
+else if (isset($_POST['remove']))
+{
+	confirm_referrer('admin_censoring.php');
+
+	$id = intval(key($_POST['remove']));
+
+	$db->query('DELETE FROM '.$db->prefix.'censoring WHERE id='.$id) or error('Unable to delete censor word', __FILE__, __LINE__, $db->error());
+
+	redirect('admin_censoring.php',  $lang_admin_censoring['Word removed redirect']);
+}
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Censoring']);
+$focus_element = array('censoring', 'new_search_for');
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('censoring');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_censoring['Censoring head'] ?></span></h2>
+		<div class="box">
+			<form id="censoring" method="post" action="admin_censoring.php?action=foo">
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_censoring['Add word subhead'] ?></legend>
+						<div class="infldset">
+							<p><?php echo $lang_admin_censoring['Add word info'].($pun_user['g_id'] != PUN_ADMIN ? '' : ' '.($pun_config['o_censoring'] == '1' ? sprintf($lang_admin_censoring['Censoring enabled'], '<a href="admin_options.php#censoring">'.$lang_admin_common['Options'].'</a>') : sprintf($lang_admin_censoring['Censoring disabled'], '<a href="admin_options.php#censoring">'.$lang_admin_common['Options'].'</a>'))) ?></p>
+							<table cellspacing="0">
+							<thead>
+								<tr>
+									<th class="tcl" scope="col"><?php echo $lang_admin_censoring['Censored word label'] ?></th>
+									<th class="tc2" scope="col"><?php echo $lang_admin_censoring['Replacement label'] ?></th>
+									<th class="hidehead" scope="col"><?php echo $lang_admin_censoring['Action label'] ?></th>
+								</tr>
+							</thead>
+							<tbody>
+								<tr>
+									<td class="tcl"><input type="text" name="new_search_for" size="24" maxlength="60" tabindex="1" /></td>
+									<td class="tc2"><input type="text" name="new_replace_with" size="24" maxlength="60" tabindex="2" /></td>
+									<td><input type="submit" name="add_word" value="<?php echo $lang_admin_common['Add'] ?>" tabindex="3" /></td>
+								</tr>
+							</tbody>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_censoring['Edit remove subhead'] ?></legend>
+						<div class="infldset">
+<?php
+
+$result = $db->query('SELECT id, search_for, replace_with FROM '.$db->prefix.'censoring ORDER BY id') or error('Unable to fetch censor word list', __FILE__, __LINE__, $db->error());
+if ($db->num_rows($result))
+{
+
+?>
+							<table cellspacing="0" >
+							<thead>
+								<tr>
+									<th class="tcl" scope="col"><?php echo $lang_admin_censoring['Censored word label'] ?></th>
+									<th class="tc2" scope="col"><?php echo $lang_admin_censoring['Replacement label'] ?></th>
+									<th class="hidehead" scope="col"><?php echo $lang_admin_censoring['Action label'] ?></th>
+								</tr>
+							</thead>
+							<tbody>
+<?php
+
+	while ($cur_word = $db->fetch_assoc($result))
+		echo "\t\t\t\t\t\t\t\t".'<tr><td class="tcl"><input type="text" name="search_for['.$cur_word['id'].']" value="'.pun_htmlspecialchars($cur_word['search_for']).'" size="24" maxlength="60" /></td><td class="tc2"><input type="text" name="replace_with['.$cur_word['id'].']" value="'.pun_htmlspecialchars($cur_word['replace_with']).'" size="24" maxlength="60" /></td><td><input type="submit" name="update['.$cur_word['id'].']" value="'.$lang_admin_common['Update'].'" /> <input type="submit" [...]
+
+?>
+							</tbody>
+							</table>
+<?php
+
+}
+else
+	echo "\t\t\t\t\t\t\t".'<p>'.$lang_admin_censoring['No words in list'].'</p>'."\n";
+
+?>
+						</div>
+					</fieldset>
+				</div>
+			</form>
+		</div>
+	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_forums.php b/admin_forums.php
new file mode 100644
index 0000000..1d1c476
--- /dev/null
+++ b/admin_forums.php
@@ -0,0 +1,473 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if ($pun_user['g_id'] != PUN_ADMIN)
+	message($lang_common['No permission']);
+
+// Load the admin_forums.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_forums.php';
+
+// Add a "default" forum
+if (isset($_POST['add_forum']))
+{
+	confirm_referrer('admin_forums.php');
+
+	$add_to_cat = intval($_POST['add_to_cat']);
+	if ($add_to_cat < 1)
+		message($lang_common['Bad request']);
+
+	$db->query('INSERT INTO '.$db->prefix.'forums (forum_name, cat_id) VALUES(\''.$db->escape($lang_admin_forums['New forum']).'\', '.$add_to_cat.')') or error('Unable to create forum', __FILE__, __LINE__, $db->error());
+
+	// Regenerate the quick jump cache
+	if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+		require PUN_ROOT.'include/cache.php';
+
+	generate_quickjump_cache();
+
+	redirect('admin_forums.php', $lang_admin_forums['Forum added redirect']);
+}
+
+// Delete a forum
+else if (isset($_GET['del_forum']))
+{
+	confirm_referrer('admin_forums.php');
+
+	$forum_id = intval($_GET['del_forum']);
+	if ($forum_id < 1)
+		message($lang_common['Bad request']);
+
+	if (isset($_POST['del_forum_comply'])) // Delete a forum with all posts
+	{
+		@set_time_limit(0);
+
+		// Prune all posts and topics
+		prune($forum_id, 1, -1);
+
+		// Locate any "orphaned redirect topics" and delete them
+		$result = $db->query('SELECT t1.id FROM '.$db->prefix.'topics AS t1 LEFT JOIN '.$db->prefix.'topics AS t2 ON t1.moved_to=t2.id WHERE t2.id IS NULL AND t1.moved_to IS NOT NULL') or error('Unable to fetch redirect topics', __FILE__, __LINE__, $db->error());
+		$num_orphans = $db->num_rows($result);
+
+		if ($num_orphans)
+		{
+			for ($i = 0; $i < $num_orphans; ++$i)
+				$orphans[] = $db->result($result, $i);
+
+			$db->query('DELETE FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $orphans).')') or error('Unable to delete redirect topics', __FILE__, __LINE__, $db->error());
+		}
+
+		// Delete the forum and any forum specific group permissions
+		$db->query('DELETE FROM '.$db->prefix.'forums WHERE id='.$forum_id) or error('Unable to delete forum', __FILE__, __LINE__, $db->error());
+		$db->query('DELETE FROM '.$db->prefix.'forum_perms WHERE forum_id='.$forum_id) or error('Unable to delete group forum permissions', __FILE__, __LINE__, $db->error());
+
+		// Regenerate the quick jump cache
+		if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+			require PUN_ROOT.'include/cache.php';
+
+		generate_quickjump_cache();
+
+		redirect('admin_forums.php', $lang_admin_forums['Forum deleted redirect']);
+	}
+	else // If the user hasn't confirmed the delete
+	{
+		$result = $db->query('SELECT forum_name FROM '.$db->prefix.'forums WHERE id='.$forum_id) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
+		$forum_name = pun_htmlspecialchars($db->result($result));
+
+		$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Forums']);
+		define('PUN_ACTIVE_PAGE', 'admin');
+		require PUN_ROOT.'header.php';
+
+		generate_admin_menu('forums');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_forums['Confirm delete head'] ?></span></h2>
+		<div class="box">
+			<form method="post" action="admin_forums.php?del_forum=<?php echo $forum_id ?>">
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_forums['Confirm delete subhead'] ?></legend>
+						<div class="infldset">
+							<p><?php printf($lang_admin_forums['Confirm delete info'], $forum_name) ?></p>
+							<p class="warntext"><?php echo $lang_admin_forums['Confirm delete warn'] ?></p>
+						</div>
+					</fieldset>
+				</div>
+				<p class="buttons"><input type="submit" name="del_forum_comply" value="<?php echo $lang_admin_common['Delete'] ?>" /><a href="javascript:history.go(-1)"><?php echo $lang_admin_common['Go back'] ?></a></p>
+			</form>
+		</div>
+	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+		require PUN_ROOT.'footer.php';
+	}
+}
+
+// Update forum positions
+else if (isset($_POST['update_positions']))
+{
+	confirm_referrer('admin_forums.php');
+
+	foreach ($_POST['position'] as $forum_id => $disp_position)
+	{
+		$disp_position = trim($disp_position);
+		if ($disp_position == '' || preg_match('/[^0-9]/', $disp_position))
+			message($lang_admin_forums['Must be integer message']);
+
+		$db->query('UPDATE '.$db->prefix.'forums SET disp_position='.$disp_position.' WHERE id='.intval($forum_id)) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
+	}
+
+	// Regenerate the quick jump cache
+	if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+		require PUN_ROOT.'include/cache.php';
+
+	generate_quickjump_cache();
+
+	redirect('admin_forums.php', $lang_admin_forums['Forums updated redirect']);
+}
+
+else if (isset($_GET['edit_forum']))
+{
+	$forum_id = intval($_GET['edit_forum']);
+	if ($forum_id < 1)
+		message($lang_common['Bad request']);
+
+	// Update group permissions for $forum_id
+	if (isset($_POST['save']))
+	{
+		confirm_referrer('admin_forums.php');
+
+		// Start with the forum details
+		$forum_name = pun_trim($_POST['forum_name']);
+		$forum_desc = pun_linebreaks(pun_trim($_POST['forum_desc']));
+		$cat_id = intval($_POST['cat_id']);
+		$sort_by = intval($_POST['sort_by']);
+		$redirect_url = isset($_POST['redirect_url']) ? trim($_POST['redirect_url']) : null;
+
+		if ($forum_name == '')
+			message($lang_admin_forums['Must enter name message']);
+
+		if ($cat_id < 1)
+			message($lang_common['Bad request']);
+
+		$forum_desc = ($forum_desc != '') ? '\''.$db->escape($forum_desc).'\'' : 'NULL';
+		$redirect_url = ($redirect_url != '') ? '\''.$db->escape($redirect_url).'\'' : 'NULL';
+
+		$db->query('UPDATE '.$db->prefix.'forums SET forum_name=\''.$db->escape($forum_name).'\', forum_desc='.$forum_desc.', redirect_url='.$redirect_url.', sort_by='.$sort_by.', cat_id='.$cat_id.' WHERE id='.$forum_id) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
+
+		// Now let's deal with the permissions
+		if (isset($_POST['read_forum_old']))
+		{
+			$result = $db->query('SELECT g_id, g_read_board, g_post_replies, g_post_topics FROM '.$db->prefix.'groups WHERE g_id!='.PUN_ADMIN) or error('Unable to fetch user group list', __FILE__, __LINE__, $db->error());
+			while ($cur_group = $db->fetch_assoc($result))
+			{
+				$read_forum_new = ($cur_group['g_read_board'] == '1') ? isset($_POST['read_forum_new'][$cur_group['g_id']]) ? '1' : '0' : intval($_POST['read_forum_old'][$cur_group['g_id']]);
+				$post_replies_new = isset($_POST['post_replies_new'][$cur_group['g_id']]) ? '1' : '0';
+				$post_topics_new = isset($_POST['post_topics_new'][$cur_group['g_id']]) ? '1' : '0';
+
+				// Check if the new settings differ from the old
+				if ($read_forum_new != $_POST['read_forum_old'][$cur_group['g_id']] || $post_replies_new != $_POST['post_replies_old'][$cur_group['g_id']] || $post_topics_new != $_POST['post_topics_old'][$cur_group['g_id']])
+				{
+					// If the new settings are identical to the default settings for this group, delete it's row in forum_perms
+					if ($read_forum_new == '1' && $post_replies_new == $cur_group['g_post_replies'] && $post_topics_new == $cur_group['g_post_topics'])
+						$db->query('DELETE FROM '.$db->prefix.'forum_perms WHERE group_id='.$cur_group['g_id'].' AND forum_id='.$forum_id) or error('Unable to delete group forum permissions', __FILE__, __LINE__, $db->error());
+					else
+					{
+						// Run an UPDATE and see if it affected a row, if not, INSERT
+						$db->query('UPDATE '.$db->prefix.'forum_perms SET read_forum='.$read_forum_new.', post_replies='.$post_replies_new.', post_topics='.$post_topics_new.' WHERE group_id='.$cur_group['g_id'].' AND forum_id='.$forum_id) or error('Unable to insert group forum permissions', __FILE__, __LINE__, $db->error());
+						if (!$db->affected_rows())
+							$db->query('INSERT INTO '.$db->prefix.'forum_perms (group_id, forum_id, read_forum, post_replies, post_topics) VALUES('.$cur_group['g_id'].', '.$forum_id.', '.$read_forum_new.', '.$post_replies_new.', '.$post_topics_new.')') or error('Unable to insert group forum permissions', __FILE__, __LINE__, $db->error());
+					}
+				}
+			}
+		}
+
+		// Regenerate the quick jump cache
+		if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+			require PUN_ROOT.'include/cache.php';
+
+		generate_quickjump_cache();
+
+		redirect('admin_forums.php', $lang_admin_forums['Forum updated redirect']);
+	}
+	else if (isset($_POST['revert_perms']))
+	{
+		confirm_referrer('admin_forums.php');
+
+		$db->query('DELETE FROM '.$db->prefix.'forum_perms WHERE forum_id='.$forum_id) or error('Unable to delete group forum permissions', __FILE__, __LINE__, $db->error());
+
+		// Regenerate the quick jump cache
+		if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+			require PUN_ROOT.'include/cache.php';
+
+		generate_quickjump_cache();
+
+		redirect('admin_forums.php?edit_forum='.$forum_id, $lang_admin_forums['Perms reverted redirect']);
+	}
+
+	// Fetch forum info
+	$result = $db->query('SELECT id, forum_name, forum_desc, redirect_url, num_topics, sort_by, cat_id FROM '.$db->prefix.'forums WHERE id='.$forum_id) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
+	if (!$db->num_rows($result))
+		message($lang_common['Bad request']);
+
+	$cur_forum = $db->fetch_assoc($result);
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Forums']);
+	define('PUN_ACTIVE_PAGE', 'admin');
+	require PUN_ROOT.'header.php';
+
+	generate_admin_menu('forums');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_forums['Edit forum head'] ?></span></h2>
+		<div class="box">
+			<form id="edit_forum" method="post" action="admin_forums.php?edit_forum=<?php echo $forum_id ?>">
+				<p class="submittop"><input type="submit" name="save" value="<?php echo $lang_admin_common['Save changes'] ?>" tabindex="6" /></p>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_forums['Edit details subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_forums['Forum name label'] ?></th>
+									<td><input type="text" name="forum_name" size="35" maxlength="80" value="<?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?>" tabindex="1" /></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_forums['Forum description label'] ?></th>
+									<td><textarea name="forum_desc" rows="3" cols="50" tabindex="2"><?php echo pun_htmlspecialchars($cur_forum['forum_desc']) ?></textarea></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_forums['Category label'] ?></th>
+									<td>
+										<select name="cat_id" tabindex="3">
+<?php
+
+	$result = $db->query('SELECT id, cat_name FROM '.$db->prefix.'categories ORDER BY disp_position') or error('Unable to fetch category list', __FILE__, __LINE__, $db->error());
+	while ($cur_cat = $db->fetch_assoc($result))
+	{
+		$selected = ($cur_cat['id'] == $cur_forum['cat_id']) ? ' selected="selected"' : '';
+		echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_cat['id'].'"'.$selected.'>'.pun_htmlspecialchars($cur_cat['cat_name']).'</option>'."\n";
+	}
+
+?>
+										</select>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_forums['Sort by label'] ?></th>
+									<td>
+										<select name="sort_by" tabindex="4">
+											<option value="0"<?php if ($cur_forum['sort_by'] == '0') echo ' selected="selected"' ?>><?php echo $lang_admin_forums['Last post'] ?></option>
+											<option value="1"<?php if ($cur_forum['sort_by'] == '1') echo ' selected="selected"' ?>><?php echo $lang_admin_forums['Topic start'] ?></option>
+										</select>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_forums['Redirect label'] ?></th>
+									<td><?php echo ($cur_forum['num_topics']) ? $lang_admin_forums['Redirect help'] : '<input type="text" name="redirect_url" size="45" maxlength="100" value="'.pun_htmlspecialchars($cur_forum['redirect_url']).'" tabindex="5" />'; ?></td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_forums['Group permissions subhead'] ?></legend>
+						<div class="infldset">
+							<p><?php printf($lang_admin_forums['Group permissions info'], '<a href="admin_groups.php">'.$lang_admin_common['User groups'].'</a>') ?></p>
+							<table id="forumperms" cellspacing="0">
+							<thead>
+								<tr>
+									<th class="atcl"> </th>
+									<th><?php echo $lang_admin_forums['Read forum label'] ?></th>
+									<th><?php echo $lang_admin_forums['Post replies label'] ?></th>
+									<th><?php echo $lang_admin_forums['Post topics label'] ?></th>
+								</tr>
+							</thead>
+							<tbody>
+<?php
+
+	$result = $db->query('SELECT g.g_id, g.g_title, g.g_read_board, g.g_post_replies, g.g_post_topics, fp.read_forum, fp.post_replies, fp.post_topics FROM '.$db->prefix.'groups AS g LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (g.g_id=fp.group_id AND fp.forum_id='.$forum_id.') WHERE g.g_id!='.PUN_ADMIN.' ORDER BY g.g_id') or error('Unable to fetch group forum permission list', __FILE__, __LINE__, $db->error());
+
+	while ($cur_perm = $db->fetch_assoc($result))
+	{
+		$read_forum = ($cur_perm['read_forum'] != '0') ? true : false;
+		$post_replies = (($cur_perm['g_post_replies'] == '0' && $cur_perm['post_replies'] == '1') || ($cur_perm['g_post_replies'] == '1' && $cur_perm['post_replies'] != '0')) ? true : false;
+		$post_topics = (($cur_perm['g_post_topics'] == '0' && $cur_perm['post_topics'] == '1') || ($cur_perm['g_post_topics'] == '1' && $cur_perm['post_topics'] != '0')) ? true : false;
+
+		// Determine if the current settings differ from the default or not
+		$read_forum_def = ($cur_perm['read_forum'] == '0') ? false : true;
+		$post_replies_def = (($post_replies && $cur_perm['g_post_replies'] == '0') || (!$post_replies && ($cur_perm['g_post_replies'] == '' || $cur_perm['g_post_replies'] == '1'))) ? false : true;
+		$post_topics_def = (($post_topics && $cur_perm['g_post_topics'] == '0') || (!$post_topics && ($cur_perm['g_post_topics'] == '' || $cur_perm['g_post_topics'] == '1'))) ? false : true;
+
+?>
+								<tr>
+									<th class="atcl"><?php echo pun_htmlspecialchars($cur_perm['g_title']) ?></th>
+									<td<?php if (!$read_forum_def) echo ' class="nodefault"'; ?>>
+										<input type="hidden" name="read_forum_old[<?php echo $cur_perm['g_id'] ?>]" value="<?php echo ($read_forum) ? '1' : '0'; ?>" />
+										<input type="checkbox" name="read_forum_new[<?php echo $cur_perm['g_id'] ?>]" value="1"<?php echo ($read_forum) ? ' checked="checked"' : ''; ?><?php echo ($cur_perm['g_read_board'] == '0') ? ' disabled="disabled"' : ''; ?> />
+									</td>
+									<td<?php if (!$post_replies_def && $cur_forum['redirect_url'] == '') echo ' class="nodefault"'; ?>>
+										<input type="hidden" name="post_replies_old[<?php echo $cur_perm['g_id'] ?>]" value="<?php echo ($post_replies) ? '1' : '0'; ?>" />
+										<input type="checkbox" name="post_replies_new[<?php echo $cur_perm['g_id'] ?>]" value="1"<?php echo ($post_replies) ? ' checked="checked"' : ''; ?><?php echo ($cur_forum['redirect_url'] != '') ? ' disabled="disabled"' : ''; ?> />
+									</td>
+									<td<?php if (!$post_topics_def && $cur_forum['redirect_url'] == '') echo ' class="nodefault"'; ?>>
+										<input type="hidden" name="post_topics_old[<?php echo $cur_perm['g_id'] ?>]" value="<?php echo ($post_topics) ? '1' : '0'; ?>" />
+										<input type="checkbox" name="post_topics_new[<?php echo $cur_perm['g_id'] ?>]" value="1"<?php echo ($post_topics) ? ' checked="checked"' : ''; ?><?php echo ($cur_forum['redirect_url'] != '') ? ' disabled="disabled"' : ''; ?> />
+									</td>
+								</tr>
+<?php
+
+	}
+
+?>
+							</tbody>
+							</table>
+							<div class="fsetsubmit"><input type="submit" name="revert_perms" value="<?php echo $lang_admin_forums['Revert to default'] ?>" /></div>
+						</div>
+					</fieldset>
+				</div>
+				<p class="submitend"><input type="submit" name="save" value="<?php echo $lang_admin_common['Save changes'] ?>" /></p>
+			</form>
+		</div>
+	</div>
+	<div class="clearer"></div>
+</div>
+
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Forums']);
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('forums');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_forums['Add forum head'] ?></span></h2>
+		<div class="box">
+			<form method="post" action="admin_forums.php?action=adddel">
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_forums['Create new subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_forums['Add forum label'] ?><div><input type="submit" name="add_forum" value="<?php echo $lang_admin_forums['Add forum'] ?>" tabindex="2" /></div></th>
+									<td>
+										<select name="add_to_cat" tabindex="1">
+<?php
+
+	$result = $db->query('SELECT id, cat_name FROM '.$db->prefix.'categories ORDER BY disp_position') or error('Unable to fetch category list', __FILE__, __LINE__, $db->error());
+	if ($db->num_rows($result) > 0)
+	{
+		while ($cur_cat = $db->fetch_assoc($result))
+			echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_cat['id'].'">'.pun_htmlspecialchars($cur_cat['cat_name']).'</option>'."\n";
+	}
+	else
+		echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="0" disabled="disabled">'.$lang_admin_forums['No categories exist'].'</option>'."\n";
+
+?>
+										</select>
+										<span><?php echo $lang_admin_forums['Add forum help'] ?></span>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+			</form>
+		</div>
+<?php
+
+// Display all the categories and forums
+$result = $db->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name, f.disp_position FROM '.$db->prefix.'categories AS c INNER JOIN '.$db->prefix.'forums AS f ON c.id=f.cat_id ORDER BY c.disp_position, c.id, f.disp_position') or error('Unable to fetch category/forum list', __FILE__, __LINE__, $db->error());
+
+if ($db->num_rows($result) > 0)
+{
+
+?>
+		<h2 class="block2"><span><?php echo $lang_admin_forums['Edit forums head'] ?></span></h2>
+		<div class="box">
+			<form id="edforum" method="post" action="admin_forums.php?action=edit">
+				<p class="submittop"><input type="submit" name="update_positions" value="<?php echo $lang_admin_forums['Update positions'] ?>" tabindex="3" /></p>
+<?php
+
+$tabindex_count = 4;
+
+$cur_category = 0;
+while ($cur_forum = $db->fetch_assoc($result))
+{
+	if ($cur_forum['cid'] != $cur_category) // A new category since last iteration?
+	{
+		if ($cur_category != 0)
+			echo "\t\t\t\t\t\t\t".'</tbody>'."\n\t\t\t\t\t\t\t".'</table>'."\n\t\t\t\t\t\t".'</div>'."\n\t\t\t\t\t".'</fieldset>'."\n\t\t\t\t".'</div>'."\n";
+
+?>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_forums['Category subhead'] ?> <?php echo pun_htmlspecialchars($cur_forum['cat_name']) ?></legend>
+						<div class="infldset">
+							<table cellspacing="0">
+							<thead>
+								<tr>
+									<th class="tcl"><?php echo $lang_admin_common['Action'] ?></th>
+									<th class="tc2"><?php echo $lang_admin_forums['Position label'] ?></th>
+									<th class="tcr"><?php echo $lang_admin_forums['Forum label'] ?></th>
+								</tr>
+							</thead>
+							<tbody>
+<?php
+
+		$cur_category = $cur_forum['cid'];
+	}
+
+?>
+								<tr>
+									<td class="tcl"><a href="admin_forums.php?edit_forum=<?php echo $cur_forum['fid'] ?>"><?php echo $lang_admin_forums['Edit link'] ?></a> | <a href="admin_forums.php?del_forum=<?php echo $cur_forum['fid'] ?>"><?php echo $lang_admin_forums['Delete link'] ?></a></td>
+									<td class="tc2"><input type="text" name="position[<?php echo $cur_forum['fid'] ?>]" size="3" maxlength="3" value="<?php echo $cur_forum['disp_position'] ?>" tabindex="<?php echo $tabindex_count ?>" /></td>
+									<td class="tcr"><strong><?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></strong></td>
+								</tr>
+<?php
+
+	$tabindex_count += 2;
+}
+
+?>
+							</tbody>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+				<p class="submitend"><input type="submit" name="update_positions" value="<?php echo $lang_admin_forums['Update positions'] ?>" tabindex="<?php echo $tabindex_count ?>" /></p>
+			</form>
+		</div>
+<?php
+
+}
+
+?>
+	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_groups.php b/admin_groups.php
new file mode 100644
index 0000000..aba8e82
--- /dev/null
+++ b/admin_groups.php
@@ -0,0 +1,565 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if ($pun_user['g_id'] != PUN_ADMIN)
+	message($lang_common['No permission']);
+
+// Load the admin_censoring.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_groups.php';
+
+// Add/edit a group (stage 1)
+if (isset($_POST['add_group']) || isset($_GET['edit_group']))
+{
+	if (isset($_POST['add_group']))
+	{
+		$base_group = intval($_POST['base_group']);
+
+		$result = $db->query('SELECT * FROM '.$db->prefix.'groups WHERE g_id='.$base_group) or error('Unable to fetch user group info', __FILE__, __LINE__, $db->error());
+		$group = $db->fetch_assoc($result);
+
+		$mode = 'add';
+	}
+	else // We are editing a group
+	{
+		$group_id = intval($_GET['edit_group']);
+		if ($group_id < 1)
+			message($lang_common['Bad request']);
+
+		$result = $db->query('SELECT * FROM '.$db->prefix.'groups WHERE g_id='.$group_id) or error('Unable to fetch user group info', __FILE__, __LINE__, $db->error());
+		if (!$db->num_rows($result))
+			message($lang_common['Bad request']);
+
+		$group = $db->fetch_assoc($result);
+
+		$mode = 'edit';
+	}
+
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['User groups']);
+	$required_fields = array('req_title' => $lang_admin_groups['Group title label']);
+	$focus_element = array('groups2', 'req_title');
+	define('PUN_ACTIVE_PAGE', 'admin');
+	require PUN_ROOT.'header.php';
+
+	generate_admin_menu('groups');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_groups['Group settings head'] ?></span></h2>
+		<div class="box">
+			<form id="groups2" method="post" action="admin_groups.php" onsubmit="return process_form(this)">
+				<p class="submittop"><input type="submit" name="add_edit_group" value="<?php echo $lang_admin_common['Save'] ?>" /></p>
+				<div class="inform">
+					<input type="hidden" name="mode" value="<?php echo $mode ?>" />
+<?php if ($mode == 'edit'): ?>					<input type="hidden" name="group_id" value="<?php echo $group_id ?>" />
+<?php endif; ?><?php if ($mode == 'add'): ?>					<input type="hidden" name="base_group" value="<?php echo $base_group ?>" />
+<?php endif; ?>					<fieldset>
+						<legend><?php echo $lang_admin_groups['Group settings subhead'] ?></legend>
+						<div class="infldset">
+							<p><?php echo $lang_admin_groups['Group settings info'] ?></p>
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['Group title label'] ?></th>
+									<td>
+										<input type="text" name="req_title" size="25" maxlength="50" value="<?php if ($mode == 'edit') echo pun_htmlspecialchars($group['g_title']); ?>" tabindex="1" />
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['User title label'] ?></th>
+									<td>
+										<input type="text" name="user_title" size="25" maxlength="50" value="<?php echo pun_htmlspecialchars($group['g_user_title']) ?>" tabindex="2" />
+										<span><?php echo $lang_admin_groups['User title help'] ?></span>
+									</td>
+								</tr>
+<?php if ($group['g_id'] != PUN_ADMIN): if ($group['g_id'] != PUN_GUEST): if ($mode != 'edit' || $pun_config['o_default_user_group'] != $group['g_id']): ?>								<tr>
+									<th scope="row"> <?php echo $lang_admin_groups['Mod privileges label'] ?></th>
+									<td>
+										<input type="radio" name="moderator" value="1"<?php if ($group['g_moderator'] == '1') echo ' checked="checked"' ?> tabindex="3" /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="moderator" value="0"<?php if ($group['g_moderator'] == '0') echo ' checked="checked"' ?> tabindex="4" /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_groups['Mod privileges help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['Edit profile label'] ?></th>
+									<td>
+										<input type="radio" name="mod_edit_users" value="1"<?php if ($group['g_mod_edit_users'] == '1') echo ' checked="checked"' ?> tabindex="5" /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="mod_edit_users" value="0"<?php if ($group['g_mod_edit_users'] == '0') echo ' checked="checked"' ?> tabindex="6" /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_groups['Edit profile help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['Rename users label'] ?></th>
+									<td>
+										<input type="radio" name="mod_rename_users" value="1"<?php if ($group['g_mod_rename_users'] == '1') echo ' checked="checked"' ?> tabindex="5" /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="mod_rename_users" value="0"<?php if ($group['g_mod_rename_users'] == '0') echo ' checked="checked"' ?> tabindex="6" /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_groups['Rename users help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['Change passwords label'] ?></th>
+									<td>
+										<input type="radio" name="mod_change_passwords" value="1"<?php if ($group['g_mod_change_passwords'] == '1') echo ' checked="checked"' ?> tabindex="5" /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="mod_change_passwords" value="0"<?php if ($group['g_mod_change_passwords'] == '0') echo ' checked="checked"' ?> tabindex="6" /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_groups['Change passwords help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['Ban users label'] ?></th>
+									<td>
+										<input type="radio" name="mod_ban_users" value="1"<?php if ($group['g_mod_ban_users'] == '1') echo ' checked="checked"' ?> tabindex="5" /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="mod_ban_users" value="0"<?php if ($group['g_mod_ban_users'] == '0') echo ' checked="checked"' ?> tabindex="6" /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_groups['Ban users help'] ?></span>
+									</td>
+								</tr>
+<?php endif; endif; ?>								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['Read board label'] ?></th>
+									<td>
+										<input type="radio" name="read_board" value="1"<?php if ($group['g_read_board'] == '1') echo ' checked="checked"' ?> tabindex="3" /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="read_board" value="0"<?php if ($group['g_read_board'] == '0') echo ' checked="checked"' ?> tabindex="4" /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_groups['Read board help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['View user info label'] ?></th>
+									<td>
+										<input type="radio" name="view_users" value="1"<?php if ($group['g_view_users'] == '1') echo ' checked="checked"' ?> tabindex="3" /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="view_users" value="0"<?php if ($group['g_view_users'] == '0') echo ' checked="checked"' ?> tabindex="4" /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_groups['View user info help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['Post replies label'] ?></th>
+									<td>
+										<input type="radio" name="post_replies" value="1"<?php if ($group['g_post_replies'] == '1') echo ' checked="checked"' ?> tabindex="5" /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="post_replies" value="0"<?php if ($group['g_post_replies'] == '0') echo ' checked="checked"' ?> tabindex="6" /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_groups['Post replies help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['Post topics label'] ?></th>
+									<td>
+										<input type="radio" name="post_topics" value="1"<?php if ($group['g_post_topics'] == '1') echo ' checked="checked"' ?> tabindex="7" /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="post_topics" value="0"<?php if ($group['g_post_topics'] == '0') echo ' checked="checked"' ?> tabindex="8" /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_groups['Post topics help'] ?></span>
+									</td>
+								</tr>
+<?php if ($group['g_id'] != PUN_GUEST): ?>								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['Edit posts label'] ?></th>
+									<td>
+										<input type="radio" name="edit_posts" value="1"<?php if ($group['g_edit_posts'] == '1') echo ' checked="checked"' ?> tabindex="11" /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="edit_posts" value="0"<?php if ($group['g_edit_posts'] == '0') echo ' checked="checked"' ?> tabindex="12" /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_groups['Edit posts help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['Delete posts label'] ?></th>
+									<td>
+										<input type="radio" name="delete_posts" value="1"<?php if ($group['g_delete_posts'] == '1') echo ' checked="checked"' ?> tabindex="13" /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="delete_posts" value="0"<?php if ($group['g_delete_posts'] == '0') echo ' checked="checked"' ?> tabindex="14" /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_groups['Delete posts help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['Delete topics label'] ?></th>
+									<td>
+										<input type="radio" name="delete_topics" value="1"<?php if ($group['g_delete_topics'] == '1') echo ' checked="checked"' ?> tabindex="15" /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="delete_topics" value="0"<?php if ($group['g_delete_topics'] == '0') echo ' checked="checked"' ?> tabindex="16" /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_groups['Delete topics help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['Set own title label'] ?></th>
+									<td>
+										<input type="radio" name="set_title" value="1"<?php if ($group['g_set_title'] == '1') echo ' checked="checked"' ?> tabindex="17" /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="set_title" value="0"<?php if ($group['g_set_title'] == '0') echo ' checked="checked"' ?> tabindex="18" /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_groups['Set own title help'] ?></span>
+									</td>
+								</tr>
+<?php endif; ?>								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['User search label'] ?></th>
+									<td>
+										<input type="radio" name="search" value="1"<?php if ($group['g_search'] == '1') echo ' checked="checked"' ?> tabindex="19" /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="search" value="0"<?php if ($group['g_search'] == '0') echo ' checked="checked"' ?> tabindex="20" /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_groups['User search help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['User list search label'] ?></th>
+									<td>
+										<input type="radio" name="search_users" value="1"<?php if ($group['g_search_users'] == '1') echo ' checked="checked"' ?> tabindex="21" /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="search_users" value="0"<?php if ($group['g_search_users'] == '0') echo ' checked="checked"' ?> tabindex="22" /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_groups['User list search help'] ?></span>
+									</td>
+								</tr>
+<?php if ($group['g_id'] != PUN_GUEST): ?>								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['Send e-mails label'] ?></th>
+									<td>
+										<input type="radio" name="send_email" value="1"<?php if ($group['g_send_email'] == '1') echo ' checked="checked"' ?> tabindex="21" /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="send_email" value="0"<?php if ($group['g_send_email'] == '0') echo ' checked="checked"' ?> tabindex="22" /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_groups['Send e-mails help'] ?></span>
+									</td>
+								</tr>
+<?php endif; ?>								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['Post flood label'] ?></th>
+									<td>
+										<input type="text" name="post_flood" size="5" maxlength="4" value="<?php echo $group['g_post_flood'] ?>" tabindex="24" />
+										<span><?php echo $lang_admin_groups['Post flood help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['Search flood label'] ?></th>
+									<td>
+										<input type="text" name="search_flood" size="5" maxlength="4" value="<?php echo $group['g_search_flood'] ?>" tabindex="25" />
+										<span><?php echo $lang_admin_groups['Search flood help'] ?></span>
+									</td>
+								</tr>
+<?php if ($group['g_id'] != PUN_GUEST): ?>								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['E-mail flood label'] ?></th>
+									<td>
+										<input type="text" name="email_flood" size="5" maxlength="4" value="<?php echo $group['g_email_flood'] ?>" tabindex="26" />
+										<span><?php echo $lang_admin_groups['E-mail flood help'] ?></span>
+									</td>
+								</tr>
+<?php endif; endif; ?>							</table>
+<?php if ($group['g_moderator'] == '1' ): ?>							<p class="warntext"><?php echo $lang_admin_groups['Moderator info'] ?></p>
+<?php endif; ?>						</div>
+					</fieldset>
+				</div>
+				<p class="submitend"><input type="submit" name="add_edit_group" value="<?php echo $lang_admin_common['Save'] ?>" tabindex="26" /></p>
+			</form>
+		</div>
+	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+
+// Add/edit a group (stage 2)
+else if (isset($_POST['add_edit_group']))
+{
+	confirm_referrer('admin_groups.php');
+
+	// Is this the admin group? (special rules apply)
+	$is_admin_group = (isset($_POST['group_id']) && $_POST['group_id'] == PUN_ADMIN) ? true : false;
+
+	$title = pun_trim($_POST['req_title']);
+	$user_title = pun_trim($_POST['user_title']);
+	$moderator = isset($_POST['moderator']) && $_POST['moderator'] == '1' ? '1' : '0';
+	$mod_edit_users = $moderator == '1' && isset($_POST['mod_edit_users']) && $_POST['mod_edit_users'] == '1' ? '1' : '0';
+	$mod_rename_users = $moderator == '1' && isset($_POST['mod_rename_users']) && $_POST['mod_rename_users'] == '1' ? '1' : '0';
+	$mod_change_passwords = $moderator == '1' && isset($_POST['mod_change_passwords']) && $_POST['mod_change_passwords'] == '1' ? '1' : '0';
+	$mod_ban_users = $moderator == '1' && isset($_POST['mod_ban_users']) && $_POST['mod_ban_users'] == '1' ? '1' : '0';
+	$read_board = isset($_POST['read_board']) ? intval($_POST['read_board']) : '1';
+	$view_users = (isset($_POST['view_users']) && $_POST['view_users'] == '1') || $is_admin_group ? '1' : '0';
+	$post_replies = isset($_POST['post_replies']) ? intval($_POST['post_replies']) : '1';
+	$post_topics = isset($_POST['post_topics']) ? intval($_POST['post_topics']) : '1';
+	$edit_posts = isset($_POST['edit_posts']) ? intval($_POST['edit_posts']) : ($is_admin_group) ? '1' : '0';
+	$delete_posts = isset($_POST['delete_posts']) ? intval($_POST['delete_posts']) : ($is_admin_group) ? '1' : '0';
+	$delete_topics = isset($_POST['delete_topics']) ? intval($_POST['delete_topics']) : ($is_admin_group) ? '1' : '0';
+	$set_title = isset($_POST['set_title']) ? intval($_POST['set_title']) : ($is_admin_group) ? '1' : '0';
+	$search = isset($_POST['search']) ? intval($_POST['search']) : '1';
+	$search_users = isset($_POST['search_users']) ? intval($_POST['search_users']) : '1';
+	$send_email = (isset($_POST['send_email']) && $_POST['send_email'] == '1') || $is_admin_group ? '1' : '0';
+	$post_flood = isset($_POST['post_flood']) ? intval($_POST['post_flood']) : '0';
+	$search_flood = isset($_POST['search_flood']) ? intval($_POST['search_flood']) : '0';
+	$email_flood = isset($_POST['email_flood']) ? intval($_POST['email_flood']) : '0';
+
+	if ($title == '')
+		message($lang_admin_groups['Must enter title message']);
+
+	$user_title = ($user_title != '') ? '\''.$db->escape($user_title).'\'' : 'NULL';
+
+	if ($_POST['mode'] == 'add')
+	{
+		$result = $db->query('SELECT 1 FROM '.$db->prefix.'groups WHERE g_title=\''.$db->escape($title).'\'') or error('Unable to check group title collision', __FILE__, __LINE__, $db->error());
+		if ($db->num_rows($result))
+			message(sprintf($lang_admin_groups['Title already exists message'], pun_htmlspecialchars($title)));
+
+		$db->query('INSERT INTO '.$db->prefix.'groups (g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood) VALUES(\''.$db->escape($title).'\', '.$user_title.', '.$moderator.', '.$mod_edit_users.', '.$mod_rename_users.', '.$mod_chan [...]
+		$new_group_id = $db->insert_id();
+
+		// Now lets copy the forum specific permissions from the group which this group is based on
+		$result = $db->query('SELECT forum_id, read_forum, post_replies, post_topics FROM '.$db->prefix.'forum_perms WHERE group_id='.intval($_POST['base_group'])) or error('Unable to fetch group forum permission list', __FILE__, __LINE__, $db->error());
+		while ($cur_forum_perm = $db->fetch_assoc($result))
+			$db->query('INSERT INTO '.$db->prefix.'forum_perms (group_id, forum_id, read_forum, post_replies, post_topics) VALUES('.$new_group_id.', '.$cur_forum_perm['forum_id'].', '.$cur_forum_perm['read_forum'].', '.$cur_forum_perm['post_replies'].', '.$cur_forum_perm['post_topics'].')') or error('Unable to insert group forum permissions', __FILE__, __LINE__, $db->error());
+	}
+	else
+	{
+		$result = $db->query('SELECT 1 FROM '.$db->prefix.'groups WHERE g_title=\''.$db->escape($title).'\' AND g_id!='.intval($_POST['group_id'])) or error('Unable to check group title collision', __FILE__, __LINE__, $db->error());
+		if ($db->num_rows($result))
+			message(sprintf($lang_admin_groups['Title already exists message'], pun_htmlspecialchars($title)));
+
+		$db->query('UPDATE '.$db->prefix.'groups SET g_title=\''.$db->escape($title).'\', g_user_title='.$user_title.', g_moderator='.$moderator.', g_mod_edit_users='.$mod_edit_users.', g_mod_rename_users='.$mod_rename_users.', g_mod_change_passwords='.$mod_change_passwords.', g_mod_ban_users='.$mod_ban_users.', g_read_board='.$read_board.', g_view_users='.$view_users.', g_post_replies='.$post_replies.', g_post_topics='.$post_topics.', g_edit_posts='.$edit_posts.', g_delete_posts='.$delete_pos [...]
+	}
+
+	// Regenerate the quick jump cache
+	if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+		require PUN_ROOT.'include/cache.php';
+
+	generate_quickjump_cache();
+
+	if ($_POST['mode'] == 'edit')
+		redirect('admin_groups.php', $lang_admin_groups['Group edited redirect']);
+	else
+		redirect('admin_groups.php', $lang_admin_groups['Group added redirect']);
+}
+
+
+// Set default group
+else if (isset($_POST['set_default_group']))
+{
+	confirm_referrer('admin_groups.php');
+
+	$group_id = intval($_POST['default_group']);
+
+	// Make sure it's not the admin or guest groups
+	if ($group_id == PUN_ADMIN || $group_id == PUN_GUEST)
+		message($lang_common['Bad request']);
+
+	// Make sure it's not a moderator group
+	$result = $db->query('SELECT 1 FROM '.$db->prefix.'groups WHERE g_id='.$group_id.' AND g_moderator=0') or error('Unable to check group moderator status', __FILE__, __LINE__, $db->error());
+	if (!$db->num_rows($result))
+		message($lang_common['Bad request']);
+
+	$db->query('UPDATE '.$db->prefix.'config SET conf_value='.$group_id.' WHERE conf_name=\'o_default_user_group\'') or error('Unable to update board config', __FILE__, __LINE__, $db->error());
+
+	// Regenerate the config cache
+	if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+		require PUN_ROOT.'include/cache.php';
+
+	generate_config_cache();
+
+	redirect('admin_groups.php', $lang_admin_groups['Default group redirect']);
+}
+
+
+// Remove a group
+else if (isset($_GET['del_group']))
+{
+	confirm_referrer('admin_groups.php');
+
+	$group_id = isset($_POST['group_to_delete']) ? intval($_POST['group_to_delete']) : intval($_GET['del_group']);
+	if ($group_id < 5)
+		message($lang_common['Bad request']);
+
+	// Make sure we don't remove the default group
+	if ($group_id == $pun_config['o_default_user_group'])
+		message($lang_admin_groups['Cannot remove default message']);
+
+	// Check if this group has any members
+	$result = $db->query('SELECT g.g_title, COUNT(u.id) FROM '.$db->prefix.'groups AS g INNER JOIN '.$db->prefix.'users AS u ON g.g_id=u.group_id WHERE g.g_id='.$group_id.' GROUP BY g.g_id, g_title') or error('Unable to fetch group info', __FILE__, __LINE__, $db->error());
+
+	// If the group doesn't have any members or if we've already selected a group to move the members to
+	if (!$db->num_rows($result) || isset($_POST['del_group']))
+	{
+		if (isset($_POST['del_group_comply']) || isset($_POST['del_group']))
+		{
+			if (isset($_POST['del_group']))
+			{
+				$move_to_group = intval($_POST['move_to_group']);
+				$db->query('UPDATE '.$db->prefix.'users SET group_id='.$move_to_group.' WHERE group_id='.$group_id) or error('Unable to move users into group', __FILE__, __LINE__, $db->error());
+			}
+
+			// Delete the group and any forum specific permissions
+			$db->query('DELETE FROM '.$db->prefix.'groups WHERE g_id='.$group_id) or error('Unable to delete group', __FILE__, __LINE__, $db->error());
+			$db->query('DELETE FROM '.$db->prefix.'forum_perms WHERE group_id='.$group_id) or error('Unable to delete group forum permissions', __FILE__, __LINE__, $db->error());
+
+			// Regenerate the quick jump cache
+			if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+				require PUN_ROOT.'include/cache.php';
+
+			generate_quickjump_cache();
+
+			redirect('admin_groups.php', $lang_admin_groups['Group removed redirect']);
+		}
+		else
+		{
+			$result = $db->query('SELECT g_title FROM '.$db->prefix.'groups WHERE g_id='.$group_id) or error('Unable to fetch group title', __FILE__, __LINE__, $db->error());
+			$group_title = $db->result($result);
+
+			$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['User groups']);
+			define('PUN_ACTIVE_PAGE', 'admin');
+			require PUN_ROOT.'header.php';
+
+			generate_admin_menu('groups');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_groups['Group delete head'] ?></span></h2>
+		<div class="box">
+			<form method="post" action="admin_groups.php?del_group=<?php echo $group_id ?>">
+				<div class="inform">
+				<input type="hidden" name="group_to_delete" value="<?php echo $group_id ?>" />
+					<fieldset>
+						<legend><?php echo $lang_admin_groups['Confirm delete subhead'] ?></legend>
+						<div class="infldset">
+							<p><?php printf($lang_admin_groups['Confirm delete info'], pun_htmlspecialchars($group_title)) ?></p>
+							<p class="warntext"><?php echo $lang_admin_groups['Confirm delete warn'] ?></p>
+						</div>
+					</fieldset>
+				</div>
+				<p class="buttons"><input type="submit" name="del_group_comply" value="<?php echo $lang_admin_common['Delete'] ?>" /><a href="javascript:history.go(-1)"><?php echo $lang_admin_common['Go back'] ?></a></p>
+			</form>
+		</div>
+	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+			require PUN_ROOT.'footer.php';
+		}
+	}
+
+	list($group_title, $group_members) = $db->fetch_row($result);
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['User groups']);
+	define('PUN_ACTIVE_PAGE', 'admin');
+	require PUN_ROOT.'header.php';
+
+	generate_admin_menu('groups');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_groups['Delete group head'] ?></span></h2>
+		<div class="box">
+			<form id="groups" method="post" action="admin_groups.php?del_group=<?php echo $group_id ?>">
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_groups['Move users subhead'] ?></legend>
+						<div class="infldset">
+							<p><?php printf($lang_admin_groups['Move users info'], pun_htmlspecialchars($group_title), forum_number_format($group_members)) ?></p>
+							<label><?php echo $lang_admin_groups['Move users label'] ?>
+							<select name="move_to_group">
+<?php
+
+	$result = $db->query('SELECT g_id, g_title FROM '.$db->prefix.'groups WHERE g_id!='.PUN_GUEST.' AND g_id!='.$group_id.' ORDER BY g_title') or error('Unable to fetch user group list', __FILE__, __LINE__, $db->error());
+
+	while ($cur_group = $db->fetch_assoc($result))
+	{
+		if ($cur_group['g_id'] == PUN_MEMBER) // Pre-select the pre-defined Members group
+			echo "\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'" selected="selected">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+		else
+			echo "\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+	}
+
+?>
+							</select>
+							<br /></label>
+						</div>
+					</fieldset>
+				</div>
+				<p class="buttons"><input type="submit" name="del_group" value="<?php echo $lang_admin_groups['Delete group'] ?>" /><a href="javascript:history.go(-1)"><?php echo $lang_admin_common['Go back'] ?></a></p>
+			</form>
+		</div>
+	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['User groups']);
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('groups');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_groups['Add groups head'] ?></span></h2>
+		<div class="box">
+			<form id="groups" method="post" action="admin_groups.php?action=foo">
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_groups['Add group subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['New group label'] ?><div><input type="submit" name="add_group" value="<?php echo $lang_admin_common['Add'] ?>" tabindex="2" /></div></th>
+									<td>
+										<select id="base_group" name="base_group" tabindex="1">
+<?php
+
+$result = $db->query('SELECT g_id, g_title FROM '.$db->prefix.'groups WHERE g_id!='.PUN_ADMIN.' AND g_id!='.PUN_GUEST.' ORDER BY g_title') or error('Unable to fetch user group list', __FILE__, __LINE__, $db->error());
+
+while ($cur_group = $db->fetch_assoc($result))
+{
+	if ($cur_group['g_id'] == $pun_config['o_default_user_group'])
+		echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'" selected="selected">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+	else
+		echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+}
+
+?>
+										</select>
+										<span><?php echo $lang_admin_groups['New group help'] ?></span>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_groups['Default group subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_groups['Default group label'] ?><div><input type="submit" name="set_default_group" value="<?php echo $lang_admin_common['Save'] ?>" tabindex="4" /></div></th>
+									<td>
+										<select id="default_group" name="default_group" tabindex="3">
+<?php
+
+$result = $db->query('SELECT g_id, g_title FROM '.$db->prefix.'groups WHERE g_id>'.PUN_GUEST.' AND g_moderator=0 ORDER BY g_title') or error('Unable to fetch user group list', __FILE__, __LINE__, $db->error());
+
+while ($cur_group = $db->fetch_assoc($result))
+{
+	if ($cur_group['g_id'] == $pun_config['o_default_user_group'])
+		echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'" selected="selected">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+	else
+		echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+}
+
+?>
+										</select>
+										<span><?php echo $lang_admin_groups['Default group help'] ?></span>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+			</form>
+		</div>
+
+		<h2 class="block2"><span><?php echo $lang_admin_groups['Existing groups head'] ?></span></h2>
+		<div class="box">
+			<div class="fakeform">
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_groups['Edit groups subhead'] ?></legend>
+						<div class="infldset">
+							<p><?php echo $lang_admin_groups['Edit groups info'] ?></p>
+							<table cellspacing="0">
+<?php
+
+$result = $db->query('SELECT g_id, g_title FROM '.$db->prefix.'groups ORDER BY g_id') or error('Unable to fetch user group list', __FILE__, __LINE__, $db->error());
+
+while ($cur_group = $db->fetch_assoc($result))
+	echo "\t\t\t\t\t\t\t\t".'<tr><th scope="row"><a href="admin_groups.php?edit_group='.$cur_group['g_id'].'">'.$lang_admin_groups['Edit link'].'</a>'.(($cur_group['g_id'] > PUN_MEMBER) ? ' | <a href="admin_groups.php?del_group='.$cur_group['g_id'].'">'.$lang_admin_groups['Delete link'].'</a>' : '').'</th><td>'.pun_htmlspecialchars($cur_group['g_title']).'</td></tr>'."\n";
+
+?>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+			</div>
+		</div>
+	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_index.php b/admin_index.php
new file mode 100644
index 0000000..70c31ad
--- /dev/null
+++ b/admin_index.php
@@ -0,0 +1,176 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if (!$pun_user['is_admmod'])
+	message($lang_common['No permission']);
+
+// Load the admin_index.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_index.php';
+
+$action = isset($_GET['action']) ? $_GET['action'] : null;
+
+// Check for upgrade
+if ($action == 'check_upgrade')
+{
+	if (!ini_get('allow_url_fopen'))
+		message($lang_admin_index['fopen disabled message']);
+
+	$latest_version = trim(@file_get_contents('http://fluxbb.org/latest_version'));
+	if (empty($latest_version))
+		message($lang_admin_index['Upgrade check failed message']);
+
+	if (version_compare($pun_config['o_cur_version'], $latest_version, '>='))
+		message($lang_admin_index['Running latest version message']);
+	else
+		message(sprintf($lang_admin_index['New version available message'], '<a href="http://fluxbb.org/">FluxBB.org</a>'));
+}
+
+
+// Show phpinfo() output
+else if ($action == 'phpinfo' && $pun_user['g_id'] == PUN_ADMIN)
+{
+	// Is phpinfo() a disabled function?
+	if (strpos(strtolower((string) ini_get('disable_functions')), 'phpinfo') !== false)
+		message($lang_admin_index['PHPinfo disabled message']);
+
+	phpinfo();
+	exit;
+}
+
+
+// Get the server load averages (if possible)
+if (@file_exists('/proc/loadavg') && is_readable('/proc/loadavg'))
+{
+	// We use @ just in case
+	$fh = @fopen('/proc/loadavg', 'r');
+	$load_averages = @fread($fh, 64);
+	@fclose($fh);
+
+	if (($fh = @fopen('/proc/loadavg', 'r')))
+	{
+		$load_averages = fread($fh, 64);
+		fclose($fh);
+	}
+	else
+		$load_averages = '';
+
+	$load_averages = @explode(' ', $load_averages);
+	$server_load = isset($load_averages[2]) ? $load_averages[0].' '.$load_averages[1].' '.$load_averages[2] : $lang_admin_index['Not available'];
+}
+else if (!in_array(PHP_OS, array('WINNT', 'WIN32')) && preg_match('/averages?: ([0-9\.]+),?\s+([0-9\.]+),?\s+([0-9\.]+)/i', @exec('uptime'), $load_averages))
+	$server_load = $load_averages[1].' '.$load_averages[2].' '.$load_averages[3];
+else
+	$server_load = $lang_admin_index['Not available'];
+
+
+// Get number of current visitors
+$result = $db->query('SELECT COUNT(user_id) FROM '.$db->prefix.'online WHERE idle=0') or error('Unable to fetch online count', __FILE__, __LINE__, $db->error());
+$num_online = $db->result($result);
+
+
+// Collect some additional info about MySQL
+if ($db_type == 'mysql' || $db_type == 'mysqli' || $db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
+{
+	// Calculate total db size/row count
+	$result = $db->query('SHOW TABLE STATUS LIKE \''.$db->prefix.'%\'') or error('Unable to fetch table status', __FILE__, __LINE__, $db->error());
+
+	$total_records = $total_size = 0;
+	while ($status = $db->fetch_assoc($result))
+	{
+		$total_records += $status['Rows'];
+		$total_size += $status['Data_length'] + $status['Index_length'];
+	}
+
+	$total_size = file_size($total_size);
+}
+
+
+// Check for the existence of various PHP opcode caches/optimizers
+if (function_exists('mmcache'))
+	$php_accelerator = '<a href="http://'.$lang_admin_index['Turck MMCache link'].'">'.$lang_admin_index['Turck MMCache'].'</a>';
+else if (isset($_PHPA))
+	$php_accelerator = '<a href="http://'.$lang_admin_index['ionCube PHP Accelerator link'].'">'.$lang_admin_index['ionCube PHP Accelerator'].'</a>';
+else if (ini_get('apc.enabled'))
+	$php_accelerator ='<a href="http://'.$lang_admin_index['Alternative PHP Cache (APC) link'].'">'.$lang_admin_index['Alternative PHP Cache (APC)'].'</a>';
+else if (ini_get('zend_optimizer.optimization_level'))
+	$php_accelerator = '<a href="http://'.$lang_admin_index['Zend Optimizer link'].'">'.$lang_admin_index['Zend Optimizer'].'</a>';
+else if (ini_get('eaccelerator.enable'))
+	$php_accelerator = '<a href="http://'.$lang_admin_index['eAccelerator link'].'">'.$lang_admin_index['eAccelerator'].'</a>';
+else if (ini_get('xcache.cacher'))
+	$php_accelerator = '<a href="http://'.$lang_admin_index['XCache link'].'">'.$lang_admin_index['XCache'].'</a>';
+else
+	$php_accelerator = $lang_admin_index['NA'];
+
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Index']);
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('index');
+
+?>
+	<div class="block">
+		<h2><span><?php echo $lang_admin_index['Forum admin head'] ?></span></h2>
+		<div id="adintro" class="box">
+			<div class="inbox">
+				<p>
+					<?php echo $lang_admin_index['Welcome to admin'] ?><br /><br />
+					 - <?php echo $lang_admin_index['Welcome 1'] ?><br />
+					 - <?php echo $lang_admin_index['Welcome 2'] ?><br />
+					 - <?php echo $lang_admin_index['Welcome 3'] ?><br />
+					 - <?php echo $lang_admin_index['Welcome 4'] ?><br />
+					 - <?php echo $lang_admin_index['Welcome 5'] ?><br />
+					 - <?php echo $lang_admin_index['Welcome 6'] ?><br />
+					 - <?php echo $lang_admin_index['Welcome 7'] ?><br />
+					 - <?php echo $lang_admin_index['Welcome 8'] ?><br />
+					 - <?php echo $lang_admin_index['Welcome 9'] ?>
+				</p>
+			</div>
+		</div>
+
+		<h2 class="block2"><span><?php echo $lang_admin_index['Statistics head'] ?></span></h2>
+		<div id="adstats" class="box">
+			<div class="inbox">
+				<dl>
+					<dt><?php echo $lang_admin_index['FluxBB version label'] ?></dt>
+					<dd>
+						<?php printf($lang_admin_index['FluxBB version data'], $pun_config['o_cur_version'], '<a href="admin_index.php?action=check_upgrade">'.$lang_admin_index['Check for upgrade'].'</a>') ?><br />
+					</dd>
+					<dt><?php echo $lang_admin_index['Server load label'] ?></dt>
+					<dd>
+						<?php printf($lang_admin_index['Server load data'], $server_load, $num_online) ?>
+					</dd>
+<?php if ($pun_user['g_id'] == PUN_ADMIN): ?>					<dt><?php echo $lang_admin_index['Environment label'] ?></dt>
+					<dd>
+						<?php printf($lang_admin_index['Environment data OS'], PHP_OS) ?><br />
+						<?php printf($lang_admin_index['Environment data version'], phpversion(), '<a href="admin_index.php?action=phpinfo">'.$lang_admin_index['Show info'].'</a>') ?><br />
+						<?php printf($lang_admin_index['Environment data acc'], $php_accelerator) ?>
+					</dd>
+					<dt><?php echo $lang_admin_index['Database label'] ?></dt>
+					<dd>
+						<?php echo implode(' ', $db->get_version())."\n" ?>
+<?php if (isset($total_records) && isset($total_size)): ?>						<br /><?php printf($lang_admin_index['Database data rows'], forum_number_format($total_records)) ?>
+						<br /><?php printf($lang_admin_index['Database data size'], $total_size) ?>
+<?php endif; endif; ?>					</dd>
+				</dl>
+			</div>
+		</div>
+	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_loader.php b/admin_loader.php
new file mode 100644
index 0000000..760f967
--- /dev/null
+++ b/admin_loader.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if (!$pun_user['is_admmod'])
+	message($lang_common['No permission']);
+
+// The plugin to load should be supplied via GET
+$plugin = isset($_GET['plugin']) ? $_GET['plugin'] : '';
+if (!preg_match('/^AM?P_(\w*?)\.php$/i', $plugin))
+	message($lang_common['Bad request']);
+
+// AP_ == Admins only, AMP_ == admins and moderators
+$prefix = substr($plugin, 0, strpos($plugin, '_'));
+if ($pun_user['g_moderator'] == '1' && $prefix == 'AP')
+	message($lang_common['No permission']);
+
+// Make sure the file actually exists
+if (!file_exists(PUN_ROOT.'plugins/'.$plugin))
+	message(sprintf($lang_admin_common['No plugin message'], $plugin));
+
+// Construct REQUEST_URI if it isn't set
+if (!isset($_SERVER['REQUEST_URI']))
+	$_SERVER['REQUEST_URI'] = (isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : '').'?'.(isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '');
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Admin'], str_replace('_', ' ', substr($plugin, strpos($plugin, '_') + 1, -4)));
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+// Attempt to load the plugin. We don't use @ here to supress error messages,
+// because if we did and a parse error occurred in the plugin, we would only
+// get the "blank page of death"
+include PUN_ROOT.'plugins/'.$plugin;
+if (!defined('PUN_PLUGIN_LOADED'))
+	message(sprintf($lang_admin_common['Plugin failed message'], $plugin));
+
+// Output the clearer div
+?>
+	<div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_maintenance.php b/admin_maintenance.php
new file mode 100644
index 0000000..dc3a125
--- /dev/null
+++ b/admin_maintenance.php
@@ -0,0 +1,178 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+// Tell common.php that we don't want output buffering
+define('PUN_DISABLE_BUFFERING', 1);
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if ($pun_user['g_id'] != PUN_ADMIN)
+	message($lang_common['No permission']);
+
+// Load the admin_maintenance.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_maintenance.php';
+
+if (isset($_GET['i_per_page']) && isset($_GET['i_start_at']))
+{
+	$per_page = intval($_GET['i_per_page']);
+	$start_at = intval($_GET['i_start_at']);
+	if ($per_page < 1 || $start_at < 1)
+		message($lang_common['Bad request']);
+
+	@set_time_limit(0);
+
+	// If this is the first cycle of posts we empty the search index before we proceed
+	if (isset($_GET['i_empty_index']))
+	{
+		// This is the only potentially "dangerous" thing we can do here, so we check the referer
+		confirm_referrer('admin_maintenance.php');
+
+		$db->truncate_table('search_matches') or error('Unable to empty search index match table', __FILE__, __LINE__, $db->error());
+		$db->truncate_table('search_words') or error('Unable to empty search index words table', __FILE__, __LINE__, $db->error());
+
+		// Reset the sequence for the search words (not needed for SQLite)
+		switch ($db_type)
+		{
+			case 'mysql':
+			case 'mysqli':
+			case 'mysql_innodb':
+			case 'mysqli_innodb':
+				$result = $db->query('ALTER TABLE '.$db->prefix.'search_words auto_increment=1') or error('Unable to update table auto_increment', __FILE__, __LINE__, $db->error());
+				break;
+
+			case 'pgsql';
+				$result = $db->query('SELECT setval(\''.$db->prefix.'search_words_id_seq\', 1, false)') or error('Unable to update sequence', __FILE__, __LINE__, $db->error());
+		}
+	}
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_maintenance['Rebuilding search index']);
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title><?php echo generate_page_title($page_title) ?></title>
+<style type="text/css">
+body {
+	font: 12px Verdana, Arial, Helvetica, sans-serif;
+	color: #333333;
+	background-color: #FFFFFF
+}
+
+h1 {
+	font-size: 16px;
+	font-weight: normal;
+}
+</style>
+</head>
+<body>
+
+<h1><?php echo $lang_admin_maintenance['Rebuilding index info'] ?></h1>
+<hr />
+
+<?php
+
+	$query_str = '';
+
+	require PUN_ROOT.'include/search_idx.php';
+
+	// Fetch posts to process this cycle
+	$result = $db->query('SELECT p.id, p.message, t.subject, t.first_post_id FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id WHERE p.id >= '.$start_at.' ORDER BY p.id ASC LIMIT '.$per_page) or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
+
+	$end_at = 0;
+	while ($cur_item = $db->fetch_assoc($result))
+	{
+		echo '<p><span>'.sprintf($lang_admin_maintenance['Processing post'], $cur_item['id']).'</span></p>'."\n";
+
+		if ($cur_item['id'] == $cur_item['first_post_id'])
+			update_search_index('post', $cur_item['id'], $cur_item['message'], $cur_item['subject']);
+		else
+			update_search_index('post', $cur_item['id'], $cur_item['message']);
+
+		$end_at = $cur_item['id'];
+	}
+
+	// Check if there is more work to do
+	if ($end_at > 0)
+	{
+		$result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE id > '.$end_at.' ORDER BY id ASC LIMIT 1') or error('Unable to fetch next ID', __FILE__, __LINE__, $db->error());
+
+		if ($db->num_rows($result) > 0)
+			$query_str = '?i_per_page='.$per_page.'&i_start_at='.$db->result($result);
+	}
+
+	$db->end_transaction();
+	$db->close();
+
+	exit('<script type="text/javascript">window.location="admin_maintenance.php'.$query_str.'"</script><hr /><p>'.sprintf($lang_admin_maintenance['Javascript redirect failed'], '<a href="admin_maintenance.php'.$query_str.'">'.$lang_admin_maintenance['Click here'].'</a>').'</p>');
+}
+
+
+// Get the first post ID from the db
+$result = $db->query('SELECT id FROM '.$db->prefix.'posts ORDER BY id ASC LIMIT 1') or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
+if ($db->num_rows($result))
+	$first_id = $db->result($result);
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Maintenance']);
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('maintenance');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_maintenance['Maintenance head'] ?></span></h2>
+		<div class="box">
+			<form method="get" action="admin_maintenance.php">
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_maintenance['Rebuild index subhead'] ?></legend>
+						<div class="infldset">
+							<p><?php printf($lang_admin_maintenance['Rebuild index info'], '<a href="admin_options.php#maintenance">'.$lang_admin_common['Maintenance mode'].'</a>') ?></p>
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_maintenance['Posts per cycle label'] ?></th>
+									<td>
+										<input type="text" name="i_per_page" size="7" maxlength="7" value="300" tabindex="1" />
+										<span><?php echo $lang_admin_maintenance['Posts per cycle help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_maintenance['Starting post label'] ?></th>
+									<td>
+										<input type="text" name="i_start_at" size="7" maxlength="7" value="<?php echo (isset($first_id)) ? $first_id : 0 ?>" tabindex="2" />
+										<span><?php echo $lang_admin_maintenance['Starting post help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_maintenance['Empty index label'] ?></th>
+									<td class="inputadmin">
+										<span><input type="checkbox" name="i_empty_index" value="1" tabindex="3" checked="checked" />  <?php echo $lang_admin_maintenance['Empty index help'] ?></span>
+									</td>
+								</tr>
+							</table>
+							<p class="topspace"><?php echo $lang_admin_maintenance['Rebuild completed info'] ?></p>
+							<div class="fsetsubmit"><input type="submit" name="rebuild_index" value="<?php echo $lang_admin_maintenance['Rebuild index'] ?>" tabindex="4" /></div>
+						</div>
+					</fieldset>
+				</div>
+			</form>
+		</div>
+	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_options.php b/admin_options.php
new file mode 100644
index 0000000..6296d32
--- /dev/null
+++ b/admin_options.php
@@ -0,0 +1,799 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if ($pun_user['g_id'] != PUN_ADMIN)
+	message($lang_common['No permission']);
+
+// Load the admin_options.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_options.php';
+
+if (isset($_POST['form_sent']))
+{
+	// Custom referrer check (so we can output a custom error message)
+	if (!preg_match('#^'.preg_quote(str_replace('www.', '', $pun_config['o_base_url']).'/admin_options.php', '#').'#i', str_replace('www.', '', (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : ''))))
+		message($lang_admin_options['Bad HTTP Referer message']);
+
+	$form = array(
+		'board_title'			=> pun_trim($_POST['form']['board_title']),
+		'board_desc'			=> pun_trim($_POST['form']['board_desc']),
+		'base_url'				=> pun_trim($_POST['form']['base_url']),
+		'default_timezone'		=> floatval($_POST['form']['default_timezone']),
+		'default_dst'			=> $_POST['form']['default_dst'] != '1' ? '0' : '1',
+		'default_lang'			=> preg_replace('#[\.\\\/]#', '', pun_trim($_POST['form']['default_lang'])),
+		'default_style'			=> preg_replace('#[\.\\\/]#', '', pun_trim($_POST['form']['default_style'])),
+		'time_format'			=> pun_trim($_POST['form']['time_format']),
+		'date_format'			=> pun_trim($_POST['form']['date_format']),
+		'timeout_visit'			=> intval($_POST['form']['timeout_visit']),
+		'timeout_online'		=> intval($_POST['form']['timeout_online']),
+		'redirect_delay'		=> intval($_POST['form']['redirect_delay']),
+		'show_version'			=> $_POST['form']['show_version'] != '1' ? '0' : '1',
+		'show_user_info'		=> $_POST['form']['show_user_info'] != '1' ? '0' : '1',
+		'show_post_count'		=> $_POST['form']['show_post_count'] != '1' ? '0' : '1',
+		'smilies'				=> $_POST['form']['smilies'] != '1' ? '0' : '1',
+		'smilies_sig'			=> $_POST['form']['smilies_sig'] != '1' ? '0' : '1',
+		'make_links'			=> $_POST['form']['make_links'] != '1' ? '0' : '1',
+		'topic_review'			=> intval($_POST['form']['topic_review']),
+		'disp_topics_default'	=> intval($_POST['form']['disp_topics_default']),
+		'disp_posts_default'	=> intval($_POST['form']['disp_posts_default']),
+		'indent_num_spaces'		=> intval($_POST['form']['indent_num_spaces']),
+		'quote_depth'			=> intval($_POST['form']['quote_depth']),
+		'quickpost'				=> $_POST['form']['quickpost'] != '1' ? '0' : '1',
+		'users_online'			=> $_POST['form']['users_online'] != '1' ? '0' : '1',
+		'censoring'				=> $_POST['form']['censoring'] != '1' ? '0' : '1',
+		'signatures'			=> $_POST['form']['signatures'] != '1' ? '0' : '1',
+		'ranks'					=> $_POST['form']['ranks'] != '1' ? '0' : '1',
+		'show_dot'				=> $_POST['form']['show_dot'] != '1' ? '0' : '1',
+		'topic_views'			=> $_POST['form']['topic_views'] != '1' ? '0' : '1',
+		'quickjump'				=> $_POST['form']['quickjump'] != '1' ? '0' : '1',
+		'gzip'					=> $_POST['form']['gzip'] != '1' ? '0' : '1',
+		'search_all_forums'		=> $_POST['form']['search_all_forums'] != '1' ? '0' : '1',
+		'additional_navlinks'	=> pun_trim($_POST['form']['additional_navlinks']),
+		'feed_type'				=> intval($_POST['form']['feed_type']),
+		'report_method'			=> intval($_POST['form']['report_method']),
+		'mailing_list'			=> pun_trim($_POST['form']['mailing_list']),
+		'avatars'				=> $_POST['form']['avatars'] != '1' ? '0' : '1',
+		'avatars_dir'			=> pun_trim($_POST['form']['avatars_dir']),
+		'avatars_width'			=> intval($_POST['form']['avatars_width']),
+		'avatars_height'		=> intval($_POST['form']['avatars_height']),
+		'avatars_size'			=> intval($_POST['form']['avatars_size']),
+		'admin_email'			=> strtolower(pun_trim($_POST['form']['admin_email'])),
+		'webmaster_email'		=> strtolower(pun_trim($_POST['form']['webmaster_email'])),
+		'subscriptions'			=> $_POST['form']['subscriptions'] != '1' ? '0' : '1',
+		'smtp_host'				=> pun_trim($_POST['form']['smtp_host']),
+		'smtp_user'				=> pun_trim($_POST['form']['smtp_user']),
+		'smtp_pass'				=> pun_trim($_POST['form']['smtp_pass']),
+		'smtp_ssl'				=> $_POST['form']['smtp_ssl'] != '1' ? '0' : '1',
+		'regs_allow'			=> $_POST['form']['regs_allow'] != '1' ? '0' : '1',
+		'regs_verify'			=> $_POST['form']['regs_verify'] != '1' ? '0' : '1',
+		'regs_report'			=> $_POST['form']['regs_report'] != '1' ? '0' : '1',
+		'rules'					=> $_POST['form']['rules'] != '1' ? '0' : '1',
+		'rules_message'			=> pun_trim($_POST['form']['rules_message']),
+		'default_email_setting'	=> intval($_POST['form']['default_email_setting']),
+		'announcement'			=> $_POST['form']['announcement'] != '1' ? '0' : '1',
+		'announcement_message'	=> pun_trim($_POST['form']['announcement_message']),
+		'maintenance'			=> $_POST['form']['maintenance'] != '1' ? '0' : '1',
+		'maintenance_message'	=> pun_trim($_POST['form']['maintenance_message']),
+	);
+
+	if ($form['board_title'] == '')
+		message($lang_admin_options['Must enter title message']);
+
+	// Make sure base_url doesn't end with a slash
+	if (substr($form['base_url'], -1) == '/')
+		$form['base_url'] = substr($form['base_url'], 0, -1);
+
+	if (!file_exists(PUN_ROOT.'lang/'.$form['default_lang'].'/common.php'))
+		message($lang_common['Bad request']);
+	if (!file_exists(PUN_ROOT.'style/'.$form['default_style'].'.css'))
+		message($lang_common['Bad request']);
+
+	if ($form['time_format'] == '')
+		$form['time_format'] = 'H:i:s';
+
+	if ($form['date_format'] == '')
+		$form['date_format'] = 'Y-m-d';
+
+
+	require PUN_ROOT.'include/email.php';
+
+	if (!is_valid_email($form['admin_email']))
+		message($lang_admin_options['Invalid e-mail message']);
+
+	if (!is_valid_email($form['webmaster_email']))
+		message($lang_admin_options['Invalid webmaster e-mail message']);
+
+	if ($form['mailing_list'] != '')
+		$form['mailing_list'] = strtolower(preg_replace('/\s/S', '', $form['mailing_list']));
+
+	// Make sure avatars_dir doesn't end with a slash
+	if (substr($form['avatars_dir'], -1) == '/')
+		$form['avatars_dir'] = substr($form['avatars_dir'], 0, -1);
+
+	if ($form['additional_navlinks'] != '')
+		$form['additional_navlinks'] = pun_trim(pun_linebreaks($form['additional_navlinks']));
+
+	if ($form['announcement_message'] != '')
+		$form['announcement_message'] = pun_linebreaks($form['announcement_message']);
+	else
+	{
+		$form['announcement_message'] = $lang_admin_options['Enter announcement here'];
+		$form['announcement'] = '0';
+	}
+
+	if ($form['rules_message'] != '')
+		$form['rules_message'] = pun_linebreaks($form['rules_message']);
+	else
+	{
+		$form['rules_message'] = $lang_admin_options['Enter rules here'];
+		$form['rules'] = '0';
+	}
+
+	if ($form['maintenance_message'] != '')
+		$form['maintenance_message'] = pun_linebreaks($form['maintenance_message']);
+	else
+	{
+		$form['maintenance_message'] = $lang_admin_options['Default maintenance message'];
+		$form['maintenance'] = '0';
+	}
+
+	// Make sure the number of displayed topics and posts is between 3 and 75
+	if ($form['disp_topics_default'] < 3)
+		$form['disp_topics_default'] = 3;
+	else if ($form['disp_topics_default'] > 75)
+		$form['disp_topics_default'] = 75;
+
+	if ($form['disp_posts_default'] < 3)
+		$form['disp_posts_default'] = 3;
+	else if ($form['disp_posts_default'] > 75)
+		$form['disp_posts_default'] = 75;
+
+	if ($form['feed_type'] < 0 || $form['feed_type'] > 2)
+		message($lang_common['Bad request']);
+
+	if ($form['report_method'] < 0 || $form['report_method'] > 2)
+		message($lang_common['Bad request']);
+
+	if ($form['default_email_setting'] < 0 || $form['default_email_setting'] > 2)
+		message($lang_common['Bad request']);
+
+	if ($form['timeout_online'] >= $form['timeout_visit'])
+		message($lang_admin_options['Timeout error message']);
+
+	foreach ($form as $key => $input)
+	{
+		// Only update values that have changed
+		if (array_key_exists('o_'.$key, $pun_config) && $pun_config['o_'.$key] != $input)
+		{
+			if ($input != '' || is_int($input))
+				$value = '\''.$db->escape($input).'\'';
+			else
+				$value = 'NULL';
+
+			$db->query('UPDATE '.$db->prefix.'config SET conf_value='.$value.' WHERE conf_name=\'o_'.$db->escape($key).'\'') or error('Unable to update board config', __FILE__, __LINE__, $db->error());
+		}
+	}
+
+	// Regenerate the config cache
+	if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+		require PUN_ROOT.'include/cache.php';
+
+	generate_config_cache();
+
+	redirect('admin_options.php', $lang_admin_options['Options updated redirect']);
+}
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Options']);
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('options');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_options['Options head'] ?></span></h2>
+		<div class="box">
+			<form method="post" action="admin_options.php?action=foo">
+				<p class="submittop"><input type="submit" name="save" value="<?php echo $lang_admin_common['Save changes'] ?>" /></p>
+				<div class="inform">
+					<input type="hidden" name="form_sent" value="1" />
+					<fieldset>
+						<legend><?php echo $lang_admin_options['Essentials subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Board title label'] ?></th>
+									<td>
+										<input type="text" name="form[board_title]" size="50" maxlength="255" value="<?php echo pun_htmlspecialchars($pun_config['o_board_title']) ?>" />
+										<span><?php echo $lang_admin_options['Board title help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Board desc label'] ?></th>
+									<td>
+										<input type="text" name="form[board_desc]" size="50" maxlength="255" value="<?php echo pun_htmlspecialchars($pun_config['o_board_desc']) ?>" />
+										<span><?php echo $lang_admin_options['Board desc help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Base URL label'] ?></th>
+									<td>
+										<input type="text" name="form[base_url]" size="50" maxlength="100" value="<?php echo $pun_config['o_base_url'] ?>" />
+										<span><?php echo $lang_admin_options['Base URL help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Timezone label'] ?></th>
+									<td>
+										<select name="form[default_timezone]">
+											<option value="-12"<?php if ($pun_config['o_default_timezone'] == -12) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-12:00'] ?></option>
+											<option value="-11"<?php if ($pun_config['o_default_timezone'] == -11) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-11:00'] ?></option>
+											<option value="-10"<?php if ($pun_config['o_default_timezone'] == -10) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-10:00'] ?></option>
+											<option value="-9.5"<?php if ($pun_config['o_default_timezone'] == -9.5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-09:30'] ?></option>
+											<option value="-9"<?php if ($pun_config['o_default_timezone'] == -9) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-09:00'] ?></option>
+											<option value="-8.5"<?php if ($pun_config['o_default_timezone'] == -8.5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-08:30'] ?></option>
+											<option value="-8"<?php if ($pun_config['o_default_timezone'] == -8) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-08:00'] ?></option>
+											<option value="-7"<?php if ($pun_config['o_default_timezone'] == -7) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-07:00'] ?></option>
+											<option value="-6"<?php if ($pun_config['o_default_timezone'] == -6) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-06:00'] ?></option>
+											<option value="-5"<?php if ($pun_config['o_default_timezone'] == -5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-05:00'] ?></option>
+											<option value="-4"<?php if ($pun_config['o_default_timezone'] == -4) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-04:00'] ?></option>
+											<option value="-3.5"<?php if ($pun_config['o_default_timezone'] == -3.5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-03:30'] ?></option>
+											<option value="-3"<?php if ($pun_config['o_default_timezone'] == -3) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-03:00'] ?></option>
+											<option value="-2"<?php if ($pun_config['o_default_timezone'] == -2) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-02:00'] ?></option>
+											<option value="-1"<?php if ($pun_config['o_default_timezone'] == -1) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC-01:00'] ?></option>
+											<option value="0"<?php if ($pun_config['o_default_timezone'] == 0) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC'] ?></option>
+											<option value="1"<?php if ($pun_config['o_default_timezone'] == 1) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+01:00'] ?></option>
+											<option value="2"<?php if ($pun_config['o_default_timezone'] == 2) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+02:00'] ?></option>
+											<option value="3"<?php if ($pun_config['o_default_timezone'] == 3) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+03:00'] ?></option>
+											<option value="3.5"<?php if ($pun_config['o_default_timezone'] == 3.5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+03:30'] ?></option>
+											<option value="4"<?php if ($pun_config['o_default_timezone'] == 4) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+04:00'] ?></option>
+											<option value="4.5"<?php if ($pun_config['o_default_timezone'] == 4.5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+04:30'] ?></option>
+											<option value="5"<?php if ($pun_config['o_default_timezone'] == 5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+05:00'] ?></option>
+											<option value="5.5"<?php if ($pun_config['o_default_timezone'] == 5.5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+05:30'] ?></option>
+											<option value="5.75"<?php if ($pun_config['o_default_timezone'] == 5.75) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+05:45'] ?></option>
+											<option value="6"<?php if ($pun_config['o_default_timezone'] == 6) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+06:00'] ?></option>
+											<option value="6.5"<?php if ($pun_config['o_default_timezone'] == 6.5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+06:30'] ?></option>
+											<option value="7"<?php if ($pun_config['o_default_timezone'] == 7) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+07:00'] ?></option>
+											<option value="8"<?php if ($pun_config['o_default_timezone'] == 8) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+08:00'] ?></option>
+											<option value="8.75"<?php if ($pun_config['o_default_timezone'] == 8.75) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+08:45'] ?></option>
+											<option value="9"<?php if ($pun_config['o_default_timezone'] == 9) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+09:00'] ?></option>
+											<option value="9.5"<?php if ($pun_config['o_default_timezone'] == 9.5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+09:30'] ?></option>
+											<option value="10"<?php if ($pun_config['o_default_timezone'] == 10) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+10:00'] ?></option>
+											<option value="10.5"<?php if ($pun_config['o_default_timezone'] == 10.5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+10:30'] ?></option>
+											<option value="11"<?php if ($pun_config['o_default_timezone'] == 11) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+11:00'] ?></option>
+											<option value="11.5"<?php if ($pun_config['o_default_timezone'] == 11.5) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+11:30'] ?></option>
+											<option value="12"<?php if ($pun_config['o_default_timezone'] == 12) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+12:00'] ?></option>
+											<option value="12.75"<?php if ($pun_config['o_default_timezone'] == 12.75) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+12:45'] ?></option>
+											<option value="13"<?php if ($pun_config['o_default_timezone'] == 13) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+13:00'] ?></option>
+											<option value="14"<?php if ($pun_config['o_default_timezone'] == 14) echo ' selected="selected"' ?>><?php echo $lang_admin_options['UTC+14:00'] ?></option>
+										</select>
+										<span><?php echo $lang_admin_options['Timezone help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['DST label'] ?></th>
+									<td>
+										<input type="radio" name="form[default_dst]" value="1"<?php if ($pun_config['o_default_dst'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[default_dst]" value="0"<?php if ($pun_config['o_default_dst'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['DST help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Language label'] ?></th>
+									<td>
+										<select name="form[default_lang]">
+<?php
+
+		$languages = forum_list_langs();
+
+		foreach ($languages as $temp)
+		{
+			if ($pun_config['o_default_lang'] == $temp)
+				echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$temp.'" selected="selected">'.$temp.'</option>'."\n";
+			else
+				echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$temp.'">'.$temp.'</option>'."\n";
+		}
+
+?>
+										</select>
+										<span><?php echo $lang_admin_options['Language help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Default style label'] ?></th>
+									<td>
+										<select name="form[default_style]">
+<?php
+
+		$styles = forum_list_styles();
+
+		foreach ($styles as $temp)
+		{
+			if ($pun_config['o_default_style'] == $temp)
+				echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$temp.'" selected="selected">'.str_replace('_', ' ', $temp).'</option>'."\n";
+			else
+				echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$temp.'">'.str_replace('_', ' ', $temp).'</option>'."\n";
+		}
+
+?>
+										</select>
+										<span><?php echo $lang_admin_options['Default style help'] ?></span>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+<?php
+
+	$diff = ($pun_user['timezone'] + $pun_user['dst']) * 3600;
+	$timestamp = time() + $diff;
+
+?>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_options['Timeouts subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Time format label'] ?></th>
+									<td>
+										<input type="text" name="form[time_format]" size="25" maxlength="25" value="<?php echo pun_htmlspecialchars($pun_config['o_time_format']) ?>" />
+										<span><?php printf($lang_admin_options['Time format help'], gmdate($pun_config['o_time_format'], $timestamp), '<a href="http://www.php.net/manual/en/function.date.php">'.$lang_admin_options['PHP manual'].'</a>') ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Date format label'] ?></th>
+									<td>
+										<input type="text" name="form[date_format]" size="25" maxlength="25" value="<?php echo pun_htmlspecialchars($pun_config['o_date_format']) ?>" />
+										<span><?php printf($lang_admin_options['Date format help'], gmdate($pun_config['o_date_format'], $timestamp), '<a href="http://www.php.net/manual/en/function.date.php">'.$lang_admin_options['PHP manual'].'</a>') ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Visit timeout label'] ?></th>
+									<td>
+										<input type="text" name="form[timeout_visit]" size="5" maxlength="5" value="<?php echo $pun_config['o_timeout_visit'] ?>" />
+										<span><?php echo $lang_admin_options['Visit timeout help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Online timeout label'] ?></th>
+									<td>
+										<input type="text" name="form[timeout_online]" size="5" maxlength="5" value="<?php echo $pun_config['o_timeout_online'] ?>" />
+										<span><?php echo $lang_admin_options['Online timeout help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Redirect time label'] ?></th>
+									<td>
+										<input type="text" name="form[redirect_delay]" size="3" maxlength="3" value="<?php echo $pun_config['o_redirect_delay'] ?>" />
+										<span><?php echo $lang_admin_options['Redirect time help'] ?></span>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_options['Display subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Version number label'] ?></th>
+									<td>
+										<input type="radio" name="form[show_version]" value="1"<?php if ($pun_config['o_show_version'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[show_version]" value="0"<?php if ($pun_config['o_show_version'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Version number help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Info in posts label'] ?></th>
+									<td>
+										<input type="radio" name="form[show_user_info]" value="1"<?php if ($pun_config['o_show_user_info'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[show_user_info]" value="0"<?php if ($pun_config['o_show_user_info'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Info in posts help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Post count label'] ?></th>
+									<td>
+										<input type="radio" name="form[show_post_count]" value="1"<?php if ($pun_config['o_show_post_count'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[show_post_count]" value="0"<?php if ($pun_config['o_show_post_count'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Post count help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Smilies label'] ?></th>
+									<td>
+										<input type="radio" name="form[smilies]" value="1"<?php if ($pun_config['o_smilies'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[smilies]" value="0"<?php if ($pun_config['o_smilies'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Smilies help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Smilies sigs label'] ?></th>
+									<td>
+										<input type="radio" name="form[smilies_sig]" value="1"<?php if ($pun_config['o_smilies_sig'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[smilies_sig]" value="0"<?php if ($pun_config['o_smilies_sig'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Smilies sigs help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Clickable links label'] ?></th>
+									<td>
+										<input type="radio" name="form[make_links]" value="1"<?php if ($pun_config['o_make_links'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[make_links]" value="0"<?php if ($pun_config['o_make_links'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Clickable links help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Topic review label'] ?></th>
+									<td>
+										<input type="text" name="form[topic_review]" size="3" maxlength="3" value="<?php echo $pun_config['o_topic_review'] ?>" />
+										<span><?php echo $lang_admin_options['Topic review help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Topics per page label'] ?></th>
+									<td>
+										<input type="text" name="form[disp_topics_default]" size="3" maxlength="3" value="<?php echo $pun_config['o_disp_topics_default'] ?>" />
+										<span><?php echo $lang_admin_options['Topics per page help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Posts per page label'] ?></th>
+									<td>
+										<input type="text" name="form[disp_posts_default]" size="3" maxlength="3" value="<?php echo $pun_config['o_disp_posts_default'] ?>" />
+										<span><?php echo $lang_admin_options['Posts per page help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Indent label'] ?></th>
+									<td>
+										<input type="text" name="form[indent_num_spaces]" size="3" maxlength="3" value="<?php echo $pun_config['o_indent_num_spaces'] ?>" />
+										<span><?php echo $lang_admin_options['Indent help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Quote depth label'] ?></th>
+									<td>
+										<input type="text" name="form[quote_depth]" size="3" maxlength="3" value="<?php echo $pun_config['o_quote_depth'] ?>" />
+										<span><?php echo $lang_admin_options['Quote depth help'] ?></span>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_options['Features subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Quick post label'] ?></th>
+									<td>
+										<input type="radio" name="form[quickpost]" value="1"<?php if ($pun_config['o_quickpost'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[quickpost]" value="0"<?php if ($pun_config['o_quickpost'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Quick post help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Users online label'] ?></th>
+									<td>
+										<input type="radio" name="form[users_online]" value="1"<?php if ($pun_config['o_users_online'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[users_online]" value="0"<?php if ($pun_config['o_users_online'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Users online help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><a name="censoring"><?php echo $lang_admin_options['Censor words label'] ?></a></th>
+									<td>
+										<input type="radio" name="form[censoring]" value="1"<?php if ($pun_config['o_censoring'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[censoring]" value="0"<?php if ($pun_config['o_censoring'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php printf($lang_admin_options['Censor words help'], '<a href="admin_censoring.php">'.$lang_admin_common['Censoring'].'</a>') ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><a name="signatures"><?php echo $lang_admin_options['Signatures label'] ?></a></th>
+									<td>
+										<input type="radio" name="form[signatures]" value="1"<?php if ($pun_config['o_signatures'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[signatures]" value="0"<?php if ($pun_config['o_signatures'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Signatures help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><a name="ranks"><?php echo $lang_admin_options['User ranks label'] ?></a></th>
+									<td>
+										<input type="radio" name="form[ranks]" value="1"<?php if ($pun_config['o_ranks'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[ranks]" value="0"<?php if ($pun_config['o_ranks'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php printf($lang_admin_options['User ranks help'], '<a href="admin_ranks.php">'.$lang_admin_common['Ranks'].'</a>') ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['User has posted label'] ?></th>
+									<td>
+										<input type="radio" name="form[show_dot]" value="1"<?php if ($pun_config['o_show_dot'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[show_dot]" value="0"<?php if ($pun_config['o_show_dot'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['User has posted help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Topic views label'] ?></th>
+									<td>
+										<input type="radio" name="form[topic_views]" value="1"<?php if ($pun_config['o_topic_views'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[topic_views]" value="0"<?php if ($pun_config['o_topic_views'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Topic views help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Quick jump label'] ?></th>
+									<td>
+										<input type="radio" name="form[quickjump]" value="1"<?php if ($pun_config['o_quickjump'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[quickjump]" value="0"<?php if ($pun_config['o_quickjump'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Quick jump help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['GZip label'] ?></th>
+									<td>
+										<input type="radio" name="form[gzip]" value="1"<?php if ($pun_config['o_gzip'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[gzip]" value="0"<?php if ($pun_config['o_gzip'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['GZip help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Search all label'] ?></th>
+									<td>
+										<input type="radio" name="form[search_all_forums]" value="1"<?php if ($pun_config['o_search_all_forums'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[search_all_forums]" value="0"<?php if ($pun_config['o_search_all_forums'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Search all help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Menu items label'] ?></th>
+									<td>
+										<textarea name="form[additional_navlinks]" rows="3" cols="55"><?php echo pun_htmlspecialchars($pun_config['o_additional_navlinks']) ?></textarea>
+										<span><?php echo $lang_admin_options['Menu items help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Default feed label'] ?></th>
+									<td>
+										<input type="radio" name="form[feed_type]" value="0"<?php if ($pun_config['o_feed_type'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_options['None'] ?></strong>   <input type="radio" name="form[feed_type]" value="1"<?php if ($pun_config['o_feed_type'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_options['RSS'] ?></strong>   <input type="radio" name="form[feed_type]" value="2"<?php if ($ [...]
+										<span><?php echo $lang_admin_options['Default feed help'] ?></span>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_options['Reports subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Reporting method label'] ?></th>
+									<td>
+										<input type="radio" name="form[report_method]" value="0"<?php if ($pun_config['o_report_method'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_options['Internal'] ?></strong>   <input type="radio" name="form[report_method]" value="1"<?php if ($pun_config['o_report_method'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_options['By e-mail'] ?></strong>   <input type="radio" name="form[report [...]
+										<span><?php echo $lang_admin_options['Reporting method help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Mailing list label'] ?></th>
+									<td>
+										<textarea name="form[mailing_list]" rows="5" cols="55"><?php echo pun_htmlspecialchars($pun_config['o_mailing_list']) ?></textarea>
+										<span><?php echo $lang_admin_options['Mailing list help'] ?></span>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_options['Avatars subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Use avatars label'] ?></th>
+									<td>
+										<input type="radio" name="form[avatars]" value="1"<?php if ($pun_config['o_avatars'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[avatars]" value="0"<?php if ($pun_config['o_avatars'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Use avatars help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Upload directory label'] ?></th>
+									<td>
+										<input type="text" name="form[avatars_dir]" size="35" maxlength="50" value="<?php echo pun_htmlspecialchars($pun_config['o_avatars_dir']) ?>" />
+										<span><?php echo $lang_admin_options['Upload directory help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Max width label'] ?></th>
+									<td>
+										<input type="text" name="form[avatars_width]" size="5" maxlength="5" value="<?php echo $pun_config['o_avatars_width'] ?>" />
+										<span><?php echo $lang_admin_options['Max width help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Max height label'] ?></th>
+									<td>
+										<input type="text" name="form[avatars_height]" size="5" maxlength="5" value="<?php echo $pun_config['o_avatars_height'] ?>" />
+										<span><?php echo $lang_admin_options['Max height help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Max size label'] ?></th>
+									<td>
+										<input type="text" name="form[avatars_size]" size="6" maxlength="6" value="<?php echo $pun_config['o_avatars_size'] ?>" />
+										<span><?php echo $lang_admin_options['Max size help'] ?></span>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_options['E-mail subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Admin e-mail label'] ?></th>
+									<td>
+										<input type="text" name="form[admin_email]" size="50" maxlength="80" value="<?php echo $pun_config['o_admin_email'] ?>" />
+										<span><?php echo $lang_admin_options['Admin e-mail help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Webmaster e-mail label'] ?></th>
+									<td>
+										<input type="text" name="form[webmaster_email]" size="50" maxlength="80" value="<?php echo $pun_config['o_webmaster_email'] ?>" />
+										<span><?php echo $lang_admin_options['Webmaster e-mail help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Subscriptions label'] ?></th>
+									<td>
+										<input type="radio" name="form[subscriptions]" value="1"<?php if ($pun_config['o_subscriptions'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[subscriptions]" value="0"<?php if ($pun_config['o_subscriptions'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Subscriptions help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['SMTP address label'] ?></th>
+									<td>
+										<input type="text" name="form[smtp_host]" size="30" maxlength="100" value="<?php echo pun_htmlspecialchars($pun_config['o_smtp_host']) ?>" />
+										<span><?php echo $lang_admin_options['SMTP address help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['SMTP username label'] ?></th>
+									<td>
+										<input type="text" name="form[smtp_user]" size="25" maxlength="50" value="<?php echo pun_htmlspecialchars($pun_config['o_smtp_user']) ?>" />
+										<span><?php echo $lang_admin_options['SMTP username help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['SMTP password label'] ?></th>
+									<td>
+										<input type="text" name="form[smtp_pass]" size="25" maxlength="50" value="<?php echo pun_htmlspecialchars($pun_config['o_smtp_pass']) ?>" />
+										<span><?php echo $lang_admin_options['SMTP password help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['SMTP SSL label'] ?></th>
+									<td>
+										<input type="radio" name="form[smtp_ssl]" value="1"<?php if ($pun_config['o_smtp_ssl'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[smtp_ssl]" value="0"<?php if ($pun_config['o_smtp_ssl'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['SMTP SSL help'] ?></span>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_options['Registration subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Allow new label'] ?></th>
+									<td>
+										<input type="radio" name="form[regs_allow]" value="1"<?php if ($pun_config['o_regs_allow'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[regs_allow]" value="0"<?php if ($pun_config['o_regs_allow'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Allow new help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Verify label'] ?></th>
+									<td>
+										<input type="radio" name="form[regs_verify]" value="1"<?php if ($pun_config['o_regs_verify'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[regs_verify]" value="0"<?php if ($pun_config['o_regs_verify'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Verify help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Report new label'] ?></th>
+									<td>
+										<input type="radio" name="form[regs_report]" value="1"<?php if ($pun_config['o_regs_report'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[regs_report]" value="0"<?php if ($pun_config['o_regs_report'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Report new help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Use rules label'] ?></th>
+									<td>
+										<input type="radio" name="form[rules]" value="1"<?php if ($pun_config['o_rules'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[rules]" value="0"<?php if ($pun_config['o_rules'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Use rules help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Rules label'] ?></th>
+									<td>
+										<textarea name="form[rules_message]" rows="10" cols="55"><?php echo pun_htmlspecialchars($pun_config['o_rules_message']) ?></textarea>
+										<span><?php echo $lang_admin_options['Rules help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['E-mail default label'] ?></th>
+									<td>
+										<span><?php echo $lang_admin_options['E-mail default help'] ?></span>
+										<input type="radio" name="form[default_email_setting]" value="0"<?php if ($pun_config['o_default_email_setting'] == '0') echo ' checked="checked"' ?> /> <?php echo $lang_admin_options['Display e-mail label'] ?><br />
+										<input type="radio" name="form[default_email_setting]" value="1"<?php if ($pun_config['o_default_email_setting'] == '1') echo ' checked="checked"' ?> /> <?php echo $lang_admin_options['Hide allow form label'] ?><br />
+										<input type="radio" name="form[default_email_setting]" value="2"<?php if ($pun_config['o_default_email_setting'] == '2') echo ' checked="checked"' ?> /> <?php echo $lang_admin_options['Hide both label'] ?><br />
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_options['Announcement subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Display announcement label'] ?></th>
+									<td>
+										<input type="radio" name="form[announcement]" value="1"<?php if ($pun_config['o_announcement'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[announcement]" value="0"<?php if ($pun_config['o_announcement'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Display announcement help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Announcement message label'] ?></th>
+									<td>
+										<textarea name="form[announcement_message]" rows="5" cols="55"><?php echo pun_htmlspecialchars($pun_config['o_announcement_message']) ?></textarea>
+										<span><?php echo $lang_admin_options['Announcement message help'] ?></span>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_options['Maintenance subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><a name="maintenance"><?php echo $lang_admin_options['Maintenance mode label'] ?></a></th>
+									<td>
+										<input type="radio" name="form[maintenance]" value="1"<?php if ($pun_config['o_maintenance'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[maintenance]" value="0"<?php if ($pun_config['o_maintenance'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_options['Maintenance mode help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_options['Maintenance message label'] ?></th>
+									<td>
+										<textarea name="form[maintenance_message]" rows="5" cols="55"><?php echo pun_htmlspecialchars($pun_config['o_maintenance_message']) ?></textarea>
+										<span><?php echo $lang_admin_options['Maintenance message help'] ?></span>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+				<p class="submitend"><input type="submit" name="save" value="<?php echo $lang_admin_common['Save changes'] ?>" /></p>
+			</form>
+		</div>
+	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_permissions.php b/admin_permissions.php
new file mode 100644
index 0000000..18fef6c
--- /dev/null
+++ b/admin_permissions.php
@@ -0,0 +1,177 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if ($pun_user['g_id'] != PUN_ADMIN)
+	message($lang_common['No permission']);
+
+// Load the admin_permissions.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_permissions.php';
+
+if (isset($_POST['form_sent']))
+{
+	confirm_referrer('admin_permissions.php');
+
+	$form = array_map('intval', $_POST['form']);
+
+	foreach ($form as $key => $input)
+	{
+		// Only update values that have changed
+		if (array_key_exists('p_'.$key, $pun_config) && $pun_config['p_'.$key] != $input)
+			$db->query('UPDATE '.$db->prefix.'config SET conf_value='.$input.' WHERE conf_name=\'p_'.$db->escape($key).'\'') or error('Unable to update board config', __FILE__, __LINE__, $db->error());
+	}
+
+	// Regenerate the config cache
+	if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+		require PUN_ROOT.'include/cache.php';
+
+	generate_config_cache();
+
+	redirect('admin_permissions.php', $lang_admin_permissions['Perms updated redirect']);
+}
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Permissions']);
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('permissions');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_permissions['Permissions head'] ?></span></h2>
+		<div class="box">
+			<form method="post" action="admin_permissions.php">
+				<p class="submittop"><input type="submit" name="save" value="<?php echo $lang_admin_common['Save changes'] ?>" /></p>
+				<div class="inform">
+					<input type="hidden" name="form_sent" value="1" />
+					<fieldset>
+						<legend><?php echo $lang_admin_permissions['Posting subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_permissions['BBCode label'] ?></th>
+									<td>
+										<input type="radio" name="form[message_bbcode]" value="1"<?php if ($pun_config['p_message_bbcode'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[message_bbcode]" value="0"<?php if ($pun_config['p_message_bbcode'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_permissions['BBCode help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_permissions['Image tag label'] ?></th>
+									<td>
+										<input type="radio" name="form[message_img_tag]" value="1"<?php if ($pun_config['p_message_img_tag'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[message_img_tag]" value="0"<?php if ($pun_config['p_message_img_tag'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_permissions['Image tag help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_permissions['All caps message label'] ?></th>
+									<td>
+										<input type="radio" name="form[message_all_caps]" value="1"<?php if ($pun_config['p_message_all_caps'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[message_all_caps]" value="0"<?php if ($pun_config['p_message_all_caps'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_permissions['All caps message help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_permissions['All caps subject label'] ?></th>
+									<td>
+										<input type="radio" name="form[subject_all_caps]" value="1"<?php if ($pun_config['p_subject_all_caps'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[subject_all_caps]" value="0"<?php if ($pun_config['p_subject_all_caps'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_permissions['All caps subject help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_permissions['Require e-mail label'] ?></th>
+									<td>
+										<input type="radio" name="form[force_guest_email]" value="1"<?php if ($pun_config['p_force_guest_email'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[force_guest_email]" value="0"<?php if ($pun_config['p_force_guest_email'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_permissions['Require e-mail help'] ?></span>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_permissions['Signatures subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_permissions['BBCode sigs label'] ?></th>
+									<td>
+										<input type="radio" name="form[sig_bbcode]" value="1"<?php if ($pun_config['p_sig_bbcode'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[sig_bbcode]" value="0"<?php if ($pun_config['p_sig_bbcode'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_permissions['BBCode sigs help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_permissions['Image tag sigs label'] ?></th>
+									<td>
+										<input type="radio" name="form[sig_img_tag]" value="1"<?php if ($pun_config['p_sig_img_tag'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[sig_img_tag]" value="0"<?php if ($pun_config['p_sig_img_tag'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_permissions['Image tag sigs help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_permissions['All caps sigs label'] ?></th>
+									<td>
+										<input type="radio" name="form[sig_all_caps]" value="1"<?php if ($pun_config['p_sig_all_caps'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[sig_all_caps]" value="0"<?php if ($pun_config['p_sig_all_caps'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_permissions['All caps sigs help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_permissions['Max sig length label'] ?></th>
+									<td>
+										<input type="text" name="form[sig_length]" size="5" maxlength="5" value="<?php echo $pun_config['p_sig_length'] ?>" />
+										<span><?php echo $lang_admin_permissions['Max sig length help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_permissions['Max sig lines label'] ?></th>
+									<td>
+										<input type="text" name="form[sig_lines]" size="3" maxlength="3" value="<?php echo $pun_config['p_sig_lines'] ?>" />
+										<span><?php echo $lang_admin_permissions['Max sig lines help'] ?></span>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_permissions['Registration subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_permissions['Banned e-mail label'] ?></th>
+									<td>
+										<input type="radio" name="form[allow_banned_email]" value="1"<?php if ($pun_config['p_allow_banned_email'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[allow_banned_email]" value="0"<?php if ($pun_config['p_allow_banned_email'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_permissions['Banned e-mail help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_permissions['Duplicate e-mail label'] ?></th>
+									<td>
+										<input type="radio" name="form[allow_dupe_email]" value="1"<?php if ($pun_config['p_allow_dupe_email'] == '1') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="form[allow_dupe_email]" value="0"<?php if ($pun_config['p_allow_dupe_email'] == '0') echo ' checked="checked"' ?> /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_permissions['Duplicate e-mail help'] ?></span>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+				<p class="submitend"><input type="submit" name="save" value="<?php echo $lang_admin_common['Save changes'] ?>" /></p>
+			</form>
+		</div>
+	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_prune.php b/admin_prune.php
new file mode 100644
index 0000000..d2ba043
--- /dev/null
+++ b/admin_prune.php
@@ -0,0 +1,217 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if ($pun_user['g_id'] != PUN_ADMIN)
+	message($lang_common['No permission']);
+
+// Load the admin_prune.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_prune.php';
+
+if (isset($_GET['action']) || isset($_POST['prune']) || isset($_POST['prune_comply']))
+{
+	$prune_from = trim($_POST['prune_from']);
+	$prune_sticky = intval($_POST['prune_sticky']);
+
+	if (isset($_POST['prune_comply']))
+	{
+		confirm_referrer('admin_prune.php');
+
+		$prune_days = intval($_POST['prune_days']);
+		$prune_date = ($prune_days) ? time() - ($prune_days * 86400) : -1;
+
+		@set_time_limit(0);
+
+		if ($prune_from == 'all')
+		{
+			$result = $db->query('SELECT id FROM '.$db->prefix.'forums') or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
+			$num_forums = $db->num_rows($result);
+
+			for ($i = 0; $i < $num_forums; ++$i)
+			{
+				$fid = $db->result($result, $i);
+
+				prune($fid, $prune_sticky, $prune_date);
+				update_forum($fid);
+			}
+		}
+		else
+		{
+			$prune_from = intval($prune_from);
+			prune($prune_from, $prune_sticky, $prune_date);
+			update_forum($prune_from);
+		}
+
+		// Locate any "orphaned redirect topics" and delete them
+		$result = $db->query('SELECT t1.id FROM '.$db->prefix.'topics AS t1 LEFT JOIN '.$db->prefix.'topics AS t2 ON t1.moved_to=t2.id WHERE t2.id IS NULL AND t1.moved_to IS NOT NULL') or error('Unable to fetch redirect topics', __FILE__, __LINE__, $db->error());
+		$num_orphans = $db->num_rows($result);
+
+		if ($num_orphans)
+		{
+			for ($i = 0; $i < $num_orphans; ++$i)
+				$orphans[] = $db->result($result, $i);
+
+			$db->query('DELETE FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $orphans).')') or error('Unable to delete redirect topics', __FILE__, __LINE__, $db->error());
+		}
+
+		redirect('admin_prune.php', $lang_admin_prune['Posts pruned redirect']);
+	}
+
+	$prune_days = trim($_POST['req_prune_days']);
+	if ($prune_days == '' || preg_match('/[^0-9]/', $prune_days))
+		message($lang_admin_prune['Must be integer message']);
+
+	$prune_date = time() - ($prune_days * 86400);
+
+	// Concatenate together the query for counting number of topics to prune
+	$sql = 'SELECT COUNT(id) FROM '.$db->prefix.'topics WHERE last_post<'.$prune_date.' AND moved_to IS NULL';
+
+	if ($prune_sticky == '0')
+		$sql .= ' AND sticky=\'0\'';
+
+	if ($prune_from != 'all')
+	{
+		$prune_from = intval($prune_from);
+		$sql .= ' AND forum_id='.$prune_from;
+
+		// Fetch the forum name (just for cosmetic reasons)
+		$result = $db->query('SELECT forum_name FROM '.$db->prefix.'forums WHERE id='.$prune_from) or error('Unable to fetch forum name', __FILE__, __LINE__, $db->error());
+		$forum = '"'.pun_htmlspecialchars($db->result($result)).'"';
+	}
+	else
+		$forum = $lang_admin_prune['All forums'];
+
+	$result = $db->query($sql) or error('Unable to fetch topic prune count', __FILE__, __LINE__, $db->error());
+	$num_topics = $db->result($result);
+
+	if (!$num_topics)
+		message(sprintf($lang_admin_prune['No old topics message'], $prune_days));
+
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Prune']);
+	define('PUN_ACTIVE_PAGE', 'admin');
+	require PUN_ROOT.'header.php';
+
+	generate_admin_menu('prune');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_prune['Prune head'] ?></span></h2>
+		<div class="box">
+			<form method="post" action="admin_prune.php?action=foo">
+				<div class="inform">
+					<input type="hidden" name="prune_days" value="<?php echo $prune_days ?>" />
+					<input type="hidden" name="prune_sticky" value="<?php echo $prune_sticky ?>" />
+					<input type="hidden" name="prune_from" value="<?php echo $prune_from ?>" />
+					<fieldset>
+						<legend><?php echo $lang_admin_prune['Confirm prune subhead'] ?></legend>
+						<div class="infldset">
+							<p><?php printf($lang_admin_prune['Confirm prune info'], $prune_days, $forum, forum_number_format($num_topics)) ?></p>
+							<p class="warntext"><?php echo $lang_admin_prune['Confirm prune warn'] ?></p>
+						</div>
+					</fieldset>
+				</div>
+				<p class="buttons"><input type="submit" name="prune_comply" value="<?php echo $lang_admin_common['Prune'] ?>" /><a href="javascript:history.go(-1)"><?php echo $lang_admin_common['Go back'] ?></a></p>
+			</form>
+		</div>
+	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+
+else
+{
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Prune']);
+	$required_fields = array('req_prune_days' => $lang_admin_prune['Days old label']);
+	$focus_element = array('prune', 'req_prune_days');
+	define('PUN_ACTIVE_PAGE', 'admin');
+	require PUN_ROOT.'header.php';
+
+	generate_admin_menu('prune');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_prune['Prune head'] ?></span></h2>
+		<div class="box">
+			<form id="prune" method="post" action="admin_prune.php?action=foo" onsubmit="return process_form(this)">
+				<div class="inform">
+					<input type="hidden" name="form_sent" value="1" />
+					<fieldset>
+						<legend><?php echo $lang_admin_prune['Prune subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_prune['Days old label'] ?></th>
+									<td>
+										<input type="text" name="req_prune_days" size="3" maxlength="3" tabindex="1" />
+										<span><?php echo $lang_admin_prune['Days old help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_prune['Prune sticky label'] ?></th>
+									<td>
+										<input type="radio" name="prune_sticky" value="1" tabindex="2" checked="checked" /> <strong><?php echo $lang_admin_common['Yes'] ?></strong>   <input type="radio" name="prune_sticky" value="0" /> <strong><?php echo $lang_admin_common['No'] ?></strong>
+										<span><?php echo $lang_admin_prune['Prune sticky help'] ?></span>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_prune['Prune from label'] ?></th>
+									<td>
+										<select name="prune_from" tabindex="3">
+											<option value="all"><?php echo $lang_admin_prune['All forums'] ?></option>
+<?php
+
+	$result = $db->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name FROM '.$db->prefix.'categories AS c INNER JOIN '.$db->prefix.'forums AS f ON c.id=f.cat_id WHERE f.redirect_url IS NULL ORDER BY c.disp_position, c.id, f.disp_position') or error('Unable to fetch category/forum list', __FILE__, __LINE__, $db->error());
+
+	$cur_category = 0;
+	while ($forum = $db->fetch_assoc($result))
+	{
+		if ($forum['cid'] != $cur_category) // Are we still in the same category?
+		{
+			if ($cur_category)
+				echo "\t\t\t\t\t\t\t\t\t\t\t".'</optgroup>'."\n";
+
+			echo "\t\t\t\t\t\t\t\t\t\t\t".'<optgroup label="'.pun_htmlspecialchars($forum['cat_name']).'">'."\n";
+			$cur_category = $forum['cid'];
+		}
+
+		echo "\t\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$forum['fid'].'">'.pun_htmlspecialchars($forum['forum_name']).'</option>'."\n";
+	}
+
+?>
+											</optgroup>
+										</select>
+										<span><?php echo $lang_admin_prune['Prune from help'] ?></span>
+									</td>
+								</tr>
+							</table>
+							<p class="topspace"><?php printf($lang_admin_prune['Prune info'], '<a href="admin_options.php#maintenance">'.$lang_admin_common['Maintenance mode'].'</a>') ?></p>
+							<div class="fsetsubmit"><input type="submit" name="prune" value="<?php echo $lang_admin_common['Prune'] ?>" tabindex="5" /></div>
+						</div>
+					</fieldset>
+				</div>
+			</form>
+		</div>
+	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
diff --git a/admin_ranks.php b/admin_ranks.php
new file mode 100644
index 0000000..c832d95
--- /dev/null
+++ b/admin_ranks.php
@@ -0,0 +1,186 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if ($pun_user['g_id'] != PUN_ADMIN)
+	message($lang_common['No permission']);
+
+// Load the admin_ranks.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_ranks.php';
+
+// Add a rank
+if (isset($_POST['add_rank']))
+{
+	confirm_referrer('admin_ranks.php');
+
+	$rank = pun_trim($_POST['new_rank']);
+	$min_posts = trim($_POST['new_min_posts']);
+
+	if ($rank == '')
+		message($lang_admin_ranks['Must enter title message']);
+
+	if ($min_posts == '' || preg_match('/[^0-9]/', $min_posts))
+		message($lang_admin_ranks['Must be integer message']);
+
+	// Make sure there isn't already a rank with the same min_posts value
+	$result = $db->query('SELECT 1 FROM '.$db->prefix.'ranks WHERE min_posts='.$min_posts) or error('Unable to fetch rank info', __FILE__, __LINE__, $db->error());
+	if ($db->num_rows($result))
+		message(sprintf($lang_admin_ranks['Dupe min posts message'], $min_posts));
+
+	$db->query('INSERT INTO '.$db->prefix.'ranks (rank, min_posts) VALUES(\''.$db->escape($rank).'\', '.$min_posts.')') or error('Unable to add rank', __FILE__, __LINE__, $db->error());
+
+	// Regenerate the ranks cache
+	if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+		require PUN_ROOT.'include/cache.php';
+
+	generate_ranks_cache();
+
+	redirect('admin_ranks.php', $lang_admin_ranks['Rank added redirect']);
+}
+
+
+// Update a rank
+else if (isset($_POST['update']))
+{
+	confirm_referrer('admin_ranks.php');
+
+	$id = intval(key($_POST['update']));
+
+	$rank = pun_trim($_POST['rank'][$id]);
+	$min_posts = trim($_POST['min_posts'][$id]);
+
+	if ($rank == '')
+		message($lang_admin_ranks['Must enter title message']);
+
+	if ($min_posts == '' || preg_match('/[^0-9]/', $min_posts))
+		message($lang_admin_ranks['Must be integer message']);
+
+	// Make sure there isn't already a rank with the same min_posts value
+	$result = $db->query('SELECT 1 FROM '.$db->prefix.'ranks WHERE id!='.$id.' AND min_posts='.$min_posts) or error('Unable to fetch rank info', __FILE__, __LINE__, $db->error());
+	if ($db->num_rows($result))
+		message(sprintf($lang_admin_ranks['Dupe min posts message'], $min_posts));
+
+	$db->query('UPDATE '.$db->prefix.'ranks SET rank=\''.$db->escape($rank).'\', min_posts='.$min_posts.' WHERE id='.$id) or error('Unable to update rank', __FILE__, __LINE__, $db->error());
+
+	// Regenerate the ranks cache
+	if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+		require PUN_ROOT.'include/cache.php';
+
+	generate_ranks_cache();
+
+	redirect('admin_ranks.php', $lang_admin_ranks['Rank updated redirect']);
+}
+
+
+// Remove a rank
+else if (isset($_POST['remove']))
+{
+	confirm_referrer('admin_ranks.php');
+
+	$id = intval(key($_POST['remove']));
+
+	$db->query('DELETE FROM '.$db->prefix.'ranks WHERE id='.$id) or error('Unable to delete rank', __FILE__, __LINE__, $db->error());
+
+	// Regenerate the ranks cache
+	if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+		require PUN_ROOT.'include/cache.php';
+
+	generate_ranks_cache();
+
+	redirect('admin_ranks.php', $lang_admin_ranks['Rank removed redirect']);
+}
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Ranks']);
+$focus_element = array('ranks', 'new_rank');
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('ranks');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_ranks['Ranks head'] ?></span></h2>
+		<div class="box">
+			<form id="ranks" method="post" action="admin_ranks.php?action=foo">
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_ranks['Add rank subhead'] ?></legend>
+						<div class="infldset">
+							<p><?php echo $lang_admin_ranks['Add rank info'].' '.($pun_config['o_ranks'] == '1' ? sprintf($lang_admin_ranks['Ranks enabled'], '<a href="admin_options.php#ranks">'.$lang_admin_common['Options'].'</a>') : sprintf($lang_admin_ranks['Ranks disabled'], '<a href="admin_options.php#ranks">'.$lang_admin_common['Options'].'</a>')) ?></p>
+							<table cellspacing="0">
+							<thead>
+								<tr>
+									<th class="tcl" scope="col"><?php echo $lang_admin_ranks['Rank title label'] ?></th>
+									<th class="tc2" scope="col"><?php echo $lang_admin_ranks['Minimum posts label'] ?></th>
+									<th class="hidehead" scope="col"><?php echo $lang_admin_ranks['Actions label'] ?></th>
+								</tr>
+							</thead>
+							<tbody>
+								<tr>
+									<td class="tcl"><input type="text" name="new_rank" size="24" maxlength="50" tabindex="1" /></td>
+									<td class="tc2"><input type="text" name="new_min_posts" size="7" maxlength="7" tabindex="2" /></td>
+									<td><input type="submit" name="add_rank" value="<?php echo $lang_admin_common['Add'] ?>" tabindex="3" /></td>
+								</tr>
+							</tbody>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_ranks['Edit remove subhead'] ?></legend>
+						<div class="infldset">
+<?php
+
+$result = $db->query('SELECT id, rank, min_posts FROM '.$db->prefix.'ranks ORDER BY min_posts') or error('Unable to fetch rank list', __FILE__, __LINE__, $db->error());
+if ($db->num_rows($result))
+{
+
+?>
+							<table cellspacing="0">
+							<thead>
+								<tr>
+									<th class="tcl" scope="col"><?php echo $lang_admin_ranks['Rank title label'] ?></th>
+									<th class="tc2" scope="col"><?php echo $lang_admin_ranks['Minimum posts label'] ?></th>
+									<th class="hidehead" scope="col"><?php echo $lang_admin_ranks['Actions label'] ?></th>
+								</tr>
+							</thead>
+							<tbody>
+<?php
+
+	while ($cur_rank = $db->fetch_assoc($result))
+		echo "\t\t\t\t\t\t\t\t".'<tr><td class="tcl"><input type="text" name="rank['.$cur_rank['id'].']" value="'.pun_htmlspecialchars($cur_rank['rank']).'" size="24" maxlength="50" /></td><td class="tc2"><input type="text" name="min_posts['.$cur_rank['id'].']" value="'.$cur_rank['min_posts'].'" size="7" maxlength="7" /></td><td><input type="submit" name="update['.$cur_rank['id'].']" value="'.$lang_admin_common['Update'].'" /> <input type="submit" name="remove['.$cur_rank['id'].']" value= [...]
+
+?>
+							</tbody>
+							</table>
+<?php
+
+}
+else
+	echo "\t\t\t\t\t\t\t".'<p>'.$lang_admin_ranks['No ranks in list'].'</p>'."\n";
+
+?>
+						</div>
+					</fieldset>
+				</div>
+			</form>
+		</div>
+	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/admin_reports.php b/admin_reports.php
new file mode 100644
index 0000000..634b848
--- /dev/null
+++ b/admin_reports.php
@@ -0,0 +1,176 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if (!$pun_user['is_admmod'])
+	message($lang_common['No permission']);
+
+// Load the admin_reports.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_reports.php';
+
+// Zap a report
+if (isset($_POST['zap_id']))
+{
+	confirm_referrer('admin_reports.php');
+
+	$zap_id = intval(key($_POST['zap_id']));
+
+	$result = $db->query('SELECT zapped FROM '.$db->prefix.'reports WHERE id='.$zap_id) or error('Unable to fetch report info', __FILE__, __LINE__, $db->error());
+	$zapped = $db->result($result);
+
+	if ($zapped == '')
+		$db->query('UPDATE '.$db->prefix.'reports SET zapped='.time().', zapped_by='.$pun_user['id'].' WHERE id='.$zap_id) or error('Unable to zap report', __FILE__, __LINE__, $db->error());
+
+	redirect('admin_reports.php', $lang_admin_reports['Report zapped redirect']);
+}
+
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Reports']);
+define('PUN_ACTIVE_PAGE', 'admin');
+require PUN_ROOT.'header.php';
+
+generate_admin_menu('reports');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_reports['New reports head'] ?></span></h2>
+		<div class="box">
+			<form method="post" action="admin_reports.php?action=zap">
+<?php
+
+$result = $db->query('SELECT r.id, r.topic_id, r.forum_id, r.reported_by, r.created, r.message, p.id AS pid, t.subject, f.forum_name, u.username AS reporter FROM '.$db->prefix.'reports AS r LEFT JOIN '.$db->prefix.'posts AS p ON r.post_id=p.id LEFT JOIN '.$db->prefix.'topics AS t ON r.topic_id=t.id LEFT JOIN '.$db->prefix.'forums AS f ON r.forum_id=f.id LEFT JOIN '.$db->prefix.'users AS u ON r.reported_by=u.id WHERE r.zapped IS NULL ORDER BY created DESC') or error('Unable to fetch repor [...]
+
+if ($db->num_rows($result))
+{
+	while ($cur_report = $db->fetch_assoc($result))
+	{
+		$reporter = ($cur_report['reporter'] != '') ? '<a href="profile.php?id='.$cur_report['reported_by'].'">'.pun_htmlspecialchars($cur_report['reporter']).'</a>' : $lang_admin_reports['Deleted user'];
+		$forum = ($cur_report['forum_name'] != '') ? '<span><a href="viewforum.php?id='.$cur_report['forum_id'].'">'.pun_htmlspecialchars($cur_report['forum_name']).'</a></span>' : '<span>'.$lang_admin_reports['Deleted'].'</span>';
+		$topic = ($cur_report['subject'] != '') ? '<span>» <a href="viewtopic.php?id='.$cur_report['topic_id'].'">'.pun_htmlspecialchars($cur_report['subject']).'</a></span>' : '<span>» '.$lang_admin_reports['Deleted'].'</span>';
+		$post = str_replace("\n", '<br />', pun_htmlspecialchars($cur_report['message']));
+		$post_id = ($cur_report['pid'] != '') ? '<span>» <a href="viewtopic.php?pid='.$cur_report['pid'].'#p'.$cur_report['pid'].'">Post #'.$cur_report['pid'].'</a></span>' : '<span>» '.$lang_admin_reports['Deleted'].'</span>';
+		$report_location = array($forum, $topic, $post_id);
+
+?>
+				<div class="inform">
+					<fieldset>
+						<legend><?php printf($lang_admin_reports['Report subhead'], format_time($cur_report['created'])) ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php printf($lang_admin_reports['Reported by'], $reporter) ?></th>
+									<td class="location"><?php echo implode(' ', $report_location) ?></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_reports['Reason'] ?><div><input type="submit" name="zap_id[<?php echo $cur_report['id'] ?>]" value="<?php echo $lang_admin_reports['Zap'] ?>" /></div></th>
+									<td><?php echo $post ?></td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+<?php
+
+	}
+}
+else
+{
+
+?>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_common['None'] ?></legend>
+						<div class="infldset">
+							<p><?php echo $lang_admin_reports['No new reports'] ?></p>
+						</div>
+					</fieldset>
+				</div>
+<?php
+
+}
+
+?>
+			</form>
+		</div>
+	</div>
+
+	<div class="blockform block2">
+		<h2><span><?php echo $lang_admin_reports['Last 10 head'] ?></span></h2>
+		<div class="box">
+			<div class="fakeform">
+<?php
+
+$result = $db->query('SELECT r.id, r.topic_id, r.forum_id, r.reported_by, r.message, r.zapped, r.zapped_by AS zapped_by_id, p.id AS pid, t.subject, f.forum_name, u.username AS reporter, u2.username AS zapped_by FROM '.$db->prefix.'reports AS r LEFT JOIN '.$db->prefix.'posts AS p ON r.post_id=p.id LEFT JOIN '.$db->prefix.'topics AS t ON r.topic_id=t.id LEFT JOIN '.$db->prefix.'forums AS f ON r.forum_id=f.id LEFT JOIN '.$db->prefix.'users AS u ON r.reported_by=u.id LEFT JOIN '.$db->prefix. [...]
+
+if ($db->num_rows($result))
+{
+	while ($cur_report = $db->fetch_assoc($result))
+	{
+		$reporter = ($cur_report['reporter'] != '') ? '<a href="profile.php?id='.$cur_report['reported_by'].'">'.pun_htmlspecialchars($cur_report['reporter']).'</a>' : $lang_admin_reports['Deleted user'];
+		$forum = ($cur_report['forum_name'] != '') ? '<span><a href="viewforum.php?id='.$cur_report['forum_id'].'">'.pun_htmlspecialchars($cur_report['forum_name']).'</a></span>' : '<span>'.$lang_admin_reports['Deleted'].'</span>';
+		$topic = ($cur_report['subject'] != '') ? '<span>» <a href="viewtopic.php?id='.$cur_report['topic_id'].'">'.pun_htmlspecialchars($cur_report['subject']).'</a></span>' : '<span>» '.$lang_admin_reports['Deleted'].'</span>';
+		$post = str_replace("\n", '<br />', pun_htmlspecialchars($cur_report['message']));
+		$post_id = ($cur_report['pid'] != '') ? '<span>» <a href="viewtopic.php?pid='.$cur_report['pid'].'#p'.$cur_report['pid'].'">Post #'.$cur_report['pid'].'</a></span>' : '<span>» '.$lang_admin_reports['Deleted'].'</span>';
+		$zapped_by = ($cur_report['zapped_by'] != '') ? '<a href="profile.php?id='.$cur_report['zapped_by_id'].'">'.pun_htmlspecialchars($cur_report['zapped_by']).'</a>' : $lang_admin_reports['NA'];
+		$zapped_by = ($cur_report['zapped_by'] != '') ? '<strong>'.pun_htmlspecialchars($cur_report['zapped_by']).'</strong>' : $lang_admin_reports['NA'];
+		$report_location = array($forum, $topic, $post_id);
+
+?>
+				<div class="inform">
+					<fieldset>
+						<legend><?php printf($lang_admin_reports['Zapped subhead'], format_time($cur_report['zapped']), $zapped_by) ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php printf($lang_admin_reports['Reported by'], $reporter) ?></th>
+									<td class="location"><?php echo implode(' ', $report_location) ?></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_reports['Reason'] ?></th>
+									<td><?php echo $post ?></td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+<?php
+
+	}
+}
+else
+{
+
+?>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_common['None'] ?></legend>
+						<div class="infldset">
+							<p><?php echo $lang_admin_reports['No zapped reports'] ?></p>
+						</div>
+					</fieldset>
+				</div>
+<?php
+
+}
+
+?>
+			</div>
+		</div>
+	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
\ No newline at end of file
diff --git a/admin_users.php b/admin_users.php
new file mode 100644
index 0000000..9552d17
--- /dev/null
+++ b/admin_users.php
@@ -0,0 +1,619 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the admin template
+define('PUN_ADMIN_CONSOLE', 1);
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+require PUN_ROOT.'include/common_admin.php';
+
+
+if (!$pun_user['is_admmod'])
+	message($lang_common['No permission']);
+
+// Load the admin_users.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_users.php';
+
+// Show IP statistics for a certain user ID
+if (isset($_GET['ip_stats']))
+{
+	$ip_stats = intval($_GET['ip_stats']);
+	if ($ip_stats < 1)
+		message($lang_common['Bad request']);
+
+	// Fetch ip count
+	$result = $db->query('SELECT poster_ip, MAX(posted) AS last_used FROM '.$db->prefix.'posts WHERE poster_id='.$ip_stats.' GROUP BY poster_ip') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+	$num_ips = $db->num_rows($result);
+
+	// Determine the ip offset (based on $_GET['p'])
+	$num_pages = ceil($num_ips / 50);
+
+	$p = (!isset($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : intval($_GET['p']);
+	$start_from = 50 * ($p - 1);
+
+	// Generate paging links
+	$paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'admin_users.php?ip_stats='.$ip_stats );
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Users'], $lang_admin_users['Results head']);
+	define('PUN_ACTIVE_PAGE', 'admin');
+	require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+	<div class="inbox crumbsplus">
+		<ul class="crumbs">
+			<li><a href="admin_index.php"><?php echo $lang_admin_common['Admin'].' '.$lang_admin_common['Index'] ?></a></li>
+			<li><span>» </span><a href="admin_users.php"><?php echo $lang_admin_common['Users'] ?></a></li>
+			<li><span>» </span><strong><?php echo $lang_admin_users['Results head'] ?></strong></li>
+		</ul>
+		<div class="pagepost">
+			<p class="pagelink"><?php echo $paging_links ?></p>
+		</div>
+		<div class="clearer"></div>
+	</div>
+</div>
+
+<div id="users1" class="blocktable">
+	<h2><span><?php echo $lang_admin_users['Results head'] ?></span></h2>
+	<div class="box">
+		<div class="inbox">
+			<table cellspacing="0">
+			<thead>
+				<tr>
+					<th class="tcl" scope="col"><?php echo $lang_admin_users['Results IP address head'] ?></th>
+					<th class="tc2" scope="col"><?php echo $lang_admin_users['Results last used head'] ?></th>
+					<th class="tc3" scope="col"><?php echo $lang_admin_users['Results times found head'] ?></th>
+					<th class="tcr" scope="col"><?php echo $lang_admin_users['Results action head'] ?></th>
+				</tr>
+			</thead>
+			<tbody>
+<?php
+
+	$result = $db->query('SELECT poster_ip, MAX(posted) AS last_used, COUNT(id) AS used_times FROM '.$db->prefix.'posts WHERE poster_id='.$ip_stats.' GROUP BY poster_ip ORDER BY last_used DESC LIMIT '.$start_from.', 50') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+	if ($db->num_rows($result))
+	{
+		while ($cur_ip = $db->fetch_assoc($result))
+		{
+
+?>
+				<tr>
+					<td class="tcl"><a href="moderate.php?get_host=<?php echo $cur_ip['poster_ip'] ?>"><?php echo $cur_ip['poster_ip'] ?></a></td>
+					<td class="tc2"><?php echo format_time($cur_ip['last_used']) ?></td>
+					<td class="tc3"><?php echo $cur_ip['used_times'] ?></td>
+					<td class="tcr"><a href="admin_users.php?show_users=<?php echo $cur_ip['poster_ip'] ?>"><?php echo $lang_admin_users['Results find more link'] ?></a></td>
+				</tr>
+<?php
+
+		}
+	}
+	else
+		echo "\t\t\t\t".'<tr><td class="tcl" colspan="4">'.$lang_admin_users['Results no posts found'].'</td></tr>'."\n";
+
+?>
+			</tbody>
+			</table>
+		</div>
+	</div>
+</div>
+
+<div class="linksb">
+	<div class="inbox crumbsplus">
+		<div class="pagepost">
+			<p class="pagelink"><?php echo $paging_links ?></p>
+		</div>
+		<ul class="crumbs">
+			<li><a href="admin_index.php"><?php echo $lang_admin_common['Admin'].' '.$lang_admin_common['Index'] ?></a></li>
+			<li><span>» </span><a href="admin_users.php"><?php echo $lang_admin_common['Users'] ?></a></li>
+			<li><span>» </span><strong><?php echo $lang_admin_users['Results head'] ?></strong></li>
+		</ul>
+		<div class="clearer"></div>
+	</div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+
+if (isset($_GET['show_users']))
+{
+	$ip = trim($_GET['show_users']);
+
+	if (!@preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $ip) && !@preg_match('/^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0- [...]
+		message($lang_admin_users['Bad IP message']);
+
+	// Fetch user count
+	$result = $db->query('SELECT DISTINCT poster_id, poster FROM '.$db->prefix.'posts WHERE poster_ip=\''.$db->escape($ip).'\'') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+	$num_users = $db->num_rows($result);
+
+	// Determine the user offset (based on $_GET['p'])
+	$num_pages = ceil($num_users / 50);
+
+	$p = (!isset($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : intval($_GET['p']);
+	$start_from = 50 * ($p - 1);
+
+	// Generate paging links
+	$paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'admin_users.php?show_users='.$ip);
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Users'], $lang_admin_users['Results head']);
+	define('PUN_ACTIVE_PAGE', 'admin');
+	require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+	<div class="inbox crumbsplus">
+		<ul class="crumbs">
+			<li><a href="admin_index.php"><?php echo $lang_admin_common['Admin'].' '.$lang_admin_common['Index'] ?></a></li>
+			<li><span>» </span><a href="admin_users.php"><?php echo $lang_admin_common['Users'] ?></a></li>
+			<li><span>» </span><strong><?php echo $lang_admin_users['Results head'] ?></strong></li>
+		</ul>
+		<div class="pagepost">
+			<p class="pagelink"><?php echo $paging_links ?></p>
+		</div>
+		<div class="clearer"></div>
+	</div>
+</div>
+
+<div id="users2" class="blocktable">
+	<h2><span><?php echo $lang_admin_users['Results head'] ?></span></h2>
+	<div class="box">
+		<div class="inbox">
+			<table cellspacing="0">
+			<thead>
+				<tr>
+					<th class="tcl" scope="col"><?php echo $lang_admin_users['Results username head'] ?></th>
+					<th class="tc2" scope="col"><?php echo $lang_admin_users['Results e-mail head'] ?></th>
+					<th class="tc3" scope="col"><?php echo $lang_admin_users['Results title head'] ?></th>
+					<th class="tc4" scope="col"><?php echo $lang_admin_users['Results posts head'] ?></th>
+					<th class="tc5" scope="col"><?php echo $lang_admin_users['Results admin note head'] ?></th>
+					<th class="tcr" scope="col"><?php echo $lang_admin_users['Results actions head'] ?></th>
+				</tr>
+			</thead>
+			<tbody>
+<?php
+
+	$result = $db->query('SELECT DISTINCT poster_id, poster FROM '.$db->prefix.'posts WHERE poster_ip=\''.$db->escape($ip).'\' ORDER BY poster DESC') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+	$num_posts = $db->num_rows($result);
+
+	if ($num_posts)
+	{
+		// Loop through users and print out some info
+		for ($i = 0; $i < $num_posts; ++$i)
+		{
+			list($poster_id, $poster) = $db->fetch_row($result);
+
+			$result2 = $db->query('SELECT u.id, u.username, u.email, u.title, u.num_posts, u.admin_note, g.g_id, g.g_user_title FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON g.g_id=u.group_id WHERE u.id>1 AND u.id='.$poster_id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+
+			if (($user_data = $db->fetch_assoc($result2)))
+			{
+				$user_title = get_title($user_data);
+
+				$actions = '<a href="admin_users.php?ip_stats='.$user_data['id'].'">'.$lang_admin_users['Results view IP link'].'</a> | <a href="search.php?action=show_user&user_id='.$user_data['id'].'">'.$lang_admin_users['Results show posts link'].'</a>';
+
+?>
+				<tr>
+					<td class="tcl"><?php echo '<a href="profile.php?id='.$user_data['id'].'">'.pun_htmlspecialchars($user_data['username']).'</a>' ?></td>
+					<td class="tc2"><a href="mailto:<?php echo $user_data['email'] ?>"><?php echo $user_data['email'] ?></a></td>
+					<td class="tc3"><?php echo $user_title ?></td>
+					<td class="tc4"><?php echo forum_number_format($user_data['num_posts']) ?></td>
+					<td class="tc5"><?php echo ($user_data['admin_note'] != '') ? pun_htmlspecialchars($user_data['admin_note']) : ' ' ?></td>
+					<td class="tcr"><?php echo $actions ?></td>
+				</tr>
+<?php
+
+			}
+			else
+			{
+
+?>
+				<tr>
+					<td class="tcl"><?php echo pun_htmlspecialchars($poster) ?></td>
+					<td class="tc2"> </td>
+					<td class="tc3"><?php echo $lang_admin_users['Results guest'] ?></td>
+					<td class="tc4"> </td>
+					<td class="tc5"> </td>
+					<td class="tcr"> </td>
+				</tr>
+<?php
+
+			}
+		}
+	}
+	else
+		echo "\t\t\t\t".'<tr><td class="tcl" colspan="6">'.$lang_admin_users['Results no IP found'].'</td></tr>'."\n";
+
+?>
+			</tbody>
+			</table>
+		</div>
+	</div>
+</div>
+
+<div class="linksb">
+	<div class="inbox crumbsplus">
+		<div class="pagepost">
+			<p class="pagelink"><?php echo $paging_links ?></p>
+		</div>
+		<ul class="crumbs">
+			<li><a href="admin_index.php"><?php echo $lang_admin_common['Admin'].' '.$lang_admin_common['Index'] ?></a></li>
+			<li><span>» </span><a href="admin_users.php"><?php echo $lang_admin_common['Users'] ?></a></li>
+			<li><span>» </span><strong><?php echo $lang_admin_users['Results head'] ?></strong></li>
+		</ul>
+		<div class="clearer"></div>
+	</div>
+</div>
+<?php
+	require PUN_ROOT.'footer.php';
+}
+
+
+else if (isset($_GET['find_user']))
+{
+	$form = isset($_GET['form']) ? $_GET['form'] : array();
+
+	// trim() all elements in $form
+	$form = array_map('pun_trim', $form);
+	$conditions = $query_str = array();
+
+	$posts_greater = isset($_GET['posts_greater']) ? trim($_GET['posts_greater']) : '';
+	$posts_less = isset($_GET['posts_less']) ? trim($_GET['posts_less']) : '';
+	$last_post_after = isset($_GET['last_post_after']) ? trim($_GET['last_post_after']) : '';
+	$last_post_before = isset($_GET['last_post_before']) ? trim($_GET['last_post_before']) : '';
+	$registered_after = isset($_GET['registered_after']) ? trim($_GET['registered_after']) : '';
+	$registered_before = isset($_GET['registered_before']) ? trim($_GET['registered_before']) : '';
+	$order_by = isset($_GET['order_by']) && in_array($_GET['order_by'], array('username', 'email', 'num_posts', 'last_post', 'registered')) ? $_GET['order_by'] : 'username';
+	$direction = isset($_GET['direction']) && $_GET['direction'] == 'DESC' ? 'DESC' : 'ASC';
+	$user_group = isset($_GET['user_group']) ? intval($_GET['user_group']) : -1;
+
+	$query_str[] = 'order_by='.$order_by;
+	$query_str[] = 'direction='.$direction;
+	$query_str[] = 'user_group='.$user_group;
+
+	if (preg_match('/[^0-9]/', $posts_greater.$posts_less))
+		message($lang_admin_users['Non numeric message']);
+
+	// Try to convert date/time to timestamps
+	if ($last_post_after != '')
+	{
+		$query_str[] = 'last_post_after='.$last_post_after;
+
+		$last_post_after = strtotime($last_post_after);
+		if ($last_post_after === false || $last_post_after == -1)
+			message($lang_admin_users['Invalid date time message']);
+
+		$conditions[] = 'u.last_post>'.$last_post_after;
+	}
+	if ($last_post_before != '')
+	{
+		$query_str[] = 'last_post_before='.$last_post_before;
+
+		$last_post_before = strtotime($last_post_before);
+		if ($last_post_before === false || $last_post_before == -1)
+			message($lang_admin_users['Invalid date time message']);
+
+		$conditions[] = 'u.last_post<'.$last_post_before;
+	}
+	if ($registered_after != '')
+	{
+		$query_str[] = 'registered_after='.$registered_after;
+
+		$registered_after = strtotime($registered_after);
+		if ($registered_after === false || $registered_after == -1)
+			message($lang_admin_users['Invalid date time message']);
+
+		$conditions[] = 'u.registered>'.$registered_after;
+	}
+	if ($registered_before != '')
+	{
+		$query_str[] = 'registered_before='.$registered_before;
+
+		$registered_before = strtotime($registered_before);
+		if ($registered_before === false || $registered_before == -1)
+			message($lang_admin_users['Invalid date time message']);
+
+		$conditions[] = 'u.registered<'.$registered_before;
+	}
+
+	$like_command = ($db_type == 'pgsql') ? 'ILIKE' : 'LIKE';
+	foreach ($form as $key => $input)
+	{
+		if ($input != '' && in_array($key, array('username', 'email', 'title', 'realname', 'url', 'jabber', 'icq', 'msn', 'aim', 'yahoo', 'location', 'signature', 'admin_note')))
+		{
+			$conditions[] = 'u.'.$db->escape($key).' '.$like_command.' \''.$db->escape(str_replace('*', '%', $input)).'\'';
+			$query_str[] = 'form%5B'.$key.'%5D='.urlencode($input);
+		}
+	}
+
+	if ($posts_greater != '')
+	{
+		$query_str[] = 'posts_greater='.$posts_greater;
+		$conditions[] = 'u.num_posts>'.$posts_greater;
+	}
+	if ($posts_less != '')
+	{
+		$query_str[] = 'posts_less='.$posts_less;
+		$conditions[] = 'u.num_posts<'.$posts_less;
+	}
+
+	if ($user_group > -1)
+		$conditions[] = 'u.group_id='.$user_group;
+
+	// Fetch user count
+	$result = $db->query('SELECT COUNT(id) FROM '.$db->prefix.'users AS u LEFT JOIN '.$db->prefix.'groups AS g ON g.g_id=u.group_id WHERE u.id>1'.(!empty($conditions) ? ' AND '.implode(' AND ', $conditions) : '')) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+	$num_users = $db->result($result);
+
+	// Determine the user offset (based on $_GET['p'])
+	$num_pages = ceil($num_users / 50);
+
+	$p = (!isset($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : intval($_GET['p']);
+	$start_from = 50 * ($p - 1);
+
+	// Generate paging links
+	$paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'admin_users.php?find_user=&'.implode('&', $query_str));
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Users'], $lang_admin_users['Results head']);
+	define('PUN_ACTIVE_PAGE', 'admin');
+	require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+	<div class="inbox crumbsplus">
+		<ul class="crumbs">
+			<li><a href="admin_index.php"><?php echo $lang_admin_common['Admin'].' '.$lang_admin_common['Index'] ?></a></li>
+			<li><span>» </span><a href="admin_users.php"><?php echo $lang_admin_common['Users'] ?></a></li>
+			<li><span>» </span><strong><?php echo $lang_admin_users['Results head'] ?></strong></li>
+		</ul>
+		<div class="pagepost">
+			<p class="pagelink"><?php echo $paging_links ?></p>
+		</div>
+		<div class="clearer"></div>
+	</div>
+</div>
+
+
+<div id="users2" class="blocktable">
+	<h2><span><?php echo $lang_admin_users['Results head'] ?></span></h2>
+	<div class="box">
+		<div class="inbox">
+			<table cellspacing="0">
+			<thead>
+				<tr>
+					<th class="tcl" scope="col"><?php echo $lang_admin_users['Results username head'] ?></th>
+					<th class="tc2" scope="col"><?php echo $lang_admin_users['Results e-mail head'] ?></th>
+					<th class="tc3" scope="col"><?php echo $lang_admin_users['Results title head'] ?></th>
+					<th class="tc4" scope="col"><?php echo $lang_admin_users['Results posts head'] ?></th>
+					<th class="tc5" scope="col"><?php echo $lang_admin_users['Results admin note head'] ?></th>
+					<th class="tcr" scope="col"><?php echo $lang_admin_users['Results actions head'] ?></th>
+				</tr>
+			</thead>
+			<tbody>
+<?php
+
+	$result = $db->query('SELECT u.id, u.username, u.email, u.title, u.num_posts, u.admin_note, g.g_id, g.g_user_title FROM '.$db->prefix.'users AS u LEFT JOIN '.$db->prefix.'groups AS g ON g.g_id=u.group_id WHERE u.id>1'.(!empty($conditions) ? ' AND '.implode(' AND ', $conditions) : '').' ORDER BY '.$db->escape($order_by).' '.$db->escape($direction).' LIMIT '.$start_from.', 50') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+	if ($db->num_rows($result))
+	{
+		while ($user_data = $db->fetch_assoc($result))
+		{
+			$user_title = get_title($user_data);
+
+			// This script is a special case in that we want to display "Not verified" for non-verified users
+			if (($user_data['g_id'] == '' || $user_data['g_id'] == PUN_UNVERIFIED) && $user_title != $lang_common['Banned'])
+				$user_title = '<span class="warntext">'.$lang_admin_users['Not verified'].'</span>';
+
+			$actions = '<a href="admin_users.php?ip_stats='.$user_data['id'].'">'.$lang_admin_users['Results view IP link'].'</a> | <a href="search.php?action=show_user&user_id='.$user_data['id'].'">'.$lang_admin_users['Results show posts link'].'</a>';
+
+?>
+				<tr>
+					<td class="tcl"><?php echo '<a href="profile.php?id='.$user_data['id'].'">'.pun_htmlspecialchars($user_data['username']).'</a>' ?></td>
+					<td class="tc2"><a href="mailto:<?php echo $user_data['email'] ?>"><?php echo $user_data['email'] ?></a></td>
+					<td class="tc3"><?php echo $user_title ?></td>
+					<td class="tc4"><?php echo forum_number_format($user_data['num_posts']) ?></td>
+					<td class="tc5"><?php echo ($user_data['admin_note'] != '') ? pun_htmlspecialchars($user_data['admin_note']) : ' ' ?></td>
+					<td class="tcr"><?php echo $actions ?></td>
+				</tr>
+<?php
+
+		}
+	}
+	else
+		echo "\t\t\t\t".'<tr><td class="tcl" colspan="6">'.$lang_admin_users['No match'].'</td></tr>'."\n";
+
+?>
+			</tbody>
+			</table>
+		</div>
+	</div>
+</div>
+
+<div class="linksb">
+	<div class="inbox crumbsplus">
+		<div class="pagepost">
+			<p class="pagelink"><?php echo $paging_links ?></p>
+		</div>
+		<ul class="crumbs">
+			<li><a href="admin_index.php"><?php echo $lang_admin_common['Admin'].' '.$lang_admin_common['Index'] ?></a></li>
+			<li><span>» </span><a href="admin_users.php"><?php echo $lang_admin_common['Users'] ?></a></li>
+			<li><span>» </span><strong><?php echo $lang_admin_users['Results head'] ?></strong></li>
+		</ul>
+		<div class="clearer"></div>
+	</div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+
+else
+{
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_admin_common['Admin'], $lang_admin_common['Users']);
+	$focus_element = array('find_user', 'username');
+	define('PUN_ACTIVE_PAGE', 'admin');
+	require PUN_ROOT.'header.php';
+
+	generate_admin_menu('users');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo $lang_admin_users['User search head'] ?></span></h2>
+		<div class="box">
+			<form id="find_user" method="get" action="admin_users.php">
+				<p class="submittop"><input type="submit" name="find_user" value="<?php echo $lang_admin_users['Submit search'] ?>" tabindex="1" /></p>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_users['User search subhead'] ?></legend>
+						<div class="infldset">
+							<p><?php echo $lang_admin_users['User search info'] ?></p>
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['Username label'] ?></th>
+									<td><input type="text" name="form[username]" size="25" maxlength="25" tabindex="2" /></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['E-mail address label'] ?></th>
+									<td><input type="text" name="form[email]" size="30" maxlength="80" tabindex="3" /></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['Title label'] ?></th>
+									<td><input type="text" name="form[title]" size="30" maxlength="50" tabindex="4" /></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['Real name label'] ?></th>
+									<td><input type="text" name="form[realname]" size="30" maxlength="40" tabindex="5" /></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['Website label'] ?></th>
+									<td><input type="text" name="form[url]" size="35" maxlength="100" tabindex="6" /></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['Jabber label'] ?></th>
+									<td><input type="text" name="form[jabber]" size="30" maxlength="75" tabindex="7" /></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['ICQ label'] ?></th>
+									<td><input type="text" name="form[icq]" size="12" maxlength="12" tabindex="8" /></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['MSN label'] ?></th>
+									<td><input type="text" name="form[msn]" size="30" maxlength="50" tabindex="9" /></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['AOL label'] ?></th>
+									<td><input type="text" name="form[aim]" size="20" maxlength="20" tabindex="10" /></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['Yahoo label'] ?></th>
+									<td><input type="text" name="form[yahoo]" size="20" maxlength="20" tabindex="11" /></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['Location label'] ?></th>
+									<td><input type="text" name="form[location]" size="30" maxlength="30" tabindex="12" /></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['Signature label'] ?></th>
+									<td><input type="text" name="form[signature]" size="35" maxlength="512" tabindex="13" /></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['Admin note label'] ?></th>
+									<td><input type="text" name="form[admin_note]" size="30" maxlength="30" tabindex="14" /></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['Posts more than label'] ?></th>
+									<td><input type="text" name="posts_greater" size="5" maxlength="8" tabindex="15" /></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['Posts less than label'] ?></th>
+									<td><input type="text" name="posts_less" size="5" maxlength="8" tabindex="16" /></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['Last post after label'] ?></th>
+									<td><input type="text" name="last_post_after" size="24" maxlength="19" tabindex="17" />
+									<span><?php echo $lang_admin_users['Date help'] ?></span></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['Last post before label'] ?></th>
+									<td><input type="text" name="last_post_before" size="24" maxlength="19" tabindex="18" />
+									<span><?php echo $lang_admin_users['Date help'] ?></span></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['Registered after label'] ?></th>
+									<td><input type="text" name="registered_after" size="24" maxlength="19" tabindex="19" />
+									<span><?php echo $lang_admin_users['Date help'] ?></span></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['Registered before label'] ?></th>
+									<td><input type="text" name="registered_before" size="24" maxlength="19" tabindex="20" />
+									<span><?php echo $lang_admin_users['Date help'] ?></span></td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['Order by label'] ?></th>
+									<td>
+										<select name="order_by" tabindex="21">
+											<option value="username" selected="selected"><?php echo $lang_admin_users['Order by username'] ?></option>
+											<option value="email"><?php echo $lang_admin_users['Order by e-mail'] ?></option>
+											<option value="num_posts"><?php echo $lang_admin_users['Order by posts'] ?></option>
+											<option value="last_post"><?php echo $lang_admin_users['Order by last post'] ?></option>
+											<option value="registered"><?php echo $lang_admin_users['Order by registered'] ?></option>
+										</select>   <select name="direction" tabindex="22">
+											<option value="ASC" selected="selected"><?php echo $lang_admin_users['Ascending'] ?></option>
+											<option value="DESC"><?php echo $lang_admin_users['Descending'] ?></option>
+										</select>
+									</td>
+								</tr>
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['User group label'] ?></th>
+									<td>
+										<select name="user_group" tabindex="23">
+											<option value="-1" selected="selected"><?php echo $lang_admin_users['All groups'] ?></option>
+											<option value="0"><?php echo $lang_admin_users['Unverified users'] ?></option>
+<?php
+
+	$result = $db->query('SELECT g_id, g_title FROM '.$db->prefix.'groups WHERE g_id!='.PUN_GUEST.' ORDER BY g_title') or error('Unable to fetch user group list', __FILE__, __LINE__, $db->error());
+
+	while ($cur_group = $db->fetch_assoc($result))
+		echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+
+?>
+										</select>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+				<p class="submitend"><input type="submit" name="find_user" value="<?php echo $lang_admin_users['Submit search'] ?>" tabindex="25" /></p>
+			</form>
+		</div>
+
+		<h2 class="block2"><span><?php echo $lang_admin_users['IP search head'] ?></span></h2>
+		<div class="box">
+			<form method="get" action="admin_users.php">
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_users['IP search subhead'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_users['IP address label'] ?><div><input type="submit" value="<?php echo $lang_admin_users['Find IP address'] ?>" tabindex="26" /></div></th>
+									<td><input type="text" name="show_users" size="18" maxlength="15" tabindex="24" />
+									<span><?php echo $lang_admin_users['IP address help'] ?></span></td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+			</form>
+		</div>
+	</div>
+	<div class="clearer"></div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
diff --git a/cache/.htaccess b/cache/.htaccess
new file mode 100755
index 0000000..e67301e
--- /dev/null
+++ b/cache/.htaccess
@@ -0,0 +1,4 @@
+<Limit GET POST PUT>
+Order Allow,Deny
+Deny from All
+</Limit>
diff --git a/cache/index.html b/cache/index.html
new file mode 100755
index 0000000..89337b2
--- /dev/null
+++ b/cache/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/db_update.php b/db_update.php
new file mode 100644
index 0000000..e7d8eba
--- /dev/null
+++ b/db_update.php
@@ -0,0 +1,1692 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// The FluxBB version this script updates to
+define('UPDATE_TO', '1.4.2');
+
+define('UPDATE_TO_DB_REVISION', 8);
+define('UPDATE_TO_SI_REVISION', 1);
+define('UPDATE_TO_PARSER_REVISION', 1);
+
+define('MIN_PHP_VERSION', '4.3.0');
+define('MIN_MYSQL_VERSION', '4.1.2');
+define('MIN_PGSQL_VERSION', '7.0.0');
+define('PUN_SEARCH_MIN_WORD', 3);
+define('PUN_SEARCH_MAX_WORD', 20);
+
+// The MySQL connection character set that was used for FluxBB 1.2 - in 99% of cases this should be detected automatically,
+// but can be overridden using the below constant if required.
+//define('FORUM_DEFAULT_CHARSET', 'latin1');
+
+
+// The number of items to process per page view (lower this if the update script times out during UTF-8 conversion)
+define('PER_PAGE', 300);
+
+// Don't set to UTF-8 until after we've found out what the default character set is
+define('FORUM_NO_SET_NAMES', 1);
+
+// Make sure we are running at least MIN_PHP_VERSION
+if (!function_exists('version_compare') || version_compare(PHP_VERSION, MIN_PHP_VERSION, '<'))
+	exit('You are running PHP version '.PHP_VERSION.'. FluxBB '.UPDATE_TO.' requires at least PHP '.MIN_PHP_VERSION.' to run properly. You must upgrade your PHP installation before you can continue.');
+
+define('PUN_ROOT', './');
+
+// Attempt to load the configuration file config.php
+if (file_exists(PUN_ROOT.'config.php'))
+	include PUN_ROOT.'config.php';
+
+// If we have the 1.3-legacy constant defined, define the proper 1.4 constant so we don't get an incorrect "need to install" message
+if (defined('FORUM'))
+	define('PUN', FORUM);
+
+// If PUN isn't defined, config.php is missing or corrupt or we are outside the root directory
+if (!defined('PUN'))
+	exit('This file must be run from the forum root directory.');
+
+// Enable debug mode
+if (!defined('PUN_DEBUG'))
+	define('PUN_DEBUG', 1);
+
+// Load the functions script
+require PUN_ROOT.'include/functions.php';
+
+// Load UTF-8 functions
+require PUN_ROOT.'include/utf8/utf8.php';
+
+// Strip out "bad" UTF-8 characters
+forum_remove_bad_characters();
+
+// Reverse the effect of register_globals
+forum_unregister_globals();
+
+// Turn on full PHP error reporting
+error_reporting(E_ALL);
+
+// Force POSIX locale (to prevent functions such as strtolower() from messing up UTF-8 strings)
+setlocale(LC_CTYPE, 'C');
+
+// Turn off magic_quotes_runtime
+if (get_magic_quotes_runtime())
+	set_magic_quotes_runtime(0);
+
+// Strip slashes from GET/POST/COOKIE (if magic_quotes_gpc is enabled)
+if (get_magic_quotes_gpc())
+{
+	function stripslashes_array($array)
+	{
+		return is_array($array) ? array_map('stripslashes_array', $array) : stripslashes($array);
+	}
+
+	$_GET = stripslashes_array($_GET);
+	$_POST = stripslashes_array($_POST);
+	$_COOKIE = stripslashes_array($_COOKIE);
+	$_REQUEST = stripslashes_array($_REQUEST);
+}
+
+// If a cookie name is not specified in config.php, we use the default (forum_cookie)
+if (empty($cookie_name))
+	$cookie_name = 'pun_cookie';
+
+// If the cache directory is not specified, we use the default setting
+if (!defined('FORUM_CACHE_DIR'))
+	define('FORUM_CACHE_DIR', PUN_ROOT.'cache/');
+
+// Turn off PHP time limit
+ at set_time_limit(0);
+
+// Define a few commonly used constants
+define('PUN_UNVERIFIED', 0);
+define('PUN_ADMIN', 1);
+define('PUN_MOD', 2);
+define('PUN_GUEST', 3);
+define('PUN_MEMBER', 4);
+
+// Load DB abstraction layer and try to connect
+require PUN_ROOT.'include/dblayer/common_db.php';
+
+// Check what the default character set is - since 1.2 didn't specify any we will use whatever the default was (usually latin1)
+$old_connection_charset = defined('FORUM_DEFAULT_CHARSET') ? FORUM_DEFAULT_CHARSET : $db->get_names();
+
+// Set the connection to UTF-8 now
+$db->set_names('utf8');
+
+// Check current version
+$result = $db->query('SELECT conf_value FROM '.$db->prefix.'config WHERE conf_name=\'o_cur_version\'') or error('Unable to fetch version info.', __FILE__, __LINE__, $db->error());
+$cur_version = $db->result($result);
+
+if (version_compare($cur_version, '1.2', '<'))
+	exit('Version mismatch. The database \''.$db_name.'\' doesn\'t seem to be running a FluxBB database schema supported by this update script.');
+
+// Do some DB type specific checks
+$mysql = false;
+switch ($db_type)
+{
+	case 'mysql':
+	case 'mysqli':
+	case 'mysql_innodb':
+	case 'mysqli_innodb':
+		$mysql_info = $db->get_version();
+		if (version_compare($mysql_info['version'], MIN_MYSQL_VERSION, '<'))
+			error('You are running MySQL version '.$mysql_info['version'].'. FluxBB '.UPDATE_TO.' requires at least MySQL '.MIN_MYSQL_VERSION.' to run properly. You must upgrade your MySQL installation before you can continue.');
+
+		$mysql = true;
+		break;
+
+	case 'pgsql':
+		$pgsql_info = $db->get_version();
+		if (version_compare($pgsql_info['version'], MIN_PGSQL_VERSION, '<'))
+			error('You are running PostgreSQL version '.$pgsql_info['version'].'. FluxBB '.UPDATE_TO.' requires at least PostgreSQL '.MIN_PGSQL_VERSION.' to run properly. You must upgrade your PostgreSQL installation before you can continue.');
+
+		break;
+}
+
+// Get the forum config
+$result = $db->query('SELECT * FROM '.$db->prefix.'config') or error('Unable to fetch config.', __FILE__, __LINE__, $db->error());
+while ($cur_config_item = $db->fetch_row($result))
+	$pun_config[$cur_config_item[0]] = $cur_config_item[1];
+
+// Check the database revision and the current version
+if (isset($pun_config['o_database_revision']) && $pun_config['o_database_revision'] >= UPDATE_TO_DB_REVISION &&
+		isset($pun_config['o_searchindex_revision']) && $pun_config['o_searchindex_revision'] >= UPDATE_TO_SI_REVISION &&
+		isset($pun_config['o_parser_revision']) && $pun_config['o_parser_revision'] >= UPDATE_TO_PARSER_REVISION &&
+		version_compare($pun_config['o_cur_version'], UPDATE_TO, '>='))
+	exit('Your database is already as up-to-date as this script can make it.');
+
+$default_style = $pun_config['o_default_style'];
+if (!file_exists(PUN_ROOT.'style/'.$default_style.'.css'))
+	$default_style = 'Air';
+
+// Start a session, used to queue up errors if duplicate users occur when converting from FluxBB v1.2.
+session_start();
+
+if (!isset($_SESSION['dupe_users']))
+	$_SESSION['dupe_users'] = array();
+
+//
+// Determines whether $str is UTF-8 encoded or not
+//
+function seems_utf8($str)
+{
+	$str_len = strlen($str);
+	for ($i = 0; $i < $str_len; ++$i)
+	{
+		if (ord($str[$i]) < 0x80) continue; # 0bbbbbbb
+		else if ((ord($str[$i]) & 0xE0) == 0xC0) $n=1; # 110bbbbb
+		else if ((ord($str[$i]) & 0xF0) == 0xE0) $n=2; # 1110bbbb
+		else if ((ord($str[$i]) & 0xF8) == 0xF0) $n=3; # 11110bbb
+		else if ((ord($str[$i]) & 0xFC) == 0xF8) $n=4; # 111110bb
+		else if ((ord($str[$i]) & 0xFE) == 0xFC) $n=5; # 1111110b
+		else return false; # Does not match any model
+
+		for ($j = 0; $j < $n; ++$j) # n bytes matching 10bbbbbb follow ?
+		{
+			if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80))
+				return false;
+		}
+	}
+
+	return true;
+}
+
+
+//
+// Translates the number from a HTML numeric entity into an UTF-8 character
+//
+function dcr2utf8($src)
+{
+	$dest = '';
+	if ($src < 0)
+		return false;
+	else if ($src <= 0x007f)
+		$dest .= chr($src);
+	else if ($src <= 0x07ff)
+	{
+		$dest .= chr(0xc0 | ($src >> 6));
+		$dest .= chr(0x80 | ($src & 0x003f));
+	}
+	else if ($src == 0xFEFF)
+	{
+		// nop -- zap the BOM
+	}
+	else if ($src >= 0xD800 && $src <= 0xDFFF)
+	{
+		// found a surrogate
+		return false;
+	}
+	else if ($src <= 0xffff)
+	{
+		$dest .= chr(0xe0 | ($src >> 12));
+		$dest .= chr(0x80 | (($src >> 6) & 0x003f));
+		$dest .= chr(0x80 | ($src & 0x003f));
+	}
+	else if ($src <= 0x10ffff)
+	{
+		$dest .= chr(0xf0 | ($src >> 18));
+		$dest .= chr(0x80 | (($src >> 12) & 0x3f));
+		$dest .= chr(0x80 | (($src >> 6) & 0x3f));
+		$dest .= chr(0x80 | ($src & 0x3f));
+	}
+	else
+	{
+		// out of range
+		return false;
+	}
+
+	return $dest;
+}
+
+
+//
+// Attempts to convert $str from $old_charset to UTF-8. Also converts HTML entities (including numeric entities) to UTF-8 characters
+//
+function convert_to_utf8(&$str, $old_charset)
+{
+	if ($str === null || $str == '')
+		return false;
+
+	$save = $str;
+
+	// Replace literal entities (for non-UTF-8 compliant html_entity_encode)
+	if (version_compare(PHP_VERSION, '5.0.0', '<') && $old_charset == 'ISO-8859-1' || $old_charset == 'ISO-8859-15')
+		$str = html_entity_decode($str, ENT_QUOTES, $old_charset);
+
+	if ($old_charset != 'UTF-8' && !seems_utf8($str))
+	{
+		if (function_exists('iconv'))
+			$str = iconv($old_charset == 'ISO-8859-1' ? 'WINDOWS-1252' : 'ISO-8859-1', 'UTF-8', $str);
+		else if (function_exists('mb_convert_encoding'))
+			$str = mb_convert_encoding($str, 'UTF-8', $old_charset == 'ISO-8859-1' ? 'WINDOWS-1252' : 'ISO-8859-1');
+		else if ($old_charset == 'ISO-8859-1')
+			$str = utf8_encode($str);
+	}
+
+	// Replace literal entities (for UTF-8 compliant html_entity_encode)
+	if (version_compare(PHP_VERSION, '5.0.0', '>='))
+		$str = html_entity_decode($str, ENT_QUOTES, 'UTF-8');
+
+	// Replace numeric entities
+	$str = preg_replace_callback('/&#([0-9]+);/', 'utf8_callback_1', $str);
+	$str = preg_replace_callback('/&#x([a-f0-9]+);/i', 'utf8_callback_2', $str);
+
+	// Remove "bad" characters
+	$str = remove_bad_characters($str);
+
+	return ($save != $str);
+}
+
+
+function utf8_callback_1($matches)
+{
+	return dcr2utf8($matches[1]);
+}
+
+
+function utf8_callback_2($matches)
+{
+	return dcr2utf8(hexdec($matches[1]));
+}
+
+
+//
+// Alter a table to be utf8. MySQL only
+// Function based on update_convert_table_utf8() from the Drupal project (http://drupal.org/)
+//
+function alter_table_utf8($table)
+{
+	global $mysql, $db;
+	static $types;
+
+	if (!$mysql)
+		return;
+
+	if (!isset($types))
+	{
+		$types = array(
+			'char'			=> 'binary',
+			'varchar'		=> 'varbinary',
+			'tinytext'		=> 'tinyblob',
+			'mediumtext'	=> 'mediumblob',
+			'text'			=> 'blob',
+			'longtext'		=> 'longblob'
+		);
+	}
+
+	// Set table default charset to utf8
+	$db->query('ALTER TABLE '.$table.' CHARACTER SET utf8') or error('Unable to set table character set', __FILE__, __LINE__, $db->error());
+
+	// Find out which columns need converting and build SQL statements
+	$result = $db->query('SHOW FULL COLUMNS FROM '.$table) or error('Unable to fetch column information', __FILE__, __LINE__, $db->error());
+	while ($cur_column = $db->fetch_assoc($result))
+	{
+		if ($cur_column['Collation'] === null)
+			continue;
+
+		list($type) = explode('(', $cur_column['Type']);
+		if (isset($types[$type]) && strpos($cur_column['Collation'], 'utf8') === false)
+		{
+			$allow_null = ($cur_column['Null'] == 'YES');
+			$collate = (substr($cur_column['Collation'], -3) == 'bin') ? 'utf8_bin' : 'utf8_general_ci';
+
+			$db->alter_field($table, $cur_column['Field'], preg_replace('/'.$type.'/i', $types[$type], $cur_column['Type']), $allow_null, $cur_column['Default'], null, true) or error('Unable to alter field to binary', __FILE__, __LINE__, $db->error());
+			$db->alter_field($table, $cur_column['Field'], $cur_column['Type'].' CHARACTER SET utf8 COLLATE '.$collate, $allow_null, $cur_column['Default'], null, true) or error('Unable to alter field to utf8', __FILE__, __LINE__, $db->error());
+		}
+	}
+}
+
+//
+// Safely converts text type columns into utf8
+// If finished returns true, otherwise returns $end_at
+//
+function convert_table_utf8($table, $callback, $old_charset, $key = null, $start_at = null, $error_callback = null)
+{
+	global $mysql, $db, $old_connection_charset;
+
+	$finished = true;
+	$end_at = 0;
+	if ($mysql)
+	{
+		// Only set up the tables if we are doing this in 1 go, or its the first go
+		if ($start_at === null || $start_at == 0)
+		{
+			// Drop any temp table that exists, in-case it's left over from a failed update
+			$db->drop_table($table.'_utf8', true) or error('Unable to drop left over temp table', __FILE__, __LINE__, $db->error());
+
+			// Copy the table
+			$db->query('CREATE TABLE '.$table.'_utf8 LIKE '.$table) or error('Unable to create new table', __FILE__, __LINE__, $db->error());
+
+			// Set table default charset to utf8
+			alter_table_utf8($table.'_utf8');
+		}
+
+		// Change to the old character set so MySQL doesn't attempt to perform conversion on the data from the old table
+		$db->set_names($old_connection_charset);
+
+		// Move & Convert everything
+		$result = $db->query('SELECT * FROM '.$table.($start_at === null ? '' : ' WHERE '.$key.'>'.$start_at).' ORDER BY '.$key.' ASC'.($start_at === null ? '' : ' LIMIT '.PER_PAGE), false) or error('Unable to select from old table', __FILE__, __LINE__, $db->error());
+
+		// Change back to utf8 mode so we can insert it into the new table
+		$db->set_names('utf8');
+
+		while ($cur_item = $db->fetch_assoc($result))
+		{
+			$cur_item = call_user_func($callback, $cur_item, $old_charset);
+
+			$temp = array();
+			foreach ($cur_item as $idx => $value)
+				$temp[$idx] = $value === null ? 'NULL' : '\''.$db->escape($value).'\'';
+
+			$db->query('INSERT INTO '.$table.'_utf8('.implode(',', array_keys($temp)).') VALUES ('.implode(',', array_values($temp)).')') or ($error_callback === null ? error('Unable to insert data to new table', __FILE__, __LINE__, $db->error()) : call_user_func($error_callback, $cur_item));
+
+			$end_at = $cur_item[$key];
+		}
+
+		// If we aren't doing this all in 1 go and $end_at has a value (i.e. we have processed at least 1 row), figure out if we have more to do or not
+		if ($start_at !== null && $end_at > 0)
+		{
+			$result = $db->query('SELECT 1 FROM '.$table.' WHERE '.$key.'>'.$end_at.' ORDER BY '.$key.' ASC LIMIT 1') or error('Unable to check for next row', __FILE__, __LINE__, $db->error());
+			$finished = $db->num_rows($result) == 0;
+		}
+
+		// Only swap the tables if we are doing this in 1 go, or its the last go
+		if ($finished)
+		{
+			// Delete old table
+			$db->drop_table($table, true) or error('Unable to drop old table', __FILE__, __LINE__, $db->error());
+
+			// Rename table
+			$db->query('ALTER TABLE '.$table.'_utf8 RENAME '.$table) or error('Unable to rename new table', __FILE__, __LINE__, $db->error());
+
+			return true;
+		}
+
+		return $end_at;
+	}
+	else
+	{
+		// Convert everything
+		$result = $db->query('SELECT * FROM '.$table.($start_at === null ? '' : ' WHERE '.$key.'>'.$start_at).' ORDER BY '.$key.' ASC'.($start_at === null ? '' : ' LIMIT '.PER_PAGE)) or error('Unable to select from table', __FILE__, __LINE__, $db->error());
+		while ($cur_item = $db->fetch_assoc($result))
+		{
+			$cur_item = call_user_func($callback, $cur_item, $old_charset);
+
+			$temp = array();
+			foreach ($cur_item as $idx => $value)
+				$temp[] = $idx.'='.($value === null ? 'NULL' : '\''.$db->escape($value).'\'');
+
+			if (!empty($temp))
+				$db->query('UPDATE '.$table.' SET '.implode(', ', $temp).' WHERE '.$key.'=\''.$db->escape($cur_item[$key]).'\'') or error('Unable to update data', __FILE__, __LINE__, $db->error());
+
+			$end_at = $cur_item[$key];
+		}
+
+		if ($start_at !== null && $end_at > 0)
+		{
+			$result = $db->query('SELECT 1 FROM '.$table.' WHERE '.$key.'>'.$end_at.' ORDER BY '.$key.' ASC LIMIT 1') or error('Unable to check for next row', __FILE__, __LINE__, $db->error());
+			if ($db->num_rows($result) == 0)
+				return true;
+
+			return $end_at;
+		}
+
+		return true;
+	}
+}
+
+
+header('Content-type: text/html; charset=utf-8');
+
+// Empty all output buffers and stop buffering
+while (@ob_end_clean());
+
+
+$stage = isset($_GET['stage']) ? $_GET['stage'] : '';
+$old_charset = isset($_GET['req_old_charset']) ? str_replace('ISO8859', 'ISO-8859', strtoupper($_GET['req_old_charset'])) : 'ISO-8859-1';
+$start_at = isset($_GET['start_at']) ? intval($_GET['start_at']) : 0;
+$query_str = '';
+
+switch ($stage)
+{
+	// Show form
+	case '':
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>FluxBB Database Update</title>
+<link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
+</head>
+<body>
+
+<div id="pundb_update" class="pun">
+<div class="top-box"><div><!-- Top Corners --></div></div>
+<div class="punwrap">
+
+<div class="blockform">
+	<h2><span>FluxBB Update</span></h2>
+	<div class="box">
+		<form method="get" action="<?php echo pun_htmlspecialchars($_SERVER['REQUEST_URI']) ?>" onsubmit="this.start.disabled=true">
+		<input type="hidden" name="stage" value="start" />
+			<div class="inform">
+				<div class="forminfo">
+					<p style="font-size: 1.1em">This script will update your forum database. The update procedure might take anything from a second to hours depending on the speed of the server and the size of the forum database. Don't forget to make a backup of the database before continuing.</p>
+					<p style="font-size: 1.1em">Did you read the update instructions in the documentation? If not, start there.</p>
+<?php
+
+if (strpos($cur_version, '1.2') === 0)
+{
+	if (!function_exists('iconv') && !function_exists('mb_convert_encoding'))
+	{
+
+?>
+					<p style="font-size: 1.1em"><strong>IMPORTANT!</strong> FluxBB has detected that this PHP environment does not have support for the encoding mechanisms required to do UTF-8 conversion from character sets other than ISO-8859-1. What this means is that if the current character set is not ISO-8859-1, FluxBB won't be able to convert your forum database to UTF-8 and you will have to do it manually. Instructions for doing manual charset conversion can be found in the update instructions.</p>
+<?php
+
+	}
+
+?>
+				</div>
+			</div>
+			<div class="inform">
+				<div class="forminfo">
+					<p style="font-size: 1.1em"><strong>Enable conversion:</strong> When enabled this update script will, after it has made the required structural changes to the database, convert all text in the database from the current character set to UTF-8. This conversion is required if you're upgrading from version 1.2.</p>
+					<p style="font-size: 1.1em"><strong>Current character set:</strong> If the primary language in your forum is English, you can leave this at the default value. However, if your forum is non-English, you should enter the character set of the primary language pack used in the forum. <i>Getting this wrong can corrupt your database so don't just guess!</i> Note: This is required even if the old database is UTF-8.</p>
+				</div>
+				<fieldset>
+					<legend>Charset conversion</legend>
+					<div class="infldset">
+						<div class="rbox">
+							<label><input type="checkbox" name="convert_charset" value="1" checked="checked" /><strong>Enable conversion</strong> (perform database charset conversion).<br /></label>
+						</div>
+						<label>
+							<strong>Current character set</strong><br />Accept default for English forums otherwise the character set of the primary language pack.<br />
+							<input type="text" name="req_old_charset" size="12" maxlength="20" value="<?php echo $old_charset ?>" /><br />
+						</label>
+					</div>
+				</fieldset>
+<?php
+
+}
+else
+	echo "\t\t\t\t".'</div>'."\n";
+
+?>
+			</div>
+			<p class="buttons"><input type="submit" name="start" value="Start update" /></p>
+		</form>
+	</div>
+</div>
+
+</div>
+<div class="end-box"><div><!-- Bottom Corners --></div></div>
+</div>
+
+</body>
+</html>
+<?php
+
+		break;
+
+
+	// Start by updating the database structure
+	case 'start':
+		$query_str = '?stage=preparse_posts';
+
+		// If we don't need to update the database, skip this stage
+		if (isset($pun_config['o_database_revision']) && $pun_config['o_database_revision'] >= UPDATE_TO_DB_REVISION)
+			break;
+
+		// Make all email fields VARCHAR(80)
+		$db->alter_field('bans', 'email', 'VARCHAR(80)', true) or error('Unable to alter email field', __FILE__, __LINE__, $db->error());
+		$db->alter_field('posts', 'poster_email', 'VARCHAR(80)', true) or error('Unable to alter poster_email field', __FILE__, __LINE__, $db->error());
+		$db->alter_field('users', 'email', 'VARCHAR(80)', false, '') or error('Unable to alter email field', __FILE__, __LINE__, $db->error());
+		$db->alter_field('users', 'jabber', 'VARCHAR(80)', true) or error('Unable to alter jabber field', __FILE__, __LINE__, $db->error());
+		$db->alter_field('users', 'msn', 'VARCHAR(80)', true) or error('Unable to alter msn field', __FILE__, __LINE__, $db->error());
+		$db->alter_field('users', 'activate_string', 'VARCHAR(80)', true) or error('Unable to alter activate_string field', __FILE__, __LINE__, $db->error());
+
+		// Make all IP fields VARCHAR(39) to support IPv6
+		$db->alter_field('posts', 'poster_ip', 'VARCHAR(39)', true) or error('Unable to alter poster_ip field', __FILE__, __LINE__, $db->error());
+		$db->alter_field('users', 'registration_ip', 'VARCHAR(39)', false, '0.0.0.0') or error('Unable to alter registration_ip field', __FILE__, __LINE__, $db->error());
+
+		// Make the message field MEDIUMTEXT to allow proper conversion of 65535 character posts to UTF-8
+		$db->alter_field('posts', 'message', 'MEDIUMTEXT', true) or error('Unable to alter message field', __FILE__, __LINE__, $db->error());
+
+		// Add the DST option to the users table
+		$db->add_field('users', 'dst', 'TINYINT(1)', false, 0, 'timezone') or error('Unable to add dst field', __FILE__, __LINE__, $db->error());
+
+		// Add the last_post field to the online table
+		$db->add_field('online', 'last_post', 'INT(10) UNSIGNED', true, null, null) or error('Unable to add last_post field', __FILE__, __LINE__, $db->error());
+
+		// Add the last_search field to the online table
+		$db->add_field('online', 'last_search', 'INT(10) UNSIGNED', true, null, null) or error('Unable to add last_search field', __FILE__, __LINE__, $db->error());
+
+		// Add the last_search column to the users table
+		$db->add_field('users', 'last_search', 'INT(10) UNSIGNED', true, null, 'last_post') or error('Unable to add last_search field', __FILE__, __LINE__, $db->error());
+
+		// Drop use_avatar column from users table
+		$db->drop_field('users', 'use_avatar') or error('Unable to drop use_avatar field', __FILE__, __LINE__, $db->error());
+
+		// Drop save_pass column from users table
+		$db->drop_field('users', 'save_pass') or error('Unable to drop save_pass field', __FILE__, __LINE__, $db->error());
+
+		// Drop g_edit_subjects_interval column from groups table
+		$db->drop_field('groups', 'g_edit_subjects_interval');
+
+		// Add database revision number
+		if (!array_key_exists('o_database_revision', $pun_config))
+			$db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_database_revision\', \'0\')') or error('Unable to insert config value \'o_database_revision\'', __FILE__, __LINE__, $db->error());
+
+		// Add search index revision number
+		if (!array_key_exists('o_searchindex_revision', $pun_config))
+			$db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_searchindex_revision\', \'0\')') or error('Unable to insert config value \'o_searchindex_revision\'', __FILE__, __LINE__, $db->error());
+
+		// Add parser revision number
+		if (!array_key_exists('o_parser_revision', $pun_config))
+			$db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_parser_revision\', \'0\')') or error('Unable to insert config value \'o_parser_revision\'', __FILE__, __LINE__, $db->error());
+
+		// Add default email setting option
+		if (!array_key_exists('o_default_email_setting', $pun_config))
+			$db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_default_email_setting\', \'1\')') or error('Unable to insert config value \'o_default_email_setting\'', __FILE__, __LINE__, $db->error());
+
+		// Make sure we have o_additional_navlinks (was added in 1.2.1)
+		if (!array_key_exists('o_additional_navlinks', $pun_config))
+			$db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_additional_navlinks\', \'\')') or error('Unable to insert config value \'o_additional_navlinks\'', __FILE__, __LINE__, $db->error());
+
+		// Insert new config option o_topic_views
+		if (!array_key_exists('o_topic_views', $pun_config))
+			$db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_topic_views\', \'1\')') or error('Unable to insert config value \'o_topic_views\'', __FILE__, __LINE__, $db->error());
+
+		// Insert new config option o_signatures
+		if (!array_key_exists('o_signatures', $pun_config))
+			$db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_signatures\', \'1\')') or error('Unable to insert config value \'o_signatures\'', __FILE__, __LINE__, $db->error());
+
+		// Insert new config option o_smtp_ssl
+		if (!array_key_exists('o_smtp_ssl', $pun_config))
+			$db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_smtp_ssl\', \'0\')') or error('Unable to insert config value \'o_smtp_ssl\'', __FILE__, __LINE__, $db->error());
+
+		// Insert new config option o_default_dst
+		if (!array_key_exists('o_default_dst', $pun_config))
+			$db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_default_dst\', \'0\')') or error('Unable to insert config value \'o_default_dst\'', __FILE__, __LINE__, $db->error());
+
+		// Insert new config option o_quote_depth
+		if (!array_key_exists('o_quote_depth', $pun_config))
+			$db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_quote_depth\', \'3\')') or error('Unable to insert config value \'o_quote_depth\'', __FILE__, __LINE__, $db->error());
+
+		// Insert new config option o_feed_type
+		if (!array_key_exists('o_feed_type', $pun_config))
+			$db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_feed_type\', \'2\')') or error('Unable to insert config value \'o_feed_type\'', __FILE__, __LINE__, $db->error());
+
+		// Insert config option o_base_url which was removed in 1.3
+		if (!array_key_exists('o_base_url', $pun_config))
+		{
+			// If it isn't in $pun_config['o_base_url'] it should be in $base_url, but just in-case it isn't we can make a guess at it
+			if (!isset($base_url))
+			{
+				// Make an educated guess regarding base_url
+				$base_url  = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';	// protocol
+				$base_url .= preg_replace('/:(80|443)$/', '', $_SERVER['HTTP_HOST']);							// host[:port]
+				$base_url .= str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME']));							// path
+			}
+
+			if (substr($base_url, -1) == '/')
+				$base_url = substr($base_url, 0, -1);
+
+			$db->query('INSERT INTO '.$db->prefix.'config (conf_name, conf_value) VALUES (\'o_base_url\', \''.$db->escape($base_url).'\')') or error('Unable to insert config value \'o_quote_depth\'', __FILE__, __LINE__, $db->error());
+		}
+
+		if (strpos($cur_version, '1.2') === 0)
+		{
+			// Groups are almost the same as 1.2:
+			// unverified:	32000 -> 0
+
+			$db->query('UPDATE '.$db->prefix.'users SET group_id = 0 WHERE group_id = 32000') or error('Unable to update unverified users', __FILE__, __LINE__, $db->error());
+		}
+		else if (strpos($cur_version, '1.3') === 0)
+		{
+			// Groups have changed quite a lot from 1.3:
+			// unverified:	0 -> 0
+			// admin:		1 -> 1
+			// mod:			? -> 2
+			// guest:		2 -> 3
+			// member:		? -> 4
+
+			$result = $db->query('SELECT MAX(g_id) + 1 FROM '.$db->prefix.'groups') or error('Unable to select temp group ID', __FILE__, __LINE__, $db->error());
+			$temp_id = $db->result($result);
+
+			$result = $db->query('SELECT g_id FROM '.$db->prefix.'groups WHERE g_moderator = 1 AND g_id > 1 LIMIT 1') or error('Unable to select moderator group', __FILE__, __LINE__, $db->error());
+			if ($db->num_rows($result))
+				$mod_gid = $db->result($result);
+			else
+			{
+				$db->query('INSERT INTO '.$db->prefix.'groups (g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood) VALUES('."'Moderators', 'Moderator', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0)") or error('Unable to add gr [...]
+				$mod_gid = $db->insert_id();
+			}
+
+			$member_gid = $pun_config['o_default_user_group'];
+
+			// move the mod group to a temp place
+			$db->query('UPDATE '.$db->prefix.'groups SET g_id = '.$temp_id.' WHERE g_id = '.$mod_gid) or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
+			$db->query('UPDATE '.$db->prefix.'users SET group_id = '.$temp_id.' WHERE group_id = '.$mod_gid) or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
+			$db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = '.$temp_id.' WHERE group_id = '.$mod_gid) or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
+			if ($member_gid == $mod_gid) $member_gid = $temp_id;
+
+			// move whoever is in 3 to a spare slot
+			$db->query('UPDATE '.$db->prefix.'groups SET g_id = '.$mod_gid.' WHERE g_id = 3') or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
+			$db->query('UPDATE '.$db->prefix.'users SET group_id = '.$mod_gid.' WHERE group_id = 3') or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
+			$db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = '.$mod_gid.' WHERE group_id = 3') or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
+			if ($member_gid == 3) $member_gid = $mod_gid;
+
+			// move guest to 3
+			$db->query('UPDATE '.$db->prefix.'groups SET g_id = 3 WHERE g_id = 2') or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
+			$db->query('UPDATE '.$db->prefix.'users SET group_id = 3 WHERE group_id = 2') or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
+			$db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = 3 WHERE group_id = 2') or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
+			if ($member_gid == 2) $member_gid = 3;
+
+			// move mod group in temp place to 2
+			$db->query('UPDATE '.$db->prefix.'groups SET g_id = 2 WHERE g_id = '.$temp_id) or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
+			$db->query('UPDATE '.$db->prefix.'users SET group_id = 2 WHERE group_id = '.$temp_id) or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
+			$db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = 2 WHERE group_id = '.$temp_id) or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
+			if ($member_gid == $temp_id) $member_gid = 2;
+
+			// Only move stuff around if it isn't already in the right place
+			if ($member_gid != $mod_gid || $member_gid != 4)
+			{
+				// move members to temp place
+				$db->query('UPDATE '.$db->prefix.'groups SET g_id = '.$temp_id.' WHERE g_id = '.$member_gid) or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
+				$db->query('UPDATE '.$db->prefix.'users SET group_id = '.$temp_id.' WHERE group_id = '.$member_gid) or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
+				$db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = '.$temp_id.' WHERE group_id = '.$member_gid) or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
+
+				// move whoever is in 4 to members place
+				$db->query('UPDATE '.$db->prefix.'groups SET g_id = '.$member_gid.' WHERE g_id = 4') or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
+				$db->query('UPDATE '.$db->prefix.'users SET group_id = '.$member_gid.' WHERE group_id = 4') or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
+				$db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = '.$member_gid.' WHERE group_id = 4') or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
+
+				// move members in temp place to 4
+				$db->query('UPDATE '.$db->prefix.'groups SET g_id = 4 WHERE g_id = '.$temp_id) or error('Unable to update group ID', __FILE__, __LINE__, $db->error());
+				$db->query('UPDATE '.$db->prefix.'users SET group_id = 4 WHERE group_id = '.$temp_id) or error('Unable to update users group ID', __FILE__, __LINE__, $db->error());
+				$db->query('UPDATE '.$db->prefix.'forum_perms SET group_id = 4 WHERE group_id = '.$temp_id) or error('Unable to forum_perms group ID', __FILE__, __LINE__, $db->error());
+			}
+
+			$db->query('UPDATE '.$db->prefix.'config SET conf_value=\''.$member_gid.'\' WHERE conf_name=\'o_default_user_group\'') or error('Unable to update default user group ID', __FILE__, __LINE__, $db->error());
+		}
+
+		// Server time zone is now simply the default time zone
+		if (!array_key_exists('o_default_timezone', $pun_config))
+			$db->query('UPDATE '.$db->prefix.'config SET conf_name = \'o_default_timezone\' WHERE conf_name = \'o_server_timezone\'') or error('Unable to update time zone config', __FILE__, __LINE__, $db->error());
+
+		// Increase visit timeout to 30 minutes (only if it hasn't been changed from the default)
+		if (!array_key_exists('o_database_revision', $pun_config) && $pun_config['o_timeout_visit'] == '600')
+			$db->query('UPDATE '.$db->prefix.'config SET conf_value = \'1800\' WHERE conf_name = \'o_timeout_visit\'') or error('Unable to update visit timeout config', __FILE__, __LINE__, $db->error());
+
+		// Remove obsolete g_post_polls permission from groups table
+		$db->drop_field('groups', 'g_post_polls');
+
+		// Make room for multiple moderator groups
+		if (!$db->field_exists('groups', 'g_moderator'))
+		{
+			// Add g_moderator column to groups table
+			$db->add_field('groups', 'g_moderator', 'TINYINT(1)', false, 0, 'g_user_title') or error('Unable to add g_moderator field', __FILE__, __LINE__, $db->error());
+
+			// Give the moderator group moderator privileges
+			$db->query('UPDATE '.$db->prefix.'groups SET g_moderator = 1 WHERE g_id = 2') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
+		}
+
+		// Replace obsolete p_mod_edit_users config setting with new per-group permission
+		if (array_key_exists('p_mod_edit_users', $pun_config))
+		{
+			$db->query('DELETE FROM '.$db->prefix.'config WHERE conf_name = \'p_mod_edit_users\'') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
+
+			$db->add_field('groups', 'g_mod_edit_users', 'TINYINT(1)', false, 0, 'g_moderator') or error('Unable to add g_mod_edit_users field', __FILE__, __LINE__, $db->error());
+
+			$db->query('UPDATE '.$db->prefix.'groups SET g_mod_edit_users = '.$pun_config['p_mod_edit_users'].' WHERE g_moderator = 1') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
+		}
+
+		// Replace obsolete p_mod_rename_users config setting with new per-group permission
+		if (array_key_exists('p_mod_rename_users', $pun_config))
+		{
+			$db->query('DELETE FROM '.$db->prefix.'config WHERE conf_name = \'p_mod_rename_users\'') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
+
+			$db->add_field('groups', 'g_mod_rename_users', 'TINYINT(1)', false, 0, 'g_mod_edit_users') or error('Unable to add g_mod_rename_users field', __FILE__, __LINE__, $db->error());
+
+			$db->query('UPDATE '.$db->prefix.'groups SET g_mod_rename_users = '.$pun_config['p_mod_rename_users'].' WHERE g_moderator = 1') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
+		}
+
+		// Replace obsolete p_mod_change_passwords config setting with new per-group permission
+		if (array_key_exists('p_mod_change_passwords', $pun_config))
+		{
+			$db->query('DELETE FROM '.$db->prefix.'config WHERE conf_name = \'p_mod_change_passwords\'') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
+
+			$db->add_field('groups', 'g_mod_change_passwords', 'TINYINT(1)', false, 0, 'g_mod_rename_users') or error('Unable to add g_mod_change_passwords field', __FILE__, __LINE__, $db->error());
+
+			$db->query('UPDATE '.$db->prefix.'groups SET g_mod_change_passwords = '.$pun_config['p_mod_change_passwords'].' WHERE g_moderator = 1') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
+		}
+
+		// Replace obsolete p_mod_ban_users config setting with new per-group permission
+		if (array_key_exists('p_mod_ban_users', $pun_config))
+		{
+			$db->query('DELETE FROM '.$db->prefix.'config WHERE conf_name = \'p_mod_ban_users\'') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
+
+			$db->add_field('groups', 'g_mod_ban_users', 'TINYINT(1)', false, 0, 'g_mod_change_passwords') or error('Unable to add g_mod_ban_users field', __FILE__, __LINE__, $db->error());
+
+			$db->query('UPDATE '.$db->prefix.'groups SET g_mod_ban_users = '.$pun_config['p_mod_ban_users'].' WHERE g_moderator = 1') or error('Unable to update moderator powers', __FILE__, __LINE__, $db->error());
+		}
+
+		// We need to add a unique index to avoid users having multiple rows in the online table
+		if (!$db->index_exists('online', 'user_id_ident_idx'))
+		{
+			$db->truncate_table('online') or error('Unable to clear online table', __FILE__, __LINE__, $db->error());
+
+			if ($mysql)
+				$db->add_index('online', 'user_id_ident_idx', array('user_id', 'ident(25)'), true) or error('Unable to add user_id_ident_idx index', __FILE__, __LINE__, $db->error());
+			else
+				$db->add_index('online', 'user_id_ident_idx', array('user_id', 'ident'), true) or error('Unable to add user_id_ident_idx index', __FILE__, __LINE__, $db->error());
+		}
+
+		// Remove the redundant user_id_idx on the online table
+		$db->drop_index('online', 'user_id_idx') or error('Unable to drop user_id_idx index', __FILE__, __LINE__, $db->error());
+
+		// Add an index to ident on the online table
+		if ($mysql)
+			$db->add_index('online', 'ident_idx', array('ident(25)')) or error('Unable to add ident_idx index', __FILE__, __LINE__, $db->error());
+		else
+			$db->add_index('online', 'ident_idx', array('ident')) or error('Unable to add ident_idx index', __FILE__, __LINE__, $db->error());
+
+		// Add an index to logged in the online table
+		$db->add_index('online', 'logged_idx', array('logged')) or error('Unable to add logged_idx index', __FILE__, __LINE__, $db->error());
+
+		// Add an index to last_post in the topics table
+		$db->add_index('topics', 'last_post_idx', array('last_post')) or error('Unable to add last_post_idx index', __FILE__, __LINE__, $db->error());
+
+		// Add an index to username on the bans table
+		if ($mysql)
+			$db->add_index('bans', 'username_idx', array('username(25)')) or error('Unable to add username_idx index', __FILE__, __LINE__, $db->error());
+		else
+			$db->add_index('bans', 'username_idx', array('username')) or error('Unable to add username_idx index', __FILE__, __LINE__, $db->error());
+
+		// Change the username_idx on users to a unique index of max size 25
+		$db->drop_index('users', 'username_idx') or error('Unable to drop old username_idx index', __FILE__, __LINE__, $db->error());
+		$field = $mysql ? 'username(25)' : 'username';
+
+		// Attempt to add a unique index. If the user doesn't use a transactional database this can fail due to multiple matching usernames in the
+		// users table. This is bad, but just giving up if it happens is even worse! If it fails just add a regular non-unique index.
+		if (!$db->add_index('users', 'username_idx', array($field), true))
+			$db->add_index('users', 'username_idx', array($field)) or error('Unable to add username_idx field', __FILE__, __LINE__, $db->error());
+
+		// Add g_view_users field to groups table
+		$db->add_field('groups', 'g_view_users', 'TINYINT(1)', false, 1, 'g_read_board') or error('Unable to add g_view_users field', __FILE__, __LINE__, $db->error());
+
+		// Add the last_email_sent column to the users table and the g_send_email and
+		// g_email_flood columns to the groups table
+		$db->add_field('users', 'last_email_sent', 'INT(10) UNSIGNED', true, null, 'last_search') or error('Unable to add last_email_sent field', __FILE__, __LINE__, $db->error());
+		$db->add_field('groups', 'g_send_email', 'TINYINT(1)', false, 1, 'g_search_users') or error('Unable to add g_send_email field', __FILE__, __LINE__, $db->error());
+		$db->add_field('groups', 'g_email_flood', 'SMALLINT(6)', false, 60, 'g_search_flood') or error('Unable to add g_email_flood field', __FILE__, __LINE__, $db->error());
+
+		// Set non-default g_send_email and g_flood_email values properly
+		$db->query('UPDATE '.$db->prefix.'groups SET g_send_email = 0 WHERE g_id = 3') or error('Unable to update group email permissions', __FILE__, __LINE__, $db->error());
+		$db->query('UPDATE '.$db->prefix.'groups SET g_email_flood = 0 WHERE g_id IN (1,2,3)') or error('Unable to update group email permissions', __FILE__, __LINE__, $db->error());
+
+		// Add the auto notify/subscription option to the users table
+		$db->add_field('users', 'auto_notify', 'TINYINT(1)', false, 0, 'notify_with_post') or error('Unable to add auto_notify field', __FILE__, __LINE__, $db->error());
+
+		// Add the first_post_id column to the topics table
+		if (!$db->field_exists('topics', 'first_post_id'))
+		{
+			$db->add_field('topics', 'first_post_id', 'INT(10) UNSIGNED', false, 0, 'posted') or error('Unable to add first_post_id field', __FILE__, __LINE__, $db->error());
+			$db->add_index('topics', 'first_post_id_idx', array('first_post_id')) or error('Unable to add first_post_id_idx index', __FILE__, __LINE__, $db->error());
+
+			// Now that we've added the column and indexed it, we need to give it correct data
+			$result = $db->query('SELECT MIN(id) AS first_post, topic_id FROM '.$db->prefix.'posts GROUP BY topic_id') or error('Unable to fetch first_post_id', __FILE__, __LINE__, $db->error());
+
+			while ($cur_post = $db->fetch_assoc($result))
+				$db->query('UPDATE '.$db->prefix.'topics SET first_post_id = '.$cur_post['first_post'].' WHERE id = '.$cur_post['topic_id']) or error('Unable to update first_post_id', __FILE__, __LINE__, $db->error());
+		}
+
+		// Move any users with the old unverified status to their new group
+		$db->query('UPDATE '.$db->prefix.'users SET group_id=0 WHERE group_id=32000') or error('Unable to move unverified users', __FILE__, __LINE__, $db->error());
+
+		// Add the ban_creator column to the bans table
+		$db->add_field('bans', 'ban_creator', 'INT(10) UNSIGNED', false, 0) or error('Unable to add ban_creator field', __FILE__, __LINE__, $db->error());
+
+		// Add the time/date format settings to the user table
+		$db->add_field('users', 'time_format', 'TINYINT(1)', false, 0, 'dst') or error('Unable to add time_format field', __FILE__, __LINE__, $db->error());
+		$db->add_field('users', 'date_format', 'TINYINT(1)', false, 0, 'dst') or error('Unable to add date_format field', __FILE__, __LINE__, $db->error());
+
+		// Change the search_data field to mediumtext
+		$db->alter_field('search_cache', 'search_data', 'MEDIUMTEXT', true) or error('Unable to alter search_data field', __FILE__, __LINE__, $db->error());
+
+		// Incase we had the fulltext search extension installed (1.3-legacy), remove it
+		$db->drop_index('topics', 'subject_idx') or error('Unable to drop subject_idx index', __FILE__, __LINE__, $db->error());
+		$db->drop_index('posts', 'message_idx') or error('Unable to drop message_idx index', __FILE__, __LINE__, $db->error());
+		// Incase we had the fulltext search mod installed (1.2), remove it
+		$db->drop_index('topics', 'subject_fulltext_search') or error('Unable to drop subject_fulltext_search index', __FILE__, __LINE__, $db->error());
+		$db->drop_index('posts', 'message_fulltext_search') or error('Unable to drop message_fulltext_search index', __FILE__, __LINE__, $db->error());
+
+		// If the search_cache table has been dropped by the fulltext search extension, recreate it
+		if (!$db->table_exists('search_cache'))
+		{
+			$schema = array(
+				'FIELDS'		=> array(
+					'id'			=> array(
+						'datatype'		=> 'INT(10) UNSIGNED',
+						'allow_null'	=> false,
+						'default'		=> '0'
+					),
+					'ident'			=> array(
+						'datatype'		=> 'VARCHAR(200)',
+						'allow_null'	=> false,
+						'default'		=> '\'\''
+					),
+					'search_data'	=> array(
+						'datatype'		=> 'MEDIUMTEXT',
+						'allow_null'	=> true
+					)
+				),
+				'PRIMARY KEY'	=> array('id'),
+				'INDEXES'		=> array(
+					'ident_idx'	=> array('ident')
+				)
+			);
+
+			if ($db_type == 'mysql' || $db_type == 'mysqli' || $db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
+				$schema['INDEXES']['ident_idx'] = array('ident(8)');
+
+			$db->create_table('search_cache', $schema);
+		}
+
+		// If the search_matches table has been dropped by the fulltext search extension, recreate it
+		if (!$db->table_exists('search_matches'))
+		{
+			$schema = array(
+				'FIELDS'		=> array(
+					'post_id'		=> array(
+						'datatype'		=> 'INT(10) UNSIGNED',
+						'allow_null'	=> false,
+						'default'		=> '0'
+					),
+					'word_id'		=> array(
+						'datatype'		=> 'INT(10) UNSIGNED',
+						'allow_null'	=> false,
+						'default'		=> '0'
+					),
+					'subject_match'	=> array(
+						'datatype'		=> 'TINYINT(1)',
+						'allow_null'	=> false,
+						'default'		=> '0'
+					)
+				),
+				'INDEXES'		=> array(
+					'word_id_idx'	=> array('word_id'),
+					'post_id_idx'	=> array('post_id')
+				)
+			);
+
+			$db->create_table('search_matches', $schema);
+		}
+
+		// If the search_words table has been dropped by the fulltext search extension, recreate it
+		if (!$db->table_exists('search_words'))
+		{
+			$schema = array(
+				'FIELDS'		=> array(
+					'id'			=> array(
+						'datatype'		=> 'SERIAL',
+						'allow_null'	=> false
+					),
+					'word'			=> array(
+						'datatype'		=> 'VARCHAR(20)',
+						'allow_null'	=> false,
+						'default'		=> '\'\'',
+						'collation'		=> 'bin'
+					)
+				),
+				'PRIMARY KEY'	=> array('word'),
+				'INDEXES'		=> array(
+					'id_idx'	=> array('id')
+				)
+			);
+
+			if ($db_type == 'sqlite')
+			{
+				$schema['PRIMARY KEY'] = array('id');
+				$schema['UNIQUE KEYS'] = array('word_idx'	=> array('word'));
+			}
+
+			$db->create_table('search_words', $schema);
+		}
+
+		// Change the default style if the old doesn't exist anymore
+		if ($pun_config['o_default_style'] != $default_style)
+			$db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.$db->escape($default_style).'\' WHERE conf_name = \'o_default_style\'') or error('Unable to update default style config', __FILE__, __LINE__, $db->error());
+
+		// Should we do charset conversion or not?
+		if (strpos($cur_version, '1.2') === 0 && isset($_GET['convert_charset']))
+			$query_str = '?stage=conv_bans&req_old_charset='.$old_charset;
+
+		break;
+
+
+	// Convert bans
+	case 'conv_bans':
+		$query_str = '?stage=conv_categories&req_old_charset='.$old_charset;
+
+		function _conv_bans($cur_item, $old_charset)
+		{
+			echo 'Converting ban '.$cur_item['id'].' …<br />'."\n";
+
+			convert_to_utf8($cur_item['username'], $old_charset);
+			convert_to_utf8($cur_item['message'], $old_charset);
+
+			return $cur_item;
+		}
+
+		$end_at = convert_table_utf8($db->prefix.'bans', '_conv_bans', $old_charset, 'id', $start_at);
+
+		if ($end_at !== true)
+			$query_str = '?stage=conv_bans&req_old_charset='.$old_charset.'&start_at='.$end_at;
+
+		break;
+
+
+	// Convert categories
+	case 'conv_categories':
+		$query_str = '?stage=conv_censors&req_old_charset='.$old_charset;
+
+		echo 'Converting categories …'."<br />\n";
+
+		function _conv_categories($cur_item, $old_charset)
+		{
+			convert_to_utf8($cur_item['cat_name'], $old_charset);
+
+			return $cur_item;
+		}
+
+		convert_table_utf8($db->prefix.'categories', '_conv_categories', $old_charset, 'id');
+
+		break;
+
+
+	// Convert censor words
+	case 'conv_censors':
+		$query_str = '?stage=conv_config&req_old_charset='.$old_charset;
+
+		echo 'Converting censor words …'."<br />\n";
+
+		function _conv_censoring($cur_item, $old_charset)
+		{
+			convert_to_utf8($cur_item['search_for'], $old_charset);
+			convert_to_utf8($cur_item['replace_with'], $old_charset);
+
+			return $cur_item;
+		}
+
+		convert_table_utf8($db->prefix.'censoring', '_conv_censoring', $old_charset, 'id');
+
+		break;
+
+
+	// Convert config
+	case 'conv_config':
+		$query_str = '?stage=conv_forums&req_old_charset='.$old_charset;
+
+		echo 'Converting configuration …'."<br />\n";
+
+		function _conv_config($cur_item, $old_charset)
+		{
+			convert_to_utf8($cur_item['conf_value'], $old_charset);
+
+			return $cur_item;
+		}
+
+		convert_table_utf8($db->prefix.'config', '_conv_config', $old_charset, 'conf_name');
+
+		break;
+
+
+	// Convert forums
+	case 'conv_forums':
+		$query_str = '?stage=conv_perms&req_old_charset='.$old_charset;
+
+		echo 'Converting forums …'."<br />\n";
+
+		function _conv_forums($cur_item, $old_charset)
+		{
+			$moderators = ($cur_item['moderators'] != '') ? unserialize($cur_item['moderators']) : array();
+			$moderators_utf8 = array();
+			foreach ($moderators as $mod_username => $mod_user_id)
+			{
+				convert_to_utf8($mod_username, $old_charset);
+				$moderators_utf8[$mod_username] = $mod_user_id;
+			}
+
+			convert_to_utf8($cur_item['forum_name'], $old_charset);
+			convert_to_utf8($cur_item['forum_desc'], $old_charset);
+
+			if (!empty($moderators_utf8))
+				$cur_item['moderators'] = serialize($moderators_utf8);
+
+			return $cur_item;
+		}
+
+		convert_table_utf8($db->prefix.'forums', '_conv_forums', $old_charset, 'id');
+
+		break;
+
+
+	// Convert forum permissions
+	case 'conv_perms':
+		$query_str = '?stage=conv_groups&req_old_charset='.$old_charset;
+
+		alter_table_utf8($db->prefix.'forum_perms');
+
+		break;
+
+
+	// Convert groups
+	case 'conv_groups':
+		$query_str = '?stage=conv_online&req_old_charset='.$old_charset;
+
+		echo 'Converting groups …'."<br />\n";
+
+		function _conv_groups($cur_item, $old_charset)
+		{
+			convert_to_utf8($cur_item['g_title'], $old_charset);
+			convert_to_utf8($cur_item['g_user_title'], $old_charset);
+
+			return $cur_item;
+		}
+
+		convert_table_utf8($db->prefix.'groups', '_conv_groups', $old_charset, 'g_id');
+
+		break;
+
+
+	// Convert online
+	case 'conv_online':
+		$query_str = '?stage=conv_posts&req_old_charset='.$old_charset;
+
+		// Truncate the table
+		$db->truncate_table('online') or error('Unable to empty online table', __FILE__, __LINE__, $db->error());
+
+		alter_table_utf8($db->prefix.'online');
+
+		break;
+
+
+	// Convert posts
+	case 'conv_posts':
+		$query_str = '?stage=conv_ranks&req_old_charset='.$old_charset;
+
+		function _conv_posts($cur_item, $old_charset)
+		{
+			echo 'Converting post '.$cur_item['id'].' …<br />'."\n";
+
+			convert_to_utf8($cur_item['poster'], $old_charset);
+			convert_to_utf8($cur_item['message'], $old_charset);
+			convert_to_utf8($cur_item['edited_by'], $old_charset);
+
+			return $cur_item;
+		}
+
+		$end_at = convert_table_utf8($db->prefix.'posts', '_conv_posts', $old_charset, 'id', $start_at);
+
+		if ($end_at !== true)
+			$query_str = '?stage=conv_posts&req_old_charset='.$old_charset.'&start_at='.$end_at;
+
+		break;
+
+
+	// Convert ranks
+	case 'conv_ranks':
+		$query_str = '?stage=conv_reports&req_old_charset='.$old_charset;
+
+		echo 'Converting ranks …'."<br />\n";
+
+		function _conv_ranks($cur_item, $old_charset)
+		{
+			convert_to_utf8($cur_item['rank'], $old_charset);
+
+			return $cur_item;
+		}
+
+		convert_table_utf8($db->prefix.'ranks', '_conv_ranks', $old_charset, 'id');
+
+		break;
+
+
+	// Convert reports
+	case 'conv_reports':
+		$query_str = '?stage=conv_search_cache&req_old_charset='.$old_charset;
+
+		function _conv_reports($cur_item, $old_charset)
+		{
+			echo 'Converting report '.$cur_item['id'].' …<br />'."\n";
+
+			convert_to_utf8($cur_item['message'], $old_charset);
+
+			return $cur_item;
+		}
+
+		$end_at = convert_table_utf8($db->prefix.'reports', '_conv_reports', $old_charset, 'id', $start_at);
+
+		if ($end_at !== true)
+			$query_str = '?stage=conv_reports&req_old_charset='.$old_charset.'&start_at='.$end_at;
+
+		break;
+
+
+	// Convert search cache
+	case 'conv_search_cache':
+		$query_str = '?stage=conv_search_matches&req_old_charset='.$old_charset;
+
+		// Truncate the table
+		$db->truncate_table('search_cache') or error('Unable to empty search cache table', __FILE__, __LINE__, $db->error());
+
+		alter_table_utf8($db->prefix.'search_cache');
+
+		break;
+
+
+	// Convert search matches
+	case 'conv_search_matches':
+		$query_str = '?stage=conv_search_words&req_old_charset='.$old_charset;
+
+		// Truncate the table
+		$db->truncate_table('search_matches') or error('Unable to empty search index match table', __FILE__, __LINE__, $db->error());
+
+		alter_table_utf8($db->prefix.'search_matches');
+
+		break;
+
+
+	// Convert search words
+	case 'conv_search_words':
+		$query_str = '?stage=conv_subscriptions&req_old_charset='.$old_charset;
+
+		// Truncate the table
+		$db->truncate_table('search_words') or error('Unable to empty search index words table', __FILE__, __LINE__, $db->error());
+
+		// Reset the sequence for the search words (not needed for SQLite)
+		switch ($db_type)
+		{
+			case 'mysql':
+			case 'mysqli':
+			case 'mysql_innodb':
+			case 'mysqli_innodb':
+				$db->query('ALTER TABLE '.$db->prefix.'search_words auto_increment=1') or error('Unable to update table auto_increment', __FILE__, __LINE__, $db->error());
+				break;
+
+			case 'pgsql';
+				$db->query('SELECT setval(\''.$db->prefix.'search_words_id_seq\', 1, false)') or error('Unable to update sequence', __FILE__, __LINE__, $db->error());
+				break;
+		}
+
+		alter_table_utf8($db->prefix.'search_words');
+
+		break;
+
+
+	// Convert subscriptions
+	case 'conv_subscriptions':
+		$query_str = '?stage=conv_topics&req_old_charset='.$old_charset;
+
+		alter_table_utf8($db->prefix.'subscriptions');
+
+		break;
+
+
+	// Convert topics
+	case 'conv_topics':
+		$query_str = '?stage=conv_users&req_old_charset='.$old_charset;
+
+		function _conv_topics($cur_item, $old_charset)
+		{
+			echo 'Converting topic '.$cur_item['id'].' …<br />'."\n";
+
+			convert_to_utf8($cur_item['poster'], $old_charset);
+			convert_to_utf8($cur_item['subject'], $old_charset);
+			convert_to_utf8($cur_item['last_poster'], $old_charset);
+
+			return $cur_item;
+		}
+
+		$end_at = convert_table_utf8($db->prefix.'topics', '_conv_topics', $old_charset, 'id', $start_at);
+
+		if ($end_at !== true)
+			$query_str = '?stage=conv_topics&req_old_charset='.$old_charset.'&start_at='.$end_at;
+
+		break;
+
+
+	// Convert users
+	case 'conv_users':
+		$query_str = '?stage=preparse_posts';
+
+		function _conv_users($cur_item, $old_charset)
+		{
+			echo 'Converting user '.$cur_item['id'].' …<br />'."\n";
+
+			convert_to_utf8($cur_item['username'], $old_charset);
+			convert_to_utf8($cur_item['title'], $old_charset);
+			convert_to_utf8($cur_item['realname'], $old_charset);
+			convert_to_utf8($cur_item['location'], $old_charset);
+			convert_to_utf8($cur_item['signature'], $old_charset);
+			convert_to_utf8($cur_item['admin_note'], $old_charset);
+
+			return $cur_item;
+		}
+
+		function _error_users($cur_user)
+		{
+			$_SESSION['dupe_users'][$cur_user['id']] = $cur_user;
+		}
+
+		$end_at = convert_table_utf8($db->prefix.'users', '_conv_users', $old_charset, 'id', $start_at, '_error_users');
+
+		if ($end_at !== true)
+			$query_str = '?stage=conv_users&req_old_charset='.$old_charset.'&start_at='.$end_at;
+		else if (!empty($_SESSION['dupe_users']))
+			$query_str = '?stage=conv_users_dupe';
+
+		break;
+
+
+	// Handle any duplicate users which occured due to conversion
+	case 'conv_users_dupe':
+		$query_str = '?stage=preparse_posts';
+
+		if (!$mysql || empty($_SESSION['dupe_users']))
+			break;
+
+		if (isset($_POST['form_sent']))
+		{
+			$errors = array();
+
+			require PUN_ROOT.'include/email.php';
+
+			foreach ($_SESSION['dupe_users'] as $id => $cur_user)
+			{
+				$errors[$id] = array();
+
+				$username = pun_trim($_POST['dupe_users'][$id]);
+
+				if (pun_strlen($username) < 2)
+					$errors[$id][] = 'Usernames must be at least 2 characters long. Please choose another (longer) username.';
+				else if (pun_strlen($username) > 25) // This usually doesn't happen since the form element only accepts 25 characters
+					$errors[$id][] = 'Usernames must not be more than 25 characters long. Please choose another (shorter) username.';
+				else if (!strcasecmp($username, 'Guest'))
+					$errors[$id][] = 'The username guest is reserved. Please choose another username.';
+				else if (preg_match('/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/', $username) || preg_match('/((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2} [...]
+					$errors[$id][] = 'Usernames may not be in the form of an IP address. Please choose another username.';
+				else if ((strpos($username, '[') !== false || strpos($username, ']') !== false) && strpos($username, '\'') !== false && strpos($username, '"') !== false)
+					$errors[$id][] = 'Usernames may not contain all the characters \', " and [ or ] at once. Please choose another username.';
+				else if (preg_match('/(?:\[\/?(?:b|u|s|ins|del|em|i|h|colou?r|quote|code|img|url|email|list|\*)\]|\[(?:img|url|quote|list)=)/i', $username))
+					$errors[$id][] = 'Usernames may not contain any of the text formatting tags (BBCode) that the forum uses. Please choose another username.';
+
+				$result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE (UPPER(username)=UPPER(\''.$db->escape($username).'\') OR UPPER(username)=UPPER(\''.$db->escape(preg_replace('/[^\w]/', '', $username)).'\')) AND id>1') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+
+				if ($db->num_rows($result))
+				{
+					$busy = $db->result($result);
+					$errors[$id][] = 'Someone is already registered with the username '.pun_htmlspecialchars($busy).'. The username you entered is too similar. The username must differ from that by at least one alphanumerical character (a-z or 0-9). Please choose a different username.';
+				}
+
+				if (empty($errors[$id]))
+				{
+					$old_username = $cur_user['username'];
+					$_SESSION['dupe_users'][$id]['username'] = $cur_user['username'] = $username;
+
+					$temp = array();
+					foreach ($cur_user as $idx => $value)
+						$temp[$idx] = $value === null ? 'NULL' : '\''.$db->escape($value).'\'';
+
+					// Insert the renamed user
+					$db->query('INSERT INTO '.$db->prefix.'users('.implode(',', array_keys($temp)).') VALUES ('.implode(',', array_values($temp)).')') or error('Unable to insert data to new table', __FILE__, __LINE__, $db->error());
+
+					// Renaming a user also affects a bunch of other stuff, lets fix that too...
+					$db->query('UPDATE '.$db->prefix.'posts SET poster=\''.$db->escape($username).'\' WHERE poster_id='.$id) or error('Unable to update posts', __FILE__, __LINE__, $db->error());
+
+					// TODO: The following must compare using collation utf8_bin otherwise we will accidently update posts/topics/etc belonging to both of the duplicate users, not just the one we renamed!
+					$db->query('UPDATE '.$db->prefix.'posts SET edited_by=\''.$db->escape($username).'\' WHERE edited_by=\''.$db->escape($old_username).'\' COLLATE utf8_bin') or error('Unable to update posts', __FILE__, __LINE__, $db->error());
+					$db->query('UPDATE '.$db->prefix.'topics SET poster=\''.$db->escape($username).'\' WHERE poster=\''.$db->escape($old_username).'\' COLLATE utf8_bin') or error('Unable to update topics', __FILE__, __LINE__, $db->error());
+					$db->query('UPDATE '.$db->prefix.'topics SET last_poster=\''.$db->escape($username).'\' WHERE last_poster=\''.$db->escape($old_username).'\' COLLATE utf8_bin') or error('Unable to update topics', __FILE__, __LINE__, $db->error());
+					$db->query('UPDATE '.$db->prefix.'forums SET last_poster=\''.$db->escape($username).'\' WHERE last_poster=\''.$db->escape($old_username).'\' COLLATE utf8_bin') or error('Unable to update forums', __FILE__, __LINE__, $db->error());
+					$db->query('UPDATE '.$db->prefix.'online SET ident=\''.$db->escape($username).'\' WHERE ident=\''.$db->escape($old_username).'\' COLLATE utf8_bin') or error('Unable to update online list', __FILE__, __LINE__, $db->error());
+
+					// If the user is a moderator or an administrator we have to update the moderator lists
+					$result = $db->query('SELECT g_moderator FROM '.$db->prefix.'groups WHERE g_id='.$cur_user['group_id']) or error('Unable to fetch group', __FILE__, __LINE__, $db->error());
+					$group_mod = $db->result($result);
+
+					if ($cur_user['group_id'] == PUN_ADMIN || $group_mod == '1')
+					{
+						$result = $db->query('SELECT id, moderators FROM '.$db->prefix.'forums') or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
+
+						while ($cur_forum = $db->fetch_assoc($result))
+						{
+							$cur_moderators = ($cur_forum['moderators'] != '') ? unserialize($cur_forum['moderators']) : array();
+
+							if (in_array($id, $cur_moderators))
+							{
+								unset($cur_moderators[$old_username]);
+								$cur_moderators[$username] = $id;
+								uksort($cur_moderators, 'utf8_strcasecmp');
+
+								$db->query('UPDATE '.$db->prefix.'forums SET moderators=\''.$db->escape(serialize($cur_moderators)).'\' WHERE id='.$cur_forum['id']) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
+							}
+						}
+					}
+
+					// Email the user alerting them of the change
+					if (file_exists(PUN_ROOT.'lang/'.$cur_user['language'].'/mail_templates/rename.tpl'))
+						$mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$cur_user['language'].'/mail_templates/rename.tpl'));
+					else if (file_exists(PUN_ROOT.'lang/'.$pun_config['o_default_lang'].'/mail_templates/rename.tpl'))
+						$mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_config['o_default_lang'].'/mail_templates/rename.tpl'));
+					else
+						$mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/English/mail_templates/rename.tpl'));
+
+					// The first row contains the subject
+					$first_crlf = strpos($mail_tpl, "\n");
+					$mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
+					$mail_message = trim(substr($mail_tpl, $first_crlf));
+
+					$mail_subject = str_replace('<board_title>', $pun_config['o_board_title'], $mail_subject);
+					$mail_message = str_replace('<base_url>', $pun_config['o_base_url'].'/', $mail_message);
+					$mail_message = str_replace('<old_username>', $old_username, $mail_message);
+					$mail_message = str_replace('<new_username>', $username, $mail_message);
+					$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'].' Mailer', $mail_message);
+
+					pun_mail($cur_user['email'], $mail_subject, $mail_message);
+
+					unset($_SESSION['dupe_users'][$id]);
+				}
+			}
+		}
+
+		if (!empty($_SESSION['dupe_users']))
+		{
+			$query_str = '';
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>FluxBB Database Update</title>
+<link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
+</head>
+<body>
+
+<div id="pundb_update" class="pun">
+<div class="top-box"><div><!-- Top Corners --></div></div>
+<div class="punwrap">
+
+<div class="blockform">
+	<h2><span>Error converting users</span></h2>
+	<div class="box">
+		<form method="post" action="db_update.php?stage=conv_users_dupe">
+			<input type="hidden" name="form_sent" value="1" />
+			<div class="inform">
+				<div class="forminfo">
+						<p style="font-size: 1.1em">There was an error converting some users. This can occur when converting from FluxBB v1.2 if multiple users have registered with very similar usernames, for example "bob" and "böb".</p>
+						<p style="font-size: 1.1em">Below is a list of users who failed to convert. Please choose a new username for each user. Users who are renamed will automatically be sent an email alerting them of the change.</p>
+				</div>
+			</div>
+<?php
+
+			foreach ($_SESSION['dupe_users'] as $id => $cur_user)
+			{
+
+?>
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo pun_htmlspecialchars($cur_user['username']); ?></legend>
+					<div class="infldset">
+						<label class="required"><strong>New username <span>(required)</span></strong><br /><input type="text" name="<?php echo 'dupe_users['.$id.']'; ?>" value="<?php if (isset($_POST['dupe_users'][$id])) echo pun_htmlspecialchars($_POST['dupe_users'][$id]); ?>" size="25" maxlength="25" /><br /></label>
+					</div>
+				</fieldset>
+<?php if (!empty($errors[$id])): ?>				<div class="forminfo error-info">
+					<h3>The following errors need to be corrected:</h3>
+					<ul class="error-list">
+<?php
+
+foreach ($errors[$id] as $cur_error)
+	echo "\t\t\t\t\t\t".'<li><strong>'.$cur_error.'</strong></li>'."\n";
+?>
+					</ul>
+				</div>
+<?php endif; ?>			</div>
+<?php
+
+			}
+
+?>
+			<p class="buttons"><input type="submit" name="rename" value="Rename users" /></p>
+		</form>
+	</div>
+</div>
+
+</div>
+<div class="end-box"><div><!-- Bottom Corners --></div></div>
+</div>
+
+</body>
+</html>
+<?php
+
+		}
+
+		break;
+
+
+	// Preparse posts
+	case 'preparse_posts':
+		$query_str = '?stage=preparse_sigs';
+
+		// If we don't need to parse the posts, skip this stage
+		if (isset($pun_config['o_parser_revision']) && $pun_config['o_parser_revision'] >= UPDATE_TO_PARSER_REVISION)
+			break;
+
+		require PUN_ROOT.'include/parser.php';
+
+		// Fetch posts to process this cycle
+		$result = $db->query('SELECT id, message FROM '.$db->prefix.'posts WHERE id > '.$start_at.' ORDER BY id ASC LIMIT '.PER_PAGE) or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
+
+		$temp = array();
+		$end_at = 0;
+		while ($cur_item = $db->fetch_assoc($result))
+		{
+			echo 'Preparsing post '.$cur_item['id'].' …<br />'."\n";
+			$db->query('UPDATE '.$db->prefix.'posts SET message = \''.$db->escape(preparse_bbcode($cur_item['message'], $temp)).'\' WHERE id = '.$cur_item['id']) or error('Unable to update post', __FILE__, __LINE__, $db->error());
+
+			$end_at = $cur_item['id'];
+		}
+
+		// Check if there is more work to do
+		if ($end_at > 0)
+		{
+			$result = $db->query('SELECT 1 FROM '.$db->prefix.'posts WHERE id > '.$end_at.' ORDER BY id ASC LIMIT 1') or error('Unable to fetch next ID', __FILE__, __LINE__, $db->error());
+
+			if ($db->num_rows($result) > 0)
+				$query_str = '?stage=preparse_posts&start_at='.$end_at;
+		}
+
+		break;
+
+
+	// Preparse signatures
+	case 'preparse_sigs':
+		$query_str = '?stage=rebuild_idx';
+
+		// If we don't need to parse the sigs, skip this stage
+		if (isset($pun_config['o_parser_revision']) && $pun_config['o_parser_revision'] >= UPDATE_TO_PARSER_REVISION)
+			break;
+
+		require PUN_ROOT.'include/parser.php';
+
+		// Fetch users to process this cycle
+		$result = $db->query('SELECT id, signature FROM '.$db->prefix.'users WHERE id > '.$start_at.' ORDER BY id ASC LIMIT '.PER_PAGE) or error('Unable to fetch users', __FILE__, __LINE__, $db->error());
+
+		$temp = array();
+		$end_at = 0;
+		while ($cur_item = $db->fetch_assoc($result))
+		{
+			echo 'Preparsing signature '.$cur_item['id'].' …<br />'."\n";
+			$db->query('UPDATE '.$db->prefix.'users SET signature = \''.$db->escape(preparse_bbcode($cur_item['signature'], $temp, true)).'\' WHERE id = '.$cur_item['id']) or error('Unable to update user', __FILE__, __LINE__, $db->error());
+
+			$end_at = $cur_item['id'];
+		}
+
+		// Check if there is more work to do
+		if ($end_at > 0)
+		{
+			$result = $db->query('SELECT 1 FROM '.$db->prefix.'users WHERE id > '.$end_at.' ORDER BY id ASC LIMIT 1') or error('Unable to fetch next ID', __FILE__, __LINE__, $db->error());
+			if ($db->num_rows($result) > 0)
+				$query_str = '?stage=preparse_sigs&start_at='.$end_at;
+		}
+
+		break;
+
+
+	// Rebuild the search index
+	case 'rebuild_idx':
+		$query_str = '?stage=finish';
+
+		// If we don't need to update the search index, skip this stage
+		if (isset($pun_config['o_searchindex_revision']) && $pun_config['o_searchindex_revision'] >= UPDATE_TO_SI_REVISION)
+			break;
+
+		if ($start_at == 0)
+		{
+			// Truncate the tables just in-case we didn't already (if we are coming directly here without converting the tables)
+			$db->truncate_table('search_cache') or error('Unable to empty search cache table', __FILE__, __LINE__, $db->error());
+			$db->truncate_table('search_matches') or error('Unable to empty search index match table', __FILE__, __LINE__, $db->error());
+			$db->truncate_table('search_words') or error('Unable to empty search index words table', __FILE__, __LINE__, $db->error());
+
+			// Reset the sequence for the search words (not needed for SQLite)
+			switch ($db_type)
+			{
+				case 'mysql':
+				case 'mysqli':
+				case 'mysql_innodb':
+				case 'mysqli_innodb':
+					$db->query('ALTER TABLE '.$db->prefix.'search_words auto_increment=1') or error('Unable to update table auto_increment', __FILE__, __LINE__, $db->error());
+					break;
+
+				case 'pgsql';
+					$db->query('SELECT setval(\''.$db->prefix.'search_words_id_seq\', 1, false)') or error('Unable to update sequence', __FILE__, __LINE__, $db->error());
+					break;
+			}
+		}
+
+		require PUN_ROOT.'include/search_idx.php';
+
+		// Fetch posts to process this cycle
+		$result = $db->query('SELECT p.id, p.message, t.subject, t.first_post_id FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id WHERE p.id > '.$start_at.' ORDER BY p.id ASC LIMIT '.PER_PAGE) or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
+
+		$end_at = 0;
+		while ($cur_item = $db->fetch_assoc($result))
+		{
+			echo 'Rebuilding index for post '.$cur_item['id'].' …<br />'."\n";
+
+			if ($cur_item['id'] == $cur_item['first_post_id'])
+				update_search_index('post', $cur_item['id'], $cur_item['message'], $cur_item['subject']);
+			else
+				update_search_index('post', $cur_item['id'], $cur_item['message']);
+
+			$end_at = $cur_item['id'];
+		}
+
+		// Check if there is more work to do
+		if ($end_at > 0)
+		{
+			$result = $db->query('SELECT 1 FROM '.$db->prefix.'posts WHERE id > '.$end_at.' ORDER BY id ASC LIMIT 1') or error('Unable to fetch next ID', __FILE__, __LINE__, $db->error());
+
+			if ($db->num_rows($result) > 0)
+				$query_str = '?stage=rebuild_idx&start_at='.$end_at;
+		}
+
+		break;
+
+
+	// Show results page
+	case 'finish':
+		// We update the version number
+		$db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.UPDATE_TO.'\' WHERE conf_name = \'o_cur_version\'') or error('Unable to update version', __FILE__, __LINE__, $db->error());
+
+		// And the database revision number
+		$db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.UPDATE_TO_DB_REVISION.'\' WHERE conf_name = \'o_database_revision\'') or error('Unable to update database revision number', __FILE__, __LINE__, $db->error());
+
+		// And the search index revision number
+		$db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.UPDATE_TO_SI_REVISION.'\' WHERE conf_name = \'o_searchindex_revision\'') or error('Unable to update search index revision number', __FILE__, __LINE__, $db->error());
+
+		// And the parser revision number
+		$db->query('UPDATE '.$db->prefix.'config SET conf_value = \''.UPDATE_TO_PARSER_REVISION.'\' WHERE conf_name = \'o_parser_revision\'') or error('Unable to update parser revision number', __FILE__, __LINE__, $db->error());
+
+		// Check the default language still exists!
+		if (!file_exists(PUN_ROOT.'lang/'.$pun_config['o_default_lang'].'/common.php'))
+			$db->query('UPDATE '.$db->prefix.'config SET conf_value = \'English\' WHERE conf_name = \'o_default_lang\'') or error('Unable to update default language', __FILE__, __LINE__, $db->error());
+
+		// Check the default style still exists!
+		if (!file_exists(PUN_ROOT.'style/'.$pun_config['o_default_style'].'.css'))
+			$db->query('UPDATE '.$db->prefix.'config SET conf_value = \'Air\' WHERE conf_name = \'o_default_style\'') or error('Unable to update default style', __FILE__, __LINE__, $db->error());
+
+		// This feels like a good time to synchronize the forums
+		$result = $db->query('SELECT id FROM '.$db->prefix.'forums') or error('Unable to fetch forum IDs', __FILE__, __LINE__, $db->error());
+
+		while ($row = $db->fetch_row($result))
+			update_forum($row[0]);
+
+		// Empty the PHP cache
+		forum_clear_cache();
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>FluxBB Database Update</title>
+<link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
+</head>
+<body>
+
+<div id="pundb_update" class="pun">
+<div class="top-box"><div><!-- Top Corners --></div></div>
+<div class="punwrap">
+
+<div class="blockform">
+	<h2><span>FluxBB Update</span></h2>
+	<div class="box">
+		<div class="fakeform">
+			<div class="inform">
+				<div class="forminfo">
+					<p style="font-size: 1.1em">Your forum database was successfully updated. You may now <a href="<?php echo PUN_ROOT ?>index.php">go to the forum index</a>.</p>
+				</div>
+			</div>
+		</div>
+	</div>
+</div>
+
+</div>
+<div class="end-box"><div><!-- Bottom Corners --></div></div>
+</div>
+
+</body>
+</html>
+<?php
+
+		break;
+}
+
+$db->end_transaction();
+$db->close();
+
+if ($query_str != '')
+	exit('<script type="text/javascript">window.location="db_update.php'.$query_str.'"</script><noscript>JavaScript seems to be disabled. <a href="db_update.php'.$query_str.'">Click here to continue</a>.</noscript>');
diff --git a/delete.php b/delete.php
new file mode 100644
index 0000000..f810b53
--- /dev/null
+++ b/delete.php
@@ -0,0 +1,133 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+
+
+if ($pun_user['g_read_board'] == '0')
+	message($lang_common['No view']);
+
+
+$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
+if ($id < 1)
+	message($lang_common['Bad request']);
+
+// Fetch some info about the post, the topic and the forum
+$result = $db->query('SELECT f.id AS fid, f.forum_name, f.moderators, f.redirect_url, fp.post_replies, fp.post_topics, t.id AS tid, t.subject, t.first_post_id, t.closed, p.posted, p.poster, p.poster_id, p.message, p.hide_smilies FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_ [...]
+if (!$db->num_rows($result))
+	message($lang_common['Bad request']);
+
+$cur_post = $db->fetch_assoc($result);
+
+if ($pun_config['o_censoring'] == '1')
+	$cur_post['subject'] = censor_words($cur_post['subject']);
+
+// Sort out who the moderators are and if we are currently a moderator (or an admin)
+$mods_array = ($cur_post['moderators'] != '') ? unserialize($cur_post['moderators']) : array();
+$is_admmod = ($pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_moderator'] == '1' && array_key_exists($pun_user['username'], $mods_array))) ? true : false;
+
+$is_topic_post = ($id == $cur_post['first_post_id']) ? true : false;
+
+// Do we have permission to edit this post?
+if (($pun_user['g_delete_posts'] == '0' ||
+	($pun_user['g_delete_topics'] == '0' && $is_topic_post) ||
+	$cur_post['poster_id'] != $pun_user['id'] ||
+	$cur_post['closed'] == '1') &&
+	!$is_admmod)
+	message($lang_common['No permission']);
+
+// Load the delete.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/delete.php';
+
+
+if (isset($_POST['delete']))
+{
+	if ($is_admmod)
+		confirm_referrer('delete.php');
+
+	require PUN_ROOT.'include/search_idx.php';
+
+	if ($is_topic_post)
+	{
+		// Delete the topic and all of it's posts
+		delete_topic($cur_post['tid']);
+		update_forum($cur_post['fid']);
+
+		redirect('viewforum.php?id='.$cur_post['fid'], $lang_delete['Topic del redirect']);
+	}
+	else
+	{
+		// Delete just this one post
+		delete_post($id, $cur_post['tid']);
+		update_forum($cur_post['fid']);
+
+		redirect('viewtopic.php?id='.$cur_post['tid'], $lang_delete['Post del redirect']);
+	}
+}
+
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_delete['Delete post']);
+define ('PUN_ACTIVE_PAGE', 'index');
+require PUN_ROOT.'header.php';
+
+require PUN_ROOT.'include/parser.php';
+$cur_post['message'] = parse_message($cur_post['message'], $cur_post['hide_smilies']);
+
+?>
+<div class="linkst">
+	<div class="inbox">
+		<ul class="crumbs">
+			<li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+			<li><span>» </span><a href="viewforum.php?id=<?php echo $cur_post['fid'] ?>"><?php echo pun_htmlspecialchars($cur_post['forum_name']) ?></a></li>
+			<li><span>» </span><a href="viewtopic.php?pid=<?php echo $id ?>#p<?php echo $id ?>"><?php echo pun_htmlspecialchars($cur_post['subject']) ?></a></li>
+			<li><span>» </span><strong><?php echo $lang_delete['Delete post'] ?></strong></li>
+		</ul>
+	</div>
+</div>
+
+<div class="blockform">
+	<h2><span><?php echo $lang_delete['Delete post'] ?></span></h2>
+	<div class="box">
+		<form method="post" action="delete.php?id=<?php echo $id ?>">
+			<div class="inform">
+				<div class="forminfo">
+					<h3><span><?php printf($is_topic_post ? $lang_delete['Topic by'] : $lang_delete['Reply by'], '<strong>'.pun_htmlspecialchars($cur_post['poster']).'</strong>', format_time($cur_post['posted'])) ?></span></h3>
+					<p><?php echo ($is_topic_post) ? '<strong>'.$lang_delete['Topic warning'].'</strong>' : '<strong>'.$lang_delete['Warning'].'</strong>' ?><br /><?php echo $lang_delete['Delete info'] ?></p>
+				</div>
+			</div>
+			<p class="buttons"><input type="submit" name="delete" value="<?php echo $lang_delete['Delete'] ?>" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+		</form>
+	</div>
+</div>
+
+<div id="postreview">
+	<div class="blockpost">
+		<div class="box<?php echo ($post_count % 2 == 0) ? ' roweven' : ' rowodd' ?>">
+			<div class="inbox">
+				<div class="postbody">
+					<div class="postleft">
+						<dl>
+							<dt><strong><?php echo pun_htmlspecialchars($cur_post['poster']) ?></strong></dt>
+							<dd><span><?php echo format_time($cur_post['posted']) ?></span></dd>
+						</dl>
+					</div>
+					<div class="postright">
+						<div class="postmsg">
+							<?php echo $cur_post['message']."\n" ?>
+						</div>
+					</div>
+				</div>
+				<div class="clearer"></div>
+			</div>
+		</div>
+	</div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/edit.php b/edit.php
new file mode 100644
index 0000000..62b29e2
--- /dev/null
+++ b/edit.php
@@ -0,0 +1,257 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+
+
+if ($pun_user['g_read_board'] == '0')
+	message($lang_common['No view']);
+
+
+$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
+if ($id < 1)
+	message($lang_common['Bad request']);
+
+// Fetch some info about the post, the topic and the forum
+$result = $db->query('SELECT f.id AS fid, f.forum_name, f.moderators, f.redirect_url, fp.post_replies, fp.post_topics, t.id AS tid, t.subject, t.posted, t.first_post_id, t.closed, p.poster, p.poster_id, p.message, p.hide_smilies FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_ [...]
+if (!$db->num_rows($result))
+	message($lang_common['Bad request']);
+
+$cur_post = $db->fetch_assoc($result);
+
+// Sort out who the moderators are and if we are currently a moderator (or an admin)
+$mods_array = ($cur_post['moderators'] != '') ? unserialize($cur_post['moderators']) : array();
+$is_admmod = ($pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_moderator'] == '1' && array_key_exists($pun_user['username'], $mods_array))) ? true : false;
+
+$can_edit_subject = $id == $cur_post['first_post_id'];
+
+if ($pun_config['o_censoring'] == '1')
+{
+	$cur_post['subject'] = censor_words($cur_post['subject']);
+	$cur_post['message'] = censor_words($cur_post['message']);
+}
+
+// Do we have permission to edit this post?
+if (($pun_user['g_edit_posts'] == '0' ||
+	$cur_post['poster_id'] != $pun_user['id'] ||
+	$cur_post['closed'] == '1') &&
+	!$is_admmod)
+	message($lang_common['No permission']);
+
+// Load the post.php/edit.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/post.php';
+
+// Start with a clean slate
+$errors = array();
+
+
+if (isset($_POST['form_sent']))
+{
+	if ($is_admmod)
+		confirm_referrer('edit.php');
+
+	// If it's a topic it must contain a subject
+	if ($can_edit_subject)
+	{
+		$subject = pun_trim($_POST['req_subject']);
+
+		if ($subject == '')
+			$errors[] = $lang_post['No subject'];
+		else if (pun_strlen($subject) > 70)
+			$errors[] = $lang_post['Too long subject'];
+		else if ($pun_config['p_subject_all_caps'] == '0' && is_all_uppercase($subject) && !$pun_user['is_admmod'])
+			$errors[] = $lang_post['All caps subject'];
+	}
+
+	// Clean up message from POST
+	$message = pun_linebreaks(pun_trim($_POST['req_message']));
+
+	// Here we use strlen() not pun_strlen() as we want to limit the post to PUN_MAX_POSTSIZE bytes, not characters
+	if (strlen($message) > PUN_MAX_POSTSIZE)
+		$errors[] = sprintf($lang_post['Too long message'], forum_number_format(PUN_MAX_POSTSIZE));
+	else if ($pun_config['p_message_all_caps'] == '0' && is_all_uppercase($message) && !$pun_user['is_admmod'])
+		$errors[] = $lang_post['All caps message'];
+
+	// Validate BBCode syntax
+	if ($pun_config['p_message_bbcode'] == '1')
+	{
+		require PUN_ROOT.'include/parser.php';
+		$message = preparse_bbcode($message, $errors);
+	}
+
+	if ($message == '')
+		$errors[] = $lang_post['No message'];
+
+	$hide_smilies = isset($_POST['hide_smilies']) ? '1' : '0';
+
+	// Did everything go according to plan?
+	if (empty($errors) && !isset($_POST['preview']))
+	{
+		$edited_sql = (!isset($_POST['silent']) || !$is_admmod) ? $edited_sql = ', edited='.time().', edited_by=\''.$db->escape($pun_user['username']).'\'' : '';
+
+		require PUN_ROOT.'include/search_idx.php';
+
+		if ($can_edit_subject)
+		{
+			// Update the topic and any redirect topics
+			$db->query('UPDATE '.$db->prefix.'topics SET subject=\''.$db->escape($subject).'\' WHERE id='.$cur_post['tid'].' OR moved_to='.$cur_post['tid']) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+
+			// We changed the subject, so we need to take that into account when we update the search words
+			update_search_index('edit', $id, $message, $subject);
+		}
+		else
+			update_search_index('edit', $id, $message);
+
+		// Update the post
+		$db->query('UPDATE '.$db->prefix.'posts SET message=\''.$db->escape($message).'\', hide_smilies='.$hide_smilies.$edited_sql.' WHERE id='.$id) or error('Unable to update post', __FILE__, __LINE__, $db->error());
+
+		redirect('viewtopic.php?pid='.$id.'#p'.$id, $lang_post['Edit redirect']);
+	}
+}
+
+
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_post['Edit post']);
+$required_fields = array('req_subject' => $lang_common['Subject'], 'req_message' => $lang_common['Message']);
+$focus_element = array('edit', 'req_message');
+define('PUN_ACTIVE_PAGE', 'index');
+require PUN_ROOT.'header.php';
+
+$cur_index = 1;
+
+?>
+<div class="linkst">
+	<div class="inbox">
+		<ul class="crumbs">
+			<li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+			<li><span>» </span><a href="viewforum.php?id=<?php echo $cur_post['fid'] ?>"><?php echo pun_htmlspecialchars($cur_post['forum_name']) ?></a></li>
+			<li><span>» </span><a href="viewtopic.php?id=<?php echo $cur_post['tid'] ?>"><?php echo pun_htmlspecialchars($cur_post['subject']) ?></a></li>
+			<li><span>» </span><strong><?php echo $lang_post['Edit post'] ?></strong></li>
+		</ul>
+	</div>
+</div>
+
+<?php
+
+// If there are errors, we display them
+if (!empty($errors))
+{
+
+?>
+<div id="posterror" class="block">
+	<h2><span><?php echo $lang_post['Post errors'] ?></span></h2>
+	<div class="box">
+		<div class="inbox error-info">
+			<p><?php echo $lang_post['Post errors info'] ?></p>
+			<ul class="error-list">
+<?php
+
+	foreach ($errors as $cur_error)
+		echo "\t\t\t\t".'<li><strong>'.$cur_error.'</strong></li>'."\n";
+?>
+			</ul>
+		</div>
+	</div>
+</div>
+
+<?php
+
+}
+else if (isset($_POST['preview']))
+{
+	require_once PUN_ROOT.'include/parser.php';
+	$preview_message = parse_message($message, $hide_smilies);
+
+?>
+<div id="postpreview" class="blockpost">
+	<h2><span><?php echo $lang_post['Post preview'] ?></span></h2>
+	<div class="box">
+		<div class="inbox">
+			<div class="postbody">
+				<div class="postright">
+					<div class="postmsg">
+						<?php echo $preview_message."\n" ?>
+					</div>
+				</div>
+			</div>
+		</div>
+	</div>
+</div>
+
+<?php
+
+}
+
+?>
+<div id="editform" class="blockform">
+	<h2><span><?php echo $lang_post['Edit post'] ?></span></h2>
+	<div class="box">
+		<form id="edit" method="post" action="edit.php?id=<?php echo $id ?>&action=edit" onsubmit="return process_form(this)">
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_post['Edit post legend'] ?></legend>
+					<input type="hidden" name="form_sent" value="1" />
+					<div class="infldset txtarea">
+<?php if ($can_edit_subject): ?>						<label class="required"><strong><?php echo $lang_common['Subject'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
+						<input class="longinput" type="text" name="req_subject" size="80" maxlength="70" tabindex="<?php echo $cur_index++ ?>" value="<?php echo pun_htmlspecialchars(isset($_POST['req_subject']) ? $_POST['req_subject'] : $cur_post['subject']) ?>" /><br /></label>
+<?php endif; ?>						<label class="required"><strong><?php echo $lang_common['Message'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
+						<textarea name="req_message" rows="20" cols="95" tabindex="<?php echo $cur_index++ ?>"><?php echo pun_htmlspecialchars(isset($_POST['req_message']) ? $message : $cur_post['message']) ?></textarea><br /></label>
+						<ul class="bblinks">
+							<li><span><a href="help.php#bbcode" onclick="window.open(this.href); return false;"><?php echo $lang_common['BBCode'] ?></a> <?php echo ($pun_config['p_message_bbcode'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+							<li><span><a href="help.php#img" onclick="window.open(this.href); return false;"><?php echo $lang_common['img tag'] ?></a> <?php echo ($pun_config['p_message_img_tag'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+							<li><span><a href="help.php#smilies" onclick="window.open(this.href); return false;"><?php echo $lang_common['Smilies'] ?></a> <?php echo ($pun_config['o_smilies'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+						</ul>
+					</div>
+				</fieldset>
+<?php
+
+$checkboxes = array();
+if ($pun_config['o_smilies'] == '1')
+{
+	if (isset($_POST['hide_smilies']) || $cur_post['hide_smilies'] == '1')
+		$checkboxes[] = '<label><input type="checkbox" name="hide_smilies" value="1" checked="checked" tabindex="'.($cur_index++).'" />'.$lang_post['Hide smilies'].'<br /></label>';
+	else
+		$checkboxes[] = '<label><input type="checkbox" name="hide_smilies" value="1" tabindex="'.($cur_index++).'" />'.$lang_post['Hide smilies'].'<br /></label>';
+}
+
+if ($is_admmod)
+{
+	if ((isset($_POST['form_sent']) && isset($_POST['silent'])) || !isset($_POST['form_sent']))
+		$checkboxes[] = '<label><input type="checkbox" name="silent" value="1" tabindex="'.($cur_index++).'" checked="checked" />'.$lang_post['Silent edit'].'<br /></label>';
+	else
+		$checkboxes[] = '<label><input type="checkbox" name="silent" value="1" tabindex="'.($cur_index++).'" />'.$lang_post['Silent edit'].'<br /></label>';
+}
+
+if (!empty($checkboxes))
+{
+
+?>
+			</div>
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_common['Options'] ?></legend>
+					<div class="infldset">
+						<div class="rbox">
+							<?php echo implode("\n\t\t\t\t\t\t\t", $checkboxes)."\n" ?>
+						</div>
+					</div>
+				</fieldset>
+<?php
+
+	}
+
+?>
+			</div>
+			<p class="buttons"><input type="submit" name="submit" value="<?php echo $lang_common['Submit'] ?>" tabindex="<?php echo $cur_index++ ?>" accesskey="s" /> <input type="submit" name="preview" value="<?php echo $lang_post['Preview'] ?>" tabindex="<?php echo $cur_index++ ?>" accesskey="p" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+		</form>
+	</div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/extern.php b/extern.php
new file mode 100644
index 0000000..e99f995
--- /dev/null
+++ b/extern.php
@@ -0,0 +1,503 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+/*-----------------------------------------------------------------------------
+
+  INSTRUCTIONS
+
+  This script is used to include information about your board from
+  pages outside the forums and to syndicate news about recent
+  discussions via RSS/Atom/XML. The script can display a list of
+  recent discussions, a list of active users or a collection of
+  general board statistics. The script can be called directly via
+  an URL, from a PHP include command or through the use of Server
+  Side Includes (SSI).
+
+  The scripts behaviour is controlled via variables supplied in the
+  URL to the script. The different variables are: action (what to
+  do), show (how many items to display), fid (the ID or IDs of
+  the forum(s) to poll for topics), nfid (the ID or IDs of forums
+  that should be excluded), tid (the ID of the topic from which to
+  display posts) and type (output as HTML or RSS). The only
+  mandatory variable is action. Possible/default values are:
+
+    action: feed - show most recent topics/posts (HTML or RSS)
+            online - show users online (HTML)
+            online_full - as above, but includes a full list (HTML)
+            stats - show board statistics (HTML)
+
+    type:   rss - output as RSS 2.0
+            atom - output as Atom 1.0
+            xml - output as XML
+            html - output as HTML (<li>'s)
+
+    fid:    One or more forum IDs (comma-separated). If ignored,
+            topics from all readable forums will be pulled.
+
+    nfid:   One or more forum IDs (comma-separated) that are to be
+            excluded. E.g. the ID of a a test forum.
+
+    tid:    A topic ID from which to show posts. If a tid is supplied,
+            fid and nfid are ignored.
+
+    show:   Any integer value between 1 and 50. The default is 15.
+
+    order:  last_post - show topics ordered by when they were last
+                        posted in, giving information about the reply.
+            posted - show topics ordered by when they were first
+                     posted, giving information about the original post.
+
+-----------------------------------------------------------------------------*/
+
+define('PUN_QUIET_VISIT', 1);
+
+if (!defined('PUN_ROOT'))
+	define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+
+// The length at which topic subjects will be truncated (for HTML output)
+if (!defined('FORUM_EXTERN_MAX_SUBJECT_LENGTH'))
+	define('FORUM_EXTERN_MAX_SUBJECT_LENGTH', 30);
+
+// If we're a guest and we've sent a username/pass, we can try to authenticate using those details
+if ($pun_user['is_guest'] && isset($_SERVER['PHP_AUTH_USER']))
+	authenticate_user($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
+
+if ($pun_user['g_read_board'] == '0')
+{
+	http_authenticate_user();
+	exit($lang_common['No view']);
+}
+
+$action = isset($_GET['action']) ? strtolower($_GET['action']) : 'feed';
+
+// Handle a couple old formats, from FluxBB 1.2
+switch ($action)
+{
+	case 'active':
+		$action = 'feed';
+		$_GET['order'] = 'last_post';
+		break;
+
+	case 'new':
+		$action = 'feed';
+		$_GET['order'] = 'posted';
+		break;
+}
+
+//
+// Sends the proper headers for Basic HTTP Authentication
+//
+function http_authenticate_user()
+{
+	global $pun_config, $pun_user;
+
+	if (!$pun_user['is_guest'])
+		return;
+
+	header('WWW-Authenticate: Basic realm="'.$pun_config['o_board_title'].' External Syndication"');
+	header('HTTP/1.0 401 Unauthorized');
+}
+
+
+//
+// Output $feed as RSS 2.0
+//
+function output_rss($feed)
+{
+	global $lang_common, $pun_config;
+
+	// Send XML/no cache headers
+	header('Content-Type: application/xml; charset=utf-8');
+	header('Expires: '.gmdate('D, d M Y H:i:s').' GMT');
+	header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+	header('Pragma: public');
+
+	echo '<?xml version="1.0" encoding="utf-8"?>'."\n";
+	echo '<rss version="2.0">'."\n";
+	echo "\t".'<channel>'."\n";
+	echo "\t\t".'<title><![CDATA['.escape_cdata($feed['title']).']]></title>'."\n";
+	echo "\t\t".'<link>'.$feed['link'].'</link>'."\n";
+	echo "\t\t".'<description><![CDATA['.escape_cdata($feed['description']).']]></description>'."\n";
+	echo "\t\t".'<lastBuildDate>'.gmdate('r', count($feed['items']) ? $feed['items'][0]['pubdate'] : time()).'</lastBuildDate>'."\n";
+
+	if ($pun_config['o_show_version'] == '1')
+		echo "\t\t".'<generator>FluxBB '.$pun_config['o_cur_version'].'</generator>'."\n";
+	else
+		echo "\t\t".'<generator>FluxBB</generator>'."\n";
+
+	foreach ($feed['items'] as $item)
+	{
+		echo "\t\t".'<item>'."\n";
+		echo "\t\t\t".'<title><![CDATA['.escape_cdata($item['title']).']]></title>'."\n";
+		echo "\t\t\t".'<link>'.$item['link'].'</link>'."\n";
+		echo "\t\t\t".'<description><![CDATA['.escape_cdata($item['description']).']]></description>'."\n";
+		echo "\t\t\t".'<author><![CDATA['.(isset($item['author']['email']) ? escape_cdata($item['author']['email']) : 'dummy at example.com').' ('.escape_cdata($item['author']['name']).')]]></author>'."\n";
+		echo "\t\t\t".'<pubDate>'.gmdate('r', $item['pubdate']).'</pubDate>'."\n";
+		echo "\t\t\t".'<guid>'.$item['link'].'</guid>'."\n";
+
+		echo "\t\t".'</item>'."\n";
+	}
+
+	echo "\t".'</channel>'."\n";
+	echo '</rss>'."\n";
+}
+
+
+//
+// Output $feed as Atom 1.0
+//
+function output_atom($feed)
+{
+	global $lang_common, $pun_config;
+
+	// Send XML/no cache headers
+	header('Content-Type: application/atom+xml; charset=utf-8');
+	header('Expires: '.gmdate('D, d M Y H:i:s').' GMT');
+	header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+	header('Pragma: public');
+
+	echo '<?xml version="1.0" encoding="utf-8"?>'."\n";
+	echo '<feed xmlns="http://www.w3.org/2005/Atom">'."\n";
+
+	echo "\t".'<title type="html"><![CDATA['.escape_cdata($feed['title']).']]></title>'."\n";
+	echo "\t".'<link rel="self" href="'.pun_htmlspecialchars(get_current_url()).'"/>'."\n";
+	echo "\t".'<link href="'.$feed['link'].'"/>'."\n";
+	echo "\t".'<updated>'.gmdate('Y-m-d\TH:i:s\Z', count($feed['items']) ? $feed['items'][0]['pubdate'] : time()).'</updated>'."\n";
+
+	if ($pun_config['o_show_version'] == '1')
+		echo "\t".'<generator version="'.$pun_config['o_cur_version'].'">FluxBB</generator>'."\n";
+	else
+		echo "\t".'<generator>FluxBB</generator>'."\n";
+
+	echo "\t".'<id>'.$feed['link'].'</id>'."\n";
+
+	$content_tag = ($feed['type'] == 'posts') ? 'content' : 'summary';
+
+	foreach ($feed['items'] as $item)
+	{
+		echo "\t".'<entry>'."\n";
+		echo "\t\t".'<title type="html"><![CDATA['.escape_cdata($item['title']).']]></title>'."\n";
+		echo "\t\t".'<link rel="alternate" href="'.$item['link'].'"/>'."\n";
+		echo "\t\t".'<'.$content_tag.' type="html"><![CDATA['.escape_cdata($item['description']).']]></'.$content_tag.'>'."\n";
+		echo "\t\t".'<author>'."\n";
+		echo "\t\t\t".'<name><![CDATA['.escape_cdata($item['author']['name']).']]></name>'."\n";
+
+		if (isset($item['author']['email']))
+			echo "\t\t\t".'<email><![CDATA['.escape_cdata($item['author']['email']).']]></email>'."\n";
+
+		if (isset($item['author']['uri']))
+			echo "\t\t\t".'<uri>'.$item['author']['uri'].'</uri>'."\n";
+
+		echo "\t\t".'</author>'."\n";
+		echo "\t\t".'<updated>'.gmdate('Y-m-d\TH:i:s\Z', $item['pubdate']).'</updated>'."\n";
+
+		echo "\t\t".'<id>'.$item['link'].'</id>'."\n";
+		echo "\t".'</entry>'."\n";
+	}
+
+	echo '</feed>'."\n";
+}
+
+
+//
+// Output $feed as XML
+//
+function output_xml($feed)
+{
+	global $lang_common, $pun_config;
+
+	// Send XML/no cache headers
+	header('Content-Type: application/xml; charset=utf-8');
+	header('Expires: '.gmdate('D, d M Y H:i:s').' GMT');
+	header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+	header('Pragma: public');
+
+	echo '<?xml version="1.0" encoding="utf-8"?>'."\n";
+	echo '<source>'."\n";
+	echo "\t".'<url>'.$feed['link'].'</url>'."\n";
+
+	$forum_tag = ($feed['type'] == 'posts') ? 'post' : 'topic';
+
+	foreach ($feed['items'] as $item)
+	{
+		echo "\t".'<'.$forum_tag.' id="'.$item['id'].'">'."\n";
+
+		echo "\t\t".'<title><![CDATA['.escape_cdata($item['title']).']]></title>'."\n";
+		echo "\t\t".'<link>'.$item['link'].'</link>'."\n";
+		echo "\t\t".'<content><![CDATA['.escape_cdata($item['description']).']]></content>'."\n";
+		echo "\t\t".'<author>'."\n";
+		echo "\t\t\t".'<name><![CDATA['.escape_cdata($item['author']['name']).']]></name>'."\n";
+
+		if (isset($item['author']['email']))
+			echo "\t\t\t".'<email><![CDATA['.escape_cdata($item['author']['email']).']]></email>'."\n";
+
+		if (isset($item['author']['uri']))
+			echo "\t\t\t".'<uri>'.$item['author']['uri'].'</uri>'."\n";
+
+		echo "\t\t".'</author>'."\n";
+		echo "\t\t".'<posted>'.gmdate('r', $item['pubdate']).'</posted>'."\n";
+
+		echo "\t".'</'.$forum_tag.'>'."\n";
+	}
+
+	echo '</source>'."\n";
+}
+
+
+//
+// Output $feed as HTML (using <li> tags)
+//
+function output_html($feed)
+{
+
+	// Send the Content-type header in case the web server is setup to send something else
+	header('Content-type: text/html; charset=utf-8');
+	header('Expires: '.gmdate('D, d M Y H:i:s').' GMT');
+	header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+	header('Pragma: public');
+
+	foreach ($feed['items'] as $item)
+	{
+		if (utf8_strlen($item['title']) > FORUM_EXTERN_MAX_SUBJECT_LENGTH)
+			$subject_truncated = pun_htmlspecialchars(pun_trim(utf8_substr($item['title'], 0, (FORUM_EXTERN_MAX_SUBJECT_LENGTH - 5)))).' …';
+		else
+			$subject_truncated = pun_htmlspecialchars($item['title']);
+
+		echo '<li><a href="'.$item['link'].'" title="'.pun_htmlspecialchars($item['title']).'">'.$subject_truncated.'</a></li>'."\n";
+	}
+}
+
+// Show recent discussions
+if ($action == 'feed')
+{
+	require PUN_ROOT.'include/parser.php';
+
+	// Determine what type of feed to output
+	$type = isset($_GET['type']) ? strtolower($_GET['type']) : 'html';
+	if (!in_array($type, array('html', 'rss', 'atom', 'xml')))
+		$type = 'html';
+
+	$show = isset($_GET['show']) ? intval($_GET['show']) : 15;
+	if ($show < 1 || $show > 50)
+		$show = 15;
+
+	// Was a topic ID supplied?
+	if (isset($_GET['tid']))
+	{
+		$tid = intval($_GET['tid']);
+
+		// Fetch topic subject
+		$result = $db->query('SELECT t.subject, t.first_post_id FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.moved_to IS NULL AND t.id='.$tid) or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
+		if (!$db->num_rows($result))
+		{
+			http_authenticate_user();
+			exit($lang_common['Bad request']);
+		}
+
+		$cur_topic = $db->fetch_assoc($result);
+
+		if ($pun_config['o_censoring'] == '1')
+			$cur_topic['subject'] = censor_words($cur_topic['subject']);
+
+		// Setup the feed
+		$feed = array(
+			'title' 		=>	$pun_config['o_board_title'].$lang_common['Title separator'].$cur_topic['subject'],
+			'link'			=>	$pun_config['o_base_url'].'/viewtopic.php?id='.$tid,
+			'description'		=>	sprintf($lang_common['RSS description topic'], $cur_topic['subject']),
+			'items'			=>	array(),
+			'type'			=>	'posts'
+		);
+
+		// Fetch $show posts
+		$result = $db->query('SELECT p.id, p.poster, p.message, p.hide_smilies, p.posted, p.poster_id, u.email_setting, u.email, p.poster_email FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'users AS u ON u.id=p.poster_id WHERE p.topic_id='.$tid.' ORDER BY p.posted DESC LIMIT '.$show) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+		while ($cur_post = $db->fetch_assoc($result))
+		{
+			$cur_post['message'] = parse_message($cur_post['message'], $cur_post['hide_smilies']);
+
+			$item = array(
+				'id'			=>	$cur_post['id'],
+				'title'			=>	$cur_topic['first_post_id'] == $cur_post['id'] ? $cur_topic['subject'] : $lang_common['RSS reply'].$cur_topic['subject'],
+				'link'			=>	$pun_config['o_base_url'].'/viewtopic.php?pid='.$cur_post['id'].'#p'.$cur_post['id'],
+				'description'		=>	$cur_post['message'],
+				'author'		=>	array(
+					'name'	=> $cur_post['poster'],
+				),
+				'pubdate'		=>	$cur_post['posted']
+			);
+
+			if ($cur_post['poster_id'] > 1)
+			{
+				if ($cur_post['email_setting'] == '0' && !$pun_user['is_guest'])
+					$item['author']['email'] = $cur_post['email'];
+
+				$item['author']['uri'] = $pun_config['o_base_url'].'/profile.php?id='.$cur_post['poster_id'];
+			}
+			else if ($cur_post['poster_email'] != '' && !$pun_user['is_guest'])
+				$item['author']['email'] = $cur_post['poster_email'];
+
+			$feed['items'][] = $item;
+		}
+
+		$output_func = 'output_'.$type;
+		$output_func($feed);
+	}
+	else
+	{
+		$order_posted = isset($_GET['order']) && strtolower($_GET['order']) == 'posted';
+		$forum_name = '';
+		$forum_sql = '';
+
+		// Were any forum IDs supplied?
+		if (isset($_GET['fid']) && is_scalar($_GET['fid']) && $_GET['fid'] != '')
+		{
+			$fids = explode(',', pun_trim($_GET['fid']));
+			$fids = array_map('intval', $fids);
+
+			if (!empty($fids))
+				$forum_sql .= ' AND t.forum_id IN('.implode(',', $fids).')';
+
+			if (count($fids) == 1)
+			{
+				// Fetch forum name
+				$result = $db->query('SELECT f.forum_name FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.id='.$fids[0]) or error('Unable to fetch forum name', __FILE__, __LINE__, $db->error());
+				if ($db->num_rows($result))
+					$forum_name = $lang_common['Title separator'].$db->result($result);
+			}
+		}
+
+		// Any forum IDs to exclude?
+		if (isset($_GET['nfid']) && is_scalar($_GET['nfid']) && $_GET['nfid'] != '')
+		{
+			$nfids = explode(',', pun_trim($_GET['nfid']));
+			$nfids = array_map('intval', $nfids);
+
+			if (!empty($nfids))
+				$forum_sql .= ' AND t.forum_id NOT IN('.implode(',', $nfids).')';
+		}
+
+		// Setup the feed
+		$feed = array(
+			'title' 		=>	$pun_config['o_board_title'].$forum_name,
+			'link'			=>	$pun_config['o_base_url'].'/index.php',
+			'description'	=>	sprintf($lang_common['RSS description'], $pun_config['o_board_title']),
+			'items'			=>	array(),
+			'type'			=>	'topics'
+		);
+
+		// Fetch $show topics
+		$result = $db->query('SELECT t.id, t.poster, t.subject, t.posted, t.last_post, t.last_poster, p.message, p.hide_smilies, u.email_setting, u.email, p.poster_id, p.poster_email FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'posts AS p ON p.id='.($order_posted ? 't.first_post_id' : 't.last_post_id').' INNER JOIN '.$db->prefix.'users AS u ON u.id=p.poster_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.rea [...]
+		while ($cur_topic = $db->fetch_assoc($result))
+		{
+			if ($pun_config['o_censoring'] == '1')
+				$cur_topic['subject'] = censor_words($cur_topic['subject']);
+
+			$cur_topic['message'] = parse_message($cur_topic['message'], $cur_topic['hide_smilies']);
+
+			$item = array(
+				'id'			=>	$cur_topic['id'],
+				'title'			=>	$cur_topic['subject'],
+				'link'			=>	$pun_config['o_base_url'].($order_posted ? '/viewtopic.php?id='.$cur_topic['id'] : '/viewtopic.php?id='.$cur_topic['id'].'&action=new'),
+				'description'	=>	$cur_topic['message'],
+				'author'		=>	array(
+					'name'	=> $order_posted ? $cur_topic['poster'] : $cur_topic['last_poster']
+				),
+				'pubdate'		=>	$order_posted ? $cur_topic['posted'] : $cur_topic['last_post']
+			);
+
+			if ($cur_topic['poster_id'] > 1)
+			{
+				if ($cur_topic['email_setting'] == '0' && !$pun_user['is_guest'])
+					$item['author']['email'] = $cur_topic['email'];
+
+				$item['author']['uri'] = $pun_config['o_base_url'].'/profile.php?id='.$cur_topic['poster_id'];
+			}
+			else if ($cur_topic['poster_email'] != '' && !$pun_user['is_guest'])
+				$item['author']['email'] = $cur_topic['poster_email'];
+
+			$feed['items'][] = $item;
+		}
+
+		$output_func = 'output_'.$type;
+		$output_func($feed);
+	}
+
+	exit;
+}
+
+// Show users online
+else if ($action == 'online' || $action == 'online_full')
+{
+	// Load the index.php language file
+	require PUN_ROOT.'lang/'.$pun_config['o_default_lang'].'/index.php';
+
+	// Fetch users online info and generate strings for output
+	$num_guests = $num_users = 0;
+	$users = array();
+
+	$result = $db->query('SELECT user_id, ident FROM '.$db->prefix.'online WHERE idle=0 ORDER BY ident', true) or error('Unable to fetch online list', __FILE__, __LINE__, $db->error());
+
+	while ($pun_user_online = $db->fetch_assoc($result))
+	{
+		if ($pun_user_online['user_id'] > 1)
+		{
+			$users[] = ($pun_user['g_view_users'] == '1') ? '<a href="'.$pun_config['o_base_url'].'/profile.php?id='.$pun_user_online['user_id'].'">'.pun_htmlspecialchars($pun_user_online['ident']).'</a>' : pun_htmlspecialchars($pun_user_online['ident']);
+			++$num_users;
+		}
+		else
+			++$num_guests;
+	}
+
+	// Send the Content-type header in case the web server is setup to send something else
+	header('Content-type: text/html; charset=utf-8');
+	header('Expires: '.gmdate('D, d M Y H:i:s').' GMT');
+	header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+	header('Pragma: public');
+
+	echo sprintf($lang_index['Guests online'], forum_number_format($num_guests)).'<br />'."\n";
+
+	if ($action == 'online_full' && !empty($users))
+		echo sprintf($lang_index['Users online'], implode(', ', $users)).'<br />'."\n";
+	else
+		echo sprintf($lang_index['Users online'], forum_number_format($num_users)).'<br />'."\n";
+
+	exit;
+}
+
+// Show board statistics
+else if ($action == 'stats')
+{
+	// Load the index.php language file
+	require PUN_ROOT.'lang/'.$pun_config['o_default_lang'].'/index.php';
+
+	// Collect some statistics from the database
+	$result = $db->query('SELECT COUNT(id)-1 FROM '.$db->prefix.'users WHERE group_id!='.PUN_UNVERIFIED) or error('Unable to fetch total user count', __FILE__, __LINE__, $db->error());
+	$stats['total_users'] = $db->result($result);
+
+	$result = $db->query('SELECT id, username FROM '.$db->prefix.'users WHERE group_id!='.PUN_UNVERIFIED.' ORDER BY registered DESC LIMIT 1') or error('Unable to fetch newest registered user', __FILE__, __LINE__, $db->error());
+	$stats['last_user'] = $db->fetch_assoc($result);
+
+	$result = $db->query('SELECT SUM(num_topics), SUM(num_posts) FROM '.$db->prefix.'forums') or error('Unable to fetch topic/post count', __FILE__, __LINE__, $db->error());
+	list($stats['total_topics'], $stats['total_posts']) = $db->fetch_row($result);
+
+	// Send the Content-type header in case the web server is setup to send something else
+	header('Content-type: text/html; charset=utf-8');
+	header('Expires: '.gmdate('D, d M Y H:i:s').' GMT');
+	header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+	header('Pragma: public');
+
+	echo sprintf($lang_index['No of users'], forum_number_format($stats['total_users'])).'<br />'."\n";
+	echo sprintf($lang_index['Newest user'], (($pun_user['g_view_users'] == '1') ? '<a href="'.$pun_config['o_base_url'].'/profile.php?id='.$stats['last_user']['id'].'">'.pun_htmlspecialchars($stats['last_user']['username']).'</a>' : pun_htmlspecialchars($stats['last_user']['username']))).'<br />'."\n";
+	echo sprintf($lang_index['No of topics'], forum_number_format($stats['total_topics'])).'<br />'."\n";
+	echo sprintf($lang_index['No of posts'], forum_number_format($stats['total_posts'])).'<br />'."\n";
+
+	exit;
+}
+
+// If we end up here, the script was called with some wacky parameters
+exit($lang_common['Bad request']);
diff --git a/footer.php b/footer.php
new file mode 100644
index 0000000..c308c9e
--- /dev/null
+++ b/footer.php
@@ -0,0 +1,201 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure no one attempts to run this script "directly"
+if (!defined('PUN'))
+	exit;
+
+$tpl_temp = trim(ob_get_contents());
+$tpl_main = str_replace('<pun_main>', $tpl_temp, $tpl_main);
+ob_end_clean();
+// END SUBST - <pun_main>
+
+
+// START SUBST - <pun_footer>
+ob_start();
+
+?>
+<div id="brdfooter" class="block">
+	<h2><span><?php echo $lang_common['Board footer'] ?></span></h2>
+	<div class="box">
+<?php
+
+if (isset($footer_style) && ($footer_style == 'viewforum' || $footer_style == 'viewtopic') && $is_admmod)
+{
+	echo "\t\t".'<div id="modcontrols" class="inbox">'."\n";
+
+	if ($footer_style == 'viewforum')
+	{
+		echo "\t\t\t".'<dl>'."\n";
+		echo "\t\t\t\t".'<dt><strong>'.$lang_forum['Mod controls'].'</strong></dt>'."\n";
+		echo "\t\t\t\t".'<dd><span><a href="moderate.php?fid='.$forum_id.'&p='.$p.'">'.$lang_common['Moderate forum'].'</a></span></dd>'."\n";
+		echo "\t\t\t".'</dl>'."\n";
+	}
+	else if ($footer_style == 'viewtopic')
+	{
+		echo "\t\t\t".'<dl>'."\n";
+		echo "\t\t\t\t".'<dt><strong>'.$lang_topic['Mod controls'].'</strong></dt>'."\n";
+		echo "\t\t\t\t".'<dd><span><a href="moderate.php?fid='.$forum_id.'&tid='.$id.'&p='.$p.'">'.$lang_common['Moderate topic'].'</a></span></dd>'."\n";
+		echo "\t\t\t\t".'<dd><span><a href="moderate.php?fid='.$forum_id.'&move_topics='.$id.'">'.$lang_common['Move topic'].'</a></span></dd>'."\n";
+
+		if ($cur_topic['closed'] == '1')
+			echo "\t\t\t\t".'<dd><span><a href="moderate.php?fid='.$forum_id.'&open='.$id.'">'.$lang_common['Open topic'].'</a></span></dd>'."\n";
+		else
+			echo "\t\t\t\t".'<dd><span><a href="moderate.php?fid='.$forum_id.'&close='.$id.'">'.$lang_common['Close topic'].'</a></span></dd>'."\n";
+
+		if ($cur_topic['sticky'] == '1')
+			echo "\t\t\t\t".'<dd><span><a href="moderate.php?fid='.$forum_id.'&unstick='.$id.'">'.$lang_common['Unstick topic'].'</a></span></dd>'."\n";
+		else
+			echo "\t\t\t\t".'<dd><span><a href="moderate.php?fid='.$forum_id.'&stick='.$id.'">'.$lang_common['Stick topic'].'</a></span></dd>'."\n";
+
+		echo "\t\t\t".'</dl>'."\n";
+	}
+
+	echo "\t\t\t".'<div class="clearer"></div>'."\n\t\t".'</div>'."\n";
+}
+
+?>
+		<div id="brdfooternav" class="inbox">
+<?php
+
+// If no footer style has been specified, we use the default (only copyright/debug info)
+$footer_style = isset($footer_style) ? $footer_style : NULL;
+
+if ($footer_style == 'index' || $footer_style == 'search')
+{
+	echo "\t\t\t".'<div class="conl">'."\n";
+
+	if (!$pun_user['is_guest'] && $pun_user['g_search'] == '1')
+	{
+		echo "\t\t\t\t".'<dl id="searchlinks">'."\n";
+		echo "\t\t\t\t\t".'<dt><strong>'.$lang_common['Search links'].'</strong></dt>'."\n";
+
+		echo "\t\t\t\t\t".'<dd><span><a href="search.php?action=show_24h">'.$lang_common['Show recent posts'].'</a></span></dd>'."\n";
+		echo "\t\t\t\t\t".'<dd><span><a href="search.php?action=show_unanswered">'.$lang_common['Show unanswered posts'].'</a></span></dd>'."\n";
+
+		if ($pun_config['o_subscriptions'] == '1')
+			echo "\t\t\t\t\t".'<dd><span><a href="search.php?action=show_subscriptions">'.$lang_common['Show subscriptions'].'</a></span></dd>'."\n";
+
+		echo "\t\t\t\t\t".'<dd><span><a href="search.php?action=show_user&user_id='.$pun_user['id'].'">'.$lang_common['Show your posts'].'</a></span></dd>'."\n";
+
+		echo "\t\t\t\t".'</dl>'."\n";
+	}
+	else
+	{
+		if ($pun_user['g_search'] == '1')
+		{
+			echo "\t\t\t\t".'<dl id="searchlinks">'."\n";
+			echo "\t\t\t\t\t".'<dt><strong>'.$lang_common['Search links'].'</strong></dt>'."\n";
+
+			echo "\t\t\t\t\t".'<dd><span><a href="search.php?action=show_24h">'.$lang_common['Show recent posts'].'</a></span></dd>'."\n";
+			echo "\t\t\t\t\t".'<dd><span><a href="search.php?action=show_unanswered">'.$lang_common['Show unanswered posts'].'</a></span></dd>'."\n";
+
+			echo "\t\t\t\t".'</dl>'."\n";
+		}
+	}
+
+	echo "\t\t\t".'</div>'."\n";
+}
+else if ($footer_style == 'viewforum' || $footer_style == 'viewtopic')
+{
+	echo "\t\t\t".'<div class="conl">'."\n";
+
+	// Display the "Jump to" drop list
+	if ($pun_config['o_quickjump'] == '1')
+	{
+		// Load cached quick jump
+		if (file_exists(FORUM_CACHE_DIR.'cache_quickjump_'.$pun_user['g_id'].'.php'))
+			include FORUM_CACHE_DIR.'cache_quickjump_'.$pun_user['g_id'].'.php';
+
+		if (!defined('PUN_QJ_LOADED'))
+		{
+			if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+				require PUN_ROOT.'include/cache.php';
+
+			generate_quickjump_cache($pun_user['g_id']);
+			require FORUM_CACHE_DIR.'cache_quickjump_'.$pun_user['g_id'].'.php';
+		}
+	}
+
+	echo "\t\t\t".'</div>'."\n";
+}
+
+?>
+			<div class="conr">
+<?php
+
+if ($footer_style == 'index')
+{
+	if ($pun_config['o_feed_type'] == '1')
+		echo "\t\t\t\t".'<p id="feedlinks"><span class="rss"><a href="extern.php?action=feed&type=rss">'.$lang_common['RSS active topics feed'].'</a></span></p>'."\n";
+	else if ($pun_config['o_feed_type'] == '2')
+		echo "\t\t\t\t".'<p id="feedlinks"><span class="atom"><a href="extern.php?action=feed&type=atom">'.$lang_common['Atom active topics feed'].'</a></span></p>'."\n";
+}
+else if ($footer_style == 'viewforum')
+{
+	if ($pun_config['o_feed_type'] == '1')
+		echo "\t\t\t\t".'<p id="feedlinks"><span class="rss"><a href="extern.php?action=feed&fid='.$forum_id.'&type=rss">'.$lang_common['RSS forum feed'].'</a></span></p>'."\n";
+	else if ($pun_config['o_feed_type'] == '2')
+		echo "\t\t\t\t".'<p id="feedlinks" class="actions"><span class="atom"><a href="extern.php?action=feed&fid='.$forum_id.'&type=atom">'.$lang_common['Atom forum feed'].'</a></span></p>'."\n";
+}
+else if ($footer_style == 'viewtopic')
+{
+	if ($pun_config['o_feed_type'] == '1')
+		echo "\t\t\t\t".'<p id="feedlinks"><span class="rss"><a href="extern.php?action=feed&tid='.$id.'&type=rss">'.$lang_common['RSS topic feed'].'</a></span></p>'."\n";
+	else if ($pun_config['o_feed_type'] == '2')
+		echo "\t\t\t\t".'<p id="feedlinks"><span class="atom"><a href="extern.php?action=feed&tid='.$id.'&type=atom">'.$lang_common['Atom topic feed'].'</a></span></p>'."\n";
+}
+
+?>
+				<p id="poweredby"><?php printf($lang_common['Powered by'], '<a href="http://fluxbb.org/">FluxBB</a>'.(($pun_config['o_show_version'] == '1') ? ' '.$pun_config['o_cur_version'] : '')) ?></p>
+			</div>
+			<div class="clearer"></div>
+		</div>
+	</div>
+</div>
+<?php
+
+// Display debug info (if enabled/defined)
+if (defined('PUN_DEBUG'))
+{
+	echo '<p id="debugtime">[ ';
+
+	// Calculate script generation time
+	$time_diff = sprintf('%.3f', get_microtime() - $pun_start);
+	echo sprintf($lang_common['Querytime'], $time_diff, $db->get_num_queries());
+
+	if (function_exists('memory_get_usage'))
+	{
+		echo ' - '.sprintf($lang_common['Memory usage'], file_size(memory_get_usage()));
+
+		if (function_exists('memory_get_peak_usage'))
+			echo ' '.sprintf($lang_common['Peak usage'], file_size(memory_get_peak_usage()));
+	}
+
+	echo ' ]</p>'."\n";
+}
+
+
+// End the transaction
+$db->end_transaction();
+
+// Display executed queries (if enabled)
+if (defined('PUN_SHOW_QUERIES'))
+	display_saved_queries();
+
+$tpl_temp = trim(ob_get_contents());
+$tpl_main = str_replace('<pun_footer>', $tpl_temp, $tpl_main);
+ob_end_clean();
+// END SUBST - <pun_footer>
+
+
+// Close the db connection (and free up any result data)
+$db->close();
+
+// Spit out the page
+exit($tpl_main);
diff --git a/header.php b/header.php
new file mode 100644
index 0000000..78335e1
--- /dev/null
+++ b/header.php
@@ -0,0 +1,244 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure no one attempts to run this script "directly"
+if (!defined('PUN'))
+	exit;
+
+// Send no-cache headers
+header('Expires: Thu, 21 Jul 1977 07:30:00 GMT'); // When yours truly first set eyes on this world! :)
+header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
+header('Cache-Control: post-check=0, pre-check=0', false);
+header('Pragma: no-cache'); // For HTTP/1.0 compatibility
+
+// Send the Content-type header in case the web server is setup to send something else
+header('Content-type: text/html; charset=utf-8');
+
+// Load the template
+if (defined('PUN_ADMIN_CONSOLE'))
+	$tpl_file = 'admin.tpl';
+else if (defined('PUN_HELP'))
+	$tpl_file = 'help.tpl';
+else
+	$tpl_file = 'main.tpl';
+
+if (file_exists(PUN_ROOT.'style/'.$pun_user['style'].'/'.$tpl_file))
+{
+	$tpl_file = PUN_ROOT.'style/'.$pun_user['style'].'/'.$tpl_file;
+	$tpl_inc_dir = PUN_ROOT.'style/'.$pun_user['style'].'/';
+}
+else
+{
+	$tpl_file = PUN_ROOT.'include/template/'.$tpl_file;
+	$tpl_inc_dir = PUN_ROOT.'include/user/';
+}
+
+$tpl_main = file_get_contents($tpl_file);
+
+// START SUBST - <pun_include "*">
+preg_match_all('#<pun_include "([^/\\\\]*?)\.(php[45]?|inc|html?|txt)">#', $tpl_main, $pun_includes, PREG_SET_ORDER);
+
+foreach ($pun_includes as $cur_include)
+{
+	ob_start();
+
+	// Allow for overriding user includes, too.
+	if (file_exists($tpl_inc_dir.$cur_include[1].'.'.$cur_include[2]))
+		require $tpl_inc_dir.$cur_include[1].'.'.$cur_include[2];
+	else if (file_exists(PUN_ROOT.'include/user/'.$cur_include[1].'.'.$cur_include[2]))
+		require PUN_ROOT.'include/user/'.$cur_include[1].'.'.$cur_include[2];
+	else
+		error(sprintf($lang_common['Pun include error'], htmlspecialchars($cur_include[0]), basename($tpl_file)));
+
+	$tpl_temp = ob_get_contents();
+	$tpl_main = str_replace($cur_include[0], $tpl_temp, $tpl_main);
+	ob_end_clean();
+}
+// END SUBST - <pun_include "*">
+
+
+// START SUBST - <pun_language>
+$tpl_main = str_replace('<pun_language>', $lang_common['lang_identifier'], $tpl_main);
+// END SUBST - <pun_language>
+
+
+// START SUBST - <pun_content_direction>
+$tpl_main = str_replace('<pun_content_direction>', $lang_common['lang_direction'], $tpl_main);
+// END SUBST - <pun_content_direction>
+
+
+// START SUBST - <pun_head>
+ob_start();
+
+// Define $p if its not set to avoid a PHP notice
+$p = isset($p) ? $p : null;
+
+// Is this a page that we want search index spiders to index?
+if (!defined('PUN_ALLOW_INDEX'))
+	echo '<meta name="ROBOTS" content="NOINDEX, FOLLOW" />'."\n";
+
+?>
+<title><?php echo generate_page_title($page_title, $p) ?></title>
+<link rel="stylesheet" type="text/css" href="style/<?php echo $pun_user['style'].'.css' ?>" />
+<?php
+
+if (defined('PUN_ADMIN_CONSOLE'))
+{
+	if (file_exists(PUN_ROOT.'style/'.$pun_user['style'].'/base_admin.css'))
+		echo '<link rel="stylesheet" type="text/css" href="style/'.$pun_user['style'].'/base_admin.css" />'."\n";
+	else
+		echo '<link rel="stylesheet" type="text/css" href="style/imports/base_admin.css" />'."\n";
+}
+
+if (isset($required_fields))
+{
+	// Output JavaScript to validate form (make sure required fields are filled out)
+
+?>
+<script type="text/javascript">
+/* <![CDATA[ */
+function process_form(the_form)
+{
+	var element_names = new Object()
+<?php
+
+	// Output a JavaScript array with localised field names
+	foreach ($required_fields as $elem_orig => $elem_trans)
+		echo "\t".'element_names["'.$elem_orig.'"] = "'.addslashes(str_replace(' ', ' ', $elem_trans)).'"'."\n";
+
+?>
+
+	if (document.all || document.getElementById)
+	{
+		for (var i = 0; i < the_form.length; ++i)
+		{
+			var elem = the_form.elements[i]
+			if (elem.name && elem.name.substring(0, 4) == "req_")
+			{
+				if (elem.type && (elem.type=="text" || elem.type=="textarea" || elem.type=="password" || elem.type=="file") && elem.value=='')
+				{
+					alert("\"" + element_names[elem.name] + "\" <?php echo $lang_common['required field'] ?>")
+					elem.focus()
+					return false
+				}
+			}
+		}
+	}
+
+	return true
+}
+/* ]]> */
+</script>
+<?php
+
+}
+
+// JavaScript tricks for IE6 and older
+echo '<!--[if lte IE 6]><script type="text/javascript" src="style/imports/minmax.js"></script><![endif]-->'."\n";
+
+if (isset($page_head))
+	echo implode("\n", $page_head)."\n";
+
+$tpl_temp = trim(ob_get_contents());
+$tpl_main = str_replace('<pun_head>', $tpl_temp, $tpl_main);
+ob_end_clean();
+// END SUBST - <pun_head>
+
+
+// START SUBST - <body>
+if (isset($focus_element))
+{
+	$tpl_main = str_replace('<body onload="', '<body onload="document.getElementById(\''.$focus_element[0].'\').'.$focus_element[1].'.focus();', $tpl_main);
+	$tpl_main = str_replace('<body>', '<body onload="document.getElementById(\''.$focus_element[0].'\').'.$focus_element[1].'.focus()">', $tpl_main);
+}
+// END SUBST - <body>
+
+
+// START SUBST - <pun_page>
+$tpl_main = str_replace('<pun_page>', htmlspecialchars(basename($_SERVER['PHP_SELF'], '.php')), $tpl_main);
+// END SUBST - <pun_page>
+
+
+// START SUBST - <pun_title>
+$tpl_main = str_replace('<pun_title>', '<h1><span>'.pun_htmlspecialchars($pun_config['o_board_title']).'</span></h1>', $tpl_main);
+// END SUBST - <pun_title>
+
+
+// START SUBST - <pun_desc>
+$tpl_main = str_replace('<pun_desc>', '<div id="brddesc">'.$pun_config['o_board_desc'].'</div>', $tpl_main);
+// END SUBST - <pun_desc>
+
+
+// START SUBST - <pun_navlinks>
+$tpl_main = str_replace('<pun_navlinks>','<div id="brdmenu" class="inbox">'."\n\t\t\t". generate_navlinks()."\n\t\t".'</div>', $tpl_main);
+// END SUBST - <pun_navlinks>
+
+
+// START SUBST - <pun_status>
+if ($pun_user['is_guest'])
+	$tpl_temp = '<div id="brdwelcome" class="inbox">'."\n\t\t\t".'<p>'.$lang_common['Not logged in'].'</p>'."\n\t\t".'</div>';
+else
+{
+	$tpl_temp = '<div id="brdwelcome" class="inbox">'."\n\t\t\t".'<ul class="conl">'."\n\t\t\t\t".'<li><span>'.$lang_common['Logged in as'].' <strong>'.pun_htmlspecialchars($pun_user['username']).'</strong></span></li>'."\n\t\t\t\t".'<li><span>'.sprintf($lang_common['Last visit'], format_time($pun_user['last_visit'])).'</span></li>';
+
+	if ($pun_user['is_admmod'])
+	{
+		if ($pun_config['o_report_method'] == '0' || $pun_config['o_report_method'] == '2')
+		{
+			$result_header = $db->query('SELECT 1 FROM '.$db->prefix.'reports WHERE zapped IS NULL') or error('Unable to fetch reports info', __FILE__, __LINE__, $db->error());
+
+			if ($db->result($result_header))
+				$tpl_temp .= "\n\t\t\t\t".'<li class="reportlink"><span><strong><a href="admin_reports.php">'.$lang_common['New reports'].'</a></strong></span></li>';
+		}
+
+		if ($pun_config['o_maintenance'] == '1')
+			$tpl_temp .= "\n\t\t\t\t".'<li class="maintenancelink"><span><strong><a href="admin_options.php#maintenance">'.$lang_common['Maintenance mode enabled'].'</a></strong></span></li>';
+	}
+
+	if (in_array(basename($_SERVER['PHP_SELF']), array('index.php', 'search.php')))
+		$tpl_temp .= "\n\t\t\t".'</ul>'."\n\t\t\t".'<ul class="conr">'.($pun_user['g_search'] == '1' ? "\n\t\t\t\t".'<li><span><a href="search.php?action=show_new">'.$lang_common['Show new posts'].'</a></span></li>' : '')."\n\t\t\t\t".'<li><span><a href="misc.php?action=markread">'.$lang_common['Mark all as read'].'</a></span></li>'."\n\t\t\t".'</ul>'."\n\t\t\t".'<div class="clearer"></div>'."\n\t\t".'</div>';
+	else if (basename($_SERVER['PHP_SELF']) == 'viewforum.php')
+		$tpl_temp .= "\n\t\t\t".'</ul>'."\n\t\t\t".'<ul class="conr">'."\n\t\t\t\t".'<li><span><a href="misc.php?action=markforumread&fid='.$id.'">'.$lang_common['Mark forum read'].'</a></span></li>'."\n\t\t\t".'</ul>'."\n\t\t\t".'<div class="clearer"></div>'."\n\t\t".'</div>';
+	else
+		$tpl_temp .= "\n\t\t\t".'</ul>'."\n\t\t\t".'<div class="clearer"></div>'."\n\t\t".'</div>';
+}
+
+$tpl_main = str_replace('<pun_status>', $tpl_temp, $tpl_main);
+// END SUBST - <pun_status>
+
+
+// START SUBST - <pun_announcement>
+if ($pun_config['o_announcement'] == '1')
+{
+	ob_start();
+
+?>
+<div id="announce" class="block">
+	<div class="hd"><h2><span><?php echo $lang_common['Announcement'] ?></span></h2></div>
+	<div class="box">
+		<div id="announce-block" class="inbox">
+			<div class="usercontent"><?php echo $pun_config['o_announcement_message'] ?></div>
+		</div>
+	</div>
+</div>
+<?php
+
+	$tpl_temp = trim(ob_get_contents());
+	$tpl_main = str_replace('<pun_announcement>', $tpl_temp, $tpl_main);
+	ob_end_clean();
+}
+else
+	$tpl_main = str_replace('<pun_announcement>', '', $tpl_main);
+// END SUBST - <pun_announcement>
+
+
+// START SUBST - <pun_main>
+ob_start();
+
+
+define('PUN_HEADER', 1);
diff --git a/help.php b/help.php
new file mode 100644
index 0000000..1d828fb
--- /dev/null
+++ b/help.php
@@ -0,0 +1,144 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Tell header.php to use the help template
+define('PUN_HELP', 1);
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+
+
+if ($pun_user['g_read_board'] == '0')
+	message($lang_common['No view']);
+
+
+// Load the help.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/help.php';
+
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_help['Help']);
+define('PUN_ACTIVE_PAGE', 'help');
+require PUN_ROOT.'header.php';
+
+?>
+<h2><span><?php echo $lang_help['BBCode'] ?></span></h2>
+<div class="box">
+	<div class="inbox">
+		<p><a name="bbcode"></a><?php echo $lang_help['BBCode info 1'] ?></p>
+		<p><?php echo $lang_help['BBCode info 2'] ?></p>
+	</div>
+</div>
+<h2><span><?php echo $lang_help['Text style'] ?></span></h2>
+<div class="box">
+	<div class="inbox">
+		<p><?php echo $lang_help['Text style info'] ?></p>
+		<p><code>[b]<?php echo $lang_help['Bold text'] ?>[/b]</code> <?php echo $lang_help['produces'] ?> <samp><strong><?php echo $lang_help['Bold text'] ?></strong></samp></p>
+		<p><code>[u]<?php echo $lang_help['Underlined text'] ?>[/u]</code> <?php echo $lang_help['produces'] ?> <samp><span class="bbu"><?php echo $lang_help['Underlined text'] ?></span></samp></p>
+		<p><code>[i]<?php echo $lang_help['Italic text'] ?>[/i]</code> <?php echo $lang_help['produces'] ?> <samp><i><?php echo $lang_help['Italic text'] ?></i></samp></p>
+		<p><code>[s]<?php echo $lang_help['Strike-through text'] ?>[/s]</code> <?php echo $lang_help['produces'] ?> <samp><span class="bbs"><?php echo $lang_help['Strike-through text'] ?></span></samp></p>
+		<p><code>[del]<?php echo $lang_help['Deleted text'] ?>[/del]</code> <?php echo $lang_help['produces'] ?> <samp><del><?php echo $lang_help['Deleted text'] ?></del></samp></p>
+		<p><code>[ins]<?php echo $lang_help['Inserted text'] ?>[/ins]</code> <?php echo $lang_help['produces'] ?> <samp><ins><?php echo $lang_help['Inserted text'] ?></ins></samp></p>
+		<p><code>[em]<?php echo $lang_help['Emphasised text'] ?>[/em]</code> <?php echo $lang_help['produces'] ?> <samp><em><?php echo $lang_help['Emphasised text'] ?></em></samp></p>
+		<p><code>[color=#FF0000]<?php echo $lang_help['Red text'] ?>[/color]</code> <?php echo $lang_help['produces'] ?> <samp><span style="color: #ff0000"><?php echo $lang_help['Red text'] ?></span></samp></p>
+		<p><code>[color=blue]<?php echo $lang_help['Blue text'] ?>[/color]</code> <?php echo $lang_help['produces'] ?> <samp><span style="color: blue"><?php echo $lang_help['Blue text'] ?></span></samp></p>
+		<p><code>[h]<?php echo $lang_help['Heading text'] ?>[/h]</code> <?php echo $lang_help['produces'] ?></p> <div class="postmsg"><h5><?php echo $lang_help['Heading text'] ?></h5></div>
+	</div>
+</div>
+<h2><span><?php echo $lang_help['Links and images'] ?></span></h2>
+<div class="box">
+	<div class="inbox">
+		<p><?php echo $lang_help['Links info'] ?></p>
+		<p><code>[url=<?php echo $pun_config['o_base_url'].'/' ?>]<?php echo pun_htmlspecialchars($pun_config['o_board_title']) ?>[/url]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo $pun_config['o_base_url'].'/' ?>"><?php echo pun_htmlspecialchars($pun_config['o_board_title']) ?></a></samp></p>
+		<p><code>[url]<?php echo $pun_config['o_base_url'].'/' ?>[/url]</code> <?php echo $lang_help['produces'] ?> <samp><a href="<?php echo $pun_config['o_base_url'] ?>"><?php echo $pun_config['o_base_url'].'/' ?></a></samp></p>
+		<p><code>[email]myname at mydomain.com[/email]</code> <?php echo $lang_help['produces'] ?> <samp><a href="mailto:myname at mydomain.com">myname at mydomain.com</a></samp></p>
+		<p><code>[email=myname at mydomain.com]<?php echo $lang_help['My email address'] ?>[/email]</code> <?php echo $lang_help['produces'] ?> <samp><a href="mailto:myname at mydomain.com"><?php echo $lang_help['My email address'] ?></a></samp></p>
+	</div>
+	<div class="inbox">
+		<p><a name="img"></a><?php echo $lang_help['Images info'] ?></p>
+		<p><code>[img=FluxBB bbcode test]<?php echo $pun_config['o_base_url'].'/' ?>img/test.png[/img]</code> <?php echo $lang_help['produces'] ?> <samp><img src="<?php echo $pun_config['o_base_url'].'/' ?>img/test.png" alt="FluxBB bbcode test" /></samp></p>
+	</div>
+</div>
+<h2><span><?php echo $lang_help['Quotes'] ?></span></h2>
+<div class="box">
+	<div class="inbox">
+		<p><?php echo $lang_help['Quotes info'] ?></p>
+		<p><code>[quote=James]<?php echo $lang_help['Quote text'] ?>[/quote]</code></p>
+		<p><?php echo $lang_help['produces quote box'] ?></p>
+		<div class="postmsg">
+			<div class="quotebox"><cite>James <?php echo $lang_common['wrote'] ?></cite><blockquote><div><p><?php echo $lang_help['Quote text'] ?></p></div></blockquote></div>
+		</div>
+		<p><?php echo $lang_help['Quotes info 2'] ?></p>
+		<p><code>[quote]<?php echo $lang_help['Quote text'] ?>[/quote]</code></p>
+		<p><?php echo $lang_help['produces quote box'] ?></p>
+		<div class="postmsg">
+			<div class="quotebox"><blockquote><div><p><?php echo $lang_help['Quote text'] ?></p></div></blockquote></div>
+		</div>
+		<p><?php echo $lang_help['quote note'] ?></p>
+	</div>
+</div>
+<h2><span><?php echo $lang_help['Code'] ?></span></h2>
+<div class="box">
+	<div class="inbox">
+		<p><?php echo $lang_help['Code info'] ?></p>
+		<p><code>[code]<?php echo $lang_help['Code text'] ?>[/code]</code></p>
+		<p><?php echo $lang_help['produces code box'] ?></p>
+		<div class="postmsg">
+			<div class="codebox"><pre><code><?php echo $lang_help['Code text'] ?></code></pre></div>
+		</div>
+	</div>
+</div>
+<h2><span><?php echo $lang_help['Lists'] ?></span></h2>
+<div class="box">
+	<div class="inbox">
+		<p><a name="lists"></a><?php echo $lang_help['List info'] ?></p>
+		<p><code>[list][*]<?php echo $lang_help['List text 1'] ?>[/*][*]<?php echo $lang_help['List text 2'] ?>[/*][*]<?php echo $lang_help['List text 3'] ?>[/*][/list]</code>
+		<br /><span><?php echo $lang_help['produces list'] ?></span></p>
+		<div class="postmsg">
+			<ul><li><?php echo $lang_help['List text 1'] ?></li><li><?php echo $lang_help['List text 2'] ?></li><li><?php echo $lang_help['List text 3'] ?></li></ul>
+		</div>
+		<p><code>[list=1][*]<?php echo $lang_help['List text 1'] ?>[/*][*]<?php echo $lang_help['List text 2'] ?>[/*][*]<?php echo $lang_help['List text 3'] ?>[/*][/list]</code>
+		<br /><span><?php echo $lang_help['produces decimal list'] ?></span></p>
+		<div class="postmsg">
+			<ol class="decimal"><li><?php echo $lang_help['List text 1'] ?></li><li><?php echo $lang_help['List text 2'] ?></li><li><?php echo $lang_help['List text 3'] ?></li></ol>
+		</div>
+		<p><code>[list=a][*]<?php echo $lang_help['List text 1'] ?>[/*][*]<?php echo $lang_help['List text 2'] ?>[/*][*]<?php echo $lang_help['List text 3'] ?>[/*][/list]</code>
+		<br /><span><?php echo $lang_help['produces alpha list'] ?></span></p>
+		<div class="postmsg">
+			<ol class="alpha"><li><?php echo $lang_help['List text 1'] ?></li><li><?php echo $lang_help['List text 2'] ?></li><li><?php echo $lang_help['List text 3'] ?></li></ol>
+		</div>
+	</div>
+</div>
+<h2><span><?php echo $lang_help['Nested tags'] ?></span></h2>
+<div class="box">
+	<div class="inbox">
+		<p><?php echo $lang_help['Nested tags info'] ?></p>
+		<p><code>[b][u]<?php echo $lang_help['Bold, underlined text'] ?>[/u][/b]</code> <?php echo $lang_help['produces'] ?> <samp><strong><span class="bbu"><?php echo $lang_help['Bold, underlined text'] ?></span></strong></samp></p>
+	</div>
+</div>
+<h2><span><?php echo $lang_help['Smilies'] ?></span></h2>
+<div class="box">
+	<div class="inbox">
+		<p><a name="smilies"></a><?php echo $lang_help['Smilies info'] ?></p>
+<?php
+
+// Display the smiley set
+require PUN_ROOT.'include/parser.php';
+
+$smiley_groups = array();
+
+foreach ($smilies as $smiley_text => $smiley_img)
+	$smiley_groups[$smiley_img][] = $smiley_text;
+
+foreach ($smiley_groups as $smiley_img => $smiley_texts)
+	echo "\t\t".'<p><code>'.implode('</code> '.$lang_common['and'].' <code>', $smiley_texts).'</code> <span>'.$lang_help['produces'].'</span> <samp><img src="'.$pun_config['o_base_url'].'/img/smilies/'.$smiley_img.'" width="15" height="15" alt="'.$smiley_texts[0].'" /></samp></p>'."\n";
+?>
+	</div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/img/avatars/index.html b/img/avatars/index.html
new file mode 100755
index 0000000..89337b2
--- /dev/null
+++ b/img/avatars/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/img/index.html b/img/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/img/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/img/smilies/big_smile.png b/img/smilies/big_smile.png
new file mode 100644
index 0000000..b29719b
Binary files /dev/null and b/img/smilies/big_smile.png differ
diff --git a/img/smilies/cool.png b/img/smilies/cool.png
new file mode 100644
index 0000000..a875361
Binary files /dev/null and b/img/smilies/cool.png differ
diff --git a/img/smilies/hmm.png b/img/smilies/hmm.png
new file mode 100644
index 0000000..69e416a
Binary files /dev/null and b/img/smilies/hmm.png differ
diff --git a/img/smilies/index.html b/img/smilies/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/img/smilies/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/img/smilies/lol.png b/img/smilies/lol.png
new file mode 100644
index 0000000..dd9058b
Binary files /dev/null and b/img/smilies/lol.png differ
diff --git a/img/smilies/mad.png b/img/smilies/mad.png
new file mode 100644
index 0000000..011079d
Binary files /dev/null and b/img/smilies/mad.png differ
diff --git a/img/smilies/neutral.png b/img/smilies/neutral.png
new file mode 100644
index 0000000..9bc5ca0
Binary files /dev/null and b/img/smilies/neutral.png differ
diff --git a/img/smilies/roll.png b/img/smilies/roll.png
new file mode 100644
index 0000000..e2e8e2a
Binary files /dev/null and b/img/smilies/roll.png differ
diff --git a/img/smilies/sad.png b/img/smilies/sad.png
new file mode 100644
index 0000000..c14e144
Binary files /dev/null and b/img/smilies/sad.png differ
diff --git a/img/smilies/smile.png b/img/smilies/smile.png
new file mode 100644
index 0000000..44cc239
Binary files /dev/null and b/img/smilies/smile.png differ
diff --git a/img/smilies/tongue.png b/img/smilies/tongue.png
new file mode 100644
index 0000000..5302c89
Binary files /dev/null and b/img/smilies/tongue.png differ
diff --git a/img/smilies/wink.png b/img/smilies/wink.png
new file mode 100644
index 0000000..595c1fe
Binary files /dev/null and b/img/smilies/wink.png differ
diff --git a/img/smilies/yikes.png b/img/smilies/yikes.png
new file mode 100644
index 0000000..bb4aefd
Binary files /dev/null and b/img/smilies/yikes.png differ
diff --git a/img/test.png b/img/test.png
new file mode 100644
index 0000000..7b46d9e
Binary files /dev/null and b/img/test.png differ
diff --git a/include/cache.php b/include/cache.php
new file mode 100644
index 0000000..b1077a4
--- /dev/null
+++ b/include/cache.php
@@ -0,0 +1,157 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure no one attempts to run this script "directly"
+if (!defined('PUN'))
+	exit;
+
+
+//
+// Generate the config cache PHP script
+//
+function generate_config_cache()
+{
+	global $db;
+
+	// Get the forum config from the DB
+	$result = $db->query('SELECT * FROM '.$db->prefix.'config', true) or error('Unable to fetch forum config', __FILE__, __LINE__, $db->error());
+	while ($cur_config_item = $db->fetch_row($result))
+		$output[$cur_config_item[0]] = $cur_config_item[1];
+
+	// Output config as PHP code
+	$fh = @fopen(FORUM_CACHE_DIR.'cache_config.php', 'wb');
+	if (!$fh)
+		error('Unable to write configuration cache file to cache directory. Please make sure PHP has write access to the directory \'cache\'', __FILE__, __LINE__);
+
+	fwrite($fh, '<?php'."\n\n".'define(\'PUN_CONFIG_LOADED\', 1);'."\n\n".'$pun_config = '.var_export($output, true).';'."\n\n".'?>');
+
+	fclose($fh);
+
+	if (function_exists('apc_delete_file'))
+		apc_delete_file(FORUM_CACHE_DIR.'cache_config.php');
+}
+
+
+//
+// Generate the bans cache PHP script
+//
+function generate_bans_cache()
+{
+	global $db;
+
+	// Get the ban list from the DB
+	$result = $db->query('SELECT * FROM '.$db->prefix.'bans', true) or error('Unable to fetch ban list', __FILE__, __LINE__, $db->error());
+
+	$output = array();
+	while ($cur_ban = $db->fetch_assoc($result))
+		$output[] = $cur_ban;
+
+	// Output ban list as PHP code
+	$fh = @fopen(FORUM_CACHE_DIR.'cache_bans.php', 'wb');
+	if (!$fh)
+		error('Unable to write bans cache file to cache directory. Please make sure PHP has write access to the directory \'cache\'', __FILE__, __LINE__);
+
+	fwrite($fh, '<?php'."\n\n".'define(\'PUN_BANS_LOADED\', 1);'."\n\n".'$pun_bans = '.var_export($output, true).';'."\n\n".'?>');
+
+	fclose($fh);
+
+	if (function_exists('apc_delete_file'))
+		apc_delete_file(FORUM_CACHE_DIR.'cache_bans.php');
+}
+
+
+//
+// Generate the ranks cache PHP script
+//
+function generate_ranks_cache()
+{
+	global $db;
+
+	// Get the rank list from the DB
+	$result = $db->query('SELECT * FROM '.$db->prefix.'ranks ORDER BY min_posts', true) or error('Unable to fetch rank list', __FILE__, __LINE__, $db->error());
+
+	$output = array();
+	while ($cur_rank = $db->fetch_assoc($result))
+		$output[] = $cur_rank;
+
+	// Output ranks list as PHP code
+	$fh = @fopen(FORUM_CACHE_DIR.'cache_ranks.php', 'wb');
+	if (!$fh)
+		error('Unable to write ranks cache file to cache directory. Please make sure PHP has write access to the directory \'cache\'', __FILE__, __LINE__);
+
+	fwrite($fh, '<?php'."\n\n".'define(\'PUN_RANKS_LOADED\', 1);'."\n\n".'$pun_ranks = '.var_export($output, true).';'."\n\n".'?>');
+
+	fclose($fh);
+
+	if (function_exists('apc_delete_file'))
+		apc_delete_file(FORUM_CACHE_DIR.'cache_ranks.php');
+}
+
+
+//
+// Generate quick jump cache PHP scripts
+//
+function generate_quickjump_cache($group_id = false)
+{
+	global $db, $lang_common, $pun_user;
+
+	// If a group_id was supplied, we generate the quick jump cache for that group only
+	if ($group_id !== false)
+		$groups[0] = $group_id;
+	else
+	{
+		// A group_id was now supplied, so we generate the quick jump cache for all groups
+		$result = $db->query('SELECT g_id FROM '.$db->prefix.'groups') or error('Unable to fetch user group list', __FILE__, __LINE__, $db->error());
+		$num_groups = $db->num_rows($result);
+
+		for ($i = 0; $i < $num_groups; ++$i)
+			$groups[] = $db->result($result, $i);
+	}
+
+	// Loop through the groups in $groups and output the cache for each of them
+	foreach ($groups as $group_id)
+	{
+		// Output quick jump as PHP code
+		$fh = @fopen(FORUM_CACHE_DIR.'cache_quickjump_'.$group_id.'.php', 'wb');
+		if (!$fh)
+			error('Unable to write quick jump cache file to cache directory. Please make sure PHP has write access to the directory \'cache\'', __FILE__, __LINE__);
+
+		$output = '<?php'."\n\n".'if (!defined(\'PUN\')) exit;'."\n".'define(\'PUN_QJ_LOADED\', 1);'."\n\n".'?>';
+		$output .= "\t\t\t\t".'<form id="qjump" method="get" action="viewforum.php">'."\n\t\t\t\t\t".'<div><label><span><?php echo $lang_common[\'Jump to\'] ?>'.'<br /></span>'."\n\t\t\t\t\t".'<select name="id" onchange="window.location=(\'viewforum.php?id=\'+this.options[this.selectedIndex].value)">'."\n";
+
+
+		$result = $db->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name, f.redirect_url FROM '.$db->prefix.'categories AS c INNER JOIN '.$db->prefix.'forums AS f ON c.id=f.cat_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$group_id.') WHERE fp.read_forum IS NULL OR fp.read_forum=1 ORDER BY c.disp_position, c.id, f.disp_position', true) or error('Unable to fetch category/forum list', __FILE__, __LINE__, $db->error());
+
+		$cur_category = 0;
+		while ($cur_forum = $db->fetch_assoc($result))
+		{
+			if ($cur_forum['cid'] != $cur_category) // A new category since last iteration?
+			{
+				if ($cur_category)
+					$output .= "\t\t\t\t\t\t".'</optgroup>'."\n";
+
+				$output .= "\t\t\t\t\t\t".'<optgroup label="'.pun_htmlspecialchars($cur_forum['cat_name']).'">'."\n";
+				$cur_category = $cur_forum['cid'];
+			}
+
+			$redirect_tag = ($cur_forum['redirect_url'] != '') ? ' >>>' : '';
+			$output .= "\t\t\t\t\t\t\t".'<option value="'.$cur_forum['fid'].'"<?php echo ($forum_id == '.$cur_forum['fid'].') ? \' selected="selected"\' : \'\' ?>>'.pun_htmlspecialchars($cur_forum['forum_name']).$redirect_tag.'</option>'."\n";
+		}
+
+		$output .= "\t\t\t\t\t\t".'</optgroup>'."\n\t\t\t\t\t".'</select>'."\n\t\t\t\t\t".'<input type="submit" value="<?php echo $lang_common[\'Go\'] ?>" accesskey="g" />'."\n\t\t\t\t\t".'</label></div>'."\n\t\t\t\t".'</form>'."\n";
+
+		fwrite($fh, $output);
+
+		fclose($fh);
+
+		if (function_exists('apc_delete_file'))
+			apc_delete_file(FORUM_CACHE_DIR.'cache_quickjump_'.$group_id.'.php');
+	}
+}
+
+define('FORUM_CACHE_FUNCTIONS_LOADED', true);
diff --git a/include/common.php b/include/common.php
new file mode 100644
index 0000000..dc5ae08
--- /dev/null
+++ b/include/common.php
@@ -0,0 +1,183 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+if (!defined('PUN_ROOT'))
+	exit('The constant PUN_ROOT must be defined and point to a valid FluxBB installation root directory.');
+
+// Define the version and database revision that this code was written for
+define('FORUM_VERSION', '1.4.2');
+
+define('FORUM_DB_REVISION', 8);
+define('FORUM_SI_REVISION', 1);
+define('FORUM_PARSER_REVISION', 1);
+
+// Block prefetch requests
+if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch')
+{
+	header('HTTP/1.1 403 Prefetching Forbidden');
+
+	// Send no-cache headers
+	header('Expires: Thu, 21 Jul 1977 07:30:00 GMT'); // When yours truly first set eyes on this world! :)
+	header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
+	header('Cache-Control: post-check=0, pre-check=0', false);
+	header('Pragma: no-cache'); // For HTTP/1.0 compatibility
+
+	exit;
+}
+
+// Attempt to load the configuration file config.php
+if (file_exists(PUN_ROOT.'config.php'))
+	require PUN_ROOT.'config.php';
+
+// If we have the 1.3-legacy constant defined, define the proper 1.4 constant so we don't get an incorrect "need to install" message
+if (defined('FORUM'))
+	define('PUN', FORUM);
+
+// If PUN isn't defined, config.php is missing or corrupt
+if (!defined('PUN'))
+	exit('The file \'config.php\' doesn\'t exist or is corrupt. Please run <a href="install.php">install.php</a> to install FluxBB first.');
+
+// Load the functions script
+require PUN_ROOT.'include/functions.php';
+
+// Load UTF-8 functions
+require PUN_ROOT.'include/utf8/utf8.php';
+
+// Strip out "bad" UTF-8 characters
+forum_remove_bad_characters();
+
+// Reverse the effect of register_globals
+forum_unregister_globals();
+
+// Record the start time (will be used to calculate the generation time for the page)
+$pun_start = get_microtime();
+
+// Make sure PHP reports all errors except E_NOTICE. FluxBB supports E_ALL, but a lot of scripts it may interact with, do not
+error_reporting(E_ALL ^ E_NOTICE);
+
+// Force POSIX locale (to prevent functions such as strtolower() from messing up UTF-8 strings)
+setlocale(LC_CTYPE, 'C');
+
+// Turn off magic_quotes_runtime
+if (get_magic_quotes_runtime())
+	set_magic_quotes_runtime(0);
+
+// Strip slashes from GET/POST/COOKIE (if magic_quotes_gpc is enabled)
+if (get_magic_quotes_gpc())
+{
+	function stripslashes_array($array)
+	{
+		return is_array($array) ? array_map('stripslashes_array', $array) : stripslashes($array);
+	}
+
+	$_GET = stripslashes_array($_GET);
+	$_POST = stripslashes_array($_POST);
+	$_COOKIE = stripslashes_array($_COOKIE);
+	$_REQUEST = stripslashes_array($_REQUEST);
+}
+
+// If a cookie name is not specified in config.php, we use the default (pun_cookie)
+if (empty($cookie_name))
+	$cookie_name = 'pun_cookie';
+
+// If the cache directory is not specified, we use the default setting
+if (!defined('FORUM_CACHE_DIR'))
+	define('FORUM_CACHE_DIR', PUN_ROOT.'cache/');
+
+// Define a few commonly used constants
+define('PUN_UNVERIFIED', 0);
+define('PUN_ADMIN', 1);
+define('PUN_MOD', 2);
+define('PUN_GUEST', 3);
+define('PUN_MEMBER', 4);
+
+// Load DB abstraction layer and connect
+require PUN_ROOT.'include/dblayer/common_db.php';
+
+// Start a transaction
+$db->start_transaction();
+
+// Load cached config
+if (file_exists(FORUM_CACHE_DIR.'cache_config.php'))
+	include FORUM_CACHE_DIR.'cache_config.php';
+
+if (!defined('PUN_CONFIG_LOADED'))
+{
+	if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+		require PUN_ROOT.'include/cache.php';
+
+	generate_config_cache();
+	require FORUM_CACHE_DIR.'cache_config.php';
+}
+
+// Verify that we are running the proper database schema revision
+if (!isset($pun_config['o_database_revision']) || $pun_config['o_database_revision'] < FORUM_DB_REVISION ||
+		!isset($pun_config['o_searchindex_revision']) || $pun_config['o_searchindex_revision'] < FORUM_SI_REVISION ||
+		!isset($pun_config['o_parser_revision']) || $pun_config['o_parser_revision'] < FORUM_PARSER_REVISION ||
+		version_compare($pun_config['o_cur_version'], FORUM_VERSION, '<'))
+	exit('Your FluxBB database is out-of-date and must be upgraded in order to continue. Please run <a href="'.PUN_ROOT.'db_update.php">db_update.php</a> in order to complete the upgrade process.');
+
+// Enable output buffering
+if (!defined('PUN_DISABLE_BUFFERING'))
+{
+	// Should we use gzip output compression?
+	if ($pun_config['o_gzip'] && extension_loaded('zlib'))
+		ob_start('ob_gzhandler');
+	else
+		ob_start();
+}
+
+// Define standard date/time formats
+$forum_time_formats = array($pun_config['o_time_format'], 'H:i:s', 'H:i', 'g:i:s a', 'g:i a');
+$forum_date_formats = array($pun_config['o_date_format'], 'Y-m-d', 'Y-d-m', 'd-m-Y', 'm-d-Y', 'M j Y', 'jS M Y');
+
+// Check/update/set cookie and fetch user info
+$pun_user = array();
+check_cookie($pun_user);
+
+// Attempt to load the common language file
+if (file_exists(PUN_ROOT.'lang/'.$pun_user['language'].'/common.php'))
+	include PUN_ROOT.'lang/'.$pun_user['language'].'/common.php';
+else
+	error('There is no valid language pack \''.pun_htmlspecialchars($pun_user['language']).'\' installed. Please reinstall a language of that name');
+
+// Check if we are to display a maintenance message
+if ($pun_config['o_maintenance'] && $pun_user['g_id'] > PUN_ADMIN && !defined('PUN_TURN_OFF_MAINT'))
+	maintenance_message();
+
+// Load cached bans
+if (file_exists(FORUM_CACHE_DIR.'cache_bans.php'))
+	include FORUM_CACHE_DIR.'cache_bans.php';
+
+if (!defined('PUN_BANS_LOADED'))
+{
+	if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+		require PUN_ROOT.'include/cache.php';
+
+	generate_bans_cache();
+	require FORUM_CACHE_DIR.'cache_bans.php';
+}
+
+// Check if current user is banned
+check_bans();
+
+// Update online list
+update_users_online();
+
+// Check to see if we logged in without a cookie being set
+if ($pun_user['is_guest'] && isset($_GET['login']))
+	message($lang_common['No cookie']);
+
+// The maximum size of a post, in bytes, since the field is now MEDIUMTEXT this allows ~16MB but lets cap at 1MB...
+if (!defined('PUN_MAX_POSTSIZE'))
+	define('PUN_MAX_POSTSIZE', 1048576);
+
+if (!defined('PUN_SEARCH_MIN_WORD'))
+	define('PUN_SEARCH_MIN_WORD', 3);
+if (!defined('PUN_SEARCH_MAX_WORD'))
+	define('PUN_SEARCH_MAX_WORD', 20);
diff --git a/include/common_admin.php b/include/common_admin.php
new file mode 100644
index 0000000..acf0b4c
--- /dev/null
+++ b/include/common_admin.php
@@ -0,0 +1,133 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure no one attempts to run this script "directly"
+if (!defined('PUN'))
+	exit;
+
+// Make sure we have a usable language pack for admin.
+if (file_exists(PUN_ROOT.'lang/'.$pun_user['language'].'/admin_common.php'))
+	$admin_language = $pun_user['language'];
+else if (file_exists(PUN_ROOT.'lang/'.$pun_config['o_default_lang'].'/admin_common.php'))
+	$admin_language = $pun_config['language'];
+else
+	$admin_language = 'English';
+
+// Attempt to load the admin_common language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_common.php';
+
+//
+// Display the admin navigation menu
+//
+function generate_admin_menu($page = '')
+{
+	global $pun_config, $pun_user, $lang_admin_common;
+
+	$is_admin = $pun_user['g_id'] == PUN_ADMIN ? true : false;
+
+?>
+<div id="adminconsole" class="block2col">
+	<div id="adminmenu" class="blockmenu">
+		<h2><span><?php echo ($is_admin) ? $lang_admin_common['Admin menu'] : $lang_admin_common['Moderator menu'] ?></span></h2>
+		<div class="box">
+			<div class="inbox">
+				<ul>
+					<li<?php if ($page == 'index') echo ' class="isactive"'; ?>><a href="admin_index.php"><?php echo $lang_admin_common['Index'] ?></a></li>
+<?php if ($is_admin): ?>					<li<?php if ($page == 'categories') echo ' class="isactive"'; ?>><a href="admin_categories.php"><?php echo $lang_admin_common['Categories'] ?></a></li>
+<?php endif; ?><?php if ($is_admin): ?>					<li<?php if ($page == 'forums') echo ' class="isactive"'; ?>><a href="admin_forums.php"><?php echo $lang_admin_common['Forums'] ?></a></li>
+<?php endif; ?>					<li<?php if ($page == 'users') echo ' class="isactive"'; ?>><a href="admin_users.php"><?php echo $lang_admin_common['Users'] ?></a></li>
+<?php if ($is_admin): ?>					<li<?php if ($page == 'groups') echo ' class="isactive"'; ?>><a href="admin_groups.php"><?php echo $lang_admin_common['User groups'] ?></a></li>
+<?php endif; ?><?php if ($is_admin): ?>					<li<?php if ($page == 'options') echo ' class="isactive"'; ?>><a href="admin_options.php"><?php echo $lang_admin_common['Options'] ?></a></li>
+<?php endif; ?><?php if ($is_admin): ?>					<li<?php if ($page == 'permissions') echo ' class="isactive"'; ?>><a href="admin_permissions.php"><?php echo $lang_admin_common['Permissions'] ?></a></li>
+<?php endif; ?><?php if ($is_admin || $pun_config['o_censoring'] == '1'): ?>					<li<?php if ($page == 'censoring') echo ' class="isactive"'; ?>><a href="admin_censoring.php"><?php echo $lang_admin_common['Censoring'] ?></a></li>
+<?php endif; ?><?php if ($is_admin): ?>					<li<?php if ($page == 'ranks') echo ' class="isactive"'; ?>><a href="admin_ranks.php"><?php echo $lang_admin_common['Ranks'] ?></a></li>
+<?php endif; ?><?php if ($is_admin || $pun_user['g_mod_ban_users'] == '1'): ?>					<li<?php if ($page == 'bans') echo ' class="isactive"'; ?>><a href="admin_bans.php"><?php echo $lang_admin_common['Bans'] ?></a></li>
+<?php endif; ?><?php if ($is_admin): ?>					<li<?php if ($page == 'prune') echo ' class="isactive"'; ?>><a href="admin_prune.php"><?php echo $lang_admin_common['Prune'] ?></a></li>
+<?php endif; ?><?php if ($is_admin): ?>					<li<?php if ($page == 'maintenance') echo ' class="isactive"'; ?>><a href="admin_maintenance.php"><?php echo $lang_admin_common['Maintenance'] ?></a></li>
+<?php endif; ?>					<li<?php if ($page == 'reports') echo ' class="isactive"'; ?>><a href="admin_reports.php"><?php echo $lang_admin_common['Reports'] ?></a></li>
+				</ul>
+			</div>
+		</div>
+<?php
+
+	// See if there are any plugins
+	$plugins = forum_list_plugins($is_admin);
+
+	// Did we find any plugins?
+	if (!empty($plugins))
+	{
+
+?>
+		<h2 class="block2"><span><?php echo $lang_admin_common['Plugins menu'] ?></span></h2>
+		<div class="box">
+			<div class="inbox">
+				<ul>
+<?php
+
+		foreach ($plugins as $cur_plugin)
+			echo "\t\t\t\t\t".'<li'.(($page == $cur_plugin[1]) ? ' class="isactive"' : '').'><a href="admin_loader.php?plugin='.$cur_plugin[1].'">'.str_replace('_', ' ', $cur_plugin[0]).'</a></li>'."\n";
+
+?>
+				</ul>
+			</div>
+		</div>
+<?php
+
+	}
+
+?>
+	</div>
+
+<?php
+
+}
+
+
+//
+// Delete topics from $forum_id that are "older than" $prune_date (if $prune_sticky is 1, sticky topics will also be deleted)
+//
+function prune($forum_id, $prune_sticky, $prune_date)
+{
+	global $db;
+
+	$extra_sql = ($prune_date != -1) ? ' AND last_post<'.$prune_date : '';
+
+	if (!$prune_sticky)
+		$extra_sql .= ' AND sticky=\'0\'';
+
+	// Fetch topics to prune
+	$result = $db->query('SELECT id FROM '.$db->prefix.'topics WHERE forum_id='.$forum_id.$extra_sql, true) or error('Unable to fetch topics', __FILE__, __LINE__, $db->error());
+
+	$topic_ids = '';
+	while ($row = $db->fetch_row($result))
+		$topic_ids .= (($topic_ids != '') ? ',' : '').$row[0];
+
+	if ($topic_ids != '')
+	{
+		// Fetch posts to prune
+		$result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id IN('.$topic_ids.')', true) or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
+
+		$post_ids = '';
+		while ($row = $db->fetch_row($result))
+			$post_ids .= (($post_ids != '') ? ',' : '').$row[0];
+
+		if ($post_ids != '')
+		{
+			// Delete topics
+			$db->query('DELETE FROM '.$db->prefix.'topics WHERE id IN('.$topic_ids.')') or error('Unable to prune topics', __FILE__, __LINE__, $db->error());
+			// Delete subscriptions
+			$db->query('DELETE FROM '.$db->prefix.'subscriptions WHERE topic_id IN('.$topic_ids.')') or error('Unable to prune subscriptions', __FILE__, __LINE__, $db->error());
+			// Delete posts
+			$db->query('DELETE FROM '.$db->prefix.'posts WHERE id IN('.$post_ids.')') or error('Unable to prune posts', __FILE__, __LINE__, $db->error());
+
+			// We removed a bunch of posts, so now we have to update the search index
+			require_once PUN_ROOT.'include/search_idx.php';
+			strip_search_index($post_ids);
+		}
+	}
+}
diff --git a/include/dblayer/common_db.php b/include/dblayer/common_db.php
new file mode 100644
index 0000000..121d895
--- /dev/null
+++ b/include/dblayer/common_db.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure no one attempts to run this script "directly"
+if (!defined('PUN'))
+	exit;
+
+
+// Load the appropriate DB layer class
+switch ($db_type)
+{
+	case 'mysql':
+		require PUN_ROOT.'include/dblayer/mysql.php';
+		break;
+
+	case 'mysql_innodb':
+		require PUN_ROOT.'include/dblayer/mysql_innodb.php';
+		break;
+
+	case 'mysqli':
+		require PUN_ROOT.'include/dblayer/mysqli.php';
+		break;
+
+	case 'mysqli_innodb':
+		require PUN_ROOT.'include/dblayer/mysqli_innodb.php';
+		break;
+
+	case 'pgsql':
+		require PUN_ROOT.'include/dblayer/pgsql.php';
+		break;
+
+	case 'sqlite':
+		require PUN_ROOT.'include/dblayer/sqlite.php';
+		break;
+
+	default:
+		error('\''.$db_type.'\' is not a valid database type. Please check settings in config.php.', __FILE__, __LINE__);
+		break;
+}
+
+
+// Create the database adapter object (and open/connect to/select db)
+$db = new DBLayer($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect);
diff --git a/include/dblayer/index.html b/include/dblayer/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/include/dblayer/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/include/dblayer/mysql.php b/include/dblayer/mysql.php
new file mode 100644
index 0000000..1ef5108
--- /dev/null
+++ b/include/dblayer/mysql.php
@@ -0,0 +1,362 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure we have built in support for MySQL
+if (!function_exists('mysql_connect'))
+	exit('This PHP environment doesn\'t have MySQL support built in. MySQL support is required if you want to use a MySQL database to run this forum. Consult the PHP documentation for further assistance.');
+
+
+class DBLayer
+{
+	var $prefix;
+	var $link_id;
+	var $query_result;
+
+	var $saved_queries = array();
+	var $num_queries = 0;
+
+	var $error_no = false;
+	var $error_msg = 'Unknown';
+
+	var $datatype_transformations = array(
+		'/^SERIAL$/'	=>	'INT(10) UNSIGNED AUTO_INCREMENT'
+	);
+
+
+	function DBLayer($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+	{
+		$this->prefix = $db_prefix;
+
+		if ($p_connect)
+			$this->link_id = @mysql_pconnect($db_host, $db_username, $db_password);
+		else
+			$this->link_id = @mysql_connect($db_host, $db_username, $db_password);
+
+		if ($this->link_id)
+		{
+			if (!@mysql_select_db($db_name, $this->link_id))
+				error('Unable to select database. MySQL reported: '.mysql_error(), __FILE__, __LINE__);
+		}
+		else
+			error('Unable to connect to MySQL server. MySQL reported: '.mysql_error(), __FILE__, __LINE__);
+
+		// Setup the client-server character set (UTF-8)
+		if (!defined('FORUM_NO_SET_NAMES'))
+			$this->set_names('utf8');
+
+		return $this->link_id;
+	}
+
+
+	function start_transaction()
+	{
+		return;
+	}
+
+
+	function end_transaction()
+	{
+		return;
+	}
+
+
+	function query($sql, $unbuffered = false)
+	{
+		if (defined('PUN_SHOW_QUERIES'))
+			$q_start = get_microtime();
+
+		if ($unbuffered)
+			$this->query_result = @mysql_unbuffered_query($sql, $this->link_id);
+		else
+			$this->query_result = @mysql_query($sql, $this->link_id);
+
+		if ($this->query_result)
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, sprintf('%.5f', get_microtime() - $q_start));
+
+			++$this->num_queries;
+
+			return $this->query_result;
+		}
+		else
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, 0);
+
+			$this->error_no = @mysql_errno($this->link_id);
+			$this->error_msg = @mysql_error($this->link_id);
+
+			return false;
+		}
+	}
+
+
+	function result($query_id = 0, $row = 0, $col = 0)
+	{
+		return ($query_id) ? @mysql_result($query_id, $row, $col) : false;
+	}
+
+
+	function fetch_assoc($query_id = 0)
+	{
+		return ($query_id) ? @mysql_fetch_assoc($query_id) : false;
+	}
+
+
+	function fetch_row($query_id = 0)
+	{
+		return ($query_id) ? @mysql_fetch_row($query_id) : false;
+	}
+
+
+	function num_rows($query_id = 0)
+	{
+		return ($query_id) ? @mysql_num_rows($query_id) : false;
+	}
+
+
+	function affected_rows()
+	{
+		return ($this->link_id) ? @mysql_affected_rows($this->link_id) : false;
+	}
+
+
+	function insert_id()
+	{
+		return ($this->link_id) ? @mysql_insert_id($this->link_id) : false;
+	}
+
+
+	function get_num_queries()
+	{
+		return $this->num_queries;
+	}
+
+
+	function get_saved_queries()
+	{
+		return $this->saved_queries;
+	}
+
+
+	function free_result($query_id = false)
+	{
+		return ($query_id) ? @mysql_free_result($query_id) : false;
+	}
+
+
+	function escape($str)
+	{
+		if (is_array($str))
+			return '';
+		else if (function_exists('mysql_real_escape_string'))
+			return mysql_real_escape_string($str, $this->link_id);
+		else
+			return mysql_escape_string($str);
+	}
+
+
+	function error()
+	{
+		$result['error_sql'] = @current(@end($this->saved_queries));
+		$result['error_no'] = $this->error_no;
+		$result['error_msg'] = $this->error_msg;
+
+		return $result;
+	}
+
+
+	function close()
+	{
+		if ($this->link_id)
+		{
+			if ($this->query_result)
+				@mysql_free_result($this->query_result);
+
+			return @mysql_close($this->link_id);
+		}
+		else
+			return false;
+	}
+
+	function get_names()
+	{
+		$result = $this->query('SHOW VARIABLES LIKE \'character_set_connection\'');
+		return $this->result($result, 0, 1);
+	}
+
+
+	function set_names($names)
+	{
+		return $this->query('SET NAMES \''.$this->escape($names).'\'');
+	}
+
+
+	function get_version()
+	{
+		$result = $this->query('SELECT VERSION()');
+
+		return array(
+			'name'		=> 'MySQL Standard',
+			'version'	=> preg_replace('/^([^-]+).*$/', '\\1', $this->result($result))
+		);
+	}
+
+
+	function table_exists($table_name, $no_prefix = false)
+	{
+		$result = $this->query('SHOW TABLES LIKE \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function field_exists($table_name, $field_name, $no_prefix = false)
+	{
+		$result = $this->query('SHOW COLUMNS FROM '.($no_prefix ? '' : $this->prefix).$table_name.' LIKE \''.$this->escape($field_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function index_exists($table_name, $index_name, $no_prefix = false)
+	{
+		$exists = false;
+
+		$result = $this->query('SHOW INDEX FROM '.($no_prefix ? '' : $this->prefix).$table_name);
+		while ($cur_index = $this->fetch_assoc($result))
+		{
+			if ($cur_index['Key_name'] == ($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name)
+			{
+				$exists = true;
+				break;
+			}
+		}
+
+		return $exists;
+	}
+
+
+	function create_table($table_name, $schema, $no_prefix = false)
+	{
+		if ($this->table_exists($table_name, $no_prefix))
+			return true;
+
+		$query = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name." (\n";
+
+		// Go through every schema element and add it to the query
+		foreach ($schema['FIELDS'] as $field_name => $field_data)
+		{
+			$field_data['datatype'] = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_data['datatype']);
+
+			$query .= $field_name.' '.$field_data['datatype'];
+
+			if (isset($field_data['collation']))
+				$query .= 'CHARACTER SET utf8 COLLATE utf8_'.$field_data['collation'];
+
+			if (!$field_data['allow_null'])
+				$query .= ' NOT NULL';
+
+			if (isset($field_data['default']))
+				$query .= ' DEFAULT '.$field_data['default'];
+
+			$query .= ",\n";
+		}
+
+		// If we have a primary key, add it
+		if (isset($schema['PRIMARY KEY']))
+			$query .= 'PRIMARY KEY ('.implode(',', $schema['PRIMARY KEY']).'),'."\n";
+
+		// Add unique keys
+		if (isset($schema['UNIQUE KEYS']))
+		{
+			foreach ($schema['UNIQUE KEYS'] as $key_name => $key_fields)
+				$query .= 'UNIQUE KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$key_name.'('.implode(',', $key_fields).'),'."\n";
+		}
+
+		// Add indexes
+		if (isset($schema['INDEXES']))
+		{
+			foreach ($schema['INDEXES'] as $index_name => $index_fields)
+				$query .= 'KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.'('.implode(',', $index_fields).'),'."\n";
+		}
+
+		// We remove the last two characters (a newline and a comma) and add on the ending
+		$query = substr($query, 0, strlen($query) - 2)."\n".') ENGINE = '.(isset($schema['ENGINE']) ? $schema['ENGINE'] : 'MyISAM').' CHARACTER SET utf8';
+
+		return $this->query($query) ? true : false;
+	}
+
+
+	function drop_table($table_name, $no_prefix = false)
+	{
+		if (!$this->table_exists($table_name, $no_prefix))
+			return true;
+
+		return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+
+
+	function add_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if ($this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+		if ($default_value !== null && !is_int($default_value) && !is_float($default_value))
+			$default_value = '\''.$this->escape($default_value).'\'';
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').($default_value !== null ? ' DEFAULT '.$default_value : ' ').($after_field != null ? ' AFTER '.$after_field : '')) ? true : false;
+	}
+
+
+	function alter_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+		if ($default_value !== null && !is_int($default_value) && !is_float($default_value))
+			$default_value = '\''.$this->escape($default_value).'\'';
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' MODIFY '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').($default_value !== null ? ' DEFAULT '.$default_value : ' ').($after_field != null ? ' AFTER '.$after_field : '')) ? true : false;
+	}
+
+
+	function drop_field($table_name, $field_name, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP '.$field_name) ? true : false;
+	}
+
+
+	function add_index($table_name, $index_name, $index_fields, $unique = false, $no_prefix = false)
+	{
+		if ($this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.($unique ? 'UNIQUE ' : '').'INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.' ('.implode(',', $index_fields).')') ? true : false;
+	}
+
+
+	function drop_index($table_name, $index_name, $no_prefix = false)
+	{
+		if (!$this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name) ? true : false;
+	}
+
+	function truncate_table($table_name, $no_prefix = false)
+	{
+		return $this->query('TRUNCATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+}
diff --git a/include/dblayer/mysql_innodb.php b/include/dblayer/mysql_innodb.php
new file mode 100644
index 0000000..a8588a9
--- /dev/null
+++ b/include/dblayer/mysql_innodb.php
@@ -0,0 +1,376 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure we have built in support for MySQL
+if (!function_exists('mysql_connect'))
+	exit('This PHP environment doesn\'t have MySQL support built in. MySQL support is required if you want to use a MySQL database to run this forum. Consult the PHP documentation for further assistance.');
+
+
+class DBLayer
+{
+	var $prefix;
+	var $link_id;
+	var $query_result;
+	var $in_transaction = 0;
+
+	var $saved_queries = array();
+	var $num_queries = 0;
+
+	var $error_no = false;
+	var $error_msg = 'Unknown';
+
+	var $datatype_transformations = array(
+		'/^SERIAL$/'	=>	'INT(10) UNSIGNED AUTO_INCREMENT'
+	);
+
+
+	function DBLayer($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+	{
+		$this->prefix = $db_prefix;
+
+		if ($p_connect)
+			$this->link_id = @mysql_pconnect($db_host, $db_username, $db_password);
+		else
+			$this->link_id = @mysql_connect($db_host, $db_username, $db_password);
+
+		if ($this->link_id)
+		{
+			if (!@mysql_select_db($db_name, $this->link_id))
+				error('Unable to select database. MySQL reported: '.mysql_error(), __FILE__, __LINE__);
+		}
+		else
+			error('Unable to connect to MySQL server. MySQL reported: '.mysql_error(), __FILE__, __LINE__);
+
+		// Setup the client-server character set (UTF-8)
+		if (!defined('FORUM_NO_SET_NAMES'))
+			$this->set_names('utf8');
+
+		return $this->link_id;
+	}
+
+
+	function start_transaction()
+	{
+		++$this->in_transaction;
+
+		mysql_query('START TRANSACTION', $this->link_id);
+		return;
+	}
+
+
+	function end_transaction()
+	{
+		--$this->in_transaction;
+
+		mysql_query('COMMIT', $this->link_id);
+		return;
+	}
+
+
+	function query($sql, $unbuffered = false)
+	{
+		if (defined('PUN_SHOW_QUERIES'))
+			$q_start = get_microtime();
+
+		if ($unbuffered)
+			$this->query_result = @mysql_unbuffered_query($sql, $this->link_id);
+		else
+			$this->query_result = @mysql_query($sql, $this->link_id);
+
+		if ($this->query_result)
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, sprintf('%.5f', get_microtime() - $q_start));
+
+			++$this->num_queries;
+
+			return $this->query_result;
+		}
+		else
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, 0);
+
+			$this->error_no = @mysql_errno($this->link_id);
+			$this->error_msg = @mysql_error($this->link_id);
+
+			// Rollback transaction
+			if ($this->in_transaction)
+				mysql_query('ROLLBACK', $this->link_id);
+
+			--$this->in_transaction;
+
+			return false;
+		}
+	}
+
+
+	function result($query_id = 0, $row = 0, $col = 0)
+	{
+		return ($query_id) ? @mysql_result($query_id, $row, $col) : false;
+	}
+
+
+	function fetch_assoc($query_id = 0)
+	{
+		return ($query_id) ? @mysql_fetch_assoc($query_id) : false;
+	}
+
+
+	function fetch_row($query_id = 0)
+	{
+		return ($query_id) ? @mysql_fetch_row($query_id) : false;
+	}
+
+
+	function num_rows($query_id = 0)
+	{
+		return ($query_id) ? @mysql_num_rows($query_id) : false;
+	}
+
+
+	function affected_rows()
+	{
+		return ($this->link_id) ? @mysql_affected_rows($this->link_id) : false;
+	}
+
+
+	function insert_id()
+	{
+		return ($this->link_id) ? @mysql_insert_id($this->link_id) : false;
+	}
+
+
+	function get_num_queries()
+	{
+		return $this->num_queries;
+	}
+
+
+	function get_saved_queries()
+	{
+		return $this->saved_queries;
+	}
+
+
+	function free_result($query_id = false)
+	{
+		return ($query_id) ? @mysql_free_result($query_id) : false;
+	}
+
+
+	function escape($str)
+	{
+		if (is_array($str))
+			return '';
+		else if (function_exists('mysql_real_escape_string'))
+			return mysql_real_escape_string($str, $this->link_id);
+		else
+			return mysql_escape_string($str);
+	}
+
+
+	function error()
+	{
+		$result['error_sql'] = @current(@end($this->saved_queries));
+		$result['error_no'] = $this->error_no;
+		$result['error_msg'] = $this->error_msg;
+
+		return $result;
+	}
+
+
+	function close()
+	{
+		if ($this->link_id)
+		{
+			if ($this->query_result)
+				@mysql_free_result($this->query_result);
+
+			return @mysql_close($this->link_id);
+		}
+		else
+			return false;
+	}
+
+
+	function get_names()
+	{
+		$result = $this->query('SHOW VARIABLES LIKE \'character_set_connection\'');
+		return $this->result($result, 0, 1);
+	}
+
+
+	function set_names($names)
+	{
+		return $this->query('SET NAMES \''.$this->escape($names).'\'');
+	}
+
+
+	function get_version()
+	{
+		$result = $this->query('SELECT VERSION()');
+
+		return array(
+			'name'		=> 'MySQL Standard (InnoDB)',
+			'version'	=> preg_replace('/^([^-]+).*$/', '\\1', $this->result($result))
+		);
+	}
+
+
+	function table_exists($table_name, $no_prefix = false)
+	{
+		$result = $this->query('SHOW TABLES LIKE \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function field_exists($table_name, $field_name, $no_prefix = false)
+	{
+		$result = $this->query('SHOW COLUMNS FROM '.($no_prefix ? '' : $this->prefix).$table_name.' LIKE \''.$this->escape($field_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function index_exists($table_name, $index_name, $no_prefix = false)
+	{
+		$exists = false;
+
+		$result = $this->query('SHOW INDEX FROM '.($no_prefix ? '' : $this->prefix).$table_name);
+		while ($cur_index = $this->fetch_assoc($result))
+		{
+			if ($cur_index['Key_name'] == ($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name)
+			{
+				$exists = true;
+				break;
+			}
+		}
+
+		return $exists;
+	}
+
+
+	function create_table($table_name, $schema, $no_prefix = false)
+	{
+		if ($this->table_exists($table_name, $no_prefix))
+			return true;
+
+		$query = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name." (\n";
+
+		// Go through every schema element and add it to the query
+		foreach ($schema['FIELDS'] as $field_name => $field_data)
+		{
+			$field_data['datatype'] = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_data['datatype']);
+
+			$query .= $field_name.' '.$field_data['datatype'];
+
+			if (isset($field_data['collation']))
+				$query .= 'CHARACTER SET utf8 COLLATE utf8_'.$field_data['collation'];
+
+			if (!$field_data['allow_null'])
+				$query .= ' NOT NULL';
+
+			if (isset($field_data['default']))
+				$query .= ' DEFAULT '.$field_data['default'];
+
+			$query .= ",\n";
+		}
+
+		// If we have a primary key, add it
+		if (isset($schema['PRIMARY KEY']))
+			$query .= 'PRIMARY KEY ('.implode(',', $schema['PRIMARY KEY']).'),'."\n";
+
+		// Add unique keys
+		if (isset($schema['UNIQUE KEYS']))
+		{
+			foreach ($schema['UNIQUE KEYS'] as $key_name => $key_fields)
+				$query .= 'UNIQUE KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$key_name.'('.implode(',', $key_fields).'),'."\n";
+		}
+
+		// Add indexes
+		if (isset($schema['INDEXES']))
+		{
+			foreach ($schema['INDEXES'] as $index_name => $index_fields)
+				$query .= 'KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.'('.implode(',', $index_fields).'),'."\n";
+		}
+
+		// We remove the last two characters (a newline and a comma) and add on the ending
+		$query = substr($query, 0, strlen($query) - 2)."\n".') ENGINE = '.(isset($schema['ENGINE']) ? $schema['ENGINE'] : 'InnoDB').' CHARACTER SET utf8';
+
+		return $this->query($query) ? true : false;
+	}
+
+
+	function drop_table($table_name, $no_prefix = false)
+	{
+		if (!$this->table_exists($table_name, $no_prefix))
+			return true;
+
+		return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+
+
+	function add_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if ($this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+		if ($default_value !== null && !is_int($default_value) && !is_float($default_value))
+			$default_value = '\''.$this->escape($default_value).'\'';
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').($default_value !== null ? ' DEFAULT '.$default_value : ' ').($after_field != null ? ' AFTER '.$after_field : '')) ? true : false;
+	}
+
+
+	function alter_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+		if ($default_value !== null && !is_int($default_value) && !is_float($default_value))
+			$default_value = '\''.$this->escape($default_value).'\'';
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' MODIFY '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').($default_value !== null ? ' DEFAULT '.$default_value : ' ').($after_field != null ? ' AFTER '.$after_field : '')) ? true : false;
+	}
+
+
+	function drop_field($table_name, $field_name, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP '.$field_name) ? true : false;
+	}
+
+
+	function add_index($table_name, $index_name, $index_fields, $unique = false, $no_prefix = false)
+	{
+		if ($this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.($unique ? 'UNIQUE ' : '').'INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.' ('.implode(',', $index_fields).')') ? true : false;
+	}
+
+
+	function drop_index($table_name, $index_name, $no_prefix = false)
+	{
+		if (!$this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name) ? true : false;
+	}
+
+	function truncate_table($table_name, $no_prefix = false)
+	{
+		return $this->query('TRUNCATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+}
diff --git a/include/dblayer/mysqli.php b/include/dblayer/mysqli.php
new file mode 100644
index 0000000..9959705
--- /dev/null
+++ b/include/dblayer/mysqli.php
@@ -0,0 +1,369 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure we have built in support for MySQL
+if (!function_exists('mysqli_connect'))
+	exit('This PHP environment doesn\'t have Improved MySQL (mysqli) support built in. Improved MySQL support is required if you want to use a MySQL 4.1 (or later) database to run this forum. Consult the PHP documentation for further assistance.');
+
+
+class DBLayer
+{
+	var $prefix;
+	var $link_id;
+	var $query_result;
+
+	var $saved_queries = array();
+	var $num_queries = 0;
+
+	var $error_no = false;
+	var $error_msg = 'Unknown';
+
+	var $datatype_transformations = array(
+		'/^SERIAL$/'	=>	'INT(10) UNSIGNED AUTO_INCREMENT'
+	);
+
+
+	function DBLayer($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+	{
+		$this->prefix = $db_prefix;
+
+		// Was a custom port supplied with $db_host?
+		if (strpos($db_host, ':') !== false)
+			list($db_host, $db_port) = explode(':', $db_host);
+
+		// Persistent connection in MySQLi are only available in PHP 5.3 and later releases
+		$p_connect = $p_connect && version_compare(PHP_VERSION, '5.3.0', '>=') ? 'p:' : '';
+
+		if (isset($db_port))
+			$this->link_id = @mysqli_connect($p_connect.$db_host, $db_username, $db_password, $db_name, $db_port);
+		else
+			$this->link_id = @mysqli_connect($p_connect.$db_host, $db_username, $db_password, $db_name);
+
+		if (!$this->link_id)
+			error('Unable to connect to MySQL and select database. MySQL reported: '.mysqli_connect_error(), __FILE__, __LINE__);
+
+		// Setup the client-server character set (UTF-8)
+		if (!defined('FORUM_NO_SET_NAMES'))
+			$this->set_names('utf8');
+
+		return $this->link_id;
+	}
+
+
+	function start_transaction()
+	{
+		return;
+	}
+
+
+	function end_transaction()
+	{
+		return;
+	}
+
+
+	function query($sql, $unbuffered = false)
+	{
+		if (defined('PUN_SHOW_QUERIES'))
+			$q_start = get_microtime();
+
+		$this->query_result = @mysqli_query($this->link_id, $sql);
+
+		if ($this->query_result)
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, sprintf('%.5f', get_microtime() - $q_start));
+
+			++$this->num_queries;
+
+			return $this->query_result;
+		}
+		else
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, 0);
+
+			$this->error_no = @mysqli_errno($this->link_id);
+			$this->error_msg = @mysqli_error($this->link_id);
+
+			return false;
+		}
+	}
+
+
+	function result($query_id = 0, $row = 0, $col = 0)
+	{
+		if ($query_id)
+		{
+			if ($row !== 0 && @mysqli_data_seek($query_id, $row) === false)
+				return false;
+
+			$cur_row = @mysqli_fetch_row($query_id);
+			if ($cur_row === false)
+				return false;
+
+			return $cur_row[$col];
+		}
+		else
+			return false;
+	}
+
+
+	function fetch_assoc($query_id = 0)
+	{
+		return ($query_id) ? @mysqli_fetch_assoc($query_id) : false;
+	}
+
+
+	function fetch_row($query_id = 0)
+	{
+		return ($query_id) ? @mysqli_fetch_row($query_id) : false;
+	}
+
+
+	function num_rows($query_id = 0)
+	{
+		return ($query_id) ? @mysqli_num_rows($query_id) : false;
+	}
+
+
+	function affected_rows()
+	{
+		return ($this->link_id) ? @mysqli_affected_rows($this->link_id) : false;
+	}
+
+
+	function insert_id()
+	{
+		return ($this->link_id) ? @mysqli_insert_id($this->link_id) : false;
+	}
+
+
+	function get_num_queries()
+	{
+		return $this->num_queries;
+	}
+
+
+	function get_saved_queries()
+	{
+		return $this->saved_queries;
+	}
+
+
+	function free_result($query_id = false)
+	{
+		return ($query_id) ? @mysqli_free_result($query_id) : false;
+	}
+
+
+	function escape($str)
+	{
+		return is_array($str) ? '' : mysqli_real_escape_string($this->link_id, $str);
+	}
+
+
+	function error()
+	{
+		$result['error_sql'] = @current(@end($this->saved_queries));
+		$result['error_no'] = $this->error_no;
+		$result['error_msg'] = $this->error_msg;
+
+		return $result;
+	}
+
+
+	function close()
+	{
+		if ($this->link_id)
+		{
+			if ($this->query_result)
+				@mysqli_free_result($this->query_result);
+
+			return @mysqli_close($this->link_id);
+		}
+		else
+			return false;
+	}
+
+
+	function get_names()
+	{
+		$result = $this->query('SHOW VARIABLES LIKE \'character_set_connection\'');
+		return $this->result($result, 0, 1);
+	}
+
+
+	function set_names($names)
+	{
+		return $this->query('SET NAMES \''.$this->escape($names).'\'');
+	}
+
+
+	function get_version()
+	{
+		$result = $this->query('SELECT VERSION()');
+
+		return array(
+			'name'		=> 'MySQL Improved',
+			'version'	=> preg_replace('/^([^-]+).*$/', '\\1', $this->result($result))
+		);
+	}
+
+
+	function table_exists($table_name, $no_prefix = false)
+	{
+		$result = $this->query('SHOW TABLES LIKE \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function field_exists($table_name, $field_name, $no_prefix = false)
+	{
+		$result = $this->query('SHOW COLUMNS FROM '.($no_prefix ? '' : $this->prefix).$table_name.' LIKE \''.$this->escape($field_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function index_exists($table_name, $index_name, $no_prefix = false)
+	{
+		$exists = false;
+
+		$result = $this->query('SHOW INDEX FROM '.($no_prefix ? '' : $this->prefix).$table_name);
+		while ($cur_index = $this->fetch_assoc($result))
+		{
+			if ($cur_index['Key_name'] == ($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name)
+			{
+				$exists = true;
+				break;
+			}
+		}
+
+		return $exists;
+	}
+
+
+	function create_table($table_name, $schema, $no_prefix = false)
+	{
+		if ($this->table_exists($table_name, $no_prefix))
+			return true;
+
+		$query = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name." (\n";
+
+		// Go through every schema element and add it to the query
+		foreach ($schema['FIELDS'] as $field_name => $field_data)
+		{
+			$field_data['datatype'] = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_data['datatype']);
+
+			$query .= $field_name.' '.$field_data['datatype'];
+
+			if (isset($field_data['collation']))
+				$query .= 'CHARACTER SET utf8 COLLATE utf8_'.$field_data['collation'];
+
+			if (!$field_data['allow_null'])
+				$query .= ' NOT NULL';
+
+			if (isset($field_data['default']))
+				$query .= ' DEFAULT '.$field_data['default'];
+
+			$query .= ",\n";
+		}
+
+		// If we have a primary key, add it
+		if (isset($schema['PRIMARY KEY']))
+			$query .= 'PRIMARY KEY ('.implode(',', $schema['PRIMARY KEY']).'),'."\n";
+
+		// Add unique keys
+		if (isset($schema['UNIQUE KEYS']))
+		{
+			foreach ($schema['UNIQUE KEYS'] as $key_name => $key_fields)
+				$query .= 'UNIQUE KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$key_name.'('.implode(',', $key_fields).'),'."\n";
+		}
+
+		// Add indexes
+		if (isset($schema['INDEXES']))
+		{
+			foreach ($schema['INDEXES'] as $index_name => $index_fields)
+				$query .= 'KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.'('.implode(',', $index_fields).'),'."\n";
+		}
+
+		// We remove the last two characters (a newline and a comma) and add on the ending
+		$query = substr($query, 0, strlen($query) - 2)."\n".') ENGINE = '.(isset($schema['ENGINE']) ? $schema['ENGINE'] : 'MyISAM').' CHARACTER SET utf8';
+
+		return $this->query($query) ? true : false;
+	}
+
+
+	function drop_table($table_name, $no_prefix = false)
+	{
+		if (!$this->table_exists($table_name, $no_prefix))
+			return true;
+
+		return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+
+
+	function add_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if ($this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+		if ($default_value !== null && !is_int($default_value) && !is_float($default_value))
+			$default_value = '\''.$this->escape($default_value).'\'';
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').($default_value !== null ? ' DEFAULT '.$default_value : ' ').($after_field != null ? ' AFTER '.$after_field : '')) ? true : false;
+	}
+
+
+	function alter_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+		if ($default_value !== null && !is_int($default_value) && !is_float($default_value))
+			$default_value = '\''.$this->escape($default_value).'\'';
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' MODIFY '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').($default_value !== null ? ' DEFAULT '.$default_value : ' ').($after_field != null ? ' AFTER '.$after_field : '')) ? true : false;
+	}
+
+
+	function drop_field($table_name, $field_name, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP '.$field_name) ? true : false;
+	}
+
+
+	function add_index($table_name, $index_name, $index_fields, $unique = false, $no_prefix = false)
+	{
+		if ($this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.($unique ? 'UNIQUE ' : '').'INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.' ('.implode(',', $index_fields).')') ? true : false;
+	}
+
+
+	function drop_index($table_name, $index_name, $no_prefix = false)
+	{
+		if (!$this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name) ? true : false;
+	}
+
+	function truncate_table($table_name, $no_prefix = false)
+	{
+		return $this->query('TRUNCATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+}
diff --git a/include/dblayer/mysqli_innodb.php b/include/dblayer/mysqli_innodb.php
new file mode 100644
index 0000000..4591946
--- /dev/null
+++ b/include/dblayer/mysqli_innodb.php
@@ -0,0 +1,382 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure we have built in support for MySQL
+if (!function_exists('mysqli_connect'))
+	exit('This PHP environment doesn\'t have Improved MySQL (mysqli) support built in. Improved MySQL support is required if you want to use a MySQL 4.1 (or later) database to run this forum. Consult the PHP documentation for further assistance.');
+
+
+class DBLayer
+{
+	var $prefix;
+	var $link_id;
+	var $query_result;
+
+	var $saved_queries = array();
+	var $num_queries = 0;
+	var $in_transaction = 0;
+
+	var $error_no = false;
+	var $error_msg = 'Unknown';
+
+	var $datatype_transformations = array(
+		'/^SERIAL$/'	=>	'INT(10) UNSIGNED AUTO_INCREMENT'
+	);
+
+
+	function DBLayer($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+	{
+		$this->prefix = $db_prefix;
+
+		// Was a custom port supplied with $db_host?
+		if (strpos($db_host, ':') !== false)
+			list($db_host, $db_port) = explode(':', $db_host);
+
+		// Persistent connection in MySQLi are only available in PHP 5.3 and later releases
+		$p_connect = $p_connect && version_compare(PHP_VERSION, '5.3.0', '>=') ? 'p:' : '';
+
+		if (isset($db_port))
+			$this->link_id = @mysqli_connect($p_connect.$db_host, $db_username, $db_password, $db_name, $db_port);
+		else
+			$this->link_id = @mysqli_connect($p_connect.$db_host, $db_username, $db_password, $db_name);
+
+		if (!$this->link_id)
+			error('Unable to connect to MySQL and select database. MySQL reported: '.mysqli_connect_error(), __FILE__, __LINE__);
+
+		// Setup the client-server character set (UTF-8)
+		if (!defined('FORUM_NO_SET_NAMES'))
+			$this->set_names('utf8');
+
+		return $this->link_id;
+	}
+
+
+	function start_transaction()
+	{
+		++$this->in_transaction;
+
+		mysqli_query($this->link_id, 'START TRANSACTION');
+		return;
+	}
+
+
+	function end_transaction()
+	{
+		--$this->in_transaction;
+
+		mysqli_query($this->link_id, 'COMMIT');
+		return;
+	}
+
+
+	function query($sql, $unbuffered = false)
+	{
+		if (defined('PUN_SHOW_QUERIES'))
+			$q_start = get_microtime();
+
+		$this->query_result = @mysqli_query($this->link_id, $sql);
+
+		if ($this->query_result)
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, sprintf('%.5f', get_microtime() - $q_start));
+
+			++$this->num_queries;
+
+			return $this->query_result;
+		}
+		else
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, 0);
+
+			$this->error_no = @mysqli_errno($this->link_id);
+			$this->error_msg = @mysqli_error($this->link_id);
+
+			// Rollback transaction
+			if ($this->in_transaction)
+				mysqli_query($this->link_id, 'ROLLBACK');
+
+			--$this->in_transaction;
+
+			return false;
+		}
+	}
+
+
+	function result($query_id = 0, $row = 0, $col = 0)
+	{
+		if ($query_id)
+		{
+			if ($row !== 0 && @mysqli_data_seek($query_id, $row) === false)
+				return false;
+
+			$cur_row = @mysqli_fetch_row($query_id);
+			if ($cur_row === false)
+				return false;
+
+			return $cur_row[$col];
+		}
+		else
+			return false;
+	}
+
+
+	function fetch_assoc($query_id = 0)
+	{
+		return ($query_id) ? @mysqli_fetch_assoc($query_id) : false;
+	}
+
+
+	function fetch_row($query_id = 0)
+	{
+		return ($query_id) ? @mysqli_fetch_row($query_id) : false;
+	}
+
+
+	function num_rows($query_id = 0)
+	{
+		return ($query_id) ? @mysqli_num_rows($query_id) : false;
+	}
+
+
+	function affected_rows()
+	{
+		return ($this->link_id) ? @mysqli_affected_rows($this->link_id) : false;
+	}
+
+
+	function insert_id()
+	{
+		return ($this->link_id) ? @mysqli_insert_id($this->link_id) : false;
+	}
+
+
+	function get_num_queries()
+	{
+		return $this->num_queries;
+	}
+
+
+	function get_saved_queries()
+	{
+		return $this->saved_queries;
+	}
+
+
+	function free_result($query_id = false)
+	{
+		return ($query_id) ? @mysqli_free_result($query_id) : false;
+	}
+
+
+	function escape($str)
+	{
+		return is_array($str) ? '' : mysqli_real_escape_string($this->link_id, $str);
+	}
+
+
+	function error()
+	{
+		$result['error_sql'] = @current(@end($this->saved_queries));
+		$result['error_no'] = $this->error_no;
+		$result['error_msg'] = $this->error_msg;
+
+		return $result;
+	}
+
+
+	function close()
+	{
+		if ($this->link_id)
+		{
+			if ($this->query_result)
+				@mysqli_free_result($this->query_result);
+
+			return @mysqli_close($this->link_id);
+		}
+		else
+			return false;
+	}
+
+
+	function get_names()
+	{
+		$result = $this->query('SHOW VARIABLES LIKE \'character_set_connection\'');
+		return $this->result($result, 0, 1);
+	}
+
+
+	function set_names($names)
+	{
+		return $this->query('SET NAMES \''.$this->escape($names).'\'');
+	}
+
+
+	function get_version()
+	{
+		$result = $this->query('SELECT VERSION()');
+
+		return array(
+			'name'		=> 'MySQL Improved (InnoDB)',
+			'version'	=> preg_replace('/^([^-]+).*$/', '\\1', $this->result($result))
+		);
+	}
+
+
+	function table_exists($table_name, $no_prefix = false)
+	{
+		$result = $this->query('SHOW TABLES LIKE \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function field_exists($table_name, $field_name, $no_prefix = false)
+	{
+		$result = $this->query('SHOW COLUMNS FROM '.($no_prefix ? '' : $this->prefix).$table_name.' LIKE \''.$this->escape($field_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function index_exists($table_name, $index_name, $no_prefix = false)
+	{
+		$exists = false;
+
+		$result = $this->query('SHOW INDEX FROM '.($no_prefix ? '' : $this->prefix).$table_name);
+		while ($cur_index = $this->fetch_assoc($result))
+		{
+			if ($cur_index['Key_name'] == ($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name)
+			{
+				$exists = true;
+				break;
+			}
+		}
+
+		return $exists;
+	}
+
+
+	function create_table($table_name, $schema, $no_prefix = false)
+	{
+		if ($this->table_exists($table_name, $no_prefix))
+			return true;
+
+		$query = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name." (\n";
+
+		// Go through every schema element and add it to the query
+		foreach ($schema['FIELDS'] as $field_name => $field_data)
+		{
+			$field_data['datatype'] = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_data['datatype']);
+
+			$query .= $field_name.' '.$field_data['datatype'];
+
+			if (isset($field_data['collation']))
+				$query .= 'CHARACTER SET utf8 COLLATE utf8_'.$field_data['collation'];
+
+			if (!$field_data['allow_null'])
+				$query .= ' NOT NULL';
+
+			if (isset($field_data['default']))
+				$query .= ' DEFAULT '.$field_data['default'];
+
+			$query .= ",\n";
+		}
+
+		// If we have a primary key, add it
+		if (isset($schema['PRIMARY KEY']))
+			$query .= 'PRIMARY KEY ('.implode(',', $schema['PRIMARY KEY']).'),'."\n";
+
+		// Add unique keys
+		if (isset($schema['UNIQUE KEYS']))
+		{
+			foreach ($schema['UNIQUE KEYS'] as $key_name => $key_fields)
+				$query .= 'UNIQUE KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$key_name.'('.implode(',', $key_fields).'),'."\n";
+		}
+
+		// Add indexes
+		if (isset($schema['INDEXES']))
+		{
+			foreach ($schema['INDEXES'] as $index_name => $index_fields)
+				$query .= 'KEY '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.'('.implode(',', $index_fields).'),'."\n";
+		}
+
+		// We remove the last two characters (a newline and a comma) and add on the ending
+		$query = substr($query, 0, strlen($query) - 2)."\n".') ENGINE = '.(isset($schema['ENGINE']) ? $schema['ENGINE'] : 'InnoDB').' CHARACTER SET utf8';
+
+		return $this->query($query) ? true : false;
+	}
+
+
+	function drop_table($table_name, $no_prefix = false)
+	{
+		if (!$this->table_exists($table_name, $no_prefix))
+			return true;
+
+		return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+
+
+	function add_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if ($this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+		if ($default_value !== null && !is_int($default_value) && !is_float($default_value))
+			$default_value = '\''.$this->escape($default_value).'\'';
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').($default_value !== null ? ' DEFAULT '.$default_value : ' ').($after_field != null ? ' AFTER '.$after_field : '')) ? true : false;
+	}
+
+
+	function alter_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+		if ($default_value !== null && !is_int($default_value) && !is_float($default_value))
+			$default_value = '\''.$this->escape($default_value).'\'';
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' MODIFY '.$field_name.' '.$field_type.($allow_null ? ' ' : ' NOT NULL').($default_value !== null ? ' DEFAULT '.$default_value : ' ').($after_field != null ? ' AFTER '.$after_field : '')) ? true : false;
+	}
+
+
+	function drop_field($table_name, $field_name, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP '.$field_name) ? true : false;
+	}
+
+
+	function add_index($table_name, $index_name, $index_fields, $unique = false, $no_prefix = false)
+	{
+		if ($this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.($unique ? 'UNIQUE ' : '').'INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.' ('.implode(',', $index_fields).')') ? true : false;
+	}
+
+
+	function drop_index($table_name, $index_name, $no_prefix = false)
+	{
+		if (!$this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name) ? true : false;
+	}
+
+	function truncate_table($table_name, $no_prefix = false)
+	{
+		return $this->query('TRUNCATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+}
diff --git a/include/dblayer/pgsql.php b/include/dblayer/pgsql.php
new file mode 100644
index 0000000..9743c6e
--- /dev/null
+++ b/include/dblayer/pgsql.php
@@ -0,0 +1,426 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure we have built in support for PostgreSQL
+if (!function_exists('pg_connect'))
+	exit('This PHP environment doesn\'t have PostgreSQL support built in. PostgreSQL support is required if you want to use a PostgreSQL database to run this forum. Consult the PHP documentation for further assistance.');
+
+
+class DBLayer
+{
+	var $prefix;
+	var $link_id;
+	var $query_result;
+	var $last_query_text = array();
+	var $in_transaction = 0;
+
+	var $saved_queries = array();
+	var $num_queries = 0;
+
+	var $error_no = false;
+	var $error_msg = 'Unknown';
+
+	var $datatype_transformations = array(
+		'/^(TINY|SMALL)INT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$/i'			=>	'SMALLINT',
+		'/^(MEDIUM)?INT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$/i'				=>	'INTEGER',
+		'/^BIGINT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$/i'						=>	'BIGINT',
+		'/^(TINY|MEDIUM|LONG)?TEXT$/i'										=>	'TEXT',
+		'/^DOUBLE( )?(\\([0-9,]+\\))?( )?(UNSIGNED)?$/i'					=>	'DOUBLE PRECISION',
+		'/^FLOAT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$/i'						=>	'REAL'
+	);
+
+
+	function DBLayer($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+	{
+		$this->prefix = $db_prefix;
+
+		if ($db_host)
+		{
+			if (strpos($db_host, ':') !== false)
+			{
+				list($db_host, $dbport) = explode(':', $db_host);
+				$connect_str[] = 'host='.$db_host.' port='.$dbport;
+			}
+			else
+				$connect_str[] = 'host='.$db_host;
+		}
+
+		if ($db_name)
+			$connect_str[] = 'dbname='.$db_name;
+
+		if ($db_username)
+			$connect_str[] = 'user='.$db_username;
+
+		if ($db_password)
+			$connect_str[] = 'password='.$db_password;
+
+		if ($p_connect)
+			$this->link_id = @pg_pconnect(implode(' ', $connect_str));
+		else
+			$this->link_id = @pg_connect(implode(' ', $connect_str));
+
+		if (!$this->link_id)
+			error('Unable to connect to PostgreSQL server', __FILE__, __LINE__);
+
+		// Setup the client-server character set (UTF-8)
+		if (!defined('FORUM_NO_SET_NAMES'))
+			$this->set_names('utf8');
+
+		return $this->link_id;
+	}
+
+
+	function start_transaction()
+	{
+		++$this->in_transaction;
+
+		return (@pg_query($this->link_id, 'BEGIN')) ? true : false;
+	}
+
+
+	function end_transaction()
+	{
+		--$this->in_transaction;
+
+		if (@pg_query($this->link_id, 'COMMIT'))
+			return true;
+		else
+		{
+			@pg_query($this->link_id, 'ROLLBACK');
+			return false;
+		}
+	}
+
+
+	function query($sql, $unbuffered = false) // $unbuffered is ignored since there is no pgsql_unbuffered_query()
+	{
+		if (strrpos($sql, 'LIMIT') !== false)
+			$sql = preg_replace('#LIMIT ([0-9]+),([ 0-9]+)#', 'LIMIT \\2 OFFSET \\1', $sql);
+
+		if (defined('PUN_SHOW_QUERIES'))
+			$q_start = get_microtime();
+
+		@pg_send_query($this->link_id, $sql);
+		$this->query_result = @pg_get_result($this->link_id);
+
+		if (pg_result_status($this->query_result) != PGSQL_FATAL_ERROR)
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, sprintf('%.5f', get_microtime() - $q_start));
+
+			++$this->num_queries;
+
+			$this->last_query_text[$this->query_result] = $sql;
+
+			return $this->query_result;
+		}
+		else
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, 0);
+
+			$this->error_no = false;
+			$this->error_msg = @pg_result_error($this->query_result);
+
+			if ($this->in_transaction)
+				@pg_query($this->link_id, 'ROLLBACK');
+
+			--$this->in_transaction;
+
+			return false;
+		}
+	}
+
+
+	function result($query_id = 0, $row = 0, $col = 0)
+	{
+		return ($query_id) ? @pg_fetch_result($query_id, $row, $col) : false;
+	}
+
+
+	function fetch_assoc($query_id = 0)
+	{
+		return ($query_id) ? @pg_fetch_assoc($query_id) : false;
+	}
+
+
+	function fetch_row($query_id = 0)
+	{
+		return ($query_id) ? @pg_fetch_row($query_id) : false;
+	}
+
+
+	function num_rows($query_id = 0)
+	{
+		return ($query_id) ? @pg_num_rows($query_id) : false;
+	}
+
+
+	function affected_rows()
+	{
+		return ($this->query_result) ? @pg_affected_rows($this->query_result) : false;
+	}
+
+
+	function insert_id()
+	{
+		$query_id = $this->query_result;
+
+		if ($query_id && $this->last_query_text[$query_id] != '')
+		{
+			if (preg_match('/^INSERT INTO ([a-z0-9\_\-]+)/is', $this->last_query_text[$query_id], $table_name))
+			{
+				// Hack (don't ask)
+				if (substr($table_name[1], -6) == 'groups')
+					$table_name[1] .= '_g';
+
+				$temp_q_id = @pg_query($this->link_id, 'SELECT currval(\''.$table_name[1].'_id_seq\')');
+				return ($temp_q_id) ? intval(@pg_fetch_result($temp_q_id, 0)) : false;
+			}
+		}
+
+		return false;
+	}
+
+
+	function get_num_queries()
+	{
+		return $this->num_queries;
+	}
+
+
+	function get_saved_queries()
+	{
+		return $this->saved_queries;
+	}
+
+
+	function free_result($query_id = false)
+	{
+		if (!$query_id)
+			$query_id = $this->query_result;
+
+		return ($query_id) ? @pg_free_result($query_id) : false;
+	}
+
+
+	function escape($str)
+	{
+		return is_array($str) ? '' : pg_escape_string($str);
+	}
+
+
+	function error()
+	{
+		$result['error_sql'] = @current(@end($this->saved_queries));
+		$result['error_no'] = $this->error_no;
+		$result['error_msg'] = $this->error_msg;
+
+		return $result;
+	}
+
+
+	function close()
+	{
+		if ($this->link_id)
+		{
+			if ($this->in_transaction)
+			{
+				if (defined('PUN_SHOW_QUERIES'))
+					$this->saved_queries[] = array('COMMIT', 0);
+
+				@pg_query($this->link_id, 'COMMIT');
+			}
+
+			if ($this->query_result)
+				@pg_free_result($this->query_result);
+
+			return @pg_close($this->link_id);
+		}
+		else
+			return false;
+	}
+
+
+	function get_names()
+	{
+		$result = $this->query('SHOW client_encoding');
+		return strtolower($this->result($result)); // MySQL returns lowercase so lets be consistent
+	}
+
+
+	function set_names($names)
+	{
+		return $this->query('SET NAMES \''.$this->escape($names).'\'');
+	}
+
+
+	function get_version()
+	{
+		$result = $this->query('SELECT VERSION()');
+
+		return array(
+			'name'		=> 'PostgreSQL',
+			'version'	=> preg_replace('/^[^0-9]+([^\s,-]+).*$/', '\\1', $this->result($result))
+		);
+	}
+
+
+	function table_exists($table_name, $no_prefix = false)
+	{
+		$result = $this->query('SELECT 1 FROM pg_class WHERE relname = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function field_exists($table_name, $field_name, $no_prefix = false)
+	{
+		$result = $this->query('SELECT 1 FROM pg_class c INNER JOIN pg_attribute a ON a.attrelid = c.oid WHERE c.relname = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\' AND a.attname = \''.$this->escape($field_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function index_exists($table_name, $index_name, $no_prefix = false)
+	{
+		$result = $this->query('SELECT 1 FROM pg_index i INNER JOIN pg_class c1 ON c1.oid = i.indrelid INNER JOIN pg_class c2 ON c2.oid = i.indexrelid WHERE c1.relname = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\' AND c2.relname = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_'.$this->escape($index_name).'\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function create_table($table_name, $schema, $no_prefix = false)
+	{
+		if ($this->table_exists($table_name, $no_prefix))
+			return true;
+
+		$query = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name." (\n";
+
+		// Go through every schema element and add it to the query
+		foreach ($schema['FIELDS'] as $field_name => $field_data)
+		{
+			$field_data['datatype'] = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_data['datatype']);
+
+			$query .= $field_name.' '.$field_data['datatype'];
+
+			// The SERIAL datatype is a special case where we don't need to say not null
+			if (!$field_data['allow_null'] && $field_data['datatype'] != 'SERIAL')
+				$query .= ' NOT NULL';
+
+			if (isset($field_data['default']))
+				$query .= ' DEFAULT '.$field_data['default'];
+
+			$query .= ",\n";
+		}
+
+		// If we have a primary key, add it
+		if (isset($schema['PRIMARY KEY']))
+			$query .= 'PRIMARY KEY ('.implode(',', $schema['PRIMARY KEY']).'),'."\n";
+
+		// Add unique keys
+		if (isset($schema['UNIQUE KEYS']))
+		{
+			foreach ($schema['UNIQUE KEYS'] as $key_name => $key_fields)
+				$query .= 'UNIQUE ('.implode(',', $key_fields).'),'."\n";
+		}
+
+		// We remove the last two characters (a newline and a comma) and add on the ending
+		$query = substr($query, 0, strlen($query) - 2)."\n".')';
+
+		$result = $this->query($query) ? true : false;
+
+		// Add indexes
+		if (isset($schema['INDEXES']))
+		{
+			foreach ($schema['INDEXES'] as $index_name => $index_fields)
+				$result &= $this->add_index($table_name, $index_name, $index_fields, false, $no_prefix);
+		}
+
+		return $result;
+	}
+
+
+	function drop_table($table_name, $no_prefix = false)
+	{
+		if (!$this->table_exists($table_name, $no_prefix))
+			return true;
+
+		return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+
+
+	function add_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if ($this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+		$result = $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ADD '.$field_name.' '.$field_type) ? true : false;
+
+		if ($default_value !== null)
+		{
+			if (!is_int($default_value) && !is_float($default_value))
+				$default_value = '\''.$this->escape($default_value).'\'';
+
+			$result &= $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ALTER '.$field_name.' SET DEFAULT '.$default_value) ? true : false;
+			$result &= $this->query('UPDATE '.($no_prefix ? '' : $this->prefix).$table_name.' SET '.$field_name.'='.$default_value) ? true : false;
+		}
+
+		if (!$allow_null)
+			$result &= $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' ALTER '.$field_name.' SET NOT NULL') ? true : false;
+
+		return $result;
+	}
+
+
+	function alter_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = null, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+
+		$result = $this->add_field($table_name, 'tmp_'.$field_name, $field_type, $allow_null, $default_value, $after_field, $no_prefix);
+		$result &= $this->query('UPDATE '.($no_prefix ? '' : $this->prefix).$table_name.' SET tmp_'.$field_name.' = '.$field_name) ? true : false;
+		$result &= $this->drop_field($table_name, $field_name, $no_prefix);
+		$result &= $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' RENAME COLUMN tmp_'.$field_name.' TO '.$field_name) ? true : false;
+
+		return $result;
+	}
+
+
+	function drop_field($table_name, $field_name, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		return $this->query('ALTER TABLE '.($no_prefix ? '' : $this->prefix).$table_name.' DROP '.$field_name) ? true : false;
+	}
+
+
+	function add_index($table_name, $index_name, $index_fields, $unique = false, $no_prefix = false)
+	{
+		if ($this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('CREATE '.($unique ? 'UNIQUE ' : '').'INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.' ON '.($no_prefix ? '' : $this->prefix).$table_name.'('.implode(',', $index_fields).')') ? true : false;
+	}
+
+
+	function drop_index($table_name, $index_name, $no_prefix = false)
+	{
+		if (!$this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('DROP INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name) ? true : false;
+	}
+
+	function truncate_table($table_name, $no_prefix = false)
+	{
+		return $this->query('DELETE FROM '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+}
diff --git a/include/dblayer/sqlite.php b/include/dblayer/sqlite.php
new file mode 100644
index 0000000..d337a8c
--- /dev/null
+++ b/include/dblayer/sqlite.php
@@ -0,0 +1,538 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure we have built in support for SQLite
+if (!function_exists('sqlite_open'))
+	exit('This PHP environment doesn\'t have SQLite support built in. SQLite support is required if you want to use a SQLite database to run this forum. Consult the PHP documentation for further assistance.');
+
+
+class DBLayer
+{
+	var $prefix;
+	var $link_id;
+	var $query_result;
+	var $in_transaction = 0;
+
+	var $saved_queries = array();
+	var $num_queries = 0;
+
+	var $error_no = false;
+	var $error_msg = 'Unknown';
+
+	var $datatype_transformations = array(
+		'/^SERIAL$/'															=>	'INTEGER',
+		'/^(TINY|SMALL|MEDIUM|BIG)?INT( )?(\\([0-9]+\\))?( )?(UNSIGNED)?$/i'	=>	'INTEGER',
+		'/^(TINY|MEDIUM|LONG)?TEXT$/i'											=>	'TEXT'
+	);
+
+
+	function DBLayer($db_host, $db_username, $db_password, $db_name, $db_prefix, $p_connect)
+	{
+		// Prepend $db_name with the path to the forum root directory
+		$db_name = PUN_ROOT.$db_name;
+
+		$this->prefix = $db_prefix;
+
+		if (!file_exists($db_name))
+		{
+			@touch($db_name);
+			@chmod($db_name, 0666);
+			if (!file_exists($db_name))
+				error('Unable to create new database \''.$db_name.'\'. Permission denied', __FILE__, __LINE__);
+		}
+
+		if (!is_readable($db_name))
+			error('Unable to open database \''.$db_name.'\' for reading. Permission denied', __FILE__, __LINE__);
+
+		if (!is_writable($db_name))
+			error('Unable to open database \''.$db_name.'\' for writing. Permission denied', __FILE__, __LINE__);
+
+		if ($p_connect)
+			$this->link_id = @sqlite_popen($db_name, 0666, $sqlite_error);
+		else
+			$this->link_id = @sqlite_open($db_name, 0666, $sqlite_error);
+
+		if (!$this->link_id)
+			error('Unable to open database \''.$db_name.'\'. SQLite reported: '.$sqlite_error, __FILE__, __LINE__);
+		else
+			return $this->link_id;
+	}
+
+
+	function start_transaction()
+	{
+		++$this->in_transaction;
+
+		return (@sqlite_query($this->link_id, 'BEGIN')) ? true : false;
+	}
+
+
+	function end_transaction()
+	{
+		--$this->in_transaction;
+
+		if (@sqlite_query($this->link_id, 'COMMIT'))
+			return true;
+		else
+		{
+			@sqlite_query($this->link_id, 'ROLLBACK');
+			return false;
+		}
+	}
+
+
+	function query($sql, $unbuffered = false)
+	{
+		if (defined('PUN_SHOW_QUERIES'))
+			$q_start = get_microtime();
+
+		if ($unbuffered)
+			$this->query_result = @sqlite_unbuffered_query($this->link_id, $sql);
+		else
+			$this->query_result = @sqlite_query($this->link_id, $sql);
+
+		if ($this->query_result)
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, sprintf('%.5f', get_microtime() - $q_start));
+
+			++$this->num_queries;
+
+			return $this->query_result;
+		}
+		else
+		{
+			if (defined('PUN_SHOW_QUERIES'))
+				$this->saved_queries[] = array($sql, 0);
+
+			$this->error_no = @sqlite_last_error($this->link_id);
+			$this->error_msg = @sqlite_error_string($this->error_no);
+
+			if ($this->in_transaction)
+				@sqlite_query($this->link_id, 'ROLLBACK');
+
+			--$this->in_transaction;
+
+			return false;
+		}
+	}
+
+
+	function result($query_id = 0, $row = 0, $col = 0)
+	{
+		if ($query_id)
+		{
+			if ($row !== 0 && @sqlite_seek($query_id, $row) === false)
+				return false;
+
+			$cur_row = @sqlite_current($query_id);
+			if ($cur_row === false)
+				return false;
+
+			return $cur_row[$col];
+		}
+		else
+			return false;
+	}
+
+
+	function fetch_assoc($query_id = 0)
+	{
+		if ($query_id)
+		{
+			$cur_row = @sqlite_fetch_array($query_id, SQLITE_ASSOC);
+			if ($cur_row)
+			{
+				// Horrible hack to get rid of table names and table aliases from the array keys
+				foreach ($cur_row as $key => $value)
+				{
+					$dot_spot = strpos($key, '.');
+					if ($dot_spot !== false)
+					{
+						unset($cur_row[$key]);
+						$key = substr($key, $dot_spot+1);
+						$cur_row[$key] = $value;
+					}
+				}
+			}
+
+			return $cur_row;
+		}
+		else
+			return false;
+	}
+
+
+	function fetch_row($query_id = 0)
+	{
+		return ($query_id) ? @sqlite_fetch_array($query_id, SQLITE_NUM) : false;
+	}
+
+
+	function num_rows($query_id = 0)
+	{
+		return ($query_id) ? @sqlite_num_rows($query_id) : false;
+	}
+
+
+	function affected_rows()
+	{
+		return ($this->query_result) ? @sqlite_changes($this->query_result) : false;
+	}
+
+
+	function insert_id()
+	{
+		return ($this->link_id) ? @sqlite_last_insert_rowid($this->link_id) : false;
+	}
+
+
+	function get_num_queries()
+	{
+		return $this->num_queries;
+	}
+
+
+	function get_saved_queries()
+	{
+		return $this->saved_queries;
+	}
+
+
+	function free_result($query_id = false)
+	{
+		return true;
+	}
+
+
+	function escape($str)
+	{
+		return is_array($str) ? '' : sqlite_escape_string($str);
+	}
+
+
+	function error()
+	{
+		$result['error_sql'] = @current(@end($this->saved_queries));
+		$result['error_no'] = $this->error_no;
+		$result['error_msg'] = $this->error_msg;
+
+		return $result;
+	}
+
+
+	function close()
+	{
+		if ($this->link_id)
+		{
+			if ($this->in_transaction)
+			{
+				if (defined('PUN_SHOW_QUERIES'))
+					$this->saved_queries[] = array('COMMIT', 0);
+
+				@sqlite_query($this->link_id, 'COMMIT');
+			}
+
+			return @sqlite_close($this->link_id);
+		}
+		else
+			return false;
+	}
+
+
+	function get_names()
+	{
+		return '';
+	}
+
+
+	function set_names($names)
+	{
+		return true;
+	}
+
+
+	function get_version()
+	{
+		return array(
+			'name'		=> 'SQLite',
+			'version'	=> sqlite_libversion()
+		);
+	}
+
+
+	function table_exists($table_name, $no_prefix = false)
+	{
+		$result = $this->query('SELECT 1 FROM sqlite_master WHERE name = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\' AND type=\'table\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function field_exists($table_name, $field_name, $no_prefix = false)
+	{
+		$result = $this->query('SELECT sql FROM sqlite_master WHERE name = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\' AND type=\'table\'');
+		if (!$this->num_rows($result))
+			return false;
+
+		return preg_match('/[\r\n]'.preg_quote($field_name).' /', $this->result($result));
+	}
+
+
+	function index_exists($table_name, $index_name, $no_prefix = false)
+	{
+		$result = $this->query('SELECT 1 FROM sqlite_master WHERE tbl_name = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\' AND name = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_'.$this->escape($index_name).'\' AND type=\'index\'');
+		return $this->num_rows($result) > 0;
+	}
+
+
+	function create_table($table_name, $schema, $no_prefix = false)
+	{
+		if ($this->table_exists($table_name, $no_prefix))
+			return true;
+
+		$query = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$table_name." (\n";
+
+		// Go through every schema element and add it to the query
+		foreach ($schema['FIELDS'] as $field_name => $field_data)
+		{
+			$field_data['datatype'] = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_data['datatype']);
+
+			$query .= $field_name.' '.$field_data['datatype'];
+
+			if (!$field_data['allow_null'])
+				$query .= ' NOT NULL';
+
+			if (isset($field_data['default']))
+				$query .= ' DEFAULT '.$field_data['default'];
+
+			$query .= ",\n";
+		}
+
+		// If we have a primary key, add it
+		if (isset($schema['PRIMARY KEY']))
+			$query .= 'PRIMARY KEY ('.implode(',', $schema['PRIMARY KEY']).'),'."\n";
+
+		// Add unique keys
+		if (isset($schema['UNIQUE KEYS']))
+		{
+			foreach ($schema['UNIQUE KEYS'] as $key_name => $key_fields)
+				$query .= 'UNIQUE ('.implode(',', $key_fields).'),'."\n";
+		}
+
+		// We remove the last two characters (a newline and a comma) and add on the ending
+		$query = substr($query, 0, strlen($query) - 2)."\n".')';
+
+		$result = $this->query($query) ? true : false;
+
+		// Add indexes
+		if (isset($schema['INDEXES']))
+		{
+			foreach ($schema['INDEXES'] as $index_name => $index_fields)
+				$result &= $this->add_index($table_name, $index_name, $index_fields, false, $no_prefix);
+		}
+
+		return $result;
+	}
+
+
+	function drop_table($table_name, $no_prefix = false)
+	{
+		if (!$this->table_exists($table_name, $no_prefix))
+			return true;
+
+		return $this->query('DROP TABLE '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+
+
+	function get_table_info($table_name, $no_prefix = false)
+	{
+		// Grab table info
+		$result = $this->query('SELECT sql FROM sqlite_master WHERE tbl_name = \''.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'\' ORDER BY type DESC') or error('Unable to fetch table information', __FILE__, __LINE__, $this->error());
+		$num_rows = $this->num_rows($result);
+
+		if ($num_rows == 0)
+			return;
+
+		$table = array();
+		$table['indices'] = array();
+		while ($cur_index = $this->fetch_assoc($result))
+		{
+			if (empty($cur_index['sql']))
+				continue;
+
+			if (!isset($table['sql']))
+				$table['sql'] = $cur_index['sql'];
+			else
+				$table['indices'][] = $cur_index['sql'];
+		}
+
+		// Work out the columns in the table currently
+		$table_lines = explode("\n", $table['sql']);
+		$table['columns'] = array();
+		foreach ($table_lines as $table_line)
+		{
+			$table_line = pun_trim($table_line);
+			if (substr($table_line, 0, 12) == 'CREATE TABLE')
+				continue;
+			else if (substr($table_line, 0, 11) == 'PRIMARY KEY')
+				$table['primary_key'] = $table_line;
+			else if (substr($table_line, 0, 6) == 'UNIQUE')
+				$table['unique'] = $table_line;
+			else if (substr($table_line, 0, strpos($table_line, ' ')) != '')
+				$table['columns'][substr($table_line, 0, strpos($table_line, ' '))] = pun_trim(substr($table_line, strpos($table_line, ' ')));
+		}
+
+		return $table;
+	}
+
+
+	function add_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = 0, $no_prefix = false)
+	{
+		if ($this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$table = $this->get_table_info($table_name, $no_prefix);
+
+		// Create temp table
+		$now = time();
+		$tmptable = str_replace('CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' (', 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now.' (', $table['sql']);
+		$result = $this->query($tmptable) ? true : false;
+		$result &= $this->query('INSERT INTO '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now.' SELECT * FROM '.($no_prefix ? '' : $this->prefix).$this->escape($table_name)) ? true : false;
+
+		// Create new table sql
+		$field_type = preg_replace(array_keys($this->datatype_transformations), array_values($this->datatype_transformations), $field_type);
+		$query = $field_type;
+		if (!$allow_null)
+			$query .= ' NOT NULL';
+		if ($default_value === null || $default_value === '')
+			$default_value = '\'\'';
+
+		$query .= ' DEFAULT '.$default_value;
+
+		$old_columns = array_keys($table['columns']);
+		array_insert($table['columns'], $after_field, $query.',', $field_name);
+
+		$new_table = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' (';
+
+		foreach ($table['columns'] as $cur_column => $column_details)
+			$new_table .= "\n".$cur_column.' '.$column_details;
+
+		if (isset($table['unique']))
+			$new_table .= "\n".$table['unique'].',';
+
+		if (isset($table['primary_key']))
+			$new_table .= "\n".$table['primary_key'];
+
+		$new_table = trim($new_table, ',')."\n".');';
+
+		// Drop old table
+		$result &= $this->drop_table(($no_prefix ? '' : $this->prefix).$this->escape($table_name));
+
+		// Create new table
+		$result &= $this->query($new_table) ? true : false;
+
+		// Recreate indexes
+		if (!empty($table['indices']))
+		{
+			foreach ($table['indices'] as $cur_index)
+				$result &= $this->query($cur_index) ? true : false;
+		}
+
+		// Copy content back
+		$result &= $this->query('INSERT INTO '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' ('.implode(', ', $old_columns).') SELECT * FROM '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now) ? true : false;
+
+		// Drop temp table
+		$result &= $this->drop_table(($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now);
+
+		return $result;
+	}
+
+
+	function alter_field($table_name, $field_name, $field_type, $allow_null, $default_value = null, $after_field = 0, $no_prefix = false)
+	{
+		// Unneeded for SQLite
+		return true;
+	}
+
+
+	function drop_field($table_name, $field_name, $no_prefix = false)
+	{
+		if (!$this->field_exists($table_name, $field_name, $no_prefix))
+			return true;
+
+		$table = $this->get_table_info($table_name, $no_prefix);
+
+		// Create temp table
+		$now = time();
+		$tmptable = str_replace('CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' (', 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now.' (', $table['sql']);
+		$result = $this->query($tmptable) ? true : false;
+		$result &= $this->query('INSERT INTO '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now.' SELECT * FROM '.($no_prefix ? '' : $this->prefix).$this->escape($table_name)) ? true : false;
+
+		// Work out the columns we need to keep and the sql for the new table
+		unset($table['columns'][$field_name]);
+		$new_columns = array_keys($table['columns']);
+
+		$new_table = 'CREATE TABLE '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' (';
+
+		foreach ($table['columns'] as $cur_column => $column_details)
+			$new_table .= "\n".$cur_column.' '.$column_details;
+
+		if (isset($table['unique']))
+			$new_table .= "\n".$table['unique'].',';
+
+		if (isset($table['primary_key']))
+			$new_table .= "\n".$table['primary_key'];
+
+		$new_table = trim($new_table, ',')."\n".');';
+
+		// Drop old table
+		$result &= $this->drop_table(($no_prefix ? '' : $this->prefix).$this->escape($table_name));
+
+		// Create new table
+		$result &= $this->query($new_table) ? true : false;
+
+		// Recreate indexes
+		if (!empty($table['indices']))
+		{
+			foreach ($table['indices'] as $cur_index)
+				if (!preg_match('%\('.preg_quote($field_name, '%').'\)%', $cur_index))
+					$result &= $this->query($cur_index) ? true : false;
+		}
+
+		// Copy content back
+		$result &= $this->query('INSERT INTO '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).' SELECT '.implode(', ', $new_columns).' FROM '.($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now) ? true : false;
+
+		// Drop temp table
+		$result &= $this->drop_table(($no_prefix ? '' : $this->prefix).$this->escape($table_name).'_t'.$now);
+
+		return $result;
+	}
+
+
+	function add_index($table_name, $index_name, $index_fields, $unique = false, $no_prefix = false)
+	{
+		if ($this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('CREATE '.($unique ? 'UNIQUE ' : '').'INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name.' ON '.($no_prefix ? '' : $this->prefix).$table_name.'('.implode(',', $index_fields).')') ? true : false;
+	}
+
+
+	function drop_index($table_name, $index_name, $no_prefix = false)
+	{
+		if (!$this->index_exists($table_name, $index_name, $no_prefix))
+			return true;
+
+		return $this->query('DROP INDEX '.($no_prefix ? '' : $this->prefix).$table_name.'_'.$index_name) ? true : false;
+	}
+
+	function truncate_table($table_name, $no_prefix = false)
+	{
+		return $this->query('DELETE FROM '.($no_prefix ? '' : $this->prefix).$table_name) ? true : false;
+	}
+}
diff --git a/include/email.php b/include/email.php
new file mode 100644
index 0000000..86f0660
--- /dev/null
+++ b/include/email.php
@@ -0,0 +1,186 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure no one attempts to run this script "directly"
+if (!defined('PUN'))
+	exit;
+
+
+//
+// Validate an email address
+//
+function is_valid_email($email)
+{
+	if (strlen($email) > 80)
+		return false;
+
+	return preg_match('/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|("[^"]+"))@((\[\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\])|(([a-zA-Z\d\-]+\.)+[a-zA-Z]{2,}))$/', $email);
+}
+
+
+//
+// Check if $email is banned
+//
+function is_banned_email($email)
+{
+	global $pun_bans;
+
+	foreach ($pun_bans as $cur_ban)
+	{
+		if ($cur_ban['email'] != '' &&
+			($email == $cur_ban['email'] ||
+			(strpos($cur_ban['email'], '@') === false && stristr($email, '@'.$cur_ban['email']))))
+			return true;
+	}
+
+	return false;
+}
+
+
+//
+// Wrapper for PHP's mail()
+//
+function pun_mail($to, $subject, $message, $reply_to_email = '', $reply_to_name = '')
+{
+	global $pun_config, $lang_common;
+
+	// Default sender/return address
+	$from_name = str_replace('"', '', $pun_config['o_board_title'].' '.$lang_common['Mailer']);
+	$from_email = $pun_config['o_webmaster_email'];
+
+	// Do a little spring cleaning
+	$to = pun_trim(preg_replace('#[\n\r]+#s', '', $to));
+	$subject = pun_trim(preg_replace('#[\n\r]+#s', '', $subject));
+	$from_email = pun_trim(preg_replace('#[\n\r:]+#s', '', $from_email));
+	$from_name = pun_trim(preg_replace('#[\n\r:]+#s', '', str_replace('"', '', $from_name)));
+	$reply_to_email = pun_trim(preg_replace('#[\n\r:]+#s', '', $reply_to_email));
+	$reply_to_name = pun_trim(preg_replace('#[\n\r:]+#s', '', str_replace('"', '', $reply_to_name)));
+
+	// Set up some headers to take advantage of UTF-8
+	$from = "=?UTF-8?B?".base64_encode($from_name)."?=".' <'.$from_email.'>';
+	$subject = "=?UTF-8?B?".base64_encode($subject)."?=";
+
+	$headers = 'From: '.$from."\r\n".'Date: '.gmdate('r')."\r\n".'MIME-Version: 1.0'."\r\n".'Content-transfer-encoding: 8bit'."\r\n".'Content-type: text/plain; charset=utf-8'."\r\n".'X-Mailer: FluxBB Mailer';
+
+	// If we specified a reply-to email, we deal with it here
+	if (!empty($reply_to_email))
+	{
+		$reply_to = "=?UTF-8?B?".base64_encode($reply_to_name)."?=".' <'.$reply_to_email.'>';
+
+		$headers .= "\r\n".'Reply-To: '.$reply_to;
+	}
+
+	// Make sure all linebreaks are CRLF in message (and strip out any NULL bytes)
+	$message = str_replace(array("\n", "\0"), array("\r\n", ''), pun_linebreaks($message));
+
+	if ($pun_config['o_smtp_host'] != '')
+		smtp_mail($to, $subject, $message, $headers);
+	else
+	{
+		// Change the linebreaks used in the headers according to OS
+		if (strtoupper(substr(PHP_OS, 0, 3)) == 'MAC')
+			$headers = str_replace("\r\n", "\r", $headers);
+		else if (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN')
+			$headers = str_replace("\r\n", "\n", $headers);
+
+		mail($to, $subject, $message, $headers);
+	}
+}
+
+
+//
+// This function was originally a part of the phpBB Group forum software phpBB2 (http://www.phpbb.com)
+// They deserve all the credit for writing it. I made small modifications for it to suit PunBB and it's coding standards
+//
+function server_parse($socket, $expected_response)
+{
+	$server_response = '';
+	while (substr($server_response, 3, 1) != ' ')
+	{
+		if (!($server_response = fgets($socket, 256)))
+			error('Couldn\'t get mail server response codes. Please contact the forum administrator.', __FILE__, __LINE__);
+	}
+
+	if (!(substr($server_response, 0, 3) == $expected_response))
+		error('Unable to send email. Please contact the forum administrator with the following error message reported by the SMTP server: "'.$server_response.'"', __FILE__, __LINE__);
+}
+
+
+//
+// This function was originally a part of the phpBB Group forum software phpBB2 (http://www.phpbb.com)
+// They deserve all the credit for writing it. I made small modifications for it to suit PunBB and it's coding standards.
+//
+function smtp_mail($to, $subject, $message, $headers = '')
+{
+	global $pun_config;
+
+	$recipients = explode(',', $to);
+
+	// Sanitize the message
+	$message = str_replace("\r\n.", "\r\n..", $message);
+	$message = (substr($message, 0, 1) == '.' ? '.'.$message : $message);
+
+	// Are we using port 25 or a custom port?
+	if (strpos($pun_config['o_smtp_host'], ':') !== false)
+		list($smtp_host, $smtp_port) = explode(':', $pun_config['o_smtp_host']);
+	else
+	{
+		$smtp_host = $pun_config['o_smtp_host'];
+		$smtp_port = 25;
+	}
+
+	if ($pun_config['o_smtp_ssl'] == '1')
+		$smtp_host = 'ssl://'.$smtp_host;
+
+	if (!($socket = fsockopen($smtp_host, $smtp_port, $errno, $errstr, 15)))
+		error('Could not connect to smtp host "'.$pun_config['o_smtp_host'].'" ('.$errno.') ('.$errstr.')', __FILE__, __LINE__);
+
+	server_parse($socket, '220');
+
+	if ($pun_config['o_smtp_user'] != '' && $pun_config['o_smtp_pass'] != '')
+	{
+		fwrite($socket, 'EHLO '.$smtp_host."\r\n");
+		server_parse($socket, '250');
+
+		fwrite($socket, 'AUTH LOGIN'."\r\n");
+		server_parse($socket, '334');
+
+		fwrite($socket, base64_encode($pun_config['o_smtp_user'])."\r\n");
+		server_parse($socket, '334');
+
+		fwrite($socket, base64_encode($pun_config['o_smtp_pass'])."\r\n");
+		server_parse($socket, '235');
+	}
+	else
+	{
+		fwrite($socket, 'HELO '.$smtp_host."\r\n");
+		server_parse($socket, '250');
+	}
+
+	fwrite($socket, 'MAIL FROM: <'.$pun_config['o_webmaster_email'].'>'."\r\n");
+	server_parse($socket, '250');
+
+	foreach ($recipients as $email)
+	{
+		fwrite($socket, 'RCPT TO: <'.$email.'>'."\r\n");
+		server_parse($socket, '250');
+	}
+
+	fwrite($socket, 'DATA'."\r\n");
+	server_parse($socket, '354');
+
+	fwrite($socket, 'Subject: '.$subject."\r\n".'To: <'.implode('>, <', $recipients).'>'."\r\n".$headers."\r\n\r\n".$message."\r\n");
+
+	fwrite($socket, '.'."\r\n");
+	server_parse($socket, '250');
+
+	fwrite($socket, 'QUIT'."\r\n");
+	fclose($socket);
+
+	return true;
+}
diff --git a/include/functions.php b/include/functions.php
new file mode 100644
index 0000000..fb7f281
--- /dev/null
+++ b/include/functions.php
@@ -0,0 +1,1746 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+
+//
+// Return current timestamp (with microseconds) as a float
+//
+function get_microtime()
+{
+	list($usec, $sec) = explode(' ', microtime());
+	return ((float)$usec + (float)$sec);
+}
+
+//
+// Cookie stuff!
+//
+function check_cookie(&$pun_user)
+{
+	global $db, $db_type, $pun_config, $cookie_name, $cookie_seed;
+
+	$now = time();
+
+	// We assume it's a guest
+	$cookie = array('user_id' => 1, 'password_hash' => 'Guest', 'expiration_time' => 0);
+
+	// If a cookie is set, we get the user_id and password hash from it
+	if (isset($_COOKIE[$cookie_name]) && preg_match('/a:3:{i:0;s:\d+:"(\d+)";i:1;s:\d+:"([0-9a-f]+)";i:2;i:(\d+);}/', $_COOKIE[$cookie_name], $matches))
+		list(, $cookie['user_id'], $cookie['password_hash'], $cookie['expiration_time']) = $matches;
+
+	if ($cookie['user_id'] > 1)
+	{
+		// Check if there's a user with the user ID and password hash from the cookie
+		$result = $db->query('SELECT u.*, g.*, o.logged, o.idle FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON u.group_id=g.g_id LEFT JOIN '.$db->prefix.'online AS o ON o.user_id=u.id WHERE u.id='.intval($cookie['user_id'])) or error('Unable to fetch user information', __FILE__, __LINE__, $db->error());
+		$pun_user = $db->fetch_assoc($result);
+
+		// If user authorisation failed
+		if (!isset($pun_user['id']) || md5($cookie_seed.$pun_user['password']) !== $cookie['password_hash'])
+		{
+			$expire = $now + 31536000; // The cookie expires after a year
+			pun_setcookie(1, md5(uniqid(rand(), true)), $expire);
+			set_default_user();
+
+			return;
+		}
+
+		// Send a new, updated cookie with a new expiration timestamp
+		$expire = (intval($cookie['expiration_time']) > $now + $pun_config['o_timeout_visit']) ? $now + 1209600 : $now + $pun_config['o_timeout_visit'];
+		pun_setcookie($pun_user['id'], $pun_user['password'], $expire);
+
+		// Set a default language if the user selected language no longer exists
+		if (!file_exists(PUN_ROOT.'lang/'.$pun_user['language']))
+			$pun_user['language'] = $pun_config['o_default_lang'];
+
+		// Set a default style if the user selected style no longer exists
+		if (!file_exists(PUN_ROOT.'style/'.$pun_user['style'].'.css'))
+			$pun_user['style'] = $pun_config['o_default_style'];
+
+		if (!$pun_user['disp_topics'])
+			$pun_user['disp_topics'] = $pun_config['o_disp_topics_default'];
+		if (!$pun_user['disp_posts'])
+			$pun_user['disp_posts'] = $pun_config['o_disp_posts_default'];
+
+		// Define this if you want this visit to affect the online list and the users last visit data
+		if (!defined('PUN_QUIET_VISIT'))
+		{
+			// Update the online list
+			if (!$pun_user['logged'])
+			{
+				$pun_user['logged'] = $now;
+
+				// With MySQL/MySQLi/SQLite, REPLACE INTO avoids a user having two rows in the online table
+				switch ($db_type)
+				{
+					case 'mysql':
+					case 'mysqli':
+					case 'mysql_innodb':
+					case 'mysqli_innodb':
+					case 'sqlite':
+						$db->query('REPLACE INTO '.$db->prefix.'online (user_id, ident, logged) VALUES('.$pun_user['id'].', \''.$db->escape($pun_user['username']).'\', '.$pun_user['logged'].')') or error('Unable to insert into online list', __FILE__, __LINE__, $db->error());
+						break;
+
+					default:
+						$db->query('INSERT INTO '.$db->prefix.'online (user_id, ident, logged) SELECT '.$pun_user['id'].', \''.$db->escape($pun_user['username']).'\', '.$pun_user['logged'].' WHERE NOT EXISTS (SELECT 1 FROM '.$db->prefix.'online WHERE user_id='.$pun_user['id'].')') or error('Unable to insert into online list', __FILE__, __LINE__, $db->error());
+						break;
+				}
+
+				// Reset tracked topics
+				set_tracked_topics(null);
+			}
+			else
+			{
+				// Special case: We've timed out, but no other user has browsed the forums since we timed out
+				if ($pun_user['logged'] < ($now-$pun_config['o_timeout_visit']))
+				{
+					$db->query('UPDATE '.$db->prefix.'users SET last_visit='.$pun_user['logged'].' WHERE id='.$pun_user['id']) or error('Unable to update user visit data', __FILE__, __LINE__, $db->error());
+					$pun_user['last_visit'] = $pun_user['logged'];
+				}
+
+				$idle_sql = ($pun_user['idle'] == '1') ? ', idle=0' : '';
+				$db->query('UPDATE '.$db->prefix.'online SET logged='.$now.$idle_sql.' WHERE user_id='.$pun_user['id']) or error('Unable to update online list', __FILE__, __LINE__, $db->error());
+
+				// Update tracked topics with the current expire time
+				if (isset($_COOKIE[$cookie_name.'_track']))
+					forum_setcookie($cookie_name.'_track', $_COOKIE[$cookie_name.'_track'], $now + $pun_config['o_timeout_visit']);
+			}
+		}
+		else
+		{
+			if (!$pun_user['logged'])
+				$pun_user['logged'] = $pun_user['last_visit'];
+		}
+
+		$pun_user['is_guest'] = false;
+		$pun_user['is_admmod'] = $pun_user['g_id'] == PUN_ADMIN || $pun_user['g_moderator'] == '1';
+	}
+	else
+		set_default_user();
+}
+
+
+//
+// Converts the CDATA end sequence ]]> into ]]>
+//
+function escape_cdata($str)
+{
+	return str_replace(']]>', ']]>', $str);
+}
+
+
+//
+// Authenticates the provided username and password against the user database
+// $user can be either a user ID (integer) or a username (string)
+// $password can be either a plaintext password or a password hash including salt ($password_is_hash must be set accordingly)
+//
+function authenticate_user($user, $password, $password_is_hash = false)
+{
+	global $db, $pun_user;
+
+	// Check if there's a user matching $user and $password
+	$result = $db->query('SELECT u.*, g.*, o.logged, o.idle FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON g.g_id=u.group_id LEFT JOIN '.$db->prefix.'online AS o ON o.user_id=u.id WHERE '.(is_int($user) ? 'u.id='.intval($user) : 'u.username=\''.$db->escape($user).'\'')) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+	$pun_user = $db->fetch_assoc($result);
+
+	if (!isset($pun_user['id']) ||
+		($password_is_hash && $password != $pun_user['password']) ||
+		(!$password_is_hash && pun_hash($password) != $pun_user['password']))
+		set_default_user();
+	else
+		$pun_user['is_guest'] = false;
+}
+
+
+//
+// Try to determine the current URL
+//
+function get_current_url($max_length = 0)
+{
+	$protocol = (!isset($_SERVER['HTTPS']) || strtolower($_SERVER['HTTPS']) == 'off') ? 'http://' : 'https://';
+	$port = (isset($_SERVER['SERVER_PORT']) && (($_SERVER['SERVER_PORT'] != '80' && $protocol == 'http://') || ($_SERVER['SERVER_PORT'] != '443' && $protocol == 'https://')) && strpos($_SERVER['HTTP_HOST'], ':') === false) ? ':'.$_SERVER['SERVER_PORT'] : '';
+
+	$url = urldecode($protocol.$_SERVER['HTTP_HOST'].$port.$_SERVER['REQUEST_URI']);
+
+	if (strlen($url) <= $max_length || $max_length == 0)
+		return $url;
+
+	// We can't find a short enough url
+	return null;
+}
+
+
+//
+// Fill $pun_user with default values (for guests)
+//
+function set_default_user()
+{
+	global $db, $db_type, $pun_user, $pun_config;
+
+	$remote_addr = get_remote_address();
+
+	// Fetch guest user
+	$result = $db->query('SELECT u.*, g.*, o.logged, o.last_post, o.last_search FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON u.group_id=g.g_id LEFT JOIN '.$db->prefix.'online AS o ON o.ident=\''.$remote_addr.'\' WHERE u.id=1') or error('Unable to fetch guest information', __FILE__, __LINE__, $db->error());
+	if (!$db->num_rows($result))
+		exit('Unable to fetch guest information. The table \''.$db->prefix.'users\' must contain an entry with id = 1 that represents anonymous users.');
+
+	$pun_user = $db->fetch_assoc($result);
+
+	// Update online list
+	if (!$pun_user['logged'])
+	{
+		$pun_user['logged'] = time();
+
+		// With MySQL/MySQLi/SQLite, REPLACE INTO avoids a user having two rows in the online table
+		switch ($db_type)
+		{
+			case 'mysql':
+			case 'mysqli':
+			case 'mysql_innodb':
+			case 'mysqli_innodb':
+			case 'sqlite':
+				$db->query('REPLACE INTO '.$db->prefix.'online (user_id, ident, logged) VALUES(1, \''.$db->escape($remote_addr).'\', '.$pun_user['logged'].')') or error('Unable to insert into online list', __FILE__, __LINE__, $db->error());
+				break;
+
+			default:
+				$db->query('INSERT INTO '.$db->prefix.'online (user_id, ident, logged) SELECT 1, \''.$db->escape($remote_addr).'\', '.$pun_user['logged'].' WHERE NOT EXISTS (SELECT 1 FROM '.$db->prefix.'online WHERE ident=\''.$db->escape($remote_addr).'\')') or error('Unable to insert into online list', __FILE__, __LINE__, $db->error());
+				break;
+		}
+	}
+	else
+		$db->query('UPDATE '.$db->prefix.'online SET logged='.time().' WHERE ident=\''.$db->escape($remote_addr).'\'') or error('Unable to update online list', __FILE__, __LINE__, $db->error());
+
+	$pun_user['disp_topics'] = $pun_config['o_disp_topics_default'];
+	$pun_user['disp_posts'] = $pun_config['o_disp_posts_default'];
+	$pun_user['timezone'] = $pun_config['o_default_timezone'];
+	$pun_user['dst'] = $pun_config['o_default_dst'];
+	$pun_user['language'] = $pun_config['o_default_lang'];
+	$pun_user['style'] = $pun_config['o_default_style'];
+	$pun_user['is_guest'] = true;
+	$pun_user['is_admmod'] = false;
+}
+
+
+//
+// Set a cookie, FluxBB style!
+// Wrapper for forum_setcookie
+//
+function pun_setcookie($user_id, $password_hash, $expire)
+{
+	global $cookie_name, $cookie_seed;
+
+	forum_setcookie($cookie_name, serialize(array($user_id, md5($cookie_seed.$password_hash), $expire)), $expire);
+}
+
+
+//
+// Set a cookie, FluxBB style!
+//
+function forum_setcookie($name, $value, $expire)
+{
+	global $cookie_path, $cookie_domain, $cookie_secure;
+
+	// Enable sending of a P3P header
+	header('P3P: CP="CUR ADM"');
+
+	if (version_compare(PHP_VERSION, '5.2.0', '>='))
+		setcookie($name, $value, $expire, $cookie_path, $cookie_domain, $cookie_secure, true);
+	else
+		setcookie($name, $value, $expire, $cookie_path.'; HttpOnly', $cookie_domain, $cookie_secure);
+}
+
+
+//
+// Check whether the connecting user is banned (and delete any expired bans while we're at it)
+//
+function check_bans()
+{
+	global $db, $pun_config, $lang_common, $pun_user, $pun_bans;
+
+	// Admins aren't affected
+	if ($pun_user['g_id'] == PUN_ADMIN || !$pun_bans)
+		return;
+
+	// Add a dot or a colon (depending on IPv4/IPv6) at the end of the IP address to prevent banned address
+	// 192.168.0.5 from matching e.g. 192.168.0.50
+	$user_ip = get_remote_address();
+	$user_ip .= (strpos($user_ip, '.') !== false) ? '.' : ':';
+
+	$bans_altered = false;
+	$is_banned = false;
+
+	foreach ($pun_bans as $cur_ban)
+	{
+		// Has this ban expired?
+		if ($cur_ban['expire'] != '' && $cur_ban['expire'] <= time())
+		{
+			$db->query('DELETE FROM '.$db->prefix.'bans WHERE id='.$cur_ban['id']) or error('Unable to delete expired ban', __FILE__, __LINE__, $db->error());
+			$bans_altered = true;
+			continue;
+		}
+
+		if ($cur_ban['username'] != '' && utf8_strtolower($pun_user['username']) == utf8_strtolower($cur_ban['username']))
+			$is_banned = true;
+
+		if ($cur_ban['ip'] != '')
+		{
+			$cur_ban_ips = explode(' ', $cur_ban['ip']);
+
+			$num_ips = count($cur_ban_ips);
+			for ($i = 0; $i < $num_ips; ++$i)
+			{
+				// Add the proper ending to the ban
+				if (strpos($user_ip, '.') !== false)
+					$cur_ban_ips[$i] = $cur_ban_ips[$i].'.';
+				else
+					$cur_ban_ips[$i] = $cur_ban_ips[$i].':';
+
+				if (substr($user_ip, 0, strlen($cur_ban_ips[$i])) == $cur_ban_ips[$i])
+				{
+					$is_banned = true;
+					break;
+				}
+			}
+		}
+
+		if ($is_banned)
+		{
+			$db->query('DELETE FROM '.$db->prefix.'online WHERE ident=\''.$db->escape($pun_user['username']).'\'') or error('Unable to delete from online list', __FILE__, __LINE__, $db->error());
+			message($lang_common['Ban message'].' '.(($cur_ban['expire'] != '') ? $lang_common['Ban message 2'].' '.strtolower(format_time($cur_ban['expire'], true)).'. ' : '').(($cur_ban['message'] != '') ? $lang_common['Ban message 3'].'<br /><br /><strong>'.pun_htmlspecialchars($cur_ban['message']).'</strong><br /><br />' : '<br /><br />').$lang_common['Ban message 4'].' <a href="mailto:'.$pun_config['o_admin_email'].'">'.$pun_config['o_admin_email'].'</a>.', true);
+		}
+	}
+
+	// If we removed any expired bans during our run-through, we need to regenerate the bans cache
+	if ($bans_altered)
+	{
+		if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+			require PUN_ROOT.'include/cache.php';
+
+		generate_bans_cache();
+	}
+}
+
+
+//
+// Check username
+//
+function check_username($username, $exclude_id = null)
+{
+	global $db, $pun_config, $errors, $lang_prof_reg, $lang_register, $lang_common, $pun_bans;
+
+	// Convert multiple whitespace characters into one (to prevent people from registering with indistinguishable usernames)
+	$username = preg_replace('#\s+#s', ' ', $username);
+
+	// Validate username
+	if (pun_strlen($username) < 2)
+		$errors[] = $lang_prof_reg['Username too short'];
+	else if (pun_strlen($username) > 25) // This usually doesn't happen since the form element only accepts 25 characters
+		$errors[] = $lang_prof_reg['Username too long'];
+	else if (!strcasecmp($username, 'Guest') || !strcasecmp($username, $lang_common['Guest']))
+		$errors[] = $lang_prof_reg['Username guest'];
+	else if (preg_match('/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/', $username) || preg_match('/((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|( [...]
+		$errors[] = $lang_prof_reg['Username IP'];
+	else if ((strpos($username, '[') !== false || strpos($username, ']') !== false) && strpos($username, '\'') !== false && strpos($username, '"') !== false)
+		$errors[] = $lang_prof_reg['Username reserved chars'];
+	else if (preg_match('/(?:\[\/?(?:b|u|s|ins|del|em|i|h|colou?r|quote|code|img|url|email|list|\*)\]|\[(?:img|url|quote|list)=)/i', $username))
+		$errors[] = $lang_prof_reg['Username BBCode'];
+
+	// Check username for any censored words
+	if ($pun_config['o_censoring'] == '1' && censor_words($username) != $username)
+		$errors[] = $lang_register['Username censor'];
+
+	// Check that the username (or a too similar username) is not already registered
+	$query = ($exclude_id) ? ' AND id!='.$exclude_id : '';
+
+	$result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE (UPPER(username)=UPPER(\''.$db->escape($username).'\') OR UPPER(username)=UPPER(\''.$db->escape(preg_replace('/[^\w]/', '', $username)).'\')) AND id>1'.$query) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+
+	if ($db->num_rows($result))
+	{
+		$busy = $db->result($result);
+		$errors[] = $lang_register['Username dupe 1'].' '.pun_htmlspecialchars($busy).'. '.$lang_register['Username dupe 2'];
+	}
+
+	// Check username for any banned usernames
+	foreach ($pun_bans as $cur_ban)
+	{
+		if ($cur_ban['username'] != '' && utf8_strtolower($username) == utf8_strtolower($cur_ban['username']))
+		{
+			$errors[] = $lang_prof_reg['Banned username'];
+			break;
+		}
+	}
+}
+
+
+//
+// Update "Users online"
+//
+function update_users_online()
+{
+	global $db, $pun_config;
+
+	$now = time();
+
+	// Fetch all online list entries that are older than "o_timeout_online"
+	$result = $db->query('SELECT user_id, ident, logged, idle FROM '.$db->prefix.'online WHERE logged<'.($now-$pun_config['o_timeout_online'])) or error('Unable to fetch old entries from online list', __FILE__, __LINE__, $db->error());
+	while ($cur_user = $db->fetch_assoc($result))
+	{
+		// If the entry is a guest, delete it
+		if ($cur_user['user_id'] == '1')
+			$db->query('DELETE FROM '.$db->prefix.'online WHERE ident=\''.$db->escape($cur_user['ident']).'\'') or error('Unable to delete from online list', __FILE__, __LINE__, $db->error());
+		else
+		{
+			// If the entry is older than "o_timeout_visit", update last_visit for the user in question, then delete him/her from the online list
+			if ($cur_user['logged'] < ($now-$pun_config['o_timeout_visit']))
+			{
+				$db->query('UPDATE '.$db->prefix.'users SET last_visit='.$cur_user['logged'].' WHERE id='.$cur_user['user_id']) or error('Unable to update user visit data', __FILE__, __LINE__, $db->error());
+				$db->query('DELETE FROM '.$db->prefix.'online WHERE user_id='.$cur_user['user_id']) or error('Unable to delete from online list', __FILE__, __LINE__, $db->error());
+			}
+			else if ($cur_user['idle'] == '0')
+				$db->query('UPDATE '.$db->prefix.'online SET idle=1 WHERE user_id='.$cur_user['user_id']) or error('Unable to insert into online list', __FILE__, __LINE__, $db->error());
+		}
+	}
+}
+
+
+//
+// Generate the "navigator" that appears at the top of every page
+//
+function generate_navlinks()
+{
+	global $pun_config, $lang_common, $pun_user;
+
+	// Index and Userlist should always be displayed
+	$links[] = '<li id="navindex"'.((PUN_ACTIVE_PAGE == 'index') ? ' class="isactive"' : '').'><a href="index.php">'.$lang_common['Index'].'</a></li>';
+
+	if ($pun_user['g_read_board'] == '1' && $pun_user['g_view_users'] == '1')
+		$links[] = '<li id="navuserlist"'.((PUN_ACTIVE_PAGE == 'userlist') ? ' class="isactive"' : '').'><a href="userlist.php">'.$lang_common['User list'].'</a></li>';
+
+	if ($pun_config['o_rules'] == '1' && (!$pun_user['is_guest'] || $pun_user['g_read_board'] == '1' || $pun_config['o_regs_allow'] == '1'))
+		$links[] = '<li id="navrules"'.((PUN_ACTIVE_PAGE == 'rules') ? ' class="isactive"' : '').'><a href="misc.php?action=rules">'.$lang_common['Rules'].'</a></li>';
+
+	if ($pun_user['is_guest'])
+	{
+		if ($pun_user['g_read_board'] == '1' && $pun_user['g_search'] == '1')
+			$links[] = '<li id="navsearch"'.((PUN_ACTIVE_PAGE == 'search') ? ' class="isactive"' : '').'><a href="search.php">'.$lang_common['Search'].'</a></li>';
+
+		$links[] = '<li id="navregister"'.((PUN_ACTIVE_PAGE == 'register') ? ' class="isactive"' : '').'><a href="register.php">'.$lang_common['Register'].'</a></li>';
+		$links[] = '<li id="navlogin"'.((PUN_ACTIVE_PAGE == 'login') ? ' class="isactive"' : '').'><a href="login.php">'.$lang_common['Login'].'</a></li>';
+	}
+	else
+	{
+		if (!$pun_user['is_admmod'])
+		{
+			if ($pun_user['g_read_board'] == '1' && $pun_user['g_search'] == '1')
+				$links[] = '<li id="navsearch"'.((PUN_ACTIVE_PAGE == 'search') ? ' class="isactive"' : '').'><a href="search.php">'.$lang_common['Search'].'</a></li>';
+
+			$links[] = '<li id="navprofile"'.((PUN_ACTIVE_PAGE == 'profile') ? ' class="isactive"' : '').'><a href="profile.php?id='.$pun_user['id'].'">'.$lang_common['Profile'].'</a></li>';
+			$links[] = '<li id="navlogout"><a href="login.php?action=out&id='.$pun_user['id'].'&csrf_token='.pun_hash($pun_user['id'].pun_hash(get_remote_address())).'">'.$lang_common['Logout'].'</a></li>';
+		}
+		else
+		{
+			$links[] = '<li id="navsearch"'.((PUN_ACTIVE_PAGE == 'search') ? ' class="isactive"' : '').'><a href="search.php">'.$lang_common['Search'].'</a></li>';
+			$links[] = '<li id="navprofile"'.((PUN_ACTIVE_PAGE == 'profile') ? ' class="isactive"' : '').'><a href="profile.php?id='.$pun_user['id'].'">'.$lang_common['Profile'].'</a></li>';
+			$links[] = '<li id="navadmin"'.((PUN_ACTIVE_PAGE == 'admin') ? ' class="isactive"' : '').'><a href="admin_index.php">'.$lang_common['Admin'].'</a></li>';
+			$links[] = '<li id="navlogout"><a href="login.php?action=out&id='.$pun_user['id'].'&csrf_token='.pun_hash($pun_user['id'].pun_hash(get_remote_address())).'">'.$lang_common['Logout'].'</a></li>';
+		}
+	}
+
+	// Are there any additional navlinks we should insert into the array before imploding it?
+	if ($pun_config['o_additional_navlinks'] != '')
+	{
+		if (preg_match_all('#([0-9]+)\s*=\s*(.*?)\n#s', $pun_config['o_additional_navlinks']."\n", $extra_links))
+		{
+			// Insert any additional links into the $links array (at the correct index)
+			$num_links = count($extra_links[1]);
+			for ($i = 0; $i < $num_links; ++$i)
+				array_splice($links, $extra_links[1][$i], 0, array('<li id="navextra'.($i + 1).'">'.$extra_links[2][$i].'</li>'));
+		}
+	}
+
+	return '<ul>'."\n\t\t\t\t".implode("\n\t\t\t\t", $links)."\n\t\t\t".'</ul>';
+}
+
+
+//
+// Display the profile navigation menu
+//
+function generate_profile_menu($page = '')
+{
+	global $lang_profile, $pun_config, $pun_user, $id;
+
+?>
+<div id="profile" class="block2col">
+	<div class="blockmenu">
+		<h2><span><?php echo $lang_profile['Profile menu'] ?></span></h2>
+		<div class="box">
+			<div class="inbox">
+				<ul>
+					<li<?php if ($page == 'essentials') echo ' class="isactive"'; ?>><a href="profile.php?section=essentials&id=<?php echo $id ?>"><?php echo $lang_profile['Section essentials'] ?></a></li>
+					<li<?php if ($page == 'personal') echo ' class="isactive"'; ?>><a href="profile.php?section=personal&id=<?php echo $id ?>"><?php echo $lang_profile['Section personal'] ?></a></li>
+					<li<?php if ($page == 'messaging') echo ' class="isactive"'; ?>><a href="profile.php?section=messaging&id=<?php echo $id ?>"><?php echo $lang_profile['Section messaging'] ?></a></li>
+<?php if ($pun_config['o_avatars'] == '1' || $pun_config['o_signatures'] == '1'): ?>					<li<?php if ($page == 'personality') echo ' class="isactive"'; ?>><a href="profile.php?section=personality&id=<?php echo $id ?>"><?php echo $lang_profile['Section personality'] ?></a></li>
+<?php endif; ?>					<li<?php if ($page == 'display') echo ' class="isactive"'; ?>><a href="profile.php?section=display&id=<?php echo $id ?>"><?php echo $lang_profile['Section display'] ?></a></li>
+					<li<?php if ($page == 'privacy') echo ' class="isactive"'; ?>><a href="profile.php?section=privacy&id=<?php echo $id ?>"><?php echo $lang_profile['Section privacy'] ?></a></li>
+<?php if ($pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_moderator'] == '1' && $pun_user['g_mod_ban_users'] == '1')): ?>					<li<?php if ($page == 'admin') echo ' class="isactive"'; ?>><a href="profile.php?section=admin&id=<?php echo $id ?>"><?php echo $lang_profile['Section admin'] ?></a></li>
+<?php endif; ?>				</ul>
+			</div>
+		</div>
+	</div>
+<?php
+
+}
+
+
+//
+// Outputs markup to display a user's avatar
+//
+function generate_avatar_markup($user_id)
+{
+	global $pun_config;
+
+	$filetypes = array('jpg', 'gif', 'png');
+	$avatar_markup = '';
+
+	foreach ($filetypes as $cur_type)
+	{
+		$path = $pun_config['o_avatars_dir'].'/'.$user_id.'.'.$cur_type;
+
+		if (file_exists(PUN_ROOT.$path) && $img_size = getimagesize(PUN_ROOT.$path))
+		{
+			$avatar_markup = '<img src="'.$pun_config['o_base_url'].'/'.$path.'?m='.filemtime(PUN_ROOT.$path).'" '.$img_size[3].' alt="" />';
+			break;
+		}
+	}
+
+	return $avatar_markup;
+}
+
+
+//
+// Generate browser's title
+//
+function generate_page_title($page_title, $p = null)
+{
+	global $pun_config, $lang_common;
+
+	$page_title = array_reverse($page_title);
+
+	if ($p != null)
+		$page_title[0] .= ' ('.sprintf($lang_common['Page'], forum_number_format($p)).')';
+
+	$crumbs = implode($lang_common['Title separator'], $page_title);
+
+	return $crumbs;
+}
+
+
+//
+// Save array of tracked topics in cookie
+//
+function set_tracked_topics($tracked_topics)
+{
+	global $cookie_name, $cookie_path, $cookie_domain, $cookie_secure, $pun_config;
+
+	$cookie_data = '';
+	if (!empty($tracked_topics))
+	{
+		// Sort the arrays (latest read first)
+		arsort($tracked_topics['topics'], SORT_NUMERIC);
+		arsort($tracked_topics['forums'], SORT_NUMERIC);
+
+		// Homebrew serialization (to avoid having to run unserialize() on cookie data)
+		foreach ($tracked_topics['topics'] as $id => $timestamp)
+			$cookie_data .= 't'.$id.'='.$timestamp.';';
+		foreach ($tracked_topics['forums'] as $id => $timestamp)
+			$cookie_data .= 'f'.$id.'='.$timestamp.';';
+
+		// Enforce a 4048 byte size limit (4096 minus some space for the cookie name)
+		if (strlen($cookie_data) > 4048)
+		{
+			$cookie_data = substr($cookie_data, 0, 4048);
+			$cookie_data = substr($cookie_data, 0, strrpos($cookie_data, ';')).';';
+		}
+	}
+
+	forum_setcookie($cookie_name.'_track', $cookie_data, time() + $pun_config['o_timeout_visit']);
+	$_COOKIE[$cookie_name.'_track'] = $cookie_data; // Set it directly in $_COOKIE as well
+}
+
+
+//
+// Extract array of tracked topics from cookie
+//
+function get_tracked_topics()
+{
+	global $cookie_name;
+
+	$cookie_data = isset($_COOKIE[$cookie_name.'_track']) ? $_COOKIE[$cookie_name.'_track'] : false;
+	if (!$cookie_data)
+		return array('topics' => array(), 'forums' => array());
+
+	if (strlen($cookie_data) > 4048)
+		return array('topics' => array(), 'forums' => array());
+
+	// Unserialize data from cookie
+	$tracked_topics = array('topics' => array(), 'forums' => array());
+	$temp = explode(';', $cookie_data);
+	foreach ($temp as $t)
+	{
+		$type = substr($t, 0, 1) == 'f' ? 'forums' : 'topics';
+		$id = intval(substr($t, 1));
+		$timestamp = intval(substr($t, strpos($t, '=') + 1));
+		if ($id > 0 && $timestamp > 0)
+			$tracked_topics[$type][$id] = $timestamp;
+	}
+
+	return $tracked_topics;
+}
+
+
+//
+// Update posts, topics, last_post, last_post_id and last_poster for a forum
+//
+function update_forum($forum_id)
+{
+	global $db;
+
+	$result = $db->query('SELECT COUNT(id), SUM(num_replies) FROM '.$db->prefix.'topics WHERE forum_id='.$forum_id) or error('Unable to fetch forum topic count', __FILE__, __LINE__, $db->error());
+	list($num_topics, $num_posts) = $db->fetch_row($result);
+
+	$num_posts = $num_posts + $num_topics; // $num_posts is only the sum of all replies (we have to add the topic posts)
+
+	$result = $db->query('SELECT last_post, last_post_id, last_poster FROM '.$db->prefix.'topics WHERE forum_id='.$forum_id.' AND moved_to IS NULL ORDER BY last_post DESC LIMIT 1') or error('Unable to fetch last_post/last_post_id/last_poster', __FILE__, __LINE__, $db->error());
+	if ($db->num_rows($result)) // There are topics in the forum
+	{
+		list($last_post, $last_post_id, $last_poster) = $db->fetch_row($result);
+
+		$db->query('UPDATE '.$db->prefix.'forums SET num_topics='.$num_topics.', num_posts='.$num_posts.', last_post='.$last_post.', last_post_id='.$last_post_id.', last_poster=\''.$db->escape($last_poster).'\' WHERE id='.$forum_id) or error('Unable to update last_post/last_post_id/last_poster', __FILE__, __LINE__, $db->error());
+	}
+	else // There are no topics
+		$db->query('UPDATE '.$db->prefix.'forums SET num_topics='.$num_topics.', num_posts='.$num_posts.', last_post=NULL, last_post_id=NULL, last_poster=NULL WHERE id='.$forum_id) or error('Unable to update last_post/last_post_id/last_poster', __FILE__, __LINE__, $db->error());
+}
+
+
+//
+// Deletes any avatars owned by the specified user ID
+//
+function delete_avatar($user_id)
+{
+	global $pun_config;
+
+	$filetypes = array('jpg', 'gif', 'png');
+
+	// Delete user avatar
+	foreach ($filetypes as $cur_type)
+	{
+		if (file_exists(PUN_ROOT.$pun_config['o_avatars_dir'].'/'.$user_id.'.'.$cur_type))
+			@unlink(PUN_ROOT.$pun_config['o_avatars_dir'].'/'.$user_id.'.'.$cur_type);
+	}
+}
+
+
+//
+// Delete a topic and all of it's posts
+//
+function delete_topic($topic_id)
+{
+	global $db;
+
+	// Delete the topic and any redirect topics
+	$db->query('DELETE FROM '.$db->prefix.'topics WHERE id='.$topic_id.' OR moved_to='.$topic_id) or error('Unable to delete topic', __FILE__, __LINE__, $db->error());
+
+	// Create a list of the post IDs in this topic
+	$post_ids = '';
+	$result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id='.$topic_id) or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
+	while ($row = $db->fetch_row($result))
+		$post_ids .= ($post_ids != '') ? ','.$row[0] : $row[0];
+
+	// Make sure we have a list of post IDs
+	if ($post_ids != '')
+	{
+		strip_search_index($post_ids);
+
+		// Delete posts in topic
+		$db->query('DELETE FROM '.$db->prefix.'posts WHERE topic_id='.$topic_id) or error('Unable to delete posts', __FILE__, __LINE__, $db->error());
+	}
+
+	// Delete any subscriptions for this topic
+	$db->query('DELETE FROM '.$db->prefix.'subscriptions WHERE topic_id='.$topic_id) or error('Unable to delete subscriptions', __FILE__, __LINE__, $db->error());
+}
+
+
+//
+// Delete a single post
+//
+function delete_post($post_id, $topic_id)
+{
+	global $db;
+
+	$result = $db->query('SELECT id, poster, posted FROM '.$db->prefix.'posts WHERE topic_id='.$topic_id.' ORDER BY id DESC LIMIT 2') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+	list($last_id, ,) = $db->fetch_row($result);
+	list($second_last_id, $second_poster, $second_posted) = $db->fetch_row($result);
+
+	// Delete the post
+	$db->query('DELETE FROM '.$db->prefix.'posts WHERE id='.$post_id) or error('Unable to delete post', __FILE__, __LINE__, $db->error());
+
+	strip_search_index($post_id);
+
+	// Count number of replies in the topic
+	$result = $db->query('SELECT COUNT(id) FROM '.$db->prefix.'posts WHERE topic_id='.$topic_id) or error('Unable to fetch post count for topic', __FILE__, __LINE__, $db->error());
+	$num_replies = $db->result($result, 0) - 1;
+
+	// If the message we deleted is the most recent in the topic (at the end of the topic)
+	if ($last_id == $post_id)
+	{
+		// If there is a $second_last_id there is more than 1 reply to the topic
+		if (!empty($second_last_id))
+			$db->query('UPDATE '.$db->prefix.'topics SET last_post='.$second_posted.', last_post_id='.$second_last_id.', last_poster=\''.$db->escape($second_poster).'\', num_replies='.$num_replies.' WHERE id='.$topic_id) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+		else
+			// We deleted the only reply, so now last_post/last_post_id/last_poster is posted/id/poster from the topic itself
+			$db->query('UPDATE '.$db->prefix.'topics SET last_post=posted, last_post_id=id, last_poster=poster, num_replies='.$num_replies.' WHERE id='.$topic_id) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+	}
+	else
+		// Otherwise we just decrement the reply counter
+		$db->query('UPDATE '.$db->prefix.'topics SET num_replies='.$num_replies.' WHERE id='.$topic_id) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+}
+
+
+//
+// Delete every .php file in the forum's cache directory
+//
+function forum_clear_cache()
+{
+	$d = dir(FORUM_CACHE_DIR);
+	while (($entry = $d->read()) !== false)
+	{
+		if (substr($entry, -4) == '.php')
+			@unlink(FORUM_CACHE_DIR.$entry);
+	}
+	$d->close();
+}
+
+
+//
+// Replace censored words in $text
+//
+function censor_words($text)
+{
+	global $db;
+	static $search_for, $replace_with;
+
+	// If not already built in a previous call, build an array of censor words and their replacement text
+	if (!isset($search_for))
+	{
+		$result = $db->query('SELECT search_for, replace_with FROM '.$db->prefix.'censoring') or error('Unable to fetch censor word list', __FILE__, __LINE__, $db->error());
+		$num_words = $db->num_rows($result);
+
+		$search_for = array();
+		for ($i = 0; $i < $num_words; ++$i)
+		{
+			list($search_for[$i], $replace_with[$i]) = $db->fetch_row($result);
+			$search_for[$i] = '/(?<=\W)('.str_replace('\*', '\w*?', preg_quote($search_for[$i], '/')).')(?=\W)/i';
+		}
+	}
+
+	if (!empty($search_for))
+		$text = substr(preg_replace($search_for, $replace_with, ' '.$text.' '), 1, -1);
+
+	return $text;
+}
+
+
+//
+// Determines the correct title for $user
+// $user must contain the elements 'username', 'title', 'posts', 'g_id' and 'g_user_title'
+//
+function get_title($user)
+{
+	global $db, $pun_config, $pun_bans, $lang_common;
+	static $ban_list, $pun_ranks;
+
+	// If not already built in a previous call, build an array of lowercase banned usernames
+	if (empty($ban_list))
+	{
+		$ban_list = array();
+
+		foreach ($pun_bans as $cur_ban)
+			$ban_list[] = strtolower($cur_ban['username']);
+	}
+
+	// If not already loaded in a previous call, load the cached ranks
+	if ($pun_config['o_ranks'] == '1' && !defined('PUN_RANKS_LOADED'))
+	{
+		if (file_exists(FORUM_CACHE_DIR.'cache_ranks.php'))
+			include FORUM_CACHE_DIR.'cache_ranks.php';
+
+		if (!defined('PUN_RANKS_LOADED'))
+		{
+			if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
+				require PUN_ROOT.'include/cache.php';
+
+			generate_ranks_cache();
+			require FORUM_CACHE_DIR.'cache_ranks.php';
+		}
+	}
+
+	// If the user has a custom title
+	if ($user['title'] != '')
+		$user_title = pun_htmlspecialchars($user['title']);
+	// If the user is banned
+	else if (in_array(strtolower($user['username']), $ban_list))
+		$user_title = $lang_common['Banned'];
+	// If the user group has a default user title
+	else if ($user['g_user_title'] != '')
+		$user_title = pun_htmlspecialchars($user['g_user_title']);
+	// If the user is a guest
+	else if ($user['g_id'] == PUN_GUEST)
+		$user_title = $lang_common['Guest'];
+	else
+	{
+		// Are there any ranks?
+		if ($pun_config['o_ranks'] == '1' && !empty($pun_ranks))
+		{
+			foreach ($pun_ranks as $cur_rank)
+			{
+				if ($user['num_posts'] >= $cur_rank['min_posts'])
+					$user_title = pun_htmlspecialchars($cur_rank['rank']);
+			}
+		}
+
+		// If the user didn't "reach" any rank (or if ranks are disabled), we assign the default
+		if (!isset($user_title))
+			$user_title = $lang_common['Member'];
+	}
+
+	return $user_title;
+}
+
+
+//
+// Generate a string with numbered links (for multipage scripts)
+//
+function paginate($num_pages, $cur_page, $link)
+{
+	global $lang_common;
+
+	$pages = array();
+	$link_to_all = false;
+
+	// If $cur_page == -1, we link to all pages (used in viewforum.php)
+	if ($cur_page == -1)
+	{
+		$cur_page = 1;
+		$link_to_all = true;
+	}
+
+	if ($num_pages <= 1)
+		$pages = array('<strong class="item1">1</strong>');
+	else
+	{
+		// Add a previous page link
+		if ($num_pages > 1 && $cur_page > 1)
+			$pages[] = '<a'.(empty($pages) ? ' class="item1"' : '').' href="'.$link.'&p='.($cur_page - 1).'">'.$lang_common['Previous'].'</a>';
+
+		if ($cur_page > 3)
+		{
+			$pages[] = '<a'.(empty($pages) ? ' class="item1"' : '').' href="'.$link.'&p=1">1</a>';
+
+			if ($cur_page > 5)
+				$pages[] = '<span class="spacer">'.$lang_common['Spacer'].'</span>';
+		}
+
+		// Don't ask me how the following works. It just does, OK? :-)
+		for ($current = ($cur_page == 5) ? $cur_page - 3 : $cur_page - 2, $stop = ($cur_page + 4 == $num_pages) ? $cur_page + 4 : $cur_page + 3; $current < $stop; ++$current)
+		{
+			if ($current < 1 || $current > $num_pages)
+				continue;
+			else if ($current != $cur_page || $link_to_all)
+				$pages[] = '<a'.(empty($pages) ? ' class="item1"' : '').' href="'.$link.'&p='.$current.'">'.forum_number_format($current).'</a>';
+			else
+				$pages[] = '<strong'.(empty($pages) ? ' class="item1"' : '').'>'.forum_number_format($current).'</strong>';
+		}
+
+		if ($cur_page <= ($num_pages-3))
+		{
+			if ($cur_page != ($num_pages-3) && $cur_page != ($num_pages-4))
+				$pages[] = '<span class="spacer">'.$lang_common['Spacer'].'</span>';
+
+			$pages[] = '<a'.(empty($pages) ? ' class="item1"' : '').' href="'.$link.'&p='.$num_pages.'">'.forum_number_format($num_pages).'</a>';
+		}
+
+		// Add a next page link
+		if ($num_pages > 1 && !$link_to_all && $cur_page < $num_pages)
+			$pages[] = '<a'.(empty($pages) ? ' class="item1"' : '').' href="'.$link.'&p='.($cur_page +1).'">'.$lang_common['Next'].'</a>';
+	}
+
+	return implode(' ', $pages);
+}
+
+
+//
+// Display a message
+//
+function message($message, $no_back_link = false)
+{
+	global $db, $lang_common, $pun_config, $pun_start, $tpl_main;
+
+	if (!defined('PUN_HEADER'))
+	{
+		global $pun_user;
+
+		$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Info']);
+		define('PUN_ACTIVE_PAGE', 'index');
+		require PUN_ROOT.'header.php';
+	}
+
+?>
+
+<div id="msg" class="block">
+	<h2><span><?php echo $lang_common['Info'] ?></span></h2>
+	<div class="box">
+		<div class="inbox">
+			<p><?php echo $message ?></p>
+<?php if (!$no_back_link): ?>			<p><a href="javascript: history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+<?php endif; ?>		</div>
+	</div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+
+//
+// Format a time string according to $time_format and time zones
+//
+function format_time($timestamp, $date_only = false, $date_format = null, $time_format = null, $time_only = false, $no_text = false)
+{
+	global $pun_config, $lang_common, $pun_user, $forum_date_formats, $forum_time_formats;
+
+	if ($timestamp == '')
+		return $lang_common['Never'];
+
+	$diff = ($pun_user['timezone'] + $pun_user['dst']) * 3600;
+	$timestamp += $diff;
+	$now = time();
+
+	if($date_format == null)
+		$date_format = $forum_date_formats[$pun_user['date_format']];
+
+	if($time_format == null)
+		$time_format = $forum_time_formats[$pun_user['time_format']];
+
+	$date = gmdate($date_format, $timestamp);
+	$today = gmdate($date_format, $now+$diff);
+	$yesterday = gmdate($date_format, $now+$diff-86400);
+
+	if(!$no_text)
+	{
+		if ($date == $today)
+			$date = $lang_common['Today'];
+		else if ($date == $yesterday)
+			$date = $lang_common['Yesterday'];
+	}
+
+	if ($date_only)
+		return $date;
+	else if ($time_only)
+		return gmdate($time_format, $timestamp);
+	else
+		return $date.' '.gmdate($time_format, $timestamp);
+}
+
+
+//
+// A wrapper for PHP's number_format function
+//
+function forum_number_format($number, $decimals = 0)
+{
+	global $lang_common;
+
+	return is_numeric($number) ? number_format($number, $decimals, $lang_common['lang_decimal_point'], $lang_common['lang_thousands_sep']) : $number;
+}
+
+
+//
+// Generate a random key of length $len
+//
+function random_key($len, $readable = false, $hash = false)
+{
+	$key = '';
+
+	if ($hash)
+		$key = substr(pun_hash(uniqid(rand(), true)), 0, $len);
+	else if ($readable)
+	{
+		$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+
+		for ($i = 0; $i < $len; ++$i)
+			$key .= substr($chars, (mt_rand() % strlen($chars)), 1);
+	}
+	else
+	{
+		for ($i = 0; $i < $len; ++$i)
+			$key .= chr(mt_rand(33, 126));
+	}
+
+	return $key;
+}
+
+
+//
+// If we are running pre PHP 4.3.0, we add our own implementation of file_get_contents
+//
+if (!function_exists('file_get_contents'))
+{
+	function file_get_contents($filename, $use_include_path = 0)
+	{
+		$data = '';
+
+		if ($fh = fopen($filename, 'rb', $use_include_path))
+		{
+			$data = fread($fh, filesize($filename));
+			fclose($fh);
+		}
+
+		return $data;
+	}
+}
+
+
+//
+// Make sure that HTTP_REFERER matches $pun_config['o_base_url']/$script
+//
+function confirm_referrer($script)
+{
+	global $pun_config, $lang_common;
+
+	if (!preg_match('#^'.preg_quote(str_replace('www.', '', $pun_config['o_base_url']).'/'.$script, '#').'#i', str_replace('www.', '', (isset($_SERVER['HTTP_REFERER']) ? urldecode($_SERVER['HTTP_REFERER']) : ''))))
+		message($lang_common['Bad referrer']);
+}
+
+
+//
+// Generate a random password of length $len
+// Compatibility wrapper for random_key
+//
+function random_pass($len)
+{
+	return random_key($len, true);
+}
+
+
+//
+// Compute a hash of $str
+//
+function pun_hash($str)
+{
+	return sha1($str);
+}
+
+
+//
+// Try to determine the correct remote IP-address
+//
+function get_remote_address()
+{
+	return $_SERVER['REMOTE_ADDR'];
+}
+
+
+//
+// Calls htmlspecialchars with a few options already set
+//
+function pun_htmlspecialchars($str)
+{
+	return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
+}
+
+
+//
+// Calls htmlspecialchars_decode with a few options already set
+//
+function pun_htmlspecialchars_decode($str)
+{
+	if (function_exists('htmlspecialchars_decode'))
+		return htmlspecialchars_decode($str, ENT_QUOTES);
+
+	static $translations;
+	if (!isset($translations))
+	{
+		$translations = get_html_translation_table(HTML_SPECIALCHARS, ENT_QUOTES);
+		$translations['''] = '\''; // get_html_translation_table doesn't include ' which is what htmlspecialchars translates ' to, but apparently that is okay?! http://bugs.php.net/bug.php?id=25927
+		$translations = array_flip($translations);
+	}
+
+	return strtr($str, $translations);
+}
+
+
+//
+// A wrapper for utf8_strlen for compatibility
+//
+function pun_strlen($str)
+{
+	return utf8_strlen($str);
+}
+
+
+//
+// Convert \r\n and \r to \n
+//
+function pun_linebreaks($str)
+{
+	return str_replace("\r", "\n", str_replace("\r\n", "\n", $str));
+}
+
+
+//
+// A wrapper for utf8_trim for compatibility
+//
+function pun_trim($str)
+{
+	return utf8_trim($str);
+}
+
+//
+// Checks if a string is in all uppercase
+//
+function is_all_uppercase($string)
+{
+	return utf8_strtoupper($string) == $string && utf8_strtolower($string) != $string;
+}
+
+
+//
+// Inserts $element into $input at $offset
+// $offset can be either a numerical offset to insert at (eg: 0 inserts at the beginning of the array)
+// or a string, which is the key that the new element should be inserted before
+// $key is optional: it's used when inserting a new key/value pair into an associative array
+//
+function array_insert(&$input, $offset, $element, $key = null)
+{
+	if ($key == null)
+		$key = $offset;
+
+	// Determine the proper offset if we're using a string
+	if (!is_int($offset))
+		$offset = array_search($offset, array_keys($input), true);
+
+	// Out of bounds checks
+	if ($offset > count($input))
+		$offset = count($input);
+	else if ($offset < 0)
+		$offset = 0;
+
+	$input = array_merge(array_slice($input, 0, $offset), array($key => $element), array_slice($input, $offset));
+}
+
+
+//
+// Display a message when board is in maintenance mode
+//
+function maintenance_message()
+{
+	global $db, $pun_config, $lang_common, $pun_user;
+
+	// Deal with newlines, tabs and multiple spaces
+	$pattern = array("\t", '  ', '  ');
+	$replace = array('    ', '  ', '  ');
+	$message = str_replace($pattern, $replace, $pun_config['o_maintenance_message']);
+
+
+	if (file_exists(PUN_ROOT.'style/'.$pun_user['style'].'/maintenance.tpl'))
+	{
+		$tpl_file = PUN_ROOT.'style/'.$pun_user['style'].'/maintenance.tpl';
+		$tpl_inc_dir = PUN_ROOT.'style/'.$pun_user['style'].'/';
+	}
+	else
+	{
+		$tpl_file = PUN_ROOT.'include/template/maintenance.tpl';
+		$tpl_inc_dir = PUN_ROOT.'include/user/';
+	}
+
+	$tpl_maint = file_get_contents($tpl_file);
+
+	// START SUBST - <pun_include "*">
+	preg_match_all('#<pun_include "([^/\\\\]*?)\.(php[45]?|inc|html?|txt)">#', $tpl_maint, $pun_includes, PREG_SET_ORDER);
+
+	foreach ($pun_includes as $cur_include)
+	{
+		ob_start();
+
+		// Allow for overriding user includes, too.
+		if (file_exists($tpl_inc_dir.$cur_include[1].'.'.$cur_include[2]))
+			require $tpl_inc_dir.$cur_include[1].'.'.$cur_include[2];
+		else if (file_exists(PUN_ROOT.'include/user/'.$cur_include[1].'.'.$cur_include[2]))
+			require PUN_ROOT.'include/user/'.$cur_include[1].'.'.$cur_include[2];
+		else
+			error(sprintf($lang_common['Pun include error'], htmlspecialchars($cur_include[0]), basename($tpl_file)));
+
+		$tpl_temp = ob_get_contents();
+		$tpl_maint = str_replace($cur_include[0], $tpl_temp, $tpl_maint);
+		ob_end_clean();
+	}
+	// END SUBST - <pun_include "*">
+
+
+	// START SUBST - <pun_language>
+	$tpl_maint = str_replace('<pun_language>', $lang_common['lang_identifier'], $tpl_maint);
+	// END SUBST - <pun_language>
+
+
+	// START SUBST - <pun_content_direction>
+	$tpl_maint = str_replace('<pun_content_direction>', $lang_common['lang_direction'], $tpl_maint);
+	// END SUBST - <pun_content_direction>
+
+
+	// START SUBST - <pun_head>
+	ob_start();
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Maintenance']);
+
+?>
+<title><?php echo generate_page_title($page_title) ?></title>
+<link rel="stylesheet" type="text/css" href="style/<?php echo $pun_user['style'].'.css' ?>" />
+<?php
+
+	$tpl_temp = trim(ob_get_contents());
+	$tpl_maint = str_replace('<pun_head>', $tpl_temp, $tpl_maint);
+	ob_end_clean();
+	// END SUBST - <pun_head>
+
+
+	// START SUBST - <pun_maint_main>
+	ob_start();
+
+?>
+<div class="block">
+	<h2><?php echo $lang_common['Maintenance'] ?></h2>
+	<div class="box">
+		<div class="inbox">
+			<p><?php echo $message ?></p>
+		</div>
+	</div>
+</div>
+<?php
+
+	$tpl_temp = trim(ob_get_contents());
+	$tpl_maint = str_replace('<pun_maint_main>', $tpl_temp, $tpl_maint);
+	ob_end_clean();
+	// END SUBST - <pun_maint_main>
+
+
+	// End the transaction
+	$db->end_transaction();
+
+
+	// Close the db connection (and free up any result data)
+	$db->close();
+
+	exit($tpl_maint);
+}
+
+
+//
+// Display $message and redirect user to $destination_url
+//
+function redirect($destination_url, $message)
+{
+	global $db, $pun_config, $lang_common, $pun_user;
+
+	// Prefix with o_base_url (unless there's already a valid URI)
+	if (strpos($destination_url, 'http://') !== 0 && strpos($destination_url, 'https://') !== 0 && strpos($destination_url, '/') !== 0)
+		$destination_url = $pun_config['o_base_url'].'/'.$destination_url;
+
+	// Do a little spring cleaning
+	$destination_url = preg_replace('/([\r\n])|(%0[ad])|(;\s*data\s*:)/i', '', $destination_url);
+
+	// If the delay is 0 seconds, we might as well skip the redirect all together
+	if ($pun_config['o_redirect_delay'] == '0')
+		header('Location: '.str_replace('&', '&', $destination_url));
+
+	// Send no-cache headers
+	header('Expires: Thu, 21 Jul 1977 07:30:00 GMT'); // When yours truly first set eyes on this world! :)
+	header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
+	header('Cache-Control: post-check=0, pre-check=0', false);
+	header('Pragma: no-cache'); // For HTTP/1.0 compatibility
+
+	// Send the Content-type header in case the web server is setup to send something else
+	header('Content-type: text/html; charset=utf-8');
+
+	if (file_exists(PUN_ROOT.'style/'.$pun_user['style'].'/redirect.tpl'))
+	{
+		$tpl_file = PUN_ROOT.'style/'.$pun_user['style'].'/redirect.tpl';
+		$tpl_inc_dir = PUN_ROOT.'style/'.$pun_user['style'].'/';
+	}
+	else
+	{
+		$tpl_file = PUN_ROOT.'include/template/redirect.tpl';
+		$tpl_inc_dir = PUN_ROOT.'include/user/';
+	}
+
+	$tpl_redir = file_get_contents($tpl_file);
+
+	// START SUBST - <pun_include "*">
+	preg_match_all('#<pun_include "([^/\\\\]*?)\.(php[45]?|inc|html?|txt)">#', $tpl_redir, $pun_includes, PREG_SET_ORDER);
+
+	foreach ($pun_includes as $cur_include)
+	{
+		ob_start();
+
+		// Allow for overriding user includes, too.
+		if (file_exists($tpl_inc_dir.$cur_include[1].'.'.$cur_include[2]))
+			require $tpl_inc_dir.$cur_include[1].'.'.$cur_include[2];
+		else if (file_exists(PUN_ROOT.'include/user/'.$cur_include[1].'.'.$cur_include[2]))
+			require PUN_ROOT.'include/user/'.$cur_include[1].'.'.$cur_include[2];
+		else
+			error(sprintf($lang_common['Pun include error'], htmlspecialchars($cur_include[0]), basename($tpl_file)));
+
+		$tpl_temp = ob_get_contents();
+		$tpl_redir = str_replace($cur_include[0], $tpl_temp, $tpl_redir);
+		ob_end_clean();
+	}
+	// END SUBST - <pun_include "*">
+
+
+	// START SUBST - <pun_language>
+	$tpl_redir = str_replace('<pun_language>', $lang_common['lang_identifier'], $tpl_redir);
+	// END SUBST - <pun_language>
+
+
+	// START SUBST - <pun_content_direction>
+	$tpl_redir = str_replace('<pun_content_direction>', $lang_common['lang_direction'], $tpl_redir);
+	// END SUBST - <pun_content_direction>
+
+
+	// START SUBST - <pun_head>
+	ob_start();
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Redirecting']);
+
+?>
+<meta http-equiv="refresh" content="<?php echo $pun_config['o_redirect_delay'] ?>;URL=<?php echo str_replace(array('<', '>', '"'), array('<', '>', '"'), $destination_url) ?>" />
+<title><?php echo generate_page_title($page_title) ?></title>
+<link rel="stylesheet" type="text/css" href="style/<?php echo $pun_user['style'].'.css' ?>" />
+<?php
+
+	$tpl_temp = trim(ob_get_contents());
+	$tpl_redir = str_replace('<pun_head>', $tpl_temp, $tpl_redir);
+	ob_end_clean();
+	// END SUBST - <pun_head>
+
+
+	// START SUBST - <pun_redir_main>
+	ob_start();
+
+?>
+<div class="block">
+	<h2><?php echo $lang_common['Redirecting'] ?></h2>
+	<div class="box">
+		<div class="inbox">
+			<p><?php echo $message.'<br /><br /><a href="'.$destination_url.'">'.$lang_common['Click redirect'].'</a>' ?></p>
+		</div>
+	</div>
+</div>
+<?php
+
+	$tpl_temp = trim(ob_get_contents());
+	$tpl_redir = str_replace('<pun_redir_main>', $tpl_temp, $tpl_redir);
+	ob_end_clean();
+	// END SUBST - <pun_redir_main>
+
+
+	// START SUBST - <pun_footer>
+	ob_start();
+
+	// End the transaction
+	$db->end_transaction();
+
+	// Display executed queries (if enabled)
+	if (defined('PUN_SHOW_QUERIES'))
+		display_saved_queries();
+
+	$tpl_temp = trim(ob_get_contents());
+	$tpl_redir = str_replace('<pun_footer>', $tpl_temp, $tpl_redir);
+	ob_end_clean();
+	// END SUBST - <pun_footer>
+
+
+	// Close the db connection (and free up any result data)
+	$db->close();
+
+	exit($tpl_redir);
+}
+
+
+//
+// Display a simple error message
+//
+function error($message, $file = null, $line = null, $db_error = false)
+{
+	global $pun_config, $lang_common;
+
+	// Set some default settings if the script failed before $pun_config could be populated
+	if (empty($pun_config))
+	{
+		$pun_config = array(
+			'o_board_title'	=> 'FluxBB',
+			'o_gzip'		=> '0'
+		);
+	}
+
+	// Set some default translations if the script failed before $lang_common could be populated
+	if (empty($lang_common))
+	{
+		$lang_common = array(
+			'Title separator'	=> ' / ',
+			'Page'				=> 'Page %s'
+		);
+	}
+
+	// Empty all output buffers and stop buffering
+	while (@ob_end_clean());
+
+	// "Restart" output buffering if we are using ob_gzhandler (since the gzip header is already sent)
+	if ($pun_config['o_gzip'] && extension_loaded('zlib'))
+		ob_start('ob_gzhandler');
+
+	// Send no-cache headers
+	header('Expires: Thu, 21 Jul 1977 07:30:00 GMT'); // When yours truly first set eyes on this world! :)
+	header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
+	header('Cache-Control: post-check=0, pre-check=0', false);
+	header('Pragma: no-cache'); // For HTTP/1.0 compatibility
+
+	// Send the Content-type header in case the web server is setup to send something else
+	header('Content-type: text/html; charset=utf-8');
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<?php $page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), 'Error') ?>
+<title><?php echo generate_page_title($page_title) ?></title>
+<style type="text/css">
+<!--
+BODY {MARGIN: 10% 20% auto 20%; font: 10px Verdana, Arial, Helvetica, sans-serif}
+#errorbox {BORDER: 1px solid #B84623}
+H2 {MARGIN: 0; COLOR: #FFFFFF; BACKGROUND-COLOR: #B84623; FONT-SIZE: 1.1em; PADDING: 5px 4px}
+#errorbox DIV {PADDING: 6px 5px; BACKGROUND-COLOR: #F1F1F1}
+-->
+</style>
+</head>
+<body>
+
+<div id="errorbox">
+	<h2>An error was encountered</h2>
+	<div>
+<?php
+
+	if (defined('PUN_DEBUG') && $file !== null && $line !== null)
+	{
+		echo "\t\t".'<strong>File:</strong> '.$file.'<br />'."\n\t\t".'<strong>Line:</strong> '.$line.'<br /><br />'."\n\t\t".'<strong>FluxBB reported</strong>: '.$message."\n";
+
+		if ($db_error)
+		{
+			echo "\t\t".'<br /><br /><strong>Database reported:</strong> '.pun_htmlspecialchars($db_error['error_msg']).(($db_error['error_no']) ? ' (Errno: '.$db_error['error_no'].')' : '')."\n";
+
+			if ($db_error['error_sql'] != '')
+				echo "\t\t".'<br /><br /><strong>Failed query:</strong> '.pun_htmlspecialchars($db_error['error_sql'])."\n";
+		}
+	}
+	else
+		echo "\t\t".'Error: <strong>'.$message.'.</strong>'."\n";
+
+?>
+	</div>
+</div>
+
+</body>
+</html>
+<?php
+
+	// If a database connection was established (before this error) we close it
+	if ($db_error)
+		$GLOBALS['db']->close();
+
+	exit;
+}
+
+
+//
+// Unset any variables instantiated as a result of register_globals being enabled
+//
+function forum_unregister_globals()
+{
+	$register_globals = ini_get('register_globals');
+	if ($register_globals === '' || $register_globals === '0' || strtolower($register_globals) === 'off')
+		return;
+
+	// Prevent script.php?GLOBALS[foo]=bar
+	if (isset($_REQUEST['GLOBALS']) || isset($_FILES['GLOBALS']))
+		exit('I\'ll have a steak sandwich and... a steak sandwich.');
+
+	// Variables that shouldn't be unset
+	$no_unset = array('GLOBALS', '_GET', '_POST', '_COOKIE', '_REQUEST', '_SERVER', '_ENV', '_FILES');
+
+	// Remove elements in $GLOBALS that are present in any of the superglobals
+	$input = array_merge($_GET, $_POST, $_COOKIE, $_SERVER, $_ENV, $_FILES, isset($_SESSION) && is_array($_SESSION) ? $_SESSION : array());
+	foreach ($input as $k => $v)
+	{
+		if (!in_array($k, $no_unset) && isset($GLOBALS[$k]))
+		{
+			unset($GLOBALS[$k]);
+			unset($GLOBALS[$k]); // Double unset to circumvent the zend_hash_del_key_or_index hole in PHP <4.4.3 and <5.1.4
+		}
+	}
+}
+
+
+//
+// Removes any "bad" characters (characters which mess with the display of a page, are invisible, etc) from user input
+//
+function forum_remove_bad_characters()
+{
+	$_GET = remove_bad_characters($_GET);
+	$_POST = remove_bad_characters($_POST);
+	$_COOKIE = remove_bad_characters($_COOKIE);
+	$_REQUEST = remove_bad_characters($_REQUEST);
+}
+
+//
+// Removes any "bad" characters (characters which mess with the display of a page, are invisible, etc) from the given string
+// See: http://kb.mozillazine.org/Network.IDN.blacklist_chars
+//
+function remove_bad_characters($array)
+{
+	static $bad_utf8_chars;
+
+	if (!isset($bad_utf8_chars))
+	{
+		$bad_utf8_chars = array(
+			"\xcc\xb7"		=> '',		// COMBINING SHORT SOLIDUS OVERLAY		0337	*
+			"\xcc\xb8"		=> '',		// COMBINING LONG SOLIDUS OVERLAY		0338	*
+			"\xe1\x85\x9F"	=> '',		// HANGUL CHOSEONG FILLER				115F	*
+			"\xe1\x85\xA0"	=> '',		// HANGUL JUNGSEONG FILLER				1160	*
+			"\xe2\x80\x8b"	=> '',		// ZERO WIDTH SPACE						200B	*
+			"\xe2\x80\x8c"	=> '',		// ZERO WIDTH NON-JOINER				200C
+			"\xe2\x80\x8d"	=> '',		// ZERO WIDTH JOINER					200D
+			"\xe2\x80\x8e"	=> '',		// LEFT-TO-RIGHT MARK					200E
+			"\xe2\x80\x8f"	=> '',		// RIGHT-TO-LEFT MARK					200F
+			"\xe2\x80\xaa"	=> '',		// LEFT-TO-RIGHT EMBEDDING				202A
+			"\xe2\x80\xab"	=> '',		// RIGHT-TO-LEFT EMBEDDING				202B
+			"\xe2\x80\xac"	=> '', 		// POP DIRECTIONAL FORMATTING			202C
+			"\xe2\x80\xad"	=> '',		// LEFT-TO-RIGHT OVERRIDE				202D
+			"\xe2\x80\xae"	=> '',		// RIGHT-TO-LEFT OVERRIDE				202E
+			"\xe2\x80\xaf"	=> '',		// NARROW NO-BREAK SPACE				202F	*
+			"\xe2\x81\x9f"	=> '',		// MEDIUM MATHEMATICAL SPACE			205F	*
+			"\xe2\x81\xa0"	=> '',		// WORD JOINER							2060
+			"\xe3\x85\xa4"	=> '',		// HANGUL FILLER						3164	*
+			"\xef\xbb\xbf"	=> '',		// ZERO WIDTH NO-BREAK SPACE			FEFF
+			"\xef\xbe\xa0"	=> '',		// HALFWIDTH HANGUL FILLER				FFA0	*
+			"\xef\xbf\xb9"	=> '',		// INTERLINEAR ANNOTATION ANCHOR		FFF9	*
+			"\xef\xbf\xba"	=> '',		// INTERLINEAR ANNOTATION SEPARATOR		FFFA	*
+			"\xef\xbf\xbb"	=> '',		// INTERLINEAR ANNOTATION TERMINATOR	FFFB	*
+			"\xef\xbf\xbc"	=> '',		// OBJECT REPLACEMENT CHARACTER			FFFC	*
+			"\xef\xbf\xbd"	=> '',		// REPLACEMENT CHARACTER				FFFD	*
+			"\xc2\xad"		=> '-',		// SOFT HYPHEN							00AD
+			"\xE2\x80\x9C"	=> '"',		// LEFT DOUBLE QUOTATION MARK			201C
+			"\xE2\x80\x9D"	=> '"',		// RIGHT DOUBLE QUOTATION MARK			201D
+			"\xE2\x80\x98"	=> '\'',	// LEFT SINGLE QUOTATION MARK			2018
+			"\xE2\x80\x99"	=> '\'',	// RIGHT SINGLE QUOTATION MARK			2019
+			"\xe2\x80\x80"	=> ' ',		// EN QUAD								2000	*
+			"\xe2\x80\x81"	=> ' ',		// EM QUAD								2001	*
+			"\xe2\x80\x82"	=> ' ',		// EN SPACE								2002	*
+			"\xe2\x80\x83"	=> ' ',		// EM SPACE								2003	*
+			"\xe2\x80\x84"	=> ' ',		// THREE-PER-EM SPACE					2004	*
+			"\xe2\x80\x85"	=> ' ',		// FOUR-PER-EM SPACE					2005	*
+			"\xe2\x80\x86"	=> ' ',		// SIX-PER-EM SPACE						2006	*
+			"\xe2\x80\x87"	=> ' ',		// FIGURE SPACE							2007	*
+			"\xe2\x80\x88"	=> ' ',		// PUNCTUATION SPACE					2008	*
+			"\xe2\x80\x89"	=> ' ',		// THIN SPACE							2009	*
+			"\xe2\x80\x8a"	=> ' ',		// HAIR SPACE							200A	*
+			"\xE3\x80\x80"	=> ' ',		// IDEOGRAPHIC SPACE					3000	*
+		);
+	}
+
+	if (is_array($array))
+		return array_map('remove_bad_characters', $array);
+
+	// Strip out any invalid characters
+	$array = utf8_bad_strip($array);
+
+	// Remove control characters
+	$array = preg_replace('/[\x{00}-\x{08}\x{0b}-\x{0c}\x{0e}-\x{1f}]/', '', $array);
+
+	// Replace some "bad" characters
+	$array = str_replace(array_keys($bad_utf8_chars), array_values($bad_utf8_chars), $array);
+
+	return $array;
+}
+
+
+//
+// Converts the file size in bytes to a human readable file size
+//
+function file_size($size)
+{
+	$units = array('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB');
+
+	for ($i = 0; $size > 1024; $i++)
+		$size /= 1024;
+
+	return round($size, 2).' '.$units[$i];
+}
+
+
+//
+// Fetch a list of available styles
+//
+function forum_list_styles()
+{
+	$styles = array();
+
+	$d = dir(PUN_ROOT.'style');
+	while (($entry = $d->read()) !== false)
+	{
+		if ($entry{0} == '.')
+			continue;
+
+		if (substr($entry, -4) == '.css')
+			$styles[] = substr($entry, 0, -4);
+	}
+	$d->close();
+
+	natcasesort($styles);
+
+	return $styles;
+}
+
+
+//
+// Fetch a list of available language packs
+//
+function forum_list_langs()
+{
+	$languages = array();
+
+	$d = dir(PUN_ROOT.'lang');
+	while (($entry = $d->read()) !== false)
+	{
+		if ($entry{0} == '.')
+			continue;
+
+		if (is_dir(PUN_ROOT.'lang/'.$entry) && file_exists(PUN_ROOT.'lang/'.$entry.'/common.php'))
+			$languages[] = $entry;
+	}
+	$d->close();
+
+	natcasesort($languages);
+
+	return $languages;
+}
+
+
+//
+// Fetch a list of available admin plugins
+//
+function forum_list_plugins($is_admin)
+{
+	$plugins = array();
+
+	$d = dir(PUN_ROOT.'plugins');
+	while (($entry = $d->read()) !== false)
+	{
+		if ($entry{0} == '.')
+			continue;
+
+		$prefix = substr($entry, 0, strpos($entry, '_'));
+		$suffix = substr($entry, strlen($entry) - 4);
+
+		if ($suffix == '.php' && ((!$is_admin && $prefix == 'AMP') || ($is_admin && ($prefix == 'AP' || $prefix == 'AMP'))))
+			$plugins[] = array(substr($entry, strpos($entry, '_') + 1, -4), $entry);
+	}
+	$d->close();
+
+	return $plugins;
+}
+
+// DEBUG FUNCTIONS BELOW
+
+//
+// Display executed queries (if enabled)
+//
+function display_saved_queries()
+{
+	global $db, $lang_common;
+
+	// Get the queries so that we can print them out
+	$saved_queries = $db->get_saved_queries();
+
+?>
+
+<div id="debug" class="blocktable">
+	<h2><span><?php echo $lang_common['Debug table'] ?></span></h2>
+	<div class="box">
+		<div class="inbox">
+			<table cellspacing="0">
+			<thead>
+				<tr>
+					<th class="tcl" scope="col"><?php echo $lang_common['Query times'] ?></th>
+					<th class="tcr" scope="col"><?php echo $lang_common['Query'] ?></th>
+				</tr>
+			</thead>
+			<tbody>
+<?php
+
+	$query_time_total = 0.0;
+	foreach ($saved_queries as $cur_query)
+	{
+		$query_time_total += $cur_query[1];
+
+?>
+				<tr>
+					<td class="tcl"><?php echo ($cur_query[1] != 0) ? $cur_query[1] : ' ' ?></td>
+					<td class="tcr"><?php echo pun_htmlspecialchars($cur_query[0]) ?></td>
+				</tr>
+<?php
+
+	}
+
+?>
+				<tr>
+					<td class="tcl" colspan="2"><?php printf($lang_common['Total query time'], $query_time_total.' s') ?></td>
+				</tr>
+			</tbody>
+			</table>
+		</div>
+	</div>
+</div>
+<?php
+
+}
+
+
+//
+// Dump contents of variable(s)
+//
+function dump()
+{
+	echo '<pre>';
+
+	$num_args = func_num_args();
+
+	for ($i = 0; $i < $num_args; ++$i)
+	{
+		print_r(func_get_arg($i));
+		echo "\n\n";
+	}
+
+	echo '</pre>';
+	exit;
+}
diff --git a/include/index.html b/include/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/include/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/include/parser.php b/include/parser.php
new file mode 100644
index 0000000..a6fceab
--- /dev/null
+++ b/include/parser.php
@@ -0,0 +1,932 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// Make sure no one attempts to run this script "directly"
+if (!defined('PUN'))
+	exit;
+
+// Global variables
+/* regular expression to match nested BBCode LIST tags
+'%
+\[list                # match opening bracket and tag name of outermost LIST tag
+(?:=([1a*]))?+        # optional attribute capture in group 1
+\]                    # closing bracket of outermost opening LIST tag
+(                     # capture contents of LIST tag in group 2
+  (?:                 # non capture group for either contents or whole nested LIST
+    [^\[]*+           # unroll the loop! consume everything up to next [ (normal *)
+    (?:               # (See "Mastering Regular Expressions" chapter 6 for details)
+      (?!             # negative lookahead ensures we are NOT on [LIST*] or [/LIST]
+        \[list        # opening LIST tag
+        (?:=[1a*])?+  # with optional attribute
+        \]            # closing bracket of opening LIST tag
+        |             # or...
+        \[/list\]     # a closing LIST tag
+      )               # end negative lookahead assertion (we are not on a LIST tag)
+      \[              # match the [ which is NOT the start of LIST tag (special)
+      [^\[]*+         # consume everything up to next [ (normal *)
+    )*+               # finish up "unrolling the loop" technique (special (normal*))*
+  |                   # or...
+    (?R)              # recursively match a whole nested LIST element
+  )*                  # as many times as necessary until deepest nested LIST tag grabbed
+)                     # end capturing contents of LIST tag into group 2
+\[/list\]             # match outermost closing LIST tag
+%iex' */
+$re_list = '%\[list(?:=([1a*]))?+\]((?:[^\[]*+(?:(?!\[list(?:=[1a*])?+\]|\[/list\])\[[^\[]*+)*+|(?R))*)\[/list\]%ie';
+
+// Here you can add additional smilies if you like (please note that you must escape single quote and backslash)
+$smilies = array(
+	':)' => 'smile.png',
+	'=)' => 'smile.png',
+	':|' => 'neutral.png',
+	'=|' => 'neutral.png',
+	':(' => 'sad.png',
+	'=(' => 'sad.png',
+	':D' => 'big_smile.png',
+	'=D' => 'big_smile.png',
+	':o' => 'yikes.png',
+	':O' => 'yikes.png',
+	';)' => 'wink.png',
+	':/' => 'hmm.png',
+	':P' => 'tongue.png',
+	':p' => 'tongue.png',
+	':lol:' => 'lol.png',
+	':mad:' => 'mad.png',
+	':rolleyes:' => 'roll.png',
+	':cool:' => 'cool.png');
+
+//
+// Make sure all BBCodes are lower case and do a little cleanup
+//
+function preparse_bbcode($text, &$errors, $is_signature = false)
+{
+	global $pun_config, $lang_common, $re_list;
+
+	if ($is_signature)
+	{
+		global $lang_profile;
+
+		if (preg_match('%\[/?(?:quote|code|list|h)\b[^\]]*\]%i', $text))
+			$errors[] = $lang_profile['Signature quote/code/list/h'];
+	}
+
+	// If the message contains a code tag we have to split it up (text within [code][/code] shouldn't be touched)
+	if (strpos($text, '[code]') !== false && strpos($text, '[/code]') !== false)
+	{
+		list($inside, $outside) = split_text($text, '[code]', '[/code]', $errors);
+		$text = implode("\1", $outside);
+	}
+
+	// Tidy up lists
+	$temp = preg_replace($re_list, 'preparse_list_tag(\'$2\', \'$1\', $errors)', $text);
+
+	// If the regex failed
+	if ($temp === null)
+		$errors[] = $lang_common['BBCode list size error'];
+	else
+		$text = str_replace('*'."\0".']', '*]', $temp);
+
+	if ($pun_config['o_make_links'] == '1')
+		$text = do_clickable($text);
+
+	// If we split up the message before we have to concatenate it together again (code tags)
+	if (isset($inside))
+	{
+		$outside = explode("\1", $text);
+		$text = '';
+
+		$num_tokens = count($outside);
+
+		for ($i = 0; $i < $num_tokens; ++$i)
+		{
+			$text .= $outside[$i];
+			if (isset($inside[$i]))
+				$text .= '[code]'.$inside[$i].'[/code]';
+		}
+	}
+	unset($inside);
+
+	$temp_text = false;
+	if (empty($errors))
+		$temp_text = preparse_tags($text, $errors, $is_signature);
+
+	if ($temp_text !== false)
+		$text = $temp_text;
+
+	// Remove empty tags
+	while (($new_text = strip_empty_bbcode($text, $errors)) !== false)
+	{
+		if ($new_text != $text)
+			$text = $new_text;
+		else
+			break;
+	}
+
+	return pun_trim($text);
+}
+
+
+//
+// Strip empty bbcode tags from some text
+//
+function strip_empty_bbcode($text, &$errors)
+{
+	// If the message contains a code tag we have to split it up (empty tags within [code][/code] are fine)
+	if (strpos($text, '[code]') !== false && strpos($text, '[/code]') !== false)
+	{
+		list($inside, $outside) = split_text($text, '[code]', '[/code]', $errors);
+		$text = implode("\1", $outside);
+	}
+
+	// Remove empty tags
+	while (($new_text = preg_replace('/\[(b|u|s|ins|del|em|i|h|colou?r|quote|img|url|email|list)(?:\=[^\]]*)?\]\s*\[\/\1\]/', '', $text)) !== false)
+	{
+		if ($new_text != $text)
+			$text = $new_text;
+		else
+			break;
+	}
+
+	// If we split up the message before we have to concatenate it together again (code tags)
+	if (isset($inside))
+	{
+		$outside = explode("\1", $text);
+		$text = '';
+
+		$num_tokens = count($outside);
+		for ($i = 0; $i < $num_tokens; ++$i)
+		{
+			$text .= $outside[$i];
+			if (isset($inside[$i]))
+				$text .= '[code]'.$inside[$i].'[/code]';
+		}
+	}
+
+	// Remove empty code tags
+	while (($new_text = preg_replace('/\[(code)\]\s*\[\/\1\]/', '', $text)) !== false)
+	{
+		if ($new_text != $text)
+			$text = $new_text;
+		else
+			break;
+	}
+
+	return $text;
+}
+
+
+//
+// Check the structure of bbcode tags and fix simple mistakes where possible
+//
+function preparse_tags($text, &$errors, $is_signature = false)
+{
+	global $lang_common, $pun_config;
+
+	// Start off by making some arrays of bbcode tags and what we need to do with each one
+
+	// List of all the tags
+	$tags = array('quote', 'code', 'b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'url', 'email', 'img', 'list', '*', 'h');
+	// List of tags that we need to check are open (You could not put b,i,u in here then illegal nesting like [b][i][/b][/i] would be allowed)
+	$tags_opened = $tags;
+	// and tags we need to check are closed (the same as above, added it just in case)
+	$tags_closed = $tags;
+	// Tags we can nest and the depth they can be nested to (only quotes)
+	$tags_nested = array('quote' => $pun_config['o_quote_depth'], 'list' => 5, '*' => 5);
+	// Tags to ignore the contents of completely (just code)
+	$tags_ignore = array('code');
+	// Block tags, block tags can only go within another block tag, they cannot be in a normal tag
+	$tags_block = array('quote', 'code', 'list', 'h', '*');
+	// Inline tags, we do not allow new lines in these
+	$tags_inline = array('b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'h');
+	// Tags we trim interior space
+	$tags_trim = array('img');
+	// Tags we remove quotes from the argument
+	$tags_quotes = array('url', 'email', 'img');
+	// Tags we limit bbcode in
+	$tags_limit_bbcode = array(
+		'*' 	=> array('b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'url', 'email', 'list', 'img', 'code'),
+		'list' 	=> array('*'),
+		'url' 	=> array('b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'img'),
+		'email' => array('b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'img'),
+		'img' 	=> array(),
+		'h'		=> array('b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'url', 'email'),
+	);
+	// Tags we can automatically fix bad nesting
+	$tags_fix = array('quote', 'b', 'i', 'u', 's', 'ins', 'del', 'em', 'color', 'colour', 'url', 'email', 'h');
+
+	$split_text = preg_split("/(\[[\*a-zA-Z0-9-\/]*?(?:=.*?)?\])/", $text, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
+
+	$open_tags = array('post');
+	$open_args = array('');
+	$opened_tag = 0;
+	$new_text = '';
+	$current_ignore = '';
+	$current_nest = '';
+	$current_depth = array();
+	$limit_bbcode = $tags;
+
+	foreach ($split_text as $current)
+	{
+		if ($current == '')
+			continue;
+
+		// Are we dealing with a tag?
+		if (substr($current, 0, 1) != '[' || substr($current, -1, 1) != ']')
+		{
+			// It's not a bbcode tag so we put it on the end and continue
+
+			// If we are nested too deeply don't add to the end
+			if ($current_nest)
+				continue;
+
+			$current = str_replace("\r\n", "\n", $current);
+			$current = str_replace("\r", "\n", $current);
+			if (in_array($open_tags[$opened_tag], $tags_inline) && strpos($current, "\n") !== false)
+			{
+				// Deal with new lines
+				$split_current = preg_split("/(\n\n+)/", $current, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
+				$current = '';
+
+				if (!pun_trim($split_current[0], "\n")) // The first part is a linebreak so we need to handle any open tags first
+					array_unshift($split_current, '');
+
+				for ($i = 1; $i < count($split_current); $i += 2)
+				{
+					$temp_opened = array();
+					$temp_opened_arg = array();
+					$temp = $split_current[$i - 1];
+					while (!empty($open_tags))
+					{
+						$temp_tag = array_pop($open_tags);
+						$temp_arg = array_pop($open_args);
+
+						if (in_array($temp_tag , $tags_inline))
+						{
+							array_push($temp_opened, $temp_tag);
+							array_push($temp_opened_arg, $temp_arg);
+							$temp .= '[/'.$temp_tag.']';
+						}
+						else
+						{
+							array_push($open_tags, $temp_tag);
+							array_push($open_args, $temp_arg);
+							break;
+						}
+					}
+					$current .= $temp.$split_current[$i];
+					$temp = '';
+					while (!empty($temp_opened))
+					{
+						$temp_tag = array_pop($temp_opened);
+						$temp_arg = array_pop($temp_opened_arg);
+						if (empty($temp_arg))
+							$temp .= '['.$temp_tag.']';
+						else
+							$temp .= '['.$temp_tag.'='.$temp_arg.']';
+						array_push($open_tags, $temp_tag);
+						array_push($open_args, $temp_arg);
+					}
+					$current .= $temp;
+				}
+
+				if (array_key_exists($i - 1, $split_current))
+					$current .= $split_current[$i - 1];
+			}
+
+			if (in_array($open_tags[$opened_tag], $tags_trim))
+				$new_text .= pun_trim($current);
+			else
+				$new_text .= $current;
+
+			continue;
+		}
+
+		// Get the name of the tag
+		$current_arg = '';
+		if (strpos($current, '/') === 1)
+		{
+			$current_tag = substr($current, 2, -1);
+		}
+		else if (strpos($current, '=') === false)
+		{
+			$current_tag = substr($current, 1, -1);
+		}
+		else
+		{
+			$current_tag = substr($current, 1, strpos($current, '=')-1);
+			$current_arg = substr($current, strpos($current, '=')+1, -1);
+		}
+		$current_tag = strtolower($current_tag);
+
+		// Is the tag defined?
+		if (!in_array($current_tag, $tags))
+		{
+			// It's not a bbcode tag so we put it on the end and continue
+			if (!$current_nest)
+				$new_text .= $current;
+
+			continue;
+		}
+
+		// We definitely have a bbcode tag
+
+		// Make the tag string lower case
+		if ($equalpos = strpos($current,'='))
+		{
+			// We have an argument for the tag which we don't want to make lowercase
+			if (strlen(substr($current, $equalpos)) == 2)
+			{
+				// Empty tag argument
+				$errors[] = sprintf($lang_common['BBCode error empty attribute'], $current_tag);
+				return false;
+			}
+			$current = strtolower(substr($current, 0, $equalpos)).substr($current, $equalpos);
+		}
+		else
+			$current = strtolower($current);
+
+		// This is if we are currently in a tag which escapes other bbcode such as code
+		if ($current_ignore)
+		{
+			if ('[/'.$current_ignore.']' == $current)
+			{
+				// We've finished the ignored section
+				$current = '[/'.$current_tag.']';
+				$current_ignore = '';
+			}
+
+			$new_text .= $current;
+
+			continue;
+		}
+
+		if ($current_nest)
+		{
+			// We are currently too deeply nested so lets see if we are closing the tag or not
+			if ($current_tag != $current_nest)
+				continue;
+
+			if (substr($current, 1, 1) == '/')
+				$current_depth[$current_nest]--;
+			else
+				$current_depth[$current_nest]++;
+
+			if ($current_depth[$current_nest] <= $tags_nested[$current_nest])
+				$current_nest = '';
+
+			continue;
+		}
+
+		// Check the current tag is allowed here
+		if (!in_array($current_tag, $limit_bbcode) && $current_tag != $open_tags[$opened_tag])
+		{
+			$errors[] = sprintf($lang_common['BBCode error invalid nesting'], $current_tag, $open_tags[$opened_tag]);
+			return false;
+		}
+
+		if (substr($current, 1, 1) == '/')
+		{
+			// This is if we are closing a tag
+
+			if ($opened_tag == 0 || !in_array($current_tag, $open_tags))
+			{
+				// We tried to close a tag which is not open
+				if (in_array($current_tag, $tags_opened))
+				{
+					$errors[] = sprintf($lang_common['BBCode error no opening tag'], $current_tag);
+					return false;
+				}
+			}
+			else
+			{
+				// Check nesting
+				while (true)
+				{
+					// Nesting is ok
+					if ($open_tags[$opened_tag] == $current_tag)
+					{
+						array_pop($open_tags);
+						array_pop($open_args);
+						$opened_tag--;
+						break;
+					}
+
+					// Nesting isn't ok, try to fix it
+					if (in_array($open_tags[$opened_tag], $tags_closed) && in_array($current_tag, $tags_closed))
+					{
+						if (in_array($current_tag, $open_tags))
+						{
+							$temp_opened = array();
+							$temp_opened_arg = array();
+							$temp = '';
+							while (!empty($open_tags))
+							{
+								$temp_tag = array_pop($open_tags);
+								$temp_arg = array_pop($open_args);
+
+								if (!in_array($temp_tag, $tags_fix))
+								{
+									// We couldn't fix nesting
+									$errors[] = sprintf($lang_common['BBCode error no closing tag'], array_pop($temp_opened));
+									return false;
+								}
+								array_push($temp_opened, $temp_tag);
+								array_push($temp_opened_arg, $temp_arg);
+
+								if ($temp_tag == $current_tag)
+									break;
+								else
+									$temp .= '[/'.$temp_tag.']';
+							}
+							$current = $temp.$current;
+							$temp = '';
+							array_pop($temp_opened);
+							array_pop($temp_opened_arg);
+
+							while (!empty($temp_opened))
+							{
+								$temp_tag = array_pop($temp_opened);
+								$temp_arg = array_pop($temp_opened_arg);
+								if (empty($temp_arg))
+									$temp .= '['.$temp_tag.']';
+								else
+									$temp .= '['.$temp_tag.'='.$temp_arg.']';
+								array_push($open_tags, $temp_tag);
+								array_push($open_args, $temp_arg);
+							}
+							$current .= $temp;
+							$opened_tag--;
+							break;
+						}
+						else
+						{
+							// We couldn't fix nesting
+							$errors[] = sprintf($lang_common['BBCode error no opening tag'], $current_tag);
+							return false;
+						}
+					}
+					else if (in_array($open_tags[$opened_tag], $tags_closed))
+						break;
+					else
+					{
+						array_pop($open_tags);
+						array_pop($open_args);
+						$opened_tag--;
+					}
+				}
+			}
+
+			if (in_array($current_tag, array_keys($tags_nested)))
+			{
+				if (isset($current_depth[$current_tag]))
+					$current_depth[$current_tag]--;
+			}
+
+			if (in_array($open_tags[$opened_tag], array_keys($tags_limit_bbcode)))
+				$limit_bbcode = $tags_limit_bbcode[$open_tags[$opened_tag]];
+			else
+				$limit_bbcode = $tags;
+
+			$new_text .= $current;
+
+			continue;
+		}
+		else
+		{
+			// We are opening a tag
+			if (in_array($current_tag, array_keys($tags_limit_bbcode)))
+				$limit_bbcode = $tags_limit_bbcode[$current_tag];
+			else
+				$limit_bbcode = $tags;
+
+			if (in_array($current_tag, $tags_block) && !in_array($open_tags[$opened_tag], $tags_block) && $opened_tag != 0)
+			{
+				// We tried to open a block tag within a non-block tag
+				$errors[] = sprintf($lang_common['BBCode error invalid nesting'], $current_tag, $open_tags[$opened_tag]);
+				return false;
+			}
+
+			if (in_array($current_tag, $tags_ignore))
+			{
+				// It's an ignore tag so we don't need to worry about what's inside it
+				$current_ignore = $current_tag;
+				$new_text .= $current;
+				continue;
+			}
+
+			// Deal with nested tags
+			if (in_array($current_tag, $open_tags) && !in_array($current_tag, array_keys($tags_nested)))
+			{
+				// We nested a tag we shouldn't
+				$errors[] = sprintf($lang_common['BBCode error invalid self-nesting'], $current_tag);
+				return false;
+			}
+			else if (in_array($current_tag, array_keys($tags_nested)))
+			{
+				// We are allowed to nest this tag
+
+				if (isset($current_depth[$current_tag]))
+					$current_depth[$current_tag]++;
+				else
+					$current_depth[$current_tag] = 1;
+
+				// See if we are nested too deep
+				if ($current_depth[$current_tag] > $tags_nested[$current_tag])
+				{
+					$current_nest = $current_tag;
+					continue;
+				}
+			}
+
+			// Remove quotes from arguments for certain tags
+			if (strpos($current, '=') !== false && in_array($current_tag, $tags_quotes))
+			{
+				$current = preg_replace('#\['.$current_tag.'=("|\'|)(.*?)\\1\]\s*#i', '['.$current_tag.'=$2]', $current);
+			}
+
+			if (in_array($current_tag, array_keys($tags_limit_bbcode)))
+				$limit_bbcode = $tags_limit_bbcode[$current_tag];
+
+			$open_tags[] = $current_tag;
+			$open_args[] = $current_arg;
+			$opened_tag++;
+			$new_text .= $current;
+			continue;
+		}
+	}
+
+	// Check we closed all the tags we needed to
+	foreach ($tags_closed as $check)
+	{
+		if (in_array($check, $open_tags))
+		{
+			// We left an important tag open
+			$errors[] = sprintf($lang_common['BBCode error no closing tag'], $check);
+			return false;
+		}
+	}
+
+	if ($current_ignore)
+	{
+		// We left an ignore tag open
+		$errors[] = sprintf($lang_common['BBCode error no closing tag'], $current_ignore);
+		return false;
+	}
+
+	return $new_text;
+}
+
+
+//
+// Preparse the contents of [list] bbcode
+//
+function preparse_list_tag($content, $type = '*', &$errors)
+{
+	global $lang_common, $re_list;
+
+	if (strlen($type) != 1)
+		$type = '*';
+
+	if (strpos($content,'[list') !== false)
+	{
+		$content = preg_replace($re_list, 'preparse_list_tag(\'$2\', \'$1\', $errors)', $content);
+	}
+
+	$items = explode('[*]', str_replace('\"', '"', $content));
+
+	$content = '';
+	foreach ($items as $item)
+	{
+		if (pun_trim($item) != '')
+			$content .= '[*'."\0".']'.str_replace('[/*]', '', pun_trim($item)).'[/*'."\0".']'."\n";
+	}
+
+	return '[list='.$type.']'."\n".$content.'[/list]';
+}
+
+
+//
+// Split text into chunks ($inside contains all text inside $start and $end, and $outside contains all text outside)
+//
+function split_text($text, $start, $end, &$errors, $retab = true)
+{
+	global $pun_config, $lang_common;
+
+	$tokens = explode($start, $text);
+
+	$outside[] = $tokens[0];
+
+	$num_tokens = count($tokens);
+	for ($i = 1; $i < $num_tokens; ++$i)
+	{
+		$temp = explode($end, $tokens[$i]);
+
+		if (count($temp) != 2)
+		{
+			$errors[] = $lang_common['BBCode code problem'];
+			return array(null, array($text));
+		}
+		$inside[] = $temp[0];
+		$outside[] = $temp[1];
+	}
+
+	if ($pun_config['o_indent_num_spaces'] != 8 && $retab)
+	{
+		$spaces = str_repeat(' ', $pun_config['o_indent_num_spaces']);
+		$inside = str_replace("\t", $spaces, $inside);
+	}
+
+	return array($inside, $outside);
+}
+
+
+//
+// Truncate URL if longer than 55 characters (add http:// or ftp:// if missing)
+//
+function handle_url_tag($url, $link = '', $bbcode = false)
+{
+	$url = pun_trim($url);
+	$full_url = str_replace(array(' ', '\'', '`', '"'), array('%20', '', '', ''), $url);
+	if (strpos($url, 'www.') === 0) // If it starts with www, we add http://
+		$full_url = 'http://'.$full_url;
+	else if (strpos($url, 'ftp.') === 0) // Else if it starts with ftp, we add ftp://
+		$full_url = 'ftp://'.$full_url;
+	else if (!preg_match('#^([a-z0-9]{3,6})://#', $url)) // Else if it doesn't start with abcdef://, we add http://
+		$full_url = 'http://'.$full_url;
+
+	// Ok, not very pretty :-)
+	if ($bbcode)
+	{
+		if ($full_url == $link)
+			return '[url]'.$link.'[/url]';
+		else
+			return '[url='.$full_url.']'.$link.'[/url]';
+	}
+	else
+	{
+		if ($link == '' || $link == $url)
+		{
+			$url = pun_htmlspecialchars_decode($url);
+			$link = utf8_strlen($url) > 55 ? utf8_substr($url, 0 , 39).' … '.utf8_substr($url, -10) : $url;
+			$link = pun_htmlspecialchars($link);
+		}
+		else
+			$link = stripslashes($link);
+
+		return '<a href="'.$full_url.'">'.$link.'</a>';
+	}
+}
+
+
+//
+// Turns an URL from the [img] tag into an <img> tag or a <a href...> tag
+//
+function handle_img_tag($url, $is_signature = false, $alt = null)
+{
+	global $lang_common, $pun_user;
+
+	if ($alt == null)
+		$alt = basename($url);
+
+	$img_tag = '<a href="'.$url.'"><'.$lang_common['Image link'].' - '.$alt.'></a>';
+
+	if ($is_signature && $pun_user['show_img_sig'] != '0')
+		$img_tag = '<img class="sigimage" src="'.$url.'" alt="'.$alt.'" />';
+	else if (!$is_signature && $pun_user['show_img'] != '0')
+		$img_tag = '<span class="postimg"><img src="'.$url.'" alt="'.$alt.'" /></span>';
+
+	return $img_tag;
+}
+
+
+//
+// Parse the contents of [list] bbcode
+//
+function handle_list_tag($content, $type = '*')
+{
+	global $re_list;
+
+	if (strlen($type) != 1)
+		$type = '*';
+
+	if (strpos($content,'[list') !== false)
+	{
+		$content = preg_replace($re_list, 'handle_list_tag(\'$2\', \'$1\')', $content);
+	}
+
+	$content = preg_replace('#\s*\[\*\](.*?)\[/\*\]\s*#s', '<li><p>$1</p></li>', pun_trim($content));
+
+	if ($type == '*')
+		$content = '<ul>'.$content.'</ul>';
+	else
+		if ($type == 'a')
+			$content = '<ol class="alpha">'.$content.'</ol>';
+		else
+			$content = '<ol class="decimal">'.$content.'</ol>';
+
+	return '</p>'.$content.'<p>';
+}
+
+
+//
+// Convert BBCodes to their HTML equivalent
+//
+function do_bbcode($text, $is_signature = false)
+{
+	global $lang_common, $pun_user, $pun_config, $re_list;
+
+	if (strpos($text, '[quote') !== false)
+	{
+		$text = preg_replace('#\[quote\]\s*#', '</p><div class="quotebox"><blockquote><div><p>', $text);
+		$text = preg_replace('#\[quote=("|"|\'|)(.*?)\\1\]#se', '"</p><div class=\"quotebox\"><cite>".str_replace(array(\'[\', \'\\"\'), array(\'[\', \'"\'), \'$2\')." ".$lang_common[\'wrote\']."</cite><blockquote><div><p>"', $text);
+		$text = preg_replace('#\s*\[\/quote\]#S', '</p></div></blockquote></div><p>', $text);
+	}
+
+	if (!$is_signature)
+	{
+		$pattern[] = $re_list;
+		$replace[] = 'handle_list_tag(\'$2\', \'$1\')';
+	}
+
+	$pattern[] = '#\[b\](.*?)\[/b\]#ms';
+	$pattern[] = '#\[i\](.*?)\[/i\]#ms';
+	$pattern[] = '#\[u\](.*?)\[/u\]#ms';
+	$pattern[] = '#\[s\](.*?)\[/s\]#ms';
+	$pattern[] = '#\[del\](.*?)\[/del\]#ms';
+	$pattern[] = '#\[ins\](.*?)\[/ins\]#ms';
+	$pattern[] = '#\[em\](.*?)\[/em\]#ms';
+	$pattern[] = '#\[colou?r=([a-zA-Z]{3,20}|\#[0-9a-fA-F]{6}|\#[0-9a-fA-F]{3})](.*?)\[/colou?r\]#ms';
+	$pattern[] = '#\[h\](.*?)\[/h\]#ms';
+
+	$replace[] = '<strong>$1</strong>';
+	$replace[] = '<em>$1</em>';
+	$replace[] = '<span class="bbu">$1</span>';
+	$replace[] = '<span class="bbs">$1</span>';
+	$replace[] = '<del>$1</del>';
+	$replace[] = '<ins>$1</ins>';
+	$replace[] = '<em>$1</em>';
+	$replace[] = '<span style="color: $1">$2</span>';
+	$replace[] = '</p><h5>$1</h5><p>';
+
+	if (($is_signature && $pun_config['p_sig_img_tag'] == '1') || (!$is_signature && $pun_config['p_message_img_tag'] == '1'))
+	{
+		$pattern[] = '#\[img\]((ht|f)tps?://)([^\s<"]*?)\[/img\]#e';
+		$pattern[] = '#\[img=([^\[]*?)\]((ht|f)tps?://)([^\s<"]*?)\[/img\]#e';
+		if ($is_signature)
+		{
+			$replace[] = 'handle_img_tag(\'$1$3\', true)';
+			$replace[] = 'handle_img_tag(\'$2$4\', true, \'$1\')';
+		}
+		else
+		{
+			$replace[] = 'handle_img_tag(\'$1$3\', false)';
+			$replace[] = 'handle_img_tag(\'$2$4\', false, \'$1\')';
+		}
+	}
+
+	$pattern[] = '#\[url\]([^\[]*?)\[/url\]#e';
+	$pattern[] = '#\[url=([^\[]+?)\](.*?)\[/url\]#e';
+	$pattern[] = '#\[email\]([^\[]*?)\[/email\]#';
+	$pattern[] = '#\[email=([^\[]+?)\](.*?)\[/email\]#';
+
+	$replace[] = 'handle_url_tag(\'$1\')';
+	$replace[] = 'handle_url_tag(\'$1\', \'$2\')';
+	$replace[] = '<a href="mailto:$1">$1</a>';
+	$replace[] = '<a href="mailto:$1">$2</a>';
+
+	// This thing takes a while! :)
+	$text = preg_replace($pattern, $replace, $text);
+
+	return $text;
+}
+
+
+//
+// Make hyperlinks clickable
+//
+function do_clickable($text)
+{
+	$text = ' '.$text;
+
+	$text = preg_replace('#(?<=[\s\]\)])(<)?(\[)?(\()?([\'"]?)(https?|ftp|news){1}://([\w\-]+\.([\w\-]+\.)*\w+(:[0-9]+)?(/[^\s\[]*[^\s.,?!\[;:-])?)\4(?(3)(\)))(?(2)(\]))(?(1)(>))(?![^\s]*\[/(?:url|img)\])#ie', 'stripslashes(\'$1$2$3$4\').handle_url_tag(\'$5://$6\', \'$5://$6\', true).stripslashes(\'$4$10$11$12\')', $text);
+	$text = preg_replace('#(?<=[\s\]\)])(<)?(\[)?(\()?([\'"]?)(www|ftp)\.(([\w\-]+\.)*\w+(:[0-9]+)?(/[^\s\[]*[^\s.,?!\[;:-])?)\4(?(3)(\)))(?(2)(\]))(?(1)(>))(?![^\s]*\[/(?:url|img)\])#ie', 'stripslashes(\'$1$2$3$4\').handle_url_tag(\'$5.$6\', \'$5.$6\', true).stripslashes(\'$4$10$11$12\')', $text);
+
+	return substr($text, 1);
+}
+
+
+//
+// Convert a series of smilies to images
+//
+function do_smilies($text)
+{
+	global $pun_config, $smilies;
+
+	$text = ' '.$text.' ';
+
+	foreach ($smilies as $smiley_text => $smiley_img)
+	{
+		if (strpos($text, $smiley_text) !== false)
+			$text = preg_replace("#(?<=[>\s])".preg_quote($smiley_text, '#')."(?=\W)#m", '<img src="'.$pun_config['o_base_url'].'/img/smilies/'.$smiley_img.'" width="15" height="15" alt="'.substr($smiley_img, 0, strrpos($smiley_img, '.')).'" />', $text);
+	}
+
+	return substr($text, 1, -1);
+}
+
+
+//
+// Parse message text
+//
+function parse_message($text, $hide_smilies)
+{
+	global $pun_config, $lang_common, $pun_user;
+
+	if ($pun_config['o_censoring'] == '1')
+		$text = censor_words($text);
+
+	// Convert applicable characters to HTML entities
+	$text = pun_htmlspecialchars($text);
+
+	// If the message contains a code tag we have to split it up (text within [code][/code] shouldn't be touched)
+	if (strpos($text, '[code]') !== false && strpos($text, '[/code]') !== false)
+	{
+		list($inside, $outside) = split_text($text, '[code]', '[/code]', $errors);
+		$text = implode("\1", $outside);
+	}
+
+	if ($pun_config['p_message_bbcode'] == '1' && strpos($text, '[') !== false && strpos($text, ']') !== false)
+		$text = do_bbcode($text);
+
+	if ($pun_config['o_smilies'] == '1' && $pun_user['show_smilies'] == '1' && $hide_smilies == '0')
+		$text = do_smilies($text);
+
+	// Deal with newlines, tabs and multiple spaces
+	$pattern = array("\n", "\t", '  ', '  ');
+	$replace = array('<br />', '    ', '  ', '  ');
+	$text = str_replace($pattern, $replace, $text);
+
+	// If we split up the message before we have to concatenate it together again (code tags)
+	if (isset($inside))
+	{
+		$outside = explode("\1", $text);
+		$text = '';
+
+		$num_tokens = count($outside);
+
+		for ($i = 0; $i < $num_tokens; ++$i)
+		{
+			$text .= $outside[$i];
+			if (isset($inside[$i]))
+			//	$text .= '</p><div class="codebox"><pre><code>'.pun_trim($inside[$i], "\n\r").'</code></pre></div><p>';
+			{
+				$num_lines = (substr_count($inside[$i], "\n"));
+				$text .= '</p><div class="codebox"><pre'.(($num_lines > 28) ? ' class="vscroll"' : '').'><code>'.pun_trim($inside[$i], "\n\r").'</code></pre></div><p>';
+			}
+		}
+	}
+
+	// Add paragraph tag around post, but make sure there are no empty paragraphs
+	$text = preg_replace('#<br />\s*?<br />((\s*<br />)*)#i', "</p>$1<p>", $text);
+	$text = str_replace('<p><br />', '<p>', $text);
+	$text = str_replace('<p></p>', '', '<p>'.$text.'</p>');
+
+	return $text;
+}
+
+
+//
+// Parse signature text
+//
+function parse_signature($text)
+{
+	global $pun_config, $lang_common, $pun_user;
+
+	if ($pun_config['o_censoring'] == '1')
+		$text = censor_words($text);
+
+	// Convert applicable characters to HTML entities
+	$text = pun_htmlspecialchars($text);
+
+	if ($pun_config['p_sig_bbcode'] == '1' && strpos($text, '[') !== false && strpos($text, ']') !== false)
+		$text = do_bbcode($text, true);
+
+	if ($pun_config['o_smilies_sig'] == '1' && $pun_user['show_smilies'] == '1')
+		$text = do_smilies($text);
+
+
+	// Deal with newlines, tabs and multiple spaces
+	$pattern = array("\n", "\t", '  ', '  ');
+	$replace = array('<br />', '    ', '  ', '  ');
+	$text = str_replace($pattern, $replace, $text);
+
+	// Add paragraph tag around post, but make sure there are no empty paragraphs
+	$text = preg_replace('#<br />\s*?<br />((\s*<br />)*)#i', "</p>$1<p>", $text);
+	$text = str_replace('<p><br />', '<p>', $text);
+	$text = str_replace('<p></p>', '', '<p>'.$text.'</p>');
+
+	return $text;
+}
diff --git a/include/search_idx.php b/include/search_idx.php
new file mode 100644
index 0000000..e9292a4
--- /dev/null
+++ b/include/search_idx.php
@@ -0,0 +1,311 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// The contents of this file are very much inspired by the file functions_search.php
+// from the phpBB Group forum software phpBB2 (http://www.phpbb.com)
+
+
+// Make sure no one attempts to run this script "directly"
+if (!defined('PUN'))
+	exit;
+
+
+// Make a regex that will match CJK or Hangul characters
+define('PUN_CJK_HANGUL_REGEX', '['.
+	'\x{1100}-\x{11FF}'.		// Hangul Jamo							1100-11FF		(http://www.fileformat.info/info/unicode/block/hangul_jamo/index.htm)
+	'\x{3130}-\x{318F}'.		// Hangul Compatibility Jamo			3130-318F		(http://www.fileformat.info/info/unicode/block/hangul_compatibility_jamo/index.htm)
+	'\x{AC00}-\x{D7AF}'.		// Hangul Syllables						AC00-D7AF		(http://www.fileformat.info/info/unicode/block/hangul_syllables/index.htm)
+
+	// Hiragana
+	'\x{3040}-\x{309F}'.		// Hiragana								3040-309F		(http://www.fileformat.info/info/unicode/block/hiragana/index.htm)
+
+	// Katakana
+	'\x{30A0}-\x{30FF}'.		// Katakana								30A0-30FF		(http://www.fileformat.info/info/unicode/block/katakana/index.htm)
+	'\x{31F0}-\x{31FF}'.		// Katakana Phonetic Extensions			31F0-31FF		(http://www.fileformat.info/info/unicode/block/katakana_phonetic_extensions/index.htm)
+
+	// CJK Unified Ideographs	(http://en.wikipedia.org/wiki/CJK_Unified_Ideographs)
+	'\x{2E80}-\x{2EFF}'.		// CJK Radicals Supplement				2E80-2EFF		(http://www.fileformat.info/info/unicode/block/cjk_radicals_supplement/index.htm)
+	'\x{2F00}-\x{2FDF}'.		// Kangxi Radicals						2F00-2FDF		(http://www.fileformat.info/info/unicode/block/kangxi_radicals/index.htm)
+	'\x{2FF0}-\x{2FFF}'.		// Ideographic Description Characters	2FF0-2FFF		(http://www.fileformat.info/info/unicode/block/ideographic_description_characters/index.htm)
+	'\x{3000}-\x{303F}'.		// CJK Symbols and Punctuation			3000-303F		(http://www.fileformat.info/info/unicode/block/cjk_symbols_and_punctuation/index.htm)
+	'\x{31C0}-\x{31EF}'.		// CJK Strokes							31C0-31EF		(http://www.fileformat.info/info/unicode/block/cjk_strokes/index.htm)
+	'\x{3200}-\x{32FF}'.		// Enclosed CJK Letters and Months		3200-32FF		(http://www.fileformat.info/info/unicode/block/enclosed_cjk_letters_and_months/index.htm)
+	'\x{3400}-\x{4DBF}'.		// CJK Unified Ideographs Extension A	3400-4DBF		(http://www.fileformat.info/info/unicode/block/cjk_unified_ideographs_extension_a/index.htm)
+	'\x{4E00}-\x{9FFF}'.		// CJK Unified Ideographs				4E00-9FFF		(http://www.fileformat.info/info/unicode/block/cjk_unified_ideographs/index.htm)
+	'\x{20000}-\x{2A6DF}'.		// CJK Unified Ideographs Extension B	20000-2A6DF		(http://www.fileformat.info/info/unicode/block/cjk_unified_ideographs_extension_b/index.htm)
+']');
+
+
+//
+// "Cleans up" a text string and returns an array of unique words
+// This function depends on the current locale setting
+//
+function split_words($text, $idx)
+{
+	// Remove BBCode
+	$text = preg_replace('/\[\/?(b|u|s|ins|del|em|i|h|colou?r|quote|code|img|url|email|list)(?:\=[^\]]*)?\]/', ' ', $text);
+
+	// Remove any apostrophes or dashes which aren't part of words
+	$text = substr(preg_replace('/((?<=\W)[\'\-]|[\'\-](?=\W))/', '', ' '.$text.' '), 1, -1);
+
+	// Remove symbols and multiple whitespace, allow % and * if we aren't indexing
+	$text = preg_replace('/[\^\$&\(\)<>`"„\|, at _\?~\+\[\]{}:=\/#\\\\;!\.…\s•'.($idx ? '%\*' : '').']+/u', ' ', $text);
+
+	// Replace multiple dashes with just one
+	$text = preg_replace('/-{2,}/', '-', $text);
+
+	// Fill an array with all the words
+	$words = array_unique(explode(' ', $text));
+
+	// Remove any words that should not be indexed
+	foreach ($words as $key => $value)
+	{
+		// If the word shouldn't be indexed, remove it
+		if (!validate_search_word($value, $idx))
+			unset($words[$key]);
+	}
+
+	return $words;
+}
+
+
+//
+// Checks if a word is a valid searchable word
+//
+function validate_search_word($word, $idx)
+{
+	global $pun_user, $pun_config;
+	static $stopwords;
+
+	// If the word is a keyword we don't want to index it, but we do want to be allowed to search it
+	if (is_keyword($word))
+		return !$idx;
+
+	$language = isset($pun_user['language']) ? $pun_user['language'] : $pun_config['o_default_lang'];
+	if (!isset($stopwords))
+	{
+		if (file_exists(PUN_ROOT.'lang/'.$language.'/stopwords.txt'))
+		{
+			$stopwords = file(PUN_ROOT.'lang/'.$language.'/stopwords.txt');
+			$stopwords = array_map('pun_trim', $stopwords);
+			$stopwords = array_filter($stopwords);
+		}
+		else
+			$stopwords = array();
+	}
+
+	// If it is a stopword it isn't valid
+	if (in_array($word, $stopwords))
+		return false;
+
+	// If the word if CJK we don't want to index it, but we do want to be allowed to search it
+	if (is_cjk($word))
+		return !$idx;
+
+	// Check the word is within the min/max length
+	$num_chars = pun_strlen($word);
+	return $num_chars >= PUN_SEARCH_MIN_WORD && $num_chars <= PUN_SEARCH_MAX_WORD;
+}
+
+
+//
+// Check a given word is a search keyword.
+//
+function is_keyword($word)
+{
+	return $word == 'and' || $word == 'or' || $word == 'not';
+}
+
+
+//
+// Check if a given word is CJK or Hangul.
+//
+function is_cjk($word)
+{
+	return preg_match('/^'.PUN_CJK_HANGUL_REGEX.'+$/u', $word) ? true : false;
+}
+
+
+//
+// Strip [img] [url] and [email] out of the message so we don't index their contents
+//
+function strip_bbcode($text)
+{
+	static $patterns;
+
+	if (!isset($patterns))
+	{
+		$patterns = array(
+			'%\[img=([^\]]*+)\][^[]*+\[/img\]%'									=>	'$1',	// Keep the alt description
+			'%\[(url|email)=[^\]]*+\]([^[]*+(?:(?!\[/\1\])\[[^[]*+)*)\[/\1\]%' =>	'$2',	// Keep the text
+			'%\[(img|url|email)\]([^[]*+(?:(?!\[/\1\])\[[^[]*+)*)\[/\1\]%'		=>	'',		// Remove the whole thing
+		);
+	}
+
+	return preg_replace(array_keys($patterns), array_values($patterns), $text);
+}
+
+
+//
+// Updates the search index with the contents of $post_id (and $subject)
+//
+function update_search_index($mode, $post_id, $message, $subject = null)
+{
+	global $db_type, $db;
+
+	$message = utf8_strtolower($message);
+	$subject = utf8_strtolower($subject);
+
+	// Remove any bbcode that we shouldn't index
+	$message = strip_bbcode($message);
+
+	// Split old and new post/subject to obtain array of 'words'
+	$words_message = split_words($message, true);
+	$words_subject = ($subject) ? split_words($subject, true) : array();
+
+	if ($mode == 'edit')
+	{
+		$result = $db->query('SELECT w.id, w.word, m.subject_match FROM '.$db->prefix.'search_words AS w INNER JOIN '.$db->prefix.'search_matches AS m ON w.id=m.word_id WHERE m.post_id='.$post_id, true) or error('Unable to fetch search index words', __FILE__, __LINE__, $db->error());
+
+		// Declare here to stop array_keys() and array_diff() from complaining if not set
+		$cur_words['post'] = array();
+		$cur_words['subject'] = array();
+
+		while ($row = $db->fetch_row($result))
+		{
+			$match_in = ($row[2]) ? 'subject' : 'post';
+			$cur_words[$match_in][$row[1]] = $row[0];
+		}
+
+		$db->free_result($result);
+
+		$words['add']['post'] = array_diff($words_message, array_keys($cur_words['post']));
+		$words['add']['subject'] = array_diff($words_subject, array_keys($cur_words['subject']));
+		$words['del']['post'] = array_diff(array_keys($cur_words['post']), $words_message);
+		$words['del']['subject'] = array_diff(array_keys($cur_words['subject']), $words_subject);
+	}
+	else
+	{
+		$words['add']['post'] = $words_message;
+		$words['add']['subject'] = $words_subject;
+		$words['del']['post'] = array();
+		$words['del']['subject'] = array();
+	}
+
+	unset($words_message);
+	unset($words_subject);
+
+	// Get unique words from the above arrays
+	$unique_words = array_unique(array_merge($words['add']['post'], $words['add']['subject']));
+
+	if (!empty($unique_words))
+	{
+		$result = $db->query('SELECT id, word FROM '.$db->prefix.'search_words WHERE word IN(\''.implode('\',\'', array_map(array($db, 'escape'), $unique_words)).'\')', true) or error('Unable to fetch search index words', __FILE__, __LINE__, $db->error());
+
+		$word_ids = array();
+		while ($row = $db->fetch_row($result))
+			$word_ids[$row[1]] = $row[0];
+
+		$db->free_result($result);
+
+		$new_words = array_diff($unique_words, array_keys($word_ids));
+		unset($unique_words);
+
+		if (!empty($new_words))
+		{
+			switch ($db_type)
+			{
+				case 'mysql':
+				case 'mysqli':
+				case 'mysql_innodb':
+				case 'mysqli_innodb':
+					$db->query('INSERT INTO '.$db->prefix.'search_words (word) VALUES(\''.implode('\'),(\'', array_map(array($db, 'escape'), $new_words)).'\')');
+					break;
+
+				default:
+					foreach ($new_words as $word)
+						$db->query('INSERT INTO '.$db->prefix.'search_words (word) VALUES(\''.$db->escape($word).'\')');
+					break;
+			}
+		}
+
+		unset($new_words);
+	}
+
+	// Delete matches (only if editing a post)
+	foreach ($words['del'] as $match_in => $wordlist)
+	{
+		$subject_match = ($match_in == 'subject') ? 1 : 0;
+
+		if (!empty($wordlist))
+		{
+			$sql = '';
+			foreach ($wordlist as $word)
+				$sql .= (($sql != '') ? ',' : '').$cur_words[$match_in][$word];
+
+			$db->query('DELETE FROM '.$db->prefix.'search_matches WHERE word_id IN('.$sql.') AND post_id='.$post_id.' AND subject_match='.$subject_match) or error('Unable to delete search index word matches', __FILE__, __LINE__, $db->error());
+		}
+	}
+
+	// Add new matches
+	foreach ($words['add'] as $match_in => $wordlist)
+	{
+		$subject_match = ($match_in == 'subject') ? 1 : 0;
+
+		if (!empty($wordlist))
+			$db->query('INSERT INTO '.$db->prefix.'search_matches (post_id, word_id, subject_match) SELECT '.$post_id.', id, '.$subject_match.' FROM '.$db->prefix.'search_words WHERE word IN(\''.implode('\',\'', array_map(array($db, 'escape'), $wordlist)).'\')') or error('Unable to insert search index word matches', __FILE__, __LINE__, $db->error());
+	}
+
+	unset($words);
+}
+
+
+//
+// Strip search index of indexed words in $post_ids
+//
+function strip_search_index($post_ids)
+{
+	global $db_type, $db;
+
+	switch ($db_type)
+	{
+		case 'mysql':
+		case 'mysqli':
+		case 'mysql_innodb':
+		case 'mysqli_innodb':
+		{
+			$result = $db->query('SELECT word_id FROM '.$db->prefix.'search_matches WHERE post_id IN('.$post_ids.') GROUP BY word_id') or error('Unable to fetch search index word match', __FILE__, __LINE__, $db->error());
+
+			if ($db->num_rows($result))
+			{
+				$word_ids = '';
+				while ($row = $db->fetch_row($result))
+					$word_ids .= ($word_ids != '') ? ','.$row[0] : $row[0];
+
+				$result = $db->query('SELECT word_id FROM '.$db->prefix.'search_matches WHERE word_id IN('.$word_ids.') GROUP BY word_id HAVING COUNT(word_id)=1') or error('Unable to fetch search index word match', __FILE__, __LINE__, $db->error());
+
+				if ($db->num_rows($result))
+				{
+					$word_ids = '';
+					while ($row = $db->fetch_row($result))
+						$word_ids .= ($word_ids != '') ? ','.$row[0] : $row[0];
+
+					$db->query('DELETE FROM '.$db->prefix.'search_words WHERE id IN('.$word_ids.')') or error('Unable to delete search index word', __FILE__, __LINE__, $db->error());
+				}
+			}
+
+			break;
+		}
+
+		default:
+			$db->query('DELETE FROM '.$db->prefix.'search_words WHERE id IN(SELECT word_id FROM '.$db->prefix.'search_matches WHERE word_id IN(SELECT word_id FROM '.$db->prefix.'search_matches WHERE post_id IN('.$post_ids.') GROUP BY word_id) GROUP BY word_id HAVING COUNT(word_id)=1)') or error('Unable to delete from search index', __FILE__, __LINE__, $db->error());
+			break;
+	}
+
+	$db->query('DELETE FROM '.$db->prefix.'search_matches WHERE post_id IN('.$post_ids.')') or error('Unable to delete search index word match', __FILE__, __LINE__, $db->error());
+}
diff --git a/include/template/admin.tpl b/include/template/admin.tpl
new file mode 100644
index 0000000..06679ee
--- /dev/null
+++ b/include/template/admin.tpl
@@ -0,0 +1,38 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<pun_language>" lang="<pun_language>" dir="<pun_content_direction>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<pun_head>
+</head>
+
+<body>
+
+<div id="punadmin" class="pun">
+<div class="top-box"><div><!-- Top Corners --></div></div>
+<div class="punwrap">
+
+<div id="brdheader" class="block">
+	<div class="box">
+		<div id="brdtitle" class="inbox">
+			<pun_title>
+			<pun_desc>
+		</div>
+		<pun_navlinks>
+		<pun_status>
+	</div>
+</div>
+
+<pun_announcement>
+
+<div id="brdmain">
+<pun_main>
+</div>
+
+<pun_footer>
+
+</div>
+<div class="end-box"><div><!-- Bottom Corners --></div></div>
+</div>
+
+</body>
+</html>
diff --git a/include/template/help.tpl b/include/template/help.tpl
new file mode 100644
index 0000000..58fec14
--- /dev/null
+++ b/include/template/help.tpl
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<pun_language>" lang="<pun_language>" dir="<pun_content_direction>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<pun_head>
+</head>
+
+<body>
+
+<div id="punhelp" class="pun">
+<div class="top-box"><div><!-- Top Corners --></div></div>
+<div class="punwrap">
+
+<div id="brdmain">
+<pun_main>
+</div>
+
+</div>
+<div class="end-box"><div><!-- Bottom Corners --></div></div>
+</div>
+
+</body>
+</html>
diff --git a/include/template/index.html b/include/template/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/include/template/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/include/template/main.tpl b/include/template/main.tpl
new file mode 100644
index 0000000..ea0a0b6
--- /dev/null
+++ b/include/template/main.tpl
@@ -0,0 +1,38 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<pun_language>" lang="<pun_language>" dir="<pun_content_direction>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<pun_head>
+</head>
+
+<body>
+
+<div id="pun<pun_page>" class="pun">
+<div class="top-box"><div><!-- Top Corners --></div></div>
+<div class="punwrap">
+
+<div id="brdheader" class="block">
+	<div class="box">
+		<div id="brdtitle" class="inbox">
+			<pun_title>
+			<pun_desc>
+		</div>
+		<pun_navlinks>
+		<pun_status>
+	</div>
+</div>
+
+<pun_announcement>
+
+<div id="brdmain">
+<pun_main>
+</div>
+
+<pun_footer>
+
+</div>
+<div class="end-box"><div><!-- Bottom corners --></div></div>
+</div>
+
+</body>
+</html>
diff --git a/include/template/maintenance.tpl b/include/template/maintenance.tpl
new file mode 100644
index 0000000..56edc49
--- /dev/null
+++ b/include/template/maintenance.tpl
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<pun_language>" lang="<pun_language>" dir="<pun_content_direction>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<pun_head>
+</head>
+
+<body>
+
+<div id="punmaint" class="pun">
+<div class="top-box"><div><!-- Top Corners --></div></div>
+<div class="punwrap">
+
+<div id="brdmain">
+<pun_maint_main>
+</div>
+
+</div>
+<div class="end-box"><div><!-- Bottom Corners --></div></div>
+</div>
+
+</body>
+</html>
diff --git a/include/template/redirect.tpl b/include/template/redirect.tpl
new file mode 100644
index 0000000..31f5f19
--- /dev/null
+++ b/include/template/redirect.tpl
@@ -0,0 +1,25 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<pun_language>" lang="<pun_language>" dir="<pun_content_direction>">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<pun_head>
+</head>
+
+<body>
+
+<div id="punredirect" class="pun">
+<div class="top-box"><div><!-- Top Corners --></div></div>
+<div class="punwrap">
+
+<div id="brdmain">
+<pun_redir_main>
+</div>
+
+<pun_footer>
+
+</div>
+<div class="end-box"><div><!-- Bottom Corners --></div></div>
+</div>
+
+</body>
+</html>
diff --git a/include/utf8/index.html b/include/utf8/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/include/utf8/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/include/utf8/mbstring/core.php b/include/utf8/mbstring/core.php
new file mode 100644
index 0000000..bea1c32
--- /dev/null
+++ b/include/utf8/mbstring/core.php
@@ -0,0 +1,144 @@
+<?php
+
+/**
+* @version $Id: core.php,v 1.5 2006/02/28 22:12:25 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+// Define UTF8_CORE as required
+if (!defined('UTF8_CORE'))
+	define('UTF8_CORE', true);
+
+/**
+* Wrapper round mb_strlen
+* Assumes you have mb_internal_encoding to UTF-8 already
+* Note: this function does not count bad bytes in the string - these
+* are simply ignored
+* @param string UTF-8 string
+* @return int number of UTF-8 characters in string
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strlen($str)
+{
+	return mb_strlen($str);
+}
+
+/**
+* Assumes mbstring internal encoding is set to UTF-8
+* Wrapper around mb_strpos
+* Find position of first occurrence of a string
+* @param string haystack
+* @param string needle (you should validate this with utf8_is_valid)
+* @param integer offset in characters (from left)
+* @return mixed integer position or FALSE on failure
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strpos($str, $search, $offset = false)
+{
+	// Strip unvalid characters
+	$str = utf8_bad_strip($str);
+
+	if ($offset === false)
+		return mb_strpos($str, $search);
+	else
+		return mb_strpos($str, $search, $offset);
+}
+
+/**
+* Assumes mbstring internal encoding is set to UTF-8
+* Wrapper around mb_strrpos
+* Find position of last occurrence of a char in a string
+* @param string haystack
+* @param string needle (you should validate this with utf8_is_valid)
+* @param integer (optional) offset (from left)
+* @return mixed integer position or FALSE on failure
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strrpos($str, $search, $offset = false)
+{
+	// Strip unvalid characters
+	$str = utf8_bad_strip($str);
+
+	if (!$offset)
+	{
+		// Emulate behaviour of strrpos rather than raising warning
+		if (empty($str))
+			return false;
+
+		return mb_strrpos($str, $search);
+	}
+	else
+	{
+		if (!is_int($offset))
+		{
+			trigger_error('utf8_strrpos expects parameter 3 to be long', E_USER_WARNING);
+			return false;
+		}
+
+		$str = mb_substr($str, $offset);
+
+		if (($pos = mb_strrpos($str, $search)) !== false)
+			return $pos + $offset;
+
+		return false;
+	}
+}
+
+/**
+* Assumes mbstring internal encoding is set to UTF-8
+* Wrapper around mb_substr
+* Return part of a string given character offset (and optionally length)
+* @param string
+* @param integer number of UTF-8 characters offset (from left)
+* @param integer (optional) length in UTF-8 characters from offset
+* @return mixed string or FALSE if failure
+* @package utf8
+* @subpackage strings
+*/
+function utf8_substr($str, $offset, $length = false)
+{
+	if ($length === false)
+		return mb_substr($str, $offset);
+	else
+		return mb_substr($str, $offset, $length);
+}
+
+/**
+* Assumes mbstring internal encoding is set to UTF-8
+* Wrapper around mb_strtolower
+* Make a string lowercase
+* Note: The concept of a characters "case" only exists is some alphabets
+* such as Latin, Greek, Cyrillic, Armenian and archaic Georgian - it does
+* not exist in the Chinese alphabet, for example. See Unicode Standard
+* Annex #21: Case Mappings
+* @param string
+* @return mixed either string in lowercase or FALSE is UTF-8 invalid
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strtolower($str)
+{
+	return mb_strtolower($str);
+}
+
+/**
+* Assumes mbstring internal encoding is set to UTF-8
+* Wrapper around mb_strtoupper
+* Make a string uppercase
+* Note: The concept of a characters "case" only exists is some alphabets
+* such as Latin, Greek, Cyrillic, Armenian and archaic Georgian - it does
+* not exist in the Chinese alphabet, for example. See Unicode Standard
+* Annex #21: Case Mappings
+* @param string
+* @return mixed either string in lowercase or FALSE is UTF-8 invalid
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strtoupper($str)
+{
+	return mb_strtoupper($str);
+}
diff --git a/include/utf8/mbstring/index.html b/include/utf8/mbstring/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/include/utf8/mbstring/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/include/utf8/native/core.php b/include/utf8/native/core.php
new file mode 100644
index 0000000..58636f5
--- /dev/null
+++ b/include/utf8/native/core.php
@@ -0,0 +1,422 @@
+<?php
+
+/**
+* @version $Id: core.php,v 1.9 2007/08/12 01:11:33 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+// Define UTF8_CORE as required
+if (!defined('UTF8_CORE'))
+	define('UTF8_CORE', true);
+
+/**
+* Unicode aware replacement for strlen(). Returns the number
+* of characters in the string (not the number of bytes), replacing
+* multibyte characters with a single byte equivalent
+* utf8_decode() converts characters that are not in ISO-8859-1
+* to '?', which, for the purpose of counting, is alright - It's
+* much faster than iconv_strlen
+* Note: this function does not count bad UTF-8 bytes in the string
+* - these are simply ignored
+* @author <chernyshevsky at hotmail dot com>
+* @link   http://www.php.net/manual/en/function.strlen.php
+* @link   http://www.php.net/manual/en/function.utf8-decode.php
+* @param string UTF-8 string
+* @return int number of UTF-8 characters in string
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strlen($str)
+{
+	return strlen(utf8_decode($str));
+}
+
+/**
+* UTF-8 aware alternative to strpos
+* Find position of first occurrence of a string
+* Note: This will get alot slower if offset is used
+* Note: requires utf8_strlen amd utf8_substr to be loaded
+* @param string haystack
+* @param string needle (you should validate this with utf8_is_valid)
+* @param integer offset in characters (from left)
+* @return mixed integer position or FALSE on failure
+* @see http://www.php.net/strpos
+* @see utf8_strlen
+* @see utf8_substr
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strpos($str, $needle, $offset = false)
+{
+	if ($offset === false)
+	{
+		$ar = explode($needle, $str, 2);
+
+		if (count($ar) > 1)
+			return utf8_strlen($ar[0]);
+
+		return false;
+	}
+	else
+	{
+		if (!is_int($offset))
+		{
+			trigger_error('utf8_strpos: Offset must be an integer', E_USER_ERROR);
+			return false;
+		}
+
+		$str = utf8_substr($str, $offset);
+
+		if (($pos = utf8_strpos($str, $needle)) !== false)
+			return $pos + $offset;
+
+		return false;
+	}
+}
+
+/**
+* UTF-8 aware alternative to strrpos
+* Find position of last occurrence of a char in a string
+* Note: This will get alot slower if offset is used
+* Note: requires utf8_substr and utf8_strlen to be loaded
+* @param string haystack
+* @param string needle (you should validate this with utf8_is_valid)
+* @param integer (optional) offset (from left)
+* @return mixed integer position or FALSE on failure
+* @see http://www.php.net/strrpos
+* @see utf8_substr
+* @see utf8_strlen
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strrpos($str, $needle, $offset = false)
+{
+	if ($offset === false)
+	{
+		$ar = explode($needle, $str);
+
+		if (count($ar) > 1)
+		{
+			// Pop off the end of the string where the last match was made
+			array_pop($ar);
+			$str = join($needle, $ar);
+
+			return utf8_strlen($str);
+		}
+
+		return false;
+	}
+	else
+	{
+		if (!is_int($offset))
+		{
+			trigger_error('utf8_strrpos expects parameter 3 to be long', E_USER_WARNING);
+			return false;
+		}
+
+		$str = utf8_substr($str, $offset);
+
+		if (($pos = utf8_strrpos($str, $needle)) !== false)
+			return $pos + $offset;
+
+		return false;
+	}
+}
+
+/**
+* UTF-8 aware alternative to substr
+* Return part of a string given character offset (and optionally length)
+*
+* Note arguments: comparied to substr - if offset or length are
+* not integers, this version will not complain but rather massages them
+* into an integer.
+*
+* Note on returned values: substr documentation states false can be
+* returned in some cases (e.g. offset > string length)
+* mb_substr never returns false, it will return an empty string instead.
+* This adopts the mb_substr approach
+*
+* Note on implementation: PCRE only supports repetitions of less than
+* 65536, in order to accept up to MAXINT values for offset and length,
+* we'll repeat a group of 65535 characters when needed.
+*
+* Note on implementation: calculating the number of characters in the
+* string is a relatively expensive operation, so we only carry it out when
+* necessary. It isn't necessary for +ve offsets and no specified length
+*
+* @author Chris Smith<chris at jalakai.co.uk>
+* @param string
+* @param integer number of UTF-8 characters offset (from left)
+* @param integer (optional) length in UTF-8 characters from offset
+* @return mixed string or FALSE if failure
+* @package utf8
+* @subpackage strings
+*/
+function utf8_substr($str, $offset, $length = false)
+{
+	// Generates E_NOTICE for PHP4 objects, but not PHP5 objects
+	$str = (string) $str;
+	$offset = (int) $offset;
+
+	if ($length)
+		$length = (int) $length;
+
+	// Handle trivial cases
+	if ($length === 0)
+		return '';
+	if ($offset < 0 && $length < 0 && $length < $offset)
+		return '';
+
+	// Normalise negative offsets (we could use a tail
+	// anchored pattern, but they are horribly slow!)
+	if ($offset < 0)
+	{
+		// See notes
+		$strlen = utf8_strlen($str);
+		$offset = $strlen + $offset;
+
+		if ($offset < 0)
+			$offset = 0;
+	}
+
+	$Op = '';
+	$Lp = '';
+
+	// Establish a pattern for offset, a
+	// non-captured group equal in length to offset
+	if ($offset > 0)
+	{
+		$Ox = (int) ($offset / 65535);
+		$Oy = $offset % 65535;
+
+		if ($Ox)
+			$Op = '(?:.{65535}){'.$Ox.'}';
+
+		$Op = '^(?:'.$Op.'.{'.$Oy.'})';
+	}
+	else
+		$Op = '^';
+
+
+	// Establish a pattern for length
+	if (!$length)
+	{
+		// The rest of the string
+		$Lp = '(.*)$';
+	}
+	else
+	{
+		// See notes
+		if (!isset($strlen))
+			$strlen = strlen(utf8_decode($str));
+
+		// Another trivial case
+		if ($offset > $strlen)
+			return '';
+
+		if ($length > 0)
+		{
+			// Reduce any length that would go passed the end of the string
+			$length = min($strlen-$offset, $length);
+
+			$Lx = (int)( $length / 65535 );
+			$Ly = $length % 65535;
+
+			// Negative length requires a captured group of length characters
+			if ($Lx) $Lp = '(?:.{65535}){'.$Lx.'}';
+			$Lp = '('.$Lp.'.{'.$Ly.'})';
+		}
+		else if ($length < 0)
+		{
+
+			if ($length < ($offset - $strlen))
+				return '';
+
+			$Lx = (int)((-$length)/65535);
+			$Ly = (-$length)%65535;
+
+			// Negative length requires ... capture everything except a group of
+			// -length characters anchored at the tail-end of the string
+			if ($Lx)
+				$Lp = '(?:.{65535}){'.$Lx.'}';
+
+			$Lp = '(.*)(?:'.$Lp.'.{'.$Ly.'})$';
+		}
+	}
+
+	if (!preg_match('#'.$Op.$Lp.'#us', $str, $match))
+		return '';
+
+	return $match[1];
+}
+
+/**
+* UTF-8 aware alternative to strtolower
+* Make a string lowercase
+* Note: The concept of a characters "case" only exists is some alphabets
+* such as Latin, Greek, Cyrillic, Armenian and archaic Georgian - it does
+* not exist in the Chinese alphabet, for example. See Unicode Standard
+* Annex #21: Case Mappings
+* Note: requires utf8_to_unicode and utf8_from_unicode
+* @author Andreas Gohr <andi at splitbrain.org>
+* @param string
+* @return mixed either string in lowercase or FALSE is UTF-8 invalid
+* @see http://www.php.net/strtolower
+* @see utf8_to_unicode
+* @see utf8_from_unicode
+* @see http://www.unicode.org/reports/tr21/tr21-5.html
+* @see http://dev.splitbrain.org/view/darcs/dokuwiki/inc/utf8.php
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strtolower($string)
+{
+	static $UTF8_UPPER_TO_LOWER = false;
+
+	if (!$UTF8_UPPER_TO_LOWER)
+	{
+		$UTF8_UPPER_TO_LOWER = array(
+			0x0041=>0x0061, 0x03A6=>0x03C6, 0x0162=>0x0163, 0x00C5=>0x00E5, 0x0042=>0x0062,
+			0x0139=>0x013A, 0x00C1=>0x00E1, 0x0141=>0x0142, 0x038E=>0x03CD, 0x0100=>0x0101,
+			0x0490=>0x0491, 0x0394=>0x03B4, 0x015A=>0x015B, 0x0044=>0x0064, 0x0393=>0x03B3,
+			0x00D4=>0x00F4, 0x042A=>0x044A, 0x0419=>0x0439, 0x0112=>0x0113, 0x041C=>0x043C,
+			0x015E=>0x015F, 0x0143=>0x0144, 0x00CE=>0x00EE, 0x040E=>0x045E, 0x042F=>0x044F,
+			0x039A=>0x03BA, 0x0154=>0x0155, 0x0049=>0x0069, 0x0053=>0x0073, 0x1E1E=>0x1E1F,
+			0x0134=>0x0135, 0x0427=>0x0447, 0x03A0=>0x03C0, 0x0418=>0x0438, 0x00D3=>0x00F3,
+			0x0420=>0x0440, 0x0404=>0x0454, 0x0415=>0x0435, 0x0429=>0x0449, 0x014A=>0x014B,
+			0x0411=>0x0431, 0x0409=>0x0459, 0x1E02=>0x1E03, 0x00D6=>0x00F6, 0x00D9=>0x00F9,
+			0x004E=>0x006E, 0x0401=>0x0451, 0x03A4=>0x03C4, 0x0423=>0x0443, 0x015C=>0x015D,
+			0x0403=>0x0453, 0x03A8=>0x03C8, 0x0158=>0x0159, 0x0047=>0x0067, 0x00C4=>0x00E4,
+			0x0386=>0x03AC, 0x0389=>0x03AE, 0x0166=>0x0167, 0x039E=>0x03BE, 0x0164=>0x0165,
+			0x0116=>0x0117, 0x0108=>0x0109, 0x0056=>0x0076, 0x00DE=>0x00FE, 0x0156=>0x0157,
+			0x00DA=>0x00FA, 0x1E60=>0x1E61, 0x1E82=>0x1E83, 0x00C2=>0x00E2, 0x0118=>0x0119,
+			0x0145=>0x0146, 0x0050=>0x0070, 0x0150=>0x0151, 0x042E=>0x044E, 0x0128=>0x0129,
+			0x03A7=>0x03C7, 0x013D=>0x013E, 0x0422=>0x0442, 0x005A=>0x007A, 0x0428=>0x0448,
+			0x03A1=>0x03C1, 0x1E80=>0x1E81, 0x016C=>0x016D, 0x00D5=>0x00F5, 0x0055=>0x0075,
+			0x0176=>0x0177, 0x00DC=>0x00FC, 0x1E56=>0x1E57, 0x03A3=>0x03C3, 0x041A=>0x043A,
+			0x004D=>0x006D, 0x016A=>0x016B, 0x0170=>0x0171, 0x0424=>0x0444, 0x00CC=>0x00EC,
+			0x0168=>0x0169, 0x039F=>0x03BF, 0x004B=>0x006B, 0x00D2=>0x00F2, 0x00C0=>0x00E0,
+			0x0414=>0x0434, 0x03A9=>0x03C9, 0x1E6A=>0x1E6B, 0x00C3=>0x00E3, 0x042D=>0x044D,
+			0x0416=>0x0436, 0x01A0=>0x01A1, 0x010C=>0x010D, 0x011C=>0x011D, 0x00D0=>0x00F0,
+			0x013B=>0x013C, 0x040F=>0x045F, 0x040A=>0x045A, 0x00C8=>0x00E8, 0x03A5=>0x03C5,
+			0x0046=>0x0066, 0x00DD=>0x00FD, 0x0043=>0x0063, 0x021A=>0x021B, 0x00CA=>0x00EA,
+			0x0399=>0x03B9, 0x0179=>0x017A, 0x00CF=>0x00EF, 0x01AF=>0x01B0, 0x0045=>0x0065,
+			0x039B=>0x03BB, 0x0398=>0x03B8, 0x039C=>0x03BC, 0x040C=>0x045C, 0x041F=>0x043F,
+			0x042C=>0x044C, 0x00DE=>0x00FE, 0x00D0=>0x00F0, 0x1EF2=>0x1EF3, 0x0048=>0x0068,
+			0x00CB=>0x00EB, 0x0110=>0x0111, 0x0413=>0x0433, 0x012E=>0x012F, 0x00C6=>0x00E6,
+			0x0058=>0x0078, 0x0160=>0x0161, 0x016E=>0x016F, 0x0391=>0x03B1, 0x0407=>0x0457,
+			0x0172=>0x0173, 0x0178=>0x00FF, 0x004F=>0x006F, 0x041B=>0x043B, 0x0395=>0x03B5,
+			0x0425=>0x0445, 0x0120=>0x0121, 0x017D=>0x017E, 0x017B=>0x017C, 0x0396=>0x03B6,
+			0x0392=>0x03B2, 0x0388=>0x03AD, 0x1E84=>0x1E85, 0x0174=>0x0175, 0x0051=>0x0071,
+			0x0417=>0x0437, 0x1E0A=>0x1E0B, 0x0147=>0x0148, 0x0104=>0x0105, 0x0408=>0x0458,
+			0x014C=>0x014D, 0x00CD=>0x00ED, 0x0059=>0x0079, 0x010A=>0x010B, 0x038F=>0x03CE,
+			0x0052=>0x0072, 0x0410=>0x0430, 0x0405=>0x0455, 0x0402=>0x0452, 0x0126=>0x0127,
+			0x0136=>0x0137, 0x012A=>0x012B, 0x038A=>0x03AF, 0x042B=>0x044B, 0x004C=>0x006C,
+			0x0397=>0x03B7, 0x0124=>0x0125, 0x0218=>0x0219, 0x00DB=>0x00FB, 0x011E=>0x011F,
+			0x041E=>0x043E, 0x1E40=>0x1E41, 0x039D=>0x03BD, 0x0106=>0x0107, 0x03AB=>0x03CB,
+			0x0426=>0x0446, 0x00DE=>0x00FE, 0x00C7=>0x00E7, 0x03AA=>0x03CA, 0x0421=>0x0441,
+			0x0412=>0x0432, 0x010E=>0x010F, 0x00D8=>0x00F8, 0x0057=>0x0077, 0x011A=>0x011B,
+			0x0054=>0x0074, 0x004A=>0x006A, 0x040B=>0x045B, 0x0406=>0x0456, 0x0102=>0x0103,
+			0x039B=>0x03BB, 0x00D1=>0x00F1, 0x041D=>0x043D, 0x038C=>0x03CC, 0x00C9=>0x00E9,
+			0x00D0=>0x00F0, 0x0407=>0x0457, 0x0122=>0x0123);
+	}
+
+	$uni = utf8_to_unicode($string);
+
+	if (!$uni)
+		return false;
+
+	$cnt = count($uni);
+
+	for ($i=0; $i < $cnt; $i++)
+		if (isset($UTF8_UPPER_TO_LOWER[$uni[$i]]))
+			$uni[$i] = $UTF8_UPPER_TO_LOWER[$uni[$i]];
+
+	return utf8_from_unicode($uni);
+}
+
+/**
+* UTF-8 aware alternative to strtoupper
+* Make a string uppercase
+* Note: The concept of a characters "case" only exists is some alphabets
+* such as Latin, Greek, Cyrillic, Armenian and archaic Georgian - it does
+* not exist in the Chinese alphabet, for example. See Unicode Standard
+* Annex #21: Case Mappings
+* Note: requires utf8_to_unicode and utf8_from_unicode
+* @author Andreas Gohr <andi at splitbrain.org>
+* @param string
+* @return mixed either string in lowercase or FALSE is UTF-8 invalid
+* @see http://www.php.net/strtoupper
+* @see utf8_to_unicode
+* @see utf8_from_unicode
+* @see http://www.unicode.org/reports/tr21/tr21-5.html
+* @see http://dev.splitbrain.org/view/darcs/dokuwiki/inc/utf8.php
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strtoupper($string)
+{
+	static $UTF8_LOWER_TO_UPPER = false;
+
+	if (!$UTF8_LOWER_TO_UPPER)
+	{
+		$UTF8_LOWER_TO_UPPER = array(
+			0x0061=>0x0041, 0x03C6=>0x03A6, 0x0163=>0x0162, 0x00E5=>0x00C5, 0x0062=>0x0042,
+			0x013A=>0x0139, 0x00E1=>0x00C1, 0x0142=>0x0141, 0x03CD=>0x038E, 0x0101=>0x0100,
+			0x0491=>0x0490, 0x03B4=>0x0394, 0x015B=>0x015A, 0x0064=>0x0044, 0x03B3=>0x0393,
+			0x00F4=>0x00D4, 0x044A=>0x042A, 0x0439=>0x0419, 0x0113=>0x0112, 0x043C=>0x041C,
+			0x015F=>0x015E, 0x0144=>0x0143, 0x00EE=>0x00CE, 0x045E=>0x040E, 0x044F=>0x042F,
+			0x03BA=>0x039A, 0x0155=>0x0154, 0x0069=>0x0049, 0x0073=>0x0053, 0x1E1F=>0x1E1E,
+			0x0135=>0x0134, 0x0447=>0x0427, 0x03C0=>0x03A0, 0x0438=>0x0418, 0x00F3=>0x00D3,
+			0x0440=>0x0420, 0x0454=>0x0404, 0x0435=>0x0415, 0x0449=>0x0429, 0x014B=>0x014A,
+			0x0431=>0x0411, 0x0459=>0x0409, 0x1E03=>0x1E02, 0x00F6=>0x00D6, 0x00F9=>0x00D9,
+			0x006E=>0x004E, 0x0451=>0x0401, 0x03C4=>0x03A4, 0x0443=>0x0423, 0x015D=>0x015C,
+			0x0453=>0x0403, 0x03C8=>0x03A8, 0x0159=>0x0158, 0x0067=>0x0047, 0x00E4=>0x00C4,
+			0x03AC=>0x0386, 0x03AE=>0x0389, 0x0167=>0x0166, 0x03BE=>0x039E, 0x0165=>0x0164,
+			0x0117=>0x0116, 0x0109=>0x0108, 0x0076=>0x0056, 0x00FE=>0x00DE, 0x0157=>0x0156,
+			0x00FA=>0x00DA, 0x1E61=>0x1E60, 0x1E83=>0x1E82, 0x00E2=>0x00C2, 0x0119=>0x0118,
+			0x0146=>0x0145, 0x0070=>0x0050, 0x0151=>0x0150, 0x044E=>0x042E, 0x0129=>0x0128,
+			0x03C7=>0x03A7, 0x013E=>0x013D, 0x0442=>0x0422, 0x007A=>0x005A, 0x0448=>0x0428,
+			0x03C1=>0x03A1, 0x1E81=>0x1E80, 0x016D=>0x016C, 0x00F5=>0x00D5, 0x0075=>0x0055,
+			0x0177=>0x0176, 0x00FC=>0x00DC, 0x1E57=>0x1E56, 0x03C3=>0x03A3, 0x043A=>0x041A,
+			0x006D=>0x004D, 0x016B=>0x016A, 0x0171=>0x0170, 0x0444=>0x0424, 0x00EC=>0x00CC,
+			0x0169=>0x0168, 0x03BF=>0x039F, 0x006B=>0x004B, 0x00F2=>0x00D2, 0x00E0=>0x00C0,
+			0x0434=>0x0414, 0x03C9=>0x03A9, 0x1E6B=>0x1E6A, 0x00E3=>0x00C3, 0x044D=>0x042D,
+			0x0436=>0x0416, 0x01A1=>0x01A0, 0x010D=>0x010C, 0x011D=>0x011C, 0x00F0=>0x00D0,
+			0x013C=>0x013B, 0x045F=>0x040F, 0x045A=>0x040A, 0x00E8=>0x00C8, 0x03C5=>0x03A5,
+			0x0066=>0x0046, 0x00FD=>0x00DD, 0x0063=>0x0043, 0x021B=>0x021A, 0x00EA=>0x00CA,
+			0x03B9=>0x0399, 0x017A=>0x0179, 0x00EF=>0x00CF, 0x01B0=>0x01AF, 0x0065=>0x0045,
+			0x03BB=>0x039B, 0x03B8=>0x0398, 0x03BC=>0x039C, 0x045C=>0x040C, 0x043F=>0x041F,
+			0x044C=>0x042C, 0x00FE=>0x00DE, 0x00F0=>0x00D0, 0x1EF3=>0x1EF2, 0x0068=>0x0048,
+			0x00EB=>0x00CB, 0x0111=>0x0110, 0x0433=>0x0413, 0x012F=>0x012E, 0x00E6=>0x00C6,
+			0x0078=>0x0058, 0x0161=>0x0160, 0x016F=>0x016E, 0x03B1=>0x0391, 0x0457=>0x0407,
+			0x0173=>0x0172, 0x00FF=>0x0178, 0x006F=>0x004F, 0x043B=>0x041B, 0x03B5=>0x0395,
+			0x0445=>0x0425, 0x0121=>0x0120, 0x017E=>0x017D, 0x017C=>0x017B, 0x03B6=>0x0396,
+			0x03B2=>0x0392, 0x03AD=>0x0388, 0x1E85=>0x1E84, 0x0175=>0x0174, 0x0071=>0x0051,
+			0x0437=>0x0417, 0x1E0B=>0x1E0A, 0x0148=>0x0147, 0x0105=>0x0104, 0x0458=>0x0408,
+			0x014D=>0x014C, 0x00ED=>0x00CD, 0x0079=>0x0059, 0x010B=>0x010A, 0x03CE=>0x038F,
+			0x0072=>0x0052, 0x0430=>0x0410, 0x0455=>0x0405, 0x0452=>0x0402, 0x0127=>0x0126,
+			0x0137=>0x0136, 0x012B=>0x012A, 0x03AF=>0x038A, 0x044B=>0x042B, 0x006C=>0x004C,
+			0x03B7=>0x0397, 0x0125=>0x0124, 0x0219=>0x0218, 0x00FB=>0x00DB, 0x011F=>0x011E,
+			0x043E=>0x041E, 0x1E41=>0x1E40, 0x03BD=>0x039D, 0x0107=>0x0106, 0x03CB=>0x03AB,
+			0x0446=>0x0426, 0x00FE=>0x00DE, 0x00E7=>0x00C7, 0x03CA=>0x03AA, 0x0441=>0x0421,
+			0x0432=>0x0412, 0x010F=>0x010E, 0x00F8=>0x00D8, 0x0077=>0x0057, 0x011B=>0x011A,
+			0x0074=>0x0054, 0x006A=>0x004A, 0x045B=>0x040B, 0x0456=>0x0406, 0x0103=>0x0102,
+			0x03BB=>0x039B, 0x00F1=>0x00D1, 0x043D=>0x041D, 0x03CC=>0x038C, 0x00E9=>0x00C9,
+			0x00F0=>0x00D0, 0x0457=>0x0407, 0x0123=>0x0122);
+	}
+
+	$uni = utf8_to_unicode($string);
+
+	if (!$uni)
+		return false;
+
+	$cnt = count($uni);
+
+	for ($i=0; $i < $cnt; $i++)
+		if(isset($UTF8_LOWER_TO_UPPER[$uni[$i]]))
+			$uni[$i] = $UTF8_LOWER_TO_UPPER[$uni[$i]];
+
+	return utf8_from_unicode($uni);
+}
diff --git a/include/utf8/native/index.html b/include/utf8/native/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/include/utf8/native/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/include/utf8/ord.php b/include/utf8/ord.php
new file mode 100644
index 0000000..a333f96
--- /dev/null
+++ b/include/utf8/ord.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+* @version $Id: ord.php,v 1.4 2006/09/11 15:22:54 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware alternative to ord
+* Returns the unicode ordinal for a character
+* @param string UTF-8 encoded character
+* @return int unicode ordinal for the character
+* @see http://www.php.net/ord
+* @see http://www.php.net/manual/en/function.ord.php#46267
+*/
+function utf8_ord($chr)
+{
+	$ord0 = ord($chr);
+
+	if ($ord0 >= 0 && $ord0 <= 127)
+		return $ord0;
+
+	if (!isset($chr{1}))
+	{
+		trigger_error('Short sequence - at least 2 bytes expected, only 1 seen');
+		return false;
+	}
+
+	$ord1 = ord($chr{1});
+	if ($ord0 >= 192 && $ord0 <= 223)
+		return ($ord0 - 192) * 64 + ($ord1 - 128);
+
+	if (!isset($chr{2}))
+	{
+		trigger_error('Short sequence - at least 3 bytes expected, only 2 seen');
+		return false;
+	}
+
+	$ord2 = ord($chr{2});
+	if ($ord0 >= 224 && $ord0 <= 239)
+		return ($ord0-224)*4096 + ($ord1-128)*64 + ($ord2-128);
+
+	if (!isset($chr{3}))
+	{
+		trigger_error('Short sequence - at least 4 bytes expected, only 3 seen');
+		return false;
+	}
+
+	$ord3 = ord($chr{3});
+	if ($ord0>=240 && $ord0<=247)
+		return ($ord0-240)*262144 + ($ord1-128)*4096 + ($ord2-128)*64 + ($ord3-128);
+
+	if (!isset($chr{4}))
+	{
+		trigger_error('Short sequence - at least 5 bytes expected, only 4 seen');
+		return false;
+	}
+
+	$ord4 = ord($chr{4});
+	if ($ord0>=248 && $ord0<=251)
+		return ($ord0-248)*16777216 + ($ord1-128)*262144 + ($ord2-128)*4096 + ($ord3-128)*64 + ($ord4-128);
+
+	if (!isset($chr{5}))
+	{
+		trigger_error('Short sequence - at least 6 bytes expected, only 5 seen');
+		return false;
+	}
+
+	if ($ord0>=252 && $ord0<=253)
+		return ($ord0-252) * 1073741824 + ($ord1-128)*16777216 + ($ord2-128)*262144 + ($ord3-128)*4096  + ($ord4-128)*64 + (ord($c{5})-128);
+
+	if ($ord0 >= 254 && $ord0 <= 255)
+	{
+		trigger_error('Invalid UTF-8 with surrogate ordinal '.$ord0);
+		return false;
+	}
+}
diff --git a/include/utf8/str_ireplace.php b/include/utf8/str_ireplace.php
new file mode 100644
index 0000000..7257b0a
--- /dev/null
+++ b/include/utf8/str_ireplace.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+* @version $Id: str_ireplace.php,v 1.2 2007/08/12 01:20:46 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware alternative to str_ireplace
+* Case-insensitive version of str_replace
+* Note: requires utf8_strtolower
+* Note: it's not fast and gets slower if $search / $replace is array
+* Notes: it's based on the assumption that the lower and uppercase
+* versions of a UTF-8 character will have the same length in bytes
+* which is currently true given the hash table to strtolower
+* @param string
+* @return string
+* @see http://www.php.net/str_ireplace
+* @see utf8_strtolower
+* @package utf8
+* @subpackage strings
+*/
+function utf8_ireplace($search, $replace, $str, $count=null)
+{
+	if (!is_array($search))
+	{
+		$slen = strlen($search);
+
+		if ($slen == 0)
+			return $str;
+
+		$lendif = strlen($replace) - strlen($search);
+		$search = utf8_strtolower($search);
+
+		$search = preg_quote($search);
+		$lstr = utf8_strtolower($str);
+		$i = 0;
+		$matched = 0;
+
+		while (preg_match('/(.*)'.$search.'/Us', $lstr, $matches))
+		{
+			if ($i === $count)
+				break;
+
+			$mlen = strlen($matches[0]);
+			$lstr = substr($lstr, $mlen);
+			$str = substr_replace($str, $replace, $matched+strlen($matches[1]), $slen);
+			$matched += $mlen + $lendif;
+			$i++;
+		}
+
+		return $str;
+	}
+	else
+	{
+		foreach (array_keys($search) as $k)
+		{
+			if (is_array($replace))
+			{
+				if (array_key_exists($k, $replace))
+					$str = utf8_ireplace($search[$k], $replace[$k], $str, $count);
+				else
+					$str = utf8_ireplace($search[$k], '', $str, $count);
+			}
+			else
+				$str = utf8_ireplace($search[$k], $replace, $str, $count);
+		}
+
+		return $str;
+	}
+}
diff --git a/include/utf8/str_pad.php b/include/utf8/str_pad.php
new file mode 100644
index 0000000..93a559a
--- /dev/null
+++ b/include/utf8/str_pad.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+* @version $Id: str_pad.php,v 1.1 2006/09/03 09:25:13 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* Replacement for str_pad. $padStr may contain multi-byte characters.
+*
+* @author Oliver Saunders <oliver (a) osinternetservices.com>
+* @param string $input
+* @param int $length
+* @param string $padStr
+* @param int $type ( same constants as str_pad )
+* @return string
+* @see http://www.php.net/str_pad
+* @see utf8_substr
+* @package utf8
+* @subpackage strings
+*/
+function utf8_str_pad($input, $length, $padStr=' ', $type=STR_PAD_RIGHT)
+{
+	$inputLen = utf8_strlen($input);
+	if ($length <= $inputLen)
+		return $input;
+
+	$padStrLen = utf8_strlen($padStr);
+	$padLen = $length - $inputLen;
+
+	if ($type == STR_PAD_RIGHT)
+	{
+		$repeatTimes = ceil($padLen / $padStrLen);
+		return utf8_substr($input.str_repeat($padStr, $repeatTimes), 0, $length);
+	}
+
+	if ($type == STR_PAD_LEFT)
+	{
+		$repeatTimes = ceil($padLen / $padStrLen);
+		return utf8_substr(str_repeat($padStr, $repeatTimes), 0, floor($padLen)).$input;
+	}
+
+	if ($type == STR_PAD_BOTH)
+	{
+		$padLen /= 2;
+		$padAmountLeft = floor($padLen);
+		$padAmountRight = ceil($padLen);
+		$repeatTimesLeft = ceil($padAmountLeft / $padStrLen);
+		$repeatTimesRight = ceil($padAmountRight / $padStrLen);
+
+		$paddingLeft = utf8_substr(str_repeat($padStr, $repeatTimesLeft), 0, $padAmountLeft);
+		$paddingRight = utf8_substr(str_repeat($padStr, $repeatTimesRight), 0, $padAmountLeft);
+
+		return $paddingLeft.$input.$paddingRight;
+	}
+
+	trigger_error('utf8_str_pad: Unknown padding type ('.$type.')', E_USER_ERROR);
+}
diff --git a/include/utf8/str_split.php b/include/utf8/str_split.php
new file mode 100644
index 0000000..15bc215
--- /dev/null
+++ b/include/utf8/str_split.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+* @version $Id: str_split.php,v 1.1 2006/02/25 13:50:17 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware alternative to str_split
+* Convert a string to an array
+* Note: requires utf8_strlen to be loaded
+* @param string UTF-8 encoded
+* @param int number to characters to split string by
+* @return string characters in string reverses
+* @see http://www.php.net/str_split
+* @see utf8_strlen
+* @package utf8
+* @subpackage strings
+*/
+function utf8_str_split($str, $split_len=1)
+{
+	if (!preg_match('/^[0-9]+$/',$split_len) || $split_len < 1)
+		return false;
+
+	$len = utf8_strlen($str);
+	if ($len <= $split_len)
+		return array($str);
+
+	preg_match_all('/.{'.$split_len.'}|[^\x00]{1,'.$split_len.'}$/us', $str, $ar);
+
+	return $ar[0];
+}
diff --git a/include/utf8/strcasecmp.php b/include/utf8/strcasecmp.php
new file mode 100644
index 0000000..423f443
--- /dev/null
+++ b/include/utf8/strcasecmp.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+* @version $Id: strcasecmp.php,v 1.1 2006/02/25 13:50:17 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware alternative to strcasecmp
+* A case insensivite string comparison
+* Note: requires utf8_strtolower
+* @param string
+* @param string
+* @return int
+* @see http://www.php.net/strcasecmp
+* @see utf8_strtolower
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strcasecmp($strX, $strY)
+{
+	$strX = utf8_strtolower($strX);
+	$strY = utf8_strtolower($strY);
+
+	return strcmp($strX, $strY);
+}
diff --git a/include/utf8/strcspn.php b/include/utf8/strcspn.php
new file mode 100644
index 0000000..1e3756d
--- /dev/null
+++ b/include/utf8/strcspn.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+* @version $Id: strcspn.php,v 1.1 2006/02/25 13:50:17 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware alternative to strcspn
+* Find length of initial segment not matching mask
+* Note: requires utf8_strlen and utf8_substr (if start, length are used)
+* @param string
+* @return int
+* @see http://www.php.net/strcspn
+* @see utf8_strlen
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strcspn($str, $mask, $start=null, $length=null)
+{
+	if (empty($mask) || strlen($mask) == 0)
+		return null;
+
+	$mask = preg_replace('!([\\\\\\-\\]\\[/^])!','\\\${1}', $mask);
+
+	if ($start !== null || $length !== null)
+		$str = utf8_substr($str, $start, $length);
+
+	preg_match('/^[^'.$mask.']+/u', $str, $matches);
+
+	if (isset($matches[0]))
+		return utf8_strlen($matches[0]);
+
+	return 0;
+}
diff --git a/include/utf8/stristr.php b/include/utf8/stristr.php
new file mode 100644
index 0000000..fb9e6a5
--- /dev/null
+++ b/include/utf8/stristr.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+* @version $Id: stristr.php,v 1.1 2006/02/25 13:50:17 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware alternative to stristr
+* Find first occurrence of a string using case insensitive comparison
+* Note: requires utf8_strtolower
+* @param string
+* @param string
+* @return int
+* @see http://www.php.net/strcasecmp
+* @see utf8_strtolower
+* @package utf8
+* @subpackage strings
+*/
+function utf8_stristr($str, $search)
+{
+	if (strlen($search) == 0)
+		return $str;
+
+	$lstr = utf8_strtolower($str);
+	$lsearch = utf8_strtolower($search);
+	preg_match('/^(.*)'.preg_quote($lsearch).'/Us', $lstr, $matches);
+
+	if (count($matches) == 2)
+		return substr($str, strlen($matches[1]));
+
+	return false;
+}
diff --git a/include/utf8/strrev.php b/include/utf8/strrev.php
new file mode 100644
index 0000000..ae9c32b
--- /dev/null
+++ b/include/utf8/strrev.php
@@ -0,0 +1,22 @@
+<?php
+
+/**
+* @version $Id: strrev.php,v 1.1 2006/02/25 13:50:17 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware alternative to strrev
+* Reverse a string
+* @param string UTF-8 encoded
+* @return string characters in string reverses
+* @see http://www.php.net/strrev
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strrev($str)
+{
+	preg_match_all('/./us', $str, $ar);
+	return implode(array_reverse($ar[0]));
+}
diff --git a/include/utf8/strspn.php b/include/utf8/strspn.php
new file mode 100644
index 0000000..424ceb7
--- /dev/null
+++ b/include/utf8/strspn.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+* @version $Id: strspn.php,v 1.1 2006/02/25 13:50:17 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware alternative to strspn
+* Find length of initial segment matching mask
+* Note: requires utf8_strlen and utf8_substr (if start, length are used)
+* @param string
+* @return int
+* @see http://www.php.net/strspn
+* @package utf8
+* @subpackage strings
+*/
+function utf8_strspn($str, $mask, $start=null, $length=null)
+{
+	$mask = preg_replace('!([\\\\\\-\\]\\[/^])!', '\\\${1}', $mask);
+
+	if ($start !== null || $length !== null)
+		$str = utf8_substr($str, $start, $length);
+
+	preg_match('/^['.$mask.']+/u', $str, $matches);
+
+	if (isset($matches[0]))
+		return utf8_strlen($matches[0]);
+
+	return 0;
+}
diff --git a/include/utf8/substr_replace.php b/include/utf8/substr_replace.php
new file mode 100644
index 0000000..7fc7369
--- /dev/null
+++ b/include/utf8/substr_replace.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+* @version $Id: substr_replace.php,v 1.1 2006/02/25 13:50:17 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware substr_replace.
+* Note: requires utf8_substr to be loaded
+* @see http://www.php.net/substr_replace
+* @see utf8_strlen
+* @see utf8_substr
+*/
+function utf8_substr_replace($str, $repl, $start , $length=null)
+{
+	preg_match_all('/./us', $str, $ar);
+	preg_match_all('/./us', $repl, $rar);
+
+	if($length === null)
+		$length = utf8_strlen($str);
+
+	array_splice($ar[0], $start, $length, $rar[0]);
+
+	return implode($ar[0]);
+}
diff --git a/include/utf8/trim.php b/include/utf8/trim.php
new file mode 100644
index 0000000..3d22840
--- /dev/null
+++ b/include/utf8/trim.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+* @version $Id: trim.php,v 1.1 2006/02/25 13:50:17 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware replacement for ltrim()
+* Note: you only need to use this if you are supplying the charlist
+* optional arg and it contains UTF-8 characters. Otherwise ltrim will
+* work normally on a UTF-8 string
+* @author Andreas Gohr <andi at splitbrain.org>
+* @see http://www.php.net/ltrim
+* @see http://dev.splitbrain.org/view/darcs/dokuwiki/inc/utf8.php
+* @return string
+* @package utf8
+* @subpackage strings
+*/
+function utf8_ltrim( $str, $charlist=false)
+{
+	if($charlist === false)
+		return ltrim($str);
+
+	// Quote charlist for use in a characterclass
+	$charlist = preg_replace('!([\\\\\\-\\]\\[/^])!', '\\\${1}', $charlist);
+
+	return preg_replace('/^['.$charlist.']+/u', '', $str);
+}
+
+/**
+* UTF-8 aware replacement for rtrim()
+* Note: you only need to use this if you are supplying the charlist
+* optional arg and it contains UTF-8 characters. Otherwise rtrim will
+* work normally on a UTF-8 string
+* @author Andreas Gohr <andi at splitbrain.org>
+* @see http://www.php.net/rtrim
+* @see http://dev.splitbrain.org/view/darcs/dokuwiki/inc/utf8.php
+* @return string
+* @package utf8
+* @subpackage strings
+*/
+function utf8_rtrim($str, $charlist=false)
+{
+	if($charlist === false)
+		return rtrim($str);
+
+	// Quote charlist for use in a characterclass
+	$charlist = preg_replace('!([\\\\\\-\\]\\[/^])!', '\\\${1}', $charlist);
+
+	return preg_replace('/['.$charlist.']+$/u', '', $str);
+}
+
+//---------------------------------------------------------------
+/**
+* UTF-8 aware replacement for trim()
+* Note: you only need to use this if you are supplying the charlist
+* optional arg and it contains UTF-8 characters. Otherwise trim will
+* work normally on a UTF-8 string
+* @author Andreas Gohr <andi at splitbrain.org>
+* @see http://www.php.net/trim
+* @see http://dev.splitbrain.org/view/darcs/dokuwiki/inc/utf8.php
+* @return string
+* @package utf8
+* @subpackage strings
+*/
+function utf8_trim( $str, $charlist=false)
+{
+	if($charlist === false)
+		return trim($str);
+
+	return utf8_ltrim(utf8_rtrim($str, $charlist), $charlist);
+}
diff --git a/include/utf8/ucfirst.php b/include/utf8/ucfirst.php
new file mode 100644
index 0000000..efee55d
--- /dev/null
+++ b/include/utf8/ucfirst.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+* @version $Id: ucfirst.php,v 1.1 2006/02/25 13:50:17 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware alternative to ucfirst
+* Make a string's first character uppercase
+* Note: requires utf8_strtoupper
+* @param string
+* @return string with first character as upper case (if applicable)
+* @see http://www.php.net/ucfirst
+* @see utf8_strtoupper
+* @package utf8
+* @subpackage strings
+*/
+function utf8_ucfirst($str)
+{
+	switch (utf8_strlen($str))
+	{
+		case 0:
+			return '';
+			break;
+		case 1:
+			return utf8_strtoupper($str);
+			break;
+		default:
+			preg_match('/^(.{1})(.*)$/us', $str, $matches);
+			return utf8_strtoupper($matches[1]).$matches[2];
+			break;
+	}
+}
diff --git a/include/utf8/ucwords.php b/include/utf8/ucwords.php
new file mode 100644
index 0000000..e985cee
--- /dev/null
+++ b/include/utf8/ucwords.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+* @version $Id: ucwords.php,v 1.1 2006/02/25 13:50:17 harryf Exp $
+* @package utf8
+* @subpackage strings
+*/
+
+/**
+* UTF-8 aware alternative to ucwords
+* Uppercase the first character of each word in a string
+* Note: requires utf8_substr_replace and utf8_strtoupper
+* @param string
+* @return string with first char of each word uppercase
+* @see http://www.php.net/ucwords
+* @package utf8
+* @subpackage strings
+*/
+function utf8_ucwords($str)
+{
+	// Note: [\x0c\x09\x0b\x0a\x0d\x20] matches;
+	// Form feeds, horizontal tabs, vertical tabs, linefeeds and carriage returns
+	// This corresponds to the definition of a "word" defined at http://www.php.net/ucwords
+	$pattern = '/(^|([\x0c\x09\x0b\x0a\x0d\x20]+))([^\x0c\x09\x0b\x0a\x0d\x20]{1})[^\x0c\x09\x0b\x0a\x0d\x20]*/u';
+
+	return preg_replace_callback($pattern, 'utf8_ucwords_callback', $str);
+}
+
+/**
+* Callback function for preg_replace_callback call in utf8_ucwords
+* You don't need to call this yourself
+* @param array of matches corresponding to a single word
+* @return string with first char of the word in uppercase
+* @see utf8_ucwords
+* @see utf8_strtoupper
+* @package utf8
+* @subpackage strings
+*/
+function utf8_ucwords_callback($matches)
+{
+	$leadingws = $matches[2];
+	$ucfirst = utf8_strtoupper($matches[3]);
+	$ucword = utf8_substr_replace(ltrim($matches[0]), $ucfirst, 0, 1);
+
+	return $leadingws.$ucword;
+}
diff --git a/include/utf8/utf8.php b/include/utf8/utf8.php
new file mode 100644
index 0000000..281f18c
--- /dev/null
+++ b/include/utf8/utf8.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+* This is the dynamic loader for the library. It checks whether you have
+* the mbstring extension available and includes relevant files
+* on that basis, falling back to the native (as in written in PHP) version
+* if mbstring is unavailabe.
+*
+* It's probably easiest to use this, if you don't want to understand
+* the dependencies involved, in conjunction with PHP versions etc. At
+* the same time, you might get better performance by managing loading
+* yourself. The smartest way to do this, bearing in mind performance,
+* is probably to "load on demand" - i.e. just before you use these
+* functions in your code, load the version you need.
+*
+* It makes sure the the following functions are available;
+* utf8_strlen, utf8_strpos, utf8_strrpos, utf8_substr,
+* utf8_strtolower, utf8_strtoupper
+* Other functions in the ./native directory depend on these
+* six functions being available
+* @package utf8
+*/
+
+// Check whether PCRE has been compiled with UTF-8 support
+$UTF8_ar = array();
+if (preg_match('/^.{1}$/u', "ñ", $UTF8_ar) != 1)
+	trigger_error('PCRE is not compiled with UTF-8 support', E_USER_ERROR);
+
+unset($UTF8_ar);
+
+// Put the current directory in this constant
+if (!defined('UTF8'))
+	define('UTF8', dirname(__FILE__));
+
+if (extension_loaded('mbstring') && !defined('UTF8_USE_MBSTRING') && !defined('UTF8_USE_NATIVE'))
+	define('UTF8_USE_MBSTRING', true);
+else
+	define('UTF8_USE_NATIVE', true);
+
+// utf8_strpos() and utf8_strrpos() need utf8_bad_strip() to strip invalid
+// characters. Mbstring doesn't do this while the Native implementation does.
+require UTF8.'/utils/bad.php';
+
+if (defined('UTF8_USE_MBSTRING'))
+{
+	/**
+	* If string overloading is active, it will break many of the
+	* native implementations. mbstring.func_overload must be set
+	* to 0, 1 or 4 in php.ini (string overloading disabled).
+	* Also need to check we have the correct internal mbstring
+	* encoding
+	*/
+	if (ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING)
+		trigger_error('String functions are overloaded by mbstring', E_USER_ERROR);
+
+	mb_language('uni');
+	mb_internal_encoding('UTF-8');
+
+	if (!defined('UTF8_CORE'))
+		require UTF8.'/mbstring/core.php';
+}
+elseif (defined('UTF8_USE_NATIVE'))
+{
+	if (!defined('UTF8_CORE'))
+	{
+		require UTF8.'/utils/unicode.php';
+		require UTF8.'/native/core.php';
+	}
+}
+
+// Load the native implementation of utf8_trim
+require UTF8.'/trim.php';
diff --git a/include/utf8/utils/ascii.php b/include/utf8/utils/ascii.php
new file mode 100644
index 0000000..af75b92
--- /dev/null
+++ b/include/utf8/utils/ascii.php
@@ -0,0 +1,221 @@
+<?php
+
+/**
+* Tools to help with ASCII in UTF-8
+* @version $Id: ascii.php,v 1.5 2006/10/16 20:38:12 harryf Exp $
+* @package utf8
+* @subpackage ascii
+*/
+
+/**
+* Tests whether a string contains only 7bit ASCII bytes.
+* You might use this to conditionally check whether a string
+* needs handling as UTF-8 or not, potentially offering performance
+* benefits by using the native PHP equivalent if it's just ASCII e.g.;
+*
+* <code>
+* if ( utf8_is_ascii($someString) ) {
+*     // It's just ASCII - use the native PHP version
+*     $someString = strtolower($someString);
+* } else {
+*     $someString = utf8_strtolower($someString);
+* }
+* </code>
+*
+* @param string
+* @return boolean TRUE if it's all ASCII
+* @package utf8
+* @subpackage ascii
+* @see utf8_is_ascii_ctrl
+*/
+function utf8_is_ascii($str)
+{
+	// Search for any bytes which are outside the ASCII range...
+	return (preg_match('/(?:[^\x00-\x7F])/', $str) !== 1);
+}
+
+/**
+* Tests whether a string contains only 7bit ASCII bytes with device
+* control codes omitted. The device control codes can be found on the
+* second table here: http://www.w3schools.com/tags/ref_ascii.asp
+*
+* @param string
+* @return boolean TRUE if it's all ASCII without device control codes
+* @package utf8
+* @subpackage ascii
+* @see utf8_is_ascii
+*/
+function utf8_is_ascii_ctrl($str)
+{
+	// Search for any bytes which are outside the ASCII range, or are device control codes
+	if (strlen($str) > 0)
+		return (preg_match('/[^\x09\x0A\x0D\x20-\x7E]/', $str) !== 1);
+
+	return false;
+}
+
+/**
+* Strip out all non-7bit ASCII bytes
+* If you need to transmit a string to system which you know can only
+* support 7bit ASCII, you could use this function.
+* @param string
+* @return string with non ASCII bytes removed
+* @package utf8
+* @subpackage ascii
+* @see utf8_strip_non_ascii_ctrl
+*/
+function utf8_strip_non_ascii($str)
+{
+	ob_start();
+
+	while (preg_match('/^([\x00-\x7F]+)|([^\x00-\x7F]+)/S', $str, $matches))
+	{
+		if (!isset($matches[2]))
+			echo $matches[0];
+
+		$str = substr($str, strlen($matches[0]));
+	}
+
+	$result = ob_get_contents();
+	ob_end_clean();
+
+	return $result;
+}
+
+/**
+* Strip out device control codes in the ASCII range
+* which are not permitted in XML. Note that this leaves
+* multi-byte characters untouched - it only removes device
+* control codes
+* @see http://hsivonen.iki.fi/producing-xml/#controlchar
+* @param string
+* @return string control codes removed
+*/
+function utf8_strip_ascii_ctrl($str)
+{
+	ob_start();
+
+	while (preg_match('/^([^\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+)|([\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+)/S', $str, $matches))
+	{
+		if (!isset($matches[2]))
+			echo $matches[0];
+
+		$str = substr($str, strlen($matches[0]));
+	}
+
+	$result = ob_get_contents();
+	ob_end_clean();
+
+	return $result;
+}
+
+/**
+* Strip out all non 7bit ASCII bytes and ASCII device control codes.
+* For a list of ASCII device control codes see the 2nd table here:
+* http://www.w3schools.com/tags/ref_ascii.asp
+*
+* @param string
+* @return boolean TRUE if it's all ASCII
+* @package utf8
+* @subpackage ascii
+*/
+function utf8_strip_non_ascii_ctrl($str)
+{
+	ob_start();
+
+	while (preg_match( '/^([\x09\x0A\x0D\x20-\x7E]+)|([^\x09\x0A\x0D\x20-\x7E]+)/S', $str, $matches))
+	{
+		if (!isset($matches[2]))
+			echo $matches[0];
+
+		$str = substr($str, strlen($matches[0]));
+	}
+
+	$result = ob_get_contents();
+	ob_end_clean();
+
+	return $result;
+}
+
+/**
+* Replace accented UTF-8 characters by unaccented ASCII-7 "equivalents".
+* The purpose of this function is to replace characters commonly found in Latin
+* alphabets with something more or less equivalent from the ASCII range. This can
+* be useful for converting a UTF-8 to something ready for a filename, for example.
+* Following the use of this function, you would probably also pass the string
+* through utf8_strip_non_ascii to clean out any other non-ASCII chars
+* Use the optional parameter to just deaccent lower ($case = -1) or upper ($case = 1)
+* letters. Default is to deaccent both cases ($case = 0)
+*
+* For a more complete implementation of transliteration, see the utf8_to_ascii package
+* available from the phputf8 project downloads:
+* http://prdownloads.sourceforge.net/phputf8
+*
+* @param string UTF-8 string
+* @param int (optional) -1 lowercase only, +1 uppercase only, 1 both cases
+* @param string UTF-8 with accented characters replaced by ASCII chars
+* @return string accented chars replaced with ascii equivalents
+* @author Andreas Gohr <andi at splitbrain.org>
+* @package utf8
+* @subpackage ascii
+*/
+function utf8_accents_to_ascii($str, $case=0)
+{
+	static $UTF8_LOWER_ACCENTS = null;
+	static $UTF8_UPPER_ACCENTS = null;
+
+	if($case <= 0)
+	{
+
+		if (is_null($UTF8_LOWER_ACCENTS))
+		{
+			$UTF8_LOWER_ACCENTS = array(
+				'à' => 'a', 'ô' => 'o', 'ď' => 'd', 'ḟ' => 'f', 'ë' => 'e', 'š' => 's', 'ơ' => 'o',
+				'ß' => 'ss', 'ă' => 'a', 'ř' => 'r', 'ț' => 't', 'ň' => 'n', 'ā' => 'a', 'ķ' => 'k',
+				'ŝ' => 's', 'ỳ' => 'y', 'ņ' => 'n', 'ĺ' => 'l', 'ħ' => 'h', 'ṗ' => 'p', 'ó' => 'o',
+				'ú' => 'u', 'ě' => 'e', 'é' => 'e', 'ç' => 'c', 'ẁ' => 'w', 'ċ' => 'c', 'õ' => 'o',
+				'ṡ' => 's', 'ø' => 'o', 'ģ' => 'g', 'ŧ' => 't', 'ș' => 's', 'ė' => 'e', 'ĉ' => 'c',
+				'ś' => 's', 'î' => 'i', 'ű' => 'u', 'ć' => 'c', 'ę' => 'e', 'ŵ' => 'w', 'ṫ' => 't',
+				'ū' => 'u', 'č' => 'c', 'ö' => 'oe', 'è' => 'e', 'ŷ' => 'y', 'ą' => 'a', 'ł' => 'l',
+				'ų' => 'u', 'ů' => 'u', 'ş' => 's', 'ğ' => 'g', 'ļ' => 'l', 'ƒ' => 'f', 'ž' => 'z',
+				'ẃ' => 'w', 'ḃ' => 'b', 'å' => 'a', 'ì' => 'i', 'ï' => 'i', 'ḋ' => 'd', 'ť' => 't',
+				'ŗ' => 'r', 'ä' => 'ae', 'í' => 'i', 'ŕ' => 'r', 'ê' => 'e', 'ü' => 'ue', 'ò' => 'o',
+				'ē' => 'e', 'ñ' => 'n', 'ń' => 'n', 'ĥ' => 'h', 'ĝ' => 'g', 'đ' => 'd', 'ĵ' => 'j',
+				'ÿ' => 'y', 'ũ' => 'u', 'ŭ' => 'u', 'ư' => 'u', 'ţ' => 't', 'ý' => 'y', 'ő' => 'o',
+				'â' => 'a', 'ľ' => 'l', 'ẅ' => 'w', 'ż' => 'z', 'ī' => 'i', 'ã' => 'a', 'ġ' => 'g',
+				'ṁ' => 'm', 'ō' => 'o', 'ĩ' => 'i', 'ù' => 'u', 'į' => 'i', 'ź' => 'z', 'á' => 'a',
+				'û' => 'u', 'þ' => 'th', 'ð' => 'dh', 'æ' => 'ae', 'µ' => 'u', 'ĕ' => 'e',
+				);
+		}
+
+		$str = str_replace(array_keys($UTF8_LOWER_ACCENTS), array_values($UTF8_LOWER_ACCENTS), $str);
+	}
+
+	if($case >= 0)
+	{
+		if (is_null($UTF8_UPPER_ACCENTS))
+		{
+			$UTF8_UPPER_ACCENTS = array(
+				'À' => 'A', 'Ô' => 'O', 'Ď' => 'D', 'Ḟ' => 'F', 'Ë' => 'E', 'Š' => 'S', 'Ơ' => 'O',
+				'Ă' => 'A', 'Ř' => 'R', 'Ț' => 'T', 'Ň' => 'N', 'Ā' => 'A', 'Ķ' => 'K',
+				'Ŝ' => 'S', 'Ỳ' => 'Y', 'Ņ' => 'N', 'Ĺ' => 'L', 'Ħ' => 'H', 'Ṗ' => 'P', 'Ó' => 'O',
+				'Ú' => 'U', 'Ě' => 'E', 'É' => 'E', 'Ç' => 'C', 'Ẁ' => 'W', 'Ċ' => 'C', 'Õ' => 'O',
+				'Ṡ' => 'S', 'Ø' => 'O', 'Ģ' => 'G', 'Ŧ' => 'T', 'Ș' => 'S', 'Ė' => 'E', 'Ĉ' => 'C',
+				'Ś' => 'S', 'Î' => 'I', 'Ű' => 'U', 'Ć' => 'C', 'Ę' => 'E', 'Ŵ' => 'W', 'Ṫ' => 'T',
+				'Ū' => 'U', 'Č' => 'C', 'Ö' => 'Oe', 'È' => 'E', 'Ŷ' => 'Y', 'Ą' => 'A', 'Ł' => 'L',
+				'Ų' => 'U', 'Ů' => 'U', 'Ş' => 'S', 'Ğ' => 'G', 'Ļ' => 'L', 'Ƒ' => 'F', 'Ž' => 'Z',
+				'Ẃ' => 'W', 'Ḃ' => 'B', 'Å' => 'A', 'Ì' => 'I', 'Ï' => 'I', 'Ḋ' => 'D', 'Ť' => 'T',
+				'Ŗ' => 'R', 'Ä' => 'Ae', 'Í' => 'I', 'Ŕ' => 'R', 'Ê' => 'E', 'Ü' => 'Ue', 'Ò' => 'O',
+				'Ē' => 'E', 'Ñ' => 'N', 'Ń' => 'N', 'Ĥ' => 'H', 'Ĝ' => 'G', 'Đ' => 'D', 'Ĵ' => 'J',
+				'Ÿ' => 'Y', 'Ũ' => 'U', 'Ŭ' => 'U', 'Ư' => 'U', 'Ţ' => 'T', 'Ý' => 'Y', 'Ő' => 'O',
+				'Â' => 'A', 'Ľ' => 'L', 'Ẅ' => 'W', 'Ż' => 'Z', 'Ī' => 'I', 'Ã' => 'A', 'Ġ' => 'G',
+				'Ṁ' => 'M', 'Ō' => 'O', 'Ĩ' => 'I', 'Ù' => 'U', 'Į' => 'I', 'Ź' => 'Z', 'Á' => 'A',
+				'Û' => 'U', 'Þ' => 'Th', 'Ð' => 'Dh', 'Æ' => 'Ae', 'Ĕ' => 'E',
+				);
+		}
+
+		$str = str_replace(array_keys($UTF8_UPPER_ACCENTS), array_values($UTF8_UPPER_ACCENTS),  $str);
+	}
+
+	return $str;
+}
diff --git a/include/utf8/utils/bad.php b/include/utf8/utils/bad.php
new file mode 100644
index 0000000..78e9d17
--- /dev/null
+++ b/include/utf8/utils/bad.php
@@ -0,0 +1,435 @@
+<?php
+
+/**
+* @version $Id: bad.php,v 1.2 2006/02/26 13:20:44 harryf Exp $
+* Tools for locating / replacing bad bytes in UTF-8 strings
+* The Original Code is Mozilla Communicator client code.
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 1998
+* the Initial Developer. All Rights Reserved.
+* Ported to PHP by Henri Sivonen (http://hsivonen.iki.fi)
+* Slight modifications to fit with phputf8 library by Harry Fuecks (hfuecks gmail com)
+* @see http://lxr.mozilla.org/seamonkey/source/intl/uconv/src/nsUTF8ToUnicode.cpp
+* @see http://lxr.mozilla.org/seamonkey/source/intl/uconv/src/nsUnicodeToUTF8.cpp
+* @see http://hsivonen.iki.fi/php-utf8/
+* @package utf8
+* @subpackage bad
+* @see utf8_is_valid
+*/
+
+/**
+* Locates the first bad byte in a UTF-8 string returning it's
+* byte index in the string
+* PCRE Pattern to locate bad bytes in a UTF-8 string
+* Comes from W3 FAQ: Multilingual Forms
+* Note: modified to include full ASCII range including control chars
+* @see http://www.w3.org/International/questions/qa-forms-utf-8
+* @param string
+* @return mixed integer byte index or FALSE if no bad found
+* @package utf8
+* @subpackage bad
+*/
+function utf8_bad_find($str)
+{
+	$UTF8_BAD =
+		'([\x00-\x7F]'.                          # ASCII (including control chars)
+		'|[\xC2-\xDF][\x80-\xBF]'.               # Non-overlong 2-byte
+		'|\xE0[\xA0-\xBF][\x80-\xBF]'.           # Excluding overlongs
+		'|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'.    # Straight 3-byte
+		'|\xED[\x80-\x9F][\x80-\xBF]'.           # Excluding surrogates
+		'|\xF0[\x90-\xBF][\x80-\xBF]{2}'.        # Planes 1-3
+		'|[\xF1-\xF3][\x80-\xBF]{3}'.            # Planes 4-15
+		'|\xF4[\x80-\x8F][\x80-\xBF]{2}'.        # Plane 16
+		'|(.{1}))';                              # Invalid byte
+	$pos = 0;
+	$badList = array();
+
+	while (preg_match('/'.$UTF8_BAD.'/S', $str, $matches))
+	{
+		$bytes = strlen($matches[0]);
+
+		if (isset($matches[2]))
+			return $pos;
+
+		$pos += $bytes;
+		$str = substr($str,$bytes);
+	}
+
+	return false;
+}
+
+/**
+* Locates all bad bytes in a UTF-8 string and returns a list of their
+* byte index in the string
+* PCRE Pattern to locate bad bytes in a UTF-8 string
+* Comes from W3 FAQ: Multilingual Forms
+* Note: modified to include full ASCII range including control chars
+* @see http://www.w3.org/International/questions/qa-forms-utf-8
+* @param string
+* @return mixed array of integers or FALSE if no bad found
+* @package utf8
+* @subpackage bad
+*/
+function utf8_bad_findall($str)
+{
+	$UTF8_BAD =
+		'([\x00-\x7F]'.                          # ASCII (including control chars)
+		'|[\xC2-\xDF][\x80-\xBF]'.               # Non-overlong 2-byte
+		'|\xE0[\xA0-\xBF][\x80-\xBF]'.           # Excluding overlongs
+		'|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'.    # Straight 3-byte
+		'|\xED[\x80-\x9F][\x80-\xBF]'.           # Excluding surrogates
+		'|\xF0[\x90-\xBF][\x80-\xBF]{2}'.        # Planes 1-3
+		'|[\xF1-\xF3][\x80-\xBF]{3}'.            # Planes 4-15
+		'|\xF4[\x80-\x8F][\x80-\xBF]{2}'.        # Plane 16
+		'|(.{1}))';                              # Invalid byte
+	$pos = 0;
+	$badList = array();
+
+	while (preg_match('/'.$UTF8_BAD.'/S', $str, $matches))
+	{
+		$bytes = strlen($matches[0]);
+
+		if (isset($matches[2]))
+			$badList[] = $pos;
+
+		$pos += $bytes;
+		$str = substr($str,$bytes);
+	}
+
+	if (count($badList) > 0)
+		return $badList;
+
+	return false;
+}
+
+/**
+* Strips out any bad bytes from a UTF-8 string and returns the rest
+* PCRE Pattern to locate bad bytes in a UTF-8 string
+* Comes from W3 FAQ: Multilingual Forms
+* Note: modified to include full ASCII range including control chars
+* @see http://www.w3.org/International/questions/qa-forms-utf-8
+* @param string
+* @return string
+* @package utf8
+* @subpackage bad
+*/
+function utf8_bad_strip($str)
+{
+	$UTF8_BAD =
+		'([\x00-\x7F]'.                          # ASCII (including control chars)
+		'|[\xC2-\xDF][\x80-\xBF]'.               # Non-overlong 2-byte
+		'|\xE0[\xA0-\xBF][\x80-\xBF]'.           # Excluding overlongs
+		'|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'.    # Straight 3-byte
+		'|\xED[\x80-\x9F][\x80-\xBF]'.           # Excluding surrogates
+		'|\xF0[\x90-\xBF][\x80-\xBF]{2}'.        # Planes 1-3
+		'|[\xF1-\xF3][\x80-\xBF]{3}'.            # Planes 4-15
+		'|\xF4[\x80-\x8F][\x80-\xBF]{2}'.        # Plane 16
+		'|(.{1}))';                              # Invalid byte
+
+	ob_start();
+
+	while (preg_match('/'.$UTF8_BAD.'/S', $str, $matches))
+	{
+		if (!isset($matches[2]))
+			echo $matches[0];
+
+		$str = substr($str, strlen($matches[0]));
+	}
+
+	$result = ob_get_contents();
+	ob_end_clean();
+
+	return $result;
+}
+
+/**
+* Replace bad bytes with an alternative character - ASCII character
+* recommended is replacement char
+* PCRE Pattern to locate bad bytes in a UTF-8 string
+* Comes from W3 FAQ: Multilingual Forms
+* Note: modified to include full ASCII range including control chars
+* @see http://www.w3.org/International/questions/qa-forms-utf-8
+* @param string to search
+* @param string to replace bad bytes with (defaults to '?') - use ASCII
+* @return string
+* @package utf8
+* @subpackage bad
+*/
+function utf8_bad_replace($str, $replace='?')
+{
+	$UTF8_BAD =
+		'([\x00-\x7F]'.                          # ASCII (including control chars)
+		'|[\xC2-\xDF][\x80-\xBF]'.               # Non-overlong 2-byte
+		'|\xE0[\xA0-\xBF][\x80-\xBF]'.           # Excluding overlongs
+		'|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'.    # Straight 3-byte
+		'|\xED[\x80-\x9F][\x80-\xBF]'.           # Excluding surrogates
+		'|\xF0[\x90-\xBF][\x80-\xBF]{2}'.        # Planes 1-3
+		'|[\xF1-\xF3][\x80-\xBF]{3}'.            # Planes 4-15
+		'|\xF4[\x80-\x8F][\x80-\xBF]{2}'.        # Plane 16
+		'|(.{1}))';                              # Invalid byte
+
+	ob_start();
+
+	while (preg_match('/'.$UTF8_BAD.'/S', $str, $matches))
+	{
+		if (!isset($matches[2]))
+			echo $matches[0];
+		else
+			echo $replace;
+
+		$str = substr($str, strlen($matches[0]));
+	}
+
+	$result = ob_get_contents();
+	ob_end_clean();
+
+	return $result;
+}
+
+/**
+* Return code from utf8_bad_identify() when a five octet sequence is detected.
+* Note: 5 octets sequences are valid UTF-8 but are not supported by Unicode so
+* do not represent a useful character
+* @see utf8_bad_identify
+* @package utf8
+* @subpackage bad
+*/
+define('UTF8_BAD_5OCTET', 1);
+
+/**
+* Return code from utf8_bad_identify() when a six octet sequence is detected.
+* Note: 6 octets sequences are valid UTF-8 but are not supported by Unicode so
+* do not represent a useful character
+* @see utf8_bad_identify
+* @package utf8
+* @subpackage bad
+*/
+define('UTF8_BAD_6OCTET', 2);
+
+/**
+* Return code from utf8_bad_identify().
+* Invalid octet for use as start of multi-byte UTF-8 sequence
+* @see utf8_bad_identify
+* @package utf8
+* @subpackage bad
+*/
+define('UTF8_BAD_SEQID', 3);
+
+/**
+* Return code from utf8_bad_identify().
+* From Unicode 3.1, non-shortest form is illegal
+* @see utf8_bad_identify
+* @package utf8
+* @subpackage bad
+*/
+define('UTF8_BAD_NONSHORT', 4);
+
+/**
+* Return code from utf8_bad_identify().
+* From Unicode 3.2, surrogate characters are illegal
+* @see utf8_bad_identify
+* @package utf8
+* @subpackage bad
+*/
+define('UTF8_BAD_SURROGATE', 5);
+
+/**
+* Return code from utf8_bad_identify().
+* Codepoints outside the Unicode range are illegal
+* @see utf8_bad_identify
+* @package utf8
+* @subpackage bad
+*/
+define('UTF8_BAD_UNIOUTRANGE', 6);
+
+/**
+* Return code from utf8_bad_identify().
+* Incomplete multi-octet sequence
+* Note: this is kind of a "catch-all"
+* @see utf8_bad_identify
+* @package utf8
+* @subpackage bad
+*/
+define('UTF8_BAD_SEQINCOMPLETE', 7);
+
+/**
+* Reports on the type of bad byte found in a UTF-8 string. Returns a
+* status code on the first bad byte found
+* @author <hsivonen at iki.fi>
+* @param string UTF-8 encoded string
+* @return mixed integer constant describing problem or FALSE if valid UTF-8
+* @see utf8_bad_explain
+* @see http://hsivonen.iki.fi/php-utf8/
+* @package utf8
+* @subpackage bad
+*/
+function utf8_bad_identify($str, &$i)
+{
+	$mState = 0;	// Cached expected number of octets after the current octet
+					// until the beginning of the next UTF8 character sequence
+	$mUcs4  = 0;	// Cached Unicode character
+	$mBytes = 1;	// Cached expected number of octets in the current sequence
+
+	$len = strlen($str);
+
+	for($i=0; $i < $len; $i++)
+	{
+		$in = ord($str{$i});
+
+		if ( $mState == 0)
+		{
+			// When mState is zero we expect either a US-ASCII character or a multi-octet sequence.
+			if (0 == (0x80 & ($in)))
+			{
+				// US-ASCII, pass straight through.
+				$mBytes = 1;
+			}
+			else if (0xC0 == (0xE0 & ($in)))
+			{
+				// First octet of 2 octet sequence
+				$mUcs4 = ($in);
+				$mUcs4 = ($mUcs4 & 0x1F) << 6;
+				$mState = 1;
+				$mBytes = 2;
+			}
+			else if (0xE0 == (0xF0 & ($in)))
+			{
+				// First octet of 3 octet sequence
+				$mUcs4 = ($in);
+				$mUcs4 = ($mUcs4 & 0x0F) << 12;
+				$mState = 2;
+				$mBytes = 3;
+			}
+			else if (0xF0 == (0xF8 & ($in)))
+			{
+				// First octet of 4 octet sequence
+				$mUcs4 = ($in);
+				$mUcs4 = ($mUcs4 & 0x07) << 18;
+				$mState = 3;
+				$mBytes = 4;
+			}
+			else if (0xF8 == (0xFC & ($in)))
+			{
+				/* First octet of 5 octet sequence.
+				*
+				* This is illegal because the encoded codepoint must be either
+				* (a) not the shortest form or
+				* (b) outside the Unicode range of 0-0x10FFFF.
+				*/
+				return UTF8_BAD_5OCTET;
+			}
+			else if (0xFC == (0xFE & ($in)))
+			{
+				// First octet of 6 octet sequence, see comments for 5 octet sequence.
+				return UTF8_BAD_6OCTET;
+			}
+			else
+			{
+				// Current octet is neither in the US-ASCII range nor a legal first
+				// octet of a multi-octet sequence.
+				return UTF8_BAD_SEQID;
+			}
+		}
+		else
+		{
+			// When mState is non-zero, we expect a continuation of the multi-octet sequence
+			if (0x80 == (0xC0 & ($in)))
+			{
+				// Legal continuation.
+				$shift = ($mState - 1) * 6;
+				$tmp = $in;
+				$tmp = ($tmp & 0x0000003F) << $shift;
+				$mUcs4 |= $tmp;
+
+				/**
+				* End of the multi-octet sequence. mUcs4 now contains the final
+				* Unicode codepoint to be output
+				*/
+				if (0 == --$mState)
+				{
+					// From Unicode 3.1, non-shortest form is illegal
+					if (((2 == $mBytes) && ($mUcs4 < 0x0080)) ||
+					((3 == $mBytes) && ($mUcs4 < 0x0800)) ||
+					((4 == $mBytes) && ($mUcs4 < 0x10000)) )
+						return UTF8_BAD_NONSHORT;
+					else if (($mUcs4 & 0xFFFFF800) == 0xD800) // From Unicode 3.2, surrogate characters are illegal
+						return UTF8_BAD_SURROGATE;
+					else if ($mUcs4 > 0x10FFFF) // Codepoints outside the Unicode range are illegal
+						return UTF8_BAD_UNIOUTRANGE;
+
+					// Initialize UTF8 cache
+					$mState = 0;
+					$mUcs4  = 0;
+					$mBytes = 1;
+				}
+
+			}
+			else
+			{
+				// ((0xC0 & (*in) != 0x80) && (mState != 0))
+				// Incomplete multi-octet sequence.
+				$i--;
+				return UTF8_BAD_SEQINCOMPLETE;
+			}
+		}
+	}
+
+	// Incomplete multi-octet sequence
+	if ($mState != 0)
+	{
+		$i--;
+		return UTF8_BAD_SEQINCOMPLETE;
+	}
+
+	// No bad octets found
+	$i = null;
+	return false;
+}
+
+/**
+* Takes a return code from utf8_bad_identify() are returns a message
+* (in English) explaining what the problem is.
+* @param int return code from utf8_bad_identify
+* @return mixed string message or FALSE if return code unknown
+* @see utf8_bad_identify
+* @package utf8
+* @subpackage bad
+*/
+function utf8_bad_explain($code)
+{
+	switch ($code)
+	{
+		case UTF8_BAD_5OCTET:
+		return 'Five octet sequences are valid UTF-8 but are not supported by Unicode';
+		break;
+
+		case UTF8_BAD_6OCTET:
+		return 'Six octet sequences are valid UTF-8 but are not supported by Unicode';
+		break;
+
+		case UTF8_BAD_SEQID:
+		return 'Invalid octet for use as start of multi-byte UTF-8 sequence';
+		break;
+
+		case UTF8_BAD_NONSHORT:
+		return 'From Unicode 3.1, non-shortest form is illegal';
+		break;
+
+		case UTF8_BAD_SURROGATE:
+		return 'From Unicode 3.2, surrogate characters are illegal';
+		break;
+
+		case UTF8_BAD_UNIOUTRANGE:
+		return 'Codepoints outside the Unicode range are illegal';
+		break;
+
+		case UTF8_BAD_SEQINCOMPLETE:
+		return 'Incomplete multi-octet sequence';
+		break;
+	}
+
+	trigger_error('Unknown error code: '.$code, E_USER_WARNING);
+
+	return false;
+}
diff --git a/include/utf8/utils/index.html b/include/utf8/utils/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/include/utf8/utils/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/include/utf8/utils/patterns.php b/include/utf8/utils/patterns.php
new file mode 100644
index 0000000..5a85a4f
--- /dev/null
+++ b/include/utf8/utils/patterns.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+* PCRE Regular expressions for UTF-8. Note this file is not actually used by
+* the rest of the library but these regular expressions can be useful to have
+* available.
+* @version $Id: patterns.php,v 1.1 2006/02/25 14:20:02 harryf Exp $
+* @see http://www.w3.org/International/questions/qa-forms-utf-8
+* @package utf8
+* @subpackage patterns
+*/
+
+/**
+* PCRE Pattern to check a UTF-8 string is valid
+* Comes from W3 FAQ: Multilingual Forms
+* Note: modified to include full ASCII range including control chars
+* @see http://www.w3.org/International/questions/qa-forms-utf-8
+* @package utf8
+* @subpackage patterns
+*/
+$UTF8_VALID = '^('.
+	'[\x00-\x7F]'.                          # ASCII (including control chars)
+	'|[\xC2-\xDF][\x80-\xBF]'.              # Non-overlong 2-byte
+	'|\xE0[\xA0-\xBF][\x80-\xBF]'.          # Excluding overlongs
+	'|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'.   # Straight 3-byte
+	'|\xED[\x80-\x9F][\x80-\xBF]'.          # Excluding surrogates
+	'|\xF0[\x90-\xBF][\x80-\xBF]{2}'.       # Planes 1-3
+	'|[\xF1-\xF3][\x80-\xBF]{3}'.           # Planes 4-15
+	'|\xF4[\x80-\x8F][\x80-\xBF]{2}'.       # Plane 16
+	')*$';
+
+/**
+* PCRE Pattern to match single UTF-8 characters
+* Comes from W3 FAQ: Multilingual Forms
+* Note: modified to include full ASCII range including control chars
+* @see http://www.w3.org/International/questions/qa-forms-utf-8
+* @package utf8
+* @subpackage patterns
+*/
+$UTF8_MATCH =
+	'([\x00-\x7F])'.                          # ASCII (including control chars)
+	'|([\xC2-\xDF][\x80-\xBF])'.              # Non-overlong 2-byte
+	'|(\xE0[\xA0-\xBF][\x80-\xBF])'.          # Excluding overlongs
+	'|([\xE1-\xEC\xEE\xEF][\x80-\xBF]{2})'.   # Straight 3-byte
+	'|(\xED[\x80-\x9F][\x80-\xBF])'.          # Excluding surrogates
+	'|(\xF0[\x90-\xBF][\x80-\xBF]{2})'.       # Planes 1-3
+	'|([\xF1-\xF3][\x80-\xBF]{3})'.           # Planes 4-15
+	'|(\xF4[\x80-\x8F][\x80-\xBF]{2})';       # Plane 16
+
+/**
+* PCRE Pattern to locate bad bytes in a UTF-8 string
+* Comes from W3 FAQ: Multilingual Forms
+* Note: modified to include full ASCII range including control chars
+* @see http://www.w3.org/International/questions/qa-forms-utf-8
+* @package utf8
+* @subpackage patterns
+*/
+$UTF8_BAD =
+	'([\x00-\x7F]'.                          # ASCII (including control chars)
+	'|[\xC2-\xDF][\x80-\xBF]'.               # Non-overlong 2-byte
+	'|\xE0[\xA0-\xBF][\x80-\xBF]'.           # Excluding overlongs
+	'|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}'.    # Straight 3-byte
+	'|\xED[\x80-\x9F][\x80-\xBF]'.           # Excluding surrogates
+	'|\xF0[\x90-\xBF][\x80-\xBF]{2}'.        # Planes 1-3
+	'|[\xF1-\xF3][\x80-\xBF]{3}'.            # Planes 4-15
+	'|\xF4[\x80-\x8F][\x80-\xBF]{2}'.        # Plane 16
+	'|(.{1}))';                              # Invalid byte
diff --git a/include/utf8/utils/position.php b/include/utf8/utils/position.php
new file mode 100644
index 0000000..7c62d10
--- /dev/null
+++ b/include/utf8/utils/position.php
@@ -0,0 +1,171 @@
+<?php
+
+/**
+* Locate a byte index given a UTF-8 character index
+* @version $Id: position.php,v 1.1 2006/10/01 00:01:31 harryf Exp $
+* @package utf8
+* @subpackage position
+*/
+
+/**
+* Given a string and a character index in the string, in
+* terms of the UTF-8 character position, returns the byte
+* index of that character. Can be useful when you want to
+* PHP's native string functions but we warned, locating
+* the byte can be expensive
+* Takes variable number of parameters - first must be
+* the search string then 1 to n UTF-8 character positions
+* to obtain byte indexes for - it is more efficient to search
+* the string for multiple characters at once, than make
+* repeated calls to this function
+*
+* @author Chris Smith<chris at jalakai.co.uk>
+* @param string string to locate index in
+* @param int (n times)
+* @return mixed - int if only one input int, array if more
+* @return boolean TRUE if it's all ASCII
+* @package utf8
+* @subpackage position
+*/
+function utf8_byte_position()
+{
+	$args = func_get_args();
+	$str =& array_shift($args);
+
+	if (!is_string($str))
+		return false;
+
+	$result = array();
+	$prev = array(0, 0); // Trivial byte index, character offset pair
+	$i = utf8_locate_next_chr($str, 300); // Use a short piece of str to estimate bytes per character. $i (& $j) -> byte indexes into $str
+	$c = strlen(utf8_decode(substr($str, 0, $i))); // $c -> character offset into $str
+
+	// Deal with arguments from lowest to highest
+	sort($args);
+
+	foreach ($args as $offset)
+	{
+		// Sanity checks FIXME
+
+		// 0 is an easy check
+		if ($offset == 0)
+		{
+			$result[] = 0; continue;
+		}
+
+		// Ensure no endless looping
+		$safety_valve = 50;
+
+		do
+		{
+			if (($c - $prev[1]) == 0)
+			{
+				// Hack: gone past end of string
+				$error = 0;
+				$i = strlen($str);
+				break;
+			}
+
+			$j = $i + (int)(($offset-$c) * ($i - $prev[0]) / ($c - $prev[1]));
+			$j = utf8_locate_next_chr($str, $j); // Correct to utf8 character boundary
+			$prev = array($i,$c); // Save the index, offset for use next iteration
+
+			if ($j > $i)
+				$c += strlen(utf8_decode(substr($str, $i, $j-$i))); // Determine new character offset
+			else
+				$c -= strlen(utf8_decode(substr($str, $j, $i-$j))); // Ditto
+
+			$error = abs($c-$offset);
+			$i = $j; // Ready for next time around
+		}
+		while (($error > 7) && --$safety_valve); // From 7 it is faster to iterate over the string
+
+		if ($error && $error <= 7)
+		{
+			if ($c < $offset)
+			{
+				// Move up
+				while ($error--)
+					$i = utf8_locate_next_chr($str, ++$i);
+			}
+			else
+			{
+				// Move down
+				while ($error--)
+					$i = utf8_locate_current_chr($str, --$i);
+			}
+
+			// Ready for next arg
+			$c = $offset;
+		}
+
+		$result[] = $i;
+	}
+
+	if (count($result) == 1)
+		return $result[0];
+
+	return $result;
+}
+
+/**
+* Given a string and any byte index, returns the byte index
+* of the start of the current UTF-8 character, relative to supplied
+* position. If the current character begins at the same place as the
+* supplied byte index, that byte index will be returned. Otherwise
+* this function will step backwards, looking for the index where
+* curent UTF-8 character begins
+* @author Chris Smith<chris at jalakai.co.uk>
+* @param string
+* @param int byte index in the string
+* @return int byte index of start of next UTF-8 character
+* @package utf8
+* @subpackage position
+*/
+function utf8_locate_current_chr( &$str, $idx )
+{
+	if ($idx <= 0)
+		return 0;
+
+	$limit = strlen($str);
+	if ($idx >= $limit)
+		return $limit;
+
+	// Binary value for any byte after the first in a multi-byte UTF-8 character
+	// will be like 10xxxxxx so & 0xC0 can be used to detect this kind
+	// of byte - assuming well formed UTF-8
+	while ($idx && ((ord($str[$idx]) & 0xC0) == 0x80))
+		$idx--;
+
+	return $idx;
+}
+
+/**
+* Given a string and any byte index, returns the byte index
+* of the start of the next UTF-8 character, relative to supplied
+* position. If the next character begins at the same place as the
+* supplied byte index, that byte index will be returned.
+* @author Chris Smith<chris at jalakai.co.uk>
+* @param string
+* @param int byte index in the string
+* @return int byte index of start of next UTF-8 character
+* @package utf8
+* @subpackage position
+*/
+function utf8_locate_next_chr(&$str, $idx)
+{
+	if ($idx <= 0)
+		return 0;
+
+	$limit = strlen($str);
+	if ($idx >= $limit)
+		return $limit;
+
+	// Binary value for any byte after the first in a multi-byte UTF-8 character
+	// will be like 10xxxxxx so & 0xC0 can be used to detect this kind
+	// of byte - assuming well formed UTF-8
+	while (($idx < $limit) && ((ord($str[$idx]) & 0xC0) == 0x80))
+		$idx++;
+
+	return $idx;
+}
diff --git a/include/utf8/utils/specials.php b/include/utf8/utils/specials.php
new file mode 100644
index 0000000..69219dc
--- /dev/null
+++ b/include/utf8/utils/specials.php
@@ -0,0 +1,131 @@
+<?php
+
+/**
+* Utilities for processing "special" characters in UTF-8. "Special" largely means anything which would
+* be regarded as a non-word character, like ASCII control characters and punctuation. This has a "Roman"
+* bias - it would be unaware of modern Chinese "punctuation" characters for example.
+* Note: requires utils/unicode.php to be loaded
+* @version $Id: specials.php,v 1.2 2006/10/16 21:13:59 harryf Exp $
+* @package utf8
+* @subpackage utils
+* @see utf8_is_valid
+*/
+
+/**
+* Used internally. Builds a PCRE pattern from the $UTF8_SPECIAL_CHARS
+* array defined in this file
+* The $UTF8_SPECIAL_CHARS should contain all special characters (non-letter/non-digit)
+* defined in the various local charsets - it's not a complete list of
+* non-alphanum characters in UTF-8. It's not perfect but should match most
+* cases of special chars.
+* This function adds the control chars 0x00 to 0x19 to the array of
+* special chars (they are not included in $UTF8_SPECIAL_CHARS)
+* @package utf8
+* @subpackage utils
+* @return string
+* @see utf8_from_unicode
+* @see utf8_is_word_chars
+* @see utf8_strip_specials
+*/
+function utf8_specials_pattern()
+{
+	static $pattern = null;
+
+	if (!$pattern)
+	{
+		$UTF8_SPECIAL_CHARS = array(
+			0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023,
+			0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c,
+			0x002f,         0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x005b,
+			0x005c, 0x005d, 0x005e,         0x0060, 0x007b, 0x007c, 0x007d, 0x007e,
+			0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088,
+			0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092,
+			0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c,
+			0x009d, 0x009e, 0x009f, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6,
+			0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0,
+			0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba,
+			0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00d7, 0x00f7, 0x02c7, 0x02d8, 0x02d9,
+			0x02da, 0x02db, 0x02dc, 0x02dd, 0x0300, 0x0301, 0x0303, 0x0309, 0x0323, 0x0384,
+			0x0385, 0x0387, 0x03b2, 0x03c6, 0x03d1, 0x03d2, 0x03d5, 0x03d6, 0x05b0, 0x05b1,
+			0x05b2, 0x05b3, 0x05b4, 0x05b5, 0x05b6, 0x05b7, 0x05b8, 0x05b9, 0x05bb, 0x05bc,
+			0x05bd, 0x05be, 0x05bf, 0x05c0, 0x05c1, 0x05c2, 0x05c3, 0x05f3, 0x05f4, 0x060c,
+			0x061b, 0x061f, 0x0640, 0x064b, 0x064c, 0x064d, 0x064e, 0x064f, 0x0650, 0x0651,
+			0x0652, 0x066a, 0x0e3f, 0x200c, 0x200d, 0x200e, 0x200f, 0x2013, 0x2014, 0x2015,
+			0x2017, 0x2018, 0x2019, 0x201a, 0x201c, 0x201d, 0x201e, 0x2020, 0x2021, 0x2022,
+			0x2026, 0x2030, 0x2032, 0x2033, 0x2039, 0x203a, 0x2044, 0x20a7, 0x20aa, 0x20ab,
+			0x20ac, 0x2116, 0x2118, 0x2122, 0x2126, 0x2135, 0x2190, 0x2191, 0x2192, 0x2193,
+			0x2194, 0x2195, 0x21b5, 0x21d0, 0x21d1, 0x21d2, 0x21d3, 0x21d4, 0x2200, 0x2202,
+			0x2203, 0x2205, 0x2206, 0x2207, 0x2208, 0x2209, 0x220b, 0x220f, 0x2211, 0x2212,
+			0x2215, 0x2217, 0x2219, 0x221a, 0x221d, 0x221e, 0x2220, 0x2227, 0x2228, 0x2229,
+			0x222a, 0x222b, 0x2234, 0x223c, 0x2245, 0x2248, 0x2260, 0x2261, 0x2264, 0x2265,
+			0x2282, 0x2283, 0x2284, 0x2286, 0x2287, 0x2295, 0x2297, 0x22a5, 0x22c5, 0x2310,
+			0x2320, 0x2321, 0x2329, 0x232a, 0x2469, 0x2500, 0x2502, 0x250c, 0x2510, 0x2514,
+			0x2518, 0x251c, 0x2524, 0x252c, 0x2534, 0x253c, 0x2550, 0x2551, 0x2552, 0x2553,
+			0x2554, 0x2555, 0x2556, 0x2557, 0x2558, 0x2559, 0x255a, 0x255b, 0x255c, 0x255d,
+			0x255e, 0x255f, 0x2560, 0x2561, 0x2562, 0x2563, 0x2564, 0x2565, 0x2566, 0x2567,
+			0x2568, 0x2569, 0x256a, 0x256b, 0x256c, 0x2580, 0x2584, 0x2588, 0x258c, 0x2590,
+			0x2591, 0x2592, 0x2593, 0x25a0, 0x25b2, 0x25bc, 0x25c6, 0x25ca, 0x25cf, 0x25d7,
+			0x2605, 0x260e, 0x261b, 0x261e, 0x2660, 0x2663, 0x2665, 0x2666, 0x2701, 0x2702,
+			0x2703, 0x2704, 0x2706, 0x2707, 0x2708, 0x2709, 0x270c, 0x270d, 0x270e, 0x270f,
+			0x2710, 0x2711, 0x2712, 0x2713, 0x2714, 0x2715, 0x2716, 0x2717, 0x2718, 0x2719,
+			0x271a, 0x271b, 0x271c, 0x271d, 0x271e, 0x271f, 0x2720, 0x2721, 0x2722, 0x2723,
+			0x2724, 0x2725, 0x2726, 0x2727, 0x2729, 0x272a, 0x272b, 0x272c, 0x272d, 0x272e,
+			0x272f, 0x2730, 0x2731, 0x2732, 0x2733, 0x2734, 0x2735, 0x2736, 0x2737, 0x2738,
+			0x2739, 0x273a, 0x273b, 0x273c, 0x273d, 0x273e, 0x273f, 0x2740, 0x2741, 0x2742,
+			0x2743, 0x2744, 0x2745, 0x2746, 0x2747, 0x2748, 0x2749, 0x274a, 0x274b, 0x274d,
+			0x274f, 0x2750, 0x2751, 0x2752, 0x2756, 0x2758, 0x2759, 0x275a, 0x275b, 0x275c,
+			0x275d, 0x275e, 0x2761, 0x2762, 0x2763, 0x2764, 0x2765, 0x2766, 0x2767, 0x277f,
+			0x2789, 0x2793, 0x2794, 0x2798, 0x2799, 0x279a, 0x279b, 0x279c, 0x279d, 0x279e,
+			0x279f, 0x27a0, 0x27a1, 0x27a2, 0x27a3, 0x27a4, 0x27a5, 0x27a6, 0x27a7, 0x27a8,
+			0x27a9, 0x27aa, 0x27ab, 0x27ac, 0x27ad, 0x27ae, 0x27af, 0x27b1, 0x27b2, 0x27b3,
+			0x27b4, 0x27b5, 0x27b6, 0x27b7, 0x27b8, 0x27b9, 0x27ba, 0x27bb, 0x27bc, 0x27bd,
+			0x27be, 0xf6d9, 0xf6da, 0xf6db, 0xf8d7, 0xf8d8, 0xf8d9, 0xf8da, 0xf8db, 0xf8dc,
+			0xf8dd, 0xf8de, 0xf8df, 0xf8e0, 0xf8e1, 0xf8e2, 0xf8e3, 0xf8e4, 0xf8e5, 0xf8e6,
+			0xf8e7, 0xf8e8, 0xf8e9, 0xf8ea, 0xf8eb, 0xf8ec, 0xf8ed, 0xf8ee, 0xf8ef, 0xf8f0,
+			0xf8f1, 0xf8f2, 0xf8f3, 0xf8f4, 0xf8f5, 0xf8f6, 0xf8f7, 0xf8f8, 0xf8f9, 0xf8fa,
+			0xf8fb, 0xf8fc, 0xf8fd, 0xf8fe, 0xfe7c, 0xfe7d);
+
+		$pattern = preg_quote(utf8_from_unicode($UTF8_SPECIAL_CHARS), '/');
+		$pattern = '/[\x00-\x19'.$pattern.']/u';
+	}
+
+	return $pattern;
+}
+
+/**
+* Checks a string for whether it contains only word characters. This
+* is logically equivalent to the \w PCRE meta character. Note that
+* this is not a 100% guarantee that the string only contains alpha /
+* numeric characters but just that common non-alphanumeric are not
+* in the string, including ASCII device control characters.
+* @package utf8
+* @subpackage utils
+* @param string to check
+* @return boolean TRUE if the string only contains word characters
+* @see utf8_specials_pattern
+*/
+function utf8_is_word_chars($str)
+{
+	return !(bool) preg_match(utf8_specials_pattern(), $str);
+}
+
+/**
+* Removes special characters (nonalphanumeric) from a UTF-8 string
+*
+* This can be useful as a helper for sanitizing a string for use as
+* something like a file name or a unique identifier. Be warned though
+* it does not handle all possible non-alphanumeric characters and is
+* not intended is some kind of security / injection filter.
+*
+* @package utf8
+* @subpackage utils
+* @author Andreas Gohr <andi at splitbrain.org>
+* @param string $string The UTF8 string to strip of special chars
+* @param string (optional) $repl   Replace special with this string
+* @return string with common non-alphanumeric characters removed
+* @see utf8_specials_pattern
+*/
+function utf8_strip_specials($string, $repl='')
+{
+	return preg_replace(utf8_specials_pattern(), $repl, $string);
+}
diff --git a/include/utf8/utils/unicode.php b/include/utf8/utils/unicode.php
new file mode 100644
index 0000000..f0e86cb
--- /dev/null
+++ b/include/utf8/utils/unicode.php
@@ -0,0 +1,241 @@
+<?php
+
+/**
+* @version $Id: unicode.php,v 1.2 2006/02/26 13:20:44 harryf Exp $
+* Tools for conversion between UTF-8 and unicode
+* The Original Code is Mozilla Communicator client code.
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 1998
+* the Initial Developer. All Rights Reserved.
+* Ported to PHP by Henri Sivonen (http://hsivonen.iki.fi)
+* Slight modifications to fit with phputf8 library by Harry Fuecks (hfuecks gmail com)
+* @see http://lxr.mozilla.org/seamonkey/source/intl/uconv/src/nsUTF8ToUnicode.cpp
+* @see http://lxr.mozilla.org/seamonkey/source/intl/uconv/src/nsUnicodeToUTF8.cpp
+* @see http://hsivonen.iki.fi/php-utf8/
+* @package utf8
+* @subpackage unicode
+*/
+
+/**
+* Takes an UTF-8 string and returns an array of ints representing the
+* Unicode characters. Astral planes are supported ie. the ints in the
+* output can be > 0xFFFF. Occurrances of the BOM are ignored. Surrogates
+* are not allowed.
+* Returns false if the input string isn't a valid UTF-8 octet sequence
+* and raises a PHP error at level E_USER_WARNING
+* Note: this function has been modified slightly in this library to
+* trigger errors on encountering bad bytes
+* @author <hsivonen at iki.fi>
+* @param string UTF-8 encoded string
+* @return mixed array of unicode code points or FALSE if UTF-8 invalid
+* @see utf8_from_unicode
+* @see http://hsivonen.iki.fi/php-utf8/
+* @package utf8
+* @subpackage unicode
+*/
+function utf8_to_unicode($str)
+{
+	$mState = 0;	// Cached expected number of octets after the current octet
+					// until the beginning of the next UTF8 character sequence
+	$mUcs4  = 0;	// Cached Unicode character
+	$mBytes = 1;	// Cached expected number of octets in the current sequence
+
+	$out = array();
+	$len = strlen($str);
+
+	for($i = 0; $i < $len; $i++)
+	{
+		$in = ord($str[$i]);
+
+		if ($mState == 0)
+		{
+			// When mState is zero we expect either a US-ASCII character or a multi-octet sequence.
+			if (0 == (0x80 & ($in)))
+			{
+				// US-ASCII, pass straight through.
+				$out[] = $in;
+				$mBytes = 1;
+			}
+			else if (0xC0 == (0xE0 & ($in)))
+			{
+				// First octet of 2 octet sequence
+				$mUcs4 = ($in);
+				$mUcs4 = ($mUcs4 & 0x1F) << 6;
+				$mState = 1;
+				$mBytes = 2;
+			}
+			else if (0xE0 == (0xF0 & ($in)))
+			{
+				// First octet of 3 octet sequence
+				$mUcs4 = ($in);
+				$mUcs4 = ($mUcs4 & 0x0F) << 12;
+				$mState = 2;
+				$mBytes = 3;
+			}
+			else if (0xF0 == (0xF8 & ($in)))
+			{
+				// First octet of 4 octet sequence
+				$mUcs4 = ($in);
+				$mUcs4 = ($mUcs4 & 0x07) << 18;
+				$mState = 3;
+				$mBytes = 4;
+			}
+			else if (0xF8 == (0xFC & ($in)))
+			{
+				/* First octet of 5 octet sequence.
+				*
+				* This is illegal because the encoded codepoint must be either
+				* (a) not the shortest form or
+				* (b) outside the Unicode range of 0-0x10FFFF.
+				* Rather than trying to resynchronize, we will carry on until the end
+				* of the sequence and let the later error handling code catch it.
+				*/
+				$mUcs4 = ($in);
+				$mUcs4 = ($mUcs4 & 0x03) << 24;
+				$mState = 4;
+				$mBytes = 5;
+			}
+			else if (0xFC == (0xFE & ($in)))
+			{
+				// First octet of 6 octet sequence, see comments for 5 octet sequence.
+				$mUcs4 = ($in);
+				$mUcs4 = ($mUcs4 & 1) << 30;
+				$mState = 5;
+				$mBytes = 6;
+			}
+			else
+			{
+				// Current octet is neither in the US-ASCII range nor a legal first octet of a multi-octet sequence
+				trigger_error('utf8_to_unicode: Illegal sequence identifier in UTF-8 at byte '.$i, E_USER_WARNING);
+				return false;
+			}
+		}
+		else
+		{
+			// When mState is non-zero, we expect a continuation of the multi-octet sequence
+			if (0x80 == (0xC0 & ($in)))
+			{
+				// Legal continuation.
+				$shift = ($mState - 1) * 6;
+				$tmp = $in;
+				$tmp = ($tmp & 0x0000003F) << $shift;
+				$mUcs4 |= $tmp;
+
+				/**
+				* End of the multi-octet sequence. mUcs4 now contains the final
+				* Unicode codepoint to be output
+				*/
+				if (0 == --$mState)
+				{
+					/*
+					* Check for illegal sequences and codepoints.
+					*/
+					// From Unicode 3.1, non-shortest form is illegal
+					if (((2 == $mBytes) && ($mUcs4 < 0x0080)) || ((3 == $mBytes) && ($mUcs4 < 0x0800)) ||
+					   ((4 == $mBytes) && ($mUcs4 < 0x10000)) || (4 < $mBytes) ||
+					   // From Unicode 3.2, surrogate characters are illegal
+					   (($mUcs4 & 0xFFFFF800) == 0xD800) ||
+					   // Codepoints outside the Unicode range are illegal
+					   ($mUcs4 > 0x10FFFF))
+					{
+						trigger_error('utf8_to_unicode: Illegal sequence or codepoint in UTF-8 at byte '.$i, E_USER_WARNING);
+						return false;
+					}
+
+					// BOM is legal but we don't want to output it
+					if (0xFEFF != $mUcs4)
+						$out[] = $mUcs4;
+
+					// Initialize UTF8 cache
+					$mState = 0;
+					$mUcs4  = 0;
+					$mBytes = 1;
+				}
+
+			}
+			else
+			{
+				/* ((0xC0 & (*in) != 0x80) && (mState != 0))
+				Incomplete multi-octet sequence. */
+				trigger_error('utf8_to_unicode: Incomplete multi-octet sequence in UTF-8 at byte '.$i, E_USER_WARNING);
+				return false;
+			}
+		}
+	}
+
+	return $out;
+}
+
+/**
+* Takes an array of ints representing the Unicode characters and returns
+* a UTF-8 string. Astral planes are supported ie. the ints in the
+* input can be > 0xFFFF. Occurrances of the BOM are ignored. Surrogates
+* are not allowed.
+* Returns false if the input array contains ints that represent
+* surrogates or are outside the Unicode range
+* and raises a PHP error at level E_USER_WARNING
+* Note: this function has been modified slightly in this library to use
+* output buffering to concatenate the UTF-8 string (faster) as well as
+* reference the array by it's keys
+* @param array of unicode code points representing a string
+* @return mixed UTF-8 string or FALSE if array contains invalid code points
+* @author <hsivonen at iki.fi>
+* @see utf8_to_unicode
+* @see http://hsivonen.iki.fi/php-utf8/
+* @package utf8
+* @subpackage unicode
+*/
+function utf8_from_unicode($arr)
+{
+	ob_start();
+
+	foreach (array_keys($arr) as $k)
+	{
+		if ( ($arr[$k] >= 0) && ($arr[$k] <= 0x007f) ) // ASCII range (including control chars)
+		{
+			echo chr($arr[$k]);
+		}
+		else if ($arr[$k] <= 0x07ff) //2 byte sequence
+		{
+			echo chr(0xc0 | ($arr[$k] >> 6));
+			echo chr(0x80 | ($arr[$k] & 0x003f));
+		}
+		else if($arr[$k] == 0xFEFF) // Byte order mark (skip)
+		{
+			// Nop -- zap the BOM
+		}
+		else if ($arr[$k] >= 0xD800 && $arr[$k] <= 0xDFFF) // Test for illegal surrogates
+		{
+			// Found a surrogate
+			trigger_error('utf8_from_unicode: Illegal surrogate at index: '.$k.', value: '.$arr[$k], E_USER_WARNING);
+
+			return false;
+		}
+		else if ($arr[$k] <= 0xffff) // 3 byte sequence
+		{
+			echo chr(0xe0 | ($arr[$k] >> 12));
+			echo chr(0x80 | (($arr[$k] >> 6) & 0x003f));
+			echo chr(0x80 | ($arr[$k] & 0x003f));
+		}
+		else if ($arr[$k] <= 0x10ffff) // 4 byte sequence
+		{
+			echo chr(0xf0 | ($arr[$k] >> 18));
+			echo chr(0x80 | (($arr[$k] >> 12) & 0x3f));
+			echo chr(0x80 | (($arr[$k] >> 6) & 0x3f));
+			echo chr(0x80 | ($arr[$k] & 0x3f));
+		}
+		else
+		{
+			trigger_error('utf8_from_unicode: Codepoint out of Unicode range at index: '.$k.', value: '.$arr[$k], E_USER_WARNING);
+
+			// Out of range
+			return false;
+		}
+	}
+
+	$result = ob_get_contents();
+	ob_end_clean();
+
+	return $result;
+}
diff --git a/include/utf8/utils/validation.php b/include/utf8/utils/validation.php
new file mode 100644
index 0000000..90dce8e
--- /dev/null
+++ b/include/utf8/utils/validation.php
@@ -0,0 +1,186 @@
+<?php
+
+/**
+* @version $Id: validation.php,v 1.2 2006/02/26 13:20:44 harryf Exp $
+* Tools for validing a UTF-8 string is well formed.
+* The Original Code is Mozilla Communicator client code.
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 1998
+* the Initial Developer. All Rights Reserved.
+* Ported to PHP by Henri Sivonen (http://hsivonen.iki.fi)
+* Slight modifications to fit with phputf8 library by Harry Fuecks (hfuecks gmail com)
+* @see http://lxr.mozilla.org/seamonkey/source/intl/uconv/src/nsUTF8ToUnicode.cpp
+* @see http://lxr.mozilla.org/seamonkey/source/intl/uconv/src/nsUnicodeToUTF8.cpp
+* @see http://hsivonen.iki.fi/php-utf8/
+* @package utf8
+* @subpackage validation
+*/
+
+/**
+* Tests a string as to whether it's valid UTF-8 and supported by the
+* Unicode standard
+* Note: this function has been modified to simple return true or false
+* @author <hsivonen at iki.fi>
+* @param string UTF-8 encoded string
+* @return boolean true if valid
+* @see http://hsivonen.iki.fi/php-utf8/
+* @see utf8_compliant
+* @package utf8
+* @subpackage validation
+*/
+function utf8_is_valid($str)
+{
+	$mState = 0;	// Cached expected number of octets after the current octet
+					// until the beginning of the next UTF8 character sequence
+	$mUcs4  = 0;	// Cached Unicode character
+	$mBytes = 1;	// Cached expected number of octets in the current sequence
+
+	$len = strlen($str);
+
+	for($i = 0; $i < $len; $i++)
+	{
+		$in = ord($str{$i});
+
+		if ( $mState == 0)
+		{
+			// When mState is zero we expect either a US-ASCII character or a multi-octet sequence.
+			if (0 == (0x80 & ($in)))
+			{
+				$mBytes = 1; // US-ASCII, pass straight through
+			}
+			else if (0xC0 == (0xE0 & ($in)))
+			{
+				// First octet of 2 octet sequence
+				$mUcs4 = ($in);
+				$mUcs4 = ($mUcs4 & 0x1F) << 6;
+				$mState = 1;
+				$mBytes = 2;
+			}
+			else if (0xE0 == (0xF0 & ($in)))
+			{
+				// First octet of 3 octet sequence
+				$mUcs4 = ($in);
+				$mUcs4 = ($mUcs4 & 0x0F) << 12;
+				$mState = 2;
+				$mBytes = 3;
+			}
+			else if (0xF0 == (0xF8 & ($in)))
+			{
+				// First octet of 4 octet sequence
+				$mUcs4 = ($in);
+				$mUcs4 = ($mUcs4 & 0x07) << 18;
+				$mState = 3;
+				$mBytes = 4;
+			}
+			else if (0xF8 == (0xFC & ($in)))
+			{
+				/* First octet of 5 octet sequence.
+				*
+				* This is illegal because the encoded codepoint must be either
+				* (a) not the shortest form or
+				* (b) outside the Unicode range of 0-0x10FFFF.
+				* Rather than trying to resynchronize, we will carry on until the end
+				* of the sequence and let the later error handling code catch it.
+				*/
+				$mUcs4 = ($in);
+				$mUcs4 = ($mUcs4 & 0x03) << 24;
+				$mState = 4;
+				$mBytes = 5;
+			}
+			else if (0xFC == (0xFE & ($in)))
+			{
+				// First octet of 6 octet sequence, see comments for 5 octet sequence.
+				$mUcs4 = ($in);
+				$mUcs4 = ($mUcs4 & 1) << 30;
+				$mState = 5;
+				$mBytes = 6;
+			}
+			else
+			{
+				// Current octet is neither in the US-ASCII range nor a legal first octet of a multi-octet sequence.
+				return false;
+			}
+		}
+		else
+		{
+			// When mState is non-zero, we expect a continuation of the multi-octet sequence
+			if (0x80 == (0xC0 & ($in)))
+			{
+				// Legal continuation.
+				$shift = ($mState - 1) * 6;
+				$tmp = $in;
+				$tmp = ($tmp & 0x0000003F) << $shift;
+				$mUcs4 |= $tmp;
+
+				/**
+				* End of the multi-octet sequence. mUcs4 now contains the final
+				* Unicode codepoint to be output
+				*/
+				if (0 == --$mState)
+				{
+					/*
+					* Check for illegal sequences and codepoints.
+					*/
+					// From Unicode 3.1, non-shortest form is illegal
+					if (((2 == $mBytes) && ($mUcs4 < 0x0080)) || ((3 == $mBytes) && ($mUcs4 < 0x0800)) ||
+					((4 == $mBytes) && ($mUcs4 < 0x10000)) || (4 < $mBytes) ||
+					// From Unicode 3.2, surrogate characters are illegal
+					(($mUcs4 & 0xFFFFF800) == 0xD800) ||
+					// Codepoints outside the Unicode range are illegal
+					($mUcs4 > 0x10FFFF))
+					{
+						return FALSE;
+					}
+
+					// Initialize UTF8 cache
+					$mState = 0;
+					$mUcs4  = 0;
+					$mBytes = 1;
+				}
+			}
+			else
+			{
+				/**
+				*((0xC0 & (*in) != 0x80) && (mState != 0))
+				* Incomplete multi-octet sequence.
+				*/
+
+				return false;
+			}
+		}
+	}
+
+	return true;
+}
+
+/**
+* Tests whether a string complies as UTF-8. This will be much
+* faster than utf8_is_valid, but will pass five and six octet
+* UTF-8 sequences, which are not supported by Unicode and
+* so cannot be displayed correctly in a browser. In other words
+* it is not as strict as utf8_is_valid but it's faster. If you use
+* is to validate user input, you place yourself at the risk that
+* attackers will be able to inject 5 and 6 byte sequences (which
+* may or may not be a significant risk, depending on what you are
+* are doing)
+* Note: Does not pass five and six octet UTF-8 sequences anymore in
+*       in the unit tests.
+* @see utf8_is_valid
+* @see http://www.php.net/manual/en/reference.pcre.pattern.modifiers.php#54805
+* @param string UTF-8 string to check
+* @return boolean TRUE if string is valid UTF-8
+* @package utf8
+* @subpackage validation
+*/
+function utf8_compliant($str)
+{
+	if (strlen($str) == 0)
+		return true;
+
+	// If even just the first character can be matched, when the /u
+	// modifier is used, then it's valid UTF-8. If the UTF-8 is somehow
+	// invalid, nothing at all will match, even if the string contains
+	// some valid sequences
+	return (preg_match('/^.{1}/us', $str, $ar) == 1);
+}
diff --git a/index.php b/index.php
new file mode 100644
index 0000000..1dccb94
--- /dev/null
+++ b/index.php
@@ -0,0 +1,241 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+
+
+if ($pun_user['g_read_board'] == '0')
+	message($lang_common['No view']);
+
+
+// Load the index.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/index.php';
+
+// Get list of forums and topics with new posts since last visit
+if (!$pun_user['is_guest'])
+{
+	$result = $db->query('SELECT t.forum_id, t.id, t.last_post FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.last_post>'.$pun_user['last_visit'].' AND t.moved_to IS NULL') or error('Unable to fetch new topics', __FILE__, __LINE__, $db->error());
+
+	$new_topics = array();
+	while ($cur_topic = $db->fetch_assoc($result))
+		$new_topics[$cur_topic['forum_id']][$cur_topic['id']] = $cur_topic['last_post'];
+
+	$tracked_topics = get_tracked_topics();
+}
+
+if ($pun_config['o_feed_type'] == '1')
+	$page_head = array('feed' => '<link rel="alternate" type="application/rss+xml" href="extern.php?action=feed&type=rss" title="'.$lang_common['RSS active topics feed'].'" />');
+else if ($pun_config['o_feed_type'] == '2')
+	$page_head = array('feed' => '<link rel="alternate" type="application/atom+xml" href="extern.php?action=feed&type=atom" title="'.$lang_common['Atom active topics feed'].'" />');
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']));
+define('PUN_ALLOW_INDEX', 1);
+define('PUN_ACTIVE_PAGE', 'index');
+require PUN_ROOT.'header.php';
+
+// Print the categories and forums
+$result = $db->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name, f.forum_desc, f.redirect_url, f.moderators, f.num_topics, f.num_posts, f.last_post, f.last_post_id, f.last_poster FROM '.$db->prefix.'categories AS c INNER JOIN '.$db->prefix.'forums AS f ON c.id=f.cat_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE fp.read_forum IS NULL OR fp.read_forum=1 ORDER BY c.disp_position, c.id, f.disp_position', true)  [...]
+
+$cur_category = 0;
+$cat_count = 0;
+$forum_count = 0;
+while ($cur_forum = $db->fetch_assoc($result))
+{
+	$moderators = '';
+
+	if ($cur_forum['cid'] != $cur_category) // A new category since last iteration?
+	{
+		if ($cur_category != 0)
+			echo "\t\t\t".'</tbody>'."\n\t\t\t".'</table>'."\n\t\t".'</div>'."\n\t".'</div>'."\n".'</div>'."\n\n";
+
+		++$cat_count;
+		$forum_count = 0;
+
+?>
+<div id="idx<?php echo $cat_count ?>" class="blocktable">
+	<h2><span><?php echo pun_htmlspecialchars($cur_forum['cat_name']) ?></span></h2>
+	<div class="box">
+		<div class="inbox">
+			<table cellspacing="0">
+			<thead>
+				<tr>
+					<th class="tcl" scope="col"><?php echo $lang_common['Forum'] ?></th>
+					<th class="tc2" scope="col"><?php echo $lang_index['Topics'] ?></th>
+					<th class="tc3" scope="col"><?php echo $lang_common['Posts'] ?></th>
+					<th class="tcr" scope="col"><?php echo $lang_common['Last post'] ?></th>
+				</tr>
+			</thead>
+			<tbody>
+<?php
+
+		$cur_category = $cur_forum['cid'];
+	}
+
+	++$forum_count;
+	$item_status = ($forum_count % 2 == 0) ? 'roweven' : 'rowodd';
+	$forum_field_new = '';
+	$icon_type = 'icon';
+
+	// Are there new posts since our last visit?
+	if (!$pun_user['is_guest'] && $cur_forum['last_post'] > $pun_user['last_visit'] && (empty($tracked_topics['forums'][$cur_forum['fid']]) || $cur_forum['last_post'] > $tracked_topics['forums'][$cur_forum['fid']]))
+	{
+		// There are new posts in this forum, but have we read all of them already?
+		foreach ($new_topics[$cur_forum['fid']] as $check_topic_id => $check_last_post)
+		{
+			if ((empty($tracked_topics['topics'][$check_topic_id]) || $tracked_topics['topics'][$check_topic_id] < $check_last_post) && (empty($tracked_topics['forums'][$cur_forum['fid']]) || $tracked_topics['forums'][$cur_forum['fid']] < $check_last_post))
+			{
+				$item_status .= ' inew';
+				$forum_field_new = '<span class="newtext">[ <a href="search.php?action=show_new&fid='.$cur_forum['fid'].'">'.$lang_common['New posts'].'</a> ]</span>';
+				$icon_type = 'icon icon-new';
+
+				break;
+			}
+		}
+	}
+
+	// Is this a redirect forum?
+	if ($cur_forum['redirect_url'] != '')
+	{
+		$forum_field = '<h3><span class="redirtext">'.$lang_index['Link to'].'</span> <a href="'.pun_htmlspecialchars($cur_forum['redirect_url']).'" title="'.$lang_index['Link to'].' '.pun_htmlspecialchars($cur_forum['redirect_url']).'">'.pun_htmlspecialchars($cur_forum['forum_name']).'</a></h3>';
+		$num_topics = $num_posts = '-';
+		$item_status .= ' iredirect';
+		$icon_type = 'icon';
+	}
+	else
+	{
+		$forum_field = '<h3><a href="viewforum.php?id='.$cur_forum['fid'].'">'.pun_htmlspecialchars($cur_forum['forum_name']).'</a>'.(!empty($forum_field_new) ? ' '.$forum_field_new : '').'</h3>';
+		$num_topics = $cur_forum['num_topics'];
+		$num_posts = $cur_forum['num_posts'];
+	}
+
+	if ($cur_forum['forum_desc'] != '')
+		$forum_field .= "\n\t\t\t\t\t\t\t\t".'<div class="forumdesc">'.$cur_forum['forum_desc'].'</div>';
+
+	// If there is a last_post/last_poster
+	if ($cur_forum['last_post'] != '')
+		$last_post = '<a href="viewtopic.php?pid='.$cur_forum['last_post_id'].'#p'.$cur_forum['last_post_id'].'">'.format_time($cur_forum['last_post']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_forum['last_poster']).'</span>';
+	else if ($cur_forum['redirect_url'] != '')
+		$last_post = '- - -';
+	else
+		$last_post = $lang_common['Never'];
+
+	if ($cur_forum['moderators'] != '')
+	{
+		$mods_array = unserialize($cur_forum['moderators']);
+		$moderators = array();
+
+		foreach ($mods_array as $mod_username => $mod_id)
+		{
+			if ($pun_user['g_view_users'] == '1')
+				$moderators[] = '<a href="profile.php?id='.$mod_id.'">'.pun_htmlspecialchars($mod_username).'</a>';
+			else
+				$moderators[] = pun_htmlspecialchars($mod_username);
+		}
+
+		$moderators = "\t\t\t\t\t\t\t\t".'<p class="modlist">(<em>'.$lang_common['Moderated by'].'</em> '.implode(', ', $moderators).')</p>'."\n";
+	}
+
+?>
+				<tr class="<?php echo $item_status ?>">
+					<td class="tcl">
+						<div class="<?php echo $icon_type ?>"><div class="nosize"><?php echo forum_number_format($forum_count) ?></div></div>
+						<div class="tclcon">
+							<div>
+								<?php echo $forum_field."\n".$moderators ?>
+							</div>
+						</div>
+					</td>
+					<td class="tc2"><?php echo forum_number_format($num_topics) ?></td>
+					<td class="tc3"><?php echo forum_number_format($num_posts) ?></td>
+					<td class="tcr"><?php echo $last_post ?></td>
+				</tr>
+<?php
+
+}
+
+// Did we output any categories and forums?
+if ($cur_category > 0)
+	echo "\t\t\t".'</tbody>'."\n\t\t\t".'</table>'."\n\t\t".'</div>'."\n\t".'</div>'."\n".'</div>'."\n\n";
+else
+	echo '<div id="idx0" class="block"><div class="box"><div class="inbox"><p>'.$lang_index['Empty board'].'</p></div></div></div>';
+
+
+// Collect some statistics from the database
+$result = $db->query('SELECT COUNT(id)-1 FROM '.$db->prefix.'users WHERE group_id!='.PUN_UNVERIFIED) or error('Unable to fetch total user count', __FILE__, __LINE__, $db->error());
+$stats['total_users'] = $db->result($result);
+
+$result = $db->query('SELECT id, username FROM '.$db->prefix.'users WHERE group_id!='.PUN_UNVERIFIED.' ORDER BY registered DESC LIMIT 1') or error('Unable to fetch newest registered user', __FILE__, __LINE__, $db->error());
+$stats['last_user'] = $db->fetch_assoc($result);
+
+$result = $db->query('SELECT SUM(num_topics), SUM(num_posts) FROM '.$db->prefix.'forums') or error('Unable to fetch topic/post count', __FILE__, __LINE__, $db->error());
+list($stats['total_topics'], $stats['total_posts']) = $db->fetch_row($result);
+
+if ($pun_user['g_view_users'] == '1')
+	$stats['newest_user'] = '<a href="profile.php?id='.$stats['last_user']['id'].'">'.pun_htmlspecialchars($stats['last_user']['username']).'</a>';
+else
+	$stats['newest_user'] = pun_htmlspecialchars($stats['last_user']['username']);
+
+?>
+<div id="brdstats" class="block">
+	<h2><span><?php echo $lang_index['Board info'] ?></span></h2>
+	<div class="box">
+		<div class="inbox">
+			<dl class="conr">
+				<dt><strong><?php echo $lang_index['Board stats'] ?></strong></dt>
+				<dd><span><?php printf($lang_index['No of users'], '<strong>'.forum_number_format($stats['total_users']).'</strong>') ?></span></dd>
+				<dd><span><?php printf($lang_index['No of topics'], '<strong>'.forum_number_format($stats['total_topics']).'</strong>') ?></span></dd>
+				<dd><span><?php printf($lang_index['No of posts'], '<strong>'.forum_number_format($stats['total_posts']).'</strong>') ?></span></dd>
+			</dl>
+			<dl class="conl">
+				<dt><strong><?php echo $lang_index['User info'] ?></strong></dt>
+				<dd><span><?php printf($lang_index['Newest user'], $stats['newest_user']) ?></span></dd>
+<?php
+
+if ($pun_config['o_users_online'] == '1')
+{
+	// Fetch users online info and generate strings for output
+	$num_guests = 0;
+	$users = array();
+	$result = $db->query('SELECT user_id, ident FROM '.$db->prefix.'online WHERE idle=0 ORDER BY ident', true) or error('Unable to fetch online list', __FILE__, __LINE__, $db->error());
+
+	while ($pun_user_online = $db->fetch_assoc($result))
+	{
+		if ($pun_user_online['user_id'] > 1)
+		{
+			if ($pun_user['g_view_users'] == '1')
+				$users[] = "\n\t\t\t\t".'<dd><a href="profile.php?id='.$pun_user_online['user_id'].'">'.pun_htmlspecialchars($pun_user_online['ident']).'</a>';
+			else
+				$users[] = "\n\t\t\t\t".'<dd>'.pun_htmlspecialchars($pun_user_online['ident']);
+		}
+		else
+			++$num_guests;
+	}
+
+	$num_users = count($users);
+	echo "\t\t\t\t".'<dd><span>'.sprintf($lang_index['Users online'], '<strong>'.forum_number_format($num_users).'</strong>').'</span></dd>'."\n\t\t\t\t".'<dd><span>'.sprintf($lang_index['Guests online'], '<strong>'.forum_number_format($num_guests).'</strong>').'</span></dd>'."\n\t\t\t".'</dl>'."\n";
+
+
+	if ($num_users > 0)
+		echo "\t\t\t".'<dl id="onlinelist" class="clearb">'."\n\t\t\t\t".'<dt><strong>'.$lang_index['Online'].' </strong></dt>'."\t\t\t\t".implode(',</dd> ', $users).'</dd>'."\n\t\t\t".'</dl>'."\n";
+	else
+		echo "\t\t\t".'<div class="clearer"></div>'."\n";
+
+}
+else
+	echo "\t\t\t".'</dl>'."\n\t\t\t".'<div class="clearer"></div>'."\n";
+
+
+?>
+		</div>
+	</div>
+</div>
+<?php
+
+$footer_style = 'index';
+require PUN_ROOT.'footer.php';
diff --git a/install.php b/install.php
new file mode 100644
index 0000000..bb0afc9
--- /dev/null
+++ b/install.php
@@ -0,0 +1,1754 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// The FluxBB version this script installs
+define('FORUM_VERSION', '1.4.2');
+
+define('FORUM_DB_REVISION', 8);
+define('FORUM_SI_REVISION', 1);
+define('FORUM_PARSER_REVISION', 1);
+
+define('MIN_PHP_VERSION', '4.3.0');
+define('MIN_MYSQL_VERSION', '4.1.2');
+define('MIN_PGSQL_VERSION', '7.0.0');
+define('PUN_SEARCH_MIN_WORD', 3);
+define('PUN_SEARCH_MAX_WORD', 20);
+
+
+define('PUN_ROOT', './');
+
+if (file_exists(PUN_ROOT.'config.php'))
+{
+	// Check to see whether FluxBB is already installed
+	include PUN_ROOT.'config.php';
+
+	// If we have the 1.3-legacy constant defined, define the proper 1.4 constant so we don't get an incorrect "need to install" message
+	if (defined('FORUM'))
+		define('PUN', FORUM);
+
+	// If PUN is defined, config.php is probably valid and thus the software is installed
+	if (defined('PUN'))
+		exit('It seems like FluxBB is already installed. You should go <a href="index.php">here</a> instead.');
+}
+
+// Define PUN because email.php requires it
+define('PUN', 1);
+
+// Make sure we are running at least MIN_PHP_VERSION
+if (!function_exists('version_compare') || version_compare(PHP_VERSION, MIN_PHP_VERSION, '<'))
+	exit('You are running PHP version '.PHP_VERSION.'. FluxBB '.FORUM_VERSION.' requires at least PHP '.MIN_PHP_VERSION.' to run properly. You must upgrade your PHP installation before you can continue.');
+
+// Load the functions script
+require PUN_ROOT.'include/functions.php';
+
+// Load UTF-8 functions
+require PUN_ROOT.'include/utf8/utf8.php';
+
+// Strip out "bad" UTF-8 characters
+forum_remove_bad_characters();
+
+// Reverse the effect of register_globals
+forum_unregister_globals();
+
+// Disable error reporting for uninitialized variables
+error_reporting(E_ALL);
+
+// Force POSIX locale (to prevent functions such as strtolower() from messing up UTF-8 strings)
+setlocale(LC_CTYPE, 'C');
+
+// Turn off magic_quotes_runtime
+if (get_magic_quotes_runtime())
+	set_magic_quotes_runtime(0);
+
+// Strip slashes from GET/POST/COOKIE (if magic_quotes_gpc is enabled)
+if (get_magic_quotes_gpc())
+{
+	function stripslashes_array($array)
+	{
+		return is_array($array) ? array_map('stripslashes_array', $array) : stripslashes($array);
+	}
+
+	$_GET = stripslashes_array($_GET);
+	$_POST = stripslashes_array($_POST);
+	$_COOKIE = stripslashes_array($_COOKIE);
+	$_REQUEST = stripslashes_array($_REQUEST);
+}
+
+// Turn off PHP time limit
+ at set_time_limit(0);
+
+//
+// Generate output to be used for config.php
+//
+function generate_config_file()
+{
+	global $db_type, $db_host, $db_name, $db_username, $db_password1, $db_prefix, $cookie_name, $cookie_seed;
+
+	return '<?php'."\n\n".'$db_type = \''.$db_type."';\n".'$db_host = \''.$db_host."';\n".'$db_name = \''.addslashes($db_name)."';\n".'$db_username = \''.addslashes($db_username)."';\n".'$db_password = \''.addslashes($db_password1)."';\n".'$db_prefix = \''.addslashes($db_prefix)."';\n".'$p_connect = false;'."\n\n".'$cookie_name = '."'".$cookie_name."';\n".'$cookie_domain = '."'';\n".'$cookie_path = '."'/';\n".'$cookie_secure = 0;'."\n".'$cookie_seed = \''.random_key(16, false, true)."';\n\n [...]
+}
+
+
+if (isset($_POST['generate_config']))
+{
+	header('Content-Type: text/x-delimtext; name="config.php"');
+	header('Content-disposition: attachment; filename=config.php');
+
+	$db_type = $_POST['db_type'];
+	$db_host = $_POST['db_host'];
+	$db_name = $_POST['db_name'];
+	$db_username = $_POST['db_username'];
+	$db_password1 = $_POST['db_password1'];
+	$db_prefix = $_POST['db_prefix'];
+	$cookie_name = $_POST['cookie_name'];
+	$cookie_seed = $_POST['cookie_seed'];
+
+	echo generate_config_file();
+	exit;
+}
+
+
+if (!isset($_POST['form_sent']))
+{
+	// Make an educated guess regarding base_url
+	$base_url  = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https://' : 'http://';	// protocol
+	$base_url .= preg_replace('/:(80|443)$/', '', $_SERVER['HTTP_HOST']);							// host[:port]
+	$base_url .= str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME']));							// path
+
+	if (substr($base_url, -1) == '/')
+		$base_url = substr($base_url, 0, -1);
+
+	$db_type = $db_name = $db_username = $db_prefix = $username = $email = '';
+	$db_host = 'localhost';
+	$title = 'My FluxBB forum';
+	$description = '<p><span>Unfortunately no one can be told what FluxBB is - you have to see it for yourself.</span></p>';
+	$default_lang = 'English';
+	$default_style = 'Air';
+}
+else
+{
+	$db_type = $_POST['req_db_type'];
+	$db_host = pun_trim($_POST['req_db_host']);
+	$db_name = pun_trim($_POST['req_db_name']);
+	$db_username = pun_trim($_POST['db_username']);
+	$db_password1 = pun_trim($_POST['db_password1']);
+	$db_password2 = pun_trim($_POST['db_password2']);
+	$db_prefix = pun_trim($_POST['db_prefix']);
+	$username = pun_trim($_POST['req_username']);
+	$email = strtolower(pun_trim($_POST['req_email']));
+	$password1 = pun_trim($_POST['req_password1']);
+	$password2 = pun_trim($_POST['req_password2']);
+	$title = pun_trim($_POST['req_title']);
+	$description = pun_trim($_POST['desc']);
+	$base_url = pun_trim($_POST['req_base_url']);
+	$default_lang = pun_trim($_POST['req_default_lang']);
+	$default_style = pun_trim($_POST['req_default_style']);
+	$alerts = array();
+
+	// Make sure base_url doesn't end with a slash
+	if (substr($base_url, -1) == '/')
+		$base_url = substr($base_url, 0, -1);
+
+	// Validate database password
+	if ($db_password1 != $db_password2)
+		$alerts[] = 'Database passwords do not match.';
+
+	// Validate username and passwords
+	if (pun_strlen($username) < 2)
+		$alerts[] = 'Usernames must be at least 2 characters long.';
+	else if (pun_strlen($username) > 25) // This usually doesn't happen since the form element only accepts 25 characters
+		$alerts[] = 'Usernames must not be more than 25 characters long.';
+	else if (!strcasecmp($username, 'Guest'))
+		$alerts[] = 'The username guest is reserved.';
+	else if (preg_match('/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/', $username) || preg_match('/((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|( [...]
+		$alerts[] = 'Usernames may not be in the form of an IP address.';
+	else if ((strpos($username, '[') !== false || strpos($username, ']') !== false) && strpos($username, '\'') !== false && strpos($username, '"') !== false)
+		$alerts[] = 'Usernames may not contain all the characters \', " and [ or ] at once.';
+	else if (preg_match('/(?:\[\/?(?:b|u|i|h|colou?r|quote|code|img|url|email|list)\]|\[(?:code|quote|list)=)/i', $username))
+		$alerts[] = 'Usernames may not contain any of the text formatting tags (BBCode) that the forum uses.';
+
+	if (pun_strlen($password1) < 4)
+		$alerts[] = 'Passwords must be at least 4 characters long.';
+	else if ($password1 != $password2)
+		$alerts[] = 'Passwords do not match.';
+
+	// Validate email
+	require PUN_ROOT.'include/email.php';
+
+	if (!is_valid_email($email))
+		$alerts[] = 'The administrator email address you entered is invalid.';
+
+	if ($title == '')
+		$alerts[] = 'You must enter a board title.';
+
+	$default_lang = preg_replace('#[\.\\\/]#', '', $default_lang);
+	if (!file_exists(PUN_ROOT.'lang/'.$default_lang.'/common.php'))
+		$alerts[] = 'The default language chosen doesn\'t seem to exist.';
+
+	$default_style = preg_replace('#[\.\\\/]#', '', $default_style);
+	if (!file_exists(PUN_ROOT.'style/'.$default_style.'.css'))
+		$alerts[] = 'The default style chosen doesn\'t seem to exist.';
+}
+
+if (!isset($_POST['form_sent']) || !empty($alerts))
+{
+	// Determine available database extensions
+	$dual_mysql = false;
+	$db_extensions = array();
+	$mysql_innodb = false;
+	if (function_exists('mysqli_connect'))
+	{
+		$db_extensions[] = array('mysqli', 'MySQL Improved');
+		$db_extensions[] = array('mysqli_innodb', 'MySQL Improved (InnoDB)');
+		$mysql_innodb = true;
+	}
+	if (function_exists('mysql_connect'))
+	{
+		$db_extensions[] = array('mysql', 'MySQL Standard');
+		$db_extensions[] = array('mysql_innodb', 'MySQL Standard (InnoDB)');
+		$mysql_innodb = true;
+
+		if (count($db_extensions) > 2)
+			$dual_mysql = true;
+	}
+	if (function_exists('sqlite_open'))
+		$db_extensions[] = array('sqlite', 'SQLite');
+	if (function_exists('pg_connect'))
+		$db_extensions[] = array('pgsql', 'PostgreSQL');
+
+	if (empty($db_extensions))
+		exit('This PHP environment does not have support for any of the databases that FluxBB supports. PHP needs to have support for either MySQL, PostgreSQL or SQLite in order for FluxBB to be installed.');
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>FluxBB Installation</title>
+<link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
+<script type="text/javascript">
+function process_form(the_form)
+{
+	var element_names = new Object()
+	element_names["req_db_type"] = "Database type"
+	element_names["req_db_host"] = "Database server hostname"
+	element_names["req_db_name"] = "Database name"
+	element_names["db_prefix"] = "Table prefix"
+	element_names["req_username"] = "Administrator username"
+	element_names["req_password1"] = "Administrator password 1"
+	element_names["req_password2"] = "Administrator password 2"
+	element_names["req_email"] = "Administrator's email"
+	element_names["req_title"] = "Board title"
+	element_names["req_base_url"] = "Base URL"
+
+	if (document.all || document.getElementById)
+	{
+		for (var i = 0; i < the_form.length; ++i)
+		{
+			var elem = the_form.elements[i]
+			if (elem.name && elem.name.substring(0, 4) == "req_")
+			{
+				if (elem.type && (elem.type=="text" || elem.type=="textarea" || elem.type=="password" || elem.type=="file") && elem.value=='')
+				{
+					alert("\"" + element_names[elem.name] + "\" is a required field in this form.")
+					elem.focus()
+					return false
+				}
+			}
+		}
+	}
+
+	return true
+}
+</script>
+</head>
+<body onload="document.getElementById('install').req_db_type.focus();document.getElementById('install').start.disabled=false;">
+
+<div id="puninstall" class="pun">
+<div class="top-box"><div><!-- Top Corners --></div></div>
+<div class="punwrap">
+
+<div id="brdheader" class="block">
+	<div class="box">
+		<div id="brdtitle" class="inbox">
+			<h1><span>FluxBB Installation</span></h1>
+			<div id="brddesc"><p>Welcome to FluxBB installation. You are about to install FluxBB. In order to install FluxBB, you must complete the form set out below. If you encounter any difficulties with the installation, please refer to the documentation.</p></div>
+		</div>
+	</div>
+</div>
+
+<div id="brdmain">
+<div class="blockform">
+	<h2><span>Install FluxBB 1.4</span></h2>
+	<div class="box">
+		<form id="install" method="post" action="install.php" onsubmit="this.start.disabled=true;if(process_form(this)){return true;}else{this.start.disabled=false;return false;}">
+		<div><input type="hidden" name="form_sent" value="1" /></div>
+			<div class="inform">
+<?php if (!empty($alerts)): ?>				<div class="forminfo error-info">
+					<h3>The following errors need to be corrected:</h3>
+					<ul class="error-list">
+<?php
+
+foreach ($alerts as $cur_alert)
+	echo "\t\t\t\t\t\t".'<li><strong>'.$cur_alert.'</strong></li>'."\n";
+?>
+					</ul>
+				</div>
+<?php endif; ?>			</div>
+			<div class="inform">
+				<div class="forminfo">
+					<h3>Database setup</h3>
+					<p>Please enter the requested information in order to setup your database for FluxBB. You must know all the information asked for before proceeding with the installation.</p>
+				</div>
+				<fieldset>
+				<legend>Select your database type</legend>
+					<div class="infldset">
+						<p>FluxBB currently supports MySQL, PostgreSQL and SQLite. If your database of choice is missing from the drop-down menu below, it means this PHP environment does not have support for that particular database. More information regarding support for particular versions of each database can be found in the FAQ.</p>
+<?php if ($dual_mysql): ?>						<p>FluxBB has detected that your PHP environment supports two different ways of communicating with MySQL. The two options are called standard and improved. If you are uncertain which one to use, start by trying improved and if that fails, try standard.</p>
+<?php endif; ?><?php if ($mysql_innodb): ?>						<p>FluxBB has detected that your MySQL server might support <a href="http://dev.mysql.com/doc/refman/5.0/en/innodb.html">InnoDB</a>. This would be a good choice if you are planning to run a large forum. If you are uncertain, it is recommended that you do not use InnoDB.</p>
+<?php endif; ?>						<label class="required"><strong>Database type <span>(Required)</span></strong>
+						<br /><select name="req_db_type">
+<?php
+
+	foreach ($db_extensions as $temp)
+	{
+		if ($temp[0] == $db_type)
+			echo "\t\t\t\t\t\t\t".'<option value="'.$temp[0].'" selected="selected">'.$temp[1].'</option>'."\n";
+		else
+			echo "\t\t\t\t\t\t\t".'<option value="'.$temp[0].'">'.$temp[1].'</option>'."\n";
+	}
+
+?>
+						</select>
+						<br /></label>
+					</div>
+				</fieldset>
+			</div>
+			<div class="inform">
+				<fieldset>
+					<legend>Enter your database server hostname</legend>
+					<div class="infldset">
+						<p>The address of the database server (example: localhost, db.myhost.com or 192.168.0.15). You can specify a custom port number if your database doesn't run on the default port (example: localhost:3580). For SQLite support, just enter anything or leave it at 'localhost'.</p>
+						<label class="required"><strong>Database server hostname <span>(Required)</span></strong><br /><input type="text" name="req_db_host" value="<?php echo pun_htmlspecialchars($db_host) ?>" size="50" maxlength="100" /><br /></label>
+					</div>
+				</fieldset>
+			</div>
+			<div class="inform">
+				<fieldset>
+					<legend>Enter the name of your database</legend>
+					<div class="infldset">
+						<p>The name of the database that FluxBB will be installed into. The database must exist. For SQLite, this is the relative path to the database file. If the SQLite database file does not exist, FluxBB will attempt to create it.</p>
+						<label class="required"><strong>Database name <span>(Required)</span></strong><br /><input id="req_db_name" type="text" name="req_db_name" value="<?php echo pun_htmlspecialchars($db_name) ?>" size="30" maxlength="50" /><br /></label>
+					</div>
+				</fieldset>
+			</div>
+			<div class="inform">
+				<fieldset>
+					<legend>Enter your database username and password</legend>
+					<div class="infldset">
+						<p>Enter the username and password with which you connect to the database. Ignore for SQLite.</p>
+						<label class="conl">Database username<br /><input type="text" name="db_username" value="<?php echo pun_htmlspecialchars($db_username) ?>" size="30" maxlength="50" /><br /></label>
+						<label class="conl">Database password<br /><input type="password" name="db_password1" size="30" maxlength="50" /><br /></label>
+						<label class="conl">Confirm database password<br /><input type="password" name="db_password2" size="30" maxlength="50" /><br /></label>
+						<div class="clearer"></div>
+					</div>
+				</fieldset>
+			</div>
+			<div class="inform">
+				<fieldset>
+					<legend>Enter database table prefix</legend>
+					<div class="infldset">
+						<p>If you like, you can specify a table prefix. This way you can run multiple copies of FluxBB in the same database (example: foo_).</p>
+						<label>Table prefix<br /><input id="db_prefix" type="text" name="db_prefix" value="<?php echo pun_htmlspecialchars($db_prefix) ?>" size="20" maxlength="30" /><br /></label>
+					</div>
+				</fieldset>
+			</div>
+			<div class="inform">
+				<div class="forminfo">
+					<h3>Administration setup</h3>
+					<p>Please enter the requested information in order to setup an administrator for your FluxBB installation.</p>
+				</div>
+				<fieldset>
+					<legend>Enter Administrator's username</legend>
+					<div class="infldset">
+						<p>The username of the forum administrator. You can later create more administrators and moderators. Usernames can be between 2 and 25 characters long.</p>
+						<label class="required"><strong>Administrator's username <span>(Required)</span></strong><br /><input type="text" name="req_username" value="<?php echo pun_htmlspecialchars($username) ?>" size="25" maxlength="25" /><br /></label>
+					</div>
+				</fieldset>
+			</div>
+			<div class="inform">
+				<fieldset>
+					<legend>Enter and confirm Administrator's password</legend>
+					<div class="infldset">
+					<p>Passwords must be at least 4 characters long. Passwords are case sensitive.</p>
+						<label class="conl required"><strong>Password <span>(Required)</span></strong><br /><input id="req_password1" type="password" name="req_password1" size="16" /><br /></label>
+						<label class="conl required"><strong>Confirm password <span>(Required)</span></strong><br /><input type="password" name="req_password2" size="16" /><br /></label>
+						<div class="clearer"></div>
+					</div>
+				</fieldset>
+			</div>
+			<div class="inform">
+				<fieldset>
+					<legend>Enter Administrator's email</legend>
+					<div class="infldset">
+						<p>The email address of the forum administrator.</p>
+						<label class="required"><strong>Administrator's email <span>(Required)</span></strong><br /><input id="req_email" type="text" name="req_email" value="<?php echo pun_htmlspecialchars($email) ?>" size="50" maxlength="80" /><br /></label>
+					</div>
+				</fieldset>
+			</div>
+			<div class="inform">
+				<div class="forminfo">
+					<h3>Board setup</h3>
+					<p>Please enter the requested information in order to setup your FluxBB board.</p>
+				</div>
+				<fieldset>
+					<legend>Enter your board's title</legend>
+					<div class="infldset">
+						<p>The title of this bulletin board (shown at the top of every page).</p>
+						<label class="required"><strong>Board title <span>(Required)</span></strong><br /><input id="req_title" type="text" name="req_title" value="<?php echo pun_htmlspecialchars($title) ?>" size="60" maxlength="255" /><br /></label>
+					</div>
+				</fieldset>
+			</div>
+			<div class="inform">
+				<fieldset>
+					<legend>Enter your board's description</legend>
+					<div class="infldset">
+						<p>A short description of this bulletin board (shown at the top of every page). This field may contain HTML.</p>
+						<label><strong>Board description</strong><br /><input id="desc" type="text" name="desc" value="<?php echo pun_htmlspecialchars($description) ?>" size="60" maxlength="255" /><br /></label>
+					</div>
+				</fieldset>
+			</div>
+			<div class="inform">
+				<fieldset>
+					<legend>Enter the Base URL of your FluxBB installation</legend>
+					<div class="infldset">
+						<p>The URL (without trailing slash) of your FluxBB forum (example: http://forum.myhost.com or http://myhost.com/~myuser). This <strong>must</strong> be correct, otherwise, administrators and moderators will not be able to submit any forms. Please note that the preset value below is just an educated guess by FluxBB.</p>
+						<label class="required"><strong>Base URL <span>(Required)</span></strong><br /><input id="req_base_url" type="text" name="req_base_url" value="<?php echo pun_htmlspecialchars($base_url) ?>" size="60" maxlength="100" /><br /></label>
+					</div>
+				</fieldset>
+			</div>
+			<div class="inform">
+				<fieldset>
+					<legend>Choose the default language</legend>
+					<div class="infldset">
+						<p>The default language used for guests and users who haven't changed from the default in their profile.</p>
+						<label class="required"><strong>Default language <span>(Required)</span></strong><br /><select id="req_default_lang" name="req_default_lang">
+<?php
+
+		$languages = forum_list_langs();
+
+		foreach ($languages as $temp)
+		{
+			if ($temp == $default_lang)
+				echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$temp.'" selected="selected">'.$temp.'</option>'."\n";
+			else
+				echo "\t\t\t\t\t\t\t\t\t\t\t".'<option value="'.$temp.'">'.$temp.'</option>'."\n";
+		}
+
+?>
+						</select><br /></label>
+					</div>
+				</fieldset>
+			</div>
+			<div class="inform">
+				<fieldset>
+					<legend>Choose the default style</legend>
+					<div class="infldset">
+						<p>The default style used for guests and users who haven't changed from the default in their profile.</p>
+						<label class="required"><strong>Default style <span>(Required)</span></strong><br /><select id="req_default_style" name="req_default_style">
+<?php
+
+		$styles = forum_list_styles();
+
+		foreach ($styles as $temp)
+		{
+			if ($temp == $default_style)
+				echo "\t\t\t\t\t\t\t\t\t".'<option value="'.$temp.'" selected="selected">'.str_replace('_', ' ', $temp).'</option>'."\n";
+			else
+				echo "\t\t\t\t\t\t\t\t\t".'<option value="'.$temp.'">'.str_replace('_', ' ', $temp).'</option>'."\n";
+		}
+
+?>
+						</select><br /></label>
+					</div>
+				</fieldset>
+			</div>
+			<p class="buttons"><input type="submit" name="start" value="Start install" /></p>
+		</form>
+	</div>
+</div>
+</div>
+
+</div>
+<div class="end-box"><div><!-- Bottom Corners --></div></div>
+</div>
+
+</body>
+</html>
+<?php
+
+}
+else
+{
+	// Load the appropriate DB layer class
+	switch ($db_type)
+	{
+		case 'mysql':
+			require PUN_ROOT.'include/dblayer/mysql.php';
+			break;
+
+		case 'mysql_innodb':
+			require PUN_ROOT.'include/dblayer/mysql_innodb.php';
+			break;
+
+		case 'mysqli':
+			require PUN_ROOT.'include/dblayer/mysqli.php';
+			break;
+
+		case 'mysqli_innodb':
+			require PUN_ROOT.'include/dblayer/mysqli_innodb.php';
+			break;
+
+		case 'pgsql':
+			require PUN_ROOT.'include/dblayer/pgsql.php';
+			break;
+
+		case 'sqlite':
+			require PUN_ROOT.'include/dblayer/sqlite.php';
+			break;
+
+		default:
+			error('\''.pun_htmlspecialchars($db_type).'\' is not a valid database type');
+	}
+
+	// Create the database object (and connect/select db)
+	$db = new DBLayer($db_host, $db_username, $db_password1, $db_name, $db_prefix, false);
+
+	// Validate prefix
+	if (strlen($db_prefix) > 0 && (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $db_prefix) || strlen($db_prefix) > 40))
+		error('The table prefix \''.$db->prefix.'\' contains illegal characters or is too long. The prefix may contain the letters a to z, any numbers and the underscore character. They must however not start with a number. The maximum length is 40 characters. Please choose a different prefix');
+
+	// Do some DB type specific checks
+	switch ($db_type)
+	{
+		case 'mysql':
+		case 'mysqli':
+		case 'mysql_innodb':
+		case 'mysqli_innodb':
+			$mysql_info = $db->get_version();
+			if (version_compare($mysql_info['version'], MIN_MYSQL_VERSION, '<'))
+				error('You are running MySQL version '.$mysql_version.'. FluxBB '.FORUM_VERSION.' requires at least MySQL '.MIN_MYSQL_VERSION.' to run properly. You must upgrade your MySQL installation before you can continue');
+			break;
+
+		case 'pgsql':
+			$pgsql_info = $db->get_version();
+			if (version_compare($pgsql_info['version'], MIN_PGSQL_VERSION, '<'))
+				error('You are running PostgreSQL version '.$pgsql_info.'. FluxBB '.FORUM_VERSION.' requires at least PostgreSQL '.MIN_PGSQL_VERSION.' to run properly. You must upgrade your PostgreSQL installation before you can continue');
+			break;
+
+		case 'sqlite':
+			if (strtolower($db_prefix) == 'sqlite_')
+				error('The table prefix \'sqlite_\' is reserved for use by the SQLite engine. Please choose a different prefix');
+			break;
+	}
+
+
+	// Make sure FluxBB isn't already installed
+	$result = $db->query('SELECT 1 FROM '.$db_prefix.'users WHERE id=1');
+	if ($db->num_rows($result))
+		error('A table called "'.$db_prefix.'users" is already present in the database "'.$db_name.'". This could mean that FluxBB is already installed or that another piece of software is installed and is occupying one or more of the table names FluxBB requires. If you want to install multiple copies of FluxBB in the same database, you must choose a different table prefix');
+
+	// Check if InnoDB is available
+	if ($db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
+	{
+		$result = $db->query('SHOW VARIABLES LIKE \'have_innodb\'');
+		list (, $result) = $db->fetch_row($result);
+		if ((strtoupper($result) != 'YES'))
+			error('InnoDB does not seem to be enabled. Please choose a database layer that does not have InnoDB support, or enable InnoDB on your MySQL server');
+	}
+
+
+	// Start a transaction
+	$db->start_transaction();
+
+
+	// Create all tables
+	$schema = array(
+		'FIELDS'		=> array(
+			'id'			=> array(
+				'datatype'		=> 'SERIAL',
+				'allow_null'	=> false
+			),
+			'username'		=> array(
+				'datatype'		=> 'VARCHAR(200)',
+				'allow_null'	=> true
+			),
+			'ip'			=> array(
+				'datatype'		=> 'VARCHAR(255)',
+				'allow_null'	=> true
+			),
+			'email'			=> array(
+				'datatype'		=> 'VARCHAR(80)',
+				'allow_null'	=> true
+			),
+			'message'		=> array(
+				'datatype'		=> 'VARCHAR(255)',
+				'allow_null'	=> true
+			),
+			'expire'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> true
+			),
+			'ban_creator'	=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			)
+		),
+		'PRIMARY KEY'	=> array('id'),
+		'INDEXES'		=> array(
+			'username_idx'	=> array('username')
+		)
+	);
+
+	if ($db_type == 'mysql' || $db_type == 'mysqli' || $db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
+		$schema['INDEXES']['username_idx'] = array('username(25)');
+
+	$db->create_table('bans', $schema) or error('Unable to create bans table', __FILE__, __LINE__, $db->error());
+
+
+	$schema = array(
+		'FIELDS'		=> array(
+			'id'			=> array(
+				'datatype'		=> 'SERIAL',
+				'allow_null'	=> false
+			),
+			'cat_name'		=> array(
+				'datatype'		=> 'VARCHAR(80)',
+				'allow_null'	=> false,
+				'default'		=> '\'New Category\''
+			),
+			'disp_position'	=> array(
+				'datatype'		=> 'INT(10)',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			)
+		),
+		'PRIMARY KEY'	=> array('id')
+	);
+
+	$db->create_table('categories', $schema) or error('Unable to create categories table', __FILE__, __LINE__, $db->error());
+
+
+	$schema = array(
+		'FIELDS'		=> array(
+			'id'			=> array(
+				'datatype'		=> 'SERIAL',
+				'allow_null'	=> false
+			),
+			'search_for'	=> array(
+				'datatype'		=> 'VARCHAR(60)',
+				'allow_null'	=> false,
+				'default'		=> '\'\''
+			),
+			'replace_with'	=> array(
+				'datatype'		=> 'VARCHAR(60)',
+				'allow_null'	=> false,
+				'default'		=> '\'\''
+			)
+		),
+		'PRIMARY KEY'	=> array('id')
+	);
+
+	$db->create_table('censoring', $schema) or error('Unable to create censoring table', __FILE__, __LINE__, $db->error());
+
+
+	$schema = array(
+		'FIELDS'		=> array(
+			'conf_name'		=> array(
+				'datatype'		=> 'VARCHAR(255)',
+				'allow_null'	=> false,
+				'default'		=> '\'\''
+			),
+			'conf_value'	=> array(
+				'datatype'		=> 'TEXT',
+				'allow_null'	=> true
+			)
+		),
+		'PRIMARY KEY'	=> array('conf_name')
+	);
+
+	$db->create_table('config', $schema) or error('Unable to create config table', __FILE__, __LINE__, $db->error());
+
+
+	$schema = array(
+		'FIELDS'		=> array(
+			'group_id'		=> array(
+				'datatype'		=> 'INT(10)',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'forum_id'		=> array(
+				'datatype'		=> 'INT(10)',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'read_forum'	=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'post_replies'	=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'post_topics'	=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			)
+		),
+		'PRIMARY KEY'	=> array('group_id', 'forum_id')
+	);
+
+	$db->create_table('forum_perms', $schema) or error('Unable to create forum_perms table', __FILE__, __LINE__, $db->error());
+
+
+	$schema = array(
+		'FIELDS'		=> array(
+			'id'			=> array(
+				'datatype'		=> 'SERIAL',
+				'allow_null'	=> false
+			),
+			'forum_name'	=> array(
+				'datatype'		=> 'VARCHAR(80)',
+				'allow_null'	=> false,
+				'default'		=> '\'New forum\''
+			),
+			'forum_desc'	=> array(
+				'datatype'		=> 'TEXT',
+				'allow_null'	=> true
+			),
+			'redirect_url'	=> array(
+				'datatype'		=> 'VARCHAR(100)',
+				'allow_null'	=> true
+			),
+			'moderators'	=> array(
+				'datatype'		=> 'TEXT',
+				'allow_null'	=> true
+			),
+			'num_topics'	=> array(
+				'datatype'		=> 'MEDIUMINT(8) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'num_posts'		=> array(
+				'datatype'		=> 'MEDIUMINT(8) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'last_post'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> true
+			),
+			'last_post_id'	=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> true
+			),
+			'last_poster'	=> array(
+				'datatype'		=> 'VARCHAR(200)',
+				'allow_null'	=> true
+			),
+			'sort_by'		=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'disp_position'	=> array(
+				'datatype'		=> 'INT(10)',
+				'allow_null'	=> false,
+				'default'		=>	'0'
+			),
+			'cat_id'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=>	'0'
+			)
+		),
+		'PRIMARY KEY'	=> array('id')
+	);
+
+	$db->create_table('forums', $schema) or error('Unable to create forums table', __FILE__, __LINE__, $db->error());
+
+
+	$schema = array(
+		'FIELDS'		=> array(
+			'g_id'						=> array(
+				'datatype'		=> 'SERIAL',
+				'allow_null'	=> false
+			),
+			'g_title'					=> array(
+				'datatype'		=> 'VARCHAR(50)',
+				'allow_null'	=> false,
+				'default'		=> '\'\''
+			),
+			'g_user_title'				=> array(
+				'datatype'		=> 'VARCHAR(50)',
+				'allow_null'	=> true
+			),
+			'g_moderator'				=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'g_mod_edit_users'			=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'g_mod_rename_users'		=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'g_mod_change_passwords'	=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'g_mod_ban_users'			=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'g_read_board'				=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'g_view_users'				=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'g_post_replies'			=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'g_post_topics'				=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'g_edit_posts'				=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'g_delete_posts'			=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'g_delete_topics'			=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'g_set_title'				=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'g_search'					=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'g_search_users'			=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'g_send_email'				=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'g_post_flood'				=> array(
+				'datatype'		=> 'SMALLINT(6)',
+				'allow_null'	=> false,
+				'default'		=> '30'
+			),
+			'g_search_flood'			=> array(
+				'datatype'		=> 'SMALLINT(6)',
+				'allow_null'	=> false,
+				'default'		=> '30'
+			),
+			'g_email_flood'				=> array(
+				'datatype'		=> 'SMALLINT(6)',
+				'allow_null'	=> false,
+				'default'		=> '60'
+			)
+		),
+		'PRIMARY KEY'	=> array('g_id')
+	);
+
+	$db->create_table('groups', $schema) or error('Unable to create groups table', __FILE__, __LINE__, $db->error());
+
+
+	$schema = array(
+		'FIELDS'		=> array(
+			'user_id'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'ident'			=> array(
+				'datatype'		=> 'VARCHAR(200)',
+				'allow_null'	=> false,
+				'default'		=> '\'\''
+			),
+			'logged'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'idle'			=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'last_post'			=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> true
+			),
+			'last_search'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> true
+			),
+		),
+		'UNIQUE KEYS'	=> array(
+			'user_id_ident_idx'	=> array('user_id', 'ident')
+		),
+		'INDEXES'		=> array(
+			'ident_idx'		=> array('ident'),
+			'logged_idx'	=> array('logged')
+		),
+		'ENGINE'		=> 'HEAP'
+	);
+
+	if ($db_type == 'mysql' || $db_type == 'mysqli' || $db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
+	{
+		$schema['UNIQUE KEYS']['user_id_ident_idx'] = array('user_id', 'ident(25)');
+		$schema['INDEXES']['ident_idx'] = array('ident(25)');
+	}
+
+	if ($db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
+		$schema['ENGINE'] = 'InnoDB';
+
+	$db->create_table('online', $schema) or error('Unable to create online table', __FILE__, __LINE__, $db->error());
+
+
+	$schema = array(
+		'FIELDS'		=> array(
+			'id'			=> array(
+				'datatype'		=> 'SERIAL',
+				'allow_null'	=> false
+			),
+			'poster'		=> array(
+				'datatype'		=> 'VARCHAR(200)',
+				'allow_null'	=> false,
+				'default'		=> '\'\''
+			),
+			'poster_id'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'poster_ip'		=> array(
+				'datatype'		=> 'VARCHAR(39)',
+				'allow_null'	=> true
+			),
+			'poster_email'	=> array(
+				'datatype'		=> 'VARCHAR(80)',
+				'allow_null'	=> true
+			),
+			'message'		=> array(
+				'datatype'		=> 'MEDIUMTEXT',
+				'allow_null'	=> true
+			),
+			'hide_smilies'	=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'posted'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'edited'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> true
+			),
+			'edited_by'		=> array(
+				'datatype'		=> 'VARCHAR(200)',
+				'allow_null'	=> true
+			),
+			'topic_id'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			)
+		),
+		'PRIMARY KEY'	=> array('id'),
+		'INDEXES'		=> array(
+			'topic_id_idx'	=> array('topic_id'),
+			'multi_idx'		=> array('poster_id', 'topic_id')
+		)
+	);
+
+	$db->create_table('posts', $schema) or error('Unable to create posts table', __FILE__, __LINE__, $db->error());
+
+
+	$schema = array(
+		'FIELDS'		=> array(
+			'id'			=> array(
+				'datatype'		=> 'SERIAL',
+				'allow_null'	=> false
+			),
+			'rank'			=> array(
+				'datatype'		=> 'VARCHAR(50)',
+				'allow_null'	=> false,
+				'default'		=> '\'\''
+			),
+			'min_posts'		=> array(
+				'datatype'		=> 'MEDIUMINT(8) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			)
+		),
+		'PRIMARY KEY'	=> array('id')
+	);
+
+	$db->create_table('ranks', $schema) or error('Unable to create ranks table', __FILE__, __LINE__, $db->error());
+
+
+	$schema = array(
+		'FIELDS'		=> array(
+			'id'			=> array(
+				'datatype'		=> 'SERIAL',
+				'allow_null'	=> false
+			),
+			'post_id'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'topic_id'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'forum_id'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'reported_by'	=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'created'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'message'		=> array(
+				'datatype'		=> 'TEXT',
+				'allow_null'	=> true
+			),
+			'zapped'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> true
+			),
+			'zapped_by'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> true
+			)
+		),
+		'PRIMARY KEY'	=> array('id'),
+		'INDEXES'		=> array(
+			'zapped_idx'	=> array('zapped')
+		)
+	);
+
+	$db->create_table('reports', $schema) or error('Unable to create reports table', __FILE__, __LINE__, $db->error());
+
+
+	$schema = array(
+		'FIELDS'		=> array(
+			'id'			=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'ident'			=> array(
+				'datatype'		=> 'VARCHAR(200)',
+				'allow_null'	=> false,
+				'default'		=> '\'\''
+			),
+			'search_data'	=> array(
+				'datatype'		=> 'MEDIUMTEXT',
+				'allow_null'	=> true
+			)
+		),
+		'PRIMARY KEY'	=> array('id'),
+		'INDEXES'		=> array(
+			'ident_idx'	=> array('ident')
+		)
+	);
+
+	if ($db_type == 'mysql' || $db_type == 'mysqli' || $db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
+		$schema['INDEXES']['ident_idx'] = array('ident(8)');
+
+	$db->create_table('search_cache', $schema) or error('Unable to create search_cache table', __FILE__, __LINE__, $db->error());
+
+
+	$schema = array(
+		'FIELDS'		=> array(
+			'post_id'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'word_id'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'subject_match'	=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			)
+		),
+		'INDEXES'		=> array(
+			'word_id_idx'	=> array('word_id'),
+			'post_id_idx'	=> array('post_id')
+		)
+	);
+
+	$db->create_table('search_matches', $schema) or error('Unable to create search_matches table', __FILE__, __LINE__, $db->error());
+
+
+	$schema = array(
+		'FIELDS'		=> array(
+			'id'			=> array(
+				'datatype'		=> 'SERIAL',
+				'allow_null'	=> false
+			),
+			'word'			=> array(
+				'datatype'		=> 'VARCHAR(20)',
+				'allow_null'	=> false,
+				'default'		=> '\'\'',
+				'collation'		=> 'bin'
+			)
+		),
+		'PRIMARY KEY'	=> array('word'),
+		'INDEXES'		=> array(
+			'id_idx'	=> array('id')
+		)
+	);
+
+	if ($db_type == 'sqlite')
+	{
+		$schema['PRIMARY KEY'] = array('id');
+		$schema['UNIQUE KEYS'] = array('word_idx'	=> array('word'));
+	}
+
+	$db->create_table('search_words', $schema) or error('Unable to create search_words table', __FILE__, __LINE__, $db->error());
+
+
+	$schema = array(
+		'FIELDS'		=> array(
+			'user_id'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'topic_id'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			)
+		),
+		'PRIMARY KEY'	=> array('user_id', 'topic_id')
+	);
+
+	$db->create_table('subscriptions', $schema) or error('Unable to create subscriptions table', __FILE__, __LINE__, $db->error());
+
+
+	$schema = array(
+		'FIELDS'		=> array(
+			'id'			=> array(
+				'datatype'		=> 'SERIAL',
+				'allow_null'	=> false
+			),
+			'poster'		=> array(
+				'datatype'		=> 'VARCHAR(200)',
+				'allow_null'	=> false,
+				'default'		=> '\'\''
+			),
+			'subject'		=> array(
+				'datatype'		=> 'VARCHAR(255)',
+				'allow_null'	=> false,
+				'default'		=> '\'\''
+			),
+			'posted'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'first_post_id'	=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'last_post'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'last_post_id'	=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'last_poster'	=> array(
+				'datatype'		=> 'VARCHAR(200)',
+				'allow_null'	=> true
+			),
+			'num_views'		=> array(
+				'datatype'		=> 'MEDIUMINT(8) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'num_replies'	=> array(
+				'datatype'		=> 'MEDIUMINT(8) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'closed'		=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'sticky'		=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'moved_to'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> true
+			),
+			'forum_id'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			)
+		),
+		'PRIMARY KEY'	=> array('id'),
+		'INDEXES'		=> array(
+			'forum_id_idx'		=> array('forum_id'),
+			'moved_to_idx'		=> array('moved_to'),
+			'last_post_idx'		=> array('last_post'),
+			'first_post_id_idx'	=> array('first_post_id')
+		)
+	);
+
+	$db->create_table('topics', $schema) or error('Unable to create topics table', __FILE__, __LINE__, $db->error());
+
+
+	$schema = array(
+		'FIELDS'		=> array(
+			'id'				=> array(
+				'datatype'		=> 'SERIAL',
+				'allow_null'	=> false
+			),
+			'group_id'			=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '3'
+			),
+			'username'			=> array(
+				'datatype'		=> 'VARCHAR(200)',
+				'allow_null'	=> false,
+				'default'		=> '\'\''
+			),
+			'password'			=> array(
+				'datatype'		=> 'VARCHAR(40)',
+				'allow_null'	=> false,
+				'default'		=> '\'\''
+			),
+			'email'				=> array(
+				'datatype'		=> 'VARCHAR(80)',
+				'allow_null'	=> false,
+				'default'		=> '\'\''
+			),
+			'title'				=> array(
+				'datatype'		=> 'VARCHAR(50)',
+				'allow_null'	=> true
+			),
+			'realname'			=> array(
+				'datatype'		=> 'VARCHAR(40)',
+				'allow_null'	=> true
+			),
+			'url'				=> array(
+				'datatype'		=> 'VARCHAR(100)',
+				'allow_null'	=> true
+			),
+			'jabber'			=> array(
+				'datatype'		=> 'VARCHAR(80)',
+				'allow_null'	=> true
+			),
+			'icq'				=> array(
+				'datatype'		=> 'VARCHAR(12)',
+				'allow_null'	=> true
+			),
+			'msn'				=> array(
+				'datatype'		=> 'VARCHAR(80)',
+				'allow_null'	=> true
+			),
+			'aim'				=> array(
+				'datatype'		=> 'VARCHAR(30)',
+				'allow_null'	=> true
+			),
+			'yahoo'				=> array(
+				'datatype'		=> 'VARCHAR(30)',
+				'allow_null'	=> true
+			),
+			'location'			=> array(
+				'datatype'		=> 'VARCHAR(30)',
+				'allow_null'	=> true
+			),
+			'signature'			=> array(
+				'datatype'		=> 'TEXT',
+				'allow_null'	=> true
+			),
+			'disp_topics'		=> array(
+				'datatype'		=> 'TINYINT(3) UNSIGNED',
+				'allow_null'	=> true
+			),
+			'disp_posts'		=> array(
+				'datatype'		=> 'TINYINT(3) UNSIGNED',
+				'allow_null'	=> true
+			),
+			'email_setting'		=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'notify_with_post'	=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'auto_notify'		=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'show_smilies'		=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'show_img'			=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'show_img_sig'		=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'show_avatars'		=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'show_sig'			=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '1'
+			),
+			'timezone'			=> array(
+				'datatype'		=> 'FLOAT',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'dst'				=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'time_format'		=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'date_format'		=> array(
+				'datatype'		=> 'TINYINT(1)',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'language'			=> array(
+				'datatype'		=> 'VARCHAR(25)',
+				'allow_null'	=> false,
+				'default'		=> '\'English\''
+			),
+			'style'				=> array(
+				'datatype'		=> 'VARCHAR(25)',
+				'allow_null'	=> false,
+				'default'		=> '\''.$db->escape($default_style).'\''
+			),
+			'num_posts'			=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'last_post'			=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> true
+			),
+			'last_search'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> true
+			),
+			'last_email_sent'	=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> true
+			),
+			'registered'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'registration_ip'	=> array(
+				'datatype'		=> 'VARCHAR(39)',
+				'allow_null'	=> false,
+				'default'		=> '\'0.0.0.0\''
+			),
+			'last_visit'		=> array(
+				'datatype'		=> 'INT(10) UNSIGNED',
+				'allow_null'	=> false,
+				'default'		=> '0'
+			),
+			'admin_note'		=> array(
+				'datatype'		=> 'VARCHAR(30)',
+				'allow_null'	=> true
+			),
+			'activate_string'	=> array(
+				'datatype'		=> 'VARCHAR(80)',
+				'allow_null'	=> true
+			),
+			'activate_key'		=> array(
+				'datatype'		=> 'VARCHAR(8)',
+				'allow_null'	=> true
+			),
+		),
+		'PRIMARY KEY'	=> array('id'),
+		'UNIQUE KEYS'	=> array(
+			'username_idx'		=> array('username')
+		),
+		'INDEXES'		=> array(
+			'registered_idx'	=> array('registered')
+		)
+	);
+
+	if ($db_type == 'mysql' || $db_type == 'mysqli' || $db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb')
+		$schema['UNIQUE KEYS']['username_idx'] = array('username(25)');
+
+	$db->create_table('users', $schema) or error('Unable to create users table', __FILE__, __LINE__, $db->error());
+
+
+	$now = time();
+
+	// Insert the four preset groups
+	$db->query('INSERT INTO '.$db->prefix.'groups ('.($db_type != 'pgsql' ? 'g_id, ' : '').'g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood) VALUES('.($db_type != 'pgsql' ? '1, ' : '')."'Administrators', 'Administrator', 0, 0 [...]
+
+	$db->query('INSERT INTO '.$db->prefix.'groups ('.($db_type != 'pgsql' ? 'g_id, ' : '').'g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood) VALUES('.($db_type != 'pgsql' ? '2, ' : '')."'Moderators', 'Moderator', 1, 1, 1, 1,  [...]
+
+	$db->query('INSERT INTO '.$db->prefix.'groups ('.($db_type != 'pgsql' ? 'g_id, ' : '').'g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood) VALUES('.($db_type != 'pgsql' ? '3, ' : '')."'Guest', NULL, 0, 0, 0, 0, 0, 1, 1, 0,  [...]
+
+	$db->query('INSERT INTO '.$db->prefix.'groups ('.($db_type != 'pgsql' ? 'g_id, ' : '').'g_title, g_user_title, g_moderator, g_mod_edit_users, g_mod_rename_users, g_mod_change_passwords, g_mod_ban_users, g_read_board, g_view_users, g_post_replies, g_post_topics, g_edit_posts, g_delete_posts, g_delete_topics, g_set_title, g_search, g_search_users, g_send_email, g_post_flood, g_search_flood, g_email_flood) VALUES('.($db_type != 'pgsql' ? '4, ' : '')."'Members', NULL, 0, 0, 0, 0, 0, 1, 1, 1 [...]
+
+	// Insert guest and first admin user
+	$db->query('INSERT INTO '.$db_prefix."users (group_id, username, password, email) VALUES(3, 'Guest', 'Guest', 'Guest')")
+		or error('Unable to add guest user. Please check your configuration and try again', __FILE__, __LINE__, $db->error());
+
+	$db->query('INSERT INTO '.$db_prefix."users (group_id, username, password, email, num_posts, last_post, registered, registration_ip, last_visit) VALUES(1, '".$db->escape($username)."', '".pun_hash($password1)."', '$email', 1, ".$now.", ".$now.", '127.0.0.1', ".$now.')')
+		or error('Unable to add administrator user. Please check your configuration and try again', __FILE__, __LINE__, $db->error());
+
+	// Enable/disable avatars depending on file_uploads setting in PHP configuration
+	$avatars = in_array(strtolower(@ini_get('file_uploads')), array('on', 'true', '1')) ? 1 : 0;
+
+	// Insert config data
+	$config = array(
+		'o_cur_version'				=> "'".FORUM_VERSION."'",
+		'o_database_revision'		=> "'".FORUM_DB_REVISION."'",
+		'o_searchindex_revision'	=> "'".FORUM_SI_REVISION."'",
+		'o_parser_revision'			=> "'".FORUM_PARSER_REVISION."'",
+		'o_board_title'				=> "'".$db->escape($title)."'",
+		'o_board_desc'				=> "'".$db->escape($description)."'",
+		'o_default_timezone'		=> "'0'",
+		'o_time_format'				=> "'H:i:s'",
+		'o_date_format'				=> "'Y-m-d'",
+		'o_timeout_visit'			=> "'1800'",
+		'o_timeout_online'			=> "'300'",
+		'o_redirect_delay'			=> "'1'",
+		'o_show_version'			=> "'0'",
+		'o_show_user_info'			=> "'1'",
+		'o_show_post_count'			=> "'1'",
+		'o_signatures'				=> "'1'",
+		'o_smilies'					=> "'1'",
+		'o_smilies_sig'				=> "'1'",
+		'o_make_links'				=> "'1'",
+		'o_default_lang'			=> "'".$db->escape($default_lang)."'",
+		'o_default_style'			=> "'".$db->escape($default_style)."'",
+		'o_default_user_group'		=> "'4'",
+		'o_topic_review'			=> "'15'",
+		'o_disp_topics_default'		=> "'30'",
+		'o_disp_posts_default'		=> "'25'",
+		'o_indent_num_spaces'		=> "'4'",
+		'o_quote_depth'				=> "'3'",
+		'o_quickpost'				=> "'1'",
+		'o_users_online'			=> "'1'",
+		'o_censoring'				=> "'0'",
+		'o_ranks'					=> "'1'",
+		'o_show_dot'				=> "'0'",
+		'o_topic_views'				=> "'1'",
+		'o_quickjump'				=> "'1'",
+		'o_gzip'					=> "'0'",
+		'o_additional_navlinks'		=> "''",
+		'o_report_method'			=> "'0'",
+		'o_regs_report'				=> "'0'",
+		'o_default_email_setting'	=> "'1'",
+		'o_mailing_list'			=> "'".$email."'",
+		'o_avatars'					=> "'".$avatars."'",
+		'o_avatars_dir'				=> "'img/avatars'",
+		'o_avatars_width'			=> "'60'",
+		'o_avatars_height'			=> "'60'",
+		'o_avatars_size'			=> "'10240'",
+		'o_search_all_forums'		=> "'1'",
+		'o_base_url'				=> "'".$db->escape($base_url)."'",
+		'o_admin_email'				=> "'".$email."'",
+		'o_webmaster_email'			=> "'".$email."'",
+		'o_subscriptions'			=> "'1'",
+		'o_smtp_host'				=> "NULL",
+		'o_smtp_user'				=> "NULL",
+		'o_smtp_pass'				=> "NULL",
+		'o_smtp_ssl'				=> "'0'",
+		'o_regs_allow'				=> "'1'",
+		'o_regs_verify'				=> "'0'",
+		'o_announcement'			=> "'0'",
+		'o_announcement_message'	=> "'Enter your announcement here.'",
+		'o_rules'					=> "'0'",
+		'o_rules_message'			=> "'Enter your rules here.'",
+		'o_maintenance'				=> "'0'",
+		'o_maintenance_message'		=> "'The forums are temporarily down for maintenance. Please try again in a few minutes.<br />\\n<br />\\n/Administrator'",
+		'o_default_dst'				=> "'0'",
+		'o_feed_type'				=> "'2'",
+		'p_message_bbcode'			=> "'1'",
+		'p_message_img_tag'			=> "'1'",
+		'p_message_all_caps'		=> "'1'",
+		'p_subject_all_caps'		=> "'1'",
+		'p_sig_all_caps'			=> "'1'",
+		'p_sig_bbcode'				=> "'1'",
+		'p_sig_img_tag'				=> "'0'",
+		'p_sig_length'				=> "'400'",
+		'p_sig_lines'				=> "'4'",
+		'p_allow_banned_email'		=> "'1'",
+		'p_allow_dupe_email'		=> "'0'",
+		'p_force_guest_email'		=> "'1'"
+	);
+
+	foreach ($config as $conf_name => $conf_value)
+	{
+		$db->query('INSERT INTO '.$db_prefix."config (conf_name, conf_value) VALUES('$conf_name', $conf_value)")
+			or error('Unable to insert into table '.$db_prefix.'config. Please check your configuration and try again', __FILE__, __LINE__, $db->error());
+	}
+
+	// Insert some other default data
+	$subject = 'Test post';
+	$message = 'If you are looking at this (which I guess you are), the install of FluxBB appears to have worked! Now log in and head over to the administration control panel to configure your forum.';
+
+	$db->query('INSERT INTO '.$db_prefix."ranks (rank, min_posts) VALUES('New member', 0)")
+		or error('Unable to insert into table '.$db_prefix.'ranks. Please check your configuration and try again', __FILE__, __LINE__, $db->error());
+
+	$db->query('INSERT INTO '.$db_prefix."ranks (rank, min_posts) VALUES('Member', 10)")
+		or error('Unable to insert into table '.$db_prefix.'ranks. Please check your configuration and try again', __FILE__, __LINE__, $db->error());
+
+	$db->query('INSERT INTO '.$db_prefix."categories (cat_name, disp_position) VALUES('Test category', 1)")
+		or error('Unable to insert into table '.$db_prefix.'categories. Please check your configuration and try again', __FILE__, __LINE__, $db->error());
+
+	$db->query('INSERT INTO '.$db_prefix."forums (forum_name, forum_desc, num_topics, num_posts, last_post, last_post_id, last_poster, disp_position, cat_id) VALUES('Test forum', 'This is just a test forum', 1, 1, ".$now.", 1, '".$db->escape($username)."', 1, 1)")
+		or error('Unable to insert into table '.$db_prefix.'forums. Please check your configuration and try again', __FILE__, __LINE__, $db->error());
+
+	$db->query('INSERT INTO '.$db_prefix."topics (poster, subject, posted, first_post_id, last_post, last_post_id, last_poster, forum_id) VALUES('".$db->escape($username)."', '".$db->escape($subject)."', ".$now.", 1, ".$now.", 1, '".$db->escape($username)."', 1)")
+		or error('Unable to insert into table '.$db_prefix.'topics. Please check your configuration and try again', __FILE__, __LINE__, $db->error());
+
+	$db->query('INSERT INTO '.$db_prefix."posts (poster, poster_id, poster_ip, message, posted, topic_id) VALUES('".$db->escape($username)."', 2, '127.0.0.1', '".$db->escape($message)."', ".$now.', 1)')
+		or error('Unable to insert into table '.$db_prefix.'posts. Please check your configuration and try again', __FILE__, __LINE__, $db->error());
+
+	// Index the test post so searching for it works
+	require PUN_ROOT.'include/search_idx.php';
+	$pun_config['o_default_lang'] = $default_lang;
+	update_search_index('post', 1, $message, $subject);
+
+	$db->end_transaction();
+
+
+	$alerts = array();
+	// Check if the cache directory is writable
+	if (!@is_writable('./cache/'))
+		$alerts[] = '<strong>The cache directory is currently not writable!</strong> In order for FluxBB to function properly, the directory named <em>cache</em> must be writable by PHP. Use chmod to set the appropriate directory permissions. If in doubt, chmod to 0777.';
+
+	// Check if default avatar directory is writable
+	if (!@is_writable('./img/avatars/'))
+		$alerts[] = '<strong>The avatar directory is currently not writable!</strong> If you want users to be able to upload their own avatar images you must see to it that the directory named <em>img/avatars</em> is writable by PHP. You can later choose to save avatar images in a different directory (see Admin/Options). Use chmod to set the appropriate directory permissions. If in doubt, chmod to 0777.';
+
+	// Check if we disabled uploading avatars because file_uploads was disabled
+	if ($avatars == '0')
+		$alerts[] = '<strong>File uploads appear to be disallowed on this server!</strong> If you want users to be able to upload their own avatar images you must enable the file_uploads configuration setting in PHP. Once file uploads have been enabled, avatar uploads can be enabled in Administration/Options/Features.';
+
+	// Add some random bytes at the end of the cookie name to prevent collisions
+	$cookie_name = 'pun_cookie_'.random_key(6, false, true);
+
+	// Generate the config.php file data
+	$config = generate_config_file();
+
+	// Attempt to write config.php and serve it up for download if writing fails
+	$written = false;
+	if (is_writable(PUN_ROOT))
+	{
+		$fh = @fopen(PUN_ROOT.'config.php', 'wb');
+		if ($fh)
+		{
+			fwrite($fh, $config);
+			fclose($fh);
+
+			$written = true;
+		}
+	}
+
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>FluxBB Installation</title>
+<link rel="stylesheet" type="text/css" href="style/<?php echo $default_style ?>.css" />
+</head>
+<body>
+
+<div id="puninstall" class="pun">
+<div class="top-box"><div><!-- Top Corners --></div></div>
+<div class="punwrap">
+
+<div id="brdheader" class="block">
+	<div class="box">
+		<div id="brdtitle" class="inbox">
+			<h1><span>FluxBB Installation</span></h1>
+			<div id="brddesc"><p>FluxBB has been installed. To finalize the installation please follow the instructions below.</p></div>
+		</div>
+	</div>
+</div>
+
+<div id="brdmain">
+
+<div class="blockform">
+	<h2><span>Final instructions</span></h2>
+	<div class="box">
+<?php
+
+if (!$written)
+{
+
+?>
+		<form method="post" action="install.php">
+			<div class="inform">
+				<div class="forminfo">
+					<p>To finalize the installation, you need to click on the button below to download a file called config.php. You then need to upload this file to the root directory of your FluxBB installation.</p>
+					<p>Once you have uploaded config.php, FluxBB will be fully installed! At that point, you may <a href="index.php">go to the forum index</a>.</p>
+				</div>
+				<input type="hidden" name="generate_config" value="1" />
+				<input type="hidden" name="db_type" value="<?php echo $db_type; ?>" />
+				<input type="hidden" name="db_host" value="<?php echo $db_host; ?>" />
+				<input type="hidden" name="db_name" value="<?php echo pun_htmlspecialchars($db_name); ?>" />
+				<input type="hidden" name="db_username" value="<?php echo pun_htmlspecialchars($db_username); ?>" />
+				<input type="hidden" name="db_password1" value="<?php echo pun_htmlspecialchars($db_password1); ?>" />
+				<input type="hidden" name="db_prefix" value="<?php echo pun_htmlspecialchars($db_prefix); ?>" />
+				<input type="hidden" name="cookie_name" value="<?php echo pun_htmlspecialchars($cookie_name); ?>" />
+				<input type="hidden" name="cookie_seed" value="<?php echo pun_htmlspecialchars($cookie_seed); ?>" />
+
+<?php if (!empty($alerts)): ?>				<div class="forminfo error-info">
+					<ul class="error-list">
+<?php
+
+foreach ($alerts as $cur_alert)
+	echo "\t\t\t\t\t".'<li>'.$cur_alert.'</li>'."\n";
+?>
+					</ul>
+				</div>
+<?php endif; ?>			</div>
+			<p class="buttons"><input type="submit" value="Download config.php file" /></p>
+		</form>
+
+<?php
+
+}
+else
+{
+
+?>
+		<div class="fakeform">
+			<div class="inform">
+				<div class="forminfo">
+					<p>FluxBB has been fully installed! You may now <a href="index.php">go to the forum index</a>.</p>
+				</div>
+			</div>
+		</div>
+<?php
+
+}
+
+?>
+	</div>
+</div>
+
+</div>
+
+</div>
+<div class="end-box"><div><!-- Bottom Corners --></div></div>
+</div>
+
+</body>
+</html>
+<?php
+
+}
diff --git a/lang/English/admin_bans.php b/lang/English/admin_bans.php
new file mode 100644
index 0000000..72f16db
--- /dev/null
+++ b/lang/English/admin_bans.php
@@ -0,0 +1,66 @@
+<?php
+
+// Language definitions used in admin_bans.php
+$lang_admin_bans = array(
+
+'No user message'			=>	'No user by that username registered. If you want to add a ban not tied to a specific username just leave the username blank.',
+'No user ID message'		=>	'No user by that ID registered.',
+'User is admin message'		=>	'The user %s is an administrator and can\'t be banned. If you want to ban an administrator, you must first demote him/her to moderator or user.',
+'Must enter message'		=>	'You must enter either a username, an IP address or an email address (at least).',
+'Cannot ban guest message'	=>	'The guest user cannot be banned.',
+'Invalid IP message'		=>	'You entered an invalid IP/IP-range.',
+'Invalid e-mail message'	=>	'The email address (e.g. user at domain.com) or partial email address domain (e.g. domain.com) you entered is invalid.',
+'Invalid date message'		=>	'You entered an invalid expire date.',
+'Invalid date reasons'		=>	'The format should be YYYY-MM-DD and the date must be at least one day in the future.',
+'Ban added redirect'		=>	'Ban added. Redirecting …' ,
+'Ban edited redirect'		=>	'Ban edited. Redirecting …',
+'Ban removed redirect'		=>	'Ban removed. Redirecting …',
+
+'New ban head'				=>	'New ban',
+'Add ban subhead'			=>	'Add ban',
+'Username label'			=>	'Username',
+'Username help'				=>	'The username to ban (case-insensitive).',
+'Username advanced help'	=>	'The username to ban (case-insensitive). The next page will let you enter a custom IP and email. If you just want to ban a specific IP/IP-range or email just leave it blank.',
+
+'Ban search head'			=>	'Ban search',
+'Ban search subhead'		=>	'Enter search criteria',
+'Ban search info'			=>	'Search for bans in the database. You can enter one or more terms to search for. Wildcards in the form of asterisks (*) are accepted.',
+'Date help'					=>	'(yyyy-mm-dd)',
+'Message label'				=>	'Message',
+'Expire after label'		=>	'Expire after',
+'Expire before label'		=>	'Expire before',
+'Order by label'			=>	'Order by',
+'Order by username'			=>	'Username',
+'Order by ip'				=>	'IP',
+'Order by e-mail'			=>	'Email',
+'Order by expire'			=>	'Expire date',
+'Ascending'					=>	'Ascending',
+'Descending'				=>	'Descending',
+'Submit search'				=>	'Submit search',
+
+'E-mail label'				=>	'Email',
+'E-mail help'				=>	'The email or email domain you wish to ban (e.g. someone at somewhere.com or somewhere.com). See "Allow banned email addresses" in Permissions for more info.',
+'IP label'					=>	'IP address/IP-ranges',
+'IP help'					=>	'The IP address or IP-ranges you wish to ban (e.g. 150.11.110.1 or 150.11.110). Separate addresses with spaces. If an IP is entered already it is the last known IP of this user in the database.',
+'IP help link'				=>	'Click %s to see IP statistics for this user.',
+'Ban advanced head'			=>	'Ban advanced settings',
+'Ban advanced subhead'		=>	'Supplement ban with IP and email',
+'Ban message label'			=>	'Ban message',
+'Ban message help'			=>	'A message that will be displayed to the banned user when he/she visits the board.',
+'Message expiry subhead'	=>	'Ban message and expiry',
+'Ban IP range info'			=>	'You should be very careful when banning an IP-range because of the possibility of multiple users matching the same partial IP.',
+'Expire date label'			=>	'Expire date',
+'Expire date help'			=>	'The date when this ban should be automatically removed (format: yyyy-mm-dd). Leave blank to remove manually.',
+
+'Results head'				=>	'Search Results',
+'Results username head'		=>	'Username',
+'Results e-mail head'		=>	'Email',
+'Results IP address head'	=>	'IP/IP-ranges',
+'Results expire head'		=>	'Expires',
+'Results message head'		=>	'Message',
+'Results banned by head'	=>	'Banned by',
+'Results actions head'		=>	'Actions',
+'No match'					=>	'No match',
+'Unknown'					=>	'Unknown',
+
+);
diff --git a/lang/English/admin_categories.php b/lang/English/admin_categories.php
new file mode 100644
index 0000000..311a5af
--- /dev/null
+++ b/lang/English/admin_categories.php
@@ -0,0 +1,29 @@
+<?php
+
+// Language definitions used in admin-categories.php
+$lang_admin_categories = array(
+
+'Must enter name message'		=>	'You must enter a name for the category',
+'Category added redirect'		=>	'Category added. Redirecting …',
+'Category deleted redirect'		=>	'Category deleted. Redirecting …',
+'Delete category head'			=>	'Delete category (together with all forums and posts it contains)',
+'Confirm delete subhead'		=>	'Confirm delete category',
+'Confirm delete info'			=>	'Are you sure that you want to delete the category <strong>%s</strong>?',
+'Delete category warn'			=>	'<strong>WARNING!</strong> Deleting a category will delete all forums and posts (if any) in this category!',
+'Must enter integer message'	=>	'Position must be a positive integer value.',
+'Categories updated redirect'	=>	'Categories updated. Redirecting …',
+'Add categories head'			=>	'Add categories',
+'Add categories subhead'		=>	'Add categories',
+'Add category label'			=>	'Add a new category',
+'Add new submit'				=>	'Add new',
+'Add category help'				=>	'The name of the new category you want to add. You can edit the name of the category later (see below). Go to %s to add forums to your new category.',
+'Delete categories head'		=>	'Delete categories',
+'Delete categories subhead'		=>	'Delete categories',
+'Delete category label'			=>	'Delete a category',
+'Delete category help'			=>	'Select the name of the category you want to delete. You will be asked to confirm your choice of category for deletion before it is deleted.',
+'Edit categories head'			=>	'Edit categories',
+'Edit categories subhead'		=>	'Edit categories',
+'Category position label'		=>	'Position',
+'Category name label'			=>	'Name',
+
+);
diff --git a/lang/English/admin_censoring.php b/lang/English/admin_censoring.php
new file mode 100644
index 0000000..9d49495
--- /dev/null
+++ b/lang/English/admin_censoring.php
@@ -0,0 +1,22 @@
+<?php
+
+// Language definitions used in admin_censoring.php
+$lang_admin_censoring = array(
+
+'Must enter both message'	=>	'You must enter both a word to censor and text to replace it with.',
+'Must search both message'	=>	'You must enter both text to search for and text to replace with.',
+'Word updated redirect'		=>	'Censor word updated. Redirecting …',
+'Word added redirect'		=>	'Censor word added. Redirecting …',
+'Word removed redirect'		=>	'Censor word removed. Redirecting …',
+'Censoring head'			=>	'Censoring',
+'Add word subhead'			=>	'Add word',
+'Add word info'				=>	'Enter a word that you want to censor and the replacement text for this word. Wildcards are accepted (i.e. *some* would match somewhere and lonesome). Censor words also affect usernames. New users will not be able to register with usernames containing any censored words. The search is case insensitive.',
+'Censoring enabled'		=>	'<strong>Censoring is enabled in %s.</strong>',
+'Censoring disabled'		=>	'<strong>Censoring is disabled in %s.</strong>',
+'Censored word label'		=>	'Censored word',
+'Replacement label'			=>	'Replacement word(s)',
+'Action label'				=>	'Action',
+'Edit remove subhead'		=>	'Edit or remove words',
+'No words in list'			=>	'No censor words in list.',
+
+);
diff --git a/lang/English/admin_common.php b/lang/English/admin_common.php
new file mode 100644
index 0000000..a87616c
--- /dev/null
+++ b/lang/English/admin_common.php
@@ -0,0 +1,44 @@
+<?php
+
+// Language definitions used in admin_common
+$lang_admin_common = array(
+
+// The menu
+'Admin menu'			=>	'Admin menu',
+'Plugins menu'			=>	'Plugins menu',
+'Moderator menu'		=>	'Moderator menu',
+'Index'					=>	'Index',
+'Categories'			=>	'Categories',
+'Forums'				=>	'Forums',
+'Users'					=>	'Users',
+'User groups'			=>	'User groups',
+'Options'				=>	'Options',
+'Permissions'			=>	'Permissions',
+'Censoring'				=>	'Censoring',
+'Ranks'					=>	'Ranks',
+'Bans'					=>	'Bans',
+'Prune'					=>	'Prune',
+'Maintenance'			=>	'Maintenance',
+'Reports'				=>	'Reports',
+
+'Admin'					=>	'Admin',
+'Go back'				=>	'Go back',
+'Delete'				=>	'Delete',
+'Update'				=>	'Update',
+'Add'					=>	'Add',
+'Edit'					=>	'Edit',
+'Remove'				=>	'Remove',
+'Yes'					=>	'Yes',
+'No'					=>	'No',
+'Save changes'			=>	'Save changes',
+'Save'					=>	'Save',
+'here'					=>	'here',
+'Action'				=>	'Action',
+'None'					=>	'None',
+'Maintenance mode'		=>	'maintenance mode', // Used for link text in more than one file
+
+// Admin loader
+'No plugin message'		=>	'There is no plugin called %s in the plugin directory.',
+'Plugin failed message'	=>	'Loading of the plugin - <strong>%s</strong> - failed.',
+
+);
diff --git a/lang/English/admin_forums.php b/lang/English/admin_forums.php
new file mode 100644
index 0000000..4a0e9df
--- /dev/null
+++ b/lang/English/admin_forums.php
@@ -0,0 +1,52 @@
+<?php
+
+// Language definitions used in admin-forums.php
+$lang_admin_forums = array(
+
+'Forum added redirect'		=>	'Forum added. Redirecting …',
+'Forum deleted redirect'	=>	'Forum deleted. Redirecting …',
+'Forums updated redirect'	=>	'Forums updated. Redirecting …',
+'Forum updated redirect'	=>	'Forum updated. Redirecting …',
+'Perms reverted redirect'	=>	'Permissions reverted to defaults. Redirecting …',
+'Must enter name message'	=>	'You must enter a forum name.',
+'Must be integer message'	=>	'Position must be a positive integer value.',
+'New forum'					=>	'New forum',
+
+// Entry page
+'Add forum head'			=>	'Add forum',
+'Create new subhead'		=>	'Create a new forum',
+'Add forum label'			=>	'Add forum to category',
+'Add forum help'			=>	'Select the category to which you wish to add a new forum.',
+'Add forum'					=>	'Add forum',
+'No categories exist'		=>	'No categories exist',
+'Edit forums head'			=>	'Edit forums',
+'Category subhead'			=>	'Category:',
+'Forum label'				=>	'Forum',
+'Edit link'					=>	'Edit',
+'Delete link'				=>	'Delete',
+'Position label'			=>	'Position',
+'Update positions'			=>	'Update positions',
+'Confirm delete head'		=>	'Confirm delete forum',
+'Confirm delete subhead'	=>	'Important! Read before deleting',
+'Confirm delete info'		=>	'Are you sure that you want to delete the forum <strong>%s</strong>?',
+'Confirm delete warn'		=>	'WARNING! Deleting a forum will delete all posts (if any) in that forum!',
+
+// Detailed edit page
+'Edit forum head'			=>	'Edit forum',
+'Edit details subhead'		=>	'Edit forum details',
+'Forum name label'			=>	'Forum name',
+'Forum description label'	=>	'Description (HTML)',
+'Category label'			=>	'Category',
+'Sort by label'				=>	'Sort topics by',
+'Last post'					=>	'Last post',
+'Topic start'				=>	'Topic start',
+'Redirect label'			=>	'Redirect URL',
+'Redirect help'				=>	'Only available in empty forums',
+'Group permissions subhead'	=>	'Edit group permissions for this forum',
+'Group permissions info'	=>	'In this form, you can set the forum specific permissions for the different user groups. If you haven\'t made any changes to this forum\'s group permissions, what you see below is the default based on settings in %s. Administrators always have full permissions and are thus excluded. Permission settings that differ from the default permissions for the user group are marked red. The "Read forum" permission checkbox will be disabled if the group in question lacks [...]
+'Read forum label'			=>	'Read forum',
+'Post replies label'		=>	'Post replies',
+'Post topics label'			=>	'Post topics',
+'Revert to default'			=>	'Revert to default',
+
+);
diff --git a/lang/English/admin_groups.php b/lang/English/admin_groups.php
new file mode 100644
index 0000000..dd6ad56
--- /dev/null
+++ b/lang/English/admin_groups.php
@@ -0,0 +1,82 @@
+<?php
+
+// Language definitions used in admin-groups.php
+$lang_admin_groups = array(
+
+'Must enter title message'		=>	'You must enter a group title.',
+'Title already exists message'	=>	'There is already a group with the title <strong>%s</strong>.',
+'Default group redirect'		=>	'Default group set. Redirecting …',
+'Cannot remove default message'	=>	'The default group cannot be removed. In order to delete this group, you must first setup a different group as the default.',
+'Group removed redirect'		=>	'Group removed. Redirecting …',
+'Group added redirect'			=>	'Group added. Redirecting …',
+'Group edited redirect'			=>	'Group edited. Redirecting …',
+
+'Add groups head'				=>	'Add/setup groups',
+'Add group subhead'				=>	'Add new group',
+'New group label'				=>	'Base new group on',
+'New group help'				=>	'Select a user group from which the new group will inherit its permission settings. The next page will let you fine-tune its settings.',
+'Default group subhead'			=>	'Set default group',
+'Default group label'			=>	'Default group',
+'Default group help'			=>	'This is the default user group, e.g. the group users are placed in when they register. For security reasons, users can\'t be placed in either the moderator or administrator user groups by default.',
+'Existing groups head'			=>	'Existing groups',
+'Edit groups subhead'			=>	'Edit/delete groups',
+'Edit groups info'				=>	'The pre-defined groups Guests, Administrators, Moderators and Members cannot be removed. However, they can be edited. Please note that in some groups, some options are unavailable (e.g. the <em>edit posts</em> permission for guests). Administrators always have full permissions.',
+'Edit link'						=>	'Edit',
+'Delete link'					=>	'Delete',
+'Group delete head'				=>	'Group delete',
+'Confirm delete subhead'		=>	'Confirm delete group',
+'Confirm delete info'			=>	'Are you sure that you want to delete the group <strong>%s</strong>?',
+'Confirm delete warn'			=>	'WARNING! After you deleted a group you can not restore it.',
+'Delete group head'				=>	'Delete group',
+'Move users subhead'			=>	'Move users currently in group',
+'Move users info'				=>	'The group <strong>%s</strong> currently has <strong>%s</strong> members. Please select a group to which these members will be assigned upon deletion.',
+'Move users label'				=>	'Move users to',
+'Delete group'					=>	'Delete group',
+
+'Group settings head'			=>	'Group settings',
+'Group settings subhead'		=>	'Setup group options and permissions',
+'Group settings info'			=>	'Below options and permissions are the default permissions for the user group. These options apply if no forum specific permissions are in effect.',
+'Group title label'				=>	'Group title',
+'User title label'				=>	'User title',
+'User title help'				=>	'This title will override any rank users in this group have attained. Leave blank to use default title or rank.',
+'Mod privileges label'			=>	'Allow users moderator privileges',
+'Mod privileges help'			=>	'In order for a user in this group to have moderator abilities, he/she must be assigned to moderate one or more forums. This is done via the user administration page of the user\'s profile.',
+'Edit profile label'			=>	'Allow moderators to edit user profiles',
+'Edit profile help'				=>	'If moderator privileges are enabled, allow users in this group to edit user profiles.',
+'Rename users label'			=>	'Allow moderators to rename users',
+'Rename users help'				=>	'If moderator privileges are enabled, allow users in this group to rename users.',
+'Change passwords label'		=>	'Allow moderators to change passwords',
+'Change passwords help'			=>	'If moderator privileges are enabled, allow users in this group to change user passwords.',
+'Ban users label'				=>	'Allow moderators to ban users',
+'Ban users help'				=>	'If moderator privileges are enabled, allow users in this group to ban users.',
+'Read board label'				=>	'Read board',
+'Read board help'				=>	'Allow users in this group to view the board. This setting applies to every aspect of the board and can therefore not be overridden by forum specific settings. If this is set to "No", users in this group will only be able to login/logout and register.',
+'View user info label'			=>	'View user information',
+'View user info help'			=>	'Allow users to view the user list and user profiles.',
+'Post replies label'			=>	'Post replies',
+'Post replies help'				=>	'Allow users in this group to post replies in topics.',
+'Post topics label'				=>	'Post topics',
+'Post topics help'				=>	'Allow users in this group to post new topics.',
+'Edit posts label'				=>	'Edit posts',
+'Edit posts help'				=>	'Allow users in this group to edit their own posts.',
+'Delete posts label'			=>	'Delete posts',
+'Delete posts help'				=>	'Allow users in this group to delete their own posts.',
+'Delete topics label'			=>	'Delete topics',
+'Delete topics help'			=>	'Allow users in this group to delete their own topics (including any replies).',
+'Set own title label'			=>	'Set own user title',
+'Set own title help'			=>	'Allow users in this group to set their own user title.',
+'User search label'				=>	'Use search',
+'User search help'				=>	'Allow users in this group to use the search feature.',
+'User list search label'		=>	'Search user list',
+'User list search help'			=>	'Allow users in this group to freetext search for users in the user list.',
+'Send e-mails label'			=>	'Send e-mails',
+'Send e-mails help'				=>	'Allow users in this group to send e-mails to other users.',
+'Post flood label'				=>	'Post flood interval',
+'Post flood help'				=>	'Number of seconds that users in this group have to wait between posts. Set to 0 to disable.',
+'Search flood label'			=>	'Search flood interval',
+'Search flood help'				=>	'Number of seconds that users in this group have to wait between searches. Set to 0 to disable.',
+'E-mail flood label'			=>	'Email flood interval',
+'E-mail flood help'				=>	'Number of seconds that users in this group have to wait between emails. Set to 0 to disable.',
+'Moderator info'				=>	'Please note that in order for a user in this group to have moderator abilities, he/she must be assigned to moderate one or more forums. This is done via the user administration page of the user\'s profile.',
+
+);
diff --git a/lang/English/admin_index.php b/lang/English/admin_index.php
new file mode 100644
index 0000000..9f9cdc7
--- /dev/null
+++ b/lang/English/admin_index.php
@@ -0,0 +1,51 @@
+<?php
+
+// Language definitions used in admin_index.php
+$lang_admin_index = array(
+
+'fopen disabled message'			=>	'Unable to check for upgrade since \'allow_url_fopen\' is disabled on this system.',
+'Upgrade check failed message'		=>	'Check for upgrade failed for unknown reasons.',
+'Running latest version message'	=>	'You are running the latest version of FluxBB.',
+'New version available message'		=>	'A new version of FluxBB has been released. You can download the latest version at %s.',
+'PHPinfo disabled message'			=>	'The PHP function phpinfo() has been disabled on this server.',
+'Not available'						=>	'Not available',
+'Forum admin head'					=>	'Forum administration',
+'NA'								=>	'N/A',
+'Welcome to admin'					=>	'Welcome to the FluxBB administration control panel. From here you can control vital aspects of the board. Depending on whether you are an administrator or a moderator you can:',
+'Welcome 1'							=>	'Organize categories and forums.',
+'Welcome 2'							=>	'Set forum-wide options and preferences.',
+'Welcome 3'							=>	'Control permissions for users and guests.',
+'Welcome 4'							=>	'View IP statistics for users.',
+'Welcome 5'							=>	'Ban users.',
+'Welcome 6'							=>	'Censor words.',
+'Welcome 7'							=>	'Set up user ranks.',
+'Welcome 8'							=>	'Prune old posts.',
+'Welcome 9'							=>	'Handle post reports.',
+'Statistics head'					=>	'Statistics',
+'FluxBB version label'				=>	'FluxBB version',
+'Check for upgrade'					=>	'Check for upgrade',
+'FluxBB version data'				=>	'v%s - %s',
+'Server load label'					=>	'Server load',
+'Server load data'					=>	'%s - %s user(s) online',
+'Environment label'					=>	'Environment',
+'Environment data OS'				=>	'Operating system: %s',
+'Show info'							=>	'Show info',
+'Environment data version'			=>	'PHP: %s - %s',
+'Environment data acc'				=>	'Accelerator: %s',
+'Turck MMCache'						=>	'Turk MMCache',
+'Turck MMCache link'				=>	'turck-mmcache.sourceforge.net',
+'ionCube PHP Accelerator'			=>	'ionCube PHP Accelerator',
+'ionCube PHP Accelerator link'		=>	'www.php-accelerator.co.uk/',
+'Alternative PHP Cache (APC)'		=>	'Alternative PHP Cache (APC)',
+'Alternative PHP Cache (APC) link'	=>	'www.php.net/apc/',
+'Zend Optimizer'					=>	'Zend Optimizer',
+'Zend Optimizer link'				=>	'www.zend.com/products/guard/zend-optimizer',
+'eAccelerator'						=>	'eAccelerator',
+'eAccelerator link'					=>	'www.eaccelerator.net/',
+'XCache'							=>	'XCache',
+'XCache link'						=>	'xcache.lighttpd.net/',
+'Database label'					=>	'Database',
+'Database data rows'				=>	'Rows: %s',
+'Database data size'				=>	'Size: %s',
+
+);
diff --git a/lang/English/admin_maintenance.php b/lang/English/admin_maintenance.php
new file mode 100644
index 0000000..a0e62b3
--- /dev/null
+++ b/lang/English/admin_maintenance.php
@@ -0,0 +1,23 @@
+<?php
+
+// Language definitions used in admin_maintenance.php
+$lang_admin_maintenance = array(
+
+'Maintenance head'				=>	'Forum maintenance',
+'Rebuild index subhead'			=>	'Rebuild search index',
+'Rebuild index info'			=>	'If you\'ve added, edited or removed posts manually in the database or if you\'re having problems searching, you should rebuild the search index. For best performance, you should put the forum in %s during rebuilding. <strong>Rebuilding the search index can take a long time and will increase server load during the rebuild process!</strong>',
+'Posts per cycle label'			=>	'Posts per cycle',
+'Posts per cycle help'			=>	'The number of posts to process per pageview. E.g. if you were to enter 300, three hundred posts would be processed and then the page would refresh. This is to prevent the script from timing out during the rebuild process.',
+'Starting post label'			=>	'Starting post ID',
+'Starting post help'			=>	'The post ID to start rebuilding at. The default value is the first available ID in the database. Normally you wouldn\'t want to change this.',
+'Empty index label'				=>	'Empty index',
+'Empty index help'				=>	'Select this if you want the search index to be emptied before rebuilding (see below).',
+'Rebuild completed info'		=>	'Once the process has completed, you will be redirected back to this page. It is highly recommended that you have JavaScript enabled in your browser during rebuilding (for automatic redirect when a cycle has completed). If you are forced to abort the rebuild process, make a note of the last processed post ID and enter that ID+1 in "Starting post ID" when/if you want to continue ("Empty index" must not be selected).',
+'Rebuild index'					=>	'Rebuild index',
+'Rebuilding search index'		=>	'Rebuilding search index',
+'Rebuilding index info'			=>	'Rebuilding index. This might be a good time to put on some coffee :-)',
+'Processing post'				=>	'Processing post <strong>%s</strong> …',
+'Click here'					=>	'Click here',
+'Javascript redirect failed'	=>	'JavaScript redirect unsuccessful. %s to continue …',
+
+);
diff --git a/lang/English/admin_options.php b/lang/English/admin_options.php
new file mode 100644
index 0000000..e701917
--- /dev/null
+++ b/lang/English/admin_options.php
@@ -0,0 +1,217 @@
+<?php
+
+// Language definitions used in admin-options.php
+$lang_admin_options = array(
+
+'Bad HTTP Referer message'			=>	'Bad HTTP_REFERER. If you have moved these forums from one location to another or switched domains, you need to update the Base URL manually in the database (look for o_base_url in the config table) and then clear the cache by deleting all .php files in the /cache directory.',
+'Must enter title message'			=>	'You must enter a board title.',
+'Invalid e-mail message'			=>	'The admin email address you entered is invalid.',
+'Invalid webmaster e-mail message'	=>	'The webmaster email address you entered is invalid.',
+'Enter announcement here'			=>	'Enter your announcement here.',
+'Enter rules here'					=>	'Enter your rules here.',
+'Default maintenance message'		=>	'The forums are temporarily down for maintenance. Please try again in a few minutes.',
+'Timeout error message'				=>	'The value of "Timeout online" must be smaller than the value of "Timeout visit".',
+'Options updated redirect'			=>	'Options updated. Redirecting …',
+'Options head'						=>	'Options',
+
+// Essentials section
+'Essentials subhead'				=>	'Essentials',
+'Board title label'					=>	'Board title',
+'Board title help'					=>	'The title of this bulletin board (shown at the top of every page). This field may <strong>not</strong> contain HTML.',
+'Board desc label'					=>	'Board description',
+'Board desc help'					=>	'A short description of this bulletin board (shown at the top of every page). This field may contain HTML.',
+'Base URL label'					=>	'Base URL',
+'Base URL help'						=>	'The complete URL of the board without trailing slash (i.e. http://www.mydomain.com/forums). This <strong>must</strong> be correct in order for all admin and moderator features to work. If you get "Bad referer" errors, it\'s probably incorrect.',
+'Timezone label'					=>	'Default time zone',
+'Timezone help'						=>	'The default time zone for guests and users attempting to register for the board.',
+'DST label'							=>	'Adjust for DST',
+'DST help'							=>	'Check if daylight savings is in effect (advances times by 1 hour).',
+'Language label'					=>	'Default language',
+'Language help'						=>	'The default language for guests and users who haven\'t changed from the default in their profile. If you remove a language pack, this must be updated.',
+'Default style label'				=>	'Default style',
+'Default style help'				=>	'The default style for guests and users who haven\'t changed from the default in their profile.',
+
+// Essentials section timezone options
+'UTC-12:00'							=>	'(UTC-12:00) International Date Line West',
+'UTC-11:00'							=>	'(UTC-11:00) Niue, Samoa',
+'UTC-10:00'							=>	'(UTC-10:00) Hawaii-Aleutian, Cook Island',
+'UTC-09:30'							=>	'(UTC-09:30) Marquesas Islands',
+'UTC-09:00'							=>	'(UTC-09:00) Alaska, Gambier Island',
+'UTC-08:30'							=>	'(UTC-08:30) Pitcairn Islands',
+'UTC-08:00'							=>	'(UTC-08:00) Pacific',
+'UTC-07:00'							=>	'(UTC-07:00) Mountain',
+'UTC-06:00'							=>	'(UTC-06:00) Central',
+'UTC-05:00'							=>	'(UTC-05:00) Eastern',
+'UTC-04:00'							=>	'(UTC-04:00) Atlantic',
+'UTC-03:30'							=>	'(UTC-03:30) Newfoundland',
+'UTC-03:00'							=>	'(UTC-03:00) Amazon, Central Greenland',
+'UTC-02:00'							=>	'(UTC-02:00) Mid-Atlantic',
+'UTC-01:00'							=>	'(UTC-01:00) Azores, Cape Verde, Eastern Greenland',
+'UTC'								=>	'(UTC) Western European, Greenwich',
+'UTC+01:00'							=>	'(UTC+01:00) Central European, West African',
+'UTC+02:00'							=>	'(UTC+02:00) Eastern European, Central African',
+'UTC+03:00'							=>	'(UTC+03:00) Moscow, Eastern African',
+'UTC+03:30'							=>	'(UTC+03:30) Iran',
+'UTC+04:00'							=>	'(UTC+04:00) Gulf, Samara',
+'UTC+04:30'							=>	'(UTC+04:30) Afghanistan',
+'UTC+05:00'							=>	'(UTC+05:00) Pakistan, Yekaterinburg',
+'UTC+05:30'							=>	'(UTC+05:30) India, Sri Lanka',
+'UTC+05:45'							=>	'(UTC+05:45) Nepal',
+'UTC+06:00'							=>	'(UTC+06:00) Bangladesh, Bhutan, Novosibirsk',
+'UTC+06:30'							=>	'(UTC+06:30) Cocos Islands, Myanmar',
+'UTC+07:00'							=>	'(UTC+07:00) Indochina, Krasnoyarsk',
+'UTC+08:00'							=>	'(UTC+08:00) Greater China, Australian Western, Irkutsk',
+'UTC+08:45'							=>	'(UTC+08:45) Southeastern Western Australia',
+'UTC+09:00'							=>	'(UTC+09:00) Japan, Korea, Chita',
+'UTC+09:30'							=>	'(UTC+09:30) Australian Central',
+'UTC+10:00'							=>	'(UTC+10:00) Australian Eastern, Vladivostok',
+'UTC+10:30'							=>	'(UTC+10:30) Lord Howe',
+'UTC+11:00'							=>	'(UTC+11:00) Solomon Island, Magadan',
+'UTC+11:30'							=>	'(UTC+11:30) Norfolk Island',
+'UTC+12:00'							=>	'(UTC+12:00) New Zealand, Fiji, Kamchatka',
+'UTC+12:45'							=>	'(UTC+12:45) Chatham Islands',
+'UTC+13:00'							=>	'(UTC+13:00) Tonga, Phoenix Islands',
+'UTC+14:00'							=>	'(UTC+14:00) Line Islands',
+
+// Timeout Section
+'Timeouts subhead'					=>	'Time and timeouts',
+'Time format label'					=>	'Time format',
+'PHP manual'						=>	'PHP manual',
+'Time format help'					=>	'[Current format: %s]. See %s for formatting options.',
+'Date format label'					=>	'Date format',
+'Date format help'					=>	'[Current format: %s]. See %s for formatting options.',
+'Visit timeout label'				=>	'Visit timeout',
+'Visit timeout help'				=>	'Number of seconds a user must be idle before his/hers last visit data is updated (primarily affects new message indicators).',
+'Online timeout label'				=>	'Online timeout',
+'Online timeout help'				=>	'Number of seconds a user must be idle before being removed from the online users list.',
+'Redirect time label'				=>	'Redirect time',
+'Redirect time help'				=>	'Number of seconds to wait when redirecting. If set to 0, no redirect page will be displayed (not recommended).',
+
+// Display Section
+'Display subhead'					=>	'Display',
+'Version number label'				=>	'Version number',
+'Version number help'				=>	'Show FluxBB version number in footer.',
+'Info in posts label'				=>	'User info in posts',
+'Info in posts help'				=>	'Show information about the poster under the username in topic view. The information affected is location, register date, post count and the contact links (email and URL).',
+'Post count label'					=>	'User post count',
+'Post count help'					=>	'Show the number of posts a user has made (affects topic view, profile and user list).',
+'Smilies label'						=>	'Smilies in posts',
+'Smilies help'						=>	'Convert smilies to small graphic icons.',
+'Smilies sigs label'				=>	'Smilies in signatures',
+'Smilies sigs help'					=>	'Convert smilies to small graphic icons in user signatures.',
+'Clickable links label'				=>	'Make clickable links',
+'Clickable links help'				=>	'When enabled, FluxBB will automatically detect any URLs in posts and make them clickable hyperlinks.',
+'Topic review label'				=>	'Topic review',
+'Topic review help'					=>	'Maximum number of posts to display when posting (newest first). Set to 0 to disable.',
+'Topics per page label'				=>	'Topics per page',
+'Topics per page help'				=>	'The default number of topics to display per page in a forum. Users can personalize this setting.',
+'Posts per page label'				=>	'Posts per page',
+'Posts per page help'				=>	'The default number of posts to display per page in a topic. Users can personalize this setting.',
+'Indent label'						=>	'Indent size',
+'Indent help'						=>	'If set to 8, a regular tab will be used when displaying text within the [code][/code] tag. Otherwise this many spaces will be used to indent the text.',
+'Quote depth label'					=>	'Maximum [quote] depth',
+'Quote depth help'					=>	'The maximum times a [quote] tag can go inside other [quote] tags, any tags deeper than this will be discarded.',
+
+// Features section
+'Features subhead'					=>	'Features',
+'Quick post label'					=>	'Quick post',
+'Quick post help'					=>	'When enabled, FluxBB will add a quick post form at the bottom of topics. This way users can post directly from the topic view.',
+'Users online label'				=>	'Users online',
+'Users online help'					=>	'Display info on the index page about guests and registered users currently browsing the board.',
+'Censor words label'				=>	'Censor words',
+'Censor words help'					=>	'Enable this to censor specific words in the board. See %s for more info.',
+'Signatures label'					=>	'Signatures',
+'Signatures help'					=>	'Allow users to attach a signature to their posts.',
+'User ranks label'					=>	'User ranks',
+'User ranks help'					=>	'Enable this to use user ranks. See %s for more info.',
+'User has posted label'				=>	'User has posted earlier',
+'User has posted help'				=>	'This feature displays a dot in front of topics in viewforum.php in case the currently logged in user has posted in that topic earlier. Disable if you are experiencing high server load.',
+'Topic views label'					=>	'Topic views',
+'Topic views help'					=>	'Keep track of the number of views a topic has. Disable if you are experiencing high server load in a busy forum.',
+'Quick jump label'					=>	'Quick jump',
+'Quick jump help'					=>	'Enable the quick jump (jump to forum) drop list.',
+'GZip label'						=>	'GZip output',
+'GZip help'							=>	'If enabled, FluxBB will gzip the output sent to browsers. This will reduce bandwidth usage, but use a little more CPU. This feature requires that PHP is configured with zlib (--with-zlib). Note: If you already have one of the Apache modules mod_gzip or mod_deflate set up to compress PHP scripts, you should disable this feature.',
+'Search all label'					=>	'Search all forums',
+'Search all help'					=>	'When disabled, searches will only be allowed in one forum at a time. Disable if server load is high due to excessive searching.',
+'Menu items label'					=>	'Additional menu items',
+'Menu items help'					=>	'By entering HTML hyperlinks into this textbox, any number of items can be added to the navigation menu at the top of all pages. The format for adding new links is X = <a href="URL">LINK</a> where X is the position at which the link should be inserted (e.g. 0 to insert at the beginning and 2 to insert after "User list"). Separate entries with a linebreak.',
+'Default feed label'				=>	'Default feed type',
+'Default feed help'					=>	'Select the type of syndication feed to display. Note: Choosing none will not disable feeds, only hide them by default.',
+'None'								=>	'None',
+'RSS'								=>	'RSS',
+'Atom'								=>	'Atom',
+
+// Reports section
+'Reports subhead'					=>	'Reports',
+'Reporting method label'			=>	'Reporting method',
+'Internal'							=>	'Internal',
+'By e-mail'							=>	'Email',
+'Both'								=>	'Both',
+'Reporting method help'				=>	'Select the method for handling topic/post reports. You can choose whether topic/post reports should be handled by the internal report system, emailed to the addresses on the mailing list (see below) or both.',
+'Mailing list label'				=>	'Mailing list',
+'Mailing list help'					=>	'A comma separated list of subscribers. The people on this list are the recipients of reports.',
+
+// Avatars section
+'Avatars subhead'					=>	'Avatars',
+'Use avatars label'					=>	'Use avatars',
+'Use avatars help'					=>	'When enabled, users will be able to upload an avatar which will be displayed under their title/rank.',
+'Upload directory label'			=>	'Upload directory',
+'Upload directory help'				=>	'The upload directory for avatars (relative to the FluxBB root directory). PHP must have write permissions to this directory.',
+'Max width label'					=>	'Max width',
+'Max width help'					=>	'The maximum allowed width of avatars in pixels (60 is recommended).',
+'Max height label'					=>	'Max height',
+'Max height help'					=>	'The maximum allowed height of avatars in pixels (60 is recommended).',
+'Max size label'					=>	'Max size',
+'Max size help'						=>	'The maximum allowed size of avatars in bytes (10240 is recommended).',
+
+// E-mail section
+'E-mail subhead'					=>	'Email',
+'Admin e-mail label'				=>	'Admin email',
+'Admin e-mail help'					=>	'The email address of the board administrator.',
+'Webmaster e-mail label'			=>	'Webmaster email',
+'Webmaster e-mail help'				=>	'This is the address that all emails sent by the board will be addressed from.',
+'Subscriptions label'				=>	'Subscriptions',
+'Subscriptions help'				=>	'Enable users to subscribe to topics (receive email when someone replies).',
+'SMTP address label'				=>	'SMTP server address',
+'SMTP address help'					=>	'The address of an external SMTP server to send emails with. You can specify a custom port number if the SMTP server doesn\'t run on the default port 25 (example: mail.myhost.com:3580). Leave blank to use the local mail program.',
+'SMTP username label'				=>	'SMTP username',
+'SMTP username help'				=>	'Username for SMTP server. Only enter a username if it is required by the SMTP server (most servers <strong>do not</strong> require authentication).',
+'SMTP password label'				=>	'SMTP password',
+'SMTP password help'				=>	'Password for SMTP server. Only enter a password if it is required by the SMTP server (most servers <strong>do not</strong> require authentication).',
+'SMTP SSL label'					=>	'Encrypt SMTP using SSL',
+'SMTP SSL help'						=>	'Encrypts the connection to the SMTP server using SSL. Should only be used if your SMTP server requires it and your version of PHP supports SSL.',
+
+// Registration Section
+'Registration subhead'				=>	'Registration',
+'Allow new label'					=>	'Allow new registrations',
+'Allow new help'					=>	'Controls whether this board accepts new registrations. Disable only under special circumstances.',
+'Verify label'						=>	'Verify registrations',
+'Verify help'						=>	'When enabled, users are emailed a random password when they register. They can then log in and change the password in their profile if they see fit. This feature also requires users to verify new email addresses if they choose to change from the one they registered with. This is an effective way of avoiding registration abuse and making sure that all users have "correct" email addresses in their profiles.',
+'Report new label'					=>	'Report new registrations',
+'Report new help'					=>	'If enabled, FluxBB will notify users on the mailing list (see above) when a new user registers in the forums.',
+'Use rules label'					=>	'User forum rules',
+'Use rules help'					=>	'When enabled, users must agree to a set of rules when registering (enter text below). The rules will always be available through a link in the navigation table at the top of every page.',
+'Rules label'						=>	'Enter your rules here',
+'Rules help'						=>	'Here you can enter any rules or other information that the user must review and accept when registering. If you enabled rules above you have to enter something here, otherwise it will be disabled. This text will not be parsed like regular posts and thus may contain HTML.',
+'E-mail default label'				=>	'Default email setting',
+'E-mail default help'				=>	'Choose the default privacy setting for new user registrations.',
+'Display e-mail label'				=>	'Display email address to other users.',
+'Hide allow form label'				=>	'Hide email address but allow form e-mail.',
+'Hide both label'					=>	'Hide email address and disallow form email.',
+
+// Announcement Section
+'Announcement subhead'				=>	'Announcements',
+'Display announcement label'		=>	'Display announcement',
+'Display announcement help'			=>	'Enable this to display the below message in the board.',
+'Announcement message label'		=>	'Announcement message',
+'Announcement message help'			=>	'This text will not be parsed like regular posts and thus may contain HTML.',
+
+// Maintenance Section
+'Maintenance subhead'				=>	'Maintenance',
+'Maintenance mode label'			=>	'Maintenance mode',
+'Maintenance mode help'				=>	'When enabled, the board will only be available to administrators. This should be used if the board needs to be taken down temporarily for maintenance. <strong>WARNING! Do not log out when the board is in maintenance mode.</strong> You will not be able to login again.',
+'Maintenance message label'			=>	'Maintenance message',
+'Maintenance message help'			=>	'The message that will be displayed to users when the board is in maintenance mode. If left blank, a default message will be used. This text will not be parsed like regular posts and thus may contain HTML.',
+
+);
diff --git a/lang/English/admin_permissions.php b/lang/English/admin_permissions.php
new file mode 100644
index 0000000..3c3600c
--- /dev/null
+++ b/lang/English/admin_permissions.php
@@ -0,0 +1,36 @@
+<?php
+
+// Language definitions used in admin-permissions.php
+$lang_admin_permissions = array(
+
+'Perms updated redirect'	=>	'Permissions updated. Redirecting …',
+'Permissions head'			=>	'Permissions',
+'Posting subhead'			=>	'Posting',
+'BBCode label'				=>	'BBCode',
+'BBCode help'				=>	'Allow BBCode in posts (recommended).',
+'Image tag label'			=>	'Image tag',
+'Image tag help'			=>	'Allow the BBCode [img][/img] tag in posts.',
+'All caps message label'	=>	'All caps message',
+'All caps message help'		=>	'Allow a message to contain only capital letters.',
+'All caps subject label'	=>	'All caps subject',
+'All caps subject help'		=>	'Allow a subject to contain only capital letters.',
+'Require e-mail label'		=>	'Require guest email',
+'Require e-mail help'		=>	'Require guests to supply an email address when posting.',
+'Signatures subhead'		=>	'Signatures',
+'BBCode sigs label'			=>	'BBCodes in signatures',
+'BBCode sigs help'			=>	'Allow BBCodes in user signatures.',
+'Image tag sigs label'		=>	'Image tag in signatures',
+'Image tag sigs help'		=>	'Allow the BBCode [img][/img] tag in user signatures (not recommended).',
+'All caps sigs label'		=>	'All caps signature',
+'All caps sigs help'		=>	'Allow a signature to contain only capital letters.',
+'Max sig length label'		=>	'Maximum signature length',
+'Max sig length help'		=>	'The maximum number of characters a user signature may contain.',
+'Max sig lines label'		=>	'Maximum signature lines',
+'Max sig lines help'		=>	'The maximum number of lines a user signature may contain.',
+'Registration subhead'		=>	'Registration',
+'Banned e-mail label'		=>	'Allow banned email addresses',
+'Banned e-mail help'		=>	'Allow users to register with or change to a banned email address/domain. If left at its default setting (yes), this action will be allowed, but an alert email will be sent to the mailing list (an effective way of detecting multiple registrations).',
+'Duplicate e-mail label'	=>	'Allow duplicate email addresses',
+'Duplicate e-mail help'		=>	'Controls whether users should be allowed to register with an email address that another user already has. If allowed, an alert email will be sent to the mailing list if a duplicate is detected.',
+
+);
diff --git a/lang/English/admin_plugin_example.php b/lang/English/admin_plugin_example.php
new file mode 100644
index 0000000..b06158d
--- /dev/null
+++ b/lang/English/admin_plugin_example.php
@@ -0,0 +1,17 @@
+<?php
+
+// Language definitions used in example Plugin
+$lang_admin_plugin_example = array(
+
+'No text'				=>	'You didn\'t enter anything!',
+'Example plugin title'	=>	'Example plugin',
+'You said'				=>	'You said "%s". Great stuff.',
+'Explanation 1'			=>	'This plugin doesn\'t do anything useful. Hence the name "Example".',
+'Explanation 2'			=>	'This would be a good spot to talk a little about your plugin. Describe what it does and how it should be used. Be brief, but informative.',
+'Example form title'	=>	'An example form',
+'Legend text'			=>	'Enter a piece of text and hit "Show text"!',
+'Text to show'			=>	'Text to show',
+'Show text button'		=>	'Show text',
+'Input content'			=>	'The text you want to display.',
+
+);
diff --git a/lang/English/admin_prune.php b/lang/English/admin_prune.php
new file mode 100644
index 0000000..532a74f
--- /dev/null
+++ b/lang/English/admin_prune.php
@@ -0,0 +1,23 @@
+<?php
+
+// Language definitions used in admin_prune.php
+$lang_admin_prune = array(
+
+'Must be integer message'	=>	'Days to prune must be a positive integer value.',
+'No old topics message'		=>	'There are no topics that are %s days old. Please decrease the value of "Days old" and try again.',
+'Posts pruned redirect'		=>	'Posts pruned. Redirecting …',
+'Prune head'				=>	'Prune',
+'Prune subhead'				=>	'Prune old posts',
+'Days old label'			=>	'Days old',
+'Days old help'				=>	'The number of days "old" a topic must be to be pruned. E.g. if you were to enter 30, every topic that didn\'t contain a post dated less than 30 days old would be deleted.',
+'Prune sticky label'		=>	'Prune sticky topics',
+'Prune sticky help'			=>	'When enabled, sticky topics will also be pruned.',
+'Prune from label'			=>	'Prune from forum',
+'All forums'				=>	'All forums',
+'Prune from help'			=>	'The forum from which you want to prune posts.',
+'Prune info'				=>	'Use this feature with caution. <strong>Pruned posts can never be recovered.</strong> For best performance, you should put the forum in %s during pruning.',
+'Confirm prune subhead'		=>	'Confirm prune posts',
+'Confirm prune info'		=>	'Are you sure that you want to prune all topics older than %s days from %s (%s topics).',
+'Confirm prune warn'		=>	'WARNING! Pruning posts deletes them permanently.',
+
+);
diff --git a/lang/English/admin_ranks.php b/lang/English/admin_ranks.php
new file mode 100644
index 0000000..de53834
--- /dev/null
+++ b/lang/English/admin_ranks.php
@@ -0,0 +1,23 @@
+<?php
+
+// Language definitions used in admin_ranks.php
+$lang_admin_ranks = array(
+
+'Must be integer message'	=>	'Minimum posts must be a positive integer value.',
+'Dupe min posts message'	=>	'There is already a rank with a minimun posts value of %s.',
+'Must enter title message'	=>	'You must enter a rank title.',
+'Rank added redirect'		=>	'Rank added. Redirecting …',
+'Rank updated redirect'		=>	'Rank updated. Redirecting …',
+'Rank removed redirect'		=>	'Rank removed. Redirecting …',
+'Ranks head'				=>	'Ranks',
+'Add rank subhead'			=>	'Add rank',
+'Add rank info'				=>	'Enter a rank and the minimum number of posts a user must have made to attain the rank. Different ranks cannot have the same value for minimum posts. If a title is set for a user, the title will be displayed instead of any rank.',
+'Ranks enabled'				=>	'<strong>User ranks is enabled in %s.</strong>',
+'Ranks disabled'			=>	'<strong>User ranks is disabled in %s.</strong>',
+'Rank title label'			=>	'Rank title',
+'Minimum posts label'		=>	'Minimum posts',
+'Actions label'				=>	'Actions',
+'Edit remove subhead'		=>	'Edit/remove ranks',
+'No ranks in list'			=>	'No ranks in list',
+
+);
diff --git a/lang/English/admin_reports.php b/lang/English/admin_reports.php
new file mode 100644
index 0000000..152bab3
--- /dev/null
+++ b/lang/English/admin_reports.php
@@ -0,0 +1,20 @@
+<?php
+
+// Language definitions used in admin_reports.php
+$lang_admin_reports = array(
+
+'Report zapped redirect'	=>	'Report zapped. Redirecting …',
+'New reports head'			=>	'New reports',
+'Deleted user'				=>	'Deleted user',
+'Deleted'					=>	'Deleted',
+'Report subhead'			=>	'Reported %s',
+'Reported by'				=>	'Reported by %s',
+'Reason'					=>	'Reason',
+'Zap'						=>	'Zap',
+'No new reports'			=>	'There are no new reports.',
+'Last 10 head'				=>	'10 last zapped reports',
+'NA'						=>	'N/A',
+'Zapped subhead'			=>	'Zapped %s by %s',
+'No zapped reports'			=>	'There are no zapped reports.',
+
+);
\ No newline at end of file
diff --git a/lang/English/admin_users.php b/lang/English/admin_users.php
new file mode 100644
index 0000000..1e21a34
--- /dev/null
+++ b/lang/English/admin_users.php
@@ -0,0 +1,70 @@
+<?php
+
+// Language definitions used in admin-users.php
+$lang_admin_users = array(
+
+'Non numeric message'		=>	'You entered a non-numeric value into a numeric only column.',
+'Invalid date time message'	=>	'You entered an invalid date/time.',
+'Not verified'				=>	'Not verified',
+'User search head'			=>	'User search',
+'User search subhead'		=>	'Enter search criteria',
+'User search info'			=>	'Search for users in the database. You can enter one or more terms to search for. Wildcards in the form of asterisks (*) are accepted.',
+'Username label'			=>	'Username',
+'E-mail address label'		=>	'Email address',
+'Title label'				=>	'Title',
+'Real name label'			=>	'Real name',
+'Website label'				=>	'Website',
+'Jabber label'				=>	'Jabber',
+'ICQ label'					=>	'ICQ',
+'MSN label'					=>	'MSN Messenger',
+'AOL label'					=>	'AOL IM',
+'Yahoo label'				=>	'Yahoo Messenger',
+'Location label'			=>	'Location',
+'Signature label'			=>	'Signature',
+'Admin note label'			=>	'Admin note',
+'Posts more than label'		=>	'Number of posts greater than',
+'Posts less than label'		=>	'Number of posts less than',
+'Last post after label'		=>	'Last post is after',
+'Date help'					=>	'(yyyy-mm-dd hh:mm:ss)',
+'Last post before label'	=>	'Last post is before',
+'Registered after label'	=>	'Registered after',
+'Registered before label'	=>	'Registered before',
+'Order by label'			=>	'Order by',
+'Order by username'			=>	'Username',
+'Order by e-mail'			=>	'Email',
+'Order by posts'			=>	'Number of posts',
+'Order by last post'		=>	'Last post',
+'Order by registered'		=>	'Registered',
+'Ascending'					=>	'Ascending',
+'Descending'				=>	'Descending',
+'User group label'			=>	'User group',
+'All groups'				=>	'All groups',
+'Unverified users'			=>	'Unverified users',
+'Submit search'				=>	'Submit search',
+'IP search head'			=>	'IP search',
+'IP search subhead'			=>	'Enter IP to search for',
+'IP address label'			=>	'IP address',
+'IP address help'			=>	'The IP address to search for in the post database.',
+'Find IP address'			=>	'Find IP address',
+
+'Results head'				=>	'Search Results',
+'Results username head'		=>	'Username',
+'Results e-mail head'		=>	'Email',
+'Results title head'		=>	'Title/Status',
+'Results posts head'		=>	'Posts',
+'Results admin note head'	=>	'Admin note',
+'Results actions head'		=>	'Actions',
+'Results IP address head'	=>	'IP address',
+'Results last used head'	=>	'Last used',
+'Results times found head'	=>	'Times found',
+'Results action head'		=>	'Action',
+'Results find more link'	=>	'Find more users for this ip',
+'Results no posts found'	=>	'There are currently no posts by that user in the forum.',
+'Bad IP message'			=>	'The supplied IP address is not correctly formatted.',
+'Results view IP link'		=>	'View IP stats',
+'Results show posts link'	=>	'Show posts',
+'Results guest'				=>	'Guest',
+'Results no IP found'		=>	'The supplied IP address could not be found in the database.',
+'No match'					=>	'No match'
+
+);
diff --git a/lang/English/common.php b/lang/English/common.php
new file mode 100644
index 0000000..85c276f
--- /dev/null
+++ b/lang/English/common.php
@@ -0,0 +1,169 @@
+<?php
+
+// Language definitions for frequently used strings
+$lang_common = array(
+
+// Text orientation and encoding
+'lang_direction'					=>	'ltr', // ltr (Left-To-Right) or rtl (Right-To-Left)
+'lang_identifier'					=>	'en',
+
+// Number formatting
+'lang_decimal_point'				=>	'.',
+'lang_thousands_sep'				=>	',',
+
+// Notices
+'Bad request'						=>	'Bad request. The link you followed is incorrect or outdated.',
+'No view'							=>	'You do not have permission to view these forums.',
+'No permission'						=>	'You do not have permission to access this page.',
+'Bad referrer'						=>	'Bad HTTP_REFERER. You were referred to this page from an unauthorized source. If the problem persists please make sure that \'Base URL\' is correctly set in Admin/Options and that you are visiting the forum by navigating to that URL. More information regarding the referrer check can be found in the FluxBB documentation.',
+'No cookie'							=>	'You appear to have logged in successfully, however a cookie has not been set. Please check your settings and if applicable, enable cookies for this website.',
+'Pun include error'					=>	'Unable to process user include %s from template %s. There is no such file in neither the template directory nor in the user include directory.',
+
+// Miscellaneous
+'Announcement'						=>	'Announcement',
+'Options'							=>	'Options',
+'Submit'							=>	'Submit', // "Name" of submit buttons
+'Ban message'						=>	'You are banned from this forum.',
+'Ban message 2'						=>	'The ban expires at the end of',
+'Ban message 3'						=>	'The administrator or moderator that banned you left the following message:',
+'Ban message 4'						=>	'Please direct any inquiries to the forum administrator at',
+'Never'								=>	'Never',
+'Today'								=>	'Today',
+'Yesterday'							=>	'Yesterday',
+'Info'								=>	'Info', // A common table header
+'Go back'							=>	'Go back',
+'Maintenance'						=>	'Maintenance',
+'Redirecting'						=>	'Redirecting',
+'Click redirect'					=>	'Click here if you do not want to wait any longer (or if your browser does not automatically forward you)',
+'on'								=>	'on', // As in "BBCode is on"
+'off'								=>	'off',
+'Invalid email'						=>	'The email address you entered is invalid.',
+'Required'							=>	'(Required)',
+'required field'					=>	'is a required field in this form.', // For javascript form validation
+'Last post'							=>	'Last post',
+'by'								=>	'by', // As in last post by someuser
+'New posts'							=>	'New posts', // The link that leads to the first new post
+'New posts info'					=>	'Go to the first new post in this topic.', // The popup text for new posts links
+'Username'							=>	'Username',
+'Password'							=>	'Password',
+'Email'								=>	'Email',
+'Send email'						=>	'Send email',
+'Moderated by'						=>	'Moderated by',
+'Registered'						=>	'Registered',
+'Subject'							=>	'Subject',
+'Message'							=>	'Message',
+'Topic'								=>	'Topic',
+'Forum'								=>	'Forum',
+'Posts'								=>	'Posts',
+'Replies'							=>	'Replies',
+'Pages'								=>	'Pages:',
+'Page'								=>	'Page %s',
+'BBCode'							=>	'BBCode:', // You probably shouldn't change this
+'img tag'							=>	'[img] tag:',
+'Smilies'							=>	'Smilies:',
+'and'								=>	'and',
+'Image link'						=>	'image', // This is displayed (i.e. <image>) instead of images when "Show images" is disabled in the profile
+'wrote'								=>	'wrote:', // For [quote]'s
+'Mailer'							=>	'Mailer', // As in "MyForums Mailer" in the signature of outgoing emails
+'Important information'				=>	'Important information',
+'Write message legend'				=>	'Write your message and submit',
+'Previous'							=>	'Previous',
+'Next'								=>	'Next',
+'Spacer'							=>	'…', // Ellipsis for paginate
+
+// Title
+'Title'								=>	'Title',
+'Member'							=>	'Member', // Default title
+'Moderator'							=>	'Moderator',
+'Administrator'						=>	'Administrator',
+'Banned'							=>	'Banned',
+'Guest'								=>	'Guest',
+
+// Stuff for include/parser.php
+'BBCode error no opening tag'		=>	'[/%1$s] was found without a matching [%1$s]',
+'BBCode error invalid nesting'		=>	'[%1$s] was opened within [%2$s], this is not allowed',
+'BBCode error invalid self-nesting'	=>	'[%s] was opened within itself, this is not allowed',
+'BBCode error no closing tag'		=>	'[%1$s] was found without a matching [/%1$s]',
+'BBCode error empty attribute'		=>	'[%s] tag had an empty attribute section',
+'BBCode code problem'				=>	'There is a problem with your [code] tags',
+'BBCode list size error'			=>	'Your list was too long to parse, please make it smaller!',
+
+// Stuff for the navigator (top of every page)
+'Index'								=>	'Index',
+'User list'							=>	'User list',
+'Rules'								=>	'Rules',
+'Search'							=>	'Search',
+'Register'							=>	'Register',
+'Login'								=>	'Login',
+'Not logged in'						=>	'You are not logged in.',
+'Profile'							=>	'Profile',
+'Logout'							=>	'Logout',
+'Logged in as'						=>	'Logged in as',
+'Admin'								=>	'Administration',
+'Last visit'						=>	'Last visit: %s',
+'Show new posts'					=>	'Show new posts since last visit',
+'Mark all as read'					=>	'Mark all topics as read',
+'Mark forum read'					=>	'Mark this forum as read',
+'Title separator'					=>	' / ',
+
+// Stuff for the page footer
+'Board footer'						=>	'Board footer',
+'Search links'						=>	'Search links',
+'Show recent posts'					=>	'Show recent posts',
+'Show unanswered posts'				=>	'Show unanswered posts',
+'Show your posts'					=>	'Show your posts',
+'Show subscriptions'				=>	'Show your subscribed topics',
+'Jump to'							=>	'Jump to',
+'Go'								=>	' Go ', // Submit button in forum jump
+'Moderate topic'					=>	'Moderate topic',
+'Move topic'						=>	'Move topic',
+'Open topic'						=>	'Open topic',
+'Close topic'						=>	'Close topic',
+'Unstick topic'						=>	'Unstick topic',
+'Stick topic'						=>	'Stick topic',
+'Moderate forum'					=>	'Moderate forum',
+'Powered by'						=>	'Powered by %s',
+
+// Debug information
+'Debug table'						=>	'Debug information',
+'Querytime'							=>	'Generated in %1$s seconds, %2$s queries executed',
+'Memory usage'						=>	'Memory usage: %1$s',
+'Peak usage'						=>	'(Peak: %1$s)',
+'Query times'						=>	'Time (s)',
+'Query'								=>	'Query',
+'Total query time'					=>	'Total query time: %s',
+
+// Email related notifications
+'New user notification'				=>	'Alert - New registration',
+'New user message'					=>	'User \'%s\' registered in the forums at %s',
+'Banned email notification'			=>	'Alert - Banned email detected',
+'Banned email register message'		=>	'User \'%s\' registered with banned email address: %s',
+'Banned email change message'		=>	'User \'%s\' changed to banned email address: %s',
+'Banned email post message'			=>	'User \'%s\' posted with banned email address: %s',
+'Duplicate email notification'		=>	'Alert - Duplicate email detected',
+'Duplicate email register message'	=>	'User \'%s\' registered with an email address that also belongs to: %s',
+'Duplicate email change message'	=>	'User \'%s\' changed to an email address that also belongs to: %s',
+'Report notification'				=>	'Report(%d) - \'%s\'',
+'Report message 1'					=>	'User \'%s\' has reported the following message: %s',
+'Report message 2'					=>	'Reason: %s',
+
+'User profile'						=>	'User profile: %s',
+'Post URL'							=>	'Post URL: %s',
+'Email signature'					=>	'Forum Mailer'."\n".'(Do not reply to this message)',
+
+// For extern.php RSS feed
+'RSS description'					=>	'The most recent topics at %s.',
+'RSS description topic'				=>	'The most recent posts in %s.',
+'RSS reply'							=>	'Re: ', // The topic subject will be appended to this string (to signify a reply)
+'RSS active topics feed'			=>	'RSS active topics feed',
+'Atom active topics feed'			=>	'Atom active topics feed',
+'RSS forum feed'					=>	'RSS forum feed',
+'Atom forum feed'					=>	'Atom forum feed',
+'RSS topic feed'					=>	'RSS topic feed',
+'Atom topic feed'					=>	'Atom topic feed',
+
+// Admin related stuff in the header
+'New reports'						=>	'There are new reports',
+'Maintenance mode enabled'			=>	'Maintenance mode is enabled!',
+
+);
diff --git a/lang/English/delete.php b/lang/English/delete.php
new file mode 100644
index 0000000..61599d2
--- /dev/null
+++ b/lang/English/delete.php
@@ -0,0 +1,16 @@
+<?php
+
+// Language definitions used in delete.php
+$lang_delete = array(
+
+'Delete post'			=>	'Delete post',
+'Warning'				=>	'You are about to permanently delete this post.',
+'Topic warning'			=>	'Warning! This is the first post in the topic, the whole topic will be permanently deleted.',
+'Delete info'			=>	'The post you have chosen to delete is set out below for you to review before proceeding.',
+'Reply by'				=>	'Reply by %s - %s',
+'Topic by'				=>	'Topic started by %s - %s',
+'Delete'				=>	'Delete', // The submit button
+'Post del redirect'		=>	'Post deleted. Redirecting …',
+'Topic del redirect'	=>	'Topic deleted. Redirecting …'
+
+);
\ No newline at end of file
diff --git a/lang/English/forum.php b/lang/English/forum.php
new file mode 100644
index 0000000..42111da
--- /dev/null
+++ b/lang/English/forum.php
@@ -0,0 +1,14 @@
+<?php
+
+// Language definitions used in viewforum.php
+$lang_forum = array(
+
+'Post topic'	=>	'Post new topic',
+'Views'			=>	'Views',
+'Moved'			=>	'Moved:',
+'Sticky'		=>	'Sticky:',
+'Closed'		=>	'Closed:',
+'Empty forum'	=>	'Forum is empty.',
+'Mod controls'	=>	'Moderator controls'
+
+);
diff --git a/lang/English/help.php b/lang/English/help.php
new file mode 100644
index 0000000..47b5e0a
--- /dev/null
+++ b/lang/English/help.php
@@ -0,0 +1,59 @@
+<?php
+
+// Language definitions used in help.php
+$lang_help = array(
+
+'Help'					=>	'Help',
+'produces'				=>	'produces',
+
+'BBCode'				=>	'BBCode',
+'BBCode info 1'			=>	'BBCode is a collection of formatting tags that are used to change the look of text in this forum. BBCode is based on the same principal as, and is very similar to, HTML. Below is a list of all the available BBCodes and instructions on how to use them.',
+'BBCode info 2'			=>	'Administrators have the ability to enable or disable BBCode. You can tell if BBCode is enabled or disabled out in the left margin whenever you post a message or edit your signature.',
+
+'Text style'			=>	'Text style',
+'Text style info'		=>	'The following tags change the appearance of text:',
+'Bold text'				=>	'Bold text',
+'Underlined text'		=>	'Underlined text',
+'Italic text'			=>	'Italic text',
+'Strike-through text'	=>	'Strike-through text',
+'Red text'				=>	'Red text',
+'Blue text'				=>	'Blue text',
+'Heading text'			=>	'Heading text',
+'Deleted text'			=>	'Deleted text',
+'Inserted text'			=>	'Inserted text',
+'Emphasised text'		=>	'Emphasised text',
+
+'Links and images'		=>	'Links and images',
+'Links info'			=>	'You can create links to other documents or to email addresses using the following tags:',
+'My email address'		=>	'My email address',
+'Images info'			=>	'If you want to display an image you can use the img tag. The text appearing after the "=" sign in the opening tag is used for the alt attribute and should be included whenever possible.',
+
+'Quotes'				=>	'Quotes',
+'Quotes info'			=>	'If you want to quote someone, you should use the quote tag.',
+'Quotes info 2'			=>	'If you don\'t want to quote anyone in particular, you can use the quote tag without specifying a name.',
+'Quote text'			=>	'This is the text i want to quote.',
+'produces quote box'	=>	'produces a quote box like this:',
+'quote note'			=>	'Note: If a username contains the characters [ or ] you can enclose it in quote marks.',
+
+'Code'					=>	'Code',
+'Code info'				=>	'When displaying source code you should make sure that you use the code tag. Text displayed with the code tag will use a monospaced font and will not be affected by other tags.',
+'Code text'				=>	'This is some code.',
+'produces code box'		=>	'produces a code box like this:',
+
+'Nested tags'			=>	'Nested tags',
+'Nested tags info'		=>	'BBCode can be nested to create more advanced formatting. For example:',
+'Bold, underlined text'	=>	'Bold, underlined text',
+
+'Lists'					=>	'Lists',
+'List info'				=>	'To create a list you can use the list tag. You can create 3 types of lists using the list tag.',
+'List text 1'			=>	'Example list item 1.',
+'List text 2'			=>	'Example list item 2.',
+'List text 3'			=>	'Example list item 3.',
+'produces list'			=>	'produces a bulleted list.',
+'produces decimal list'	=>	'produces a numbered list.',
+'produces alpha list'	=>	'produces an alphabetically labelled list.',
+
+'Smilies'				=>	'Smilies',
+'Smilies info'			=>	'If you like (and if it is enabled), the forum can convert a series of smilies to images representations of that smiley. This forum recognizes the following smilies and replaces them with images:'
+
+);
diff --git a/lang/English/index.html b/lang/English/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/lang/English/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/lang/English/index.php b/lang/English/index.php
new file mode 100644
index 0000000..0ed222a
--- /dev/null
+++ b/lang/English/index.php
@@ -0,0 +1,20 @@
+<?php
+
+// Language definitions used in index.php
+$lang_index = array(
+
+'Topics'		=>	'Topics',
+'Link to'		=>	'Link to:', // As in "Link to: http://fluxbb.org/"
+'Empty board'	=>	'Board is empty.',
+'Newest user'	=>	'Newest registered user: %s',
+'Users online'	=>	'Registered users online: %s',
+'Guests online'	=>	'Guests online: %s',
+'No of users'	=>	'Total number of registered users: %s',
+'No of topics'	=>	'Total number of topics: %s',
+'No of posts'	=>	'Total number of posts: %s',
+'Online'		=>	'Online:', // As in "Online: User A, User B etc."
+'Board info'	=>	'Board information',
+'Board stats'	=>	'Board statistics',
+'User info'		=>	'User information'
+
+);
diff --git a/lang/English/login.php b/lang/English/login.php
new file mode 100644
index 0000000..244ee06
--- /dev/null
+++ b/lang/English/login.php
@@ -0,0 +1,26 @@
+<?php
+
+// Language definitions used in login.php
+$lang_login = array(
+
+// Miscellaneous
+'Wrong user/pass'			=>	'Wrong username and/or password.',
+'Forgotten pass'			=>	'Forgotten your password?',
+'Login redirect'			=>	'Logged in successfully. Redirecting …',
+'Logout redirect'			=>	'Logged out. Redirecting …',
+'No email match'			=>	'There is no user registered with the email address',
+'Request pass'				=>	'Request password',
+'Request pass legend'		=>	'Enter the email address with which you registered',
+'Request pass info'			=>	'A new password together with a link to activate the new password will be sent to that address.',
+'Not registered'			=>	'Not registered yet?',
+'Login legend'				=>	'Enter your username and password below',
+'Remember me'				=>	'Log me in automatically each time I visit.',
+'Login info'				=>	'If you have not registered or have forgotten your password click on the appropriate link below.',
+'New password errors'		=>	'Password request error',
+'New passworderrors info'	=>	'The following error needs to be corrected before a new password can be sent:',
+
+// Forget password mail stuff
+'Forget mail'				=>	'An email has been sent to the specified address with instructions on how to change your password. If it does not arrive you can contact the forum administrator at',
+'Email flood'				=>	'This account has already requested a password reset in the past hour. Please wait a while before requesting a new password again.'
+
+);
diff --git a/lang/English/mail_templates/activate_email.tpl b/lang/English/mail_templates/activate_email.tpl
new file mode 100644
index 0000000..f17066f
--- /dev/null
+++ b/lang/English/mail_templates/activate_email.tpl
@@ -0,0 +1,12 @@
+Subject: Change email address requested
+
+Hello <username>,
+
+You have requested to have a new email address assigned to your account in the discussion forum at <base_url>. If you didn't request this or if you don't want to change your email address you should just ignore this message. Only if you visit the activation page below will your email address be changed. In order for the activation page to work, you must be logged in to the forum.
+
+To change your email address, please visit the following page:
+<activation_url>
+
+--
+<board_mailer>
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/activate_password.tpl b/lang/English/mail_templates/activate_password.tpl
new file mode 100644
index 0000000..33b2b3e
--- /dev/null
+++ b/lang/English/mail_templates/activate_password.tpl
@@ -0,0 +1,14 @@
+Subject: New password requested
+
+Hello <username>,
+
+You have requested to have a new password assigned to your account in the discussion forum at <base_url>. If you didn't request this or if you don't want to change your password you should just ignore this message. Only if you visit the activation page below will your password be changed.
+
+Your new password is: <new_password>
+
+To change your password, please visit the following page:
+<activation_url>
+
+--
+<board_mailer>
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/form_email.tpl b/lang/English/mail_templates/form_email.tpl
new file mode 100644
index 0000000..b862422
--- /dev/null
+++ b/lang/English/mail_templates/form_email.tpl
@@ -0,0 +1,13 @@
+Subject: <mail_subject>
+
+<sender> from <board_title> has sent you a message. You can reply to <sender> by replying to this email.
+
+The message reads as follows:
+-----------------------------------------------------------------------
+
+<mail_message>
+
+-----------------------------------------------------------------------
+
+--
+<board_mailer>
diff --git a/lang/English/mail_templates/index.html b/lang/English/mail_templates/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/lang/English/mail_templates/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/lang/English/mail_templates/new_reply.tpl b/lang/English/mail_templates/new_reply.tpl
new file mode 100644
index 0000000..e9dab0b
--- /dev/null
+++ b/lang/English/mail_templates/new_reply.tpl
@@ -0,0 +1,11 @@
+Subject: Reply to topic: <topic_subject>
+
+<replier> has replied to the topic <topic_subject> to which you are subscribed. There may be more new replies, but this is the only notification you will receive until you visit the board again.
+
+The post is located at <post_url>
+
+You can unsubscribe by going to <unsubscribe_url>
+
+--
+<board_mailer>
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/new_reply_full.tpl b/lang/English/mail_templates/new_reply_full.tpl
new file mode 100644
index 0000000..7231145
--- /dev/null
+++ b/lang/English/mail_templates/new_reply_full.tpl
@@ -0,0 +1,18 @@
+Subject: Reply to topic: <topic_subject>
+
+<replier> has replied to the topic <topic_subject> to which you are subscribed. There may be more new replies, but this is the only notification you will receive until you visit the board again.
+
+The message reads as follows:
+-----------------------------------------------------------------------
+
+<message>
+
+-----------------------------------------------------------------------
+
+The post is located at <post_url>
+
+You can unsubscribe by going to <unsubscribe_url>
+
+--
+<board_mailer>
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/rename.tpl b/lang/English/mail_templates/rename.tpl
new file mode 100644
index 0000000..f1ea88c
--- /dev/null
+++ b/lang/English/mail_templates/rename.tpl
@@ -0,0 +1,12 @@
+Subject: User account renamed
+
+During an upgrade to the forums at <base_url> it was determined your username is too similar to an existing user. Your username has been changed accordingly.
+
+Old username: <old_username>
+New username: <new_username>
+
+We apologise for any inconvenience caused.
+
+--
+<board_mailer>
+(Do not reply to this message)
diff --git a/lang/English/mail_templates/welcome.tpl b/lang/English/mail_templates/welcome.tpl
new file mode 100644
index 0000000..0110042
--- /dev/null
+++ b/lang/English/mail_templates/welcome.tpl
@@ -0,0 +1,12 @@
+Subject: Welcome to <board_title>!
+
+Thank you for registering in the forums at <base_url>. Your account details are:
+
+Username: <username>
+Password: <password>
+
+Login at <login_url> to activate the account.
+
+--
+<board_mailer>
+(Do not reply to this message)
diff --git a/lang/English/misc.php b/lang/English/misc.php
new file mode 100644
index 0000000..c6b9503
--- /dev/null
+++ b/lang/English/misc.php
@@ -0,0 +1,91 @@
+<?php
+
+// Language definitions used in various scripts
+$lang_misc = array(
+
+'Mark read redirect'			=>	'All topics and forums have been marked as read. Redirecting …',
+'Mark forum read redirect'		=>	'All topics in the specified forum have been marked as read. Redirecting …',
+
+// Send email
+'Form email disabled'			=>	'The user you are trying to send an email to has disabled form email.',
+'No email subject'				=>	'You must enter a subject.',
+'No email message'				=>	'You must enter a message.',
+'Too long email message'		=>	'Messages cannot be longer than 65535 characters (64 KB).',
+'Email flood'					=>	'At least %s seconds have to pass between sent emails. Please wait a while and try sending again.',
+'Email sent redirect'			=>	'Email sent. Redirecting …',
+'Send email to'					=>	'Send email to',
+'Email subject'					=>	'Subject',
+'Email message'					=>	'Message',
+'Email disclosure note'			=>	'Please note that by using this form, your email address will be disclosed to the recipient.',
+'Write email'					=>	'Write and submit your email message',
+
+// Report
+'No reason'						=>	'You must enter a reason.',
+'Reason too long'				=>	'Your message must be under 65535 bytes (~64kb).',
+'Report flood'					=>	'At least %s seconds have to pass between reports. Please wait a while and try sending again.',
+'Report redirect'				=>	'Post reported. Redirecting …',
+'Report post'					=>	'Report post',
+'Reason'						=>	'Reason',
+'Reason desc'					=>	'Please enter a short reason why you are reporting this post',
+
+// Subscriptions
+'Already subscribed'			=>	'You are already subscribed to this topic.',
+'Subscribe redirect'			=>	'Your subscription has been added. Redirecting …',
+'Not subscribed'				=>	'You are not subscribed to this topic.',
+'Unsubscribe redirect'			=>	'Your subscription has been removed. Redirecting …',
+
+// General forum and topic moderation
+'Moderate'						=>	'Moderate',
+'Select'						=>	'Select', // the header of a column of checkboxes
+'Move'							=>	'Move',
+'Split'							=>	'Split',
+'Delete'						=>	'Delete',
+'Merge'							=>	'Merge',
+
+// Moderate forum
+'Open'							=>	'Open',
+'Close'							=>	'Close',
+'Move topic'					=>	'Move topic',
+'Move topics'					=>	'Move topics',
+'Move legend'					=>	'Select destination of move',
+'Move to'						=>	'Move to',
+'Nowhere to move'				=>	'There are no forums into which you can move topics.',
+'Leave redirect'				=>	'Leave redirect topic(s)',
+'Move topic redirect'			=>	'Topic moved. Redirecting …',
+'Move topics redirect'			=>	'Topics moved. Redirecting …',
+'Confirm delete legend'			=>	'Please confirm deletion',
+'Delete topics'					=>	'Delete topics',
+'Delete topics comply'			=>	'Are you sure you want to delete the selected topics?',
+'Delete topics redirect'		=>	'Topics deleted. Redirecting …',
+'Open topic redirect'			=>	'Topic opened. Redirecting …',
+'Open topics redirect'			=>	'Topics opened. Redirecting …',
+'Close topic redirect'			=>	'Topic closed. Redirecting …',
+'Close topics redirect'			=>	'Topics closed. Redirecting …',
+'No topics selected'			=>	'You must select at least one topic for move/delete/open/close.',
+'Not enough topics selected'	=>	'You must select at least two topics for merge.',
+'Stick topic redirect'			=>	'Topic sticked. Redirecting …',
+'Unstick topic redirect'		=>	'Topic unsticked. Redirecting …',
+'Merge topics'					=>	'Merge topics',
+'Merge topics redirect'			=>	'Topics merged. Redirecting …',
+'Confirm merge legend'			=>	'Please confirm merge',
+'New subject'					=>	'New subject',
+
+// Split multiple posts in topic
+'Confirm split legend'			=>	'Please confirm split of selected posts.',
+'Split posts'					=>	'Split posts',
+'Split posts comply'			=>	'Are you sure you want to split the selected posts?',
+'Split posts redirect'			=>	'Posts have been split. Redirecting …',
+
+// Delete multiple posts in topic
+'Delete posts'					=>	'Delete posts',
+'Cannot select first'			=>	'First post cannot be selected for split/delete.',
+'Delete posts comply'			=>	'Are you sure you want to delete the selected posts?',
+'Delete posts redirect'			=>	'Posts deleted. Redirecting …',
+'No posts selected'				=>	'You must select at least one post for split/delete.',
+
+// Get host
+'Host info 1'					=>	'The IP address is: %s',
+'Host info 2'					=>	'The host name is: %s',
+'Show more users'				=>	'Show more users for this IP',
+
+);
diff --git a/lang/English/post.php b/lang/English/post.php
new file mode 100644
index 0000000..c02eb6b
--- /dev/null
+++ b/lang/English/post.php
@@ -0,0 +1,36 @@
+<?php
+
+// Language definitions used in post.php and edit.php
+$lang_post = array(
+
+// Post validation stuff (many are similiar to those in edit.php)
+'No subject'		=>	'Topics must contain a subject.',
+'Too long subject'	=>	'Subjects cannot be longer than 70 characters.',
+'No message'		=>	'You must enter a message.',
+'Too long message'	=>	'Posts cannot be longer that %s bytes.',
+'All caps subject'	=>	'Subjects cannot contain only capital letters.',
+'All caps message'	=>	'Posts cannot contain only capital letters.',
+
+// Posting
+'Post errors'		=>	'Post errors',
+'Post errors info'	=>	'The following errors need to be corrected before the message can be posted:',
+'Post preview'		=>	'Post preview',
+'Guest name'		=>	'Name', // For guests (instead of Username)
+'Post redirect'		=>	'Post entered. Redirecting …',
+'Post a reply'		=>	'Post a reply',
+'Post new topic'	=>	'Post new topic',
+'Hide smilies'		=>	'Never show smilies as icons for this post',
+'Subscribe'			=>	'Subscribe to this topic',
+'Stay subscribed'	=>	'Stay subscribed to this topic',
+'Topic review'		=>	'Topic review (newest first)',
+'Flood start'		=>	'At least',
+'flood end'			=>	'seconds have to pass between posts. Please wait a little while and try posting again.',
+'Preview'			=>	'Preview', // submit button to preview message
+
+// Edit post
+'Edit post legend'	=>	'Edit the post and submit changes',
+'Silent edit'		=>	'Silent edit (don\'t display "Edited by ..." in topic view)',
+'Edit post'			=>	'Edit post',
+'Edit redirect'		=>	'Post updated. Redirecting …'
+
+);
diff --git a/lang/English/prof_reg.php b/lang/English/prof_reg.php
new file mode 100644
index 0000000..1cbbec6
--- /dev/null
+++ b/lang/English/prof_reg.php
@@ -0,0 +1,79 @@
+<?php
+
+// Language definitions used in both profile.php and register.php
+$lang_prof_reg = array(
+
+'Email legend'				=>	'Enter a valid email address',
+'Email legend 2'			=>	'Enter and confirm a valid email address',
+'Localisation legend'		=>	'Set your localisation options',
+'Time zone'					=>	'Time zone',
+'Time zone info'			=>	'For the forum to display times correctly you must select your local time zone. If Daylight Savings Time is in effect you should also check the option provided which will advance times by 1 hour.',
+'DST'						=>	'Daylight Savings Time is in effect (advance time by 1 hour).',
+'Time format'				=>	'Time format',
+'Date format'				=>	'Date format',
+'Default'					=>	'Default',
+'Language'					=>	'Language',
+'Email setting info'		=>	'Select whether you want your email address to be viewable to other users or not and if you want other users to be able to send you email via the forum (form email) or not.',
+'Email setting 1'			=>	'Display your email address.',
+'Email setting 2'			=>	'Hide your email address but allow form email.',
+'Email setting 3'			=>	'Hide your email address and disallow form email.',
+'Privacy options legend'	=>	'Set your privacy options',
+'Confirm pass'				=>	'Confirm password',
+
+'Username too short'		=>	'Usernames must be at least 2 characters long. Please choose another (longer) username.',
+'Username too long'			=>	'Usernames must not be more than 25 characters long. Please choose another (shorter) username.',
+'Username guest'			=>	'The username guest is reserved. Please choose another username.',
+'Username IP'				=>	'Usernames may not be in the form of an IP address. Please choose another username.',
+'Username reserved chars'	=>	'Usernames may not contain all the characters \', " and [ or ] at once. Please choose another username.',
+'Username BBCode'			=>	'Usernames may not contain any of the text formatting tags (BBCode) that the forum uses. Please choose another username.',
+'Banned username'			=>	'The username you entered is banned in this forum. Please choose another username.',
+'Pass too short'			=>	'Passwords must be at least 4 characters long. Please choose another (longer) password.',
+'Pass not match'			=>	'Passwords do not match.',
+'Banned email'				=>	'The email address you entered is banned in this forum. Please choose another email address.',
+'Dupe email'				=>	'Someone else is already registered with that email address. Please choose another email address.',
+'Sig too long'				=>	'Signatures cannot be longer than %1$s characters. Please reduce your signature by %2$s characters.',
+'Sig too many lines'		=>	'Signatures cannot have more than %s lines.',
+'Bad ICQ'					=>	'You entered an invalid ICQ UIN. Please go back and correct.',
+
+'UTC-12:00'					=>	'(UTC-12:00) International Date Line West',
+'UTC-11:00'					=>	'(UTC-11:00) Niue, Samoa',
+'UTC-10:00'					=>	'(UTC-10:00) Hawaii-Aleutian, Cook Island',
+'UTC-09:30'					=>	'(UTC-09:30) Marquesas Islands',
+'UTC-09:00'					=>	'(UTC-09:00) Alaska, Gambier Island',
+'UTC-08:30'					=>	'(UTC-08:30) Pitcairn Islands',
+'UTC-08:00'					=>	'(UTC-08:00) Pacific',
+'UTC-07:00'					=>	'(UTC-07:00) Mountain',
+'UTC-06:00'					=>	'(UTC-06:00) Central',
+'UTC-05:00'					=>	'(UTC-05:00) Eastern',
+'UTC-04:00'					=>	'(UTC-04:00) Atlantic',
+'UTC-03:30'					=>	'(UTC-03:30) Newfoundland',
+'UTC-03:00'					=>	'(UTC-03:00) Amazon, Central Greenland',
+'UTC-02:00'					=>	'(UTC-02:00) Mid-Atlantic',
+'UTC-01:00'					=>	'(UTC-01:00) Azores, Cape Verde, Eastern Greenland',
+'UTC'						=>	'(UTC) Western European, Greenwich',
+'UTC+01:00'					=>	'(UTC+01:00) Central European, West African',
+'UTC+02:00'					=>	'(UTC+02:00) Eastern European, Central African',
+'UTC+03:00'					=>	'(UTC+03:00) Moscow, Eastern African',
+'UTC+03:30'					=>	'(UTC+03:30) Iran',
+'UTC+04:00'					=>	'(UTC+04:00) Gulf, Samara',
+'UTC+04:30'					=>	'(UTC+04:30) Afghanistan',
+'UTC+05:00'					=>	'(UTC+05:00) Pakistan, Yekaterinburg',
+'UTC+05:30'					=>	'(UTC+05:30) India, Sri Lanka',
+'UTC+05:45'					=>	'(UTC+05:45) Nepal',
+'UTC+06:00'					=>	'(UTC+06:00) Bangladesh, Bhutan, Novosibirsk',
+'UTC+06:30'					=>	'(UTC+06:30) Cocos Islands, Myanmar',
+'UTC+07:00'					=>	'(UTC+07:00) Indochina, Krasnoyarsk',
+'UTC+08:00'					=>	'(UTC+08:00) Greater China, Australian Western, Irkutsk',
+'UTC+08:45'					=>	'(UTC+08:45) Southeastern Western Australia',
+'UTC+09:00'					=>	'(UTC+09:00) Japan, Korea, Chita',
+'UTC+09:30'					=>	'(UTC+09:30) Australian Central',
+'UTC+10:00'					=>	'(UTC+10:00) Australian Eastern, Vladivostok',
+'UTC+10:30'					=>	'(UTC+10:30) Lord Howe',
+'UTC+11:00'					=>	'(UTC+11:00) Solomon Island, Magadan',
+'UTC+11:30'					=>	'(UTC+11:30) Norfolk Island',
+'UTC+12:00'					=>	'(UTC+12:00) New Zealand, Fiji, Kamchatka',
+'UTC+12:45'					=>	'(UTC+12:45) Chatham Islands',
+'UTC+13:00'					=>	'(UTC+13:00) Tonga, Phoenix Islands',
+'UTC+14:00'					=>	'(UTC+14:00) Line Islands'
+
+);
diff --git a/lang/English/profile.php b/lang/English/profile.php
new file mode 100644
index 0000000..69c993c
--- /dev/null
+++ b/lang/English/profile.php
@@ -0,0 +1,137 @@
+<?php
+
+// Language definitions used in profile.php
+$lang_profile = array(
+
+// Navigation and sections
+'Profile menu'					=>	'Profile menu',
+'Section essentials'			=>	'Essentials',
+'Section personal'				=>	'Personal',
+'Section messaging'				=>	'Messaging',
+'Section personality'			=>	'Personality',
+'Section display'				=>	'Display',
+'Section privacy'				=>	'Privacy',
+'Section admin'					=>	'Administration',
+
+// Miscellaneous
+'Username and pass legend'		=>	'Enter your username and password',
+'Personal details legend'		=>	'Enter your personal details',
+'Contact details legend'		=>	'Enter your messaging details',
+'User activity'					=>	'User activity',
+'Paginate info'					=>	'Enter the number of topics and posts you wish to view on each page.',
+
+// Password stuff
+'Pass key bad'					=>	'The specified password activation key was incorrect or has expired. Please re-request a new password. If that fails, contact the forum administrator at',
+'Pass updated'					=>	'Your password has been updated. You can now login with your new password.',
+'Pass updated redirect'			=>	'Password updated. Redirecting …',
+'Wrong pass'					=>	'Wrong old password.',
+'Change pass'					=>	'Change password',
+'Change pass legend'			=>	'Enter and confirm your new password',
+'Old pass'						=>	'Old password',
+'New pass'						=>	'New password',
+'Confirm new pass'				=>	'Confirm new password',
+'Pass info'						=>	'Passwords must be at least 4 characters long. Passwords are case sensitive.',
+
+// Email stuff
+'Email key bad'					=>	'The specified email activation key was incorrect or has expired. Please re-request change of email address. If that fails, contact the forum administrator at',
+'Email updated'					=>	'Your email address has been updated.',
+'Activate email sent'			=>	'An email has been sent to the specified address with instructions on how to activate the new email address. If it doesn\'t arrive you can contact the forum administrator at',
+'Email legend'					=>	'Enter your new email address',
+'Email instructions'			=>	'An email will be sent to your new address with an activation link. You must click the link in the email you receive to activate the new address.',
+'Change email'					=>	'Change email address',
+'New email'						=>	'New email',
+
+// Avatar upload stuff
+'Avatars disabled'				=>	'The administrator has disabled avatar support.',
+'Too large ini'					=>	'The selected file was too large to upload. The server didn\'t allow the upload.',
+'Partial upload'				=>	'The selected file was only partially uploaded. Please try again.',
+'No tmp directory'				=>	'PHP was unable to save the uploaded file to a temporary location.',
+'No file'						=>	'You did not select a file for upload.',
+'Bad type'						=>	'The file you tried to upload is not of an allowed type. Allowed types are gif, jpeg and png.',
+'Too wide or high'				=>	'The file you tried to upload is wider and/or higher than the maximum allowed',
+'Too large'						=>	'The file you tried to upload is larger than the maximum allowed',
+'pixels'						=>	'pixels',
+'bytes'							=>	'bytes',
+'Move failed'					=>	'The server was unable to save the uploaded file. Please contact the forum administrator at',
+'Unknown failure'				=>	'An unknown error occurred. Please try again.',
+'Avatar upload redirect'		=>	'Avatar uploaded. Redirecting …',
+'Avatar deleted redirect'		=>	'Avatar deleted. Redirecting …',
+'Avatar desc'					=>	'An avatar is a small image that will be displayed under your username in your posts. It must not be any bigger than',
+'Upload avatar'					=>	'Upload avatar',
+'Upload avatar legend'			=>	'Enter an avatar file to upload',
+'Delete avatar'					=>	'Delete avatar', // only for admins
+'File'							=>	'File',
+'Upload'						=>	'Upload', // submit button
+
+// Form validation stuff
+'Forbidden title'				=>	'The title you entered contains a forbidden word. You must choose a different title.',
+'Profile redirect'				=>	'Profile updated. Redirecting …',
+
+// Profile display stuff
+'Users profile'					=>	'%s\'s profile',
+'Username info'					=>	'Username: %s',
+'Email info'					=>	'Email: %s',
+'Posts info'					=>	'Posts: %s',
+'Registered info'				=>	'Registered: %s',
+'Last post info'				=>	'Last post: %s',
+'Show posts'					=>	'Show all posts',
+'Realname'						=>	'Real name',
+'Location'						=>	'Location',
+'Website'						=>	'Website',
+'Jabber'						=>	'Jabber',
+'ICQ'							=>	'ICQ',
+'MSN'							=>	'MSN Messenger',
+'AOL IM'						=>	'AOL IM',
+'Yahoo'							=>	'Yahoo! Messenger',
+'Avatar'						=>	'Avatar',
+'Signature'						=>	'Signature',
+'Sig max size'					=>	'Max length: %s characters / Max lines: %s',
+'Avatar legend'					=>	'Set your avatar display options',
+'Avatar info'					=>	'An avatar is a small image that will be displayed with all your posts. You can upload an avatar by clicking the link below.',
+'Change avatar'					=>	'Change avatar',
+'Signature legend'				=>	'Compose your signature',
+'Signature info'				=>	'A signature is a small piece of text that is attached to your posts. In it, you can enter just about anything you like. Perhaps you would like to enter your favourite quote or your star sign. It\'s up to you! In your signature you can use BBCode if it is allowed in this particular forum. You can see the features that are allowed/enabled listed below whenever you edit your signature.',
+'Sig preview'					=>	'Current signature preview:',
+'No sig'						=>	'No signature currently stored in profile.',
+'Signature quote/code/list/h'	=>	'The quote, code, list, and heading BBCodes are not allowed in signatures.',
+'Topics per page'				=>	'Topics',
+'Posts per page'				=>	'Posts',
+'Leave blank'					=>	'Leave blank to use forum default.',
+'Subscription legend'			=>	'Set your subscription options',
+'Notify full'					=>	'Include a plain text version of new posts in subscription notification emails.',
+'Auto notify full'				=>	'Automatically subscribe to every topic you post in.',
+'Show smilies'					=>	'Show smilies as graphic icons.',
+'Show images'					=>	'Show images in posts.',
+'Show images sigs'				=>	'Show images in user signatures.',
+'Show avatars'					=>	'Show user avatars in posts.',
+'Show sigs'						=>	'Show user signatures.',
+'Style legend'					=>	'Select your preferred style',
+'Styles'						=>	'Styles',
+'Admin note'					=>	'Admin note',
+'Pagination legend'				=>	'Enter your pagination options',
+'Post display legend'			=>	'Set your options for viewing posts',
+'Post display info'				=>	'If you are on a slow connection, disabling these options, particularly showing images in posts and signatures, will make pages load faster.',
+'Instructions'					=>	'When you update your profile, you will be redirected back to this page.',
+
+// Administration stuff
+'Group membership legend'		=>	'Choose user group',
+'Save'							=>	'Save',
+'Set mods legend'				=>	'Set moderator access',
+'Moderator in info'				=>	'Choose which forums this user should be allowed to moderate. Note: This only applies to moderators. Administrators always have full permissions in all forums.',
+'Update forums'					=>	'Update forums',
+'Delete ban legend'				=>	'Delete (administrators only) or ban user',
+'Delete user'					=>	'Delete user',
+'Ban user'						=>	'Ban user',
+'Confirm delete legend'			=>	'Important: read before deleting user',
+'Confirm delete user'			=>	'Confirm delete user',
+'Confirmation info'				=>	'Please confirm that you want to delete the user', // the username will be appended to this string
+'Delete warning'				=>	'Warning! Deleted users and/or posts cannot be restored. If you choose not to delete the posts made by this user, the posts can only be deleted manually at a later time.',
+'Delete posts'					=>	'Delete any posts and topics this user has made.',
+'Delete'						=>	'Delete', // submit button (confirm user delete)
+'User delete redirect'			=>	'User deleted. Redirecting …',
+'Group membership redirect'		=>	'Group membership saved. Redirecting …',
+'Update forums redirect'		=>	'Forum moderator rights updated. Redirecting …',
+'Ban redirect'					=>	'Redirecting …',
+'No delete admin message'		=>	'Administrators cannot be deleted. In order to delete this user, you must first move him/her to a different user group.',
+
+);
diff --git a/lang/English/register.php b/lang/English/register.php
new file mode 100644
index 0000000..c3e24df
--- /dev/null
+++ b/lang/English/register.php
@@ -0,0 +1,37 @@
+<?php
+
+// Language definitions used in register.php
+$lang_register = array(
+
+// Miscellaneous
+'No new regs'				=>	'This forum is not accepting new registrations.',
+'Reg cancel redirect'		=>	'Registration cancelled. Redirecting …',
+'Forum rules'				=>	'Forum rules',
+'Rules legend'				=>	'You must agree to the following in order to register',
+'Registration flood'		=>	'A new user was registered with the same IP address as you within the last hour. To prevent registration flooding, at least an hour has to pass between registrations from the same IP. Sorry for the inconvenience.',
+'Agree'						=>	'Agree',
+'Cancel'					=>	'Cancel',
+'Register'					=>	'Register',
+
+// Form validation stuff (some of these are also used in post.php)
+'Registration errors'		=>	'Registration errors',
+'Registration errors info'	=>	'The following errors need to be corrected before you can register:',
+'Username censor'			=>	'The username you entered contains one or more censored words. Please choose a different username.',
+'Username dupe 1'			=>	'Someone is already registered with the username',
+'Username dupe 2'			=>	'The username you entered is too similar. The username must differ from that by at least one alphanumerical character (a-z or 0-9). Please choose a different username.',
+'Email not match'			=>	'Email addresses do not match.',
+
+// Registration email stuff
+'Reg email'					=>	'Thank you for registering. Your password has been sent to the specified address. If it doesn\'t arrive you can contact the forum administrator at',
+'Reg complete'				=>	'Registration complete. Logging in and redirecting …',
+
+// Register info
+'Desc 1'					=>	'Registration will grant you access to a number of features and capabilities otherwise unavailable. These functions include the ability to edit and delete posts, design your own signature that accompanies your posts and much more. If you have any questions regarding this forum you should ask an administrator.',
+'Desc 2'					=>	'Below is a form you must fill out in order to register. Once you are registered you should visit your profile and review the different settings you can change. The fields below only make up a small part of all the settings you can alter in your profile.',
+'Username legend'			=>	'Please enter a username between 2 and 25 characters long',
+'Pass legend'				=>	'Please enter and confirm your chosen password',
+'Pass info'					=>	'Passwords must be at least 4 characters long. Passwords are case sensitive.',
+'Email info'				=>	'You must enter a valid email address as your randomly generated password will be sent to that address.',
+'Confirm email'				=>	'Confirm email address',
+
+);
diff --git a/lang/English/search.php b/lang/English/search.php
new file mode 100644
index 0000000..d6a5081
--- /dev/null
+++ b/lang/English/search.php
@@ -0,0 +1,49 @@
+<?php
+
+// Language definitions used in search.php
+$lang_search = array(
+
+// The search form
+'User search'				=>	'User search',
+'No search permission'		=>	'You do not have permission to use the search feature.',
+'Search flood'				=>	'At least %s seconds have to pass between searches. Please wait a while and try searching again.',
+'Search'					=>	'Search',
+'Search criteria legend'	=>	'Enter your search criteria',
+'Search info'				=>	'To search by keyword, enter a term or terms to search for. Separate terms with spaces. Use AND, OR and NOT to refine your search. To search by author enter the username of the author whose posts you wish to search for. Use wildcard character * for partial matches.',
+'Keyword search'			=>	'Keyword search',
+'Author search'				=>	'Author search',
+'Search in legend'			=>	'Select where to search',
+'Search in info'			=>	'Choose in which forum you would like to search and if you want to search in topic subjects, message text or both.',
+'Forum search'				=>	'Forum',
+'All forums'				=>	'All forums',
+'Search in'					=>	'Search in',
+'Message and subject'		=>	'Message text and topic subject',
+'Message only'				=>	'Message text only',
+'Topic only'				=>	'Topic subject only',
+'Sort by'					=>	'Sort by',
+'Sort order'				=>	'Sort order',
+'Search results legend'		=>	'Select how to view search results',
+'Search results info'		=>	'You can choose how you wish to sort and show your results.',
+'Sort by post time'			=>	'Post time',
+'Sort by author'			=>	'Author',
+'Sort by subject'			=>	'Subject',
+'Sort by forum'				=>	'Forum',
+'Ascending'					=>	'Ascending',
+'Descending'				=>	'Descending',
+'Show as'					=>	'Show results as',
+'Show as topics'			=>	'Topics',
+'Show as posts'				=>	'Posts',
+
+// Results
+'Search results'			=>	'Search results',
+'No terms'					=>	'You have to enter at least one keyword and/or an author to search for.',
+'No hits'					=>	'Your search returned no hits.',
+'No user posts'				=>	'There are no posts by this user in this forum.',
+'No subscriptions'			=>	'You are currently not subscribed to any topics.',
+'No new posts'				=>	'There are no topics with new posts since your last visit.',
+'No recent posts'			=>	'No new posts have been made within the last 24 hours.',
+'No unanswered'				=>	'There are no unanswered posts in this forum.',
+'Go to post'				=>	'Go to post',
+'Go to topic'				=>	'Go to topic'
+
+);
diff --git a/lang/English/stopwords.txt b/lang/English/stopwords.txt
new file mode 100644
index 0000000..907a260
--- /dev/null
+++ b/lang/English/stopwords.txt
@@ -0,0 +1,175 @@
+about
+after
+ago
+all
+almost
+along
+also
+any
+anybody
+anywhere
+are
+arent
+aren't
+around
+ask
+been
+before
+being
+between
+but
+came
+can
+cant
+can't
+come
+could
+couldnt
+couldn't
+did
+didnt
+didn't
+does
+doesnt
+doesn't
+dont
+don't
+each
+either
+else
+even
+every
+everybody
+everyone
+find
+for
+from
+get
+going
+gone
+got
+had
+has
+have
+havent
+haven't
+having
+her
+here
+hers
+him
+his
+how
+ill
+i'll
+i'm
+into
+isnt
+isn't
+itll
+it'll
+its
+it's
+ive
+i've
+just
+know
+less
+like
+make
+many
+may
+more
+most
+much
+must
+near
+never
+none
+nothing
+now
+off
+often
+once
+one
+only
+other
+our
+ours
+our's
+out
+over
+please
+rather
+really
+said
+see
+she
+should
+small
+some
+something
+sometime
+somewhere
+take
+than
+thank
+thanks
+that
+thats
+that's
+the
+their
+theirs
+them
+then
+there
+these
+they
+thing
+think
+this
+those
+though
+through
+thus
+too
+true
+two
+under
+until
+upon
+use
+very
+want
+was
+way
+well
+were
+what
+when
+where
+which
+who
+whom
+whose
+why
+will
+with
+within
+without
+would
+yes
+yet
+you
+your
+youre
+you're
+yours
+http
+https
+ftp
+www
+com
+net
+org
diff --git a/lang/English/topic.php b/lang/English/topic.php
new file mode 100644
index 0000000..1f74dda
--- /dev/null
+++ b/lang/English/topic.php
@@ -0,0 +1,31 @@
+<?php
+
+// Language definitions used in viewtopic.php
+$lang_topic = array(
+
+'Post reply'		=>	'Post reply',
+'Topic closed'		=>	'Topic closed',
+'From'				=>	'From:', // User location
+'IP address logged'	=>	'IP address logged',
+'Note'				=>	'Note:', // Admin note
+'Posts'				=>	'Posts:',
+'Registered'		=>	'Registered:',
+'Replies'			=>	'Replies:',
+'Website'			=>	'Website',
+'Guest'				=>	'Guest',
+'Online'			=>	'Online',
+'Offline'			=>	'Offline',
+'Last edit'			=>	'Last edited by',
+'Report'			=>	'Report',
+'Delete'			=>	'Delete',
+'Edit'				=>	'Edit',
+'Quote'				=>	'Quote',
+'Is subscribed'		=>	'You are currently subscribed to this topic',
+'Unsubscribe'		=>	'Unsubscribe',
+'Subscribe'			=>	'Subscribe to this topic',
+'Quick post'		=>	'Quick post',
+'Mod controls'		=>	'Moderator controls',
+'New icon'			=>	'New post',
+'Re'				=>	'Re:'
+
+);
diff --git a/lang/English/userlist.php b/lang/English/userlist.php
new file mode 100644
index 0000000..0853d25
--- /dev/null
+++ b/lang/English/userlist.php
@@ -0,0 +1,13 @@
+<?php
+
+// Language definitions used in userlist.php
+$lang_ul = array(
+
+'User find legend'	=>	'Find and sort users',
+'User search info'	=>	'Enter a username to search for and/or a user group to filter by. The username field can be left blank. Use the wildcard character * for partial matches.',
+'User sort info'	=>	'Sort users by name, date registered or number of posts and in ascending/descending order.',
+'User group'		=>	'User group',
+'No of posts'		=>	'Number of posts',
+'All users'			=>	'All'
+
+);
diff --git a/lang/index.html b/lang/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/lang/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/login.php b/login.php
new file mode 100644
index 0000000..0f899c4
--- /dev/null
+++ b/login.php
@@ -0,0 +1,263 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+if (isset($_GET['action']))
+	define('PUN_QUIET_VISIT', 1);
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+
+
+// Load the login.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/login.php';
+
+$action = isset($_GET['action']) ? $_GET['action'] : null;
+
+if (isset($_POST['form_sent']) && $action == 'in')
+{
+	$form_username = pun_trim($_POST['req_username']);
+	$form_password = pun_trim($_POST['req_password']);
+	$save_pass = isset($_POST['save_pass']);
+
+	$username_sql = ($db_type == 'mysql' || $db_type == 'mysqli' || $db_type == 'mysql_innodb' || $db_type == 'mysqli_innodb') ? 'username=\''.$db->escape($form_username).'\'' : 'LOWER(username)=LOWER(\''.$db->escape($form_username).'\')';
+
+	$result = $db->query('SELECT * FROM '.$db->prefix.'users WHERE '.$username_sql) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+	$cur_user = $db->fetch_assoc($result);
+
+	$authorized = false;
+
+	if (!empty($cur_user['password']))
+	{
+		$form_password_hash = pun_hash($form_password); // Will result in a SHA-1 hash
+
+		// If there is a salt in the database we have upgraded from 1.3-legacy though havent yet logged in
+		if (!empty($cur_user['salt']))
+		{
+			if (sha1($cur_user['salt'].sha1($form_password)) == $cur_user['password']) // 1.3 used sha1(salt.sha1(pass))
+			{
+				$authorized = true;
+
+				$db->query('UPDATE '.$db->prefix.'users SET password=\''.$form_password_hash.'\', salt=NULL WHERE id='.$cur_user['id']) or error('Unable to update user password', __FILE__, __LINE__, $db->error());
+			}
+		}
+		// If the length isn't 40 then the password isn't using sha1, so it must be md5 from 1.2
+		else if (strlen($cur_user['password']) != 40)
+		{
+			if (md5($form_password) == $cur_user['password'])
+			{
+				$authorized = true;
+
+				$db->query('UPDATE '.$db->prefix.'users SET password=\''.$form_password_hash.'\' WHERE id='.$cur_user['id']) or error('Unable to update user password', __FILE__, __LINE__, $db->error());
+			}
+		}
+		// Otherwise we should have a normal sha1 password
+		else
+			$authorized = ($cur_user['password'] == $form_password_hash);
+	}
+
+	if (!$authorized)
+		message($lang_login['Wrong user/pass'].' <a href="login.php?action=forget">'.$lang_login['Forgotten pass'].'</a>');
+
+	// Update the status if this is the first time the user logged in
+	if ($cur_user['group_id'] == PUN_UNVERIFIED)
+		$db->query('UPDATE '.$db->prefix.'users SET group_id='.$pun_config['o_default_user_group'].' WHERE id='.$cur_user['id']) or error('Unable to update user status', __FILE__, __LINE__, $db->error());
+
+	// Remove this users guest entry from the online list
+	$db->query('DELETE FROM '.$db->prefix.'online WHERE ident=\''.$db->escape(get_remote_address()).'\'') or error('Unable to delete from online list', __FILE__, __LINE__, $db->error());
+
+	$expire = ($save_pass == '1') ? time() + 1209600 : time() + $pun_config['o_timeout_visit'];
+	pun_setcookie($cur_user['id'], $form_password_hash, $expire);
+
+	// Reset tracked topics
+	set_tracked_topics(null);
+
+	redirect(htmlspecialchars($_POST['redirect_url']), $lang_login['Login redirect']);
+}
+
+
+else if ($action == 'out')
+{
+	if ($pun_user['is_guest'] || !isset($_GET['id']) || $_GET['id'] != $pun_user['id'] || !isset($_GET['csrf_token']) || $_GET['csrf_token'] != pun_hash($pun_user['id'].pun_hash(get_remote_address())))
+	{
+		header('Location: index.php');
+		exit;
+	}
+
+	// Remove user from "users online" list
+	$db->query('DELETE FROM '.$db->prefix.'online WHERE user_id='.$pun_user['id']) or error('Unable to delete from online list', __FILE__, __LINE__, $db->error());
+
+	// Update last_visit (make sure there's something to update it with)
+	if (isset($pun_user['logged']))
+		$db->query('UPDATE '.$db->prefix.'users SET last_visit='.$pun_user['logged'].' WHERE id='.$pun_user['id']) or error('Unable to update user visit data', __FILE__, __LINE__, $db->error());
+
+	pun_setcookie(1, md5(uniqid(rand(), true)), time() + 31536000);
+
+	redirect('index.php', $lang_login['Logout redirect']);
+}
+
+
+else if ($action == 'forget' || $action == 'forget_2')
+{
+	if (!$pun_user['is_guest'])
+		header('Location: index.php');
+
+	if (isset($_POST['form_sent']))
+	{
+		// Start with a clean slate
+		$errors = array();
+
+		require PUN_ROOT.'include/email.php';
+
+		// Validate the email address
+		$email = strtolower(trim($_POST['req_email']));
+		if (!is_valid_email($email))
+			$errors[] = $lang_common['Invalid email'];
+
+		// Did everything go according to plan?
+		if (empty($errors))
+		{
+			$result = $db->query('SELECT id, username, last_email_sent FROM '.$db->prefix.'users WHERE email=\''.$db->escape($email).'\'') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+
+			if ($db->num_rows($result))
+			{
+				// Load the "activate password" template
+				$mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/activate_password.tpl'));
+
+				// The first row contains the subject
+				$first_crlf = strpos($mail_tpl, "\n");
+				$mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
+				$mail_message = trim(substr($mail_tpl, $first_crlf));
+
+				// Do the generic replacements first (they apply to all emails sent out here)
+				$mail_message = str_replace('<base_url>', $pun_config['o_base_url'].'/', $mail_message);
+				$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'].' '.$lang_common['Mailer'], $mail_message);
+
+				// Loop through users we found
+				while ($cur_hit = $db->fetch_assoc($result))
+				{
+					if ($cur_hit['last_email_sent'] != '' && (time() - $cur_hit['last_email_sent']) < 3600 && (time() - $cur_hit['last_email_sent']) >= 0)
+						message($lang_login['Email flood'], true);
+
+					// Generate a new password and a new password activation code
+					$new_password = random_pass(8);
+					$new_password_key = random_pass(8);
+
+					$db->query('UPDATE '.$db->prefix.'users SET activate_string=\''.pun_hash($new_password).'\', activate_key=\''.$new_password_key.'\', last_email_sent = '.time().' WHERE id='.$cur_hit['id']) or error('Unable to update activation data', __FILE__, __LINE__, $db->error());
+
+					// Do the user specific replacements to the template
+					$cur_mail_message = str_replace('<username>', $cur_hit['username'], $mail_message);
+					$cur_mail_message = str_replace('<activation_url>', $pun_config['o_base_url'].'/profile.php?id='.$cur_hit['id'].'&action=change_pass&key='.$new_password_key, $cur_mail_message);
+					$cur_mail_message = str_replace('<new_password>', $new_password, $cur_mail_message);
+
+					pun_mail($email, $mail_subject, $cur_mail_message);
+				}
+
+				message($lang_login['Forget mail'].' <a href="mailto:'.$pun_config['o_admin_email'].'">'.$pun_config['o_admin_email'].'</a>.', true);
+			}
+			else
+				$errors[] = $lang_login['No email match'].' '.htmlspecialchars($email).'.';
+			}
+		}
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_login['Request pass']);
+	$required_fields = array('req_email' => $lang_common['Email']);
+	$focus_element = array('request_pass', 'req_email');
+	define ('PUN_ACTIVE_PAGE', 'login');
+	require PUN_ROOT.'header.php';
+
+// If there are errors, we display them
+if (!empty($errors))
+{
+
+?>
+<div id="posterror" class="block">
+	<h2><span><?php echo $lang_login['New password errors'] ?></span></h2>
+	<div class="box">
+		<div class="inbox error-info">
+			<p><?php echo $lang_login['New passworderrors info'] ?></p>
+			<ul class="error-list">
+<?php
+
+	foreach ($errors as $cur_error)
+		echo "\t\t\t\t".'<li><strong>'.$cur_error.'</strong></li>'."\n";
+?>
+			</ul>
+		</div>
+	</div>
+</div>
+
+<?php
+
+}
+?>
+<div class="blockform">
+	<h2><span><?php echo $lang_login['Request pass'] ?></span></h2>
+	<div class="box">
+		<form id="request_pass" method="post" action="login.php?action=forget_2" onsubmit="this.request_pass.disabled=true;if(process_form(this)){return true;}else{this.request_pass.disabled=false;return false;}">
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_login['Request pass legend'] ?></legend>
+					<div class="infldset">
+						<input type="hidden" name="form_sent" value="1" />
+						<label class="required"><strong><?php echo $lang_common['Email'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input id="req_email" type="text" name="req_email" size="50" maxlength="80" /><br /></label>
+						<p><?php echo $lang_login['Request pass info'] ?></p>
+					</div>
+				</fieldset>
+			</div>
+			<p class="buttons"><input type="submit" name="request_pass" value="<?php echo $lang_common['Submit'] ?>" /><?php if (empty($errors)): ?> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a><?php endif; ?></p>
+		</form>
+	</div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+
+if (!$pun_user['is_guest'])
+	header('Location: index.php');
+
+// Try to determine if the data in HTTP_REFERER is valid (if not, we redirect to index.php after login)
+$redirect_url = (isset($_SERVER['HTTP_REFERER']) && preg_match('#^'.preg_quote($pun_config['o_base_url']).'/(.*?)\.php#i', $_SERVER['HTTP_REFERER'])) ? htmlspecialchars($_SERVER['HTTP_REFERER']) : $pun_config['o_base_url'].'/index.php';
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Login']);
+$required_fields = array('req_username' => $lang_common['Username'], 'req_password' => $lang_common['Password']);
+$focus_element = array('login', 'req_username');
+define('PUN_ACTIVE_PAGE', 'login');
+require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+	<h2><span><?php echo $lang_common['Login'] ?></span></h2>
+	<div class="box">
+		<form id="login" method="post" action="login.php?action=in" onsubmit="return process_form(this)">
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_login['Login legend'] ?></legend>
+					<div class="infldset">
+						<input type="hidden" name="form_sent" value="1" />
+						<input type="hidden" name="redirect_url" value="<?php echo $redirect_url ?>" />
+						<label class="conl required"><strong><?php echo $lang_common['Username'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="text" name="req_username" size="25" maxlength="25" tabindex="1" /><br /></label>
+						<label class="conl required"><strong><?php echo $lang_common['Password'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="password" name="req_password" size="25" tabindex="2" /><br /></label>
+
+						<div class="rbox clearb">
+							<label><input type="checkbox" name="save_pass" value="1" tabindex="3" /><?php echo $lang_login['Remember me'] ?><br /></label>
+						</div>
+
+						<p class="clearb"><?php echo $lang_login['Login info'] ?></p>
+						<p class="actions"><span><a href="register.php" tabindex="4"><?php echo $lang_login['Not registered'] ?></a></span> <span><a href="login.php?action=forget" tabindex="5"><?php echo $lang_login['Forgotten pass'] ?></a></span></p>
+					</div>
+				</fieldset>
+			</div>
+			<p class="buttons"><input type="submit" name="login" value="<?php echo $lang_common['Login'] ?>" tabindex="3" /></p>
+		</form>
+	</div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/misc.php b/misc.php
new file mode 100644
index 0000000..7b31df2
--- /dev/null
+++ b/misc.php
@@ -0,0 +1,334 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+if (isset($_GET['action']))
+	define('PUN_QUIET_VISIT', 1);
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+
+
+// Load the misc.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/misc.php';
+
+$action = isset($_GET['action']) ? $_GET['action'] : null;
+
+
+if ($action == 'rules')
+{
+	if ($pun_config['o_rules'] == '0' || ($pun_user['is_guest'] && $pun_user['g_read_board'] == '0' && $pun_config['o_regs_allow'] == '0'))
+		message($lang_common['Bad request']);
+
+	// Load the register.php language file
+	require PUN_ROOT.'lang/'.$pun_user['language'].'/register.php';
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_register['Forum rules']);
+	define('PUN_ACTIVE_PAGE', 'rules');
+	require PUN_ROOT.'header.php';
+
+?>
+<div id="rules" class="block">
+	<div class="hd"><h2><span><?php echo $lang_register['Forum rules'] ?></span></h2></div>
+	<div class="box">
+		<div id="rules-block" class="inbox">
+			<div class="usercontent"><?php echo $pun_config['o_rules_message'] ?></div>
+		</div>
+	</div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+
+else if ($action == 'markread')
+{
+	if ($pun_user['is_guest'])
+		message($lang_common['No permission']);
+
+	$db->query('UPDATE '.$db->prefix.'users SET last_visit='.$pun_user['logged'].' WHERE id='.$pun_user['id']) or error('Unable to update user last visit data', __FILE__, __LINE__, $db->error());
+
+	// Reset tracked topics
+	set_tracked_topics(null);
+
+	redirect('index.php', $lang_misc['Mark read redirect']);
+}
+
+
+// Mark the topics/posts in a forum as read?
+else if ($action == 'markforumread')
+{
+	if ($pun_user['is_guest'])
+		message($lang_common['No permission']);
+
+	$fid = isset($_GET['fid']) ? intval($_GET['fid']) : 0;
+	if ($fid < 1)
+		message($lang_common['Bad request']);
+
+	$tracked_topics = get_tracked_topics();
+	$tracked_topics['forums'][$fid] = time();
+	set_tracked_topics($tracked_topics);
+
+	redirect('viewforum.php?id='.$fid, $lang_misc['Mark forum read redirect']);
+}
+
+
+else if (isset($_GET['email']))
+{
+	if ($pun_user['is_guest'] || $pun_user['g_send_email'] == '0')
+		message($lang_common['No permission']);
+
+	$recipient_id = intval($_GET['email']);
+	if ($recipient_id < 2)
+		message($lang_common['Bad request']);
+
+	$result = $db->query('SELECT username, email, email_setting FROM '.$db->prefix.'users WHERE id='.$recipient_id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+	if (!$db->num_rows($result))
+		message($lang_common['Bad request']);
+
+	list($recipient, $recipient_email, $email_setting) = $db->fetch_row($result);
+
+	if ($email_setting == 2 && !$pun_user['is_admmod'])
+		message($lang_misc['Form email disabled']);
+
+
+	if (isset($_POST['form_sent']))
+	{
+		// Clean up message and subject from POST
+		$subject = pun_trim($_POST['req_subject']);
+		$message = pun_trim($_POST['req_message']);
+
+		if ($subject == '')
+			message($lang_misc['No email subject']);
+		else if ($message == '')
+			message($lang_misc['No email message']);
+		else if (pun_strlen($message) > PUN_MAX_POSTSIZE)
+			message($lang_misc['Too long email message']);
+
+		if ($pun_user['last_email_sent'] != '' && (time() - $pun_user['last_email_sent']) < $pun_user['g_email_flood'] && (time() - $pun_user['last_email_sent']) >= 0)
+			message(sprintf($lang_misc['Email flood'], $pun_user['g_email_flood']));
+
+		// Load the "form email" template
+		$mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/form_email.tpl'));
+
+		// The first row contains the subject
+		$first_crlf = strpos($mail_tpl, "\n");
+		$mail_subject = pun_trim(substr($mail_tpl, 8, $first_crlf-8));
+		$mail_message = pun_trim(substr($mail_tpl, $first_crlf));
+
+		$mail_subject = str_replace('<mail_subject>', $subject, $mail_subject);
+		$mail_message = str_replace('<sender>', $pun_user['username'], $mail_message);
+		$mail_message = str_replace('<board_title>', $pun_config['o_board_title'], $mail_message);
+		$mail_message = str_replace('<mail_message>', $message, $mail_message);
+		$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'].' '.$lang_common['Mailer'], $mail_message);
+
+		require_once PUN_ROOT.'include/email.php';
+
+		pun_mail($recipient_email, $mail_subject, $mail_message, $pun_user['email'], $pun_user['username']);
+
+		$db->query('UPDATE '.$db->prefix.'users SET last_email_sent='.time().' WHERE id='.$pun_user['id']) or error('Unable to update user', __FILE__, __LINE__, $db->error());
+
+		redirect(htmlspecialchars($_POST['redirect_url']), $lang_misc['Email sent redirect']);
+	}
+
+
+	// Try to determine if the data in HTTP_REFERER is valid (if not, we redirect to the users profile after the email is sent)
+	$redirect_url = (isset($_SERVER['HTTP_REFERER']) && preg_match('#^'.preg_quote($pun_config['o_base_url']).'/(.*?)\.php#i', $_SERVER['HTTP_REFERER'])) ? htmlspecialchars($_SERVER['HTTP_REFERER']) : 'index.php';
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_misc['Send email to'].' '.pun_htmlspecialchars($recipient));
+	$required_fields = array('req_subject' => $lang_misc['Email subject'], 'req_message' => $lang_misc['Email message']);
+	$focus_element = array('email', 'req_subject');
+	define('PUN_ACTIVE_PAGE', 'index');
+	require PUN_ROOT.'header.php';
+
+?>
+<div id="emailform" class="blockform">
+	<h2><span><?php echo $lang_misc['Send email to'] ?> <?php echo pun_htmlspecialchars($recipient) ?></span></h2>
+	<div class="box">
+		<form id="email" method="post" action="misc.php?email=<?php echo $recipient_id ?>" onsubmit="this.submit.disabled=true;if(process_form(this)){return true;}else{this.submit.disabled=false;return false;}">
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_misc['Write email'] ?></legend>
+					<div class="infldset txtarea">
+						<input type="hidden" name="form_sent" value="1" />
+						<input type="hidden" name="redirect_url" value="<?php echo $redirect_url ?>" />
+						<label class="required"><strong><?php echo $lang_misc['Email subject'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
+						<input class="longinput" type="text" name="req_subject" size="75" maxlength="70" tabindex="1" /><br /></label>
+						<label class="required"><strong><?php echo $lang_misc['Email message'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
+						<textarea name="req_message" rows="10" cols="75" tabindex="2"></textarea><br /></label>
+						<p><?php echo $lang_misc['Email disclosure note'] ?></p>
+					</div>
+				</fieldset>
+			</div>
+			<p class="buttons"><input type="submit" name="submit" value="<?php echo $lang_common['Submit'] ?>" tabindex="3" accesskey="s" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+		</form>
+	</div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+
+else if (isset($_GET['report']))
+{
+	if ($pun_user['is_guest'])
+		message($lang_common['No permission']);
+
+	$post_id = intval($_GET['report']);
+	if ($post_id < 1)
+		message($lang_common['Bad request']);
+
+	if (isset($_POST['form_sent']))
+	{
+		// Clean up reason from POST
+		$reason = pun_linebreaks(pun_trim($_POST['req_reason']));
+		if ($reason == '')
+			message($lang_misc['No reason']);
+		else if (strlen($reason) > 65535) // TEXT field can only hold 65535 bytes
+			message($lang_misc['Reason too long']);
+
+		if ($pun_user['last_email_sent'] != '' && (time() - $pun_user['last_email_sent']) < $pun_user['g_email_flood'] && (time() - $pun_user['last_email_sent']) >= 0)
+			message(sprintf($lang_misc['Report flood'], $pun_user['g_email_flood']));
+
+		// Get the topic ID
+		$result = $db->query('SELECT topic_id FROM '.$db->prefix.'posts WHERE id='.$post_id) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+		if (!$db->num_rows($result))
+			message($lang_common['Bad request']);
+
+		$topic_id = $db->result($result);
+
+		// Get the subject and forum ID
+		$result = $db->query('SELECT subject, forum_id FROM '.$db->prefix.'topics WHERE id='.$topic_id) or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
+		if (!$db->num_rows($result))
+			message($lang_common['Bad request']);
+
+		list($subject, $forum_id) = $db->fetch_row($result);
+
+		// Should we use the internal report handling?
+		if ($pun_config['o_report_method'] == '0' || $pun_config['o_report_method'] == '2')
+			$db->query('INSERT INTO '.$db->prefix.'reports (post_id, topic_id, forum_id, reported_by, created, message) VALUES('.$post_id.', '.$topic_id.', '.$forum_id.', '.$pun_user['id'].', '.time().', \''.$db->escape($reason).'\')' ) or error('Unable to create report', __FILE__, __LINE__, $db->error());
+
+		// Should we email the report?
+		if ($pun_config['o_report_method'] == '1' || $pun_config['o_report_method'] == '2')
+		{
+			// We send it to the complete mailing-list in one swoop
+			if ($pun_config['o_mailing_list'] != '')
+			{
+				$mail_subject = sprintf($lang_common['Report notification'], $forum_id, $subject);
+				$mail_message = sprintf($lang_common['Report message 1'], $pun_user['username'], $pun_config['o_base_url'].'/viewtopic.php?pid='.$post_id.'#p'.$post_id)."\n";
+				$mail_message .= sprintf($lang_common['Report message 2'], $reason)."\n";
+				$mail_message .= "\n".'--'."\n".$lang_common['Email signature'];
+
+				require PUN_ROOT.'include/email.php';
+
+				pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
+			}
+		}
+
+		$db->query('UPDATE '.$db->prefix.'users SET last_email_sent='.time().' WHERE id='.$pun_user['id']) or error('Unable to update user', __FILE__, __LINE__, $db->error());
+
+		redirect('viewtopic.php?pid='.$post_id.'#p'.$post_id, $lang_misc['Report redirect']);
+	}
+
+	// Fetch some info about the post, the topic and the forum
+	$result = $db->query('SELECT f.id AS fid, f.forum_name, t.id AS tid, t.subject FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND p.id='.$post_id) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+	if (!$db->num_rows($result))
+		message($lang_common['Bad request']);
+
+	$cur_post = $db->fetch_assoc($result);
+
+	if ($pun_config['o_censoring'] == '1')
+		$cur_post['subject'] = censor_words($cur_post['subject']);
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_misc['Report post']);
+	$required_fields = array('req_reason' => $lang_misc['Reason']);
+	$focus_element = array('report', 'req_reason');
+	define('PUN_ACTIVE_PAGE', 'index');
+	require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+	<div class="inbox">
+		<ul class="crumbs">
+			<li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+			<li><span>» </span><a href="viewforum.php?id=<?php echo $cur_post['fid'] ?>"><?php echo pun_htmlspecialchars($cur_post['forum_name']) ?></a></li>
+			<li><span>» </span><a href="viewtopic.php?pid=<?php echo $post_id ?>#p<?php echo $post_id ?>"><?php echo pun_htmlspecialchars($cur_post['subject']) ?></a></li>
+			<li><span>» </span><strong><?php echo $lang_misc['Report post'] ?></strong></li>
+		</ul>
+	</div>
+</div>
+
+<div id="reportform" class="blockform">
+	<h2><span><?php echo $lang_misc['Report post'] ?></span></h2>
+	<div class="box">
+		<form id="report" method="post" action="misc.php?report=<?php echo $post_id ?>" onsubmit="this.submit.disabled=true;if(process_form(this)){return true;}else{this.submit.disabled=false;return false;}">
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_misc['Reason desc'] ?></legend>
+					<div class="infldset txtarea">
+						<input type="hidden" name="form_sent" value="1" />
+						<label class="required"><strong><?php echo $lang_misc['Reason'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><textarea name="req_reason" rows="5" cols="60"></textarea><br /></label>
+					</div>
+				</fieldset>
+			</div>
+			<p class="buttons"><input type="submit" name="submit" value="<?php echo $lang_common['Submit'] ?>" accesskey="s" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+		</form>
+	</div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+
+else if (isset($_GET['subscribe']))
+{
+	if ($pun_user['is_guest'] || $pun_config['o_subscriptions'] != '1')
+		message($lang_common['No permission']);
+
+	$topic_id = intval($_GET['subscribe']);
+	if ($topic_id < 1)
+		message($lang_common['Bad request']);
+
+	// Make sure the user can view the topic
+	$result = $db->query('SELECT 1 FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.id='.$topic_id.' AND t.moved_to IS NULL') or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
+	if (!$db->num_rows($result))
+		message($lang_common['Bad request']);
+
+	$result = $db->query('SELECT 1 FROM '.$db->prefix.'subscriptions WHERE user_id='.$pun_user['id'].' AND topic_id='.$topic_id) or error('Unable to fetch subscription info', __FILE__, __LINE__, $db->error());
+	if ($db->num_rows($result))
+		message($lang_misc['Already subscribed']);
+
+	$db->query('INSERT INTO '.$db->prefix.'subscriptions (user_id, topic_id) VALUES('.$pun_user['id'].' ,'.$topic_id.')') or error('Unable to add subscription', __FILE__, __LINE__, $db->error());
+
+	redirect('viewtopic.php?id='.$topic_id, $lang_misc['Subscribe redirect']);
+}
+
+
+else if (isset($_GET['unsubscribe']))
+{
+	if ($pun_user['is_guest'] || $pun_config['o_subscriptions'] != '1')
+		message($lang_common['No permission']);
+
+	$topic_id = intval($_GET['unsubscribe']);
+	if ($topic_id < 1)
+		message($lang_common['Bad request']);
+
+	$result = $db->query('SELECT 1 FROM '.$db->prefix.'subscriptions WHERE user_id='.$pun_user['id'].' AND topic_id='.$topic_id) or error('Unable to fetch subscription info', __FILE__, __LINE__, $db->error());
+	if (!$db->num_rows($result))
+		message($lang_misc['Not subscribed']);
+
+	$db->query('DELETE FROM '.$db->prefix.'subscriptions WHERE user_id='.$pun_user['id'].' AND topic_id='.$topic_id) or error('Unable to remove subscription', __FILE__, __LINE__, $db->error());
+
+	redirect('viewtopic.php?id='.$topic_id, $lang_misc['Unsubscribe redirect']);
+}
+
+
+else
+	message($lang_common['Bad request']);
diff --git a/moderate.php b/moderate.php
new file mode 100644
index 0000000..52fda43
--- /dev/null
+++ b/moderate.php
@@ -0,0 +1,923 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+
+
+// This particular function doesn't require forum-based moderator access. It can be used
+// by all moderators and admins
+if (isset($_GET['get_host']))
+{
+	if (!$pun_user['is_admmod'])
+		message($lang_common['No permission']);
+
+	// Is get_host an IP address or a post ID?
+	if (@preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $_GET['get_host']) || @preg_match('/^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1 [...]
+		$ip = $_GET['get_host'];
+	else
+	{
+		$get_host = intval($_GET['get_host']);
+		if ($get_host < 1)
+			message($lang_common['Bad request']);
+
+		$result = $db->query('SELECT poster_ip FROM '.$db->prefix.'posts WHERE id='.$get_host) or error('Unable to fetch post IP address', __FILE__, __LINE__, $db->error());
+		if (!$db->num_rows($result))
+			message($lang_common['Bad request']);
+
+		$ip = $db->result($result);
+	}
+
+	// Load the misc.php language file
+	require PUN_ROOT.'lang/'.$pun_user['language'].'/misc.php';
+
+	message(sprintf($lang_misc['Host info 1'], $ip).'<br />'.sprintf($lang_misc['Host info 2'], @gethostbyaddr($ip)).'<br /><br /><a href="admin_users.php?show_users='.$ip.'">'.$lang_misc['Show more users'].'</a>');
+}
+
+
+// All other functions require moderator/admin access
+$fid = isset($_GET['fid']) ? intval($_GET['fid']) : 0;
+if ($fid < 1)
+	message($lang_common['Bad request']);
+
+$result = $db->query('SELECT moderators FROM '.$db->prefix.'forums WHERE id='.$fid) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
+
+$moderators = $db->result($result);
+$mods_array = ($moderators != '') ? unserialize($moderators) : array();
+
+if ($pun_user['g_id'] != PUN_ADMIN && ($pun_user['g_moderator'] == '0' || !array_key_exists($pun_user['username'], $mods_array)))
+	message($lang_common['No permission']);
+
+// Get topic/forum tracking data
+if (!$pun_user['is_guest'])
+	$tracked_topics = get_tracked_topics();
+
+// Load the misc.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/misc.php';
+
+
+// All other topic moderation features require a topic ID in GET
+if (isset($_GET['tid']))
+{
+	$tid = intval($_GET['tid']);
+	if ($tid < 1)
+		message($lang_common['Bad request']);
+
+	// Fetch some info about the topic
+	$result = $db->query('SELECT t.subject, t.num_replies, t.first_post_id, f.id AS forum_id, forum_name FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'subscriptions AS s ON (t.id=s.topic_id AND s.user_id='.$pun_user['id'].') LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.id='.$fid.' AND t.id='.$tid.' AND t.moved_to [...]
+	if (!$db->num_rows($result))
+		message($lang_common['Bad request']);
+
+	$cur_topic = $db->fetch_assoc($result);
+
+	// Delete one or more posts
+	if (isset($_POST['delete_posts']) || isset($_POST['delete_posts_comply']))
+	{
+		$posts = isset($_POST['posts']) ? $_POST['posts'] : array();
+		if (empty($posts))
+			message($lang_misc['No posts selected']);
+
+		if (isset($_POST['delete_posts_comply']))
+		{
+			confirm_referrer('moderate.php');
+
+			if (@preg_match('/[^0-9,]/', $posts))
+				message($lang_common['Bad request']);
+
+			// Verify that the post IDs are valid
+			$result = $db->query('SELECT 1 FROM '.$db->prefix.'posts WHERE id IN('.$posts.') AND topic_id='.$tid) or error('Unable to check posts', __FILE__, __LINE__, $db->error());
+
+			if ($db->num_rows($result) != substr_count($posts, ',') + 1)
+				message($lang_common['Bad request']);
+
+			// Delete the posts
+			$db->query('DELETE FROM '.$db->prefix.'posts WHERE id IN('.$posts.')') or error('Unable to delete posts', __FILE__, __LINE__, $db->error());
+
+			require PUN_ROOT.'include/search_idx.php';
+			strip_search_index($posts);
+
+			// Get last_post, last_post_id, and last_poster for the topic after deletion
+			$result = $db->query('SELECT id, poster, posted FROM '.$db->prefix.'posts WHERE topic_id='.$tid.' ORDER BY id DESC LIMIT 1') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+			$last_post = $db->fetch_assoc($result);
+
+			// How many posts did we just delete?
+			$num_posts_deleted = substr_count($posts, ',') + 1;
+
+			// Update the topic
+			$db->query('UPDATE '.$db->prefix.'topics SET last_post='.$last_post['posted'].', last_post_id='.$last_post['id'].', last_poster=\''.$db->escape($last_post['poster']).'\', num_replies=num_replies-'.$num_posts_deleted.' WHERE id='.$tid) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+
+			update_forum($fid);
+
+			redirect('viewtopic.php?id='.$tid, $lang_misc['Delete posts redirect']);
+		}
+
+
+		$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_misc['Moderate']);
+		define('PUN_ACTIVE_PAGE', 'index');
+		require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+	<h2><span><?php echo $lang_misc['Delete posts'] ?></span></h2>
+	<div class="box">
+		<form method="post" action="moderate.php?fid=<?php echo $fid ?>&tid=<?php echo $tid ?>">
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_misc['Confirm delete legend'] ?></legend>
+					<div class="infldset">
+						<input type="hidden" name="posts" value="<?php echo implode(',', array_map('intval', array_keys($posts))) ?>" />
+						<p><?php echo $lang_misc['Delete posts comply'] ?></p>
+					</div>
+				</fieldset>
+			</div>
+			<p class="buttons"><input type="submit" name="delete_posts_comply" value="<?php echo $lang_misc['Delete'] ?>" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+		</form>
+	</div>
+</div>
+<?php
+
+		require PUN_ROOT.'footer.php';
+	}
+	else if (isset($_POST['split_posts']) || isset($_POST['split_posts_comply']))
+	{
+		$posts = isset($_POST['posts']) ? $_POST['posts'] : array();
+		if (empty($posts))
+			message($lang_misc['No posts selected']);
+
+		if (isset($_POST['split_posts_comply']))
+		{
+			confirm_referrer('moderate.php');
+
+			if (@preg_match('/[^0-9,]/', $posts))
+				message($lang_common['Bad request']);
+
+			// How many posts did we just split off?
+			$num_posts_splitted = substr_count($posts, ',') + 1;
+
+			// Verify that the post IDs are valid
+			$result = $db->query('SELECT 1 FROM '.$db->prefix.'posts WHERE id IN('.$posts.') AND topic_id='.$tid) or error('Unable to check posts', __FILE__, __LINE__, $db->error());
+
+			if ($db->num_rows($result) != $num_posts_splitted)
+				message($lang_common['Bad request']);
+
+			// Load the post.php language file
+			require PUN_ROOT.'lang/'.$pun_user['language'].'/post.php';
+
+			// Check subject
+			$new_subject = isset($_POST['new_subject']) ? pun_trim($_POST['new_subject']) : '';
+
+			if ($new_subject == '')
+				message($lang_post['No subject']);
+			else if (pun_strlen($new_subject) > 70)
+				message($lang_post['Too long subject']);
+
+			// Get data from the new first post
+			$result = $db->query('SELECT p.id, p.poster, p.posted FROM '.$db->prefix.'posts AS p WHERE id IN('.$posts.') ORDER BY p.id ASC LIMIT 1') or error('Unable to get first post', __FILE__, __LINE__, $db->error());
+			$first_post_data = $db->fetch_assoc($result);
+
+			// Create the new topic
+			$db->query('INSERT INTO '.$db->prefix.'topics (poster, subject, posted, first_post_id, forum_id) VALUES (\''.$db->escape($first_post_data['poster']).'\', \''.$db->escape($new_subject).'\', '.$first_post_data['posted'].', '.$first_post_data['id'].', '.$fid.')') or error('Unable to create new topic', __FILE__, __LINE__, $db->error());
+			$new_tid = $db->insert_id();
+
+			// Move the posts to the new topic
+			$db->query('UPDATE '.$db->prefix.'posts SET topic_id='.$new_tid.' WHERE id IN('.$posts.')') or error('Unable to move posts into new topic', __FILE__, __LINE__, $db->error());
+
+			// Get last_post, last_post_id, and last_poster from the topic and update it
+			$result = $db->query('SELECT id, poster, posted FROM '.$db->prefix.'posts WHERE topic_id='.$tid.' ORDER BY id DESC LIMIT 1') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+			$last_post_data = $db->fetch_assoc($result);
+			$db->query('UPDATE '.$db->prefix.'topics SET last_post='.$last_post_data['posted'].', last_post_id='.$last_post_data['id'].', last_poster=\''.$db->escape($last_post_data['poster']).'\', num_replies=num_replies-'.$num_posts_splitted.' WHERE id='.$tid) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+
+			// Get last_post, last_post_id, and last_poster from the new topic and update it
+			$result = $db->query('SELECT id, poster, posted FROM '.$db->prefix.'posts WHERE topic_id='.$new_tid.' ORDER BY id DESC LIMIT 1') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+			$last_post_data = $db->fetch_assoc($result);
+			$db->query('UPDATE '.$db->prefix.'topics SET last_post='.$last_post_data['posted'].', last_post_id='.$last_post_data['id'].', last_poster=\''.$db->escape($last_post_data['poster']).'\', num_replies='.($num_posts_splitted-1).' WHERE id='.$new_tid) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+
+			update_forum($fid);
+
+			redirect('viewtopic.php?id='.$new_tid, $lang_misc['Split posts redirect']);
+		}
+
+		$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_misc['Moderate']);
+		$focus_element = array('subject','new_subject');
+		define('PUN_ACTIVE_PAGE', 'index');
+		require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+	<h2><span><?php echo $lang_misc['Split posts'] ?></span></h2>
+	<div class="box">
+		<form id="subject" method="post" action="moderate.php?fid=<?php echo $fid ?>&tid=<?php echo $tid ?>">
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_misc['Confirm split legend'] ?></legend>
+					<div class="infldset">
+						<input type="hidden" name="posts" value="<?php echo implode(',', array_map('intval', array_keys($posts))) ?>" />
+						<label class="required"><strong><?php echo $lang_misc['New subject'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="text" name="new_subject" size="80" maxlength="70" /><br /></label>
+						<p><?php echo $lang_misc['Split posts comply'] ?></p>
+					</div>
+				</fieldset>
+			</div>
+			<p class="buttons"><input type="submit" name="split_posts_comply" value="<?php echo $lang_misc['Split'] ?>" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+		</form>
+	</div>
+</div>
+<?php
+
+		require PUN_ROOT.'footer.php';
+	}
+
+
+	// Show the moderate posts view
+
+	// Load the viewtopic.php language file
+	require PUN_ROOT.'lang/'.$pun_user['language'].'/topic.php';
+
+	// Used to disable the Move and Delete buttons if there are no replies to this topic
+	$button_status = ($cur_topic['num_replies'] == 0) ? ' disabled="disabled"' : '';
+
+
+	// Determine the post offset (based on $_GET['p'])
+	$num_pages = ceil(($cur_topic['num_replies'] + 1) / $pun_user['disp_posts']);
+
+	$p = (!isset($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : intval($_GET['p']);
+	$start_from = $pun_user['disp_posts'] * ($p - 1);
+
+	// Generate paging links
+	$paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'moderate.php?fid='.$fid.'&tid='.$tid);
+
+
+	if ($pun_config['o_censoring'] == '1')
+		$cur_topic['subject'] = censor_words($cur_topic['subject']);
+
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), pun_htmlspecialchars($cur_topic['forum_name']), pun_htmlspecialchars($cur_topic['subject']));
+	define('PUN_ACTIVE_PAGE', 'index');
+	require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+	<div class="inbox crumbsplus">
+		<ul class="crumbs">
+			<li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+			<li><span>» </span><a href="viewforum.php?id=<?php echo $fid ?>"><?php echo pun_htmlspecialchars($cur_topic['forum_name']) ?></a></li>
+			<li><span>» </span><a href="viewtopic.php?id=<?php echo $tid ?>"><?php echo pun_htmlspecialchars($cur_topic['subject']) ?></a></li>
+			<li><span>» </span><strong><?php echo $lang_misc['Moderate'] ?></strong></li>
+		</ul>
+		<div class="pagepost">
+			<p class="pagelink conl"><?php echo $paging_links ?></p>
+		</div>
+		<div class="clearer"></div>
+	</div>
+</div>
+
+<form method="post" action="moderate.php?fid=<?php echo $fid ?>&tid=<?php echo $tid ?>">
+<?php
+
+	require PUN_ROOT.'include/parser.php';
+
+	$post_count = 0; // Keep track of post numbers
+
+	// Retrieve a list of post IDs, LIMIT is (really) expensive so we only fetch the IDs here then later fetch the remaining data
+	$result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id='.$tid.' ORDER BY id LIMIT '.$start_from.','.$pun_user['disp_posts']) or error('Unable to fetch post IDs', __FILE__, __LINE__, $db->error());
+
+	$post_ids = array();
+	for ($i = 0;$cur_post_id = $db->result($result, $i);$i++)
+		$post_ids[] = $cur_post_id;
+
+	// Retrieve the posts (and their respective poster)
+	$result = $db->query('SELECT u.title, u.num_posts, g.g_id, g.g_user_title, p.id, p.poster, p.poster_id, p.message, p.hide_smilies, p.posted, p.edited, p.edited_by FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'users AS u ON u.id=p.poster_id INNER JOIN '.$db->prefix.'groups AS g ON g.g_id=u.group_id WHERE p.id IN ('.implode(',', $post_ids).') ORDER BY p.id', true) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+
+	while ($cur_post = $db->fetch_assoc($result))
+	{
+		$post_count++;
+
+		// If the poster is a registered user
+		if ($cur_post['poster_id'] > 1)
+		{
+			if ($pun_user['g_view_users'] == '1')
+				$poster = '<a href="profile.php?id='.$cur_post['poster_id'].'">'.pun_htmlspecialchars($cur_post['poster']).'</a>';
+			else
+				$poster = pun_htmlspecialchars($cur_post['poster']);
+
+			// get_title() requires that an element 'username' be present in the array
+			$cur_post['username'] = $cur_post['poster'];
+			$user_title = get_title($cur_post);
+
+			if ($pun_config['o_censoring'] == '1')
+				$user_title = censor_words($user_title);
+		}
+		// If the poster is a guest (or a user that has been deleted)
+		else
+		{
+			$poster = pun_htmlspecialchars($cur_post['poster']);
+			$user_title = $lang_topic['Guest'];
+		}
+
+		// Perform the main parsing of the message (BBCode, smilies, censor words etc)
+		$cur_post['message'] = parse_message($cur_post['message'], $cur_post['hide_smilies']);
+
+?>
+
+<div id="p<?php echo $cur_post['id'] ?>" class="blockpost<?php if($cur_post['id'] == $cur_topic['first_post_id']) echo ' firstpost' ?><?php echo ($post_count % 2 == 0) ? ' roweven' : ' rowodd' ?><?php if ($post_count == 1) echo ' blockpost1' ?>">
+	<h2><span><span class="conr">#<?php echo ($start_from + $post_count) ?></span> <a href="viewtopic.php?pid=<?php echo $cur_post['id'].'#p'.$cur_post['id'] ?>"><?php echo format_time($cur_post['posted']) ?></a></span></h2>
+	<div class="box">
+		<div class="inbox">
+			<div class="postbody">
+				<div class="postleft">
+					<dl>
+						<dt><strong><?php echo $poster ?></strong></dt>
+						<dd class="usertitle"><strong><?php echo $user_title ?></strong></dd>
+					</dl>
+				</div>
+				<div class="postright">
+					<h3 class="nosize"><?php echo $lang_common['Message'] ?></h3>
+					<div class="postmsg">
+						<?php echo $cur_post['message']."\n" ?>
+<?php if ($cur_post['edited'] != '') echo "\t\t\t\t\t\t".'<p class="postedit"><em>'.$lang_topic['Last edit'].' '.pun_htmlspecialchars($cur_post['edited_by']).' ('.format_time($cur_post['edited']).')</em></p>'."\n"; ?>
+					</div>
+				</div>
+			</div>
+		</div>
+		<div class="inbox">
+			<div class="postfoot clearb">
+				<div class="postfootright"><?php echo ($cur_post['id'] != $cur_topic['first_post_id']) ? '<p class="multidelete"><label><strong>'.$lang_misc['Select'].'</strong> <input type="checkbox" name="posts['.$cur_post['id'].']" value="1" /></label></p>' : '<p>'.$lang_misc['Cannot select first'].'</p>' ?></div>
+			</div>
+		</div>
+	</div>
+</div>
+
+<?php
+
+	}
+
+?>
+<div class="postlinksb">
+	<div class="inbox">
+		<div class="pagepost">
+			<p class="pagelink conl"><?php echo $paging_links ?></p>
+			<p class="conr modbuttons"><input type="submit" name="split_posts" value="<?php echo $lang_misc['Split'] ?>"<?php echo $button_status ?> /> <input type="submit" name="delete_posts" value="<?php echo $lang_misc['Delete'] ?>"<?php echo $button_status ?> /></p>
+		</div>
+	</div>
+</div>
+</form>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+
+// Move one or more topics
+if (isset($_REQUEST['move_topics']) || isset($_POST['move_topics_to']))
+{
+	if (isset($_POST['move_topics_to']))
+	{
+		confirm_referrer('moderate.php');
+
+		if (@preg_match('/[^0-9,]/', $_POST['topics']))
+			message($lang_common['Bad request']);
+
+		$topics = explode(',', $_POST['topics']);
+		$move_to_forum = isset($_POST['move_to_forum']) ? intval($_POST['move_to_forum']) : 0;
+		if (empty($topics) || $move_to_forum < 1)
+			message($lang_common['Bad request']);
+
+		// Verify that the topic IDs are valid
+		$result = $db->query('SELECT 1 FROM '.$db->prefix.'topics WHERE id IN('.implode(',',$topics).') AND forum_id='.$fid) or error('Unable to check topics', __FILE__, __LINE__, $db->error());
+
+		if ($db->num_rows($result) != count($topics))
+			message($lang_common['Bad request']);
+
+		// Delete any redirect topics if there are any (only if we moved/copied the topic back to where it was once moved from)
+		$db->query('DELETE FROM '.$db->prefix.'topics WHERE forum_id='.$move_to_forum.' AND moved_to IN('.implode(',',$topics).')') or error('Unable to delete redirect topics', __FILE__, __LINE__, $db->error());
+
+		// Move the topic(s)
+		$db->query('UPDATE '.$db->prefix.'topics SET forum_id='.$move_to_forum.' WHERE id IN('.implode(',',$topics).')') or error('Unable to move topics', __FILE__, __LINE__, $db->error());
+
+		// Should we create redirect topics?
+		if (isset($_POST['with_redirect']))
+		{
+			foreach ($topics as $cur_topic)
+			{
+				// Fetch info for the redirect topic
+				$result = $db->query('SELECT poster, subject, posted, last_post FROM '.$db->prefix.'topics WHERE id='.$cur_topic) or error('Unable to fetch topic info', __FILE__, __LINE__, $db->error());
+				$moved_to = $db->fetch_assoc($result);
+
+				// Create the redirect topic
+				$db->query('INSERT INTO '.$db->prefix.'topics (poster, subject, posted, last_post, moved_to, forum_id) VALUES(\''.$db->escape($moved_to['poster']).'\', \''.$db->escape($moved_to['subject']).'\', '.$moved_to['posted'].', '.$moved_to['last_post'].', '.$cur_topic.', '.$fid.')') or error('Unable to create redirect topic', __FILE__, __LINE__, $db->error());
+			}
+		}
+
+		update_forum($fid); // Update the forum FROM which the topic was moved
+		update_forum($move_to_forum); // Update the forum TO which the topic was moved
+
+		$redirect_msg = (count($topics) > 1) ? $lang_misc['Move topics redirect'] : $lang_misc['Move topic redirect'];
+		redirect('viewforum.php?id='.$move_to_forum, $redirect_msg);
+	}
+
+	if (isset($_POST['move_topics']))
+	{
+		$topics = isset($_POST['topics']) ? $_POST['topics'] : array();
+		if (empty($topics))
+			message($lang_misc['No topics selected']);
+
+		$topics = implode(',', array_map('intval', array_keys($topics)));
+		$action = 'multi';
+	}
+	else
+	{
+		$topics = intval($_GET['move_topics']);
+		if ($topics < 1)
+			message($lang_common['Bad request']);
+
+		$action = 'single';
+	}
+
+	$result = $db->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name FROM '.$db->prefix.'categories AS c INNER JOIN '.$db->prefix.'forums AS f ON c.id=f.cat_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.redirect_url IS NULL ORDER BY c.disp_position, c.id, f.disp_position') or error('Unable to fetch category/forum list', __FILE__, __LINE__, $db->error());
+	if ($db->num_rows($result) < 2)
+		message($lang_misc['Nowhere to move']);
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_misc['Moderate']);
+	define('PUN_ACTIVE_PAGE', 'index');
+	require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+	<h2><span><?php echo ($action == 'single') ? $lang_misc['Move topic'] : $lang_misc['Move topics'] ?></span></h2>
+	<div class="box">
+		<form method="post" action="moderate.php?fid=<?php echo $fid ?>">
+			<div class="inform">
+			<input type="hidden" name="topics" value="<?php echo $topics ?>" />
+				<fieldset>
+					<legend><?php echo $lang_misc['Move legend'] ?></legend>
+					<div class="infldset">
+						<label><?php echo $lang_misc['Move to'] ?>
+						<br /><select name="move_to_forum">
+<?php
+
+	$cur_category = 0;
+	while ($cur_forum = $db->fetch_assoc($result))
+	{
+		if ($cur_forum['cid'] != $cur_category) // A new category since last iteration?
+		{
+			if ($cur_category)
+				echo "\t\t\t\t\t\t\t".'</optgroup>'."\n";
+
+			echo "\t\t\t\t\t\t\t".'<optgroup label="'.pun_htmlspecialchars($cur_forum['cat_name']).'">'."\n";
+			$cur_category = $cur_forum['cid'];
+		}
+
+		if ($cur_forum['fid'] != $fid)
+			echo "\t\t\t\t\t\t\t\t".'<option value="'.$cur_forum['fid'].'">'.pun_htmlspecialchars($cur_forum['forum_name']).'</option>'."\n";
+	}
+
+?>
+							</optgroup>
+						</select>
+						<br /></label>
+						<div class="rbox">
+							<label><input type="checkbox" name="with_redirect" value="1"<?php if ($action == 'single') echo ' checked="checked"' ?> /><?php echo $lang_misc['Leave redirect'] ?><br /></label>
+						</div>
+					</div>
+				</fieldset>
+			</div>
+			<p class="buttons"><input type="submit" name="move_topics_to" value="<?php echo $lang_misc['Move'] ?>" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+		</form>
+	</div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+// Merge two or more topics
+else if (isset($_POST['merge_topics']) || isset($_POST['merge_topics_comply']))
+{
+	if (isset($_POST['merge_topics_comply']))
+	{
+		confirm_referrer('moderate.php');
+
+		if (@preg_match('/[^0-9,]/', $_POST['topics']))
+			message($lang_common['Bad request']);
+
+		$topics = explode(',', $_POST['topics']);
+		if (count($topics) < 2)
+			message($lang_misc['Not enough topics selected']);
+
+		// Verify that the topic IDs are valid (moved topics can not be merged?)
+		// $result = $db->query('SELECT 1 FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $topics).') AND moved_to IS NULL AND forum_id='.$fid) or error('Unable to check topics', __FILE__, __LINE__, $db->error());
+		$result = $db->query('SELECT 1 FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $topics).') AND forum_id='.$fid) or error('Unable to check topics', __FILE__, __LINE__, $db->error());
+		if ($db->num_rows($result) != count($topics))
+			message($lang_common['Bad request']);
+
+		// Fetch the topic that we're merging into
+		$result = $db->query('SELECT MIN(t.id) FROM '.$db->prefix.'topics AS t WHERE t.id IN('.implode(',', $topics).')') or error('Unable to get topic', __FILE__, __LINE__, $db->error());
+		$merge_to_tid = $db->result($result);
+
+		// Make any redirect topics point to our new, merged topic
+		$query = 'UPDATE '.$db->prefix.'topics SET moved_to='.$merge_to_tid.' WHERE moved_to IN('.implode(',', $topics).')';
+
+		// Should we create redirect topics?
+		if (isset($_POST['with_redirect']))
+			$query .= ' OR (id IN('.implode(',', $topics).') AND id != '.$merge_to_tid.')';
+
+		$db->query($query) or error('Unable to make redirection topics', __FILE__, __LINE__, $db->error());
+
+		// Merge the posts into the topic
+		$db->query('UPDATE '.$db->prefix.'posts SET topic_id='.$merge_to_tid.' WHERE topic_id IN('.implode(',', $topics).')') or error('Unable to merge the posts into the topic', __FILE__, __LINE__, $db->error());
+
+		// Delete any subscriptions
+		$db->query('DELETE FROM '.$db->prefix.'subscriptions WHERE topic_id IN('.implode(',', $topics).') AND topic_id != '.$merge_to_tid) or error('Unable to delete subscriptions', __FILE__, __LINE__, $db->error());
+
+		// Without redirection the old topics are removed
+		if (!isset($_POST['with_redirect']))
+			$db->query('DELETE FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $topics).') AND id != '.$merge_to_tid) or error('Unable to delete old topics', __FILE__, __LINE__, $db->error());
+
+		// Count number of replies in the topic
+		$result = $db->query('SELECT COUNT(id) FROM '.$db->prefix.'posts WHERE topic_id='.$merge_to_tid) or error('Unable to fetch post count for topic', __FILE__, __LINE__, $db->error());
+		$num_replies = $db->result($result, 0) - 1;
+
+		// Get last_post, last_post_id and last_poster
+		$result = $db->query('SELECT posted, id, poster FROM '.$db->prefix.'posts WHERE topic_id='.$merge_to_tid.' ORDER BY id DESC LIMIT 1') or error('Unable to get last post info', __FILE__, __LINE__, $db->error());
+		list($last_post, $last_post_id, $last_poster) = $db->fetch_row($result);
+
+		// Update topic
+		$db->query('UPDATE '.$db->prefix.'topics SET num_replies='.$num_replies.', last_post='.$last_post.', last_post_id='.$last_post_id.', last_poster=\''.$db->escape($last_poster).'\' WHERE id='.$merge_to_tid) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+
+		// Update the forum FROM which the topic was moved and redirect
+		update_forum($fid);
+		redirect('viewforum.php?id='.$fid, $lang_misc['Merge topics redirect']);
+	}
+
+	$topics = isset($_POST['topics']) ? $_POST['topics'] : array();
+	if (count($topics) < 2)
+		message($lang_misc['Not enough topics selected']);
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_misc['Moderate']);
+	define('PUN_ACTIVE_PAGE', 'index');
+	require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+	<h2><span><?php echo $lang_misc['Merge topics'] ?></span></h2>
+	<div class="box">
+		<form method="post" action="moderate.php?fid=<?php echo $fid ?>">
+			<input type="hidden" name="topics" value="<?php echo implode(',', array_map('intval', array_keys($topics))) ?>" />
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_misc['Confirm merge legend'] ?></legend>
+					<div class="infldset">
+						<div class="rbox">
+							<label><input type="checkbox" name="with_redirect" value="1" /><?php echo $lang_misc['Leave redirect'] ?><br /></label>
+						</div>
+					</div>
+				</fieldset>
+			</div>
+			<p class="buttons"><input type="submit" name="merge_topics_comply" value="<?php echo $lang_misc['Merge'] ?>" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+		</form>
+	</div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+// Delete one or more topics
+else if (isset($_POST['delete_topics']) || isset($_POST['delete_topics_comply']))
+{
+	$topics = isset($_POST['topics']) ? $_POST['topics'] : array();
+	if (empty($topics))
+		message($lang_misc['No topics selected']);
+
+	if (isset($_POST['delete_topics_comply']))
+	{
+		confirm_referrer('moderate.php');
+
+		if (@preg_match('/[^0-9,]/', $topics))
+			message($lang_common['Bad request']);
+
+		require PUN_ROOT.'include/search_idx.php';
+
+		// Verify that the topic IDs are valid
+		$result = $db->query('SELECT 1 FROM '.$db->prefix.'topics WHERE id IN('.$topics.') AND forum_id='.$fid) or error('Unable to check topics', __FILE__, __LINE__, $db->error());
+
+		if ($db->num_rows($result) != substr_count($topics, ',') + 1)
+			message($lang_common['Bad request']);
+
+		// Delete the topics and any redirect topics
+		$db->query('DELETE FROM '.$db->prefix.'topics WHERE id IN('.$topics.') OR moved_to IN('.$topics.')') or error('Unable to delete topic', __FILE__, __LINE__, $db->error());
+
+		// Delete any subscriptions
+		$db->query('DELETE FROM '.$db->prefix.'subscriptions WHERE topic_id IN('.$topics.')') or error('Unable to delete subscriptions', __FILE__, __LINE__, $db->error());
+
+		// Create a list of the post IDs in this topic and then strip the search index
+		$result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id IN('.$topics.')') or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
+
+		$post_ids = '';
+		while ($row = $db->fetch_row($result))
+			$post_ids .= ($post_ids != '') ? ','.$row[0] : $row[0];
+
+		// We have to check that we actually have a list of post IDs since we could be deleting just a redirect topic
+		if ($post_ids != '')
+			strip_search_index($post_ids);
+
+		// Delete posts
+		$db->query('DELETE FROM '.$db->prefix.'posts WHERE topic_id IN('.$topics.')') or error('Unable to delete posts', __FILE__, __LINE__, $db->error());
+
+		update_forum($fid);
+
+		redirect('viewforum.php?id='.$fid, $lang_misc['Delete topics redirect']);
+	}
+
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_misc['Moderate']);
+	define('PUN_ACTIVE_PAGE', 'index');
+	require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+	<h2><span><?php echo $lang_misc['Delete topics'] ?></span></h2>
+	<div class="box">
+		<form method="post" action="moderate.php?fid=<?php echo $fid ?>">
+			<input type="hidden" name="topics" value="<?php echo implode(',', array_map('intval', array_keys($topics))) ?>" />
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_misc['Confirm delete legend'] ?></legend>
+					<div class="infldset">
+						<p><?php echo $lang_misc['Delete topics comply'] ?></p>
+					</div>
+				</fieldset>
+			</div>
+			<p class="buttons"><input type="submit" name="delete_topics_comply" value="<?php echo $lang_misc['Delete'] ?>" /><a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+		</form>
+	</div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+
+// Open or close one or more topics
+else if (isset($_REQUEST['open']) || isset($_REQUEST['close']))
+{
+	$action = (isset($_REQUEST['open'])) ? 0 : 1;
+
+	// There could be an array of topic IDs in $_POST
+	if (isset($_POST['open']) || isset($_POST['close']))
+	{
+		confirm_referrer('moderate.php');
+
+		$topics = isset($_POST['topics']) ? @array_map('intval', @array_keys($_POST['topics'])) : array();
+		if (empty($topics))
+			message($lang_misc['No topics selected']);
+
+		$db->query('UPDATE '.$db->prefix.'topics SET closed='.$action.' WHERE id IN('.implode(',', $topics).') AND forum_id='.$fid) or error('Unable to close topics', __FILE__, __LINE__, $db->error());
+
+		$redirect_msg = ($action) ? $lang_misc['Close topics redirect'] : $lang_misc['Open topics redirect'];
+		redirect('moderate.php?fid='.$fid, $redirect_msg);
+	}
+	// Or just one in $_GET
+	else
+	{
+		confirm_referrer('viewtopic.php');
+
+		$topic_id = ($action) ? intval($_GET['close']) : intval($_GET['open']);
+		if ($topic_id < 1)
+			message($lang_common['Bad request']);
+
+		$db->query('UPDATE '.$db->prefix.'topics SET closed='.$action.' WHERE id='.$topic_id.' AND forum_id='.$fid) or error('Unable to close topic', __FILE__, __LINE__, $db->error());
+
+		$redirect_msg = ($action) ? $lang_misc['Close topic redirect'] : $lang_misc['Open topic redirect'];
+		redirect('viewtopic.php?id='.$topic_id, $redirect_msg);
+	}
+}
+
+
+// Stick a topic
+else if (isset($_GET['stick']))
+{
+	confirm_referrer('viewtopic.php');
+
+	$stick = intval($_GET['stick']);
+	if ($stick < 1)
+		message($lang_common['Bad request']);
+
+	$db->query('UPDATE '.$db->prefix.'topics SET sticky=\'1\' WHERE id='.$stick.' AND forum_id='.$fid) or error('Unable to stick topic', __FILE__, __LINE__, $db->error());
+
+	redirect('viewtopic.php?id='.$stick, $lang_misc['Stick topic redirect']);
+}
+
+
+// Unstick a topic
+else if (isset($_GET['unstick']))
+{
+	confirm_referrer('viewtopic.php');
+
+	$unstick = intval($_GET['unstick']);
+	if ($unstick < 1)
+		message($lang_common['Bad request']);
+
+	$db->query('UPDATE '.$db->prefix.'topics SET sticky=\'0\' WHERE id='.$unstick.' AND forum_id='.$fid) or error('Unable to unstick topic', __FILE__, __LINE__, $db->error());
+
+	redirect('viewtopic.php?id='.$unstick, $lang_misc['Unstick topic redirect']);
+}
+
+
+// No specific forum moderation action was specified in the query string, so we'll display the moderator forum
+
+// Load the viewforum.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/forum.php';
+
+// Fetch some info about the forum
+$result = $db->query('SELECT f.forum_name, f.redirect_url, f.num_topics, f.sort_by FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.id='.$fid) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
+if (!$db->num_rows($result))
+	message($lang_common['Bad request']);
+
+$cur_forum = $db->fetch_assoc($result);
+
+// Is this a redirect forum? In that case, abort!
+if ($cur_forum['redirect_url'] != '')
+	message($lang_common['Bad request']);
+
+// Determine the topic offset (based on $_GET['p'])
+$num_pages = ceil($cur_forum['num_topics'] / $pun_user['disp_topics']);
+
+$p = (!isset($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : intval($_GET['p']);
+$start_from = $pun_user['disp_topics'] * ($p - 1);
+
+// Generate paging links
+$paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'moderate.php?fid='.$fid);
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), pun_htmlspecialchars($cur_forum['forum_name']));
+define('PUN_ACTIVE_PAGE', 'index');
+require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+	<div class="inbox crumbsplus">
+		<ul class="crumbs">
+			<li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+			<li><span>» </span><a href="viewforum.php?id=<?php echo $fid ?>"><?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></a></li>
+			<li><span>» </span><strong><?php echo $lang_misc['Moderate'] ?></strong></li>
+		</ul>
+		<div class="pagepost">
+			<p class="pagelink conl"><?php echo $paging_links ?></p>
+		</div>
+		<div class="clearer"></div>
+	</div>
+</div>
+
+<form method="post" action="moderate.php?fid=<?php echo $fid ?>">
+<div id="vf" class="blocktable">
+	<h2><span><?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></span></h2>
+	<div class="box">
+		<div class="inbox">
+			<table cellspacing="0">
+			<thead>
+				<tr>
+					<th class="tcl" scope="col"><?php echo $lang_common['Topic'] ?></th>
+					<th class="tc2" scope="col"><?php echo $lang_common['Replies'] ?></th>
+<?php if ($pun_config['o_topic_views'] == '1'): ?>					<th class="tc3" scope="col"><?php echo $lang_forum['Views'] ?></th>
+<?php endif; ?>					<th class="tcr"><?php echo $lang_common['Last post'] ?></th>
+					<th class="tcmod" scope="col"><?php echo $lang_misc['Select'] ?></th>
+				</tr>
+			</thead>
+			<tbody>
+<?php
+
+
+// Retrieve a list of topic IDs, LIMIT is (really) expensive so we only fetch the IDs here then later fetch the remaining data
+$result = $db->query('SELECT id FROM '.$db->prefix.'topics WHERE forum_id='.$fid.' ORDER BY sticky DESC, '.(($cur_forum['sort_by'] == '1') ? 'posted' : 'last_post').' DESC, id DESC LIMIT '.$start_from.', '.$pun_user['disp_topics']) or error('Unable to fetch topic IDs', __FILE__, __LINE__, $db->error());
+
+// If there are topics in this forum
+if ($db->num_rows($result))
+{
+	$topic_ids = array();
+	for ($i = 0;$cur_topic_id = $db->result($result, $i);$i++)
+		$topic_ids[] = $cur_topic_id;
+
+	// Select topics
+	$result = $db->query('SELECT id, poster, subject, posted, last_post, last_post_id, last_poster, num_views, num_replies, closed, sticky, moved_to FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $topic_ids).') ORDER BY sticky DESC, '.(($cur_forum['sort_by'] == '1') ? 'posted' : 'last_post').' DESC, id DESC') or error('Unable to fetch topic list for forum', __FILE__, __LINE__, $db->error());
+
+	$button_status = '';
+	$topic_count = 0;
+	while ($cur_topic = $db->fetch_assoc($result))
+	{
+
+		++$topic_count;
+		$status_text = array();
+		$item_status = ($topic_count % 2 == 0) ? 'roweven' : 'rowodd';
+		$icon_type = 'icon';
+
+		if ($cur_topic['moved_to'] == null)
+		{
+			$last_post = '<a href="viewtopic.php?pid='.$cur_topic['last_post_id'].'#p'.$cur_topic['last_post_id'].'">'.format_time($cur_topic['last_post']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_topic['last_poster']).'</span>';
+			$ghost_topic = false;
+		}
+		else
+		{
+			$last_post = '- - -';
+			$ghost_topic = true;
+		}
+
+		if ($pun_config['o_censoring'] == '1')
+			$cur_topic['subject'] = censor_words($cur_topic['subject']);
+
+		if ($cur_topic['sticky'] == '1')
+		{
+			$item_status .= ' isticky';
+			$status_text[] = '<span class="stickytext">'.$lang_forum['Sticky'].'</span>';
+		}
+
+		if ($cur_topic['moved_to'] != 0)
+		{
+			$subject = '<a href="viewtopic.php?id='.$cur_topic['moved_to'].'">'.pun_htmlspecialchars($cur_topic['subject']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_topic['poster']).'</span>';
+			$status_text[] = '<span class="movedtext">'.$lang_forum['Moved'].'</span>';
+			$item_status .= ' imoved';
+		}
+		else if ($cur_topic['closed'] == '0')
+			$subject = '<a href="viewtopic.php?id='.$cur_topic['id'].'">'.pun_htmlspecialchars($cur_topic['subject']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_topic['poster']).'</span>';
+		else
+		{
+			$subject = '<a href="viewtopic.php?id='.$cur_topic['id'].'">'.pun_htmlspecialchars($cur_topic['subject']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_topic['poster']).'</span>';
+			$status_text[] = '<span class="closedtext">'.$lang_forum['Closed'].'</span>';
+			$item_status .= ' iclosed';
+		}
+
+		if (!$ghost_topic && $cur_topic['last_post'] > $pun_user['last_visit'] && (!isset($tracked_topics['topics'][$cur_topic['id']]) || $tracked_topics['topics'][$cur_topic['id']] < $cur_topic['last_post']) && (!isset($tracked_topics['forums'][$fid]) || $tracked_topics['forums'][$fid] < $cur_topic['last_post']))
+		{
+			$item_status .= ' inew';
+			$icon_type = 'icon icon-new';
+			$subject = '<strong>'.$subject.'</strong>';
+			$subject_new_posts = '<span class="newtext">[ <a href="viewtopic.php?id='.$cur_topic['id'].'&action=new" title="'.$lang_common['New posts info'].'">'.$lang_common['New posts'].'</a> ]</span>';
+		}
+		else
+			$subject_new_posts = null;
+
+		// Insert the status text before the subject
+		$subject = implode(' ', $status_text).' '.$subject;
+
+		$num_pages_topic = ceil(($cur_topic['num_replies'] + 1) / $pun_user['disp_posts']);
+
+		if ($num_pages_topic > 1)
+			$subject_multipage = '<span class="pagestext">[ '.paginate($num_pages_topic, -1, 'viewtopic.php?id='.$cur_topic['id']).' ]</span>';
+		else
+			$subject_multipage = null;
+
+		// Should we show the "New posts" and/or the multipage links?
+		if (!empty($subject_new_posts) || !empty($subject_multipage))
+		{
+			$subject .= !empty($subject_new_posts) ? ' '.$subject_new_posts : '';
+			$subject .= !empty($subject_multipage) ? ' '.$subject_multipage : '';
+		}
+
+?>
+				<tr class="<?php echo $item_status ?>">
+					<td class="tcl">
+						<div class="<?php echo $icon_type ?>"><div class="nosize"><?php echo forum_number_format($topic_count + $start_from) ?></div></div>
+						<div class="tclcon">
+							<div>
+								<?php echo $subject."\n" ?>
+							</div>
+						</div>
+					</td>
+					<td class="tc2"><?php echo (!$ghost_topic) ? forum_number_format($cur_topic['num_replies']) : '-' ?></td>
+<?php if ($pun_config['o_topic_views'] == '1'): ?>					<td class="tc3"><?php echo (!$ghost_topic) ? forum_number_format($cur_topic['num_views']) : '-' ?></td>
+<?php endif; ?>					<td class="tcr"><?php echo $last_post ?></td>
+					<td class="tcmod"><input type="checkbox" name="topics[<?php echo $cur_topic['id'] ?>]" value="1" /></td>
+				</tr>
+<?php
+
+	}
+}
+else
+{
+	$colspan = ($pun_config['o_topic_views'] == '1') ? 5 : 4;
+	$button_status = ' disabled="disabled"';
+	echo "\t\t\t\t\t".'<tr><td class="tcl" colspan="'.$colspan.'">'.$lang_forum['Empty forum'].'</td></tr>'."\n";
+}
+
+?>
+			</tbody>
+			</table>
+		</div>
+	</div>
+</div>
+
+<div class="linksb">
+	<div class="inbox">
+		<div class="pagepost">
+			<p class="pagelink conl"><?php echo $paging_links ?></p>
+			<p class="conr modbuttons"><input type="submit" name="move_topics" value="<?php echo $lang_misc['Move'] ?>"<?php echo $button_status ?> /> <input type="submit" name="delete_topics" value="<?php echo $lang_misc['Delete'] ?>"<?php echo $button_status ?> /> <input type="submit" name="merge_topics" value="<?php echo $lang_misc['Merge'] ?>"<?php echo $button_status ?> /> <input type="submit" name="open" value="<?php echo $lang_misc['Open'] ?>"<?php echo $button_status ?> /> <input type="su [...]
+		</div>
+	</div>
+</div>
+</form>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/plugins/AMP_Example.php b/plugins/AMP_Example.php
new file mode 100644
index 0000000..d0496f3
--- /dev/null
+++ b/plugins/AMP_Example.php
@@ -0,0 +1,132 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+##
+##
+##  A few notes of interest for aspiring plugin authors:
+##
+##  1. If you want to display a message via the message() function, you
+##     must do so before calling generate_admin_menu($plugin).
+##
+##  2. Plugins are loaded by admin_loader.php and must not be
+##     terminated (e.g. by calling exit()). After the plugin script has
+##     finished, the loader script displays the footer, so don't worry
+##     about that. Please note that terminating a plugin by calling
+##     message() or redirect() is fine though.
+##
+##  3. The action attribute of any and all <form> tags and the target
+##     URL for the redirect() function must be set to the value of
+##     $_SERVER['REQUEST_URI']. This URL can however be extended to
+##     include extra variables (like the addition of &foo=bar in
+##     the form of this example plugin).
+##
+##  4. If your plugin is for administrators only, the filename must
+##     have the prefix "AP_". If it is for both administrators and
+##     moderators, use the prefix "AMP_". This example plugin has the
+##     prefix "AMP_" and is therefore available for both admins and
+##     moderators in the navigation menu.
+##
+##  5. Use _ instead of spaces in the file name.
+##
+##  6. Since plugin scripts are included from the FluxBB script
+##     admin_loader.php, you have access to all FluxBB functions and
+##     global variables (e.g. $db, $pun_config, $pun_user etc).
+##
+##  7. Do your best to keep the look and feel of your plugins' user
+##     interface similar to the rest of the admin scripts. Feel free to
+##     borrow markup and code from the admin scripts to use in your
+##     plugins. If you create your own styles they need to be added to
+##     the "base_admin" style sheet.
+##
+##  8. Plugins must be released under the GNU General Public License or
+##     a GPL compatible license. Copy the GPL preamble at the top of
+##     this file into your plugin script and alter the copyright notice
+##     to refrect the author of the plugin (i.e. you).
+##
+##
+
+
+// Make sure no one attempts to run this script "directly"
+if (!defined('PUN'))
+	exit;
+
+// Load the admin_plugin_example.php language file
+require PUN_ROOT.'lang/'.$admin_language.'/admin_plugin_example.php';
+
+// Tell admin_loader.php that this is indeed a plugin and that it is loaded
+define('PUN_PLUGIN_LOADED', 1);
+
+//
+// The rest is up to you!
+//
+
+// If the "Show text" button was clicked
+if (isset($_POST['show_text']))
+{
+	// Make sure something was entered
+	if (trim($_POST['text_to_show']) == '')
+		message($lang_admin_plugin_example['No text']);
+
+	// Display the admin navigation menu
+	generate_admin_menu($plugin);
+
+?>
+	<div class="block">
+		<h2><span><?php echo $lang_admin_plugin_example['Example plugin title'] ?></span></h2>
+		<div class="box">
+			<div class="inbox">
+				<p><?php printf($lang_admin_plugin_example['You said'], pun_htmlspecialchars($_POST['text_to_show'])) ?></p>
+				<p><a href="javascript: history.go(-1)"><?php echo $lang_admin_common['Go back'] ?></a></p>
+			</div>
+		</div>
+	</div>
+<?php
+
+}
+else // If not, we show the "Show text" form
+{
+	// Display the admin navigation menu
+	generate_admin_menu($plugin);
+
+?>
+	<div class="plugin blockform">
+		<h2><span><?php echo $lang_admin_plugin_example['Example plugin title'] ?></span></h2>
+		<div class="box">
+			<div class="inbox">
+				<p><?php echo $lang_admin_plugin_example['Explanation 1'] ?></p>
+				<p><?php echo $lang_admin_plugin_example['Explanation 2'] ?></p>
+			</div>
+		</div>
+
+		<h2 class="block2"><span><?php echo $lang_admin_plugin_example['Example form title'] ?></span></h2>
+		<div class="box">
+			<form id="example" method="post" action="<?php echo pun_htmlspecialchars($_SERVER['REQUEST_URI']) ?>&foo=bar">
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_admin_plugin_example['Legend text'] ?></legend>
+						<div class="infldset">
+							<table class="aligntop" cellspacing="0">
+								<tr>
+									<th scope="row"><?php echo $lang_admin_plugin_example['Text to show'] ?><div><input type="submit" name="show_text" value="<?php echo $lang_admin_plugin_example['Show text button'] ?>" tabindex="2" /></div></th>
+									<td>
+										<input type="text" name="text_to_show" size="25" tabindex="1" />
+										<span><?php echo $lang_admin_plugin_example['Input content'] ?></span>
+									</td>
+								</tr>
+							</table>
+						</div>
+					</fieldset>
+				</div>
+			</form>
+		</div>
+	</div>
+<?php
+
+}
+
+// Note that the script just ends here. The footer will be included by admin_loader.php
diff --git a/plugins/index.html b/plugins/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/plugins/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/post.php b/post.php
new file mode 100644
index 0000000..ecf7e61
--- /dev/null
+++ b/post.php
@@ -0,0 +1,617 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+
+
+if ($pun_user['g_read_board'] == '0')
+	message($lang_common['No view']);
+
+
+$tid = isset($_GET['tid']) ? intval($_GET['tid']) : 0;
+$fid = isset($_GET['fid']) ? intval($_GET['fid']) : 0;
+if ($tid < 1 && $fid < 1 || $tid > 0 && $fid > 0)
+	message($lang_common['Bad request']);
+
+// Fetch some info about the topic and/or the forum
+if ($tid)
+	$result = $db->query('SELECT f.id, f.forum_name, f.moderators, f.redirect_url, fp.post_replies, fp.post_topics, t.subject, t.closed, s.user_id AS is_subscribed FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') LEFT JOIN '.$db->prefix.'subscriptions AS s ON (t.id=s.topic_id AND s.user_id='.$pun_user['id'].') WHERE (fp.read_forum IS NULL OR fp.read_ [...]
+else
+	$result = $db->query('SELECT f.id, f.forum_name, f.moderators, f.redirect_url, fp.post_replies, fp.post_topics FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.id='.$fid) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
+
+if (!$db->num_rows($result))
+	message($lang_common['Bad request']);
+
+$cur_posting = $db->fetch_assoc($result);
+$is_subscribed = $tid && $cur_posting['is_subscribed'];
+
+// Is someone trying to post into a redirect forum?
+if ($cur_posting['redirect_url'] != '')
+	message($lang_common['Bad request']);
+
+// Sort out who the moderators are and if we are currently a moderator (or an admin)
+$mods_array = ($cur_posting['moderators'] != '') ? unserialize($cur_posting['moderators']) : array();
+$is_admmod = ($pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_moderator'] == '1' && array_key_exists($pun_user['username'], $mods_array))) ? true : false;
+
+if ($tid && $pun_config['o_censoring'] == '1')
+	$cur_posting['subject'] = censor_words($cur_posting['subject']);
+
+// Do we have permission to post?
+if ((($tid && (($cur_posting['post_replies'] == '' && $pun_user['g_post_replies'] == '0') || $cur_posting['post_replies'] == '0')) ||
+	($fid && (($cur_posting['post_topics'] == '' && $pun_user['g_post_topics'] == '0') || $cur_posting['post_topics'] == '0')) ||
+	(isset($cur_posting['closed']) && $cur_posting['closed'] == '1')) &&
+	!$is_admmod)
+	message($lang_common['No permission']);
+
+// Load the post.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/post.php';
+
+// Start with a clean slate
+$errors = array();
+
+
+// Did someone just hit "Submit" or "Preview"?
+if (isset($_POST['form_sent']))
+{
+	// Make sure form_user is correct
+	if (($pun_user['is_guest'] && $_POST['form_user'] != 'Guest') || (!$pun_user['is_guest'] && $_POST['form_user'] != $pun_user['username']))
+		message($lang_common['Bad request']);
+
+	// Flood protection
+	if (!isset($_POST['preview']) && $pun_user['last_post'] != '' && (time() - $pun_user['last_post']) < $pun_user['g_post_flood'])
+		$errors[] = $lang_post['Flood start'].' '.$pun_user['g_post_flood'].' '.$lang_post['flood end'];
+
+	// If it's a new topic
+	if ($fid)
+	{
+		$subject = pun_trim($_POST['req_subject']);
+
+		if ($subject == '')
+			$errors[] = $lang_post['No subject'];
+		else if (pun_strlen($subject) > 70)
+			$errors[] = $lang_post['Too long subject'];
+		else if ($pun_config['p_subject_all_caps'] == '0' && is_all_uppercase($subject) && !$pun_user['is_admmod'])
+			$errors[] = $lang_post['All caps subject'];
+	}
+
+	// If the user is logged in we get the username and email from $pun_user
+	if (!$pun_user['is_guest'])
+	{
+		$username = $pun_user['username'];
+		$email = $pun_user['email'];
+	}
+	// Otherwise it should be in $_POST
+	else
+	{
+		$username = pun_trim($_POST['req_username']);
+		$email = strtolower(trim(($pun_config['p_force_guest_email'] == '1') ? $_POST['req_email'] : $_POST['email']));
+
+		// Load the register.php/profile.php language files
+		require PUN_ROOT.'lang/'.$pun_user['language'].'/prof_reg.php';
+		require PUN_ROOT.'lang/'.$pun_user['language'].'/register.php';
+
+		// It's a guest, so we have to validate the username
+		check_username($username);
+
+		if ($pun_config['p_force_guest_email'] == '1' || $email != '')
+		{
+			require PUN_ROOT.'include/email.php';
+			if (!is_valid_email($email))
+				$errors[] = $lang_common['Invalid email'];
+
+			// Check if it's a banned email address
+			// we should only check guests because members addresses are already verified
+			if ($pun_user['is_guest'] && is_banned_email($email))
+			{
+				if ($pun_config['p_allow_banned_email'] == '0')
+					$errors[] = $lang_prof_reg['Banned email'];
+
+				$banned_email = true; // Used later when we send an alert email
+			}
+			else
+				$banned_email = false;
+		}
+	}
+
+	// Clean up message from POST
+	$message = pun_linebreaks(pun_trim($_POST['req_message']));
+
+	// Here we use strlen() not pun_strlen() as we want to limit the post to PUN_MAX_POSTSIZE bytes, not characters
+	if (strlen($message) > PUN_MAX_POSTSIZE)
+		$errors[] = sprintf($lang_post['Too long message'], forum_number_format(PUN_MAX_POSTSIZE));
+	else if ($pun_config['p_message_all_caps'] == '0' && is_all_uppercase($message) && !$pun_user['is_admmod'])
+		$errors[] = $lang_post['All caps message'];
+
+	// Validate BBCode syntax
+	if ($pun_config['p_message_bbcode'] == '1')
+	{
+		require PUN_ROOT.'include/parser.php';
+		$message = preparse_bbcode($message, $errors);
+	}
+
+	if ($message == '')
+		$errors[] = $lang_post['No message'];
+
+	$hide_smilies = isset($_POST['hide_smilies']) ? '1' : '0';
+	$subscribe = isset($_POST['subscribe']) ? '1' : '0';
+
+	$now = time();
+
+	// Did everything go according to plan?
+	if (empty($errors) && !isset($_POST['preview']))
+	{
+		require PUN_ROOT.'include/search_idx.php';
+
+		// If it's a reply
+		if ($tid)
+		{
+			if (!$pun_user['is_guest'])
+			{
+				$new_tid = $tid;
+
+				// Insert the new post
+				$db->query('INSERT INTO '.$db->prefix.'posts (poster, poster_id, poster_ip, message, hide_smilies, posted, topic_id) VALUES(\''.$db->escape($username).'\', '.$pun_user['id'].', \''.get_remote_address().'\', \''.$db->escape($message).'\', '.$hide_smilies.', '.$now.', '.$tid.')') or error('Unable to create post', __FILE__, __LINE__, $db->error());
+				$new_pid = $db->insert_id();
+
+				// To subscribe or not to subscribe, that ...
+				if ($pun_config['o_subscriptions'] == '1')
+				{
+					if ($subscribe && !$is_subscribed)
+						$db->query('INSERT INTO '.$db->prefix.'subscriptions (user_id, topic_id) VALUES('.$pun_user['id'].' ,'.$tid.')') or error('Unable to add subscription', __FILE__, __LINE__, $db->error());
+					else if (!$subscribe && $is_subscribed)
+						$db->query('DELETE FROM '.$db->prefix.'subscriptions WHERE user_id='.$pun_user['id'].' AND topic_id='.$tid) or error('Unable to remove subscription', __FILE__, __LINE__, $db->error());
+				}
+			}
+			else
+			{
+				// It's a guest. Insert the new post
+				$email_sql = ($pun_config['p_force_guest_email'] == '1' || $email != '') ? '\''.$email.'\'' : 'NULL';
+				$db->query('INSERT INTO '.$db->prefix.'posts (poster, poster_ip, poster_email, message, hide_smilies, posted, topic_id) VALUES(\''.$db->escape($username).'\', \''.get_remote_address().'\', '.$email_sql.', \''.$db->escape($message).'\', '.$hide_smilies.', '.$now.', '.$tid.')') or error('Unable to create post', __FILE__, __LINE__, $db->error());
+				$new_pid = $db->insert_id();
+			}
+
+			// Count number of replies in the topic
+			$result = $db->query('SELECT COUNT(id) FROM '.$db->prefix.'posts WHERE topic_id='.$tid) or error('Unable to fetch post count for topic', __FILE__, __LINE__, $db->error());
+			$num_replies = $db->result($result, 0) - 1;
+
+			// Update topic
+			$db->query('UPDATE '.$db->prefix.'topics SET num_replies='.$num_replies.', last_post='.$now.', last_post_id='.$new_pid.', last_poster=\''.$db->escape($username).'\' WHERE id='.$tid) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+
+			update_search_index('post', $new_pid, $message);
+
+			update_forum($cur_posting['id']);
+
+			// Should we send out notifications?
+			if ($pun_config['o_subscriptions'] == '1')
+			{
+				// Get the post time for the previous post in this topic
+				$result = $db->query('SELECT posted FROM '.$db->prefix.'posts WHERE topic_id='.$tid.' ORDER BY id DESC LIMIT 1, 1') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+				$previous_post_time = $db->result($result);
+
+				// Get any subscribed users that should be notified (banned users are excluded)
+				$result = $db->query('SELECT u.id, u.email, u.notify_with_post, u.language FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'subscriptions AS s ON u.id=s.user_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id='.$cur_posting['id'].' AND fp.group_id=u.group_id) LEFT JOIN '.$db->prefix.'online AS o ON u.id=o.user_id LEFT JOIN '.$db->prefix.'bans AS b ON u.username=b.username WHERE b.username IS NULL AND COALESCE(o.logged, u.last_visit)>'.$previous_post_time.' AND (fp. [...]
+				if ($db->num_rows($result))
+				{
+					require_once PUN_ROOT.'include/email.php';
+
+					$notification_emails = array();
+
+					// Loop through subscribed users and send emails
+					while ($cur_subscriber = $db->fetch_assoc($result))
+					{
+						// Is the subscription email for $cur_subscriber['language'] cached or not?
+						if (!isset($notification_emails[$cur_subscriber['language']]))
+						{
+							if (file_exists(PUN_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_reply.tpl'))
+							{
+								// Load the "new reply" template
+								$mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_reply.tpl'));
+
+								// Load the "new reply full" template (with post included)
+								$mail_tpl_full = trim(file_get_contents(PUN_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_reply_full.tpl'));
+
+								// The first row contains the subject (it also starts with "Subject:")
+								$first_crlf = strpos($mail_tpl, "\n");
+								$mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
+								$mail_message = trim(substr($mail_tpl, $first_crlf));
+
+								$first_crlf = strpos($mail_tpl_full, "\n");
+								$mail_subject_full = trim(substr($mail_tpl_full, 8, $first_crlf-8));
+								$mail_message_full = trim(substr($mail_tpl_full, $first_crlf));
+
+								$mail_subject = str_replace('<topic_subject>', '\''.$cur_posting['subject'].'\'', $mail_subject);
+								$mail_message = str_replace('<topic_subject>', '\''.$cur_posting['subject'].'\'', $mail_message);
+								$mail_message = str_replace('<replier>', $username, $mail_message);
+								$mail_message = str_replace('<post_url>', $pun_config['o_base_url'].'/viewtopic.php?pid='.$new_pid.'#p'.$new_pid, $mail_message);
+								$mail_message = str_replace('<unsubscribe_url>', $pun_config['o_base_url'].'/misc.php?unsubscribe='.$tid, $mail_message);
+								$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'].' '.$lang_common['Mailer'], $mail_message);
+
+								$mail_subject_full = str_replace('<topic_subject>', '\''.$cur_posting['subject'].'\'', $mail_subject_full);
+								$mail_message_full = str_replace('<topic_subject>', '\''.$cur_posting['subject'].'\'', $mail_message_full);
+								$mail_message_full = str_replace('<replier>', $username, $mail_message_full);
+								$mail_message_full = str_replace('<message>', $message, $mail_message_full);
+								$mail_message_full = str_replace('<post_url>', $pun_config['o_base_url'].'/viewtopic.php?pid='.$new_pid.'#p'.$new_pid, $mail_message_full);
+								$mail_message_full = str_replace('<unsubscribe_url>', $pun_config['o_base_url'].'/misc.php?unsubscribe='.$tid, $mail_message_full);
+								$mail_message_full = str_replace('<board_mailer>', $pun_config['o_board_title'].' '.$lang_common['Mailer'], $mail_message_full);
+
+								$notification_emails[$cur_subscriber['language']][0] = $mail_subject;
+								$notification_emails[$cur_subscriber['language']][1] = $mail_message;
+								$notification_emails[$cur_subscriber['language']][2] = $mail_subject_full;
+								$notification_emails[$cur_subscriber['language']][3] = $mail_message_full;
+
+								$mail_subject = $mail_message = $mail_subject_full = $mail_message_full = null;
+							}
+						}
+
+						// We have to double check here because the templates could be missing
+						if (isset($notification_emails[$cur_subscriber['language']]))
+						{
+							if ($cur_subscriber['notify_with_post'] == '0')
+								pun_mail($cur_subscriber['email'], $notification_emails[$cur_subscriber['language']][0], $notification_emails[$cur_subscriber['language']][1]);
+							else
+								pun_mail($cur_subscriber['email'], $notification_emails[$cur_subscriber['language']][2], $notification_emails[$cur_subscriber['language']][3]);
+						}
+					}
+				}
+			}
+		}
+		// If it's a new topic
+		else if ($fid)
+		{
+			// Create the topic
+			$db->query('INSERT INTO '.$db->prefix.'topics (poster, subject, posted, last_post, last_poster, forum_id) VALUES(\''.$db->escape($username).'\', \''.$db->escape($subject).'\', '.$now.', '.$now.', \''.$db->escape($username).'\', '.$fid.')') or error('Unable to create topic', __FILE__, __LINE__, $db->error());
+			$new_tid = $db->insert_id();
+
+			if (!$pun_user['is_guest'])
+			{
+				// To subscribe or not to subscribe, that ...
+				if ($pun_config['o_subscriptions'] == '1' && $subscribe)
+					$db->query('INSERT INTO '.$db->prefix.'subscriptions (user_id, topic_id) VALUES('.$pun_user['id'].' ,'.$new_tid.')') or error('Unable to add subscription', __FILE__, __LINE__, $db->error());
+
+				// Create the post ("topic post")
+				$db->query('INSERT INTO '.$db->prefix.'posts (poster, poster_id, poster_ip, message, hide_smilies, posted, topic_id) VALUES(\''.$db->escape($username).'\', '.$pun_user['id'].', \''.get_remote_address().'\', \''.$db->escape($message).'\', '.$hide_smilies.', '.$now.', '.$new_tid.')') or error('Unable to create post', __FILE__, __LINE__, $db->error());
+			}
+			else
+			{
+				// Create the post ("topic post")
+				$email_sql = ($pun_config['p_force_guest_email'] == '1' || $email != '') ? '\''.$email.'\'' : 'NULL';
+				$db->query('INSERT INTO '.$db->prefix.'posts (poster, poster_ip, poster_email, message, hide_smilies, posted, topic_id) VALUES(\''.$db->escape($username).'\', \''.get_remote_address().'\', '.$email_sql.', \''.$db->escape($message).'\', '.$hide_smilies.', '.$now.', '.$new_tid.')') or error('Unable to create post', __FILE__, __LINE__, $db->error());
+			}
+			$new_pid = $db->insert_id();
+
+			// Update the topic with last_post_id
+			$db->query('UPDATE '.$db->prefix.'topics SET last_post_id='.$new_pid.', first_post_id='.$new_pid.' WHERE id='.$new_tid) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+
+			update_search_index('post', $new_pid, $message, $subject);
+
+			update_forum($fid);
+		}
+
+		// If we previously found out that the email was banned
+		if ($pun_user['is_guest'] && $banned_email && $pun_config['o_mailing_list'] != '')
+		{
+			$mail_subject = $lang_common['Banned email notification'];
+			$mail_message = sprintf($lang_common['Banned email post message'], $username, $email)."\n";
+			$mail_message .= sprintf($lang_common['Post URL'], $pun_config['o_base_url'].'/viewtopic.php?pid='.$new_pid.'#p'.$new_pid)."\n";
+			$mail_message .= "\n".'--'."\n".$lang_common['Email signature'];
+
+			pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
+		}
+
+		// If the posting user is logged in, increment his/her post count
+		if (!$pun_user['is_guest'])
+		{
+			$db->query('UPDATE '.$db->prefix.'users SET num_posts=num_posts+1, last_post='.$now.' WHERE id='.$pun_user['id']) or error('Unable to update user', __FILE__, __LINE__, $db->error());
+
+			$tracked_topics = get_tracked_topics();
+			$tracked_topics['topics'][$new_tid] = time();
+			set_tracked_topics($tracked_topics);
+		}
+		else
+		{
+			$db->query('UPDATE '.$db->prefix.'online SET last_post='.$now.' WHERE ident=\''.$db->escape(get_remote_address()).'\'' ) or error('Unable to update user', __FILE__, __LINE__, $db->error());
+		}
+
+		redirect('viewtopic.php?pid='.$new_pid.'#p'.$new_pid, $lang_post['Post redirect']);
+	}
+}
+
+
+// If a topic ID was specified in the url (it's a reply)
+if ($tid)
+{
+	$action = $lang_post['Post a reply'];
+	$form = '<form id="post" method="post" action="post.php?action=post&tid='.$tid.'" onsubmit="this.submit.disabled=true;if(process_form(this)){return true;}else{this.submit.disabled=false;return false;}">';
+
+	// If a quote ID was specified in the url
+	if (isset($_GET['qid']))
+	{
+		$qid = intval($_GET['qid']);
+		if ($qid < 1)
+			message($lang_common['Bad request']);
+
+		$result = $db->query('SELECT poster, message FROM '.$db->prefix.'posts WHERE id='.$qid.' AND topic_id='.$tid) or error('Unable to fetch quote info', __FILE__, __LINE__, $db->error());
+		if (!$db->num_rows($result))
+			message($lang_common['Bad request']);
+
+		list($q_poster, $q_message) = $db->fetch_row($result);
+
+		$q_message = preg_replace('%\[img(?:=.*?)?\]%', '[url]', $q_message);
+		$q_message = str_replace('[/img]', '[/url]', $q_message);
+
+		if ($pun_config['o_censoring'] == '1')
+			$q_message = censor_words($q_message);
+
+		$q_message = pun_htmlspecialchars($q_message);
+
+		if ($pun_config['p_message_bbcode'] == '1')
+		{
+			// If username contains a square bracket, we add "" or '' around it (so we know when it starts and ends)
+			if (strpos($q_poster, '[') !== false || strpos($q_poster, ']') !== false)
+			{
+				if (strpos($q_poster, '\'') !== false)
+					$q_poster = '"'.$q_poster.'"';
+				else
+					$q_poster = '\''.$q_poster.'\'';
+			}
+			else
+			{
+				// Get the characters at the start and end of $q_poster
+				$ends = substr($q_poster, 0, 1).substr($q_poster, -1, 1);
+
+				// Deal with quoting "Username" or 'Username' (becomes '"Username"' or "'Username'")
+				if ($ends == '\'\'')
+					$q_poster = '"'.$q_poster.'"';
+				else if ($ends == '""')
+					$q_poster = '\''.$q_poster.'\'';
+			}
+
+			$quote = '[quote='.$q_poster.']'.$q_message.'[/quote]'."\n";
+		}
+		else
+			$quote = '> '.$q_poster.' '.$lang_common['wrote']."\n\n".'> '.$q_message."\n";
+	}
+}
+// If a forum ID was specified in the url (new topic)
+else if ($fid)
+{
+	$action = $lang_post['Post new topic'];
+	$form = '<form id="post" method="post" action="post.php?action=post&fid='.$fid.'" onsubmit="return process_form(this)">';
+}
+else
+	message($lang_common['Bad request']);
+
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $action);
+$required_fields = array('req_email' => $lang_common['Email'], 'req_subject' => $lang_common['Subject'], 'req_message' => $lang_common['Message']);
+$focus_element = array('post');
+
+if (!$pun_user['is_guest'])
+	$focus_element[] = ($fid) ? 'req_subject' : 'req_message';
+else
+{
+	$required_fields['req_username'] = $lang_post['Guest name'];
+	$focus_element[] = 'req_username';
+}
+
+define('PUN_ACTIVE_PAGE', 'index');
+require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+	<div class="inbox">
+		<ul class="crumbs">
+			<li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+			<li><span>» </span><a href="viewforum.php?id=<?php echo $cur_posting['id'] ?>"><?php echo pun_htmlspecialchars($cur_posting['forum_name']) ?></a></li>
+<?php if (isset($cur_posting['subject'])): ?>			<li><span>» </span><a href="viewtopic.php?id=<?php echo $tid ?>"><?php echo pun_htmlspecialchars($cur_posting['subject']) ?></a></li>
+<?php endif; ?>			<li><span>» </span><strong><?php echo $action ?></strong></li>
+		</ul>
+	</div>
+</div>
+
+<?php
+
+// If there are errors, we display them
+if (!empty($errors))
+{
+
+?>
+<div id="posterror" class="block">
+	<h2><span><?php echo $lang_post['Post errors'] ?></span></h2>
+	<div class="box">
+		<div class="inbox error-info">
+			<p><?php echo $lang_post['Post errors info'] ?></p>
+			<ul class="error-list">
+<?php
+
+	foreach ($errors as $cur_error)
+		echo "\t\t\t\t".'<li><strong>'.$cur_error.'</strong></li>'."\n";
+?>
+			</ul>
+		</div>
+	</div>
+</div>
+
+<?php
+
+}
+else if (isset($_POST['preview']))
+{
+	require_once PUN_ROOT.'include/parser.php';
+	$preview_message = parse_message($message, $hide_smilies);
+
+?>
+<div id="postpreview" class="blockpost">
+	<h2><span><?php echo $lang_post['Post preview'] ?></span></h2>
+	<div class="box">
+		<div class="inbox">
+			<div class="postbody">
+				<div class="postright">
+					<div class="postmsg">
+						<?php echo $preview_message."\n" ?>
+					</div>
+				</div>
+			</div>
+		</div>
+	</div>
+</div>
+
+<?php
+
+}
+
+
+$cur_index = 1;
+
+?>
+<div id="postform" class="blockform">
+	<h2><span><?php echo $action ?></span></h2>
+	<div class="box">
+		<?php echo $form."\n" ?>
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_common['Write message legend'] ?></legend>
+					<div class="infldset txtarea">
+						<input type="hidden" name="form_sent" value="1" />
+						<input type="hidden" name="form_user" value="<?php echo (!$pun_user['is_guest']) ? pun_htmlspecialchars($pun_user['username']) : 'Guest'; ?>" />
+<?php
+
+if ($pun_user['is_guest'])
+{
+	$email_label = ($pun_config['p_force_guest_email'] == '1') ? '<strong>'.$lang_common['Email'].' <span>'.$lang_common['Required'].'</span></strong>' : $lang_common['Email'];
+	$email_form_name = ($pun_config['p_force_guest_email'] == '1') ? 'req_email' : 'email';
+
+?>
+						<label class="conl required"><strong><?php echo $lang_post['Guest name'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="text" name="req_username" value="<?php if (isset($_POST['req_username'])) echo pun_htmlspecialchars($username); ?>" size="25" maxlength="25" tabindex="<?php echo $cur_index++ ?>" /><br /></label>
+						<label class="conl<?php echo ($pun_config['p_force_guest_email'] == '1') ? ' required' : '' ?>"><?php echo $email_label ?><br /><input type="text" name="<?php echo $email_form_name ?>" value="<?php if (isset($_POST[$email_form_name])) echo pun_htmlspecialchars($email); ?>" size="50" maxlength="80" tabindex="<?php echo $cur_index++ ?>" /><br /></label>
+						<div class="clearer"></div>
+<?php
+
+}
+
+if ($fid): ?>
+						<label class="required"><strong><?php echo $lang_common['Subject'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input class="longinput" type="text" name="req_subject" value="<?php if (isset($_POST['req_subject'])) echo pun_htmlspecialchars($subject); ?>" size="80" maxlength="70" tabindex="<?php echo $cur_index++ ?>" /><br /></label>
+<?php endif; ?>						<label class="required"><strong><?php echo $lang_common['Message'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
+						<textarea name="req_message" rows="20" cols="95" tabindex="<?php echo $cur_index++ ?>"><?php echo isset($_POST['req_message']) ? pun_htmlspecialchars($message) : (isset($quote) ? $quote : ''); ?></textarea><br /></label>
+						<ul class="bblinks">
+							<li><span><a href="help.php#bbcode" onclick="window.open(this.href); return false;"><?php echo $lang_common['BBCode'] ?></a> <?php echo ($pun_config['p_message_bbcode'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+							<li><span><a href="help.php#img" onclick="window.open(this.href); return false;"><?php echo $lang_common['img tag'] ?></a> <?php echo ($pun_config['p_message_img_tag'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+							<li><span><a href="help.php#smilies" onclick="window.open(this.href); return false;"><?php echo $lang_common['Smilies'] ?></a> <?php echo ($pun_config['o_smilies'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+						</ul>
+					</div>
+				</fieldset>
+<?php
+
+$checkboxes = array();
+if (!$pun_user['is_guest'])
+{
+	if ($pun_config['o_smilies'] == '1')
+		$checkboxes[] = '<label><input type="checkbox" name="hide_smilies" value="1" tabindex="'.($cur_index++).'"'.(isset($_POST['hide_smilies']) ? ' checked="checked"' : '').' />'.$lang_post['Hide smilies'].'<br /></label>';
+
+	if ($pun_config['o_subscriptions'] == '1')
+	{
+		$subscr_checked = false;
+
+		// If it's a preview
+		if (isset($_POST['preview']))
+			$subscr_checked = isset($_POST['subscribe']) ? true : false;
+		// If auto subscribed
+		else if ($pun_user['auto_notify'])
+			$subscr_checked = true;
+		// If already subscribed to the topic
+		else if ($is_subscribed)
+			$subscr_checked = true;
+
+		$checkboxes[] = '<label><input type="checkbox" name="subscribe" value="1" tabindex="'.($cur_index++).'"'.($subscr_checked ? ' checked="checked"' : '').' />'.($is_subscribed ? $lang_post['Stay subscribed'] : $lang_post['Subscribe']).'<br /></label>';
+	}
+}
+else if ($pun_config['o_smilies'] == '1')
+	$checkboxes[] = '<label><input type="checkbox" name="hide_smilies" value="1" tabindex="'.($cur_index++).'"'.(isset($_POST['hide_smilies']) ? ' checked="checked"' : '').' />'.$lang_post['Hide smilies'].'<br /></label>';
+
+if (!empty($checkboxes))
+{
+
+?>
+			</div>
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_common['Options'] ?></legend>
+					<div class="infldset">
+						<div class="rbox">
+							<?php echo implode("\n\t\t\t\t\t\t\t", $checkboxes)."\n" ?>
+						</div>
+					</div>
+				</fieldset>
+<?php
+
+}
+
+?>
+			</div>
+			<p class="buttons"><input type="submit" name="submit" value="<?php echo $lang_common['Submit'] ?>" tabindex="<?php echo $cur_index++ ?>" accesskey="s" /> <input type="submit" name="preview" value="<?php echo $lang_post['Preview'] ?>" tabindex="<?php echo $cur_index++ ?>" accesskey="p" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+		</form>
+	</div>
+</div>
+
+<?php
+
+// Check to see if the topic review is to be displayed
+if ($tid && $pun_config['o_topic_review'] != '0')
+{
+	require_once PUN_ROOT.'include/parser.php';
+
+	$result = $db->query('SELECT poster, message, hide_smilies, posted FROM '.$db->prefix.'posts WHERE topic_id='.$tid.' ORDER BY id DESC LIMIT '.$pun_config['o_topic_review']) or error('Unable to fetch topic review', __FILE__, __LINE__, $db->error());
+
+?>
+
+<div id="postreview">
+	<h2><span><?php echo $lang_post['Topic review'] ?></span></h2>
+<?php
+
+	// Set background switching on
+	$post_count = 0;
+
+	while ($cur_post = $db->fetch_assoc($result))
+	{
+		$post_count++;
+
+		$cur_post['message'] = parse_message($cur_post['message'], $cur_post['hide_smilies']);
+
+?>
+	<div class="blockpost">
+		<div class="box<?php echo ($post_count % 2 == 0) ? ' roweven' : ' rowodd' ?>">
+			<div class="inbox">
+				<div class="postbody">
+					<div class="postleft">
+						<dl>
+							<dt><strong><?php echo pun_htmlspecialchars($cur_post['poster']) ?></strong></dt>
+							<dd><span><?php echo format_time($cur_post['posted']) ?></span></dd>
+						</dl>
+					</div>
+					<div class="postright">
+						<div class="postmsg">
+							<?php echo $cur_post['message']."\n" ?>
+						</div>
+					</div>
+				</div>
+				<div class="clearer"></div>
+			</div>
+		</div>
+	</div>
+<?php
+
+	}
+
+?>
+</div>
+<?php
+
+}
+
+require PUN_ROOT.'footer.php';
diff --git a/profile.php b/profile.php
new file mode 100644
index 0000000..53110d9
--- /dev/null
+++ b/profile.php
@@ -0,0 +1,1718 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+
+// Include UTF-8 function
+require PUN_ROOT.'include/utf8/substr_replace.php';
+require PUN_ROOT.'include/utf8/ucwords.php'; // utf8_ucwords needs utf8_substr_replace
+require PUN_ROOT.'include/utf8/strcasecmp.php';
+
+$action = isset($_GET['action']) ? $_GET['action'] : null;
+$section = isset($_GET['section']) ? $_GET['section'] : null;
+$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
+if ($id < 2)
+	message($lang_common['Bad request']);
+
+if ($action != 'change_pass' || !isset($_GET['key']))
+{
+	if ($pun_user['g_read_board'] == '0')
+		message($lang_common['No view']);
+	else if ($pun_user['g_view_users'] == '0' && ($pun_user['is_guest'] || $pun_user['id'] != $id))
+		message($lang_common['No permission']);
+}
+
+// Load the profile.php/register.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/prof_reg.php';
+
+// Load the profile.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/profile.php';
+
+
+if ($action == 'change_pass')
+{
+	if (isset($_GET['key']))
+	{
+		// If the user is already logged in we shouldn't be here :)
+		if (!$pun_user['is_guest'])
+		{
+			header('Location: index.php');
+			exit;
+		}
+
+		$key = $_GET['key'];
+
+		$result = $db->query('SELECT * FROM '.$db->prefix.'users WHERE id='.$id) or error('Unable to fetch new password', __FILE__, __LINE__, $db->error());
+		$cur_user = $db->fetch_assoc($result);
+
+		if ($key == '' || $key != $cur_user['activate_key'])
+			message($lang_profile['Pass key bad'].' <a href="mailto:'.$pun_config['o_admin_email'].'">'.$pun_config['o_admin_email'].'</a>.');
+		else
+		{
+			$db->query('UPDATE '.$db->prefix.'users SET password=\''.$cur_user['activate_string'].'\', activate_string=NULL, activate_key=NULL'.(!empty($cur_user['salt']) ? ', salt=NULL' : '').' WHERE id='.$id) or error('Unable to update password', __FILE__, __LINE__, $db->error());
+
+			message($lang_profile['Pass updated'], true);
+		}
+	}
+
+	// Make sure we are allowed to change this users password
+	if ($pun_user['id'] != $id)
+	{
+		if (!$pun_user['is_admmod']) // A regular user trying to change another users password?
+			message($lang_common['No permission']);
+		else if ($pun_user['g_moderator'] == '1') // A moderator trying to change a users password?
+		{
+			$result = $db->query('SELECT u.group_id, g.g_moderator FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON (g.g_id=u.group_id) WHERE u.id='.$id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+			if (!$db->num_rows($result))
+				message($lang_common['Bad request']);
+
+			list($group_id, $is_moderator) = $db->fetch_row($result);
+
+			if ($pun_user['g_mod_edit_users'] == '0' || $pun_user['g_mod_change_passwords'] == '0' || $group_id == PUN_ADMIN || $is_moderator == '1')
+				message($lang_common['No permission']);
+		}
+	}
+
+	if (isset($_POST['form_sent']))
+	{
+		if ($pun_user['is_admmod'])
+			confirm_referrer('profile.php');
+
+		$old_password = isset($_POST['req_old_password']) ? pun_trim($_POST['req_old_password']) : '';
+		$new_password1 = pun_trim($_POST['req_new_password1']);
+		$new_password2 = pun_trim($_POST['req_new_password2']);
+
+		if ($new_password1 != $new_password2)
+			message($lang_prof_reg['Pass not match']);
+		if (pun_strlen($new_password1) < 4)
+			message($lang_prof_reg['Pass too short']);
+
+		$result = $db->query('SELECT * FROM '.$db->prefix.'users WHERE id='.$id) or error('Unable to fetch password', __FILE__, __LINE__, $db->error());
+		$cur_user = $db->fetch_assoc($result);
+
+		$authorized = false;
+
+		if (!empty($cur_user['password']))
+		{
+			$old_password_hash = pun_hash($old_password);
+
+			if ($cur_user['password'] == $old_password_hash || $pun_user['is_admmod'])
+				$authorized = true;
+		}
+
+		if (!$authorized)
+			message($lang_profile['Wrong pass']);
+
+		$new_password_hash = pun_hash($new_password1);
+
+		$db->query('UPDATE '.$db->prefix.'users SET password=\''.$new_password_hash.'\''.(!empty($cur_user['salt']) ? ', salt=NULL' : '').' WHERE id='.$id) or error('Unable to update password', __FILE__, __LINE__, $db->error());
+
+		if ($pun_user['id'] == $id)
+			pun_setcookie($pun_user['id'], $new_password_hash, time() + $pun_config['o_timeout_visit']);
+
+		redirect('profile.php?section=essentials&id='.$id, $lang_profile['Pass updated redirect']);
+	}
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Change pass']);
+	$required_fields = array('req_old_password' => $lang_profile['Old pass'], 'req_new_password1' => $lang_profile['New pass'], 'req_new_password2' => $lang_profile['Confirm new pass']);
+	$focus_element = array('change_pass', ((!$pun_user['is_admmod']) ? 'req_old_password' : 'req_new_password1'));
+	define('PUN_ACTIVE_PAGE', 'profile');
+	require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+	<h2><span><?php echo $lang_profile['Change pass'] ?></span></h2>
+	<div class="box">
+		<form id="change_pass" method="post" action="profile.php?action=change_pass&id=<?php echo $id ?>" onsubmit="return process_form(this)">
+			<div class="inform">
+				<input type="hidden" name="form_sent" value="1" />
+				<fieldset>
+					<legend><?php echo $lang_profile['Change pass legend'] ?></legend>
+					<div class="infldset">
+<?php if (!$pun_user['is_admmod']): ?>						<label class="required"><strong><?php echo $lang_profile['Old pass'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
+						<input type="password" name="req_old_password" size="16" /><br /></label>
+<?php endif; ?>						<label class="conl required"><strong><?php echo $lang_profile['New pass'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
+						<input type="password" name="req_new_password1" size="16" /><br /></label>
+						<label class="conl required"><strong><?php echo $lang_profile['Confirm new pass'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
+						<input type="password" name="req_new_password2" size="16" /><br /></label>
+						<p class="clearb"><?php echo $lang_profile['Pass info'] ?></p>
+					</div>
+				</fieldset>
+			</div>
+			<p class="buttons"><input type="submit" name="update" value="<?php echo $lang_common['Submit'] ?>" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+		</form>
+	</div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+
+else if ($action == 'change_email')
+{
+	// Make sure we are allowed to change this users email
+	if ($pun_user['id'] != $id)
+	{
+		if (!$pun_user['is_admmod']) // A regular user trying to change another users email?
+			message($lang_common['No permission']);
+		else if ($pun_user['g_moderator'] == '1') // A moderator trying to change a users email?
+		{
+			$result = $db->query('SELECT u.group_id, g.g_moderator FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON (g.g_id=u.group_id) WHERE u.id='.$id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+			if (!$db->num_rows($result))
+				message($lang_common['Bad request']);
+
+			list($group_id, $is_moderator) = $db->fetch_row($result);
+
+			if ($pun_user['g_mod_edit_users'] == '0' || $group_id == PUN_ADMIN || $is_moderator == '1')
+				message($lang_common['No permission']);
+		}
+	}
+
+	if (isset($_GET['key']))
+	{
+		$key = $_GET['key'];
+
+		$result = $db->query('SELECT activate_string, activate_key FROM '.$db->prefix.'users WHERE id='.$id) or error('Unable to fetch activation data', __FILE__, __LINE__, $db->error());
+		list($new_email, $new_email_key) = $db->fetch_row($result);
+
+		if ($key == '' || $key != $new_email_key)
+			message($lang_profile['Email key bad'].' <a href="mailto:'.$pun_config['o_admin_email'].'">'.$pun_config['o_admin_email'].'</a>.');
+		else
+		{
+			$db->query('UPDATE '.$db->prefix.'users SET email=activate_string, activate_string=NULL, activate_key=NULL WHERE id='.$id) or error('Unable to update email address', __FILE__, __LINE__, $db->error());
+
+			message($lang_profile['Email updated'], true);
+		}
+	}
+	else if (isset($_POST['form_sent']))
+	{
+		if (pun_hash($_POST['req_password']) !== $pun_user['password'])
+			message($lang_profile['Wrong pass']);
+
+		require PUN_ROOT.'include/email.php';
+
+		// Validate the email address
+		$new_email = strtolower(trim($_POST['req_new_email']));
+		if (!is_valid_email($new_email))
+			message($lang_common['Invalid email']);
+
+		// Check if it's a banned email address
+		if (is_banned_email($new_email))
+		{
+			if ($pun_config['p_allow_banned_email'] == '0')
+				message($lang_prof_reg['Banned email']);
+			else if ($pun_config['o_mailing_list'] != '')
+			{
+				$mail_subject = $lang_common['Banned email notification'];
+				$mail_message = sprintf($lang_common['Banned email change message'], $pun_user['username'], $new_email)."\n";
+				$mail_message .= sprintf($lang_common['User profile'], $pun_config['o_base_url'].'/profile.php?id='.$id)."\n";
+				$mail_message .= "\n".'--'."\n".$lang_common['Email signature'];
+
+				pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
+			}
+		}
+
+		// Check if someone else already has registered with that email address
+		$result = $db->query('SELECT id, username FROM '.$db->prefix.'users WHERE email=\''.$db->escape($new_email).'\'') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+		if ($db->num_rows($result))
+		{
+			if ($pun_config['p_allow_dupe_email'] == '0')
+				message($lang_prof_reg['Dupe email']);
+			else if ($pun_config['o_mailing_list'] != '')
+			{
+				while ($cur_dupe = $db->fetch_assoc($result))
+					$dupe_list[] = $cur_dupe['username'];
+
+				$mail_subject = $lang_common['Duplicate email notification'];
+				$mail_message = sprintf($lang_common['Duplicate email change message'], $pun_user['username'], implode(', ', $dupe_list))."\n";
+				$mail_message .= sprintf($lang_common['User profile'], $pun_config['o_base_url'].'/profile.php?id='.$id)."\n";
+				$mail_message .= "\n".'--'."\n".$lang_common['Email signature'];
+
+				pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
+			}
+		}
+
+
+		$new_email_key = random_pass(8);
+
+		$db->query('UPDATE '.$db->prefix.'users SET activate_string=\''.$db->escape($new_email).'\', activate_key=\''.$new_email_key.'\' WHERE id='.$id) or error('Unable to update activation data', __FILE__, __LINE__, $db->error());
+
+		// Load the "activate email" template
+		$mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/activate_email.tpl'));
+
+		// The first row contains the subject
+		$first_crlf = strpos($mail_tpl, "\n");
+		$mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
+		$mail_message = trim(substr($mail_tpl, $first_crlf));
+
+		$mail_message = str_replace('<username>', $pun_user['username'], $mail_message);
+		$mail_message = str_replace('<base_url>', $pun_config['o_base_url'], $mail_message);
+		$mail_message = str_replace('<activation_url>', $pun_config['o_base_url'].'/profile.php?action=change_email&id='.$id.'&key='.$new_email_key, $mail_message);
+		$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'].' '.$lang_common['Mailer'], $mail_message);
+
+		pun_mail($new_email, $mail_subject, $mail_message);
+
+		message($lang_profile['Activate email sent'].' <a href="mailto:'.$pun_config['o_admin_email'].'">'.$pun_config['o_admin_email'].'</a>.', true);
+	}
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Change email']);
+	$required_fields = array('req_new_email' => $lang_profile['New email'], 'req_password' => $lang_common['Password']);
+	$focus_element = array('change_email', 'req_new_email');
+	define('PUN_ACTIVE_PAGE', 'profile');
+	require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+	<h2><span><?php echo $lang_profile['Change email'] ?></span></h2>
+	<div class="box">
+		<form id="change_email" method="post" action="profile.php?action=change_email&id=<?php echo $id ?>" id="change_email" onsubmit="return process_form(this)">
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_profile['Email legend'] ?></legend>
+					<div class="infldset">
+						<input type="hidden" name="form_sent" value="1" />
+						<label class="required"><strong><?php echo $lang_profile['New email'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="text" name="req_new_email" size="50" maxlength="80" /><br /></label>
+						<label class="required"><strong><?php echo $lang_common['Password'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="password" name="req_password" size="16" /><br /></label>
+						<p><?php echo $lang_profile['Email instructions'] ?></p>
+					</div>
+				</fieldset>
+			</div>
+			<p class="buttons"><input type="submit" name="new_email" value="<?php echo $lang_common['Submit'] ?>" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+		</form>
+	</div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+
+else if ($action == 'upload_avatar' || $action == 'upload_avatar2')
+{
+	if ($pun_config['o_avatars'] == '0')
+		message($lang_profile['Avatars disabled']);
+
+	if ($pun_user['id'] != $id && !$pun_user['is_admmod'])
+		message($lang_common['No permission']);
+
+	if (isset($_POST['form_sent']))
+	{
+		if (!isset($_FILES['req_file']))
+			message($lang_profile['No file']);
+
+		$uploaded_file = $_FILES['req_file'];
+
+		// Make sure the upload went smooth
+		if (isset($uploaded_file['error']))
+		{
+			switch ($uploaded_file['error'])
+			{
+				case 1: // UPLOAD_ERR_INI_SIZE
+				case 2: // UPLOAD_ERR_FORM_SIZE
+					message($lang_profile['Too large ini']);
+					break;
+
+				case 3: // UPLOAD_ERR_PARTIAL
+					message($lang_profile['Partial upload']);
+					break;
+
+				case 4: // UPLOAD_ERR_NO_FILE
+					message($lang_profile['No file']);
+					break;
+
+				case 6: // UPLOAD_ERR_NO_TMP_DIR
+					message($lang_profile['No tmp directory']);
+					break;
+
+				default:
+					// No error occured, but was something actually uploaded?
+					if ($uploaded_file['size'] == 0)
+						message($lang_profile['No file']);
+					break;
+			}
+		}
+
+		if (is_uploaded_file($uploaded_file['tmp_name']))
+		{
+			// Preliminary file check, adequate in most cases
+			$allowed_types = array('image/gif', 'image/jpeg', 'image/pjpeg', 'image/png', 'image/x-png');
+			if (!in_array($uploaded_file['type'], $allowed_types))
+				message($lang_profile['Bad type']);
+
+			// Make sure the file isn't too big
+			if ($uploaded_file['size'] > $pun_config['o_avatars_size'])
+				message($lang_profile['Too large'].' '.forum_number_format($pun_config['o_avatars_size']).' '.$lang_profile['bytes'].'.');
+
+			// Move the file to the avatar directory. We do this before checking the width/height to circumvent open_basedir restrictions
+			if (!@move_uploaded_file($uploaded_file['tmp_name'], $pun_config['o_avatars_dir'].'/'.$id.'.tmp'))
+				message($lang_profile['Move failed'].' <a href="mailto:'.$pun_config['o_admin_email'].'">'.$pun_config['o_admin_email'].'</a>.');
+
+			list($width, $height, $type,) = @getimagesize($pun_config['o_avatars_dir'].'/'.$id.'.tmp');
+
+			// Determine type
+			$extension = null;
+			if ($type == IMAGETYPE_GIF)
+				$extension = '.gif';
+			else if ($type == IMAGETYPE_JPEG)
+				$extension = '.jpg';
+			else if ($type == IMAGETYPE_PNG)
+				$extension = '.png';
+			else
+			{
+				// Invalid type
+				@unlink($pun_config['o_avatars_dir'].'/'.$id.'.tmp');
+				message($lang_profile['Bad type']);
+			}
+
+			// Now check the width/height
+			if (empty($width) || empty($height) || $width > $pun_config['o_avatars_width'] || $height > $pun_config['o_avatars_height'])
+			{
+				@unlink($pun_config['o_avatars_dir'].'/'.$id.'.tmp');
+				message($lang_profile['Too wide or high'].' '.$pun_config['o_avatars_width'].'x'.$pun_config['o_avatars_height'].' '.$lang_profile['pixels'].'.');
+			}
+
+			// Delete any old avatars and put the new one in place
+			delete_avatar($id);
+			@rename($pun_config['o_avatars_dir'].'/'.$id.'.tmp', $pun_config['o_avatars_dir'].'/'.$id.$extension);
+			@chmod($pun_config['o_avatars_dir'].'/'.$id.$extension, 0644);
+		}
+		else
+			message($lang_profile['Unknown failure']);
+
+		redirect('profile.php?section=personality&id='.$id, $lang_profile['Avatar upload redirect']);
+	}
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Upload avatar']);
+	$required_fields = array('req_file' => $lang_profile['File']);
+	$focus_element = array('upload_avatar', 'req_file');
+	define('PUN_ACTIVE_PAGE', 'profile');
+	require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+	<h2><span><?php echo $lang_profile['Upload avatar'] ?></span></h2>
+	<div class="box">
+		<form id="upload_avatar" method="post" enctype="multipart/form-data" action="profile.php?action=upload_avatar2&id=<?php echo $id ?>" onsubmit="return process_form(this)">
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_profile['Upload avatar legend'] ?></legend>
+					<div class="infldset">
+						<input type="hidden" name="form_sent" value="1" />
+						<input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $pun_config['o_avatars_size'] ?>" />
+						<label class="required"><strong><?php echo $lang_profile['File'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input name="req_file" type="file" size="40" /><br /></label>
+						<p><?php echo $lang_profile['Avatar desc'].' '.$pun_config['o_avatars_width'].' x '.$pun_config['o_avatars_height'].' '.$lang_profile['pixels'].' '.$lang_common['and'].' '.forum_number_format($pun_config['o_avatars_size']).' '.$lang_profile['bytes'].' ('.forum_number_format(ceil($pun_config['o_avatars_size'] / 1024)) ?> KB).</p>
+					</div>
+				</fieldset>
+			</div>
+			<p class="buttons"><input type="submit" name="upload" value="<?php echo $lang_profile['Upload'] ?>" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+		</form>
+	</div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+
+else if ($action == 'delete_avatar')
+{
+	if ($pun_user['id'] != $id && !$pun_user['is_admmod'])
+		message($lang_common['No permission']);
+
+	confirm_referrer('profile.php');
+
+	delete_avatar($id);
+
+	redirect('profile.php?section=personality&id='.$id, $lang_profile['Avatar deleted redirect']);
+}
+
+
+else if (isset($_POST['update_group_membership']))
+{
+	if ($pun_user['g_id'] > PUN_ADMIN)
+		message($lang_common['No permission']);
+
+	confirm_referrer('profile.php');
+
+	$new_group_id = intval($_POST['group_id']);
+
+	$db->query('UPDATE '.$db->prefix.'users SET group_id='.$new_group_id.' WHERE id='.$id) or error('Unable to change user group', __FILE__, __LINE__, $db->error());
+
+	$result = $db->query('SELECT g_moderator FROM '.$db->prefix.'groups WHERE g_id='.$new_group_id) or error('Unable to fetch group', __FILE__, __LINE__, $db->error());
+	$new_group_mod = $db->result($result);
+
+	// If the user was a moderator or an administrator, we remove him/her from the moderator list in all forums as well
+	if ($new_group_id != PUN_ADMIN && $new_group_mod != '1')
+	{
+		$result = $db->query('SELECT id, moderators FROM '.$db->prefix.'forums') or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
+
+		while ($cur_forum = $db->fetch_assoc($result))
+		{
+			$cur_moderators = ($cur_forum['moderators'] != '') ? unserialize($cur_forum['moderators']) : array();
+
+			if (in_array($id, $cur_moderators))
+			{
+				$username = array_search($id, $cur_moderators);
+				unset($cur_moderators[$username]);
+				$cur_moderators = (!empty($cur_moderators)) ? '\''.$db->escape(serialize($cur_moderators)).'\'' : 'NULL';
+
+				$db->query('UPDATE '.$db->prefix.'forums SET moderators='.$cur_moderators.' WHERE id='.$cur_forum['id']) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
+			}
+		}
+	}
+
+	redirect('profile.php?section=admin&id='.$id, $lang_profile['Group membership redirect']);
+}
+
+
+else if (isset($_POST['update_forums']))
+{
+	if ($pun_user['g_id'] > PUN_ADMIN)
+		message($lang_common['No permission']);
+
+	confirm_referrer('profile.php');
+
+	// Get the username of the user we are processing
+	$result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE id='.$id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+	$username = $db->result($result);
+
+	$moderator_in = (isset($_POST['moderator_in'])) ? array_keys($_POST['moderator_in']) : array();
+
+	// Loop through all forums
+	$result = $db->query('SELECT id, moderators FROM '.$db->prefix.'forums') or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
+
+	while ($cur_forum = $db->fetch_assoc($result))
+	{
+		$cur_moderators = ($cur_forum['moderators'] != '') ? unserialize($cur_forum['moderators']) : array();
+		// If the user should have moderator access (and he/she doesn't already have it)
+		if (in_array($cur_forum['id'], $moderator_in) && !in_array($id, $cur_moderators))
+		{
+			$cur_moderators[$username] = $id;
+			uksort($cur_moderators, 'utf8_strcasecmp');
+
+			$db->query('UPDATE '.$db->prefix.'forums SET moderators=\''.$db->escape(serialize($cur_moderators)).'\' WHERE id='.$cur_forum['id']) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
+		}
+		// If the user shouldn't have moderator access (and he/she already has it)
+		else if (!in_array($cur_forum['id'], $moderator_in) && in_array($id, $cur_moderators))
+		{
+			unset($cur_moderators[$username]);
+			$cur_moderators = (!empty($cur_moderators)) ? '\''.$db->escape(serialize($cur_moderators)).'\'' : 'NULL';
+
+			$db->query('UPDATE '.$db->prefix.'forums SET moderators='.$cur_moderators.' WHERE id='.$cur_forum['id']) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
+		}
+	}
+
+	redirect('profile.php?section=admin&id='.$id, $lang_profile['Update forums redirect']);
+}
+
+
+else if (isset($_POST['ban']))
+{
+	if ($pun_user['g_id'] != PUN_ADMIN && ($pun_user['g_moderator'] != '1' || $pun_user['g_mod_ban_users'] == '0'))
+		message($lang_common['No permission']);
+
+	redirect('admin_bans.php?add_ban='.$id, $lang_profile['Ban redirect']);
+}
+
+
+else if (isset($_POST['delete_user']) || isset($_POST['delete_user_comply']))
+{
+	if ($pun_user['g_id'] > PUN_ADMIN)
+		message($lang_common['No permission']);
+
+	confirm_referrer('profile.php');
+
+	// Get the username and group of the user we are deleting
+	$result = $db->query('SELECT group_id, username FROM '.$db->prefix.'users WHERE id='.$id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+	list($group_id, $username) = $db->fetch_row($result);
+
+	if ($group_id == PUN_ADMIN)
+		message($lang_profile['No delete admin message']);
+
+	if (isset($_POST['delete_user_comply']))
+	{
+		// If the user is a moderator or an administrator, we remove him/her from the moderator list in all forums as well
+		$result = $db->query('SELECT g_moderator FROM '.$db->prefix.'groups WHERE g_id='.$group_id) or error('Unable to fetch group', __FILE__, __LINE__, $db->error());
+		$group_mod = $db->result($result);
+
+		if ($group_id == PUN_ADMIN || $group_mod == '1')
+		{
+			$result = $db->query('SELECT id, moderators FROM '.$db->prefix.'forums') or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
+
+			while ($cur_forum = $db->fetch_assoc($result))
+			{
+				$cur_moderators = ($cur_forum['moderators'] != '') ? unserialize($cur_forum['moderators']) : array();
+
+				if (in_array($id, $cur_moderators))
+				{
+					unset($cur_moderators[$username]);
+					$cur_moderators = (!empty($cur_moderators)) ? '\''.$db->escape(serialize($cur_moderators)).'\'' : 'NULL';
+
+					$db->query('UPDATE '.$db->prefix.'forums SET moderators='.$cur_moderators.' WHERE id='.$cur_forum['id']) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
+				}
+			}
+		}
+
+		// Delete any subscriptions
+		$db->query('DELETE FROM '.$db->prefix.'subscriptions WHERE user_id='.$id) or error('Unable to delete subscriptions', __FILE__, __LINE__, $db->error());
+
+		// Remove him/her from the online list (if they happen to be logged in)
+		$db->query('DELETE FROM '.$db->prefix.'online WHERE user_id='.$id) or error('Unable to remove user from online list', __FILE__, __LINE__, $db->error());
+
+		// Should we delete all posts made by this user?
+		if (isset($_POST['delete_posts']))
+		{
+			require PUN_ROOT.'include/search_idx.php';
+			@set_time_limit(0);
+
+			// Find all posts made by this user
+			$result = $db->query('SELECT p.id, p.topic_id, t.forum_id FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id WHERE p.poster_id='.$id) or error('Unable to fetch posts', __FILE__, __LINE__, $db->error());
+			if ($db->num_rows($result))
+			{
+				while ($cur_post = $db->fetch_assoc($result))
+				{
+					// Determine whether this post is the "topic post" or not
+					$result2 = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id='.$cur_post['topic_id'].' ORDER BY posted LIMIT 1') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+
+					if ($db->result($result2) == $cur_post['id'])
+						delete_topic($cur_post['topic_id']);
+					else
+						delete_post($cur_post['id'], $cur_post['topic_id']);
+
+					update_forum($cur_post['forum_id']);
+				}
+			}
+		}
+		else
+			// Set all his/her posts to guest
+			$db->query('UPDATE '.$db->prefix.'posts SET poster_id=1 WHERE poster_id='.$id) or error('Unable to update posts', __FILE__, __LINE__, $db->error());
+
+		// Delete the user
+		$db->query('DELETE FROM '.$db->prefix.'users WHERE id='.$id) or error('Unable to delete user', __FILE__, __LINE__, $db->error());
+
+		// Delete user avatar
+		delete_avatar($id);
+
+		redirect('index.php', $lang_profile['User delete redirect']);
+	}
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Confirm delete user']);
+	define('PUN_ACTIVE_PAGE', 'profile');
+	require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+	<h2><span><?php echo $lang_profile['Confirm delete user'] ?></span></h2>
+	<div class="box">
+		<form id="confirm_del_user" method="post" action="profile.php?id=<?php echo $id ?>">
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_profile['Confirm delete legend'] ?></legend>
+					<div class="infldset">
+						<p><?php echo $lang_profile['Confirmation info'].' <strong>'.pun_htmlspecialchars($username).'</strong>.' ?></p>
+						<div class="rbox">
+							<label><input type="checkbox" name="delete_posts" value="1" checked="checked" /><?php echo $lang_profile['Delete posts'] ?><br /></label>
+						</div>
+						<p class="warntext"><strong><?php echo $lang_profile['Delete warning'] ?></strong></p>
+					</div>
+				</fieldset>
+			</div>
+			<p class="buttons"><input type="submit" name="delete_user_comply" value="<?php echo $lang_profile['Delete'] ?>" /> <a href="javascript:history.go(-1)"><?php echo $lang_common['Go back'] ?></a></p>
+		</form>
+	</div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+
+else if (isset($_POST['form_sent']))
+{
+	// Fetch the user group of the user we are editing
+	$result = $db->query('SELECT u.group_id, g.g_moderator FROM '.$db->prefix.'users AS u INNER JOIN '.$db->prefix.'groups AS g ON (g.g_id=u.group_id) WHERE u.id='.$id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+	if (!$db->num_rows($result))
+		message($lang_common['Bad request']);
+
+	list($group_id, $is_moderator) = $db->fetch_row($result);
+
+	if ($pun_user['id'] != $id &&
+		(!$pun_user['is_admmod'] ||
+		($pun_user['g_moderator'] == '1' && $pun_user['g_mod_edit_users'] == '0') ||
+		($pun_user['g_moderator'] == '1' && $is_moderator)))
+		message($lang_common['No permission']);
+
+	if ($pun_user['is_admmod'])
+		confirm_referrer('profile.php');
+
+	$username_updated = false;
+
+	// Validate input depending on section
+	switch ($section)
+	{
+		case 'essentials':
+		{
+			$form = array(
+				'timezone'		=> floatval($_POST['form']['timezone']),
+				'dst'			=> isset($_POST['form']['dst']) ? '1' : '0',
+				'time_format'	=> intval($_POST['form']['time_format']),
+				'date_format'	=> intval($_POST['form']['date_format']),
+			);
+
+			// Make sure we got a valid language string
+			if (isset($_POST['form']['language']))
+			{
+				$form['language'] = preg_replace('#[\.\\\/]#', '', pun_trim($_POST['form']['language']));
+				if (!file_exists(PUN_ROOT.'lang/'.$form['language'].'/common.php'))
+						message($lang_common['Bad request']);
+			}
+
+			if ($pun_user['is_admmod'])
+			{
+				$form['admin_note'] = pun_trim($_POST['admin_note']);
+
+				// Are we allowed to change usernames?
+				if ($pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_moderator'] == '1' && $pun_user['g_mod_rename_users'] == '1'))
+				{
+					$form['username'] = pun_trim($_POST['req_username']);
+					$old_username = pun_trim($_POST['old_username']);
+
+					// Check username
+					require PUN_ROOT.'lang/'.$pun_user['language'].'/register.php';
+
+					$errors = array();
+					check_username($form['username'], $id);
+					if (!empty($errors))
+						message($errors[0]);
+
+					if ($form['username'] != $old_username)
+						$username_updated = true;
+				}
+
+				// We only allow administrators to update the post count
+				if ($pun_user['g_id'] == PUN_ADMIN)
+					$form['num_posts'] = intval($_POST['num_posts']);
+			}
+
+			if ($pun_config['o_regs_verify'] == '0' || $pun_user['is_admmod'])
+			{
+				require PUN_ROOT.'include/email.php';
+
+				// Validate the email address
+				$form['email'] = strtolower(trim($_POST['req_email']));
+				if (!is_valid_email($form['email']))
+					message($lang_common['Invalid email']);
+			}
+
+			break;
+		}
+
+		case 'personal':
+		{
+			$form = array(
+				'realname'		=> pun_trim($_POST['form']['realname']),
+				'url'			=> pun_trim($_POST['form']['url']),
+				'location'		=> pun_trim($_POST['form']['location']),
+			);
+
+			// Add http:// if the URL doesn't contain it already (while allowing https://, too)
+			if ($form['url'] != '' && !preg_match('#^https?://#i', $form['url']))
+				$form['url'] = 'http://'.$form['url'];
+
+			if ($pun_user['g_id'] == PUN_ADMIN)
+				$form['title'] = pun_trim($_POST['title']);
+			else if ($pun_user['g_set_title'] == '1')
+			{
+				$form['title'] = pun_trim($_POST['title']);
+
+				if ($form['title'] != '')
+				{
+					// A list of words that the title may not contain
+					// If the language is English, there will be some duplicates, but it's not the end of the world
+					$forbidden = array('member', 'moderator', 'administrator', 'banned', 'guest', utf8_strtolower($lang_common['Member']), utf8_strtolower($lang_common['Moderator']), utf8_strtolower($lang_common['Administrator']), utf8_strtolower($lang_common['Banned']), utf8_strtolower($lang_common['Guest']));
+
+					if (in_array(utf8_strtolower($form['title']), $forbidden))
+						message($lang_profile['Forbidden title']);
+				}
+			}
+
+			break;
+		}
+
+		case 'messaging':
+		{
+			$form = array(
+				'jabber'		=> pun_trim($_POST['form']['jabber']),
+				'icq'			=> pun_trim($_POST['form']['icq']),
+				'msn'			=> pun_trim($_POST['form']['msn']),
+				'aim'			=> pun_trim($_POST['form']['aim']),
+				'yahoo'			=> pun_trim($_POST['form']['yahoo']),
+			);
+
+			// If the ICQ UIN contains anything other than digits it's invalid
+			if (preg_match('/[^0-9]/', $form['icq']))
+				message($lang_prof_reg['Bad ICQ']);
+
+			break;
+		}
+
+		case 'personality':
+		{
+			$form = array();
+
+			// Clean up signature from POST
+			if ($pun_config['o_signatures'] == '1')
+			{
+				$form['signature'] = pun_linebreaks(pun_trim($_POST['signature']));
+
+				// Validate signature
+				if (pun_strlen($form['signature']) > $pun_config['p_sig_length'])
+					message(sprintf($lang_prof_reg['Sig too long'], $pun_config['p_sig_length'], pun_strlen($form['signature']) - $pun_config['p_sig_length']));
+				else if (substr_count($form['signature'], "\n") > ($pun_config['p_sig_lines']-1))
+					message(sprintf($lang_prof_reg['Sig too many lines'], $pun_config['p_sig_lines']));
+				else if ($form['signature'] && $pun_config['p_sig_all_caps'] == '0' && is_all_uppercase($form['signature']) && !$pun_user['is_admmod'])
+					$form['signature'] = utf8_ucwords(utf8_strtolower($form['signature']));
+
+				// Validate BBCode syntax
+				if ($pun_config['p_sig_bbcode'] == '1')
+				{
+					require PUN_ROOT.'include/parser.php';
+
+					$errors = array();
+
+					$form['signature'] = preparse_bbcode($form['signature'], $errors, true);
+
+					if(count($errors) > 0)
+						message('<ul><li>'.implode('</li><li>', $errors).'</li></ul>');
+				}
+			}
+
+			break;
+		}
+
+		case 'display':
+		{
+			$form = array(
+				'disp_topics'		=> pun_trim($_POST['form']['disp_topics']),
+				'disp_posts'		=> pun_trim($_POST['form']['disp_posts']),
+				'show_smilies'		=> isset($_POST['form']['show_smilies']) ? '1' : '0',
+				'show_img'			=> isset($_POST['form']['show_img']) ? '1' : '0',
+				'show_img_sig'		=> isset($_POST['form']['show_img_sig']) ? '1' : '0',
+				'show_avatars'		=> isset($_POST['form']['show_avatars']) ? '1' : '0',
+				'show_sig'			=> isset($_POST['form']['show_sig']) ? '1' : '0',
+			);
+
+			if ($form['disp_topics'] != '')
+			{
+				$form['disp_topics'] = intval($form['disp_topics']);
+				if ($form['disp_topics'] < 3)
+					$form['disp_topics'] = 3;
+				else if ($form['disp_topics'] > 75)
+					$form['disp_topics'] = 75;
+			}
+
+			if ($form['disp_posts'] != '')
+			{
+				$form['disp_posts'] = intval($form['disp_posts']);
+				if ($form['disp_posts'] < 3)
+					$form['disp_posts'] = 3;
+				else if ($form['disp_posts'] > 75)
+					$form['disp_posts'] = 75;
+			}
+
+			// Make sure we got a valid style string
+			if (isset($_POST['form']['style']))
+			{
+				$form['style'] = preg_replace('#[\.\\\/]#', '', pun_trim($_POST['form']['style']));
+				if (!file_exists(PUN_ROOT.'style/'.$form['style'].'.css'))
+						message($lang_common['Bad request']);
+			}
+
+			break;
+		}
+
+		case 'privacy':
+		{
+			$form = array(
+				'email_setting'			=> intval($_POST['form']['email_setting']),
+				'notify_with_post'		=> isset($_POST['form']['notify_with_post']) ? '1' : '0',
+				'auto_notify'			=> isset($_POST['form']['auto_notify']) ? '1' : '0',
+			);
+
+			if ($form['email_setting'] < 0 || $form['email_setting'] > 2)
+				$form['email_setting'] = $pun_config['o_default_email_setting'];
+
+			break;
+		}
+
+		default:
+			message($lang_common['Bad request']);
+	}
+
+
+	// Single quotes around non-empty values and NULL for empty values
+	$temp = array();
+	foreach ($form as $key => $input)
+	{
+		$value = ($input !== '') ? '\''.$db->escape($input).'\'' : 'NULL';
+
+		$temp[] = $key.'='.$value;
+	}
+
+	if (empty($temp))
+		message($lang_common['Bad request']);
+
+
+	$db->query('UPDATE '.$db->prefix.'users SET '.implode(',', $temp).' WHERE id='.$id) or error('Unable to update profile', __FILE__, __LINE__, $db->error());
+
+	// If we changed the username we have to update some stuff
+	if ($username_updated)
+	{
+		$db->query('UPDATE '.$db->prefix.'posts SET poster=\''.$db->escape($form['username']).'\' WHERE poster_id='.$id) or error('Unable to update posts', __FILE__, __LINE__, $db->error());
+		$db->query('UPDATE '.$db->prefix.'posts SET edited_by=\''.$db->escape($form['username']).'\' WHERE edited_by=\''.$db->escape($old_username).'\'') or error('Unable to update posts', __FILE__, __LINE__, $db->error());
+		$db->query('UPDATE '.$db->prefix.'topics SET poster=\''.$db->escape($form['username']).'\' WHERE poster=\''.$db->escape($old_username).'\'') or error('Unable to update topics', __FILE__, __LINE__, $db->error());
+		$db->query('UPDATE '.$db->prefix.'topics SET last_poster=\''.$db->escape($form['username']).'\' WHERE last_poster=\''.$db->escape($old_username).'\'') or error('Unable to update topics', __FILE__, __LINE__, $db->error());
+		$db->query('UPDATE '.$db->prefix.'forums SET last_poster=\''.$db->escape($form['username']).'\' WHERE last_poster=\''.$db->escape($old_username).'\'') or error('Unable to update forums', __FILE__, __LINE__, $db->error());
+		$db->query('UPDATE '.$db->prefix.'online SET ident=\''.$db->escape($form['username']).'\' WHERE ident=\''.$db->escape($old_username).'\'') or error('Unable to update online list', __FILE__, __LINE__, $db->error());
+
+		// If the user is a moderator or an administrator we have to update the moderator lists
+		$result = $db->query('SELECT group_id FROM '.$db->prefix.'users WHERE id='.$id) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+		$group_id = $db->result($result);
+
+		$result = $db->query('SELECT g_moderator FROM '.$db->prefix.'groups WHERE g_id='.$group_id) or error('Unable to fetch group', __FILE__, __LINE__, $db->error());
+		$group_mod = $db->result($result);
+
+		if ($group_id == PUN_ADMIN || $group_mod == '1')
+		{
+			$result = $db->query('SELECT id, moderators FROM '.$db->prefix.'forums') or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
+
+			while ($cur_forum = $db->fetch_assoc($result))
+			{
+				$cur_moderators = ($cur_forum['moderators'] != '') ? unserialize($cur_forum['moderators']) : array();
+
+				if (in_array($id, $cur_moderators))
+				{
+					unset($cur_moderators[$old_username]);
+					$cur_moderators[$form['username']] = $id;
+					uksort($cur_moderators, 'utf8_strcasecmp');
+
+					$db->query('UPDATE '.$db->prefix.'forums SET moderators=\''.$db->escape(serialize($cur_moderators)).'\' WHERE id='.$cur_forum['id']) or error('Unable to update forum', __FILE__, __LINE__, $db->error());
+				}
+			}
+		}
+	}
+
+	redirect('profile.php?section='.$section.'&id='.$id, $lang_profile['Profile redirect']);
+}
+
+
+$result = $db->query('SELECT u.username, u.email, u.title, u.realname, u.url, u.jabber, u.icq, u.msn, u.aim, u.yahoo, u.location, u.signature, u.disp_topics, u.disp_posts, u.email_setting, u.notify_with_post, u.auto_notify, u.show_smilies, u.show_img, u.show_img_sig, u.show_avatars, u.show_sig, u.timezone, u.dst, u.language, u.style, u.num_posts, u.last_post, u.registered, u.registration_ip, u.admin_note, u.date_format, u.time_format, g.g_id, g.g_user_title, g.g_moderator FROM '.$db->pre [...]
+if (!$db->num_rows($result))
+	message($lang_common['Bad request']);
+
+$user = $db->fetch_assoc($result);
+
+$last_post = format_time($user['last_post']);
+
+if ($user['signature'] != '')
+{
+	require PUN_ROOT.'include/parser.php';
+	$parsed_signature = parse_signature($user['signature']);
+}
+
+
+// View or edit?
+if ($pun_user['id'] != $id &&
+	(!$pun_user['is_admmod'] ||
+	($pun_user['g_moderator'] == '1' && $pun_user['g_mod_edit_users'] == '0') ||
+	($pun_user['g_moderator'] == '1' && $user['g_moderator'] == '1')))
+{
+	$user_personal = array();
+
+	$user_personal[] = '<dt>'.$lang_common['Username'].'</dt>';
+	$user_personal[] = '<dd>'.pun_htmlspecialchars($user['username']).'</dd>';
+
+	$user_title_field = get_title($user);
+	$user_personal[] = '<dt>'.$lang_common['Title'].'</dt>';
+	$user_personal[] = '<dd>'.(($pun_config['o_censoring'] == '1') ? censor_words($user_title_field) : $user_title_field).'</dd>';
+
+	if ($user['realname'] != '')
+	{
+		$user_personal[] = '<dt>'.$lang_profile['Realname'].'</dt>';
+		$user_personal[] = '<dd>'.pun_htmlspecialchars(($pun_config['o_censoring'] == '1') ? censor_words($user['realname']) : $user['realname']).'</dd>';
+	}
+
+	if ($user['location'] != '')
+	{
+		$user_personal[] = '<dt>'.$lang_profile['Location'].'</dt>';
+		$user_personal[] = '<dd>'.pun_htmlspecialchars(($pun_config['o_censoring'] == '1') ? censor_words($user['location']) : $user['location']).'</dd>';
+	}
+
+	if ($user['url'] != '')
+	{
+		$user['url'] = pun_htmlspecialchars(($pun_config['o_censoring'] == '1') ? censor_words($user['url']) : $user['url']);
+		$user_personal[] = '<dt>'.$lang_profile['Website'].'</dt>';
+		$user_personal[] = '<dd><span class="website"><a href="'.$user['url'].'">'.$user['url'].'</a></span></dd>';
+	}
+
+	if ($user['email_setting'] == '0' && !$pun_user['is_guest'] && $pun_user['g_send_email'] == '1')
+		$email_field = '<a href="mailto:'.$user['email'].'">'.$user['email'].'</a>';
+	else if ($user['email_setting'] == '1' && !$pun_user['is_guest'] && $pun_user['g_send_email'] == '1')
+		$email_field = '<a href="misc.php?email='.$id.'">'.$lang_common['Send email'].'</a>';
+	else
+		$email_field = '';
+	if ($email_field != '')
+	{
+		$user_personal[] = '<dt>'.$lang_common['Email'].'</dt>';
+		$user_personal[] = '<dd><span class="email">'.$email_field.'</span></dd>';
+	}
+
+	$user_messaging = array();
+
+	if ($user['jabber'] != '')
+	{
+		$user_messaging[] = '<dt>'.$lang_profile['Jabber'].'</dt>';
+		$user_messaging[] = '<dd>'.pun_htmlspecialchars(($pun_config['o_censoring'] == '1') ? censor_words($user['jabber']) : $user['jabber']).'</dd>';
+	}
+
+	if ($user['icq'] != '')
+	{
+		$user_messaging[] = '<dt>'.$lang_profile['ICQ'].'</dt>';
+		$user_messaging[] = '<dd>'.$user['icq'].'</dd>';
+	}
+
+	if ($user['msn'] != '')
+	{
+		$user_messaging[] = '<dt>'.$lang_profile['MSN'].'</dt>';
+		$user_messaging[] = '<dd>'.pun_htmlspecialchars(($pun_config['o_censoring'] == '1') ? censor_words($user['msn']) : $user['msn']).'</dd>';
+	}
+
+	if ($user['aim'] != '')
+	{
+		$user_messaging[] = '<dt>'.$lang_profile['AOL IM'].'</dt>';
+		$user_messaging[] = '<dd>'.pun_htmlspecialchars(($pun_config['o_censoring'] == '1') ? censor_words($user['aim']) : $user['aim']).'</dd>';
+	}
+
+	if ($user['yahoo'] != '')
+	{
+		$user_messaging[] = '<dt>'.$lang_profile['Yahoo'].'</dt>';
+		$user_messaging[] = '<dd>'.pun_htmlspecialchars(($pun_config['o_censoring'] == '1') ? censor_words($user['yahoo']) : $user['yahoo']).'</dd>';
+	}
+
+	$user_personality = array();
+
+	if ($pun_config['o_avatars'] == '1')
+	{
+		$avatar_field = generate_avatar_markup($id);
+		if ($avatar_field != '')
+		{
+			$user_personality[] = '<dt>'.$lang_profile['Avatar'].'</dt>';
+			$user_personality[] = '<dd>'.$avatar_field.'</dd>';
+		}
+	}
+
+	if ($pun_config['o_signatures'] == '1')
+	{
+		if (isset($parsed_signature))
+		{
+			$user_personality[] = '<dt>'.$lang_profile['Signature'].'</dt>';
+			$user_personality[] = '<dd><div class="postsignature postmsg">'.$parsed_signature.'</div></dd>';
+		}
+	}
+
+	$user_activity = array();
+
+	$posts_field = '';
+	if ($pun_config['o_show_post_count'] == '1' || $pun_user['is_admmod'])
+		$posts_field = forum_number_format($user['num_posts']);
+	if ($pun_user['g_search'] == '1' && $user['num_posts'] > 0)
+		$posts_field .= (($posts_field != '') ? ' - ' : '').'<a href="search.php?action=show_user&user_id='.$id.'">'.$lang_profile['Show posts'].'</a>';
+	if ($posts_field != '')
+	{
+		$user_activity[] = '<dt>'.$lang_common['Posts'].'</dt>';
+		$user_activity[] = '<dd>'.$posts_field.'</dd>';
+	}
+
+	if ($user['num_posts'] > 0)
+	{
+		$user_activity[] = '<dt>'.$lang_common['Last post'].'</dt>';
+		$user_activity[] = '<dd>'.$last_post.'</dd>';
+	}
+
+	$user_activity[] = '<dt>'.$lang_common['Registered'].'</dt>';
+	$user_activity[] = '<dd>'.format_time($user['registered'], true).'</dd>';
+
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), sprintf($lang_profile['Users profile'], pun_htmlspecialchars($user['username'])));
+	define('PUN_ALLOW_INDEX', 1);
+	define('PUN_ACTIVE_PAGE', 'index');
+	require PUN_ROOT.'header.php';
+
+?>
+<div id="viewprofile" class="block">
+	<h2><span><?php echo $lang_common['Profile'] ?></span></h2>
+	<div class="box">
+		<div class="fakeform">
+			<div class="inform">
+				<fieldset>
+				<legend><?php echo $lang_profile['Section personal'] ?></legend>
+					<div class="infldset">
+						<dl>
+							<?php echo implode("\n\t\t\t\t\t\t\t", $user_personal)."\n" ?>
+						</dl>
+						<div class="clearer"></div>
+					</div>
+				</fieldset>
+			</div>
+<?php if (!empty($user_messaging)): ?>			<div class="inform">
+				<fieldset>
+				<legend><?php echo $lang_profile['Section messaging'] ?></legend>
+					<div class="infldset">
+						<dl>
+							<?php echo implode("\n\t\t\t\t\t\t\t", $user_messaging)."\n" ?>
+						</dl>
+						<div class="clearer"></div>
+					</div>
+				</fieldset>
+			</div>
+<?php endif; if (!empty($user_personality)): ?>			<div class="inform">
+				<fieldset>
+				<legend><?php echo $lang_profile['Section personality'] ?></legend>
+					<div class="infldset">
+						<dl>
+							<?php echo implode("\n\t\t\t\t\t\t\t", $user_personality)."\n" ?>
+						</dl>
+						<div class="clearer"></div>
+					</div>
+				</fieldset>
+			</div>
+<?php endif; ?>			<div class="inform">
+				<fieldset>
+				<legend><?php echo $lang_profile['User activity'] ?></legend>
+					<div class="infldset">
+						<dl>
+							<?php echo implode("\n\t\t\t\t\t\t\t", $user_activity)."\n" ?>
+						</dl>
+						<div class="clearer"></div>
+					</div>
+				</fieldset>
+			</div>
+		</div>
+	</div>
+</div>
+
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+else
+{
+	if (!$section || $section == 'essentials')
+	{
+		if ($pun_user['is_admmod'])
+		{
+			if ($pun_user['g_id'] == PUN_ADMIN || $pun_user['g_mod_rename_users'] == '1')
+				$username_field = '<input type="hidden" name="old_username" value="'.pun_htmlspecialchars($user['username']).'" /><label class="required"><strong>'.$lang_common['Username'].' <span>'.$lang_common['Required'].'</span></strong><br /><input type="text" name="req_username" value="'.pun_htmlspecialchars($user['username']).'" size="25" maxlength="25" /><br /></label>'."\n";
+			else
+				$username_field = '<p>'.sprintf($lang_profile['Username info'], pun_htmlspecialchars($user['username'])).'</p>'."\n";
+
+			$email_field = '<label class="required"><strong>'.$lang_common['Email'].' <span>'.$lang_common['Required'].'</span></strong><br /><input type="text" name="req_email" value="'.$user['email'].'" size="40" maxlength="80" /><br /></label><p><span class="email"><a href="misc.php?email='.$id.'">'.$lang_common['Send email'].'</a></span></p>'."\n";
+		}
+		else
+		{
+			$username_field = '<p>'.$lang_common['Username'].': '.pun_htmlspecialchars($user['username']).'</p>'."\n";
+
+			if ($pun_config['o_regs_verify'] == '1')
+				$email_field = '<p>'.sprintf($lang_profile['Email info'], $user['email'].' - <a href="profile.php?action=change_email&id='.$id.'">'.$lang_profile['Change email'].'</a>').'</p>'."\n";
+			else
+				$email_field = '<label class="required"><strong>'.$lang_common['Email'].' <span>'.$lang_common['Required'].'</span></strong><br /><input type="text" name="req_email" value="'.$user['email'].'" size="40" maxlength="80" /><br /></label>'."\n";
+		}
+
+		$posts_field = '';
+		if ($pun_user['g_id'] == PUN_ADMIN)
+			$posts_field = '<label>'.$lang_common['Posts'].'<br /><input type="text" name="num_posts" value="'.$user['num_posts'].'" size="8" maxlength="8" /><br /></label><p class="actions"><span><a href="search.php?action=show_user&user_id='.$id.'">'.$lang_profile['Show posts'].'</a></span></p>'."\n";
+		else if ($pun_config['o_show_post_count'] == '1' || $pun_user['is_admmod'])
+			$posts_field = '<p>'.sprintf($lang_profile['Posts info'], forum_number_format($user['num_posts']).($pun_user['g_search'] == '1' ? ' - <a href="search.php?action=show_user&user_id='.$id.'">'.$lang_profile['Show posts'].'</a>' : '')).'</p>'."\n";
+		else if ($pun_user['g_search'] == '1')
+			$posts_field = '<p class="actions"><span><a href="search.php?action=show_user&user_id='.$id.'">'.$lang_profile['Show posts'].'</a></span></p>'."\n";
+
+
+		$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Section essentials']);
+		$required_fields = array('req_username' => $lang_common['Username'], 'req_email' => $lang_common['Email']);
+		define('PUN_ACTIVE_PAGE', 'profile');
+		require PUN_ROOT.'header.php';
+
+		generate_profile_menu('essentials');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo pun_htmlspecialchars($user['username']).' - '.$lang_profile['Section essentials'] ?></span></h2>
+		<div class="box">
+			<form id="profile1" method="post" action="profile.php?section=essentials&id=<?php echo $id ?>" onsubmit="return process_form(this)">
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_profile['Username and pass legend'] ?></legend>
+						<div class="infldset">
+							<input type="hidden" name="form_sent" value="1" />
+							<?php echo $username_field ?>
+<?php if ($pun_user['id'] == $id || $pun_user['g_id'] == PUN_ADMIN || ($user['g_moderator'] == '0' && $pun_user['g_mod_change_passwords'] == '1')): ?>							<p class="actions"><span><a href="profile.php?action=change_pass&id=<?php echo $id ?>"><?php echo $lang_profile['Change pass'] ?></a></span></p>
+<?php endif; ?>						</div>
+					</fieldset>
+				</div>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_prof_reg['Email legend'] ?></legend>
+						<div class="infldset">
+							<?php echo $email_field ?>
+						</div>
+					</fieldset>
+				</div>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_prof_reg['Localisation legend'] ?></legend>
+						<div class="infldset">
+							<p><?php echo $lang_prof_reg['Time zone info'] ?></p>
+							<label><?php echo $lang_prof_reg['Time zone']."\n" ?>
+							<br /><select name="form[timezone]">
+								<option value="-12"<?php if ($user['timezone'] == -12) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-12:00'] ?></option>
+								<option value="-11"<?php if ($user['timezone'] == -11) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-11:00'] ?></option>
+								<option value="-10"<?php if ($user['timezone'] == -10) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-10:00'] ?></option>
+								<option value="-9.5"<?php if ($user['timezone'] == -9.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-09:30'] ?></option>
+								<option value="-9"<?php if ($user['timezone'] == -9) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-09:00'] ?></option>
+								<option value="-8.5"<?php if ($user['timezone'] == -8.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-08:30'] ?></option>
+								<option value="-8"<?php if ($user['timezone'] == -8) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-08:00'] ?></option>
+								<option value="-7"<?php if ($user['timezone'] == -7) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-07:00'] ?></option>
+								<option value="-6"<?php if ($user['timezone'] == -6) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-06:00'] ?></option>
+								<option value="-5"<?php if ($user['timezone'] == -5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-05:00'] ?></option>
+								<option value="-4"<?php if ($user['timezone'] == -4) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-04:00'] ?></option>
+								<option value="-3.5"<?php if ($user['timezone'] == -3.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-03:30'] ?></option>
+								<option value="-3"<?php if ($user['timezone'] == -3) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-03:00'] ?></option>
+								<option value="-2"<?php if ($user['timezone'] == -2) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-02:00'] ?></option>
+								<option value="-1"<?php if ($user['timezone'] == -1) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-01:00'] ?></option>
+								<option value="0"<?php if ($user['timezone'] == 0) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC'] ?></option>
+								<option value="1"<?php if ($user['timezone'] == 1) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+01:00'] ?></option>
+								<option value="2"<?php if ($user['timezone'] == 2) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+02:00'] ?></option>
+								<option value="3"<?php if ($user['timezone'] == 3) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+03:00'] ?></option>
+								<option value="3.5"<?php if ($user['timezone'] == 3.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+03:30'] ?></option>
+								<option value="4"<?php if ($user['timezone'] == 4) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+04:00'] ?></option>
+								<option value="4.5"<?php if ($user['timezone'] == 4.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+04:30'] ?></option>
+								<option value="5"<?php if ($user['timezone'] == 5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+05:00'] ?></option>
+								<option value="5.5"<?php if ($user['timezone'] == 5.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+05:30'] ?></option>
+								<option value="5.75"<?php if ($user['timezone'] == 5.75) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+05:45'] ?></option>
+								<option value="6"<?php if ($user['timezone'] == 6) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+06:00'] ?></option>
+								<option value="6.5"<?php if ($user['timezone'] == 6.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+06:30'] ?></option>
+								<option value="7"<?php if ($user['timezone'] == 7) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+07:00'] ?></option>
+								<option value="8"<?php if ($user['timezone'] == 8) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+08:00'] ?></option>
+								<option value="8.75"<?php if ($user['timezone'] == 8.75) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+08:45'] ?></option>
+								<option value="9"<?php if ($user['timezone'] == 9) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+09:00'] ?></option>
+								<option value="9.5"<?php if ($user['timezone'] == 9.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+09:30'] ?></option>
+								<option value="10"<?php if ($user['timezone'] == 10) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+10:00'] ?></option>
+								<option value="10.5"<?php if ($user['timezone'] == 10.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+10:30'] ?></option>
+								<option value="11"<?php if ($user['timezone'] == 11) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+11:00'] ?></option>
+								<option value="11.5"<?php if ($user['timezone'] == 11.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+11:30'] ?></option>
+								<option value="12"<?php if ($user['timezone'] == 12) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+12:00'] ?></option>
+								<option value="12.75"<?php if ($user['timezone'] == 12.75) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+12:45'] ?></option>
+								<option value="13"<?php if ($user['timezone'] == 13) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+13:00'] ?></option>
+								<option value="14"<?php if ($user['timezone'] == 14) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+14:00'] ?></option>
+							</select>
+							<br /></label>
+							<div class="rbox">
+								<label><input type="checkbox" name="form[dst]" value="1"<?php if ($user['dst'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_prof_reg['DST'] ?><br /></label>
+							</div>
+							<label><?php echo $lang_prof_reg['Time format'] ?>
+
+							<br /><select name="form[time_format]">
+<?php
+								foreach (array_unique($forum_time_formats) as $key => $time_format)
+								{
+									echo "\t\t\t\t\t\t\t\t".'<option value="'.$key.'"';
+									if ($user['time_format'] == $key)
+										echo ' selected="selected"';
+									echo '>'. format_time(time(), false, null, $time_format, true, true);
+									if ($key == 0)
+										echo ' ('.$lang_prof_reg['Default'].')';
+									echo "</option>\n";
+								}
+								?>
+							</select>
+							<br /></label>
+							<label><?php echo $lang_prof_reg['Date format'] ?>
+
+							<br /><select name="form[date_format]">
+<?php
+								foreach (array_unique($forum_date_formats) as $key => $date_format)
+								{
+									echo "\t\t\t\t\t\t\t\t".'<option value="'.$key.'"';
+									if ($user['date_format'] == $key)
+										echo ' selected="selected"';
+									echo '>'. format_time(time(), true, $date_format, null, false, true);
+									if ($key == 0)
+										echo ' ('.$lang_prof_reg['Default'].')';
+									echo "</option>\n";
+								}
+								?>
+							</select>
+							<br /></label>
+
+<?php
+
+		$languages = forum_list_langs();
+
+		// Only display the language selection box if there's more than one language available
+		if (count($languages) > 1)
+		{
+
+?>
+							<label><?php echo $lang_prof_reg['Language'] ?>
+							<br /><select name="form[language]">
+<?php
+
+			foreach ($languages as $temp)
+			{
+				if ($user['language'] == $temp)
+					echo "\t\t\t\t\t\t\t\t".'<option value="'.$temp.'" selected="selected">'.$temp.'</option>'."\n";
+				else
+					echo "\t\t\t\t\t\t\t\t".'<option value="'.$temp.'">'.$temp.'</option>'."\n";
+			}
+
+?>
+							</select>
+							<br /></label>
+<?php
+
+		}
+
+?>
+						</div>
+					</fieldset>
+				</div>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_profile['User activity'] ?></legend>
+						<div class="infldset">
+							<p><?php printf($lang_profile['Registered info'], format_time($user['registered'], true).(($pun_user['is_admmod']) ? ' (<a href="moderate.php?get_host='.pun_htmlspecialchars($user['registration_ip']).'">'.pun_htmlspecialchars($user['registration_ip']).'</a>)' : '')) ?></p>
+							<p><?php printf($lang_profile['Last post info'], $last_post) ?></p>
+							<?php echo $posts_field ?>
+<?php if ($pun_user['is_admmod']): ?>							<label><?php echo $lang_profile['Admin note'] ?><br />
+							<input id="admin_note" type="text" name="admin_note" value="<?php echo pun_htmlspecialchars($user['admin_note']) ?>" size="30" maxlength="30" /><br /></label>
+<?php endif; ?>						</div>
+					</fieldset>
+				</div>
+				<p class="buttons"><input type="submit" name="update" value="<?php echo $lang_common['Submit'] ?>" /> <?php echo $lang_profile['Instructions'] ?></p>
+			</form>
+		</div>
+	</div>
+<?php
+
+	}
+	else if ($section == 'personal')
+	{
+		if ($pun_user['g_set_title'] == '1')
+			$title_field = '<label>'.$lang_common['Title'].' <em>('.$lang_profile['Leave blank'].')</em><br /><input type="text" name="title" value="'.pun_htmlspecialchars($user['title']).'" size="30" maxlength="50" /><br /></label>'."\n";
+
+		$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Section personal']);
+		define('PUN_ACTIVE_PAGE', 'profile');
+		require PUN_ROOT.'header.php';
+
+		generate_profile_menu('personal');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo pun_htmlspecialchars($user['username']).' - '.$lang_profile['Section personal'] ?></span></h2>
+		<div class="box">
+			<form id="profile2" method="post" action="profile.php?section=personal&id=<?php echo $id ?>">
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_profile['Personal details legend'] ?></legend>
+						<div class="infldset">
+							<input type="hidden" name="form_sent" value="1" />
+							<label><?php echo $lang_profile['Realname'] ?><br /><input type="text" name="form[realname]" value="<?php echo pun_htmlspecialchars($user['realname']) ?>" size="40" maxlength="40" /><br /></label>
+<?php if (isset($title_field)): ?>							<?php echo $title_field ?>
+<?php endif; ?>							<label><?php echo $lang_profile['Location'] ?><br /><input type="text" name="form[location]" value="<?php echo pun_htmlspecialchars($user['location']) ?>" size="30" maxlength="30" /><br /></label>
+							<label><?php echo $lang_profile['Website'] ?><br /><input type="text" name="form[url]" value="<?php echo pun_htmlspecialchars($user['url']) ?>" size="50" maxlength="80" /><br /></label>
+						</div>
+					</fieldset>
+				</div>
+				<p class="buttons"><input type="submit" name="update" value="<?php echo $lang_common['Submit'] ?>" /> <?php echo $lang_profile['Instructions'] ?></p>
+			</form>
+		</div>
+	</div>
+<?php
+
+	}
+	else if ($section == 'messaging')
+	{
+
+		$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Section messaging']);
+		define('PUN_ACTIVE_PAGE', 'profile');
+		require PUN_ROOT.'header.php';
+
+		generate_profile_menu('messaging');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo pun_htmlspecialchars($user['username']).' - '.$lang_profile['Section messaging'] ?></span></h2>
+		<div class="box">
+			<form id="profile3" method="post" action="profile.php?section=messaging&id=<?php echo $id ?>">
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_profile['Contact details legend'] ?></legend>
+						<div class="infldset">
+							<input type="hidden" name="form_sent" value="1" />
+							<label><?php echo $lang_profile['Jabber'] ?><br /><input id="jabber" type="text" name="form[jabber]" value="<?php echo pun_htmlspecialchars($user['jabber']) ?>" size="40" maxlength="75" /><br /></label>
+							<label><?php echo $lang_profile['ICQ'] ?><br /><input id="icq" type="text" name="form[icq]" value="<?php echo $user['icq'] ?>" size="12" maxlength="12" /><br /></label>
+							<label><?php echo $lang_profile['MSN'] ?><br /><input id="msn" type="text" name="form[msn]" value="<?php echo pun_htmlspecialchars($user['msn']) ?>" size="40" maxlength="50" /><br /></label>
+							<label><?php echo $lang_profile['AOL IM'] ?><br /><input id="aim" type="text" name="form[aim]" value="<?php echo pun_htmlspecialchars($user['aim']) ?>" size="20" maxlength="30" /><br /></label>
+							<label><?php echo $lang_profile['Yahoo'] ?><br /><input id="yahoo" type="text" name="form[yahoo]" value="<?php echo pun_htmlspecialchars($user['yahoo']) ?>" size="20" maxlength="30" /><br /></label>
+						</div>
+					</fieldset>
+				</div>
+				<p class="buttons"><input type="submit" name="update" value="<?php echo $lang_common['Submit'] ?>" /> <?php echo $lang_profile['Instructions'] ?></p>
+			</form>
+		</div>
+	</div>
+<?php
+
+	}
+	else if ($section == 'personality')
+	{
+		if ($pun_config['o_avatars'] == '0' && $pun_config['o_signatures'] == '0')
+			message($lang_common['Bad request']);
+
+		$avatar_field = '<span><a href="profile.php?action=upload_avatar&id='.$id.'">'.$lang_profile['Change avatar'].'</a></span>';
+
+		$user_avatar = generate_avatar_markup($id);
+		if ($user_avatar)
+			$avatar_field .= ' <span><a href="profile.php?action=delete_avatar&id='.$id.'">'.$lang_profile['Delete avatar'].'</a></span>';
+		else
+			$avatar_field = '<span><a href="profile.php?action=upload_avatar&id='.$id.'">'.$lang_profile['Upload avatar'].'</a></span>';
+
+		if ($user['signature'] != '')
+			$signature_preview = '<p>'.$lang_profile['Sig preview'].'</p>'."\n\t\t\t\t\t\t\t".'<div class="postsignature postmsg">'."\n\t\t\t\t\t\t\t\t".'<hr />'."\n\t\t\t\t\t\t\t\t".$parsed_signature."\n\t\t\t\t\t\t\t".'</div>'."\n";
+		else
+			$signature_preview = '<p>'.$lang_profile['No sig'].'</p>'."\n";
+
+		$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Section personality']);
+		define('PUN_ACTIVE_PAGE', 'profile');
+		require PUN_ROOT.'header.php';
+
+		generate_profile_menu('personality');
+
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo pun_htmlspecialchars($user['username']).' - '.$lang_profile['Section personality'] ?></span></h2>
+		<div class="box">
+			<form id="profile4" method="post" action="profile.php?section=personality&id=<?php echo $id ?>">
+				<div><input type="hidden" name="form_sent" value="1" /></div>
+<?php if ($pun_config['o_avatars'] == '1'): ?>				<div class="inform">
+					<fieldset id="profileavatar">
+						<legend><?php echo $lang_profile['Avatar legend'] ?></legend>
+						<div class="infldset">
+<?php if ($user_avatar): ?>							<div class="useravatar"><?php echo $user_avatar ?></div>
+<?php endif; ?>							<p><?php echo $lang_profile['Avatar info'] ?></p>
+							<p class="clearb actions"><?php echo $avatar_field ?></p>
+						</div>
+					</fieldset>
+				</div>
+<?php endif; if ($pun_config['o_signatures'] == '1'): ?>				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_profile['Signature legend'] ?></legend>
+						<div class="infldset">
+							<p><?php echo $lang_profile['Signature info'] ?></p>
+							<div class="txtarea">
+								<label><?php printf($lang_profile['Sig max size'], forum_number_format($pun_config['p_sig_length']), $pun_config['p_sig_lines']) ?><br />
+								<textarea name="signature" rows="4" cols="65"><?php echo pun_htmlspecialchars($user['signature']) ?></textarea><br /></label>
+							</div>
+							<ul class="bblinks">
+								<li><span><a href="help.php#bbcode" onclick="window.open(this.href); return false;"><?php echo $lang_common['BBCode'] ?></a> <?php echo ($pun_config['p_sig_bbcode'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+								<li><span><a href="help.php#img" onclick="window.open(this.href); return false;"><?php echo $lang_common['img tag'] ?></a> <?php echo ($pun_config['p_sig_img_tag'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+								<li><span><a href="help.php#smilies" onclick="window.open(this.href); return false;"><?php echo $lang_common['Smilies'] ?></a> <?php echo ($pun_config['o_smilies_sig'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+							</ul>
+							<?php echo $signature_preview ?>
+						</div>
+					</fieldset>
+				</div>
+<?php endif; ?>				<p class="buttons"><input type="submit" name="update" value="<?php echo $lang_common['Submit'] ?>" /> <?php echo $lang_profile['Instructions'] ?></p>
+			</form>
+		</div>
+	</div>
+<?php
+
+	}
+	else if ($section == 'display')
+	{
+		$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Section display']);
+		define('PUN_ACTIVE_PAGE', 'profile');
+		require PUN_ROOT.'header.php';
+
+		generate_profile_menu('display');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo pun_htmlspecialchars($user['username']).' - '.$lang_profile['Section display'] ?></span></h2>
+		<div class="box">
+			<form id="profile5" method="post" action="profile.php?section=display&id=<?php echo $id ?>">
+				<div><input type="hidden" name="form_sent" value="1" /></div>
+<?php
+
+		$styles = forum_list_styles();
+
+		// Only display the style selection box if there's more than one style available
+		if (count($styles) == 1)
+			echo "\t\t\t".'<div><input type="hidden" name="form[style]" value="'.$styles[0].'" /></div>'."\n";
+		else if (count($styles) > 1)
+		{
+
+?>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_profile['Style legend'] ?></legend>
+						<div class="infldset">
+							<label><?php echo $lang_profile['Styles'] ?><br />
+							<select name="form[style]">
+<?php
+
+			foreach ($styles as $temp)
+			{
+				if ($user['style'] == $temp)
+					echo "\t\t\t\t\t\t\t\t".'<option value="'.$temp.'" selected="selected">'.str_replace('_', ' ', $temp).'</option>'."\n";
+				else
+					echo "\t\t\t\t\t\t\t\t".'<option value="'.$temp.'">'.str_replace('_', ' ', $temp).'</option>'."\n";
+			}
+
+?>
+							</select>
+							<br /></label>
+						</div>
+					</fieldset>
+				</div>
+<?php
+
+		}
+
+?>
+<?php if ($pun_config['o_smilies'] == '1' || $pun_config['o_smilies_sig'] == '1' || $pun_config['o_signatures'] == '1' || $pun_config['o_avatars'] == '1' || $pun_config['p_message_img_tag'] == '1' || $pun_config['p_sig_img_tag'] == '1'): ?>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_profile['Post display legend'] ?></legend>
+						<div class="infldset">
+							<p><?php echo $lang_profile['Post display info'] ?></p>
+							<div class="rbox">
+<?php if ($pun_config['o_smilies'] == '1' || $pun_config['o_smilies_sig'] == '1'): ?>								<label><input type="checkbox" name="form[show_smilies]" value="1"<?php if ($user['show_smilies'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_profile['Show smilies'] ?><br /></label>
+<?php endif; if ($pun_config['o_signatures'] == '1'): ?>								<label><input type="checkbox" name="form[show_sig]" value="1"<?php if ($user['show_sig'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_profile['Show sigs'] ?><br /></label>
+<?php endif; if ($pun_config['o_avatars'] == '1'): ?>								<label><input type="checkbox" name="form[show_avatars]" value="1"<?php if ($user['show_avatars'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_profile['Show avatars'] ?><br /></label>
+<?php endif; if ($pun_config['p_message_img_tag'] == '1'): ?>								<label><input type="checkbox" name="form[show_img]" value="1"<?php if ($user['show_img'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_profile['Show images'] ?><br /></label>
+<?php endif; if ($pun_config['p_sig_img_tag'] == '1'): ?>								<label><input type="checkbox" name="form[show_img_sig]" value="1"<?php if ($user['show_img_sig'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_profile['Show images sigs'] ?><br /></label>
+<?php endif; ?>
+							</div>
+						</div>
+					</fieldset>
+				</div>
+<?php endif; ?>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_profile['Pagination legend'] ?></legend>
+						<div class="infldset">
+							<label class="conl"><?php echo $lang_profile['Topics per page'] ?><br /><input type="text" name="form[disp_topics]" value="<?php echo $user['disp_topics'] ?>" size="6" maxlength="3" /><br /></label>
+							<label class="conl"><?php echo $lang_profile['Posts per page'] ?><br /><input type="text" name="form[disp_posts]" value="<?php echo $user['disp_posts'] ?>" size="6" maxlength="3" /><br /></label>
+							<p class="clearb"><?php echo $lang_profile['Paginate info'] ?> <?php echo $lang_profile['Leave blank'] ?></p>
+						</div>
+					</fieldset>
+				</div>
+				<p class="buttons"><input type="submit" name="update" value="<?php echo $lang_common['Submit'] ?>" /> <?php echo $lang_profile['Instructions'] ?></p>
+			</form>
+		</div>
+	</div>
+<?php
+
+	}
+	else if ($section == 'privacy')
+	{
+		$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Section privacy']);
+		define('PUN_ACTIVE_PAGE', 'profile');
+		require PUN_ROOT.'header.php';
+
+		generate_profile_menu('privacy');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo pun_htmlspecialchars($user['username']).' - '.$lang_profile['Section privacy'] ?></span></h2>
+		<div class="box">
+			<form id="profile6" method="post" action="profile.php?section=privacy&id=<?php echo $id ?>">
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_prof_reg['Privacy options legend'] ?></legend>
+						<div class="infldset">
+							<input type="hidden" name="form_sent" value="1" />
+							<p><?php echo $lang_prof_reg['Email setting info'] ?></p>
+							<div class="rbox">
+								<label><input type="radio" name="form[email_setting]" value="0"<?php if ($user['email_setting'] == '0') echo ' checked="checked"' ?> /><?php echo $lang_prof_reg['Email setting 1'] ?><br /></label>
+								<label><input type="radio" name="form[email_setting]" value="1"<?php if ($user['email_setting'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_prof_reg['Email setting 2'] ?><br /></label>
+								<label><input type="radio" name="form[email_setting]" value="2"<?php if ($user['email_setting'] == '2') echo ' checked="checked"' ?> /><?php echo $lang_prof_reg['Email setting 3'] ?><br /></label>
+							</div>
+						</div>
+					</fieldset>
+				</div>
+<?php if ($pun_config['o_subscriptions'] == '1'): ?>				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_profile['Subscription legend'] ?></legend>
+						<div class="infldset">
+							<div class="rbox">
+								<label><input type="checkbox" name="form[notify_with_post]" value="1"<?php if ($user['notify_with_post'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_profile['Notify full'] ?><br /></label>
+								<label><input type="checkbox" name="form[auto_notify]" value="1"<?php if ($user['auto_notify'] == '1') echo ' checked="checked"' ?> /><?php echo $lang_profile['Auto notify full'] ?><br /></label>
+							</div>
+						</div>
+					</fieldset>
+				</div>
+<?php endif; ?>				<p class="buttons"><input type="submit" name="update" value="<?php echo $lang_common['Submit'] ?>" /> <?php echo $lang_profile['Instructions'] ?></p>
+			</form>
+		</div>
+	</div>
+<?php
+
+	}
+	else if ($section == 'admin')
+	{
+		if (!$pun_user['is_admmod'] || ($pun_user['g_moderator'] == '1' && $pun_user['g_mod_ban_users'] == '0'))
+			message($lang_common['Bad request']);
+
+		$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['Profile'], $lang_profile['Section admin']);
+		define('PUN_ACTIVE_PAGE', 'profile');
+		require PUN_ROOT.'header.php';
+
+		generate_profile_menu('admin');
+
+?>
+	<div class="blockform">
+		<h2><span><?php echo pun_htmlspecialchars($user['username']).' - '.$lang_profile['Section admin'] ?></span></h2>
+		<div class="box">
+			<form id="profile7" method="post" action="profile.php?section=admin&id=<?php echo $id ?>&action=foo">
+				<div class="inform">
+				<input type="hidden" name="form_sent" value="1" />
+					<fieldset>
+<?php
+
+		if ($pun_user['g_moderator'] == '1')
+		{
+
+?>
+						<legend><?php echo $lang_profile['Delete ban legend'] ?></legend>
+						<div class="infldset">
+							<p><input type="submit" name="ban" value="<?php echo $lang_profile['Ban user'] ?>" /></p>
+						</div>
+					</fieldset>
+				</div>
+<?php
+
+		}
+		else
+		{
+			if ($pun_user['id'] != $id)
+			{
+
+?>
+						<legend><?php echo $lang_profile['Group membership legend'] ?></legend>
+						<div class="infldset">
+							<select id="group_id" name="group_id">
+<?php
+
+				$result = $db->query('SELECT g_id, g_title FROM '.$db->prefix.'groups WHERE g_id!='.PUN_GUEST.' ORDER BY g_title') or error('Unable to fetch user group list', __FILE__, __LINE__, $db->error());
+
+				while ($cur_group = $db->fetch_assoc($result))
+				{
+					if ($cur_group['g_id'] == $user['g_id'] || ($cur_group['g_id'] == $pun_config['o_default_user_group'] && $user['g_id'] == ''))
+						echo "\t\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'" selected="selected">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+					else
+						echo "\t\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+				}
+
+?>
+							</select>
+							<input type="submit" name="update_group_membership" value="<?php echo $lang_profile['Save'] ?>" />
+						</div>
+					</fieldset>
+				</div>
+				<div class="inform">
+					<fieldset>
+<?php
+
+			}
+
+?>
+						<legend><?php echo $lang_profile['Delete ban legend'] ?></legend>
+						<div class="infldset">
+							<input type="submit" name="delete_user" value="<?php echo $lang_profile['Delete user'] ?>" /> <input type="submit" name="ban" value="<?php echo $lang_profile['Ban user'] ?>" />
+						</div>
+					</fieldset>
+				</div>
+<?php
+
+			if ($user['g_moderator'] == '1' || $user['g_id'] == PUN_ADMIN)
+			{
+
+?>
+				<div class="inform">
+					<fieldset>
+						<legend><?php echo $lang_profile['Set mods legend'] ?></legend>
+						<div class="infldset">
+							<p><?php echo $lang_profile['Moderator in info'] ?></p>
+<?php
+
+				$result = $db->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name, f.moderators FROM '.$db->prefix.'categories AS c INNER JOIN '.$db->prefix.'forums AS f ON c.id=f.cat_id WHERE f.redirect_url IS NULL ORDER BY c.disp_position, c.id, f.disp_position') or error('Unable to fetch category/forum list', __FILE__, __LINE__, $db->error());
+
+				$cur_category = 0;
+				while ($cur_forum = $db->fetch_assoc($result))
+				{
+					if ($cur_forum['cid'] != $cur_category) // A new category since last iteration?
+					{
+						if ($cur_category)
+							echo "\n\t\t\t\t\t\t\t\t".'</div>';
+
+						if ($cur_category != 0)
+							echo "\n\t\t\t\t\t\t\t".'</div>'."\n";
+
+						echo "\t\t\t\t\t\t\t".'<div class="conl">'."\n\t\t\t\t\t\t\t\t".'<p><strong>'.$cur_forum['cat_name'].'</strong></p>'."\n\t\t\t\t\t\t\t\t".'<div class="rbox">';
+						$cur_category = $cur_forum['cid'];
+					}
+
+					$moderators = ($cur_forum['moderators'] != '') ? unserialize($cur_forum['moderators']) : array();
+
+					echo "\n\t\t\t\t\t\t\t\t\t".'<label><input type="checkbox" name="moderator_in['.$cur_forum['fid'].']" value="1"'.((in_array($id, $moderators)) ? ' checked="checked"' : '').' />'.pun_htmlspecialchars($cur_forum['forum_name']).'<br /></label>'."\n";
+				}
+
+?>
+								</div>
+							</div>
+							<br class="clearb" /><input type="submit" name="update_forums" value="<?php echo $lang_profile['Update forums'] ?>" />
+						</div>
+					</fieldset>
+				</div>
+<?php
+
+			}
+		}
+
+?>
+			</form>
+		</div>
+	</div>
+<?php
+
+	}
+	else
+		message($lang_common['Bad request']);
+
+?>
+	<div class="clearer"></div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
\ No newline at end of file
diff --git a/register.php b/register.php
new file mode 100644
index 0000000..d79ba60
--- /dev/null
+++ b/register.php
@@ -0,0 +1,411 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+
+
+// If we are logged in, we shouldn't be here
+if (!$pun_user['is_guest'])
+{
+	header('Location: index.php');
+	exit;
+}
+
+// Load the register.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/register.php';
+
+// Load the register.php/profile.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/prof_reg.php';
+
+if ($pun_config['o_regs_allow'] == '0')
+	message($lang_register['No new regs']);
+
+
+// User pressed the cancel button
+if (isset($_GET['cancel']))
+	redirect('index.php', $lang_register['Reg cancel redirect']);
+
+
+else if ($pun_config['o_rules'] == '1' && !isset($_GET['agree']) && !isset($_POST['form_sent']))
+{
+	$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_register['Register'], $lang_register['Forum rules']);
+	define('PUN_ACTIVE_PAGE', 'register');
+	require PUN_ROOT.'header.php';
+
+?>
+<div id="rules" class="blockform">
+	<div class="hd"><h2><span><?php echo $lang_register['Forum rules'] ?></span></h2></div>
+	<div class="box">
+		<form method="get" action="register.php">
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_register['Rules legend'] ?></legend>
+					<div class="infldset">
+						<div class="usercontent"><?php echo $pun_config['o_rules_message'] ?></div>
+					</div>
+				</fieldset>
+			</div>
+			<p class="buttons"><input type="submit" name="agree" value="<?php echo $lang_register['Agree'] ?>" /> <input type="submit" name="cancel" value="<?php echo $lang_register['Cancel'] ?>" /></p>
+		</form>
+	</div>
+</div>
+<?php
+
+	require PUN_ROOT.'footer.php';
+}
+
+// Start with a clean slate
+$errors = array();
+
+if (isset($_POST['form_sent']))
+{
+	// Check that someone from this IP didn't register a user within the last hour (DoS prevention)
+	$result = $db->query('SELECT 1 FROM '.$db->prefix.'users WHERE registration_ip=\''.get_remote_address().'\' AND registered>'.(time() - 3600)) or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+
+	if ($db->num_rows($result))
+		message($lang_register['Registration flood']);
+
+
+	$username = pun_trim($_POST['req_user']);
+	$email1 = strtolower(trim($_POST['req_email1']));
+
+	if ($pun_config['o_regs_verify'] == '1')
+	{
+		$email2 = strtolower(trim($_POST['req_email2']));
+
+		$password1 = random_pass(8);
+		$password2 = $password1;
+	}
+	else
+	{
+		$password1 = pun_trim($_POST['req_password1']);
+		$password2 = pun_trim($_POST['req_password2']);
+	}
+
+	// Validate username and passwords
+	check_username($username);
+
+	if (pun_strlen($password1) < 4)
+		$errors[] = $lang_prof_reg['Pass too short'];
+	else if ($password1 != $password2)
+		$errors[] = $lang_prof_reg['Pass not match'];
+
+	// Validate email
+	require PUN_ROOT.'include/email.php';
+
+	if (!is_valid_email($email1))
+		$errors[] = $lang_common['Invalid email'];
+	else if ($pun_config['o_regs_verify'] == '1' && $email1 != $email2)
+		$errors[] = $lang_register['Email not match'];
+
+	// Check if it's a banned email address
+	if (is_banned_email($email1))
+	{
+		if ($pun_config['p_allow_banned_email'] == '0')
+			$errors[] = $lang_prof_reg['Banned email'];
+
+		$banned_email = true; // Used later when we send an alert email
+	}
+	else
+		$banned_email = false;
+
+	// Check if someone else already has registered with that email address
+	$dupe_list = array();
+
+	$result = $db->query('SELECT username FROM '.$db->prefix.'users WHERE email=\''.$db->escape($email1).'\'') or error('Unable to fetch user info', __FILE__, __LINE__, $db->error());
+	if ($db->num_rows($result))
+	{
+		if ($pun_config['p_allow_dupe_email'] == '0')
+			$errors[] = $lang_prof_reg['Dupe email'];
+
+		while ($cur_dupe = $db->fetch_assoc($result))
+			$dupe_list[] = $cur_dupe['username'];
+	}
+
+	// Make sure we got a valid language string
+	if (isset($_POST['language']))
+	{
+		$language = preg_replace('#[\.\\\/]#', '', $_POST['language']);
+		if (!file_exists(PUN_ROOT.'lang/'.$language.'/common.php'))
+			message($lang_common['Bad request']);
+	}
+	else
+		$language = $pun_config['o_default_lang'];
+
+	$timezone = round($_POST['timezone'], 1);
+
+	$dst = isset($_POST['dst']) ? '1' : '0';
+
+	$email_setting = intval($_POST['email_setting']);
+	if ($email_setting < 0 || $email_setting > 2)
+		$email_setting = $pun_config['o_default_email_setting'];
+
+	// Did everything go according to plan?
+	if (empty($errors))
+	{
+		// Insert the new user into the database. We do this now to get the last inserted ID for later use
+		$now = time();
+
+		$intial_group_id = ($pun_config['o_regs_verify'] == '0') ? $pun_config['o_default_user_group'] : PUN_UNVERIFIED;
+		$password_hash = pun_hash($password1);
+
+		// Add the user
+		$db->query('INSERT INTO '.$db->prefix.'users (username, group_id, password, email, email_setting, timezone, dst, language, style, registered, registration_ip, last_visit) VALUES(\''.$db->escape($username).'\', '.$intial_group_id.', \''.$password_hash.'\', \''.$db->escape($email1).'\', '.$email_setting.', '.$timezone.' , '.$dst.', \''.$db->escape($language).'\', \''.$pun_config['o_default_style'].'\', '.$now.', \''.get_remote_address().'\', '.$now.')') or error('Unable to create user',  [...]
+		$new_uid = $db->insert_id();
+
+		// If the mailing list isn't empty, we may need to send out some alerts
+		if ($pun_config['o_mailing_list'] != '')
+		{
+			// If we previously found out that the email was banned
+			if ($banned_email)
+			{
+				$mail_subject = $lang_common['Banned email notification'];
+				$mail_message = sprintf($lang_common['Banned email register message'], $username, $email1)."\n";
+				$mail_message .= sprintf($lang_common['User profile'], $pun_config['o_base_url'].'/profile.php?id='.$new_uid)."\n";
+				$mail_message .= "\n".'--'."\n".$lang_common['Email signature'];
+
+				pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
+			}
+
+			// If we previously found out that the email was a dupe
+			if (!empty($dupe_list))
+			{
+				$mail_subject = $lang_common['Duplicate email notification'];
+				$mail_message = sprintf($lang_common['Duplicate email register message'], $username, implode(', ', $dupe_list))."\n";
+				$mail_message .= sprintf($lang_common['User profile'], $pun_config['o_base_url'].'/profile.php?id='.$new_uid)."\n";
+				$mail_message .= "\n".'--'."\n".$lang_common['Email signature'];
+
+				pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
+			}
+
+			// Should we alert people on the admin mailing list that a new user has registered?
+			if ($pun_config['o_regs_report'] == '1')
+			{
+				$mail_subject = $lang_common['New user notification'];
+				$mail_message = sprintf($lang_common['New user message'], $username, $pun_config['o_base_url'].'/')."\n";
+				$mail_message .= sprintf($lang_common['User profile'], $pun_config['o_base_url'].'/profile.php?id='.$new_uid)."\n";
+				$mail_message .= "\n".'--'."\n".$lang_common['Email signature'];
+
+				pun_mail($pun_config['o_mailing_list'], $mail_subject, $mail_message);
+			}
+		}
+
+		// Must the user verify the registration or do we log him/her in right now?
+		if ($pun_config['o_regs_verify'] == '1')
+		{
+			// Load the "welcome" template
+			$mail_tpl = trim(file_get_contents(PUN_ROOT.'lang/'.$pun_user['language'].'/mail_templates/welcome.tpl'));
+
+			// The first row contains the subject
+			$first_crlf = strpos($mail_tpl, "\n");
+			$mail_subject = trim(substr($mail_tpl, 8, $first_crlf-8));
+			$mail_message = trim(substr($mail_tpl, $first_crlf));
+
+			$mail_subject = str_replace('<board_title>', $pun_config['o_board_title'], $mail_subject);
+			$mail_message = str_replace('<base_url>', $pun_config['o_base_url'].'/', $mail_message);
+			$mail_message = str_replace('<username>', $username, $mail_message);
+			$mail_message = str_replace('<password>', $password1, $mail_message);
+			$mail_message = str_replace('<login_url>', $pun_config['o_base_url'].'/login.php', $mail_message);
+			$mail_message = str_replace('<board_mailer>', $pun_config['o_board_title'].' '.$lang_common['Mailer'], $mail_message);
+
+			pun_mail($email1, $mail_subject, $mail_message);
+
+			message($lang_register['Reg email'].' <a href="mailto:'.$pun_config['o_admin_email'].'">'.$pun_config['o_admin_email'].'</a>.', true);
+		}
+
+		pun_setcookie($new_uid, $password_hash, time() + $pun_config['o_timeout_visit']);
+
+		redirect('index.php', $lang_register['Reg complete']);
+	}
+}
+
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_register['Register']);
+$required_fields = array('req_user' => $lang_common['Username'], 'req_password1' => $lang_common['Password'], 'req_password2' => $lang_prof_reg['Confirm pass'], 'req_email1' => $lang_common['Email'], 'req_email2' => $lang_common['Email'].' 2');
+$focus_element = array('register', 'req_user');
+define('PUN_ACTIVE_PAGE', 'register');
+require PUN_ROOT.'header.php';
+
+$timezone = isset($timezone) ? $timezone : $pun_config['o_default_timezone'];
+$dst = isset($dst) ? $dst : $pun_config['o_default_dst'];
+$email_setting = isset($email_setting) ? $email_setting : $pun_config['o_default_email_setting'];
+
+?>
+<?php
+
+// If there are errors, we display them
+if (!empty($errors))
+{
+
+?>
+<div id="posterror" class="block">
+	<h2><span><?php echo $lang_register['Registration errors'] ?></span></h2>
+	<div class="box">
+		<div class="inbox error-info">
+			<p><?php echo $lang_register['Registration errors info'] ?></p>
+			<ul class="error-list">
+<?php
+
+	foreach ($errors as $cur_error)
+		echo "\t\t\t\t".'<li><strong>'.$cur_error.'</strong></li>'."\n";
+?>
+			</ul>
+		</div>
+	</div>
+</div>
+
+<?php
+
+}
+?>
+<div id="regform" class="blockform">
+	<h2><span><?php echo $lang_register['Register'] ?></span></h2>
+	<div class="box">
+		<form id="register" method="post" action="register.php?action=register" onsubmit="this.register.disabled=true;if(process_form(this)){return true;}else{this.register.disabled=false;return false;}">
+			<div class="inform">
+				<div class="forminfo">
+					<h3><?php echo $lang_common['Important information'] ?></h3>
+					<p><?php echo $lang_register['Desc 1'] ?></p>
+					<p><?php echo $lang_register['Desc 2'] ?></p>
+				</div>
+			</div>
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_register['Username legend'] ?></legend>
+					<div class="infldset">
+						<input type="hidden" name="form_sent" value="1" />
+						<label class="required"><strong><?php echo $lang_common['Username'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="text" name="req_user" value="<?php if (isset($_POST['req_user'])) echo pun_htmlspecialchars($_POST['req_user']); ?>" size="25" maxlength="25" /><br /></label>
+					</div>
+				</fieldset>
+			</div>
+<?php if ($pun_config['o_regs_verify'] == '0'): ?>			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_register['Pass legend'] ?></legend>
+					<div class="infldset">
+						<label class="conl required"><strong><?php echo $lang_common['Password'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="password" name="req_password1" value="<?php if (isset($_POST['req_password1'])) echo pun_htmlspecialchars($_POST['req_password1']); ?>" size="16" /><br /></label>
+						<label class="conl required"><strong><?php echo $lang_prof_reg['Confirm pass'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br /><input type="password" name="req_password2" value="<?php if (isset($_POST['req_password2'])) echo pun_htmlspecialchars($_POST['req_password2']); ?>" size="16" /><br /></label>
+						<p class="clearb"><?php echo $lang_register['Pass info'] ?></p>
+					</div>
+				</fieldset>
+			</div>
+<?php endif; ?>			<div class="inform">
+				<fieldset>
+					<legend><?php echo ($pun_config['o_regs_verify'] == '1') ? $lang_prof_reg['Email legend 2'] : $lang_prof_reg['Email legend'] ?></legend>
+					<div class="infldset">
+<?php if ($pun_config['o_regs_verify'] == '1'): ?>						<p><?php echo $lang_register['Email info'] ?></p>
+<?php endif; ?>						<label class="required"><strong><?php echo $lang_common['Email'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
+						<input type="text" name="req_email1" value="<?php if (isset($_POST['req_email1'])) echo pun_htmlspecialchars($_POST['req_email1']); ?>" size="50" maxlength="80" /><br /></label>
+<?php if ($pun_config['o_regs_verify'] == '1'): ?>						<label class="required"><strong><?php echo $lang_register['Confirm email'] ?> <span><?php echo $lang_common['Required'] ?></span></strong><br />
+						<input type="text" name="req_email2" value="<?php if (isset($_POST['req_email2'])) echo pun_htmlspecialchars($_POST['req_email2']); ?>" size="50" maxlength="80" /><br /></label>
+<?php endif; ?>					</div>
+				</fieldset>
+			</div>
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_prof_reg['Localisation legend'] ?></legend>
+					<div class="infldset">
+						<p><?php echo $lang_prof_reg['Time zone info'] ?></p>
+						<label><?php echo $lang_prof_reg['Time zone']."\n" ?>
+						<br /><select id="time_zone" name="timezone">
+							<option value="-12"<?php if ($timezone == -12) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-12:00'] ?></option>
+							<option value="-11"<?php if ($timezone == -11) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-11:00'] ?></option>
+							<option value="-10"<?php if ($timezone == -10) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-10:00'] ?></option>
+							<option value="-9.5"<?php if ($timezone == -9.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-09:30'] ?></option>
+							<option value="-9"<?php if ($timezone == -9) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-09:00'] ?></option>
+							<option value="-8.5"<?php if ($timezone == -8.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-08:30'] ?></option>
+							<option value="-8"<?php if ($timezone == -8) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-08:00'] ?></option>
+							<option value="-7"<?php if ($timezone == -7) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-07:00'] ?></option>
+							<option value="-6"<?php if ($timezone == -6) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-06:00'] ?></option>
+							<option value="-5"<?php if ($timezone == -5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-05:00'] ?></option>
+							<option value="-4"<?php if ($timezone == -4) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-04:00'] ?></option>
+							<option value="-3.5"<?php if ($timezone == -3.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-03:30'] ?></option>
+							<option value="-3"<?php if ($timezone == -3) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-03:00'] ?></option>
+							<option value="-2"<?php if ($timezone == -2) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-02:00'] ?></option>
+							<option value="-1"<?php if ($timezone == -1) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC-01:00'] ?></option>
+							<option value="0"<?php if ($timezone == 0) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC'] ?></option>
+							<option value="1"<?php if ($timezone == 1) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+01:00'] ?></option>
+							<option value="2"<?php if ($timezone == 2) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+02:00'] ?></option>
+							<option value="3"<?php if ($timezone == 3) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+03:00'] ?></option>
+							<option value="3.5"<?php if ($timezone == 3.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+03:30'] ?></option>
+							<option value="4"<?php if ($timezone == 4) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+04:00'] ?></option>
+							<option value="4.5"<?php if ($timezone == 4.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+04:30'] ?></option>
+							<option value="5"<?php if ($timezone == 5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+05:00'] ?></option>
+							<option value="5.5"<?php if ($timezone == 5.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+05:30'] ?></option>
+							<option value="5.75"<?php if ($timezone == 5.75) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+05:45'] ?></option>
+							<option value="6"<?php if ($timezone == 6) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+06:00'] ?></option>
+							<option value="6.5"<?php if ($timezone == 6.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+06:30'] ?></option>
+							<option value="7"<?php if ($timezone == 7) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+07:00'] ?></option>
+							<option value="8"<?php if ($timezone == 8) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+08:00'] ?></option>
+							<option value="8.75"<?php if ($timezone == 8.75) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+08:45'] ?></option>
+							<option value="9"<?php if ($timezone == 9) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+09:00'] ?></option>
+							<option value="9.5"<?php if ($timezone == 9.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+09:30'] ?></option>
+							<option value="10"<?php if ($timezone == 10) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+10:00'] ?></option>
+							<option value="10.5"<?php if ($timezone == 10.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+10:30'] ?></option>
+							<option value="11"<?php if ($timezone == 11) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+11:00'] ?></option>
+							<option value="11.5"<?php if ($timezone == 11.5) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+11:30'] ?></option>
+							<option value="12"<?php if ($timezone == 12) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+12:00'] ?></option>
+							<option value="12.75"<?php if ($timezone == 12.75) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+12:45'] ?></option>
+							<option value="13"<?php if ($timezone == 13) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+13:00'] ?></option>
+							<option value="14"<?php if ($timezone == 14) echo ' selected="selected"' ?>><?php echo $lang_prof_reg['UTC+14:00'] ?></option>
+						</select>
+						<br /></label>
+						<div class="rbox">
+							<label><input type="checkbox" name="dst" value="1"<?php if ($dst == '1') echo ' checked="checked"' ?> /><?php echo $lang_prof_reg['DST'] ?><br /></label>
+						</div>
+<?php
+
+		$languages = forum_list_langs();
+
+		// Only display the language selection box if there's more than one language available
+		if (count($languages) > 1)
+		{
+
+?>
+							<label><?php echo $lang_prof_reg['Language'] ?>
+							<br /><select name="language">
+<?php
+
+			foreach ($languages as $temp)
+			{
+				if ($pun_config['o_default_lang'] == $temp)
+					echo "\t\t\t\t\t\t\t\t".'<option value="'.$temp.'" selected="selected">'.$temp.'</option>'."\n";
+				else
+					echo "\t\t\t\t\t\t\t\t".'<option value="'.$temp.'">'.$temp.'</option>'."\n";
+			}
+
+?>
+							</select>
+							<br /></label>
+<?php
+
+		}
+?>
+					</div>
+				</fieldset>
+			</div>
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_prof_reg['Privacy options legend'] ?></legend>
+					<div class="infldset">
+						<p><?php echo $lang_prof_reg['Email setting info'] ?></p>
+						<div class="rbox">
+							<label><input type="radio" name="email_setting" value="0"<?php if ($email_setting == '0') echo ' checked="checked"' ?> /><?php echo $lang_prof_reg['Email setting 1'] ?><br /></label>
+							<label><input type="radio" name="email_setting" value="1"<?php if ($email_setting == '1') echo ' checked="checked"' ?> /><?php echo $lang_prof_reg['Email setting 2'] ?><br /></label>
+							<label><input type="radio" name="email_setting" value="2"<?php if ($email_setting == '2') echo ' checked="checked"' ?> /><?php echo $lang_prof_reg['Email setting 3'] ?><br /></label>
+						</div>
+					</div>
+				</fieldset>
+			</div>
+			<p class="buttons"><input type="submit" name="register" value="<?php echo $lang_register['Register'] ?>" /></p>
+		</form>
+	</div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/search.php b/search.php
new file mode 100644
index 0000000..28c4661
--- /dev/null
+++ b/search.php
@@ -0,0 +1,750 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+// The contents of this file are very much inspired by the file search.php
+// from the phpBB Group forum software phpBB2 (http://www.phpbb.com)
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+
+// Load the search.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/search.php';
+require PUN_ROOT.'lang/'.$pun_user['language'].'/forum.php';
+
+
+if ($pun_user['g_read_board'] == '0')
+	message($lang_common['No view']);
+else if ($pun_user['g_search'] == '0')
+	message($lang_search['No search permission']);
+
+require PUN_ROOT.'include/search_idx.php';
+
+// Figure out what to do :-)
+if (isset($_GET['action']) || isset($_GET['search_id']))
+{
+	$action = (isset($_GET['action'])) ? $_GET['action'] : null;
+	$forum = (isset($_GET['forum'])) ? intval($_GET['forum']) : -1;
+	$sort_dir = (isset($_GET['sort_dir']) && $_GET['sort_dir'] == 'DESC') ? 'DESC' : 'ASC';
+
+	// If a search_id was supplied
+	if (isset($_GET['search_id']))
+	{
+		$search_id = intval($_GET['search_id']);
+		if ($search_id < 1)
+			message($lang_common['Bad request']);
+	}
+	// If it's a regular search (keywords and/or author)
+	else if ($action == 'search')
+	{
+		$keywords = (isset($_GET['keywords'])) ? utf8_strtolower(pun_trim($_GET['keywords'])) : null;
+		$author = (isset($_GET['author'])) ? utf8_strtolower(pun_trim($_GET['author'])) : null;
+
+		if (preg_match('#^[\*%]+$#', $keywords) || (pun_strlen(str_replace(array('*', '%'), '', $keywords)) < PUN_SEARCH_MIN_WORD && !is_cjk($keywords)))
+			$keywords = '';
+
+		if (preg_match('#^[\*%]+$#', $author) || pun_strlen(str_replace(array('*', '%'), '', $author)) < 2)
+			$author = '';
+
+		if (!$keywords && !$author)
+			message($lang_search['No terms']);
+
+		if ($author)
+			$author = str_replace('*', '%', $author);
+
+		$show_as = (isset($_GET['show_as'])) ? $_GET['show_as'] : 'posts';
+		$sort_by = (isset($_GET['sort_by'])) ? intval($_GET['sort_by']) : null;
+		$search_in = (!isset($_GET['search_in']) || $_GET['search_in'] == 'all') ? 0 : (($_GET['search_in'] == 'message') ? 1 : -1);
+	}
+	// If it's a user search (by ID)
+	else if ($action == 'show_user')
+	{
+		$user_id = (isset($_GET['user_id'])) ? intval($_GET['user_id']) : 0;
+		if ($user_id < 2)
+			message($lang_common['Bad request']);
+	}
+	else
+	{
+		if ($action != 'show_new' && $action != 'show_24h' && $action != 'show_unanswered' && $action != 'show_subscriptions')
+			message($lang_common['Bad request']);
+	}
+
+
+	// If a valid search_id was supplied we attempt to fetch the search results from the db
+	if (isset($search_id))
+	{
+		$ident = ($pun_user['is_guest']) ? get_remote_address() : $pun_user['username'];
+
+		$result = $db->query('SELECT search_data FROM '.$db->prefix.'search_cache WHERE id='.$search_id.' AND ident=\''.$db->escape($ident).'\'') or error('Unable to fetch search results', __FILE__, __LINE__, $db->error());
+		if ($row = $db->fetch_assoc($result))
+		{
+			$temp = unserialize($row['search_data']);
+
+			$search_ids = unserialize($temp['search_ids']);
+			$num_hits = $temp['num_hits'];
+			$sort_by = $temp['sort_by'];
+			$sort_dir = $temp['sort_dir'];
+			$show_as = $temp['show_as'];
+
+			unset($temp);
+		}
+		else
+			message($lang_search['No hits']);
+	}
+	else
+	{
+		$keyword_results = $author_results = array();
+
+		// Search a specific forum?
+		$forum_sql = ($forum != -1 || ($forum == -1 && $pun_config['o_search_all_forums'] == '0' && !$pun_user['is_admmod'])) ? ' AND t.forum_id = '.$forum : '';
+
+		if (!empty($author) || !empty($keywords))
+		{
+			// Flood protection
+			if ($pun_user['last_search'] && (time() - $pun_user['last_search']) < $pun_user['g_search_flood'] && (time() - $pun_user['last_search']) >= 0)
+				message(sprintf($lang_search['Search flood'], $pun_user['g_search_flood']));
+
+			if (!$pun_user['is_guest'])
+				$db->query('UPDATE '.$db->prefix.'users SET last_search='.time().' WHERE id='.$pun_user['id']) or error('Unable to update user', __FILE__, __LINE__, $db->error());
+			else
+				$db->query('UPDATE '.$db->prefix.'online SET last_search='.time().' WHERE ident=\''.$db->escape(get_remote_address()).'\'' ) or error('Unable to update user', __FILE__, __LINE__, $db->error());
+
+			switch ($sort_by)
+			{
+				case 1:
+					$sort_by_sql = ($show_as == 'topics') ? 't.poster' : 'p.poster';
+					$sort_type = SORT_STRING;
+					break;
+
+				case 2:
+					$sort_by_sql = 't.subject';
+					$sort_type = SORT_STRING;
+					break;
+
+				case 3:
+					$sort_by_sql = 't.forum_id';
+					$sort_type = SORT_NUMERIC;
+					break;
+
+				case 4:
+					$sort_by_sql = 't.last_post';
+					$sort_type = SORT_NUMERIC;
+					break;
+
+				default:
+					$sort_by_sql = ($show_as == 'topics') ? 't.last_post' : 'p.posted';
+					$sort_type = SORT_NUMERIC;
+					break;
+			}
+
+			// If it's a search for keywords
+			if ($keywords)
+			{
+				// split the keywords into words
+				$keywords_array = split_words($keywords, false);
+
+				if (empty($keywords_array))
+					message($lang_search['No hits']);
+
+				// Should we search in message body or topic subject specifically?
+				$search_in_cond = ($search_in) ? (($search_in > 0) ? ' AND m.subject_match = 0' : ' AND m.subject_match = 1') : '';
+
+				$word_count = 0;
+				$match_type = 'and';
+
+				$sort_data = array();
+				foreach ($keywords_array as $cur_word)
+				{
+					switch ($cur_word)
+					{
+						case 'and':
+						case 'or':
+						case 'not':
+							$match_type = $cur_word;
+							break;
+
+						default:
+						{
+							if (is_cjk($cur_word))
+							{
+								$where_cond = str_replace('*', '%', $cur_word);
+								$where_cond = ($search_in ? (($search_in > 0) ? 'p.message LIKE \'%'.$db->escape($where_cond).'%\'' : 't.subject LIKE \'%'.$db->escape($where_cond).'%\'') : 'p.message LIKE \'%'.$db->escape($where_cond).'%\' OR t.subject LIKE \'%'.$db->escape($where_cond).'%\'');
+
+								$result = $db->query('SELECT p.id AS post_id, p.topic_id, '.$sort_by_sql.' AS sort_by FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE ('.$where_cond.') AND (fp.read_forum IS NULL OR fp.read_forum=1)'.$forum_sql, true) or error('Unable to search for posts', __FILE__, __LINE__, $db->error());
+							}
+							else
+								$result = $db->query('SELECT m.post_id, p.topic_id, '.$sort_by_sql.' AS sort_by FROM '.$db->prefix.'search_words AS w INNER JOIN '.$db->prefix.'search_matches AS m ON m.word_id = w.id INNER JOIN '.$db->prefix.'posts AS p ON p.id=m.post_id INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE w.word LIKE \''.$db->escape(str_replace('*', '%', $cur_word)).'\''.$searc [...]
+
+							$row = array();
+							while ($temp = $db->fetch_assoc($result))
+							{
+								$row[$temp['post_id']] = $temp['topic_id'];
+
+								if (!$word_count)
+								{
+									$keyword_results[$temp['post_id']] = $temp['topic_id'];
+									$sort_data[$temp['post_id']] = $temp['sort_by'];
+								}
+								else if ($match_type == 'or')
+								{
+									$keyword_results[$temp['post_id']] = $temp['topic_id'];
+									$sort_data[$temp['post_id']] = $temp['sort_by'];
+								}
+								else if ($match_type == 'not')
+								{
+									unset($keyword_results[$temp['post_id']]);
+									unset($sort_data[$temp['post_id']]);
+								}
+							}
+
+							if ($match_type == 'and' && $word_count)
+							{
+								foreach ($keyword_results as $post_id => $topic_id)
+								{
+									if (!isset($row[$post_id]))
+									{
+										unset($keyword_results[$post_id]);
+										unset($sort_data[$post_id]);
+									}
+								}
+							}
+
+							++$word_count;
+							$db->free_result($result);
+
+							break;
+						}
+					}
+				}
+
+				// Sort the results - annoyingly array_multisort re-indexes arrays with numeric keys, so we need to split the keys out into a seperate array then combine them again after
+				$post_ids = array_keys($keyword_results);
+				$topic_ids = array_values($keyword_results);
+
+				array_multisort(array_values($sort_data), $sort_dir == 'DESC' ? SORT_DESC : SORT_ASC, $sort_type, $post_ids, $topic_ids);
+
+				// combine the arrays back into a key=>value array (array_combine is PHP5 only unfortunately)
+				$num_results = count($keyword_results);
+				$keyword_results = array();
+				for ($i = 0;$i < $num_results;$i++)
+					$keyword_results[$post_ids[$i]] = $topic_ids[$i];
+
+				unset($sort_data, $post_ids, $topic_ids);
+			}
+
+			// If it's a search for author name (and that author name isn't Guest)
+			if ($author && $author != 'guest' && $author != utf8_strtolower($lang_common['Guest']))
+			{
+				switch ($db_type)
+				{
+					case 'pgsql':
+						$result = $db->query('SELECT id FROM '.$db->prefix.'users WHERE username ILIKE \''.$db->escape($author).'\'') or error('Unable to fetch users', __FILE__, __LINE__, $db->error());
+						break;
+
+					default:
+						$result = $db->query('SELECT id FROM '.$db->prefix.'users WHERE username LIKE \''.$db->escape($author).'\'') or error('Unable to fetch users', __FILE__, __LINE__, $db->error());
+						break;
+				}
+
+				if ($db->num_rows($result))
+				{
+					$user_ids = array();
+					while ($row = $db->fetch_row($result))
+						$user_ids[] = $row[0];
+
+					$result = $db->query('SELECT p.id AS post_id, p.topic_id FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND p.poster_id IN('.implode(',', $user_ids).')'.$forum_sql.' ORDER BY '.$sort_by_sql.' '.$sort_dir) or error('Unable to fetch matched posts list', __FILE__, __LINE__, $db->error());
+					while ($temp = $db->fetch_assoc($result))
+						$author_results[$temp['post_id']] = $temp['topic_id'];
+
+					$db->free_result($result);
+				}
+			}
+
+			// If we searched for both keywords and author name we want the intersection between the results
+			if ($author && $keywords)
+				$search_ids = array_intersect_assoc($keyword_results, $author_results);
+			else if ($keywords)
+				$search_ids = $keyword_results;
+			else
+				$search_ids = $author_results;
+
+			unset($keyword_results, $author_results);
+
+			if ($show_as == 'topics')
+				$search_ids = array_values($search_ids);
+			else
+				$search_ids = array_keys($search_ids);
+
+			$search_ids = array_unique($search_ids);
+
+			$num_hits = count($search_ids);
+			if (!$num_hits)
+				message($lang_search['No hits']);
+		}
+		else if ($action == 'show_new' || $action == 'show_24h' || $action == 'show_user' || $action == 'show_subscriptions' || $action == 'show_unanswered')
+		{
+			// If it's a search for new posts
+			if ($action == 'show_new')
+			{
+				if ($pun_user['is_guest'])
+					message($lang_common['No permission']);
+
+				$result = $db->query('SELECT t.id FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.last_post>'.$pun_user['last_visit'].' AND t.moved_to IS NULL'.(isset($_GET['fid']) ? ' AND t.forum_id='.intval($_GET['fid']) : '').' ORDER BY t.last_post DESC') or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());
+				$num_hits = $db->num_rows($result);
+
+				if (!$num_hits)
+					message($lang_search['No new posts']);
+			}
+			// If it's a search for todays posts
+			else if ($action == 'show_24h')
+			{
+				$result = $db->query('SELECT t.id FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.last_post>'.(time() - 86400).' AND t.moved_to IS NULL ORDER BY t.last_post DESC') or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());
+				$num_hits = $db->num_rows($result);
+
+				if (!$num_hits)
+					message($lang_search['No recent posts']);
+			}
+			// If it's a search for posts by a specific user ID
+			else if ($action == 'show_user')
+			{
+				$result = $db->query('SELECT t.id FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'posts AS p ON t.id=p.topic_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND p.poster_id='.$user_id.' GROUP BY t.id ORDER BY t.last_post DESC') or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());
+				$num_hits = $db->num_rows($result);
+
+				if (!$num_hits)
+					message($lang_search['No user posts']);
+			}
+			// If it's a search for subscribed topics
+			else if ($action == 'show_subscriptions')
+			{
+				if ($pun_user['is_guest'])
+					message($lang_common['Bad request']);
+
+				$result = $db->query('SELECT t.id FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'subscriptions AS s ON (t.id=s.topic_id AND s.user_id='.$pun_user['id'].') LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) ORDER BY t.last_post DESC') or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());
+				$num_hits = $db->num_rows($result);
+
+				if (!$num_hits)
+					message($lang_search['No subscriptions']);
+			}
+			// If it's a search for unanswered posts
+			else
+			{
+				$result = $db->query('SELECT t.id FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=t.forum_id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.num_replies=0 AND t.moved_to IS NULL ORDER BY t.last_post DESC') or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());
+				$num_hits = $db->num_rows($result);
+
+				if (!$num_hits)
+					message($lang_search['No unanswered']);
+			}
+
+			// We want to sort things after last post
+			$sort_by = 4;
+			$sort_dir = 'DESC';
+
+			$search_ids = array();
+			while ($row = $db->fetch_row($result))
+				$search_ids[] = $row[0];
+
+			$db->free_result($result);
+
+			$show_as = 'topics';
+		}
+		else
+			message($lang_common['Bad request']);
+
+
+		// Prune "old" search results
+		$old_searches = array();
+		$result = $db->query('SELECT ident FROM '.$db->prefix.'online') or error('Unable to fetch online list', __FILE__, __LINE__, $db->error());
+
+		if ($db->num_rows($result))
+		{
+			while ($row = $db->fetch_row($result))
+				$old_searches[] = '\''.$db->escape($row[0]).'\'';
+
+			$db->query('DELETE FROM '.$db->prefix.'search_cache WHERE ident NOT IN('.implode(',', $old_searches).')') or error('Unable to delete search results', __FILE__, __LINE__, $db->error());
+		}
+
+		// Fill an array with our results and search properties
+		$temp = serialize(array(
+			'search_ids'		=> serialize($search_ids),
+			'num_hits'			=> $num_hits,
+			'sort_by'			=> $sort_by,
+			'sort_dir'			=> $sort_dir,
+			'show_as'			=> $show_as,
+		));
+		$search_id = mt_rand(1, 2147483647);
+
+		$ident = ($pun_user['is_guest']) ? get_remote_address() : $pun_user['username'];
+
+		$db->query('INSERT INTO '.$db->prefix.'search_cache (id, ident, search_data) VALUES('.$search_id.', \''.$db->escape($ident).'\', \''.$db->escape($temp).'\')') or error('Unable to insert search results', __FILE__, __LINE__, $db->error());
+
+		if ($action != 'show_new' && $action != 'show_24h')
+		{
+			$db->end_transaction();
+			$db->close();
+
+			// Redirect the user to the cached result page
+			header('Location: search.php?search_id='.$search_id);
+			exit;
+		}
+	}
+
+
+	// Fetch results to display
+	if (!empty($search_ids))
+	{
+		switch ($sort_by)
+		{
+			case 1:
+				$sort_by_sql = ($show_as == 'topics') ? 't.poster' : 'p.poster';
+				break;
+
+			case 2:
+				$sort_by_sql = 't.subject';
+				break;
+
+			case 3:
+				$sort_by_sql = 't.forum_id';
+				break;
+
+			case 4:
+				$sort_by_sql = 't.last_post';
+				break;
+
+			default:
+				$sort_by_sql = ($show_as == 'topics') ? 't.last_post' : 'p.posted';
+				break;
+		}
+
+		// Determine the topic or post offset (based on $_GET['p'])
+		$per_page = ($show_as == 'posts') ? $pun_user['disp_posts'] : $pun_user['disp_topics'];
+		$num_pages = ceil($num_hits / $per_page);
+
+		$p = (!isset($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : intval($_GET['p']);
+		$start_from = $per_page * ($p - 1);
+
+		// Generate paging links
+		$paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'search.php?search_id='.$search_id);
+
+		// throw away the first $start_from of $search_ids, only keep the top $per_page of $search_ids
+		$search_ids = array_slice($search_ids, $start_from, $per_page);
+
+		$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_search['Search results']);
+		define('PUN_ACTIVE_PAGE', 'search');
+		require PUN_ROOT.'header.php';
+
+
+?>
+<div class="linkst">
+	<div class="inbox">
+		<p class="pagelink"><?php echo $paging_links ?></p>
+		<div class="clearer"></div>
+	</div>
+</div>
+
+<?php
+
+		if ($show_as == 'topics')
+		{
+			$topic_count = 0;
+
+?>
+<div id="vf" class="blocktable">
+	<h2><span><?php echo $lang_search['Search results'] ?></span></h2>
+	<div class="box">
+		<div class="inbox">
+			<table cellspacing="0">
+			<thead>
+				<tr>
+					<th class="tcl" scope="col"><?php echo $lang_common['Topic'] ?></th>
+					<th class="tc2" scope="col"><?php echo $lang_common['Forum'] ?></th>
+					<th class="tc3" scope="col"><?php echo $lang_common['Replies'] ?></th>
+					<th class="tcr" scope="col"><?php echo $lang_common['Last post'] ?></th>
+				</tr>
+			</thead>
+			<tbody>
+<?php
+
+		}
+		else if ($show_as == 'posts')
+		{
+			require PUN_ROOT.'lang/'.$pun_user['language'].'/topic.php';
+
+			require PUN_ROOT.'include/parser.php';
+
+			$post_count = 0;
+		}
+
+		// Get topic/forum tracking data
+		if (!$pun_user['is_guest'])
+			$tracked_topics = get_tracked_topics();
+
+		if ($show_as == 'posts')
+			$result = $db->query('SELECT p.id AS pid, p.poster AS pposter, p.posted AS pposted, p.poster_id, p.message, p.hide_smilies, t.id AS tid, t.poster, t.subject, t.first_post_id, t.last_post, t.last_post_id, t.last_poster, t.num_replies, t.forum_id, f.forum_name FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'topics AS t ON t.id=p.topic_id INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id WHERE p.id IN('.implode(',', $search_ids).') ORDER BY '.$sort_by_sql.' '.$sort_dir) o [...]
+		else
+			$result = $db->query('SELECT t.id AS tid, t.poster, t.subject, t.last_post, t.last_post_id, t.last_poster, t.num_replies, t.closed, t.sticky, t.forum_id, f.forum_name FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id WHERE t.id IN('.implode(',', $search_ids).') ORDER BY '.$sort_by_sql.' '.$sort_dir) or error('Unable to fetch search results', __FILE__, __LINE__, $db->error());
+
+		while ($cur_search = $db->fetch_assoc($result))
+		{
+			$forum = '<a href="viewforum.php?id='.$cur_search['forum_id'].'">'.pun_htmlspecialchars($cur_search['forum_name']).'</a>';
+
+			if ($pun_config['o_censoring'] == '1')
+				$cur_search['subject'] = censor_words($cur_search['subject']);
+
+			if ($show_as == 'posts')
+			{
+				++$post_count;
+				$icon_type = 'icon';
+
+				if (!$pun_user['is_guest'] && $cur_search['last_post'] > $pun_user['last_visit'] && (!isset($tracked_topics['topics'][$cur_search['tid']]) || $tracked_topics['topics'][$cur_search['tid']] < $cur_search['last_post']) && (!isset($tracked_topics['forums'][$cur_search['forum_id']]) || $tracked_topics['forums'][$cur_search['forum_id']] < $cur_search['last_post']))
+				{
+					$item_status = 'inew';
+					$icon_type = 'icon icon-new';
+					$icon_text = $lang_topic['New icon'];
+				}
+				else
+				{
+					$item_status = '';
+					$icon_text = '<!-- -->';
+				}
+
+				if ($pun_config['o_censoring'] == '1')
+					$cur_search['message'] = censor_words($cur_search['message']);
+
+				$message = parse_message($cur_search['message'], $cur_search['hide_smilies']);
+				$pposter = pun_htmlspecialchars($cur_search['pposter']);
+
+				if ($cur_search['poster_id'] > 1)
+				{
+					if ($pun_user['g_view_users'] == '1')
+						$pposter = '<strong><a href="profile.php?id='.$cur_search['poster_id'].'">'.$pposter.'</a></strong>';
+					else
+						$pposter = '<strong>'.$pposter.'</strong>';
+				}
+
+
+?>
+<div class="blockpost<?php echo ($post_count % 2 == 0) ? ' roweven' : ' rowodd' ?><?php if ($cur_search['pid'] == $cur_search['first_post_id']) echo ' firstpost' ?><?php if ($post_count == 1) echo ' blockpost1' ?><?php if ($item_status != '') echo ' '.$item_status ?>">
+	<h2><span><span class="conr">#<?php echo ($start_from + $post_count) ?></span> <span><?php if ($cur_search['pid'] != $cur_search['first_post_id']) echo $lang_topic['Re'].' ' ?><?php echo $forum ?></span> <span>» <a href="viewtopic.php?id=<?php echo $cur_search['tid'] ?>"><?php echo pun_htmlspecialchars($cur_search['subject']) ?></a></span> <span>» <a href="viewtopic.php?pid=<?php echo $cur_search['pid'].'#p'.$cur_search['pid'] ?>"><?php echo format_time($cur_search['pposted']) [...]
+	<div class="box">
+		<div class="inbox">
+			<div class="postbody">
+				<div class="postleft">
+					<dl>
+						<dt><?php echo $pposter ?></dt>
+						<dd><span><?php echo $lang_topic['Replies'].' '.forum_number_format($cur_search['num_replies']) ?></span></dd>
+						<dd><div class="<?php echo $icon_type ?>"><div class="nosize"><?php echo $icon_text ?></div></div></dd>
+					</dl>
+				</div>
+				<div class="postright">
+					<div class="postmsg">
+						<?php echo $message."\n" ?>
+					</div>
+				</div>
+				<div class="clearer"></div>
+			</div>
+		</div>
+		<div class="inbox">
+			<div class="postfoot clearb">
+				<div class="postfootright">
+					<ul>
+						<li><span><a href="viewtopic.php?id=<?php echo $cur_search['tid'] ?>"><?php echo $lang_search['Go to topic'] ?></a></span></li>
+						<li><span><a href="viewtopic.php?pid=<?php echo $cur_search['pid'].'#p'.$cur_search['pid'] ?>"><?php echo $lang_search['Go to post'] ?></a></span></li>
+					</ul>
+				</div>
+			</div>
+		</div>
+	</div>
+</div>
+<?php
+
+			}
+			else
+			{
+				++$topic_count;
+				$status_text = array();
+				$item_status = ($topic_count % 2 == 0) ? 'roweven' : 'rowodd';
+				$icon_type = 'icon';
+
+				$subject = '<a href="viewtopic.php?id='.$cur_search['tid'].'">'.pun_htmlspecialchars($cur_search['subject']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_search['poster']).'</span>';
+
+				if ($cur_search['sticky'] == '1')
+				{
+					$item_status .= ' isticky';
+					$status_text[] = '<span class="stickytext">'.$lang_forum['Sticky'].'</span>';
+				}
+
+				if ($cur_search['closed'] != '0')
+				{
+					$status_text[] = '<span class="closedtext">'.$lang_forum['Closed'].'</span>';
+					$item_status .= ' iclosed';
+				}
+
+				if (!$pun_user['is_guest'] && $cur_search['last_post'] > $pun_user['last_visit'] && (!isset($tracked_topics['topics'][$cur_search['tid']]) || $tracked_topics['topics'][$cur_search['tid']] < $cur_search['last_post']) && (!isset($tracked_topics['forums'][$cur_search['forum_id']]) || $tracked_topics['forums'][$cur_search['forum_id']] < $cur_search['last_post']))
+				{
+					$item_status .= ' inew';
+					$icon_type = 'icon icon-new';
+					$subject = '<strong>'.$subject.'</strong>';
+					$subject_new_posts = '<span class="newtext">[ <a href="viewtopic.php?id='.$cur_search['tid'].'&action=new" title="'.$lang_common['New posts info'].'">'.$lang_common['New posts'].'</a> ]</span>';
+				}
+				else
+					$subject_new_posts = null;
+
+				// Insert the status text before the subject
+				$subject = implode(' ', $status_text).' '.$subject;
+
+				$num_pages_topic = ceil(($cur_search['num_replies'] + 1) / $pun_user['disp_posts']);
+
+				if ($num_pages_topic > 1)
+					$subject_multipage = '<span class="pagestext">[ '.paginate($num_pages_topic, -1, 'viewtopic.php?id='.$cur_search['tid']).' ]</span>';
+				else
+					$subject_multipage = null;
+
+				// Should we show the "New posts" and/or the multipage links?
+				if (!empty($subject_new_posts) || !empty($subject_multipage))
+				{
+					$subject .= !empty($subject_new_posts) ? ' '.$subject_new_posts : '';
+					$subject .= !empty($subject_multipage) ? ' '.$subject_multipage : '';
+				}
+
+?>
+				<tr class="<?php echo $item_status ?>">
+					<td class="tcl">
+						<div class="<?php echo $icon_type ?>"><div class="nosize"><?php echo forum_number_format($topic_count + $start_from) ?></div></div>
+						<div class="tclcon">
+							<div>
+								<?php echo $subject."\n" ?>
+							</div>
+						</div>
+					</td>
+					<td class="tc2"><?php echo $forum ?></td>
+					<td class="tc3"><?php echo forum_number_format($cur_search['num_replies']) ?></td>
+					<td class="tcr"><?php echo '<a href="viewtopic.php?pid='.$cur_search['last_post_id'].'#p'.$cur_search['last_post_id'].'">'.format_time($cur_search['last_post']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_search['last_poster']) ?></span></td>
+				</tr>
+<?php
+
+			}
+		}
+
+		if ($show_as == 'topics')
+			echo "\t\t\t".'</tbody>'."\n\t\t\t".'</table>'."\n\t\t".'</div>'."\n\t".'</div>'."\n".'</div>'."\n\n";
+
+?>
+<div class="<?php echo ($show_as == 'topics') ? 'linksb' : 'postlinksb'; ?>">
+	<div class="inbox">
+		<p class="pagelink"><?php echo $paging_links ?></p>
+		<div class="clearer"></div>
+	</div>
+</div>
+<?php
+
+		$footer_style = 'search';
+		require PUN_ROOT.'footer.php';
+	}
+	else
+		message($lang_search['No hits']);
+}
+
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_search['Search']);
+$focus_element = array('search', 'keywords');
+define('PUN_ACTIVE_PAGE', 'search');
+require PUN_ROOT.'header.php';
+
+?>
+<div id="searchform" class="blockform">
+	<h2><span><?php echo $lang_search['Search'] ?></span></h2>
+	<div class="box">
+		<form id="search" method="get" action="search.php">
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_search['Search criteria legend'] ?></legend>
+					<div class="infldset">
+						<input type="hidden" name="action" value="search" />
+						<label class="conl"><?php echo $lang_search['Keyword search'] ?><br /><input type="text" name="keywords" size="40" maxlength="100" /><br /></label>
+						<label class="conl"><?php echo $lang_search['Author search'] ?><br /><input id="author" type="text" name="author" size="25" maxlength="25" /><br /></label>
+						<p class="clearb"><?php echo $lang_search['Search info'] ?></p>
+					</div>
+				</fieldset>
+			</div>
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_search['Search in legend'] ?></legend>
+					<div class="infldset">
+						<label class="conl"><?php echo $lang_search['Forum search']."\n" ?>
+						<br /><select id="forum" name="forum">
+<?php
+
+if ($pun_config['o_search_all_forums'] == '1' || $pun_user['is_admmod'])
+	echo "\t\t\t\t\t\t\t".'<option value="-1">'.$lang_search['All forums'].'</option>'."\n";
+
+$result = $db->query('SELECT c.id AS cid, c.cat_name, f.id AS fid, f.forum_name, f.redirect_url FROM '.$db->prefix.'categories AS c INNER JOIN '.$db->prefix.'forums AS f ON c.id=f.cat_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.redirect_url IS NULL ORDER BY c.disp_position, c.id, f.disp_position', true) or error('Unable to fetch category/forum list', __FILE__, __LINE__, $d [...]
+
+$cur_category = 0;
+while ($cur_forum = $db->fetch_assoc($result))
+{
+	if ($cur_forum['cid'] != $cur_category) // A new category since last iteration?
+	{
+		if ($cur_category)
+			echo "\t\t\t\t\t\t\t".'</optgroup>'."\n";
+
+		echo "\t\t\t\t\t\t\t".'<optgroup label="'.pun_htmlspecialchars($cur_forum['cat_name']).'">'."\n";
+		$cur_category = $cur_forum['cid'];
+	}
+
+	echo "\t\t\t\t\t\t\t\t".'<option value="'.$cur_forum['fid'].'">'.pun_htmlspecialchars($cur_forum['forum_name']).'</option>'."\n";
+}
+
+?>
+							</optgroup>
+						</select>
+						<br /></label>
+						<label class="conl"><?php echo $lang_search['Search in']."\n" ?>
+						<br /><select id="search_in" name="search_in">
+							<option value="all"><?php echo $lang_search['Message and subject'] ?></option>
+							<option value="message"><?php echo $lang_search['Message only'] ?></option>
+							<option value="topic"><?php echo $lang_search['Topic only'] ?></option>
+						</select>
+						<br /></label>
+						<p class="clearb"><?php echo $lang_search['Search in info'] ?></p>
+					</div>
+				</fieldset>
+			</div>
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_search['Search results legend'] ?></legend>
+					<div class="infldset">
+						<label class="conl"><?php echo $lang_search['Sort by']."\n" ?>
+						<br /><select name="sort_by">
+							<option value="0"><?php echo $lang_search['Sort by post time'] ?></option>
+							<option value="1"><?php echo $lang_search['Sort by author'] ?></option>
+							<option value="2"><?php echo $lang_search['Sort by subject'] ?></option>
+							<option value="3"><?php echo $lang_search['Sort by forum'] ?></option>
+						</select>
+						<br /></label>
+						<label class="conl"><?php echo $lang_search['Sort order']."\n" ?>
+						<br /><select name="sort_dir">
+							<option value="DESC"><?php echo $lang_search['Descending'] ?></option>
+							<option value="ASC"><?php echo $lang_search['Ascending'] ?></option>
+						</select>
+						<br /></label>
+						<label class="conl"><?php echo $lang_search['Show as']."\n" ?>
+						<br /><select name="show_as">
+							<option value="topics"><?php echo $lang_search['Show as topics'] ?></option>
+							<option value="posts"><?php echo $lang_search['Show as posts'] ?></option>
+						</select>
+						<br /></label>
+						<p class="clearb"><?php echo $lang_search['Search results info'] ?></p>
+					</div>
+				</fieldset>
+			</div>
+			<p class="buttons"><input type="submit" name="search" value="<?php echo $lang_common['Submit'] ?>" accesskey="s" /></p>
+		</form>
+	</div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/style/Air.css b/style/Air.css
new file mode 100644
index 0000000..00e193b
--- /dev/null
+++ b/style/Air.css
@@ -0,0 +1,1606 @@
+/*****************************************************************
+1. INITIAL SETTINGS
+*****************************************************************/
+
+/* Limited Reset
+----------------------------------------------------------------*/
+
+html, body, .pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3, .pun h4, .pun h5, .pun pre, .pun blockquote,
+.pun ul, .pun ol, .pun li, .pun dl, .pun dt, .pun dd, .pun th, .pun td, .pun fieldset, .pun legend .pun img,
+.pun abbr, .pun cite {
+	border: 0;
+	font-style: normal;
+	font-weight: normal;
+	margin: 0;
+	padding: 0;
+}
+
+.pun ul, .pun ol {
+	list-style: none;
+}
+
+.pun select {
+	padding-bottom: 1px;
+	padding-top: 1px;
+}
+
+/* Content Defaults
+----------------------------------------------------------------*/
+
+.pun {
+	font: 81.25%/1.462em Arial, Helvetica, sans-serif;
+}
+
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea {
+	font-family: Arial, Helvetica, sans-serif;
+	font-size: 1em;
+}
+
+.pun pre, .pun code {
+	font-family: consolas, monaco, "bitstream vera sans mono", "courier new", courier, monospace;
+	font-size: 1em;
+}
+
+.pun pre code {
+	font-size: 1em;
+}
+
+.pun table {
+	border-collapse: collapse;
+	border-spacing: 0;
+	border: 0;
+	empty-cells: show;
+	width: 100%;
+}
+
+.pun h1 {
+	font:2.154em/1em "Trebuchet MS", Arial, Helvetica, sans-serif;
+	padding: 7px 0;
+}
+
+.pun h2, .pun .hd h2 {
+	font: 1.462em/1em "Trebuchet MS", Arial, Helvetica, sans-serif;
+	padding: 7px 0;
+}
+
+.pun h3 {
+	font-size: 1.154em;
+	line-height: 1.267em;
+	padding: 7px 0;
+}
+
+.pun h4 {
+	font-size: 1.077em;
+	font-weight: bold;
+	padding: 7px 0;
+}
+
+.pun h5, .pun h6 {
+	font-size: 1em;
+	font-weight: bold;
+	padding: 7px 0;
+}
+
+.pun p, .pun ul, .pun ol, .pun dl, .pun th, .pun td, .pun legend {
+	padding: 7px 0;
+}
+
+.pun strong, .pun th, .pun span.warntext, .pun p.warntext {
+	font-weight: bold;
+}
+
+.pun em {
+	font-style: italic;
+}
+
+.pun a, .pun a:link, .pun a:visited {
+	text-decoration: none;
+}
+
+.pun a:hover, .pun a:active, .pun a:focus {
+	text-decoration: underline;
+}
+
+.pun .actions span {
+	padding-left: 16px;
+	padding-right: 8px;
+	background: url(Air/img/bull.png) center left no-repeat;
+	display: inline-block;
+	line-height: normal;
+}
+
+/* Hidden Elements
+----------------------------------------------------------------*/
+
+#brdfooter h2, #brdstats h2, #debug h2, #brdstats .conl dt, #brdstats .conr dt, #modcontrols dt,
+#searchlinks dt, div.postright h3, .pun .subscribelink span, #announce .hd, #reportform h2, #punmoderate #vf h2,
+#punviewforum #vf h2, .pun .required strong span, .pun .icon div {
+	display: block;
+	overflow: hidden;
+	position: absolute;
+	text-indent: -9999em;
+	width: 0;
+}
+
+/* Generic Float Clear
+----------------------------------------------------------------*/
+
+.pun .inbox, .pun #brdmain, .pun .crumbs, .pun .pagepost, .pun .block2col {
+	min-height: 1px;
+}
+
+* html .pun .inbox, * html .pun #brdmain, * html .pun .infldset, * html .pun .crumbs, * html .pun .pagepost, * html .pun .block2col {
+	display: inline-block;
+}
+
+* html .pun .inbox, * html .pun #bdrdmain, * html .pun .infldset, * html .pun .crumbs, * html .pun .pagepost, * html .pun .block2col {
+	display: block;
+}
+
+.pun .inbox:after, .pun #brdmain:after, .pun .crumbs:after, .pun .pagepost:after, .pun .block2col:after {
+	content: " ";
+	display: block;
+	height: 0;
+	font-size: 0;
+	clear: both;
+	visibility: hidden;
+}
+
+.pun .block2col .inbox:after {
+	content: none;
+	clear: none;
+}
+
+/*****************************************************************
+2. COMMON STYLES
+*****************************************************************/
+
+/* Page Layout
+----------------------------------------------------------------*/
+
+.pun {
+	padding: 30px 40px;
+}
+
+#punredirect, #punmaint {
+	padding: 60px 20% 12px 20%;
+}
+
+#puninstall, #pundb_update {
+	padding: 20px 10%;
+}
+
+.pun .punwrap {
+	border: 1px solid;
+	padding: 18px;
+}
+
+.pun .top-box {
+	height: 18px;
+	margin-bottom: -18px;
+	position: relative;
+	background: url(Air/img/main_top.png) top left no-repeat;
+}
+
+.pun .top-box div {
+	height: 18px;
+	margin-left: 50%;
+	background: url(Air/img/main_top.png) top right no-repeat;
+}
+
+.pun .end-box {
+	margin-top: -17px;
+	height: 18px;
+	position: relative;
+	background: url(Air/img/main_end.png) bottom left no-repeat;
+}
+
+.pun .end-box div {
+	height: 18px;
+	margin-left: 50%;
+	background: url(Air/img/main_end.png) bottom right no-repeat;
+}
+
+#punredirect h2, #punmaint h2 {
+	border-bottom-style: dotted;
+	border-bottom-width: 1px;
+	margin-bottom: 3px;
+}
+
+/* Section Spacing and Borders
+----------------------------------------------------------------*/
+
+#brdmain {
+	border-style: solid none;
+	border-width: 2px 0;
+	margin-bottom: 12px;
+	padding: 12px 0;
+}
+
+#punindex #brdmain {
+	padding-top: 24px;
+}
+
+#punredirect #brdmain, #punmaint #brdmain {
+	border: 0;
+	margin: 0;
+	padding: 0;
+}
+
+#brdstats {
+	border-style: solid none none none;
+	border-width: 2px 0 0 0;
+	margin-top: 24px;
+	padding-top: 12px;
+}
+
+#quickpost {
+	border-style: solid none none none;
+	border-width: 2px 0 0 0;
+	margin-top: 12px;
+	padding-top: 12px;
+}
+
+#announce {
+	border-style: solid none none none;
+	border-width: 2px 0 0 0;
+	padding-top: 3px;
+}
+
+/*****************************************************************
+3. COMMON BOARD ELEMENTS
+*****************************************************************/
+
+/* Logo, Description and Main Menu
+----------------------------------------------------------------*/
+
+#brdtitle h1 {
+	padding: 0 0 10px 0;
+}
+
+#brddesc {
+	border-top-style: dotted;
+	border-top-width: 1px;
+	padding: 10px 0;
+}
+
+#brddesc p {
+	padding: 0;
+}
+
+#brdmenu ul {
+	padding: 0;
+}
+
+#brdmenu li {
+	float: left;
+}
+
+#brdmenu a:link, #brdmenu a:visited {
+	border-right-style: solid;
+	border-width: 1px;
+	display: block;
+	min-width: 60px;
+	padding: 12px 16px 6px 8px;
+	white-space: nowrap;
+}
+
+#brdmenu a:hover, #brmenu a:active, #brdmenu a:focus {
+	text-decoration: none;
+}
+
+/* Welcome Box
+----------------------------------------------------------------*/
+
+#brdwelcome {
+	padding: 10px 0;
+}
+
+#brdwelcome .conl, #brdwelcome .conr, #brdwelcome p, #brdwelcome li {
+	display: inline;
+	padding: 0;
+}
+
+#brdwelcome li span {
+	background: url(Air/img/bull.png) center left no-repeat;
+	padding-left: 18px;
+	margin-right: 3px;
+	display: inline-block;
+	line-height: normal;
+	white-space: nowrap;
+}
+
+#brdwelcome .conl li:first-child span {
+	padding-left: 0;
+	background: none;
+}
+
+/* Stats
+----------------------------------------------------------------*/
+
+#brdstats .conl {
+	float: left;
+}
+
+#brdstats .conr {
+	float: right;
+	text-align: right;
+}
+
+#brdstats #onlinelist {
+	border-top-style: dotted;
+	border-top-width: 1px;
+	clear: both;
+}
+
+#brdstats #onlinelist dt, #brdstats #onlinelist dd {
+	display: inline;
+}
+
+/* Footer
+----------------------------------------------------------------*/
+
+.pun #modcontrols {
+	border-style: none none dotted none;
+	border-width: 0 0 1px 0;
+	margin-bottom: 4px;
+	text-align: center;
+	width: 100%;
+}
+
+.pun #modcontrols dd {
+	display: inline;
+}
+
+.pun #brdfooter #modcontrols dd span {
+	background: url(Air/img/bull.png) center left no-repeat;
+	display: inline-block;
+	line-height: normal;
+	padding-left: 18px;
+	white-space: nowrap;
+}
+
+.pun #brdfooter .conl {
+	float: left;
+}
+
+.pun #brdfooter .conr {
+	text-align: right;
+	float: right;
+}
+
+.pun #brdfooter #poweredby a {
+	font-size: 1.077em;
+	font-weight: bold;
+}
+
+.pun #brdfooter #qjump {
+	padding-top: 5px;
+}
+
+.pun #brdfooter #qjump * {
+	white-space: nowrap;
+}
+
+.pun #brdfooter #searchlinks dd span {
+	background: url(Air/img/bull.png) center left no-repeat;
+	display: inline-block;
+	line-height: normal;
+	padding-left: 18px;
+	white-space: nowrap;
+}
+
+.pun #brdfooter #feedlinks {
+	padding-bottom: 0;
+}
+
+.pun #brdfooter #feedlinks span {
+	background: url(Air/img/feed.png) center left no-repeat;
+	display: inline-block;
+	padding-left: 18px;
+	white-space: nowrap;
+}
+
+.pun #debugtime {
+	border-style: dotted none none none;
+	border-width: 1px 0 0 0;
+	margin-top: 7px;
+	text-align: center;
+}
+
+/* Breadcrumbs, Postlink, Pagination
+----------------------------------------------------------------*/
+
+.pun .linkst .inbox, .pun linksb .inbox, .pun .postlinksb .inbox {
+	overflow: hidden;
+}
+
+.pun .linksb, .pun .postlinksb, .pun .linkst, .pun .crumbs {
+	clear: both;
+	position: relative;
+}
+
+.pun .linkst .crumbs {
+	font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+	font-size: 1.462em;
+	line-height: 1.211em;
+	padding: 7px 0;
+}
+
+.pun .linksb .crumbs, .pun .postlinksb .crumbs {
+	font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+	font-size: 1.154em;
+}
+
+.pun .linkst .crumbsplus .pagepost {
+	border-top-style: dotted;
+	border-top-width: 1px;
+}
+
+.pun .linksb .crumbsplus .pagepost, .pun .postlinksb .crumbsplus .pagepost {
+	border-bottom-style: dotted;
+	border-bottom-width: 1px;
+}
+
+.pun .postlinksb .crumbs {
+	margin-right: 11em;
+}
+
+.pun .crumbs li {
+	float: left;
+	padding-right: 0.4em;
+	white-space: nowrap;
+}
+
+.pun .crumbs li strong {
+	font-weight: normal;
+}
+
+.pun .pagelink {
+	float: left;
+	white-space: nowrap;
+}
+
+.pun .pagelink strong, .pun .pagelink a, .pun .pagelink span.spacer {
+	border-style: none none none solid;
+	border-width: 0 0 0 1px;
+	display: inline-block;
+	padding: 0 12px 0 10px;
+	margin-right: -6px;
+}
+
+.pun .pagelink .item1 {
+	border: 0;
+}
+
+.pun .pagelink .pages-label {
+	display: inline-block;
+}
+
+.pun .postlink {
+	float: right;
+	font-weight: bold;
+	text-align: right;
+}
+
+.pun .modbuttons {
+	float: right;
+	padding: 5px 0 3px 0;
+}
+
+.pun .modbuttons input {
+	margin-left: 8px;
+}
+
+.pun .subscribelink {
+	position: absolute;
+	right: 0;
+	text-align: right;
+	top: 33px;
+}
+
+/*****************************************************************
+4. MAIN TABLES
+*****************************************************************/
+
+.pun #brdmain .blocktable {
+	position: relative;
+}
+
+#punindex #brdmain .blocktable h2, #punsearch #vf h2 {
+	font: 1em/1.462em Arial, Helvetica, sans-serif;
+	font-weight: bold;
+	margin: 1px 1px 0 1px;
+	padding-left: 8px;
+	position: absolute;
+	left: 0;
+	white-space: nowrap;
+	z-index: 100;
+}
+
+#punindex .blocktable th.tcl, #punsearch #vf th.tcl {
+	font-size: 0;
+	text-indent: -9999em;
+}
+
+.pun .blocktable .box {
+	border-style: solid;
+	border-width: 1px;
+	margin-bottom: -1px;
+	overflow: hidden;
+	position: relative;
+}
+
+* html .pun .blocktable .box {
+	display: inline-block;
+}
+
+.pun .blocktable table {
+	table-layout: fixed;
+	margin-bottom: -1px;
+}
+
+.pun .blocktable th {
+	padding: 7px 8px;
+	border-style: none none solid none;
+	border-width: 1px;
+	text-align: left;
+}
+
+.pun .blocktable td {
+	padding: 7px 8px;
+	line-height: 1.3077em;
+	border-style: none none solid none;
+	border-width: 1px;
+	text-align: left;
+}
+
+.pun .blocktable h3 {
+	font-size: 1.077em;
+	font-weight: bold;
+	padding: 0;
+}
+
+.pun .blocktable p {
+	padding: 0;
+}
+
+.pun .blocktable .tcl p {
+	padding: 5px 0 0 0;
+}
+
+.pun .blocktable .tcl {
+	width: auto;
+}
+
+.pun .blocktable .tc2, .pun .blocktable .tc3, .pun .blocktable .tcmod {
+	padding-left: 0;
+	padding-right: 0;
+	text-align: center;
+	width: 11%;
+}
+
+.pun .blocktable .tcr {
+	width: 30%;
+}
+
+.pun .blocktable td .newtext, .pun .blocktable td .pagestext, .pun .blocktable td .byuser {
+	white-space: nowrap;
+}
+
+.pun .blocktable .tcl h3 span.newtext {
+	font-size: 0.929em;
+	font-weight: normal;
+}
+
+.pun #vf td.tcl span.stickytext, .pun #vf td.tcl span.closedtext {
+	font-size: 1em;
+	font-weight: bold;
+}
+
+#punsearch #vf .tc2 {
+	padding-left: 8px;
+	padding-right: 8px;
+	text-align: left;
+	width: 18%;
+}
+
+#users1 .tcr {
+	width: 25%;
+}
+
+#users1 .tc2 {
+	padding-left: 8px;
+	padding-right: 8px;
+	text-align: left;
+	width: 25%;
+}
+
+#debug {
+	margin-top: 12px;
+}
+
+#debug .tcl {
+	width: 10%;
+}
+
+#punredirect #debug .tcl, #punmaint #debug .tcl {
+	width: 20%;
+}
+
+#debug .tcr {
+	width: 90%;
+	white-space: normal
+}
+
+#punindex .tcr .byuser {
+	display: block
+}
+
+#punindex td.tc2, #punindex td.tc3, #punindex td.tcr, .pun #vf td.tc2, .pun #vf td.tc3,
+.pun #vf td.tcr, #punindex td.tcl div.forumdesc, .pun #vf td.tcl span {
+	font-size: 0.923em;
+}
+
+.pun #vf td.tcl a {
+	font-weight: bold;
+}
+
+.pun #vf td.tcl span a {
+	font-weight: normal;
+}
+
+.pun .blocktable .tclcon {
+	height: 1%;
+	min-height: 1px;
+	overflow: hidden;
+	padding: 0 11px 0 12px;
+	position: relative;
+}
+
+.pun .blocktable .tclcon div {
+	width: 100%;
+	overflow: hidden;
+}
+
+.pun .icon {
+	border-style: solid;
+	border-width: 8px;
+	float: left;
+	height: 0;
+	overflow: hidden;
+	width: 0;
+}
+
+.pun .iposted .ipost {
+	font-weight: bold;
+	left: 0;
+	padding-left: 4px;
+	position: absolute;
+	text-align: center;
+	top: 0;
+	width: 8px;
+}
+
+/*****************************************************************
+MAIN POSTS
+*****************************************************************/
+
+/* Structure
+----------------------------------------------------------------*/
+
+.pun .blockpost {
+	border-style: solid;
+	border-width: 1px;
+	margin-bottom: -1px;
+	overflow: hidden;
+	position: relative;
+}
+
+* html .pun .blockpost {
+	display: inline-block;
+}
+
+.pun .blockpost h2 {
+	font: 1em/1.462em Arial, Helvetica, sans-serif;
+	white-space: nowrap;
+	border-bottom-style: solid;
+	border-bottom-width: 1px;
+	height: 1.462em;
+	padding: 0.538em 8px 0.538em 236px;
+	font-weight: normal;
+}
+
+#punsearch .blockpost h2 {
+	height: auto;
+	padding-left: 36px;
+	white-space: normal;
+}
+
+#punsearch .blockpost h2 span span {
+	white-space: nowrap;
+	display: inline-block;
+	font: 1.077em "Trebuchet MS", Arial, Helvetica, sans-serif
+}
+
+#punsearch .blockpost .icon {
+	position: absolute;
+	top: 0;
+	margin-top: -2.154em;
+}
+
+.pun .blockpost h2 .conr {
+	float: right;
+	text-align: right;
+}
+
+.pun .blockpost .inbox {
+	float: right;
+	position: relative;
+	width: 100%;
+}
+
+.pun .blockpost .postbody, .pun .blockpost .postfoot {
+	border-left-style: solid;
+	border-left-width: 1px;
+	float: right;
+	margin-right: -218px;
+	position: relative;
+	text-align: left;
+	width: 100%;
+}
+
+.pun .blockpost .postleft, .pun .blockpost .postfootleft {
+	width: 206px;
+	padding: 7px 0 7px 12px;
+	float: left;
+	margin-left: -218px;
+	position: relative;
+}
+
+.pun .blockpost .postleft dl {
+	padding: 0;
+}
+
+#punviewtopic .blockpost dt, #punmoderate .blockpost dt {
+	display: block;
+	position: absolute;
+	padding: 0.538em 0 0.538em 12px;
+	height: 1.462em;
+	top: -2.615em;
+	left: 0;
+	overflow: hidden;
+	width: 206px;
+}
+
+.pun .blockpost dt strong {
+	font-size: 1.231em;
+	font-weight: bold;
+}
+
+.pun .blockpost .postleft dd {
+	font-size: 0.923em;
+}
+
+.pun .blockpost .postleft .usertitle {
+	padding: 4px 0 6px 0;
+	font-size: 1em;
+}
+
+.pun .blockpost .postleft .postavatar {
+	display: block;
+	margin: 0 0 4px 0;
+}
+
+.pun .blockpost .postright {
+	position: relative;
+	padding: 4px 230px 7px 18px;
+}
+
+.pun .postmsg {
+	width:100%;
+	overflow: hidden;
+}
+
+.pun .blockpost .postfootright {
+	position: relative;
+	padding: 7px 230px 7px 18px;
+	text-align: right;
+}
+
+.pun .postfoot p, .pun .postfoot ul {
+	padding: 0;
+}
+
+.pun .blockpost .postfootright li {
+	display: inline;
+}
+
+.pun .blockpost .postfootright li span {
+	display: inline-block;
+	padding-left: 16px;
+	margin-left: 8px;
+	line-height: normal;
+	background: url(Air/img/bull.png) center left no-repeat;
+}
+
+.pun .blockpost .usercontacts {
+	padding: 7px 0;
+}
+
+.pun .blockpost .usercontacts .email {
+	background: url(Air/img/email.png) left 65% no-repeat;
+	margin-right: 5px;
+	padding-left: 21px;
+	display: inline-block;
+	line-height: normal;
+}
+
+.pun .blockpost .usercontacts .website {
+	background: url(Air/img/ext.png) left 65% no-repeat;
+	padding-left: 18px;
+	display: inline-block;
+	line-height: normal;
+}
+
+.pun .postsignature hr {
+	border:none;
+	height: 1px;
+	margin-left: 0px;
+	text-align: left;
+}
+
+/* Content (includes other user content)
+----------------------------------------------------------------*/
+
+.pun .usercontent {
+	padding: 7px 0;
+}
+
+.pun .postmsg p, .pun .postmsg li, #punhelp p samp {
+	font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+.pun .usercontent h1, .pun .usercontent h2, .pun .usercontent h3,
+.pun .usercontent h4, .pun .usercontent h5, .pun .usercontent h6 {
+	padding: 7px 0 0 0;
+}
+
+.pun .postmsg h5, #punhelp h5 {
+	font-size: 1.231em;
+	font-weight: bold;
+	padding: 7px 0;
+}
+
+.pun .usercontent ul, .pun .postmsg ul {
+	list-style: disc;
+	padding: 4px 13px 4px 30px;
+}
+
+.pun .usercontent ol, .pun .postmsg ol {
+	list-style: decimal;
+	padding: 4px 13px 4px 30px;
+}
+
+.pun .usercontent ol.alpha, .pun .postmsg ol.alpha {
+	list-style: lower-alpha;
+}
+
+.pun .usercontent li, .pun .postmsg li {
+	padding: 0 3px;
+}
+
+.pun .usercontent li p, .pun .postmsg li p {
+	padding: 0;
+}
+
+.pun span.bbu {
+	text-decoration: underline;
+}
+
+.pun span.bbs, .pun del {
+	text-decoration: line-through;
+}
+
+.pun .postmsg ins, #punhelp samp ins {
+	text-decoration: none;
+}
+
+.pun .blockpost .postmsg .postedit {
+	font-size: 0.857em;
+}
+
+.pun .blockform .postsignature, .pun .blockpost .postsignature {
+	font-size: 0.923em;
+}
+
+.pun .codebox {
+	border-style: solid;
+	border-width: 1px;
+	margin: 0.75em 1em;
+	padding: 0;
+}
+
+.pun .quotebox {
+	border-style: solid;
+	border-width: 1px 1px 1px 3px;
+	margin: 0.75em 1em;
+	padding: 0 0.75em;
+}
+
+.pun .quotebox cite {
+	display: block;
+	padding: 0.75em 0 0 0;
+	font-weight: bold;
+	line-height: 1.462em;
+}
+
+.pun .quotebox blockquote {
+	overflow: hidden;
+	width: 100%;
+}
+
+.pun .codebox pre {
+	overflow-y:hidden;
+	overflow: auto;
+	width: 100%;
+	direction: ltr;
+	text-align: left;
+}
+
+* html .pun .codebox pre {
+	padding-bottom: 10px;
+}
+
+*:first-child+html .pun .codebox pre {
+	padding-bottom: 10px;
+}
+
+.pun .codebox pre code {
+	padding: 0.75em;
+	white-space: pre;
+}
+
+.pun div[class*=codebox] pre code {
+	display: inline-block;
+}
+
+* html .pun .codebox pre code {
+	display: block;
+}
+
+.pun .codebox pre.vscroll {
+	height: 32em;
+	overflow: auto;
+	overflow-y: auto
+}
+
+.pun .postmsg img, #punhelp samp img {
+	vertical-align: text-top;
+}
+
+.pun .postmsg .postimg img {
+	max-width: 98%;
+	vertical-align: middle;
+}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+	border-style: solid;
+	border-width: 2px;
+}
+
+/*****************************************************************
+MAIN FORMS
+*****************************************************************/
+
+#punedit .blockform h2, #punpost .blockform h2, #postpreview h2, #posterror h2,
+.pun #quickpost h2, .pun #reportform h2, #pundelete .blockform h2 {
+	font: 1em/1.462em Arial, Helvetica, sans-serif;
+	font-weight: bold;
+	white-space: nowrap;
+	padding: 10px 19px 4px 37px;
+	border: 0;
+}
+
+#punpost .blockform h2, #punedit .blockform h2,.pun #quickpost h2,
+#pundelete .blockform h2 {
+	margin: 1px 1px 0 1px;
+	width: 25em;
+	position: absolute;
+	z-index: 100;
+}
+
+.pun #quickpost legend, #punpost legend, #punedit legend {
+	width: 25em;
+	overflow: hidden;
+	white-space: nowrap;
+}
+
+.pun .blockform .box {
+	border-style: solid;
+	border-width: 1px;
+	padding-bottom: 12px;
+}
+
+.pun #posterror {
+	border-style: solid;
+	border-width: 1px;
+}
+
+.pun #posterror .box {
+	padding: 0 18px 12px 18px;
+}
+
+* html .pun .blockform .box, * html .pun #posterror {
+	display: inline-block;
+}
+
+.pun .blockform .forminfo, .pun .error-info {
+	padding: 12px 18px;
+	border-style: solid;
+	border-width: 1px;
+	position: relative;
+}
+
+.pun .blockform .forminfo {
+	margin-top: 12px;
+}
+
+#pundelete .blockform .forminfo {
+	margin-top: 33px;
+}
+
+.pun .forminfo h3 {
+	padding-bottom: 0;
+}
+
+.pun .error-list li {
+	padding-left: 24px;
+	background: url(Air/img/exclaim.png) center left no-repeat;
+}
+
+.pun .inform {
+	padding: 0 18px;
+}
+
+.pun legend {
+	font-weight: bold;
+	padding: 10px 19px 4px 19px;
+}
+
+* html .pun legend {
+	margin-left: -7px;
+}
+
+*:first-child+html .pun legend {
+	margin-left: -7px;
+}
+
+.pun .infldset {
+	border-style: solid;
+	border-width: 1px;
+	padding: 12px 18px;
+}
+
+#punregister #rules .infldset {
+	padding: 5px 18px;
+}
+
+.pun fieldset p {
+	padding: 0 0 7px 0;
+	width: 100%;
+}
+
+.pun fieldset .usercontent p {
+	padding: 7px 0;
+}
+
+.pun fieldset label {
+	display: block;
+	padding: 0 0 7px 0;
+}
+
+.pun label em {
+	font-weight: normal;
+	font-style: normal;
+}
+
+.pun .required strong {
+	background: url(Air/img/asterisk.png) center right no-repeat;
+	font-weight: normal;
+	padding-right: 14px;
+	white-space: pre;
+	display: inline-block;
+	line-height: normal;
+}
+
+.pun label input, .pun label select, .pun label textarea {
+	margin-top: 2px;
+}
+
+.pun label.conl {
+	display: inline-block;
+	padding-right: 12px;
+}
+
+.pun form .buttons {
+	padding: 8px 19px 8px 34px;
+	margin-bottom: -12px;
+}
+
+.pun .blockform .buttons input {
+	margin-right: 12px;
+}
+
+.pun .rbox {
+	padding: 3px 0;
+}
+
+.pun .rbox label {
+	padding: 3px 0 3px 1.75em;
+	position: relative;
+	min-height: 1px;
+}
+
+* html .pun .rbox label {
+	text-indent: -3px;
+	height: 1%;
+}
+
+.pun .rbox input {
+	margin: 3px 0.75em 3px -1.75em;
+	float: left;
+	position: relative;
+	vertical-align: middle;
+	padding: 0;
+	height: 1em;
+	width: 1em;
+}
+
+.pun input[type=text], .pun select, .pun textarea {
+	font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+.pun .txtarea textarea, .pun input.longinput {
+	width: 98%;
+}
+
+.pun #quickpost .txtarea {
+	padding-right: 12px;
+	padding-left: 200px;
+	position: relative;
+}
+
+.pun .blockform .bblinks {
+	padding-top: 0;
+}
+
+.pun .blockform .bblinks li {
+	display: inline;
+}
+
+.pun .blockform .bblinks li span {
+	background: url(Air/img/help.png) center left no-repeat;
+	margin-right: 8px;
+	padding-left: 20px;
+	display: inline-block;
+}
+
+.pun #quickpost .bblinks {
+	left: 18px;
+	line-height: 1.75em;
+	position:absolute;
+	top: 18px;
+	width: 12em;
+}
+
+.pun #quickpost .bblinks li {
+	display: block;
+}
+
+.pun #login p.clearb {
+	border-top-style: dotted;
+	border-top-width: 1px;
+	font-size: 0;
+	height: 0;
+	line-height: 0;
+	margin-top: 7px;
+	overflow: hidden;
+	padding-bottom: 3px;
+	padding-top: 7px;
+	text-indent: -9999em;
+	width: 100%;
+}
+
+.pun #postreview {
+	padding-top: 12px;
+}
+
+.pun #postpreview, .pun #posterror {
+	margin-bottom: 12px;
+}
+
+.pun #postpreview .postright {
+	padding: 0;
+}
+
+.pun #postpreview .postbody {
+	border-style: solid;
+	border-width: 1px;
+	float: none;
+	margin: 0 18px 12px 18px;
+	padding: 0;
+	padding: 4px 18px 4px 18px;
+	width: auto;
+}
+
+.pun span.email {
+	background: url(Air/img/email.png) left 65% no-repeat;
+	margin-right: 5px;
+	padding-left: 21px;
+	display: inline-block;
+	line-height: normal;
+}
+
+.pun span.website {
+	background: url(Air/img/ext.png) left 65% no-repeat;
+	padding-left: 18px;
+	display: inline-block;
+	line-height: normal;
+}
+
+#punmisc #rules .box {
+	border-style: solid;
+	border-width: 1px;
+	padding: 5px 18px;
+}
+
+
+#punhelp .box {
+	border-style: solid;
+	border-width: 1px;
+	padding: 7px 12px;
+}
+
+/*****************************************************************
+PROFILES (+ ADMIN MENU)
+*****************************************************************/
+
+/* Profile / Admin
+----------------------------------------------------------------*/
+
+.pun .blockmenu {
+	width: 13em;
+	float: left;
+	padding-bottom: 12px;
+}
+
+.pun .block2col .blockform, .pun .block2col .block {
+	margin-left: 15em;
+}
+
+.pun .blockmenu .block2 {
+	padding-top: 19px;
+}
+
+.pun .blockmenu ul {
+	border-top-style: dotted;
+	border-top-width: 1px;
+	padding: 0;
+}
+
+.pun .blockmenu li {
+	border-bottom-style: dotted;
+	border-bottom-width: 1px;
+	font-weight: bold;
+	padding: 0;
+}
+
+.pun .blockmenu a:link, .pun .blockmenu a:visited {
+	display: block;
+	padding: 9px 6px 3px 6px;
+	min-height: 1px;
+	text-decoration: none;
+}
+
+* html .pun .blockmenu a:link,  * html .pun .blockmenu a:visited {
+	height: 1%;
+}
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active, .pun .blockmenu a:focus {
+	text-decoration: none;
+}
+
+#viewprofile .box {
+	border-style: solid;
+	border-width: 1px;
+	padding-bottom: 18px;
+}
+
+#viewprofile dt, #adstats dt {
+	padding: 7px 0;
+	position: absolute;
+	width: 13em;
+	left: 0;
+}
+
+#viewprofile dl {
+	border-style: solid none none none;
+	border-width: 1px;
+	margin: 7px 0;
+	padding: 0;
+	width: 100%;
+	position: relative;
+}
+
+#adintro, #adstats {
+	border-style: solid;
+	border-width: 1px;
+	padding: 18px;
+}
+
+#adstats .inbox, #adintro .inbox {
+	border-style: solid;
+	border-width: 1px;
+	padding: 18px;
+}
+
+#adstats dl {
+	margin: 0;
+	padding: 0;
+	width: 100%;
+	position: relative;
+}
+
+#viewprofile dd, #adstats dd {
+	border-style: none none solid none;
+	border-width: 1px;
+	padding: 7px 0 7px 13em;
+}
+
+/*****************************************************************
+COLOUR SCHEME
+*****************************************************************/
+
+/* Basic defaults and Common Items
+----------------------------------------------------------------*/
+
+html, body, .pun {
+	background: #f6f9fc;
+	color: #333;
+}
+
+.pun .punwrap {
+	background: #fff;
+	border-color: #cad7e1;
+	color: #566579;
+}
+
+#brdtitle #brddesc, .pun .pagepost, #brdstats #onlinelist, #brdfooter #searchlinks, #brdfooter #modcontrols,
+#punmaint h2, #punredirect h2, #adminconsole .submittop, .pun #debugtime, .pun .pagelink a, .pun .pagelink * {
+	border-color: #b9c5ce;
+}
+
+.pun a, .pun a:link, .pun a:visited {
+	color: #2365B0;
+}
+
+.pun a:hover, .pun a:active, .pun a:focus {
+	color: #b50000;
+}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+	border-color: #22538a;
+}
+
+.pun .postmsg .postimg a:hover img, .pun .postmsg .postimg a:active img, .pun .postmsg .postimg a:focus img {
+	border-color: #b50000;
+}
+
+/* Primary Navigation
+----------------------------------------------------------------*/
+
+#brdmenu {
+	background: #44699c;
+}
+
+#brdmenu a, #brdmenu a:link, #brdmenu a:visited {
+	background: #44699c;
+	border-color: #fff;
+	color: #d4dae2;
+}
+
+#brdmenu a:hover, #brdmenu a:active, #brdmenu a:focus {
+	background: #b50000;
+	border-color: #fff;
+	color: #fff;
+}
+
+/* Main Tables
+----------------------------------------------------------------*/
+
+.pun .blocktable .box {
+	background: #fcfdfe;
+	border-color: #b9c5ce #d9e1e7;
+}
+
+#punindex .blocktable h2, .pun #vf h2 {
+	color: #357082;
+}
+
+#adminconsole fieldset th, #adminconsole fieldset td {
+	background: #f6f9fc;
+	border-color: #dfe6ee;
+}
+
+.pun #users1 h2 {
+	background: #fff;
+}
+
+.pun .blocktable td {
+	border-color: #dfe6ee;
+}
+
+.pun .blocktable th {
+	background: #ebf1f5;
+	border-color: #cad7e1;
+	color: #357082;
+}
+
+.pun .blocktable td.tcl span.stickytext {
+	color: #3399CC;
+}
+
+/* Main Posts
+----------------------------------------------------------------*/
+
+.pun .blockpost {
+	background: #f6f9fc;
+	border-color: #b9c5ce #d9e1e7;
+}
+
+.pun .blockpost h2 {
+	background: #ebf1f5;
+	border-color: #cad7e1;
+	color: #357082;
+}
+
+.pun .blockpost .postbody, .pun .blockpost .postfoot {
+	background: #fcfdfe;
+	border-color: #dfe6ee;
+}
+
+.pun .blockpost .postfootright li {
+	color: #fcfdfe;
+}
+
+.pun .postmsg, #punhelp code, #punhelp samp {
+	color: #333;
+}
+
+.pun .postsignature, .pun .postmsg .postedit {
+	color: #566579;
+}
+
+.pun .quotebox {
+	background: #f8f9f0;
+	border-color: #7aadbd;
+	color: #566579;
+}
+
+.pun .quotebox cite {
+	color: #357082;
+}
+
+.pun .codebox, #punhelp .codebox code {
+	background: #333;
+	color: #fff;
+}
+
+.pun .postmsg hr {
+	background: #b9c5ce;
+}
+
+.pun .postmsg ins, #punhelp samp ins {
+	background-color: #ff0;
+}
+
+/* Main Forms + Profile
+----------------------------------------------------------------*/
+
+.pun .blockform .box, #adstats, #adintro, #postpreview, #posterror {
+	border-color: #b9c5ce #d9e1e7;
+	background: #ebf1f5;
+}
+
+#punmisc #rules .box, #punhelp .box {
+	border-color: #b9c5ce #d9e1e7;
+	background: #f6f9fc;
+}
+
+.pun #quickpost h2, #punpost .blockform h2, #punedit .blockform h2, #posterror h2,
+#pundelete .blockform h2 {
+	background: #ebf1f5;
+	color: #357082;
+}
+
+.pun .forminfo {
+	background: #fff;
+	border-color: #dfe6ee;
+}
+
+#puninstall form#install .forminfo {
+	background: #44699c;
+	color: #fff;
+}
+
+.pun #posterror .error-info {
+	background: #ffffe1;
+	border-color: #dfe6ee;
+}
+
+#puninstall form#install .error-info {
+	background: #ffffe1;
+	border-color: #dfe6ee;
+	color: #333;
+}
+
+.pun .infldset, #adintro .inbox, #adstats .inbox {
+	background: #f6f9fc;
+	border-color: #dfe6ee;
+}
+
+.pun label, .pun legend, #adminconsole fieldset th {
+	color: #357082;
+}
+
+.pun fieldset p {
+	border-color: #b9c5ce;
+}
+
+.pun .blockmenu ul, .pun .blockmenu li {
+	border-color: #b9c5ce;
+}
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active, .pun .blockmenu a:focus {
+	background: #ffffe6;
+}
+
+.pun .blockmenu .isactive a:link, .pun .blockmenu .isactive a:visited {
+	color: #333;
+	background: #f6f9fc;
+	}
+
+.pun #viewprofile .box {
+	border-color: #b9c5ce #d9e1e7;
+	background: #ebf1f5;
+}
+
+.pun #viewprofile dt, #adstats dt {
+	color: #357082;
+}
+
+.pun #viewprofile dl, .pun #viewprofile dd, #adstats dl, #adstats dd {
+	border-color: #dfe6ee;
+}
+
+#adminconsole fieldset td.nodefault {
+	background: #d59b9b;
+}
+
+/* Status Indicators
+----------------------------------------------------------------*/
+
+.pun .icon {
+	border-color: #e8ecf1 #d4d9dd #dfe3e8 #e8ecf1;
+}
+
+.pun .iredirect .icon {
+	border-color: #b9c5ce;
+	border-width: 1px;
+	padding: 7px;
+}
+
+.pun .inew .icon {
+	border-color: #91b3d9 #87a8d1 #6c85bb #7292c3;
+}
\ No newline at end of file
diff --git a/style/Air/base_admin.css b/style/Air/base_admin.css
new file mode 100644
index 0000000..60a064e
--- /dev/null
+++ b/style/Air/base_admin.css
@@ -0,0 +1,143 @@
+#adminconsole .blockform .box {
+	padding-bottom: 12px;
+}
+
+#adminconsole fieldset .infldset {
+	position: relative;
+	overflow: hidden;
+}
+
+#adminconsole fieldset table {
+	margin-top: -1px;
+	margin-bottom: -1px;
+}
+
+#adminconsole fieldset td, #adminconsole fieldset th {
+	padding: 10px 8px 10px 0;
+	text-align: left;
+	white-space: normal;
+	border-style: solid none;
+	border-width: 1px 0;
+}
+
+#punadmin thead th {
+	border-top: 0;
+	font-weight: normal;
+}
+
+#adminconsole fieldset td span, #adminconsole fieldset th span {
+	display: block; font-size: 1em;
+	font-weight: normal;
+}
+
+#adminconsole fieldset td.location span {
+	display: inline-block;
+}
+
+#adminconsole fieldset tbody th {
+	width: 15em;
+	font-weight: normal;
+	padding-right: 8px;
+}
+
+#adminconsole table.aligntop th, #adminconsole table.aligntop td {
+	vertical-align: top;
+}
+
+#adminconsole table.aligntop th div {
+	padding-top: 3px;
+}
+
+#adminconsole .inform {
+	padding-bottom: 0;
+}
+
+#adminconsole .infldset {
+	padding-bottom: 0;
+	padding-top: 0;
+}
+
+#adminconsole p.submittop {
+	text-align: center;
+	border-bottom-style: dotted;
+	border-bottom-width: 1px;
+	margin: 0 18px;
+	padding-top: 12px;
+}
+
+#adminconsole p.submitend {
+	text-align: center;
+	padding-bottom: 0;
+}
+
+#adminconsole fieldset p {
+	padding: 10px 0;
+}
+
+#adminconsole .fsetsubmit {
+	padding: 10px 0 12px 0;
+}
+
+#categoryedit .tcl {
+	width: 25%;
+}
+
+#censoring .tcl, #censoring .tc2, #ranks .tcl, #ranks .tc2 {
+	width: 20%;
+}
+
+#edforum .tcl {
+	width: 18%;
+}
+
+#edforum .tc2 {
+	width: 12%;
+}
+
+#forumperms thead th, #forumperms tbody td {
+	text-align: center;
+}
+
+.pun .linkst .backlink, .pun .linksb .backlink {
+	padding: 7px 0;
+}
+
+#punadmin #users1 h2, #punadmin #users2 h2, #punadmin #bans1 h2 {
+	display: block;
+	left: -9999em;
+	overflow: hidden;
+	position: absolute;
+	text-indent: -9999em;
+	width: 0;
+}
+
+#punadmin #users1 th, #punadmin #users2 th, #punadmin #bans1 th {
+	font-weight: bold;
+	}
+
+#users2 th, #bans1 th {
+	text-align: left;
+}
+
+#users2 .tcl, #users2 .tc3, #users2 .tc5, #bans1 .tcl, #bans1 .tc3, #bans1 .tc5, #bans1 .tc6 {
+	width: 15%;
+	text-align: left;
+}
+
+#users2 .tc2, #bans1 .tc2 {
+	width: 22%;
+	text-align: left;
+}
+
+#users2 .tc4, #bans1 .tc4 {
+	width: 8%;
+	text-align: center;
+}
+
+#users2 .tcr, #bans1 .tcr {
+	white-space: nowrap;
+}
+
+.plugin p {
+	padding: 12px 18px 0;
+}
diff --git a/style/Air/img/asterisk.png b/style/Air/img/asterisk.png
new file mode 100644
index 0000000..d00ef54
Binary files /dev/null and b/style/Air/img/asterisk.png differ
diff --git a/style/Air/img/bull.png b/style/Air/img/bull.png
new file mode 100644
index 0000000..c0706f8
Binary files /dev/null and b/style/Air/img/bull.png differ
diff --git a/style/Air/img/email.png b/style/Air/img/email.png
new file mode 100644
index 0000000..e3ee58d
Binary files /dev/null and b/style/Air/img/email.png differ
diff --git a/style/Air/img/exclaim.png b/style/Air/img/exclaim.png
new file mode 100644
index 0000000..92eb57b
Binary files /dev/null and b/style/Air/img/exclaim.png differ
diff --git a/style/Air/img/ext.png b/style/Air/img/ext.png
new file mode 100644
index 0000000..4d63756
Binary files /dev/null and b/style/Air/img/ext.png differ
diff --git a/style/Air/img/feed.png b/style/Air/img/feed.png
new file mode 100644
index 0000000..d4cf8ce
Binary files /dev/null and b/style/Air/img/feed.png differ
diff --git a/style/Air/img/help.png b/style/Air/img/help.png
new file mode 100644
index 0000000..2b596de
Binary files /dev/null and b/style/Air/img/help.png differ
diff --git a/style/Air/img/index.html b/style/Air/img/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/style/Air/img/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/style/Air/img/main_end.png b/style/Air/img/main_end.png
new file mode 100644
index 0000000..14fb076
Binary files /dev/null and b/style/Air/img/main_end.png differ
diff --git a/style/Air/img/main_top.png b/style/Air/img/main_top.png
new file mode 100644
index 0000000..b98d1bb
Binary files /dev/null and b/style/Air/img/main_top.png differ
diff --git a/style/Air/index.html b/style/Air/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/style/Air/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/style/Cobalt.css b/style/Cobalt.css
new file mode 100644
index 0000000..a4c49a7
--- /dev/null
+++ b/style/Cobalt.css
@@ -0,0 +1,1086 @@
+/*****************************************************************
+1. INITIAL SETTINGS
+*****************************************************************/
+
+/* Limited Reset
+----------------------------------------------------------------*/
+
+.pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
+.pun h4, .pun h5, .pun pre, .pun blockquote, .pun ul, .pun ol, .pun li, .pun dl,
+.pun dt, .pun dd, .pun th, .pun td, .pun fieldset, .pun img, .pun abbr, .pun cite {
+	margin: 0;
+	padding: 0;
+	border: 0;
+	}
+
+.pun ul, .pun ol {
+	list-style: none
+	}
+
+
+/* Structural Settings
+----------------------------------------------------------------*/
+
+.pun .clearer, .pun .nosize {
+	height: 0;
+	width: 0;
+	line-height: 0;
+	font-size: 0;
+	overflow: hidden
+	}
+
+.pun .clearer, .pun .clearb {
+	clear: both
+	}
+
+.pun .nosize {
+	position: absolute;
+	left: -9999em;
+	text-indent: -9999em;
+	width: 0;
+	}
+
+* html .inbox, * html .inform, * html .pun, * html .tclcon, * html .codebox {
+	height: 1px
+	}
+
+.pun, .pun .inbox, .pun .inform, .pun .tclcon, .pun .codebox {
+	min-height: 1px
+	}
+
+
+/* Hidden Elements
+----------------------------------------------------------------*/
+
+#brdfooter h2, #brdstats h2, #brdstats .conl dt, #brdstats .conr dt,
+#modcontrols dt, #searchlinks dt, div.postright h3, span.closedtext,
+.pun .required strong span {
+	position: absolute;
+	display: block;
+	overflow: hidden;
+	width: 0;
+	left: -9999em;
+	text-indent: -9999em;
+	}
+
+/*****************************************************************
+2. TEXT & CONTENT
+*****************************************************************/
+
+/* Text Defaults
+----------------------------------------------------------------*/
+
+.pun {
+	font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
+	}
+
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp {
+	font-size: 1em;
+	font-family: verdana, helvetica, arial, sans-serif;
+	}
+
+.pun pre, .pun code {
+	font-size: 1.182em;
+	font-family: consolas, monaco, "bitstream vera sans mono", "courier new", courier, monospace
+	}
+
+.pun pre code {
+	font-size: 1em;
+	}
+
+.pun strong {
+	font-weight: bold;
+	}
+
+.pun em {
+	font-style: italic;
+	}
+
+
+/* Content Defaults
+----------------------------------------------------------------*/
+
+.pun p, .pun ul, .pun ol, .pun dl {
+	font-size: 1em;
+	padding: 3px 0;
+	}
+
+.pun h2 {
+	font-size: 1em;
+	font-weight: normal;
+	padding: 4px 7px;
+	}
+
+.pun h3 {
+	font-size: 1.091em;
+	padding: 3px 0;
+	}
+
+.pun table p, .pun table h3 {
+	padding: 0;
+	}
+
+.pun span.warntext, .pun p.warntext {
+	font-weight: bold
+	}
+
+/* User Content (Announcements, Rules, Posts)
+----------------------------------------------------------------*/
+
+.pun .usercontent p, .pun .postmsg p {
+	padding: 0.75em 0
+	}
+
+.pun .usercontent ul, .pun .postmsg ul {
+	padding: 0.75em 1em 0.75em 2.5em;
+	list-style: disc
+	}
+
+.pun .usercontent ol, .pun .postmsg ol {
+	padding: 0.75em 1em 0.75em 2.5em;
+	list-style: decimal
+	}
+
+.pun .usercontent ol.alpha, .pun .postmsg ol.alpha {
+	list-style: lower-alpha
+	}
+
+.pun .usercontent li ol, .pun .usercontent li ul, .pun .postmsg li ol, .pun .postmsg li ul {
+	padding: 0.25em 1em 0.75em 2.5em
+	}
+
+.pun .usercontent li p, .pun .postmsg li p {
+	padding: 0
+	}
+
+.pun .usercontent h1 {
+	font-size: 1.4em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h2 {
+	font-size: 1.2em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h3 {
+	font-size: 1.1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h4, .pun .usercontent h5, .pun .usercontent h6 {
+	font-size: 1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .quotebox cite {
+	font-weight: bold;
+	font-style: normal;
+	padding: 0.75em 0.75em 0 0.75em
+	}
+
+.pun span.bbu {
+	text-decoration: underline
+	}
+
+.pun span.bbs, .pun del {
+	text-decoration: line-through;
+	}
+
+.pun .postmsg ins, #punhelp samp ins {
+	text-decoration: none;
+	}
+
+.pun div.postmsg h5, #punhelp h5 {
+	font-size: 1.1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0;
+	}
+
+
+/*****************************************************************
+3. COMMON STYLES
+*****************************************************************/
+
+/* Page Layout
+----------------------------------------------------------------*/
+
+.pun {
+	margin: 12px 20px
+	}
+
+#punredirect, #punmaint, #puninstall, #pundb_update {
+	margin: 50px 20% 12px 20%
+	}
+
+
+/* Vertical Element Spacing
+----------------------------------------------------------------*/
+
+#brdheader {
+	margin: 0 0 12px 0;
+	}
+
+#announce, #brdstats {
+	margin: 12px 0 12px 0;
+	}
+
+.pun .blocktable, .pun .block, .pun .blockform, .pun .block2col, #postreview {
+	margin-bottom: 12px
+	}
+
+#punindex .blocktable, .pun .blockpost {
+	margin-bottom: 6px
+	}
+
+#postreview .blockpost {
+	margin-bottom: -1px;
+	}
+
+.pun .block2col .blockform, .pun .block2col .block {
+	margin-bottom: 0px
+	}
+
+.pun .linkst, .pun .linksb {
+	margin-top: -12px
+	}
+
+.pun .postlinksb {
+	margin-top: -6px
+	}
+
+
+/* External Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+	border-style: solid;
+	border-width: 1px;
+	}
+
+#brdheader .box {
+	border-top-width: 4px;
+	}
+
+/* Default Internal Spacing
+----------------------------------------------------------------*/
+
+.pun .block .inbox, .pun .blockmenu .inbox {
+	padding: 3px 6px
+	}
+
+/*****************************************************************
+4. COMMON BOARD ELEMENTS
+*****************************************************************/
+
+/* Board Header
+----------------------------------------------------------------*/
+
+#brdtitle h1 {
+	font-size: 1.454em;
+	font-weight: bold;
+	line-height: 1em;
+	padding: 3px 0 0 0;
+	}
+
+#brddesc {
+	padding: 3px 0;
+	}
+
+#brddesc * {
+	padding-top: 0;
+	padding-bottom: 0;
+	}
+
+#brdmenu li {
+	display: inline;
+	margin-right: 12px;
+	}
+
+#brdmenu a:link, #brdmenu a:visited {
+	text-decoration: none
+	}
+
+#brdmenu a:hover, #brdmenu a:active {
+	text-decoration: underline
+	}
+
+#brdwelcome .conl {
+	float: left;
+	}
+
+#brdwelcome .conr {
+	float: right;
+	text-align: right;
+	}
+
+/* Breadcrumbs and Post Links
+----------------------------------------------------------------*/
+
+.pun .linkst {
+	padding: 8px 6px 3px 6px
+	}
+
+.pun .linksb, .pun .postlinksb {
+	padding: 3px 6px 8px 6px
+	}
+
+.pun .crumbs {
+	clear: both;
+	width: 100%;
+	overflow: hidden;
+	}
+
+.pun .crumbs li {
+	display: inline;
+	white-space: nowrap;
+	font-weight: bold;
+	}
+
+.pun .pagelink {
+	float: left;
+	white-space: nowrap;
+	}
+
+.pun .postlink {
+	font-weight: bold;
+	white-space: nowrap;
+	}
+
+.pun .postlink, .pun .modbuttons {
+	float: right;
+	text-align: right;
+	}
+
+.pun .modbuttons {
+	padding: 1px 0;
+	white-space: nowrap;
+	}
+
+.pun .modbuttons input {
+	margin-left: 6px;
+	}
+
+.pun .postlink a:link, .pun .postlink a:visited {
+	text-decoration: none
+	}
+
+.pun .postlink a:hover, .pun .postlink a:active {
+	text-decoration: underline;
+	}
+
+
+/* Board Footer
+----------------------------------------------------------------*/
+
+#brdfooter .conl {
+	float: left;
+	}
+
+#brdfooter .conr {
+	float: right;
+	text-align: right;
+	}
+
+#brdfooter #modcontrols {
+	border-bottom-style: solid;
+	border-bottom-width: 1px;
+	text-align: center;
+	}
+
+#brdfooter #modcontrols dd {
+	display: inline;
+	margin:0 6px;
+	}
+
+
+/* Board Stats
+----------------------------------------------------------------*/
+
+#brdstats .conl {
+	float: left;
+	}
+
+#brdstats .conr {
+	float: right;
+	text-align: right;
+	}
+
+#onlinelist dd, #onlinelist dt {
+	display: inline;
+	}
+
+
+/*****************************************************************
+5. MAIN TABLES
+*****************************************************************/
+
+.pun table {
+	width: 100%;
+	border-collapse: collapse;
+	border-spacing: 0;
+	empty-cells: show;
+	}
+
+.pun .blocktable table {
+	table-layout: fixed;
+	}
+
+.pun td, .pun th {
+	padding: 4px 6px;
+	line-height: 1.273em;
+	text-align: left;
+	font-weight: normal;
+	}
+
+.pun td {
+	border-style: solid none none solid;
+	border-width: 1px;
+	}
+
+.pun .tcl {
+	border-left: 0;
+	width: auto;
+	}
+
+.pun .tc2, .pun .tc3, .pun .tcmod {
+	width: 10%;
+	text-align: center;
+	padding: 4px 0;
+	}
+
+.pun .tcr {
+	width: 30%;
+	}
+
+.pun .tcl h3 {
+	font-size: 1.091em;
+	font-weight: bold;
+	}
+
+.pun .tcl h3 span.newtext {
+	font-size: 0.917em;
+	}
+
+.pun .tcl span.newtext, .pun .tcl span.pagestext {
+	white-space: nowrap;
+	font-weight: normal;
+	}
+
+.pun td span.byuser {
+	white-space: nowrap;
+	}
+
+.pun .tcl p {
+	padding: 5px 0 0 0
+	}
+
+#punsearch #vf .tc2 {
+	width: 18%;
+	text-align: left;
+	padding: 4px 6px;
+	}
+
+#users1 .tcr {
+	width: 25%
+	}
+
+#users1 .tc2 {
+	width: 25%;
+	text-align: left;
+	padding: 4px 6px;
+	}
+
+#debug .tcl {
+	width: 10%
+	}
+
+#debug .tcr {
+	width: 90%;
+	white-space: normal
+	}
+
+#punindex .tcr .byuser {
+	display: block
+	}
+
+.pun .blocktable .tclcon {
+	padding: 0 11px 0 12px;
+	overflow: hidden;
+	height: 1%;
+	min-height: 1px;
+	position: relative;
+	}
+
+.pun .blocktable .tclcon div {
+	width: 100%;
+	overflow: hidden;
+	}
+
+.pun .icon {
+	border-width: 7px;
+	border-style: solid;
+	height: 0;
+	width: 0;
+	overflow: hidden;
+	float: left;
+	}
+
+.pun .icon div {
+	position: absolute;
+	left: -9999em;
+	text-indent: -9999em;
+	height: 0;
+	}
+
+.pun .iposted .ipost {
+	position: absolute;
+	left: 0;
+	font-weight: bold;
+	width: 8px;
+	padding-left: 4px;
+	text-align: center;
+	top: 0;
+	}
+
+/*****************************************************************
+6. MAIN FORMS
+*****************************************************************/
+
+.pun .blockform form, .pun .fakeform {
+	PADDING: 20px 20px 15px 20px
+	}
+
+.pun .forminfo {
+	margin-bottom: 12px;
+	padding: 9px 10px;
+	border-style: solid;
+	border-width: 1px;
+	}
+
+.pun .forminfo h3 {
+	font-weight: bold;
+	}
+
+.pun .inform {
+	padding-bottom: 12px
+	}
+
+.pun fieldset {
+	padding: 0px 12px 0px 12px;
+	border-style: solid;
+	border-width: 1px
+	}
+
+.pun legend {
+	padding: 0px 6px
+	}
+
+.pun .infldset {
+	padding: 9px 0px 12px 0
+	}
+
+.pun label {
+	display: block;
+	padding: 3px 0
+	}
+
+.pun label.conl {
+	float: left;
+	overflow: visible;
+	margin-right: 10px
+	}
+
+.pun select {
+	padding-top: 1px;
+	padding-bottom: 1px;
+	}
+
+.pun fieldset .rbox {
+	}
+
+.pun fieldset .rbox br {
+	display: none;
+	}
+
+.pun fieldset .rbox label {
+	padding: 3px 0 3px 25px;
+	position: relative;
+	vertical-align: middle;
+	}
+
+.pun fieldset .rbox input {
+	margin: 0 9px 0 -25px;
+	padding: 0;
+	width: 16px;
+	position: relative;
+	vertical-align: middle;
+	}
+
+.pun .txtarea {
+	width: 75%
+	}
+
+.pun .txtarea textarea, .pun input.longinput {
+	width: 100%
+	}
+
+.pun .bblinks {
+	padding-bottom: 10px;
+	padding-left: 4px
+	}
+
+.pun .bblinks li {
+	display: inline;
+	padding-right: 20px
+	}
+
+.pun .blockform .buttons {
+	padding-left: 12px;
+	}
+
+.pun .blockform .buttons input {
+	margin-right: 8px;
+	}
+
+#posterror ul {
+	list-style: square;
+	padding: 3px 0 3px 24px;
+	}
+
+.pun .deletemsg {
+	border-style: solid;
+	border-width: 1px;
+	padding: 6px 15px;
+	}
+
+.pun p.actions span {
+	margin-right: 12px;
+	}
+
+/*****************************************************************
+7. PROFILES AND ADMIN
+*****************************************************************/
+
+.pun .block2col {
+	padding-bottom: 1px
+	}
+
+.pun .block2col .blockform, .pun .block2col .block {
+	margin-left: 17em
+	}
+
+.pun .blockmenu {
+	float:left;
+	width: 16em
+	}
+
+.pun .blockmenu li {
+	padding: 3px 0;
+	font-weight: bold;
+	}
+
+.pun .blockmenu a:link, .pun .blockmenu a:visited {
+	text-decoration: none
+	}
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active {
+	text-decoration: underline
+	}
+
+#viewprofile dl {
+	float: left;
+	width: 100%;
+	overflow: hidden
+	}
+
+#viewprofile dd {
+	margin-left: 14em;
+	padding: 3px;
+	}
+
+#viewprofile dt {
+	float: left;
+	width: 13em;
+	margin: 3px 0;
+	}
+
+#profileavatar img {
+	float: right;
+	margin-left: 1em
+	}
+
+/*****************************************************************
+8. MAIN POSTS
+*****************************************************************/
+
+.pun .blockpost h2 a:link, .pun .blockpost h2 a:visited {
+	text-decoration: none;
+	}
+
+.pun .blockpost h2 a:hover, .pun .blockpost h2 a:active {
+	text-decoration: underline;
+	}
+
+.pun .blockpost h2 .conr {
+	float: right;
+	text-align: right;
+	}
+
+#punsearch .blockpost h2 span {
+	white-space: nowrap;
+	}
+
+.pun .blockpost .box {
+	overflow: hidden;
+	}
+
+.pun .postleft, .pun .postfootleft {
+	float:left;
+	width: 18em;
+	position: relative;
+	overflow: hidden;
+	}
+
+.pun .postleft dl {
+	padding: 0.75em 6px;
+	}
+
+.pun .postleft .usercontacts, .pun .postleft .icon {
+	margin-top: 6px
+	}
+
+.pun .postleft .postavatar, .pun .postleft .usertitle {
+	margin-bottom: 6px;
+	display: block;
+	}
+
+.pun .blockpost dt {
+	font-size: 1.091em;
+	font-weight: bold;
+	}
+
+.pun .blockpost dt a:link, .pun .blockpost dt a:visited {
+	text-decoration: none;
+	}
+
+.pun .blockpost dt a:hover, .pun .blockpost dt a:active {
+	text-decoration: underline;
+	}
+
+.pun .postright, .pun .postfootright {
+	border-left-width: 18em;
+	border-left-style: solid
+	}
+
+#postpreview .postright {
+	border-left: 0
+	}
+
+.pun .postright {
+	padding: 0 6px;
+	}
+
+.pun .postfootright, .pun .multidelete {
+	text-align: right
+	}
+
+.pun .postmsg {
+	width:98%;
+	overflow: hidden;
+	padding-bottom: 6px;
+	}
+
+.pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
+.pun .postfootleft p {
+	padding: 6px 6px 6px 6px;
+	}
+
+.pun .postfootright li {
+	display: inline;
+	margin-left: 12px;
+	}
+
+.pun .postfootright a:link, .pun .postfootright a:visited {
+	text-decoration: none
+	}
+
+.pun .postfootright a:hover, .pun .postfootright a:active {
+	text-decoration: underline
+	}
+
+.pun .codebox {
+	border-style: solid;
+	border-width: 1px;
+	margin: 0.75em 1em;
+	padding: 0;
+	}
+
+.pun .quotebox {
+	border-style: solid;
+	border-width: 1px;
+	margin: 0.75em 1em;
+	padding: 0 0.75em;
+	}
+
+.pun .quotebox cite {
+	display: block;
+	padding: 0.75em 0 0 0;
+	}
+
+.pun .quotebox blockquote {
+	width: 100%;
+	overflow: hidden
+	}
+
+.pun .codebox pre {
+	overflow: auto;
+	width: 100%;
+	overflow-y:hidden
+	}
+
+* html .pun .codebox pre {
+	padding-bottom: 10px;
+	}
+
+*+html .pun .codebox pre {
+	padding-bottom: 10px
+	}
+
+.pun .codebox pre code {
+	display: block;
+	padding: 0.75em;
+	}
+
+.pun .codebox pre.vscroll {
+	height: 32em;
+	overflow: auto;
+	overflow-y: auto
+	}
+
+.pun .postmsg img {
+	vertical-align: bottom;
+	}
+
+.pun .postsignature hr {
+	margin-left: 0px;
+	width: 200px;
+	text-align: left;
+	height: 1px;
+	border:none
+	}
+
+.pun .postmsg .postimg img {
+	max-width: 98%;
+	vertical-align: middle;
+}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+	border-style: solid;
+	border-width: 2px;
+}
+
+.pun .blockpost label {
+	padding: 3px 6px;
+	border-style: solid;
+	border-width: 1px;
+	vertical-align: middle;
+	display: inline-block;
+	}
+
+.pun .blockpost label * {
+	vertical-align: middle;
+	margin: 0;
+	padding: 0;
+	}
+
+/****************************************************************/
+/* 9. HELP FILES AND MISC. */
+/****************************************************************/
+
+#punhelp h2 {
+	margin-top: 12px
+	}
+
+#punhelp div.box {
+	padding: 10px
+	}
+
+#debugtime {
+	margin-top: -12px;
+	text-align: center;
+	}
+
+/*****************************************************************
+COLOUR SCHEME
+*****************************************************************/
+
+/* Background / Text
+----------------------------------------------------------------*/
+
+body {
+	background: #2a2a2a;
+	color: #d4d4d4
+	}
+
+.pun {
+	color: #d4d4d4
+	}
+
+.pun .box, #adminconsole fieldset th {
+	background-color: #383838
+	}
+
+.pun td.tc2, .pun td.tc3, .pun td.tcmod, #postpreview, #viewprofile dd, .pun .forminfo,
+#brdfooter #modcontrols, #adminconsole fieldset td, .pun .blockmenu .box, #adstats dd {
+	background-color: #424242
+	}
+
+.pun h2, #brdmenu {
+	background-color: #565656;
+	color: #d4d4d4
+	}
+
+.pun th {
+	background-color: #484848
+	}
+
+.pun legend {
+	color: #60a0dc
+	}
+
+.pun .blockmenu li.isactive a, #posterror li strong {
+	color: #d4d4d4
+	}
+
+.pun .usercontent * {
+	background: transparent;
+	color: #d4d4d4
+	}
+
+.pun textarea, .pun input, .pun select {
+	background-color: #2a2a2a;
+	color: #d4d4d4
+	}
+
+/* Posts
+----------------------------------------------------------------*/
+
+.pun .blockpost .box, .pun .postright, .pun .postfootright, .pun .deletemsg {
+	background-color: #383838
+	}
+
+.pun .postright, .pun .postfootright {
+	border-left-color: #424242
+	}
+
+.pun .postleft, .pun .postfootleft, .pun .blockpost label, .pun .codebox, .pun .quotebox {
+	background-color: #424242
+	}
+
+.pun .blockpost h2 {
+	background-color: #565656
+	}
+
+.pun .blockpost h2 span.conr {
+	color: #a19e96
+	}
+
+.pun .postmsg ins, #punhelp samp ins {
+	background-color: #ff0;
+	}
+
+.pun hr {
+	background-color: #606060;
+	color: #606060
+	}
+
+/* Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+	border-color:#565656
+	}
+
+.pun td, #brdfooter #modcontrols {
+	border-color: #565656
+	}
+
+.pun th, .pun fieldset {
+	border-color: #484848
+	}
+
+#adminconsole td, #adminconsole th {
+	border-color: #383838
+	}
+
+.pun .quotebox, .pun .codebox, .pun .forminfo,
+.pun .blockpost label, .pun .deletemsg {
+	border-color: #606060
+	}
+
+/* Links
+----------------------------------------------------------------*/
+
+.pun a:link, .pun a:visited {
+	color: #60a0dc
+	}
+
+.pun a:hover, .pun a:active, .pun a:focus {
+	color: #80d6ff
+	}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+	border-color: #60a0dc;
+}
+
+.pun .postmsg .postimg a:hover img, .pun .postmsg .postimg a:active img, .pun .postmsg .postimg a:focus img {
+	border-color: #80d6ff;
+}
+
+.pun h2 a:link, .pun h2 a:visited,
+#brdmenu a:link, #brdmenu a:visited {
+	color: #d4d4d4
+	}
+
+.pun h2 a:hover, .pun h2 a:active,
+#brdmenu a:hover, #brdmenu a:active {
+	color: #d4d4d4
+	}
+
+.pun .postreport a:link, .pun .postreport a:visited,
+.pun .iclosed td.tcl a:link, .pun .iclosed td.tcl a:visited {
+	color: #888
+	}
+
+.pun .postreport a:hover, .pun .postreport a:active,
+.pun .iclosed td.tcl a:hover, .pun .iclosed td.tcl a:active {
+	color: #aaa
+	}
+
+.pun .maintenancelink a:link, .pun .maintenancelink a:visited {
+	color: #ff4000
+	}
+
+.pun .maintenancelink a:hover, .pun .maintenancelink a:active {
+	color: #ff5010
+	}
+
+/* Status Indicators
+----------------------------------------------------------------*/
+
+.pun .icon {
+	border-color: #484848 #404040 #3c3c3c #444444
+	}
+
+.pun .iredirect .icon {
+	border-color: #383838 #383838 #383838 #383838
+	}
+
+.pun .inew .icon {
+	border-color: #5496d8 #4b85c0 #4377ac #4f8dcb
+	}
diff --git a/style/Earth.css b/style/Earth.css
new file mode 100644
index 0000000..23f60df
--- /dev/null
+++ b/style/Earth.css
@@ -0,0 +1,1606 @@
+/*****************************************************************
+1. INITIAL SETTINGS
+*****************************************************************/
+
+/* Limited Reset
+----------------------------------------------------------------*/
+
+html, body, .pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3, .pun h4, .pun h5, .pun pre, .pun blockquote,
+.pun ul, .pun ol, .pun li, .pun dl, .pun dt, .pun dd, .pun th, .pun td, .pun fieldset, .pun legend .pun img,
+.pun abbr, .pun cite {
+	border: 0;
+	font-style: normal;
+	font-weight: normal;
+	margin: 0;
+	padding: 0;
+}
+
+.pun ul, .pun ol {
+	list-style: none;
+}
+
+.pun select {
+	padding-bottom: 1px;
+	padding-top: 1px;
+}
+
+/* Content Defaults
+----------------------------------------------------------------*/
+
+.pun {
+	font: 81.25%/1.462em Arial, Helvetica, sans-serif;
+}
+
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea {
+	font-family: Arial, Helvetica, sans-serif;
+	font-size: 1em;
+}
+
+.pun pre, .pun code {
+	font-family: consolas, monaco, "bitstream vera sans mono", "courier new", courier, monospace;
+	font-size: 1em;
+}
+
+.pun pre code {
+	font-size: 1em;
+}
+
+.pun table {
+	border-collapse: collapse;
+	border-spacing: 0;
+	border: 0;
+	empty-cells: show;
+	width: 100%;
+}
+
+.pun h1 {
+	font:2.154em/1em "Trebuchet MS", Arial, Helvetica, sans-serif;
+	padding: 7px 0;
+}
+
+.pun h2, .pun .hd h2 {
+	font: 1.462em/1em "Trebuchet MS", Arial, Helvetica, sans-serif;
+	padding: 7px 0;
+}
+
+.pun h3 {
+	font-size: 1.154em;
+	line-height: 1.267em;
+	padding: 7px 0;
+}
+
+.pun h4 {
+	font-size: 1.077em;
+	font-weight: bold;
+	padding: 7px 0;
+}
+
+.pun h5, .pun h6 {
+	font-size: 1em;
+	font-weight: bold;
+	padding: 7px 0;
+}
+
+.pun p, .pun ul, .pun ol, .pun dl, .pun th, .pun td, .pun legend {
+	padding: 7px 0;
+}
+
+.pun strong, .pun th, .pun span.warntext, .pun p.warntext {
+	font-weight: bold;
+}
+
+.pun em {
+	font-style: italic;
+}
+
+.pun a, .pun a:link, .pun a:visited {
+	text-decoration: none;
+}
+
+.pun a:hover, .pun a:active, .pun a:focus {
+	text-decoration: underline;
+}
+
+.pun .actions span {
+	padding-left: 16px;
+	padding-right: 8px;
+	background: url(Earth/img/bull.png) center left no-repeat;
+	display: inline-block;
+	line-height: normal;
+}
+
+/* Hidden Elements
+----------------------------------------------------------------*/
+
+#brdfooter h2, #brdstats h2, #debug h2, #brdstats .conl dt, #brdstats .conr dt, #modcontrols dt,
+#searchlinks dt, div.postright h3, .pun .subscribelink span, #announce .hd, #reportform h2, #punmoderate #vf h2,
+#punviewforum #vf h2, .pun .required strong span, .pun .icon div {
+	display: block;
+	overflow: hidden;
+	position: absolute;
+	text-indent: -9999em;
+	width: 0;
+}
+
+/* Generic Float Clear
+----------------------------------------------------------------*/
+
+.pun .inbox, .pun #brdmain, .pun .crumbs, .pun .pagepost, .pun .block2col {
+	min-height: 1px;
+}
+
+* html .pun .inbox, * html .pun #brdmain, * html .pun .infldset, * html .pun .crumbs, * html .pun .pagepost, * html .pun .block2col {
+	display: inline-block;
+}
+
+* html .pun .inbox, * html .pun #bdrdmain, * html .pun .infldset, * html .pun .crumbs, * html .pun .pagepost, * html .pun .block2col {
+	display: block;
+}
+
+.pun .inbox:after, .pun #brdmain:after, .pun .crumbs:after, .pun .pagepost:after, .pun .block2col:after {
+	content: " ";
+	display: block;
+	height: 0;
+	font-size: 0;
+	clear: both;
+	visibility: hidden;
+}
+
+.pun .block2col .inbox:after {
+	content: none;
+	clear: none;
+}
+
+/*****************************************************************
+2. COMMON STYLES
+*****************************************************************/
+
+/* Page Layout
+----------------------------------------------------------------*/
+
+.pun {
+	padding: 30px 40px;
+}
+
+#punredirect, #punmaint {
+	padding: 60px 20% 12px 20%;
+}
+
+#puninstall, #pundb_update {
+	padding: 20px 10%;
+}
+
+.pun .punwrap {
+	border: 1px solid;
+	padding: 18px;
+}
+
+.pun .top-box {
+	height: 18px;
+	margin-bottom: -18px;
+	position: relative;
+	background: url(Earth/img/main_top.png) top left no-repeat;
+}
+
+.pun .top-box div {
+	height: 18px;
+	margin-left: 50%;
+	background: url(Earth/img/main_top.png) top right no-repeat;
+}
+
+.pun .end-box {
+	margin-top: -17px;
+	height: 18px;
+	position: relative;
+	background: url(Earth/img/main_end.png) bottom left no-repeat;
+}
+
+.pun .end-box div {
+	height: 18px;
+	margin-left: 50%;
+	background: url(Earth/img/main_end.png) bottom right no-repeat;
+}
+
+#punredirect h2, #punmaint h2 {
+	border-bottom-style: dotted;
+	border-bottom-width: 1px;
+	margin-bottom: 3px;
+}
+
+/* Section Spacing and Borders
+----------------------------------------------------------------*/
+
+#brdmain {
+	border-style: solid none;
+	border-width: 2px 0;
+	margin-bottom: 12px;
+	padding: 12px 0;
+}
+
+#punindex #brdmain {
+	padding-top: 24px;
+}
+
+#punredirect #brdmain, #punmaint #brdmain {
+	border: 0;
+	margin: 0;
+	padding: 0;
+}
+
+#brdstats {
+	border-style: solid none none none;
+	border-width: 2px 0 0 0;
+	margin-top: 24px;
+	padding-top: 12px;
+}
+
+#quickpost {
+	border-style: solid none none none;
+	border-width: 2px 0 0 0;
+	margin-top: 12px;
+	padding-top: 12px;
+}
+
+#announce {
+	border-style: solid none none none;
+	border-width: 2px 0 0 0;
+	padding-top: 3px;
+}
+
+/*****************************************************************
+3. COMMON BOARD ELEMENTS
+*****************************************************************/
+
+/* Logo, Description and Main Menu
+----------------------------------------------------------------*/
+
+#brdtitle h1 {
+	padding: 0 0 10px 0;
+}
+
+#brddesc {
+	border-top-style: dotted;
+	border-top-width: 1px;
+	padding: 10px 0;
+}
+
+#brddesc p {
+	padding: 0;
+}
+
+#brdmenu ul {
+	padding: 0;
+}
+
+#brdmenu li {
+	float: left;
+}
+
+#brdmenu a:link, #brdmenu a:visited {
+	border-right-style: solid;
+	border-width: 1px;
+	display: block;
+	min-width: 60px;
+	padding: 12px 16px 6px 8px;
+	white-space: nowrap;
+}
+
+#brdmenu a:hover, #brmenu a:active, #brdmenu a:focus {
+	text-decoration: none;
+}
+
+/* Welcome Box
+----------------------------------------------------------------*/
+
+#brdwelcome {
+	padding: 10px 0;
+}
+
+#brdwelcome .conl, #brdwelcome .conr, #brdwelcome p, #brdwelcome li {
+	display: inline;
+	padding: 0;
+}
+
+#brdwelcome li span {
+	background: url(Earth/img/bull.png) center left no-repeat;
+	padding-left: 18px;
+	margin-right: 3px;
+	display: inline-block;
+	line-height: normal;
+	white-space: nowrap;
+}
+
+#brdwelcome .conl li:first-child span {
+	padding-left: 0;
+	background: none;
+}
+
+/* Stats
+----------------------------------------------------------------*/
+
+#brdstats .conl {
+	float: left;
+}
+
+#brdstats .conr {
+	float: right;
+	text-align: right;
+}
+
+#brdstats #onlinelist {
+	border-top-style: dotted;
+	border-top-width: 1px;
+	clear: both;
+}
+
+#brdstats #onlinelist dt, #brdstats #onlinelist dd {
+	display: inline;
+}
+
+/* Footer
+----------------------------------------------------------------*/
+
+.pun #modcontrols {
+	border-style: none none dotted none;
+	border-width: 0 0 1px 0;
+	margin-bottom: 4px;
+	text-align: center;
+	width: 100%;
+}
+
+.pun #modcontrols dd {
+	display: inline;
+}
+
+.pun #brdfooter #modcontrols dd span {
+	background: url(Earth/img/bull.png) center left no-repeat;
+	display: inline-block;
+	line-height: normal;
+	padding-left: 18px;
+	white-space: nowrap;
+}
+
+.pun #brdfooter .conl {
+	float: left;
+}
+
+.pun #brdfooter .conr {
+	text-align: right;
+	float: right;
+}
+
+.pun #brdfooter #poweredby a {
+	font-size: 1.077em;
+	font-weight: bold;
+}
+
+.pun #brdfooter #qjump {
+	padding-top: 5px;
+}
+
+.pun #brdfooter #qjump * {
+	white-space: nowrap;
+}
+
+.pun #brdfooter #searchlinks dd span {
+	background: url(Earth/img/bull.png) center left no-repeat;
+	display: inline-block;
+	line-height: normal;
+	padding-left: 18px;
+	white-space: nowrap;
+}
+
+.pun #brdfooter #feedlinks {
+	padding-bottom: 0;
+}
+
+.pun #brdfooter #feedlinks span {
+	background: url(Earth/img/feed.png) center left no-repeat;
+	display: inline-block;
+	padding-left: 18px;
+	white-space: nowrap;
+}
+
+.pun #debugtime {
+	border-style: dotted none none none;
+	border-width: 1px 0 0 0;
+	margin-top: 7px;
+	text-align: center;
+}
+
+/* Breadcrumbs, Postlink, Pagination
+----------------------------------------------------------------*/
+
+.pun .linkst .inbox, .pun linksb .inbox, .pun .postlinksb .inbox {
+	overflow: hidden;
+}
+
+.pun .linksb, .pun .postlinksb, .pun .linkst, .pun .crumbs {
+	clear: both;
+	position: relative;
+}
+
+.pun .linkst .crumbs {
+	font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+	font-size: 1.462em;
+	line-height: 1.211em;
+	padding: 7px 0;
+}
+
+.pun .linksb .crumbs, .pun .postlinksb .crumbs {
+	font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+	font-size: 1.154em;
+}
+
+.pun .linkst .crumbsplus .pagepost {
+	border-top-style: dotted;
+	border-top-width: 1px;
+}
+
+.pun .linksb .crumbsplus .pagepost, .pun .postlinksb .crumbsplus .pagepost {
+	border-bottom-style: dotted;
+	border-bottom-width: 1px;
+}
+
+.pun .postlinksb .crumbs {
+	margin-right: 11em;
+}
+
+.pun .crumbs li {
+	float: left;
+	padding-right: 0.4em;
+	white-space: nowrap;
+}
+
+.pun .crumbs li strong {
+	font-weight: normal;
+}
+
+.pun .pagelink {
+	float: left;
+	white-space: nowrap;
+}
+
+.pun .pagelink strong, .pun .pagelink a, .pun .pagelink span.spacer {
+	border-style: none none none solid;
+	border-width: 0 0 0 1px;
+	display: inline-block;
+	padding: 0 12px 0 10px;
+	margin-right: -6px;
+}
+
+.pun .pagelink .item1 {
+	border: 0;
+}
+
+.pun .pagelink .pages-label {
+	display: inline-block;
+}
+
+.pun .postlink {
+	float: right;
+	font-weight: bold;
+	text-align: right;
+}
+
+.pun .modbuttons {
+	float: right;
+	padding: 5px 0 3px 0;
+}
+
+.pun .modbuttons input {
+	margin-left: 8px;
+}
+
+.pun .subscribelink {
+	position: absolute;
+	right: 0;
+	text-align: right;
+	top: 33px;
+}
+
+/*****************************************************************
+4. MAIN TABLES
+*****************************************************************/
+
+.pun #brdmain .blocktable {
+	position: relative;
+}
+
+#punindex #brdmain .blocktable h2, #punsearch #vf h2 {
+	font: 1em/1.462em Arial, Helvetica, sans-serif;
+	font-weight: bold;
+	margin: 1px 1px 0 1px;
+	padding-left: 8px;
+	position: absolute;
+	left: 0;
+	white-space: nowrap;
+	z-index: 100;
+}
+
+#punindex .blocktable th.tcl, #punsearch #vf th.tcl {
+	font-size: 0;
+	text-indent: -9999em;
+}
+
+.pun .blocktable .box {
+	border-style: solid;
+	border-width: 1px;
+	margin-bottom: -1px;
+	overflow: hidden;
+	position: relative;
+}
+
+* html .pun .blocktable .box {
+	display: inline-block;
+}
+
+.pun .blocktable table {
+	table-layout: fixed;
+	margin-bottom: -1px;
+}
+
+.pun .blocktable th {
+	padding: 7px 8px;
+	border-style: none none solid none;
+	border-width: 1px;
+	text-align: left;
+}
+
+.pun .blocktable td {
+	padding: 7px 8px;
+	line-height: 1.3077em;
+	border-style: none none solid none;
+	border-width: 1px;
+	text-align: left;
+}
+
+.pun .blocktable h3 {
+	font-size: 1.077em;
+	font-weight: bold;
+	padding: 0;
+}
+
+.pun .blocktable p {
+	padding: 0;
+}
+
+.pun .blocktable .tcl p {
+	padding: 5px 0 0 0;
+}
+
+.pun .blocktable .tcl {
+	width: auto;
+}
+
+.pun .blocktable .tc2, .pun .blocktable .tc3, .pun .blocktable .tcmod {
+	padding-left: 0;
+	padding-right: 0;
+	text-align: center;
+	width: 11%;
+}
+
+.pun .blocktable .tcr {
+	width: 30%;
+}
+
+.pun .blocktable td .newtext, .pun .blocktable td .pagestext, .pun .blocktable td .byuser {
+	white-space: nowrap;
+}
+
+.pun .blocktable .tcl h3 span.newtext {
+	font-size: 0.929em;
+	font-weight: normal;
+}
+
+.pun #vf td.tcl span.stickytext, .pun #vf td.tcl span.closedtext {
+	font-size: 1em;
+	font-weight: bold;
+}
+
+#punsearch #vf .tc2 {
+	padding-left: 8px;
+	padding-right: 8px;
+	text-align: left;
+	width: 18%;
+}
+
+#users1 .tcr {
+	width: 25%;
+}
+
+#users1 .tc2 {
+	padding-left: 8px;
+	padding-right: 8px;
+	text-align: left;
+	width: 25%;
+}
+
+#debug {
+	margin-top: 12px;
+}
+
+#debug .tcl {
+	width: 10%;
+}
+
+#punredirect #debug .tcl, #punmaint #debug .tcl {
+	width: 20%;
+}
+
+#debug .tcr {
+	width: 90%;
+	white-space: normal
+}
+
+#punindex .tcr .byuser {
+	display: block
+}
+
+#punindex td.tc2, #punindex td.tc3, #punindex td.tcr, .pun #vf td.tc2, .pun #vf td.tc3,
+.pun #vf td.tcr, #punindex td.tcl div.forumdesc, .pun #vf td.tcl span {
+	font-size: 0.923em;
+}
+
+.pun #vf td.tcl a {
+	font-weight: bold;
+}
+
+.pun #vf td.tcl span a {
+	font-weight: normal;
+}
+
+.pun .blocktable .tclcon {
+	height: 1%;
+	min-height: 1px;
+	overflow: hidden;
+	padding: 0 11px 0 12px;
+	position: relative;
+}
+
+.pun .blocktable .tclcon div {
+	width: 100%;
+	overflow: hidden;
+}
+
+.pun .icon {
+	border-style: solid;
+	border-width: 8px;
+	float: left;
+	height: 0;
+	overflow: hidden;
+	width: 0;
+}
+
+.pun .iposted .ipost {
+	font-weight: bold;
+	left: 0;
+	padding-left: 4px;
+	position: absolute;
+	text-align: center;
+	top: 0;
+	width: 8px;
+}
+
+/*****************************************************************
+MAIN POSTS
+*****************************************************************/
+
+/* Structure
+----------------------------------------------------------------*/
+
+.pun .blockpost {
+	border-style: solid;
+	border-width: 1px;
+	margin-bottom: -1px;
+	overflow: hidden;
+	position: relative;
+}
+
+* html .pun .blockpost {
+	display: inline-block;
+}
+
+.pun .blockpost h2 {
+	font: 1em/1.462em Arial, Helvetica, sans-serif;
+	white-space: nowrap;
+	border-bottom-style: solid;
+	border-bottom-width: 1px;
+	height: 1.462em;
+	padding: 0.538em 8px 0.538em 236px;
+	font-weight: normal;
+}
+
+#punsearch .blockpost h2 {
+	height: auto;
+	padding-left: 36px;
+	white-space: normal;
+}
+
+#punsearch .blockpost h2 span span {
+	white-space: nowrap;
+	display: inline-block;
+	font: 1.077em "Trebuchet MS", Arial, Helvetica, sans-serif
+}
+
+#punsearch .blockpost .icon {
+	position: absolute;
+	top: 0;
+	margin-top: -2.154em;
+}
+
+.pun .blockpost h2 .conr {
+	float: right;
+	text-align: right;
+}
+
+.pun .blockpost .inbox {
+	float: right;
+	position: relative;
+	width: 100%;
+}
+
+.pun .blockpost .postbody, .pun .blockpost .postfoot {
+	border-left-style: solid;
+	border-left-width: 1px;
+	float: right;
+	margin-right: -218px;
+	position: relative;
+	text-align: left;
+	width: 100%;
+}
+
+.pun .blockpost .postleft, .pun .blockpost .postfootleft {
+	width: 206px;
+	padding: 7px 0 7px 12px;
+	float: left;
+	margin-left: -218px;
+	position: relative;
+}
+
+.pun .blockpost .postleft dl {
+	padding: 0;
+}
+
+#punviewtopic .blockpost dt, #punmoderate .blockpost dt {
+	display: block;
+	position: absolute;
+	padding: 0.538em 0 0.538em 12px;
+	height: 1.462em;
+	top: -2.615em;
+	left: 0;
+	overflow: hidden;
+	width: 206px;
+}
+
+.pun .blockpost dt strong {
+	font-size: 1.231em;
+	font-weight: bold;
+}
+
+.pun .blockpost .postleft dd {
+	font-size: 0.923em;
+}
+
+.pun .blockpost .postleft .usertitle {
+	padding: 4px 0 6px 0;
+	font-size: 1em;
+}
+
+.pun .blockpost .postleft .postavatar {
+	display: block;
+	margin: 0 0 4px 0;
+}
+
+.pun .blockpost .postright {
+	position: relative;
+	padding: 4px 230px 7px 18px;
+}
+
+.pun .postmsg {
+	width:100%;
+	overflow: hidden;
+}
+
+.pun .blockpost .postfootright {
+	position: relative;
+	padding: 7px 230px 7px 18px;
+	text-align: right;
+}
+
+.pun .postfoot p, .pun .postfoot ul {
+	padding: 0;
+}
+
+.pun .blockpost .postfootright li {
+	display: inline;
+}
+
+.pun .blockpost .postfootright li span {
+	display: inline-block;
+	padding-left: 16px;
+	margin-left: 8px;
+	line-height: normal;
+	background: url(Earth/img/bull.png) center left no-repeat;
+}
+
+.pun .blockpost .usercontacts {
+	padding: 7px 0;
+}
+
+.pun .blockpost .usercontacts .email {
+	background: url(Earth/img/email.png) left 65% no-repeat;
+	margin-right: 5px;
+	padding-left: 21px;
+	display: inline-block;
+	line-height: normal;
+}
+
+.pun .blockpost .usercontacts .website {
+	background: url(Earth/img/ext.png) left 65% no-repeat;
+	padding-left: 18px;
+	display: inline-block;
+	line-height: normal;
+}
+
+.pun .postsignature hr {
+	border:none;
+	height: 1px;
+	margin-left: 0px;
+	text-align: left;
+}
+
+/* Content (includes other user content)
+----------------------------------------------------------------*/
+
+.pun .usercontent {
+	padding: 7px 0;
+}
+
+.pun .postmsg p, .pun .postmsg li, #punhelp p samp {
+	font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+.pun .usercontent h1, .pun .usercontent h2, .pun .usercontent h3,
+.pun .usercontent h4, .pun .usercontent h5, .pun .usercontent h6 {
+	padding: 7px 0 0 0;
+}
+
+.pun .postmsg h5, #punhelp h5 {
+	font-size: 1.231em;
+	font-weight: bold;
+	padding: 7px 0;
+}
+
+.pun .usercontent ul, .pun .postmsg ul {
+	list-style: disc;
+	padding: 4px 13px 4px 30px;
+}
+
+.pun .usercontent ol, .pun .postmsg ol {
+	list-style: decimal;
+	padding: 4px 13px 4px 30px;
+}
+
+.pun .usercontent ol.alpha, .pun .postmsg ol.alpha {
+	list-style: lower-alpha;
+}
+
+.pun .usercontent li, .pun .postmsg li {
+	padding: 0 3px;
+}
+
+.pun .usercontent li p, .pun .postmsg li p {
+	padding: 0;
+}
+
+.pun span.bbu {
+	text-decoration: underline;
+}
+
+.pun span.bbs, .pun del {
+	text-decoration: line-through;
+}
+
+.pun .postmsg ins, #punhelp samp ins {
+	text-decoration: none;
+}
+
+.pun .blockpost .postmsg .postedit {
+	font-size: 0.857em;
+}
+
+.pun .blockform .postsignature, .pun .blockpost .postsignature {
+	font-size: 0.923em;
+}
+
+.pun .codebox {
+	border-style: solid;
+	border-width: 1px;
+	margin: 0.75em 1em;
+	padding: 0;
+}
+
+.pun .quotebox {
+	border-style: solid;
+	border-width: 1px 1px 1px 3px;
+	margin: 0.75em 1em;
+	padding: 0 0.75em;
+}
+
+.pun .quotebox cite {
+	display: block;
+	padding: 0.75em 0 0 0;
+	font-weight: bold;
+	line-height: 1.462em;
+}
+
+.pun .quotebox blockquote {
+	overflow: hidden;
+	width: 100%;
+}
+
+.pun .codebox pre {
+	overflow-y:hidden;
+	overflow: auto;
+	width: 100%;
+	direction: ltr;
+	text-align: left;
+}
+
+* html .pun .codebox pre {
+	padding-bottom: 10px;
+}
+
+*:first-child+html .pun .codebox pre {
+	padding-bottom: 10px;
+}
+
+.pun .codebox pre code {
+	padding: 0.75em;
+	white-space: pre;
+}
+
+.pun div[class*=codebox] pre code {
+	display: inline-block;
+}
+
+* html .pun .codebox pre code {
+	display: block;
+}
+
+.pun .codebox pre.vscroll {
+	height: 32em;
+	overflow: auto;
+	overflow-y: auto
+}
+
+.pun .postmsg img, #punhelp samp img {
+	vertical-align: text-top;
+}
+
+.pun .postmsg .postimg img {
+	max-width: 98%;
+	vertical-align: middle;
+}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+	border-style: solid;
+	border-width: 2px;
+}
+
+/*****************************************************************
+MAIN FORMS
+*****************************************************************/
+
+#punedit .blockform h2, #punpost .blockform h2, #postpreview h2, #posterror h2,
+.pun #quickpost h2, .pun #reportform h2, #pundelete .blockform h2 {
+	font: 1em/1.462em Arial, Helvetica, sans-serif;
+	font-weight: bold;
+	white-space: nowrap;
+	padding: 10px 19px 4px 37px;
+	border: 0;
+}
+
+#punpost .blockform h2, #punedit .blockform h2,.pun #quickpost h2,
+#pundelete .blockform h2 {
+	margin: 1px 1px 0 1px;
+	width: 25em;
+	position: absolute;
+	z-index: 100;
+}
+
+.pun #quickpost legend, #punpost legend, #punedit legend {
+	width: 25em;
+	overflow: hidden;
+	white-space: nowrap;
+}
+
+.pun .blockform .box {
+	border-style: solid;
+	border-width: 1px;
+	padding-bottom: 12px;
+}
+
+.pun #posterror {
+	border-style: solid;
+	border-width: 1px;
+}
+
+.pun #posterror .box {
+	padding: 0 18px 12px 18px;
+}
+
+* html .pun .blockform .box, * html .pun #posterror {
+	display: inline-block;
+}
+
+.pun .blockform .forminfo, .pun .error-info {
+	padding: 12px 18px;
+	border-style: solid;
+	border-width: 1px;
+	position: relative;
+}
+
+.pun .blockform .forminfo {
+	margin-top: 12px;
+}
+
+#pundelete .blockform .forminfo {
+	margin-top: 33px;
+}
+
+.pun .forminfo h3 {
+	padding-bottom: 0;
+}
+
+.pun .error-list li {
+	padding-left: 24px;
+	background: url(Earth/img/exclaim.png) center left no-repeat;
+}
+
+.pun .inform {
+	padding: 0 18px;
+}
+
+.pun legend {
+	font-weight: bold;
+	padding: 10px 19px 4px 19px;
+}
+
+* html .pun legend {
+	margin-left: -7px;
+}
+
+*:first-child+html .pun legend {
+	margin-left: -7px;
+}
+
+.pun .infldset {
+	border-style: solid;
+	border-width: 1px;
+	padding: 12px 18px;
+}
+
+#punregister #rules .infldset {
+	padding: 5px 18px;
+}
+
+.pun fieldset p {
+	padding: 0 0 7px 0;
+	width: 100%;
+}
+
+.pun fieldset .usercontent p {
+	padding: 7px 0;
+}
+
+.pun fieldset label {
+	display: block;
+	padding: 0 0 7px 0;
+}
+
+.pun label em {
+	font-weight: normal;
+	font-style: normal;
+}
+
+.pun .required strong {
+	background: url(Earth/img/asterisk.png) center right no-repeat;
+	font-weight: normal;
+	padding-right: 14px;
+	white-space: pre;
+	display: inline-block;
+	line-height: normal;
+}
+
+.pun label input, .pun label select, .pun label textarea {
+	margin-top: 2px;
+}
+
+.pun label.conl {
+	display: inline-block;
+	padding-right: 12px;
+}
+
+.pun form .buttons {
+	padding: 8px 19px 8px 34px;
+	margin-bottom: -12px;
+}
+
+.pun .blockform .buttons input {
+	margin-right: 12px;
+}
+
+.pun .rbox {
+	padding: 3px 0;
+}
+
+.pun .rbox label {
+	padding: 3px 0 3px 1.75em;
+	position: relative;
+	min-height: 1px;
+}
+
+* html .pun .rbox label {
+	text-indent: -3px;
+	height: 1%;
+}
+
+.pun .rbox input {
+	margin: 3px 0.75em 3px -1.75em;
+	float: left;
+	position: relative;
+	vertical-align: middle;
+	padding: 0;
+	height: 1em;
+	width: 1em;
+}
+
+.pun input[type=text], .pun select, .pun textarea {
+	font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+.pun .txtarea textarea, .pun input.longinput {
+	width: 98%;
+}
+
+.pun #quickpost .txtarea {
+	padding-right: 12px;
+	padding-left: 200px;
+	position: relative;
+}
+
+.pun .blockform .bblinks {
+	padding-top: 0;
+}
+
+.pun .blockform .bblinks li {
+	display: inline;
+}
+
+.pun .blockform .bblinks li span {
+	background: url(Earth/img/help.png) center left no-repeat;
+	margin-right: 8px;
+	padding-left: 20px;
+	display: inline-block;
+}
+
+.pun #quickpost .bblinks {
+	left: 18px;
+	line-height: 1.75em;
+	position:absolute;
+	top: 18px;
+	width: 12em;
+}
+
+.pun #quickpost .bblinks li {
+	display: block;
+}
+
+.pun #login p.clearb {
+	border-top-style: dotted;
+	border-top-width: 1px;
+	font-size: 0;
+	height: 0;
+	line-height: 0;
+	margin-top: 7px;
+	overflow: hidden;
+	padding-bottom: 3px;
+	padding-top: 7px;
+	text-indent: -9999em;
+	width: 100%;
+}
+
+.pun #postreview {
+	padding-top: 12px;
+}
+
+.pun #postpreview, .pun #posterror {
+	margin-bottom: 12px;
+}
+
+.pun #postpreview .postright {
+	padding: 0;
+}
+
+.pun #postpreview .postbody {
+	border-style: solid;
+	border-width: 1px;
+	float: none;
+	margin: 0 18px 12px 18px;
+	padding: 0;
+	padding: 4px 18px 4px 18px;
+	width: auto;
+}
+
+.pun span.email {
+	background: url(Earth/img/email.png) left 65% no-repeat;
+	margin-right: 5px;
+	padding-left: 21px;
+	display: inline-block;
+	line-height: normal;
+}
+
+.pun span.website {
+	background: url(Earth/img/ext.png) left 65% no-repeat;
+	padding-left: 18px;
+	display: inline-block;
+	line-height: normal;
+}
+
+#punmisc #rules .box {
+	border-style: solid;
+	border-width: 1px;
+	padding: 5px 18px;
+}
+
+
+#punhelp .box {
+	border-style: solid;
+	border-width: 1px;
+	padding: 7px 12px;
+}
+
+/*****************************************************************
+PROFILES (+ ADMIN MENU)
+*****************************************************************/
+
+/* Profile / Admin
+----------------------------------------------------------------*/
+
+.pun .blockmenu {
+	width: 13em;
+	float: left;
+	padding-bottom: 12px;
+}
+
+.pun .block2col .blockform, .pun .block2col .block {
+	margin-left: 15em;
+}
+
+.pun .blockmenu .block2 {
+	padding-top: 19px;
+}
+
+.pun .blockmenu ul {
+	border-top-style: dotted;
+	border-top-width: 1px;
+	padding: 0;
+}
+
+.pun .blockmenu li {
+	border-bottom-style: dotted;
+	border-bottom-width: 1px;
+	font-weight: bold;
+	padding: 0;
+}
+
+.pun .blockmenu a:link, .pun .blockmenu a:visited {
+	display: block;
+	padding: 9px 6px 3px 6px;
+	min-height: 1px;
+	text-decoration: none;
+}
+
+* html .pun .blockmenu a:link,  * html .pun .blockmenu a:visited {
+	height: 1%;
+}
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active, .pun .blockmenu a:focus {
+	text-decoration: none;
+}
+
+#viewprofile .box {
+	border-style: solid;
+	border-width: 1px;
+	padding-bottom: 18px;
+}
+
+#viewprofile dt, #adstats dt {
+	padding: 7px 0;
+	position: absolute;
+	width: 13em;
+	left: 0;
+}
+
+#viewprofile dl {
+	border-style: solid none none none;
+	border-width: 1px;
+	margin: 7px 0;
+	padding: 0;
+	width: 100%;
+	position: relative;
+}
+
+#adintro, #adstats {
+	border-style: solid;
+	border-width: 1px;
+	padding: 18px;
+}
+
+#adstats .inbox, #adintro .inbox {
+	border-style: solid;
+	border-width: 1px;
+	padding: 18px;
+}
+
+#adstats dl {
+	margin: 0;
+	padding: 0;
+	width: 100%;
+	position: relative;
+}
+
+#viewprofile dd, #adstats dd {
+	border-style: none none solid none;
+	border-width: 1px;
+	padding: 7px 0 7px 13em;
+}
+
+/*****************************************************************
+COLOUR SCHEME
+*****************************************************************/
+
+/* Basic defaults and Common Items
+----------------------------------------------------------------*/
+
+html, body, .pun {
+	background: #eaede2;
+	color: #333;
+}
+
+.pun .punwrap {
+	background: #fff;
+	border-color: #ccd7c1;
+	color: #526550;
+}
+
+#brdtitle #brddesc, .pun .pagepost, #brdstats #onlinelist, #brdfooter #searchlinks, #brdfooter #modcontrols,
+#punmaint h2, #punredirect h2, #adminconsole .submittop, .pun #debugtime, .pun .pagelink a, .pun .pagelink * {
+	border-color: #bbc6b2;
+}
+
+.pun a, .pun a:link, .pun a:visited {
+	color: #047E00;
+}
+
+.pun a:hover, .pun a:active, .pun a:focus {
+	color: #73A900;
+}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+	border-color: #047E00;
+}
+
+.pun .postmsg .postimg a:hover img, .pun .postmsg .postimg a:active img, .pun .postmsg .postimg a:focus img {
+	border-color: #73A900;
+}
+
+/* Primary Navigation
+----------------------------------------------------------------*/
+
+#brdmenu {
+	background: #32671d;
+}
+
+#brdmenu a, #brdmenu a:link, #brdmenu a:visited {
+	background: #32671d;
+	border-color: #fff;
+	color: #e3e3c8;
+}
+
+#brdmenu a:hover, #brdmenu a:active, #brdmenu a:focus {
+	background: #2ca100;
+	border-color: #fff;
+	color: #fff;
+}
+
+/* Main Tables
+----------------------------------------------------------------*/
+
+.pun .blocktable .box {
+	background: #fcfcf4;
+	border-color: #bbc6b2 #d8dccf;
+}
+
+#punindex .blocktable h2, .pun #vf h2 {
+	color: #83866a;
+}
+
+#adminconsole fieldset th, #adminconsole fieldset td {
+	background: #f6f6ea;
+	border-color: #dce6d8;
+}
+
+.pun #users1 h2 {
+	background: #fff;
+}
+
+.pun .blocktable td {
+	border-color: #dce6d8;
+}
+
+.pun .blocktable th {
+	background: #eaecda;
+	border-color: #ccd7c1;
+	color: #83866a;
+}
+
+.pun .blocktable td.tcl span.stickytext {
+	color: #c08b20;
+}
+
+/* Main Posts
+----------------------------------------------------------------*/
+
+.pun .blockpost {
+	background: #f6f6ea;
+	border-color: #bbc6b2 #d8dccf;
+}
+
+.pun .blockpost h2 {
+	background: #eaecda;
+	border-color: #ccd7c1;
+	color: #83866a;
+}
+
+.pun .blockpost .postbody, .pun .blockpost .postfoot {
+	background: #fcfcf4;
+	border-color: #dce6d8;
+}
+
+.pun .blockpost .postfootright li {
+	color: #fcfcf4;
+}
+
+.pun .postmsg, #punhelp code, #punhelp samp {
+	color: #333;
+}
+
+.pun .postsignature, .pun .postmsg .postedit {
+	color: #526550;
+}
+
+.pun .quotebox {
+	background: #f9fae5;
+	border-color: #bdbc7a;
+	color: #566579;
+}
+
+.pun .quotebox cite {
+	color: #83866a;
+}
+
+.pun .codebox, #punhelp .codebox code {
+	background: #333;
+	color: #fff;
+}
+
+.pun .postmsg hr {
+	background: #bbc6b2;
+}
+
+.pun .postmsg ins, #punhelp samp ins {
+	background-color: #ff0;
+}
+
+/* Main Forms + Profile
+----------------------------------------------------------------*/
+
+.pun .blockform .box, #adstats, #adintro, #postpreview, #posterror {
+	border-color: #bbc6b2 #d8dccf;
+	background: #eaecda;
+}
+
+#punmisc #rules .box, #punhelp .box {
+	border-color: #bbc6b2 #d8dccf;
+	background: #f6f6ea;
+}
+
+.pun #quickpost h2, #punpost .blockform h2, #punedit .blockform h2, #posterror h2,
+#pundelete .blockform h2 {
+	background: #eaecda;
+	color: #83866a;
+}
+
+.pun .forminfo {
+	background: #fff;
+	border-color: #dce6d8;
+}
+
+#puninstall form#install .forminfo {
+	background: #32671d;
+	color: #fff;
+}
+
+.pun #posterror .error-info {
+	background: #ffffe1;
+	border-color: #dfe6ee;
+}
+
+#puninstall form#install .error-info {
+	background: #ffffe1;
+	border-color: #dfe6ee;
+	color: #333;
+}
+
+.pun .infldset, #adintro .inbox, #adstats .inbox {
+	background: #f6f6ea;
+	border-color: #dce6d8;
+}
+
+.pun label, .pun legend, #adminconsole fieldset th {
+	color: #83866a;
+}
+
+.pun fieldset p {
+	border-color: #bbc6b2;
+}
+
+.pun .blockmenu ul, .pun .blockmenu li {
+	border-color: #bbc6b2;
+}
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active, .pun .blockmenu a:focus {
+	background: #ffffe6;
+}
+
+.pun .blockmenu .isactive a:link, .pun .blockmenu .isactive a:visited {
+	color: #333;
+	background: #f6f6ea;
+	}
+
+.pun #viewprofile .box {
+	border-color: #bbc6b2 #d8dccf;
+	background: #eaecda;
+}
+
+.pun #viewprofile dt, #adstats dt {
+	color: #83866a;
+}
+
+.pun #viewprofile dl, .pun #viewprofile dd, #adstats dl, #adstats dd {
+	border-color: #dce6d8;
+}
+
+#adminconsole fieldset td.nodefault {
+	background: #d59b9b;
+}
+
+/* Status Indicators
+----------------------------------------------------------------*/
+
+.pun .icon {
+	border-color: #eef1e8 #dbddd4 #e6e8df #eef1e8;
+}
+
+.pun .iredirect .icon {
+	border-color: #bbc6b2;
+	border-width: 1px;
+	padding: 7px;
+}
+
+.pun .inew .icon {
+	border-color: #50a42f #408426 #32671d #4a982c;
+}
diff --git a/style/Earth/base_admin.css b/style/Earth/base_admin.css
new file mode 100644
index 0000000..60a064e
--- /dev/null
+++ b/style/Earth/base_admin.css
@@ -0,0 +1,143 @@
+#adminconsole .blockform .box {
+	padding-bottom: 12px;
+}
+
+#adminconsole fieldset .infldset {
+	position: relative;
+	overflow: hidden;
+}
+
+#adminconsole fieldset table {
+	margin-top: -1px;
+	margin-bottom: -1px;
+}
+
+#adminconsole fieldset td, #adminconsole fieldset th {
+	padding: 10px 8px 10px 0;
+	text-align: left;
+	white-space: normal;
+	border-style: solid none;
+	border-width: 1px 0;
+}
+
+#punadmin thead th {
+	border-top: 0;
+	font-weight: normal;
+}
+
+#adminconsole fieldset td span, #adminconsole fieldset th span {
+	display: block; font-size: 1em;
+	font-weight: normal;
+}
+
+#adminconsole fieldset td.location span {
+	display: inline-block;
+}
+
+#adminconsole fieldset tbody th {
+	width: 15em;
+	font-weight: normal;
+	padding-right: 8px;
+}
+
+#adminconsole table.aligntop th, #adminconsole table.aligntop td {
+	vertical-align: top;
+}
+
+#adminconsole table.aligntop th div {
+	padding-top: 3px;
+}
+
+#adminconsole .inform {
+	padding-bottom: 0;
+}
+
+#adminconsole .infldset {
+	padding-bottom: 0;
+	padding-top: 0;
+}
+
+#adminconsole p.submittop {
+	text-align: center;
+	border-bottom-style: dotted;
+	border-bottom-width: 1px;
+	margin: 0 18px;
+	padding-top: 12px;
+}
+
+#adminconsole p.submitend {
+	text-align: center;
+	padding-bottom: 0;
+}
+
+#adminconsole fieldset p {
+	padding: 10px 0;
+}
+
+#adminconsole .fsetsubmit {
+	padding: 10px 0 12px 0;
+}
+
+#categoryedit .tcl {
+	width: 25%;
+}
+
+#censoring .tcl, #censoring .tc2, #ranks .tcl, #ranks .tc2 {
+	width: 20%;
+}
+
+#edforum .tcl {
+	width: 18%;
+}
+
+#edforum .tc2 {
+	width: 12%;
+}
+
+#forumperms thead th, #forumperms tbody td {
+	text-align: center;
+}
+
+.pun .linkst .backlink, .pun .linksb .backlink {
+	padding: 7px 0;
+}
+
+#punadmin #users1 h2, #punadmin #users2 h2, #punadmin #bans1 h2 {
+	display: block;
+	left: -9999em;
+	overflow: hidden;
+	position: absolute;
+	text-indent: -9999em;
+	width: 0;
+}
+
+#punadmin #users1 th, #punadmin #users2 th, #punadmin #bans1 th {
+	font-weight: bold;
+	}
+
+#users2 th, #bans1 th {
+	text-align: left;
+}
+
+#users2 .tcl, #users2 .tc3, #users2 .tc5, #bans1 .tcl, #bans1 .tc3, #bans1 .tc5, #bans1 .tc6 {
+	width: 15%;
+	text-align: left;
+}
+
+#users2 .tc2, #bans1 .tc2 {
+	width: 22%;
+	text-align: left;
+}
+
+#users2 .tc4, #bans1 .tc4 {
+	width: 8%;
+	text-align: center;
+}
+
+#users2 .tcr, #bans1 .tcr {
+	white-space: nowrap;
+}
+
+.plugin p {
+	padding: 12px 18px 0;
+}
diff --git a/style/Earth/img/asterisk.png b/style/Earth/img/asterisk.png
new file mode 100644
index 0000000..d00ef54
Binary files /dev/null and b/style/Earth/img/asterisk.png differ
diff --git a/style/Earth/img/bull.png b/style/Earth/img/bull.png
new file mode 100644
index 0000000..5fd1a9a
Binary files /dev/null and b/style/Earth/img/bull.png differ
diff --git a/style/Earth/img/email.png b/style/Earth/img/email.png
new file mode 100644
index 0000000..bd1ee24
Binary files /dev/null and b/style/Earth/img/email.png differ
diff --git a/style/Earth/img/exclaim.png b/style/Earth/img/exclaim.png
new file mode 100644
index 0000000..92eb57b
Binary files /dev/null and b/style/Earth/img/exclaim.png differ
diff --git a/style/Earth/img/ext.png b/style/Earth/img/ext.png
new file mode 100644
index 0000000..1e4a0b2
Binary files /dev/null and b/style/Earth/img/ext.png differ
diff --git a/style/Earth/img/feed.png b/style/Earth/img/feed.png
new file mode 100644
index 0000000..d4cf8ce
Binary files /dev/null and b/style/Earth/img/feed.png differ
diff --git a/style/Earth/img/help.png b/style/Earth/img/help.png
new file mode 100644
index 0000000..9b9114d
Binary files /dev/null and b/style/Earth/img/help.png differ
diff --git a/style/Earth/img/index.html b/style/Earth/img/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/style/Earth/img/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/style/Earth/img/main_end.png b/style/Earth/img/main_end.png
new file mode 100644
index 0000000..c9d5659
Binary files /dev/null and b/style/Earth/img/main_end.png differ
diff --git a/style/Earth/img/main_top.png b/style/Earth/img/main_top.png
new file mode 100644
index 0000000..787718f
Binary files /dev/null and b/style/Earth/img/main_top.png differ
diff --git a/style/Earth/index.html b/style/Earth/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/style/Earth/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/style/Fire.css b/style/Fire.css
new file mode 100644
index 0000000..803980d
--- /dev/null
+++ b/style/Fire.css
@@ -0,0 +1,1606 @@
+/*****************************************************************
+1. INITIAL SETTINGS
+*****************************************************************/
+
+/* Limited Reset
+----------------------------------------------------------------*/
+
+html, body, .pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3, .pun h4, .pun h5, .pun pre, .pun blockquote,
+.pun ul, .pun ol, .pun li, .pun dl, .pun dt, .pun dd, .pun th, .pun td, .pun fieldset, .pun legend .pun img,
+.pun abbr, .pun cite {
+	border: 0;
+	font-style: normal;
+	font-weight: normal;
+	margin: 0;
+	padding: 0;
+}
+
+.pun ul, .pun ol {
+	list-style: none;
+}
+
+.pun select {
+	padding-bottom: 1px;
+	padding-top: 1px;
+}
+
+/* Content Defaults
+----------------------------------------------------------------*/
+
+.pun {
+	font: 81.25%/1.462em Arial, Helvetica, sans-serif;
+}
+
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea {
+	font-family: Arial, Helvetica, sans-serif;
+	font-size: 1em;
+}
+
+.pun pre, .pun code {
+	font-family: consolas, monaco, "bitstream vera sans mono", "courier new", courier, monospace;
+	font-size: 1em;
+}
+
+.pun pre code {
+	font-size: 1em;
+}
+
+.pun table {
+	border-collapse: collapse;
+	border-spacing: 0;
+	border: 0;
+	empty-cells: show;
+	width: 100%;
+}
+
+.pun h1 {
+	font:2.154em/1em "Trebuchet MS", Arial, Helvetica, sans-serif;
+	padding: 7px 0;
+}
+
+.pun h2, .pun .hd h2 {
+	font: 1.462em/1em "Trebuchet MS", Arial, Helvetica, sans-serif;
+	padding: 7px 0;
+}
+
+.pun h3 {
+	font-size: 1.154em;
+	line-height: 1.267em;
+	padding: 7px 0;
+}
+
+.pun h4 {
+	font-size: 1.077em;
+	font-weight: bold;
+	padding: 7px 0;
+}
+
+.pun h5, .pun h6 {
+	font-size: 1em;
+	font-weight: bold;
+	padding: 7px 0;
+}
+
+.pun p, .pun ul, .pun ol, .pun dl, .pun th, .pun td, .pun legend {
+	padding: 7px 0;
+}
+
+.pun strong, .pun th, .pun span.warntext, .pun p.warntext {
+	font-weight: bold;
+}
+
+.pun em {
+	font-style: italic;
+}
+
+.pun a, .pun a:link, .pun a:visited {
+	text-decoration: none;
+}
+
+.pun a:hover, .pun a:active, .pun a:focus {
+	text-decoration: underline;
+}
+
+.pun .actions span {
+	padding-left: 16px;
+	padding-right: 8px;
+	background: url(Fire/img/bull.png) center left no-repeat;
+	display: inline-block;
+	line-height: normal;
+}
+
+/* Hidden Elements
+----------------------------------------------------------------*/
+
+#brdfooter h2, #brdstats h2, #debug h2, #brdstats .conl dt, #brdstats .conr dt, #modcontrols dt,
+#searchlinks dt, div.postright h3, .pun .subscribelink span, #announce .hd, #reportform h2, #punmoderate #vf h2,
+#punviewforum #vf h2, .pun .required strong span, .pun .icon div {
+	display: block;
+	overflow: hidden;
+	position: absolute;
+	text-indent: -9999em;
+	width: 0;
+}
+
+/* Generic Float Clear
+----------------------------------------------------------------*/
+
+.pun .inbox, .pun #brdmain, .pun .crumbs, .pun .pagepost, .pun .block2col {
+	min-height: 1px;
+}
+
+* html .pun .inbox, * html .pun #brdmain, * html .pun .infldset, * html .pun .crumbs, * html .pun .pagepost, * html .pun .block2col {
+	display: inline-block;
+}
+
+* html .pun .inbox, * html .pun #bdrdmain, * html .pun .infldset, * html .pun .crumbs, * html .pun .pagepost, * html .pun .block2col {
+	display: block;
+}
+
+.pun .inbox:after, .pun #brdmain:after, .pun .crumbs:after, .pun .pagepost:after, .pun .block2col:after {
+	content: " ";
+	display: block;
+	height: 0;
+	font-size: 0;
+	clear: both;
+	visibility: hidden;
+}
+
+.pun .block2col .inbox:after {
+	content: none;
+	clear: none;
+}
+
+/*****************************************************************
+2. COMMON STYLES
+*****************************************************************/
+
+/* Page Layout
+----------------------------------------------------------------*/
+
+.pun {
+	padding: 30px 40px;
+}
+
+#punredirect, #punmaint {
+	padding: 60px 20% 12px 20%;
+}
+
+#puninstall, #pundb_update {
+	padding: 20px 10%;
+}
+
+.pun .punwrap {
+	border: 1px solid;
+	padding: 18px;
+}
+
+.pun .top-box {
+	height: 18px;
+	margin-bottom: -18px;
+	position: relative;
+	background: url(Fire/img/main_top.png) top left no-repeat;
+}
+
+.pun .top-box div {
+	height: 18px;
+	margin-left: 50%;
+	background: url(Fire/img/main_top.png) top right no-repeat;
+}
+
+.pun .end-box {
+	margin-top: -17px;
+	height: 18px;
+	position: relative;
+	background: url(Fire/img/main_end.png) bottom left no-repeat;
+}
+
+.pun .end-box div {
+	height: 18px;
+	margin-left: 50%;
+	background: url(Fire/img/main_end.png) bottom right no-repeat;
+}
+
+#punredirect h2, #punmaint h2 {
+	border-bottom-style: dotted;
+	border-bottom-width: 1px;
+	margin-bottom: 3px;
+}
+
+/* Section Spacing and Borders
+----------------------------------------------------------------*/
+
+#brdmain {
+	border-style: solid none;
+	border-width: 2px 0;
+	margin-bottom: 12px;
+	padding: 12px 0;
+}
+
+#punindex #brdmain {
+	padding-top: 24px;
+}
+
+#punredirect #brdmain, #punmaint #brdmain {
+	border: 0;
+	margin: 0;
+	padding: 0;
+}
+
+#brdstats {
+	border-style: solid none none none;
+	border-width: 2px 0 0 0;
+	margin-top: 24px;
+	padding-top: 12px;
+}
+
+#quickpost {
+	border-style: solid none none none;
+	border-width: 2px 0 0 0;
+	margin-top: 12px;
+	padding-top: 12px;
+}
+
+#announce {
+	border-style: solid none none none;
+	border-width: 2px 0 0 0;
+	padding-top: 3px;
+}
+
+/*****************************************************************
+3. COMMON BOARD ELEMENTS
+*****************************************************************/
+
+/* Logo, Description and Main Menu
+----------------------------------------------------------------*/
+
+#brdtitle h1 {
+	padding: 0 0 10px 0;
+}
+
+#brddesc {
+	border-top-style: dotted;
+	border-top-width: 1px;
+	padding: 10px 0;
+}
+
+#brddesc p {
+	padding: 0;
+}
+
+#brdmenu ul {
+	padding: 0;
+}
+
+#brdmenu li {
+	float: left;
+}
+
+#brdmenu a:link, #brdmenu a:visited {
+	border-right-style: solid;
+	border-width: 1px;
+	display: block;
+	min-width: 60px;
+	padding: 12px 16px 6px 8px;
+	white-space: nowrap;
+}
+
+#brdmenu a:hover, #brmenu a:active, #brdmenu a:focus {
+	text-decoration: none;
+}
+
+/* Welcome Box
+----------------------------------------------------------------*/
+
+#brdwelcome {
+	padding: 10px 0;
+}
+
+#brdwelcome .conl, #brdwelcome .conr, #brdwelcome p, #brdwelcome li {
+	display: inline;
+	padding: 0;
+}
+
+#brdwelcome li span {
+	background: url(Fire/img/bull.png) center left no-repeat;
+	padding-left: 18px;
+	margin-right: 3px;
+	display: inline-block;
+	line-height: normal;
+	white-space: nowrap;
+}
+
+#brdwelcome .conl li:first-child span {
+	padding-left: 0;
+	background: none;
+}
+
+/* Stats
+----------------------------------------------------------------*/
+
+#brdstats .conl {
+	float: left;
+}
+
+#brdstats .conr {
+	float: right;
+	text-align: right;
+}
+
+#brdstats #onlinelist {
+	border-top-style: dotted;
+	border-top-width: 1px;
+	clear: both;
+}
+
+#brdstats #onlinelist dt, #brdstats #onlinelist dd {
+	display: inline;
+}
+
+/* Footer
+----------------------------------------------------------------*/
+
+.pun #modcontrols {
+	border-style: none none dotted none;
+	border-width: 0 0 1px 0;
+	margin-bottom: 4px;
+	text-align: center;
+	width: 100%;
+}
+
+.pun #modcontrols dd {
+	display: inline;
+}
+
+.pun #brdfooter #modcontrols dd span {
+	background: url(Fire/img/bull.png) center left no-repeat;
+	display: inline-block;
+	line-height: normal;
+	padding-left: 18px;
+	white-space: nowrap;
+}
+
+.pun #brdfooter .conl {
+	float: left;
+}
+
+.pun #brdfooter .conr {
+	text-align: right;
+	float: right;
+}
+
+.pun #brdfooter #poweredby a {
+	font-size: 1.077em;
+	font-weight: bold;
+}
+
+.pun #brdfooter #qjump {
+	padding-top: 5px;
+}
+
+.pun #brdfooter #qjump * {
+	white-space: nowrap;
+}
+
+.pun #brdfooter #searchlinks dd span {
+	background: url(Fire/img/bull.png) center left no-repeat;
+	display: inline-block;
+	line-height: normal;
+	padding-left: 18px;
+	white-space: nowrap;
+}
+
+.pun #brdfooter #feedlinks {
+	padding-bottom: 0;
+}
+
+.pun #brdfooter #feedlinks span {
+	background: url(Fire/img/feed.png) center left no-repeat;
+	display: inline-block;
+	padding-left: 18px;
+	white-space: nowrap;
+}
+
+.pun #debugtime {
+	border-style: dotted none none none;
+	border-width: 1px 0 0 0;
+	margin-top: 7px;
+	text-align: center;
+}
+
+/* Breadcrumbs, Postlink, Pagination
+----------------------------------------------------------------*/
+
+.pun .linkst .inbox, .pun linksb .inbox, .pun .postlinksb .inbox {
+	overflow: hidden;
+}
+
+.pun .linksb, .pun .postlinksb, .pun .linkst, .pun .crumbs {
+	clear: both;
+	position: relative;
+}
+
+.pun .linkst .crumbs {
+	font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+	font-size: 1.462em;
+	line-height: 1.211em;
+	padding: 7px 0;
+}
+
+.pun .linksb .crumbs, .pun .postlinksb .crumbs {
+	font-family: "Trebuchet MS", Helvetica, Arial, sans-serif;
+	font-size: 1.154em;
+}
+
+.pun .linkst .crumbsplus .pagepost {
+	border-top-style: dotted;
+	border-top-width: 1px;
+}
+
+.pun .linksb .crumbsplus .pagepost, .pun .postlinksb .crumbsplus .pagepost {
+	border-bottom-style: dotted;
+	border-bottom-width: 1px;
+}
+
+.pun .postlinksb .crumbs {
+	margin-right: 11em;
+}
+
+.pun .crumbs li {
+	float: left;
+	padding-right: 0.4em;
+	white-space: nowrap;
+}
+
+.pun .crumbs li strong {
+	font-weight: normal;
+}
+
+.pun .pagelink {
+	float: left;
+	white-space: nowrap;
+}
+
+.pun .pagelink strong, .pun .pagelink a, .pun .pagelink span.spacer {
+	border-style: none none none solid;
+	border-width: 0 0 0 1px;
+	display: inline-block;
+	padding: 0 12px 0 10px;
+	margin-right: -6px;
+}
+
+.pun .pagelink .item1 {
+	border: 0;
+}
+
+.pun .pagelink .pages-label {
+	display: inline-block;
+}
+
+.pun .postlink {
+	float: right;
+	font-weight: bold;
+	text-align: right;
+}
+
+.pun .modbuttons {
+	float: right;
+	padding: 5px 0 3px 0;
+}
+
+.pun .modbuttons input {
+	margin-left: 8px;
+}
+
+.pun .subscribelink {
+	position: absolute;
+	right: 0;
+	text-align: right;
+	top: 33px;
+}
+
+/*****************************************************************
+4. MAIN TABLES
+*****************************************************************/
+
+.pun #brdmain .blocktable {
+	position: relative;
+}
+
+#punindex #brdmain .blocktable h2, #punsearch #vf h2 {
+	font: 1em/1.462em Arial, Helvetica, sans-serif;
+	font-weight: bold;
+	margin: 1px 1px 0 1px;
+	padding-left: 8px;
+	position: absolute;
+	left: 0;
+	white-space: nowrap;
+	z-index: 100;
+}
+
+#punindex .blocktable th.tcl, #punsearch #vf th.tcl {
+	font-size: 0;
+	text-indent: -9999em;
+}
+
+.pun .blocktable .box {
+	border-style: solid;
+	border-width: 1px;
+	margin-bottom: -1px;
+	overflow: hidden;
+	position: relative;
+}
+
+* html .pun .blocktable .box {
+	display: inline-block;
+}
+
+.pun .blocktable table {
+	table-layout: fixed;
+	margin-bottom: -1px;
+}
+
+.pun .blocktable th {
+	padding: 7px 8px;
+	border-style: none none solid none;
+	border-width: 1px;
+	text-align: left;
+}
+
+.pun .blocktable td {
+	padding: 7px 8px;
+	line-height: 1.3077em;
+	border-style: none none solid none;
+	border-width: 1px;
+	text-align: left;
+}
+
+.pun .blocktable h3 {
+	font-size: 1.077em;
+	font-weight: bold;
+	padding: 0;
+}
+
+.pun .blocktable p {
+	padding: 0;
+}
+
+.pun .blocktable .tcl p {
+	padding: 5px 0 0 0;
+}
+
+.pun .blocktable .tcl {
+	width: auto;
+}
+
+.pun .blocktable .tc2, .pun .blocktable .tc3, .pun .blocktable .tcmod {
+	padding-left: 0;
+	padding-right: 0;
+	text-align: center;
+	width: 11%;
+}
+
+.pun .blocktable .tcr {
+	width: 30%;
+}
+
+.pun .blocktable td .newtext, .pun .blocktable td .pagestext, .pun .blocktable td .byuser {
+	white-space: nowrap;
+}
+
+.pun .blocktable .tcl h3 span.newtext {
+	font-size: 0.929em;
+	font-weight: normal;
+}
+
+.pun #vf td.tcl span.stickytext, .pun #vf td.tcl span.closedtext {
+	font-size: 1em;
+	font-weight: bold;
+}
+
+#punsearch #vf .tc2 {
+	padding-left: 8px;
+	padding-right: 8px;
+	text-align: left;
+	width: 18%;
+}
+
+#users1 .tcr {
+	width: 25%;
+}
+
+#users1 .tc2 {
+	padding-left: 8px;
+	padding-right: 8px;
+	text-align: left;
+	width: 25%;
+}
+
+#debug {
+	margin-top: 12px;
+}
+
+#debug .tcl {
+	width: 10%;
+}
+
+#punredirect #debug .tcl, #punmaint #debug .tcl {
+	width: 20%;
+}
+
+#debug .tcr {
+	width: 90%;
+	white-space: normal
+}
+
+#punindex .tcr .byuser {
+	display: block
+}
+
+#punindex td.tc2, #punindex td.tc3, #punindex td.tcr, .pun #vf td.tc2, .pun #vf td.tc3,
+.pun #vf td.tcr, #punindex td.tcl div.forumdesc, .pun #vf td.tcl span {
+	font-size: 0.923em;
+}
+
+.pun #vf td.tcl a {
+	font-weight: bold;
+}
+
+.pun #vf td.tcl span a {
+	font-weight: normal;
+}
+
+.pun .blocktable .tclcon {
+	height: 1%;
+	min-height: 1px;
+	overflow: hidden;
+	padding: 0 11px 0 12px;
+	position: relative;
+}
+
+.pun .blocktable .tclcon div {
+	width: 100%;
+	overflow: hidden;
+}
+
+.pun .icon {
+	border-style: solid;
+	border-width: 8px;
+	float: left;
+	height: 0;
+	overflow: hidden;
+	width: 0;
+}
+
+.pun .iposted .ipost {
+	font-weight: bold;
+	left: 0;
+	padding-left: 4px;
+	position: absolute;
+	text-align: center;
+	top: 0;
+	width: 8px;
+}
+
+/*****************************************************************
+MAIN POSTS
+*****************************************************************/
+
+/* Structure
+----------------------------------------------------------------*/
+
+.pun .blockpost {
+	border-style: solid;
+	border-width: 1px;
+	margin-bottom: -1px;
+	overflow: hidden;
+	position: relative;
+}
+
+* html .pun .blockpost {
+	display: inline-block;
+}
+
+.pun .blockpost h2 {
+	font: 1em/1.462em Arial, Helvetica, sans-serif;
+	white-space: nowrap;
+	border-bottom-style: solid;
+	border-bottom-width: 1px;
+	height: 1.462em;
+	padding: 0.538em 8px 0.538em 236px;
+	font-weight: normal;
+}
+
+#punsearch .blockpost h2 {
+	height: auto;
+	padding-left: 36px;
+	white-space: normal;
+}
+
+#punsearch .blockpost h2 span span {
+	white-space: nowrap;
+	display: inline-block;
+	font: 1.077em "Trebuchet MS", Arial, Helvetica, sans-serif
+}
+
+#punsearch .blockpost .icon {
+	position: absolute;
+	top: 0;
+	margin-top: -2.154em;
+}
+
+.pun .blockpost h2 .conr {
+	float: right;
+	text-align: right;
+}
+
+.pun .blockpost .inbox {
+	float: right;
+	position: relative;
+	width: 100%;
+}
+
+.pun .blockpost .postbody, .pun .blockpost .postfoot {
+	border-left-style: solid;
+	border-left-width: 1px;
+	float: right;
+	margin-right: -218px;
+	position: relative;
+	text-align: left;
+	width: 100%;
+}
+
+.pun .blockpost .postleft, .pun .blockpost .postfootleft {
+	width: 206px;
+	padding: 7px 0 7px 12px;
+	float: left;
+	margin-left: -218px;
+	position: relative;
+}
+
+.pun .blockpost .postleft dl {
+	padding: 0;
+}
+
+#punviewtopic .blockpost dt, #punmoderate .blockpost dt {
+	display: block;
+	position: absolute;
+	padding: 0.538em 0 0.538em 12px;
+	height: 1.462em;
+	top: -2.615em;
+	left: 0;
+	overflow: hidden;
+	width: 206px;
+}
+
+.pun .blockpost dt strong {
+	font-size: 1.231em;
+	font-weight: bold;
+}
+
+.pun .blockpost .postleft dd {
+	font-size: 0.923em;
+}
+
+.pun .blockpost .postleft .usertitle {
+	padding: 4px 0 6px 0;
+	font-size: 1em;
+}
+
+.pun .blockpost .postleft .postavatar {
+	display: block;
+	margin: 0 0 4px 0;
+}
+
+.pun .blockpost .postright {
+	position: relative;
+	padding: 4px 230px 7px 18px;
+}
+
+.pun .postmsg {
+	width:100%;
+	overflow: hidden;
+}
+
+.pun .blockpost .postfootright {
+	position: relative;
+	padding: 7px 230px 7px 18px;
+	text-align: right;
+}
+
+.pun .postfoot p, .pun .postfoot ul {
+	padding: 0;
+}
+
+.pun .blockpost .postfootright li {
+	display: inline;
+}
+
+.pun .blockpost .postfootright li span {
+	display: inline-block;
+	padding-left: 16px;
+	margin-left: 8px;
+	line-height: normal;
+	background: url(Fire/img/bull.png) center left no-repeat;
+}
+
+.pun .blockpost .usercontacts {
+	padding: 7px 0;
+}
+
+.pun .blockpost .usercontacts .email {
+	background: url(Fire/img/email.png) left 65% no-repeat;
+	margin-right: 5px;
+	padding-left: 21px;
+	display: inline-block;
+	line-height: normal;
+}
+
+.pun .blockpost .usercontacts .website {
+	background: url(Fire/img/ext.png) left 65% no-repeat;
+	padding-left: 18px;
+	display: inline-block;
+	line-height: normal;
+}
+
+.pun .postsignature hr {
+	border:none;
+	height: 1px;
+	margin-left: 0px;
+	text-align: left;
+}
+
+/* Content (includes other user content)
+----------------------------------------------------------------*/
+
+.pun .usercontent {
+	padding: 7px 0;
+}
+
+.pun .postmsg p, .pun .postmsg li, #punhelp p samp {
+	font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+.pun .usercontent h1, .pun .usercontent h2, .pun .usercontent h3,
+.pun .usercontent h4, .pun .usercontent h5, .pun .usercontent h6 {
+	padding: 7px 0 0 0;
+}
+
+.pun .postmsg h5, #punhelp h5 {
+	font-size: 1.231em;
+	font-weight: bold;
+	padding: 7px 0;
+}
+
+.pun .usercontent ul, .pun .postmsg ul {
+	list-style: disc;
+	padding: 4px 13px 4px 30px;
+}
+
+.pun .usercontent ol, .pun .postmsg ol {
+	list-style: decimal;
+	padding: 4px 13px 4px 30px;
+}
+
+.pun .usercontent ol.alpha, .pun .postmsg ol.alpha {
+	list-style: lower-alpha;
+}
+
+.pun .usercontent li, .pun .postmsg li {
+	padding: 0 3px;
+}
+
+.pun .usercontent li p, .pun .postmsg li p {
+	padding: 0;
+}
+
+.pun span.bbu {
+	text-decoration: underline;
+}
+
+.pun span.bbs, .pun del {
+	text-decoration: line-through;
+}
+
+.pun .postmsg ins, #punhelp samp ins {
+	text-decoration: none;
+}
+
+.pun .blockpost .postmsg .postedit {
+	font-size: 0.857em;
+}
+
+.pun .blockform .postsignature, .pun .blockpost .postsignature {
+	font-size: 0.923em;
+}
+
+.pun .codebox {
+	border-style: solid;
+	border-width: 1px;
+	margin: 0.75em 1em;
+	padding: 0;
+}
+
+.pun .quotebox {
+	border-style: solid;
+	border-width: 1px 1px 1px 3px;
+	margin: 0.75em 1em;
+	padding: 0 0.75em;
+}
+
+.pun .quotebox cite {
+	display: block;
+	padding: 0.75em 0 0 0;
+	font-weight: bold;
+	line-height: 1.462em;
+}
+
+.pun .quotebox blockquote {
+	overflow: hidden;
+	width: 100%;
+}
+
+.pun .codebox pre {
+	overflow-y:hidden;
+	overflow: auto;
+	width: 100%;
+	direction: ltr;
+	text-align: left;
+}
+
+* html .pun .codebox pre {
+	padding-bottom: 10px;
+}
+
+*:first-child+html .pun .codebox pre {
+	padding-bottom: 10px;
+}
+
+.pun .codebox pre code {
+	padding: 0.75em;
+	white-space: pre;
+}
+
+.pun div[class*=codebox] pre code {
+	display: inline-block;
+}
+
+* html .pun .codebox pre code {
+	display: block;
+}
+
+.pun .codebox pre.vscroll {
+	height: 32em;
+	overflow: auto;
+	overflow-y: auto
+}
+
+.pun .postmsg img, #punhelp samp img {
+	vertical-align: text-top;
+}
+
+.pun .postmsg .postimg img {
+	max-width: 98%;
+	vertical-align: middle;
+}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+	border-style: solid;
+	border-width: 2px;
+}
+
+/*****************************************************************
+MAIN FORMS
+*****************************************************************/
+
+#punedit .blockform h2, #punpost .blockform h2, #postpreview h2, #posterror h2,
+.pun #quickpost h2, .pun #reportform h2, #pundelete .blockform h2 {
+	font: 1em/1.462em Arial, Helvetica, sans-serif;
+	font-weight: bold;
+	white-space: nowrap;
+	padding: 10px 19px 4px 37px;
+	border: 0;
+}
+
+#punpost .blockform h2, #punedit .blockform h2,.pun #quickpost h2,
+#pundelete .blockform h2 {
+	margin: 1px 1px 0 1px;
+	width: 25em;
+	position: absolute;
+	z-index: 100;
+}
+
+.pun #quickpost legend, #punpost legend, #punedit legend {
+	width: 25em;
+	overflow: hidden;
+	white-space: nowrap;
+}
+
+.pun .blockform .box {
+	border-style: solid;
+	border-width: 1px;
+	padding-bottom: 12px;
+}
+
+.pun #posterror {
+	border-style: solid;
+	border-width: 1px;
+}
+
+.pun #posterror .box {
+	padding: 0 18px 12px 18px;
+}
+
+* html .pun .blockform .box, * html .pun #posterror {
+	display: inline-block;
+}
+
+.pun .blockform .forminfo, .pun .error-info {
+	padding: 12px 18px;
+	border-style: solid;
+	border-width: 1px;
+	position: relative;
+}
+
+.pun .blockform .forminfo {
+	margin-top: 12px;
+}
+
+#pundelete .blockform .forminfo {
+	margin-top: 33px;
+}
+
+.pun .forminfo h3 {
+	padding-bottom: 0;
+}
+
+.pun .error-list li {
+	padding-left: 24px;
+	background: url(Fire/img/exclaim.png) center left no-repeat;
+}
+
+.pun .inform {
+	padding: 0 18px;
+}
+
+.pun legend {
+	font-weight: bold;
+	padding: 10px 19px 4px 19px;
+}
+
+* html .pun legend {
+	margin-left: -7px;
+}
+
+*:first-child+html .pun legend {
+	margin-left: -7px;
+}
+
+.pun .infldset {
+	border-style: solid;
+	border-width: 1px;
+	padding: 12px 18px;
+}
+
+#punregister #rules .infldset {
+	padding: 5px 18px;
+}
+
+.pun fieldset p {
+	padding: 0 0 7px 0;
+	width: 100%;
+}
+
+.pun fieldset .usercontent p {
+	padding: 7px 0;
+}
+
+.pun fieldset label {
+	display: block;
+	padding: 0 0 7px 0;
+}
+
+.pun label em {
+	font-weight: normal;
+	font-style: normal;
+}
+
+.pun .required strong {
+	background: url(Fire/img/asterisk.png) center right no-repeat;
+	font-weight: normal;
+	padding-right: 14px;
+	white-space: pre;
+	display: inline-block;
+	line-height: normal;
+}
+
+.pun label input, .pun label select, .pun label textarea {
+	margin-top: 2px;
+}
+
+.pun label.conl {
+	display: inline-block;
+	padding-right: 12px;
+}
+
+.pun form .buttons {
+	padding: 8px 19px 8px 34px;
+	margin-bottom: -12px;
+}
+
+.pun .blockform .buttons input {
+	margin-right: 12px;
+}
+
+.pun .rbox {
+	padding: 3px 0;
+}
+
+.pun .rbox label {
+	padding: 3px 0 3px 1.75em;
+	position: relative;
+	min-height: 1px;
+}
+
+* html .pun .rbox label {
+	text-indent: -3px;
+	height: 1%;
+}
+
+.pun .rbox input {
+	margin: 3px 0.75em 3px -1.75em;
+	float: left;
+	position: relative;
+	vertical-align: middle;
+	padding: 0;
+	height: 1em;
+	width: 1em;
+}
+
+.pun input[type=text], .pun select, .pun textarea {
+	font-family: Verdana, Arial, Helvetica, sans-serif;
+}
+
+.pun .txtarea textarea, .pun input.longinput {
+	width: 98%;
+}
+
+.pun #quickpost .txtarea {
+	padding-right: 12px;
+	padding-left: 200px;
+	position: relative;
+}
+
+.pun .blockform .bblinks {
+	padding-top: 0;
+}
+
+.pun .blockform .bblinks li {
+	display: inline;
+}
+
+.pun .blockform .bblinks li span {
+	background: url(Fire/img/help.png) center left no-repeat;
+	margin-right: 8px;
+	padding-left: 20px;
+	display: inline-block;
+}
+
+.pun #quickpost .bblinks {
+	left: 18px;
+	line-height: 1.75em;
+	position:absolute;
+	top: 18px;
+	width: 12em;
+}
+
+.pun #quickpost .bblinks li {
+	display: block;
+}
+
+.pun #login p.clearb {
+	border-top-style: dotted;
+	border-top-width: 1px;
+	font-size: 0;
+	height: 0;
+	line-height: 0;
+	margin-top: 7px;
+	overflow: hidden;
+	padding-bottom: 3px;
+	padding-top: 7px;
+	text-indent: -9999em;
+	width: 100%;
+}
+
+.pun #postreview {
+	padding-top: 12px;
+}
+
+.pun #postpreview, .pun #posterror {
+	margin-bottom: 12px;
+}
+
+.pun #postpreview .postright {
+	padding: 0;
+}
+
+.pun #postpreview .postbody {
+	border-style: solid;
+	border-width: 1px;
+	float: none;
+	margin: 0 18px 12px 18px;
+	padding: 0;
+	padding: 4px 18px 4px 18px;
+	width: auto;
+}
+
+.pun span.email {
+	background: url(Fire/img/email.png) left 65% no-repeat;
+	margin-right: 5px;
+	padding-left: 21px;
+	display: inline-block;
+	line-height: normal;
+}
+
+.pun span.website {
+	background: url(Fire/img/ext.png) left 65% no-repeat;
+	padding-left: 18px;
+	display: inline-block;
+	line-height: normal;
+}
+
+#punmisc #rules .box {
+	border-style: solid;
+	border-width: 1px;
+	padding: 5px 18px;
+}
+
+
+#punhelp .box {
+	border-style: solid;
+	border-width: 1px;
+	padding: 7px 12px;
+}
+
+/*****************************************************************
+PROFILES (+ ADMIN MENU)
+*****************************************************************/
+
+/* Profile / Admin
+----------------------------------------------------------------*/
+
+.pun .blockmenu {
+	width: 13em;
+	float: left;
+	padding-bottom: 12px;
+}
+
+.pun .block2col .blockform, .pun .block2col .block {
+	margin-left: 15em;
+}
+
+.pun .blockmenu .block2 {
+	padding-top: 19px;
+}
+
+.pun .blockmenu ul {
+	border-top-style: dotted;
+	border-top-width: 1px;
+	padding: 0;
+}
+
+.pun .blockmenu li {
+	border-bottom-style: dotted;
+	border-bottom-width: 1px;
+	font-weight: bold;
+	padding: 0;
+}
+
+.pun .blockmenu a:link, .pun .blockmenu a:visited {
+	display: block;
+	padding: 9px 6px 3px 6px;
+	min-height: 1px;
+	text-decoration: none;
+}
+
+* html .pun .blockmenu a:link,  * html .pun .blockmenu a:visited {
+	height: 1%;
+}
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active, .pun .blockmenu a:focus {
+	text-decoration: none;
+}
+
+#viewprofile .box {
+	border-style: solid;
+	border-width: 1px;
+	padding-bottom: 18px;
+}
+
+#viewprofile dt, #adstats dt {
+	padding: 7px 0;
+	position: absolute;
+	width: 13em;
+	left: 0;
+}
+
+#viewprofile dl {
+	border-style: solid none none none;
+	border-width: 1px;
+	margin: 7px 0;
+	padding: 0;
+	width: 100%;
+	position: relative;
+}
+
+#adintro, #adstats {
+	border-style: solid;
+	border-width: 1px;
+	padding: 18px;
+}
+
+#adstats .inbox, #adintro .inbox {
+	border-style: solid;
+	border-width: 1px;
+	padding: 18px;
+}
+
+#adstats dl {
+	margin: 0;
+	padding: 0;
+	width: 100%;
+	position: relative;
+}
+
+#viewprofile dd, #adstats dd {
+	border-style: none none solid none;
+	border-width: 1px;
+	padding: 7px 0 7px 13em;
+}
+
+/*****************************************************************
+COLOUR SCHEME
+*****************************************************************/
+
+/* Basic defaults and Common Items
+----------------------------------------------------------------*/
+
+html, body, .pun {
+	background: #eeedea;
+	color: #333;
+}
+
+.pun .punwrap {
+	background: #fff;
+	border-color: #e1d9ca;
+	color: #665858;
+}
+
+#brdtitle #brddesc, .pun .pagepost, #brdstats #onlinelist, #brdfooter #searchlinks, #brdfooter #modcontrols,
+#punmaint h2, #punredirect h2, #adminconsole .submittop, .pun #debugtime, .pun .pagelink * {
+	border-color: #cec7b9;
+}
+
+.pun a, .pun a:link, .pun a:visited {
+	color: #990000;
+}
+
+.pun a:hover, .pun a:active, .pun a:focus {
+	color: #f60;
+}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+	border-color: #990000;
+}
+
+.pun .postmsg .postimg a:hover img, .pun .postmsg .postimg a:active img, .pun .postmsg .postimg a:focus img {
+	border-color: #f60;
+}
+
+/* Primary Navigation
+----------------------------------------------------------------*/
+
+#brdmenu {
+	background: #990000;
+}
+
+#brdmenu a, #brdmenu a:link, #brdmenu a:visited {
+	background: #990000;
+	border-color: #fff;
+	color: #ddd;
+}
+
+#brdmenu a:hover, #brdmenu a:active, #brdmenu a:focus {
+	background: #f60;
+	border-color: #fff;
+	color: #fff;
+}
+
+/* Main Tables
+----------------------------------------------------------------*/
+
+.pun .blocktable .box {
+	background: #fbfaf5;
+	border-color: #cec7b9 #e7e2d9;
+}
+
+#punindex .blocktable h2, .pun #vf h2 {
+	color: #836359;
+}
+
+#adminconsole fieldset th, #adminconsole fieldset td {
+	background: #f3f2ed;
+	border-color: #ebe0dc;
+}
+
+.pun #users1 h2 {
+	background: #fff;
+}
+
+.pun .blocktable td {
+	border-color: #ebe0dc;
+}
+
+.pun .blocktable th {
+	background: #ecebe7;
+	border-color: #e1d9ca;
+	color: #836359;
+}
+
+.pun .blocktable td.tcl span.stickytext {
+	color: #000;
+}
+
+/* Main Posts
+----------------------------------------------------------------*/
+
+.pun .blockpost {
+	background: #f3f2ed;
+	border-color: #cec7b9 #e7e2d9;
+}
+
+.pun .blockpost h2 {
+	background: #ecebe7;
+	border-color: #e1d9ca;
+	color: #836359;
+}
+
+.pun .blockpost .postbody, .pun .blockpost .postfoot {
+	background: #fbfaf5;
+	border-color: #ebe0dc;
+}
+
+.pun .blockpost .postfootright li {
+	color: #fbfaf5;
+}
+
+.pun .postmsg, #punhelp code, #punhelp samp {
+	color: #333;
+}
+
+.pun .postsignature, .pun .postmsg .postedit {
+	color: #665858;
+}
+
+.pun .quotebox {
+	background: #f4f4e8;
+	border-color: #ccc;
+	color: #665858;
+}
+
+.pun .quotebox cite {
+	color: #836359;
+}
+
+.pun .codebox, #punhelp .codebox code {
+	background: #333;
+	color: #fff;
+}
+
+.pun .postmsg hr {
+	background: #cec7b9;
+}
+
+.pun .postmsg ins, #punhelp samp ins {
+	background-color: #ff0;
+}
+
+/* Main Forms + Profile
+----------------------------------------------------------------*/
+
+.pun .blockform .box, #adstats, #adintro, #postpreview, #posterror {
+	border-color: #cec7b9 #e7e2d9;
+	background: #ecebe7;
+}
+
+#punmisc #rules .box, #punhelp .box {
+	border-color: #cec7b9 #e7e2d9;
+	background: #f3f2ed;
+}
+
+.pun #quickpost h2, #punpost .blockform h2, #punedit .blockform h2, #posterror h2,
+#pundelete .blockform h2 {
+	background: #ecebe7;
+	color: #836359;
+}
+
+.pun .forminfo {
+	background: #fff;
+	border-color: #ebe0dc;
+}
+
+#puninstall form#install .forminfo {
+	background: #990000;
+	color: #fff;
+}
+
+.pun #posterror .error-info {
+	background: #ffffe1;
+	border-color: #dfe6ee;
+}
+
+#puninstall form#install .error-info {
+	background: #ffffe1;
+	border-color: #dfe6ee;
+	color: #333;
+}
+
+.pun .infldset, #adintro .inbox, #adstats .inbox {
+	background: #f3f2ed;
+	border-color: #ebe0dc;
+}
+
+.pun label, .pun legend, #adminconsole fieldset th {
+	color: #836359;
+}
+
+.pun fieldset p {
+	border-color: #cec7b9;
+}
+
+.pun .blockmenu ul, .pun .blockmenu li {
+	border-color: #cec7b9;
+}
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active, .pun .blockmenu a:focus {
+	background: #ffffe6;
+}
+
+.pun .blockmenu .isactive a:link, .pun .blockmenu .isactive a:visited {
+	color: #333;
+	background: #f3f2ed;
+	}
+
+.pun #viewprofile .box {
+	border-color: #cec7b9 #e7e2d9;
+	background: #ecebe7;
+}
+
+.pun #viewprofile dt, #adstats dt {
+	color: #836359;
+}
+
+.pun #viewprofile dl, .pun #viewprofile dd, #adstats dl, #adstats dd {
+	border-color: #ebe0dc;
+}
+
+#adminconsole fieldset td.nodefault {
+	background: #d59b9b;
+}
+
+/* Status Indicators
+----------------------------------------------------------------*/
+
+.pun .icon {
+	border-color: #f1eee8 #dddad4 #e8e5df #f1efe8;
+}
+
+.pun .iredirect .icon {
+	border-color: #cec7b9;
+	border-width: 1px;
+	padding: 7px;
+}
+
+.pun .inew .icon {
+	border-color: #ca0000 #ab0000 #bb0000 #d30000;
+}
diff --git a/style/Fire/base_admin.css b/style/Fire/base_admin.css
new file mode 100644
index 0000000..60a064e
--- /dev/null
+++ b/style/Fire/base_admin.css
@@ -0,0 +1,143 @@
+#adminconsole .blockform .box {
+	padding-bottom: 12px;
+}
+
+#adminconsole fieldset .infldset {
+	position: relative;
+	overflow: hidden;
+}
+
+#adminconsole fieldset table {
+	margin-top: -1px;
+	margin-bottom: -1px;
+}
+
+#adminconsole fieldset td, #adminconsole fieldset th {
+	padding: 10px 8px 10px 0;
+	text-align: left;
+	white-space: normal;
+	border-style: solid none;
+	border-width: 1px 0;
+}
+
+#punadmin thead th {
+	border-top: 0;
+	font-weight: normal;
+}
+
+#adminconsole fieldset td span, #adminconsole fieldset th span {
+	display: block; font-size: 1em;
+	font-weight: normal;
+}
+
+#adminconsole fieldset td.location span {
+	display: inline-block;
+}
+
+#adminconsole fieldset tbody th {
+	width: 15em;
+	font-weight: normal;
+	padding-right: 8px;
+}
+
+#adminconsole table.aligntop th, #adminconsole table.aligntop td {
+	vertical-align: top;
+}
+
+#adminconsole table.aligntop th div {
+	padding-top: 3px;
+}
+
+#adminconsole .inform {
+	padding-bottom: 0;
+}
+
+#adminconsole .infldset {
+	padding-bottom: 0;
+	padding-top: 0;
+}
+
+#adminconsole p.submittop {
+	text-align: center;
+	border-bottom-style: dotted;
+	border-bottom-width: 1px;
+	margin: 0 18px;
+	padding-top: 12px;
+}
+
+#adminconsole p.submitend {
+	text-align: center;
+	padding-bottom: 0;
+}
+
+#adminconsole fieldset p {
+	padding: 10px 0;
+}
+
+#adminconsole .fsetsubmit {
+	padding: 10px 0 12px 0;
+}
+
+#categoryedit .tcl {
+	width: 25%;
+}
+
+#censoring .tcl, #censoring .tc2, #ranks .tcl, #ranks .tc2 {
+	width: 20%;
+}
+
+#edforum .tcl {
+	width: 18%;
+}
+
+#edforum .tc2 {
+	width: 12%;
+}
+
+#forumperms thead th, #forumperms tbody td {
+	text-align: center;
+}
+
+.pun .linkst .backlink, .pun .linksb .backlink {
+	padding: 7px 0;
+}
+
+#punadmin #users1 h2, #punadmin #users2 h2, #punadmin #bans1 h2 {
+	display: block;
+	left: -9999em;
+	overflow: hidden;
+	position: absolute;
+	text-indent: -9999em;
+	width: 0;
+}
+
+#punadmin #users1 th, #punadmin #users2 th, #punadmin #bans1 th {
+	font-weight: bold;
+	}
+
+#users2 th, #bans1 th {
+	text-align: left;
+}
+
+#users2 .tcl, #users2 .tc3, #users2 .tc5, #bans1 .tcl, #bans1 .tc3, #bans1 .tc5, #bans1 .tc6 {
+	width: 15%;
+	text-align: left;
+}
+
+#users2 .tc2, #bans1 .tc2 {
+	width: 22%;
+	text-align: left;
+}
+
+#users2 .tc4, #bans1 .tc4 {
+	width: 8%;
+	text-align: center;
+}
+
+#users2 .tcr, #bans1 .tcr {
+	white-space: nowrap;
+}
+
+.plugin p {
+	padding: 12px 18px 0;
+}
diff --git a/style/Fire/img/asterisk.png b/style/Fire/img/asterisk.png
new file mode 100644
index 0000000..d00ef54
Binary files /dev/null and b/style/Fire/img/asterisk.png differ
diff --git a/style/Fire/img/bull.png b/style/Fire/img/bull.png
new file mode 100644
index 0000000..3fd1f61
Binary files /dev/null and b/style/Fire/img/bull.png differ
diff --git a/style/Fire/img/email.png b/style/Fire/img/email.png
new file mode 100644
index 0000000..bdd5ff8
Binary files /dev/null and b/style/Fire/img/email.png differ
diff --git a/style/Fire/img/exclaim.png b/style/Fire/img/exclaim.png
new file mode 100644
index 0000000..92eb57b
Binary files /dev/null and b/style/Fire/img/exclaim.png differ
diff --git a/style/Fire/img/ext.png b/style/Fire/img/ext.png
new file mode 100644
index 0000000..30a27ec
Binary files /dev/null and b/style/Fire/img/ext.png differ
diff --git a/style/Fire/img/feed.png b/style/Fire/img/feed.png
new file mode 100644
index 0000000..d4cf8ce
Binary files /dev/null and b/style/Fire/img/feed.png differ
diff --git a/style/Fire/img/help.png b/style/Fire/img/help.png
new file mode 100644
index 0000000..a8a7367
Binary files /dev/null and b/style/Fire/img/help.png differ
diff --git a/style/Fire/img/index.html b/style/Fire/img/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/style/Fire/img/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/style/Fire/img/main_end.png b/style/Fire/img/main_end.png
new file mode 100644
index 0000000..15a5937
Binary files /dev/null and b/style/Fire/img/main_end.png differ
diff --git a/style/Fire/img/main_top.png b/style/Fire/img/main_top.png
new file mode 100644
index 0000000..e951ebd
Binary files /dev/null and b/style/Fire/img/main_top.png differ
diff --git a/style/Fire/index.html b/style/Fire/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/style/Fire/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/style/Lithium.css b/style/Lithium.css
new file mode 100644
index 0000000..96f75a0
--- /dev/null
+++ b/style/Lithium.css
@@ -0,0 +1,1085 @@
+/*****************************************************************
+1. INITIAL SETTINGS
+*****************************************************************/
+
+/* Limited Reset
+----------------------------------------------------------------*/
+
+.pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
+.pun h4, .pun h5, .pun pre, .pun blockquote, .pun ul, .pun ol, .pun li, .pun dl,
+.pun dt, .pun dd, .pun th, .pun td, .pun fieldset, .pun img, .pun abbr, .pun cite {
+	margin: 0;
+	padding: 0;
+	border: 0;
+	}
+
+.pun ul, .pun ol {
+	list-style: none
+	}
+
+
+/* Structural Settings
+----------------------------------------------------------------*/
+
+.pun .clearer, .pun .nosize {
+	height: 0;
+	width: 0;
+	line-height: 0;
+	font-size: 0;
+	overflow: hidden
+	}
+
+.pun .clearer, .pun .clearb {
+	clear: both
+	}
+
+.pun .nosize {
+	position: absolute;
+	left: -9999em;
+	text-indent: -9999em;
+	width: 0;
+	}
+
+* html .inbox, * html .inform, * html .pun, * html .tclcon, * html .codebox {
+	height: 1px
+	}
+
+.pun, .pun .inbox, .pun .inform, .pun .tclcon, .pun .codebox {
+	min-height: 1px
+	}
+
+
+/* Hidden Elements
+----------------------------------------------------------------*/
+
+#brdfooter h2, #brdstats h2, #brdstats .conl dt, #brdstats .conr dt,
+#modcontrols dt, #searchlinks dt, div.postright h3, span.closedtext,
+.pun .required strong span {
+	position: absolute;
+	display: block;
+	overflow: hidden;
+	width: 0;
+	left: -9999em;
+	text-indent: -9999em;
+	}
+
+/*****************************************************************
+2. TEXT & CONTENT
+*****************************************************************/
+
+/* Text Defaults
+----------------------------------------------------------------*/
+
+.pun {
+	font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
+	}
+
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp {
+	font-size: 1em;
+	font-family: verdana, helvetica, arial, sans-serif;
+	}
+
+.pun pre, .pun code {
+	font-size: 1.182em;
+	font-family: consolas, monaco, "bitstream vera sans mono", "courier new", courier, monospace
+	}
+
+.pun pre code {
+	font-size: 1em;
+	}
+
+.pun strong {
+	font-weight: bold;
+	}
+
+.pun em {
+	font-style: italic;
+	}
+
+
+/* Content Defaults
+----------------------------------------------------------------*/
+
+.pun p, .pun ul, .pun ol, .pun dl {
+	font-size: 1em;
+	padding: 3px 0;
+	}
+
+.pun h2 {
+	font-size: 1em;
+	font-weight: normal;
+	padding: 4px 7px;
+	}
+
+.pun h3 {
+	font-size: 1.091em;
+	padding: 3px 0;
+	}
+
+.pun table p, .pun table h3 {
+	padding: 0;
+	}
+
+.pun span.warntext, .pun p.warntext {
+	font-weight: bold
+	}
+
+/* User Content (Announcements, Rules, Posts)
+----------------------------------------------------------------*/
+
+.pun .usercontent p, .pun .postmsg p {
+	padding: 0.75em 0
+	}
+
+.pun .usercontent ul, .pun .postmsg ul {
+	padding: 0.75em 1em 0.75em 2.5em;
+	list-style: disc
+	}
+
+.pun .usercontent ol, .pun .postmsg ol {
+	padding: 0.75em 1em 0.75em 2.5em;
+	list-style: decimal
+	}
+
+.pun .usercontent ol.alpha, .pun .postmsg ol.alpha {
+	list-style: lower-alpha
+	}
+
+.pun .usercontent li ol, .pun .usercontent li ul, .pun .postmsg li ol, .pun .postmsg li ul {
+	padding: 0.25em 1em 0.75em 2.5em
+	}
+
+.pun .usercontent li p, .pun .postmsg li p {
+	padding: 0
+	}
+
+.pun .usercontent h1 {
+	font-size: 1.4em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h2 {
+	font-size: 1.2em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h3 {
+	font-size: 1.1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h4, .pun .usercontent h5, .pun .usercontent h6 {
+	font-size: 1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .quotebox cite {
+	font-weight: bold;
+	font-style: normal;
+	padding: 0.75em 0.75em 0 0.75em
+	}
+
+.pun span.bbu {
+	text-decoration: underline
+	}
+
+.pun span.bbs, .pun del {
+	text-decoration: line-through;
+	}
+
+.pun .postmsg ins, #punhelp samp ins {
+	text-decoration: none;
+	}
+
+.pun div.postmsg h5, #punhelp h5 {
+	font-size: 1.1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0;
+	}
+
+
+/*****************************************************************
+3. COMMON STYLES
+*****************************************************************/
+
+/* Page Layout
+----------------------------------------------------------------*/
+
+.pun {
+	margin: 12px 20px
+	}
+
+#punredirect, #punmaint, #puninstall, #pundb_update {
+	margin: 50px 20% 12px 20%
+	}
+
+
+/* Vertical Element Spacing
+----------------------------------------------------------------*/
+
+#brdheader {
+	margin: 0 0 12px 0;
+	}
+
+#announce, #brdstats {
+	margin: 12px 0 12px 0;
+	}
+
+.pun .blocktable, .pun .block, .pun .blockform, .pun .block2col, #postreview {
+	margin-bottom: 12px
+	}
+
+#punindex .blocktable, .pun .blockpost {
+	margin-bottom: 6px
+	}
+
+#postreview .blockpost {
+	margin-bottom: -1px;
+	}
+
+.pun .block2col .blockform, .pun .block2col .block {
+	margin-bottom: 0px
+	}
+
+.pun .linkst, .pun .linksb {
+	margin-top: -12px
+	}
+
+.pun .postlinksb {
+	margin-top: -6px
+	}
+
+
+/* External Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+	border-style: solid;
+	border-width: 1px;
+	}
+
+#brdheader .box {
+	border-top-width: 4px;
+	}
+
+/* Default Internal Spacing
+----------------------------------------------------------------*/
+
+.pun .block .inbox, .pun .blockmenu .inbox {
+	padding: 3px 6px
+	}
+
+/*****************************************************************
+4. COMMON BOARD ELEMENTS
+*****************************************************************/
+
+/* Board Header
+----------------------------------------------------------------*/
+
+#brdtitle h1 {
+	font-size: 1.454em;
+	font-weight: bold;
+	line-height: 1em;
+	padding: 3px 0 0 0;
+	}
+
+#brddesc {
+	padding: 3px 0;
+	}
+
+#brddesc * {
+	padding-top: 0;
+	padding-bottom: 0;
+	}
+
+#brdmenu li {
+	display: inline;
+	margin-right: 12px;
+	}
+
+#brdmenu a:link, #brdmenu a:visited {
+	text-decoration: none
+	}
+
+#brdmenu a:hover, #brdmenu a:active {
+	text-decoration: underline
+	}
+
+#brdwelcome .conl {
+	float: left;
+	}
+
+#brdwelcome .conr {
+	float: right;
+	text-align: right;
+	}
+
+/* Breadcrumbs and Post Links
+----------------------------------------------------------------*/
+
+.pun .linkst {
+	padding: 8px 6px 3px 6px
+	}
+
+.pun .linksb, .pun .postlinksb {
+	padding: 3px 6px 8px 6px
+	}
+
+.pun .crumbs {
+	clear: both;
+	width: 100%;
+	overflow: hidden;
+	}
+
+.pun .crumbs li {
+	display: inline;
+	white-space: nowrap;
+	font-weight: bold;
+	}
+
+.pun .pagelink {
+	float: left;
+	white-space: nowrap;
+	}
+
+.pun .postlink {
+	font-weight: bold;
+	white-space: nowrap;
+	}
+
+.pun .postlink, .pun .modbuttons {
+	float: right;
+	text-align: right;
+	}
+
+.pun .modbuttons {
+	padding: 1px 0;
+	white-space: nowrap;
+	}
+
+.pun .modbuttons input {
+	margin-left: 6px;
+	}
+
+.pun .postlink a:link, .pun .postlink a:visited {
+	text-decoration: none
+	}
+
+.pun .postlink a:hover, .pun .postlink a:active {
+	text-decoration: underline;
+	}
+
+
+/* Board Footer
+----------------------------------------------------------------*/
+
+#brdfooter .conl {
+	float: left;
+	}
+
+#brdfooter .conr {
+	float: right;
+	text-align: right;
+	}
+
+#brdfooter #modcontrols {
+	border-bottom-style: solid;
+	border-bottom-width: 1px;
+	text-align: center;
+	}
+
+#brdfooter #modcontrols dd {
+	display: inline;
+	margin:0 6px;
+	}
+
+
+/* Board Stats
+----------------------------------------------------------------*/
+
+#brdstats .conl {
+	float: left;
+	}
+
+#brdstats .conr {
+	float: right;
+	text-align: right;
+	}
+
+#onlinelist dd, #onlinelist dt {
+	display: inline;
+	}
+
+
+/*****************************************************************
+5. MAIN TABLES
+*****************************************************************/
+
+.pun table {
+	width: 100%;
+	border-collapse: collapse;
+	border-spacing: 0;
+	empty-cells: show;
+	}
+
+.pun .blocktable table {
+	table-layout: fixed;
+	}
+
+.pun td, .pun th {
+	padding: 4px 6px;
+	line-height: 1.273em;
+	text-align: left;
+	font-weight: normal;
+	}
+
+.pun td {
+	border-style: solid none none solid;
+	border-width: 1px;
+	}
+
+.pun .tcl {
+	border-left: 0;
+	width: auto;
+	}
+
+.pun .tc2, .pun .tc3, .pun .tcmod {
+	width: 10%;
+	text-align: center;
+	padding: 4px 0;
+	}
+
+.pun .tcr {
+	width: 30%;
+	}
+
+.pun .tcl h3 {
+	font-size: 1.091em;
+	font-weight: bold;
+	}
+
+.pun .tcl h3 span.newtext {
+	font-size: 0.917em;
+	}
+
+.pun .tcl span.newtext, .pun .tcl span.pagestext {
+	white-space: nowrap;
+	font-weight: normal;
+	}
+
+.pun td span.byuser {
+	white-space: nowrap;
+	}
+
+.pun .tcl p {
+	padding: 5px 0 0 0
+	}
+
+#punsearch #vf .tc2 {
+	width: 18%;
+	text-align: left;
+	padding: 4px 6px;
+	}
+
+#users1 .tcr {
+	width: 25%
+	}
+
+#users1 .tc2 {
+	width: 25%;
+	text-align: left;
+	padding: 4px 6px;
+	}
+
+#debug .tcl {
+	width: 10%
+	}
+
+#debug .tcr {
+	width: 90%;
+	white-space: normal
+	}
+
+#punindex .tcr .byuser {
+	display: block
+	}
+
+.pun .blocktable .tclcon {
+	padding: 0 11px 0 12px;
+	overflow: hidden;
+	height: 1%;
+	min-height: 1px;
+	position: relative;
+	}
+
+.pun .blocktable .tclcon div {
+	width: 100%;
+	overflow: hidden;
+	}
+
+.pun .icon {
+	border-width: 7px;
+	border-style: solid;
+	height: 0;
+	width: 0;
+	overflow: hidden;
+	float: left;
+	}
+
+.pun .icon div {
+	position: absolute;
+	left: -9999em;
+	text-indent: -9999em;
+	height: 0;
+	}
+
+.pun .iposted .ipost {
+	position: absolute;
+	left: 0;
+	font-weight: bold;
+	width: 8px;
+	padding-left: 4px;
+	text-align: center;
+	top: 0;
+	}
+
+/*****************************************************************
+6. MAIN FORMS
+*****************************************************************/
+
+.pun .blockform form, .pun .fakeform {
+	PADDING: 20px 20px 15px 20px
+	}
+
+.pun .forminfo {
+	margin-bottom: 12px;
+	padding: 9px 10px;
+	border-style: solid;
+	border-width: 1px;
+	}
+
+.pun .forminfo h3 {
+	font-weight: bold;
+	}
+
+.pun .inform {
+	padding-bottom: 12px
+	}
+
+.pun fieldset {
+	padding: 0px 12px 0px 12px;
+	border-style: solid;
+	border-width: 1px
+	}
+
+.pun legend {
+	padding: 0px 6px
+	}
+
+.pun .infldset {
+	padding: 9px 0px 12px 0
+	}
+
+.pun label {
+	display: block;
+	padding: 3px 0
+	}
+
+.pun label.conl {
+	float: left;
+	overflow: visible;
+	margin-right: 10px
+	}
+
+.pun select {
+	padding-top: 1px;
+	padding-bottom: 1px;
+	}
+
+.pun fieldset .rbox {
+	}
+
+.pun fieldset .rbox br {
+	display: none;
+	}
+
+.pun fieldset .rbox label {
+	padding: 3px 0 3px 25px;
+	position: relative;
+	vertical-align: middle;
+	}
+
+.pun fieldset .rbox input {
+	margin: 0 9px 0 -25px;
+	padding: 0;
+	width: 16px;
+	position: relative;
+	vertical-align: middle;
+	}
+
+.pun .txtarea {
+	width: 75%
+	}
+
+.pun .txtarea textarea, .pun input.longinput {
+	width: 100%
+	}
+
+.pun .bblinks {
+	padding-bottom: 10px;
+	padding-left: 4px
+	}
+
+.pun .bblinks li {
+	display: inline;
+	padding-right: 20px
+	}
+
+.pun .blockform .buttons {
+	padding-left: 12px;
+	}
+
+.pun .blockform .buttons input {
+	margin-right: 8px;
+	}
+
+#posterror ul {
+	list-style: square;
+	padding: 3px 0 3px 24px;
+	}
+
+.pun .deletemsg {
+	border-style: solid;
+	border-width: 1px;
+	padding: 6px 15px;
+	}
+
+.pun p.actions span {
+	margin-right: 12px;
+	}
+
+/*****************************************************************
+7. PROFILES AND ADMIN
+*****************************************************************/
+
+.pun .block2col {
+	padding-bottom: 1px
+	}
+
+.pun .block2col .blockform, .pun .block2col .block {
+	margin-left: 17em
+	}
+
+.pun .blockmenu {
+	float:left;
+	width: 16em
+	}
+
+.pun .blockmenu li {
+	padding: 3px 0;
+	font-weight: bold;
+	}
+
+.pun .blockmenu a:link, .pun .blockmenu a:visited {
+	text-decoration: none
+	}
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active {
+	text-decoration: underline
+	}
+
+#viewprofile dl {
+	float: left;
+	width: 100%;
+	overflow: hidden
+	}
+
+#viewprofile dd {
+	margin-left: 14em;
+	padding: 3px;
+	}
+
+#viewprofile dt {
+	float: left;
+	width: 13em;
+	margin: 3px 0;
+	}
+
+#profileavatar img {
+	float: right;
+	margin-left: 1em
+	}
+
+/*****************************************************************
+8. MAIN POSTS
+*****************************************************************/
+
+.pun .blockpost h2 a:link, .pun .blockpost h2 a:visited {
+	text-decoration: none;
+	}
+
+.pun .blockpost h2 a:hover, .pun .blockpost h2 a:active {
+	text-decoration: underline;
+	}
+
+.pun .blockpost h2 .conr {
+	float: right;
+	text-align: right;
+	}
+
+#punsearch .blockpost h2 span {
+	white-space: nowrap;
+	}
+
+.pun .blockpost .box {
+	overflow: hidden;
+	}
+
+.pun .postleft, .pun .postfootleft {
+	float:left;
+	width: 18em;
+	position: relative;
+	overflow: hidden;
+	}
+
+.pun .postleft dl {
+	padding: 0.75em 6px;
+	}
+
+.pun .postleft .usercontacts, .pun .postleft .icon {
+	margin-top: 6px
+	}
+
+.pun .postleft .postavatar, .pun .postleft .usertitle {
+	margin-bottom: 6px;
+	display: block;
+	}
+
+.pun .blockpost dt {
+	font-size: 1.091em;
+	font-weight: bold;
+	}
+
+.pun .blockpost dt a:link, .pun .blockpost dt a:visited {
+	text-decoration: none;
+	}
+
+.pun .blockpost dt a:hover, .pun .blockpost dt a:active {
+	text-decoration: underline;
+	}
+
+.pun .postright, .pun .postfootright {
+	border-left-width: 18em;
+	border-left-style: solid
+	}
+
+#postpreview .postright {
+	border-left: 0
+	}
+
+.pun .postright {
+	padding: 0 6px;
+	}
+
+.pun .postfootright, .pun .multidelete {
+	text-align: right
+	}
+
+.pun .postmsg {
+	width:98%;
+	overflow: hidden;
+	padding-bottom: 6px;
+	}
+
+.pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
+.pun .postfootleft p {
+	padding: 6px 6px 6px 6px;
+	}
+
+.pun .postfootright li {
+	display: inline;
+	margin-left: 12px;
+	}
+
+.pun .postfootright a:link, .pun .postfootright a:visited {
+	text-decoration: none
+	}
+
+.pun .postfootright a:hover, .pun .postfootright a:active {
+	text-decoration: underline
+	}
+
+.pun .codebox {
+	border-style: solid;
+	border-width: 1px;
+	margin: 0.75em 1em;
+	padding: 0;
+	}
+
+.pun .quotebox {
+	border-style: solid;
+	border-width: 1px;
+	margin: 0.75em 1em;
+	padding: 0 0.75em;
+	}
+
+.pun .quotebox cite {
+	display: block;
+	padding: 0.75em 0 0 0;
+	}
+
+.pun .quotebox blockquote {
+	width: 100%;
+	overflow: hidden
+	}
+
+.pun .codebox pre {
+	overflow: auto;
+	width: 100%;
+	overflow-y:hidden
+	}
+
+* html .pun .codebox pre {
+	padding-bottom: 10px;
+	}
+
+*+html .pun .codebox pre {
+	padding-bottom: 10px
+	}
+
+.pun .codebox pre code {
+	display: block;
+	padding: 0.75em;
+	}
+
+.pun .codebox pre.vscroll {
+	height: 32em;
+	overflow: auto;
+	overflow-y: auto
+	}
+
+.pun .postmsg img {
+	vertical-align: bottom;
+	}
+
+.pun .postsignature hr {
+	margin-left: 0px;
+	width: 200px;
+	text-align: left;
+	height: 1px;
+	border:none
+	}
+
+.pun .postmsg .postimg img {
+	max-width: 98%;
+	vertical-align: middle;
+}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+	border-style: solid;
+	border-width: 2px;
+}
+
+.pun .blockpost label {
+	padding: 3px 6px;
+	border-style: solid;
+	border-width: 1px;
+	vertical-align: middle;
+	display: inline-block;
+	}
+
+.pun .blockpost label * {
+	vertical-align: middle;
+	margin: 0;
+	padding: 0;
+	}
+
+/****************************************************************/
+/* 9. HELP FILES AND MISC. */
+/****************************************************************/
+
+#punhelp h2 {
+	margin-top: 12px
+	}
+
+#punhelp div.box {
+	padding: 10px
+	}
+
+#debugtime {
+	margin-top: -12px;
+	text-align: center;
+	}
+
+/*****************************************************************
+COLOUR SCHEME
+*****************************************************************/
+
+/* Background / Text
+----------------------------------------------------------------*/
+
+body {
+	background: #fff;
+	color: #333
+	}
+
+.pun {
+	color: #333
+	}
+
+.pun .box, #adminconsole fieldset th {
+	background-color: #f1f1f1
+	}
+
+.pun td.tc2, .pun td.tc3, .pun td.tcmod, #postpreview, #viewprofile dd, .pun .forminfo,
+#brdfooter #modcontrols, #adminconsole fieldset td, .pun .blockmenu .box, #adstats dd {
+	background-color: #dedfdf
+	}
+
+.pun h2, #brdmenu {
+	background-color: #6c8a3f;
+	color: #fff
+	}
+
+.pun th {
+	background-color: #d1d1d1
+	}
+
+.pun legend {
+	color: #6c8a3f
+	}
+
+.pun .blockmenu li.isactive a, #posterror li strong {
+	color: #333
+	}
+
+.pun .usercontent * {
+	background: transparent;
+	color: #333
+	}
+
+/* posts
+----------------------------------------------------------------*/
+
+.pun .blockpost .box, .pun .postright, .pun .postfootright, .pun .deletemsg {
+	background-color: #dedfdf
+	}
+
+.pun .postright, .pun .postfootright {
+	border-left-color: #f1f1f1
+	}
+
+.pun .postleft, .pun .postfootleft, .pun .blockpost label, .pun .codebox, .pun .quotebox {
+	background-color: #f1f1f1
+	}
+
+#punhelp .codebox, #punhelp .quotebox {
+	background-color: #f9f9f9;
+	}
+
+.pun .blockpost h2 {
+	background-color: #7ea34b
+	}
+
+.pun .blockpost h2 span.conr {
+	color: #b7d094
+	}
+
+.pun .postmsg ins, #punhelp samp ins {
+	background-color: #ff0;
+	}
+
+.pun hr {
+	background-color: #333;
+	color: #333
+	}
+
+/* Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+	border-color: #6c8a3f
+	}
+
+.pun td, #brdfooter #modcontrols {
+	border-color: #cedeb9
+	}
+
+.pun th, .pun fieldset {
+	border-color: #d1d1d1
+	}
+
+#adminconsole td, #adminconsole th {
+	border-color: #f1f1f1
+	}
+
+.pun .quotebox, .pun .codebox, .pun .forminfo,
+.pun .blockpost label, .pun .deletemsg {
+	border-color: #aca899 #fff #fff #aca899
+	}
+
+/* Links
+----------------------------------------------------------------*/
+
+.pun a:link, .pun a:visited {
+	color: #638137
+	}
+
+.pun a:hover, .pun a:active, .pun a:focus {
+	color: #8eb653
+	}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+	border-color: #638137;
+}
+
+.pun .postmsg .postimg a:hover img, .pun .postmsg .postimg a:active img, .pun .postmsg .postimg a:focus img {
+	border-color: #8eb653;
+}
+
+.pun h2 a:link, .pun h2 a:visited,
+#brdmenu a:link, #brdmenu a:visited {
+	color: #fff
+	}
+
+.pun h2 a:hover, .pun h2 a:active,
+#brdmenu a:hover, #brdmenu a:active {
+	color: #fff
+	}
+
+.pun .postreport a:link, .pun .postreport a:visited,
+.pun .iclosed td.tcl a:link, .pun .iclosed td.tcl a:visited {
+	color: #888
+	}
+
+.pun .postreport a:hover, .pun .postreport a:active,
+.pun .iclosed td.tcl a:hover, .pun .iclosed td.tcl a:active {
+	color: #aaa
+	}
+
+.pun .maintenancelink a:link, .pun .maintenancelink a:visited {
+	color: #b42000
+	}
+
+.pun .maintenancelink a:hover, .pun .maintenancelink a:active {
+	color: #b42000
+	}
+
+/* Status Indicators
+----------------------------------------------------------------*/
+
+.pun .icon {
+	border-color: #e6e6e6 #dedede #dadada #e2e2e2
+	}
+
+.pun .iredirect .icon {
+	border-color: #f1f1f1 #f1f1f1 #f1f1f1 #f1f1f1
+	}
+
+.pun .inew .icon {
+	border-color: #8bb453 #7a9e48 #709142 #799c47
+	}
diff --git a/style/Mercury.css b/style/Mercury.css
new file mode 100644
index 0000000..708bde6
--- /dev/null
+++ b/style/Mercury.css
@@ -0,0 +1,1086 @@
+/*****************************************************************
+1. INITIAL SETTINGS
+*****************************************************************/
+
+/* Limited Reset
+----------------------------------------------------------------*/
+
+.pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
+.pun h4, .pun h5, .pun pre, .pun blockquote, .pun ul, .pun ol, .pun li, .pun dl,
+.pun dt, .pun dd, .pun th, .pun td, .pun fieldset, .pun img, .pun abbr, .pun cite {
+	margin: 0;
+	padding: 0;
+	border: 0;
+	}
+
+.pun ul, .pun ol {
+	list-style: none
+	}
+
+
+/* Structural Settings
+----------------------------------------------------------------*/
+
+.pun .clearer, .pun .nosize {
+	height: 0;
+	width: 0;
+	line-height: 0;
+	font-size: 0;
+	overflow: hidden
+	}
+
+.pun .clearer, .pun .clearb {
+	clear: both
+	}
+
+.pun .nosize {
+	position: absolute;
+	left: -9999em;
+	text-indent: -9999em;
+	width: 0;
+	}
+
+* html .inbox, * html .inform, * html .pun, * html .tclcon, * html .codebox {
+	height: 1px
+	}
+
+.pun, .pun .inbox, .pun .inform, .pun .tclcon, .pun .codebox {
+	min-height: 1px
+	}
+
+
+/* Hidden Elements
+----------------------------------------------------------------*/
+
+#brdfooter h2, #brdstats h2, #brdstats .conl dt, #brdstats .conr dt,
+#modcontrols dt, #searchlinks dt, div.postright h3, span.closedtext,
+.pun .required strong span {
+	position: absolute;
+	display: block;
+	overflow: hidden;
+	width: 0;
+	left: -9999em;
+	text-indent: -9999em;
+	}
+
+/*****************************************************************
+2. TEXT & CONTENT
+*****************************************************************/
+
+/* Text Defaults
+----------------------------------------------------------------*/
+
+.pun {
+	font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
+	}
+
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp {
+	font-size: 1em;
+	font-family: verdana, helvetica, arial, sans-serif;
+	}
+
+.pun pre, .pun code {
+	font-size: 1.182em;
+	font-family: consolas, monaco, "bitstream vera sans mono", "courier new", courier, monospace
+	}
+
+.pun pre code {
+	font-size: 1em;
+	}
+
+.pun strong {
+	font-weight: bold;
+	}
+
+.pun em {
+	font-style: italic;
+	}
+
+
+/* Content Defaults
+----------------------------------------------------------------*/
+
+.pun p, .pun ul, .pun ol, .pun dl {
+	font-size: 1em;
+	padding: 3px 0;
+	}
+
+.pun h2 {
+	font-size: 1em;
+	font-weight: normal;
+	padding: 4px 7px;
+	}
+
+.pun h3 {
+	font-size: 1.091em;
+	padding: 3px 0;
+	}
+
+.pun table p, .pun table h3 {
+	padding: 0;
+	}
+
+.pun span.warntext, .pun p.warntext {
+	font-weight: bold
+	}
+
+/* User Content (Announcements, Rules, Posts)
+----------------------------------------------------------------*/
+
+.pun .usercontent p, .pun .postmsg p {
+	padding: 0.75em 0
+	}
+
+.pun .usercontent ul, .pun .postmsg ul {
+	padding: 0.75em 1em 0.75em 2.5em;
+	list-style: disc
+	}
+
+.pun .usercontent ol, .pun .postmsg ol {
+	padding: 0.75em 1em 0.75em 2.5em;
+	list-style: decimal
+	}
+
+.pun .usercontent ol.alpha, .pun .postmsg ol.alpha {
+	list-style: lower-alpha
+	}
+
+.pun .usercontent li ol, .pun .usercontent li ul, .pun .postmsg li ol, .pun .postmsg li ul {
+	padding: 0.25em 1em 0.75em 2.5em
+	}
+
+.pun .usercontent li p, .pun .postmsg li p {
+	padding: 0
+	}
+
+.pun .usercontent h1 {
+	font-size: 1.4em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h2 {
+	font-size: 1.2em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h3 {
+	font-size: 1.1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h4, .pun .usercontent h5, .pun .usercontent h6 {
+	font-size: 1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .quotebox cite {
+	font-weight: bold;
+	font-style: normal;
+	padding: 0.75em 0.75em 0 0.75em
+	}
+
+.pun span.bbu {
+	text-decoration: underline
+	}
+
+.pun span.bbs, .pun del {
+	text-decoration: line-through;
+	}
+
+.pun .postmsg ins, #punhelp samp ins {
+	text-decoration: none;
+	}
+
+.pun div.postmsg h5, #punhelp h5 {
+	font-size: 1.1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0;
+	}
+
+
+/*****************************************************************
+3. COMMON STYLES
+*****************************************************************/
+
+/* Page Layout
+----------------------------------------------------------------*/
+
+.pun {
+	margin: 12px 20px
+	}
+
+#punredirect, #punmaint, #puninstall, #pundb_update {
+	margin: 50px 20% 12px 20%
+	}
+
+
+/* Vertical Element Spacing
+----------------------------------------------------------------*/
+
+#brdheader {
+	margin: 0 0 12px 0;
+	}
+
+#announce, #brdstats {
+	margin: 12px 0 12px 0;
+	}
+
+.pun .blocktable, .pun .block, .pun .blockform, .pun .block2col, #postreview {
+	margin-bottom: 12px
+	}
+
+#punindex .blocktable, .pun .blockpost {
+	margin-bottom: 6px
+	}
+
+#postreview .blockpost {
+	margin-bottom: -1px;
+	}
+
+.pun .block2col .blockform, .pun .block2col .block {
+	margin-bottom: 0px
+	}
+
+.pun .linkst, .pun .linksb {
+	margin-top: -12px
+	}
+
+.pun .postlinksb {
+	margin-top: -6px
+	}
+
+
+/* External Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+	border-style: solid;
+	border-width: 1px;
+	}
+
+#brdheader .box {
+	border-top-width: 4px;
+	}
+
+/* Default Internal Spacing
+----------------------------------------------------------------*/
+
+.pun .block .inbox, .pun .blockmenu .inbox {
+	padding: 3px 6px
+	}
+
+/*****************************************************************
+4. COMMON BOARD ELEMENTS
+*****************************************************************/
+
+/* Board Header
+----------------------------------------------------------------*/
+
+#brdtitle h1 {
+	font-size: 1.454em;
+	font-weight: bold;
+	line-height: 1em;
+	padding: 3px 0 0 0;
+	}
+
+#brddesc {
+	padding: 3px 0;
+	}
+
+#brddesc * {
+	padding-top: 0;
+	padding-bottom: 0;
+	}
+
+#brdmenu li {
+	display: inline;
+	margin-right: 12px;
+	}
+
+#brdmenu a:link, #brdmenu a:visited {
+	text-decoration: none
+	}
+
+#brdmenu a:hover, #brdmenu a:active {
+	text-decoration: underline
+	}
+
+#brdwelcome .conl {
+	float: left;
+	}
+
+#brdwelcome .conr {
+	float: right;
+	text-align: right;
+	}
+
+/* Breadcrumbs and Post Links
+----------------------------------------------------------------*/
+
+.pun .linkst {
+	padding: 8px 6px 3px 6px
+	}
+
+.pun .linksb, .pun .postlinksb {
+	padding: 3px 6px 8px 6px
+	}
+
+.pun .crumbs {
+	clear: both;
+	width: 100%;
+	overflow: hidden;
+	}
+
+.pun .crumbs li {
+	display: inline;
+	white-space: nowrap;
+	font-weight: bold;
+	}
+
+.pun .pagelink {
+	float: left;
+	white-space: nowrap;
+	}
+
+.pun .postlink {
+	font-weight: bold;
+	white-space: nowrap;
+	}
+
+.pun .postlink, .pun .modbuttons {
+	float: right;
+	text-align: right;
+	}
+
+.pun .modbuttons {
+	padding: 1px 0;
+	white-space: nowrap;
+	}
+
+.pun .modbuttons input {
+	margin-left: 6px;
+	}
+
+.pun .postlink a:link, .pun .postlink a:visited {
+	text-decoration: none
+	}
+
+.pun .postlink a:hover, .pun .postlink a:active {
+	text-decoration: underline;
+	}
+
+
+/* Board Footer
+----------------------------------------------------------------*/
+
+#brdfooter .conl {
+	float: left;
+	}
+
+#brdfooter .conr {
+	float: right;
+	text-align: right;
+	}
+
+#brdfooter #modcontrols {
+	border-bottom-style: solid;
+	border-bottom-width: 1px;
+	text-align: center;
+	}
+
+#brdfooter #modcontrols dd {
+	display: inline;
+	margin:0 6px;
+	}
+
+
+/* Board Stats
+----------------------------------------------------------------*/
+
+#brdstats .conl {
+	float: left;
+	}
+
+#brdstats .conr {
+	float: right;
+	text-align: right;
+	}
+
+#onlinelist dd, #onlinelist dt {
+	display: inline;
+	}
+
+
+/*****************************************************************
+5. MAIN TABLES
+*****************************************************************/
+
+.pun table {
+	width: 100%;
+	border-collapse: collapse;
+	border-spacing: 0;
+	empty-cells: show;
+	}
+
+.pun .blocktable table {
+	table-layout: fixed;
+	}
+
+.pun td, .pun th {
+	padding: 4px 6px;
+	line-height: 1.273em;
+	text-align: left;
+	font-weight: normal;
+	}
+
+.pun td {
+	border-style: solid none none solid;
+	border-width: 1px;
+	}
+
+.pun .tcl {
+	border-left: 0;
+	width: auto;
+	}
+
+.pun .tc2, .pun .tc3, .pun .tcmod {
+	width: 10%;
+	text-align: center;
+	padding: 4px 0;
+	}
+
+.pun .tcr {
+	width: 30%;
+	}
+
+.pun .tcl h3 {
+	font-size: 1.091em;
+	font-weight: bold;
+	}
+
+.pun .tcl h3 span.newtext {
+	font-size: 0.917em;
+	}
+
+.pun .tcl span.newtext, .pun .tcl span.pagestext {
+	white-space: nowrap;
+	font-weight: normal;
+	}
+
+.pun td span.byuser {
+	white-space: nowrap;
+	}
+
+.pun .tcl p {
+	padding: 5px 0 0 0
+	}
+
+#punsearch #vf .tc2 {
+	width: 18%;
+	text-align: left;
+	padding: 4px 6px;
+	}
+
+#users1 .tcr {
+	width: 25%
+	}
+
+#users1 .tc2 {
+	width: 25%;
+	text-align: left;
+	padding: 4px 6px;
+	}
+
+#debug .tcl {
+	width: 10%
+	}
+
+#debug .tcr {
+	width: 90%;
+	white-space: normal
+	}
+
+#punindex .tcr .byuser {
+	display: block
+	}
+
+.pun .blocktable .tclcon {
+	padding: 0 11px 0 12px;
+	overflow: hidden;
+	height: 1%;
+	min-height: 1px;
+	position: relative;
+	}
+
+.pun .blocktable .tclcon div {
+	width: 100%;
+	overflow: hidden;
+	}
+
+.pun .icon {
+	border-width: 7px;
+	border-style: solid;
+	height: 0;
+	width: 0;
+	overflow: hidden;
+	float: left;
+	}
+
+.pun .icon div {
+	position: absolute;
+	left: -9999em;
+	text-indent: -9999em;
+	height: 0;
+	}
+
+.pun .iposted .ipost {
+	position: absolute;
+	left: 0;
+	font-weight: bold;
+	width: 8px;
+	padding-left: 4px;
+	text-align: center;
+	top: 0;
+	}
+
+/*****************************************************************
+6. MAIN FORMS
+*****************************************************************/
+
+.pun .blockform form, .pun .fakeform {
+	PADDING: 20px 20px 15px 20px
+	}
+
+.pun .forminfo {
+	margin-bottom: 12px;
+	padding: 9px 10px;
+	border-style: solid;
+	border-width: 1px;
+	}
+
+.pun .forminfo h3 {
+	font-weight: bold;
+	}
+
+.pun .inform {
+	padding-bottom: 12px
+	}
+
+.pun fieldset {
+	padding: 0px 12px 0px 12px;
+	border-style: solid;
+	border-width: 1px
+	}
+
+.pun legend {
+	padding: 0px 6px
+	}
+
+.pun .infldset {
+	padding: 9px 0px 12px 0
+	}
+
+.pun label {
+	display: block;
+	padding: 3px 0
+	}
+
+.pun label.conl {
+	float: left;
+	overflow: visible;
+	margin-right: 10px
+	}
+
+.pun select {
+	padding-top: 1px;
+	padding-bottom: 1px;
+	}
+
+.pun fieldset .rbox {
+	}
+
+.pun fieldset .rbox br {
+	display: none;
+	}
+
+.pun fieldset .rbox label {
+	padding: 3px 0 3px 25px;
+	position: relative;
+	vertical-align: middle;
+	}
+
+.pun fieldset .rbox input {
+	margin: 0 9px 0 -25px;
+	padding: 0;
+	width: 16px;
+	position: relative;
+	vertical-align: middle;
+	}
+
+.pun .txtarea {
+	width: 75%
+	}
+
+.pun .txtarea textarea, .pun input.longinput {
+	width: 100%
+	}
+
+.pun .bblinks {
+	padding-bottom: 10px;
+	padding-left: 4px
+	}
+
+.pun .bblinks li {
+	display: inline;
+	padding-right: 20px
+	}
+
+.pun .blockform .buttons {
+	padding-left: 12px;
+	}
+
+.pun .blockform .buttons input {
+	margin-right: 8px;
+	}
+
+#posterror ul {
+	list-style: square;
+	padding: 3px 0 3px 24px;
+	}
+
+.pun .deletemsg {
+	border-style: solid;
+	border-width: 1px;
+	padding: 6px 15px;
+	}
+
+.pun p.actions span {
+	margin-right: 12px;
+	}
+
+/*****************************************************************
+7. PROFILES AND ADMIN
+*****************************************************************/
+
+.pun .block2col {
+	padding-bottom: 1px
+	}
+
+.pun .block2col .blockform, .pun .block2col .block {
+	margin-left: 17em
+	}
+
+.pun .blockmenu {
+	float:left;
+	width: 16em
+	}
+
+.pun .blockmenu li {
+	padding: 3px 0;
+	font-weight: bold;
+	}
+
+.pun .blockmenu a:link, .pun .blockmenu a:visited {
+	text-decoration: none
+	}
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active {
+	text-decoration: underline
+	}
+
+#viewprofile dl {
+	float: left;
+	width: 100%;
+	overflow: hidden
+	}
+
+#viewprofile dd {
+	margin-left: 14em;
+	padding: 3px;
+	}
+
+#viewprofile dt {
+	float: left;
+	width: 13em;
+	margin: 3px 0;
+	}
+
+#profileavatar img {
+	float: right;
+	margin-left: 1em
+	}
+
+/*****************************************************************
+8. MAIN POSTS
+*****************************************************************/
+
+.pun .blockpost h2 a:link, .pun .blockpost h2 a:visited {
+	text-decoration: none;
+	}
+
+.pun .blockpost h2 a:hover, .pun .blockpost h2 a:active {
+	text-decoration: underline;
+	}
+
+.pun .blockpost h2 .conr {
+	float: right;
+	text-align: right;
+	}
+
+#punsearch .blockpost h2 span {
+	white-space: nowrap;
+	}
+
+.pun .blockpost .box {
+	overflow: hidden;
+	}
+
+.pun .postleft, .pun .postfootleft {
+	float:left;
+	width: 18em;
+	position: relative;
+	overflow: hidden;
+	}
+
+.pun .postleft dl {
+	padding: 0.75em 6px;
+	}
+
+.pun .postleft .usercontacts, .pun .postleft .icon {
+	margin-top: 6px
+	}
+
+.pun .postleft .postavatar, .pun .postleft .usertitle {
+	margin-bottom: 6px;
+	display: block;
+	}
+
+.pun .blockpost dt {
+	font-size: 1.091em;
+	font-weight: bold;
+	}
+
+.pun .blockpost dt a:link, .pun .blockpost dt a:visited {
+	text-decoration: none;
+	}
+
+.pun .blockpost dt a:hover, .pun .blockpost dt a:active {
+	text-decoration: underline;
+	}
+
+.pun .postright, .pun .postfootright {
+	border-left-width: 18em;
+	border-left-style: solid
+	}
+
+#postpreview .postright {
+	border-left: 0
+	}
+
+.pun .postright {
+	padding: 0 6px;
+	}
+
+.pun .postfootright, .pun .multidelete {
+	text-align: right
+	}
+
+.pun .postmsg {
+	width:98%;
+	overflow: hidden;
+	padding-bottom: 6px;
+	}
+
+.pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
+.pun .postfootleft p {
+	padding: 6px 6px 6px 6px;
+	}
+
+.pun .postfootright li {
+	display: inline;
+	margin-left: 12px;
+	}
+
+.pun .postfootright a:link, .pun .postfootright a:visited {
+	text-decoration: none
+	}
+
+.pun .postfootright a:hover, .pun .postfootright a:active {
+	text-decoration: underline
+	}
+
+.pun .codebox {
+	border-style: solid;
+	border-width: 1px;
+	margin: 0.75em 1em;
+	padding: 0;
+	}
+
+.pun .quotebox {
+	border-style: solid;
+	border-width: 1px;
+	margin: 0.75em 1em;
+	padding: 0 0.75em;
+	}
+
+.pun .quotebox cite {
+	display: block;
+	padding: 0.75em 0 0 0;
+	}
+
+.pun .quotebox blockquote {
+	width: 100%;
+	overflow: hidden
+	}
+
+.pun .codebox pre {
+	overflow: auto;
+	width: 100%;
+	overflow-y:hidden
+	}
+
+* html .pun .codebox pre {
+	padding-bottom: 10px;
+	}
+
+*+html .pun .codebox pre {
+	padding-bottom: 10px
+	}
+
+.pun .codebox pre code {
+	display: block;
+	padding: 0.75em;
+	}
+
+.pun .codebox pre.vscroll {
+	height: 32em;
+	overflow: auto;
+	overflow-y: auto
+	}
+
+.pun .postmsg img {
+	vertical-align: bottom;
+	}
+
+.pun .postsignature hr {
+	margin-left: 0px;
+	width: 200px;
+	text-align: left;
+	height: 1px;
+	border:none
+	}
+
+.pun .postmsg .postimg img {
+	max-width: 98%;
+	vertical-align: middle;
+}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+	border-style: solid;
+	border-width: 2px;
+}
+
+.pun .blockpost label {
+	padding: 3px 6px;
+	border-style: solid;
+	border-width: 1px;
+	vertical-align: middle;
+	display: inline-block;
+	}
+
+.pun .blockpost label * {
+	vertical-align: middle;
+	margin: 0;
+	padding: 0;
+	}
+
+/****************************************************************/
+/* 9. HELP FILES AND MISC. */
+/****************************************************************/
+
+#punhelp h2 {
+	margin-top: 12px
+	}
+
+#punhelp div.box {
+	padding: 10px
+	}
+
+#debugtime {
+	margin-top: -12px;
+	text-align: center;
+	}
+
+/*****************************************************************
+COLOUR SCHEME
+*****************************************************************/
+
+/* Background / Text
+----------------------------------------------------------------*/
+
+body {
+	background: #2a2a2a;
+	color: #d4d4d4
+	}
+
+.pun {
+	color: #d4d4d4
+	}
+
+.pun .box, #adminconsole fieldset th {
+	background-color: #383838
+	}
+
+.pun td.tc2, .pun td.tc3, .pun td.tcmod, #postpreview, #viewprofile dd, .pun .forminfo,
+#brdfooter #modcontrols, #adminconsole fieldset td, .pun .blockmenu .box, #adstats dd {
+	background-color: #424242
+	}
+
+.pun h2, #brdmenu {
+	background-color: #565656;
+	color: #d4d4d4
+	}
+
+.pun th {
+	background-color: #484848
+	}
+
+.pun legend {
+	color: #f6b620
+	}
+
+.pun .blockmenu li.isactive a, #posterror li strong {
+	color: #d4d4d4
+	}
+
+.pun .usercontent * {
+	background: transparent;
+	color: #d4d4d4
+	}
+
+.pun textarea, .pun input, .pun select {
+	background-color: #2a2a2a;
+	color: #d4d4d4
+	}
+
+/* Posts
+----------------------------------------------------------------*/
+
+.pun .blockpost .box, .pun .postright, .pun .postfootright, .pun .deletemsg {
+	background-color: #383838
+	}
+
+.pun .postright, .pun .postfootright {
+	border-left-color: #424242
+	}
+
+.pun .postleft, .pun .postfootleft, .pun .blockpost label, .pun .codebox, .pun .quotebox {
+	background-color: #424242
+	}
+
+.pun .blockpost h2 {
+	background-color: #565656
+	}
+
+.pun .blockpost h2 span.conr {
+	color: #a19e96
+	}
+
+.pun .postmsg ins, #punhelp samp ins {
+	background-color: #ff0;
+	}
+
+.pun hr {
+	background-color: #606060;
+	color: #606060
+	}
+
+/* Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+	border-color:#565656
+	}
+
+.pun td, #brdfooter #modcontrols {
+	border-color: #565656
+	}
+
+.pun th, .pun fieldset {
+	border-color: #484848
+	}
+
+#adminconsole td, #adminconsole th {
+	border-color: #383838
+	}
+
+.pun .quotebox, .pun .codebox, .pun .forminfo,
+.pun .blockpost label, .pun .deletemsg {
+	border-color: #565656
+	}
+
+/* Links
+----------------------------------------------------------------*/
+
+.pun a:link, .pun a:visited {
+	color: #f6b620
+	}
+
+.pun a:hover, .pun a:active, .pun a:focus {
+	color: #ffee40
+	}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+	border-color: #f6b620;
+}
+
+.pun .postmsg .postimg a:hover img, .pun .postmsg .postimg a:active img, .pun .postmsg .postimg a:focus img {
+	border-color: #ffee40;
+}
+
+.pun h2 a:link, .pun h2 a:visited,
+#brdmenu a:link, #brdmenu a:visited {
+	color: #d4d4d4
+	}
+
+.pun h2 a:hover, .pun h2 a:active,
+#brdmenu a:hover, #brdmenu a:active {
+	color: #d4d4d4
+	}
+
+.pun .postreport a:link, .pun .postreport a:visited,
+.pun .iclosed td.tcl a:link, .pun .iclosed td.tcl a:visited {
+	color: #888
+	}
+
+.pun .postreport a:hover, .pun .postreport a:active,
+.pun .iclosed td.tcl a:hover, .pun .iclosed td.tcl a:active {
+	color: #aaa
+	}
+
+.pun .maintenancelink a:link, .pun .maintenancelink a:visited {
+	color: #ff4000
+	}
+
+.pun .maintenancelink a:hover, .pun .maintenancelink a:active {
+	color: #ff5010
+	}
+
+/* Status Indicators
+----------------------------------------------------------------*/
+
+.pun .icon {
+	border-color: #484848 #404040 #3c3c3c #444444
+	}
+
+.pun .iredirect .icon {
+	border-color: #383838 #383838 #383838 #383838
+	}
+
+.pun .inew .icon {
+	border-color: #f6b620 #ecae1f #d09a1b #e1a61d
+	}
diff --git a/style/Oxygen.css b/style/Oxygen.css
new file mode 100644
index 0000000..4d0fe79
--- /dev/null
+++ b/style/Oxygen.css
@@ -0,0 +1,1086 @@
+/*****************************************************************
+1. INITIAL SETTINGS
+*****************************************************************/
+
+/* Limited Reset
+----------------------------------------------------------------*/
+
+.pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
+.pun h4, .pun h5, .pun pre, .pun blockquote, .pun ul, .pun ol, .pun li, .pun dl,
+.pun dt, .pun dd, .pun th, .pun td, .pun fieldset, .pun img, .pun abbr, .pun cite {
+	margin: 0;
+	padding: 0;
+	border: 0;
+	}
+
+.pun ul, .pun ol {
+	list-style: none
+	}
+
+
+/* Structural Settings
+----------------------------------------------------------------*/
+
+.pun .clearer, .pun .nosize {
+	height: 0;
+	width: 0;
+	line-height: 0;
+	font-size: 0;
+	overflow: hidden
+	}
+
+.pun .clearer, .pun .clearb {
+	clear: both
+	}
+
+.pun .nosize {
+	position: absolute;
+	left: -9999em;
+	text-indent: -9999em;
+	width: 0;
+	}
+
+* html .inbox, * html .inform, * html .pun, * html .tclcon, * html .codebox {
+	height: 1px
+	}
+
+.pun, .pun .inbox, .pun .inform, .pun .tclcon, .pun .codebox {
+	min-height: 1px
+	}
+
+
+/* Hidden Elements
+----------------------------------------------------------------*/
+
+#brdfooter h2, #brdstats h2, #brdstats .conl dt, #brdstats .conr dt,
+#modcontrols dt, #searchlinks dt, div.postright h3, span.closedtext,
+.pun .required strong span {
+	position: absolute;
+	display: block;
+	overflow: hidden;
+	width: 0;
+	left: -9999em;
+	text-indent: -9999em;
+	}
+
+/*****************************************************************
+2. TEXT & CONTENT
+*****************************************************************/
+
+/* Text Defaults
+----------------------------------------------------------------*/
+
+.pun {
+	font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
+	}
+
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun small, .pun samp {
+	font-size: 1em;
+	font-family: verdana, helvetica, arial, sans-serif;
+	}
+
+.pun pre, .pun code {
+	font-size: 1.182em;
+	font-family: consolas, monaco, "bitstream vera sans mono", "courier new", courier, monospace
+	}
+
+.pun pre code {
+	font-size: 1em;
+	}
+
+.pun strong {
+	font-weight: bold;
+	}
+
+.pun em {
+	font-style: italic;
+	}
+
+
+/* Content Defaults
+----------------------------------------------------------------*/
+
+.pun p, .pun ul, .pun ol, .pun dl {
+	font-size: 1em;
+	padding: 3px 0;
+	}
+
+.pun h2 {
+	font-size: 1em;
+	font-weight: normal;
+	padding: 4px 7px;
+	}
+
+.pun h3 {
+	font-size: 1.091em;
+	padding: 3px 0;
+	}
+
+.pun table p, .pun table h3 {
+	padding: 0;
+	}
+
+.pun span.warntext, .pun p.warntext {
+	font-weight: bold
+	}
+
+
+/* User Content (Announcements, Rules, Posts)
+----------------------------------------------------------------*/
+
+.pun .usercontent p, .pun .postmsg p {
+	padding: 0.75em 0
+	}
+
+.pun .usercontent ul, .pun .postmsg ul {
+	padding: 0.75em 1em 0.75em 2.5em;
+	list-style: disc
+	}
+
+.pun .usercontent ol, .pun .postmsg ol {
+	padding: 0.75em 1em 0.75em 2.5em;
+	list-style: decimal
+	}
+
+.pun .usercontent ol.alpha, .pun .postmsg ol.alpha {
+	list-style: lower-alpha
+	}
+
+.pun .usercontent li ol, .pun .usercontent li ul, .pun .postmsg li ol, .pun .postmsg li ul {
+	padding: 0.25em 1em 0.75em 2.5em
+	}
+
+.pun .usercontent li p, .pun .postmsg li p {
+	padding: 0
+	}
+
+.pun .usercontent h1 {
+	font-size: 1.4em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h2 {
+	font-size: 1.2em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h3 {
+	font-size: 1.1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h4, .pun .usercontent h5, .pun .usercontent h6 {
+	font-size: 1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .quotebox cite {
+	font-weight: bold;
+	font-style: normal;
+	padding: 0.75em 0.75em 0 0.75em
+	}
+
+.pun span.bbu {
+	text-decoration: underline
+	}
+
+.pun span.bbs, .pun del {
+	text-decoration: line-through;
+	}
+
+.pun .postmsg ins, #punhelp samp ins {
+	text-decoration: none;
+	}
+
+.pun div.postmsg h5, #punhelp h5 {
+	font-size: 1.1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0;
+	}
+
+
+/*****************************************************************
+3. COMMON STYLES
+*****************************************************************/
+
+/* Page Layout
+----------------------------------------------------------------*/
+
+.pun {
+	margin: 12px 20px
+	}
+
+#punredirect, #punmaint, #puninstall, #pundb_update {
+	margin: 50px 20% 12px 20%
+	}
+
+
+/* Vertical Element Spacing
+----------------------------------------------------------------*/
+
+#brdheader {
+	margin: 0 0 12px 0;
+	}
+
+#announce, #brdstats {
+	margin: 12px 0 12px 0;
+	}
+
+.pun .blocktable, .pun .block, .pun .blockform, .pun .block2col, #postreview {
+	margin-bottom: 12px
+	}
+
+#punindex .blocktable, .pun .blockpost {
+	margin-bottom: 6px
+	}
+
+#postreview .blockpost {
+	margin-bottom: -1px;
+	}
+
+.pun .block2col .blockform, .pun .block2col .block {
+	margin-bottom: 0px
+	}
+
+.pun .linkst, .pun .linksb {
+	margin-top: -12px
+	}
+
+.pun .postlinksb {
+	margin-top: -6px
+	}
+
+
+/* External Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+	border-style: solid;
+	border-width: 1px;
+	}
+
+#brdheader .box {
+	border-top-width: 4px;
+	}
+
+/* Default Internal Spacing
+----------------------------------------------------------------*/
+
+.pun .block .inbox, .pun .blockmenu .inbox {
+	padding: 3px 6px
+	}
+
+/*****************************************************************
+4. COMMON BOARD ELEMENTS
+*****************************************************************/
+
+/* Board Header
+----------------------------------------------------------------*/
+
+#brdtitle h1 {
+	font-size: 1.454em;
+	font-weight: bold;
+	line-height: 1em;
+	padding: 3px 0 0 0;
+	}
+
+#brddesc {
+	padding: 3px 0;
+	}
+
+#brddesc * {
+	padding-top: 0;
+	padding-bottom: 0;
+	}
+
+#brdmenu li {
+	display: inline;
+	margin-right: 12px;
+	}
+
+#brdmenu a:link, #brdmenu a:visited {
+	text-decoration: none
+	}
+
+#brdmenu a:hover, #brdmenu a:active {
+	text-decoration: underline
+	}
+
+#brdwelcome .conl {
+	float: left;
+	}
+
+#brdwelcome .conr {
+	float: right;
+	text-align: right;
+	}
+
+/* Breadcrumbs and Post Links
+----------------------------------------------------------------*/
+
+.pun .linkst {
+	padding: 8px 6px 3px 6px
+	}
+
+.pun .linksb, .pun .postlinksb {
+	padding: 3px 6px 8px 6px
+	}
+
+.pun .crumbs {
+	clear: both;
+	width: 100%;
+	overflow: hidden;
+	}
+
+.pun .crumbs li {
+	display: inline;
+	white-space: nowrap;
+	font-weight: bold;
+	}
+
+.pun .pagelink {
+	float: left;
+	white-space: nowrap;
+	}
+
+.pun .postlink {
+	font-weight: bold;
+	white-space: nowrap;
+	}
+
+.pun .postlink, .pun .modbuttons {
+	float: right;
+	text-align: right;
+	}
+
+.pun .modbuttons {
+	padding: 1px 0;
+	white-space: nowrap;
+	}
+
+.pun .modbuttons input {
+	margin-left: 6px;
+	}
+
+.pun .postlink a:link, .pun .postlink a:visited {
+	text-decoration: none
+	}
+
+.pun .postlink a:hover, .pun .postlink a:active {
+	text-decoration: underline;
+	}
+
+
+/* Board Footer
+----------------------------------------------------------------*/
+
+#brdfooter .conl {
+	float: left;
+	}
+
+#brdfooter .conr {
+	float: right;
+	text-align: right;
+	}
+
+#brdfooter #modcontrols {
+	border-bottom-style: solid;
+	border-bottom-width: 1px;
+	text-align: center;
+	}
+
+#brdfooter #modcontrols dd {
+	display: inline;
+	margin:0 6px;
+	}
+
+
+/* Board Stats
+----------------------------------------------------------------*/
+
+#brdstats .conl {
+	float: left;
+	}
+
+#brdstats .conr {
+	float: right;
+	text-align: right;
+	}
+
+#onlinelist dd, #onlinelist dt {
+	display: inline;
+	}
+
+
+/*****************************************************************
+5. MAIN TABLES
+*****************************************************************/
+
+.pun table {
+	width: 100%;
+	border-collapse: collapse;
+	border-spacing: 0;
+	empty-cells: show;
+	}
+
+.pun .blocktable table {
+	table-layout: fixed;
+	}
+
+.pun td, .pun th {
+	padding: 4px 6px;
+	line-height: 1.273em;
+	text-align: left;
+	font-weight: normal;
+	}
+
+.pun td {
+	border-style: solid none none solid;
+	border-width: 1px;
+	}
+
+.pun .tcl {
+	border-left: 0;
+	width: auto;
+	}
+
+.pun .tc2, .pun .tc3, .pun .tcmod {
+	width: 10%;
+	text-align: center;
+	padding: 4px 0;
+	}
+
+.pun .tcr {
+	width: 30%;
+	}
+
+.pun .tcl h3 {
+	font-size: 1.091em;
+	font-weight: bold;
+	}
+
+.pun .tcl h3 span.newtext {
+	font-size: 0.917em;
+	}
+
+.pun .tcl span.newtext, .pun .tcl span.pagestext {
+	white-space: nowrap;
+	font-weight: normal;
+	}
+
+.pun td span.byuser {
+	white-space: nowrap;
+	}
+
+.pun .tcl p {
+	padding: 5px 0 0 0
+	}
+
+#punsearch #vf .tc2 {
+	width: 18%;
+	text-align: left;
+	padding: 4px 6px;
+	}
+
+#users1 .tcr {
+	width: 25%
+	}
+
+#users1 .tc2 {
+	width: 25%;
+	text-align: left;
+	padding: 4px 6px;
+	}
+
+#debug .tcl {
+	width: 10%
+	}
+
+#debug .tcr {
+	width: 90%;
+	white-space: normal
+	}
+
+#punindex .tcr .byuser {
+	display: block
+	}
+
+.pun .blocktable .tclcon {
+	padding: 0 11px 0 12px;
+	overflow: hidden;
+	height: 1%;
+	min-height: 1px;
+	position: relative;
+	}
+
+.pun .blocktable .tclcon div {
+	width: 100%;
+	overflow: hidden;
+	}
+
+.pun .icon {
+	border-width: 7px;
+	border-style: solid;
+	height: 0;
+	width: 0;
+	overflow: hidden;
+	float: left;
+	}
+
+.pun .icon div {
+	position: absolute;
+	left: -9999em;
+	text-indent: -9999em;
+	height: 0;
+	}
+
+.pun .iposted .ipost {
+	position: absolute;
+	left: 0;
+	font-weight: bold;
+	width: 8px;
+	padding-left: 4px;
+	text-align: center;
+	top: 0;
+	}
+
+/*****************************************************************
+6. MAIN FORMS
+*****************************************************************/
+
+.pun .blockform form, .pun .fakeform {
+	PADDING: 20px 20px 15px 20px
+	}
+
+.pun .forminfo {
+	margin-bottom: 12px;
+	padding: 9px 10px;
+	border-style: solid;
+	border-width: 1px;
+	}
+
+.pun .forminfo h3 {
+	font-weight: bold;
+	}
+
+.pun .inform {
+	padding-bottom: 12px
+	}
+
+.pun fieldset {
+	padding: 0px 12px 0px 12px;
+	border-style: solid;
+	border-width: 1px
+	}
+
+.pun legend {
+	padding: 0px 6px
+	}
+
+.pun .infldset {
+	padding: 9px 0px 12px 0
+	}
+
+.pun label {
+	display: block;
+	padding: 3px 0
+	}
+
+.pun label.conl {
+	float: left;
+	overflow: visible;
+	margin-right: 10px
+	}
+
+.pun select {
+	padding-top: 1px;
+	padding-bottom: 1px;
+	}
+
+.pun fieldset .rbox {
+	}
+
+.pun fieldset .rbox br {
+	display: none;
+	}
+
+.pun fieldset .rbox label {
+	padding: 3px 0 3px 25px;
+	position: relative;
+	vertical-align: middle;
+	}
+
+.pun fieldset .rbox input {
+	margin: 0 9px 0 -25px;
+	padding: 0;
+	width: 16px;
+	position: relative;
+	vertical-align: middle;
+	}
+
+.pun .txtarea {
+	width: 75%
+	}
+
+.pun .txtarea textarea, .pun input.longinput {
+	width: 100%
+	}
+
+.pun .bblinks {
+	padding-bottom: 10px;
+	padding-left: 4px
+	}
+
+.pun .bblinks li {
+	display: inline;
+	padding-right: 20px
+	}
+
+.pun .blockform .buttons {
+	padding-left: 12px;
+	}
+
+.pun .blockform .buttons input {
+	margin-right: 8px;
+	}
+
+#posterror ul {
+	list-style: square;
+	padding: 3px 0 3px 24px;
+	}
+
+.pun .deletemsg {
+	border-style: solid;
+	border-width: 1px;
+	padding: 6px 15px;
+	}
+
+.pun p.actions span {
+	margin-right: 12px;
+	}
+
+/*****************************************************************
+7. PROFILES AND ADMIN
+*****************************************************************/
+
+.pun .block2col {
+	padding-bottom: 1px
+	}
+
+.pun .block2col .blockform, .pun .block2col .block {
+	margin-left: 17em
+	}
+
+.pun .blockmenu {
+	float:left;
+	width: 16em
+	}
+
+.pun .blockmenu li {
+	padding: 3px 0;
+	font-weight: bold;
+	}
+
+.pun .blockmenu a:link, .pun .blockmenu a:visited {
+	text-decoration: none
+	}
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active {
+	text-decoration: underline
+	}
+
+#viewprofile dl {
+	float: left;
+	width: 100%;
+	overflow: hidden
+	}
+
+#viewprofile dd {
+	margin-left: 14em;
+	padding: 3px;
+	}
+
+#viewprofile dt {
+	float: left;
+	width: 13em;
+	margin: 3px 0;
+	}
+
+#profileavatar img {
+	float: right;
+	margin-left: 1em
+	}
+
+/*****************************************************************
+8. MAIN POSTS
+*****************************************************************/
+
+.pun .blockpost h2 a:link, .pun .blockpost h2 a:visited {
+	text-decoration: none;
+	}
+
+.pun .blockpost h2 a:hover, .pun .blockpost h2 a:active {
+	text-decoration: underline;
+	}
+
+.pun .blockpost h2 .conr {
+	float: right;
+	text-align: right;
+	}
+
+#punsearch .blockpost h2 span {
+	white-space: nowrap;
+	}
+
+.pun .blockpost .box {
+	overflow: hidden;
+	}
+
+.pun .postleft, .pun .postfootleft {
+	float:left;
+	width: 18em;
+	position: relative;
+	overflow: hidden;
+	}
+
+.pun .postleft dl {
+	padding: 0.75em 6px;
+	}
+
+.pun .postleft .usercontacts, .pun .postleft .icon {
+	margin-top: 6px
+	}
+
+.pun .postleft .postavatar, .pun .postleft .usertitle {
+	margin-bottom: 6px;
+	display: block;
+	}
+
+.pun .blockpost dt {
+	font-size: 1.091em;
+	font-weight: bold;
+	}
+
+.pun .blockpost dt a:link, .pun .blockpost dt a:visited {
+	text-decoration: none;
+	}
+
+.pun .blockpost dt a:hover, .pun .blockpost dt a:active {
+	text-decoration: underline;
+	}
+
+.pun .postright, .pun .postfootright {
+	border-left-width: 18em;
+	border-left-style: solid
+	}
+
+#postpreview .postright {
+	border-left: 0
+	}
+
+.pun .postright {
+	padding: 0 6px;
+	}
+
+.pun .postfootright, .pun .multidelete {
+	text-align: right
+	}
+
+.pun .postmsg {
+	width:98%;
+	overflow: hidden;
+	padding-bottom: 6px;
+	}
+
+.pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
+.pun .postfootleft p {
+	padding: 6px 6px 6px 6px;
+	}
+
+.pun .postfootright li {
+	display: inline;
+	margin-right: 12px;
+	}
+
+.pun .postfootright a:link, .pun .postfootright a:visited {
+	text-decoration: none
+	}
+
+.pun .postfootright a:hover, .pun .postfootright a:active {
+	text-decoration: underline
+	}
+
+.pun .codebox {
+	border-style: solid;
+	border-width: 1px;
+	margin: 0.75em 1em;
+	padding: 0;
+	}
+
+.pun .quotebox {
+	border-style: solid;
+	border-width: 1px;
+	margin: 0.75em 1em;
+	padding: 0 0.75em;
+	}
+
+.pun .quotebox cite {
+	display: block;
+	padding: 0.75em 0 0 0;
+	}
+
+.pun .quotebox blockquote {
+	width: 100%;
+	overflow: hidden
+	}
+
+.pun .codebox pre {
+	overflow: auto;
+	width: 100%;
+	overflow-y:hidden
+	}
+
+* html .pun .codebox pre {
+	padding-bottom: 10px;
+	}
+
+*+html .pun .codebox pre {
+	padding-bottom: 10px
+	}
+
+.pun .codebox pre code {
+	display: block;
+	padding: 0.75em;
+	}
+
+.pun .codebox pre.vscroll {
+	height: 32em;
+	overflow: auto;
+	overflow-y: auto
+	}
+
+.pun .postmsg img {
+	vertical-align: bottom;
+	}
+
+.pun .postsignature hr {
+	margin-left: 0px;
+	width: 200px;
+	text-align: left;
+	height: 1px;
+	border:none
+	}
+
+.pun .postmsg .postimg img {
+	max-width: 98%;
+	vertical-align: middle;
+}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+	border-style: solid;
+	border-width: 2px;
+}
+
+.pun .blockpost label {
+	padding: 3px 6px;
+	border-style: solid;
+	border-width: 1px;
+	vertical-align: middle;
+	display: inline-block;
+	}
+
+.pun .blockpost label * {
+	vertical-align: middle;
+	margin: 0;
+	padding: 0;
+	}
+
+/****************************************************************/
+/* 9. HELP FILES AND MISC. */
+/****************************************************************/
+
+#punhelp h2 {
+	margin-top: 12px
+	}
+
+#punhelp div.box {
+	padding: 10px
+	}
+
+#debugtime {
+	margin-top: -12px;
+	text-align: center;
+	}
+
+/*****************************************************************
+COLOUR SCHEME
+*****************************************************************/
+
+/* Background / Text
+----------------------------------------------------------------*/
+
+body {
+	background: #fff;
+	color: #333
+	}
+
+.pun {
+	color: #333
+	}
+
+.pun .box, #adminconsole fieldset th {
+	background-color: #f1f1f1
+	}
+
+.pun td.tc2, .pun td.tc3, .pun td.tcmod, #postpreview, #viewprofile dd, .pun .forminfo,
+#adminconsole fieldset td, .pun .blockmenu .box, #adstats dd, #brdfooter #modcontrols {
+	background-color: #dedfdf
+	}
+
+.pun h2, #brdmenu {
+	background-color: #0066b9;
+	color: #fff
+	}
+
+.pun th {
+	background-color: #d1d1d1
+	}
+
+.pun legend {
+	color: #005cb1
+	}
+
+.pun .blockmenu li.isactive a, #posterror li strong {
+	color: #333
+	}
+
+.pun .usercontent * {
+	background: transparent;
+	color: #333
+	}
+
+/* Posts
+----------------------------------------------------------------*/
+
+.pun .blockpost .box, .pun .postright, .pun .postfootright, .pun .deletemsg {
+	background-color: #dedfdf
+	}
+
+.pun .postright, .pun .postfootright {
+	border-left-color: #f1f1f1
+	}
+
+.pun .postleft, .pun .postfootleft, .pun .blockpost label, .pun .codebox, .pun .quotebox {
+	background-color: #f1f1f1
+	}
+
+#punhelp .codebox, #punhelp .quotebox {
+	background-color: #f9f9f9;
+	}
+
+.pun .blockpost h2 {
+	background-color: #006fc9
+	}
+
+.pun .blockpost h2 span.conr {
+	color: #aabdcd
+	}
+
+.pun .postmsg ins, #punhelp samp ins {
+	background-color: #ff0;
+	}
+
+.pun hr {
+	background-color: #333;
+	color: #333
+	}
+
+/* Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+	border-color: #0066b9
+	}
+
+.pun td, #brdfooter #modcontrols {
+	border-color: #bbcede
+	}
+
+.pun th, .pun fieldset {
+	border-color: #d1d1d1
+	}
+
+#adminconsole td, #adminconsole th {
+	border-color: #f1f1f1
+	}
+
+.pun .quotebox, .pun .codebox, .pun .forminfo,
+.pun .blockpost label, .pun .deletemsg {
+	border-color: #aca899 #fff #fff #aca899
+	}
+
+/* Links
+----------------------------------------------------------------*/
+
+.pun a:link, .pun a:visited {
+	color: #005cb1
+	}
+
+.pun a:hover, .pun a:active, .pun a:focus {
+	color: #b42000
+	}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+	border-color: #005cb1;
+}
+
+.pun .postmsg .postimg a:hover img, .pun .postmsg .postimg a:active img, .pun .postmsg .postimg a:focus img {
+	border-color: #b42000;
+}
+
+.pun h2 a:link, .pun h2 a:visited,
+#brdmenu a:link, #brdmenu a:visited {
+	color: #fff
+	}
+
+.pun h2 a:hover, .pun h2 a:active,
+#brdmenu a:hover, #brdmenu a:active {
+	color: #fff
+	}
+
+.pun .postreport a:link, .pun .postreport a:visited,
+.pun .iclosed td.tcl a:link, .pun .iclosed td.tcl a:visited {
+	color: #888
+	}
+
+.pun .postreport a:hover, .pun .postreport a:active,
+.pun .iclosed td.tcl a:hover, .pun .iclosed td.tcl a:active {
+	color: #aaa
+	}
+
+.pun .maintenancelink a:link, .pun .maintenancelink a:visited {
+	color: #b42000
+	}
+
+.pun .maintenancelink a:hover, .pun .maintenancelink a:active {
+	color: #b42000
+	}
+
+/* Status Indicators
+----------------------------------------------------------------*/
+
+.pun .icon {
+	border-color: #e6e6e6 #dedede #dadada #e2e2e2
+	}
+
+.pun .iredirect .icon {
+	border-color: #f1f1f1 #f1f1f1 #f1f1f1 #f1f1f1
+	}
+
+.pun .inew .icon {
+	border-color: #0080d7 #0065c0 #0058b3 #0072ca
+	}
diff --git a/style/Radium.css b/style/Radium.css
new file mode 100644
index 0000000..505c92e
--- /dev/null
+++ b/style/Radium.css
@@ -0,0 +1,1086 @@
+/*****************************************************************
+1. INITIAL SETTINGS
+*****************************************************************/
+
+/* Limited Reset
+----------------------------------------------------------------*/
+
+.pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
+.pun h4, .pun h5, .pun pre, .pun blockquote, .pun ul, .pun ol, .pun li, .pun dl,
+.pun dt, .pun dd, .pun th, .pun td, .pun fieldset, .pun img, .pun abbr, .pun cite {
+	margin: 0;
+	padding: 0;
+	border: 0;
+	}
+
+.pun ul, .pun ol {
+	list-style: none
+	}
+
+
+/* Structural Settings
+----------------------------------------------------------------*/
+
+.pun .clearer, .pun .nosize {
+	height: 0;
+	width: 0;
+	line-height: 0;
+	font-size: 0;
+	overflow: hidden
+	}
+
+.pun .clearer, .pun .clearb {
+	clear: both
+	}
+
+.pun .nosize {
+	position: absolute;
+	left: -9999em;
+	text-indent: -9999em;
+	width: 0;
+	}
+
+* html .inbox, * html .inform, * html .pun, * html .tclcon, * html .codebox {
+	height: 1px
+	}
+
+.pun, .pun .inbox, .pun .inform, .pun .tclcon, .pun .codebox {
+	min-height: 1px
+	}
+
+
+/* Hidden Elements
+----------------------------------------------------------------*/
+
+#brdfooter h2, #brdstats h2, #brdstats .conl dt, #brdstats .conr dt,
+#modcontrols dt, #searchlinks dt, div.postright h3, span.closedtext,
+.pun .required strong span {
+	position: absolute;
+	display: block;
+	overflow: hidden;
+	width: 0;
+	left: -9999em;
+	text-indent: -9999em;
+	}
+
+/*****************************************************************
+2. TEXT & CONTENT
+*****************************************************************/
+
+/* Text Defaults
+----------------------------------------------------------------*/
+
+.pun {
+	font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
+	}
+
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp {
+	font-size: 1em;
+	font-family: verdana, helvetica, arial, sans-serif;
+	}
+
+.pun pre, .pun code {
+	font-size: 1.182em;
+	font-family: consolas, monaco, "bitstream vera sans mono", "courier new", courier, monospace
+	}
+
+.pun pre code {
+	font-size: 1em;
+	}
+
+.pun strong {
+	font-weight: bold;
+	}
+
+.pun em {
+	font-style: italic;
+	}
+
+
+/* Content Defaults
+----------------------------------------------------------------*/
+
+.pun p, .pun ul, .pun ol, .pun dl {
+	font-size: 1em;
+	padding: 3px 0;
+	}
+
+.pun h2 {
+	font-size: 1em;
+	font-weight: normal;
+	padding: 4px 7px;
+	}
+
+.pun h3 {
+	font-size: 1.091em;
+	padding: 3px 0;
+	}
+
+.pun table p, .pun table h3 {
+	padding: 0;
+	}
+
+.pun span.warntext, .pun p.warntext {
+	font-weight: bold
+	}
+
+/* User Content (Announcements, Rules, Posts)
+----------------------------------------------------------------*/
+
+.pun .usercontent p, .pun .postmsg p {
+	padding: 0.75em 0
+	}
+
+.pun .usercontent ul, .pun .postmsg ul {
+	padding: 0.75em 1em 0.75em 2.5em;
+	list-style: disc
+	}
+
+.pun .usercontent ol, .pun .postmsg ol {
+	padding: 0.75em 1em 0.75em 2.5em;
+	list-style: decimal
+	}
+
+.pun .usercontent ol.alpha, .pun .postmsg ol.alpha {
+	list-style: lower-alpha
+	}
+
+.pun .usercontent li ol, .pun .usercontent li ul, .pun .postmsg li ol, .pun .postmsg li ul {
+	padding: 0.25em 1em 0.75em 2.5em
+	}
+
+.pun .usercontent li p, .pun .postmsg li p {
+	padding: 0
+	}
+
+.pun .usercontent h1 {
+	font-size: 1.4em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h2 {
+	font-size: 1.2em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h3 {
+	font-size: 1.1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h4, .pun .usercontent h5, .pun .usercontent h6 {
+	font-size: 1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .quotebox cite {
+	font-weight: bold;
+	font-style: normal;
+	padding: 0.75em 0.75em 0 0.75em
+	}
+
+.pun span.bbu {
+	text-decoration: underline
+	}
+
+.pun span.bbs, .pun del {
+	text-decoration: line-through;
+	}
+
+.pun .postmsg ins, #punhelp samp ins {
+	text-decoration: none;
+	}
+
+.pun div.postmsg h5, #punhelp h5 {
+	font-size: 1.1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0;
+	}
+
+
+/*****************************************************************
+3. COMMON STYLES
+*****************************************************************/
+
+/* Page Layout
+----------------------------------------------------------------*/
+
+.pun {
+	margin: 12px 20px
+	}
+
+#punredirect, #punmaint, #puninstall, #pundb_update {
+	margin: 50px 20% 12px 20%
+	}
+
+
+/* Vertical Element Spacing
+----------------------------------------------------------------*/
+
+#brdheader {
+	margin: 0 0 12px 0;
+	}
+
+#announce, #brdstats {
+	margin: 12px 0 12px 0;
+	}
+
+.pun .blocktable, .pun .block, .pun .blockform, .pun .block2col, #postreview {
+	margin-bottom: 12px
+	}
+
+#punindex .blocktable, .pun .blockpost {
+	margin-bottom: 6px
+	}
+
+#postreview .blockpost {
+	margin-bottom: -1px;
+	}
+
+.pun .block2col .blockform, .pun .block2col .block {
+	margin-bottom: 0px
+	}
+
+.pun .linkst, .pun .linksb {
+	margin-top: -12px
+	}
+
+.pun .postlinksb {
+	margin-top: -6px
+	}
+
+
+/* External Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+	border-style: solid;
+	border-width: 1px;
+	}
+
+#brdheader .box {
+	border-top-width: 4px;
+	}
+
+/* Default Internal Spacing
+----------------------------------------------------------------*/
+
+.pun .block .inbox, .pun .blockmenu .inbox {
+	padding: 3px 6px
+	}
+
+/*****************************************************************
+4. COMMON BOARD ELEMENTS
+*****************************************************************/
+
+/* Board Header
+----------------------------------------------------------------*/
+
+#brdtitle h1 {
+	font-size: 1.454em;
+	font-weight: bold;
+	line-height: 1em;
+	padding: 3px 0 0 0;
+	}
+
+#brddesc {
+	padding: 3px 0;
+	}
+
+#brddesc * {
+	padding-top: 0;
+	padding-bottom: 0;
+	}
+
+#brdmenu li {
+	display: inline;
+	margin-right: 12px;
+	}
+
+#brdmenu a:link, #brdmenu a:visited {
+	text-decoration: none
+	}
+
+#brdmenu a:hover, #brdmenu a:active {
+	text-decoration: underline
+	}
+
+#brdwelcome .conl {
+	float: left;
+	}
+
+#brdwelcome .conr {
+	float: right;
+	text-align: right;
+	}
+
+/* Breadcrumbs and Post Links
+----------------------------------------------------------------*/
+
+.pun .linkst {
+	padding: 8px 6px 3px 6px
+	}
+
+.pun .linksb, .pun .postlinksb {
+	padding: 3px 6px 8px 6px
+	}
+
+.pun .crumbs {
+	clear: both;
+	width: 100%;
+	overflow: hidden;
+	}
+
+.pun .crumbs li {
+	display: inline;
+	white-space: nowrap;
+	font-weight: bold;
+	}
+
+.pun .pagelink {
+	float: left;
+	white-space: nowrap;
+	}
+
+.pun .postlink {
+	font-weight: bold;
+	white-space: nowrap;
+	}
+
+.pun .postlink, .pun .modbuttons {
+	float: right;
+	text-align: right;
+	}
+
+.pun .modbuttons {
+	padding: 1px 0;
+	white-space: nowrap;
+	}
+
+.pun .modbuttons input {
+	margin-left: 6px;
+	}
+
+.pun .postlink a:link, .pun .postlink a:visited {
+	text-decoration: none
+	}
+
+.pun .postlink a:hover, .pun .postlink a:active {
+	text-decoration: underline;
+	}
+
+
+/* Board Footer
+----------------------------------------------------------------*/
+
+#brdfooter .conl {
+	float: left;
+	}
+
+#brdfooter .conr {
+	float: right;
+	text-align: right;
+	}
+
+#brdfooter #modcontrols {
+	border-bottom-style: solid;
+	border-bottom-width: 1px;
+	text-align: center;
+	}
+
+#brdfooter #modcontrols dd {
+	display: inline;
+	margin:0 6px;
+	}
+
+
+/* Board Stats
+----------------------------------------------------------------*/
+
+#brdstats .conl {
+	float: left;
+	}
+
+#brdstats .conr {
+	float: right;
+	text-align: right;
+	}
+
+#onlinelist dd, #onlinelist dt {
+	display: inline;
+	}
+
+
+/*****************************************************************
+5. MAIN TABLES
+*****************************************************************/
+
+.pun table {
+	width: 100%;
+	border-collapse: collapse;
+	border-spacing: 0;
+	empty-cells: show;
+	}
+
+.pun .blocktable table {
+	table-layout: fixed;
+	}
+
+.pun td, .pun th {
+	padding: 4px 6px;
+	line-height: 1.273em;
+	text-align: left;
+	font-weight: normal;
+	}
+
+.pun td {
+	border-style: solid none none solid;
+	border-width: 1px;
+	}
+
+.pun .tcl {
+	border-left: 0;
+	width: auto;
+	}
+
+.pun .tc2, .pun .tc3, .pun .tcmod {
+	width: 10%;
+	text-align: center;
+	padding: 4px 0;
+	}
+
+.pun .tcr {
+	width: 30%;
+	}
+
+.pun .tcl h3 {
+	font-size: 1.091em;
+	font-weight: bold;
+	}
+
+.pun .tcl h3 span.newtext {
+	font-size: 0.917em;
+	}
+
+.pun .tcl span.newtext, .pun .tcl span.pagestext {
+	white-space: nowrap;
+	font-weight: normal;
+	}
+
+.pun td span.byuser {
+	white-space: nowrap;
+	}
+
+.pun .tcl p {
+	padding: 5px 0 0 0
+	}
+
+#punsearch #vf .tc2 {
+	width: 18%;
+	text-align: left;
+	padding: 4px 6px;
+	}
+
+#users1 .tcr {
+	width: 25%
+	}
+
+#users1 .tc2 {
+	width: 25%;
+	text-align: left;
+	padding: 4px 6px;
+	}
+
+#debug .tcl {
+	width: 10%
+	}
+
+#debug .tcr {
+	width: 90%;
+	white-space: normal
+	}
+
+#punindex .tcr .byuser {
+	display: block
+	}
+
+.pun .blocktable .tclcon {
+	padding: 0 11px 0 12px;
+	overflow: hidden;
+	height: 1%;
+	min-height: 1px;
+	position: relative;
+	}
+
+.pun .blocktable .tclcon div {
+	width: 100%;
+	overflow: hidden;
+	}
+
+.pun .icon {
+	border-width: 7px;
+	border-style: solid;
+	height: 0;
+	width: 0;
+	overflow: hidden;
+	float: left;
+	}
+
+.pun .icon div {
+	position: absolute;
+	left: -9999em;
+	text-indent: -9999em;
+	height: 0;
+	}
+
+.pun .iposted .ipost {
+	position: absolute;
+	left: 0;
+	font-weight: bold;
+	width: 8px;
+	padding-left: 4px;
+	text-align: center;
+	top: 0;
+	}
+
+/*****************************************************************
+6. MAIN FORMS
+*****************************************************************/
+
+.pun .blockform form, .pun .fakeform {
+	PADDING: 20px 20px 15px 20px
+	}
+
+.pun .forminfo {
+	margin-bottom: 12px;
+	padding: 9px 10px;
+	border-style: solid;
+	border-width: 1px;
+	}
+
+.pun .forminfo h3 {
+	font-weight: bold;
+	}
+
+.pun .inform {
+	padding-bottom: 12px
+	}
+
+.pun fieldset {
+	padding: 0px 12px 0px 12px;
+	border-style: solid;
+	border-width: 1px
+	}
+
+.pun legend {
+	padding: 0px 6px
+	}
+
+.pun .infldset {
+	padding: 9px 0px 12px 0
+	}
+
+.pun label {
+	display: block;
+	padding: 3px 0
+	}
+
+.pun label.conl {
+	float: left;
+	overflow: visible;
+	margin-right: 10px
+	}
+
+.pun select {
+	padding-top: 1px;
+	padding-bottom: 1px;
+	}
+
+.pun fieldset .rbox {
+	}
+
+.pun fieldset .rbox br {
+	display: none;
+	}
+
+.pun fieldset .rbox label {
+	padding: 3px 0 3px 25px;
+	position: relative;
+	vertical-align: middle;
+	}
+
+.pun fieldset .rbox input {
+	margin: 0 9px 0 -25px;
+	padding: 0;
+	width: 16px;
+	position: relative;
+	vertical-align: middle;
+	}
+
+.pun .txtarea {
+	width: 75%
+	}
+
+.pun .txtarea textarea, .pun input.longinput {
+	width: 100%
+	}
+
+.pun .bblinks {
+	padding-bottom: 10px;
+	padding-left: 4px
+	}
+
+.pun .bblinks li {
+	display: inline;
+	padding-right: 20px
+	}
+
+.pun .blockform .buttons {
+	padding-left: 12px;
+	}
+
+.pun .blockform .buttons input {
+	margin-right: 8px;
+	}
+
+#posterror ul {
+	list-style: square;
+	padding: 3px 0 3px 24px;
+	}
+
+.pun .deletemsg {
+	border-style: solid;
+	border-width: 1px;
+	padding: 6px 15px;
+	}
+
+.pun p.actions span {
+	margin-right: 12px;
+	}
+
+/*****************************************************************
+7. PROFILES AND ADMIN
+*****************************************************************/
+
+.pun .block2col {
+	padding-bottom: 1px
+	}
+
+.pun .block2col .blockform, .pun .block2col .block {
+	margin-left: 17em
+	}
+
+.pun .blockmenu {
+	float:left;
+	width: 16em
+	}
+
+.pun .blockmenu li {
+	padding: 3px 0;
+	font-weight: bold;
+	}
+
+.pun .blockmenu a:link, .pun .blockmenu a:visited {
+	text-decoration: none
+	}
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active {
+	text-decoration: underline
+	}
+
+#viewprofile dl {
+	float: left;
+	width: 100%;
+	overflow: hidden
+	}
+
+#viewprofile dd {
+	margin-left: 14em;
+	padding: 3px;
+	}
+
+#viewprofile dt {
+	float: left;
+	width: 13em;
+	margin: 3px 0;
+	}
+
+#profileavatar img {
+	float: right;
+	margin-left: 1em
+	}
+
+/*****************************************************************
+8. MAIN POSTS
+*****************************************************************/
+
+.pun .blockpost h2 a:link, .pun .blockpost h2 a:visited {
+	text-decoration: none;
+	}
+
+.pun .blockpost h2 a:hover, .pun .blockpost h2 a:active {
+	text-decoration: underline;
+	}
+
+.pun .blockpost h2 .conr {
+	float: right;
+	text-align: right;
+	}
+
+#punsearch .blockpost h2 span {
+	white-space: nowrap;
+	}
+
+.pun .blockpost .box {
+	overflow: hidden;
+	}
+
+.pun .postleft, .pun .postfootleft {
+	float:left;
+	width: 18em;
+	position: relative;
+	overflow: hidden;
+	}
+
+.pun .postleft dl {
+	padding: 0.75em 6px;
+	}
+
+.pun .postleft .usercontacts, .pun .postleft .icon {
+	margin-top: 6px
+	}
+
+.pun .postleft .postavatar, .pun .postleft .usertitle {
+	margin-bottom: 6px;
+	display: block;
+	}
+
+.pun .blockpost dt {
+	font-size: 1.091em;
+	font-weight: bold;
+	}
+
+.pun .blockpost dt a:link, .pun .blockpost dt a:visited {
+	text-decoration: none;
+	}
+
+.pun .blockpost dt a:hover, .pun .blockpost dt a:active {
+	text-decoration: underline;
+	}
+
+.pun .postright, .pun .postfootright {
+	border-left-width: 18em;
+	border-left-style: solid
+	}
+
+#postpreview .postright {
+	border-left: 0
+	}
+
+.pun .postright {
+	padding: 0 6px;
+	}
+
+.pun .postfootright, .pun .multidelete {
+	text-align: right
+	}
+
+.pun .postmsg {
+	width:98%;
+	overflow: hidden;
+	padding-bottom: 6px;
+	}
+
+.pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
+.pun .postfootleft p {
+	padding: 6px 6px 6px 6px;
+	}
+
+.pun .postfootright li {
+	display: inline;
+	margin-left: 12px;
+	}
+
+.pun .postfootright a:link, .pun .postfootright a:visited {
+	text-decoration: none
+	}
+
+.pun .postfootright a:hover, .pun .postfootright a:active {
+	text-decoration: underline
+	}
+
+.pun .codebox {
+	border-style: solid;
+	border-width: 1px;
+	margin: 0.75em 1em;
+	padding: 0;
+	}
+
+.pun .quotebox {
+	border-style: solid;
+	border-width: 1px;
+	margin: 0.75em 1em;
+	padding: 0 0.75em;
+	}
+
+.pun .quotebox cite {
+	display: block;
+	padding: 0.75em 0 0 0;
+	}
+
+.pun .quotebox blockquote {
+	width: 100%;
+	overflow: hidden
+	}
+
+.pun .codebox pre {
+	overflow: auto;
+	width: 100%;
+	overflow-y:hidden
+	}
+
+* html .pun .codebox pre {
+	padding-bottom: 10px;
+	}
+
+*+html .pun .codebox pre {
+	padding-bottom: 10px
+	}
+
+.pun .codebox pre code {
+	display: block;
+	padding: 0.75em;
+	}
+
+.pun .codebox pre.vscroll {
+	height: 32em;
+	overflow: auto;
+	overflow-y: auto
+	}
+
+.pun .postmsg img {
+	vertical-align: bottom;
+	}
+
+.pun .postsignature hr {
+	margin-left: 0px;
+	width: 200px;
+	text-align: left;
+	height: 1px;
+	border:none
+	}
+
+.pun .postmsg .postimg img {
+	max-width: 98%;
+	vertical-align: middle;
+}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+	border-style: solid;
+	border-width: 2px;
+}
+
+.pun .blockpost label {
+	padding: 3px 6px;
+	border-style: solid;
+	border-width: 1px;
+	vertical-align: middle;
+	display: inline-block;
+	}
+
+.pun .blockpost label * {
+	vertical-align: middle;
+	margin: 0;
+	padding: 0;
+	}
+
+/****************************************************************/
+/* 9. HELP FILES AND MISC. */
+/****************************************************************/
+
+#punhelp h2 {
+	margin-top: 12px
+	}
+
+#punhelp div.box {
+	padding: 10px
+	}
+
+#debugtime {
+	margin-top: -12px;
+	text-align: center;
+	}
+
+/*****************************************************************
+COLOUR SCHEME
+*****************************************************************/
+
+/* Background / Text
+----------------------------------------------------------------*/
+
+body {
+	background: #2a2a2a;
+	color: #d4d4d4
+	}
+
+.pun {
+	color: #d4d4d4
+	}
+
+.pun .box, #adminconsole fieldset th {
+	background-color: #383838
+	}
+
+.pun td.tc2, .pun td.tc3, .pun td.tcmod, #postpreview, #viewprofile dd, .pun .forminfo,
+#brdfooter #modcontrols, #adminconsole fieldset td, .pun .blockmenu .box, #adstats dd {
+	background-color: #424242
+	}
+
+.pun h2, #brdmenu {
+	background-color: #565656;
+	color: #d4d4d4
+	}
+
+.pun th {
+	background-color: #484848
+	}
+
+.pun legend {
+	color: #60c860
+	}
+
+.pun .blockmenu li.isactive a, #posterror li strong {
+	color: #d4d4d4
+	}
+
+.pun .usercontent * {
+	background: transparent;
+	color: #d4d4d4
+	}
+
+.pun textarea, .pun input, .pun select {
+	background-color: #2a2a2a;
+	color: #d4d4d4
+	}
+
+/* Posts
+----------------------------------------------------------------*/
+
+.pun .blockpost .box, .pun .postright, .pun .postfootright, .pun .deletemsg {
+	background-color: #383838
+	}
+
+.pun .postright, .pun .postfootright {
+	border-left-color: #424242
+	}
+
+.pun .postleft, .pun .postfootleft, .pun .blockpost label, .pun .codebox, .pun .quotebox {
+	background-color: #424242
+	}
+
+.pun .blockpost h2 {
+	background-color: #565656
+	}
+
+.pun .blockpost h2 span.conr {
+	color: #a19e96
+	}
+
+.pun .postmsg ins, #punhelp samp ins {
+	background-color: #ff0;
+	}
+
+.pun hr {
+	background-color: #606060;
+	color: #606060
+	}
+
+/* Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+	border-color:#565656
+	}
+
+.pun td, #brdfooter #modcontrols {
+	border-color: #565656
+	}
+
+.pun th, .pun fieldset {
+	border-color: #484848
+	}
+
+#adminconsole td, #adminconsole th {
+	border-color: #383838
+	}
+
+.pun .quotebox, .pun .codebox, .pun .forminfo,
+.pun .blockpost label, .pun .deletemsg {
+	border-color: #606060
+	}
+
+/* Links
+----------------------------------------------------------------*/
+
+.pun a:link, .pun a:visited {
+	color: #60c860
+	}
+
+.pun a:hover, .pun a:active, .pun a:focus {
+	color: #80ee80
+	}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+	border-color: #60c860;
+}
+
+.pun .postmsg .postimg a:hover img, .pun .postmsg .postimg a:active img, .pun .postmsg .postimg a:focus img {
+	border-color: #80ee80;
+}
+
+.pun h2 a:link, .pun h2 a:visited,
+#brdmenu a:link, #brdmenu a:visited {
+	color: #d4d4d4
+	}
+
+.pun h2 a:hover, .pun h2 a:active,
+#brdmenu a:hover, #brdmenu a:active {
+	color: #d4d4d4
+	}
+
+.pun .postreport a:link, .pun .postreport a:visited,
+.pun .iclosed td.tcl a:link, .pun .iclosed td.tcl a:visited {
+	color: #888
+	}
+
+.pun .postreport a:hover, .pun .postreport a:active,
+.pun .iclosed td.tcl a:hover, .pun .iclosed td.tcl a:active {
+	color: #aaa
+	}
+
+.pun .maintenancelink a:link, .pun .maintenancelink a:visited {
+	color: #ff4000
+	}
+
+.pun .maintenancelink a:hover, .pun .maintenancelink a:active {
+	color: #ff5010
+	}
+
+/* Status Indicators
+----------------------------------------------------------------*/
+
+.pun .icon {
+	border-color: #484848 #404040 #3c3c3c #444444
+	}
+
+.pun .iredirect .icon {
+	border-color: #383838 #383838 #383838 #383838
+	}
+
+.pun .inew .icon {
+	border-color: #60c860 #54af54 #499849 #59b657
+	}
diff --git a/style/Sulfur.css b/style/Sulfur.css
new file mode 100644
index 0000000..49b3e2d
--- /dev/null
+++ b/style/Sulfur.css
@@ -0,0 +1,1085 @@
+/*****************************************************************
+1. INITIAL SETTINGS
+*****************************************************************/
+
+/* Limited Reset
+----------------------------------------------------------------*/
+
+.pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
+.pun h4, .pun h5, .pun pre, .pun blockquote, .pun ul, .pun ol, .pun li, .pun dl,
+.pun dt, .pun dd, .pun th, .pun td, .pun fieldset, .pun img, .pun abbr, .pun cite {
+	margin: 0;
+	padding: 0;
+	border: 0;
+	}
+
+.pun ul, .pun ol {
+	list-style: none
+	}
+
+
+/* Structural Settings
+----------------------------------------------------------------*/
+
+.pun .clearer, .pun .nosize {
+	height: 0;
+	width: 0;
+	line-height: 0;
+	font-size: 0;
+	overflow: hidden
+	}
+
+.pun .clearer, .pun .clearb {
+	clear: both
+	}
+
+.pun .nosize {
+	position: absolute;
+	left: -9999em;
+	text-indent: -9999em;
+	width: 0;
+	}
+
+* html .inbox, * html .inform, * html .pun, * html .tclcon, * html .codebox {
+	height: 1px
+	}
+
+.pun, .pun .inbox, .pun .inform, .pun .tclcon, .pun .codebox {
+	min-height: 1px
+	}
+
+
+/* Hidden Elements
+----------------------------------------------------------------*/
+
+#brdfooter h2, #brdstats h2, #brdstats .conl dt, #brdstats .conr dt,
+#modcontrols dt, #searchlinks dt, div.postright h3, span.closedtext,
+.pun .required strong span {
+	position: absolute;
+	display: block;
+	overflow: hidden;
+	width: 0;
+	left: -9999em;
+	text-indent: -9999em;
+	}
+
+/*****************************************************************
+2. TEXT & CONTENT
+*****************************************************************/
+
+/* Text Defaults
+----------------------------------------------------------------*/
+
+.pun {
+	font: 68.75%/1.4545em Verdana, Helvetica, Arial, sans-serif;
+	}
+
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp {
+	font-size: 1em;
+	font-family: verdana, helvetica, arial, sans-serif;
+	}
+
+.pun pre, .pun code {
+	font-size: 1.182em;
+	font-family: consolas, monaco, "bitstream vera sans mono", "courier new", courier, monospace
+	}
+
+.pun pre code {
+	font-size: 1em;
+	}
+
+.pun strong {
+	font-weight: bold;
+	}
+
+.pun em {
+	font-style: italic;
+	}
+
+
+/* Content Defaults
+----------------------------------------------------------------*/
+
+.pun p, .pun ul, .pun ol, .pun dl {
+	font-size: 1em;
+	padding: 3px 0;
+	}
+
+.pun h2 {
+	font-size: 1em;
+	font-weight: normal;
+	padding: 4px 7px;
+	}
+
+.pun h3 {
+	font-size: 1.091em;
+	padding: 3px 0;
+	}
+
+.pun table p, .pun table h3 {
+	padding: 0;
+	}
+
+.pun span.warntext, .pun p.warntext {
+	font-weight: bold
+	}
+
+/* User Content (Announcements, Rules, Posts)
+----------------------------------------------------------------*/
+
+.pun .usercontent p, .pun .postmsg p {
+	padding: 0.75em 0
+	}
+
+.pun .usercontent ul, .pun .postmsg ul {
+	padding: 0.75em 1em 0.75em 2.5em;
+	list-style: disc
+	}
+
+.pun .usercontent ol, .pun .postmsg ol {
+	padding: 0.75em 1em 0.75em 2.5em;
+	list-style: decimal
+	}
+
+.pun .usercontent ol.alpha, .pun .postmsg ol.alpha {
+	list-style: lower-alpha
+	}
+
+.pun .usercontent li ol, .pun .usercontent li ul, .pun .postmsg li ol, .pun .postmsg li ul {
+	padding: 0.25em 1em 0.75em 2.5em
+	}
+
+.pun .usercontent li p, .pun .postmsg li p {
+	padding: 0
+	}
+
+.pun .usercontent h1 {
+	font-size: 1.4em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h2 {
+	font-size: 1.2em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h3 {
+	font-size: 1.1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h4, .pun .usercontent h5, .pun .usercontent h6 {
+	font-size: 1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .quotebox cite {
+	font-weight: bold;
+	font-style: normal;
+	padding: 0.75em 0.75em 0 0.75em
+	}
+
+.pun span.bbu {
+	text-decoration: underline
+	}
+
+.pun span.bbs, .pun del {
+	text-decoration: line-through;
+	}
+
+.pun .postmsg ins, #punhelp samp ins {
+	text-decoration: none;
+	}
+
+.pun div.postmsg h5, #punhelp h5 {
+	font-size: 1.1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0;
+	}
+
+
+/*****************************************************************
+3. COMMON STYLES
+*****************************************************************/
+
+/* Page Layout
+----------------------------------------------------------------*/
+
+.pun {
+	margin: 12px 20px
+	}
+
+#punredirect, #punmaint, #puninstall, #pundb_update {
+	margin: 50px 20% 12px 20%
+	}
+
+
+/* Vertical Element Spacing
+----------------------------------------------------------------*/
+
+#brdheader {
+	margin: 0 0 12px 0;
+	}
+
+#announce, #brdstats {
+	margin: 12px 0 12px 0;
+	}
+
+.pun .blocktable, .pun .block, .pun .blockform, .pun .block2col, #postreview {
+	margin-bottom: 12px
+	}
+
+#punindex .blocktable, .pun .blockpost {
+	margin-bottom: 6px
+	}
+
+#postreview .blockpost {
+	margin-bottom: -1px;
+	}
+
+.pun .block2col .blockform, .pun .block2col .block {
+	margin-bottom: 0px
+	}
+
+.pun .linkst, .pun .linksb {
+	margin-top: -12px
+	}
+
+.pun .postlinksb {
+	margin-top: -6px
+	}
+
+
+/* External Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+	border-style: solid;
+	border-width: 1px;
+	}
+
+#brdheader .box {
+	border-top-width: 4px;
+	}
+
+/* Default Internal Spacing
+----------------------------------------------------------------*/
+
+.pun .block .inbox, .pun .blockmenu .inbox {
+	padding: 3px 6px
+	}
+
+/*****************************************************************
+4. COMMON BOARD ELEMENTS
+*****************************************************************/
+
+/* Board Header
+----------------------------------------------------------------*/
+
+#brdtitle h1 {
+	font-size: 1.454em;
+	font-weight: bold;
+	line-height: 1em;
+	padding: 3px 0 0 0;
+	}
+
+#brddesc {
+	padding: 3px 0;
+	}
+
+#brddesc * {
+	padding-top: 0;
+	padding-bottom: 0;
+	}
+
+#brdmenu li {
+	display: inline;
+	margin-right: 12px;
+	}
+
+#brdmenu a:link, #brdmenu a:visited {
+	text-decoration: none
+	}
+
+#brdmenu a:hover, #brdmenu a:active {
+	text-decoration: underline
+	}
+
+#brdwelcome .conl {
+	float: left;
+	}
+
+#brdwelcome .conr {
+	float: right;
+	text-align: right;
+	}
+
+/* Breadcrumbs and Post Links
+----------------------------------------------------------------*/
+
+.pun .linkst {
+	padding: 8px 6px 3px 6px
+	}
+
+.pun .linksb, .pun .postlinksb {
+	padding: 3px 6px 8px 6px
+	}
+
+.pun .crumbs {
+	clear: both;
+	width: 100%;
+	overflow: hidden;
+	}
+
+.pun .crumbs li {
+	display: inline;
+	white-space: nowrap;
+	font-weight: bold;
+	}
+
+.pun .pagelink {
+	float: left;
+	white-space: nowrap;
+	}
+
+.pun .postlink {
+	font-weight: bold;
+	white-space: nowrap;
+	}
+
+.pun .postlink, .pun .modbuttons {
+	float: right;
+	text-align: right;
+	}
+
+.pun .modbuttons {
+	padding: 1px 0;
+	white-space: nowrap;
+	}
+
+.pun .modbuttons input {
+	margin-left: 6px;
+	}
+
+.pun .postlink a:link, .pun .postlink a:visited {
+	text-decoration: none
+	}
+
+.pun .postlink a:hover, .pun .postlink a:active {
+	text-decoration: underline;
+	}
+
+
+/* Board Footer
+----------------------------------------------------------------*/
+
+#brdfooter .conl {
+	float: left;
+	}
+
+#brdfooter .conr {
+	float: right;
+	text-align: right;
+	}
+
+#brdfooter #modcontrols {
+	border-bottom-style: solid;
+	border-bottom-width: 1px;
+	text-align: center;
+	}
+
+#brdfooter #modcontrols dd {
+	display: inline;
+	margin:0 6px;
+	}
+
+
+/* Board Stats
+----------------------------------------------------------------*/
+
+#brdstats .conl {
+	float: left;
+	}
+
+#brdstats .conr {
+	float: right;
+	text-align: right;
+	}
+
+#onlinelist dd, #onlinelist dt {
+	display: inline;
+	}
+
+
+/*****************************************************************
+5. MAIN TABLES
+*****************************************************************/
+
+.pun table {
+	width: 100%;
+	border-collapse: collapse;
+	border-spacing: 0;
+	empty-cells: show;
+	}
+
+.pun .blocktable table {
+	table-layout: fixed;
+	}
+
+.pun td, .pun th {
+	padding: 4px 6px;
+	line-height: 1.273em;
+	text-align: left;
+	font-weight: normal;
+	}
+
+.pun td {
+	border-style: solid none none solid;
+	border-width: 1px;
+	}
+
+.pun .tcl {
+	border-left: 0;
+	width: auto;
+	}
+
+.pun .tc2, .pun .tc3, .pun .tcmod {
+	width: 10%;
+	text-align: center;
+	padding: 4px 0;
+	}
+
+.pun .tcr {
+	width: 30%;
+	}
+
+.pun .tcl h3 {
+	font-size: 1.091em;
+	font-weight: bold;
+	}
+
+.pun .tcl h3 span.newtext {
+	font-size: 0.917em;
+	}
+
+.pun .tcl span.newtext, .pun .tcl span.pagestext {
+	white-space: nowrap;
+	font-weight: normal;
+	}
+
+.pun td span.byuser {
+	white-space: nowrap;
+	}
+
+.pun .tcl p {
+	padding: 5px 0 0 0
+	}
+
+#punsearch #vf .tc2 {
+	width: 18%;
+	text-align: left;
+	padding: 4px 6px;
+	}
+
+#users1 .tcr {
+	width: 25%
+	}
+
+#users1 .tc2 {
+	width: 25%;
+	text-align: left;
+	padding: 4px 6px;
+	}
+
+#debug .tcl {
+	width: 10%
+	}
+
+#debug .tcr {
+	width: 90%;
+	white-space: normal
+	}
+
+#punindex .tcr .byuser {
+	display: block
+	}
+
+.pun .blocktable .tclcon {
+	padding: 0 11px 0 12px;
+	overflow: hidden;
+	height: 1%;
+	min-height: 1px;
+	position: relative;
+	}
+
+.pun .blocktable .tclcon div {
+	width: 100%;
+	overflow: hidden;
+	}
+
+.pun .icon {
+	border-width: 7px;
+	border-style: solid;
+	height: 0;
+	width: 0;
+	overflow: hidden;
+	float: left;
+	}
+
+.pun .icon div {
+	position: absolute;
+	left: -9999em;
+	text-indent: -9999em;
+	height: 0;
+	}
+
+.pun .iposted .ipost {
+	position: absolute;
+	left: 0;
+	font-weight: bold;
+	width: 8px;
+	padding-left: 4px;
+	text-align: center;
+	top: 0;
+	}
+
+/*****************************************************************
+6. MAIN FORMS
+*****************************************************************/
+
+.pun .blockform form, .pun .fakeform {
+	PADDING: 20px 20px 15px 20px
+	}
+
+.pun .forminfo {
+	margin-bottom: 12px;
+	padding: 9px 10px;
+	border-style: solid;
+	border-width: 1px;
+	}
+
+.pun .forminfo h3 {
+	font-weight: bold;
+	}
+
+.pun .inform {
+	padding-bottom: 12px
+	}
+
+.pun fieldset {
+	padding: 0px 12px 0px 12px;
+	border-style: solid;
+	border-width: 1px
+	}
+
+.pun legend {
+	padding: 0px 6px
+	}
+
+.pun .infldset {
+	padding: 9px 0px 12px 0
+	}
+
+.pun label {
+	display: block;
+	padding: 3px 0
+	}
+
+.pun label.conl {
+	float: left;
+	overflow: visible;
+	margin-right: 10px
+	}
+
+.pun select {
+	padding-top: 1px;
+	padding-bottom: 1px;
+	}
+
+.pun fieldset .rbox {
+	}
+
+.pun fieldset .rbox br {
+	display: none;
+	}
+
+.pun fieldset .rbox label {
+	padding: 3px 0 3px 25px;
+	position: relative;
+	vertical-align: middle;
+	}
+
+.pun fieldset .rbox input {
+	margin: 0 9px 0 -25px;
+	padding: 0;
+	width: 16px;
+	position: relative;
+	vertical-align: middle;
+	}
+
+.pun .txtarea {
+	width: 75%
+	}
+
+.pun .txtarea textarea, .pun input.longinput {
+	width: 100%
+	}
+
+.pun .bblinks {
+	padding-bottom: 10px;
+	padding-left: 4px
+	}
+
+.pun .bblinks li {
+	display: inline;
+	padding-right: 20px
+	}
+
+.pun .blockform .buttons {
+	padding-left: 12px;
+	}
+
+.pun .blockform .buttons input {
+	margin-right: 8px;
+	}
+
+#posterror ul {
+	list-style: square;
+	padding: 3px 0 3px 24px;
+	}
+
+.pun .deletemsg {
+	border-style: solid;
+	border-width: 1px;
+	padding: 6px 15px;
+	}
+
+.pun p.actions span {
+	margin-right: 12px;
+	}
+
+/*****************************************************************
+7. PROFILES AND ADMIN
+*****************************************************************/
+
+.pun .block2col {
+	padding-bottom: 1px
+	}
+
+.pun .block2col .blockform, .pun .block2col .block {
+	margin-left: 17em
+	}
+
+.pun .blockmenu {
+	float:left;
+	width: 16em
+	}
+
+.pun .blockmenu li {
+	padding: 3px 0;
+	font-weight: bold;
+	}
+
+.pun .blockmenu a:link, .pun .blockmenu a:visited {
+	text-decoration: none
+	}
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active {
+	text-decoration: underline
+	}
+
+#viewprofile dl {
+	float: left;
+	width: 100%;
+	overflow: hidden
+	}
+
+#viewprofile dd {
+	margin-left: 14em;
+	padding: 3px;
+	}
+
+#viewprofile dt {
+	float: left;
+	width: 13em;
+	margin: 3px 0;
+	}
+
+#profileavatar img {
+	float: right;
+	margin-left: 1em
+	}
+
+/*****************************************************************
+8. MAIN POSTS
+*****************************************************************/
+
+.pun .blockpost h2 a:link, .pun .blockpost h2 a:visited {
+	text-decoration: none;
+	}
+
+.pun .blockpost h2 a:hover, .pun .blockpost h2 a:active {
+	text-decoration: underline;
+	}
+
+.pun .blockpost h2 .conr {
+	float: right;
+	text-align: right;
+	}
+
+#punsearch .blockpost h2 span {
+	white-space: nowrap;
+	}
+
+.pun .blockpost .box {
+	overflow: hidden;
+	}
+
+.pun .postleft, .pun .postfootleft {
+	float:left;
+	width: 18em;
+	position: relative;
+	overflow: hidden;
+	}
+
+.pun .postleft dl {
+	padding: 0.75em 6px;
+	}
+
+.pun .postleft .usercontacts, .pun .postleft .icon {
+	margin-top: 6px
+	}
+
+.pun .postleft .postavatar, .pun .postleft .usertitle {
+	margin-bottom: 6px;
+	display: block;
+	}
+
+.pun .blockpost dt {
+	font-size: 1.091em;
+	font-weight: bold;
+	}
+
+.pun .blockpost dt a:link, .pun .blockpost dt a:visited {
+	text-decoration: none;
+	}
+
+.pun .blockpost dt a:hover, .pun .blockpost dt a:active {
+	text-decoration: underline;
+	}
+
+.pun .postright, .pun .postfootright {
+	border-left-width: 18em;
+	border-left-style: solid
+	}
+
+#postpreview .postright {
+	border-left: 0
+	}
+
+.pun .postright {
+	padding: 0 6px;
+	}
+
+.pun .postfootright, .pun .multidelete {
+	text-align: right
+	}
+
+.pun .postmsg {
+	width:98%;
+	overflow: hidden;
+	padding-bottom: 6px;
+	}
+
+.pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
+.pun .postfootleft p {
+	padding: 6px 6px 6px 6px;
+	}
+
+.pun .postfootright li {
+	display: inline;
+	margin-left: 12px;
+	}
+
+.pun .postfootright a:link, .pun .postfootright a:visited {
+	text-decoration: none
+	}
+
+.pun .postfootright a:hover, .pun .postfootright a:active {
+	text-decoration: underline
+	}
+
+.pun .codebox {
+	border-style: solid;
+	border-width: 1px;
+	margin: 0.75em 1em;
+	padding: 0;
+	}
+
+.pun .quotebox {
+	border-style: solid;
+	border-width: 1px;
+	margin: 0.75em 1em;
+	padding: 0 0.75em;
+	}
+
+.pun .quotebox cite {
+	display: block;
+	padding: 0.75em 0 0 0;
+	}
+
+.pun .quotebox blockquote {
+	width: 100%;
+	overflow: hidden
+	}
+
+.pun .codebox pre {
+	overflow: auto;
+	width: 100%;
+	overflow-y:hidden
+	}
+
+* html .pun .codebox pre {
+	padding-bottom: 10px;
+	}
+
+*+html .pun .codebox pre {
+	padding-bottom: 10px
+	}
+
+.pun .codebox pre code {
+	display: block;
+	padding: 0.75em;
+	}
+
+.pun .codebox pre.vscroll {
+	height: 32em;
+	overflow: auto;
+	overflow-y: auto
+	}
+
+.pun .postmsg img {
+	vertical-align: bottom;
+	}
+
+.pun .postsignature hr {
+	margin-left: 0px;
+	width: 200px;
+	text-align: left;
+	height: 1px;
+	border:none
+	}
+
+.pun .postmsg .postimg img {
+	max-width: 98%;
+	vertical-align: middle;
+}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+	border-style: solid;
+	border-width: 2px;
+}
+
+.pun .blockpost label {
+	padding: 3px 6px;
+	border-style: solid;
+	border-width: 1px;
+	vertical-align: middle;
+	display: inline-block;
+	}
+
+.pun .blockpost label * {
+	vertical-align: middle;
+	margin: 0;
+	padding: 0;
+	}
+
+/****************************************************************/
+/* 9. HELP FILES AND MISC. */
+/****************************************************************/
+
+#punhelp h2 {
+	margin-top: 12px
+	}
+
+#punhelp div.box {
+	padding: 10px
+	}
+
+#debugtime {
+	margin-top: -12px;
+	text-align: center;
+	}
+
+/*****************************************************************
+COLOUR SCHEME
+*****************************************************************/
+
+/* Background / Text
+----------------------------------------------------------------*/
+
+body {
+	background: #fff;
+	color: #333
+	}
+
+.pun {
+	color: #333
+	}
+
+.pun .box, #adminconsole fieldset th {
+	background-color: #f1f1f1
+	}
+
+.pun td.tc2, .pun td.tc3, .pun td.tcmod, #postpreview, #viewprofile dd, .pun .forminfo,
+#brdfooter #modcontrols, #adminconsole fieldset td, .pun .blockmenu .box, #adstats dd {
+	background-color: #dedfdf
+	}
+
+.pun h2, #brdmenu {
+	background-color: #b84623;
+	color: #fff
+	}
+
+.pun th {
+	background-color: #d1d1d1
+	}
+
+.pun legend {
+	color: #822100
+	}
+
+.pun .blockmenu li.isactive a, #posterror li strong {
+	color: #333
+	}
+
+.pun .usercontent * {
+	background: transparent;
+	color: #333
+	}
+
+/* Posts
+----------------------------------------------------------------*/
+
+.pun .blockpost .box, .pun .postright, .pun .postfootright, .pun .deletemsg {
+	background-color: #dedfdf
+	}
+
+.pun .postright, .pun .postfootright {
+	border-left-color: #f1f1f1
+	}
+
+.pun .postleft, .pun .postfootleft, .pun .blockpost label, .pun .codebox, .pun .quotebox {
+	background-color: #f1f1f1
+	}
+
+#punhelp .codebox, #punhelp .quotebox {
+	background-color: #f9f9f9;
+	}
+
+.pun .blockpost h2 {
+	background-color: #d25028
+	}
+
+.pun .blockpost h2 span.conr {
+	color: #fccfc1
+	}
+
+.pun .postmsg ins, #punhelp samp ins {
+	background-color: #ff0;
+	}
+
+.pun hr {
+	background-color: #333;
+	color: #333
+	}
+
+/* Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+	border-color: #b84623
+	}
+
+.pun td, #brdfooter #modcontrols {
+	border-color: #e1c3c3
+	}
+
+.pun th, .pun fieldset {
+	border-color: #d1d1d1
+	}
+
+#adminconsole td, #adminconsole th {
+	border-color: #f1f1f1
+	}
+
+.pun .quotebox, .pun .codebox, .pun .forminfo,
+.pun .blockpost label, .pun .deletemsg {
+	border-color: #aca899 #fff #fff #aca899
+	}
+
+/* Links
+----------------------------------------------------------------*/
+
+.pun a:link, .pun a:visited {
+	color: #822100
+	}
+
+.pun a:hover, .pun a:active, .pun a:focus {
+	color: #ca3300
+	}
+
+.pun .postmsg .postimg a:link img, .pun .postmsg .postimg a:visited img {
+	border-color: #822100;
+}
+
+.pun .postmsg .postimg a:hover img, .pun .postmsg .postimg a:active img, .pun .postmsg .postimg a:focus img {
+	border-color: #ca3300;
+}
+
+.pun h2 a:link, .pun h2 a:visited,
+#brdmenu a:link, #brdmenu a:visited {
+	color: #fff
+	}
+
+.pun h2 a:hover, .pun h2 a:active,
+#brdmenu a:hover, #brdmenu a:active {
+	color: #fff
+	}
+
+.pun .postreport a:link, .pun .postreport a:visited,
+.pun .iclosed td.tcl a:link, .pun .iclosed td.tcl a:visited {
+	color: #888
+	}
+
+.pun .postreport a:hover, .pun .postreport a:active,
+.pun .iclosed td.tcl a:hover, .pun .iclosed td.tcl a:active {
+	color: #aaa
+	}
+
+.pun .maintenancelink a:link, .pun .maintenancelink a:visited {
+	color: #b42000
+	}
+
+.pun .maintenancelink a:hover, .pun .maintenancelink a:active {
+	color: #b42000
+	}
+
+/* Status Indicators
+----------------------------------------------------------------*/
+
+.pun .icon {
+	border-color: #e6e6e6 #dedede #dadada #e2e2e2
+	}
+
+.pun .iredirect .icon {
+	border-color: #f1f1f1 #f1f1f1 #f1f1f1 #f1f1f1
+	}
+
+.pun .inew .icon {
+	border-color: #c23000 #af2c00 #992600 #ac2b00
+	}
diff --git a/style/Technetium.css b/style/Technetium.css
new file mode 100644
index 0000000..6afd693
--- /dev/null
+++ b/style/Technetium.css
@@ -0,0 +1,1335 @@
+/*****************************************************************
+Technitium theme CSS
+
+Yes, it is valid CSS 3, but unfortunately border-radius doesn't seem to work correctly in my Firefox version so I have to give it the moz versions, which break the validator. Sorry.
+*****************************************************************/
+
+/*****************************************************************
+1. INITIAL SETTINGS
+*****************************************************************/
+
+/* Limited Reset
+----------------------------------------------------------------*/
+
+.pun table, .pun div, .pun form, .pun p, .pun h1, .pun h2, .pun h3,
+.pun h4, .pun h5, .pun pre, .pun blockquote, .pun ul, .pun ol, .pun li, .pun dl,
+.pun dt, .pun dd, .pun th, .pun td, .pun fieldset, .pun img, .pun abbr, .pun cite {
+	margin: 0;
+	padding: 0;
+	border: 0;
+	}
+
+.pun ul, .pun ol {
+	list-style: none
+	}
+
+
+/* Structural Settings
+----------------------------------------------------------------*/
+
+body {
+	line-height: 1.9em;
+}
+
+.pun {
+	width: 95%;
+	margin: 0 auto;
+	}
+
+.pun .clearer, .pun .nosize {
+	height: 0;
+	width: 0;
+	line-height: 0;
+	font-size: 0;
+	overflow: hidden
+	}
+
+.pun .clearer, .pun .clearb {
+	clear: both
+	}
+
+.pun .nosize {
+	position: absolute;
+	left: -9999em;
+	text-indent: -9999em;
+	width: 0;
+	}
+
+* html .inbox, * html .inform, * html .pun, * html .tclcon, * html .codebox {
+	height: 1px
+	}
+
+.pun, .pun .inbox, .pun .inform, .pun .tclcon, .pun .codebox {
+	min-height: 1px
+	}
+
+
+/* Hidden Elements
+----------------------------------------------------------------*/
+
+#brdfooter h2, #brdstats h2, #brdstats .conl dt, #brdstats .conr dt,
+#modcontrols dt, #searchlinks dt, div.postright h3 {
+	position: absolute;
+	display: block;
+	overflow: hidden;
+	width: 0;
+	left: -9999em;
+	text-indent: -9999em;
+	}
+
+/*****************************************************************
+2. TEXT & CONTENT
+*****************************************************************/
+
+/* Text Defaults
+----------------------------------------------------------------*/
+
+.pun {
+	font: 10pt Georgia, Times, "Times New Roman", serif
+	}
+
+.pun table, .pun td, .pun th, .pun input, .pun select, .pun optgroup, .pun textarea, .pun samp {
+	font-size: 1em;
+	font-family: Georgia, Times, "Times New Roman", serif;
+	}
+
+.pun pre, .pun code {
+	font-size: 1.182em;
+	font-family: consolas, monaco, "bitstream vera sans mono", "courier new", courier, monospace
+	}
+
+.pun pre code {
+	font-size: 1em;
+	}
+
+.pun strong {
+	font-weight: bold;
+	}
+
+.pun em {
+	font-style: italic;
+	}
+
+#vf td {
+	font-size: 1.1em;
+	}
+
+
+/* Content Defaults
+----------------------------------------------------------------*/
+
+.pun p, .pun ul, .pun ol, .pun dl {
+	font-size: 1em;
+	padding: 3px 0;
+	}
+
+.pun h2 {
+	font-size: 1.5em;
+	font-weight: normal;
+	padding: 4px 6px;
+	padding: 6px;
+	-moz-border-radius-topleft: 0.3em;
+	-moz-border-radius-topright: 0.3em;
+	-webkit-border-top-left-radius: 0.3em;
+	-webkit-border-top-right-radius: 0.3em;
+	}
+
+.pun h2 #brdmenu {
+	font-size: 1.1em;
+	}
+
+.pun .blockpost h2 {
+	font-size: 1.0em;
+	padding: 8px;
+	}
+
+.pun h3 {
+	font-size: 1.091em;
+	padding: 3px 0;
+	}
+
+.pun table p, .pun table h3 {
+	padding: 0;
+	}
+
+.pun span.warntext, .pun p.warntext {
+	font-weight: bold
+	}
+
+.pun .postleft dl dt {
+	font-size: 1.4em;
+	}
+
+/* User Content (Announcements, Rules, Posts)
+----------------------------------------------------------------*/
+
+.pun .usercontent p, .pun .postmsg p {
+	padding: 0.5em 0;
+	line-height: 1.6em;
+	}
+
+.pun .usercontent ul, .pun .postmsg ul {
+	padding: 0.75em 1em 0.75em 2.5em;
+	list-style: disc
+	}
+
+.pun .usercontent ol, .pun .postmsg ol {
+	padding: 0.75em 1em 0.75em 2.5em;
+	list-style: decimal
+	}
+
+.pun .usercontent ol.alpha, .pun .postmsg ol.alpha {
+	list-style: lower-alpha
+	}
+
+.pun .usercontent li ol, .pun .usercontent li ul, .pun .postmsg li ol, .pun .postmsg li ul {
+	padding: 0.25em 1em 0.75em 2.5em
+	}
+
+.pun .usercontent li p, .pun .postmsg li p {
+	padding: 0
+	}
+
+.pun .usercontent h1 {
+	font-size: 1.4em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h2 {
+	font-size: 1.2em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h3 {
+	font-size: 1.1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .usercontent h4, .pun .usercontent h5, .pun .usercontent h6 {
+	font-size: 1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0
+	}
+
+.pun .quotebox cite {
+	font-weight: bold;
+	font-style: normal;
+	padding: 0.75em 0.75em 0 0.75em
+	}
+
+.pun span.bbu {
+	text-decoration: underline
+	}
+
+.pun span.bbs, .pun del {
+	text-decoration: line-through;
+	}
+
+.pun .postmsg ins, #punhelp samp ins {
+	text-decoration: none;
+	}
+
+.pun div.postmsg h5, #punhelp h5 {
+	font-size: 1.1em;
+	font-weight: bold;
+	padding: 0.75em 0 0 0;
+	}
+
+/*****************************************************************
+3. COMMON STYLES
+*****************************************************************/
+
+/* Page Layout
+----------------------------------------------------------------*/
+
+.pun {
+	margin: 12px 20px
+	}
+
+#punredirect, #punmaint, #puninstall, #pundb_update {
+	margin: 50px 20% 12px 20%;
+	width: auto;
+	}
+
+
+/* Vertical Element Spacing
+----------------------------------------------------------------*/
+
+#brdheader {
+	margin: 0 0 12px 0;
+	}
+
+#announce, #brdstats {
+	margin: 12px 0 12px 0;
+	}
+
+.pun .blocktable, .pun .block, .pun .blockform, .pun .block2col, #postreview {
+	margin-bottom: 12px
+	}
+
+#punindex .blocktable, .pun .blockpost {
+	margin-bottom: 6px
+	}
+
+#postreview .box {
+	margin-bottom: 3px;
+	}
+
+.pun .block2col .blockform, .pun .block2col .block {
+	margin-bottom: 0px
+	}
+
+.pun .linkst, .pun .linksb {
+	margin-top: -12px
+	}
+
+.pun .postlinksb {
+	margin-top: -6px
+	}
+
+
+/* External Borders
+----------------------------------------------------------------*/
+
+.pun .box {
+	border-style: solid;
+	border-width: 1px;
+	}
+
+
+.pun h2 {
+	border-width: 1px;
+	border-style: solid;
+	border-bottom: none;
+}
+
+
+/* Default Internal Spacing
+----------------------------------------------------------------*/
+
+.pun .block .inbox, .pun .blockmenu .inbox {
+	padding: 3px 6px
+	}
+
+/*****************************************************************
+4. COMMON BOARD ELEMENTS
+*****************************************************************/
+
+/* Board Header
+----------------------------------------------------------------*/
+
+#brdtitle h1 {
+	font-size: 1.5em;
+	line-height: 1.1em;
+	padding: 3px 0 0 0;
+	}
+
+#brdmenu {
+	font-size:1.2em;
+	}
+
+#brddesc {
+	padding: 3px 0;
+	}
+
+#brddesc * {
+	padding-top: 0;
+	padding-bottom: 0;
+	}
+
+
+#brdmenu li {
+	display: inline;
+	margin-right: 10px;
+	margin-top: 12px;
+	margin-bottom: 12px;
+	}
+
+
+#brdmenu a:link, #brdmenu a:visited {
+	text-decoration: none;
+	text-shadow: #FFF 1px -1px 0.4em;
+	padding: 0.2em;
+	}
+
+#brdwelcome .conl {
+	float: left;
+	white-space: nowrap;
+	}
+
+#brdwelcome li {
+	float:left;
+	margin-right:8px;
+	white-space:nowrap;
+}
+
+#brdwelcome .conl li strong:after {
+	content: '.'
+	}
+
+#brdwelcome .conr {
+	float: right;
+	text-align: right;
+	}
+
+#brdwelcome .conr li {
+	float: right;
+	text-align: right;
+	}
+
+/* Breadcrumbs and Post Links
+----------------------------------------------------------------*/
+
+.pun .linkst {
+	padding: 8px 6px 3px 6px
+	}
+
+.pun .linksb, .pun .postlinksb {
+	padding: 3px 6px 8px 6px
+	}
+
+.pun .crumbs {
+	clear: both;
+	width: 100%;
+	overflow: hidden;
+	}
+
+.pun .crumbs li {
+	display: inline;
+	white-space: nowrap;
+	font-weight: bold;
+	}
+
+.pun .linkst .crumbs {
+	font-size: 1.3em;
+	}
+
+.pun .pagelink {
+	float: left;
+	display: block;
+	white-space: normal;
+	}
+
+.pun .postlink {
+	font-size: 1.1em;
+	font-weight: bold;
+	white-space: wrap;
+	text-decoration: none;
+	}
+
+.pun .linkst .postlink {
+	position: relative;
+	margin-top: -0.2em;
+	margin-bottom: 0.5em;
+	}
+
+.pun .linksb .postlink, .pun .postlinksb .postlink {
+	position: relative;
+	margin-top: 0.5em;
+	margin-bottom: -0.5em;
+	}
+
+.pun .postlink, .pun .modbuttons {
+	float: right;
+	text-align: right;
+	}
+
+.pun .modbuttons {
+	padding: 1px 0;
+	white-space: nowrap;
+	}
+
+.pun .modbuttons input {
+	margin-left: 6px;
+	}
+
+
+/* Board Footer
+----------------------------------------------------------------*/
+
+#brdfooter #modcontrols {
+	border-bottom-style: solid;
+	border-bottom-width: 1px;
+	text-align: center;
+	}
+
+#brdfooter #modcontrols dd {
+	display: inline;
+	margin-right: 10px;
+	}
+
+#brdfooter .conl {
+	float: left;
+	}
+
+#brdfooter .conr {
+	float: right;
+	text-align: right;
+	}
+
+#brdfooter #feedlinks span {
+	background:url("Technetium/feed.png") no-repeat scroll left center transparent;
+	padding-left:18px;
+	display: inline;
+}
+
+
+/* Board Stats
+----------------------------------------------------------------*/
+
+#brdstats .conl {
+	float: left;
+	}
+
+#brdstats .conr {
+	float: right;
+	text-align: right;
+	}
+
+#onlinelist dd, #onlinelist dt {
+	display: inline;
+	}
+
+
+/*****************************************************************
+5. MAIN TABLES
+*****************************************************************/
+
+.pun table {
+	width: 100%;
+	border-collapse: collapse;
+	border-spacing: 0;
+	empty-cells: show;
+	}
+
+.pun .blocktable table {
+	table-layout: fixed;
+	}
+
+.pun td, .pun th {
+	padding: 4px 6px;
+	line-height: 1.4em;
+	text-align: left;
+	font-weight: normal;
+	}
+
+.pun td {
+	border-style: solid none none solid;
+	border-width: 1px;
+	}
+
+.pun .tcl {
+	border-left: 0;
+	width: auto;
+	}
+
+.pun .tc2, .pun .tc3, .pun .tcmod {
+	width: 10%;
+	text-align: center;
+	padding: 4px 0;
+	}
+
+.pun .tcr {
+	width: 30%;
+	}
+
+.pun .tcl h3 {
+	font-size: 1.3em;
+	font-weight: bold;
+	}
+
+.pun .tcl span.newtext, .pun .tcl span.pagestext {
+	white-space: nowrap
+	}
+
+.pun .tcl p {
+	padding: 5px 0 0 0
+	}
+
+#punsearch #vf .tc2 {
+	width: 18%;
+	text-align: left;
+	padding: 4px 6px;
+	}
+
+#users1 .tcr {
+	width: 25%
+	}
+
+#users1 .tc2 {
+	width: 25%;
+	text-align: left;
+	padding: 4px 6px;
+	}
+
+#debug .tcl {
+	width: 10%
+	}
+
+#debug .tcr {
+	width: 90%;
+	white-space: normal
+	}
+
+#punindex .tcr .byuser {
+	display: block
+	}
+
+.pun .blocktable .tclcon {
+	padding: 0 11px 0 12px;
+	overflow: hidden;
+	height: 1%;
+	min-height: 1px;
+	position: relative;
+	}
+
+.pun .blocktable .tclcon div {
+	width: 100%;
+	overflow: hidden;
+	}
+
+.pun .icon {
+	height: 24px;
+	width: 24px;
+	overflow: hidden;
+	float: left;
+	}
+
+.pun .icon div {
+	position: absolute;
+	left: -9999em;
+	text-indent: -9999em;
+	height: 0;
+	}
+
+.pun .iposted .ipost {
+	position: absolute;
+	left: 0;
+	font-weight: bold;
+	width: 8px;
+	padding-left: 4px;
+	text-align: center;
+	top: 0;
+	}
+
+.pun .stickytext {
+	display: none;
+	}
+
+.pun .movedtext {
+	display: none;
+}
+
+.pun .closedtext {
+	font-weight: bold;
+	}
+/*****************************************************************
+6. MAIN FORMS
+*****************************************************************/
+
+
+.pun .forminfo {
+	margin-bottom: 12px;
+	padding: 9px 10px;
+	border-style: solid;
+	border-width: 1px;
+	}
+
+.pun .forminfo h3 {
+	font-weight: bold;
+	}
+
+
+
+.pun .inform {
+	padding: 0 18px;
+}
+
+.pun fieldset {
+	overflow: hidden;
+	width: 100%;
+	padding-bottom: 0.8em;
+}
+
+.pun legend {
+	font-size: 1.2em;
+	font-weight: bold;
+	margin-left: -7px;
+	padding: 10px 19px 7px 19px;
+}
+
+.pun div[class*="inform"] legend {
+	margin-left: 0;
+}
+
+.pun .infldset {
+	border-style: solid;
+	border-width: 1px;
+	display: inline-block;
+	overflow: hidden;
+	padding: 12px 18px;
+}
+
+.pun div[class*="infldset"] {
+	display: block;
+}
+
+#punregister #rules .infldset {
+	padding: 5px 18px;
+}
+
+.pun fieldset p {
+	clear: both;
+	padding: 0 0 7px 0;
+	width: 100%;
+}
+
+.pun fieldset .usercontent p {
+	padding: 7px 0;
+}
+
+.pun fieldset label {
+	clear: both;
+	display: block;
+	padding: 0 0 7px 0;
+}
+
+
+.pun label {
+	display: block;
+	padding: 3px 0
+	}
+
+.pun label.conl {
+	float: left;
+	overflow: visible;
+	margin-right: 10px
+	}
+
+.pun select {
+	padding-top: 1px;
+	padding-bottom: 1px;
+	}
+
+.pun fieldset .rbox {
+	}
+
+.pun fieldset .rbox br {
+	display: none;
+	}
+
+.pun fieldset .rbox label {
+	padding: 3px 0 3px 25px;
+	position: relative;
+	vertical-align: middle;
+	}
+
+.pun fieldset .rbox input {
+	margin: 0 9px 0 -25px;
+	padding: 0;
+	width: 16px;
+	position: relative;
+	vertical-align: middle;
+	}
+
+.pun .txtarea {
+	width: 90%
+	}
+
+.pun .txtarea textarea, .pun input.longinput {
+	width: 100%
+	}
+
+.pun .bblinks {
+	padding-bottom: 10px;
+	padding-left: 4px
+	}
+
+.pun .bblinks li {
+	display: inline;
+	padding-right: 20px
+	}
+
+.pun .blockform .buttons {
+	padding-left: 18px;
+	padding-bottom: 5px;
+	}
+
+.pun .blockform .buttons input {
+	margin-right: 8px;
+	}
+
+#posterror ul {
+	list-style: square;
+	padding: 3px 0 3px 24px;
+	}
+
+.pun .deletemsg {
+	border-style: solid;
+	border-width: 1px;
+	padding: 6px 15px;
+	}
+
+
+/*****************************************************************
+7. PROFILES AND ADMIN
+*****************************************************************/
+
+.pun .block2col {
+	padding-bottom: 1px
+	}
+
+.pun .block2col .blockform, .pun .block2col .block {
+	margin-left: 14em
+	}
+
+.pun .blockmenu {
+	float:left;
+	width: 13em
+	}
+
+.pun .blockmenu li {
+	padding: 3px 0;
+	font-weight: bold;
+	}
+
+.pun .blockmenu a:link, .pun .blockmenu a:visited {
+	text-decoration: none
+	}
+
+.pun .blockmenu a:hover, .pun .blockmenu a:active {
+	text-decoration: underline
+	}
+
+#viewprofile dl {
+	float: left;
+	width: 100%;
+	overflow: hidden
+	}
+
+#viewprofile dd {
+	margin-left: 14em;
+	padding: 3px;
+	}
+
+#viewprofile dt {
+	float: left;
+	width: 13em;
+	margin: 3px 0;
+	}
+
+#profileavatar img {
+	float: right;
+	margin-left: 1em
+	}
+
+/*****************************************************************
+8. MAIN POSTS
+*****************************************************************/
+
+.pun .blockpost h2 a:link, .pun .blockpost h2 a:visited {
+	text-decoration: none;
+	}
+
+.pun .blockpost h2 a:hover, .pun .blockpost h2 a:active {
+	text-decoration: underline;
+	}
+
+.pun .blockpost h2 .conr {
+	float: right;
+	text-align: right;
+	}
+
+#punsearch .blockpost h2 span {
+	white-space: nowrap;
+	}
+
+.pun .blockpost .box {
+	overflow: hidden;
+	}
+
+.pun .postleft, .pun .postfootleft {
+	float:left;
+	width: 18em;
+	overflow: hidden;
+	position: relative;
+	overflow: hidden;
+	}
+
+.pun .postleft dl {
+	padding: 0.5em 6px;
+	}
+
+.pun .postleft .usercontacts, .pun .postleft .icon {
+	margin-top: 6px
+	}
+
+.pun .postleft .postavatar, .pun .postleft .usertitle {
+	margin-bottom: 6px;
+	display: block;
+	}
+
+.pun .blockpost dt {
+	font-size: 1.091em;
+	font-weight: bold;
+	}
+
+.pun .blockpost dt a:link, .pun .blockpost dt a:visited {
+	text-decoration: none;
+	}
+
+.pun .blockpost dt a:hover, .pun .blockpost dt a:active {
+	text-decoration: underline;
+	}
+
+.pun .postright, .pun .postfootright {
+	border-left-width: 18em;
+	border-left-style: solid
+	}
+
+#postpreview .postright {
+	border-left: 0
+	}
+
+.pun .postright {
+	padding: 0 6px;
+	}
+
+.pun .postfootright, .pun .multidelete {
+	text-align: right
+	}
+
+.pun .postmsg {
+	width:98%;
+	overflow: hidden;
+	padding-bottom: 6px;
+	}
+
+.pun .postfootright ul, .pun .postfootright div, .pun .postfootright p,
+.pun .postfootleft p {
+	padding: 6px 6px 6px 6px;
+	}
+
+.pun .postfootright li {
+	display: inline;
+	}
+
+.pun .postfootright a:link, .pun .postfootright a:visited {
+	text-decoration: none
+	}
+
+.pun .postfootright a:hover, .pun .postfootright a:active {
+	text-decoration: underline
+	}
+
+
+.pun .quotebox, .pun .codebox {
+	border-style: solid;
+	border-width: 2px;
+	border-left: 5px solid;
+	margin: 0.75em 1em;
+	padding: 0 0.75em;
+	}
+
+.pun .quotebox cite {
+	display: block;
+	padding: 0.75em 0 0 0;
+	}
+
+.pun .quotebox blockquote {
+	width: 100%;
+	overflow: hidden
+	}
+
+.pun .codebox pre {
+	overflow: auto;
+	width: 100%;
+	overflow-y:hidden
+	}
+
+* html .pun .codebox pre {
+	padding-bottom: 10px;
+	}
+
+*+html .pun .codebox pre {
+	padding-bottom: 10px
+	}
+
+.pun .codebox pre code {
+	display: block;
+	padding: 0.75em;
+	}
+
+.pun .codebox pre.vscroll {
+	height: 32em;
+	overflow: auto;
+	overflow-y: auto
+	}
+
+.pun .postmsg img.postimg, .pun .postmsg a img.postimg {
+	max-width: 100%;
+	vertical-align: middle;
+	}
+
+.pun .postmsg img {
+	vertical-align: bottom;
+	}
+
+.pun .postsignature hr {
+	margin-left: 0px;
+	width: 200px;
+	text-align: left;
+	height: 1px;
+	border:none
+	}
+
+.pun .blockpost label {
+	padding: 3px 6px;
+	border-style: solid;
+	border-width: 1px;
+	vertical-align: middle;
+	display: inline-block;
+	}
+
+.pun .blockpost label * {
+	vertical-align: middle;
+	margin: 0;
+	padding: 0;
+	}
+
+
+/****************************************************************/
+/* 9. HELP FILES AND MISC. */
+/****************************************************************/
+
+#punhelp h2 {
+	margin-top: 12px
+	}
+
+#punhelp div.box {
+	padding: 10px
+	}
+
+/*****************************************************************
+COLOUR SCHEME
+*****************************************************************/
+
+/* Background / Text
+----------------------------------------------------------------*/
+
+body {
+	background: #fff url("Technetium/bg.png") repeat-x top;
+	color: #122434
+	}
+
+.pun {
+	color: #122434
+	}
+
+.pun .box, #adminconsole fieldset th {
+	background-color: #FBFCFD;
+	background-image: url("Technetium/light-shade.png");
+	background-position: bottom;
+	background-repeat: repeat-x;
+	}
+
+.pun td.tc2, .pun td.tc3, .pun td.tcmod, #postpreview, #viewprofile dd, .pun .forminfo,
+#brdfooter #modcontrols, #adminconsole fieldset td, .pun .blockmenu .box, #adstats dd {
+	background-color: #F4F9FD;
+	background-image: url("Technetium/light-shade.png");
+	background-repeat: repeat-x;
+	background-position: bottom;
+	}
+
+.pun h2, #brdmenu {
+	background-color: #C5D8EB;
+	color: #122434;
+	background-image: url("Technetium/dark-shade.png");
+	background-repeat: repeat-x;
+	background-position: top;
+	}
+
+.pun h2  {
+	background-color: #C5D8EB;
+	background-image: url("Technetium/inv-shade.png");
+	background-repeat: repeat-x;
+	color: #122434;
+	text-shadow: #FFF 1px -1px 0.7em;
+	}
+
+.pun #announce h2  {
+	display: none;
+	}
+
+.box #announce-block {
+	background-color: #FDFCE3;
+}
+
+.pun th {
+	background-color: #EFF3F8;
+	background-image: url("Technetium/light-shade.png");
+	background-repeat: repeat-x;
+	}
+
+.pun td {
+	background-image: url("Technetium/light-shade.png");
+	background-color: #FBFCFD;
+	background-position: bottom;
+	background-repeat: repeat-x;
+	}
+
+.pun .isticky td {
+	background-color: #E4EBF1;
+	}
+
+.pun .iclosed td {
+	background-color: #ECEEF0
+	}
+
+.pun .iclosed.isticky td {
+	background-color: #CDD6E0;
+	}
+
+.pun legend {
+	color: #122434
+	}
+
+.pun .blockmenu li.isactive a, #posterror li strong {
+	color: #333
+	}
+
+.pun .usercontent * {
+	background: transparent;
+	color: #333
+	}
+
+#adminmenu .box {
+	background: #FBFCFD;
+	}
+
+.pun .postlink a:link, .pun .postlink a:visited, .pun .postlink a:active  {
+	background-color:#F0F5FA;
+	background-image:url(Technetium/inv-shade.png);
+	background-repeat:repeat-x;
+	border: 1px solid;
+	color:#2B3037;
+	text-shadow: #FFF 1px -1px 0.4em;
+	padding: 0.3em;
+	border-radius: 0.4em;
+	position: relative;
+	-moz-border-radius: 0.4em;
+	-webkit-border-top-left-radius: 0.4em;
+	-webkit-border-top-right-radius: 0.4em;
+	-webkit-border-bottom-left-radius: 0.4em;
+	-webkit-border-bottom-right-radius: 0.4em;
+
+	}
+
+.pun .postlink a:hover {
+	background-color: #C0D6E9;
+	color: #000;
+	}
+
+#brdheader .box {
+	background-color:#F0F5FA;
+	background-image:url(Technetium/light-shade.png);
+	background-repeat:repeat-x;
+	background-position: top;
+	color:#122434;
+	border-radius: 0.4em;
+	-moz-border-radius: 0.4em;
+	-webkit-border-top-left-radius: 0.4em;
+	-webkit-border-top-right-radius: 0.4em;
+	-webkit-border-bottom-left-radius: 0.4em;
+	-webkit-border-bottom-right-radius: 0.4em;
+	}
+
+.pun #brdtitle {
+	background-color: #D1E1EF;
+	background-image: url("Technetium/inv-shade.png");
+	background-repeat: repeat-x;
+	text-shadow: #FFF 1px -1px 0.4em;
+	}
+
+
+.pun .infldset, #pundelete .deletemsg, #adintro .inbox, #adstats .inbox {
+	background: #FBFCFD url(Technetium/light-shade.png) bottom repeat-x;
+	border-color: #C0CCD8;
+}
+
+
+/* Posts
+----------------------------------------------------------------*/
+
+.pun .blockpost .box, .pun .postright, .pun .postfootright, .pun .deletemsg {
+	background-color: #F8FAFD;
+	}
+
+.pun .postright, .pun .postfootright {
+	border-left-color: #F0F5FA
+	}
+
+.pun .postleft, .pun .postfootleft, .pun .blockpost label, .pun .codebox, .pun .quotebox {
+	background-color: #F0F5FA
+	}
+
+#punhelp .codebox, #punhelp .quotebox {
+	background-color: #f9f9f9;
+	}
+
+.pun .blockpost h2 {
+	background-color: #F0F5FA;
+	}
+
+.pun .blockpost h2 span.conr {
+	color: #aabdcd
+	}
+
+.pun hr {
+	background-color: #333;
+	color: #333
+	}
+
+.pun .quotebox {
+	background-color: #FAFCFE;
+	}
+
+.pun .postmsg ins, #punhelp samp ins {
+	background-color: #ff0;
+	}
+
+/* Borders
+----------------------------------------------------------------*/
+
+.pun .box, .pun h2, #brdfooter #modcontrols {
+	border-color: #A2B5CC;
+}
+
+.blocktable #announce h2 {
+	border-color: #F6B620;
+}
+
+.pun td {
+	border-color: #A2B5CC;
+	}
+
+.pun th, .pun fieldset {
+	border-color: #A2B5CC;
+	}
+
+#adminconsole td, #adminconsole th {
+	border-style:solid;
+	border-width:1px 0px !important;
+}
+
+#adminconsole th {
+	border-width: 1px 0px 1px 1px !important;
+}
+
+#adminconsole td {
+	border-width: 1px 1px 1px 1px !important;
+}
+
+.pun .forminfo, .pun .blockpost label, .pun .deletemsg {
+	border-color: #aca899 #fff #fff #aca899;
+	}
+
+.pun .quotebox, .pun .codebox {
+	border-color: #BCD2E9;
+	}
+
+.pun .postlink a:link, .pun .postlink a:visited {
+	border-color: #C0D6E9
+	}
+
+
+/* Links
+----------------------------------------------------------------*/
+
+.pun a:link, .pun a:visited {
+	color: #1F537B;
+	text-decoration: none;
+	}
+
+.pun a:hover, .pun a:active, .pun a:focus {
+	color: #9E1D00
+	}
+
+#brdmenu li a:active, #brdmenu li a:link, #brdmenu li a:visited {
+	color: #1F2E3D;
+	}
+
+#brdmenu li a:hover {
+	color: #000;
+	text-shadow: #8895A2 1px -1px 0.4em;
+	}
+
+#vf a {
+	font-weight: bold;
+	}
+
+.pun .postreport a:link, .pun .postreport a:visited,
+.pun .iclosed td.tcl a:link, .pun .iclosed td.tcl a:visited {
+	color: #888
+	}
+
+.pun .isticky.iclosed td.tcl a:link, .pun .isticky.iclosed td.tcl a:visited {
+	color: #475F6B !important;
+	}
+
+.pun .isticky.iclosed td.tcl a:hover {
+	color: #577382 !important;
+	}
+
+
+.pun .postreport a:hover, .pun .postreport a:active,
+.pun .iclosed td.tcl a:hover, .pun .iclosed td.tcl a:active {
+	color: #aaa
+	}
+
+.pun .maintenancelink a:link, .pun .maintenancelink a:visited {
+	color: #b42000
+	}
+
+.pun .maintenancelink a:hover, .pun .maintenancelink a:active {
+	color: #b42000
+	}
+
+
+/* Status Indicators
+----------------------------------------------------------------*/
+
+.pun .icon {
+	background-image:url(Technetium/icon-nonew.png);
+	}
+
+.pun .iredirect .icon {
+	background-image:url(Technetium/icon-moved.png);
+	}
+
+.pun .inew .icon{
+	background-image:url(Technetium/icon-new.png);
+	}
+
+.pun .iclosed .icon  {
+	background-image:url(Technetium/icon-closed.png);
+	}
+
+.pun .isticky .icon {
+	background-image:url(Technetium/icon-nonew-sticky.png);
+	}
+
+.pun .isticky.inew .icon {
+	background-image:url(Technetium/icon-new-sticky.png);
+	}
+
+.pun .iclosed.isticky .icon {
+	background-image:url(Technetium/icon-closed-sticky.png);
+	}
+
+.pun .imoved .icon {
+	background-image:url(Technetium/icon-moved.png);
+	}
diff --git a/style/Technetium/bg.png b/style/Technetium/bg.png
new file mode 100644
index 0000000..fb5c156
Binary files /dev/null and b/style/Technetium/bg.png differ
diff --git a/style/Technetium/dark-shade.png b/style/Technetium/dark-shade.png
new file mode 100644
index 0000000..2f4a975
Binary files /dev/null and b/style/Technetium/dark-shade.png differ
diff --git a/style/Technetium/darker-shade.png b/style/Technetium/darker-shade.png
new file mode 100644
index 0000000..914eb26
Binary files /dev/null and b/style/Technetium/darker-shade.png differ
diff --git a/style/Technetium/feed.png b/style/Technetium/feed.png
new file mode 100644
index 0000000..d4cf8ce
Binary files /dev/null and b/style/Technetium/feed.png differ
diff --git a/style/Technetium/icon-closed-sticky.png b/style/Technetium/icon-closed-sticky.png
new file mode 100644
index 0000000..cfa6e63
Binary files /dev/null and b/style/Technetium/icon-closed-sticky.png differ
diff --git a/style/Technetium/icon-closed.png b/style/Technetium/icon-closed.png
new file mode 100644
index 0000000..a260ac3
Binary files /dev/null and b/style/Technetium/icon-closed.png differ
diff --git a/style/Technetium/icon-moved.png b/style/Technetium/icon-moved.png
new file mode 100644
index 0000000..56a313b
Binary files /dev/null and b/style/Technetium/icon-moved.png differ
diff --git a/style/Technetium/icon-new-sticky.png b/style/Technetium/icon-new-sticky.png
new file mode 100644
index 0000000..738d179
Binary files /dev/null and b/style/Technetium/icon-new-sticky.png differ
diff --git a/style/Technetium/icon-new.png b/style/Technetium/icon-new.png
new file mode 100644
index 0000000..95af1e8
Binary files /dev/null and b/style/Technetium/icon-new.png differ
diff --git a/style/Technetium/icon-nonew-sticky.png b/style/Technetium/icon-nonew-sticky.png
new file mode 100644
index 0000000..2440799
Binary files /dev/null and b/style/Technetium/icon-nonew-sticky.png differ
diff --git a/style/Technetium/icon-nonew.png b/style/Technetium/icon-nonew.png
new file mode 100644
index 0000000..e769387
Binary files /dev/null and b/style/Technetium/icon-nonew.png differ
diff --git a/style/Technetium/index.html b/style/Technetium/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/style/Technetium/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/style/Technetium/inv-shade.png b/style/Technetium/inv-shade.png
new file mode 100644
index 0000000..e2c80f2
Binary files /dev/null and b/style/Technetium/inv-shade.png differ
diff --git a/style/Technetium/light-shade.png b/style/Technetium/light-shade.png
new file mode 100644
index 0000000..b191465
Binary files /dev/null and b/style/Technetium/light-shade.png differ
diff --git a/style/imports/base_admin.css b/style/imports/base_admin.css
new file mode 100644
index 0000000..efb51f4
--- /dev/null
+++ b/style/imports/base_admin.css
@@ -0,0 +1,48 @@
+#adminconsole .block2 {MARGIN-TOP: 12px}
+
+/*** Admin Main Content ***/
+* HTML #adstats DD {HEIGHT: 1%}
+#adstats DD {MARGIN-LEFT: 14em; PADDING: 3px; MARGIN-BOTTOM: 5px; LINE-HEIGHT: 1.5em}
+#adstats DT {FLOAT: left; WIDTH: 13em; PADDING: 3px; LINE-HEIGHT: 1.5em}
+#adstats {PADDING: 15px 15px 5px 10px}
+#adintro {PADDING: 5px}
+#adintro P {PADDING: 10px}
+#adstats DL {PADDING: 5px 0 10px 5px}
+
+#adminconsole FIELDSET TD {TEXT-ALIGN: left; PADDING: 4px; WHITE-SPACE: normal}
+#adminconsole FIELDSET TH {TEXT-ALIGN: left; PADDING: 4px; WHITE-SPACE: normal}
+#adminconsole FIELDSET TD SPAN, #adminconsole FIELDSET TH SPAN {DISPLAY: block; FONT-SIZE: 1em; FONT-WEIGHT: normal}
+#adminconsole TH {WIDTH: 15em; FONT-WEIGHT: bold}
+#adminconsole INPUT, #adminconsole SELECT, #adminconsole TEXTAREA {MARGIN-BOTTOM: 0; MARGIN-TOP: 0; FONT-WEIGHT: normal}
+#adminconsole TABLE.aligntop TH, #adminconsole TABLE.aligntop TD {VERTICAL-ALIGN: top}
+#adminconsole TABLE.aligntop TH {PADDING-TOP: 0.7em}
+#adminconsole TD, #adminconsole TH {BORDER-STYLE: solid; BORDER-WIDTH: 3px 0 3px 0}
+#adminconsole P {PADDING-BOTTOM: 6px}
+#adminconsole .topspace {PADDING-TOP: 6px}
+#adminconsole P.submittop, #adminconsole P.submitend {TEXT-ALIGN: center}
+#adminconsole TH.hidehead {COLOR: #f1f1f1}
+#adminconsole THEAD TH {PADDING-BOTTOM: 0}
+#adminconsole P.linkactions {FONT-WEIGHT: bold; PADDING-LEFT: 5px}
+#adminconsole TH INPUT, #adminconsole DIV.fsetsubmit {MARGIN-TOP: 6px}
+
+/*** Particular table settings ***/
+#categoryedit .tcl {WIDTH: 25%}
+#censoring .tcl, #censoring .tc2, #ranks .tcl, #ranks .tc2 {WIDTH: 20%}
+#edforum .tcl, #edforum .tc2 {WIDTH: 8%}
+#edforum .tc2 {WIDTH: 6%}
+
+TABLE#forumperms TH, TABLE#forumperms TD {WHITE-SPACE: normal; WIDTH: auto; TEXT-ALIGN: center}
+TABLE#forumperms .atcl {TEXT-ALIGN: left; WIDTH: 15em; WHITE-SPACE: nowrap}
+#adminconsole TD.nodefault {BACKGROUND-COLOR: #D59B9B}
+
+/*** User/Ban Search Result Tables ***/
+#users2 TH, #bans1 TH {TEXT-ALIGN: left}
+#users2 .tcl, #users2 .tc3, #users2 .tc5, #bans1 .tcl, #bans1 .tc3, #bans1 .tc5, #bans1 .tc6 {WIDTH: 15%; TEXT-ALIGN: left; PADDING: 4px 6px}
+#users2 .tc2, #bans1 .tc2 {WIDTH: 22%; TEXT-ALIGN: left; PADDING: 4px 6px}
+#users2 .tc4, #bans1 .tc4 {WIDTH: 8%; TEXT-ALIGN: center}
+#users2 .tcr, #bans1 .tcr {WHITE-SPACE: nowrap}
+#adminconsole #linkst, #adminconsole #linksb A {FONT-WEIGHT: bold}
+
+/*** Plugins ***/
+.plugin .inbox {PADDING-BOTTOM: 10px}
+.plugin p {PADDING: 10px 10px 0}
diff --git a/style/imports/index.html b/style/imports/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/style/imports/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/style/imports/minmax.js b/style/imports/minmax.js
new file mode 100644
index 0000000..65fddca
--- /dev/null
+++ b/style/imports/minmax.js
@@ -0,0 +1,84 @@
+// minmax.js - written by Andrew Clover <and at doxdesk.com>
+// Adapted for PunBB by Rickard Andersson and Paul Sullivan
+
+/*@cc_on
+ at if (@_win32 && @_jscript_version>4)
+
+var minmax_elements;
+
+function minmax_bind(el) {
+	var em, ms;
+	var st= el.style, cs= el.currentStyle;
+
+	if (minmax_elements==window.undefined) {
+		if (!document.body || !document.body.currentStyle) return;
+		minmax_elements= new Array();
+		window.attachEvent('onresize', minmax_delayout);
+	}
+
+	if (cs['max-width'])
+		st['maxWidth']= cs['max-width'];
+
+	ms= cs['maxWidth'];
+	if (ms && ms!='auto' && ms!='none' && ms!='0' && ms!='') {
+		st.minmaxWidth= cs.width;
+		minmax_elements[minmax_elements.length]= el;
+		minmax_delayout();
+	}
+}
+
+var minmax_delaying= false;
+function minmax_delayout() {
+	if (minmax_delaying) return;
+	minmax_delaying= true;
+	window.setTimeout(minmax_layout, 0);
+}
+
+function minmax_stopdelaying() {
+	minmax_delaying= false;
+}
+
+function minmax_layout() {
+	window.setTimeout(minmax_stopdelaying, 100);
+	var i, el, st, cs, optimal, inrange;
+	for (i= minmax_elements.length; i-->0;) {
+		el= minmax_elements[i]; st= el.style; cs= el.currentStyle;
+
+		st.width= st.minmaxWidth; optimal= el.offsetWidth;
+		inrange= true;
+		if (inrange && cs.minWidth && cs.minWidth!='0' && cs.minWidth!='auto' && cs.minWidth!='') {
+			st.width= cs.minWidth;
+			inrange= (el.offsetWidth<optimal);
+		}
+		if (inrange && cs.maxWidth && cs.maxWidth!='none' && cs.maxWidth!='auto' && cs.maxWidth!='') {
+			st.width= cs.maxWidth;
+			inrange= (el.offsetWidth>optimal);
+		}
+		if (inrange) st.width= st.minmaxWidth;
+	}
+}
+
+var minmax_SCANDELAY= 500;
+
+function minmax_scan() {
+	var el;
+	for (var i= 0; i<document.all.length; i++) {
+		el= document.all[i];
+		if (!el.minmax_bound) {
+			el.minmax_bound= true;
+			minmax_bind(el);
+		}
+	}
+}
+
+var minmax_scanner;
+function minmax_stop() {
+	window.clearInterval(minmax_scanner);
+	minmax_scan();
+}
+
+minmax_scan();
+minmax_scanner= window.setInterval(minmax_scan, minmax_SCANDELAY);
+window.attachEvent('onload', minmax_stop);
+
+ at end @*/
diff --git a/style/index.html b/style/index.html
new file mode 100644
index 0000000..89337b2
--- /dev/null
+++ b/style/index.html
@@ -0,0 +1 @@
+<html><head><title>.</title></head><body>.</body></html>
diff --git a/userlist.php b/userlist.php
new file mode 100644
index 0000000..5c96381
--- /dev/null
+++ b/userlist.php
@@ -0,0 +1,182 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+
+
+if ($pun_user['g_read_board'] == '0')
+	message($lang_common['No view']);
+else if ($pun_user['g_view_users'] == '0')
+	message($lang_common['No permission']);
+
+// Load the userlist.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/userlist.php';
+
+// Load the search.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/search.php';
+
+
+// Determine if we are allowed to view post counts
+$show_post_count = ($pun_config['o_show_post_count'] == '1' || $pun_user['is_admmod']) ? true : false;
+
+$username = isset($_GET['username']) && $pun_user['g_search_users'] == '1' ? pun_trim($_GET['username']) : '';
+$show_group = isset($_GET['show_group']) ? intval($_GET['show_group']) : -1;
+$sort_by = isset($_GET['sort_by']) && (in_array($_GET['sort_by'], array('username', 'registered')) || ($_GET['sort_by'] == 'num_posts' && $show_post_count)) ? $_GET['sort_by'] : 'username';
+$sort_dir = isset($_GET['sort_dir']) && $_GET['sort_dir'] == 'DESC' ? 'DESC' : 'ASC';
+
+// Create any SQL for the WHERE clause
+$where_sql = array();
+$like_command = ($db_type == 'pgsql') ? 'ILIKE' : 'LIKE';
+
+if ($username != '')
+	$where_sql[] = 'u.username '.$like_command.' \''.$db->escape(str_replace('*', '%', $username)).'\'';
+if ($show_group > -1)
+	$where_sql[] = 'u.group_id='.$show_group;
+
+// Fetch user count
+$result = $db->query('SELECT COUNT(id) FROM '.$db->prefix.'users AS u WHERE u.id>1 AND u.group_id!='.PUN_UNVERIFIED.(!empty($where_sql) ? ' AND '.implode(' AND ', $where_sql) : '')) or error('Unable to fetch user list count', __FILE__, __LINE__, $db->error());
+$num_users = $db->result($result);
+
+// Determine the user offset (based on $_GET['p'])
+$num_pages = ceil($num_users / 50);
+
+$p = (!isset($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : intval($_GET['p']);
+$start_from = 50 * ($p - 1);
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), $lang_common['User list']);
+if ($pun_user['g_search_users'] == '1')
+	$focus_element = array('userlist', 'username');
+
+// Generate paging links
+$paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'userlist.php?username='.urlencode($username).'&show_group='.$show_group.'&sort_by='.$sort_by.'&sort_dir='.$sort_dir);
+
+
+define('PUN_ALLOW_INDEX', 1);
+define('PUN_ACTIVE_PAGE', 'userlist');
+require PUN_ROOT.'header.php';
+
+?>
+<div class="blockform">
+	<h2><span><?php echo $lang_common['User list'] ?></span></h2>
+	<div class="box">
+		<form id="userlist" method="get" action="userlist.php">
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_ul['User find legend'] ?></legend>
+					<div class="infldset">
+<?php if ($pun_user['g_search_users'] == '1'): ?>						<label class="conl"><?php echo $lang_common['Username'] ?><br /><input type="text" name="username" value="<?php echo pun_htmlspecialchars($username) ?>" size="25" maxlength="25" /><br /></label>
+<?php endif; ?>						<label class="conl"><?php echo $lang_ul['User group']."\n" ?>
+						<br /><select name="show_group">
+							<option value="-1"<?php if ($show_group == -1) echo ' selected="selected"' ?>><?php echo $lang_ul['All users'] ?></option>
+<?php
+
+$result = $db->query('SELECT g_id, g_title FROM '.$db->prefix.'groups WHERE g_id!='.PUN_GUEST.' ORDER BY g_id') or error('Unable to fetch user group list', __FILE__, __LINE__, $db->error());
+
+while ($cur_group = $db->fetch_assoc($result))
+{
+	if ($cur_group['g_id'] == $show_group)
+		echo "\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'" selected="selected">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+	else
+		echo "\t\t\t\t\t\t\t".'<option value="'.$cur_group['g_id'].'">'.pun_htmlspecialchars($cur_group['g_title']).'</option>'."\n";
+}
+
+?>
+						</select>
+						<br /></label>
+						<label class="conl"><?php echo $lang_search['Sort by']."\n" ?>
+						<br /><select name="sort_by">
+							<option value="username"<?php if ($sort_by == 'username') echo ' selected="selected"' ?>><?php echo $lang_common['Username'] ?></option>
+							<option value="registered"<?php if ($sort_by == 'registered') echo ' selected="selected"' ?>><?php echo $lang_common['Registered'] ?></option>
+<?php if ($show_post_count): ?>							<option value="num_posts"<?php if ($sort_by == 'num_posts') echo ' selected="selected"' ?>><?php echo $lang_ul['No of posts'] ?></option>
+<?php endif; ?>						</select>
+						<br /></label>
+						<label class="conl"><?php echo $lang_search['Sort order']."\n" ?>
+						<br /><select name="sort_dir">
+							<option value="ASC"<?php if ($sort_dir == 'ASC') echo ' selected="selected"' ?>><?php echo $lang_search['Ascending'] ?></option>
+							<option value="DESC"<?php if ($sort_dir == 'DESC') echo ' selected="selected"' ?>><?php echo $lang_search['Descending'] ?></option>
+						</select>
+						<br /></label>
+						<p class="clearb"><?php echo ($pun_user['g_search_users'] == '1' ? $lang_ul['User search info'].' ' : '').$lang_ul['User sort info']; ?></p>
+					</div>
+				</fieldset>
+			</div>
+			<p class="buttons"><input type="submit" name="search" value="<?php echo $lang_common['Submit'] ?>" accesskey="s" /></p>
+		</form>
+	</div>
+</div>
+
+<div class="linkst">
+	<div class="inbox">
+		<p class="pagelink"><?php echo $paging_links ?></p>
+		<div class="clearer"></div>
+	</div>
+</div>
+
+<div id="users1" class="blocktable">
+	<div class="box">
+		<div class="inbox">
+			<table cellspacing="0">
+			<thead>
+				<tr>
+					<th class="tcl" scope="col"><?php echo $lang_common['Username'] ?></th>
+					<th class="tc2" scope="col"><?php echo $lang_common['Title'] ?></th>
+<?php if ($show_post_count): ?>					<th class="tc3" scope="col"><?php echo $lang_common['Posts'] ?></th>
+<?php endif; ?>					<th class="tcr" scope="col"><?php echo $lang_common['Registered'] ?></th>
+				</tr>
+			</thead>
+			<tbody>
+<?php
+
+// Retrieve a list of user IDs, LIMIT is (really) expensive so we only fetch the IDs here then later fetch the remaining data
+$result = $db->query('SELECT u.id FROM '.$db->prefix.'users AS u WHERE u.id>1 AND u.group_id!='.PUN_UNVERIFIED.(!empty($where_sql) ? ' AND '.implode(' AND ', $where_sql) : '').' ORDER BY '.$sort_by.' '.$sort_dir.', u.id ASC LIMIT '.$start_from.', 50') or error('Unable to fetch user IDs', __FILE__, __LINE__, $db->error());
+
+if ($db->num_rows($result))
+{
+	$user_ids = array();
+	for ($i = 0;$cur_user_id = $db->result($result, $i);$i++)
+		$user_ids[] = $cur_user_id;
+
+	// Grab the users
+	$result = $db->query('SELECT u.id, u.username, u.title, u.num_posts, u.registered, g.g_id, g.g_user_title FROM '.$db->prefix.'users AS u LEFT JOIN '.$db->prefix.'groups AS g ON g.g_id=u.group_id WHERE u.id IN('.implode(',', $user_ids).') ORDER BY '.$sort_by.' '.$sort_dir.', u.id ASC') or error('Unable to fetch user list', __FILE__, __LINE__, $db->error());
+
+	while ($user_data = $db->fetch_assoc($result))
+	{
+		$user_title_field = get_title($user_data);
+
+?>
+				<tr>
+					<td class="tcl"><?php echo '<a href="profile.php?id='.$user_data['id'].'">'.pun_htmlspecialchars($user_data['username']).'</a>' ?></td>
+					<td class="tc2"><?php echo $user_title_field ?></td>
+<?php if ($show_post_count): ?>					<td class="tc3"><?php echo forum_number_format($user_data['num_posts']) ?></td>
+<?php endif; ?>
+					<td class="tcr"><?php echo format_time($user_data['registered'], true) ?></td>
+				</tr>
+<?php
+
+	}
+}
+else
+	echo "\t\t\t".'<tr>'."\n\t\t\t\t\t".'<td class="tcl" colspan="'.(($show_post_count) ? 4 : 3).'">'.$lang_search['No hits'].'</td></tr>'."\n";
+
+?>
+			</tbody>
+			</table>
+		</div>
+	</div>
+</div>
+
+<div class="linksb">
+	<div class="inbox">
+		<p class="pagelink"><?php echo $paging_links ?></p>
+		<div class="clearer"></div>
+	</div>
+</div>
+<?php
+
+require PUN_ROOT.'footer.php';
diff --git a/viewforum.php b/viewforum.php
new file mode 100644
index 0000000..95b25d1
--- /dev/null
+++ b/viewforum.php
@@ -0,0 +1,263 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+
+
+if ($pun_user['g_read_board'] == '0')
+	message($lang_common['No view']);
+
+
+$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
+if ($id < 1)
+	message($lang_common['Bad request']);
+
+// Load the viewforum.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/forum.php';
+
+// Fetch some info about the forum
+$result = $db->query('SELECT f.forum_name, f.redirect_url, f.moderators, f.num_topics, f.sort_by, fp.post_topics FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND f.id='.$id) or error('Unable to fetch forum info', __FILE__, __LINE__, $db->error());
+if (!$db->num_rows($result))
+	message($lang_common['Bad request']);
+
+$cur_forum = $db->fetch_assoc($result);
+
+// Is this a redirect forum? In that case, redirect!
+if ($cur_forum['redirect_url'] != '')
+{
+	header('Location: '.$cur_forum['redirect_url']);
+	exit;
+}
+
+// Sort out who the moderators are and if we are currently a moderator (or an admin)
+$mods_array = ($cur_forum['moderators'] != '') ? unserialize($cur_forum['moderators']) : array();
+$is_admmod = ($pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_moderator'] == '1' && array_key_exists($pun_user['username'], $mods_array))) ? true : false;
+
+// Can we or can we not post new topics?
+if (($cur_forum['post_topics'] == '' && $pun_user['g_post_topics'] == '1') || $cur_forum['post_topics'] == '1' || $is_admmod)
+	$post_link = "\t\t\t".'<p class="postlink conr"><a href="post.php?fid='.$id.'">'.$lang_forum['Post topic'].'</a></p>'."\n";
+else
+	$post_link = '';
+
+
+// Get topic/forum tracking data
+if (!$pun_user['is_guest'])
+	$tracked_topics = get_tracked_topics();
+
+
+// Determine the topic offset (based on $_GET['p'])
+$num_pages = ceil($cur_forum['num_topics'] / $pun_user['disp_topics']);
+
+$p = (!isset($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : intval($_GET['p']);
+$start_from = $pun_user['disp_topics'] * ($p - 1);
+
+// Generate paging links
+$paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'viewforum.php?id='.$id);
+
+if ($pun_config['o_feed_type'] == '1')
+	$page_head = array('feed' => '<link rel="alternate" type="application/rss+xml" href="extern.php?action=feed&fid='.$id.'&type=rss" title="'.$lang_common['RSS forum feed'].'" />');
+else if ($pun_config['o_feed_type'] == '2')
+	$page_head = array('feed' => '<link rel="alternate" type="application/atom+xml" href="extern.php?action=feed&fid='.$id.'&type=atom" title="'.$lang_common['Atom forum feed'].'" />');
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), pun_htmlspecialchars($cur_forum['forum_name']));
+define('PUN_ALLOW_INDEX', 1);
+define('PUN_ACTIVE_PAGE', 'index');
+require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+	<div class="inbox crumbsplus">
+		<ul class="crumbs">
+			<li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+			<li><span>» </span><strong><?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></strong></li>
+		</ul>
+		<div class="pagepost">
+			<p class="pagelink conl"><?php echo $paging_links ?></p>
+<?php echo $post_link ?>
+		</div>
+		<div class="clearer"></div>
+	</div>
+</div>
+
+<div id="vf" class="blocktable">
+	<h2><span><?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></span></h2>
+	<div class="box">
+		<div class="inbox">
+			<table cellspacing="0">
+			<thead>
+				<tr>
+					<th class="tcl" scope="col"><?php echo $lang_common['Topic'] ?></th>
+					<th class="tc2" scope="col"><?php echo $lang_common['Replies'] ?></th>
+<?php if ($pun_config['o_topic_views'] == '1'): ?>					<th class="tc3" scope="col"><?php echo $lang_forum['Views'] ?></th>
+<?php endif; ?>					<th class="tcr" scope="col"><?php echo $lang_common['Last post'] ?></th>
+				</tr>
+			</thead>
+			<tbody>
+<?php
+
+// Retrieve a list of topic IDs, LIMIT is (really) expensive so we only fetch the IDs here then later fetch the remaining data
+$result = $db->query('SELECT id FROM '.$db->prefix.'topics WHERE forum_id='.$id.' ORDER BY sticky DESC, '.(($cur_forum['sort_by'] == '1') ? 'posted' : 'last_post').' DESC, id DESC LIMIT '.$start_from.', '.$pun_user['disp_topics']) or error('Unable to fetch topic IDs', __FILE__, __LINE__, $db->error());
+
+// If there are topics in this forum
+if ($db->num_rows($result))
+{
+	$topic_ids = array();
+	for ($i = 0;$cur_topic_id = $db->result($result, $i);$i++)
+		$topic_ids[] = $cur_topic_id;
+
+	// Fetch list of topics to display on this page
+	if ($pun_user['is_guest'] || $pun_config['o_show_dot'] == '0')
+	{
+		// Without "the dot"
+		$sql = 'SELECT id, poster, subject, posted, last_post, last_post_id, last_poster, num_views, num_replies, closed, sticky, moved_to FROM '.$db->prefix.'topics WHERE id IN('.implode(',', $topic_ids).') ORDER BY sticky DESC, '.(($cur_forum['sort_by'] == '1') ? 'posted' : 'last_post').' DESC, id DESC';
+	}
+	else
+	{
+		// With "the dot"
+		$sql = 'SELECT p.poster_id AS has_posted, t.id, t.subject, t.poster, t.posted, t.last_post, t.last_post_id, t.last_poster, t.num_views, t.num_replies, t.closed, t.sticky, t.moved_to FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'posts AS p ON t.id=p.topic_id AND p.poster_id='.$pun_user['id'].' WHERE t.id IN('.implode(',', $topic_ids).') GROUP BY t.id'.($db_type == 'pgsql' ? ', t.subject, t.poster, t.posted, t.last_post, t.last_post_id, t.last_poster, t.num_views, t.num_replie [...]
+	}
+
+	$result = $db->query($sql) or error('Unable to fetch topic list', __FILE__, __LINE__, $db->error());
+
+	$topic_count = 0;
+	while ($cur_topic = $db->fetch_assoc($result))
+	{
+		++$topic_count;
+		$status_text = array();
+		$item_status = ($topic_count % 2 == 0) ? 'roweven' : 'rowodd';
+		$icon_type = 'icon';
+
+		if ($cur_topic['moved_to'] == null)
+			$last_post = '<a href="viewtopic.php?pid='.$cur_topic['last_post_id'].'#p'.$cur_topic['last_post_id'].'">'.format_time($cur_topic['last_post']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_topic['last_poster']).'</span>';
+		else
+			$last_post = '- - -';
+
+		if ($pun_config['o_censoring'] == '1')
+			$cur_topic['subject'] = censor_words($cur_topic['subject']);
+
+		if ($cur_topic['sticky'] == '1')
+		{
+			$item_status .= ' isticky';
+			$status_text[] = '<span class="stickytext">'.$lang_forum['Sticky'].'</span>';
+		}
+
+		if ($cur_topic['moved_to'] != 0)
+		{
+			$subject = '<a href="viewtopic.php?id='.$cur_topic['moved_to'].'">'.pun_htmlspecialchars($cur_topic['subject']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_topic['poster']).'</span>';
+			$status_text[] = '<span class="movedtext">'.$lang_forum['Moved'].'</span>';
+			$item_status .= ' imoved';
+		}
+		else if ($cur_topic['closed'] == '0')
+			$subject = '<a href="viewtopic.php?id='.$cur_topic['id'].'">'.pun_htmlspecialchars($cur_topic['subject']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_topic['poster']).'</span>';
+		else
+		{
+			$subject = '<a href="viewtopic.php?id='.$cur_topic['id'].'">'.pun_htmlspecialchars($cur_topic['subject']).'</a> <span class="byuser">'.$lang_common['by'].' '.pun_htmlspecialchars($cur_topic['poster']).'</span>';
+			$status_text[] = '<span class="closedtext">'.$lang_forum['Closed'].'</span>';
+			$item_status .= ' iclosed';
+		}
+
+		if (!$pun_user['is_guest'] && $cur_topic['last_post'] > $pun_user['last_visit'] && (!isset($tracked_topics['topics'][$cur_topic['id']]) || $tracked_topics['topics'][$cur_topic['id']] < $cur_topic['last_post']) && (!isset($tracked_topics['forums'][$id]) || $tracked_topics['forums'][$id] < $cur_topic['last_post']) && $cur_topic['moved_to'] == null)
+		{
+			$item_status .= ' inew';
+			$icon_type = 'icon icon-new';
+			$subject = '<strong>'.$subject.'</strong>';
+			$subject_new_posts = '<span class="newtext">[ <a href="viewtopic.php?id='.$cur_topic['id'].'&action=new" title="'.$lang_common['New posts info'].'">'.$lang_common['New posts'].'</a> ]</span>';
+		}
+		else
+			$subject_new_posts = null;
+
+		// Insert the status text before the subject
+		$subject = implode(' ', $status_text).' '.$subject;
+
+		// Should we display the dot or not? :)
+		if (!$pun_user['is_guest'] && $pun_config['o_show_dot'] == '1')
+		{
+			if ($cur_topic['has_posted'] == $pun_user['id'])
+			{
+				$subject = '<strong class="ipost">· </strong>'.$subject;
+				$item_status .= ' iposted';
+			}
+		}
+
+		$num_pages_topic = ceil(($cur_topic['num_replies'] + 1) / $pun_user['disp_posts']);
+
+		if ($num_pages_topic > 1)
+			$subject_multipage = '<span class="pagestext">[ '.paginate($num_pages_topic, -1, 'viewtopic.php?id='.$cur_topic['id']).' ]</span>';
+		else
+			$subject_multipage = null;
+
+		// Should we show the "New posts" and/or the multipage links?
+		if (!empty($subject_new_posts) || !empty($subject_multipage))
+		{
+			$subject .= !empty($subject_new_posts) ? ' '.$subject_new_posts : '';
+			$subject .= !empty($subject_multipage) ? ' '.$subject_multipage : '';
+		}
+
+?>
+				<tr class="<?php echo $item_status ?>">
+					<td class="tcl">
+						<div class="<?php echo $icon_type ?>"><div class="nosize"><?php echo forum_number_format($topic_count + $start_from) ?></div></div>
+						<div class="tclcon">
+							<div>
+								<?php echo $subject."\n" ?>
+							</div>
+						</div>
+					</td>
+					<td class="tc2"><?php echo ($cur_topic['moved_to'] == null) ? forum_number_format($cur_topic['num_replies']) : '-' ?></td>
+<?php if ($pun_config['o_topic_views'] == '1'): ?>					<td class="tc3"><?php echo ($cur_topic['moved_to'] == null) ? forum_number_format($cur_topic['num_views']) : '-' ?></td>
+<?php endif; ?>					<td class="tcr"><?php echo $last_post ?></td>
+				</tr>
+<?php
+
+	}
+}
+else
+{
+	$colspan = ($pun_config['o_topic_views'] == '1') ? 4 : 3;
+
+?>
+				<tr class="rowodd inone">
+					<td class="tcl" colspan="<?php echo $colspan ?>">
+						<div class="icon inone"><div class="nosize"><!-- --></div></div>
+						<div class="tclcon">
+							<div>
+								<strong><?php echo $lang_forum['Empty forum'] ?></strong>
+							</div>
+						</div>
+					</td>
+				</tr>
+<?php
+
+}
+
+?>
+			</tbody>
+			</table>
+		</div>
+	</div>
+</div>
+
+<div class="linksb">
+	<div class="inbox crumbsplus">
+		<div class="pagepost">
+			<p class="pagelink conl"><?php echo $paging_links ?></p>
+<?php echo $post_link ?>
+		</div>
+		<ul class="crumbs">
+			<li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+			<li><span>» </span><strong><?php echo pun_htmlspecialchars($cur_forum['forum_name']) ?></strong></li>
+		</ul>
+		<div class="clearer"></div>
+	</div>
+</div>
+<?php
+
+$forum_id = $id;
+$footer_style = 'viewforum';
+require PUN_ROOT.'footer.php';
diff --git a/viewtopic.php b/viewtopic.php
new file mode 100644
index 0000000..7774823
--- /dev/null
+++ b/viewtopic.php
@@ -0,0 +1,434 @@
+<?php
+
+/**
+ * Copyright (C) 2008-2010 FluxBB
+ * based on code by Rickard Andersson copyright (C) 2002-2008 PunBB
+ * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
+ */
+
+define('PUN_ROOT', './');
+require PUN_ROOT.'include/common.php';
+
+
+if ($pun_user['g_read_board'] == '0')
+	message($lang_common['No view']);
+
+
+$action = isset($_GET['action']) ? $_GET['action'] : null;
+$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
+$pid = isset($_GET['pid']) ? intval($_GET['pid']) : 0;
+if ($id < 1 && $pid < 1)
+	message($lang_common['Bad request']);
+
+// Load the viewtopic.php language file
+require PUN_ROOT.'lang/'.$pun_user['language'].'/topic.php';
+
+
+// If a post ID is specified we determine topic ID and page number so we can redirect to the correct message
+if ($pid)
+{
+	$result = $db->query('SELECT topic_id FROM '.$db->prefix.'posts WHERE id='.$pid) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+	if (!$db->num_rows($result))
+		message($lang_common['Bad request']);
+
+	$id = $db->result($result);
+
+	// Determine on what page the post is located (depending on $pun_user['disp_posts'])
+	$result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id='.$id.' ORDER BY posted') or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+	$num_posts = $db->num_rows($result);
+
+	for ($i = 0; $i < $num_posts; ++$i)
+	{
+		$cur_id = $db->result($result, $i);
+		if ($cur_id == $pid)
+			break;
+	}
+	++$i; // we started at 0
+
+	$_GET['p'] = ceil($i / $pun_user['disp_posts']);
+}
+
+// If action=new, we redirect to the first new post (if any)
+else if ($action == 'new')
+{
+	if (!$pun_user['is_guest'])
+	{
+		// We need to check if this topic has been viewed recently by the user
+		$tracked_topics = get_tracked_topics();
+		$last_viewed = isset($tracked_topics['topics'][$id]) ? $tracked_topics['topics'][$id] : $pun_user['last_visit'];
+
+		$result = $db->query('SELECT MIN(id) FROM '.$db->prefix.'posts WHERE topic_id='.$id.' AND posted>'.$last_viewed) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+		$first_new_post_id = $db->result($result);
+
+		if ($first_new_post_id)
+		{
+			header('Location: viewtopic.php?pid='.$first_new_post_id.'#p'.$first_new_post_id);
+			exit;
+		}
+	}
+
+	// If there is no new post, we go to the last post
+	header('Location: viewtopic.php?id='.$id.'&action=last');
+	exit;
+}
+
+// If action=last, we redirect to the last post
+else if ($action == 'last')
+{
+	$result = $db->query('SELECT MAX(id) FROM '.$db->prefix.'posts WHERE topic_id='.$id) or error('Unable to fetch post info', __FILE__, __LINE__, $db->error());
+	$last_post_id = $db->result($result);
+
+	if ($last_post_id)
+	{
+		header('Location: viewtopic.php?pid='.$last_post_id.'#p'.$last_post_id);
+		exit;
+	}
+}
+
+
+// Fetch some info about the topic
+if (!$pun_user['is_guest'])
+	$result = $db->query('SELECT t.subject, t.closed, t.num_replies, t.sticky, t.first_post_id, f.id AS forum_id, f.forum_name, f.moderators, fp.post_replies, s.user_id AS is_subscribed FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'subscriptions AS s ON (t.id=s.topic_id AND s.user_id='.$pun_user['id'].') LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_for [...]
+else
+	$result = $db->query('SELECT t.subject, t.closed, t.num_replies, t.sticky, t.first_post_id, f.id AS forum_id, f.forum_name, f.moderators, fp.post_replies, 0 FROM '.$db->prefix.'topics AS t INNER JOIN '.$db->prefix.'forums AS f ON f.id=t.forum_id LEFT JOIN '.$db->prefix.'forum_perms AS fp ON (fp.forum_id=f.id AND fp.group_id='.$pun_user['g_id'].') WHERE (fp.read_forum IS NULL OR fp.read_forum=1) AND t.id='.$id.' AND t.moved_to IS NULL') or error('Unable to fetch topic info', __FILE__, __ [...]
+
+if (!$db->num_rows($result))
+	message($lang_common['Bad request']);
+
+$cur_topic = $db->fetch_assoc($result);
+
+// Sort out who the moderators are and if we are currently a moderator (or an admin)
+$mods_array = ($cur_topic['moderators'] != '') ? unserialize($cur_topic['moderators']) : array();
+$is_admmod = ($pun_user['g_id'] == PUN_ADMIN || ($pun_user['g_moderator'] == '1' && array_key_exists($pun_user['username'], $mods_array))) ? true : false;
+
+// Can we or can we not post replies?
+if ($cur_topic['closed'] == '0')
+{
+	if (($cur_topic['post_replies'] == '' && $pun_user['g_post_replies'] == '1') || $cur_topic['post_replies'] == '1' || $is_admmod)
+		$post_link = "\t\t\t".'<p class="postlink conr"><a href="post.php?tid='.$id.'">'.$lang_topic['Post reply'].'</a></p>'."\n";
+	else
+		$post_link = '';
+}
+else
+{
+	$post_link = $lang_topic['Topic closed'];
+
+	if ($is_admmod)
+		$post_link .= ' / <a href="post.php?tid='.$id.'">'.$lang_topic['Post reply'].'</a>';
+
+	$post_link = "\t\t\t".'<p class="postlink conr">'.$post_link.'</p>'."\n";
+}
+
+
+// Add/update this topic in our list of tracked topics
+if (!$pun_user['is_guest'])
+{
+	$tracked_topics = get_tracked_topics();
+	$tracked_topics['topics'][$id] = time();
+	set_tracked_topics($tracked_topics);
+}
+
+
+// Determine the post offset (based on $_GET['p'])
+$num_pages = ceil(($cur_topic['num_replies'] + 1) / $pun_user['disp_posts']);
+
+$p = (!isset($_GET['p']) || $_GET['p'] <= 1 || $_GET['p'] > $num_pages) ? 1 : intval($_GET['p']);
+$start_from = $pun_user['disp_posts'] * ($p - 1);
+
+// Generate paging links
+$paging_links = '<span class="pages-label">'.$lang_common['Pages'].' </span>'.paginate($num_pages, $p, 'viewtopic.php?id='.$id);
+
+
+if ($pun_config['o_censoring'] == '1')
+	$cur_topic['subject'] = censor_words($cur_topic['subject']);
+
+
+$quickpost = false;
+if ($pun_config['o_quickpost'] == '1' &&
+	!$pun_user['is_guest'] &&
+	($cur_topic['post_replies'] == '1' || ($cur_topic['post_replies'] == '' && $pun_user['g_post_replies'] == '1')) &&
+	($cur_topic['closed'] == '0' || $is_admmod))
+{
+	$required_fields = array('req_message' => $lang_common['Message']);
+	$quickpost = true;
+}
+
+if (!$pun_user['is_guest'] && $pun_config['o_subscriptions'] == '1')
+{
+	if ($cur_topic['is_subscribed'])
+		// I apologize for the variable naming here. It's a mix of subscription and action I guess :-)
+		$subscraction = "\t\t".'<p class="subscribelink clearb"><span>'.$lang_topic['Is subscribed'].' - </span><a href="misc.php?unsubscribe='.$id.'">'.$lang_topic['Unsubscribe'].'</a></p>'."\n";
+	else
+		$subscraction = "\t\t".'<p class="subscribelink clearb"><a href="misc.php?subscribe='.$id.'">'.$lang_topic['Subscribe'].'</a></p>'."\n";
+}
+else
+	$subscraction = '';
+
+if ($pun_config['o_feed_type'] == '1')
+	$page_head = array('feed' => '<link rel="alternate" type="application/rss+xml" href="extern.php?action=feed&tid='.$id.'&type=rss" title="'.$lang_common['RSS topic feed'].'" />');
+else if ($pun_config['o_feed_type'] == '2')
+	$page_head = array('feed' => '<link rel="alternate" type="application/atom+xml" href="extern.php?action=feed&tid='.$id.'&type=atom" title="'.$lang_common['Atom topic feed'].'" />');
+
+$page_title = array(pun_htmlspecialchars($pun_config['o_board_title']), pun_htmlspecialchars($cur_topic['forum_name']), pun_htmlspecialchars($cur_topic['subject']));
+define('PUN_ALLOW_INDEX', 1);
+define('PUN_ACTIVE_PAGE', 'index');
+require PUN_ROOT.'header.php';
+
+?>
+<div class="linkst">
+	<div class="inbox crumbsplus">
+		<ul class="crumbs">
+			<li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+			<li><span>» </span><a href="viewforum.php?id=<?php echo $cur_topic['forum_id'] ?>"><?php echo pun_htmlspecialchars($cur_topic['forum_name']) ?></a></li>
+			<li><span>» </span><strong><?php echo pun_htmlspecialchars($cur_topic['subject']) ?></strong></li>
+		</ul>
+		<div class="pagepost">
+			<p class="pagelink conl"><?php echo $paging_links ?></p>
+<?php echo $post_link ?>
+		</div>
+		<div class="clearer"></div>
+	</div>
+</div>
+
+<?php
+
+
+require PUN_ROOT.'include/parser.php';
+
+$post_count = 0; // Keep track of post numbers
+
+// Retrieve a list of post IDs, LIMIT is (really) expensive so we only fetch the IDs here then later fetch the remaining data
+$result = $db->query('SELECT id FROM '.$db->prefix.'posts WHERE topic_id='.$id.' ORDER BY id LIMIT '.$start_from.','.$pun_user['disp_posts']) or error('Unable to fetch post IDs', __FILE__, __LINE__, $db->error());
+
+$post_ids = array();
+for ($i = 0;$cur_post_id = $db->result($result, $i);$i++)
+	$post_ids[] = $cur_post_id;
+
+// Retrieve the posts (and their respective poster/online status)
+$result = $db->query('SELECT u.email, u.title, u.url, u.location, u.signature, u.email_setting, u.num_posts, u.registered, u.admin_note, p.id, p.poster AS username, p.poster_id, p.poster_ip, p.poster_email, p.message, p.hide_smilies, p.posted, p.edited, p.edited_by, g.g_id, g.g_user_title, o.user_id AS is_online FROM '.$db->prefix.'posts AS p INNER JOIN '.$db->prefix.'users AS u ON u.id=p.poster_id INNER JOIN '.$db->prefix.'groups AS g ON g.g_id=u.group_id LEFT JOIN '.$db->prefix.'online [...]
+while ($cur_post = $db->fetch_assoc($result))
+{
+	$post_count++;
+	$user_avatar = '';
+	$user_info = array();
+	$user_contacts = array();
+	$post_actions = array();
+	$is_online = '';
+	$signature = '';
+
+	// If the poster is a registered user
+	if ($cur_post['poster_id'] > 1)
+	{
+		if ($pun_user['g_view_users'] == '1')
+			$username = '<a href="profile.php?id='.$cur_post['poster_id'].'">'.pun_htmlspecialchars($cur_post['username']).'</a>';
+		else
+			$username = pun_htmlspecialchars($cur_post['username']);
+
+		$user_title = get_title($cur_post);
+
+		if ($pun_config['o_censoring'] == '1')
+			$user_title = censor_words($user_title);
+
+		// Format the online indicator
+		$is_online = ($cur_post['is_online'] == $cur_post['poster_id']) ? '<strong>'.$lang_topic['Online'].'</strong>' : '<span>'.$lang_topic['Offline'].'</span>';
+
+		if ($pun_config['o_avatars'] == '1' && $pun_user['show_avatars'] != '0')
+		{
+			if (isset($user_avatar_cache[$cur_post['poster_id']]))
+				$user_avatar = $user_avatar_cache[$cur_post['poster_id']];
+			else
+				$user_avatar = $user_avatar_cache[$cur_post['poster_id']] = generate_avatar_markup($cur_post['poster_id']);
+		}
+
+		// We only show location, register date, post count and the contact links if "Show user info" is enabled
+		if ($pun_config['o_show_user_info'] == '1')
+		{
+			if ($cur_post['location'] != '')
+			{
+				if ($pun_config['o_censoring'] == '1')
+					$cur_post['location'] = censor_words($cur_post['location']);
+
+				$user_info[] = '<dd><span>'.$lang_topic['From'].' '.pun_htmlspecialchars($cur_post['location']).'</span></dd>';
+			}
+
+			$user_info[] = '<dd><span>'.$lang_topic['Registered'].' '.format_time($cur_post['registered'], true).'</span></dd>';
+
+			if ($pun_config['o_show_post_count'] == '1' || $pun_user['is_admmod'])
+				$user_info[] = '<dd><span>'.$lang_topic['Posts'].' '.forum_number_format($cur_post['num_posts']).'</span></dd>';
+
+			// Now let's deal with the contact links (Email and URL)
+			if ((($cur_post['email_setting'] == '0' && !$pun_user['is_guest']) || $pun_user['is_admmod']) && $pun_user['g_send_email'] == '1')
+				$user_contacts[] = '<span class="email"><a href="mailto:'.$cur_post['email'].'">'.$lang_common['Email'].'</a></span>';
+			else if ($cur_post['email_setting'] == '1' && !$pun_user['is_guest'] && $pun_user['g_send_email'] == '1')
+				$user_contacts[] = '<span class="email"><a href="misc.php?email='.$cur_post['poster_id'].'">'.$lang_common['Email'].'</a></span>';
+
+			if ($cur_post['url'] != '')
+			{
+				if ($pun_config['o_censoring'] == '1')
+					$cur_post['url'] = censor_words($cur_post['url']);
+
+				$user_contacts[] = '<span class="website"><a href="'.pun_htmlspecialchars($cur_post['url']).'">'.$lang_topic['Website'].'</a></span>';
+			}
+		}
+
+		if ($pun_user['is_admmod'])
+		{
+			$user_info[] = '<dd><span><a href="moderate.php?get_host='.$cur_post['id'].'" title="'.$cur_post['poster_ip'].'">'.$lang_topic['IP address logged'].'</a></span></dd>';
+
+			if ($cur_post['admin_note'] != '')
+				$user_info[] = '<dd><span>'.$lang_topic['Note'].' <strong>'.pun_htmlspecialchars($cur_post['admin_note']).'</strong></span></dd>';
+		}
+	}
+	// If the poster is a guest (or a user that has been deleted)
+	else
+	{
+		$username = pun_htmlspecialchars($cur_post['username']);
+		$user_title = get_title($cur_post);
+
+		if ($pun_user['is_admmod'])
+			$user_info[] = '<dd><span><a href="moderate.php?get_host='.$cur_post['id'].'" title="'.$cur_post['poster_ip'].'">'.$lang_topic['IP address logged'].'</a></span></dd>';
+
+		if ($pun_config['o_show_user_info'] == '1' && $cur_post['poster_email'] != '' && !$pun_user['is_guest'] && $pun_user['g_send_email'] == '1')
+			$user_contacts[] = '<span class="email"><a href="mailto:'.$cur_post['poster_email'].'">'.$lang_common['Email'].'</a></span>';
+	}
+
+	// Generation post action array (quote, edit, delete etc.)
+	if (!$is_admmod)
+	{
+		if (!$pun_user['is_guest'])
+			$post_actions[] = '<li class="postreport"><span><a href="misc.php?report='.$cur_post['id'].'">'.$lang_topic['Report'].'</a></span></li>';
+
+		if ($cur_topic['closed'] == '0')
+		{
+			if ($cur_post['poster_id'] == $pun_user['id'])
+			{
+				if ((($start_from + $post_count) == 1 && $pun_user['g_delete_topics'] == '1') || (($start_from + $post_count) > 1 && $pun_user['g_delete_posts'] == '1'))
+					$post_actions[] = '<li class="postdelete"><span><a href="delete.php?id='.$cur_post['id'].'">'.$lang_topic['Delete'].'</a></span></li>';
+				if ($pun_user['g_edit_posts'] == '1')
+					$post_actions[] = '<li class="postedit"><span><a href="edit.php?id='.$cur_post['id'].'">'.$lang_topic['Edit'].'</a></span></li>';
+			}
+
+			if (($cur_topic['post_replies'] == '' && $pun_user['g_post_replies'] == '1') || $cur_topic['post_replies'] == '1')
+				$post_actions[] = '<li class="postquote"><span><a href="post.php?tid='.$id.'&qid='.$cur_post['id'].'">'.$lang_topic['Quote'].'</a></span></li>';
+		}
+	}
+	else
+	{
+		$post_actions[] = '<li class="postreport"><span><a href="misc.php?report='.$cur_post['id'].'">'.$lang_topic['Report'].'</a></span></li>';
+		$post_actions[] = '<li class="postdelete"><span><a href="delete.php?id='.$cur_post['id'].'">'.$lang_topic['Delete'].'</a></span></li>';
+		$post_actions[] = '<li class="postedit"><span><a href="edit.php?id='.$cur_post['id'].'">'.$lang_topic['Edit'].'</a></span></li>';
+		$post_actions[] = '<li class="postquote"><span><a href="post.php?tid='.$id.'&qid='.$cur_post['id'].'">'.$lang_topic['Quote'].'</a></span></li>';
+	}
+
+	// Perform the main parsing of the message (BBCode, smilies, censor words etc)
+	$cur_post['message'] = parse_message($cur_post['message'], $cur_post['hide_smilies']);
+
+	// Do signature parsing/caching
+	if ($pun_config['o_signatures'] == '1' && $cur_post['signature'] != '' && $pun_user['show_sig'] != '0')
+	{
+		if (isset($signature_cache[$cur_post['poster_id']]))
+			$signature = $signature_cache[$cur_post['poster_id']];
+		else
+		{
+			$signature = parse_signature($cur_post['signature']);
+			$signature_cache[$cur_post['poster_id']] = $signature;
+		}
+	}
+
+?>
+<div id="p<?php echo $cur_post['id'] ?>" class="blockpost<?php echo ($post_count % 2 == 0) ? ' roweven' : ' rowodd' ?><?php if ($cur_post['id'] == $cur_topic['first_post_id']) echo ' firstpost'; ?><?php if ($post_count == 1) echo ' blockpost1'; ?>">
+	<h2><span><span class="conr">#<?php echo ($start_from + $post_count) ?></span> <a href="viewtopic.php?pid=<?php echo $cur_post['id'].'#p'.$cur_post['id'] ?>"><?php echo format_time($cur_post['posted']) ?></a></span></h2>
+	<div class="box">
+		<div class="inbox">
+			<div class="postbody">
+				<div class="postleft">
+					<dl>
+						<dt><strong><?php echo $username ?></strong></dt>
+						<dd class="usertitle"><strong><?php echo $user_title ?></strong></dd>
+<?php if ($user_avatar != '') echo "\t\t\t\t\t\t".'<dd class="postavatar">'.$user_avatar.'</dd>'."\n"; ?>
+<?php if (count($user_info)) echo "\t\t\t\t\t\t".implode("\n\t\t\t\t\t\t", $user_info)."\n"; ?>
+<?php if (count($user_contacts)) echo "\t\t\t\t\t\t".'<dd class="usercontacts">'.implode(' ', $user_contacts).'</dd>'."\n"; ?>
+					</dl>
+				</div>
+				<div class="postright">
+					<h3><?php if ($cur_post['id'] != $cur_topic['first_post_id']) echo $lang_topic['Re'].' '; ?><?php echo pun_htmlspecialchars($cur_topic['subject']) ?></h3>
+					<div class="postmsg">
+						<?php echo $cur_post['message']."\n" ?>
+<?php if ($cur_post['edited'] != '') echo "\t\t\t\t\t\t".'<p class="postedit"><em>'.$lang_topic['Last edit'].' '.pun_htmlspecialchars($cur_post['edited_by']).' ('.format_time($cur_post['edited']).')</em></p>'."\n"; ?>
+					</div>
+<?php if ($signature != '') echo "\t\t\t\t\t".'<div class="postsignature postmsg"><hr />'.$signature.'</div>'."\n"; ?>
+				</div>
+			</div>
+		</div>
+		<div class="inbox">
+			<div class="postfoot clearb">
+				<div class="postfootleft"><?php if ($cur_post['poster_id'] > 1) echo '<p>'.$is_online.'</p>'; ?></div>
+<?php if (count($post_actions)) echo "\t\t\t\t".'<div class="postfootright">'."\n\t\t\t\t\t".'<ul>'."\n\t\t\t\t\t\t".implode("\n\t\t\t\t\t\t", $post_actions)."\n\t\t\t\t\t".'</ul>'."\n\t\t\t\t".'</div>'."\n" ?>
+			</div>
+		</div>
+	</div>
+</div>
+
+<?php
+
+}
+
+?>
+<div class="postlinksb">
+	<div class="inbox crumbsplus">
+		<div class="pagepost">
+			<p class="pagelink conl"><?php echo $paging_links ?></p>
+<?php echo $post_link ?>
+		</div>
+		<ul class="crumbs">
+			<li><a href="index.php"><?php echo $lang_common['Index'] ?></a></li>
+			<li><span>» </span><a href="viewforum.php?id=<?php echo $cur_topic['forum_id'] ?>"><?php echo pun_htmlspecialchars($cur_topic['forum_name']) ?></a></li>
+			<li><span>» </span><strong><?php echo pun_htmlspecialchars($cur_topic['subject']) ?></strong></li>
+		</ul>
+<?php echo $subscraction ?>
+		<div class="clearer"></div>
+	</div>
+</div>
+
+<?php
+
+// Display quick post if enabled
+if ($quickpost)
+{
+
+?>
+<div id="quickpost" class="blockform">
+	<h2><span><?php echo $lang_topic['Quick post'] ?></span></h2>
+	<div class="box">
+		<form id="quickpostform" method="post" action="post.php?tid=<?php echo $id ?>" onsubmit="this.submit.disabled=true;if(process_form(this)){return true;}else{this.submit.disabled=false;return false;}">
+			<div class="inform">
+				<fieldset>
+					<legend><?php echo $lang_common['Write message legend'] ?></legend>
+					<div class="infldset txtarea">
+						<input type="hidden" name="form_sent" value="1" />
+						<input type="hidden" name="form_user" value="<?php echo pun_htmlspecialchars($pun_user['username']) ?>" />
+<?php if ($pun_config['o_subscriptions'] == '1' && ($pun_user['auto_notify'] == '1' || $cur_topic['is_subscribed'])): ?>						<input type="hidden" name="subscribe" value="1" />
+<?php endif; ?>						<label><textarea name="req_message" rows="7" cols="75" tabindex="1"></textarea></label>
+						<ul class="bblinks">
+							<li><span><a href="help.php#bbcode" onclick="window.open(this.href); return false;"><?php echo $lang_common['BBCode'] ?></a> <?php echo ($pun_config['p_message_bbcode'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+							<li><span><a href="help.php#img" onclick="window.open(this.href); return false;"><?php echo $lang_common['img tag'] ?></a> <?php echo ($pun_config['p_message_img_tag'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+							<li><span><a href="help.php#smilies" onclick="window.open(this.href); return false;"><?php echo $lang_common['Smilies'] ?></a> <?php echo ($pun_config['o_smilies'] == '1') ? $lang_common['on'] : $lang_common['off']; ?></span></li>
+						</ul>
+					</div>
+				</fieldset>
+			</div>
+			<p class="buttons"><input type="submit" name="submit" tabindex="2" value="<?php echo $lang_common['Submit'] ?>" accesskey="s" /></p>
+		</form>
+	</div>
+</div>
+<?php
+
+}
+
+// Increment "num_views" for topic
+if ($pun_config['o_topic_views'] == '1')
+	$db->query('UPDATE '.$db->prefix.'topics SET num_views=num_views+1 WHERE id='.$id) or error('Unable to update topic', __FILE__, __LINE__, $db->error());
+
+$forum_id = $cur_topic['forum_id'];
+$footer_style = 'viewtopic';
+require PUN_ROOT.'footer.php';

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the Xfce4-commits mailing list