public inbox for ipfire-scm@lists.ipfire.org
 help / color / mirror / Atom feed
* [git.ipfire.org] IPFire 2.x development tree branch, next, updated. 027614d2dc3107a4e071e8dac49ce8a74c36e4ea
@ 2017-10-04 15:10 git
  0 siblings, 0 replies; only message in thread
From: git @ 2017-10-04 15:10 UTC (permalink / raw)
  To: ipfire-scm

[-- Attachment #1: Type: text/plain, Size: 165185 bytes --]

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "IPFire 2.x development tree".

The branch, next has been updated
       via  027614d2dc3107a4e071e8dac49ce8a74c36e4ea (commit)
       via  0a219160ac7dfe243c94c1f04c182d29206df8ec (commit)
       via  1f06098ba7f4894c9e3dfb744c38f6ef5c576743 (commit)
       via  70f6cba43efc1269712ed17020c2e1cf9d6fe86e (commit)
       via  fb96829a746d5a7ace9835c1aa566f5b0d8bc406 (commit)
       via  afd0cd232fa0d545ebdb70a114f044cde7b99985 (commit)
       via  5f1b9511022bca252c27b9e60284bf4f4486af46 (commit)
       via  1d68e28753c7eae374924e0b4ba5ee7cc985c711 (commit)
       via  963beecdb8bf374d71f22fb0aaf39e5f215318d7 (commit)
       via  78148cc1e55b0fc85e8433ef30e7e4652e6c6c63 (commit)
       via  5511fa319a68abcccd68e575f68383f58cb1a388 (commit)
       via  94e1d35c541d7cbba43c1703a4a590b441bfd3e5 (commit)
       via  9b6227cc2a961148d007a3f5a2d388c231af47b4 (commit)
       via  445b43f87732df392776964a82e3ed3058cfee6b (commit)
       via  c91899797b58c719132445778b3b1f1b017a80f4 (commit)
       via  b020b6190f8dd8c5728731148ab11ded9687dcd0 (commit)
       via  22ce4c3ad6d93879955060e5582384f334ef15f6 (commit)
       via  6033b2710346f9bf4e1182db706775ccde59e5c1 (commit)
       via  4eecd722b746fa325d126849c3f3cc0e743bf70c (commit)
       via  02de682e2118769f1e80fab42f3adb284f60d7c6 (commit)
       via  733de0e4a0b16d4b03f193f7eec7ce51de071a30 (commit)
       via  115b5e624e22212b0b0ba8db852bfec7a402c3fe (commit)
       via  45b1b7cf65ed067d4df4560716e401e4915e02d8 (commit)
       via  497b9108559e8eac8daba6e8e9e8ff83d5040c69 (commit)
       via  eb3e3017cbd65d3b46bf1ca69195e72e56f64442 (commit)
       via  aed55ef9f81ea07da49b5b051354911d87d428ce (commit)
       via  8b0679cc89926ccd7e6f8b42e4a20dd3b3c4e371 (commit)
       via  792f1a3bdf47eba051a752f27e30a92d53a523b3 (commit)
       via  41964aba093a6b37f7534c0990b25c29e38ea371 (commit)
       via  a0b271e4745dafb7daade5dcf4d18406daaddee9 (commit)
       via  733932de749c1c92f8f4b38a53cdbf876dd7fe6e (commit)
       via  9cba29f119459e3ebf5964ee265edd8194042b5a (commit)
       via  5cd9e28bc407f9c3f01a2512a14adda83f89f92e (commit)
       via  58fd5dd482d1e4fdf4a5eb554dd9c4a6ad93369b (commit)
       via  4f75fbfe12ba48d8f4275d76259db8dd02bfe0fa (commit)
       via  810198110e60d29c6ca9e73b622ab350ae9f9f82 (commit)
       via  e7d16ea553d56ad24240f1f6fe1276ecadaa647f (commit)
       via  a41fe924aefb1237c546fdebb55a4fd4c13d580c (commit)
       via  b7a126d9c8bfb9d03752672b90750a23bdd9119a (commit)
       via  8802d70a2836012e8e7a8c17eb5fbc589a944e5c (commit)
       via  e2752bfe719b51b1cb2f9153b8ddfab5d4483938 (commit)
       via  278309b9ef3d1a053b9cc201f1b6e0284334d233 (commit)
       via  297ebdd47bbe5170c57f7ea0079c37830da61827 (commit)
       via  827d3f61da96ebc8881f3fbf283fd67c8bad6809 (commit)
       via  97b91e8a946233d1a5cf90ed36d744dd07f434f9 (commit)
       via  9735e1670aa846e8e022a88260943bfc348927fc (commit)
       via  f8d35875d8c83347c71a724e1d5af06d1660d4c9 (commit)
       via  4b33d29de7ea95d2ae9a128d6fb2304331d9bdc1 (commit)
       via  dbfd2622f58727f4b0f9bed8934a770a2050491f (commit)
       via  966971e58914ef254595532118d37dd604231a5c (commit)
       via  48fb1d3b6935e0c94f022df389e1212ef2dbf9d7 (commit)
       via  050ce7567826a0a45f092a74d8bdb180daa6a86d (commit)
       via  a2c26388dbc9cd388da37aa326bb9e6806d94f12 (commit)
       via  0a02d9bb0c0dd52d2d0f3f45264bdf512eb66380 (commit)
       via  d57e8c67bc7ef3d71cb0c339734e49d9912cf581 (commit)
       via  11fc9575d79644d7830eca59cf90b0d0393bf32b (commit)
       via  abc41f02dd31774d3bc311b4631fb5cf7f8992cd (commit)
       via  43834c49693a6c2ce25e31509e9ab2f8d6f32bd7 (commit)
       via  4ddf453804d10c387297fb1a5acb1e6694daddbc (commit)
       via  a79b220c5a6ad33126276958a506befe8b437a25 (commit)
       via  59a2d9c2d5ab9be52d437f890dcb513a0b4af0c1 (commit)
       via  0806170370f87f949efd327e1ad7377050b3626f (commit)
       via  213335372da6a5cb106e6ded17521b9a70d84d40 (commit)
       via  6945954c445ff0af6196571bd352e8c8fe0e286b (commit)
       via  1d77d1262a0b4b1d0fc975da799d7e85d29c94d3 (commit)
       via  7ab3f84b2ab7a6bbf1428a1e04755ebd3742830f (commit)
       via  2d92a4eca4768b79c6cb2ce4604c0eced3f59a2c (commit)
       via  f339af27044567c36ddc8f27a15a0e4b90297c0b (commit)
       via  45129439bc2b16b712f015a661af4bdfbd815f60 (commit)
       via  6820454e5436954c56f28116ee06d5266cd8484f (commit)
       via  52383f583ed7602a7822564dd497165a1b0084f7 (commit)
       via  b32d9e92bee391e172236e4ee4d7661bfd5a1f11 (commit)
       via  83ba0896f6240a9110198e19a936e86b492cb766 (commit)
       via  fb1d26d1bc50a7a453026a600f763309150320ce (commit)
       via  76ece3236237626450838b2fb18d5ca213549f51 (commit)
       via  07d56062a90d84c8504783b1c70fb06466a90dc2 (commit)
       via  5dc32e5877f7cd8bb96ef6becc9a9370490a1c15 (commit)
       via  f3802750ac9b03d7bfdb767e71436a1387607ec0 (commit)
       via  facfdcd040d158b529da6134140bf42a1c5a2fca (commit)
       via  6d31cfdd58e5826373da4f5eaa07fd31dbcbee7d (commit)
       via  e14adf759ae830dc6f36c97d31e42a449529c502 (commit)
       via  c7e78cc62e6699f00962caf440ed25270c461334 (commit)
       via  e01c5ab71a78b4061cf98fd03be76112842a6bf2 (commit)
       via  47406df0fea9108a9b921884c58c53ca6ce6cb37 (commit)
       via  a6c985284d0d1a56ba8646a817b6bcd85aaa68e3 (commit)
       via  8ef627839fa3e7fd636a243153ed68126f967871 (commit)
       via  c04d2de74ee3b300b59dacdb29d0c174ed269025 (commit)
       via  585703d8a3da1ec4952851efa53d3ab926870b14 (commit)
       via  19cd2b6a7cd5951bb2c74004f27c5be82924880a (commit)
       via  1fc9a43056d516e335e6761443d77bba77ba44d9 (commit)
       via  5ca163cd82d651f5036cda30a4fa4c54c1559439 (commit)
       via  bbaa3613b4e779fed958dc3f7918a65c57576fe8 (commit)
       via  4d9002279f6cd5c73f0e6092c22ae2bfa49460bb (commit)
       via  8b92078917ab3c3fdbdd2070ba803f23aff237d7 (commit)
       via  cec16b8242f27a7718cb54477403651f752c109e (commit)
       via  5906c96206c934e55883390aa0f931b851b4a8b3 (commit)
       via  0d6a599aba381244bd67de8bf6d6a7ed812cbe9e (commit)
       via  0c24f0a9df0cee1c7c7e1cf804fe23856ea7bb6b (commit)
       via  5fbeaf133312d0ca8e28b842f6eb7d31a172d8ab (commit)
       via  7ef66b6199e6215fd3b2b0fb42ec3e03ff1bddc7 (commit)
       via  ee40139d9ad0f5b1f07c5735641e45ebb6bd573a (commit)
      from  e2d934cf2bbc9409e878b719a6688476ab9b6874 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 027614d2dc3107a4e071e8dac49ce8a74c36e4ea
Merge: e2d934c 0a21916
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Wed Oct 4 16:10:07 2017 +0100

    Merge branch 'captive-portal' into next

commit 0a219160ac7dfe243c94c1f04c182d29206df8ec
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Wed Oct 4 14:21:12 2017 +0100

    captive portal: Allow sessions to expire after 8 hours
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 1f06098ba7f4894c9e3dfb744c38f6ef5c576743
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Wed Oct 4 12:55:17 2017 +0100

    captive-portal: Serve Ubuntu font files locally
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 70f6cba43efc1269712ed17020c2e1cf9d6fe86e
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Wed Oct 4 12:47:28 2017 +0100

    Add Ubuntu font family package
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit fb96829a746d5a7ace9835c1aa566f5b0d8bc406
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Fri Sep 22 19:34:29 2017 +0100

    captive: Update configuration for Apache 2.4
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit afd0cd232fa0d545ebdb70a114f044cde7b99985
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Fri May 5 11:48:03 2017 +0100

    Rootfile update
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 5f1b9511022bca252c27b9e60284bf4f4486af46
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Wed May 3 15:39:50 2017 +0200

    captive: Show access page in browser language
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>
    Tested-by: Daniel Weismüller <daniel.weismüller(a)ipfire.org>

commit 1d68e28753c7eae374924e0b4ba5ee7cc985c711
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Wed May 3 15:11:30 2017 +0200

    captive: Do not try to execute the favicon
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 963beecdb8bf374d71f22fb0aaf39e5f215318d7
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Wed May 3 15:01:02 2017 +0200

    captive: Fix passing redirection URL
    
    Reported-by: Daniel Weismüller <daniel.weismueller(a)ipfire.org>
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 78148cc1e55b0fc85e8433ef30e7e4652e6c6c63
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Wed May 3 14:58:23 2017 +0200

    captive: Run apache in HTTP/1.0 mode
    
    Reported-by: Daniel Weismüller <daniel.weismueller(a)ipfire.org>
    Tested-by: Daniel Weismüller <daniel.weismueller(a)ipfire.org>
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 5511fa319a68abcccd68e575f68383f58cb1a388
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Tue May 2 11:04:06 2017 +0100

    captive: Fix another typo in captivectrl
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 94e1d35c541d7cbba43c1703a4a590b441bfd3e5
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Tue May 2 11:02:21 2017 +0100

    captive: Fix typo in German translation
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 9b6227cc2a961148d007a3f5a2d388c231af47b4
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Tue May 2 10:58:57 2017 +0100

    captive: Let apache follow symlinks to load bootstrap
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 445b43f87732df392776964a82e3ed3058cfee6b
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Tue May 2 10:55:58 2017 +0100

    captive: Fix directory permissions
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit c91899797b58c719132445778b3b1f1b017a80f4
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Tue May 2 10:55:19 2017 +0100

    captive: Logo directory no longer exists
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit b020b6190f8dd8c5728731148ab11ded9687dcd0
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Tue May 2 10:37:54 2017 +0100

    Rootfile update
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 22ce4c3ad6d93879955060e5582384f334ef15f6
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Tue May 2 10:37:41 2017 +0100

    apache2: Create captive portal logging directory
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 6033b2710346f9bf4e1182db706775ccde59e5c1
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Tue May 2 10:36:43 2017 +0100

    Actually build bootstrap
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 4eecd722b746fa325d126849c3f3cc0e743bf70c
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Mon May 1 16:13:06 2017 +0100

    Update German translation for captive portal
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 02de682e2118769f1e80fab42f3adb284f60d7c6
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Mon May 1 16:08:24 2017 +0100

    captive: Link .map files as well
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 733de0e4a0b16d4b03f193f7eec7ce51de071a30
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Mon May 1 16:07:40 2017 +0100

    bootstrap: Install map files, too
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 115b5e624e22212b0b0ba8db852bfec7a402c3fe
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Mon May 1 12:39:42 2017 +0100

    captive: Show message when an invalid coupon code was entered
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 45b1b7cf65ed067d4df4560716e401e4915e02d8
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Mon May 1 12:37:44 2017 +0100

    captive: Do not show checkbox when in coupon mode
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 497b9108559e8eac8daba6e8e9e8ff83d5040c69
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Mon May 1 12:35:45 2017 +0100

    captive: Verify that the user actually accepted the terms and conditions
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit eb3e3017cbd65d3b46bf1ca69195e72e56f64442
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Mon May 1 12:24:07 2017 +0100

    captive: Style any error messages
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit aed55ef9f81ea07da49b5b051354911d87d428ce
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Mon May 1 12:06:11 2017 +0100

    captive: Show a message when no coupon code was entered
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 8b0679cc89926ccd7e6f8b42e4a20dd3b3c4e371
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Mon May 1 11:47:04 2017 +0100

    captive: Fix some template updates that I forgot
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 792f1a3bdf47eba051a752f27e30a92d53a523b3
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Mon May 1 11:41:51 2017 +0100

    captive: Drop duplicate function to list active clients
    
    There was a function with different name but essentially
    same functionality which is already existant in &show_clients().
    
    Therefore this patch drops the old function without any functional
    changes.
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 41964aba093a6b37f7534c0990b25c29e38ea371
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Mon May 1 11:40:06 2017 +0100

    captive: Redesign clients list box
    
    Mostly code cleanup
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit a0b271e4745dafb7daade5dcf4d18406daaddee9
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Mon May 1 11:26:04 2017 +0100

    captive: Redesign generated coupons table
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 733932de749c1c92f8f4b38a53cdbf876dd7fe6e
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Fri Apr 28 15:39:05 2017 +0100

    captive: Correctly set coupon lifetime
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 9cba29f119459e3ebf5964ee265edd8194042b5a
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Fri Apr 28 15:12:08 2017 +0100

    captive: Remember selected coupon expiry time
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 5cd9e28bc407f9c3f01a2512a14adda83f89f92e
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Fri Apr 28 15:09:56 2017 +0100

    captive: Allow creating multiple coupons in bulk
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 58fd5dd482d1e4fdf4a5eb554dd9c4a6ad93369b
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Fri Apr 28 14:56:48 2017 +0100

    captive: Reformat times
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 4f75fbfe12ba48d8f4275d76259db8dd02bfe0fa
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Fri Apr 28 14:54:26 2017 +0100

    captive: Cleanup coupon generation block
    
    No functional changes
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 810198110e60d29c6ca9e73b622ab350ae9f9f82
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Fri Apr 28 14:41:57 2017 +0100

    captive: Cleanup logo upload
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit e7d16ea553d56ad24240f1f6fe1276ecadaa647f
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Fri Apr 28 14:38:59 2017 +0100

    captive: Cleanup authentication selection
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit a41fe924aefb1237c546fdebb55a4fd4c13d580c
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Fri Apr 28 14:34:14 2017 +0100

    captive: Code cleanup
    
    No functional changes
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit b7a126d9c8bfb9d03752672b90750a23bdd9119a
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Fri Apr 28 14:29:15 2017 +0100

    captive: Allow uploading JPEG images, too
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 8802d70a2836012e8e7a8c17eb5fbc589a944e5c
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Fri Apr 28 14:21:18 2017 +0100

    captive: Serve logo through an extra CGI script
    
    This CGI script makes saving the logo easier (especially for
    backup purposes).
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit e2752bfe719b51b1cb2f9153b8ddfab5d4483938
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Fri Apr 28 13:53:57 2017 +0100

    captive: Save logo in /var/ipfire/captive
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 278309b9ef3d1a053b9cc201f1b6e0284334d233
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Wed Apr 26 16:44:27 2017 +0100

    captive: Allow selecting the session expiry time for terms
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 297ebdd47bbe5170c57f7ea0079c37830da61827
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Wed Apr 26 12:59:36 2017 +0100

    captive: Group settings together and create branding section
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 827d3f61da96ebc8881f3fbf283fd67c8bad6809
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Wed Apr 26 12:55:40 2017 +0100

    captive: Some more CGI cleanup
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 97b91e8a946233d1a5cf90ed36d744dd07f434f9
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Wed Apr 26 12:50:43 2017 +0100

    captive: Rename "Voucher" mode to "Coupon"
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 9735e1670aa846e8e022a88260943bfc348927fc
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Wed Apr 26 12:27:35 2017 +0100

    captive: Rename "License" mode to "Terms & Conditions"
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit f8d35875d8c83347c71a724e1d5af06d1660d4c9
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Wed Apr 26 12:03:08 2017 +0100

    captive: Allow selecting highlight colour in web interface
    
    To be able to customise the access page, we now allow the
    user to select a brand colour.
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 4b33d29de7ea95d2ae9a128d6fb2304331d9bdc1
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Wed Apr 26 11:53:15 2017 +0100

    captive: Refactor the access page
    
    This brings no functional changes, but cleans up the code
    to re-use more and write less.
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit dbfd2622f58727f4b0f9bed8934a770a2050491f
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Tue Apr 25 12:52:29 2017 +0100

    captive: Get MAC address of a device without calling arp
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 966971e58914ef254595532118d37dd604231a5c
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Tue Apr 25 12:32:05 2017 +0100

    captive: Don't compare action string
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 48fb1d3b6935e0c94f022df389e1212ef2dbf9d7
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Tue Apr 25 12:24:45 2017 +0100

    captive: Import new design
    
    This is the new design of the access page of the captive
    portal. It is based on the Bootstrap 4 grid system and
    reboot but does not use anything else from it.
    
    It is responsive and customisable.
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 050ce7567826a0a45f092a74d8bdb180daa6a86d
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Mon Apr 24 12:25:53 2017 +0100

    bootstrap: New package
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit a2c26388dbc9cd388da37aa326bb9e6806d94f12
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Fri Apr 21 17:34:59 2017 +0100

    captive-portal: Use template engine to render HTML template
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 0a02d9bb0c0dd52d2d0f3f45264bdf512eb66380
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Fri Apr 21 16:46:49 2017 +0100

    captive-portal: Move CGI files to CGI directory
    
    Previously the assets directory has ExecCGI privileges
    which is not at all required and potentially dangerous.
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit d57e8c67bc7ef3d71cb0c339734e49d9912cf581
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Fri Apr 21 16:29:39 2017 +0100

    captive-portal: Code cleanup
    
    No functional changes
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 11fc9575d79644d7830eca59cf90b0d0393bf32b
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Sun Sep 4 09:57:26 2016 +0100

    captive: Fix bug with multiple license clients
    
    If one active client with a license existed, any other client
    authenticating will overwrite the configuration line.
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit abc41f02dd31774d3bc311b4631fb5cf7f8992cd
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Sun Sep 4 09:45:53 2016 +0100

    captive: Do not generally allow access to TCP/1013
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 43834c49693a6c2ce25e31509e9ab2f8d6f32bd7
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Sun Sep 4 09:31:46 2016 +0100

    captive: Only make CGI script executable in document root
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 4ddf453804d10c387297fb1a5acb1e6694daddbc
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Sun Sep 4 09:27:29 2016 +0100

    captive: Reindent apache configuration
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit a79b220c5a6ad33126276958a506befe8b437a25
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Sun Sep 4 09:25:41 2016 +0100

    captive: Log into default apache log files
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 59a2d9c2d5ab9be52d437f890dcb513a0b4af0c1
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Tue Jul 12 08:13:04 2016 +0200

    Captive-portal: Design changes
    
    When choosing voucher as authentication type there is no need to display the license agreement textbox
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 0806170370f87f949efd327e1ad7377050b3626f
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Thu Jul 7 11:27:40 2016 +0200

    BUG11141: Redesign of configuration website
    
    To improve the user experience, the configuration part of generating new vouchers has been reworked.
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 213335372da6a5cb106e6ded17521b9a70d84d40
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Tue Jul 5 11:39:16 2016 +0200

    BUG11140: Captive logo dimensions
    
    Now the min and max logo dimensions are shown in webinterface.
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 6945954c445ff0af6196571bd352e8c8fe0e286b
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Wed Jun 29 12:47:55 2016 +0200

    BUG11137: Captive save action messes up the form
    
    When configuring the captiveportal for the first time the form
    will be empty after clicking on save button if not all relevant fields are set.
    
    Now the settings are stored even if there is an error.
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 1d77d1262a0b4b1d0fc975da799d7e85d29c94d3
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Wed Jun 29 12:36:18 2016 +0200

    BUG11139: Captive voucher table too wide
    
    Set table to 100% and the remark textfield to 96% (cellwidth)
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 7ab3f84b2ab7a6bbf1428a1e04755ebd3742830f
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Mon Jun 20 20:26:00 2016 +0100

    Improve the wording of the Captive Portal configuration site
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 2d92a4eca4768b79c6cb2ce4604c0eced3f59a2c
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Mon Jun 20 15:50:05 2016 +0100

    Update translations
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit f339af27044567c36ddc8f27a15a0e4b90297c0b
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Mon Jun 20 15:49:26 2016 +0100

    Rootfile update
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 45129439bc2b16b712f015a661af4bdfbd815f60
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Thu Feb 11 07:21:29 2016 +0100

    Captive-Portal: fix fontsize of generated voucher
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit 6820454e5436954c56f28116ee06d5266cd8484f
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Wed Feb 10 15:36:49 2016 +0100

    Captive-Portal: Fix folder permissions
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit 52383f583ed7602a7822564dd497165a1b0084f7
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Wed Feb 10 15:05:35 2016 +0100

    Captive-Portal: fix some typos and missing dir
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit b32d9e92bee391e172236e4ee4d7661bfd5a1f11
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Wed Feb 10 10:20:52 2016 +0100

    Captive-Portal: Add logo upload feature
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit 83ba0896f6240a9110198e19a936e86b492cb766
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Wed Feb 10 10:09:23 2016 +0100

    Captive-portal: Add directory for logo upload
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit fb1d26d1bc50a7a453026a600f763309150320ce
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Mon Feb 8 16:38:34 2016 +0000

    captivectrl: Add protection against DNS tunnels
    
    Limit the amount of DNS traffic for each client that
    has not registered, yet.
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 76ece3236237626450838b2fb18d5ca213549f51
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Mon Feb 8 16:37:21 2016 +0000

    captivectrl: Skip all lines that start with #
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 07d56062a90d84c8504783b1c70fb06466a90dc2
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Mon Feb 8 10:56:01 2016 +0100

    Captive-Portal: fix cleanup script
    
    The cleanup-script did not write back the hash after the expired voucher
    was delted
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit 5dc32e5877f7cd8bb96ef6becc9a9370490a1c15
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Thu Feb 4 13:26:35 2016 +0100

    Captive-Portal: add Errormessage when wrong code is entered
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit f3802750ac9b03d7bfdb767e71436a1387607ec0
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Thu Feb 4 12:04:47 2016 +0100

    Captive-Portal: fix wrong expiretime of unused vouchers
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit facfdcd040d158b529da6134140bf42a1c5a2fca
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Thu Feb 4 12:01:30 2016 +0100

    Captive-Portal: fix voucher form
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit 6d31cfdd58e5826373da4f5eaa07fd31dbcbee7d
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Thu Feb 4 08:10:11 2016 +0100

    Captive-Portal: add logging to syslog
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit e14adf759ae830dc6f36c97d31e42a449529c502
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Thu Feb 4 07:30:05 2016 +0100

    Captive-Portal: SHow always licencebox in config
    
    Also fix index.cgi to show individual title
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit c7e78cc62e6699f00962caf440ed25270c461334
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Tue Feb 2 14:30:13 2016 +0100

    Captive-Portal: several design changes
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit e01c5ab71a78b4061cf98fd03be76112842a6bf2
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Mon Feb 1 16:14:50 2016 +0100

    Captive-Portal: redesign Webinterface
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit 47406df0fea9108a9b921884c58c53ca6ce6cb37
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Fri Jan 29 12:52:27 2016 +0100

    Captive-Portal: fix some rootfiles
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit a6c985284d0d1a56ba8646a817b6bcd85aaa68e3
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Fri Jan 29 09:17:22 2016 +0100

    Captive-Portal: add backup-part
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit 8ef627839fa3e7fd636a243153ed68126f967871
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Fri Jan 29 09:02:31 2016 +0100

    Captive-Portal: add captive logdir to apache2 rootfile
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit c04d2de74ee3b300b59dacdb29d0c174ed269025
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Fri Jan 29 08:56:16 2016 +0100

    Captive-Portal: add files to configroot rootfile
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit 585703d8a3da1ec4952851efa53d3ab926870b14
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Fri Jan 29 08:52:32 2016 +0100

    Captive-Portal: Add files for webinterface tio rootfile
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit 19cd2b6a7cd5951bb2c74004f27c5be82924880a
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Fri Jan 29 08:44:01 2016 +0100

    Captive-Portal: add vhost config to apache2 rootfile
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit 1fc9a43056d516e335e6761443d77bba77ba44d9
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Thu Jan 28 16:21:51 2016 +0100

    Captive-Portal: create dir for cative logfiles
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit 5ca163cd82d651f5036cda30a4fa4c54c1559439
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Thu Jan 28 16:14:34 2016 +0100

    Captive-Portal: add captive dirs and files to configroot
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit bbaa3613b4e779fed958dc3f7918a65c57576fe8
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Thu Jan 28 16:08:32 2016 +0100

    Captive-Portal: add captive chains to firewall initscript
    
    When loading the initscript of the firewall the neccessary chains for
    the captive portalneed to be created.
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit 4d9002279f6cd5c73f0e6092c22ae2bfa49460bb
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Thu Jan 28 16:05:53 2016 +0100

    Captive-Portal: add crontab and cleanup scripts
    
    The cleanup script is called every hour and deletes expired clients from
    the clients file.
    every night the captivectrl warpper runs once to flush the chains and
    reload rules for active clients
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit 8b92078917ab3c3fdbdd2070ba803f23aff237d7
Author: Alexander Marx <alexander.marx(a)ipfire.org>
Date:   Thu Jan 28 11:18:59 2016 +0100

    Captive-Portal: add web-part
    
    Introduce new Captive-Portal.
    Here we add the menu, apache configuration (vhost), IPFire configuration
    website and Captive-Portal Access site. Also the languagefiles are
    updated.
    
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

commit cec16b8242f27a7718cb54477403651f752c109e
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Thu Feb 4 14:34:11 2016 +0000

    captivectrl: Move sure that the settings are always initialised
    
    This just removes a compiler warning.
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 5906c96206c934e55883390aa0f931b851b4a8b3
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Thu Feb 4 14:29:57 2016 +0000

    wirelessctrl: Disable MAC filter on blue if captive portal is enabled
    
    Fixes #11038
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 0d6a599aba381244bd67de8bf6d6a7ed812cbe9e
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Sun Jan 31 21:38:26 2016 +0000

    captivectrl: Add missing space character
    
    The iptables argument list was botched. Oops. Sorry.
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 0c24f0a9df0cee1c7c7e1cf804fe23856ea7bb6b
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Sat Jan 30 14:46:55 2016 +0000

    captivectrl: Support unlimited leases
    
    When the expiry time equals zero, the lease will have
    no time constraints. The IP address will also be removed
    as it might probably change.
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 5fbeaf133312d0ca8e28b842f6eb7d31a172d8ab
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Sat Jan 30 14:30:13 2016 +0000

    captivectrl: Allow empty IP addresses
    
    Probably required for very long leases
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit 7ef66b6199e6215fd3b2b0fb42ec3e03ff1bddc7
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Sat Jan 30 13:55:44 2016 +0000

    captivectrl: Change format of clients configuration
    
    We store the start of the lease now and the time in
    seconds after the lease expires
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>

commit ee40139d9ad0f5b1f07c5735641e45ebb6bd573a
Author: Michael Tremer <michael.tremer(a)ipfire.org>
Date:   Thu Jan 28 14:24:07 2016 +0100

    Captive Portal: add c-wrapper captivectrl
    
    This wrapper reads the captive settings and clients and sets the
    firewall access rules. It is called every time the config changed or
    everytime that a client changes. Also this wrapper is later called once
    hourly to flush the chains and rebuild rules for actual clients.
    
    Signed-off-by: Michael Tremer <michael.tremer(a)ipfire.org>
    Signed-off-by: Alexander Marx <alexander.marx(a)ipfire.org>

-----------------------------------------------------------------------

Summary of changes:
 config/backup/include                              |   1 +
 config/cfgroot/general-functions.pl                |   8 +-
 config/cfgroot/lang.pl                             |  14 +
 config/cfgroot/network-functions.pl                |  20 +
 config/cron/crontab                                |   6 +
 config/httpd/vhosts.d/captive.conf                 |  30 +
 config/menu/30-network.menu                        |   5 +
 config/rootfiles/common/apache2                    |   4 +
 config/rootfiles/common/bootstrap                  |  17 +
 config/rootfiles/common/configroot                 |  10 +-
 config/rootfiles/common/misc-progs                 |   1 +
 config/rootfiles/common/stage2                     |   1 +
 config/rootfiles/common/ubuntu-font-family         |  13 +
 config/rootfiles/common/web-user-interface         |  20 +-
 config/rootfiles/common/x86_64/stage2              |   1 +
 doc/language_issues.de                             |  26 +
 doc/language_issues.en                             |  23 +
 doc/language_issues.es                             |  35 ++
 doc/language_issues.fr                             |  35 ++
 doc/language_issues.it                             |  35 ++
 doc/language_issues.nl                             |  35 ++
 doc/language_issues.pl                             |  35 ++
 doc/language_issues.ru                             |  35 ++
 doc/language_issues.tr                             |  35 ++
 doc/language_missings                              | 200 +++++++
 html/cgi-bin/captive.cgi                           | 666 +++++++++++++++++++++
 html/cgi-bin/captive/index.cgi                     | 238 ++++++++
 .../pakfire.conf => html/cgi-bin/captive/logo.cgi  |  38 +-
 html/cgi-bin/{mdstat.cgi => captive/redirect.cgi}  |  49 +-
 html/cgi-bin/logs.cgi/log.dat                      |   2 +
 html/html/captive/assets/captive.css               | 194 ++++++
 html/html/{ => captive/assets}/favicon.ico         | Bin
 .../images/tux2.png => captive/assets/ipfire.png}  | Bin
 html/html/captive/template.html                    |  79 +++
 langs/de/cgi-bin/de.pl                             |  49 ++
 langs/en/cgi-bin/en.pl                             |  62 +-
 lfs/apache2                                        |   3 +
 lfs/{usb_modeswitch => bootstrap}                  |  20 +-
 lfs/configroot                                     |   4 +-
 lfs/stage2                                         |   1 +
 lfs/{mdadm => ubuntu-font-family}                  |  21 +-
 lfs/web-user-interface                             |  21 +-
 make.sh                                            |   2 +
 src/initscripts/system/firewall                    |  11 +
 src/misc-progs/Makefile                            |   3 +-
 src/misc-progs/captivectrl.c                       | 369 ++++++++++++
 src/misc-progs/wirelessctrl.c                      |  17 +
 .../getdnsfromdhcpc.pl => scripts/captive-cleanup} |  43 +-
 48 files changed, 2441 insertions(+), 96 deletions(-)
 create mode 100644 config/httpd/vhosts.d/captive.conf
 create mode 100644 config/rootfiles/common/bootstrap
 create mode 100644 config/rootfiles/common/ubuntu-font-family
 create mode 100755 html/cgi-bin/captive.cgi
 create mode 100755 html/cgi-bin/captive/index.cgi
 copy src/pakfire/pakfire.conf => html/cgi-bin/captive/logo.cgi (70%)
 copy html/cgi-bin/{mdstat.cgi => captive/redirect.cgi} (57%)
 mode change 100644 => 100755
 create mode 100644 html/html/captive/assets/captive.css
 copy html/html/{ => captive/assets}/favicon.ico (100%)
 copy html/html/{themes/ipfire/images/tux2.png => captive/assets/ipfire.png} (100%)
 create mode 100644 html/html/captive/template.html
 copy lfs/{usb_modeswitch => bootstrap} (87%)
 copy lfs/{mdadm => ubuntu-font-family} (88%)
 create mode 100644 src/misc-progs/captivectrl.c
 copy src/{initscripts/helper/getdnsfromdhcpc.pl => scripts/captive-cleanup} (65%)
 mode change 100644 => 100755

Difference in files:
diff --git a/config/backup/include b/config/backup/include
index e1edeff..ba78c69 100644
--- a/config/backup/include
+++ b/config/backup/include
@@ -41,3 +41,4 @@
 /root/.bash_history
 /var/ipfire/ethernet/aliases
 /var/ipfire/ethernet/wireless
+/var/ipfire/captive/*
diff --git a/config/cfgroot/general-functions.pl b/config/cfgroot/general-functions.pl
index f448c34..0577afe 100644
--- a/config/cfgroot/general-functions.pl
+++ b/config/cfgroot/general-functions.pl
@@ -235,7 +235,13 @@ sub writehashpart
 sub age {
 	my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
 		$atime, $mtime, $ctime, $blksize, $blocks) = stat $_[0];
-	my $totalsecs = time() - $mtime;
+	my $t = time() - $mtime;
+
+	return &format_time($t);
+}
+
+sub format_time($) {
+	my $totalsecs = shift;
 	my @s = ();
 
 	my $secs = $totalsecs % 60;
diff --git a/config/cfgroot/lang.pl b/config/cfgroot/lang.pl
index c77e0a0..2b09c4a 100644
--- a/config/cfgroot/lang.pl
+++ b/config/cfgroot/lang.pl
@@ -179,4 +179,18 @@ sub FindWebLanguage() {
 	return undef;
 }
 
+sub DetectBrowserLanguages() {
+	my $langs = $ENV{"HTTP_ACCEPT_LANGUAGE"};
+	my @results = ();
+
+	foreach my $lang (split /[,;]/, $langs) {
+		# Drop all q= arguments
+		next if ($lang =~ m/^q=/);
+
+		push(@results, $lang);
+	}
+
+	return @results;
+}
+
 1;
diff --git a/config/cfgroot/network-functions.pl b/config/cfgroot/network-functions.pl
index 66f1ed5..93b8190 100644
--- a/config/cfgroot/network-functions.pl
+++ b/config/cfgroot/network-functions.pl
@@ -374,6 +374,26 @@ sub wifi_get_signal_level($) {
 
 	return $signal_level;
 }
+
+sub get_hardware_address($) {
+	my $ip_address = shift;
+	my $ret;
+
+	open(FILE, "/proc/net/arp") or die("Could not read ARP table");
+
+	while (<FILE>) {
+		my ($ip_addr, $hwtype, $flags, $hwaddr, $mask, $device) = split(/\s+/, $_);
+		if ($ip_addr eq $ip_address) {
+			$ret = $hwaddr;
+			last;
+		}
+	}
+
+	close(FILE);
+
+	return $ret;
+}
+
 1;
 
 # Remove the next line to enable the testsuite
diff --git a/config/cron/crontab b/config/cron/crontab
index c6d8a72..4561f4a 100644
--- a/config/cron/crontab
+++ b/config/cron/crontab
@@ -65,6 +65,12 @@ HOME=/
 # Retry sending spooled mails regularly
 %hourly * /usr/sbin/dma -q
 
+# Cleanup captive clients
+%hourly * /usr/bin/captive-cleanup
+
+# Reload captive firewall rules
+%nightly * 23-1   /usr/local/bin/captivectrl >/dev/null
+
 # Cleanup the mail spool directory
 %weekly * * /usr/sbin/dma-cleanup-spool
 
diff --git a/config/httpd/vhosts.d/captive.conf b/config/httpd/vhosts.d/captive.conf
new file mode 100644
index 0000000..e4e1d78
--- /dev/null
+++ b/config/httpd/vhosts.d/captive.conf
@@ -0,0 +1,30 @@
+Listen 1013
+
+<VirtualHost *:1013>
+	DocumentRoot /srv/web/ipfire/html/captive
+
+	# Close all connections as soon as a reply has been sent.
+	# Most browsers open loads of connections which then causes
+	# the access page being loaded again after a correct coupon
+	# code was entered.
+	KeepAlive Off
+
+	ScriptAlias /cgi-bin/ /srv/web/ipfire/cgi-bin/captive/
+	Alias /assets/ /srv/web/ipfire/html/captive/assets/
+
+	Alias /favicon.ico /srv/web/ipfire/html/captive/assets/favicon.ico
+
+	# All unknown URIs will be redirected to the first
+	# redirector script.
+	ScriptAliasMatch .* /srv/web/ipfire/cgi-bin/captive/redirect.cgi
+
+	<Directory /srv/web/ipfire/cgi-bin/captive>
+		Options ExecCGI
+		Require all granted
+	</Directory>
+
+	<Directory /srv/web/ipfire/html/captive>
+		Options +FollowSymlinks
+		Require all granted
+	</Directory>
+</VirtualHost>
diff --git a/config/menu/30-network.menu b/config/menu/30-network.menu
index 8e1336a..137fd68 100644
--- a/config/menu/30-network.menu
+++ b/config/menu/30-network.menu
@@ -23,6 +23,11 @@
 				 'title' => "$Lang::tr{'dhcp server'}",
 				 'enabled' => 1,
 				 };
+	$subnetwork->{'32.captive'} = {'caption' => $Lang::tr{'Captive menu'},
+			        'uri' => '/cgi-bin/captive.cgi',
+			        'title' => $Lang::tr{'Captive menu'},
+			        'enabled' => 1,
+			        };
     $subnetwork->{'40.scheduler'} = {
 				 'caption' => $Lang::tr{'connscheduler'},
 				 'uri' => '/cgi-bin/connscheduler.cgi',
diff --git a/config/rootfiles/common/apache2 b/config/rootfiles/common/apache2
index 233301a..040b482 100644
--- a/config/rootfiles/common/apache2
+++ b/config/rootfiles/common/apache2
@@ -43,6 +43,7 @@ etc/httpd/conf/server-tuning.conf
 etc/httpd/conf/ssl-global.conf
 etc/httpd/conf/uid.conf
 #etc/httpd/conf/vhosts.d
+etc/httpd/conf/vhosts.d/captive.conf
 etc/httpd/conf/vhosts.d/ipfire-interface-ssl.conf
 etc/httpd/conf/vhosts.d/ipfire-interface.conf
 #etc/httpd/conf/vhosts.d/nagios.conf
@@ -91,6 +92,8 @@ etc/httpd/conf/vhosts.d/ipfire-interface.conf
 #srv/web/ipfire/error/include/top.html
 #srv/web/ipfire/htdocs
 #srv/web/ipfire/htdocs/index.html
+#srv/web/ipfire/html
+srv/web/ipfire/html/captive
 #srv/web/ipfire/icons
 #srv/web/ipfire/icons/README
 #srv/web/ipfire/icons/README.html
@@ -1662,3 +1665,4 @@ usr/sbin/httpd
 #usr/share/man/man8/rotatelogs.8
 #usr/share/man/man8/suexec.8
 var/log/httpd
+var/log/httpd/captive
diff --git a/config/rootfiles/common/bootstrap b/config/rootfiles/common/bootstrap
new file mode 100644
index 0000000..cc5566a
--- /dev/null
+++ b/config/rootfiles/common/bootstrap
@@ -0,0 +1,17 @@
+#usr/share/bootstrap
+#usr/share/bootstrap/css
+usr/share/bootstrap/css/bootstrap.css
+usr/share/bootstrap/css/bootstrap.css.map
+usr/share/bootstrap/css/bootstrap-grid.css
+usr/share/bootstrap/css/bootstrap-grid.css.map
+usr/share/bootstrap/css/bootstrap-grid.min.css
+usr/share/bootstrap/css/bootstrap-grid.min.css.map
+usr/share/bootstrap/css/bootstrap.min.css
+usr/share/bootstrap/css/bootstrap.min.css.map
+usr/share/bootstrap/css/bootstrap-reboot.css
+usr/share/bootstrap/css/bootstrap-reboot.css.map
+usr/share/bootstrap/css/bootstrap-reboot.min.css
+usr/share/bootstrap/css/bootstrap-reboot.min.css.map
+#usr/share/bootstrap/js
+usr/share/bootstrap/js/bootstrap.js
+usr/share/bootstrap/js/bootstrap.min.js
diff --git a/config/rootfiles/common/configroot b/config/rootfiles/common/configroot
index 911a780..58424ed 100644
--- a/config/rootfiles/common/configroot
+++ b/config/rootfiles/common/configroot
@@ -11,6 +11,11 @@ var/ipfire/auth
 var/ipfire/backup/exclude.user
 var/ipfire/backup/include.user
 var/ipfire/ca
+var/ipfire/captive
+var/ipfire/captive/agb.txt
+var/ipfire/captive/clients
+var/ipfire/captive/settings
+var/ipfire/captive/voucher_out
 var/ipfire/certs
 #var/ipfire/certs/index.txt
 #var/ipfire/certs/serial
@@ -51,16 +56,14 @@ var/ipfire/extrahd
 #var/ipfire/extrahd/settings
 var/ipfire/firewall
 #var/ipfire/firewall/config
-#var/ipfire/firewall/dmz
 #var/ipfire/firewall/geoipblock
 #var/ipfire/firewall/input
-#var/ipfire/firewall/nat
 #var/ipfire/firewall/outgoing
 #var/ipfire/firewall/p2protocols
 #var/ipfire/firewall/settings
 var/ipfire/fwhosts
-#var/ipfire/fwhosts/customgroups
 #var/ipfire/fwhosts/customgeoipgrp
+#var/ipfire/fwhosts/customgroups
 #var/ipfire/fwhosts/customhosts
 #var/ipfire/fwhosts/customnetworks
 #var/ipfire/fwhosts/customservicegrp
@@ -116,6 +119,7 @@ var/ipfire/menu.d/70-log.menu
 #var/ipfire/menu.d/EX-tor.menu
 #var/ipfire/menu.d/EX-wlanap.menu
 var/ipfire/modem
+var/ipfire/modem-lib.pl
 #var/ipfire/modem/defaults
 #var/ipfire/modem/settings
 var/ipfire/modem-lib.pl
diff --git a/config/rootfiles/common/misc-progs b/config/rootfiles/common/misc-progs
index f94f319..7891795 100644
--- a/config/rootfiles/common/misc-progs
+++ b/config/rootfiles/common/misc-progs
@@ -1,6 +1,7 @@
 usr/local/bin/addonctrl
 #usr/local/bin/applejuicectrl
 usr/local/bin/backupctrl
+usr/local/bin/captivectrl
 #usr/local/bin/clamavctrl
 usr/local/bin/collectdctrl
 usr/local/bin/ddnsctrl
diff --git a/config/rootfiles/common/stage2 b/config/rootfiles/common/stage2
index ec36774..8c6dd40 100644
--- a/config/rootfiles/common/stage2
+++ b/config/rootfiles/common/stage2
@@ -69,6 +69,7 @@ run
 #sbin
 #srv
 #usr/bin
+usr/bin/captive-cleanup
 #usr/bin/perl
 #usr/include
 #usr/lib
diff --git a/config/rootfiles/common/ubuntu-font-family b/config/rootfiles/common/ubuntu-font-family
new file mode 100644
index 0000000..b9f7c0b
--- /dev/null
+++ b/config/rootfiles/common/ubuntu-font-family
@@ -0,0 +1,13 @@
+usr/share/fonts/Ubuntu-B.ttf
+usr/share/fonts/Ubuntu-BI.ttf
+usr/share/fonts/Ubuntu-C.ttf
+usr/share/fonts/Ubuntu-L.ttf
+usr/share/fonts/Ubuntu-LI.ttf
+usr/share/fonts/Ubuntu-M.ttf
+usr/share/fonts/Ubuntu-MI.ttf
+usr/share/fonts/Ubuntu-R.ttf
+usr/share/fonts/Ubuntu-RI.ttf
+usr/share/fonts/UbuntuMono-B.ttf
+usr/share/fonts/UbuntuMono-BI.ttf
+usr/share/fonts/UbuntuMono-R.ttf
+usr/share/fonts/UbuntuMono-RI.ttf
diff --git a/config/rootfiles/common/web-user-interface b/config/rootfiles/common/web-user-interface
index f204f16..531f8bd 100644
--- a/config/rootfiles/common/web-user-interface
+++ b/config/rootfiles/common/web-user-interface
@@ -8,6 +8,11 @@ srv/web/ipfire/cgi-bin/aliases.cgi
 srv/web/ipfire/cgi-bin/atm-status.cgi
 srv/web/ipfire/cgi-bin/backup.cgi
 srv/web/ipfire/cgi-bin/bluetooth.cgi
+#srv/web/ipfire/cgi-bin/captive
+srv/web/ipfire/cgi-bin/captive.cgi
+srv/web/ipfire/cgi-bin/captive/index.cgi
+srv/web/ipfire/cgi-bin/captive/logo.cgi
+srv/web/ipfire/cgi-bin/captive/redirect.cgi
 srv/web/ipfire/cgi-bin/chpasswd.cgi
 srv/web/ipfire/cgi-bin/connections.cgi
 srv/web/ipfire/cgi-bin/connscheduler.cgi
@@ -23,8 +28,8 @@ srv/web/ipfire/cgi-bin/fireinfo.cgi
 srv/web/ipfire/cgi-bin/firewall.cgi
 srv/web/ipfire/cgi-bin/fwhosts.cgi
 srv/web/ipfire/cgi-bin/geoip-block.cgi
-#srv/web/ipfire/cgi-bin/guardian.cgi
 srv/web/ipfire/cgi-bin/gpl.cgi
+#srv/web/ipfire/cgi-bin/guardian.cgi
 srv/web/ipfire/cgi-bin/gui.cgi
 srv/web/ipfire/cgi-bin/hardwaregraphs.cgi
 srv/web/ipfire/cgi-bin/hosts.cgi
@@ -89,6 +94,19 @@ srv/web/ipfire/cgi-bin/wirelessclient.cgi
 srv/web/ipfire/cgi-bin/wlanap.cgi
 #srv/web/ipfire/html
 srv/web/ipfire/html/blob.gif
+#srv/web/ipfire/html/captive
+#srv/web/ipfire/html/captive/assets
+srv/web/ipfire/html/captive/assets/Ubuntu-L.ttf
+srv/web/ipfire/html/captive/assets/Ubuntu-M.ttf
+srv/web/ipfire/html/captive/assets/Ubuntu-R.ttf
+srv/web/ipfire/html/captive/assets/bootstrap-grid.min.css
+srv/web/ipfire/html/captive/assets/bootstrap-grid.min.css.map
+srv/web/ipfire/html/captive/assets/bootstrap-reboot.min.css
+srv/web/ipfire/html/captive/assets/bootstrap-reboot.min.css.map
+srv/web/ipfire/html/captive/assets/captive.css
+srv/web/ipfire/html/captive/assets/favicon.ico
+srv/web/ipfire/html/captive/assets/ipfire.png
+srv/web/ipfire/html/captive/template.html
 srv/web/ipfire/html/clwarn.cgi
 srv/web/ipfire/html/dial.cgi
 srv/web/ipfire/html/favicon.ico
diff --git a/config/rootfiles/common/x86_64/stage2 b/config/rootfiles/common/x86_64/stage2
index 95eff36..2897adc 100644
--- a/config/rootfiles/common/x86_64/stage2
+++ b/config/rootfiles/common/x86_64/stage2
@@ -70,6 +70,7 @@ run
 #sbin
 #srv
 #usr/bin
+usr/bin/captive-cleanup
 #usr/bin/perl
 #usr/include
 #usr/lib
diff --git a/doc/language_issues.de b/doc/language_issues.de
index b9f7747..f5d6d36 100644
--- a/doc/language_issues.de
+++ b/doc/language_issues.de
@@ -1,4 +1,24 @@
 WARNING: translation string unused: Async logging enabled
+WARNING: translation string unused: Captive 1day
+WARNING: translation string unused: Captive 1month
+WARNING: translation string unused: Captive 1week
+WARNING: translation string unused: Captive activate
+WARNING: translation string unused: Captive auth_lic
+WARNING: translation string unused: Captive auth_vou
+WARNING: translation string unused: Captive err doublevoucher
+WARNING: translation string unused: Captive expire
+WARNING: translation string unused: Captive genvoucher
+WARNING: translation string unused: Captive invalid logosize
+WARNING: translation string unused: Captive invalid_voucher
+WARNING: translation string unused: Captive ip
+WARNING: translation string unused: Captive logo_set
+WARNING: translation string unused: Captive logo_upload
+WARNING: translation string unused: Captive logo_upload1
+WARNING: translation string unused: Captive nr
+WARNING: translation string unused: Captive time
+WARNING: translation string unused: Captive voactive
+WARNING: translation string unused: Captive voucher
+WARNING: translation string unused: Captive vout
 WARNING: translation string unused: ConnSched scheduler
 WARNING: translation string unused: ConnSched select profile
 WARNING: translation string unused: HDD temperature
@@ -665,9 +685,11 @@ WARNING: translation string unused: xtaccess all error
 WARNING: translation string unused: xtaccess bad transfert
 WARNING: translation string unused: year-graph
 WARNING: translation string unused: yearly firewallhits
+WARNING: untranslated string: 24 hours
 WARNING: untranslated string: Scan for Songs
 WARNING: untranslated string: addons
 WARNING: untranslated string: bytes
+WARNING: untranslated string: captive
 WARNING: untranslated string: community rules
 WARNING: untranslated string: dead peer detection
 WARNING: untranslated string: emerging rules
@@ -714,11 +736,15 @@ WARNING: untranslated string: ike lifetime should be between 1 and 8 hours
 WARNING: untranslated string: info messages
 WARNING: untranslated string: no data
 WARNING: untranslated string: none
+WARNING: untranslated string: one hour
+WARNING: untranslated string: one month
+WARNING: untranslated string: one week
 WARNING: untranslated string: qos add subclass
 WARNING: untranslated string: route config changed
 WARNING: untranslated string: routing config added
 WARNING: untranslated string: routing config changed
 WARNING: untranslated string: routing table
 WARNING: untranslated string: show tls-auth key
+WARNING: untranslated string: unlimited
 WARNING: untranslated string: vpn force mobike
 WARNING: untranslated string: vpn statistics n2n
diff --git a/doc/language_issues.en b/doc/language_issues.en
index f2f6ab7..1a22e28 100644
--- a/doc/language_issues.en
+++ b/doc/language_issues.en
@@ -1,4 +1,25 @@
 WARNING: translation string unused: Async logging enabled
+WARNING: translation string unused: Captive 1day
+WARNING: translation string unused: Captive 1month
+WARNING: translation string unused: Captive 1week
+WARNING: translation string unused: Captive activate
+WARNING: translation string unused: Captive auth_lic
+WARNING: translation string unused: Captive auth_vou
+WARNING: translation string unused: Captive err doublevoucher
+WARNING: translation string unused: Captive expire
+WARNING: translation string unused: Captive heading terms
+WARNING: translation string unused: Captive heading voucher
+WARNING: translation string unused: Captive invalid coupon
+WARNING: translation string unused: Captive invalid logosize
+WARNING: translation string unused: Captive invalid_voucher
+WARNING: translation string unused: Captive ip
+WARNING: translation string unused: Captive logo_set
+WARNING: translation string unused: Captive nr
+WARNING: translation string unused: Captive please enter a coupon code
+WARNING: translation string unused: Captive time
+WARNING: translation string unused: Captive voactive
+WARNING: translation string unused: Captive voucher
+WARNING: translation string unused: Captive vout
 WARNING: translation string unused: ConnSched scheduler
 WARNING: translation string unused: ConnSched select profile
 WARNING: translation string unused: HDD temperature
@@ -701,8 +722,10 @@ WARNING: translation string unused: xtaccess all error
 WARNING: translation string unused: xtaccess bad transfert
 WARNING: translation string unused: year-graph
 WARNING: translation string unused: yearly firewallhits
+WARNING: untranslated string: Captive clients
 WARNING: untranslated string: Scan for Songs
 WARNING: untranslated string: bytes
+WARNING: untranslated string: captive
 WARNING: untranslated string: fwhost cust geoipgrp
 WARNING: untranslated string: fwhost err hostip
 WARNING: untranslated string: guardian block a host
diff --git a/doc/language_issues.es b/doc/language_issues.es
index d34d63f..a701010 100644
--- a/doc/language_issues.es
+++ b/doc/language_issues.es
@@ -618,6 +618,35 @@ WARNING: translation string unused: xtaccess all error
 WARNING: translation string unused: xtaccess bad transfert
 WARNING: translation string unused: year-graph
 WARNING: translation string unused: yearly firewallhits
+WARNING: untranslated string: 24 hours
+WARNING: untranslated string: Captive ACTIVATE
+WARNING: untranslated string: Captive GAIN ACCESS
+WARNING: untranslated string: Captive activated
+WARNING: untranslated string: Captive active on
+WARNING: untranslated string: Captive agree tac
+WARNING: untranslated string: Captive authentication
+WARNING: untranslated string: Captive brand color
+WARNING: untranslated string: Captive branding
+WARNING: untranslated string: Captive client session expiry time
+WARNING: untranslated string: Captive clients
+WARNING: untranslated string: Captive config
+WARNING: untranslated string: Captive coupon
+WARNING: untranslated string: Captive expiry time
+WARNING: untranslated string: Captive generate coupon
+WARNING: untranslated string: Captive issued coupons
+WARNING: untranslated string: Captive logo uploaded
+WARNING: untranslated string: Captive mac
+WARNING: untranslated string: Captive menu
+WARNING: untranslated string: Captive noexpiretime
+WARNING: untranslated string: Captive nolimit
+WARNING: untranslated string: Captive please accept the terms and conditions
+WARNING: untranslated string: Captive terms
+WARNING: untranslated string: Captive terms short
+WARNING: untranslated string: Captive title
+WARNING: untranslated string: Captive upload logo
+WARNING: untranslated string: Captive upload logo recommendations
+WARNING: untranslated string: Captive vouchervalid
+WARNING: untranslated string: Captive wrong ext
 WARNING: untranslated string: ConnSched dial
 WARNING: untranslated string: ConnSched hangup
 WARNING: untranslated string: ConnSched reboot
@@ -649,6 +678,7 @@ WARNING: untranslated string: bit
 WARNING: untranslated string: block
 WARNING: untranslated string: bytes
 WARNING: untranslated string: capabilities
+WARNING: untranslated string: captive
 WARNING: untranslated string: ccd add
 WARNING: untranslated string: ccd choose net
 WARNING: untranslated string: ccd client options
@@ -729,6 +759,7 @@ WARNING: untranslated string: drop action1
 WARNING: untranslated string: drop action2
 WARNING: untranslated string: drop forward
 WARNING: untranslated string: drop outgoing
+WARNING: untranslated string: eight hours
 WARNING: untranslated string: email config
 WARNING: untranslated string: email empty field
 WARNING: untranslated string: email invalid
@@ -1019,6 +1050,9 @@ WARNING: untranslated string: no hardware random number generator
 WARNING: untranslated string: none
 WARNING: untranslated string: not a valid dh key
 WARNING: untranslated string: notice
+WARNING: untranslated string: one hour
+WARNING: untranslated string: one month
+WARNING: untranslated string: one week
 WARNING: untranslated string: openvpn default
 WARNING: untranslated string: openvpn destination port used
 WARNING: untranslated string: openvpn fragment allowed with udp
@@ -1143,6 +1177,7 @@ WARNING: untranslated string: tor use exit nodes
 WARNING: untranslated string: unblock
 WARNING: untranslated string: unblock all
 WARNING: untranslated string: uncheck all
+WARNING: untranslated string: unlimited
 WARNING: untranslated string: uplink
 WARNING: untranslated string: uplink bit rate
 WARNING: untranslated string: upload dh key
diff --git a/doc/language_issues.fr b/doc/language_issues.fr
index 4a91676..1389cbb 100644
--- a/doc/language_issues.fr
+++ b/doc/language_issues.fr
@@ -628,6 +628,35 @@ WARNING: translation string unused: xtaccess all error
 WARNING: translation string unused: xtaccess bad transfert
 WARNING: translation string unused: year-graph
 WARNING: translation string unused: yearly firewallhits
+WARNING: untranslated string: 24 hours
+WARNING: untranslated string: Captive ACTIVATE
+WARNING: untranslated string: Captive GAIN ACCESS
+WARNING: untranslated string: Captive activated
+WARNING: untranslated string: Captive active on
+WARNING: untranslated string: Captive agree tac
+WARNING: untranslated string: Captive authentication
+WARNING: untranslated string: Captive brand color
+WARNING: untranslated string: Captive branding
+WARNING: untranslated string: Captive client session expiry time
+WARNING: untranslated string: Captive clients
+WARNING: untranslated string: Captive config
+WARNING: untranslated string: Captive coupon
+WARNING: untranslated string: Captive expiry time
+WARNING: untranslated string: Captive generate coupon
+WARNING: untranslated string: Captive issued coupons
+WARNING: untranslated string: Captive logo uploaded
+WARNING: untranslated string: Captive mac
+WARNING: untranslated string: Captive menu
+WARNING: untranslated string: Captive noexpiretime
+WARNING: untranslated string: Captive nolimit
+WARNING: untranslated string: Captive please accept the terms and conditions
+WARNING: untranslated string: Captive terms
+WARNING: untranslated string: Captive terms short
+WARNING: untranslated string: Captive title
+WARNING: untranslated string: Captive upload logo
+WARNING: untranslated string: Captive upload logo recommendations
+WARNING: untranslated string: Captive vouchervalid
+WARNING: untranslated string: Captive wrong ext
 WARNING: untranslated string: ConnSched dial
 WARNING: untranslated string: ConnSched hangup
 WARNING: untranslated string: ConnSched reboot
@@ -658,6 +687,7 @@ WARNING: untranslated string: bit
 WARNING: untranslated string: block
 WARNING: untranslated string: bytes
 WARNING: untranslated string: capabilities
+WARNING: untranslated string: captive
 WARNING: untranslated string: ccd add
 WARNING: untranslated string: ccd choose net
 WARNING: untranslated string: ccd client options
@@ -739,6 +769,7 @@ WARNING: untranslated string: drop action1
 WARNING: untranslated string: drop action2
 WARNING: untranslated string: drop forward
 WARNING: untranslated string: drop outgoing
+WARNING: untranslated string: eight hours
 WARNING: untranslated string: email config
 WARNING: untranslated string: email empty field
 WARNING: untranslated string: email invalid
@@ -1038,6 +1069,9 @@ WARNING: untranslated string: not a valid dh key
 WARNING: untranslated string: notice
 WARNING: untranslated string: ntp common settings
 WARNING: untranslated string: ntp sync
+WARNING: untranslated string: one hour
+WARNING: untranslated string: one month
+WARNING: untranslated string: one week
 WARNING: untranslated string: openvpn default
 WARNING: untranslated string: openvpn destination port used
 WARNING: untranslated string: openvpn fragment allowed with udp
@@ -1157,6 +1191,7 @@ WARNING: untranslated string: tor use exit nodes
 WARNING: untranslated string: unblock
 WARNING: untranslated string: unblock all
 WARNING: untranslated string: uncheck all
+WARNING: untranslated string: unlimited
 WARNING: untranslated string: uplink
 WARNING: untranslated string: uplink bit rate
 WARNING: untranslated string: upload dh key
diff --git a/doc/language_issues.it b/doc/language_issues.it
index 0baf400..5b921a8 100644
--- a/doc/language_issues.it
+++ b/doc/language_issues.it
@@ -695,6 +695,35 @@ WARNING: translation string unused: xtaccess all error
 WARNING: translation string unused: xtaccess bad transfert
 WARNING: translation string unused: year-graph
 WARNING: translation string unused: yearly firewallhits
+WARNING: untranslated string: 24 hours
+WARNING: untranslated string: Captive ACTIVATE
+WARNING: untranslated string: Captive GAIN ACCESS
+WARNING: untranslated string: Captive activated
+WARNING: untranslated string: Captive active on
+WARNING: untranslated string: Captive agree tac
+WARNING: untranslated string: Captive authentication
+WARNING: untranslated string: Captive brand color
+WARNING: untranslated string: Captive branding
+WARNING: untranslated string: Captive client session expiry time
+WARNING: untranslated string: Captive clients
+WARNING: untranslated string: Captive config
+WARNING: untranslated string: Captive coupon
+WARNING: untranslated string: Captive expiry time
+WARNING: untranslated string: Captive generate coupon
+WARNING: untranslated string: Captive issued coupons
+WARNING: untranslated string: Captive logo uploaded
+WARNING: untranslated string: Captive mac
+WARNING: untranslated string: Captive menu
+WARNING: untranslated string: Captive noexpiretime
+WARNING: untranslated string: Captive nolimit
+WARNING: untranslated string: Captive please accept the terms and conditions
+WARNING: untranslated string: Captive terms
+WARNING: untranslated string: Captive terms short
+WARNING: untranslated string: Captive title
+WARNING: untranslated string: Captive upload logo
+WARNING: untranslated string: Captive upload logo recommendations
+WARNING: untranslated string: Captive vouchervalid
+WARNING: untranslated string: Captive wrong ext
 WARNING: untranslated string: MTU settings
 WARNING: untranslated string: Number of Countries for the pie chart
 WARNING: untranslated string: Scan for Songs
@@ -707,6 +736,7 @@ WARNING: untranslated string: advproxy group required
 WARNING: untranslated string: application layer gateways
 WARNING: untranslated string: block
 WARNING: untranslated string: bytes
+WARNING: untranslated string: captive
 WARNING: untranslated string: check all
 WARNING: untranslated string: dhcp dns enable update
 WARNING: untranslated string: dhcp dns key name
@@ -715,6 +745,7 @@ WARNING: untranslated string: dhcp dns update algo
 WARNING: untranslated string: dhcp dns update secret
 WARNING: untranslated string: dl client arch insecure
 WARNING: untranslated string: dnssec disabled warning
+WARNING: untranslated string: eight hours
 WARNING: untranslated string: email config
 WARNING: untranslated string: email empty field
 WARNING: untranslated string: email invalid
@@ -808,6 +839,9 @@ WARNING: untranslated string: masquerading enabled
 WARNING: untranslated string: messages
 WARNING: untranslated string: no data
 WARNING: untranslated string: none
+WARNING: untranslated string: one hour
+WARNING: untranslated string: one month
+WARNING: untranslated string: one week
 WARNING: untranslated string: outgoing compression in bytes per second
 WARNING: untranslated string: outgoing overhead in bytes per second
 WARNING: untranslated string: ovpn add conf
@@ -825,6 +859,7 @@ WARNING: untranslated string: search
 WARNING: untranslated string: unblock
 WARNING: untranslated string: unblock all
 WARNING: untranslated string: uncheck all
+WARNING: untranslated string: unlimited
 WARNING: untranslated string: uplink bit rate
 WARNING: untranslated string: vpn broken
 WARNING: untranslated string: vpn connecting
diff --git a/doc/language_issues.nl b/doc/language_issues.nl
index 4f64b5d..16f6c0d 100644
--- a/doc/language_issues.nl
+++ b/doc/language_issues.nl
@@ -691,6 +691,35 @@ WARNING: translation string unused: xtaccess all error
 WARNING: translation string unused: xtaccess bad transfert
 WARNING: translation string unused: year-graph
 WARNING: translation string unused: yearly firewallhits
+WARNING: untranslated string: 24 hours
+WARNING: untranslated string: Captive ACTIVATE
+WARNING: untranslated string: Captive GAIN ACCESS
+WARNING: untranslated string: Captive activated
+WARNING: untranslated string: Captive active on
+WARNING: untranslated string: Captive agree tac
+WARNING: untranslated string: Captive authentication
+WARNING: untranslated string: Captive brand color
+WARNING: untranslated string: Captive branding
+WARNING: untranslated string: Captive client session expiry time
+WARNING: untranslated string: Captive clients
+WARNING: untranslated string: Captive config
+WARNING: untranslated string: Captive coupon
+WARNING: untranslated string: Captive expiry time
+WARNING: untranslated string: Captive generate coupon
+WARNING: untranslated string: Captive issued coupons
+WARNING: untranslated string: Captive logo uploaded
+WARNING: untranslated string: Captive mac
+WARNING: untranslated string: Captive menu
+WARNING: untranslated string: Captive noexpiretime
+WARNING: untranslated string: Captive nolimit
+WARNING: untranslated string: Captive please accept the terms and conditions
+WARNING: untranslated string: Captive terms
+WARNING: untranslated string: Captive terms short
+WARNING: untranslated string: Captive title
+WARNING: untranslated string: Captive upload logo
+WARNING: untranslated string: Captive upload logo recommendations
+WARNING: untranslated string: Captive vouchervalid
+WARNING: untranslated string: Captive wrong ext
 WARNING: untranslated string: MTU settings
 WARNING: untranslated string: Number of Countries for the pie chart
 WARNING: untranslated string: Scan for Songs
@@ -706,6 +735,7 @@ WARNING: untranslated string: atm device
 WARNING: untranslated string: block
 WARNING: untranslated string: bytes
 WARNING: untranslated string: capabilities
+WARNING: untranslated string: captive
 WARNING: untranslated string: check all
 WARNING: untranslated string: default
 WARNING: untranslated string: dh
@@ -727,6 +757,7 @@ WARNING: untranslated string: dnssec not supported
 WARNING: untranslated string: dnssec validating
 WARNING: untranslated string: download tls-auth key
 WARNING: untranslated string: drop outgoing
+WARNING: untranslated string: eight hours
 WARNING: untranslated string: email config
 WARNING: untranslated string: email empty field
 WARNING: untranslated string: email invalid
@@ -840,6 +871,9 @@ WARNING: untranslated string: nameserver
 WARNING: untranslated string: no data
 WARNING: untranslated string: none
 WARNING: untranslated string: not a valid dh key
+WARNING: untranslated string: one hour
+WARNING: untranslated string: one month
+WARNING: untranslated string: one week
 WARNING: untranslated string: outgoing compression in bytes per second
 WARNING: untranslated string: outgoing overhead in bytes per second
 WARNING: untranslated string: ovpn add conf
@@ -871,6 +905,7 @@ WARNING: untranslated string: ta key
 WARNING: untranslated string: unblock
 WARNING: untranslated string: unblock all
 WARNING: untranslated string: uncheck all
+WARNING: untranslated string: unlimited
 WARNING: untranslated string: uplink bit rate
 WARNING: untranslated string: upload dh key
 WARNING: untranslated string: vendor
diff --git a/doc/language_issues.pl b/doc/language_issues.pl
index d34d63f..a701010 100644
--- a/doc/language_issues.pl
+++ b/doc/language_issues.pl
@@ -618,6 +618,35 @@ WARNING: translation string unused: xtaccess all error
 WARNING: translation string unused: xtaccess bad transfert
 WARNING: translation string unused: year-graph
 WARNING: translation string unused: yearly firewallhits
+WARNING: untranslated string: 24 hours
+WARNING: untranslated string: Captive ACTIVATE
+WARNING: untranslated string: Captive GAIN ACCESS
+WARNING: untranslated string: Captive activated
+WARNING: untranslated string: Captive active on
+WARNING: untranslated string: Captive agree tac
+WARNING: untranslated string: Captive authentication
+WARNING: untranslated string: Captive brand color
+WARNING: untranslated string: Captive branding
+WARNING: untranslated string: Captive client session expiry time
+WARNING: untranslated string: Captive clients
+WARNING: untranslated string: Captive config
+WARNING: untranslated string: Captive coupon
+WARNING: untranslated string: Captive expiry time
+WARNING: untranslated string: Captive generate coupon
+WARNING: untranslated string: Captive issued coupons
+WARNING: untranslated string: Captive logo uploaded
+WARNING: untranslated string: Captive mac
+WARNING: untranslated string: Captive menu
+WARNING: untranslated string: Captive noexpiretime
+WARNING: untranslated string: Captive nolimit
+WARNING: untranslated string: Captive please accept the terms and conditions
+WARNING: untranslated string: Captive terms
+WARNING: untranslated string: Captive terms short
+WARNING: untranslated string: Captive title
+WARNING: untranslated string: Captive upload logo
+WARNING: untranslated string: Captive upload logo recommendations
+WARNING: untranslated string: Captive vouchervalid
+WARNING: untranslated string: Captive wrong ext
 WARNING: untranslated string: ConnSched dial
 WARNING: untranslated string: ConnSched hangup
 WARNING: untranslated string: ConnSched reboot
@@ -649,6 +678,7 @@ WARNING: untranslated string: bit
 WARNING: untranslated string: block
 WARNING: untranslated string: bytes
 WARNING: untranslated string: capabilities
+WARNING: untranslated string: captive
 WARNING: untranslated string: ccd add
 WARNING: untranslated string: ccd choose net
 WARNING: untranslated string: ccd client options
@@ -729,6 +759,7 @@ WARNING: untranslated string: drop action1
 WARNING: untranslated string: drop action2
 WARNING: untranslated string: drop forward
 WARNING: untranslated string: drop outgoing
+WARNING: untranslated string: eight hours
 WARNING: untranslated string: email config
 WARNING: untranslated string: email empty field
 WARNING: untranslated string: email invalid
@@ -1019,6 +1050,9 @@ WARNING: untranslated string: no hardware random number generator
 WARNING: untranslated string: none
 WARNING: untranslated string: not a valid dh key
 WARNING: untranslated string: notice
+WARNING: untranslated string: one hour
+WARNING: untranslated string: one month
+WARNING: untranslated string: one week
 WARNING: untranslated string: openvpn default
 WARNING: untranslated string: openvpn destination port used
 WARNING: untranslated string: openvpn fragment allowed with udp
@@ -1143,6 +1177,7 @@ WARNING: untranslated string: tor use exit nodes
 WARNING: untranslated string: unblock
 WARNING: untranslated string: unblock all
 WARNING: untranslated string: uncheck all
+WARNING: untranslated string: unlimited
 WARNING: untranslated string: uplink
 WARNING: untranslated string: uplink bit rate
 WARNING: untranslated string: upload dh key
diff --git a/doc/language_issues.ru b/doc/language_issues.ru
index d07c421..a2a771e 100644
--- a/doc/language_issues.ru
+++ b/doc/language_issues.ru
@@ -619,7 +619,36 @@ WARNING: translation string unused: wlanap wlan services
 WARNING: translation string unused: xtaccess all error
 WARNING: translation string unused: xtaccess bad transfert
 WARNING: translation string unused: yearly firewallhits
+WARNING: untranslated string: 24 hours
 WARNING: untranslated string: Add a route
+WARNING: untranslated string: Captive ACTIVATE
+WARNING: untranslated string: Captive GAIN ACCESS
+WARNING: untranslated string: Captive activated
+WARNING: untranslated string: Captive active on
+WARNING: untranslated string: Captive agree tac
+WARNING: untranslated string: Captive authentication
+WARNING: untranslated string: Captive brand color
+WARNING: untranslated string: Captive branding
+WARNING: untranslated string: Captive client session expiry time
+WARNING: untranslated string: Captive clients
+WARNING: untranslated string: Captive config
+WARNING: untranslated string: Captive coupon
+WARNING: untranslated string: Captive expiry time
+WARNING: untranslated string: Captive generate coupon
+WARNING: untranslated string: Captive issued coupons
+WARNING: untranslated string: Captive logo uploaded
+WARNING: untranslated string: Captive mac
+WARNING: untranslated string: Captive menu
+WARNING: untranslated string: Captive noexpiretime
+WARNING: untranslated string: Captive nolimit
+WARNING: untranslated string: Captive please accept the terms and conditions
+WARNING: untranslated string: Captive terms
+WARNING: untranslated string: Captive terms short
+WARNING: untranslated string: Captive title
+WARNING: untranslated string: Captive upload logo
+WARNING: untranslated string: Captive upload logo recommendations
+WARNING: untranslated string: Captive vouchervalid
+WARNING: untranslated string: Captive wrong ext
 WARNING: untranslated string: ConnSched dial
 WARNING: untranslated string: ConnSched hangup
 WARNING: untranslated string: ConnSched reboot
@@ -651,6 +680,7 @@ WARNING: untranslated string: bit
 WARNING: untranslated string: block
 WARNING: untranslated string: bytes
 WARNING: untranslated string: capabilities
+WARNING: untranslated string: captive
 WARNING: untranslated string: ccd add
 WARNING: untranslated string: ccd choose net
 WARNING: untranslated string: ccd client options
@@ -733,6 +763,7 @@ WARNING: untranslated string: drop action1
 WARNING: untranslated string: drop action2
 WARNING: untranslated string: drop forward
 WARNING: untranslated string: drop outgoing
+WARNING: untranslated string: eight hours
 WARNING: untranslated string: email config
 WARNING: untranslated string: email empty field
 WARNING: untranslated string: email invalid
@@ -1021,6 +1052,9 @@ WARNING: untranslated string: no hardware random number generator
 WARNING: untranslated string: none
 WARNING: untranslated string: not a valid dh key
 WARNING: untranslated string: notice
+WARNING: untranslated string: one hour
+WARNING: untranslated string: one month
+WARNING: untranslated string: one week
 WARNING: untranslated string: openvpn default
 WARNING: untranslated string: openvpn destination port used
 WARNING: untranslated string: openvpn fragment allowed with udp
@@ -1138,6 +1172,7 @@ WARNING: untranslated string: tor use exit nodes
 WARNING: untranslated string: unblock
 WARNING: untranslated string: unblock all
 WARNING: untranslated string: uncheck all
+WARNING: untranslated string: unlimited
 WARNING: untranslated string: uplink
 WARNING: untranslated string: uplink bit rate
 WARNING: untranslated string: upload dh key
diff --git a/doc/language_issues.tr b/doc/language_issues.tr
index 034dd2a..96849cd 100644
--- a/doc/language_issues.tr
+++ b/doc/language_issues.tr
@@ -703,10 +703,41 @@ WARNING: translation string unused: xtaccess all error
 WARNING: translation string unused: xtaccess bad transfert
 WARNING: translation string unused: year-graph
 WARNING: translation string unused: yearly firewallhits
+WARNING: untranslated string: 24 hours
+WARNING: untranslated string: Captive ACTIVATE
+WARNING: untranslated string: Captive GAIN ACCESS
+WARNING: untranslated string: Captive activated
+WARNING: untranslated string: Captive active on
+WARNING: untranslated string: Captive agree tac
+WARNING: untranslated string: Captive authentication
+WARNING: untranslated string: Captive brand color
+WARNING: untranslated string: Captive branding
+WARNING: untranslated string: Captive client session expiry time
+WARNING: untranslated string: Captive clients
+WARNING: untranslated string: Captive config
+WARNING: untranslated string: Captive coupon
+WARNING: untranslated string: Captive expiry time
+WARNING: untranslated string: Captive generate coupon
+WARNING: untranslated string: Captive issued coupons
+WARNING: untranslated string: Captive logo uploaded
+WARNING: untranslated string: Captive mac
+WARNING: untranslated string: Captive menu
+WARNING: untranslated string: Captive noexpiretime
+WARNING: untranslated string: Captive nolimit
+WARNING: untranslated string: Captive please accept the terms and conditions
+WARNING: untranslated string: Captive terms
+WARNING: untranslated string: Captive terms short
+WARNING: untranslated string: Captive title
+WARNING: untranslated string: Captive upload logo
+WARNING: untranslated string: Captive upload logo recommendations
+WARNING: untranslated string: Captive vouchervalid
+WARNING: untranslated string: Captive wrong ext
 WARNING: untranslated string: Scan for Songs
 WARNING: untranslated string: application layer gateways
 WARNING: untranslated string: bytes
+WARNING: untranslated string: captive
 WARNING: untranslated string: dnssec disabled warning
+WARNING: untranslated string: eight hours
 WARNING: untranslated string: firewall graph country
 WARNING: untranslated string: firewall graph ip
 WARNING: untranslated string: firewall graph port
@@ -755,10 +786,14 @@ WARNING: untranslated string: guardian watch snort alertfile
 WARNING: untranslated string: ike lifetime should be between 1 and 8 hours
 WARNING: untranslated string: info messages
 WARNING: untranslated string: no data
+WARNING: untranslated string: one hour
+WARNING: untranslated string: one month
+WARNING: untranslated string: one week
 WARNING: untranslated string: route config changed
 WARNING: untranslated string: routing config added
 WARNING: untranslated string: routing config changed
 WARNING: untranslated string: routing table
+WARNING: untranslated string: unlimited
 WARNING: untranslated string: uplink bit rate
 WARNING: untranslated string: vpn broken
 WARNING: untranslated string: vpn connecting
diff --git a/doc/language_missings b/doc/language_missings
index fd372c1..46f6d69 100644
--- a/doc/language_missings
+++ b/doc/language_missings
@@ -2,6 +2,10 @@
 # Checking cgi-bin translations for language: en                           #
 ############################################################################
 < addon
+< Captive clients
+< Captive genvoucher
+< Captive logo_upload
+< Captive logo_upload1
 < ccd maxclients
 < ovpn_fragment
 ############################################################################
@@ -32,6 +36,54 @@
 < bit
 < block
 < capabilities
+< Captive 1day
+< Captive 1month
+< Captive 1week
+< Captive activate
+< Captive ACTIVATE
+< Captive activated
+< Captive active on
+< Captive agree tac
+< Captive authentication
+< Captive auth_lic
+< Captive auth_vou
+< Captive brand color
+< Captive branding
+< Captive clients
+< Captive client session expiry time
+< Captive config
+< Captive coupon
+< Captive err doublevoucher
+< Captive expire
+< Captive expiry time
+< Captive GAIN ACCESS
+< Captive generate coupon
+< Captive genvoucher
+< Captive invalid logosize
+< Captive invalid_voucher
+< Captive ip
+< Captive issued coupons
+< Captive logo_set
+< Captive logo_upload
+< Captive logo_upload1
+< Captive logo uploaded
+< Captive mac
+< Captive menu
+< Captive noexpiretime
+< Captive nolimit
+< Captive nr
+< Captive please accept the terms and conditions
+< Captive terms
+< Captive terms short
+< Captive time
+< Captive title
+< Captive upload logo
+< Captive upload logo recommendations
+< Captive voactive
+< Captive voucher
+< Captive vouchervalid
+< Captive vout
+< Captive wrong ext
 < ccd add
 < ccd choose net
 < ccd clientip
@@ -121,6 +173,7 @@
 < drop action2
 < drop forward
 < drop outgoing
+< eight hours
 < email config
 < email empty field
 < email error
@@ -674,6 +727,54 @@
 < bit
 < block
 < capabilities
+< Captive 1day
+< Captive 1month
+< Captive 1week
+< Captive activate
+< Captive ACTIVATE
+< Captive activated
+< Captive active on
+< Captive agree tac
+< Captive authentication
+< Captive auth_lic
+< Captive auth_vou
+< Captive brand color
+< Captive branding
+< Captive clients
+< Captive client session expiry time
+< Captive config
+< Captive coupon
+< Captive err doublevoucher
+< Captive expire
+< Captive expiry time
+< Captive GAIN ACCESS
+< Captive generate coupon
+< Captive genvoucher
+< Captive invalid logosize
+< Captive invalid_voucher
+< Captive ip
+< Captive issued coupons
+< Captive logo_set
+< Captive logo_upload
+< Captive logo_upload1
+< Captive logo uploaded
+< Captive mac
+< Captive menu
+< Captive noexpiretime
+< Captive nolimit
+< Captive nr
+< Captive please accept the terms and conditions
+< Captive terms
+< Captive terms short
+< Captive time
+< Captive title
+< Captive upload logo
+< Captive upload logo recommendations
+< Captive voactive
+< Captive voucher
+< Captive vouchervalid
+< Captive vout
+< Captive wrong ext
 < ccd add
 < ccd choose net
 < ccd clientip
@@ -762,6 +863,7 @@
 < drop action2
 < drop forward
 < drop outgoing
+< eight hours
 < email config
 < email empty field
 < email error
@@ -1297,6 +1399,54 @@
 < bit
 < block
 < capabilities
+< Captive 1day
+< Captive 1month
+< Captive 1week
+< Captive activate
+< Captive ACTIVATE
+< Captive activated
+< Captive active on
+< Captive agree tac
+< Captive authentication
+< Captive auth_lic
+< Captive auth_vou
+< Captive brand color
+< Captive branding
+< Captive clients
+< Captive client session expiry time
+< Captive config
+< Captive coupon
+< Captive err doublevoucher
+< Captive expire
+< Captive expiry time
+< Captive GAIN ACCESS
+< Captive generate coupon
+< Captive genvoucher
+< Captive invalid logosize
+< Captive invalid_voucher
+< Captive ip
+< Captive issued coupons
+< Captive logo_set
+< Captive logo_upload
+< Captive logo_upload1
+< Captive logo uploaded
+< Captive mac
+< Captive menu
+< Captive noexpiretime
+< Captive nolimit
+< Captive nr
+< Captive please accept the terms and conditions
+< Captive terms
+< Captive terms short
+< Captive time
+< Captive title
+< Captive upload logo
+< Captive upload logo recommendations
+< Captive voactive
+< Captive voucher
+< Captive vouchervalid
+< Captive vout
+< Captive wrong ext
 < ccd add
 < ccd choose net
 < ccd clientip
@@ -1385,6 +1535,7 @@
 < drop action2
 < drop forward
 < drop outgoing
+< eight hours
 < email config
 < email empty field
 < email error
@@ -1907,6 +2058,54 @@
 < bit
 < block
 < capabilities
+< Captive 1day
+< Captive 1month
+< Captive 1week
+< Captive activate
+< Captive ACTIVATE
+< Captive activated
+< Captive active on
+< Captive agree tac
+< Captive authentication
+< Captive auth_lic
+< Captive auth_vou
+< Captive brand color
+< Captive branding
+< Captive clients
+< Captive client session expiry time
+< Captive config
+< Captive coupon
+< Captive err doublevoucher
+< Captive expire
+< Captive expiry time
+< Captive GAIN ACCESS
+< Captive generate coupon
+< Captive genvoucher
+< Captive invalid logosize
+< Captive invalid_voucher
+< Captive ip
+< Captive issued coupons
+< Captive logo_set
+< Captive logo_upload
+< Captive logo_upload1
+< Captive logo uploaded
+< Captive mac
+< Captive menu
+< Captive noexpiretime
+< Captive nolimit
+< Captive nr
+< Captive please accept the terms and conditions
+< Captive terms
+< Captive terms short
+< Captive time
+< Captive title
+< Captive upload logo
+< Captive upload logo recommendations
+< Captive voactive
+< Captive voucher
+< Captive vouchervalid
+< Captive vout
+< Captive wrong ext
 < ccd add
 < ccd choose net
 < ccd clientip
@@ -1998,6 +2197,7 @@
 < drop forward
 < drop outgoing
 < Edit an existing route
+< eight hours
 < email config
 < email empty field
 < email error
diff --git a/html/cgi-bin/captive.cgi b/html/cgi-bin/captive.cgi
new file mode 100755
index 0000000..2f85b17
--- /dev/null
+++ b/html/cgi-bin/captive.cgi
@@ -0,0 +1,666 @@
+#!/usr/bin/perl
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2016  IPFire Team  <alexander.marx(a)ipfire.org>                #
+#                                                                             #
+# 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 3 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, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+#use strict;
+use HTML::Entities();
+use File::Basename;
+
+# enable only the following on debugging purpose
+#use warnings;
+#use CGI::Carp 'fatalsToBrowser';
+
+require '/var/ipfire/general-functions.pl';
+require "${General::swroot}/lang.pl";
+require "${General::swroot}/header.pl";
+
+my %selected = ();
+
+my $coupons = "${General::swroot}/captive/coupons";
+my %couponhash = ();
+
+my $logo = "${General::swroot}/captive/logo.dat";
+
+my %settings=();
+my %mainsettings;
+my %color;
+my %cgiparams=();
+my %netsettings=();
+my %checked=();
+my $errormessage='';
+my $clients="${General::swroot}/captive/clients";
+my %clientshash=();
+my $settingsfile="${General::swroot}/captive/settings";
+unless (-e $settingsfile)	{ system("touch $settingsfile"); }
+
+&Header::getcgihash(\%cgiparams);
+
+&General::readhash("${General::swroot}/main/settings", \%mainsettings);
+&General::readhash("/srv/web/ipfire/html/themes/".$mainsettings{'THEME'}."/include/colors.txt", \%color);
+&General::readhash("$settingsfile", \%settings) if(-f $settingsfile);
+&General::readhash("${General::swroot}/ethernet/settings", \%netsettings);
+
+&Header::showhttpheaders();
+
+if ($cgiparams{'ACTION'} eq $Lang::tr{'save'}) {
+	my $file = $cgiparams{'logo'};
+	if ($file) {
+		# Check if the file extension is PNG/JPEG
+		chomp $file;
+
+		my ($name, $path, $ext) = fileparse($file, qr/\.[^.]*$/);
+		if ($ext ne ".png" && $ext ne ".jpg" && $ext ne ".jpeg") {
+			$errormessage = $Lang::tr{'Captive wrong ext'};
+		}
+	}
+
+	$settings{'ENABLE_GREEN'}		= $cgiparams{'ENABLE_GREEN'};
+	$settings{'ENABLE_BLUE'}		= $cgiparams{'ENABLE_BLUE'};
+	$settings{'AUTH'}				= $cgiparams{'AUTH'};
+	$settings{'TITLE'}				= $cgiparams{'TITLE'};
+	$settings{'COLOR'}			= $cgiparams{'COLOR'};
+	$settings{'SESSION_TIME'}		= $cgiparams{'SESSION_TIME'};
+
+	if (!$errormessage){
+		#Check if we need to upload a new logo
+		if ($file) {
+			# Save logo
+			my ($filehandle) = CGI::upload("logo");
+
+			# XXX check filesize
+
+			open(FILE, ">$logo");
+			binmode $filehandle;
+			while (<$filehandle>) {
+				print FILE;
+			}
+			close(FILE);
+		}
+
+		&General::writehash("$settingsfile", \%settings);
+
+		# Save terms
+		if ($cgiparams{'TERMS'}){
+			$cgiparams{'TERMS'} = &Header::escape($cgiparams{'TERMS'});
+			open(FH, ">:utf8", "/var/ipfire/captive/terms.txt") or die("$!");
+			print FH $cgiparams{'TERMS'};
+			close(FH);
+			$cgiparams{'TERMS'} = "";
+		}
+
+		#execute binary to reload firewall rules
+		system("/usr/local/bin/captivectrl");
+
+		if ($cgiparams{'ENABLE_BLUE'} eq 'on'){
+				system("/usr/local/bin/wirelessctrl");
+		}
+	}
+}
+
+if ($cgiparams{'ACTION'} eq "$Lang::tr{'Captive generate coupon'}") {
+	# Check expiry time
+	if ($cgiparams{'EXP_HOUR'} + $cgiparams{'EXP_DAY'} + $cgiparams{'EXP_WEEK'} + $cgiparams{'EXP_MONTH'} == 0 && $cgiparams{'UNLIMITED'} == '') {
+		$errormessage = $Lang::tr{'Captive noexpiretime'};
+	}
+
+	#check valid remark
+	if ($cgiparams{'REMARK'} ne '' && !&validremark($cgiparams{'REMARK'})){
+		$errormessage=$Lang::tr{'fwhost err remark'};
+	}
+
+	if (!$errormessage) {
+		# Remember selected values
+		foreach my $val (("UNLIMITED", "EXP_HOUR", "EXP_DAY", "EXP_WEEK", "EXP_MONTH")) {
+			$settings{$val} = $cgiparams{$val};
+		}
+		&General::writehash($settingsfile, \%settings);
+
+		&General::readhasharray($coupons, \%couponhash) if (-e $coupons);
+		my $now = time();
+
+		# Calculate expiry time in seconds
+		my $expires = 0;
+
+		if ($settings{'UNLIMITED'} ne 'on') {
+			$expires += $settings{'EXP_HOUR'};
+			$expires += $settings{'EXP_DAY'};
+			$expires += $settings{'EXP_WEEK'};
+			$expires += $settings{'EXP_MONTH'};
+		}
+
+		my $count = $cgiparams{'COUNT'} || 1;
+		while($count-- > 0) {
+			# Generate a new code
+			my $code = &gencode();
+
+			# Check if the coupon code already exists
+			foreach my $key (keys %couponhash) {
+				if($couponhash{$key}[1] eq $code) {
+					# Code already exists, so try again
+					$code = "";
+					$count++;
+					last;
+				}
+			}
+
+			next if ($code eq "");
+
+			# Get a new key from hash
+			my $key = &General::findhasharraykey(\%couponhash);
+
+			# Initialize all fields
+			foreach my $i (0 .. 3) { $couponhash{$key}[$i] = ""; }
+
+			$couponhash{$key}[0] = $now;
+			$couponhash{$key}[1] = $code;
+			$couponhash{$key}[2] = $expires;
+			$couponhash{$key}[3] = $cgiparams{'REMARK'};
+		}
+
+		# Save everything to disk
+		&General::writehasharray($coupons, \%couponhash);
+	}
+}
+
+if ($cgiparams{'ACTION'} eq 'delete-coupon') {
+	#deletes an already generated but unused voucher
+
+	#read all generated vouchers
+	&General::readhasharray($coupons, \%couponhash) if (-e $coupons);
+	foreach my $key (keys %couponhash) {
+		if($cgiparams{'key'} eq $couponhash{$key}[0]){
+			#write logenty with decoded remark
+			my $rem=HTML::Entities::decode_entities($couponhash{$key}[4]);
+			&General::log("Captive", "Delete unused coupon $couponhash{$key}[1] $couponhash{$key}[2] hours valid expires on $couponhash{$key}[3] remark $rem");
+			#delete line from hash
+			delete $couponhash{$key};
+			last;
+		}
+	}
+	#write back hash
+	&General::writehasharray($coupons, \%couponhash);
+}
+
+if ($cgiparams{'ACTION'} eq 'delete-client') {
+	#delete voucher and connection in use
+
+	#read all active clients
+	&General::readhasharray($clients, \%clientshash) if (-e $clients);
+	foreach my $key (keys %clientshash) {
+		if($cgiparams{'key'} eq $clientshash{$key}[0]){
+			#prepare log entry with decoded remark
+			my $rem=HTML::Entities::decode_entities($clientshash{$key}[7]);
+			#write logentry
+			&General::log("Captive", "Deleted client in use $clientshash{$key}[1] $clientshash{$key}[2] hours valid expires on $clientshash{$key}[3] remark $rem - Connection will be terminated");
+			#delete line from hash
+			delete $clientshash{$key};
+			last;
+		}
+	}
+	#write back hash
+	&General::writehasharray("$clients", \%clientshash);
+	#reload firewallrules to kill connection of client
+	system("/usr/local/bin/captivectrl");
+}
+
+#open webpage, print header and open box
+&Header::openpage($Lang::tr{'Captive menu'}, 1, '');
+&Header::openbigbox();
+
+# If an error message exists, show a box with the error message
+if ($errormessage) {
+	&Header::openbox('100%', 'left', $Lang::tr{'error messages'});
+	print $errormessage;
+	&Header::closebox();
+}
+
+# Prints the config box on the website
+&Header::openbox('100%', 'left', $Lang::tr{'Captive config'});
+print <<END
+	<form method='post' action='$ENV{'SCRIPT_NAME'}' enctype="multipart/form-data">\n
+		<table width='100%' border="0">
+		<tr>
+END
+;
+
+#check which parameters have to be enabled (from settings file)
+$checked{'ENABLE_GREEN'}{'off'} = '';
+$checked{'ENABLE_GREEN'}{'on'} = '';
+$checked{'ENABLE_GREEN'}{$settings{'ENABLE_GREEN'}} = "checked='checked'";
+
+$checked{'ENABLE_BLUE'}{'off'} = '';
+$checked{'ENABLE_BLUE'}{'on'} = '';
+$checked{'ENABLE_BLUE'}{$settings{'ENABLE_BLUE'}} = "checked='checked'";
+
+$checked{'UNLIMITED'}{'off'} = '';
+$checked{'UNLIMITED'}{'on'} = '';
+$checked{'UNLIMITED'}{$settings{'UNLIMITED'}} = "checked='checked'";
+
+$selected{'AUTH'} = ();
+$selected{'AUTH'}{'COUPON'} = "";
+$selected{'AUTH'}{'TERMS'} = "";
+$selected{'AUTH'}{$settings{'AUTH'}} = "selected";
+
+if ($netsettings{'GREEN_DEV'}){
+	print "<td width='30%'>$Lang::tr{'Captive active on'} <font color='$Header::colourgreen'>Green</font></td><td><input type='checkbox' name='ENABLE_GREEN' $checked{'ENABLE_GREEN'}{'on'} /></td></tr>";
+}
+if ($netsettings{'BLUE_DEV'}){
+	print "<td width='30%'>$Lang::tr{'Captive active on'} <font color='$Header::colourblue'>Blue</font></td><td><input type='checkbox' name='ENABLE_BLUE' $checked{'ENABLE_BLUE'}{'on'} /></td></tr>";
+}
+
+print<<END
+	</tr>
+	<tr>
+		<td>
+			$Lang::tr{'Captive authentication'}
+		</td>
+		<td>
+			<select name='AUTH'>
+				<option value="TERMS"  $selected{'AUTH'}{'TERMS'} >$Lang::tr{'Captive terms'}</option>
+				<option value="COUPON" $selected{'AUTH'}{'COUPON'}>$Lang::tr{'Captive coupon'}</option>
+			</select>
+		</td>
+	</tr>
+END
+;
+
+if ($settings{'AUTH'} eq 'TERMS') {
+	$selected{'SESSION_TIME'} = ();
+	$selected{'SESSION_TIME'}{'0'} = "";
+	$selected{'SESSION_TIME'}{'3600'} = "";
+	$selected{'SESSION_TIME'}{'86400'} = "";
+	$selected{'SESSION_TIME'}{'604800'} = "";
+	$selected{'SESSION_TIME'}{'18144000'} = "";
+	$selected{'SESSION_TIME'}{$settings{'SESSION_TIME'}} = "selected";
+
+	my $terms = &getterms();
+	print <<END;
+		<tr>
+			<td></td>
+			<td>
+				<textarea cols="50" rows="10" name="TERMS">$terms</textarea>
+			</td>
+		</tr>
+
+		<tr>
+			<td>$Lang::tr{'Captive client session expiry time'}</td>
+			<td>
+				<select name="SESSION_TIME">
+					<option value="0"        $selected{'SESSION_TIME'}{'0'}>- $Lang::tr{'unlimited'} -</option>
+					<option value="3600"     $selected{'SESSION_TIME'}{'3600'}>$Lang::tr{'one hour'}</option>
+					<option value="28800"    $selected{'SESSION_TIME'}{'28800'}>$Lang::tr{'eight hours'}</option>
+					<option value="86400"    $selected{'SESSION_TIME'}{'86400'}>$Lang::tr{'24 hours'}</option>
+					<option value="604800"   $selected{'SESSION_TIME'}{'604800'}>$Lang::tr{'one week'}</option>
+					<option value="18144000" $selected{'SESSION_TIME'}{'18144000'}>$Lang::tr{'one month'}</option>
+				</select>
+			</td>
+		</tr>
+END
+}
+
+print<<END;
+	<tr>
+		<td colspan="2">
+			<br>
+			<strong>$Lang::tr{'Captive branding'}</strong>
+		</td>
+	</tr>
+	<tr>
+		<td>
+			$Lang::tr{'Captive title'}
+		</td>
+		<td>
+			<input type='text' name='TITLE' value="$settings{'TITLE'}" size='40'>
+		</td>
+	</tr>
+	<tr>
+		<td>$Lang::tr{'Captive brand color'}</td>
+		<td>
+			<input type="color" name="COLOR" value="$settings{'COLOR'}">
+		</td>
+	</tr>
+	<tr>
+		<td>
+			$Lang::tr{'Captive upload logo'}
+		</td>
+		<td>
+			<input type="file" name="logo">
+			<br>$Lang::tr{'Captive upload logo recommendations'}
+		</td>
+	</tr>
+END
+
+if (-e $logo) {
+	print <<END;
+		<tr>
+			<td>$Lang::tr{'Captive logo uploaded'}</td>
+			<td>$Lang::tr{'yes'}</td>
+		</tr>
+END
+}
+
+print <<END;
+	<tr>
+		<td></td>
+		<td align='right'>
+			<input type='submit' name='ACTION' value="$Lang::tr{'save'}"/>
+		</td>
+	</tr>
+	</table></form>
+END
+
+&Header::closebox();
+
+#if settings is set to use coupons, the coupon part has to be displayed
+if ($settings{'AUTH'} eq 'COUPON') {
+	&coupons();
+}
+
+# Show active clients
+&show_clients();
+
+sub getterms() {
+	my @ret;
+
+	open(FILE, "<:utf8", "/var/ipfire/captive/terms.txt");
+	while(<FILE>) {
+		push(@ret, HTML::Entities::decode_entities($_));
+	}
+	close(FILE);
+
+	return join(/\n/, @ret);
+}
+
+sub gencode(){
+	#generate a random code only letters from A-Z except 'O'  and 0-9
+	my @chars = ("A".."N", "P".."Z", "0".."9");
+	my $randomstring;
+	$randomstring .= $chars[rand @chars] for 1..8;
+	return $randomstring;
+}
+
+sub coupons() {
+	&Header::openbox('100%', 'left', $Lang::tr{'Captive generate coupon'});
+	print <<END;
+		<form method='post' action='$ENV{'SCRIPT_NAME'}'>
+			<table border='0' width='100%'>
+				<tr>
+					<td width='30%'>
+						$Lang::tr{'Captive vouchervalid'}
+					</td>
+					<td width='70%'>
+						<table class='tbl' border='0' width='100%'>
+							<tr>
+								<th>$Lang::tr{'hours'}</th>
+								<th>$Lang::tr{'days'}</th>
+								<th>$Lang::tr{'weeks'}</th>
+								<th>$Lang::tr{'months'}</th>
+								<th></th>
+							</tr>
+END
+
+		#print hour-dropdownbox
+		my $hrs=3600;
+		print "<tr height='40px'><td><select name='EXP_HOUR' style='width:8em;'>";
+		print "<option value='0' ";
+		print " selected='selected'" if ($settings{'EXP_HOUR'} eq '0');
+		print ">--</option>";
+		for (my $i = 1; $i<25; $i++){
+			my $exp_sec = $i * $hrs;
+			print "<option value='$exp_sec' ";
+			print " selected='selected'" if ($settings{'EXP_HOUR'} eq $exp_sec);
+			print ">$i</option>";
+		}
+		print "</td><td>";
+
+		#print day-dropdownbox
+		my $days=3600*24;
+		print "<select name='EXP_DAY' style='width:8em;'>";
+		print "<option value='0' ";
+		print " selected='selected'" if ($settings{'EXP_DAY'} eq '0');
+		print ">--</option>";
+		for (my $i = 1; $i<8; $i++){
+			my $exp_sec = $i * $days;
+			print "<option value='$exp_sec' ";
+			print " selected='selected'" if ($settings{'EXP_DAY'} eq $exp_sec);
+			print ">$i</option>";
+		}
+		print "</td><td>";
+
+		#print week-dropdownbox
+		my $week=3600*24*7;
+		print "<select name='EXP_WEEK' style='width:8em;'>";
+		print "<option value='0' ";
+		print " selected='selected'" if ($settings{'EXP_WEEK'} eq '0');
+		print ">--</option>";
+		for (my $i = 1; $i<5; $i++){
+			my $exp_sec = $i * $week;
+			print "<option value='$exp_sec' ";
+			print " selected='selected'" if ($settings{'EXP_WEEK'} eq $exp_sec);
+			print ">$i</option>";
+		}
+		print "</td><td>";
+
+		#print month-dropdownbox
+		my $month=3600*24*30;
+		print "<select name='EXP_MONTH' style='width:8em;'>";
+		print "<option value='0' ";
+		print " selected='selected'" if ($settings{'EXP_MONTH'} eq '0');
+		print ">--</option>";
+		for (my $i = 1; $i<13; $i++){
+			my $exp_sec = $i * $month;
+			print "<option value='$exp_sec' ";
+			print " selected='selected'" if ($settings{'EXP_MONTH'} eq $exp_sec);
+			print ">$i</option>";
+		}
+		print <<END;
+								</td>
+								<td>
+									<label>
+										<input type='checkbox' name='UNLIMITED' $checked{'UNLIMITED'}{'on'} />
+										$Lang::tr{'Captive nolimit'}
+									</label>
+								</td>
+							</tr>
+						</table>
+					</td>
+				</tr>
+				<tr>
+					<td>$Lang::tr{'remark'}</td>
+					<td>
+						<input type='text' style='width: 98%;' name='REMARK' align='left'>
+					</td>
+				</tr>
+			</table>
+
+			<div align="right">
+				<select name="COUNT">
+					<option value="1">1</option>
+					<option value="2">2</option>
+					<option value="3">3</option>
+					<option value="4">4</option>
+					<option value="5">5</option>
+					<option value="6">6</option>
+					<option value="7">7</option>
+					<option value="8">8</option>
+					<option value="9">9</option>
+					<option value="10">10</option>
+					<option value="20">20</option>
+					<option value="50">50</option>
+					<option value="100">100</option>
+				</select>
+
+				<input type="submit" name="ACTION" value="$Lang::tr{'Captive generate coupon'}">
+			</div>
+		</form>
+END
+
+	&Header::closebox();
+
+	# Show all coupons if exist
+	if (! -z $coupons) {
+		&show_coupons();
+	}
+}
+
+sub show_coupons() {
+	&General::readhasharray($coupons, \%couponhash) if (-e $coupons);
+
+	#if there are already generated but unsused coupons, print a table
+	&Header::openbox('100%', 'left', $Lang::tr{'Captive issued coupons'});
+
+	print <<END;
+		<table class='tbl' border='0'>
+			<tr>
+				<th align='center' width='15%'>
+					$Lang::tr{'Captive coupon'}
+				</th>
+				<th align='center' width='15%'>$Lang::tr{'Captive expiry time'}</th>
+				<th align='center' width='65%'>$Lang::tr{'remark'}</th>
+				<th align='center' width='5%'>$Lang::tr{'delete'}</th>
+			</tr>
+END
+
+	foreach my $key (keys %couponhash) {
+		my $expirytime = $Lang::tr{'Captive nolimit'};
+		if ($couponhash{$key}[2] > 0) {
+			$expirytime = &General::format_time($couponhash{$key}[2]);
+		}
+
+		if ($count++ % 2) {
+			$col="bgcolor='$color{'color20'}'";
+		} else {
+			$col="bgcolor='$color{'color22'}'";
+		}
+
+		print <<END;
+			<tr>
+				<td $col align="center">
+					<b>$couponhash{$key}[1]</b>
+				</td>
+				<td $col align="center">
+					$expirytime
+				</td>
+				<td $col align="center">
+					$couponhash{$key}[3]
+				</td>
+				<td $col align="center">
+					<form method='post'>
+						<input type='image' src='/images/delete.gif' align='middle' alt='$Lang::tr{'delete'}' title='$Lang::tr{'delete'}' />
+						<input type='hidden' name='ACTION' value='delete-coupon' />
+						<input type='hidden' name='key' value='$couponhash{$key}[0]' />
+					</form>
+				</td>
+			</tr>
+END
+	}
+
+	print "</table>";
+
+	&Header::closebox();
+}
+
+sub show_clients() {
+	# if there are active clients which use coupons show table
+	return if ( -z $clients || ! -f $clients );
+
+	my $count=0;
+	my $col;
+
+	&Header::openbox('100%', 'left', $Lang::tr{'Captive clients'});
+
+	print <<END;
+		<table class='tbl' width='100%'>
+			<tr>
+				<th align='center' width='15%'>$Lang::tr{'Captive coupon'}</th>
+				<th align='center' width='15%'>$Lang::tr{'Captive activated'}</th>
+				<th align='center' width='15%'>$Lang::tr{'Captive expiry time'}</th>
+				<th align='center' width='10%'>$Lang::tr{'Captive mac'}</th>
+				<th align='center' width='43%'>$Lang::tr{'remark'}</th>
+				<th align='center' width='5%'>$Lang::tr{'delete'}</th>
+			</tr>
+END
+
+	&General::readhasharray($clients, \%clientshash) if (-e $clients);
+	foreach my $key (keys %clientshash) {
+		#calculate time from clientshash (starttime)
+		my $starttime = sub{sprintf '%02d.%02d.%04d %02d:%02d', $_[3], $_[4]+1, $_[5]+1900, $_[2], $_[1]  }->(localtime($clientshash{$key}[2]));
+
+		#calculate endtime from clientshash
+		my $endtime;
+		if ($clientshash{$key}[3] eq '0'){
+			$endtime=$Lang::tr{'Captive nolimit'};
+		} else {
+			$endtime = sub{sprintf '%02d.%02d.%04d %02d:%02d', $_[3], $_[4]+1, $_[5]+1900, $_[2], $_[1]  }->(localtime($clientshash{$key}[2]+$clientshash{$key}[3]));
+		}
+
+		if ($count++ % 2) {
+			$col="bgcolor='$color{'color20'}'";
+		} else {
+			$col="bgcolor='$color{'color22'}'";
+		}
+
+		my $coupon = ($clientshash{$key}[4] eq "LICENSE") ? $Lang::tr{'Captive terms short'} : $clientshash{$key}[4];
+
+		print <<END;
+			<tr>
+				<td $col align="center"><b>$coupon</b></td>
+				<td $col align="center">$starttime</td>
+				<td $col align="center">$endtime</td>
+				<td $col align="center">$clientshash{$key}[0]</td>
+				<td $col align="center">$clientshash{$key}[5]</td>
+				<td $col align="center">
+					<form method='post'>
+						<input type='image' src='/images/delete.gif' align='middle' alt='$Lang::tr{'delete'}' title='$Lang::tr{'delete'}' />
+						<input type='hidden' name='ACTION' value='delete-client' />
+						<input type='hidden' name='key' value='$clientshash{$key}[0]' />
+					</form>
+				</td>
+			</tr>
+END
+	}
+
+	print "</table>";
+
+	&Header::closebox();
+}
+
+sub validremark
+{
+	# Checks a hostname against RFC1035
+        my $remark = $_[0];
+	# Each part should be at least two characters in length
+	# but no more than 63 characters
+	if (length ($remark) < 1 || length ($remark) > 255) {
+		return 0;}
+	# Only valid characters are a-z, A-Z, 0-9 and -
+	if ($remark !~ /^[a-zäöüA-ZÖÄÜ0-9-.:;\|_()\/\s]*$/) {
+		return 0;}
+	# First character can only be a letter or a digit
+	if (substr ($remark, 0, 1) !~ /^[a-zäöüA-ZÖÄÜ0-9]*$/) {
+		return 0;}
+	# Last character can only be a letter or a digit
+	if (substr ($remark, -1, 1) !~ /^[a-zöäüA-ZÖÄÜ0-9.:;_)]*$/) {
+		return 0;}
+	return 1;
+}
+
+&Header::closebigbox();
+&Header::closepage();
diff --git a/html/cgi-bin/captive/index.cgi b/html/cgi-bin/captive/index.cgi
new file mode 100755
index 0000000..e9606b1
--- /dev/null
+++ b/html/cgi-bin/captive/index.cgi
@@ -0,0 +1,238 @@
+#!/usr/bin/perl
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2016  Alexander Marx alexander.marx(a)ipfire.org                #
+#                                                                             #
+# 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 3 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, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+use strict;
+use CGI ':standard';
+use URI::Escape;
+use HTML::Entities();
+use HTML::Template;
+
+# enable only the following on debugging purpose
+#use warnings;
+#use CGI::Carp 'fatalsToBrowser';
+
+require '/var/ipfire/general-functions.pl';
+require "${General::swroot}/lang.pl";
+
+# Load the most appropriate language from the browser configuration
+my @langs = &Lang::DetectBrowserLanguages();
+&Lang::reload(@langs);
+
+my $coupons = "${General::swroot}/captive/coupons";
+my %couponhash = ();
+
+my %clientshash=();
+my %cgiparams=();
+my %settings=();
+my $clients="${General::swroot}/captive/clients";
+my $settingsfile="${General::swroot}/captive/settings";
+my $errormessage;
+my $url=param('redirect');
+
+#Create /var/ipfire/captive/clients if not exist
+unless (-f $clients){ system("touch $clients"); }
+
+#Get GUI variables
+&getcgihash(\%cgiparams);
+
+#Read settings
+&General::readhash("$settingsfile", \%settings) if(-f $settingsfile);
+
+# Actions
+if ($cgiparams{'ACTION'} eq "SUBMIT") {
+	# Get client IP address
+	my $ip_address = $ENV{X_FORWARDED_FOR} || $ENV{REMOTE_ADDR};
+
+	# Retrieve the MAC address from the ARP table
+	my $mac_address = &Network::get_hardware_address($ip_address);
+
+	&General::readhasharray("$clients", \%clientshash);
+	my $key = &General::findhasharraykey(\%clientshash);
+
+	# Create a new client line
+	foreach my $i (0 .. 5) { $clientshash{$key}[$i] = ""; }
+
+	# MAC address of the client
+	$clientshash{$key}[0] = $mac_address;
+
+	# IP address of the client
+	$clientshash{$key}[1] = $ip_address;
+
+	# Current time
+	$clientshash{$key}[2] = time();
+
+	if ($settings{"AUTH"} eq "COUPON") {
+		&General::readhasharray($coupons, \%couponhash);
+
+		if ($cgiparams{'COUPON'}) {
+			# Convert coupon input to uppercase
+			$cgiparams{'COUPON'} = uc $cgiparams{'COUPON'};
+
+			# Walk through all valid coupons and find the right one
+			my $found = 0;
+			foreach my $coupon (keys %couponhash) {
+				if ($couponhash{$coupon}[1] eq $cgiparams{'COUPON'}) {
+					$found = 1;
+
+					# Copy expiry time
+					$clientshash{$key}[3] = $couponhash{$coupon}[2];
+
+					# Save coupon code
+					$clientshash{$key}[4] = $cgiparams{'COUPON'};
+
+					# Copy coupon remark
+					$clientshash{$key}[5] = $couponhash{$coupon}[3];
+
+					# Delete used coupon
+					delete $couponhash{$coupon};
+					&General::writehasharray($coupons, \%couponhash);
+
+					last;
+				}
+			}
+
+			if ($found == 1) {
+				&General::log("Captive", "Internet access granted via coupon ($clientshash{$key}[4]) for $ip_address until $clientshash{$key}[3]");
+			} else {
+				$errormessage = $Lang::tr{"Captive invalid coupon"};
+			}
+
+		# No coupon given
+		} else {
+			$errormessage = $Lang::tr{"Captive please enter a coupon code"};
+		}
+
+	# Terms
+	} else {
+		# Make sure that they have been accepted
+		if ($cgiparams{'TERMS'} eq "on") {
+			# Copy session expiry time
+			$clientshash{$key}[3] = $settings{'SESSION_TIME'} || "0";
+
+			# No coupon code
+			$clientshash{$key}[4] = "TERMS";
+
+			&General::log("Captive", "Internet access granted via license agreement for $ip_address until $clientshash{$key}[3]");
+
+		# The terms have not been accepted
+		} else {
+			$errormessage = $Lang::tr{'Captive please accept the terms and conditions'};
+		}
+	}
+
+	# If no errors were found, save configruation and reload
+	if (!$errormessage) {
+		&General::writehasharray("$clients", \%clientshash);
+
+		system("/usr/local/bin/captivectrl");
+
+		# Redirect client to the original URL
+		print "Status: 302 Moved Temporarily\n";
+		print "Location: $url\n";
+		print "Connection: close\n\n";
+		exit 0;
+	}
+}
+
+my $tmpl = HTML::Template->new(
+	filename => "/srv/web/ipfire/html/captive/template.html",
+	die_on_bad_params => 0
+);
+
+$tmpl->param(REDIRECT => $url);
+
+# Coupon
+if ($settings{'AUTH'} eq "COUPON") {
+	$tmpl->param(COUPON => 1);
+	$tmpl->param(L_HEADING => $Lang::tr{'Captive coupon'});
+} else {
+	$tmpl->param(L_HEADING => $Lang::tr{'Captive terms'});
+}
+
+$tmpl->param(TITLE => $settings{'TITLE'});
+$tmpl->param(COLOR => $settings{'COLOR'});
+$tmpl->param(ERROR => $errormessage);
+
+$tmpl->param(TERMS => &getterms());
+
+# Some translated strings
+$tmpl->param(L_ACTIVATE        => $Lang::tr{'Captive ACTIVATE'});
+$tmpl->param(L_GAIN_ACCESS     => $Lang::tr{'Captive GAIN ACCESS'});
+$tmpl->param(L_AGREE_TERMS     => $Lang::tr{'Captive agree tac'});
+
+# Print header
+print "Pragma: no-cache\n";
+print "Cache-control: no-cache\n";
+print "Connection: close\n";
+print "Content-type: text/html\n\n";
+
+# Print rendered template
+print $tmpl->output();
+
+sub getcgihash {
+	my ($hash, $params) = @_;
+	my $cgi = CGI->new ();
+	$hash->{'__CGI__'} = $cgi;
+	return if ($ENV{'REQUEST_METHOD'} ne 'POST');
+	if (!$params->{'wantfile'}) {
+		$CGI::DISABLE_UPLOADS = 1;
+		$CGI::POST_MAX        = 1024 * 1024;
+	} else {
+		$CGI::POST_MAX = 10 * 1024 * 1024;
+	}
+	$cgi->referer() =~ m/^http?\:\/\/([^\/]+)/;
+	my $referer = $1;
+	$cgi->url() =~ m/^http?\:\/\/([^\/]+)/;
+	my $servername = $1;
+	return if ($referer ne $servername);
+
+	### Modified for getting multi-vars, split by |
+	my %temp = $cgi->Vars();
+        foreach my $key (keys %temp) {
+		$hash->{$key} = $temp{$key};
+		$hash->{$key} =~ s/\0/|/g;
+		$hash->{$key} =~ s/^\s*(.*?)\s*$/$1/;
+        }
+
+	if (($params->{'wantfile'})&&($params->{'filevar'})) {
+		$hash->{$params->{'filevar'}} = $cgi->upload
+						($params->{'filevar'});
+	}
+	return;
+}
+
+sub getterms() {
+	my @terms = ();
+
+	open(my $handle, "<:utf8", "/var/ipfire/captive/terms.txt");
+	while(<$handle>) {
+		$_ = HTML::Entities::decode_entities($_);
+		push(@terms, $_);
+	}
+	close($handle);
+
+	my $terms = join("\n", @terms);
+
+	# Format paragraphs
+	$terms =~ s/\n\n/<\/p>\n<p>/g;
+
+	return $terms;
+}
diff --git a/html/cgi-bin/captive/logo.cgi b/html/cgi-bin/captive/logo.cgi
new file mode 100644
index 0000000..8f292b1
--- /dev/null
+++ b/html/cgi-bin/captive/logo.cgi
@@ -0,0 +1,44 @@
+#!/usr/bin/perl
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2016  Alexander Marx alexander.marx(a)ipfire.org                #
+#                                                                             #
+# 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 3 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, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+use strict;
+use CGI;
+use File::Copy;
+
+# enable only the following on debugging purpose
+#use warnings;
+#use CGI::Carp 'fatalsToBrowser';
+
+require '/var/ipfire/general-functions.pl';
+
+my $logo = "${General::swroot}/captive/logo.dat";
+
+# Send 404 if logo was not uploaded and exit
+if (!-e $logo) {
+	print CGI::header(status => 404);
+	exit(0);
+}
+
+print "Content-Type: application/octet-stream\n\n";
+
+# Send image data
+File::Copy::copy $logo, \*STDOUT;
+exit(0);
diff --git a/html/cgi-bin/captive/redirect.cgi b/html/cgi-bin/captive/redirect.cgi
new file mode 100755
index 0000000..7c5b5e7
--- /dev/null
+++ b/html/cgi-bin/captive/redirect.cgi
@@ -0,0 +1,58 @@
+#!/usr/bin/perl
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2016 Alexander Marx alexander.marx(a)ipfire.org                 #
+#                                                                             #
+# 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 3 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, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+use strict;
+use URI::Escape;
+use CGI::Carp qw(fatalsToBrowser);
+
+require '/var/ipfire/general-functions.pl';
+
+my $url = "http://$ENV{'SERVER_NAME'}$ENV{'REQUEST_URI'}";
+my $safe_url = uri_escape($url);
+
+my %settingshash = ();
+my %ethernethash = ();
+my $target;
+
+# Read settings
+&General::readhash("${General::swroot}/captive/settings", \%settingshash);
+&General::readhash("${General::swroot}/ethernet/settings", \%ethernethash);
+
+# Get the client's IP address
+my $client_address = $ENV{X_FORWARDED_FOR} || $ENV{REMOTE_ADDR} || "";
+
+if ($settingshash{'ENABLE_GREEN'} eq "on" && $ethernethash{'GREEN_ADDRESS'} ne '') {
+	if (&General::IpInSubnet($client_address, $ethernethash{'GREEN_ADDRESS'}, $ethernethash{'GREEN_NETMASK'})) {
+		$target = $ethernethash{'GREEN_ADDRESS'};
+	}
+
+} elsif($settingshash{'ENABLE_BLUE'} eq "on" && $ethernethash{'BLUE_ADDRESS'} ne '') {
+	if (&General::IpInSubnet($client_address, $ethernethash{'BLUE_ADDRESS'}, $ethernethash{'BLUE_NETMASK'})) {
+		$target = $ethernethash{'BLUE_ADDRESS'};
+	}
+
+} else {
+	exit 0;
+}
+
+print "Status: 302 Moved Temporarily\n";
+print "Location: http://$target:1013/cgi-bin/index.cgi?redirect=$safe_url\n";
+print "Connection: close\n\n";
diff --git a/html/cgi-bin/logs.cgi/log.dat b/html/cgi-bin/logs.cgi/log.dat
index 5734fed..f241365 100644
--- a/html/cgi-bin/logs.cgi/log.dat
+++ b/html/cgi-bin/logs.cgi/log.dat
@@ -51,6 +51,7 @@ $cgiparams{'SECTION'} = 'ipfire';
 my %sections = (
         'auth' => '(\w+\(pam_unix\)\[.*\]: )',
 		'wio' => '(wio|wio\[.*\])',
+        'captive' => '(Captive:)',
         'clamav' => '(clamd\[.*\]: |freshclam\[.*\]: )',
         'collectd' => '(collectd\[.*\]: )',
         'cron' => '(fcron\[.*\]: )',
@@ -77,6 +78,7 @@ my %sections = (
 my %trsections = (
         'auth' => "$Lang::tr{'loginlogout'}",
 		'wio' => 'Who Is Online?',
+        'captive' => $Lang::tr{'captive'},
         'clamav' => 'ClamAV',
         'collectd' => 'Collectd',
         'cron' => 'Cron',
diff --git a/html/html/captive/assets/captive.css b/html/html/captive/assets/captive.css
new file mode 100644
index 0000000..6f231bc
--- /dev/null
+++ b/html/html/captive/assets/captive.css
@@ -0,0 +1,194 @@
+(a)font-face {
+	font-family: "Ubuntu";
+	font-weight: 300;
+	src: local("Ubuntu Light"), local("Ubuntu-Light"), url("Ubuntu-L.ttf") format("truetype");
+}
+
+(a)font-face {
+	font-family: "Ubuntu";
+	font-weight: 400;
+	src: local("Ubuntu Regular"), local("Ubuntu-Regular"), url("Ubuntu-R.ttf") format("truetype");
+}
+
+(a)font-face {
+	font-family: "Ubuntu";
+	font-weight: 500;
+	src: local("Ubuntu Medium"), local("Ubuntu-Medium"), url("Ubuntu-M.ttf") format("truetype");
+}
+
+body {
+	background-image: url("../cgi-bin/logo.cgi");
+	background-size: 100%;
+	background-repeat: no-repeat;
+
+	background-color: #eceff1;
+	color: #263238;
+
+	display: flex;
+	min-height: 100vh;
+	flex-direction: column;
+}
+
+body, input {
+	font-family: "Ubuntu", sans-serif;
+	font-size: 14px;
+}
+
+.content {
+	flex: 1;
+}
+
+.box {
+	margin: 272px 0 48px 0;
+	padding: 0 30px 48px 30px;
+
+	position: relative;
+	display: flex;
+	flex-direction: column;
+	background-color: white;
+
+	border: none;
+	border-radius: 4px;
+	box-shadow:
+		0 19px 38px 0 rgba(0, 0, 0, 0.3),
+		0 15px 12px 0 rgba(0, 0, 0, 0.22);
+}
+
+.box-header {
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	min-height: 128px;
+}
+
+.box-header h1 {
+	font-size: 40px;
+}
+
+(a)media (min-width: 1200px) {
+	.box-header {
+		min-height: none;
+	}
+
+	.box-header h1 {
+		margin-top: 24px;
+		margin-bottom: 20px;
+	}
+}
+
+.box-block {
+	padding: 24px 16px 24px 16px;
+	margin: 0 -8px 40px -8px;
+
+	text-align: center;
+	background-color: #b71c1c;
+	color: white;
+	border-radius: 4px;
+}
+
+(a)media (min-width: 1200px) {
+	.box-block {
+		padding: 18px 16px 18px 16px;
+		margin: 0 0 48px 0;
+	}
+}
+
+.box-block h4 {
+	font-size: 20px;
+}
+
+footer {
+	height: 64px;
+	background-color: rgba(84, 110, 122, 0.06); /* #546e7a */
+}
+
+.footer {
+	display: flex;
+	align-items: center;
+}
+
+.footer .logo {
+	width: 64px;
+	height: 64px;
+	padding: 8px;
+}
+
+.form-text {
+	display: inline-block;
+	height: 36px;
+
+	color: #263238;
+	background-color: rgba(38, 49, 56, 0,08);
+
+	border: 0;
+	border-radius: 2px;
+	box-shadow: inset 0 -2px 0 0 #546e7a;
+}
+
+.form-error {
+	box-shadow: inset 0 -2px 0 0 #ff3d00;
+}
+
+.form-submit {
+	display: inline-block;
+
+	font-weight: 500;
+	text-transform: uppercase;
+
+	height: 36px;
+	padding: 0 16px 0 16px;
+	margin: 0 8px 0 8px;
+
+	color: #263238;
+	background-color: white;
+
+	border: none;
+	border-radius: 2px;
+	box-shadow:
+		0 2px 4px 0 rgba(0, 0, 0, 0.16),
+		0 1px 2px 0 rgba(0, 0, 0, 0.23);
+}
+
+.form-submit:hover {
+	box-shadow:
+		0 3px 6px 0 rgba(0, 0, 0, 0.16),
+		0 3px 6px 0 rgba(0, 0, 0, 0.23);
+}
+
+.form-submit:active {
+	background-color: black;
+	opacity: 0.16;
+	box-shadow: none;
+}
+
+.checkbox {
+	position: relative;
+	display: block;
+
+	margin-top: 20px;
+}
+
+.checkbox .form-checkbox {
+	position: absolute;
+	margin-top: 1px;
+	margin-left: -24px;
+
+	width: 20px;
+	height: 20px;
+
+	background-color: rgba(38, 49, 56, 0.08);
+
+	border: 2px solid #546e7a;
+	border-radius: 2px;
+}
+
+.text-error {
+	position: block;
+	margin-top: 6px;
+	min-height: 23px;
+
+	color: white;
+	background-color: #ff3d00;
+
+	border-radius: 2px;
+}
diff --git a/html/html/captive/assets/favicon.ico b/html/html/captive/assets/favicon.ico
new file mode 100644
index 0000000..52da262
Binary files /dev/null and b/html/html/captive/assets/favicon.ico differ
diff --git a/html/html/captive/assets/ipfire.png b/html/html/captive/assets/ipfire.png
new file mode 100644
index 0000000..cd84ad5
Binary files /dev/null and b/html/html/captive/assets/ipfire.png differ
diff --git a/html/html/captive/template.html b/html/html/captive/template.html
new file mode 100644
index 0000000..04be2b1
--- /dev/null
+++ b/html/html/captive/template.html
@@ -0,0 +1,79 @@
+<!DOCTYPE HTML>
+<html>
+	<head>
+		<meta charset="UTF-8">
+		<title><TMPL_VAR NAME="TITLE"></title>
+
+		<link rel="stylesheet" href="../assets/bootstrap-reboot.min.css">
+		<link rel="stylesheet" href="../assets/bootstrap-grid.min.css">
+		<link rel="stylesheet" href="../assets/captive.css">
+
+		<TMPL_IF NAME="COLOR">
+			<style>
+				.box-block {
+					background-color: <TMPL_VAR NAME="COLOR">;
+				}
+			</style>
+		</TMPL_IF>
+
+		<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+	</head>
+
+	<body>
+		<div class="container content">
+			<div class="row">
+				<div class="col-12 col-lg-10 offset-lg-1 col-xl-8 offset-xl-2">
+					<div class="box">
+						<div class="box-header">
+							<h1><TMPL_VAR NAME="TITLE"></h1>
+						</div>
+
+						<div class="box-block">
+							<h4><TMPL_VAR NAME="L_HEADING"></h4>
+
+							<form action="" method="POST">
+								<input type="hidden" name="ACTION" value="SUBMIT">
+								<input type="hidden" name="redirect" value="<TMPL_VAR NAME="REDIRECT">">
+
+								<TMPL_IF NAME="COUPON">
+									<input class="form-text <TMPL_IF NAME="ERROR">form-error</TMPL_IF>" type="text" name="COUPON">
+									<input class="form-submit" type="submit"
+										value="<TMPL_VAR NAME="L_ACTIVATE">">
+								<TMPL_ELSE>
+									<div class="checkbox">
+										<label>
+											<input class="form-checkbox <TMPL_IF NAME="ERROR">error</TMPL_IF>" type="checkbox" name="TERMS">
+											<TMPL_VAR NAME="L_AGREE_TERMS">
+										</label>
+									</div>
+
+									<input class="form-submit" type="submit"
+										value="<TMPL_VAR NAME="L_GAIN_ACCESS">">
+								</TMPL_IF>
+
+								<TMPL_IF NAME="ERROR">
+									<div class="text-error">
+										<TMPL_VAR NAME="ERROR">
+									</div>
+								</TMPL_IF>
+							</form>
+						</div>
+
+						<TMPL_IF NAME="TERMS">
+							<TMPL_VAR NAME="TERMS">
+						</TMPL_IF>
+					</div>
+				</div>
+			</div>
+		</div>
+
+		<footer>
+			<div class="container">
+				<div class="footer">
+					<img class="logo" src="../assets/ipfire.png" alt="IPFire Logo">
+					Powered by IPFire
+				</div>
+			</div>
+		</footer>
+	</body>
+</html>
diff --git a/langs/de/cgi-bin/de.pl b/langs/de/cgi-bin/de.pl
index 7bc1c49..af96a64 100644
--- a/langs/de/cgi-bin/de.pl
+++ b/langs/de/cgi-bin/de.pl
@@ -7,6 +7,54 @@
 'Add Rule' => 'Regel hinzufügen',
 'Add a route' => 'Eine Route hinzufügen',
 'Async logging enabled' => 'Aktiviere asynchrones Schreiben des Syslogs',
+'Captive 1day' => '1 Tag',
+'Captive 1month' => '1 Monat',
+'Captive 1week' => '1 Woche',
+'Captive ACTIVATE' => 'AKTIVIEREN',
+'Captive GAIN ACCESS' => 'ZUGANG',
+'Captive activate' => 'Aktivieren',
+'Captive activated' => 'Aktiviert',
+'Captive active on' => 'Aktiviert auf',
+'Captive agree tac' => 'Bedingungen akzeptieren',
+'Captive auth_lic' => 'Lizenz',
+'Captive auth_vou' => 'Gutschein',
+'Captive authentication' => 'Art der Anmeldung',
+'Captive brand color' => 'Highlight-Farbe',
+'Captive branding' => 'Branding',
+'Captive client session expiry time' => 'Ablaufzeit',
+'Captive clients' => 'Clients',
+'Captive config' => 'Konfiguration',
+'Captive coupon' => 'Coupon',
+'Captive err doublevoucher' => 'Ein Gutschein mit diesem Code ist bereits im Umlauf',
+'Captive expire' => 'Ablauf',
+'Captive expiry time' => 'Ablaufzeit',
+'Captive generate coupon' => 'Coupon generieren',
+'Captive genvoucher' => 'Gutschein generieren',
+'Captive invalid logosize' => 'Die hochgeladene Datei entspricht nicht der vorgegeben Auflösung von mindestens 1280x400 und maximal 1920x800 Pixeln',
+'Captive invalid_voucher' => 'Dieser Code ist ungültig. Bitte versuchen Sie es erneut',
+'Captive ip' => 'IP-Addresse',
+'Captive issued coupons' => 'Ausgestellte Coupons',
+'Captive logo uploaded' => 'Logo hochgeladen',
+'Captive logo_set' => 'Aktuelles Logo',
+'Captive logo_upload' => 'Logo hochladen',
+'Captive logo_upload1' => '(PNG, min. 1280x400, max. 1920x800)',
+'Captive mac' => 'MAC-Adresse',
+'Captive menu' => 'Captive-Portal',
+'Captive noexpiretime' => 'Es wurde kein gültiger Verbindungszeitraum angegeben',
+'Captive nolimit' => 'Unbegrenzt',
+'Captive nr' => 'Nummer',
+'Captive please accept the terms and conditions' => 'Bitte akzeptieren Sie die Bedingungen',
+'Captive terms' => 'Bedingungen',
+'Captive terms short' => 'Bedingungen',
+'Captive time' => 'Erlaubter Nutzungszeitraum nach Aktivierung (Stunden)',
+'Captive title' => 'Titel der Anmeldeseite',
+'Captive upload logo' => 'Logo hochladen',
+'Captive upload logo recommendations' => '(PNG oder JPEG, 1280x720 Pixel empfohlen)',
+'Captive voactive' => 'Aktive Gutscheine',
+'Captive voucher' => 'Gutschein',
+'Captive vouchervalid' => 'Erlaubter Zeitraum für Gutschein',
+'Captive vout' => 'Ausgegebene Gutscheine',
+'Captive wrong ext' => 'Die hochgeladene Datei hat den falschen Dateityp',
 'Choose Rule' => 'Wählen Sie <u>eine</u> der untenstehenden Regeln aus.',
 'Class' => 'Klasse',
 'Class was deleted' => 'wurde mit eventuell vorhandenen Unterklassen gelöscht',
@@ -841,6 +889,7 @@
 'edit share' => 'Freigabe bearbeiten',
 'editor' => 'Editor',
 'eg' => 'z.B.:',
+'eight hours' => '8 Stunden',
 'email config' => 'Konfiguration',
 'email empty field' => 'Leeres Feld',
 'email error' => 'FEHLER: Test-E-Mail konnte nicht versendet werden',
diff --git a/langs/en/cgi-bin/en.pl b/langs/en/cgi-bin/en.pl
index ab1f401..7e4f95c 100644
--- a/langs/en/cgi-bin/en.pl
+++ b/langs/en/cgi-bin/en.pl
@@ -1,12 +1,61 @@
 %tr = ( 
 %tr,
 
+'24 hours' => '24 Hours',
 'Act as' => 'Act as:',
 'Add Level7 rule' => 'Add Level7 rule',
 'Add Port Rule' => 'Add port rule',
 'Add Rule' => 'Add rule',
 'Add a route' => 'Add a route',
 'Async logging enabled' => 'Enable asynchronous writing of the syslog file',
+'Captive 1day' => '1 day',
+'Captive 1month' => '1 month',
+'Captive 1week' => '1 week',
+'Captive ACTIVATE' => 'ACTIVATE',
+'Captive GAIN ACCESS' => 'GAIN ACCESS',
+'Captive activate' => 'Activate',
+'Captive activated' => 'Activated',
+'Captive active on' => 'Activated on',
+'Captive agree tac' => 'I agree with the terms & conditions below.',
+'Captive auth_lic' => 'License',
+'Captive auth_vou' => 'Voucher',
+'Captive authentication' => 'Type of Access',
+'Captive brand color' => 'Brand Color',
+'Captive branding' => 'Branding',
+'Captive client session expiry time' => 'Session Expiry Time',
+'Captive config' => 'Settings',
+'Captive coupon' => 'Coupon',
+'Captive err doublevoucher' => 'A voucher with this code already exists',
+'Captive expire' => 'Expire',
+'Captive expiry time' => 'Expiry Time',
+'Captive generate coupon' => 'Generate Coupon',
+'Captive heading terms' => 'Terms &amp; Conditions',
+'Captive heading voucher' => 'Voucher or Access Code',
+'Captive invalid coupon' => 'You entered an invalid coupon code. Please try again.',
+'Captive invalid logosize' => 'The uploaded image file does not meet the required resolution of at least 1280x400 but not larger than 1920x800 pixels',
+'Captive invalid_voucher' => 'Invalid code. Please try again',
+'Captive ip' => 'IP Address',
+'Captive issued coupons' => 'Issued Coupons',
+'Captive logo uploaded' => 'Logo uploaded',
+'Captive logo_set' => 'Current Logo',
+'Captive mac' => 'MAC Address',
+'Captive menu' => 'Captive Portal',
+'Captive noexpiretime' => 'No valid connection time range given',
+'Captive nolimit' => 'unlimited',
+'Captive nr' => 'Number',
+'Captive please accept the terms and conditions' => 'Please accept the terms &amp; conditions',
+'Captive please enter a coupon code' => 'Please enter a coupon code',
+'Captive terms' => 'Terms &amp; Conditions',
+'Captive terms short' => 'T&Cs',
+'Captive time' => 'Access time post activation (hours)',
+'Captive title' => 'Title of Login Page',
+'Captive upload logo' => 'Upload Logo',
+'Captive upload logo recommendations' => '(PNG or JPEG, recommended 1280x720 pixels)',
+'Captive voactive' => 'Active Vouchers',
+'Captive voucher' => 'Voucher',
+'Captive vouchervalid' => 'Allowed time for this voucher',
+'Captive vout' => 'Issued Vouchers',
+'Captive wrong ext' => 'Uploaded file has wrong filetype',
 'Choose Rule' => 'Choose <u>one</u> of the following rules.',
 'Class' => 'Class',
 'Class was deleted' => 'with potential subclasses was deleted',
@@ -650,7 +699,7 @@
 'day after' => 'Day after',
 'day before' => 'Day before',
 'day-graph' => 'Day',
-'days' => 'days.',
+'days' => 'Days',
 'dbfile' => 'Dbfile',
 'ddns help dnsmadeeasy' => 'In the field for hostname enter your ID (or a list of IDs seperated by;)',
 'ddns help freedns' => 'In the fied username enter your connect string',
@@ -867,6 +916,7 @@
 'edit share' => 'Edit share',
 'editor' => 'Editor',
 'eg' => 'e.g.:',
+'eight hours' => '8 Hours',
 'email config' => 'Configuration',
 'email empty field' => 'Empty field',
 'email error' => 'ERROR: Test mail could not be sent',
@@ -1282,7 +1332,7 @@
 'hosts config changed' => 'Hosts config changed',
 'hour' => 'Hour',
 'hour-graph' => 'Hour',
-'hours' => 'hours',
+'hours' => 'Hours',
 'hours2' => 'Hours',
 'ibod for dual isdn only' => 'iBOD can only be used with Dual ISDN.',
 'icmp selected but no type' => 'ICMP selected for protocol, but no ICMP type specified.',
@@ -1606,7 +1656,7 @@
 'monthly volume' => 'Monthly volume',
 'monthly volume start day' => 'First day of monthly period',
 'monthly volume start day short' => 'First day',
-'months' => 'months',
+'months' => 'Months',
 'more' => 'more',
 'most preferred' => 'most preferred',
 'mount' => 'Mount',
@@ -1701,6 +1751,9 @@
 'ok' => 'OK',
 'older' => 'Older',
 'on' => 'on',
+'one hour' => 'One Hour',
+'one month' => 'One Month',
+'one week' => 'One Week',
 'online help en' => 'Online help (in english)',
 'only digits allowed in holdoff field' => 'Only digits allowed in holdoff field',
 'only digits allowed in max retries field' => 'Only digits allowed in max retries field.',
@@ -2314,6 +2367,7 @@
 'unix password sync' => 'Unix Password Sync',
 'unix shell' => 'UNIX Shell',
 'unknown' => 'UNKNOWN',
+'unlimited' => 'Unlimited',
 'unnamed' => 'Unnamed',
 'update' => 'Update',
 'update accelerator' => 'Update Accelerator',
@@ -2694,7 +2748,7 @@
 'week' => 'Week',
 'week-graph' => 'Week',
 'weekly firewallhits' => 'weekly firewallhits',
-'weeks' => 'weeks',
+'weeks' => 'Weeks',
 'wildcards' => 'Wildcards',
 'wins server' => 'Wins Server',
 'wins support' => 'Wins Support',
diff --git a/lfs/apache2 b/lfs/apache2
index b7a7416..138ede8 100644
--- a/lfs/apache2
+++ b/lfs/apache2
@@ -117,5 +117,8 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
 	cp -rf $(DIR_CONF)/httpd/* /etc/httpd/conf
 	ln -sf $(CONFIG_ROOT)/main/hostname.conf /etc/httpd/conf/
 
+	# Create captive logging directory
+	-mkdir -pv /var/log/httpd/captive
+
 	@rm -rf $(DIR_APP)
 	@$(POSTBUILD)
diff --git a/lfs/bootstrap b/lfs/bootstrap
new file mode 100644
index 0000000..15e882e
--- /dev/null
+++ b/lfs/bootstrap
@@ -0,0 +1,79 @@
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2007-2016 IPFire Team  <info(a)ipfire.org>                      #
+#                                                                             #
+# 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 3 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, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+###############################################################################
+# Definitions
+###############################################################################
+
+include Config
+
+VER        = 4.0.0-alpha.6
+
+THISAPP    = bootstrap-$(VER)
+DL_FILE    = $(THISAPP)-dist.zip
+DL_FROM    = $(URL_IPFIRE)
+DIR_APP    = $(DIR_SRC)/$(THISAPP)-dist
+TARGET     = $(DIR_INFO)/$(THISAPP)
+
+###############################################################################
+# Top-level Rules
+###############################################################################
+
+objects = $(DL_FILE)
+
+$(DL_FILE) = $(DL_FROM)/$(DL_FILE)
+
+$(DL_FILE)_MD5 = 7fed4691d4d7ca19820009dad86e1aef
+
+install : $(TARGET)
+
+check : $(patsubst %,$(DIR_CHK)/%,$(objects))
+
+download :$(patsubst %,$(DIR_DL)/%,$(objects))
+
+md5 : $(subst %,%_MD5,$(objects))
+
+###############################################################################
+# Downloading, checking, md5sum
+###############################################################################
+
+$(patsubst %,$(DIR_CHK)/%,$(objects)) :
+	@$(CHECK)
+
+$(patsubst %,$(DIR_DL)/%,$(objects)) :
+	@$(LOAD)
+
+$(subst %,%_MD5,$(objects)) :
+	@$(MD5)
+
+###############################################################################
+# Installation Details
+###############################################################################
+
+$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
+	@$(PREBUILD)
+	@rm -rf $(DIR_APP) && cd $(DIR_SRC) && unzip $(DIR_DL)/$(DL_FILE)
+
+	-mkdir -pv /usr/share/bootstrap/{css,js}
+	cd $(DIR_APP) && cp -vf css/* /usr/share/bootstrap/css
+	cd $(DIR_APP) && cp -vf js/*  /usr/share/bootstrap/js
+
+	@rm -rf $(DIR_APP)
+	@$(POSTBUILD)
diff --git a/lfs/configroot b/lfs/configroot
index 760505e..ad02996 100644
--- a/lfs/configroot
+++ b/lfs/configroot
@@ -50,7 +50,7 @@ $(TARGET) :
 	@$(PREBUILD)
 
 	# Create all directories
-	for i in addon-lang auth backup ca certs connscheduler crls ddns dhcp dhcpc dns dnsforward \
+	for i in addon-lang auth backup ca captive certs connscheduler crls ddns dhcp dhcpc dns dnsforward \
 			ethernet extrahd/bin fwlogs fwhosts firewall isdn key langs logging mac main \
 			menu.d modem nfs optionsfw \
 			ovpn patches pakfire portfw ppp private proxy/advanced/cre \
@@ -62,7 +62,7 @@ $(TARGET) :
 
 	# Touch empty files
 	for i in auth/users backup/include.user backup/exclude.user \
-	    certs/index.txt ddns/config ddns/noipsettings ddns/settings ddns/ipcache dhcp/settings \
+	    captive/settings captive/agb.txt captive/clients captive/voucher_out certs/index.txt ddns/config ddns/noipsettings ddns/settings ddns/ipcache dhcp/settings \
 	    dhcp/fixleases dhcp/advoptions dhcp/dhcpd.conf.local dns/settings dnsforward/config ethernet/aliases ethernet/settings ethernet/known_nics ethernet/scanned_nics \
 	    ethernet/wireless extrahd/scan extrahd/devices extrahd/partitions extrahd/settings firewall/settings firewall/config firewall/geoipblock firewall/input firewall/outgoing \
 	    fwhosts/customnetworks fwhosts/customhosts fwhosts/customgroups fwhosts/customservicegrp fwhosts/customgeoipgrp fwlogs/ipsettings fwlogs/portsettings \
diff --git a/lfs/stage2 b/lfs/stage2
index b70bde7..0a5ac65 100644
--- a/lfs/stage2
+++ b/lfs/stage2
@@ -107,6 +107,7 @@ endif
 	# Move script to correct place.
 	mv -vf /usr/local/bin/ovpn-ccd-convert /usr/sbin/
 	mv -vf /usr/local/bin/ovpn-collectd-convert /usr/sbin/
+	mv -vf /usr/local/bin/captive-cleanup /usr/bin/
 	
 	# Install firewall scripts.
 	mkdir -pv /usr/lib/firewall
diff --git a/lfs/ubuntu-font-family b/lfs/ubuntu-font-family
new file mode 100644
index 0000000..f817b85
--- /dev/null
+++ b/lfs/ubuntu-font-family
@@ -0,0 +1,78 @@
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2007-2017 IPFire Team  <info(a)ipfire.org>                      #
+#                                                                             #
+# 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 3 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, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+###############################################################################
+# Definitions
+###############################################################################
+
+include Config
+
+VER        = 0.83
+
+THISAPP    = ubuntu-font-family-$(VER)
+DL_FILE    = $(THISAPP).zip
+DL_FROM    = $(URL_IPFIRE)
+DIR_APP    = $(DIR_SRC)/$(THISAPP)
+TARGET     = $(DIR_INFO)/$(THISAPP)
+
+###############################################################################
+# Top-level Rules
+###############################################################################
+
+objects = $(DL_FILE)
+
+$(DL_FILE) = $(DL_FROM)/$(DL_FILE)
+
+$(DL_FILE)_MD5 = a24b8136b8f3bb93f166baf97d9328de
+
+install : $(TARGET)
+
+check : $(patsubst %,$(DIR_CHK)/%,$(objects))
+
+download :$(patsubst %,$(DIR_DL)/%,$(objects))
+
+md5 : $(subst %,%_MD5,$(objects))
+
+###############################################################################
+# Downloading, checking, md5sum
+###############################################################################
+
+$(patsubst %,$(DIR_CHK)/%,$(objects)) :
+	@$(CHECK)
+
+$(patsubst %,$(DIR_DL)/%,$(objects)) :
+	@$(LOAD)
+
+$(subst %,%_MD5,$(objects)) :
+	@$(MD5)
+
+###############################################################################
+# Installation Details
+###############################################################################
+
+$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
+	@$(PREBUILD)
+	@rm -rf $(DIR_APP) && cd $(DIR_SRC) && unzip $(DIR_DL)/$(DL_FILE)
+
+	-mkdir -pv /usr/share/fonts
+	cd $(DIR_APP) && cp -vf *.ttf /usr/share/fonts/
+
+	@rm -rf $(DIR_APP)
+	@$(POSTBUILD)
diff --git a/lfs/web-user-interface b/lfs/web-user-interface
index 919acbe..3e9eb9a 100644
--- a/lfs/web-user-interface
+++ b/lfs/web-user-interface
@@ -68,5 +68,24 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
 	ln -svf ipfire /srv/web/ipfire/html/themes/ipfire-rounded
 
 	# Reset permissions of redirect templates and theme directories
-	find /srv/web/ipfire/html/{redirect-templates,themes} -type d | xargs chmod -v 755
+	find /srv/web/ipfire/html/{captive,redirect-templates,themes} -type d | xargs chmod -v 755
+
+	# Captive Portal CSS
+	ln -svf --relative /usr/share/bootstrap/css/bootstrap-grid.min.css \
+		/srv/web/ipfire/html/captive/assets/bootstrap-grid.min.css
+	ln -svf --relative /usr/share/bootstrap/css/bootstrap-grid.min.css.map \
+		/srv/web/ipfire/html/captive/assets/bootstrap-grid.min.css.map
+	ln -svf --relative /usr/share/bootstrap/css/bootstrap-reboot.min.css \
+		/srv/web/ipfire/html/captive/assets/bootstrap-reboot.min.css
+	ln -svf --relative /usr/share/bootstrap/css/bootstrap-reboot.min.css.map \
+		/srv/web/ipfire/html/captive/assets/bootstrap-reboot.min.css.map
+
+	# Captive Portal Fonts
+	ln -svf --relative /usr/share/fonts/Ubuntu-L.ttf \
+		/srv/web/ipfire/html/captive/assets/Ubuntu-L.ttf
+	ln -svf --relative /usr/share/fonts/Ubuntu-M.ttf \
+		/srv/web/ipfire/html/captive/assets/Ubuntu-M.ttf
+	ln -svf --relative /usr/share/fonts/Ubuntu-R.ttf \
+		/srv/web/ipfire/html/captive/assets/Ubuntu-R.ttf
+
 	@$(POSTBUILD)
diff --git a/make.sh b/make.sh
index 815b25a..790c6b8 100755
--- a/make.sh
+++ b/make.sh
@@ -548,6 +548,7 @@ buildipfire() {
   lfsmake2 web-user-interface
   lfsmake2 flag-icons
   lfsmake2 jquery
+  lfsmake2 bootstrap
   lfsmake2 arping
   lfsmake2 beep
   lfsmake2 dvdrtools
@@ -626,6 +627,7 @@ buildipfire() {
   lfsmake2 openssh
   lfsmake2 fontconfig
   lfsmake2 dejavu-fonts-ttf
+  lfsmake2 ubuntu-font-family
   lfsmake2 freefont
   lfsmake2 pixman
   lfsmake2 cairo
diff --git a/src/initscripts/system/firewall b/src/initscripts/system/firewall
index 7bdb292..c4d2fef 100644
--- a/src/initscripts/system/firewall
+++ b/src/initscripts/system/firewall
@@ -224,6 +224,13 @@ iptables_init() {
 		iptables -A ${i} -j LOOPBACK
 	done
 
+	# Captive portal
+	iptables -N CAPTIVE_PORTAL
+	iptables -N CAPTIVE_PORTAL_CLIENTS
+	for i in INPUT FORWARD; do
+		iptables -A ${i} -j CAPTIVE_PORTAL
+	done
+
 	# Accept everything connected
 	for i in INPUT FORWARD OUTPUT; do
 		iptables -A ${i} -j CONNTRACK
@@ -337,6 +344,10 @@ iptables_init() {
 	iptables -N UPNPFW
 	iptables -A FORWARD -m conntrack --ctstate NEW -j UPNPFW
 
+	# Captive Portal
+	iptables -t nat -N CAPTIVE_PORTAL
+	iptables -t nat -A PREROUTING -j CAPTIVE_PORTAL
+
 	# RED chain, used for the red interface
 	iptables -N REDINPUT
 	iptables -A INPUT -j REDINPUT
diff --git a/src/misc-progs/Makefile b/src/misc-progs/Makefile
index 7cc70b4..c3329b1 100644
--- a/src/misc-progs/Makefile
+++ b/src/misc-progs/Makefile
@@ -31,7 +31,8 @@ SUID_PROGS = squidctrl sshctrl ipfirereboot \
 	redctrl syslogdctrl extrahdctrl sambactrl upnpctrl \
 	smartctrl clamavctrl addonctrl pakfire mpfirectrl wlanapctrl \
 	setaliases urlfilterctrl updxlratorctrl fireinfoctrl rebuildroutes \
-	getconntracktable wirelessclient torctrl ddnsctrl unboundctrl
+	getconntracktable wirelessclient torctrl ddnsctrl unboundctrl \
+	captivectrl
 SUID_UPDX = updxsetperms
 
 OBJS = $(patsubst %,%.o,$(PROGS) $(SUID_PROGS))
diff --git a/src/misc-progs/captivectrl.c b/src/misc-progs/captivectrl.c
new file mode 100644
index 0000000..6b68f97
--- /dev/null
+++ b/src/misc-progs/captivectrl.c
@@ -0,0 +1,369 @@
+/* This file is part of the IPFire Firewall.
+*
+* This program is distributed under the terms of the GNU General Public
+* Licence.  See the file COPYING for details. */
+
+#define _BSD_SOURCE
+#define _XOPEN_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "libsmooth.h"
+#include "setuid.h"
+
+#define CAPTIVE_PORTAL_SETTINGS		CONFIG_ROOT "/captive/settings"
+#define ETHERNET_SETTINGS		CONFIG_ROOT "/ethernet/settings"
+
+#define CLIENTS				CONFIG_ROOT "/captive/clients"
+#define IPTABLES			"/sbin/iptables --wait"
+#define HTTP_PORT			80
+#define REDIRECT_PORT			1013
+
+typedef struct client {
+	char etheraddr[STRING_SIZE];
+	char ipaddr[STRING_SIZE];
+	time_t time_start;
+	int expires;
+
+	struct client* next;
+} client_t;
+
+static time_t parse_time(const char* s) {
+	int t = 0;
+
+	if (sscanf(s, "%d", &t) == 1) {
+		return (time_t)t;
+	}
+
+	return -1;
+}
+
+static char* format_time(const time_t* t) {
+	char buffer[STRING_SIZE];
+
+	struct tm* tm = gmtime(t);
+	if (tm == NULL)
+		return NULL;
+
+	strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M", tm);
+
+	return strdup(buffer);
+}
+
+static client_t* read_clients(char* filename) {
+	FILE* f = NULL;
+
+	if (!(f = fopen(filename, "r"))) {
+		fprintf(stderr, "Could not open configuration file: %s\n", filename);
+		return NULL;;
+	}
+
+	char line[STRING_SIZE];
+
+	client_t* client_first = NULL;
+	client_t* client_last = NULL;
+	client_t* client_curr;
+
+	while ((fgets(line, STRING_SIZE, f) != NULL)) {
+		if (line[strlen(line) - 1] == '\n')
+			line[strlen(line) - 1] = '\0';
+
+		// Skip all commented lines
+		if (*line == '#')
+			continue;
+
+		client_curr = (client_t*)malloc(sizeof(client_t));
+		memset(client_curr, 0, sizeof(client_t));
+
+		if (client_first == NULL)
+			client_first = client_curr;
+		else
+			client_last->next = client_curr;
+		client_last = client_curr;
+
+		unsigned int count = 0;
+		char* lineptr = line;
+		while (1) {
+			if (!*lineptr)
+				break;
+
+			char* word = lineptr;
+			while (*lineptr != '\0') {
+				if (*lineptr == ',') {
+					*lineptr = '\0';
+					lineptr++;
+					break;
+				}
+				lineptr++;
+			}
+
+			switch (count++) {
+				// Ethernet address
+				case 1:
+					strcpy(client_curr->etheraddr, word);
+					break;
+
+				// IP address
+				case 2:
+					strcpy(client_curr->ipaddr, word);
+					break;
+
+				// Start time
+				case 3:
+					client_curr->time_start = parse_time(word);
+					break;
+
+				// Expire duration
+				case 4:
+					client_curr->expires = atoi(word);
+					break;
+
+				default:
+					break;
+			}
+		}
+	}
+
+	if (f)
+		fclose(f);
+
+	return client_first;
+}
+
+static void flush_chains() {
+	// filter
+	safe_system(IPTABLES " -F CAPTIVE_PORTAL");
+	safe_system(IPTABLES " -F CAPTIVE_PORTAL_CLIENTS");
+
+	// nat
+	safe_system(IPTABLES " -t nat -F CAPTIVE_PORTAL");
+}
+
+static int setup_dns_filters() {
+	const char* protos[] = { "udp", "tcp", NULL };
+
+	// Limits the number of DNS requests to 3 kByte/s
+	// A burst of 1MB is permitted at the start
+	const char* limiter = "-m hashlimit --hashlimit-name dns-filter"
+		" --hashlimit-mode srcip --hashlimit-upto 3kb/sec --hashlimit-burst 1024kb";
+
+	char command[STRING_SIZE];
+
+	const char** proto = protos;
+	while (*proto) {
+		snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS -p %s"
+			" --dport 53 %s -j RETURN", *proto, limiter);
+
+		int r = safe_system(command);
+		if (r)
+			return r;
+
+		proto++;
+	}
+
+	return 0;
+}
+
+static int add_client_rules(const client_t* clients) {
+	char command[STRING_SIZE];
+	char match[STRING_SIZE];
+
+	while (clients) {
+		size_t len = 0;
+
+		if (*clients->ipaddr && clients->expires > 0) {
+			len += snprintf(match + len, sizeof(match) - len,
+				"-s %s", clients->ipaddr);
+		}
+
+		len += snprintf(match + len, sizeof(match) - len,
+			" -m mac --mac-source %s", clients->etheraddr);
+
+		if (clients->expires > 0) {
+			time_t expires = clients->time_start + clients->expires;
+
+			char* time_start = format_time(&clients->time_start);
+			char* time_end = format_time(&expires);
+
+			len += snprintf(match + len, sizeof(match) - len,
+				" -m time --datestart %s --datestop %s",
+				time_start, time_end);
+
+			free(time_start);
+			free(time_end);
+		}
+
+		// filter
+		snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS"
+			" %s -j RETURN", match);
+		safe_system(command);
+
+		// nat
+		snprintf(command, sizeof(command), IPTABLES " -t nat -A CAPTIVE_PORTAL"
+			" %s -j RETURN", match);
+		safe_system(command);
+
+		// Move on to the next client
+		clients = clients->next;
+	}
+
+	return 0;
+}
+
+static char* get_key(struct keyvalue* settings, char* key) {
+	char value[STRING_SIZE];
+
+	if (!findkey(settings, key, value))
+		return NULL;
+
+	return strdup(value);
+}
+
+static int add_interface_rule(const char* intf, int allow_webif_access) {
+	int r;
+	char command[STRING_SIZE];
+
+	if ((intf == NULL) || (strlen(intf) == 0)) {
+		fprintf(stderr, "Empty interface given\n");
+		return -1;
+	}
+
+	snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL -i %s"
+		" -j CAPTIVE_PORTAL_CLIENTS", intf);
+	r = safe_system(command);
+	if (r)
+		return r;
+
+#if 0
+	snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL -o %s"
+		" -j CAPTIVE_PORTAL_CLIENTS", intf);
+	r = safe_system(command);
+	if (r)
+		return r;
+#endif
+
+	if (allow_webif_access) {
+		snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS"
+			" -i %s -p tcp --dport 444 -j RETURN", intf);
+		r = safe_system(command);
+		if (r)
+			return r;
+	}
+
+	// Redirect all unauthenticated clients
+	snprintf(command, sizeof(command), IPTABLES " -t nat -A CAPTIVE_PORTAL -i %s"
+		" -p tcp --dport %d -j REDIRECT --to-ports %d", intf, HTTP_PORT, REDIRECT_PORT);
+	r = safe_system(command);
+	if (r)
+		return r;
+
+	// Allow access to captive portal site
+	snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS"
+		" -i %s -p tcp --dport %d -j RETURN", intf, REDIRECT_PORT);
+	r = safe_system(command);
+	if (r)
+		return r;
+
+	return 0;
+}
+
+static int add_interface_rules(struct keyvalue* captive_portal_settings, struct keyvalue* ethernet_settings) {
+	const char* intf;
+	char* setting;
+	int r = 0;
+
+	setting = get_key(captive_portal_settings, "ENABLE_GREEN");
+	if (setting && (strcmp(setting, "on") == 0)) {
+		free(setting);
+
+		intf = get_key(ethernet_settings, "GREEN_DEV");
+		r = add_interface_rule(intf, /* allow webif access from green */ 1);
+		if (r)
+			return r;
+	}
+
+	setting = get_key(captive_portal_settings, "ENABLE_BLUE");
+	if (setting && (strcmp(setting, "on") == 0)) {
+		free(setting);
+
+		intf = get_key(ethernet_settings, "BLUE_DEV");
+		r = add_interface_rule(intf, /* do not allow webif access */ 0);
+		if (r)
+			return r;
+	}
+
+	// Always pass DNS packets through all firewall rules
+	r = setup_dns_filters();
+	if (r)
+		return r;
+
+	// Add the last rule
+	r = safe_system(IPTABLES " -A CAPTIVE_PORTAL_CLIENTS -j DROP");
+	if (r)
+		return r;
+
+	return r;
+}
+
+int main(int argc, char** argv) {
+	int r = 0;
+	char* intf = NULL;
+	client_t* clients = NULL;
+
+	struct keyvalue* captive_portal_settings = NULL;
+	struct keyvalue* ethernet_settings = NULL;
+
+	if (!(initsetuid()))
+		exit(2);
+
+	ethernet_settings = initkeyvalues();
+	if (!readkeyvalues(ethernet_settings, ETHERNET_SETTINGS)) {
+		fprintf(stderr, "Could not read %s\n", ETHERNET_SETTINGS);
+		r = 1;
+		goto END;
+	}
+
+	captive_portal_settings = initkeyvalues();
+	if (!readkeyvalues(captive_portal_settings, CAPTIVE_PORTAL_SETTINGS)) {
+		fprintf(stderr, "Could not read %s\n", CAPTIVE_PORTAL_SETTINGS);
+		r = 1;
+		goto END;
+	}
+
+	clients = read_clients(CLIENTS);
+
+	// Clean up all old rules
+	flush_chains();
+
+	// Add all client rules
+	r = add_client_rules(clients);
+	if (r)
+		goto END;
+
+	// Add all interface rules
+	r = add_interface_rules(captive_portal_settings, ethernet_settings);
+	if (r)
+		goto END;
+
+END:
+	while (clients) {
+		client_t* head = clients;
+		clients = clients->next;
+
+		free(head);
+	}
+
+	if (ethernet_settings)
+		freekeyvalues(ethernet_settings);
+
+	if (captive_portal_settings)
+		freekeyvalues(captive_portal_settings);
+
+	if (intf)
+		free(intf);
+
+	return r;
+}
diff --git a/src/misc-progs/wirelessctrl.c b/src/misc-progs/wirelessctrl.c
index 1e166eb..7cffc89 100644
--- a/src/misc-progs/wirelessctrl.c
+++ b/src/misc-progs/wirelessctrl.c
@@ -42,6 +42,7 @@ int main(void) {
 	char buffer[STRING_SIZE];
 	char *index, *ipaddress, *macaddress, *enabled;
 	struct keyvalue *kv = NULL;
+	struct keyvalue* captive_settings = NULL;
 
 	if (!(initsetuid()))
 		exit(1);
@@ -67,6 +68,13 @@ int main(void) {
 		exit(1);
 	}
 
+	// Read captive portal settings
+	captive_settings = initkeyvalues();
+	if (!readkeyvalues(captive_settings, CONFIG_ROOT "/captive/settings")) {
+		fprintf(stderr, "Could not read captive portal settings\n");
+		exit(1);
+	}
+
 	/* Get the BLUE interface details */
 	if (findkey(kv, "BLUE_DEV", blue_dev) > 0) {
 		if ((strlen(blue_dev) > 0) && !VALID_DEVICE(blue_dev)) {
@@ -79,6 +87,15 @@ int main(void) {
 		exit(0);
 	}
 
+	// Check if the captive portal is enabled on blue. If so, we will
+	// just keep the chains flushed and do not add any rules.
+	char captive_enabled[STRING_SIZE];
+	if (findkey(captive_settings, "ENABLE_BLUE", captive_enabled) > 0) {
+		if (strcmp(captive_enabled, "on") == 0) {
+			return 0;
+		}
+	}
+
 	if ((fd = fopen(CONFIG_ROOT "/wireless/nodrop", "r")))
 		return 0;
 
diff --git a/src/scripts/captive-cleanup b/src/scripts/captive-cleanup
new file mode 100755
index 0000000..c39e488
--- /dev/null
+++ b/src/scripts/captive-cleanup
@@ -0,0 +1,46 @@
+#!/usr/bin/perl
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2016  IPFire Team  <alexander.marx(a)ipfire.org>                #
+#                                                                             #
+# 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 3 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, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+use strict;
+
+require '/var/ipfire/general-functions.pl';
+
+my %settings=();
+my %clientshash=();
+my $settingsfile="${General::swroot}/captive/settings";
+my $clients="${General::swroot}/captive/clients";
+my $time;
+my $expiretime;
+
+if (-f $settingsfile && -f $clients && ! -z $clients){
+	&General::readhash("$settingsfile", \%settings) if(-f $settingsfile);
+	&General::readhasharray("$clients", \%clientshash);
+	$time = time();
+	foreach my $key (keys %clientshash) {
+		$expiretime=($clientshash{$key}[2])+$clientshash{$key}[3];
+		if ($expiretime < $time){
+			delete $clientshash{$key};
+			my $exp = gmtime($expiretime);
+			&General::log("Captive", "Delete expired voucher $clientshash{$key}[4] expired on $exp. Remark: $clientshash{$key}[5]");
+		}
+	}
+	&General::writehasharray("$clients", \%clientshash);
+}


hooks/post-receive
--
IPFire 2.x development tree

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2017-10-04 15:10 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-10-04 15:10 [git.ipfire.org] IPFire 2.x development tree branch, next, updated. 027614d2dc3107a4e071e8dac49ce8a74c36e4ea git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox